From 29d0a0e50e855d6a3cd2d0e90377421938dd0e9a Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 7 May 2026 10:15:26 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=89=B9?= =?UTF-8?q?=E5=A4=84=E7=90=86=E8=84=9A=E6=9C=AC=E7=94=A8=E4=BA=8E=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E3=80=81=E6=9E=84=E5=BB=BA=E5=92=8C=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加多个批处理脚本用于不同场景: - server.bat: 启动API和WebSocket服务 - dev.bat: 开发环境启动器,支持CLI和服务模式 - build.bat: 构建脚本,生成可执行文件 - desktop/dev.bat: 桌面开发启动器,支持前端和Tauri模式 - desktop/build.bat: 桌面应用构建脚本,支持前端和完整Tauri构建 脚本包含错误检查、依赖安装和环境配置功能 --- build.bat | 138 ++++++++++++++++++++++++++++++++++++++++++++++ desktop/build.bat | 136 +++++++++++++++++++++++++++++++++++++++++++++ desktop/dev.bat | 95 +++++++++++++++++++++++++++++++ dev.bat | 97 ++++++++++++++++++++++++++++++++ server.bat | 47 ++++++++++++++++ 5 files changed, 513 insertions(+) create mode 100644 build.bat create mode 100644 desktop/build.bat create mode 100644 desktop/dev.bat create mode 100644 dev.bat create mode 100644 server.bat diff --git a/build.bat b/build.bat new file mode 100644 index 000000000..556033e55 --- /dev/null +++ b/build.bat @@ -0,0 +1,138 @@ +@echo off +setlocal EnableDelayedExpansion + +echo ============================================ +echo cc-haha Build Script +echo ============================================ +echo. + +cd /d "%~dp0" + +set "BUN_EXE=" +where bun >nul 2>&1 +if %ERRORLEVEL% equ 0 ( + set "BUN_EXE=bun" +) else if exist "%USERPROFILE%\.bun\bin\bun.exe" ( + set "BUN_EXE=%USERPROFILE%\.bun\bin\bun.exe" +) else if exist "C:\Users\Administrator\.bun\bin\bun.exe" ( + set "BUN_EXE=C:\Users\Administrator\.bun\bin\bun.exe" +) else ( + echo [ERROR] bun not found, please install: https://bun.sh + pause + exit /b 1 +) + +if "%BUN_EXE%"=="bun" goto :build_bun_in_path +for %%I in ("%BUN_EXE%") do set "BUN_DIR=%%~dpI" +set "PATH=%BUN_DIR%;%PATH%" +:build_bun_in_path + +if not exist "node_modules" ( + echo [INFO] Installing dependencies... + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Install failed + pause + exit /b 1 + ) + echo. +) + +set OUTPUT_DIR=%~dp0dist +if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%" + +set VERSION=999.0.0-local +for /f "tokens=2 delims=:," %%a in ('findstr /c:"\"version\"" package.json') do ( + set VERSION=%%a + set VERSION=!VERSION: =! + set VERSION=!VERSION:"=! +) +for /f "tokens=*" %%t in ('powershell -command "Get-Date -Format yyyy-MM-ddTHH:mm:ss"') do set BUILD_TIME=%%t + +echo [INFO] Version: %VERSION% +echo [INFO] Build time: %BUILD_TIME% +echo. + +set EXTERNALS=^ + --external @anthropic-ai/bedrock-sdk ^ + --external @anthropic-ai/foundry-sdk ^ + --external @anthropic-ai/vertex-sdk ^ + --external @anthropic-ai/mcpb ^ + --external @azure/identity ^ + --external @aws-sdk/client-sts ^ + --external @aws-sdk/client-bedrock ^ + --external @opentelemetry/exporter-* ^ + --external fflate ^ + --external sharp + +echo [1/3] Building CLI entry... +"%BUN_EXE%" build ./src/entrypoints/cli.tsx ^ + --compile ^ + --outfile "%OUTPUT_DIR%\claude-haha.exe" ^ + --define:process.env.CLAUDE_CODE_LOCAL_VERSION="'%VERSION%'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_BUILD_TIME="'%BUILD_TIME%'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_PACKAGE_URL="'claude-code-local'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH="'1'" ^ + %EXTERNALS% +if %ERRORLEVEL% neq 0 ( + echo [ERROR] CLI build failed + pause + exit /b 1 +) +echo -^> %OUTPUT_DIR%\claude-haha.exe + +echo [2/3] Building Server entry... +"%BUN_EXE%" build ./src/server/index.ts ^ + --compile ^ + --outfile "%OUTPUT_DIR%\cc-haha-server.exe" ^ + --define:process.env.CLAUDE_CODE_LOCAL_VERSION="'%VERSION%'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_BUILD_TIME="'%BUILD_TIME%'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_PACKAGE_URL="'claude-code-local'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH="'1'" ^ + %EXTERNALS% +if %ERRORLEVEL% neq 0 ( + echo [ERROR] Server build failed + pause + exit /b 1 +) +echo -^> %OUTPUT_DIR%\cc-haha-server.exe + +echo [3/3] Building MCP entry... +"%BUN_EXE%" build ./src/entrypoints/mcp.ts ^ + --compile ^ + --outfile "%OUTPUT_DIR%\cc-haha-mcp.exe" ^ + --define:process.env.CLAUDE_CODE_LOCAL_VERSION="'%VERSION%'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_BUILD_TIME="'%BUILD_TIME%'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_PACKAGE_URL="'claude-code-local'" ^ + --define:process.env.CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH="'1'" ^ + %EXTERNALS% +if %ERRORLEVEL% neq 0 ( + echo [WARN] MCP build failed (non-critical, skipping) +) else ( + echo -^> %OUTPUT_DIR%\cc-haha-mcp.exe +) + +echo. +echo ============================================ +echo Build complete! +echo ============================================ +echo. +echo Output: %OUTPUT_DIR%\ +echo. +echo Usage: +echo CLI: claude-haha.exe +echo Server: cc-haha-server.exe --port 3456 +echo MCP: cc-haha-mcp.exe +echo. +echo Environment variables: +echo SERVER_PORT Server port (default 3456) +echo ANTHROPIC_API_KEY API key +echo ANTHROPIC_BASE_URL API base URL +echo. + +for %%f in ("%OUTPUT_DIR%\*.exe") do ( + echo %%~nxf: %%~zf bytes +) +echo. + +pause diff --git a/desktop/build.bat b/desktop/build.bat new file mode 100644 index 000000000..0298e6b4b --- /dev/null +++ b/desktop/build.bat @@ -0,0 +1,136 @@ +@echo off +setlocal EnableDelayedExpansion + +echo ============================================ +echo cc-haha Desktop Build Script +echo ============================================ +echo. + +cd /d "%~dp0" + +set "BUN_EXE=" +where bun >nul 2>&1 +if %ERRORLEVEL% equ 0 ( + set "BUN_EXE=bun" +) else if exist "%USERPROFILE%\.bun\bin\bun.exe" ( + set "BUN_EXE=%USERPROFILE%\.bun\bin\bun.exe" +) else if exist "C:\Users\Administrator\.bun\bin\bun.exe" ( + set "BUN_EXE=C:\Users\Administrator\.bun\bin\bun.exe" +) else ( + echo [ERROR] bun not found, please install: https://bun.sh + pause + exit /b 1 +) + +if "%BUN_EXE%"=="bun" goto :desktop_build_bun_in_path +for %%I in ("%BUN_EXE%") do set "BUN_DIR=%%~dpI" +set "PATH=%BUN_DIR%;%PATH%" +:desktop_build_bun_in_path + +where cargo >nul 2>&1 +if %ERRORLEVEL% neq 0 ( + echo [ERROR] cargo not found. Install Rust: https://rustup.rs + pause + exit /b 1 +) + +echo Select build type: +echo 1. Frontend only (Vite build, no installer) +echo 2. Full Tauri build (MSI installer) +echo. +set /p MODE="Enter option (1/2): " + +if "%MODE%"=="1" goto :frontend +if "%MODE%"=="2" goto :tauri +echo [ERROR] Invalid option +pause +exit /b 1 + +:frontend +echo. +echo [1/2] Type checking... +"%BUN_EXE%" run lint +if %ERRORLEVEL% neq 0 ( + echo [ERROR] Type check failed + pause + exit /b 1 +) + +echo [2/2] Building frontend... +"%BUN_EXE%" run build +if %ERRORLEVEL% neq 0 ( + echo [ERROR] Frontend build failed + pause + exit /b 1 +) + +echo. +echo ============================================ +echo Frontend build complete! +echo ============================================ +echo. +echo Output: dist\ +echo. +goto :end + +:tauri +echo. +echo [INFO] Full Tauri build for Windows x64 +echo ============================================ +echo. +echo This requires: +echo - Visual Studio 2022 Build Tools (C++ workload) +echo - Rust toolchain (stable-x86_64-pc-windows-msvc) +echo. +echo The build may take several minutes on first run. +echo. + +if not exist "node_modules" ( + echo [INFO] Installing root dependencies... + pushd .. + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Root install failed + popd + pause + exit /b 1 + ) + popd + echo [INFO] Installing desktop dependencies... + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Desktop install failed + pause + exit /b 1 + ) + echo. +) + +echo Starting Tauri build... +powershell -ExecutionPolicy Bypass -File ".\scripts\build-windows-x64.ps1" +if %ERRORLEVEL% neq 0 ( + echo. + echo [ERROR] Tauri build failed + pause + exit /b 1 +) + +echo. +echo ============================================ +echo Tauri build complete! +echo ============================================ +echo. +echo Output: build-artifacts\windows-x64\ +echo Look for the .msi installer there. +echo. + +if exist "build-artifacts\windows-x64\BUILD_INFO.txt" ( + echo Build info: + for /f "tokens=*" %%l in (build-artifacts\windows-x64\BUILD_INFO.txt) do ( + echo %%l + ) +) + +:end +echo. +pause diff --git a/desktop/dev.bat b/desktop/dev.bat new file mode 100644 index 000000000..1abf16017 --- /dev/null +++ b/desktop/dev.bat @@ -0,0 +1,95 @@ +@echo off +setlocal EnableDelayedExpansion + +echo ============================================ +echo cc-haha Desktop Dev Launcher +echo ============================================ +echo. + +cd /d "%~dp0" + +set "BUN_EXE=" +where bun >nul 2>&1 +if %ERRORLEVEL% equ 0 ( + set "BUN_EXE=bun" +) else if exist "%USERPROFILE%\.bun\bin\bun.exe" ( + set "BUN_EXE=%USERPROFILE%\.bun\bin\bun.exe" +) else if exist "C:\Users\Administrator\.bun\bin\bun.exe" ( + set "BUN_EXE=C:\Users\Administrator\.bun\bin\bun.exe" +) else ( + echo [ERROR] bun not found, please install: https://bun.sh + pause + exit /b 1 +) + +if "%BUN_EXE%"=="bun" goto :desktop_dev_bun_in_path +for %%I in ("%BUN_EXE%") do set "BUN_DIR=%%~dpI" +set "PATH=%BUN_DIR%;%PATH%" +:desktop_dev_bun_in_path + +if not exist "node_modules" ( + echo [INFO] Installing root dependencies... + pushd .. + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Root install failed + popd + pause + exit /b 1 + ) + popd + echo [INFO] Installing desktop dependencies... + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Desktop install failed + pause + exit /b 1 + ) + echo. +) + +echo Select dev mode: +echo 1. Frontend only (Vite dev server on port 1420) +echo 2. Full Tauri dev (Frontend + Rust backend) +echo. +set /p MODE="Enter option (1/2): " + +if "%MODE%"=="1" goto :vite +if "%MODE%"=="2" goto :tauri +echo [ERROR] Invalid option +pause +exit /b 1 + +:vite +echo. +echo [START] Vite dev server - http://localhost:1420 +echo ============================================ +echo. +echo NOTE: This mode only starts the frontend. +echo You need to manually start the backend server: +echo %BUN_EXE% run ..\src\server\index.ts --port 3456 +echo. +"%BUN_EXE%" run dev +goto :end + +:tauri +echo. +echo [START] Tauri dev mode (Frontend + Rust backend) +echo ============================================ +echo. +echo This will compile the Rust backend on first run. +echo Subsequent starts will be much faster. +echo. + +where cargo >nul 2>&1 +if %ERRORLEVEL% neq 0 ( + echo [ERROR] cargo not found. Install Rust: https://rustup.rs + pause + exit /b 1 +) + +"%BUN_EXE%" run tauri dev +goto :end + +:end +pause diff --git a/dev.bat b/dev.bat new file mode 100644 index 000000000..c670ed74e --- /dev/null +++ b/dev.bat @@ -0,0 +1,97 @@ +@echo off +setlocal EnableDelayedExpansion + +echo ============================================ +echo cc-haha Dev Launcher +echo ============================================ +echo. + +cd /d "%~dp0" + +set "BUN_EXE=" +where bun >nul 2>&1 +if %ERRORLEVEL% equ 0 ( + set "BUN_EXE=bun" +) else if exist "%USERPROFILE%\.bun\bin\bun.exe" ( + set "BUN_EXE=%USERPROFILE%\.bun\bin\bun.exe" +) else if exist "C:\Users\Administrator\.bun\bin\bun.exe" ( + set "BUN_EXE=C:\Users\Administrator\.bun\bin\bun.exe" +) else ( + echo [ERROR] bun not found, please install: https://bun.sh + pause + exit /b 1 +) + +if "%BUN_EXE%"=="bun" goto :dev_bun_in_path +for %%I in ("%BUN_EXE%") do set "BUN_DIR=%%~dpI" +set "PATH=%BUN_DIR%;%PATH%" +:dev_bun_in_path + +if not exist "node_modules" ( + echo [INFO] Installing dependencies... + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Install failed + pause + exit /b 1 + ) + echo. +) + +echo Select mode: +echo 1. CLI mode (interactive terminal) +echo 2. Server mode (API + WebSocket server) +echo 3. Server + CLI (two windows) +echo. +set /p MODE="Enter option (1/2/3): " + +if "%MODE%"=="1" goto :cli +if "%MODE%"=="2" goto :server +if "%MODE%"=="3" goto :both +echo [ERROR] Invalid option +pause +exit /b 1 + +:cli +echo. +echo [START] CLI mode - interactive terminal +echo ============================================ +set CALLER_DIR=%CD% +if exist .env ( + "%BUN_EXE%" --env-file=.env ./src/entrypoints/cli.tsx %* +) else ( + "%BUN_EXE%" ./src/entrypoints/cli.tsx %* +) +goto :end + +:server +echo. +echo [START] Server mode - API + WebSocket server +echo ============================================ +set SERVER_PORT=3456 +echo Server: http://127.0.0.1:%SERVER_PORT% +echo API: http://127.0.0.1:%SERVER_PORT%/api/providers +echo. +"%BUN_EXE%" ./src/server/index.ts --port %SERVER_PORT% +goto :end + +:both +echo. +echo [START] Server + CLI dual mode +echo ============================================ +set SERVER_PORT=3456 +echo Starting server on port %SERVER_PORT%... +start "cc-haha Server" "%BUN_EXE%" ./src/server/index.ts --port %SERVER_PORT% +timeout /t 2 /nobreak >nul +echo Starting CLI... +set CALLER_DIR=%CD% +set CC_HAHA_DESKTOP_SERVER_URL=http://127.0.0.1:%SERVER_PORT% +if exist .env ( + "%BUN_EXE%" --env-file=.env ./src/entrypoints/cli.tsx %* +) else ( + "%BUN_EXE%" ./src/entrypoints/cli.tsx %* +) +goto :end + +:end +pause diff --git a/server.bat b/server.bat new file mode 100644 index 000000000..ffdf4d084 --- /dev/null +++ b/server.bat @@ -0,0 +1,47 @@ +@echo off +setlocal + +cd /d "%~dp0" + +set "BUN_EXE=" +where bun >nul 2>&1 +if %ERRORLEVEL% equ 0 ( + set "BUN_EXE=bun" +) else if exist "%USERPROFILE%\.bun\bin\bun.exe" ( + set "BUN_EXE=%USERPROFILE%\.bun\bin\bun.exe" +) else if exist "C:\Users\Administrator\.bun\bin\bun.exe" ( + set "BUN_EXE=C:\Users\Administrator\.bun\bin\bun.exe" +) else ( + echo [ERROR] bun not found, please install: https://bun.sh + pause + exit /b 1 +) + +if "%BUN_EXE%"=="bun" goto :server_bun_in_path +for %%I in ("%BUN_EXE%") do set "BUN_DIR=%%~dpI" +set "PATH=%BUN_DIR%;%PATH%" +:server_bun_in_path + +if not exist "node_modules" ( + echo [INFO] Installing dependencies... + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Install failed + pause + exit /b 1 + ) +) + +set SERVER_PORT=3456 +if not "%1"=="" set SERVER_PORT=%1 + +echo ============================================ +echo cc-haha Server +echo ============================================ +echo. +echo URL: http://127.0.0.1:%SERVER_PORT% +echo API: http://127.0.0.1:%SERVER_PORT%/api/providers +echo WS: ws://127.0.0.1:%SERVER_PORT%/ws/ +echo. + +"%BUN_EXE%" ./src/server/index.ts --port %SERVER_PORT% From 6ad42f7edf5020609e65f4203ba2ed82edc8ddec Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 7 May 2026 11:19:51 +0800 Subject: [PATCH 2/3] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E9=92=89?= =?UTF-8?q?=E9=92=89=E5=92=8C=E9=A3=9E=E4=B9=A6SDK=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加dingtalk-stream和@larksuiteoapi/node-sdk依赖 更新dev.bat和build.bat以包含适配器依赖安装 在.gitignore中添加/dist目录 --- .gitignore | 1 + bun.lock | 63 +++++++++++++++++++++++++++++++++++++++++++- desktop/build.bat | 12 +++++++++ desktop/bun.lock | 9 +++++++ desktop/dev.bat | 12 +++++++++ desktop/package.json | 1 + package.json | 3 +++ 7 files changed, 100 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7160cc0ff..af2198f47 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ runtime/__pycache__ # Codex logs .codex-logs/ +/dist diff --git a/bun.lock b/bun.lock index f321d88e8..a29f130bf 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.1020.0", "@commander-js/extra-typings": "^14.0.0", "@growthbook/growthbook": "^1.6.5", + "@larksuiteoapi/node-sdk": "^1.60.0", "@modelcontextprotocol/sdk": "^1.29.0", "@opentelemetry/api-logs": "^0.214.0", "@opentelemetry/core": "^2.6.1", @@ -28,6 +29,7 @@ "cli-boxes": "^4.0.1", "code-excerpt": "^4.0.0", "diff": "^8.0.4", + "dingtalk-stream": "2.1.4", "emoji-regex": "^10.6.0", "env-paths": "^4.0.0", "execa": "^9.6.1", @@ -35,6 +37,7 @@ "fuse.js": "^7.1.0", "get-east-asian-width": "^1.5.0", "google-auth-library": "^10.6.2", + "grammy": "^1.42.0", "highlight.js": "^11.11.1", "https-proxy-agent": "^8.0.0", "ignore": "^7.0.5", @@ -268,6 +271,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@grammyjs/types": ["@grammyjs/types@3.26.0", "", {}, "sha512-jlnyfxfev/2o68HlvAGRocAXgdPPX5QabG7jZlbqC2r9DZyWBfzTlg+nu3O3Fy4EhgLWu28hZ/8wr7DsNamP9A=="], + "@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="], "@hono/node-server": ["@hono/node-server@1.19.12", "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.12.tgz", { "peerDependencies": { "hono": "^4" } }, "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw=="], @@ -280,6 +285,8 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@larksuiteoapi/node-sdk": ["@larksuiteoapi/node-sdk@1.62.1", "", { "dependencies": { "axios": "~1.13.3", "lodash.identity": "^3.0.0", "lodash.merge": "^4.6.2", "lodash.pickby": "^4.6.0", "protobufjs": "^7.2.6", "qs": "^6.14.2", "ws": "^8.19.0" } }, "sha512-o9oAjv5Ffnp/6iXIJLHrO6N0US/r2ZZy3xmO6ylGegjuVSC05cx0fADA38Dc1h0FV8T9BDK+ariWk84TNMGbKg=="], + "@mermaid-js/mermaid-mindmap": ["@mermaid-js/mermaid-mindmap@9.3.0", "https://registry.npmmirror.com/@mermaid-js/mermaid-mindmap/-/mermaid-mindmap-9.3.0.tgz", { "dependencies": { "@braintree/sanitize-url": "^6.0.0", "cytoscape": "^3.23.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.1.0", "d3": "^7.0.0", "khroma": "^2.0.0", "non-layered-tidy-tree-layout": "^2.0.2" } }, "sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw=="], "@mermaid-js/parser": ["@mermaid-js/parser@1.1.0", "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-1.1.0.tgz", { "dependencies": { "langium": "^4.0.0" } }, "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw=="], @@ -306,6 +313,26 @@ "@pondwader/socks5-server": ["@pondwader/socks5-server@1.0.10", "https://registry.npmmirror.com/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", {}, "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg=="], + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.1", "", {}, "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA=="], @@ -544,6 +571,8 @@ "@types/mdurl": ["@types/mdurl@2.0.0", "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/unist": ["@types/unist@3.0.3", "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], @@ -588,6 +617,8 @@ "@vueuse/shared": ["@vueuse/shared@12.8.2", "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.8.2.tgz", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + "accepts": ["accepts@2.0.0", "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.16.0", "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], @@ -788,6 +819,8 @@ "dijkstrajs": ["dijkstrajs@1.0.3", "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="], + "dingtalk-stream": ["dingtalk-stream@2.1.4", "", { "dependencies": { "axios": "^1.4.0", "debug": "^4.3.4", "ws": "^8.13.0" } }, "sha512-rgQbXLGWfASuB9onFcqXTnRSj4ZotimhBOnzrB4kS19AaU9lshXiuofs1GAYcKh5uzPWCAuEs3tMtiadTQWP4A=="], + "dom-mutator": ["dom-mutator@0.6.0", "https://registry.npmmirror.com/dom-mutator/-/dom-mutator-0.6.0.tgz", {}, "sha512-iCt9o0aYfXMUkz/43ZOAUFQYotjGB+GNbYJiJdz4TgXkyToXbbRy5S6FbTp72lRBtfpUMwEc1KmpFEU4CZeoNg=="], "dompurify": ["dompurify@3.4.0", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.0.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg=="], @@ -830,6 +863,8 @@ "etag": ["etag@1.8.1", "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + "eventsource": ["eventsource@3.0.7", "https://registry.npmmirror.com/eventsource/-/eventsource-3.0.7.tgz", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], "eventsource-parser": ["eventsource-parser@3.0.6", "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], @@ -898,6 +933,8 @@ "graceful-fs": ["graceful-fs@4.2.11", "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "grammy": ["grammy@1.42.0", "", { "dependencies": { "@grammyjs/types": "3.26.0", "abort-controller": "^3.0.0", "debug": "^4.4.3", "node-fetch": "^2.7.0" } }, "sha512-1AdCge+AkjSdp2FwfICSFnVbl8Mq3KVHJDy+DgTI9+D6keJ0zWALPRKas5jv/8psiCzL4N2cEOcGW7O45Kn39g=="], + "hachure-fill": ["hachure-fill@0.5.2", "https://registry.npmmirror.com/hachure-fill/-/hachure-fill-0.5.2.tgz", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], "has-flag": ["has-flag@5.0.1", "https://registry.npmmirror.com/has-flag/-/has-flag-5.0.1.tgz", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="], @@ -990,6 +1027,14 @@ "lodash.debounce": ["lodash.debounce@4.0.8", "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], + "lodash.identity": ["lodash.identity@3.0.0", "", {}, "sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "lodash.pickby": ["lodash.pickby@4.6.0", "", {}, "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + "lru-cache": ["lru-cache@11.2.7", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.7.tgz", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], "magic-string": ["magic-string@0.30.21", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], @@ -1040,7 +1085,7 @@ "node-domexception": ["node-domexception@1.0.0", "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], - "node-fetch": ["node-fetch@3.3.2", "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "non-layered-tidy-tree-layout": ["non-layered-tidy-tree-layout@2.0.2", "https://registry.npmmirror.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", {}, "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw=="], @@ -1112,6 +1157,8 @@ "property-information": ["property-information@7.1.0", "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + "protobufjs": ["protobufjs@7.5.6", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.1", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg=="], + "proxy-addr": ["proxy-addr@2.0.7", "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], "proxy-from-env": ["proxy-from-env@2.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], @@ -1234,6 +1281,8 @@ "toidentifier": ["toidentifier@1.0.1", "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "tree-kill": ["tree-kill@1.2.2", "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", { "bin": "cli.js" }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], "trim-lines": ["trim-lines@3.0.1", "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], @@ -1254,6 +1303,8 @@ "undici": ["undici@7.24.6", "https://registry.npmmirror.com/undici/-/undici-7.24.6.tgz", {}, "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA=="], + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + "unicorn-magic": ["unicorn-magic@0.3.0", "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], "unist-util-is": ["unist-util-is@6.0.1", "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], @@ -1300,6 +1351,10 @@ "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + "which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "which-module": ["which-module@2.0.1", "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="], @@ -1340,6 +1395,8 @@ "@commander-js/extra-typings/commander": ["commander@14.0.3", "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + "@larksuiteoapi/node-sdk/axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], + "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="], "cliui/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1362,6 +1419,8 @@ "gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "gaxios/node-fetch": ["node-fetch@3.3.2", "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "ink/cli-boxes": ["cli-boxes@3.0.0", "https://registry.npmmirror.com/cli-boxes/-/cli-boxes-3.0.0.tgz", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], "ink/signal-exit": ["signal-exit@3.0.7", "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -1388,6 +1447,8 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "https://registry.npmmirror.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@larksuiteoapi/node-sdk/axios/proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], diff --git a/desktop/build.bat b/desktop/build.bat index 0298e6b4b..202943a0d 100644 --- a/desktop/build.bat +++ b/desktop/build.bat @@ -103,6 +103,18 @@ if not exist "node_modules" ( pause exit /b 1 ) + echo [INFO] Installing adapter dependencies... + pushd ..\adapters + if not exist "node_modules" ( + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Adapter install failed + popd + pause + exit /b 1 + ) + ) + popd echo. ) diff --git a/desktop/bun.lock b/desktop/bun.lock index fc1b3058d..6b01369fd 100644 --- a/desktop/bun.lock +++ b/desktop/bun.lock @@ -9,6 +9,7 @@ "@types/dompurify": "^3.2.0", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", + "dingtalk-stream": "^2.1.6-beta.1", "dompurify": "^3.3.3", "lucide-react": "^0.469.0", "marked": "^15.0.7", @@ -512,6 +513,8 @@ "asynckit": ["asynckit@0.4.0", "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + "axios": ["axios@1.16.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w=="], + "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "https://registry.npmmirror.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], "cac": ["cac@6.7.14", "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], @@ -672,6 +675,8 @@ "dijkstrajs": ["dijkstrajs@1.0.3", "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="], + "dingtalk-stream": ["dingtalk-stream@2.1.6-beta.1", "", { "dependencies": { "axios": "^1.4.0", "debug": "^4.3.4", "ws": "^8.13.0" } }, "sha512-uYcBnf0Z4rfHHyN1ae4YnAFA6hUW2DmGVb0OZ53r/A272kuHnZynylE5pEJIJHkNIer6R9PCqpnsfsk9IuvglQ=="], + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], "dompurify": ["dompurify@3.3.3", "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.3.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="], @@ -712,6 +717,8 @@ "find-up": ["find-up@4.1.0", "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], + "form-data": ["form-data@4.0.5", "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -976,6 +983,8 @@ "property-information": ["property-information@7.1.0", "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], + "punycode": ["punycode@2.3.1", "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "qrcode": ["qrcode@1.5.4", "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="], diff --git a/desktop/dev.bat b/desktop/dev.bat index 1abf16017..a11e7aebc 100644 --- a/desktop/dev.bat +++ b/desktop/dev.bat @@ -45,6 +45,18 @@ if not exist "node_modules" ( pause exit /b 1 ) + echo [INFO] Installing adapter dependencies... + pushd ..\adapters + if not exist "node_modules" ( + "%BUN_EXE%" install + if %ERRORLEVEL% neq 0 ( + echo [ERROR] Adapter install failed + popd + pause + exit /b 1 + ) + ) + popd echo. ) diff --git a/desktop/package.json b/desktop/package.json index e73135d3b..a36f5e257 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -20,6 +20,7 @@ "@types/dompurify": "^3.2.0", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", + "dingtalk-stream": "^2.1.6-beta.1", "dompurify": "^3.3.3", "lucide-react": "^0.469.0", "marked": "^15.0.7", diff --git a/package.json b/package.json index f9f8c724e..795f2a40f 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.1020.0", "@commander-js/extra-typings": "^14.0.0", "@growthbook/growthbook": "^1.6.5", + "@larksuiteoapi/node-sdk": "^1.60.0", "@modelcontextprotocol/sdk": "^1.29.0", "@opentelemetry/api-logs": "^0.214.0", "@opentelemetry/core": "^2.6.1", @@ -50,6 +51,7 @@ "cli-boxes": "^4.0.1", "code-excerpt": "^4.0.0", "diff": "^8.0.4", + "dingtalk-stream": "2.1.4", "emoji-regex": "^10.6.0", "env-paths": "^4.0.0", "execa": "^9.6.1", @@ -57,6 +59,7 @@ "fuse.js": "^7.1.0", "get-east-asian-width": "^1.5.0", "google-auth-library": "^10.6.2", + "grammy": "^1.42.0", "highlight.js": "^11.11.1", "https-proxy-agent": "^8.0.0", "ignore": "^7.0.5", From 07ff47329b7a2a58302e48bc7201c2a71e896fd4 Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 7 May 2026 17:17:24 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(chat):=20=E6=B7=BB=E5=8A=A0/add-dir?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E6=94=AF=E6=8C=81=E8=B7=A8=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增/add-dir命令,允许用户添加工作目录使Agent能够跨目录读取和编辑文件。包含以下功能: 1. 支持通过输入路径或浏览文件夹选择目录 2. 可选择是否记住目录供后续会话使用 3. 添加中英文国际化支持 4. 实现目录验证和权限管理逻辑 --- .../chat/LocalSlashCommandPanel.tsx | 247 +++++++++++++++++- desktop/src/components/chat/composerUtils.ts | 1 + desktop/src/i18n/locales/en.ts | 16 ++ desktop/src/i18n/locales/zh.ts | 16 ++ src/commands/add-dir/add-dir.tsx | 240 +++++++++-------- 5 files changed, 414 insertions(+), 106 deletions(-) diff --git a/desktop/src/components/chat/LocalSlashCommandPanel.tsx b/desktop/src/components/chat/LocalSlashCommandPanel.tsx index 65612bb8f..2f1489a65 100644 --- a/desktop/src/components/chat/LocalSlashCommandPanel.tsx +++ b/desktop/src/components/chat/LocalSlashCommandPanel.tsx @@ -1,6 +1,7 @@ -import { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useMemo, useRef, useState, useCallback } from 'react' import { skillsApi } from '../../api/skills' import { mcpApi } from '../../api/mcp' +import { filesystemApi } from '../../api/filesystem' import { sessionsApi, type SessionContextSnapshot, @@ -9,6 +10,7 @@ import { } from '../../api/sessions' import { useTranslation, type TranslationKey } from '../../i18n' import { useUIStore } from '../../stores/uiStore' +import { useChatStore } from '../../stores/chatStore' import { SETTINGS_TAB_ID, useTabStore } from '../../stores/tabStore' import { useMcpStore } from '../../stores/mcpStore' import { useSkillStore } from '../../stores/skillStore' @@ -16,7 +18,7 @@ import type { McpServerRecord } from '../../types/mcp' import type { SkillMeta } from '../../types/skill' import type { SlashCommandOption } from './composerUtils' -export type LocalSlashCommandName = 'mcp' | 'skills' | 'help' | 'status' | 'cost' | 'context' +export type LocalSlashCommandName = 'mcp' | 'skills' | 'add-dir' | 'help' | 'status' | 'cost' | 'context' type Props = { command: LocalSlashCommandName @@ -947,6 +949,244 @@ function SkillsPanel({ cwd, onClose }: { cwd?: string; onClose: () => void }) { ) } +type BrowseEntry = { name: string; path: string; isDirectory: boolean } + +function AddDirPanel({ + sessionId, + cwd, + onClose, +}: { + sessionId?: string + cwd?: string + onClose: () => void +}) { + const t = useTranslation() + const sendMessage = useChatStore((s) => s.sendMessage) + const [selectedPath, setSelectedPath] = useState('') + const [remember, setRemember] = useState(false) + const [browseEntries, setBrowseEntries] = useState([]) + const [browsePath, setBrowsePath] = useState('') + const [browseParent, setBrowseParent] = useState('') + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + const [mode, setMode] = useState<'input' | 'browse'>('input') + + const loadBrowseDir = useCallback(async (path?: string) => { + setLoading(true) + setError(null) + try { + const result = await filesystemApi.browse(path) + setBrowsePath(result.currentPath) + setBrowseParent(result.parentPath) + setBrowseEntries(result.entries.filter((e) => e.isDirectory)) + } catch { + setError(t('slash.addDir.browseError')) + } + setLoading(false) + }, [t]) + + useEffect(() => { + if (mode === 'browse' && browseEntries.length === 0 && !browsePath) { + loadBrowseDir(cwd || undefined) + } + }, [mode, browseEntries.length, browsePath, cwd, loadBrowseDir]) + + const handleSubmit = () => { + if (!selectedPath.trim() || !sessionId) return + const command = remember + ? `/add-dir ${selectedPath} --remember` + : `/add-dir ${selectedPath}` + sendMessage(sessionId, command) + onClose() + } + + const handleChooseFolder = async () => { + try { + const { open } = await import('@tauri-apps/plugin-dialog') + const selected = await open({ + directory: true, + multiple: false, + title: t('slash.addDir.chooseFolder'), + }) + if (selected) { + setSelectedPath(selected) + } + } catch (err) { + console.error('[AddDirPanel] Failed to open folder dialog:', err) + } + } + + const handleBrowseSelect = (path: string) => { + setSelectedPath(path) + setMode('input') + } + + return ( + +
+ {mode === 'input' ? ( + <> +
+ +
+ setSelectedPath(e.target.value)} + placeholder={t('slash.addDir.pathPlaceholder')} + className="flex-1 rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-2 text-sm text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:border-[var(--color-brand)] focus:outline-none" + onKeyDown={(e) => { + if (e.key === 'Enter') handleSubmit() + }} + /> + + +
+
+ +
+ +
+ + {remember && ( +
+ {t('slash.addDir.rememberHint')} +
+ )} + +
+ + +
+ + ) : ( + <> +
+ + + {browsePath} + +
+ + {error ? ( + + ) : loading ? ( + + ) : ( +
+ {browseParent && browseParent !== browsePath && ( + + )} + {browseEntries.length === 0 ? ( + + ) : ( + browseEntries.map((entry) => ( +
+ + +
+ )) + )} +
+ )} + + {browsePath && ( +
+ + {browsePath} + + +
+ )} + + )} +
+
+ ) +} + const COMMAND_GROUPS = [ { titleKey: 'slash.help.group.context', @@ -954,7 +1194,7 @@ const COMMAND_GROUPS = [ }, { titleKey: 'slash.help.group.project', - names: ['init', 'review', 'commit', 'pr'], + names: ['init', 'review', 'commit', 'pr', 'add-dir'], }, { titleKey: 'slash.help.group.desktop', @@ -1037,6 +1277,7 @@ function HelpPanel({ export function LocalSlashCommandPanel({ command, sessionId, cwd, commands, onClose }: Props) { if (command === 'mcp') return if (command === 'skills') return + if (command === 'add-dir') return if (command === 'status' || command === 'cost' || command === 'context') { return } diff --git a/desktop/src/components/chat/composerUtils.ts b/desktop/src/components/chat/composerUtils.ts index 502923921..eaa5a683e 100644 --- a/desktop/src/components/chat/composerUtils.ts +++ b/desktop/src/components/chat/composerUtils.ts @@ -3,6 +3,7 @@ import type { SettingsTab } from '../../stores/uiStore' export const PANEL_SLASH_COMMANDS = [ { name: 'mcp', description: 'Open available MCP tools for the current chat context' }, { name: 'skills', description: 'Browse user-invocable skills for the current chat context' }, + { name: 'add-dir', description: 'Add a new working directory to the current session' }, { name: 'help', description: 'Show available desktop and agent commands' }, { name: 'status', description: 'Show session status, usage, and context' }, { name: 'cost', description: 'Show session usage and costs' }, diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts index 64fbe7cbd..7e7c9e829 100644 --- a/desktop/src/i18n/locales/en.ts +++ b/desktop/src/i18n/locales/en.ts @@ -764,6 +764,22 @@ export const en = { 'slash.inspector.context.categoryTitle': 'Estimated usage by category', 'slash.inspector.context.noCategoriesTitle': 'No context categories', 'slash.inspector.context.noCategoriesBody': 'Context categories will appear after the CLI reports context analysis.', + + 'slash.addDir.title': 'Add working directory', + 'slash.addDir.subtitle': 'Add a new working directory so the agent can read and edit files across directories.', + 'slash.addDir.pathLabel': 'Directory path', + 'slash.addDir.pathPlaceholder': '/path/to/directory', + 'slash.addDir.browseFolders': 'Browse folders', + 'slash.addDir.browseTree': 'Browse directory tree', + 'slash.addDir.rememberLabel': 'Remember for future sessions', + 'slash.addDir.rememberHint': 'The directory will be saved to local settings and automatically available in future sessions.', + 'slash.addDir.addButton': 'Add directory', + 'slash.addDir.chooseFolder': 'Choose folder', + 'slash.addDir.backToInput': 'Back', + 'slash.addDir.browseError': 'Failed to browse directory.', + 'slash.addDir.noSubdirs': 'No subdirectories', + 'slash.addDir.noSubdirsBody': 'This directory has no subdirectories to browse.', + 'contextIndicator.ariaLabel': 'Context usage {percent}', 'contextIndicator.pendingAria': 'Context usage not calculated', 'contextIndicator.loadingAria': 'Context usage loading', diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts index 54121d8c1..25e3162ea 100644 --- a/desktop/src/i18n/locales/zh.ts +++ b/desktop/src/i18n/locales/zh.ts @@ -766,6 +766,22 @@ export const zh: Record = { 'slash.inspector.context.categoryTitle': '按类别估算', 'slash.inspector.context.noCategoriesTitle': '暂无上下文分类', 'slash.inspector.context.noCategoriesBody': 'CLI 返回上下文分析后,这里会显示分类数据。', + + 'slash.addDir.title': '添加工作目录', + 'slash.addDir.subtitle': '添加新的工作目录,让 Agent 可以跨目录读取和编辑文件。', + 'slash.addDir.pathLabel': '目录路径', + 'slash.addDir.pathPlaceholder': '/路径/到/目录', + 'slash.addDir.browseFolders': '浏览文件夹', + 'slash.addDir.browseTree': '浏览目录树', + 'slash.addDir.rememberLabel': '记住此目录(后续会话自动可用)', + 'slash.addDir.rememberHint': '该目录将保存到本地设置,在后续会话中自动可用。', + 'slash.addDir.addButton': '添加目录', + 'slash.addDir.chooseFolder': '选择文件夹', + 'slash.addDir.backToInput': '返回', + 'slash.addDir.browseError': '浏览目录失败。', + 'slash.addDir.noSubdirs': '无子目录', + 'slash.addDir.noSubdirsBody': '该目录下没有子目录可供浏览。', + 'contextIndicator.ariaLabel': '上下文用量 {percent}', 'contextIndicator.pendingAria': '上下文用量待计算', 'contextIndicator.loadingAria': '上下文用量加载中', diff --git a/src/commands/add-dir/add-dir.tsx b/src/commands/add-dir/add-dir.tsx index b1a2b4de1..39bf39382 100644 --- a/src/commands/add-dir/add-dir.tsx +++ b/src/commands/add-dir/add-dir.tsx @@ -1,126 +1,160 @@ -import { c as _c } from "react/compiler-runtime"; -import chalk from 'chalk'; -import figures from 'figures'; -import React, { useEffect } from 'react'; -import { getAdditionalDirectoriesForClaudeMd, setAdditionalDirectoriesForClaudeMd } from '../../bootstrap/state.js'; -import type { LocalJSXCommandContext } from '../../commands.js'; -import { MessageResponse } from '../../components/MessageResponse.js'; -import { AddWorkspaceDirectory } from '../../components/permissions/rules/AddWorkspaceDirectory.js'; -import { Box, Text } from '../../ink.js'; -import type { LocalJSXCommandOnDone } from '../../types/command.js'; -import { applyPermissionUpdate, persistPermissionUpdate } from '../../utils/permissions/PermissionUpdate.js'; -import type { PermissionUpdateDestination } from '../../utils/permissions/PermissionUpdateSchema.js'; -import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'; -import { addDirHelpMessage, validateDirectoryForWorkspace } from './validation.js'; -function AddDirError(t0) { - const $ = _c(10); - const { - message, - args, - onDone - } = t0; - let t1; - let t2; - if ($[0] !== onDone) { - t1 = () => { - const timer = setTimeout(onDone, 0); - return () => clearTimeout(timer); - }; - t2 = [onDone]; - $[0] = onDone; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== args) { - t3 = {figures.pointer} /add-dir {args}; - $[3] = args; - $[4] = t3; - } else { - t3 = $[4]; - } - let t4; - if ($[5] !== message) { - t4 = {message}; - $[5] = message; - $[6] = t4; - } else { - t4 = $[6]; - } - let t5; - if ($[7] !== t3 || $[8] !== t4) { - t5 = {t3}{t4}; - $[7] = t3; - $[8] = t4; - $[9] = t5; - } else { - t5 = $[9]; - } - return t5; +import chalk from 'chalk' +import figures from 'figures' +import React, { useEffect } from 'react' +import { + getAdditionalDirectoriesForClaudeMd, + setAdditionalDirectoriesForClaudeMd, +} from '../../bootstrap/state.js' +import type { LocalJSXCommandContext } from '../../commands.js' +import { MessageResponse } from '../../components/MessageResponse.js' +import { AddWorkspaceDirectory } from '../../components/permissions/rules/AddWorkspaceDirectory.js' +import { Box, Text } from '../../ink.js' +import type { LocalJSXCommandOnDone } from '../../types/command.js' +import { + applyPermissionUpdate, + persistPermissionUpdate, +} from '../../utils/permissions/PermissionUpdate.js' +import type { PermissionUpdateDestination } from '../../utils/permissions/PermissionUpdateSchema.js' +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' +import { + addDirHelpMessage, + validateDirectoryForWorkspace, +} from './validation.js' + +function AddDirError({ + message, + args, + onDone, +}: { + message: string + args: string + onDone: () => void +}): React.ReactNode { + useEffect(() => { + const timer = setTimeout(onDone, 0) + return () => clearTimeout(timer) + }, [onDone]) + + return ( + + + {figures.pointer} /add-dir {args} + + + {message} + + + ) +} + +function parseArgs(args: string | undefined): { path: string; remember: boolean } { + const raw = (args ?? '').trim() + const remember = /\s--remember\b/.test(raw) + const path = raw.replace(/\s--remember\b/, '').trim() + return { path, remember } } -export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext, args?: string): Promise { - const directoryPath = (args ?? '').trim(); - const appState = context.getAppState(); - // Helper to handle adding a directory (shared by both with-path and no-path cases) +export async function call( + onDone: LocalJSXCommandOnDone, + context: LocalJSXCommandContext, + args?: string, +): Promise { + const { path: directoryPath, remember: rememberFlag } = parseArgs(args) + const appState = context.getAppState() + const isNonInteractive = context.options.isNonInteractiveSession + const handleAddDirectory = async (path: string, remember = false) => { - const destination: PermissionUpdateDestination = remember ? 'localSettings' : 'session'; + const destination: PermissionUpdateDestination = remember + ? 'localSettings' + : 'session' const permissionUpdate = { type: 'addDirectories' as const, directories: [path], - destination - }; + destination, + } - // Apply to session context - const latestAppState = context.getAppState(); - const updatedContext = applyPermissionUpdate(latestAppState.toolPermissionContext, permissionUpdate); - context.setAppState(prev => ({ + const latestAppState = context.getAppState() + const updatedContext = applyPermissionUpdate( + latestAppState.toolPermissionContext, + permissionUpdate, + ) + context.setAppState((prev) => ({ ...prev, - toolPermissionContext: updatedContext - })); + toolPermissionContext: updatedContext, + })) - // Update sandbox config so Bash commands can access the new directory. - // Bootstrap state is the source of truth for session-only dirs; persisted - // dirs are picked up via the settings subscription, but we refresh - // eagerly here to avoid a race when the user acts immediately. - const currentDirs = getAdditionalDirectoriesForClaudeMd(); + const currentDirs = getAdditionalDirectoriesForClaudeMd() if (!currentDirs.includes(path)) { - setAdditionalDirectoriesForClaudeMd([...currentDirs, path]); + setAdditionalDirectoriesForClaudeMd([...currentDirs, path]) } - SandboxManager.refreshConfig(); - let message: string; + SandboxManager.refreshConfig() + + let message: string if (remember) { try { - persistPermissionUpdate(permissionUpdate); - message = `Added ${chalk.bold(path)} as a working directory and saved to local settings`; + persistPermissionUpdate(permissionUpdate) + message = `Added ${chalk.bold(path)} as a working directory and saved to local settings` } catch (error) { - message = `Added ${chalk.bold(path)} as a working directory. Failed to save to local settings: ${error instanceof Error ? error.message : 'Unknown error'}`; + message = `Added ${chalk.bold(path)} as a working directory. Failed to save to local settings: ${error instanceof Error ? error.message : 'Unknown error'}` } } else { - message = `Added ${chalk.bold(path)} as a working directory for this session`; + message = `Added ${chalk.bold(path)} as a working directory for this session` + } + const messageWithHint = `${message} ${chalk.dim('· /permissions to manage')}` + onDone(messageWithHint) + } + + if (isNonInteractive && directoryPath) { + const result = await validateDirectoryForWorkspace( + directoryPath, + appState.toolPermissionContext, + ) + if (result.resultType !== 'success') { + const message = addDirHelpMessage(result) + onDone(message) + return null } - const messageWithHint = `${message} ${chalk.dim('· /permissions to manage')}`; - onDone(messageWithHint); - }; + await handleAddDirectory(result.absolutePath, rememberFlag) + return null + } - // When no path is provided, show AddWorkspaceDirectory input form directly - // and return to REPL after confirmation if (!directoryPath) { - return { - onDone('Did not add a working directory.'); - }} />; + return ( + { + onDone('Did not add a working directory.') + }} + /> + ) } - const result = await validateDirectoryForWorkspace(directoryPath, appState.toolPermissionContext); + + const result = await validateDirectoryForWorkspace( + directoryPath, + appState.toolPermissionContext, + ) if (result.resultType !== 'success') { - const message = addDirHelpMessage(result); - return onDone(message)} />; + const message = addDirHelpMessage(result) + return ( + onDone(message)} + /> + ) } - return { - onDone(`Did not add ${chalk.bold(result.absolutePath)} as a working directory.`); - }} />; + + return ( + { + onDone( + `Did not add ${chalk.bold(result.absolutePath)} as a working directory.`, + ) + }} + /> + ) } -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsImZpZ3VyZXMiLCJSZWFjdCIsInVzZUVmZmVjdCIsImdldEFkZGl0aW9uYWxEaXJlY3Rvcmllc0ZvckNsYXVkZU1kIiwic2V0QWRkaXRpb25hbERpcmVjdG9yaWVzRm9yQ2xhdWRlTWQiLCJMb2NhbEpTWENvbW1hbmRDb250ZXh0IiwiTWVzc2FnZVJlc3BvbnNlIiwiQWRkV29ya3NwYWNlRGlyZWN0b3J5IiwiQm94IiwiVGV4dCIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImFwcGx5UGVybWlzc2lvblVwZGF0ZSIsInBlcnNpc3RQZXJtaXNzaW9uVXBkYXRlIiwiUGVybWlzc2lvblVwZGF0ZURlc3RpbmF0aW9uIiwiU2FuZGJveE1hbmFnZXIiLCJhZGREaXJIZWxwTWVzc2FnZSIsInZhbGlkYXRlRGlyZWN0b3J5Rm9yV29ya3NwYWNlIiwiQWRkRGlyRXJyb3IiLCJ0MCIsIiQiLCJfYyIsIm1lc3NhZ2UiLCJhcmdzIiwib25Eb25lIiwidDEiLCJ0MiIsInRpbWVyIiwic2V0VGltZW91dCIsImNsZWFyVGltZW91dCIsInQzIiwicG9pbnRlciIsInQ0IiwidDUiLCJjYWxsIiwiY29udGV4dCIsIlByb21pc2UiLCJSZWFjdE5vZGUiLCJkaXJlY3RvcnlQYXRoIiwidHJpbSIsImFwcFN0YXRlIiwiZ2V0QXBwU3RhdGUiLCJoYW5kbGVBZGREaXJlY3RvcnkiLCJwYXRoIiwicmVtZW1iZXIiLCJkZXN0aW5hdGlvbiIsInBlcm1pc3Npb25VcGRhdGUiLCJ0eXBlIiwiY29uc3QiLCJkaXJlY3RvcmllcyIsImxhdGVzdEFwcFN0YXRlIiwidXBkYXRlZENvbnRleHQiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJzZXRBcHBTdGF0ZSIsInByZXYiLCJjdXJyZW50RGlycyIsImluY2x1ZGVzIiwicmVmcmVzaENvbmZpZyIsImJvbGQiLCJlcnJvciIsIkVycm9yIiwibWVzc2FnZVdpdGhIaW50IiwiZGltIiwicmVzdWx0IiwicmVzdWx0VHlwZSIsImFic29sdXRlUGF0aCJdLCJzb3VyY2VzIjpbImFkZC1kaXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICBnZXRBZGRpdGlvbmFsRGlyZWN0b3JpZXNGb3JDbGF1ZGVNZCxcbiAgc2V0QWRkaXRpb25hbERpcmVjdG9yaWVzRm9yQ2xhdWRlTWQsXG59IGZyb20gJy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ29udGV4dCB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBBZGRXb3Jrc3BhY2VEaXJlY3RvcnkgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL3Blcm1pc3Npb25zL3J1bGVzL0FkZFdvcmtzcGFjZURpcmVjdG9yeS5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7XG4gIGFwcGx5UGVybWlzc2lvblVwZGF0ZSxcbiAgcGVyc2lzdFBlcm1pc3Npb25VcGRhdGUsXG59IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGUuanMnXG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb25VcGRhdGVEZXN0aW5hdGlvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGVTY2hlbWEuanMnXG5pbXBvcnQgeyBTYW5kYm94TWFuYWdlciB9IGZyb20gJy4uLy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuaW1wb3J0IHtcbiAgYWRkRGlySGVscE1lc3NhZ2UsXG4gIHZhbGlkYXRlRGlyZWN0b3J5Rm9yV29ya3NwYWNlLFxufSBmcm9tICcuL3ZhbGlkYXRpb24uanMnXG5cbmZ1bmN0aW9uIEFkZERpckVycm9yKHtcbiAgbWVzc2FnZSxcbiAgYXJncyxcbiAgb25Eb25lLFxufToge1xuICBtZXNzYWdlOiBzdHJpbmdcbiAgYXJnczogc3RyaW5nXG4gIG9uRG9uZTogKCkgPT4gdm9pZFxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgLy8gV2UgbmVlZCB0byBkZWZlciBjYWxsaW5nIG9uRG9uZSB0byBhdm9pZCB0aGUgXCJyZXR1cm4gbnVsbFwiIGJ1ZyB3aGVyZVxuICAgIC8vIHRoZSBjb21wb25lbnQgdW5tb3VudHMgYmVmb3JlIFJlYWN0IGNhbiByZW5kZXIgdGhlIGVycm9yIG1lc3NhZ2UuXG4gICAgLy8gVXNpbmcgc2V0VGltZW91dCBlbnN1cmVzIHRoZSBlcnJvciBkaXNwbGF5cyBiZWZvcmUgdGhlIGNvbW1hbmQgZXhpdHMuXG4gICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KG9uRG9uZSwgMClcbiAgICByZXR1cm4gKCkgPT4gY2xlYXJUaW1lb3V0KHRpbWVyKVxuICB9LCBbb25Eb25lXSlcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIHtmaWd1cmVzLnBvaW50ZXJ9IC9hZGQtZGlyIHthcmdzfVxuICAgICAgPC9UZXh0PlxuICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgPFRleHQ+e21lc3NhZ2V9PC9UZXh0PlxuICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuICBhcmdzPzogc3RyaW5nLFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGU+IHtcbiAgY29uc3QgZGlyZWN0b3J5UGF0aCA9IChhcmdzID8/ICcnKS50cmltKClcbiAgY29uc3QgYXBwU3RhdGUgPSBjb250ZXh0LmdldEFwcFN0YXRlKClcblxuICAvLyBIZWxwZXIgdG8gaGFuZGxlIGFkZGluZyBhIGRpcmVjdG9yeSAoc2hhcmVkIGJ5IGJvdGggd2l0aC1wYXRoIGFuZCBuby1wYXRoIGNhc2VzKVxuICBjb25zdCBoYW5kbGVBZGREaXJlY3RvcnkgPSBhc3luYyAocGF0aDogc3RyaW5nLCByZW1lbWJlciA9IGZhbHNlKSA9PiB7XG4gICAgY29uc3QgZGVzdGluYXRpb246IFBlcm1pc3Npb25VcGRhdGVEZXN0aW5hdGlvbiA9IHJlbWVtYmVyXG4gICAgICA/ICdsb2NhbFNldHRpbmdzJ1xuICAgICAgOiAnc2Vzc2lvbidcblxuICAgIGNvbnN0IHBlcm1pc3Npb25VcGRhdGUgPSB7XG4gICAgICB0eXBlOiAnYWRkRGlyZWN0b3JpZXMnIGFzIGNvbnN0LFxuICAgICAgZGlyZWN0b3JpZXM6IFtwYXRoXSxcbiAgICAgIGRlc3RpbmF0aW9uLFxuICAgIH1cblxuICAgIC8vIEFwcGx5IHRvIHNlc3Npb24gY29udGV4dFxuICAgIGNvbnN0IGxhdGVzdEFwcFN0YXRlID0gY29udGV4dC5nZXRBcHBTdGF0ZSgpXG4gICAgY29uc3QgdXBkYXRlZENvbnRleHQgPSBhcHBseVBlcm1pc3Npb25VcGRhdGUoXG4gICAgICBsYXRlc3RBcHBTdGF0ZS50b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICBwZXJtaXNzaW9uVXBkYXRlLFxuICAgIClcbiAgICBjb250ZXh0LnNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHVwZGF0ZWRDb250ZXh0LFxuICAgIH0pKVxuXG4gICAgLy8gVXBkYXRlIHNhbmRib3ggY29uZmlnIHNvIEJhc2ggY29tbWFuZHMgY2FuIGFjY2VzcyB0aGUgbmV3IGRpcmVjdG9yeS5cbiAgICAvLyBCb290c3RyYXAgc3RhdGUgaXMgdGhlIHNvdXJjZSBvZiB0cnV0aCBmb3Igc2Vzc2lvbi1vbmx5IGRpcnM7IHBlcnNpc3RlZFxuICAgIC8vIGRpcnMgYXJlIHBpY2tlZCB1cCB2aWEgdGhlIHNldHRpbmdzIHN1YnNjcmlwdGlvbiwgYnV0IHdlIHJlZnJlc2hcbiAgICAvLyBlYWdlcmx5IGhlcmUgdG8gYXZvaWQgYSByYWNlIHdoZW4gdGhlIHVzZXIgYWN0cyBpbW1lZGlhdGVseS5cbiAgICBjb25zdCBjdXJyZW50RGlycyA9IGdldEFkZGl0aW9uYWxEaXJlY3Rvcmllc0ZvckNsYXVkZU1kKClcbiAgICBpZiAoIWN1cnJlbnREaXJzLmluY2x1ZGVzKHBhdGgpKSB7XG4gICAgICBzZXRBZGRpdGlvbmFsRGlyZWN0b3JpZXNGb3JDbGF1ZGVNZChbLi4uY3VycmVudERpcnMsIHBhdGhdKVxuICAgIH1cbiAgICBTYW5kYm94TWFuYWdlci5yZWZyZXNoQ29uZmlnKClcblxuICAgIGxldCBtZXNzYWdlOiBzdHJpbmdcblxuICAgIGlmIChyZW1lbWJlcikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcGVyc2lzdFBlcm1pc3Npb25VcGRhdGUocGVybWlzc2lvblVwZGF0ZSlcbiAgICAgICAgbWVzc2FnZSA9IGBBZGRlZCAke2NoYWxrLmJvbGQocGF0aCl9IGFzIGEgd29ya2luZyBkaXJlY3RvcnkgYW5kIHNhdmVkIHRvIGxvY2FsIHNldHRpbmdzYFxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbWVzc2FnZSA9IGBBZGRlZCAke2NoYWxrLmJvbGQocGF0aCl9IGFzIGEgd29ya2luZyBkaXJlY3RvcnkuIEZhaWxlZCB0byBzYXZlIHRvIGxvY2FsIHNldHRpbmdzOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ1Vua25vd24gZXJyb3InfWBcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgbWVzc2FnZSA9IGBBZGRlZCAke2NoYWxrLmJvbGQocGF0aCl9IGFzIGEgd29ya2luZyBkaXJlY3RvcnkgZm9yIHRoaXMgc2Vzc2lvbmBcbiAgICB9XG5cbiAgICBjb25zdCBtZXNzYWdlV2l0aEhpbnQgPSBgJHttZXNzYWdlfSAke2NoYWxrLmRpbSgnwrcgL3Blcm1pc3Npb25zIHRvIG1hbmFnZScpfWBcbiAgICBvbkRvbmUobWVzc2FnZVdpdGhIaW50KVxuICB9XG5cbiAgLy8gV2hlbiBubyBwYXRoIGlzIHByb3ZpZGVkLCBzaG93IEFkZFdvcmtzcGFjZURpcmVjdG9yeSBpbnB1dCBmb3JtIGRpcmVjdGx5XG4gIC8vIGFuZCByZXR1cm4gdG8gUkVQTCBhZnRlciBjb25maXJtYXRpb25cbiAgaWYgKCFkaXJlY3RvcnlQYXRoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxBZGRXb3Jrc3BhY2VEaXJlY3RvcnlcbiAgICAgICAgcGVybWlzc2lvbkNvbnRleHQ9e2FwcFN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dH1cbiAgICAgICAgb25BZGREaXJlY3Rvcnk9e2hhbmRsZUFkZERpcmVjdG9yeX1cbiAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICBvbkRvbmUoJ0RpZCBub3QgYWRkIGEgd29ya2luZyBkaXJlY3RvcnkuJylcbiAgICAgICAgfX1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdmFsaWRhdGVEaXJlY3RvcnlGb3JXb3Jrc3BhY2UoXG4gICAgZGlyZWN0b3J5UGF0aCxcbiAgICBhcHBTdGF0ZS50b29sUGVybWlzc2lvbkNvbnRleHQsXG4gIClcblxuICBpZiAocmVzdWx0LnJlc3VsdFR5cGUgIT09ICdzdWNjZXNzJykge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBhZGREaXJIZWxwTWVzc2FnZShyZXN1bHQpXG5cbiAgICByZXR1cm4gKFxuICAgICAgPEFkZERpckVycm9yXG4gICAgICAgIG1lc3NhZ2U9e21lc3NhZ2V9XG4gICAgICAgIGFyZ3M9e2FyZ3MgPz8gJyd9XG4gICAgICAgIG9uRG9uZT17KCkgPT4gb25Eb25lKG1lc3NhZ2UpfVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxBZGRXb3Jrc3BhY2VEaXJlY3RvcnlcbiAgICAgIGRpcmVjdG9yeVBhdGg9e3Jlc3VsdC5hYnNvbHV0ZVBhdGh9XG4gICAgICBwZXJtaXNzaW9uQ29udGV4dD17YXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0fVxuICAgICAgb25BZGREaXJlY3Rvcnk9e2hhbmRsZUFkZERpcmVjdG9yeX1cbiAgICAgIG9uQ2FuY2VsPXsoKSA9PiB7XG4gICAgICAgIG9uRG9uZShcbiAgICAgICAgICBgRGlkIG5vdCBhZGQgJHtjaGFsay5ib2xkKHJlc3VsdC5hYnNvbHV0ZVBhdGgpfSBhcyBhIHdvcmtpbmcgZGlyZWN0b3J5LmAsXG4gICAgICAgIClcbiAgICAgIH19XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsT0FBT0MsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUFJQyxTQUFTLFFBQVEsT0FBTztBQUN4QyxTQUNFQyxtQ0FBbUMsRUFDbkNDLG1DQUFtQyxRQUM5QiwwQkFBMEI7QUFDakMsY0FBY0Msc0JBQXNCLFFBQVEsbUJBQW1CO0FBQy9ELFNBQVNDLGVBQWUsUUFBUSxxQ0FBcUM7QUFDckUsU0FBU0MscUJBQXFCLFFBQVEsNkRBQTZEO0FBQ25HLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLFNBQ0VDLHFCQUFxQixFQUNyQkMsdUJBQXVCLFFBQ2xCLDZDQUE2QztBQUNwRCxjQUFjQywyQkFBMkIsUUFBUSxtREFBbUQ7QUFDcEcsU0FBU0MsY0FBYyxRQUFRLHdDQUF3QztBQUN2RSxTQUNFQyxp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 \ No newline at end of file