diff --git a/package.json b/package.json index a019d96eb..218e99287 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "better-sqlite3-multiple-ciphers": "12.4.1", "cheerio": "^1.1.2", "compare-versions": "^6.1.1", + "cross-spawn": "^7.0.6", "diff": "^7.0.0", "electron-log": "^5.4.3", "electron-store": "^8.2.0", diff --git a/src/main/presenter/configPresenter/acpConfHelper.ts b/src/main/presenter/configPresenter/acpConfHelper.ts index f56d1eb86..f1b24edaf 100644 --- a/src/main/presenter/configPresenter/acpConfHelper.ts +++ b/src/main/presenter/configPresenter/acpConfHelper.ts @@ -35,7 +35,7 @@ const BUILTIN_TEMPLATES: Record = { name: DEFAULT_PROFILE_NAME, command: 'npx', args: ['-y', '@zed-industries/claude-code-acp'], - env: { ANTHROPIC_API_KEY: '' } + env: {} }) }, 'codex-acp': { @@ -44,7 +44,7 @@ const BUILTIN_TEMPLATES: Record = { name: DEFAULT_PROFILE_NAME, command: 'npx', args: ['-y', '@zed-industries/codex-acp'], - env: { OPENAI_API_KEY: '' } + env: {} }) } } @@ -52,6 +52,7 @@ const BUILTIN_TEMPLATES: Record = { type InternalStore = Partial & { agents?: AcpAgentConfig[] builtinsVersion?: string + useBuiltinRuntime?: boolean } const deepClone = (value: T): T => { @@ -71,6 +72,7 @@ export class AcpConfHelper { builtins: [], customs: [], enabled: false, + useBuiltinRuntime: false, version: ACP_STORE_VERSION } }) @@ -92,6 +94,14 @@ export class AcpConfHelper { return true } + getUseBuiltinRuntime(): boolean { + return Boolean(this.store.get('useBuiltinRuntime')) + } + + setUseBuiltinRuntime(enabled: boolean): void { + this.store.set('useBuiltinRuntime', enabled) + } + getEnabledAgents(): AcpAgentConfig[] { const data = this.getData() if (!data.enabled) { diff --git a/src/main/presenter/configPresenter/index.ts b/src/main/presenter/configPresenter/index.ts index 8d8baba3a..87ea7a72d 100644 --- a/src/main/presenter/configPresenter/index.ts +++ b/src/main/presenter/configPresenter/index.ts @@ -947,6 +947,14 @@ export class ConfigPresenter implements IConfigPresenter { this.notifyAcpAgentsChanged() } + async getAcpUseBuiltinRuntime(): Promise { + return this.acpConfHelper.getUseBuiltinRuntime() + } + + async setAcpUseBuiltinRuntime(enabled: boolean): Promise { + this.acpConfHelper.setUseBuiltinRuntime(enabled) + } + // ===================== ACP configuration methods ===================== async getAcpAgents(): Promise { return this.acpConfHelper.getEnabledAgents() diff --git a/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts b/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts index 520643204..94150cd0a 100644 --- a/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts +++ b/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts @@ -1,4 +1,5 @@ -import { spawn, type ChildProcessWithoutNullStreams, execSync } from 'child_process' +import spawn from 'cross-spawn' +import type { ChildProcessWithoutNullStreams } from 'child_process' import { Readable, Writable } from 'node:stream' import { app } from 'electron' import * as fs from 'fs' @@ -22,6 +23,9 @@ export interface AcpProcessHandle extends AgentProcessHandle { interface AcpProcessManagerOptions { providerId: string + getUseBuiltinRuntime: () => Promise + getNpmRegistry?: () => Promise + getUvRegistry?: () => Promise } export type SessionNotificationHandler = (notification: schema.SessionNotification) => void @@ -35,6 +39,14 @@ interface SessionListenerEntry { handlers: Set } +/** + * Check if running in Electron environment. + * Reference: @modelcontextprotocol/sdk/client/stdio.js + */ +function isElectron(): boolean { + return 'type' in process +} + interface PermissionResolverEntry { agentId: string resolver: PermissionResolver @@ -42,6 +54,9 @@ interface PermissionResolverEntry { export class AcpProcessManager implements AgentProcessManager { private readonly providerId: string + private readonly getUseBuiltinRuntime: () => Promise + private readonly getNpmRegistry?: () => Promise + private readonly getUvRegistry?: () => Promise private readonly handles = new Map() private readonly pendingHandles = new Map>() private readonly sessionListeners = new Map() @@ -53,6 +68,9 @@ export class AcpProcessManager implements AgentProcessManager { @@ -152,7 +170,7 @@ export class AcpProcessManager implements AgentProcessManager { - const child = this.spawnAgentProcess(agent) + const child = await this.spawnAgentProcess(agent) const stream = this.createAgentStream(child) const client = this.createClientProxy() const connection = new ClientSideConnection(() => client, stream) @@ -262,72 +280,68 @@ export class AcpProcessManager implements AgentProcessManager { + return process.env[varName] || match + }) + + // Handle simple $VAR format (without braces) + expandedPath = expandedPath.replace(/\$([A-Z_][A-Z0-9_]*)/g, (match, varName) => { + return process.env[varName] || match + }) + + return expandedPath + } + + private async spawnAgentProcess(agent: AcpAgentConfig): Promise { // Initialize runtime paths if not already done this.setupRuntimes() + // Get useBuiltinRuntime configuration + const useBuiltinRuntime = await this.getUseBuiltinRuntime() + + // Validate command + if (!agent.command || agent.command.trim().length === 0) { + throw new Error(`[ACP] Invalid command for agent ${agent.id}: command is empty`) + } + + // Handle path expansion (including ~ and environment variables) + let expandedCommand = this.expandPath(agent.command) + let expandedArgs = (agent.args ?? []).map((arg) => + typeof arg === 'string' ? this.expandPath(arg) : arg + ) + // Replace command with runtime version if needed - const processedCommand = this.replaceWithRuntimeCommand(agent.command) - const processedArgs = (agent.args ?? []).map((arg) => this.replaceWithRuntimeCommand(arg)) + const processedCommand = this.replaceWithRuntimeCommand(expandedCommand, useBuiltinRuntime) - // Prepare environment variables - const mergedEnv = agent.env ? { ...process.env, ...agent.env } : { ...process.env } + // Validate processed command + if (!processedCommand || processedCommand.trim().length === 0) { + throw new Error( + `[ACP] Invalid processed command for agent ${agent.id}: "${agent.command}" -> empty` + ) + } - // Add runtime paths to PATH for fallback - const existingPaths: string[] = [] - Object.entries(mergedEnv).forEach(([key, value]) => { - if (value !== undefined && ['PATH', 'Path', 'path'].includes(key)) { - existingPaths.push(value) - } + // Log command processing for debugging + console.info(`[ACP] Spawning process for agent ${agent.id}:`, { + originalCommand: agent.command, + processedCommand, + args: agent.args ?? [] }) - const allPaths = [...existingPaths] - if (process.platform === 'win32') { - if (this.uvRuntimePath) { - allPaths.unshift(this.uvRuntimePath) - } - if (this.nodeRuntimePath) { - allPaths.unshift(this.nodeRuntimePath) + if (processedCommand !== agent.command) { + console.info( + `[ACP] Command replaced for agent ${agent.id}: "${agent.command}" -> "${processedCommand}"` + ) + } + + // Use expanded args + const processedArgs = expandedArgs + + // Determine if it's Node.js/Bun/UV related command + const isNodeCommand = ['node', 'npm', 'npx', 'bun', 'uv', 'uvx'].some( + (cmd) => + processedCommand.includes(cmd) || + processedArgs.some((arg) => typeof arg === 'string' && arg.includes(cmd)) + ) + + // Define allowed environment variables whitelist for Node.js/Bun/UV commands + const allowedEnvVars = [ + 'PATH', + 'path', + 'Path', + 'npm_config_registry', + 'npm_config_cache', + 'npm_config_prefix', + 'npm_config_tmp', + 'NPM_CONFIG_REGISTRY', + 'NPM_CONFIG_CACHE', + 'NPM_CONFIG_PREFIX', + 'NPM_CONFIG_TMP', + 'ANTHROPIC_BASE_URL', + 'ANTHROPIC_AUTH_TOKEN', + 'ANTHROPIC_MODEL', + 'OPENAI_BASE_URL', + 'OPENAI_API_KEY' + ] + + const HOME_DIR = app.getPath('home') + const env: Record = {} + let pathKey = process.platform === 'win32' ? 'Path' : 'PATH' + let pathValue = '' + + if (isNodeCommand) { + // Node.js/Bun/UV commands use whitelist processing + if (process.env) { + const existingPaths: string[] = [] + + // Collect all PATH-related values + Object.entries(process.env).forEach(([key, value]) => { + if (value !== undefined) { + if (['PATH', 'Path', 'path'].includes(key)) { + existingPaths.push(value) + } else if (allowedEnvVars.includes(key) && !['PATH', 'Path', 'path'].includes(key)) { + env[key] = value + } + } + }) + + // Get default paths + const defaultPaths = this.getDefaultPaths(HOME_DIR) + + // Merge all paths + const allPaths = [...existingPaths, ...defaultPaths] + // Add runtime paths + if (process.platform === 'win32') { + // Windows platform only adds node and uv paths + if (this.uvRuntimePath) { + allPaths.unshift(this.uvRuntimePath) + console.info(`[ACP] Added UV runtime path to PATH: ${this.uvRuntimePath}`) + } + if (this.nodeRuntimePath) { + allPaths.unshift(this.nodeRuntimePath) + console.info(`[ACP] Added Node runtime path to PATH: ${this.nodeRuntimePath}`) + } + } else { + // Other platforms priority: bun > node > uv + if (this.uvRuntimePath) { + allPaths.unshift(this.uvRuntimePath) + console.info(`[ACP] Added UV runtime path to PATH: ${this.uvRuntimePath}`) + } + if (this.nodeRuntimePath) { + const nodeBinPath = path.join(this.nodeRuntimePath, 'bin') + allPaths.unshift(nodeBinPath) + console.info(`[ACP] Added Node bin path to PATH: ${nodeBinPath}`) + } + if (this.bunRuntimePath) { + allPaths.unshift(this.bunRuntimePath) + console.info(`[ACP] Added Bun runtime path to PATH: ${this.bunRuntimePath}`) + } + } + + // Normalize and set PATH + const normalized = this.normalizePathEnv(allPaths) + pathKey = normalized.key + pathValue = normalized.value + env[pathKey] = pathValue } } else { - if (this.uvRuntimePath) { - allPaths.unshift(this.uvRuntimePath) + // Non Node.js/Bun/UV commands, preserve all system environment variables, only supplement PATH + Object.entries(process.env).forEach(([key, value]) => { + if (value !== undefined) { + env[key] = value + } + }) + + // Supplement PATH + const existingPaths: string[] = [] + if (env.PATH) { + existingPaths.push(env.PATH) } - if (this.nodeRuntimePath) { - allPaths.unshift(path.join(this.nodeRuntimePath, 'bin')) + if (env.Path) { + existingPaths.push(env.Path) + } + + // Get default paths + const defaultPaths = this.getDefaultPaths(HOME_DIR) + + // Merge all paths + const allPaths = [...existingPaths, ...defaultPaths] + // Add runtime paths + if (process.platform === 'win32') { + // Windows platform only adds node and uv paths + if (this.uvRuntimePath) { + allPaths.unshift(this.uvRuntimePath) + console.info(`[ACP] Added UV runtime path to PATH: ${this.uvRuntimePath}`) + } + if (this.nodeRuntimePath) { + allPaths.unshift(this.nodeRuntimePath) + console.info(`[ACP] Added Node runtime path to PATH: ${this.nodeRuntimePath}`) + } + } else { + // Other platforms priority: bun > node > uv + if (this.uvRuntimePath) { + allPaths.unshift(this.uvRuntimePath) + console.info(`[ACP] Added UV runtime path to PATH: ${this.uvRuntimePath}`) + } + if (this.nodeRuntimePath) { + const nodeBinPath = path.join(this.nodeRuntimePath, 'bin') + allPaths.unshift(nodeBinPath) + console.info(`[ACP] Added Node bin path to PATH: ${nodeBinPath}`) + } + if (this.bunRuntimePath) { + allPaths.unshift(this.bunRuntimePath) + console.info(`[ACP] Added Bun runtime path to PATH: ${this.bunRuntimePath}`) + } + } + + // Normalize and set PATH + const normalized = this.normalizePathEnv(allPaths) + pathKey = normalized.key + pathValue = normalized.value + env[pathKey] = pathValue + } + + // Add custom environment variables + if (agent.env) { + Object.entries(agent.env).forEach(([key, value]) => { + if (value !== undefined) { + // If it's a PATH-related variable, merge into main PATH + if (['PATH', 'Path', 'path'].includes(key)) { + const currentPathKey = process.platform === 'win32' ? 'Path' : 'PATH' + const separator = process.platform === 'win32' ? ';' : ':' + env[currentPathKey] = env[currentPathKey] + ? `${value}${separator}${env[currentPathKey]}` + : value + } else { + env[key] = value + } + } + }) + } + + // Add registry environment variables when using builtin runtime + if (useBuiltinRuntime) { + if (this.getNpmRegistry) { + const npmRegistry = await this.getNpmRegistry() + if (npmRegistry) { + env.npm_config_registry = npmRegistry + } } - if (this.bunRuntimePath) { - allPaths.unshift(this.bunRuntimePath) + + if (this.getUvRegistry) { + const uvRegistry = await this.getUvRegistry() + if (uvRegistry) { + env.UV_DEFAULT_INDEX = uvRegistry + env.PIP_INDEX_URL = uvRegistry + } } } - const { key, value } = this.normalizePathEnv(allPaths) - mergedEnv[key] = value + const mergedEnv = env - return spawn(processedCommand, processedArgs, { - env: mergedEnv, - stdio: ['pipe', 'pipe', 'pipe'] + console.info(`[ACP] Environment variables for agent ${agent.id}:`, { + pathKey, + pathValue, + hasCustomEnv: !!agent.env, + customEnvKeys: agent.env ? Object.keys(agent.env) : [] }) + + // Determine working directory (default to current working directory) + let cwd = process.cwd() + // Validate cwd exists + if (!fs.existsSync(cwd)) { + console.warn(`[ACP] Working directory does not exist: ${cwd}, using fallback`) + cwd = process.platform === 'win32' ? 'C:\\' : '/' + } + + console.info(`[ACP] Spawning process with options:`, { + command: processedCommand, + args: processedArgs, + cwd, + platform: process.platform + }) + + const child = spawn(processedCommand, processedArgs, { + env: mergedEnv, + cwd, + stdio: ['pipe', 'pipe', 'pipe'], + shell: false, + windowsHide: process.platform === 'win32' && isElectron() + }) as ChildProcessWithoutNullStreams + + console.info(`[ACP] Process spawned successfully for agent ${agent.id}, PID: ${child.pid}`) + + return child } private createAgentStream(child: ChildProcessWithoutNullStreams): Stream { diff --git a/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts b/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts index 9a6f9dff3..96cae9638 100644 --- a/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts +++ b/src/main/presenter/llmProviderPresenter/providers/acpProvider.ts @@ -27,6 +27,7 @@ import { AcpContentMapper } from '../agent/acpContentMapper' import { AcpMessageFormatter } from '../agent/acpMessageFormatter' import { AcpSessionPersistence } from '../agent/acpSessionPersistence' import { nanoid } from 'nanoid' +import { presenter } from '@/presenter' type EventQueue = { push: (event: LLMCoreStreamEvent | null) => void @@ -68,7 +69,20 @@ export class AcpProvider extends BaseAgentProvider< ) { super(provider, configPresenter) this.sessionPersistence = sessionPersistence - this.processManager = new AcpProcessManager({ providerId: provider.id }) + this.processManager = new AcpProcessManager({ + providerId: provider.id, + getUseBuiltinRuntime: () => this.configPresenter.getAcpUseBuiltinRuntime(), + getNpmRegistry: async () => { + // Get npm registry from MCP presenter's server manager + // This will use the fastest registry from speed test + return presenter.mcpPresenter.getNpmRegistry?.() ?? null + }, + getUvRegistry: async () => { + // Get uv registry from MCP presenter's server manager + // This will use the fastest registry from speed test + return presenter.mcpPresenter.getUvRegistry?.() ?? null + } + }) this.sessionManager = new AcpSessionManager({ providerId: provider.id, processManager: this.processManager, diff --git a/src/main/presenter/mcpPresenter/index.ts b/src/main/presenter/mcpPresenter/index.ts index d56711713..19f6a0707 100644 --- a/src/main/presenter/mcpPresenter/index.ts +++ b/src/main/presenter/mcpPresenter/index.ts @@ -1301,4 +1301,14 @@ export class McpPresenter implements IMCPPresenter { this.configPresenter.clearNpmRegistryCache?.() console.log('[MCP] NPM Registry cache cleared') } + + // Get npm registry (for ACP and other internal use) + getNpmRegistry(): string | null { + return this.serverManager.getNpmRegistry() + } + + // Get uv registry (for ACP and other internal use) + getUvRegistry(): string | null { + return this.serverManager.getUvRegistry() + } } diff --git a/src/renderer/settings/components/AcpSettings.vue b/src/renderer/settings/components/AcpSettings.vue index 176c27556..067b6aa92 100644 --- a/src/renderer/settings/components/AcpSettings.vue +++ b/src/renderer/settings/components/AcpSettings.vue @@ -16,6 +16,21 @@ @update:model-value="handleToggle" /> +
+
+
{{ t('settings.acp.useBuiltinRuntimeTitle') }}
+

+ {{ t('settings.acp.useBuiltinRuntimeDescription') }} +

+
+ +
@@ -260,6 +275,8 @@ const llmProviderPresenter = usePresenter('llmproviderPresenter') const acpEnabled = ref(false) const toggling = ref(false) +const useBuiltinRuntime = ref(false) +const togglingUseBuiltinRuntime = ref(false) const loading = ref(false) const builtins = ref([]) const customAgents = ref([]) @@ -355,6 +372,28 @@ const loadAcpEnabled = async () => { } } +const loadAcpUseBuiltinRuntime = async () => { + try { + useBuiltinRuntime.value = await configPresenter.getAcpUseBuiltinRuntime() + } catch (error) { + handleError(error) + } +} + +const handleUseBuiltinRuntimeToggle = async (enabled: boolean) => { + if (togglingUseBuiltinRuntime.value) return + togglingUseBuiltinRuntime.value = true + try { + await configPresenter.setAcpUseBuiltinRuntime(enabled) + useBuiltinRuntime.value = enabled + } catch (error) { + handleError(error) + useBuiltinRuntime.value = !enabled + } finally { + togglingUseBuiltinRuntime.value = false + } +} + const loadAcpData = async () => { if (!acpEnabled.value) { builtins.value = [] @@ -617,6 +656,7 @@ watch( onMounted(async () => { await loadAcpEnabled() + await loadAcpUseBuiltinRuntime() await loadAcpData() }) diff --git a/src/renderer/src/i18n/en-US/settings.json b/src/renderer/src/i18n/en-US/settings.json index e19f029d0..a20cf5e02 100644 --- a/src/renderer/src/i18n/en-US/settings.json +++ b/src/renderer/src/i18n/en-US/settings.json @@ -800,6 +800,8 @@ "description": "Manage local Agent Client Protocol agents started by DeepChat.", "enabledTitle": "Enable ACP", "enabledDescription": "When enabled, configured ACP agents will appear as models in the selector.", + "useBuiltinRuntimeTitle": "Use DeepChat Built-in Runtime", + "useBuiltinRuntimeDescription": "When enabled, bypasses system node and uv commands, using DeepChat's bundled versions.", "enableToAccess": "Enable ACP to configure agents.", "addCustomAgent": "Add Custom Agent", "customEmpty": "No custom agents yet.", diff --git a/src/renderer/src/i18n/fa-IR/settings.json b/src/renderer/src/i18n/fa-IR/settings.json index 5e2b29d9c..082459b77 100644 --- a/src/renderer/src/i18n/fa-IR/settings.json +++ b/src/renderer/src/i18n/fa-IR/settings.json @@ -832,6 +832,8 @@ "enableToAccess": "لطفاً ابتدا ACP را فعال کنید تا به پیکربندی پروکسی دسترسی پیدا کنید", "enabledDescription": "پس از فعال‌سازی، پروکسی ACP پیکربندی شده به عنوان یک مدل در انتخاب‌گر مدل ظاهر خواهد شد.", "enabledTitle": "فعال کردن ACP", + "useBuiltinRuntimeTitle": "استفاده از محیط اجرای داخلی DeepChat", + "useBuiltinRuntimeDescription": "هنگام فعال‌سازی، دستورات node و uv سیستم را دور می‌زند و از نسخه‌های همراه DeepChat استفاده می‌کند.", "env": "متغیر محیطی", "envKeyPlaceholder": "کلید", "envValuePlaceholder": "مقدار", diff --git a/src/renderer/src/i18n/fr-FR/settings.json b/src/renderer/src/i18n/fr-FR/settings.json index 669ce4511..ad6c641e8 100644 --- a/src/renderer/src/i18n/fr-FR/settings.json +++ b/src/renderer/src/i18n/fr-FR/settings.json @@ -832,6 +832,8 @@ "enableToAccess": "Veuillez d'abord activer l'ACP pour accéder à la configuration du proxy.", "enabledDescription": "Une fois activé, le proxy ACP configuré apparaîtra comme modèle dans le sélecteur de modèles.", "enabledTitle": "Activer ACP", + "useBuiltinRuntimeTitle": "Utiliser l'environnement d'exécution intégré de DeepChat", + "useBuiltinRuntimeDescription": "Lorsqu'il est activé, contourne les commandes node et uv du système, en utilisant les versions fournies avec DeepChat.", "env": "Variable d'environnement", "envKeyPlaceholder": "CLÉ", "envValuePlaceholder": "VALEUR", diff --git a/src/renderer/src/i18n/ja-JP/settings.json b/src/renderer/src/i18n/ja-JP/settings.json index 278fab2d8..ded9c7df1 100644 --- a/src/renderer/src/i18n/ja-JP/settings.json +++ b/src/renderer/src/i18n/ja-JP/settings.json @@ -832,6 +832,8 @@ "enableToAccess": "まずACPを有効にしてプロキシ設定にアクセスしてください", "enabledDescription": "有効にすると、設定されたACPプロキシがモデルセレクターにモデルとして表示されます。", "enabledTitle": "ACPを有効にする", + "useBuiltinRuntimeTitle": "DeepChat内蔵ランタイムを使用", + "useBuiltinRuntimeDescription": "有効にすると、システムのnodeとuvコマンドをバイパスし、DeepChatにバンドルされたバージョンを使用します。", "env": "環境変数", "envKeyPlaceholder": "キー", "envValuePlaceholder": "価値", diff --git a/src/renderer/src/i18n/ko-KR/settings.json b/src/renderer/src/i18n/ko-KR/settings.json index 7e3841ed4..c4d0ef0ce 100644 --- a/src/renderer/src/i18n/ko-KR/settings.json +++ b/src/renderer/src/i18n/ko-KR/settings.json @@ -832,6 +832,8 @@ "enableToAccess": "프록시 구성에 액세스하려면 먼저 ACP를 활성화하세요.", "enabledDescription": "활성화되면 구성된 ACP 에이전트가 모델 선택기에서 모델로 나타납니다.", "enabledTitle": "ACP 활성화", + "useBuiltinRuntimeTitle": "DeepChat 내장 런타임 사용", + "useBuiltinRuntimeDescription": "활성화하면 시스템의 node 및 uv 명령을 우회하여 DeepChat에 번들로 제공되는 버전을 사용합니다.", "env": "환경 변수", "envKeyPlaceholder": "열쇠", "envValuePlaceholder": "값", diff --git a/src/renderer/src/i18n/pt-BR/settings.json b/src/renderer/src/i18n/pt-BR/settings.json index 4008057cd..1a421f7e5 100644 --- a/src/renderer/src/i18n/pt-BR/settings.json +++ b/src/renderer/src/i18n/pt-BR/settings.json @@ -832,6 +832,8 @@ "enableToAccess": "Ative primeiro o ACP para acessar a configuração do proxy", "enabledDescription": "Quando habilitado, o agente ACP configurado aparecerá como modelo no seletor de modelo.", "enabledTitle": "Habilitar ACP", + "useBuiltinRuntimeTitle": "Usar ambiente de execução integrado do DeepChat", + "useBuiltinRuntimeDescription": "Quando habilitado, ignora os comandos node e uv do sistema, usando as versões incluídas com o DeepChat.", "env": "variáveis ​​de ambiente", "envKeyPlaceholder": "CHAVE", "envValuePlaceholder": "VALOR", diff --git a/src/renderer/src/i18n/ru-RU/settings.json b/src/renderer/src/i18n/ru-RU/settings.json index 44185e61b..984f7da9f 100644 --- a/src/renderer/src/i18n/ru-RU/settings.json +++ b/src/renderer/src/i18n/ru-RU/settings.json @@ -832,6 +832,8 @@ "enableToAccess": "Пожалуйста, сначала включите ACP для доступа к настройкам прокси.", "enabledDescription": "После включения настроенный прокси ACP будет отображаться в селекторе моделей как модель.", "enabledTitle": "Включить ACP", + "useBuiltinRuntimeTitle": "Использовать встроенную среду выполнения DeepChat", + "useBuiltinRuntimeDescription": "При включении обходит системные команды node и uv, используя версии, входящие в состав DeepChat.", "env": "Переменная окружения", "envKeyPlaceholder": "КЛЮЧ", "envValuePlaceholder": "ЗНАЧЕНИЕ", diff --git a/src/renderer/src/i18n/zh-CN/settings.json b/src/renderer/src/i18n/zh-CN/settings.json index 985899832..92cf2b642 100644 --- a/src/renderer/src/i18n/zh-CN/settings.json +++ b/src/renderer/src/i18n/zh-CN/settings.json @@ -800,6 +800,8 @@ "description": "管理由 DeepChat 启动的本地 ACP Agent。", "enabledTitle": "启用 ACP", "enabledDescription": "启用后,配置好的 ACP Agent 会出现在模型列表中。", + "useBuiltinRuntimeTitle": "使用 DeepChat 内置环境", + "useBuiltinRuntimeDescription": "勾选后会绕过系统的 node 和 uv 命令,使用 DeepChat 自带的版本。", "enableToAccess": "启用 ACP 后才能配置 Agent。", "addCustomAgent": "新增自定义 Agent", "customEmpty": "还没有自定义 Agent。", diff --git a/src/renderer/src/i18n/zh-HK/settings.json b/src/renderer/src/i18n/zh-HK/settings.json index 8e7d6b8b3..1a8f94611 100644 --- a/src/renderer/src/i18n/zh-HK/settings.json +++ b/src/renderer/src/i18n/zh-HK/settings.json @@ -826,6 +826,8 @@ "description": "管理由 DeepChat 啟動嘅本地 ACP Agent。", "enabledTitle": "啟用 ACP", "enabledDescription": "啟用後,設定好嘅 ACP Agent 會出現在模型清單。", + "useBuiltinRuntimeTitle": "使用 DeepChat 內建環境", + "useBuiltinRuntimeDescription": "勾選後會繞過系統嘅 node 同 uv 命令,使用 DeepChat 自帶嘅版本。", "enableToAccess": "啟用 ACP 後先可以設定 Agent。", "addCustomAgent": "新增自訂 Agent", "customEmpty": "仲未有自訂 Agent。", diff --git a/src/renderer/src/i18n/zh-TW/settings.json b/src/renderer/src/i18n/zh-TW/settings.json index 30ae213a8..b50f41f7f 100644 --- a/src/renderer/src/i18n/zh-TW/settings.json +++ b/src/renderer/src/i18n/zh-TW/settings.json @@ -826,6 +826,8 @@ "description": "管理由 DeepChat 啟動的本地 ACP Agent。", "enabledTitle": "啟用 ACP", "enabledDescription": "啟用後,設定好的 ACP Agent 會出現在模型列表。", + "useBuiltinRuntimeTitle": "使用 DeepChat 內建環境", + "useBuiltinRuntimeDescription": "勾選後會繞過系統的 node 和 uv 命令,使用 DeepChat 自帶的版本。", "enableToAccess": "啟用 ACP 後才能設定 Agent。", "addCustomAgent": "新增自訂 Agent", "customEmpty": "還沒有自訂 Agent。", diff --git a/src/shared/types/presenters/legacy.presenters.d.ts b/src/shared/types/presenters/legacy.presenters.d.ts index a91e5d035..4cad317aa 100644 --- a/src/shared/types/presenters/legacy.presenters.d.ts +++ b/src/shared/types/presenters/legacy.presenters.d.ts @@ -498,6 +498,8 @@ export interface IConfigPresenter { // ACP configuration methods getAcpEnabled(): Promise setAcpEnabled(enabled: boolean): Promise + getAcpUseBuiltinRuntime(): Promise + setAcpUseBuiltinRuntime(enabled: boolean): Promise setAcpAgents(agents: AcpAgentConfig[]): Promise getAcpAgents(): Promise addAcpAgent(agent: Omit & { id?: string }): Promise @@ -1420,6 +1422,9 @@ export interface IMCPPresenter { setCustomNpmRegistry?(registry: string | undefined): Promise setAutoDetectNpmRegistry?(enabled: boolean): Promise clearNpmRegistryCache?(): Promise + // Get npm/uv registry for internal use (ACP, etc.) + getNpmRegistry?(): string | null + getUvRegistry?(): string | null // McpRouter marketplace listMcpRouterServers?(