fix: WIndows not finding the gith bash path (#724)

* fix: WIndows not finding the gith bash path

* Update apps/frontend/src/main/utils/windows-paths.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Update apps/backend/core/client.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Update apps/backend/core/auth.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix: improve code quality in Windows path detection

- Use splitlines() instead of split("\n") for robust cross-platform line handling
- Add explanatory comment for intentionally suppressed exceptions
- Standardize Windows detection to platform.system() for consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alex
2026-01-06 10:47:10 +01:00
committed by GitHub
parent 78aceaed1e
commit 09aa4f4f71
6 changed files with 421 additions and 24 deletions
+91
View File
@@ -36,6 +36,8 @@ SDK_ENV_VARS = [
"DISABLE_TELEMETRY",
"DISABLE_COST_WARNINGS",
"API_TIMEOUT_MS",
# Windows-specific: Git Bash path for Claude Code CLI
"CLAUDE_CODE_GIT_BASH_PATH",
]
@@ -215,6 +217,85 @@ def require_auth_token() -> str:
return token
def _find_git_bash_path() -> str | None:
"""
Find git-bash (bash.exe) path on Windows.
Uses 'where git' to find git.exe, then derives bash.exe location from it.
Git for Windows installs bash.exe in the 'bin' directory alongside git.exe
or in the parent 'bin' directory when git.exe is in 'cmd'.
Returns:
Full path to bash.exe if found, None otherwise
"""
if platform.system() != "Windows":
return None
# If already set in environment, use that
existing = os.environ.get("CLAUDE_CODE_GIT_BASH_PATH")
if existing and os.path.exists(existing):
return existing
git_path = None
# Method 1: Use 'where' command to find git.exe
try:
# Use where.exe explicitly for reliability
result = subprocess.run(
["where.exe", "git"],
capture_output=True,
text=True,
timeout=5,
shell=False,
)
if result.returncode == 0 and result.stdout.strip():
git_paths = result.stdout.strip().splitlines()
if git_paths:
git_path = git_paths[0].strip()
except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError):
# Intentionally suppress errors - best-effort detection with fallback to common paths
pass
# Method 2: Check common installation paths if 'where' didn't work
if not git_path:
common_git_paths = [
os.path.expandvars(r"%PROGRAMFILES%\Git\cmd\git.exe"),
os.path.expandvars(r"%PROGRAMFILES%\Git\bin\git.exe"),
os.path.expandvars(r"%PROGRAMFILES(X86)%\Git\cmd\git.exe"),
os.path.expandvars(r"%LOCALAPPDATA%\Programs\Git\cmd\git.exe"),
]
for path in common_git_paths:
if os.path.exists(path):
git_path = path
break
if not git_path:
return None
# Derive bash.exe location from git.exe location
# Git for Windows structure:
# C:\...\Git\cmd\git.exe -> bash.exe is at C:\...\Git\bin\bash.exe
# C:\...\Git\bin\git.exe -> bash.exe is at C:\...\Git\bin\bash.exe
# C:\...\Git\mingw64\bin\git.exe -> bash.exe is at C:\...\Git\bin\bash.exe
git_dir = os.path.dirname(git_path)
git_parent = os.path.dirname(git_dir)
git_grandparent = os.path.dirname(git_parent)
# Check common bash.exe locations relative to git installation
possible_bash_paths = [
os.path.join(git_parent, "bin", "bash.exe"), # cmd -> bin
os.path.join(git_dir, "bash.exe"), # If git.exe is in bin
os.path.join(git_grandparent, "bin", "bash.exe"), # mingw64/bin -> bin
]
for bash_path in possible_bash_paths:
if os.path.exists(bash_path):
return bash_path
return None
def get_sdk_env_vars() -> dict[str, str]:
"""
Get environment variables to pass to SDK.
@@ -222,6 +303,8 @@ def get_sdk_env_vars() -> dict[str, str]:
Collects relevant env vars (ANTHROPIC_BASE_URL, etc.) that should
be passed through to the claude-agent-sdk subprocess.
On Windows, auto-detects CLAUDE_CODE_GIT_BASH_PATH if not already set.
Returns:
Dict of env var name -> value for non-empty vars
"""
@@ -230,6 +313,14 @@ def get_sdk_env_vars() -> dict[str, str]:
value = os.environ.get(var)
if value:
env[var] = value
# On Windows, auto-detect git-bash path if not already set
# Claude Code CLI requires bash.exe to run on Windows
if platform.system() == "Windows" and "CLAUDE_CODE_GIT_BASH_PATH" not in env:
bash_path = _find_git_bash_path()
if bash_path:
env["CLAUDE_CODE_GIT_BASH_PATH"] = bash_path
return env
+7
View File
@@ -16,6 +16,7 @@ import copy
import json
import logging
import os
import platform
import threading
import time
from pathlib import Path
@@ -488,6 +489,12 @@ def create_client(
# Collect env vars to pass to SDK (ANTHROPIC_BASE_URL, etc.)
sdk_env = get_sdk_env_vars()
# Debug: Log git-bash path detection on Windows
if "CLAUDE_CODE_GIT_BASH_PATH" in sdk_env:
logger.info(f"Git Bash path found: {sdk_env['CLAUDE_CODE_GIT_BASH_PATH']}")
elif platform.system() == "Windows":
logger.warning("Git Bash path not detected on Windows!")
# Check if Linear integration is enabled
linear_enabled = is_linear_enabled()
linear_api_key = os.environ.get("LINEAR_API_KEY", "")
@@ -17,6 +17,62 @@ import { readSettingsFile } from '../settings-utils';
import type { AppSettings } from '../../shared/types/settings';
import { getOAuthModeClearVars } from './env-utils';
import { getAugmentedEnv } from '../env-utils';
import { getToolInfo } from '../cli-tool-manager';
function deriveGitBashPath(gitExePath: string): string | null {
if (process.platform !== 'win32') {
return null;
}
try {
const gitDir = path.dirname(gitExePath); // e.g., D:\...\Git\mingw64\bin
const gitDirName = path.basename(gitDir).toLowerCase();
// Find Git installation root
let gitRoot: string;
if (gitDirName === 'cmd') {
// .../Git/cmd/git.exe -> .../Git
gitRoot = path.dirname(gitDir);
} else if (gitDirName === 'bin') {
// Could be .../Git/bin/git.exe OR .../Git/mingw64/bin/git.exe
const parent = path.dirname(gitDir);
const parentName = path.basename(parent).toLowerCase();
if (parentName === 'mingw64' || parentName === 'mingw32') {
// .../Git/mingw64/bin/git.exe -> .../Git
gitRoot = path.dirname(parent);
} else {
// .../Git/bin/git.exe -> .../Git
gitRoot = parent;
}
} else {
// Unknown structure - try to find 'bin' sibling
gitRoot = path.dirname(gitDir);
}
// Bash.exe is in Git/bin/bash.exe
const bashPath = path.join(gitRoot, 'bin', 'bash.exe');
if (existsSync(bashPath)) {
console.log('[AgentProcess] Derived git-bash path:', bashPath);
return bashPath;
}
// Fallback: check one level up if gitRoot didn't work
const altBashPath = path.join(path.dirname(gitRoot), 'bin', 'bash.exe');
if (existsSync(altBashPath)) {
console.log('[AgentProcess] Found git-bash at alternate path:', altBashPath);
return altBashPath;
}
console.warn('[AgentProcess] Could not find bash.exe from git path:', gitExePath);
return null;
} catch (error) {
console.error('[AgentProcess] Error deriving git-bash path:', error);
return null;
}
}
/**
* Process spawning and lifecycle management
@@ -59,8 +115,28 @@ export class AgentProcessManager {
// Use getAugmentedEnv() to ensure common tool paths (dotnet, homebrew, etc.)
// are available even when app is launched from Finder/Dock
const augmentedEnv = getAugmentedEnv();
// On Windows, detect and pass git-bash path for Claude Code CLI
// Electron can detect git via where.exe, but Python subprocess may not have the same PATH
const gitBashEnv: Record<string, string> = {};
if (process.platform === 'win32' && !process.env.CLAUDE_CODE_GIT_BASH_PATH) {
try {
const gitInfo = getToolInfo('git');
if (gitInfo.found && gitInfo.path) {
const bashPath = deriveGitBashPath(gitInfo.path);
if (bashPath) {
gitBashEnv['CLAUDE_CODE_GIT_BASH_PATH'] = bashPath;
console.log('[AgentProcess] Setting CLAUDE_CODE_GIT_BASH_PATH:', bashPath);
}
}
} catch (error) {
console.warn('[AgentProcess] Failed to detect git-bash path:', error);
}
}
return {
...augmentedEnv,
...gitBashEnv,
...extraEnv,
...profileEnv,
PYTHONUNBUFFERED: '1',
+39 -1
View File
@@ -28,6 +28,11 @@ import { app } from 'electron';
import { findExecutable } from './env-utils';
import type { ToolDetectionResult } from '../shared/types';
import { findHomebrewPython as findHomebrewPythonUtil } from './utils/homebrew-python';
import {
getWindowsExecutablePaths,
WINDOWS_GIT_PATHS,
findWindowsExecutableViaWhere,
} from './utils/windows-paths';
/**
* Supported CLI tools managed by this system
@@ -392,7 +397,40 @@ class CLIToolManager {
}
}
// 4. Not found - fallback to 'git'
// 4. Windows-specific detection using 'where' command (most reliable for custom installs)
if (process.platform === 'win32') {
// First try 'where' command - finds git regardless of installation location
const whereGitPath = findWindowsExecutableViaWhere('git', '[Git]');
if (whereGitPath) {
const validation = this.validateGit(whereGitPath);
if (validation.valid) {
return {
found: true,
path: whereGitPath,
version: validation.version,
source: 'system-path',
message: `Using Windows Git: ${whereGitPath}`,
};
}
}
// Fallback to checking common installation paths
const windowsPaths = getWindowsExecutablePaths(WINDOWS_GIT_PATHS, '[Git]');
for (const winGitPath of windowsPaths) {
const validation = this.validateGit(winGitPath);
if (validation.valid) {
return {
found: true,
path: winGitPath,
version: validation.version,
source: 'system-path',
message: `Using Windows Git: ${winGitPath}`,
};
}
}
}
// 5. Not found - fallback to 'git'
return {
found: false,
source: 'fallback',
@@ -0,0 +1,172 @@
/**
* Windows Executable Path Discovery Utility
*
* Provides reusable logic for finding Windows executables in common installation
* locations. Handles environment variable expansion and security validation.
*
* Used by cli-tool-manager.ts for Git, GitHub CLI, Claude CLI, etc.
* Follows the same pattern as homebrew-python.ts for platform-specific detection.
*/
import { existsSync } from 'fs';
import { execFileSync } from 'child_process';
import path from 'path';
import os from 'os';
export interface WindowsToolPaths {
toolName: string;
executable: string;
patterns: string[];
}
export const WINDOWS_GIT_PATHS: WindowsToolPaths = {
toolName: 'Git',
executable: 'git.exe',
patterns: [
'%PROGRAMFILES%\\Git\\cmd',
'%PROGRAMFILES(X86)%\\Git\\cmd',
'%LOCALAPPDATA%\\Programs\\Git\\cmd',
'%USERPROFILE%\\scoop\\apps\\git\\current\\cmd',
'%PROGRAMFILES%\\Git\\bin',
'%PROGRAMFILES(X86)%\\Git\\bin',
'%PROGRAMFILES%\\Git\\mingw64\\bin',
],
};
function isSecurePath(pathStr: string): boolean {
const dangerousPatterns = [
/[;&|`$(){}[\]<>!]/, // Shell metacharacters
/\.\.\//, // Unix directory traversal
/\.\.\\/, // Windows directory traversal
/[\r\n]/, // Newlines (command injection)
];
for (const pattern of dangerousPatterns) {
if (pattern.test(pathStr)) {
return false;
}
}
return true;
}
export function expandWindowsPath(pathPattern: string): string | null {
const envVars: Record<string, string | undefined> = {
'%PROGRAMFILES%': process.env.ProgramFiles || 'C:\\Program Files',
'%PROGRAMFILES(X86)%': process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)',
'%LOCALAPPDATA%': process.env.LOCALAPPDATA,
'%APPDATA%': process.env.APPDATA,
'%USERPROFILE%': process.env.USERPROFILE || os.homedir(),
};
let expandedPath = pathPattern;
for (const [placeholder, value] of Object.entries(envVars)) {
if (expandedPath.includes(placeholder)) {
if (!value) {
return null;
}
expandedPath = expandedPath.replace(placeholder, value);
}
}
// Verify no unexpanded placeholders remain (indicates unknown variable)
if (/%[^%]+%/.test(expandedPath)) {
return null;
}
// Normalize the path (resolve double backslashes, etc.)
return path.normalize(expandedPath);
}
export function getWindowsExecutablePaths(
toolPaths: WindowsToolPaths,
logPrefix: string = '[Windows Paths]'
): string[] {
// Only run on Windows
if (process.platform !== 'win32') {
return [];
}
const validPaths: string[] = [];
for (const pattern of toolPaths.patterns) {
const expandedDir = expandWindowsPath(pattern);
if (!expandedDir) {
console.warn(`${logPrefix} Could not expand path pattern: ${pattern}`);
continue;
}
const fullPath = path.join(expandedDir, toolPaths.executable);
// Security validation - reject potentially dangerous paths
if (!isSecurePath(fullPath)) {
console.warn(`${logPrefix} Path failed security validation: ${fullPath}`);
continue;
}
if (existsSync(fullPath)) {
validPaths.push(fullPath);
}
}
return validPaths;
}
/**
* Find a Windows executable using the `where` command.
* This is the most reliable method as it searches:
* - All directories in PATH
* - App Paths registry entries
* - Current directory
*
* Works regardless of where the tool is installed (custom paths, different drives, etc.)
*
* @param executable - The executable name (e.g., 'git', 'gh', 'python')
* @param logPrefix - Prefix for console logging
* @returns The full path to the executable, or null if not found
*/
export function findWindowsExecutableViaWhere(
executable: string,
logPrefix: string = '[Windows Where]'
): string | null {
if (process.platform !== 'win32') {
return null;
}
// Security: Only allow simple executable names (alphanumeric, dash, underscore, dot)
if (!/^[\w.-]+$/.test(executable)) {
console.warn(`${logPrefix} Invalid executable name: ${executable}`);
return null;
}
try {
// Use 'where' command to find the executable
// where.exe is a built-in Windows command that finds executables
const result = execFileSync('where.exe', [executable], {
encoding: 'utf-8',
timeout: 5000,
windowsHide: true,
}).trim();
// 'where' returns multiple paths separated by newlines if found in multiple locations
// We take the first one (highest priority in PATH)
const paths = result.split(/\r?\n/).filter(p => p.trim());
if (paths.length > 0) {
const foundPath = paths[0].trim();
// Validate the path exists and is secure
if (existsSync(foundPath) && isSecurePath(foundPath)) {
console.log(`${logPrefix} Found via where: ${foundPath}`);
return foundPath;
}
}
return null;
} catch {
// 'where' returns exit code 1 if not found, which throws an error
return null;
}
}
+36 -23
View File
@@ -268,6 +268,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -652,6 +653,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -695,6 +697,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@@ -734,6 +737,7 @@
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2",
@@ -1599,7 +1603,6 @@
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"cross-dirname": "^0.1.0",
"debug": "^4.3.4",
@@ -1621,7 +1624,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -1638,7 +1640,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"universalify": "^2.0.0"
},
@@ -1653,7 +1654,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
@@ -2807,6 +2807,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -2828,6 +2829,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz",
"integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": "^18.19.0 || >=20.6.0"
},
@@ -2840,6 +2842,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz",
"integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
},
@@ -2855,6 +2858,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz",
"integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/api-logs": "0.208.0",
"import-in-the-middle": "^2.0.0",
@@ -3242,6 +3246,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
@@ -3258,6 +3263,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz",
"integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/core": "2.2.0",
"@opentelemetry/resources": "2.2.0",
@@ -3275,6 +3281,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz",
"integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=14"
}
@@ -5512,8 +5519,7 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -5744,6 +5750,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -5754,6 +5761,7 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -5861,6 +5869,7 @@
"integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.51.0",
"@typescript-eslint/types": "8.51.0",
@@ -6275,6 +6284,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6344,6 +6354,7 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -6964,6 +6975,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -7777,8 +7789,7 @@
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
"optional": true
},
"node_modules/cross-env": {
"version": "10.1.0",
@@ -8164,6 +8175,7 @@
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "26.0.12",
"builder-util": "26.0.11",
@@ -8259,8 +8271,7 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/dotenv": {
"version": "16.6.1",
@@ -8335,6 +8346,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^22.7.7",
@@ -8583,7 +8595,6 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/asar": "^3.2.1",
"debug": "^4.1.1",
@@ -8604,7 +8615,6 @@
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
@@ -8979,6 +8989,7 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -10204,6 +10215,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4"
},
@@ -11006,6 +11018,7 @@
"integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@acemir/cssom": "^0.9.28",
"@asamuzakjp/dom-selector": "^6.7.6",
@@ -11742,7 +11755,6 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -13812,6 +13824,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -13887,7 +13900,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"commander": "^9.4.0"
},
@@ -13905,7 +13917,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^12.20.0 || >=14"
}
@@ -13926,7 +13937,6 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -13942,7 +13952,6 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10"
},
@@ -14070,6 +14079,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -14079,6 +14089,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -14118,8 +14129,7 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/react-markdown": {
"version": "10.1.0",
@@ -14554,7 +14564,6 @@
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -15445,7 +15454,8 @@
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/tapable": {
"version": "2.3.0",
@@ -15555,7 +15565,6 @@
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
@@ -15619,7 +15628,6 @@
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.6"
},
@@ -15711,6 +15719,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -15973,6 +15982,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -16322,6 +16332,7 @@
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -16914,6 +16925,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -17454,6 +17466,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.2.tgz",
"integrity": "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}