feat(opencode): 新增 OpenCode 工具配置支持#16
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds the OpenCodeTool to the project, providing a comprehensive implementation for managing OpenCode CLI configurations, including model selection flows and cross-platform environment variable persistence. The review feedback correctly identifies a security vulnerability on Windows where execSync usage could lead to command injection via shell expansion; the reviewer recommends refactoring to execFileSync to mitigate this risk. Other feedback includes improving UI robustness by preventing the clearing of warning messages and removing dead code resulting from the suggested refactor.
|
|
||
| function writeEnvVar(name: string, value: string): EnvWriteResult { | ||
| if (process.platform === 'win32') { | ||
| execSync(`setx ${name} "${escapeWindowsValue(value)}"`, { stdio: 'pipe' }); |
There was a problem hiding this comment.
在 Windows 上使用 execSync 配合字符串模板执行 setx 时,如果 API Key 中包含 % 等字符,会被 cmd.exe 尝试进行变量扩展,导致写入的值不正确。建议改用 execFileSync 并传入参数数组,这样可以绕过 shell 的解析,更加安全可靠。同时,使用 execFileSync 后不再需要手动调用 escapeWindowsValue。
| execSync(`setx ${name} "${escapeWindowsValue(value)}"`, { stdio: 'pipe' }); | |
| execFileSync('setx', [name, value], { stdio: 'pipe' }); |
| import fs from 'node:fs'; | ||
| import path from 'node:path'; | ||
| import os from 'node:os'; | ||
| import { execSync } from 'node:child_process'; |
| function removeEnvVar(name: string): EnvWriteResult { | ||
| if (process.platform === 'win32') { | ||
| try { | ||
| execSync(`reg delete "HKCU\\Environment" /v ${name} /f`, { stdio: 'pipe' }); |
| function escapeWindowsValue(value: string): string { | ||
| return value.replace(/"/g, '\\"'); | ||
| } |
| if (models.length === 0) { | ||
| uiRenderer.renderWarning(t('model_no_models')); | ||
| } | ||
|
|
||
| uiRenderer.renderHeader(); |
There was a problem hiding this comment.
uiRenderer.renderHeader() 通常会清空控制台屏幕。如果在第 30 行渲染了「未获取到模型」的警告,紧接着调用 renderHeader() 会导致该警告被立即清除,用户可能看不见。建议将模型列表为空的检查移动到 renderHeader() 之后。
| if (models.length === 0) { | |
| uiRenderer.renderWarning(t('model_no_models')); | |
| } | |
| uiRenderer.renderHeader(); | |
| uiRenderer.renderHeader(); | |
| if (models.length === 0) { | |
| uiRenderer.renderWarning(t('model_no_models')); | |
| } |
| config.model = model; | ||
| } | ||
|
|
||
| const provider = (isPlainObject(config.provider) ? config.provider : {}) as Record<string, unknown>; |
There was a problem hiding this comment.
buildOpenCodeConfig() unconditionally replaces provider.anthropic. That means anyone who already uses OpenCode with their own Anthropic provider settings loses that config on load, and removeManagedOpenCodeConfig() later deletes the whole block instead of restoring it. This PR says it preserves user-defined provider fields, but the built-in anthropic provider is a destructive exception.
| function parseJson(content: string): Record<string, unknown> { | ||
| if (!content.trim()) return {}; | ||
| try { | ||
| const parsed = JSON.parse(content); |
There was a problem hiding this comment.
OpenCode supports JSONC configs, but this helper parses the existing file with plain JSON.parse(). Any valid config that contains comments or trailing commas will be treated as {}, so loadConfig()/unloadConfig() rewrites the file and drops the user's non-managed settings instead of preserving them.
- 新增 OpenCodeTool 实现 ITool 接口,配置写入 ~/.config/opencode/opencode.json - 采用统一自定义 provider qiniu(@ai-sdk/openai-compatible 适配器),覆盖 deepseek/qwen/anthropic 等任意前缀的模型;baseURL 自动补 /v1 - API Key 明文写入配置文件,目录权限 0o700、文件权限 0o600 兜底(对齐 Claude Code / Codex 行为) - 用 provider.qiniu.npm 字段作为托管标志,避免误删用户手写的同名 provider;卸载时只删 qiniu/-前缀的 model - 新增单选模型流程 runOpenCodeModelSelectionFlow,复用七牛 /v1/models 列表 - 扩展 ModelConfig 增加 opencodeModel 字段 - 中英文 i18n 同步新增翻译键 - 单测覆盖配置生成、幂等累积 models、用户 provider 保护、model 篡改保护
3aa3eac to
5582712
Compare
renderHeader() 内部会清屏,原本放在 renderWarning 之后会立即清掉 "未获取到模型" 提示,用户看不到。改为先 renderHeader 再 renderWarning。
|
/review |
|
@S0ngRu1 Thanks for the trigger request. This repository currently enforces member-only triggers, and your account does not have repository membership access, so xgopilot cannot run for this request. To proceed, you can:
|
|
/review |
|
@S0ngRu1 Thanks for the trigger request. This repository currently enforces member-only triggers, and your account does not have repository membership access, so xgopilot cannot run for this request. To proceed, you can:
|
Description
新增对 OpenCode 编码助手的配置支持,对应 #8。
工具采用统一的自定义 provider
qiniu(基于@ai-sdk/openai-compatible适配器)接入七牛端点,配置写入~/.config/opencode/opencode.json,目录权限0o700、文件权限0o600兜底。API Key 明文落盘,与 Claude Code / Codex 工具的策略保持一致。模型采用多选流程(checkbox),从七牛/v1/models列表中勾选若干模型写入provider.qiniu.models,方便在 OpenCode 的/models列表中切换;OpenCode 顶层model字段自动取首个选中的模型并加qiniu/前缀路由到该 provider,覆盖 deepseek / qwen / anthropic 等任意前缀的模型。Changes
OpenCodeTool实现ITool接口(src/lib/tools/opencode-tool.ts),管理model+provider.qiniu两个键路径,保留用户自定义的其他 provider / agent / mcp 字段provider.qiniu.npm === '@ai-sdk/openai-compatible'作为托管标志,避免误删用户手写的同名 provider;卸载时只删以qiniu/前缀开头的model,用户手动改成openai/gpt-4等情况不会被清理baseURL自动剥离尾部斜杠并补/v1,符合 OpenAI 兼容协议要求provider.qiniu.models在多次写入时累积,并自动补全$schema字段loadConfig通过marketModelService.fetchModels校验已配置的模型是否仍在七牛模型市场,命中部分缺失时跳过并 warning 提示,全部缺失时抛错引导重新配置runOpenCodeModelSelectionFlow多选模型流程(src/lib/wizard/flows/opencode-model-selection-flow.ts),检测到已有配置时先询问是否复用,避免每次都重新勾选;模型列表附带 tool_call / images / reasoning 能力标签renderHeader清屏覆盖ModelConfig/Config扩展opencodeModels: string[](多选结果)与opencodeModel: string(向后兼容的首选项)两个字段ToolManager注册OpenCodeTool,别名ocopencode_select_models/opencode_reuse_previous_selection/opencode_models_unavailable等)models、用户 provider 与 agent/mcp 字段保护、model字段被用户改写后的边界保护、baseURL尾斜杠归一化、$schema自动补全、空 / 非法 JSON 容错Checklist