@vibeapi/api-helper 0.0.35 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,30 +26,52 @@ npx --yes --registry https://npm.cnb.cool/vibeapi/npm/-/packages/ @vibeapi/cli@l
26
26
 
27
27
  ## 快速开始
28
28
 
29
+ 下面三种方式**三选一即可**,任选一种就已经配置完成 —— 不要按顺序依次执行。
30
+
31
+ ### 方式 A:一条命令直配(推荐)
32
+
33
+ 无需任何交互,粘贴即用;重复执行即换令牌。
34
+
29
35
  ```bash
30
- vibecli init # 向导:API Key → 选工具 → 配置该工具的模型(中文系统自动中文界面,非中文系统先选语言)
31
- vibecli status # 查看当前配置
36
+ vibecli setup --key sk-xxx
32
37
  ```
33
38
 
34
- 不想走向导?一条命令直配(重复执行即换令牌):
39
+ 拿到的是两个分组的令牌(如 Claude 分组 + GPT 分组),就各配各的:
35
40
 
36
41
  ```bash
37
- vibecli setup --key sk-xxx # 校验后按 Key 能力自动配置(能访问 Claude 模型配 Claude Code、能访问 GPT 模型配 Codex)
38
- vibecli setup --claudekey sk-a --codexkey sk-b # 双令牌各配各的(如 Claude 分组 + GPT 分组),存为工具专属 Key
42
+ vibecli setup --claudekey sk-a --codexkey sk-b
39
43
  ```
40
44
 
41
- 非交互(CI):
45
+ 配完直接运行 `claude` / `codex` 即可。
46
+
47
+ ### 方式 B:交互式向导
48
+
49
+ 想在菜单里挑工具和模型时用(中文系统自动中文界面,非中文系统先选语言):
50
+
51
+ ```bash
52
+ vibecli init
53
+ ```
54
+
55
+ ### 方式 C:CI / 脚本
42
56
 
43
57
  ```bash
44
58
  vibecli auth sk-xxx --non-interactive
45
59
  vibecli config claude-code --non-interactive
46
60
  ```
47
61
 
62
+ ### 配完之后
63
+
64
+ ```bash
65
+ vibecli status # 查看当前配置
66
+ vibecli config claude-code clear-model # 模型选择被钉住时,交还给工具自带默认(保留 API Key)
67
+ ```
68
+
48
69
  ## 能做什么
49
70
 
50
71
  - **多工具一键配置**:把当前 API Key / 模型注入 Claude Code、Codex、OpenClaw、Hermes、AtomCode(OpenCode 暂时下架,`vibecli config opencode` 仍可用)。
51
72
  - **按工具选模型**:使用默认 / 实时从 API 拉取 / 内置预设 / 手动输入;每个工具可用不同模型。
52
- - Claude Code 默认用 Claude 组合(sonnet 1M 变体 `claude-sonnet-5[1m]`,opus 1M 变体 `claude-opus-4-8[1m]`);Codex 默认 `gpt-5.6-sol`,其余工具默认 `gpt-5.5`。
73
+ - Claude Code 用默认模型时不写 `ANTHROPIC_*_MODEL` 环境变量,顶层 `model` `settings-template.json` 提供(当前为 `opus[1m]`);显式选择其它模型时会覆盖该模板值。Codex 默认 `gpt-5.6-sol`,其余工具默认 `gpt-5.5`。
74
+ - **不动你的其它配置**:只改本工具的 provider 字段,你的 hooks、MCP、插件、权限设置原样保留。
53
75
  - **改前自动备份 + 一键还原**:覆盖配置前先快照(每文件留 10 份),`vibecli config <tool> restore` 随时回退;还原本身也可再撤销。
54
76
  - **多 Profile + 可迁移凭据**:work/personal 各自独立;API Key 存在权限为 `0600` 的 `~/.vibecli/config.yaml`,与 Claude/Codex 本身的明文凭据方式一致,换机不会被主机绑定加密阻断。
55
77
  - **CI 友好**:所有命令支持 `--json` / `--non-interactive` / `--yes` / `--dry-run` / `--no-color`。
@@ -59,6 +81,7 @@ vibecli config claude-code --non-interactive
59
81
  ```bash
60
82
  vibecli config <tool> # 配置某工具(选模型 → 应用 / 移除 / 还原)
61
83
  vibecli config <tool> restore # 还原该工具到上次更改前
84
+ vibecli config <tool> clear-model # 清掉钉死的模型设置,回到工具自带默认(保留 API Key)
62
85
  vibecli config tool-model set codex gpt-5.6-sol # 单独给某工具指定模型
63
86
  vibecli config profile use work # 切换 profile
64
87
  vibecli doctor # 健康巡检
@@ -1 +1 @@
1
- import o from"inquirer";import e from"chalk";import{wizard as t}from"../lib/wizard.js";import{toolManager as n,SUPPORTED_TOOLS as i}from"../lib/tool-manager.js";import{i18n as l}from"../lib/i18n.js";import{configManager as r,DEFAULT_PROFILE_NAME as s}from"../lib/config.js";import{confirmAction as a,applyOrDryRun as c}from"../lib/confirm.js";import{isNonInteractive as f,shouldAutoConfirm as d}from"../lib/runtime-options.js";const g=/^[a-zA-Z0-9._-]+$/;function u(o,e){const t=(o||"").trim();if(!t||!g.test(t))throw new Error(l.t(`config.${e}_invalid_name`,{name:t||"<empty>"}));return t}function m(o,t){console.log("\n"+e.bold(o)),0!==t.length?t.forEach(o=>console.log(" "+o)):console.log(e.gray(" ("+l.t("config.empty_list")+")"))}function p(o,t){if("backups"===t){const t=n.listToolBackups(o).flatMap(o=>{const t=o.file.replace(/^.*[/\\]/,"");return o.backups.length?o.backups.map(o=>`${e.cyan(t)} ${e.gray(o.name.split(".bak-")[1]||o.name)}`):[`${e.cyan(t)} ${e.gray("("+l.t("config.empty_list")+")")}`]});return void m(l.t("config.tool_backups_title",{tool:i[o].displayName}),t)}const r=n.restorePrivateConfig(o).filter(o=>o.result.restored);0!==r.length?(console.log(e.green(l.t("config.config_restored",{tool:i[o].displayName}))),r.forEach(o=>console.log(e.gray(` ${o.file} ← ${o.result.from}`)))):console.log(e.yellow(l.t("config.restore_no_backup",{tool:i[o].displayName})))}async function _(s,c,f){if(!i[s])return void console.log(e.red(l.t("config.unknown_tool",{tool:s})));const d=(c[0]||"").toLowerCase();if("backups"!==d&&"list-backups"!==d){if("restore"===d){if(!await a({message:l.t("config.confirm_restore",{tool:i[s].displayName}),defaultValue:!1}))return;return void p(s,"restore")}if(!n.isToolInstalled(s)){if(f.nonInteractive)return void console.log(e.yellow(l.t("wizard.tool_not_installed",{tool:i[s].displayName})));console.log(e.yellow(l.t("wizard.tool_not_installed",{tool:i[s].displayName})));const{shouldInstall:t}=await o.prompt([{type:"confirm",name:"shouldInstall",message:l.t("wizard.install_tool_confirm"),default:!0}]);if(!t)return void console.log(e.yellow(l.t("wizard.install_skipped")));try{await n.installTool(s)}catch(o){return console.error(e.red(l.t("install.install_failed_detail"))),void(o instanceof Error&&o.message&&console.error(e.gray(o.message)))}}if(f.nonInteractive){const o=r.getApiKeyForTool(s);return o?(n.loadPrivateConfig(s,o,r.getModelsForTool(s)),void console.log(e.green(l.t("config.tool_config_loaded",{tool:i[s].displayName})))):void console.log(e.red(l.t("wizard.api_key_required")))}await t.showToolMenu(s)}else p(s,"backups")}export async function configCommand(t){const{positional:n,options:p}=function(o){const e=[],t={};(f()||d())&&(t.nonInteractive=!0);for(const n of o)"--non-interactive"===n||"-y"===n?t.nonInteractive=!0:e.push(n);return{positional:e,options:t}}(t),v=n[0],y=n.slice(1);if(v){switch(v){case"profile":return void await async function(o){const t=o[0],n=o[1];if(!t||"list"===t){const o=r.getActiveProfileName(),t=r.listProfiles().map(t=>{const n=t===o;return`${n?e.green("●"):e.gray("○")} ${t}${n?e.gray(" ("+l.t("config.profile_active_label")+")"):""}`});return void m(l.t("config.profile_list_title"),t)}if("create"===t){const o=u(n,"profile");return r.listProfiles().includes(o)?void console.log(e.yellow(l.t("config.profile_exists",{name:o}))):(r.createProfile(o),void console.log(e.green(l.t("config.profile_created",{name:o}))))}if("use"===t){const o=u(n,"profile");return r.setActiveProfile(o),void console.log(e.green(l.t("config.profile_activated",{name:o})))}if("delete"===t||"remove"===t){const o=u(n,"profile");return o===s&&1===r.listProfiles().length?void console.log(e.yellow(l.t("config.profile_cannot_delete_last"))):void await c({description:l.t("config.profile_delete_dry_run",{name:o}),json:{action:"profile_delete",name:o},apply:async()=>{await a({message:l.t("config.profile_delete_confirm",{name:o}),defaultValue:!1})?(r.deleteProfile(o),console.log(e.green(l.t("config.profile_deleted",{name:o})))):console.log(e.yellow(l.t("config.profile_delete_aborted")))}})}console.log(e.red(l.t("config.unknown_action",{action:t})))}(y);case"preset":return void function(o){const t=o[0],n=o[1];if(!t||"list"===t){const o=Object.entries(r.getCustomPresets()).map(([o,t])=>`${e.cyan(o)} ${e.gray(`H:${t.models.haiku} S:${t.models.sonnet} O:${t.models.opus}`)}`);return void m(l.t("config.preset_list_title"),o)}if("save"===t){const o=u(n,"preset");return r.saveCustomPreset(o,r.getModels()),void console.log(e.green(l.t("config.preset_saved",{name:o})))}if("use"===t||"apply"===t){const o=u(n,"preset"),t=r.getCustomPresets()[o];return t?(r.setModels(t.models),void console.log(e.green(l.t("config.preset_applied",{name:o})))):void console.log(e.red(l.t("config.preset_not_found",{name:o})))}if("delete"===t||"remove"===t){const o=u(n,"preset");return r.deleteCustomPreset(o),void console.log(e.green(l.t("config.preset_deleted",{name:o})))}console.log(e.red(l.t("config.unknown_action",{action:t})))}(y);case"tool-model":return void function(o){const t=o[0];if(!t||"list"===t){const o=r.getProfile().tool_models||{},t=Object.entries(o).map(([o,t])=>{const n=Object.entries(t).map(([o,e])=>`${o}=${e}`).join(" ");return`${e.cyan(o)} ${e.gray(n)}`});return void m(l.t("config.tool_model_list_title"),t)}if("set"===t){const t=(o[1]||"").trim(),n=o.length>=4?(o[2]||"").trim():"opus",s=(o.length>=4?o[3]:o[2]||"").trim();return i[t]?s&&g.test(n)?(r.setToolModel(t,{[n]:s}),void console.log(e.green(l.t("config.tool_model_set",{tool:t,slot:n,model:s})))):void console.log(e.red(l.t("config.tool_model_usage"))):void console.log(e.red(l.t("config.unknown_tool",{tool:t})))}if("clear"===t||"remove"===t){const t=(o[1]||"").trim();return i[t]?(r.setToolModel(t,null),void console.log(e.green(l.t("config.tool_model_cleared",{tool:t})))):void console.log(e.red(l.t("config.unknown_tool",{tool:t})))}console.log(e.red(l.t("config.unknown_action",{action:t})))}(y);case"backup":return void await function(o){return async function(o){const t=o[0],n=o[1];if(!t||"list"===t){const o=r.listBackups().map(o=>{const t=new Date(o.mtimeMs).toISOString();return`${e.cyan(o.name)} ${e.gray(t)}`});return void m(l.t("config.backup_list_title"),o)}if("create"===t||"snapshot"===t){const o=r.createBackup(n);return void console.log(e.green(l.t("config.backup_created",{path:o})))}if("restore"===t)return n?void await c({description:l.t("config.backup_restore_dry_run",{name:n}),json:{action:"backup_restore",name:n},apply:async()=>{await a({message:l.t("config.backup_restore_confirm",{name:n}),defaultValue:!1})?(r.restoreBackup(n),console.log(e.green(l.t("config.backup_restored",{name:n})))):console.log(e.yellow(l.t("config.backup_restore_aborted")))}}):void console.log(e.red(l.t("config.backup_restore_missing_name")));console.log(e.red(l.t("config.unknown_action",{action:t})))}(o)}(y)}"claude-code"!==v&&"claude-code"!==y[0]?i[v]?await _(v,y,p):(console.error(e.red(l.t("config.unknown_command",{command:v}))),console.log(e.gray(l.t("config.usage_hint"))),Object.values(i).filter(o=>!o.hidden).forEach(o=>{console.log(e.gray(` - ${o.name}: ${o.displayName}`))})):await _("claude-code",y,p)}else await async function(t){if(t.nonInteractive)return void console.log(e.red(l.t("config.requires_argument")));const{selectedTool:n}=await o.prompt([{type:"list",name:"selectedTool",message:l.t("wizard.select_tool"),choices:Object.values(i).filter(o=>!o.hidden).map(o=>({name:`${o.displayName} (${o.name})`,value:o.name}))}]);await _(n,[],t)}(p)}
1
+ import o from"inquirer";import e from"chalk";import{wizard as t}from"../lib/wizard.js";import{toolManager as n,SUPPORTED_TOOLS as l}from"../lib/tool-manager.js";import{i18n as i}from"../lib/i18n.js";import{configManager as r,DEFAULT_PROFILE_NAME as s}from"../lib/config.js";import{confirmAction as a,applyOrDryRun as c}from"../lib/confirm.js";import{isNonInteractive as d,shouldAutoConfirm as g}from"../lib/runtime-options.js";const f=/^[a-zA-Z0-9._-]+$/;function m(o,e){const t=(o||"").trim();if(!t||!f.test(t))throw new Error(i.t(`config.${e}_invalid_name`,{name:t||"<empty>"}));return t}function u(o,t){console.log("\n"+e.bold(o)),0!==t.length?t.forEach(o=>console.log(" "+o)):console.log(e.gray(" ("+i.t("config.empty_list")+")"))}function p(o,t){if("backups"===t){const t=n.listToolBackups(o).flatMap(o=>{const t=o.file.replace(/^.*[/\\]/,"");return o.backups.length?o.backups.map(o=>`${e.cyan(t)} ${e.gray(o.name.split(".bak-")[1]||o.name)}`):[`${e.cyan(t)} ${e.gray("("+i.t("config.empty_list")+")")}`]});return void u(i.t("config.tool_backups_title",{tool:l[o].displayName}),t)}const r=n.restorePrivateConfig(o).filter(o=>o.result.restored);0!==r.length?(console.log(e.green(i.t("config.config_restored",{tool:l[o].displayName}))),r.forEach(o=>console.log(e.gray(` ${o.file} ← ${o.result.from}`)))):console.log(e.yellow(i.t("config.restore_no_backup",{tool:l[o].displayName})))}async function _(s,d,g){if(!l[s])return void console.log(e.red(i.t("config.unknown_tool",{tool:s})));const f=(d[0]||"").toLowerCase();if("backups"!==f&&"list-backups"!==f){if("restore"===f){if(!await a({message:i.t("config.confirm_restore",{tool:l[s].displayName}),defaultValue:!1}))return;return void p(s,"restore")}if("clear-model"===f||"clear-models"===f){const o=l[s].displayName;return n.supportsClearPinnedModels(s)?void await c({description:i.t("config.clear_model_dry_run",{tool:o}),json:{action:"clear_model",tool:s},apply:async()=>{const t=n.clearPinnedModels(s);0!==t.length?(console.log(e.green(i.t("config.clear_model_done",{tool:o}))),t.forEach(o=>console.log(e.gray(` - ${o}`))),console.log(e.gray(i.t("config.clear_model_restart_hint",{tool:o})))):console.log(e.yellow(i.t("config.clear_model_none",{tool:o})))}}):void console.log(e.yellow(i.t("config.clear_model_unsupported",{tool:o})))}if(!n.isToolInstalled(s)){if(g.nonInteractive)return void console.log(e.yellow(i.t("wizard.tool_not_installed",{tool:l[s].displayName})));console.log(e.yellow(i.t("wizard.tool_not_installed",{tool:l[s].displayName})));const{shouldInstall:t}=await o.prompt([{type:"confirm",name:"shouldInstall",message:i.t("wizard.install_tool_confirm"),default:!0}]);if(!t)return void console.log(e.yellow(i.t("wizard.install_skipped")));try{await n.installTool(s)}catch(o){return console.error(e.red(i.t("install.install_failed_detail"))),void(o instanceof Error&&o.message&&console.error(e.gray(o.message)))}}if(g.nonInteractive){const o=r.getApiKeyForTool(s);return o?(n.loadPrivateConfig(s,o,r.getModelsForTool(s)),void console.log(e.green(i.t("config.tool_config_loaded",{tool:l[s].displayName})))):void console.log(e.red(i.t("wizard.api_key_required")))}await t.showToolMenu(s)}else p(s,"backups")}export async function configCommand(t){const{positional:n,options:p}=function(o){const e=[],t={};(d()||g())&&(t.nonInteractive=!0);for(const n of o)"--non-interactive"===n||"-y"===n?t.nonInteractive=!0:e.push(n);return{positional:e,options:t}}(t),y=n[0],v=n.slice(1);if(y){switch(y){case"profile":return void await async function(o){const t=o[0],n=o[1];if(!t||"list"===t){const o=r.getActiveProfileName(),t=r.listProfiles().map(t=>{const n=t===o;return`${n?e.green("●"):e.gray("○")} ${t}${n?e.gray(" ("+i.t("config.profile_active_label")+")"):""}`});return void u(i.t("config.profile_list_title"),t)}if("create"===t){const o=m(n,"profile");return r.listProfiles().includes(o)?void console.log(e.yellow(i.t("config.profile_exists",{name:o}))):(r.createProfile(o),void console.log(e.green(i.t("config.profile_created",{name:o}))))}if("use"===t){const o=m(n,"profile");return r.setActiveProfile(o),void console.log(e.green(i.t("config.profile_activated",{name:o})))}if("delete"===t||"remove"===t){const o=m(n,"profile");return o===s&&1===r.listProfiles().length?void console.log(e.yellow(i.t("config.profile_cannot_delete_last"))):void await c({description:i.t("config.profile_delete_dry_run",{name:o}),json:{action:"profile_delete",name:o},apply:async()=>{await a({message:i.t("config.profile_delete_confirm",{name:o}),defaultValue:!1})?(r.deleteProfile(o),console.log(e.green(i.t("config.profile_deleted",{name:o})))):console.log(e.yellow(i.t("config.profile_delete_aborted")))}})}console.log(e.red(i.t("config.unknown_action",{action:t})))}(v);case"preset":return void function(o){const t=o[0],n=o[1];if(!t||"list"===t){const o=Object.entries(r.getCustomPresets()).map(([o,t])=>`${e.cyan(o)} ${e.gray(`H:${t.models.haiku} S:${t.models.sonnet} O:${t.models.opus}`)}`);return void u(i.t("config.preset_list_title"),o)}if("save"===t){const o=m(n,"preset");return r.saveCustomPreset(o,r.getModels()),void console.log(e.green(i.t("config.preset_saved",{name:o})))}if("use"===t||"apply"===t){const o=m(n,"preset"),t=r.getCustomPresets()[o];return t?(r.setModels(t.models),void console.log(e.green(i.t("config.preset_applied",{name:o})))):void console.log(e.red(i.t("config.preset_not_found",{name:o})))}if("delete"===t||"remove"===t){const o=m(n,"preset");return r.deleteCustomPreset(o),void console.log(e.green(i.t("config.preset_deleted",{name:o})))}console.log(e.red(i.t("config.unknown_action",{action:t})))}(v);case"tool-model":return void function(o){const t=o[0];if(!t||"list"===t){const o=r.getProfile().tool_models||{},t=Object.entries(o).map(([o,t])=>{const n=Object.entries(t).map(([o,e])=>`${o}=${e}`).join(" ");return`${e.cyan(o)} ${e.gray(n)}`});return void u(i.t("config.tool_model_list_title"),t)}if("set"===t){const t=(o[1]||"").trim(),n=o.length>=4?(o[2]||"").trim():"opus",s=(o.length>=4?o[3]:o[2]||"").trim();return l[t]?s&&f.test(n)?(r.setToolModel(t,{[n]:s}),void console.log(e.green(i.t("config.tool_model_set",{tool:t,slot:n,model:s})))):void console.log(e.red(i.t("config.tool_model_usage"))):void console.log(e.red(i.t("config.unknown_tool",{tool:t})))}if("clear"===t||"remove"===t){const t=(o[1]||"").trim();return l[t]?(r.setToolModel(t,null),void console.log(e.green(i.t("config.tool_model_cleared",{tool:t})))):void console.log(e.red(i.t("config.unknown_tool",{tool:t})))}console.log(e.red(i.t("config.unknown_action",{action:t})))}(v);case"backup":return void await function(o){return async function(o){const t=o[0],n=o[1];if(!t||"list"===t){const o=r.listBackups().map(o=>{const t=new Date(o.mtimeMs).toISOString();return`${e.cyan(o.name)} ${e.gray(t)}`});return void u(i.t("config.backup_list_title"),o)}if("create"===t||"snapshot"===t){const o=r.createBackup(n);return void console.log(e.green(i.t("config.backup_created",{path:o})))}if("restore"===t)return n?void await c({description:i.t("config.backup_restore_dry_run",{name:n}),json:{action:"backup_restore",name:n},apply:async()=>{await a({message:i.t("config.backup_restore_confirm",{name:n}),defaultValue:!1})?(r.restoreBackup(n),console.log(e.green(i.t("config.backup_restored",{name:n})))):console.log(e.yellow(i.t("config.backup_restore_aborted")))}}):void console.log(e.red(i.t("config.backup_restore_missing_name")));console.log(e.red(i.t("config.unknown_action",{action:t})))}(o)}(v)}"claude-code"!==y&&"claude-code"!==v[0]?l[y]?await _(y,v,p):(console.error(e.red(i.t("config.unknown_command",{command:y}))),console.log(e.gray(i.t("config.usage_hint"))),Object.values(l).filter(o=>!o.hidden).forEach(o=>{console.log(e.gray(` - ${o.name}: ${o.displayName}`))})):await _("claude-code",v,p)}else await async function(t){if(t.nonInteractive)return void console.log(e.red(i.t("config.requires_argument")));const{selectedTool:n}=await o.prompt([{type:"list",name:"selectedTool",message:i.t("wizard.select_tool"),choices:Object.values(l).filter(o=>!o.hidden).map(o=>({name:`${o.displayName} (${o.name})`,value:o.name}))}]);await _(n,[],t)}(p)}
@@ -1,4 +1,9 @@
1
1
  import { DEFAULT_MODELS } from './config.js';
2
+ /**
3
+ * settings.json 里所有"钉死模型"的 env 变量。写入、卸载与 clearPinnedModels
4
+ * 共用这一份清单——新增模型槽时只改这里,避免某条路径漏清导致残留。
5
+ */
6
+ export declare const MODEL_ENV_VARS: readonly ["ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
2
7
  export interface ClaudeCodeSettingsConfig {
3
8
  env?: {
4
9
  ANTHROPIC_AUTH_TOKEN?: string;
@@ -68,6 +73,13 @@ export declare class ClaudeCodeManager {
68
73
  * 卸载私有配置从 Claude Code
69
74
  */
70
75
  unloadPrivateConfig(): void;
76
+ /**
77
+ * 只摘掉钉死的模型(env 里的 ANTHROPIC_*_MODEL + 顶层 model),保留凭据与
78
+ * 用户的 hooks / MCP / 插件配置。用于把 Claude Code 交还给自带默认模型,
79
+ * 让 /model 列表恢复完整(新上线的模型不再被旧配置挡住)。
80
+ * @returns 实际被清除的键名(未改动时为空数组)
81
+ */
82
+ clearPinnedModels(): string[];
71
83
  /**
72
84
  * 检测当前 Claude Code 配置的 API Key
73
85
  * @returns 包含 API Key 的对象
@@ -1 +1 @@
1
- import{existsSync as t,mkdirSync as e,readFileSync as n,writeFileSync as s,renameSync as i,unlinkSync as r}from"fs";import{join as a,dirname as o}from"path";import{homedir as g}from"os";import{logger as C}from"../utils/logger.js";import{configManager as c,DEFAULT_MODELS as O}from"./config.js";import{loadSettingsTemplate as h}from"./settings-template.js";import{cleanupAnthropicEnvVars as u}from"./env-cleanup.js";export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;constructor(){this.settingsPath=a(g(),".claude","settings.json"),this.mcpConfigPath=a(g(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const s=o(n);t(s)||e(s,{recursive:!0})}atomicWrite(e,n){const a=`${e}.tmp`;try{s(a,n,"utf-8"),i(a,e)}catch(e){if(t(a))try{r(a)}catch{}throw e}}getSettings(){try{if(t(this.settingsPath)){const t=n(this.settingsPath,"utf-8");return JSON.parse(t)}}catch(t){C.warn("Failed to read Claude Code settings:",t),C.logError("ClaudeCodeManager.getSettings",t)}return{}}saveSettings(t){try{this.ensureConfigDir(this.settingsPath),this.atomicWrite(this.settingsPath,JSON.stringify(t,null,2))}catch(t){throw new Error(`Failed to save Claude Code settings: ${t}`)}}getMCPConfig(){try{if(t(this.mcpConfigPath)){const t=n(this.mcpConfigPath,"utf-8");return JSON.parse(t)}}catch(t){C.warn("Failed to read Claude Code MCP config:",t),C.logError("ClaudeCodeManager.getMCPConfig",t)}return{}}saveMCPConfig(t){try{this.ensureConfigDir(this.mcpConfigPath),this.atomicWrite(this.mcpConfigPath,JSON.stringify(t,null,2))}catch(t){throw new Error(`Failed to save Claude Code MCP config: ${t}`)}}loadPrivateConfig(t,e){this.ensureOnboardingCompleted(),u();const n=h(),s=n.env||{},{env:i,...r}=n,a=this.getSettings(),o=a.env||{},{ANTHROPIC_API_KEY:g,ANTHROPIC_DEFAULT_HAIKU_MODEL:C,ANTHROPIC_DEFAULT_SONNET_MODEL:_,ANTHROPIC_DEFAULT_OPUS_MODEL:d,ANTHROPIC_MODEL:l,ANTHROPIC_REASONING_MODEL:E,...A}=o,P={...a,...r,env:{...A,...s,ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:c.getBaseUrl(),ANTHROPIC_DEFAULT_HAIKU_MODEL:e.haiku||O.haiku,ANTHROPIC_DEFAULT_SONNET_MODEL:e.sonnet||O.sonnet,ANTHROPIC_DEFAULT_OPUS_MODEL:e.opus||O.opus,ANTHROPIC_MODEL:e.opus||O.opus}};this.saveSettings(P)}ensureOnboardingCompleted(){try{const t=this.getMCPConfig();t.hasCompletedOnboarding||this.saveMCPConfig({...t,hasCompletedOnboarding:!0})}catch(t){C.warn("Failed to ensure onboarding completed:",t),C.logError("ClaudeCodeManager.ensureOnboardingCompleted",t)}}unloadPrivateConfig(){const t=this.getSettings();if(!t.env)return;const{ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:n,ANTHROPIC_DEFAULT_HAIKU_MODEL:s,ANTHROPIC_DEFAULT_SONNET_MODEL:i,ANTHROPIC_DEFAULT_OPUS_MODEL:r,ANTHROPIC_MODEL:a,ANTHROPIC_REASONING_MODEL:o,API_TIMEOUT_MS:g,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:C,...c}=t.env,O={...t,env:c};O.env&&0===Object.keys(O.env).length&&delete O.env,this.saveSettings(O)}detectCurrentConfig(){try{const t=this.getSettings();return t.env&&t.env.ANTHROPIC_AUTH_TOKEN?{apiKey:t.env.ANTHROPIC_AUTH_TOKEN}:{apiKey:null}}catch{return{apiKey:null}}}getExperimentalFeature(t){const e=this.getSettings();return e.env?.[t]}setExperimentalFeature(t,e){const n=this.getSettings(),s=n.env||{},i={...n,env:{...s,[t]:e}};this.saveSettings(i)}removeExperimentalFeature(t){const e=this.getSettings();if(!e.env)return;const{[t]:n,...s}=e.env,i={...e,env:s};this.saveSettings(i)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
1
+ import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as s,renameSync as o,unlinkSync as i}from"fs";import{join as r,dirname as a}from"path";import{homedir as g}from"os";import{logger as c}from"../utils/logger.js";import{configManager as l,DEFAULT_MODELS as C}from"./config.js";import{loadSettingsTemplate as d}from"./settings-template.js";import{cleanupAnthropicEnvVars as h}from"./env-cleanup.js";export const MODEL_ENV_VARS=["ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;constructor(){this.settingsPath=r(g(),".claude","settings.json"),this.mcpConfigPath=r(g(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const s=a(n);e(s)||t(s,{recursive:!0})}atomicWrite(t,n){const r=`${t}.tmp`;try{s(r,n,"utf-8"),o(r,t)}catch(t){if(e(r))try{i(r)}catch{}throw t}}getSettings(){try{if(e(this.settingsPath)){const e=n(this.settingsPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code settings:",e),c.logError("ClaudeCodeManager.getSettings",e)}return{}}saveSettings(e){try{this.ensureConfigDir(this.settingsPath),this.atomicWrite(this.settingsPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code settings: ${e}`)}}getMCPConfig(){try{if(e(this.mcpConfigPath)){const e=n(this.mcpConfigPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code MCP config:",e),c.logError("ClaudeCodeManager.getMCPConfig",e)}return{}}saveMCPConfig(e){try{this.ensureConfigDir(this.mcpConfigPath),this.atomicWrite(this.mcpConfigPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code MCP config: ${e}`)}}loadPrivateConfig(e,t){this.ensureOnboardingCompleted(),h();const n=d(),s=n.env||{},{env:o,...i}=n,r=this.getSettings(),{ANTHROPIC_API_KEY:a,...g}=r.env||{},c={...g};for(const e of MODEL_ENV_VARS)delete c[e];const u={},O=e=>{const n=(t[e]||"").trim();return n&&n!==C[e]?n:null},f=O("haiku"),_=O("sonnet"),m=O("opus");f&&(u.ANTHROPIC_DEFAULT_HAIKU_MODEL=f),_&&(u.ANTHROPIC_DEFAULT_SONNET_MODEL=_),m&&(u.ANTHROPIC_DEFAULT_OPUS_MODEL=m,u.ANTHROPIC_MODEL=m);const E={...r};for(const[e,t]of Object.entries(i))e in E||(E[e]=t);E.showThinkingSummaries=!0;const P={...E,env:{...c,...Object.fromEntries(Object.entries(s).filter(([e])=>!(e in c))),ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:l.getBaseUrl(),...u}};m?P.model=m:Object.prototype.hasOwnProperty.call(i,"model")?P.model=i.model:delete P.model,this.saveSettings(P)}ensureOnboardingCompleted(){try{const e=this.getMCPConfig();e.hasCompletedOnboarding||this.saveMCPConfig({...e,hasCompletedOnboarding:!0})}catch(e){c.warn("Failed to ensure onboarding completed:",e),c.logError("ClaudeCodeManager.ensureOnboardingCompleted",e)}}unloadPrivateConfig(){const e=this.getSettings();if(!e.env){if(void 0!==e.model){const{model:t,...n}=e;this.saveSettings(n)}return}const{ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:n,API_TIMEOUT_MS:s,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:o,...i}=e.env,r={...i};for(const e of MODEL_ENV_VARS)delete r[e];const a={...e,env:r};delete a.model,a.env&&0===Object.keys(a.env).length&&delete a.env,this.saveSettings(a)}clearPinnedModels(){const e=this.getSettings(),t=e.env,n=[];for(const e of MODEL_ENV_VARS)t&&e in t&&n.push(e);if(void 0!==e.model&&n.push("model"),0===n.length)return[];const s={...e};if(delete s.model,t){const e={...t};for(const t of MODEL_ENV_VARS)delete e[t];0===Object.keys(e).length?delete s.env:s.env=e}return this.saveSettings(s),n}detectCurrentConfig(){try{const e=this.getSettings();return e.env&&e.env.ANTHROPIC_AUTH_TOKEN?{apiKey:e.env.ANTHROPIC_AUTH_TOKEN}:{apiKey:null}}catch{return{apiKey:null}}}getExperimentalFeature(e){const t=this.getSettings();return t.env?.[e]}setExperimentalFeature(e,t){const n=this.getSettings(),s=n.env||{},o={...n,env:{...s,[e]:t}};this.saveSettings(o)}removeExperimentalFeature(e){const t=this.getSettings();if(!t.env)return;const{[e]:n,...s}=t.env,o={...t,env:s};this.saveSettings(o)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
@@ -1 +1 @@
1
- import{Command as a}from"commander";import{i18n as e,detectSystemLang as o}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as p}from"../commands/logs.js";import{completionCommand as l}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{setupCommand as u}from"../commands/setup.js";import h from"chalk";import{readFileSync as f}from"fs";import{join as y,dirname as b}from"path";import{fileURLToPath as v}from"url";import{setRuntimeOptions as w,detectColorPreferenceFromEnv as k,isColorEnabled as $,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as C}from"./env-cleanup.js";import{runStartupUpdate as I}from"./auto-update.js";export class Command{program;constructor(){const i=t.isFirstRun()?o():t.getLang();e.loadFromConfig(i),this.program=new a,this.setupProgram()}getVersion(){try{const a=v(import.meta.url),e=b(a),o=y(e,"../../package.json");return JSON.parse(f(o,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(e.t("cli.title")).version(this.getVersion(),"-v, --version",e.t("commands.version")).helpOption("-h, --help",e.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(e.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--all","Configure every tool the key can serve (default behavior)").action(async a=>{await u(a)}),this.program.command("init").description(e.t("commands.init")).action(async()=>{await this.handleInitCommand()});const a=this.program.command("lang").description(e.t("commands.lang"));a.command("show").description(e.t("lang.show_usage")).action(async()=>{await n(["show"])}),a.command("set <locale>").description(e.t("lang.set_usage")).action(async a=>{await n(["set",a])});const o=this.program.command("auth").description(e.t("commands.auth"));o.argument("[token]","API token").action(async a=>{const e=[];a&&e.push(a),await s(e)}),o.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),o.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async a=>{await s(["reload",a])}),this.program.command("doctor").description(e.t("commands.doctor")).action(async()=>{await r()}),this.program.command("status").description(e.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(a,e)=>{const o=[];a&&o.push(a),e&&o.push(e),await p(o)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async a=>{await l([a])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async a=>{const e=[];a.samples&&e.push("--samples",a.samples),await d(e)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async a=>{await c(a||[])}),this.program.command("enter [option]").description(e.t("commands.enter")).action(async a=>{if(a)switch(a){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const e=[a];await c(e);break}}else await i.showMainMenu()}),this.program.action(async()=>{t.isFirstRun()?(console.log(h.cyan(e.t("messages.first_run"))),await i.runFirstTimeSetup()):await i.showMainMenu()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:a=>a.name()+" "+a.usage()}),this.program.addHelpText("after",`\n${h.bold(e.t("cli.examples"))}:\n ${h.gray("$ vibecli # Interactive main menu")}\n ${h.gray("$ vibecli init # Run first-time setup wizard")}\n ${h.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${h.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b")}\n ${h.gray("$ vibecli status --json # Machine-readable status")}\n ${h.gray("$ vibecli config profile list")}\n ${h.gray("$ vibecli config profile create work")}\n ${h.gray("$ vibecli config profile use work")}\n ${h.gray("$ vibecli config preset save fast")}\n ${h.gray("$ vibecli config backup create snapshot")}\n ${h.gray("$ vibecli auth <token> --non-interactive")}\n ${h.gray("$ vibecli auth revoke --yes")}\n ${h.gray("$ vibecli logs path")}\n ${h.gray("$ vibecli completion bash")}\n ${h.gray("$ vibecli bench --samples 3")}\n ${h.gray("$ vibecli update")}\n ${h.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const a=C();if(0===a.length||j())return;const o=e.t("messages.env_vars_cleared",{vars:a.join(", ")});console.log($()?h.yellow(o):o)}preflightArgs(a){const e=e=>a.includes(e),o=!e("--no-color")&&k();let i;w({nonInteractive:e("--non-interactive"),json:e("--json"),yes:e("--yes")||e("-y"),dryRun:e("--dry-run"),color:o}),o||(h.level=0);for(let e=0;e<a.length;e+=1){const o=a[e];if("--base"===o&&a[e+1]){i=a[e+1];break}if(o.startsWith("--base=")){i=o.slice(7);break}}i&&t.setBaseUrl(i)}async execute(a){try{if(this.preflightArgs(a),this.cleanupLeakedEnvVars(),(await I(a)).handled)return;await this.program.parseAsync(a,{from:"user"})}catch(a){if(a instanceof Error){const o=$()?h.red:a=>a;console.error(o(e.t("cli.error_general")),a.message)}process.exit(1)}}getProgram(){return this.program}}
1
+ import{Command as a}from"commander";import{i18n as o,detectSystemLang as e}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as p}from"../commands/logs.js";import{completionCommand as l}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{setupCommand as u}from"../commands/setup.js";import h from"chalk";import{readFileSync as f}from"fs";import{join as y,dirname as b}from"path";import{fileURLToPath as v}from"url";import{setRuntimeOptions as k,detectColorPreferenceFromEnv as w,isColorEnabled as $,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as C}from"./env-cleanup.js";import{runStartupUpdate as I}from"./auto-update.js";export class Command{program;constructor(){const i=t.isFirstRun()?e():t.getLang();o.loadFromConfig(i),this.program=new a,this.setupProgram()}getVersion(){try{const a=v(import.meta.url),o=b(a),e=y(o,"../../package.json");return JSON.parse(f(e,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--all","Configure every tool the key can serve (default behavior)").action(async a=>{await u(a)}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const a=this.program.command("lang").description(o.t("commands.lang"));a.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),a.command("set <locale>").description(o.t("lang.set_usage")).action(async a=>{await n(["set",a])});const e=this.program.command("auth").description(o.t("commands.auth"));e.argument("[token]","API token").action(async a=>{const o=[];a&&o.push(a),await s(o)}),e.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),e.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async a=>{await s(["reload",a])}),this.program.command("doctor").description(o.t("commands.doctor")).action(async()=>{await r()}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(a,o)=>{const e=[];a&&e.push(a),o&&e.push(o),await p(e)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async a=>{await l([a])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async a=>{const o=[];a.samples&&o.push("--samples",a.samples),await d(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async a=>{await c(a||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async a=>{if(a)switch(a){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[a];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:a=>a.name()+" "+a.usage()}),this.program.addHelpText("after",`\n${h.bold(o.t("cli.examples"))}:\n ${h.gray("$ vibecli # Interactive main menu")}\n ${h.gray("$ vibecli init # Run first-time setup wizard")}\n ${h.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${h.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b")}\n ${h.gray("$ vibecli status --json # Machine-readable status")}\n ${h.gray("$ vibecli config profile list")}\n ${h.gray("$ vibecli config profile create work")}\n ${h.gray("$ vibecli config profile use work")}\n ${h.gray("$ vibecli config preset save fast")}\n ${h.gray("$ vibecli config backup create snapshot")}\n ${h.gray("$ vibecli auth <token> --non-interactive")}\n ${h.gray("$ vibecli auth revoke --yes")}\n ${h.gray("$ vibecli logs path")}\n ${h.gray("$ vibecli completion bash")}\n ${h.gray("$ vibecli bench --samples 3")}\n ${h.gray("$ vibecli update")}\n ${h.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const a=C();if(0===a.length||j())return;const e=o.t("messages.env_vars_cleared",{vars:a.join(", ")});console.log($()?h.yellow(e):e)}preflightArgs(a){const o=o=>a.includes(o),e=!o("--no-color")&&w();let i;k({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:e}),e||(h.level=0);for(let o=0;o<a.length;o+=1){const e=a[o];if("--base"===e&&a[o+1]){i=a[o+1];break}if(e.startsWith("--base=")){i=e.slice(7);break}}i&&t.setBaseUrl(i)}async execute(a){try{if(this.preflightArgs(a),this.cleanupLeakedEnvVars(),(await I(a)).handled)return;await this.program.parseAsync(a,{from:"user"})}catch(a){if(a instanceof Error){const e=$()?h.red:a=>a;console.error(e(o.t("cli.error_general")),a.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Shell 环境变量里的 ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL 会覆盖 vibecli 写进
3
- * settings.json 的凭据,导致"改了配置却不生效"。这里统一负责把它们清掉:
3
+ * settings.json 的凭据,导致"改了配置却不生效";同理,export 过的 ANTHROPIC_*_MODEL
4
+ * 会盖住模型选择,让 /model 里选不到新模型。这里统一负责把它们清掉:
4
5
  * CLI 启动时检测一次(见 command.ts),给 Claude Code 写配置前再兜底一次。
5
6
  */
6
- export declare const ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"];
7
+ export declare const ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
7
8
  /**
8
9
  * 获取当前 shell 的 rc 文件路径(Windows 不使用 rc 文件,返回 null)。
9
10
  */
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as r,writeFileSync as n}from"fs";import{join as t}from"path";import{logger as o}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const t=getShellRcFilePath();if(!t||!e(t))return!1;let s=r(t,"utf-8");const l=s,c=[/^\s*export\s+ANTHROPIC_BASE_URL=.*$/gm,/^\s*export\s+ANTHROPIC_API_KEY=.*$/gm,/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of c)s=s.replace(e,"");if(s!==l)return n(t,s,"utf-8"),o.info(`Cleaned up ANTHROPIC_* environment variables from ${t}`),!0}catch(e){o.warn("Failed to cleanup shell environment variables:",e),o.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars();for(const r of e)delete process.env[r];return[...e]}
1
+ import{existsSync as e,readFileSync as r,writeFileSync as n}from"fs";import{join as t}from"path";import{logger as o}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const t=getShellRcFilePath();if(!t||!e(t))return!1;let s=r(t,"utf-8");const l=s,c=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of c)s=s.replace(e,"");if(s!==l)return n(t,s,"utf-8"),o.info(`Cleaned up ANTHROPIC_* environment variables from ${t}`),!0}catch(e){o.warn("Failed to cleanup shell environment variables:",e),o.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars();for(const r of e)delete process.env[r];return[...e]}
@@ -31,6 +31,14 @@ export declare class ToolManager {
31
31
  updateToolConfig(toolName: string, config: any): void;
32
32
  loadPrivateConfig(toolName: string, apiKey: string, models: VibecliModels): void;
33
33
  unloadPrivateConfig(toolName: string): void;
34
+ /**
35
+ * Clear the tool's pinned model, keeping its credentials. Also drops this
36
+ * CLI's own per-tool model override so a later re-apply doesn't pin it back.
37
+ * @returns the config keys cleared in the tool's own config file
38
+ */
39
+ clearPinnedModels(toolName: string): string[];
40
+ /** Whether this tool supports clearing a pinned model. */
41
+ supportsClearPinnedModels(toolName: string): boolean;
34
42
  detectCurrentConfig(toolName: string): {
35
43
  apiKey: string | null;
36
44
  };
@@ -1 +1 @@
1
- import{execSync as o}from"child_process";import{i18n as n}from"./i18n.js";import t from"inquirer";import l from"chalk";import e from"terminal-link";import s from"ora";import{logger as i}from"../utils/logger.js";import{configManager as a}from"./config.js";import{getAdapter as r,listAdapters as c,TOOL_ADAPTERS as d}from"./tools/registry.js";import{listBackups as g,restoreBackup as m}from"./tools/backup.js";function u(o){const n=d[o];return{name:n.name,command:n.command,installCommand:n.installCommand,configPath:n.configPath,displayName:n.displayName,...n.hidden?{hidden:!0}:{}}}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(d).map(o=>[o,u(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(n){const t=SUPPORTED_TOOLS[n];if(!t)return!1;try{const n="win32"===process.platform?`where ${t.command}`:`which ${t.command}`;return o(n,{stdio:"pipe"}),!0}catch{return!1}}async installTool(a){const r=SUPPORTED_TOOLS[a];if(!r)throw new Error(`Unknown tool: ${a}`);const c=s(n.t("wizard.installing_tool")).start();try{c.info(l.gray("$ ")+l.white(r.installCommand)),c.stop(),o(r.installCommand,{stdio:"inherit"}),c.succeed(n.t("wizard.tool_installed"))}catch(s){c.fail(n.t("wizard.install_failed"));const d=(s.message||"")+(s.stderr?.toString()||"")+(s.stdout?.toString()||"");if(!(d.includes("EACCES")||d.includes("permission denied")||d.includes("EPERM")||243===s.status))throw new Error(`Failed to install ${r.displayName}: ${s}`);if(console.log("\n⚠️ "+n.t("install.permission_detected")+"\n"),"win32"===process.platform)try{const t=r.installCommand.replace("npm install -g","npm install -g --force");return console.log("🔧 "+n.t("install.trying_solution",{num:"1",desc:n.t("install.using_force")})),console.log(l.gray("$ ")+l.white(t)),o(t,{stdio:"inherit"}),void console.log("\n✅ "+n.t("install.permission_fixed"))}catch(o){i.warn("Retry install failed:",o),console.log("\n❌ "+n.t("install.auto_fix_failed")),console.log(l.yellow("\n📌 "+n.t("install.windows_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_1_title"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step1"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step2"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step3"))),console.log(l.white(` ${r.installCommand}`)),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_2_title"))),console.log(l.gray(" "+n.t("install.windows_solution_2_command"))),console.log(l.white(` ${r.installCommand.replace("npm install -g","npm install -g --prefix=%APPDATA%\\npm")}`)),console.log("");const{action:e}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===e)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);return void console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}console.log(l.yellow("\n📌 "+n.t("install.unix_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.unix_solution_1_title"))),console.log(l.gray(" "+n.t("install.unix_solution_1_desc"))),console.log(l.white(` sudo ${r.installCommand}`)),console.log("");const g="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally",m=e(g,g,{fallback:()=>g});console.log(l.cyan.bold(n.t("install.unix_solution_2_title"))),console.log(l.gray(" "+n.t("install.unix_solution_2_desc"))),console.log(l.blue(" 📖 "+n.t("install.npm_docs_link")+": ")+m),console.log("");const{action:u}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===u)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}}getToolConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);try{return n.getConfig()}catch(n){return i.warn(`Failed to read config for ${o}:`,n),i.logError(`ToolManager.getToolConfig(${o})`,n),null}}updateToolConfig(o,n){const t=r(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(!t.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);t.updateConfig(n)}loadPrivateConfig(o,n,t){const l=r(o);if(!l)throw new Error(`Unknown tool: ${o}`);l.loadPrivateConfig({apiKey:n,baseUrl:a.getBaseUrl(),models:t})}unloadPrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);n.unloadPrivateConfig()}detectCurrentConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return n.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,backups:g(o)}))}restorePrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,result:m(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return c().filter(o=>!o.hidden).map(o=>u(o.name))}isGitInstalled(){try{const n="win32"===process.platform?"where git":"which git";return o(n,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
1
+ import{execSync as o}from"child_process";import{i18n as n}from"./i18n.js";import t from"inquirer";import l from"chalk";import e from"terminal-link";import s from"ora";import{logger as i}from"../utils/logger.js";import{configManager as a}from"./config.js";import{getAdapter as r,listAdapters as c,TOOL_ADAPTERS as d}from"./tools/registry.js";import{listBackups as g,restoreBackup as m}from"./tools/backup.js";function u(o){const n=d[o];return{name:n.name,command:n.command,installCommand:n.installCommand,configPath:n.configPath,displayName:n.displayName,...n.hidden?{hidden:!0}:{}}}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(d).map(o=>[o,u(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(n){const t=SUPPORTED_TOOLS[n];if(!t)return!1;try{const n="win32"===process.platform?`where ${t.command}`:`which ${t.command}`;return o(n,{stdio:"pipe"}),!0}catch{return!1}}async installTool(a){const r=SUPPORTED_TOOLS[a];if(!r)throw new Error(`Unknown tool: ${a}`);const c=s(n.t("wizard.installing_tool")).start();try{c.info(l.gray("$ ")+l.white(r.installCommand)),c.stop(),o(r.installCommand,{stdio:"inherit"}),c.succeed(n.t("wizard.tool_installed"))}catch(s){c.fail(n.t("wizard.install_failed"));const d=(s.message||"")+(s.stderr?.toString()||"")+(s.stdout?.toString()||"");if(!(d.includes("EACCES")||d.includes("permission denied")||d.includes("EPERM")||243===s.status))throw new Error(`Failed to install ${r.displayName}: ${s}`);if(console.log("\n⚠️ "+n.t("install.permission_detected")+"\n"),"win32"===process.platform)try{const t=r.installCommand.replace("npm install -g","npm install -g --force");return console.log("🔧 "+n.t("install.trying_solution",{num:"1",desc:n.t("install.using_force")})),console.log(l.gray("$ ")+l.white(t)),o(t,{stdio:"inherit"}),void console.log("\n✅ "+n.t("install.permission_fixed"))}catch(o){i.warn("Retry install failed:",o),console.log("\n❌ "+n.t("install.auto_fix_failed")),console.log(l.yellow("\n📌 "+n.t("install.windows_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_1_title"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step1"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step2"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step3"))),console.log(l.white(` ${r.installCommand}`)),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_2_title"))),console.log(l.gray(" "+n.t("install.windows_solution_2_command"))),console.log(l.white(` ${r.installCommand.replace("npm install -g","npm install -g --prefix=%APPDATA%\\npm")}`)),console.log("");const{action:e}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===e)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);return void console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}console.log(l.yellow("\n📌 "+n.t("install.unix_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.unix_solution_1_title"))),console.log(l.gray(" "+n.t("install.unix_solution_1_desc"))),console.log(l.white(` sudo ${r.installCommand}`)),console.log("");const g="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally",m=e(g,g,{fallback:()=>g});console.log(l.cyan.bold(n.t("install.unix_solution_2_title"))),console.log(l.gray(" "+n.t("install.unix_solution_2_desc"))),console.log(l.blue(" 📖 "+n.t("install.npm_docs_link")+": ")+m),console.log("");const{action:u}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===u)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}}getToolConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);try{return n.getConfig()}catch(n){return i.warn(`Failed to read config for ${o}:`,n),i.logError(`ToolManager.getToolConfig(${o})`,n),null}}updateToolConfig(o,n){const t=r(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(!t.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);t.updateConfig(n)}loadPrivateConfig(o,n,t){const l=r(o);if(!l)throw new Error(`Unknown tool: ${o}`);l.loadPrivateConfig({apiKey:n,baseUrl:a.getBaseUrl(),models:t})}unloadPrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);n.unloadPrivateConfig()}clearPinnedModels(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return a.setToolModel(o,null),n.clearPinnedModels?n.clearPinnedModels():[]}supportsClearPinnedModels(o){return Boolean(r(o)?.clearPinnedModels)}detectCurrentConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return n.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,backups:g(o)}))}restorePrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,result:m(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return c().filter(o=>!o.hidden).map(o=>u(o.name))}isGitInstalled(){try{const n="win32"===process.platform?"where git":"which git";return o(n,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
@@ -1 +1 @@
1
- import{join as e}from"path";import{homedir as t}from"os";import{claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as a}from"./backup.js";const n=e(t(),".claude","settings.json"),i=e(t(),".claude.json");export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",configPath:n,loadPrivateConfig(e){a(n),a(i),o.loadPrivateConfig(e.apiKey,e.models)},unloadPrivateConfig(){a(n),o.unloadPrivateConfig()},detectCurrentConfig:()=>o.detectCurrentConfig(),getConfig:()=>({settings:o.getSettings(),mcp:o.getMCPConfig()}),updateConfig(e){const t=e||{};t.settings&&o.saveSettings(t.settings),t.mcp&&o.saveMCPConfig(t.mcp)},backupPaths:()=>[n,i]};
1
+ import{join as e}from"path";import{homedir as o}from"os";import{claudeCodeManager as t}from"../claude-code-manager.js";import{backupBeforeWrite as a}from"./backup.js";const n=e(o(),".claude","settings.json"),i=e(o(),".claude.json");export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",configPath:n,loadPrivateConfig(e){a(n),a(i),t.loadPrivateConfig(e.apiKey,e.models)},unloadPrivateConfig(){a(n),t.unloadPrivateConfig()},detectCurrentConfig:()=>t.detectCurrentConfig(),getConfig:()=>({settings:t.getSettings(),mcp:t.getMCPConfig()}),updateConfig(e){const o=e||{};o.settings&&t.saveSettings(o.settings),o.mcp&&t.saveMCPConfig(o.mcp)},clearPinnedModels:()=>(a(n),t.clearPinnedModels()),backupPaths:()=>[n,i]};
@@ -47,6 +47,13 @@ export interface ToolAdapter {
47
47
  getConfig(): unknown;
48
48
  /** Overwrite the raw config object. Optional — not all tools support it. */
49
49
  updateConfig?(config: unknown): void;
50
+ /**
51
+ * Drop any pinned model selection while keeping credentials and the user's
52
+ * own config intact, handing the tool back to its built-in default model.
53
+ * Optional — only tools that can pin a model implement it.
54
+ * @returns the config keys actually cleared (empty when nothing was pinned)
55
+ */
56
+ clearPinnedModels?(): string[];
50
57
  /**
51
58
  * Every file this adapter writes (and therefore snapshots) — used by restore.
52
59
  * Defaults to `[configPath]` when omitted; multi-file tools (Codex auth.json,
@@ -1,7 +1,8 @@
1
1
  export declare class Wizard {
2
2
  private static instance;
3
3
  private BOX_WIDTH;
4
- private apiKeyValid;
4
+ /** Keys validated during this process. Kept per key so dedicated tool keys cannot share auth state. */
5
+ private validatedApiKeys;
5
6
  private constructor();
6
7
  static getInstance(): Wizard;
7
8
  createBox(title: string): void;
@@ -22,7 +23,7 @@ export declare class Wizard {
22
23
  skipLoadPrompt?: boolean;
23
24
  }): Promise<void>;
24
25
  promptLoadAfterModelConfig(): Promise<void>;
25
- configApiKey(): Promise<void>;
26
+ configApiKey(): Promise<boolean>;
26
27
  selectAndConfigureTool(firstRun?: boolean): Promise<void>;
27
28
  configureTool(toolName: string, firstRun?: boolean): Promise<void>;
28
29
  showToolMenu(toolName: string): Promise<void>;
@@ -33,6 +34,12 @@ export declare class Wizard {
33
34
  * the main (opus) slot and keep the other slots at their defaults.
34
35
  */
35
36
  private singleModelOverride;
37
+ private hasConfiguredApiKey;
38
+ /**
39
+ * Validate the exact credential about to be used. A per-key cache avoids
40
+ * repeated requests without allowing one tool's key to authorize another.
41
+ */
42
+ private validatedKeyForConfiguration;
36
43
  /**
37
44
  * Per-tool model picker shown after a tool is selected, before loading.
38
45
  * Sources: use default / built-in presets / manual input. Returns false if
@@ -40,6 +47,11 @@ export declare class Wizard {
40
47
  */
41
48
  configureToolModel(toolName: string): Promise<boolean>;
42
49
  loadConfig(toolName: string): Promise<void>;
50
+ /**
51
+ * 只摘掉钉死的模型、保留 API Key 与用户自有配置(hooks / MCP / 插件),
52
+ * 让工具回到自带默认模型。
53
+ */
54
+ clearPinnedModels(toolName: string): Promise<void>;
43
55
  unloadConfig(toolName: string): Promise<void>;
44
56
  restoreConfig(toolName: string): Promise<void>;
45
57
  run(): Promise<void>;
@@ -1 +1 @@
1
- import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as d}from"./tool-manager.js";import{i18n as c,detectSystemLang as p}from"./i18n.js";import{createBorderLine as m,createContentLine as u}from"../utils/string-width.js";import{validateApiKey as g,fetchModels as w}from"./api-validator.js";import{readFileSync as _}from"fs";import{dirname as f,join as y}from"path";import{fileURLToPath as h}from"url";const z=f(h(import.meta.url)),v=t.hex("#FF8700");export class Wizard{static instance;BOX_WIDTH=63;apiKeyValid=!1;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(v.bold("\n"+m("╔","╗","═",this.BOX_WIDTH))),console.log(v.bold(u(e,"║","║",this.BOX_WIDTH,"center"))),console.log(v.bold(m("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(c.t("wizard.hint_navigate")),t.gray(c.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=65,t=c.t("wizard.banner_subtitle"),a=u(t,"║","║",e,"center"),o=u("","║","║",e,"center"),i=u(`API Helper v${function(){try{return JSON.parse(_(y(z,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[" ▄▀▄ █▀▄ █ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," █▀█ █▀ █ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(t=>u(t,"║","║",e,"center")),r=[m("╔","╗","═",e),o,...n,o,i,a,m("╚","╝","═",e)];console.log(v.bold("\n"+r.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{try{return Boolean(l.detectCurrentConfig(e.name).apiKey)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=c.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+c.t("wizard.goodbye_claude_hint"))}else a=c.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a))}async runFirstTimeSetup(){const e="zh_CN"===p();e&&(o.setLang("zh_CN"),c.setLocale("zh_CN")),this.resetScreen(),console.log(v.bold("\n"+c.t("wizard.welcome"))),console.log(t.gray(c.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(c.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await this.configApiKey(),o.getApiKey()?await this.selectAndConfigureTool(!0):console.log(t.red("\n"+c.t("wizard.api_key_required")))}async configLanguage(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_language"));const a=c.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+c.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void c.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(c.t(e.name)!==e.name?c.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+c.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+c.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:c.t("wizard.custom_model_mode"),choices:[{name:c.t("wizard.custom_model_single"),value:"single"},{name:c.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:c.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:c.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:c.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:c.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:c.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+c.t("wizard.models_saved")))}async configModels(a){if(!o.getApiKey())return console.log(t.red("\n"+c.t("wizard.api_key_required"))),void await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]);for(;;){this.resetScreen(),this.createBox(c.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+c.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:c.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:c.t("wizard.load_config_now"),choices:[{name:c.t("wizard.load_config_yes"),value:!0},{name:c.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configApiKey(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+c.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+c.t("wizard.api_key_get_hint",{url:"https://www.vibeapi.cn/console/token"}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:c.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return;continue}const n=a(c.t("wizard.validating_api_key")).start(),r=await g(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.apiKeyValid=!0,void console.log(t.green("\n✓ "+c.t("wizard.api_key_valid")));{this.apiKeyValid=!1,"invalid_api_key"===r.error?console.log(t.red("\n✗ "+c.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+c.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+c.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=d[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+c.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:c.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(c.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=d[t];if(!a)return;this.createBox(a.displayName+" "+c.t("wizard.menu_title"));const{action:o}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+c.t("wizard.select_action"),choices:[{name:"📥 "+c.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+c.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"},{name:"↩️ "+c.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else{if("back"===o)return;"load"===o?await this.configureToolModel(t)&&await this.loadConfig(t):"unload"===o?await this.unloadConfig(t):"restore"===o&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}async configureToolModel(i){const n=d[i];if(!n)return!1;const l="claude-code"===i,p=s(i),m=l||!p?c.t("wizard.model_builtin_default"):p;this.resetScreen(),this.createBox(n.displayName+" · "+c.t("wizard.config_models"));const u=[{name:"✅ "+c.t("wizard.use_default_model",{model:m}),value:{type:"default"}},{name:"🌐 "+c.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+c.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}],{choice:g}=await this.promptWithHints([{type:"list",name:"choice",message:c.t("wizard.select_tool_model",{tool:n.displayName}),choices:u}]);if("back"===g.type)return!1;if("default"===g.type)o.setToolModel(i,null);else if("preset"===g.type)o.setToolModel(i,r(g.preset));else if("custom"===g.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!l&&p?p:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===g.type){const n=o.getApiKey();if(!n)return console.log(t.red("\n"+c.t("wizard.api_key_required"))),!1;const r=a(c.t("wizard.fetching_models")).start(),s=await w(n);let d;if(r.stop(),s.ok&&s.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:c.t("wizard.select_fetched_model"),pageSize:20,choices:[...s.models,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;d=t}else{console.log(t.yellow("\n"+c.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!l&&p?p:void 0}]);d=(a||"").trim()}o.setToolModel(i,d?this.singleModelOverride(d):null)}return console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),l&&console.log(t.gray("💡 "+c.t("wizard.claude_model_hint"))),!0}async loadConfig(i){const n=o.getApiKey(),r=o.getModelsForTool(i);if(!n)return console.log(t.red("\n"+c.t("wizard.api_key_required"))),console.log(t.yellow(c.t("wizard.please_config_api_key_first"))),void await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]);if(!this.apiKeyValid){const o=a(c.t("wizard.validating_api_key")).start();if(!(await g(n)).valid)return o.fail(c.t("wizard.api_key_invalid")),console.log(t.red("\n"+c.t("wizard.please_reconfig_api_key"))),void await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]);this.apiKeyValid=!0,o.stop()}const s=a(c.t("wizard.loading_config")).start();try{l.loadPrivateConfig(i,o.getApiKeyForTool(i)||n,r),s.succeed(c.t("wizard.config_loaded",{tool:d[i]?.displayName||i}));const a=d[i]?.command;a&&console.log(t.green("\n"+c.t("wizard.run_tool_hint",{command:a})));const{nextAction:p}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===p&&(this.printGoodbye(),process.exit(0))}catch(a){s.fail(c.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_unload_config",{tool:d[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(c.t("wizard.unloading_config")).start();try{l.unloadPrivateConfig(o),n.succeed(c.t("wizard.config_unloaded"));const{nextAction:t}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===t&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(c.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async restoreConfig(o){const i=d[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(c.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(c.t("wizard.restore_no_backup")):(r.succeed(c.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))))}catch(e){r.fail(c.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async run(){const e=o.getConfig();if(e.api_key){const t=await g(e.api_key);this.apiKeyValid=t.valid}else this.apiKeyValid=!1;e.api_key&&this.apiKeyValid?await this.showMainMenu():await this.runFirstTimeSetup()}async showMainMenu(){for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(v("── "+c.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+c.t("wizard.settings")+" ──")),{name:"🔑 "+c.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🌐 "+c.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+c.t("wizard.select_tool"),choices:r}]);if("exit"===s.type)this.printGoodbye(),process.exit(0);else if("tool"===s.type)await this.configureTool(s.tool);else if("apikey"===s.type){await this.configApiKey();const e=o.getApiKey();if(e){const t=await g(e);this.apiKeyValid=t.valid}else this.apiKeyValid=!1}else"language"===s.type&&await this.configLanguage()}}}export const wizard=Wizard.getInstance();
1
+ import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as d}from"./tool-manager.js";import{i18n as c,detectSystemLang as p}from"./i18n.js";import{createBorderLine as m,createContentLine as u}from"../utils/string-width.js";import{validateApiKey as g,fetchModels as _}from"./api-validator.js";import{readFileSync as w}from"fs";import{dirname as f,join as y}from"path";import{fileURLToPath as h}from"url";const z=f(h(import.meta.url)),v=t.hex("#FF8700");export class Wizard{static instance;BOX_WIDTH=63;validatedApiKeys=new Set;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(v.bold("\n"+m("╔","╗","═",this.BOX_WIDTH))),console.log(v.bold(u(e,"║","║",this.BOX_WIDTH,"center"))),console.log(v.bold(m("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(c.t("wizard.hint_navigate")),t.gray(c.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=65,t=c.t("wizard.banner_subtitle"),a=u(t,"║","║",e,"center"),o=u("","║","║",e,"center"),i=u(`API Helper v${function(){try{return JSON.parse(w(y(z,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[" ▄▀▄ █▀▄ █ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," █▀█ █▀ █ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(t=>u(t,"║","║",e,"center")),r=[m("╔","╗","═",e),o,...n,o,i,a,m("╚","╝","═",e)];console.log(v.bold("\n"+r.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{try{return Boolean(l.detectCurrentConfig(e.name).apiKey)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=c.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+c.t("wizard.goodbye_claude_hint"))}else a=c.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a))}async runFirstTimeSetup(){const e="zh_CN"===p();e&&(o.setLang("zh_CN"),c.setLocale("zh_CN")),this.resetScreen(),console.log(v.bold("\n"+c.t("wizard.welcome"))),console.log(t.gray(c.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(c.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_language"));const a=c.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+c.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void c.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(c.t(e.name)!==e.name?c.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+c.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+c.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:c.t("wizard.custom_model_mode"),choices:[{name:c.t("wizard.custom_model_single"),value:"single"},{name:c.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:c.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:c.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:c.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:c.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:c.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+c.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(c.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+c.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:c.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:c.t("wizard.load_config_now"),choices:[{name:c.t("wizard.load_config_yes"),value:!0},{name:c.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configApiKey(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+c.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+c.t("wizard.api_key_get_hint",{url:"https://www.vibeapi.cn/console/token"}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:c.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(c.t("wizard.validating_api_key")).start(),r=await g(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+c.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+c.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+c.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+c.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=d[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+c.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:c.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(c.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=d[t];if(!a)return;this.createBox(a.displayName+" "+c.t("wizard.menu_title"));const o=[{name:"📥 "+c.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+c.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+c.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"}),o.push({name:"↩️ "+c.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+c.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"unload"===i?await this.unloadConfig(t):"clear-model"===i?await this.clearPinnedModels(t):"restore"===i&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}hasConfiguredApiKey(){return!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+c.t("wizard.api_key_required"))),console.log(t.yellow(c.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(c.t("wizard.validating_api_key")).start(),n=await g(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(c.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?c.t("wizard.api_key_invalid"):c.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+c.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=d[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]),!1;const p="claude-code"===i,m=s(i),u=p||!m?c.t("wizard.model_builtin_default"):m;this.resetScreen(),this.createBox(n.displayName+" · "+c.t("wizard.config_models"));const g=[{name:"✅ "+c.t("wizard.use_default_model",{model:u}),value:{type:"default"}},{name:"🌐 "+c.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+c.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}],{choice:w}=await this.promptWithHints([{type:"list",name:"choice",message:c.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===w.type)return!1;if("default"===w.type)o.setToolModel(i,null);else if("preset"===w.type)o.setToolModel(i,r(w.preset));else if("custom"===w.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===w.type){const n=a(c.t("wizard.fetching_models")).start(),r=await _(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:c.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+c.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}return console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),p&&console.log(t.gray("💡 "+c.t("wizard.claude_model_hint"))),!0}async loadConfig(i){const n=o.getModelsForTool(i),r=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!r)return void await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]);const s=a(c.t("wizard.loading_config")).start();try{l.loadPrivateConfig(i,r,n),s.succeed(c.t("wizard.config_loaded",{tool:d[i]?.displayName||i}));const a=d[i]?.command;a&&console.log(t.green("\n"+c.t("wizard.run_tool_hint",{command:a})));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){s.fail(c.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=d[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+c.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+c.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(c.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_unload_config",{tool:d[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(c.t("wizard.unloading_config")).start();try{l.unloadPrivateConfig(o),n.succeed(c.t("wizard.config_unloaded"));const{nextAction:t}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===t&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(c.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async restoreConfig(o){const i=d[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(c.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(c.t("wizard.restore_no_backup")):(r.succeed(c.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))))}catch(e){r.fail(c.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(v("── "+c.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+c.t("wizard.settings")+" ──")),{name:"🔑 "+c.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🌐 "+c.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+c.t("wizard.select_tool"),choices:r}]);"exit"===s.type?(this.printGoodbye(),process.exit(0)):"tool"===s.type?await this.configureTool(s.tool):"apikey"===s.type?await this.configApiKey():"language"===s.type&&await this.configLanguage()}else await this.runFirstTimeSetup()}}export const wizard=Wizard.getInstance();
@@ -59,6 +59,7 @@
59
59
  "retry_input": "Retry input",
60
60
  "action_load_config": "Apply Configuration - (Apply your API configuration to {{tool}})",
61
61
  "action_unload_config": "Remove Configuration - (Remove API configuration from {{tool}})",
62
+ "action_clear_model": "Clear Model Settings - (Let {{tool}} use its built-in default; API key kept)",
62
63
  "action_view_config": "Current Configuration",
63
64
  "action_back": "Back",
64
65
  "action_exit": "Exit",
@@ -68,6 +69,7 @@
68
69
  "run_tool_hint": "✅ Now run `{{command}}` to start using your config",
69
70
  "config_failed": "Failed to load configuration",
70
71
  "confirm_unload_config": "Are you sure you want to remove configuration from {{tool}}?",
72
+ "confirm_clear_model": "Clear the pinned model settings for {{tool}}? (your API key, hooks and MCP config stay untouched)",
71
73
  "unloading_config": "Removing configuration...",
72
74
  "config_unloaded": "Configuration removed",
73
75
  "config_unload_failed": "Failed to remove configuration",
@@ -260,7 +262,12 @@
260
262
  "confirm_restore": "Restore {{tool}} to before the last change? (the current state is backed up first)",
261
263
  "config_restored": "{{tool}} config restored",
262
264
  "restore_no_backup": "{{tool}} has no backup to restore",
263
- "tool_backups_title": "{{tool}} config backups"
265
+ "tool_backups_title": "{{tool}} config backups",
266
+ "clear_model_dry_run": "Would clear the pinned model settings for {{tool}} (API key kept)",
267
+ "clear_model_done": "Cleared the pinned model settings for {{tool}}; it now uses its built-in default:",
268
+ "clear_model_none": "{{tool}} has no pinned model settings — nothing to clear",
269
+ "clear_model_unsupported": "{{tool}} does not support clearing model settings",
270
+ "clear_model_restart_hint": "Restart {{tool}} to apply (your API key, hooks and MCP config were left untouched)"
264
271
  },
265
272
  "logs": {
266
273
  "directory": "Logs directory: {{dir}}",
@@ -59,6 +59,7 @@
59
59
  "retry_input": "重新输入",
60
60
  "action_load_config": "应用配置 - (把你的 API 配置写入 {{tool}})",
61
61
  "action_unload_config": "移除配置 - (从 {{tool}} 删掉 API 配置)",
62
+ "action_clear_model": "清除模型设置 - (让 {{tool}} 用自带默认模型,保留 API Key)",
62
63
  "action_view_config": "当前配置",
63
64
  "action_back": "返回",
64
65
  "action_exit": "退出",
@@ -68,6 +69,7 @@
68
69
  "run_tool_hint": "✅ 现在运行 `{{command}}` 就能用上你的配置了",
69
70
  "config_failed": "配置装载失败",
70
71
  "confirm_unload_config": "确认从 {{tool}} 移除配置吗?",
72
+ "confirm_clear_model": "确认清除 {{tool}} 钉死的模型设置吗?(API Key 与你的 hooks / MCP 配置保持不变)",
71
73
  "unloading_config": "正在移除配置...",
72
74
  "config_unloaded": "配置已移除",
73
75
  "config_unload_failed": "移除配置失败",
@@ -260,7 +262,12 @@
260
262
  "confirm_restore": "确定把 {{tool}} 还原到上次更改前吗?(当前状态会先自动备份)",
261
263
  "config_restored": "{{tool}} 配置已还原",
262
264
  "restore_no_backup": "{{tool}} 没有可还原的备份",
263
- "tool_backups_title": "{{tool}} 的配置备份"
265
+ "tool_backups_title": "{{tool}} 的配置备份",
266
+ "clear_model_dry_run": "将清除 {{tool}} 中钉死的模型设置(保留 API Key)",
267
+ "clear_model_done": "已清除 {{tool}} 钉死的模型设置,恢复使用工具自带默认模型:",
268
+ "clear_model_none": "{{tool}} 当前没有钉死的模型设置,无需清理",
269
+ "clear_model_unsupported": "{{tool}} 不支持清除模型设置",
270
+ "clear_model_restart_hint": "请重启 {{tool}} 后生效(API Key 与你的 hooks / MCP 配置未改动)"
264
271
  },
265
272
  "logs": {
266
273
  "directory": "日志目录: {{dir}}",
@@ -9,6 +9,6 @@
9
9
  "commit": "",
10
10
  "pr": ""
11
11
  },
12
- "model": "opus[1m]",
13
- "showThinkingSummaries": true
12
+ "showThinkingSummaries": true,
13
+ "model": "opus[1m]"
14
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/api-helper",
3
- "version": "0.0.35",
3
+ "version": "0.0.37",
4
4
  "description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode) with one unified config",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,6 +9,6 @@
9
9
  "commit": "",
10
10
  "pr": ""
11
11
  },
12
- "model": "opus[1m]",
13
- "showThinkingSummaries": true
12
+ "showThinkingSummaries": true,
13
+ "model": "opus[1m]"
14
14
  }