From 7b2f6b6425b21fe911e683b8b577ac9c366d4cc1 Mon Sep 17 00:00:00 2001 From: laiyanbiao66-debug Date: Sat, 30 May 2026 10:33:04 +0800 Subject: [PATCH 1/2] feat: multi-provider API support with per-provider API keys and model name mapping Backend: - Add 10 built-in API providers (DeepSeek official, SiliconFlow, Aliyun, Volcengine, Tencent, Baidu, OpenRouter, Fireworks, Together, Replicate) - Add resolveModelForProvider() to map canonical model names to provider-specific names at API call time - Add loadResolvedModel() convenience function - Wire providerId through CacheFirstLoop, ContextManager, subagents, and all CLI entry points - Auto-escalation and fold-summary calls now use provider-resolved model names - emitSettings sends masked key prefixes for ALL providers, not just the current one CLI: - Add reasonix provider command (list, set, show, key, add, remove) Desktop UI: - Move provider/key section from General to Models settings page - Provider dropdown, API key input per provider, custom provider management - Key input resets when switching providers, no fallback leak between providers - UI always shows canonical model names (deepseek-v4-flash / deepseek-v4-pro) --- .env.example | 7 + desktop/package-lock.json | 596 +++++++++++++++++++--------------- desktop/src/App.tsx | 19 +- desktop/src/i18n/de.ts | 9 + desktop/src/i18n/en.ts | 9 + desktop/src/i18n/ja.ts | 9 + desktop/src/i18n/zh-CN.ts | 9 + desktop/src/protocol.ts | 9 + desktop/src/ui/settings.tsx | 142 +++++++- src/cli/commands/acp.ts | 2 + src/cli/commands/chat.tsx | 8 +- src/cli/commands/commit.ts | 4 +- src/cli/commands/desktop.ts | 80 ++++- src/cli/commands/run.ts | 2 + src/cli/index.ts | 118 +++++++ src/cli/ui/App.tsx | 3 + src/cli/ui/PlanPanel.tsx | 3 +- src/cli/ui/ProviderPicker.tsx | 60 ++++ src/cli/ui/Setup.tsx | 29 +- src/code/setup.ts | 2 + src/config.ts | 220 +++++++++++++ src/context-manager.ts | 6 +- src/index.ts | 2 + src/loop.ts | 22 +- src/loop/errors.ts | 6 + src/tools/subagent.ts | 6 + 26 files changed, 1083 insertions(+), 299 deletions(-) create mode 100644 src/cli/ui/ProviderPicker.tsx diff --git a/.env.example b/.env.example index 39b3f93fa..3ae800eb6 100644 --- a/.env.example +++ b/.env.example @@ -8,3 +8,10 @@ REASONIX_TRANSCRIPT_DIR=./transcripts # TELEGRAM_BOT_TOKEN=123456:botfather-token # TELEGRAM_OWNER_USER_ID=123456789 # TELEGRAM_ALLOWLIST=123456789,987654321 + +# API 提供商选择(可选) +# 可选值: deepseek, siliconflow, aliyun, volcengine, tencent, baidu, openrouter, fireworks, together, replicate +# DEEPSEEK_API_PROVIDER=siliconflow + +# 或者直接指定 Base URL(优先级高于 apiProvider) +# DEEPSEEK_BASE_URL=https://api.siliconflow.cn/v1 diff --git a/desktop/package-lock.json b/desktop/package-lock.json index ed0721fcf..d758c768e 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -48,13 +48,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -63,9 +63,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -73,21 +73,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -104,14 +104,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -121,14 +121,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -138,9 +138,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -148,29 +148,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -180,9 +180,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -190,9 +190,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -200,9 +200,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -210,9 +210,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -220,27 +220,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -250,13 +250,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -266,13 +266,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -282,33 +282,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -316,14 +316,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -809,9 +809,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", - "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", "cpu": [ "arm" ], @@ -823,9 +823,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", - "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", "cpu": [ "arm64" ], @@ -837,9 +837,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", - "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", "cpu": [ "arm64" ], @@ -851,9 +851,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", - "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", "cpu": [ "x64" ], @@ -865,9 +865,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", - "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", "cpu": [ "arm64" ], @@ -879,9 +879,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", - "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", "cpu": [ "x64" ], @@ -893,13 +893,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", - "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -907,13 +910,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", - "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -921,13 +927,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", - "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -935,13 +944,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", - "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -949,13 +961,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", - "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -963,13 +978,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", - "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -977,13 +995,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", - "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -991,13 +1012,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", - "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1005,13 +1029,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", - "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1019,13 +1046,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", - "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1033,13 +1063,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", - "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1047,13 +1080,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", - "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1061,13 +1097,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", - "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1075,9 +1114,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", - "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", "cpu": [ "x64" ], @@ -1089,9 +1128,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", - "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", "cpu": [ "arm64" ], @@ -1103,9 +1142,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", - "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", "cpu": [ "arm64" ], @@ -1117,9 +1156,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", - "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", "cpu": [ "ia32" ], @@ -1131,9 +1170,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", - "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", "cpu": [ "x64" ], @@ -1145,9 +1184,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", - "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", "cpu": [ "x64" ], @@ -1169,9 +1208,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.1.tgz", - "integrity": "sha512-rpEbaJ/HzNb6fwsquwoAbq29/Vt4gADhS423A8fdkwL4edJ0wZmoB8ar7O6JPDL834MUKOCm/rrJ7c9oAaEaYQ==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz", + "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -1185,23 +1224,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.11.1", - "@tauri-apps/cli-darwin-x64": "2.11.1", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.1", - "@tauri-apps/cli-linux-arm64-gnu": "2.11.1", - "@tauri-apps/cli-linux-arm64-musl": "2.11.1", - "@tauri-apps/cli-linux-riscv64-gnu": "2.11.1", - "@tauri-apps/cli-linux-x64-gnu": "2.11.1", - "@tauri-apps/cli-linux-x64-musl": "2.11.1", - "@tauri-apps/cli-win32-arm64-msvc": "2.11.1", - "@tauri-apps/cli-win32-ia32-msvc": "2.11.1", - "@tauri-apps/cli-win32-x64-msvc": "2.11.1" + "@tauri-apps/cli-darwin-arm64": "2.11.2", + "@tauri-apps/cli-darwin-x64": "2.11.2", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.2", + "@tauri-apps/cli-linux-arm64-musl": "2.11.2", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2", + "@tauri-apps/cli-linux-x64-gnu": "2.11.2", + "@tauri-apps/cli-linux-x64-musl": "2.11.2", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.2", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.2", + "@tauri-apps/cli-win32-x64-msvc": "2.11.2" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.1.tgz", - "integrity": "sha512-6eEKMBXsQPCuM1EmvrjT2+aBuxWQuFdKdW8pzNuNQtpq45nEEpBlD5gr8pUeAyOU1DQKlkFaEc/MPBxb/Pfjtg==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz", + "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==", "cpu": [ "arm64" ], @@ -1216,9 +1255,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.1.tgz", - "integrity": "sha512-LQUO7exfRWjWALNhetph5guWpMeHphRpokOLk0OIbTTExaNwJNFu3I4vb+CCM/4G/QGoZe/5XikZOJdNEFP1ig==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz", + "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==", "cpu": [ "x64" ], @@ -1233,9 +1272,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.1.tgz", - "integrity": "sha512-5i/awiBCRRhOUG8yjn0fMHXIWD5Ez8eEk5LtvOxyQrKuJkRaZDvnbIjZbE183blAwkoA4xN3aO/prJiqscl02Q==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz", + "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==", "cpu": [ "arm" ], @@ -1250,13 +1289,16 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.1.tgz", - "integrity": "sha512-9LrwDw3S9Fygtw/Q6WDhOP+3svJRGAsejeE+GKrc0eO1ThMVhwi2LL6hw4dlKw93IfS7VY1G19sWGxJ/NcU4nA==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz", + "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1267,13 +1309,16 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.1.tgz", - "integrity": "sha512-mNA5dbbqPqDUdTIwdUYYuhO2GvIe9UnB2r0VU2njxBOS3Opbx4gKNC5yP0Iu4rYmEmqdlwry9VzGZQ3wq9dyFg==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz", + "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1284,13 +1329,16 @@ } }, "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.1.tgz", - "integrity": "sha512-fZj3Gwq+6fUs305T5WQiD5iSGJw+j/4w/HGmk4sHDAcy+rp9zU5eaxB7nOyz5/I/nkNAuKPqfp6uIbiUBXkBCw==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz", + "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1301,13 +1349,16 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.1.tgz", - "integrity": "sha512-XFxGxOvHM7jjeD6ozCKdGfhzJ7lERYDGZl1/Kb4fsvchaJsfLJ981TlyTG8Qy/gFq+f5GitH3bfrX9JAkjPEyw==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz", + "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1318,13 +1369,16 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.1.tgz", - "integrity": "sha512-d5C2/Zm+68v7R9wTuTCjRQEVrWjcdMkJBZ1+rXse+QdMMlTB9+u9PDNDLw9PQflWxYLaYZ7tjxxL9Nb9II6PbA==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz", + "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1335,9 +1389,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.1.tgz", - "integrity": "sha512-YdeVWFAR1pTXzUU6NLstPq4G6OLxuDrXCXEBdmBH+5EZIDXUx0D2kJlz3+YjpazkKvAzYpgziTsyRagls0OfRQ==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz", + "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==", "cpu": [ "arm64" ], @@ -1352,9 +1406,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.1.tgz", - "integrity": "sha512-VBGkuH0eB9K9LLSMv361Gzr5Ou72sCS4+ztpmkWEQ+wd/amhcYOsf3X6qn1RJZDzIhiOYHJEOysZUC3baD01rA==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz", + "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==", "cpu": [ "ia32" ], @@ -1369,9 +1423,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.1.tgz", - "integrity": "sha512-b3ORhIAKgp9ZYY+zBt7b7r0kLU2kjvyGF0+MS2SBym3emsweGPybEqocJcmtMuxyBhkOKHP4CiuEJEDuAlTx6A==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz", + "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==", "cpu": [ "x64" ], @@ -1396,7 +1450,7 @@ }, "node_modules/@tauri-apps/plugin-notification": { "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/@tauri-apps/plugin-notification/-/plugin-notification-2.3.3.tgz", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.3.3.tgz", "integrity": "sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg==", "license": "MIT OR Apache-2.0", "dependencies": { @@ -1485,9 +1539,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -1536,9 +1590,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", + "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1598,9 +1652,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.29", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", - "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1645,9 +1699,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001792", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", - "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -1809,9 +1863,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.353", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", - "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", "dev": true, "license": "ISC" }, @@ -3168,11 +3222,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", - "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/parse-entities": { "version": "4.0.2", @@ -3219,9 +3276,9 @@ "license": "ISC" }, "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -3239,7 +3296,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3455,9 +3512,9 @@ } }, "node_modules/rollup": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", - "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", "dev": true, "license": "MIT", "dependencies": { @@ -3471,34 +3528,41 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.3", - "@rollup/rollup-android-arm64": "4.60.3", - "@rollup/rollup-darwin-arm64": "4.60.3", - "@rollup/rollup-darwin-x64": "4.60.3", - "@rollup/rollup-freebsd-arm64": "4.60.3", - "@rollup/rollup-freebsd-x64": "4.60.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", - "@rollup/rollup-linux-arm-musleabihf": "4.60.3", - "@rollup/rollup-linux-arm64-gnu": "4.60.3", - "@rollup/rollup-linux-arm64-musl": "4.60.3", - "@rollup/rollup-linux-loong64-gnu": "4.60.3", - "@rollup/rollup-linux-loong64-musl": "4.60.3", - "@rollup/rollup-linux-ppc64-gnu": "4.60.3", - "@rollup/rollup-linux-ppc64-musl": "4.60.3", - "@rollup/rollup-linux-riscv64-gnu": "4.60.3", - "@rollup/rollup-linux-riscv64-musl": "4.60.3", - "@rollup/rollup-linux-s390x-gnu": "4.60.3", - "@rollup/rollup-linux-x64-gnu": "4.60.3", - "@rollup/rollup-linux-x64-musl": "4.60.3", - "@rollup/rollup-openbsd-x64": "4.60.3", - "@rollup/rollup-openharmony-arm64": "4.60.3", - "@rollup/rollup-win32-arm64-msvc": "4.60.3", - "@rollup/rollup-win32-ia32-msvc": "4.60.3", - "@rollup/rollup-win32-x64-gnu": "4.60.3", - "@rollup/rollup-win32-x64-msvc": "4.60.3", + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 286a4868e..82c3bf909 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -302,6 +302,12 @@ export type Settings = { contextTokens?: Record; showSystemEvents?: boolean; version: string; + apiProvider?: string; + apiKeys?: Record; + customProviders?: Record; + availableProviders?: Array<{ id: string; name: string; baseUrl: string; custom: boolean }>; + /** Canonical → provider-specific model names for the current provider. */ + availableModels?: Record; }; export type BalanceInfoItem = { @@ -420,6 +426,9 @@ function sanitizeSettingsPatch(patch: SettingsPatch): Partial { exaApiKey: _exa, ollamaApiKey: _ollama, webSearchEndpoint, + apiProvider: _apiProvider, + apiKey: _apiKey, + customProviders: _customProviders, ...rest } = patch; const sanitized: Partial = { ...rest }; @@ -1049,6 +1058,11 @@ function applyIncomingRaw(state: State, ev: IncomingEvent): State { subagentModels: ev.subagentModels, showSystemEvents: ev.showSystemEvents, version: ev.version, + apiProvider: ev.apiProvider, + apiKeys: ev.apiKeys, + customProviders: ev.customProviders, + availableProviders: ev.availableProviders, + availableModels: ev.availableModels, }, }; } @@ -1598,10 +1612,6 @@ function TabRuntime({ sendRpc({ cmd: "qq_config_save", ...patch }), [sendRpc], ); - const saveApiKey = useCallback( - (key: string) => sendRpc({ cmd: "setup_save_key", key }), - [sendRpc], - ); const addMcpSpec = useCallback( (spec: string) => sendRpc({ cmd: "mcp_specs_add", spec }), [sendRpc], @@ -2877,7 +2887,6 @@ function TabRuntime({ qq={state.qq} onClose={() => setSettingsOpen(false)} onSave={saveSettings} - onSaveApiKey={saveApiKey} onLoadQQ={loadQQSettings} onConnectQQ={connectQQ} onDisconnectQQ={disconnectQQ} diff --git a/desktop/src/i18n/de.ts b/desktop/src/i18n/de.ts index 679e32dd3..1575da755 100644 --- a/desktop/src/i18n/de.ts +++ b/desktop/src/i18n/de.ts @@ -221,6 +221,15 @@ export const de: typeof en = { baseUrl: "DeepSeek-Basis-URL", baseUrlHint: "Nur bei Verwendung eines Proxys überschreiben. Leer = offizieller Endpunkt. Neustart erforderlich.", + provider: "API-Anbieter", + providerHint: "Anbieter auswählen, um die Basis-URL automatisch zu konfigurieren.", + providerCustom: "Benutzerdefinierter Anbieter", + providerCustomAdd: "Hinzufügen", + providerCustomRemove: "Entfernen", + providerCustomId: "ID", + providerCustomUrl: "Basis-URL", + providerCustomName: "Anzeigename (optional)", + providerCustomSave: "Hinzufügen", workspace: "Arbeitsbereich", workspaceHint: "Root-Verzeichnis, in dem Agent-Tools arbeiten. Wechseln speichert in der Konfiguration und lädt Tools neu.", diff --git a/desktop/src/i18n/en.ts b/desktop/src/i18n/en.ts index 059e95b71..3a974e2e5 100644 --- a/desktop/src/i18n/en.ts +++ b/desktop/src/i18n/en.ts @@ -211,6 +211,15 @@ export const en = { }, baseUrl: "DeepSeek base URL", baseUrlHint: "Override only if using a proxy. Empty = official endpoint. Restart required.", + provider: "API provider", + providerHint: "Select a provider to auto-configure Base URL.", + providerCustom: "Custom provider", + providerCustomAdd: "Add", + providerCustomRemove: "Remove", + providerCustomId: "ID", + providerCustomUrl: "Base URL", + providerCustomName: "Display name (optional)", + providerCustomSave: "Add", workspace: "Workspace", workspaceHint: "Root dir agent tools operate inside. Switching saves to config and reloads tools.", diff --git a/desktop/src/i18n/ja.ts b/desktop/src/i18n/ja.ts index 32981d30d..68cb44179 100644 --- a/desktop/src/i18n/ja.ts +++ b/desktop/src/i18n/ja.ts @@ -219,6 +219,15 @@ export const ja: typeof en = { }, baseUrl: "DeepSeek ベースURL", baseUrlHint: "プロキシ使用時のみ上書き。空欄 = 公式エンドポイント。再起動が必要です。", + provider: "APIプロバイダー", + providerHint: "プロバイダーを選択するとベースURLが自動設定されます。", + providerCustom: "カスタムプロバイダー", + providerCustomAdd: "追加", + providerCustomRemove: "削除", + providerCustomId: "ID", + providerCustomUrl: "ベースURL", + providerCustomName: "表示名(任意)", + providerCustomSave: "追加", workspace: "ワークスペース", workspaceHint: "エージェントツールが操作するルートディレクトリ。切り替えは設定に保存され、ツールが再読み込みされます。", diff --git a/desktop/src/i18n/zh-CN.ts b/desktop/src/i18n/zh-CN.ts index 11f80343c..6609d6daa 100644 --- a/desktop/src/i18n/zh-CN.ts +++ b/desktop/src/i18n/zh-CN.ts @@ -210,6 +210,15 @@ export const zhCN: typeof en = { }, baseUrl: "DeepSeek base URL", baseUrlHint: "走代理时再改。留空 = 官方地址。需重启。", + provider: "API 提供商", + providerHint: "选择 API 提供商后自动配置 Base URL。", + providerCustom: "自定义提供商", + providerCustomAdd: "添加", + providerCustomRemove: "删除", + providerCustomId: "标识 (ID)", + providerCustomUrl: "Base URL", + providerCustomName: "显示名称 (可选)", + providerCustomSave: "添加", workspace: "工作目录", workspaceHint: "agent 工具操作的根目录。切换会写入配置并重载工具。", workspaceChange: "更换…", diff --git a/desktop/src/protocol.ts b/desktop/src/protocol.ts index 4f5414f7e..41024de20 100644 --- a/desktop/src/protocol.ts +++ b/desktop/src/protocol.ts @@ -370,6 +370,12 @@ export type SettingsEvent = { subagentModels?: Record; showSystemEvents?: boolean; version: string; + apiProvider?: string; + apiKeys?: Record; + customProviders?: Record; + availableProviders?: Array<{ id: string; name: string; baseUrl: string; custom: boolean }>; + /** Canonical → provider-specific model names for the current provider. */ + availableModels?: Record; }; export type QQSettingsEvent = { @@ -423,6 +429,9 @@ export type SettingsPatch = { /** Per-model context-window override (tokens). Keys are model ids; values are the prompt-side token cap. */ contextTokens?: Record; showSystemEvents?: boolean; + apiProvider?: string; + apiKey?: string; + customProviders?: { add?: { id: string; baseUrl: string; name?: string }; remove?: string }; }; export type QQConfigPatch = { diff --git a/desktop/src/ui/settings.tsx b/desktop/src/ui/settings.tsx index e88a4e22b..f3f283a0d 100644 --- a/desktop/src/ui/settings.tsx +++ b/desktop/src/ui/settings.tsx @@ -78,7 +78,6 @@ export function SettingsModal({ qq, onClose, onSave, - onSaveApiKey, onLoadQQ, onConnectQQ, onDisconnectQQ, @@ -117,7 +116,6 @@ export function SettingsModal({ qq: QQDesktopSettingsState | null; onClose: () => void; onSave: (patch: SettingsPatch) => void; - onSaveApiKey: (key: string) => void; onLoadQQ: () => void; onConnectQQ: () => void; onDisconnectQQ: () => void; @@ -201,7 +199,19 @@ export function SettingsModal({ onPickWorkspace={onPickWorkspace} /> )} - {page === "models" && } + {page === "models" && ( + <> + + + + )} {page === "mcp" && ( } {page === "general" ? ( <> - ; + apiProvider?: string; + apiKeys?: Record; onSave: (patch: SettingsPatch) => void; - onSaveApiKey: (key: string) => void; }) { const [key, setKey] = useState(""); const [urlDraft, setUrlDraft] = useState(baseUrl ?? ""); + const [addOpen, setAddOpen] = useState(false); + const [addId, setAddId] = useState(""); + const [addUrl, setAddUrl] = useState(""); + const [addName, setAddName] = useState(""); + // Reset key input when switching providers so the previous + // provider's key doesn't linger in the input field. + useEffect(() => { setKey(""); }, [apiProvider]); + useEffect(() => { setUrlDraft(baseUrl ?? ""); }, [baseUrl]); + const providers = availableProviders ?? []; + const current = providers.find((p) => p.id === apiProvider); + // Only show prefix if this specific provider has a saved key in apiKeys. + // No fallback to apiKeyPrefix — that would leak another provider's key. + const currentApiKeyPrefix = apiKeys?.[apiProvider ?? ""]; return (
{t("settings.apiSection")}
+
+
+
{t("settings.provider")}
+
{t("settings.providerHint")}
+
+ +
{t("settings.apiKey")}
- {apiKeyPrefix - ? t("settings.apiKeySet", { prefix: apiKeyPrefix }) + {currentApiKeyPrefix + ? t("settings.apiKeySet", { prefix: currentApiKeyPrefix }) : t("settings.apiKeyNotSet")}
@@ -1003,7 +1044,7 @@ function ApiKeySection({ disabled={!key} onClick={() => { if (!key) return; - onSaveApiKey(key); + onSave({ apiKey: key }); setKey(""); }} > @@ -1014,7 +1055,7 @@ function ApiKeySection({
{t("settings.baseUrl")}
-
{t("settings.baseUrlHint")}
+
{current ? current.baseUrl : t("settings.baseUrlHint")}
onSave({ baseUrl: urlDraft.trim() })} />
+
+
+
{t("settings.providerCustom")}
+
+
+ {!addOpen ? ( + + ) : null} + {current?.custom ? ( + + ) : null} +
+
+ {addOpen ? ( +
+
+ setAddId(e.target.value)} + placeholder={t("settings.providerCustomId")} + style={{ flex: 1 }} + /> + setAddUrl(e.target.value)} + placeholder={t("settings.providerCustomUrl")} + style={{ flex: 2 }} + /> + setAddName(e.target.value)} + placeholder={t("settings.providerCustomName")} + style={{ flex: 1 }} + /> + + +
+
+ ) : null}
); } diff --git a/src/cli/commands/acp.ts b/src/cli/commands/acp.ts index 2bc42ca21..614ed7682 100644 --- a/src/cli/commands/acp.ts +++ b/src/cli/commands/acp.ts @@ -27,6 +27,7 @@ import { DEFAULT_MODEL, bridgeEndpointEnv, loadApiKey, + loadApiProvider, loadEditMode, loadEndpoint, loadMaxIterPerTurn, @@ -184,6 +185,7 @@ async function buildSession(opts: { prefix, tools: toolset.tools, model, + providerId: loadApiProvider(), budgetUsd: opts.budgetUsd, maxIterPerTurn: loadMaxIterPerTurn(), session: `acp-${timestampSuffix()}`, diff --git a/src/cli/commands/chat.tsx b/src/cli/commands/chat.tsx index 7bb36c3e8..6ac2eb764 100644 --- a/src/cli/commands/chat.tsx +++ b/src/cli/commands/chat.tsx @@ -4,6 +4,7 @@ import { type ReasoningEffort, bridgeEndpointEnv, loadApiKey, + loadApiProvider, loadHistoryScrollMode, loadToolRateLimit, normalizeMcpConfig, @@ -117,6 +118,7 @@ export interface ChatOptions { interface RootProps extends ChatOptions { initialKey: string | undefined; + apiProvider: string; tools: ToolRegistry | undefined; mcpSpecs: string[]; mcpServers: McpServerSummary[]; @@ -146,6 +148,7 @@ interface RootProps extends ChatOptions { function Root({ initialKey, + apiProvider, tools, mcpSpecs, mcpServers, @@ -177,7 +180,8 @@ function Root({ }; }, [appProps.codeMode, activeRoot]); - if (!key) { + // 如果没有 API Key 或者没有配置提供商,显示 Setup 界面 + if (!key || apiProvider === "deepseek") { return ( { markPhase("chat_command_enter"); loadDotenv(); const initialKey = loadApiKey(); + const apiProvider = loadApiProvider(); markPhase("config_loaded"); const requestedSpecs = opts.mcp ?? []; @@ -444,6 +449,7 @@ export async function chatCommand(opts: ChatOptions): Promise { const { waitUntilExit } = render( { } const client = new DeepSeekClient({ apiKey: ep.apiKey, baseUrl: ep.baseUrl }); - const model = opts.model ?? DEFAULT_MODEL; + const model = resolveModelForProvider(loadApiProvider(), opts.model ?? DEFAULT_MODEL); const recentCommits = readRecentCommits(); let message = ""; diff --git a/src/cli/commands/desktop.ts b/src/cli/commands/desktop.ts index 81aa6bf5d..4573d4665 100644 --- a/src/cli/commands/desktop.ts +++ b/src/cli/commands/desktop.ts @@ -16,18 +16,23 @@ import { pickPrimaryBalance } from "../../client.js"; import { codeSystemPrompt } from "../../code/prompt.js"; import { applyPlanMode, buildCodeToolset } from "../../code/setup.js"; import { + API_PROVIDER_PRESETS, DEFAULT_MODEL, type DesktopCloseBehavior, type DesktopOpenTab, type EditMode, type McpServerConfig, type ReasonixConfig, + SUPPORTED_OFFICIAL_MODELS, bridgeEndpointEnv, isPlausibleKey, isReasoningEffort, loadApiKey, + loadApiKeyForProvider, + loadApiProvider, loadBaiduApiKey, loadBraveApiKey, + loadCustomProviders, loadDesktopCloseBehavior, loadDesktopOpenTabs, loadEditMode, @@ -51,8 +56,13 @@ import { pushRecentWorkspace, readConfig, webSearchEngine as readWebSearchEngine, + removeCustomProvider, + resolveModelForProvider, saveApiKey, + saveApiKeyForProvider, + saveApiProvider, saveBaseUrl, + saveCustomProvider, saveDesktopCloseBehavior, saveDesktopOpenTabs, saveEditMode, @@ -202,6 +212,9 @@ type InMessage = { tabId?: string } & ( subagentModels?: Record; contextTokens?: Record; showSystemEvents?: boolean; + apiProvider?: string; + apiKey?: string; + customProviders?: { add?: { id: string; baseUrl: string; name?: string }; remove?: string }; } | { cmd: "qq_status_get" } | { cmd: "qq_connect" } @@ -285,6 +298,12 @@ interface SettingsEvent { contextTokens?: Record; showSystemEvents?: boolean; version: string; + apiProvider?: string; + apiKeys?: Record; + customProviders?: Record; + availableProviders?: Array<{ id: string; name: string; baseUrl: string; custom: boolean }>; + /** Canonical → provider-specific model names for the current provider. */ + availableModels?: Record; } interface QQSettingsEvent { @@ -1013,6 +1032,27 @@ function emitSettings(tab: Tab): void { const editMode = loadEditMode(); if (tab.toolset) applyPlanMode(tab.toolset.tools, editMode); const recent = loadRecentWorkspaces().filter((p) => p !== tab.rootDir); + const cfg = readConfig(); + const providerId = loadApiProvider(); + const customProviders = cfg.customProviders ?? {}; + // Collect masked key prefixes for ALL providers so the UI can show + // which ones already have a saved key when switching. + // Read apiKeys directly — no fallback to legacy apiKey, so each + // provider shows its own prefix and "(not set)" for unsaved ones. + const allApiKeys: Record = {}; + const allProviderIds = [ + ...API_PROVIDER_PRESETS.map((p) => p.id), + ...Object.keys(customProviders), + ]; + for (const pid of allProviderIds) { + const k = cfg.apiKeys?.[pid]; + if (k) allApiKeys[pid] = `${k.slice(0, 6)}…${k.slice(-3)}`; + } + // Build canonical → provider-specific model name map for the settings UI. + const availableModels: Record = {}; + for (const canonical of SUPPORTED_OFFICIAL_MODELS) { + availableModels[canonical] = resolveModelForProvider(providerId, canonical); + } emit( { type: "$settings", @@ -1027,12 +1067,30 @@ function emitSettings(tab: Tab): void { editor: loadEditor(), desktopCloseBehavior: loadDesktopCloseBehavior(), webSearchEngine: readWebSearchEngine(), - webSearchEndpoint: readConfig().webSearchEndpoint, + webSearchEndpoint: cfg.webSearchEndpoint, webSearchApiKeys: collectWebSearchApiKeyPrefixes(), subagentModels: loadSubagentModels(), - contextTokens: readConfig().contextTokens, + contextTokens: cfg.contextTokens, showSystemEvents: loadShowSystemEvents(), version: VERSION, + apiProvider: providerId, + apiKeys: allApiKeys, + customProviders, + availableProviders: [ + ...API_PROVIDER_PRESETS.map((p) => ({ + id: p.id, + name: p.name, + baseUrl: p.baseUrl, + custom: false, + })), + ...Object.entries(customProviders).map(([id, c]) => ({ + id, + name: c.name ?? id, + baseUrl: c.baseUrl, + custom: true, + })), + ], + availableModels, }, tab.id, ); @@ -1379,11 +1437,13 @@ function buildRuntimeFor(tab: Tab): RuntimeState { const client = new DeepSeekClient({ apiKey: ep.apiKey, baseUrl: ep.baseUrl }); const prefix = new ImmutablePrefix({ system: tab.system, toolSpecs: toolset.tools.specs() }); const reasoningEffort = loadReasoningEffort(); + const providerId = loadApiProvider(); const loop = new CacheFirstLoop({ client, prefix, tools: toolset.tools, model: tab.currentModel, + providerId, budgetUsd: tab.budgetUsd, session: tab.currentSession, reasoningEffort, @@ -3310,6 +3370,22 @@ export async function desktopCommand(opts: DesktopOptions): Promise { } } } + if (msg.apiProvider !== undefined) { + saveApiProvider(msg.apiProvider); + } + if (msg.apiKey !== undefined) { + const pid = loadApiProvider(); + saveApiKeyForProvider(pid, msg.apiKey); + } + if (msg.customProviders !== undefined) { + if (msg.customProviders.add) { + const { id, baseUrl, name } = msg.customProviders.add; + saveCustomProvider(id, baseUrl, name); + } + if (msg.customProviders.remove) { + removeCustomProvider(msg.customProviders.remove); + } + } emitSettings(tab); } catch (err) { emit( diff --git a/src/cli/commands/run.ts b/src/cli/commands/run.ts index e29f742f1..770b3164b 100644 --- a/src/cli/commands/run.ts +++ b/src/cli/commands/run.ts @@ -6,6 +6,7 @@ import { defaultConfigPath, isPlausibleKey, loadApiKey, + loadApiProvider, loadEndpoint, loadMaxIterPerTurn, loadToolRateLimit, @@ -151,6 +152,7 @@ export async function runCommand(opts: RunOptions): Promise { prefix, tools, model: opts.model, + providerId: loadApiProvider(), budgetUsd: opts.budgetUsd, maxIterPerTurn: loadMaxIterPerTurn(), }); diff --git a/src/cli/index.ts b/src/cli/index.ts index 84d7feaf0..56ad446f6 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -710,6 +710,124 @@ program }, ); +program + .command("provider") + .description("管理 API 提供商") + .option("--list", "列出所有可用提供商") + .option("--set ", "设置当前提供商") + .option("--show", "显示当前提供商") + .option("--key ", "为当前提供商设置 API Key") + .option("--add ", "添加自定义提供商") + .option("--url ", "自定义提供商 Base URL(配合 --add)") + .option("--name ", "自定义提供商显示名称(配合 --add)") + .option("--remove ", "删除自定义提供商") + .action( + async (opts: { + list?: boolean; + set?: string; + show?: boolean; + key?: string; + add?: string; + url?: string; + name?: string; + remove?: string; + }) => { + const { + API_PROVIDER_PRESETS, + loadApiProvider, + saveApiProvider, + loadApiKeyForProvider, + saveApiKeyForProvider, + resolveProviderPreset, + loadCustomProviders, + saveCustomProvider, + removeCustomProvider, + resolveAnyProvider, + } = await import("../config.js"); + + if (opts.add) { + if (!opts.url) { + console.error("缺少 --url 参数"); + console.error("用法: reasonix provider --add --url [--name ]"); + process.exit(1); + } + saveCustomProvider(opts.add, opts.url, opts.name); + console.log(`已添加自定义提供商: ${opts.name ?? opts.add}`); + console.log(` Base URL: ${opts.url}`); + return; + } + + if (opts.remove) { + removeCustomProvider(opts.remove); + console.log(`已删除自定义提供商: ${opts.remove}`); + return; + } + + if (opts.list) { + console.log("可用的 API 提供商:\n"); + for (const p of API_PROVIDER_PRESETS) { + const marker = p.id === "deepseek" ? " (默认)" : ""; + console.log(` ${p.id}: ${p.name}${marker}`); + console.log(` ${p.description}`); + console.log(` Base URL: ${p.baseUrl}`); + console.log(""); + } + const customs = loadCustomProviders(); + const customKeys = Object.keys(customs); + if (customKeys.length > 0) { + console.log("自定义提供商:\n"); + for (const id of customKeys) { + const c = customs[id]!; + console.log(` ${id}: ${c.name ?? id}`); + console.log(` Base URL: ${c.baseUrl}`); + console.log(""); + } + } + return; + } + + if (opts.show) { + const providerId = loadApiProvider(); + const provider = resolveAnyProvider(providerId); + const apiKey = loadApiKeyForProvider(providerId); + console.log(`当前提供商: ${provider?.name ?? providerId}`); + console.log(`Base URL: ${provider?.baseUrl ?? "未知"}`); + console.log(`API Key: ${apiKey ? "已设置" : "未设置"}`); + return; + } + + if (opts.set) { + const provider = resolveAnyProvider(opts.set); + if (!provider) { + console.error(`未知提供商: ${opts.set}`); + console.error("使用 --list 查看所有可用提供商"); + console.error("或使用 --add 添加自定义提供商"); + process.exit(1); + } + saveApiProvider(opts.set); + console.log(`已切换到提供商: ${provider.name}`); + return; + } + + if (opts.key) { + const providerId = loadApiProvider(); + const provider = resolveAnyProvider(providerId); + saveApiKeyForProvider(providerId, opts.key); + console.log(`已为 ${provider?.name ?? providerId} 设置 API Key`); + return; + } + + // Default: show help + console.log("用法:"); + console.log(" reasonix provider --list 列出所有可用提供商"); + console.log(" reasonix provider --show 显示当前提供商"); + console.log(" reasonix provider --set 设置当前提供商"); + console.log(" reasonix provider --key 为当前提供商设置 API Key"); + console.log(" reasonix provider --add --url [--name ] 添加自定义提供商"); + console.log(" reasonix provider --remove 删除自定义提供商"); + }, + ); + program.parseAsync(process.argv).catch((err) => { console.error(err); process.exit(1); diff --git a/src/cli/ui/App.tsx b/src/cli/ui/App.tsx index 5f410619b..d2638cd33 100644 --- a/src/cli/ui/App.tsx +++ b/src/cli/ui/App.tsx @@ -37,6 +37,7 @@ import { defaultConfigPath, editModeHintShown, isReasoningEffort, + loadApiProvider, loadEndpoint, loadEngineeringLifecycleMode, loadHistoryScrollMode, @@ -1032,6 +1033,7 @@ function AppInner({ // Per-skill model override (frontmatter `model: ...`), // else falls through to spawnSubagent's default. model: skill.model, + providerId: loadApiProvider(), allowedTools: skill.allowedTools, sink: subagentSinkRef.current, // Stamped onto every event so the TUI sink + usage log can @@ -1051,6 +1053,7 @@ function AppInner({ prefix, tools, model, + providerId: loadApiProvider(), budgetUsd, session, hooks: hookList, diff --git a/src/cli/ui/PlanPanel.tsx b/src/cli/ui/PlanPanel.tsx index 59d0a1215..99eab892a 100644 --- a/src/cli/ui/PlanPanel.tsx +++ b/src/cli/ui/PlanPanel.tsx @@ -16,7 +16,8 @@ */ import { Box, type Color, Text } from "ink"; -import React, { useMemo, useState } from "react"; +import type React from "react"; +import { useMemo, useState } from "react"; import { t } from "../../i18n/index.js"; import type { PlanStep, StepCompletion } from "../../tools/plan.js"; import type { CheckpointChoice } from "./PlanCheckpointConfirm.js"; diff --git a/src/cli/ui/ProviderPicker.tsx b/src/cli/ui/ProviderPicker.tsx new file mode 100644 index 000000000..333798ea3 --- /dev/null +++ b/src/cli/ui/ProviderPicker.tsx @@ -0,0 +1,60 @@ +import { Box, Text } from "ink"; +import React, { useState } from "react"; +import { API_PROVIDER_PRESETS, type ApiProviderPreset } from "../../config.js"; +import { useKeystroke } from "./keystroke-context.js"; +import { COLOR, GLYPH } from "./theme.js"; +import { FG } from "./theme/tokens.js"; + +export interface ProviderPickerProps { + current?: string; + onChoose: (provider: ApiProviderPreset) => void; + onCancel: () => void; +} + +export function ProviderPicker({ current, onChoose, onCancel }: ProviderPickerProps) { + const [focus, setFocus] = useState(() => { + const idx = API_PROVIDER_PRESETS.findIndex((p) => p.id === current); + return idx >= 0 ? idx : 0; + }); + + useKeystroke((ev) => { + if (ev.escape) return onCancel(); + if (ev.upArrow) return setFocus((f) => Math.max(0, f - 1)); + if (ev.downArrow) return setFocus((f) => Math.min(API_PROVIDER_PRESETS.length - 1, f + 1)); + if (ev.return) { + return onChoose(API_PROVIDER_PRESETS[focus]!); + } + }); + + return ( + + + + {GLYPH.brand} + + {" "} + 选择 API 提供商 + + + 所有提供商都兼容 DeepSeek API,选择一个即可 + + + {API_PROVIDER_PRESETS.map((provider, i) => { + const focused = i === focus; + const active = provider.id === current; + return ( + + {focused ? " › " : " "} + + {provider.name} + + {` — ${provider.description}`} + + ); + })} + + ↑↓ 选择 | Enter 确认 | Esc 取消 + + + ); +} diff --git a/src/cli/ui/Setup.tsx b/src/cli/ui/Setup.tsx index 99681c05d..8a6f2d1ad 100644 --- a/src/cli/ui/Setup.tsx +++ b/src/cli/ui/Setup.tsx @@ -1,8 +1,16 @@ import { Box, Text, useApp } from "ink"; import React, { useState } from "react"; -import { defaultConfigPath, isPlausibleKey, redactKey, saveApiKey } from "../../config.js"; +import { + type ApiProviderPreset, + DEFAULT_PROVIDER_ID, + defaultConfigPath, + isPlausibleKey, + redactKey, + saveApiKeyForProvider, +} from "../../config.js"; import { t } from "../../i18n/index.js"; import { MaskedInput } from "./MaskedInput.js"; +import { ProviderPicker } from "./ProviderPicker.js"; import { COLOR, GLYPH, GRADIENT } from "./theme.js"; import { FG } from "./theme/tokens.js"; @@ -10,11 +18,20 @@ export interface SetupProps { onReady: (apiKey: string) => void; } +type SetupStep = "provider" | "apikey"; + export function Setup({ onReady }: SetupProps) { + const [step, setStep] = useState("provider"); + const [selectedProvider, setSelectedProvider] = useState(null); const [value, setValue] = useState(""); const [error, setError] = useState(null); const { exit } = useApp(); + const handleProviderChoose = (provider: ApiProviderPreset) => { + setSelectedProvider(provider); + setStep("apikey"); + }; + const handleSubmit = (raw: string) => { const trimmed = raw.trim(); if (trimmed === "/exit" || trimmed === "/quit") { @@ -27,7 +44,8 @@ export function Setup({ onReady }: SetupProps) { return; } try { - saveApiKey(trimmed); + const providerId = selectedProvider?.id ?? DEFAULT_PROVIDER_ID; + saveApiKeyForProvider(providerId, trimmed); } catch (err) { setError(t("wizard.reviewSaveError", { message: (err as Error).message })); return; @@ -35,6 +53,10 @@ export function Setup({ onReady }: SetupProps) { onReady(trimmed); }; + if (step === "provider") { + return exit()} />; + } + return ( @@ -44,6 +66,9 @@ export function Setup({ onReady }: SetupProps) { {" "} {t("wizard.welcomeTitle")} + + {`提供商: ${selectedProvider?.name ?? "DeepSeek 官方"}`} + {t("wizard.apiKeyPrompt")} diff --git a/src/code/setup.ts b/src/code/setup.ts index 3ed351029..1e1a879cc 100644 --- a/src/code/setup.ts +++ b/src/code/setup.ts @@ -1,6 +1,7 @@ import { DeepSeekClient } from "../client.js"; import { type EditMode, + loadApiProvider, loadEditMode, loadEndpoint, loadFilesystemOutlineThresholdBytes, @@ -129,6 +130,7 @@ export async function buildCodeToolset(opts: CodeToolsetOpts): Promise p.id === providerId); +} + +export function getProviderModels(providerId: string): string[] { + const preset = resolveProviderPreset(providerId); + return preset?.models ?? []; +} + +/** + * Resolve a canonical model name to the provider-specific name for API calls. + * Uses positional mapping: `SUPPORTED_OFFICIAL_MODELS[i]` → `preset.models[i]`. + * For custom providers (no preset), returns the canonical name unchanged. + */ +export function resolveModelForProvider(providerId: string, canonicalModel: string): string { + const preset = resolveProviderPreset(providerId); + if (!preset || preset.models.length === 0) return canonicalModel; + const idx = SUPPORTED_OFFICIAL_MODELS.indexOf(canonicalModel); + if (idx < 0 || idx >= preset.models.length) return canonicalModel; + return preset.models[idx] ?? canonicalModel; +} + +/** + * Load the active provider + model from config and resolve to the + * provider-specific model name ready for API calls. + */ +export function loadResolvedModel(path: string = defaultConfigPath()): string { + const providerId = loadApiProvider(path); + const model = loadModel(path); + return resolveModelForProvider(providerId, model); +} + export const DEFAULT_MODEL = "deepseek-v4-flash"; /** Models the official api.deepseek.com endpoint currently accepts. v3-era @@ -173,6 +292,12 @@ export interface ProxyConfig { export interface ReasonixConfig { apiKey?: string; baseUrl?: string; + /** 当前使用的 API 提供商 ID(默认 "deepseek") */ + apiProvider?: string; + /** 每个提供商独立的 API Key 存储 */ + apiKeys?: Record; + /** 用户自定义 API 提供商,key 为提供商标识,value 为 { baseUrl, name } */ + customProviders?: Record; lang?: LanguageCode; /** Persisted DeepSeek model id — `/model ` and the dashboard model picker write through this. */ model?: string; @@ -780,11 +905,29 @@ export function resolveBaseUrlEnv(): string | undefined { // (baseUrl, apiKey) is a tuple: whichever source defines baseUrl owns apiKey too, // so a stale env DEEPSEEK_API_KEY doesn't bleed into a custom config baseUrl (#1631). export function loadEndpoint(path: string = defaultConfigPath()): ResolvedEndpoint { + // 优先级:环境变量 > 配置文件 apiProvider > 配置文件 baseUrl > 默认值 const envBaseUrl = resolveBaseUrlEnv(); if (envBaseUrl) { return { baseUrl: envBaseUrl, apiKey: process.env.DEEPSEEK_API_KEY }; } const cfg = readConfig(path); + + // 检查是否指定了提供商 + if (cfg.apiProvider) { + const preset = API_PROVIDER_PRESETS.find((p) => p.id === cfg.apiProvider); + if (preset) { + const apiKey = cfg.apiKeys?.[cfg.apiProvider] ?? cfg.apiKey; + return { baseUrl: preset.baseUrl, apiKey }; + } + // 检查用户自定义提供商 + const custom = cfg.customProviders?.[cfg.apiProvider]; + if (custom) { + const apiKey = cfg.apiKeys?.[cfg.apiProvider] ?? cfg.apiKey; + return { baseUrl: custom.baseUrl, apiKey }; + } + } + + // 原有逻辑 if (cfg.baseUrl) { return { baseUrl: cfg.baseUrl, apiKey: cfg.apiKey }; } @@ -1115,6 +1258,83 @@ export function saveApiKey(key: string, path: string = defaultConfigPath()): voi if (trimmed) process.env.DEEPSEEK_API_KEY = trimmed; } +// ============================================================================ +// API Provider Management Functions +// ============================================================================ + +export function loadApiProvider(path: string = defaultConfigPath()): string { + const cfg = readConfig(path); + return cfg.apiProvider ?? DEFAULT_PROVIDER_ID; +} + +export function saveApiProvider(providerId: string, path: string = defaultConfigPath()): void { + const cfg = readConfig(path); + cfg.apiProvider = providerId; + writeConfig(cfg, path); +} + +export function loadApiKeyForProvider( + providerId: string, + path: string = defaultConfigPath(), +): string | undefined { + const cfg = readConfig(path); + return cfg.apiKeys?.[providerId] ?? cfg.apiKey; +} + +export function saveApiKeyForProvider( + providerId: string, + key: string, + path: string = defaultConfigPath(), +): void { + const cfg = readConfig(path); + if (!cfg.apiKeys) cfg.apiKeys = {}; + cfg.apiKeys[providerId] = key.trim(); + writeConfig(cfg, path); + // Also set as current apiKey for backward compatibility + cfg.apiKey = key.trim(); + writeConfig(cfg, path); + // Update process env + if (key.trim()) process.env.DEEPSEEK_API_KEY = key.trim(); +} + +export function loadCustomProviders( + path: string = defaultConfigPath(), +): Record { + return readConfig(path).customProviders ?? {}; +} + +export function saveCustomProvider( + providerId: string, + baseUrl: string, + name?: string, + path: string = defaultConfigPath(), +): void { + const cfg = readConfig(path); + if (!cfg.customProviders) cfg.customProviders = {}; + cfg.customProviders[providerId] = { baseUrl, ...(name ? { name } : {}) }; + writeConfig(cfg, path); +} + +export function removeCustomProvider(providerId: string, path: string = defaultConfigPath()): void { + const cfg = readConfig(path); + if (cfg.customProviders) { + delete cfg.customProviders[providerId]; + writeConfig(cfg, path); + } +} + +/** 查找提供商信息(内置预设 + 自定义提供商) */ +export function resolveAnyProvider( + providerId: string, + path: string = defaultConfigPath(), +): { name: string; baseUrl: string } | undefined { + const preset = API_PROVIDER_PRESETS.find((p) => p.id === providerId); + if (preset) return { name: preset.name, baseUrl: preset.baseUrl }; + const custom = readConfig(path).customProviders?.[providerId]; + if (custom) return { name: custom.name ?? providerId, baseUrl: custom.baseUrl }; + return undefined; +} + /** Windows: case-insensitive — NTFS treats `F:\Foo` and `f:\foo` as one directory (#402). */ function findProjectKey(cfg: ReasonixConfig, rootDir: string): string | undefined { const projects = cfg.projects; diff --git a/src/context-manager.ts b/src/context-manager.ts index 61fa8e76f..be51f7c2a 100644 --- a/src/context-manager.ts +++ b/src/context-manager.ts @@ -69,6 +69,8 @@ export interface ContextManagerDeps { getFewShots?: () => readonly ChatMessage[]; /** Fired when the message log was rewritten by fold; lets the loop drop session-scoped caches whose validity rested on the elided history (e.g. read-before-edit tracker). */ onLogRewrite?: () => void; + /** Provider-resolved model name for fold-summary API calls. Defaults to "deepseek-v4-flash". */ + summaryModel?: string; } export type PostUsageDecisionKind = "none" | "fold" | "exit-with-summary"; @@ -348,7 +350,9 @@ export class ContextManager { messagesToSummarize: ChatMessage[], pinnedSkillNames: string[], ): Promise<{ content: string; reasoningContent: string }> { - const summaryModel = "deepseek-v4-flash"; + // Provider-resolved model for fold summaries — avoids hardcoded "deepseek-v4-flash" + // which would 400 on third-party providers that use different model names. + const summaryModel = this.deps.summaryModel ?? "deepseek-v4-flash"; const healed = healLoadedMessages(messagesToSummarize, DEFAULT_MAX_RESULT_CHARS).messages; const agentSystem = this.deps.getSystemPrompt(); const fewShots = this.deps.getFewShots?.() ?? []; diff --git a/src/index.ts b/src/index.ts index 52b6139ae..2bd24436f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -300,8 +300,10 @@ export { loadExaApiKey, loadOllamaApiKey, loadBraveApiKey, + loadResolvedModel, readConfig, redactKey, + resolveModelForProvider, saveApiKey, saveBaseUrl, writeConfig, diff --git a/src/loop.ts b/src/loop.ts index 3668edf57..83d636916 100644 --- a/src/loop.ts +++ b/src/loop.ts @@ -1,5 +1,5 @@ import { type DeepSeekClient, Usage } from "./client.js"; -import type { ReasoningEffort } from "./config.js"; +import { type ReasoningEffort, resolveModelForProvider } from "./config.js"; import type { PauseGate } from "./core/pause-gate.js"; import { pauseGate as defaultPauseGate } from "./core/pause-gate.js"; import { type HookPayload, type ResolvedHook, runHooks } from "./hooks.js"; @@ -100,6 +100,9 @@ export interface CacheFirstLoopOptions { prefix: ImmutablePrefix; tools?: ToolRegistry; model?: string; + /** ID of the current API provider, used to resolve model names for + * auto-escalation and fold-summary calls. Defaults to "deepseek". */ + providerId?: string; stream?: boolean; reasoningEffort?: ReasoningEffort; /** Per-turn output token cap passed as `max_tokens`. Undefined = no cap (server default). */ @@ -164,6 +167,10 @@ export class CacheFirstLoop { /** Files the model has read this session; gates edit_file / multi_edit so SEARCH text matches on-disk bytes. Cleared on fold / mechanical truncate (the model's byte-level view of the elided history is gone). In-memory only — naturally empty on resume. */ readonly readTracker = new ReadTracker(); + /** Resolved at construction so auto-escalation and fold-summary calls + * use the provider-specific model name. */ + readonly providerId: string; + // Mutable via configure() — slash commands in the TUI / library callers tweak // these mid-session so users don't have to restart. model: string; @@ -241,6 +248,7 @@ export class CacheFirstLoop { sessionPath: this.sessionName ? sessionPath(this.sessionName) : undefined, }); this.model = opts.model ?? "deepseek-v4-flash"; + this.providerId = opts.providerId ?? "deepseek"; this.reasoningEffort = opts.reasoningEffort ?? "high"; this.maxOutputTokens = opts.maxOutputTokens; this.budgetUsd = @@ -315,6 +323,9 @@ export class CacheFirstLoop { this.resumedMessageCount = 0; } + // Resolve the fold summary model to the provider-specific name. + const foldSummaryModel = resolveModelForProvider(this.providerId, "deepseek-v4-flash"); + this.context = new ContextManager({ client: this.client, log: this.log, @@ -326,6 +337,7 @@ export class CacheFirstLoop { getToolSpecs: () => this.prefix.toolSpecs, getFewShots: () => this.prefix.fewShots, onLogRewrite: () => this.readTracker.reset(), + summaryModel: foldSummaryModel, }); } @@ -950,10 +962,12 @@ export class CacheFirstLoop { try { callModel = this.model; + // Resolve canonical model → provider-specific name for the API call. + const apiModel = resolveModelForProvider(this.providerId, callModel); if (this.stream) { const result = yield* streamModelResponse({ client: this.client, - model: callModel, + model: apiModel, messages, toolSpecs, signal, @@ -967,11 +981,11 @@ export class CacheFirstLoop { usage = result.usage; } else { const resp = await this.client.chat({ - model: callModel, + model: apiModel, messages, tools: toolSpecs.length ? toolSpecs : undefined, signal, - thinking: thinkingModeForModel(callModel), + thinking: thinkingModeForModel(apiModel), reasoningEffort: this.reasoningEffort, maxTokens: this.maxOutputTokens, }); diff --git a/src/loop/errors.ts b/src/loop/errors.ts index fd277abeb..f649fa1cf 100644 --- a/src/loop/errors.ts +++ b/src/loop/errors.ts @@ -77,6 +77,12 @@ export function isDeepSeekHost(baseUrl: string | undefined | null): boolean { } } +/** 检测是否为第三方提供商(非 DeepSeek 官方) */ +export function isThirdPartyProvider(baseUrl: string | undefined | null): boolean { + if (!baseUrl) return false; + return !isDeepSeekHost(baseUrl); +} + function is5xxStatus(status: string): boolean { return status === "500" || status === "502" || status === "503" || status === "504"; } diff --git a/src/tools/subagent.ts b/src/tools/subagent.ts index 27e9d71bf..44d8800ee 100644 --- a/src/tools/subagent.ts +++ b/src/tools/subagent.ts @@ -57,6 +57,8 @@ export interface SpawnSubagentOptions { system: string; task: string; model?: string; + /** Provider ID for resolving model names in subagent API calls. Defaults to "deepseek". */ + providerId?: string; maxResultChars?: number; sink?: SubagentSink; /** Forwarded into the child loop so parent Esc cancels nested work. */ @@ -89,6 +91,8 @@ export interface SubagentToolOptions { defaultSystem?: string; projectRoot?: string; defaultModel?: string; + /** Provider ID for resolving model names in subagent API calls. Defaults to "deepseek". */ + providerId?: string; maxResultChars?: number; sink?: SubagentSink; /** Fires once per spawn, after `spawnSubagent` returns and before its result is formatted for the parent. Bind a `SubagentTelemetry.record` here for automatic distillation capture. */ @@ -211,6 +215,7 @@ export async function spawnSubagent(opts: SpawnSubagentOptions): Promise Date: Sat, 30 May 2026 10:35:13 +0800 Subject: [PATCH 2/2] chore: update public-api snapshot for new exports --- tests/public-api.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/public-api.test.ts b/tests/public-api.test.ts index fe5b17981..ca4f9d71a 100644 --- a/tests/public-api.test.ts +++ b/tests/public-api.test.ts @@ -288,6 +288,7 @@ const PUBLIC_API: readonly string[] = [ "loadMetasoApiKey", "loadOllamaApiKey", "loadPerplexityApiKey", + "loadResolvedModel", "loadSessionMessages", "matchesTool", "memoryEnabled", @@ -324,6 +325,7 @@ const PUBLIC_API: readonly string[] = [ "repairTruncatedJson", "replayFromFile", "resolveExecutable", + "resolveModelForProvider", "resolveProjectMemoryWritePath", "restoreSnapshots", "runCommand",