@vibeapi/cli 0.0.43 → 0.0.45

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
@@ -1,6 +1,6 @@
1
1
  # @vibeapi/api-helper
2
2
 
3
- > 一条命令,在 Claude Code / Codex / OpenClaw / Hermes / AtomCode / Grok Build 之间统一配置 API Key 与模型。
3
+ > 一条命令,在 Claude Code / Claude Desktop / Codex / OpenClaw / Hermes / AtomCode / Grok Build 之间统一配置 API Key 与模型。
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@vibeapi/api-helper.svg)](https://www.npmjs.com/package/@vibeapi/api-helper)
6
6
  [![Node](https://img.shields.io/node/v/@vibeapi/api-helper.svg)](https://nodejs.org)
@@ -36,10 +36,10 @@ npx --yes --registry https://npm.cnb.cool/vibeapi/npm/-/packages/ @vibeapi/cli@l
36
36
  vibecli setup --key sk-xxx
37
37
  ```
38
38
 
39
- 拿到的是两个分组的令牌(如 Claude 分组 + GPT 分组),就各配各的:
39
+ `--key` 会按令牌能访问的模型分组自动配置:有 Claude 模型配 Claude Code(机器上装了 Claude Desktop 也一并配好)、有 GPT 模型配 Codex、有 Grok 模型配 Grok Build。拿到的是多个分组的令牌(如 Claude 分组 + GPT 分组 + Grok 分组),就各配各的:
40
40
 
41
41
  ```bash
42
- vibecli setup --claudekey sk-a --codexkey sk-b
42
+ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c --desktopkey sk-d
43
43
  ```
44
44
 
45
45
  Codex 同时开启 1M 上下文时,末尾加最短参数 `--1m`;已经配置过的用户无需再次输入 Key。单独执行 `--1m` 只幂等更新上下文字段,不改写 Codex 凭据:
@@ -50,7 +50,7 @@ vibecli setup --1m
50
50
  vibecli setup --no-1m
51
51
  ```
52
52
 
53
- 配完直接运行 `claude` / `codex` 即可。
53
+ 配完直接运行 `claude` / `codex` / `grok` 即可。
54
54
 
55
55
  ### 方式 B:交互式向导
56
56
 
@@ -76,7 +76,7 @@ vibecli config claude-code clear-model # 模型选择被钉住时,交还给
76
76
 
77
77
  ## 能做什么
78
78
 
79
- - **多工具一键配置**:把当前 API Key / 模型注入 Claude Code、Codex、OpenClaw、Hermes、AtomCode、Grok Build(OpenCode 暂时下架,`vibecli config opencode` 仍可用)。
79
+ - **多工具一键配置**:把当前 API Key / 模型注入 Claude Code、Claude Desktop(桌面端第三方推理模式,写应用本地配置库,无需管理员 / 描述文件)、Codex、OpenClaw、Hermes、AtomCode、Grok Build(OpenCode 暂时下架,`vibecli config opencode` 仍可用)。
80
80
  - **按工具选模型**:使用默认 / 实时从 API 拉取 / 内置预设 / 手动输入;每个工具可用不同模型。
81
81
  - Claude Code 用默认模型时不写 `ANTHROPIC_*_MODEL` 环境变量,顶层 `model` 由 `settings-template.json` 提供(当前为 `opus[1m]`);显式选择其它模型时会覆盖该模板值。Codex 默认 `gpt-6-astra`,Grok Build 默认 `grok-4.6`,其余工具默认 `gpt-5.5`。
82
82
  - Codex 默认启用 1M 上下文窗口(自动压缩阈值 900K);交互配置 Codex 时可取消。
@@ -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 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
+ 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?(await n.applyPrivateConfig(s,o,r.getModelsForTool(s)),console.log(e.green(i.t("config.tool_config_loaded",{tool:l[s].displayName}))),void n.applyNotes(s).forEach(o=>console.log(e.yellow(o)))):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)}
@@ -3,11 +3,14 @@
3
3
  *
4
4
  * vibecli setup --key sk-xxx
5
5
  * 校验 Key → 用它拉 /v1/models 探测能力:能访问 Claude 模型就配 Claude Code、
6
- * 能访问 GPT 模型就配 Codex;只有一边可用就只配那一边(打印原因)。
6
+ * 能访问 GPT 模型就配 Codex、能访问 Grok 模型就配 Grok Build;
7
+ * 哪些可用就配哪些(跳过的打印原因)。
7
8
  *
8
- * vibecli setup --claudekey sk-a --codexkey sk-b
9
- * 双令牌各配各的(如 Claude 分组 + GPT 分组),各自校验 + 能力核对,
9
+ * vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c --desktopkey sk-d
10
+ * 多令牌各配各的(如 Claude 分组 + GPT 分组 + Grok 分组),各自校验 + 能力核对,
10
11
  * 明文存入 config.yaml 的工具专属 Key。只给其中一个旗标就只配对应工具。
12
+ * `--desktopkey` 配 Claude Desktop(桌面端 3p 模式);`--key` 只在检测到已安装
13
+ * Claude Desktop 时才顺带配置它,不给没装桌面端的机器留下无主文件。
11
14
  *
12
15
  * vibecli setup --1m
13
16
  * 复用当前 profile 已保存的 Codex Key,开启 1M 上下文并重新应用配置。
@@ -21,6 +24,8 @@ export interface SetupCommandOptions {
21
24
  key?: string;
22
25
  claudekey?: string;
23
26
  codexkey?: string;
27
+ grokkey?: string;
28
+ desktopkey?: string;
24
29
  all?: boolean;
25
30
  oneMillion?: boolean;
26
31
  }
@@ -1 +1 @@
1
- import e from"chalk";import{i18n as o}from"../lib/i18n.js";import{configManager as t}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as l}from"../lib/tool-manager.js";import{validateApiKey as s,fetchModels as i}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as n,setCodexOneMillionContext as d}from"../lib/tools/codex.js";async function r(e){const o=await i(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),probed:!0}}function c(o){console.log(e.green(o))}function p(o){console.log(e.yellow(o))}function u(o){console.error(e.red(o))}async function m(t,a){console.log(e.gray(o.t("setup.validating",{label:t})));const l=await s(a);return!!l.valid||(u(o.t("setup.invalid_key",{label:t,reason:l.message||l.error||"invalid"})),!1)}function f(s,i,d){const r=l[s];try{"codex"===s&&void 0!==d&&t.setToolSetting("codex",n,d),a.loadPrivateConfig(s,i,t.getModelsForTool(s))}catch(e){return u(o.t("setup.apply_failed",{tool:r.displayName,reason:e instanceof Error?e.message:String(e)})),!1}return c(o.t("setup.configured",{tool:r.displayName,command:r.command})),"codex"===s&&void 0!==d&&c(o.t(d?"setup.codex_1m_enabled":"setup.codex_1m_disabled")),a.isToolInstalled(s)||console.log(e.gray(o.t("setup.install_hint",{tool:r.displayName,install:r.installCommand}))),!0}export async function setupCommand(e){const a=e.key?.trim(),s=e.claudekey?.trim(),i=e.codexkey?.trim(),y=void 0!==e.oneMillion;if(!a&&!s&&!i&&y){if(!t.getApiKeyForTool("codex"))return u(o.t("setup.codex_1m_missing_key")),void(process.exitCode=1);try{const a=!0===e.oneMillion;t.setToolSetting("codex",n,a),d(a),c(o.t(a?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){u(o.t("setup.apply_failed",{tool:l.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}return}if(!a&&!s&&!i)return u(o.t("setup.usage")),void(process.exitCode=1);let g=0,b=0;const _=new Set,x=[];s&&x.push({tool:"claude-code",label:"--claudekey",apiKey:s,need:"claude"}),i&&x.push({tool:"codex",label:"--codexkey",apiKey:i,need:"gpt"});for(const a of x){if(!await m(a.label,a.apiKey)){b+=1;continue}const l=await r(a.apiKey),s="claude"===a.need?l.hasClaude:l.hasGpt;!l.probed||s?(l.probed||p(o.t("setup.probe_failed",{label:a.label})),t.setToolApiKey(a.tool,a.apiKey),f(a.tool,a.apiKey,e.oneMillion)?(g+=1,_.add(a.tool)):b+=1):(u(o.t("claude"===a.need?"setup.key_no_claude":"setup.key_no_gpt",{label:a.label})),b+=1)}if(a){if(!await m("--key",a))return void(process.exitCode=1);t.setApiKey(a);const l=await r(a);if(l.probed||p(o.t("setup.probe_failed",{label:"--key"})),l.probed&&!l.hasClaude&&!l.hasGpt)return u(o.t("setup.key_no_family")),void(process.exitCode=1);const s=[{tool:"claude-code",capable:l.hasClaude,skipMsg:o.t("setup.skip_no_claude")},{tool:"codex",capable:l.hasGpt,skipMsg:o.t("setup.skip_no_gpt")}];for(const o of s)_.has(o.tool)||(o.capable?f(o.tool,a,e.oneMillion)?(g+=1,_.add(o.tool)):b+=1:p(o.skipMsg))}if(g>0){const e=[..._].map(e=>l[e]?.command).filter(Boolean).join(" / ");c("\n"+o.t("setup.done",{count:String(g),commands:e}))}(b>0||0===g)&&(process.exitCode=1)}
1
+ import e from"chalk";import{i18n as o}from"../lib/i18n.js";import{configManager as t}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as s}from"../lib/tool-manager.js";import{validateApiKey as l,fetchModels as i}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as n,setCodexOneMillionContext as d}from"../lib/tools/codex.js";async function p(e){const o=await i(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),hasGrok:t.some(e=>e.includes("grok")),probed:!0}}function r(e,o){return"claude"===o?e.hasClaude:"gpt"===o?e.hasGpt:e.hasGrok}function c(o){console.log(e.green(o))}function u(o){console.log(e.yellow(o))}function m(o){console.error(e.red(o))}async function y(t,a){console.log(e.gray(o.t("setup.validating",{label:t})));const s=await l(a);return!!s.valid||(m(o.t("setup.invalid_key",{label:t,reason:s.message||s.error||"invalid"})),!1)}async function k(l,i,d){const p=s[l];try{"codex"===l&&void 0!==d&&t.setToolSetting("codex",n,d),await a.applyPrivateConfig(l,i,t.getModelsForTool(l))}catch(e){return m(o.t("setup.apply_failed",{tool:p.displayName,reason:e instanceof Error?e.message:String(e)})),!1}return c(o.t("setup.configured",{tool:p.displayName,command:p.command})),"codex"===l&&void 0!==d&&c(o.t(d?"setup.codex_1m_enabled":"setup.codex_1m_disabled")),a.applyNotes(l).forEach(e=>u(" "+e)),a.isToolInstalled(l)||console.log(e.gray(o.t("setup.install_hint",{tool:p.displayName,install:p.installCommand}))),!0}export async function setupCommand(e){const l=e.key?.trim(),i=e.claudekey?.trim(),g=e.codexkey?.trim(),f=e.grokkey?.trim(),b=e.desktopkey?.trim(),_=void 0!==e.oneMillion;if(!l&&!i&&!g&&!f&&!b&&_){if(!t.getApiKeyForTool("codex"))return m(o.t("setup.codex_1m_missing_key")),void(process.exitCode=1);try{const a=!0===e.oneMillion;t.setToolSetting("codex",n,a),d(a),c(o.t(a?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){m(o.t("setup.apply_failed",{tool:s.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}return}if(!(l||i||g||f||b))return m(o.t("setup.usage")),void(process.exitCode=1);let h=0,x=0;const C=new Set,v=[];i&&v.push({tool:"claude-code",label:"--claudekey",apiKey:i,need:"claude"}),g&&v.push({tool:"codex",label:"--codexkey",apiKey:g,need:"gpt"}),f&&v.push({tool:"grok",label:"--grokkey",apiKey:f,need:"grok"}),b&&v.push({tool:"claude-desktop",label:"--desktopkey",apiKey:b,need:"claude"});for(const a of v){if(!await y(a.label,a.apiKey)){x+=1;continue}const l=await p(a.apiKey);!l.probed||r(l,a.need)?(l.probed||u(o.t("setup.probe_failed",{label:a.label})),t.setToolApiKey(a.tool,a.apiKey),await k(a.tool,a.apiKey,e.oneMillion)?(h+=1,C.add(a.tool)):x+=1):(m(o.t(`setup.key_no_${a.need}`,{label:a.label,tool:s[a.tool].displayName})),x+=1)}if(l){if(!await y("--key",l))return void(process.exitCode=1);t.setApiKey(l);const s=await p(l);if(s.probed||u(o.t("setup.probe_failed",{label:"--key"})),s.probed&&!s.hasClaude&&!s.hasGpt&&!s.hasGrok)return m(o.t("setup.key_no_family")),void(process.exitCode=1);const i=[{tool:"claude-code",capable:s.hasClaude,skipMsg:o.t("setup.skip_no_claude")},{tool:"codex",capable:s.hasGpt,skipMsg:o.t("setup.skip_no_gpt")},{tool:"grok",capable:s.hasGrok,skipMsg:o.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:s.hasClaude&&a.isToolInstalled("claude-desktop"),skipMsg:o.t("setup.skip_desktop_not_installed"),quiet:!s.hasClaude}];for(const o of i)C.has(o.tool)||(o.capable?await k(o.tool,l,e.oneMillion)?(h+=1,C.add(o.tool)):x+=1:o.quiet||u(o.skipMsg))}if(h>0){const e=[...C].map(e=>s[e]?.command).filter(Boolean).join(" / ");c("\n"+o.t("setup.done",{count:String(h),commands:e}))}(x>0||0===h)&&(process.exitCode=1)}
@@ -28,6 +28,27 @@ export declare function fetchModels(apiKey: string, options?: {
28
28
  timeoutMs?: number;
29
29
  fetchFn?: typeof fetch;
30
30
  }): Promise<FetchModelsResult>;
31
+ /** One row of the console's per-group Claude Desktop model table. */
32
+ export interface GatewayGroupModel {
33
+ name: string;
34
+ labelOverride?: string;
35
+ supports1m?: boolean;
36
+ }
37
+ export interface FetchGatewayStatusResult {
38
+ ok: boolean;
39
+ /** `claude_desktop_group_models`: group name → models the group serves to Claude Desktop. */
40
+ claudeDesktopGroupModels: Record<string, GatewayGroupModel[]>;
41
+ error?: string;
42
+ }
43
+ /**
44
+ * 拉取网关公开的 `/api/status`(无需鉴权),目前只取「桌面助手」的分组模型表——
45
+ * 这是站长维护的、哪些模型在哪个分组支持 1M 上下文的唯一权威来源。
46
+ * 任何失败都返回 ok=false,调用方回退到 models.json 里的静态规则。
47
+ */
48
+ export declare function fetchGatewayStatus(options?: {
49
+ timeoutMs?: number;
50
+ fetchFn?: typeof fetch;
51
+ }): Promise<FetchGatewayStatusResult>;
31
52
  /**
32
53
  * 验证 API Key 的有效性和网络连通性,遇到网络错误时按指数退避重试
33
54
  */
@@ -1 +1 @@
1
- import{logger as r}from"../utils/logger.js";import{configManager as e}from"./config.js";const t=r=>new Promise(e=>setTimeout(e,r));async function o(r,t,o){const a=`${e.getBaseUrl()}/v1/models`,i=new AbortController,n=setTimeout(()=>i.abort(),t);try{const e=await o(a,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:i.signal});if(401===e.status)return{valid:!1,error:"invalid_api_key",message:"API Key is invalid or expired"};if(e.ok){try{const r=await e.json();if(r&&"list"===r.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${e.status}: ${e.statusText}`}}catch(r){return r instanceof Error&&"AbortError"===r.name?{valid:!1,error:"network_error",message:"Request timeout"}:{valid:!1,error:"network_error",message:r instanceof Error?r.message:"Network connection failed"}}finally{clearTimeout(n)}}export async function fetchModels(t,o={}){const{timeoutMs:a=3e4,fetchFn:i=fetch}=o,n=`${e.getBaseUrl()}/v1/models`,s=new AbortController,l=setTimeout(()=>s.abort(),a);try{const r=await i(n,{method:"GET",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},signal:s.signal});if(!r.ok)return{ok:!1,models:[],error:`HTTP ${r.status}`};const e=await r.json();return{ok:!0,models:(Array.isArray(e?.data)?e.data:[]).map(r=>"string"==typeof r?r:r?.id).filter(r=>"string"==typeof r&&r.trim().length>0).map(r=>r.trim())}}catch(e){return r.logError("fetchModels",e),{ok:!1,models:[],error:e instanceof Error?e.message:"network error"}}finally{clearTimeout(l)}}export async function validateApiKey(e,a={}){const{timeoutMs:i=3e4,retries:n=2,backoffMs:s=500,fetchFn:l=fetch,sleepFn:c=t}=a;let d={valid:!1,error:"network_error",message:"Network connection failed"};for(let t=0;t<=n;t++){const a=await o(e,i,l);if(a.valid||"invalid_api_key"===a.error)return a.valid||r.logError("validateApiKey",new Error(a.message||"invalid_api_key")),a;d=a,t<n&&await c(s*Math.pow(2,t))}return r.logError("validateApiKey",new Error(d.message||"network_error")),d}
1
+ import{logger as e}from"../utils/logger.js";import{configManager as r}from"./config.js";const t=e=>new Promise(r=>setTimeout(r,e));async function o(e,t,o){const a=`${r.getBaseUrl()}/v1/models`,s=new AbortController,n=setTimeout(()=>s.abort(),t);try{const r=await o(a,{method:"GET",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},signal:s.signal});if(401===r.status)return{valid:!1,error:"invalid_api_key",message:"API Key is invalid or expired"};if(r.ok){try{const e=await r.json();if(e&&"list"===e.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${r.status}: ${r.statusText}`}}catch(e){return e instanceof Error&&"AbortError"===e.name?{valid:!1,error:"network_error",message:"Request timeout"}:{valid:!1,error:"network_error",message:e instanceof Error?e.message:"Network connection failed"}}finally{clearTimeout(n)}}export async function fetchModels(t,o={}){const{timeoutMs:a=3e4,fetchFn:s=fetch}=o,n=`${r.getBaseUrl()}/v1/models`,i=new AbortController,l=setTimeout(()=>i.abort(),a);try{const e=await s(n,{method:"GET",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},signal:i.signal});if(!e.ok)return{ok:!1,models:[],error:`HTTP ${e.status}`};const r=await e.json();return{ok:!0,models:(Array.isArray(r?.data)?r.data:[]).map(e=>"string"==typeof e?e:e?.id).filter(e=>"string"==typeof e&&e.trim().length>0).map(e=>e.trim())}}catch(r){return e.logError("fetchModels",r),{ok:!1,models:[],error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}export async function fetchGatewayStatus(t={}){const{timeoutMs:o=15e3,fetchFn:a=fetch}=t,s=`${r.getBaseUrl()}/api/status`,n=new AbortController,i=setTimeout(()=>n.abort(),o);try{const e=await a(s,{method:"GET",signal:n.signal});if(!e.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${e.status}`};const r=await e.json();let t=r?.data?.claude_desktop_group_models;if("string"==typeof t)try{t=JSON.parse(t)}catch{t=void 0}const o={};if(t&&"object"==typeof t&&!Array.isArray(t))for(const[e,r]of Object.entries(t))Array.isArray(r)&&(o[e]=r.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.name).map(e=>({name:e.name.trim(),..."string"==typeof e.labelOverride?{labelOverride:e.labelOverride}:{},..."boolean"==typeof e.supports1m?{supports1m:e.supports1m}:{}})).filter(e=>e.name.length>0));return{ok:!0,claudeDesktopGroupModels:o}}catch(r){return e.logError("fetchGatewayStatus",r),{ok:!1,claudeDesktopGroupModels:{},error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(i)}}export async function validateApiKey(r,a={}){const{timeoutMs:s=3e4,retries:n=2,backoffMs:i=500,fetchFn:l=fetch,sleepFn:c=t}=a;let u={valid:!1,error:"network_error",message:"Network connection failed"};for(let t=0;t<=n;t++){const a=await o(r,s,l);if(a.valid||"invalid_api_key"===a.error)return a.valid||e.logError("validateApiKey",new Error(a.message||"invalid_api_key")),a;u=a,t<n&&await c(i*Math.pow(2,t))}return e.logError("validateApiKey",new Error(u.message||"network_error")),u}
@@ -1 +1 @@
1
- import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as n}from"./wizard.js";import{langCommand as i,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 y}from"fs";import{join as f,dirname as b}from"path";import{fileURLToPath as k}from"url";import{setRuntimeOptions as v,detectColorPreferenceFromEnv as w,isColorEnabled as $,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as x}from"./env-cleanup.js";import{runStartupUpdate as C}from"./auto-update.js";export class Command{program;constructor(){const n=t.isFirstRun()?a():t.getLang();o.loadFromConfig(n),this.program=new e,this.setupProgram()}getVersion(){try{const e=k(import.meta.url),o=b(e),a=f(o,"../../package.json");return JSON.parse(y(a,"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("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await u({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,all:e.all,oneMillion:e["1m"]})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await i(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await i(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),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(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await p(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await l([e])}),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 e=>{const o=[];e.samples&&o.push("--samples",e.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 e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await n.configLanguage();break;case"apikey":case"api-key":await n.configApiKey();break;case"models":await n.configModels();break;default:{const o=[e];await c(o);break}}else await n.run()}),this.program.action(async()=>{await n.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.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 n.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=x();if(0===e.length||j())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log($()?h.yellow(a):a)}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&w();let n;v({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(h.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){n=e[o+1];break}if(a.startsWith("--base=")){n=a.slice(7);break}}n&&t.setBaseUrl(n)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await C(e)).handled)return;await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=$()?h.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
1
+ import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}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 y from"chalk";import{readFileSync as h}from"fs";import{join as f,dirname as k}from"path";import{fileURLToPath as b}from"url";import{setRuntimeOptions as v,detectColorPreferenceFromEnv as w,isColorEnabled as $,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as x}from"./env-cleanup.js";import{runStartupUpdate as C}from"./auto-update.js";export class Command{program;constructor(){const i=t.isFirstRun()?a():t.getLang();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=b(import.meta.url),o=k(e),a=f(o,"../../package.json");return JSON.parse(h(a,"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("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await u({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"]})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),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(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await p(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await l([e])}),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 e=>{const o=[];e.samples&&o.push("--samples",e.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 e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){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=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${y.bold(o.t("cli.examples"))}:\n ${y.gray("$ vibecli # Interactive main menu")}\n ${y.gray("$ vibecli init # Run first-time setup wizard")}\n ${y.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${y.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${y.gray("$ vibecli status --json # Machine-readable status")}\n ${y.gray("$ vibecli config profile list")}\n ${y.gray("$ vibecli config profile create work")}\n ${y.gray("$ vibecli config profile use work")}\n ${y.gray("$ vibecli config preset save fast")}\n ${y.gray("$ vibecli config backup create snapshot")}\n ${y.gray("$ vibecli auth <token> --non-interactive")}\n ${y.gray("$ vibecli auth revoke --yes")}\n ${y.gray("$ vibecli logs path")}\n ${y.gray("$ vibecli completion bash")}\n ${y.gray("$ vibecli bench --samples 3")}\n ${y.gray("$ vibecli update")}\n ${y.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=x();if(0===e.length||j())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log($()?y.yellow(a):a)}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&w();let i;v({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(y.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await C(e)).handled)return;await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=$()?y.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -0,0 +1,42 @@
1
+ import { type GatewayGroupModel } from './api-validator.js';
2
+ /**
3
+ * What a key can actually reach on the gateway, resolved once per apply so
4
+ * adapters that advertise a model list (Claude Desktop) can offer every model
5
+ * the key is entitled to instead of only the profile's three slots.
6
+ */
7
+ export interface ModelCatalog {
8
+ /** Model ids returned by `/v1/models` for this key (trimmed, deduped). */
9
+ models: string[];
10
+ /** Base ids the gateway serves with a 1M context window for this key. */
11
+ oneMillion: string[];
12
+ /** Friendly labels the console configured per model, when any. */
13
+ labels: Record<string, string>;
14
+ /** Where `oneMillion` came from — surfaced in notes / diagnostics. */
15
+ oneMillionSource: 'gateway' | 'fallback';
16
+ /** Console group names whose model table matched this key (empty when none did). */
17
+ matchedGroups: string[];
18
+ }
19
+ /** Strip a bracketed context-variant suffix: `claude-opus-4-8[1m]` → `claude-opus-4-8`. */
20
+ export declare function baseModelId(id: string): string;
21
+ export declare function isClaudeModel(id: string): boolean;
22
+ /**
23
+ * Pick the console groups that could have issued this key. `/v1/models` never
24
+ * says which group a token belongs to, so match by model set: a group qualifies
25
+ * when it serves every Claude model the key sees. Exact matches win; otherwise
26
+ * every superset counts and disagreements are resolved conservatively (a model
27
+ * is 1M only if ALL candidate groups say so).
28
+ */
29
+ export declare function matchGroups(keyClaudeIds: string[], groups: Record<string, GatewayGroupModel[]>): string[];
30
+ /**
31
+ * Compute the 1M set for `claudeIds`. Gateway group table first (operator
32
+ * maintained), else the static `models.json` fallback; then the operator's
33
+ * hard exclusions (e.g. Fable groups don't serve 1M Opus 4.6) on top of both.
34
+ */
35
+ export declare function resolveOneMillion(claudeIds: string[], groups: Record<string, GatewayGroupModel[]> | null): Pick<ModelCatalog, 'oneMillion' | 'labels' | 'oneMillionSource' | 'matchedGroups'>;
36
+ /**
37
+ * Fetch `/v1/models` (+ the console's group table) and build the catalog. Never
38
+ * throws: returns null when the model list itself cannot be fetched, so callers
39
+ * fall back to the profile's slots; a failed status fetch only downgrades the
40
+ * 1M source to the static fallback.
41
+ */
42
+ export declare function resolveModelCatalog(apiKey: string): Promise<ModelCatalog | null>;
@@ -0,0 +1 @@
1
+ import{logger as e}from"../utils/logger.js";import{fetchModels as o,fetchGatewayStatus as t}from"./api-validator.js";import{getClaudeDesktopRules as l}from"./model-registry.js";export function baseModelId(e){return e.replace(/\[[^\]]*\]$/,"").trim()}export function isClaudeModel(e){return/claude/i.test(baseModelId(e))}export function matchGroups(e,o){const t=new Set(e.map(baseModelId));if(0===t.size)return[];const l=[],r=[];for(const[e,n]of Object.entries(o)){const o=new Set(n.map(e=>baseModelId(e.name)).filter(isClaudeModel));[...t].every(e=>o.has(e))&&(o.size===t.size?l:r).push(e)}return l.length>0?l:r}export function resolveOneMillion(e,o){const t=l(),r=[...new Set(e.map(baseModelId))],n=new Set(e.filter(e=>/\[1m\]/i.test(e)).map(baseModelId)),s={};let a,i="fallback",d=[];if(o&&(d=matchGroups(r,o)),d.length>0){i="gateway",a=new Set(r);for(const e of d){const t=new Map(o[e].map(e=>[baseModelId(e.name),e]));for(const e of r){const o=t.get(e);o?.labelOverride&&!s[e]&&(s[e]=o.labelOverride),!0!==o?.supports1m&&a.delete(e)}}}else{const e=new Set(t.oneMillionFallback);a=new Set(r.filter(o=>e.has(o)))}for(const e of n)a.add(e);for(const e of t.oneMillionExclusions)if(r.some(o=>o.startsWith(e.whenPrefixPresent)))for(const o of e.models)a.delete(o);return{oneMillion:[...a],labels:s,oneMillionSource:i,matchedGroups:d}}export async function resolveModelCatalog(l){try{const[e,r]=await Promise.all([o(l),t()]);if(!e.ok||0===e.models.length)return null;const n=[...new Set(e.models.map(e=>e.trim()).filter(Boolean))],s=n.filter(isClaudeModel);return{models:n,...resolveOneMillion(s,r.ok?r.claudeDesktopGroupModels:null)}}catch(o){return e.logError("resolveModelCatalog",o),null}}
@@ -12,6 +12,19 @@ export interface ModelPreset {
12
12
  models?: Models;
13
13
  recommended?: boolean;
14
14
  }
15
+ /** Gateway-specific rules for the Claude Desktop model catalog. */
16
+ export interface ClaudeDesktopRules {
17
+ /** Models known to serve a 1M context when the gateway's group table is unavailable. */
18
+ oneMillionFallback: string[];
19
+ /**
20
+ * Groups that carry a model with `whenPrefixPresent` do NOT serve 1M for
21
+ * `models` even if the group table says so (e.g. Fable groups: no 1M Opus 4.6).
22
+ */
23
+ oneMillionExclusions: Array<{
24
+ whenPrefixPresent: string;
25
+ models: string[];
26
+ }>;
27
+ }
15
28
  export interface ModelRegistryData {
16
29
  defaultModels: Models;
17
30
  /** Default model for non-Claude tools (Codex/OpenCode/OpenClaw/Hermes/AtomCode/Grok). */
@@ -19,6 +32,7 @@ export interface ModelRegistryData {
19
32
  /** Per-tool default model. Empty string (e.g. claude-code) means "use the tool's built-in default". */
20
33
  toolDefaults?: Record<string, string>;
21
34
  presets: ModelPreset[];
35
+ claudeDesktop?: Partial<ClaudeDesktopRules>;
22
36
  }
23
37
  export declare function getDefaultModels(): Models;
24
38
  /** The model non-Claude tools use unless the user configured something else. */
@@ -31,4 +45,5 @@ export declare function getDefaultToolModel(): string;
31
45
  export declare function getToolDefaultModel(toolName: string): string;
32
46
  export declare function getPresets(): ModelPreset[];
33
47
  export declare function resolvePreset(preset: ModelPreset): Models;
48
+ export declare function getClaudeDesktopRules(): ClaudeDesktopRules;
34
49
  export declare function reloadRegistry(): void;
@@ -1 +1 @@
1
- import{dirname as o,join as e}from"path";import{fileURLToPath as t}from"url";import{existsSync as r,readFileSync as l}from"fs";const n=o(t(import.meta.url));let s=null;function u(){return s||(s=function(){const o=[e(n,"..","..","models.json"),e(n,"..","models.json"),e(n,"models.json")];for(const e of o)if(r(e))return JSON.parse(l(e,"utf-8"));throw new Error(`models.json not found, searched: ${o.join(", ")}`)}()),s}export function getDefaultModels(){return{...u().defaultModels}}export function getDefaultToolModel(){return u().defaultToolModel?.trim()||"gpt-5.5"}export function getToolDefaultModel(o){const e=u().toolDefaults;return e&&Object.prototype.hasOwnProperty.call(e,o)?(e[o]??"").trim():getDefaultToolModel()}export function getPresets(){return[...u().presets]}export function resolvePreset(o){return o.models?{...o.models}:o.model?{haiku:o.model,sonnet:o.model,opus:o.model,reasoning:o.model}:getDefaultModels()}export function reloadRegistry(){s=null}
1
+ import{dirname as o,join as e}from"path";import{fileURLToPath as t}from"url";import{existsSync as l,readFileSync as n}from"fs";const r=o(t(import.meta.url));let s=null;function u(){return s||(s=function(){const o=[e(r,"..","..","models.json"),e(r,"..","models.json"),e(r,"models.json")];for(const e of o)if(l(e))return JSON.parse(n(e,"utf-8"));throw new Error(`models.json not found, searched: ${o.join(", ")}`)}()),s}export function getDefaultModels(){return{...u().defaultModels}}export function getDefaultToolModel(){return u().defaultToolModel?.trim()||"gpt-5.5"}export function getToolDefaultModel(o){const e=u().toolDefaults;return e&&Object.prototype.hasOwnProperty.call(e,o)?(e[o]??"").trim():getDefaultToolModel()}export function getPresets(){return[...u().presets]}export function resolvePreset(o){return o.models?{...o.models}:o.model?{haiku:o.model,sonnet:o.model,opus:o.model,reasoning:o.model}:getDefaultModels()}export function getClaudeDesktopRules(){const o=u().claudeDesktop??{};return{oneMillionFallback:[...o.oneMillionFallback??[]],oneMillionExclusions:(o.oneMillionExclusions??[]).map(o=>({...o,models:[...o.models]}))}}export function reloadRegistry(){s=null}
@@ -1,5 +1,6 @@
1
1
  import { type VibecliModels } from './config.js';
2
2
  import { type BackupEntry, type RestoreResult } from './tools/backup.js';
3
+ import { type ModelCatalog } from './model-catalog.js';
3
4
  export interface ToolRestoreEntry {
4
5
  file: string;
5
6
  result: RestoreResult;
@@ -29,7 +30,15 @@ export declare class ToolManager {
29
30
  installTool(toolName: string): Promise<void>;
30
31
  getToolConfig(toolName: string): any;
31
32
  updateToolConfig(toolName: string, config: any): void;
32
- loadPrivateConfig(toolName: string, apiKey: string, models: VibecliModels): void;
33
+ loadPrivateConfig(toolName: string, apiKey: string, models: VibecliModels, catalog?: ModelCatalog): void;
34
+ /**
35
+ * `loadPrivateConfig` plus the network lookup adapters with
36
+ * `wantsModelCatalog` need (what the key can reach, which models are 1M).
37
+ * A failed lookup degrades to the profile's slots instead of failing the apply.
38
+ */
39
+ applyPrivateConfig(toolName: string, apiKey: string, models: VibecliModels): Promise<void>;
40
+ /** Post-apply follow-ups the adapter wants shown to the user (may be empty). */
41
+ applyNotes(toolName: string): string[];
33
42
  unloadPrivateConfig(toolName: string): void;
34
43
  /**
35
44
  * Clear the tool's pinned model, keeping its credentials. Also drops this
@@ -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,settings:a.getToolSettings(o)})}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
+ 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";import{resolveModelCatalog as p}from"./model-catalog.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;const l=r(n);if(l?.isInstalled)try{return l.isInstalled()}catch{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:p}=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"===p)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,l){const e=r(o);if(!e)throw new Error(`Unknown tool: ${o}`);e.loadPrivateConfig({apiKey:n,baseUrl:a.getBaseUrl(),models:t,settings:a.getToolSettings(o),...l?{catalog:l}:{}})}async applyPrivateConfig(o,n,t){const l=r(o);if(!l)throw new Error(`Unknown tool: ${o}`);const e=l.wantsModelCatalog?await p(n):null;e?this.loadPrivateConfig(o,n,t,e):this.loadPrivateConfig(o,n,t)}applyNotes(o){const n=r(o);if(!n?.applyNotes)return[];try{return n.applyNotes()}catch(n){return i.logError(`ToolManager.applyNotes(${o})`,n),[]}}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();
@@ -0,0 +1,45 @@
1
+ import { type ToolAdapter } from './types.js';
2
+ import type { VibecliModels } from '../config.js';
3
+ import { type ModelCatalog } from '../model-catalog.js';
4
+ /** One model as advertised to Claude Desktop's picker (`inferenceModels[]`). */
5
+ export interface InferenceModel {
6
+ name: string;
7
+ labelOverride: string;
8
+ supports1m?: boolean;
9
+ anthropicFamilyTier?: string;
10
+ isFamilyDefault?: boolean;
11
+ }
12
+ /**
13
+ * The app's user-data directory for 3p mode. Honors `CLAUDE_USER_DATA_DIR`
14
+ * (the app does too) and `VIBECLI_TEST_HOME` (so tests redirect it); otherwise
15
+ * mirrors Electron's per-platform `userData` + the `-3p` suffix.
16
+ */
17
+ export declare function claudeDesktopUserDataDir(): string;
18
+ /** `Opus 4.8`, `Fable 5.1`, `Haiku 4.5` (date suffixes dropped); raw id when unparsable. */
19
+ export declare function humanizeModelId(base: string): string;
20
+ /**
21
+ * Build Claude Desktop's `inferenceModels` list.
22
+ *
23
+ * With a catalog (what the key can reach on the gateway): every Claude model the
24
+ * key sees, so groups without Fable simply don't get Fable and groups with it
25
+ * do; `supports1m` follows the catalog (gateway group table + operator rules).
26
+ * Without one (offline): the profile's slots, `[1m]` suffix → `supports1m`.
27
+ *
28
+ * Order: the profile's main model first (Claude Desktop treats the first entry
29
+ * as the default), then by family (opus, fable, sonnet, haiku) newest-first.
30
+ * Each family's default is the model the profile slot points at, else its newest.
31
+ */
32
+ export declare function buildInferenceModels(models: VibecliModels, catalog?: ModelCatalog): InferenceModel[];
33
+ type RegRunner = (args: string[]) => string;
34
+ /** Test seam: swap the `reg.exe` invoker (there is no registry off Windows). */
35
+ export declare function setRegRunnerForTests(runner: RegRunner | null): void;
36
+ /**
37
+ * Where a managed (MDM / policy) configuration would live on this machine.
38
+ * Returns the first source found, or null. Managed config makes the app ignore
39
+ * the local library we write, so callers surface this to the user. On Windows
40
+ * the HKCU policy is ours by design, so only a machine policy (HKLM) counts —
41
+ * that one overrides HKCU and needs an administrator to remove.
42
+ */
43
+ export declare function detectManagedConfig(): string | null;
44
+ export declare const claudeDesktopAdapter: ToolAdapter;
45
+ export {};
@@ -0,0 +1 @@
1
+ import{chmodSync as e,existsSync as t,mkdirSync as o,readFileSync as n,rmSync as r}from"fs";import{dirname as s}from"path";import{join as a}from"path";import{randomUUID as i}from"crypto";import{execFileSync as l}from"child_process";import{userInfo as c}from"os";import{logger as d}from"../../utils/logger.js";import{i18n as p}from"../i18n.js";import{BRAND_NAME as u}from"../brand.js";import{backupBeforeWrite as f,atomicWriteFile as m}from"./backup.js";import{toolHome as g}from"./types.js";import{baseModelId as y,isClaudeModel as h}from"../model-catalog.js";const C=u,k=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"],b=/^[a-f0-9-]{36}$/;function D(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=g(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return k.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const A=()=>a(claudeDesktopUserDataDir(),"configLibrary"),w=()=>a(A(),"_meta.json"),M=e=>a(A(),`${e}.json`),_=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function E(e){if(!t(e))return null;const o=n(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return D(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function v(n,r){const a=s(n);t(a)||o(a,{recursive:!0,mode:448}),f(n),m(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){d.logError("claude-desktop.writeJson.chmod",e)}}function j(){const e=E(w());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>D(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function P(e){return e?.entries.find(e=>e.name===C&&b.test(e.id))}const S={opus:0,fable:1,sonnet:2,haiku:3};function L(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=L(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function O(e){return L(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>y(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let a;if(t&&t.models.some(h)){a=[...new Set(t.models.filter(h).map(y))];for(const e of t.oneMillion)r.add(e)}else{a=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=y(o);n&&(a.includes(n)||a.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const i=e=>S[O(e)??""]??9;a.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=i(e)-i(t);if(0!==n)return n;const r=L(e),s=L(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=O(e);t&&a.includes(e)&&!c.has(t)&&c.set(t,e)}return a.map(e=>{const t=O(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let I=null;function x(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!0,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!0}}const $="HKCU\\SOFTWARE\\Policies\\Claude",U="HKLM\\SOFTWARE\\Policies\\Claude",G="vibecli-claude-policy.reg",T=e=>l("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let K=T;export function setRegRunnerForTests(e){K=e??T}function N(){return"win32"===process.platform}function R(e){let t;try{t=K(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function F(){if(null===R($))return;const e=a(claudeDesktopUserDataDir(),G);try{t(s(e))||o(s(e),{recursive:!0,mode:448}),f(e),K(["export",$,e,"/y"])}catch(e){d.logError("claude-desktop.snapshotPolicy",e)}}const B=Object.keys(x({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}}));export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=c().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return R(U)?U:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return d.logError("claude-desktop.detectManagedConfig",e),null}}const H="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",z="https://claude.ai/download",J="darwin"===process.platform?`open ${z}`:"win32"===process.platform?`start ${z}`:`xdg-open ${z}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:H,installCommand:J,configPath:w(),isInstalled:function(){const e=g();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...k.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},wantsModelCatalog:!0,loadPrivateConfig(e){const t=j()??{appliedId:"",entries:[]};let o=P(t);o||(o={id:i(),name:C},t.entries.push(o));const n=x(e);I={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=E(M(o.id))??{};v(M(o.id),{...r,...n}),t.appliedId=o.id,v(w(),t);const s=E(_())??{};"3p"!==s.deploymentMode&&v(_(),{...s,deploymentMode:"3p"}),N()&&function(e){F();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];K(["add",$,"/v",t,"/t",e,"/d",n,"/f"])}}(n)},unloadPrivateConfig(){N()&&function(e){const t=R($);if(!t)return;F();let o=t.size;for(const n of e)t.has(n)&&(K(["delete",$,"/v",n,"/f"]),o-=1);o<=0&&K(["delete",$,"/f"])}(B);const e=j(),t=P(e);if(!e||!t)return;f(M(t.id)),r(M(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(f(w()),r(w(),{force:!0})):v(w(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(N()){const e=R($)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=P(j());if(!e)return{apiKey:null};const t=E(M(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return d.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=j(),t=P(e);return{meta:e,applied:t?E(M(t.id)):null,desktop:E(_()),...N()?{policy:Object.fromEntries(R($)??[])}:{}}}catch(e){return d.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;N()&&R($)?.has("inferenceModels")&&(F(),K(["delete",$,"/v","inferenceModels","/f"]),e=!0);const t=P(j()),o=t?E(M(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;v(M(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(I){const t=I.models.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", ");e.push(p.t(`tools.claude_desktop.models_written_${I.source}`,{count:String(I.models.length),list:t,groups:I.groups.join(" / ")}))}e.push(p.t("tools.claude_desktop.restart_hint")),N()&&e.push(p.t("tools.claude_desktop.registry_written",{key:$}));const t=detectManagedConfig();return t&&e.push(p.t("tools.claude_desktop.managed_conflict",{path:t})),e},backupPaths(){const e=[w(),_()];N()&&e.push(a(claudeDesktopUserDataDir(),G));try{const t=P(j());t&&e.push(M(t.id))}catch{}return e}};
@@ -1 +1 @@
1
- import{claudeCodeAdapter as o}from"./claude-code.js";import{opencodeAdapter as r}from"./opencode.js";import{codexAdapter as t}from"./codex.js";import{openclawAdapter as e}from"./openclaw.js";import{hermesAdapter as m}from"./hermes.js";import{atomcodeAdapter as p}from"./atomcode.js";import{grokAdapter as s}from"./grok.js";const n=[o,r,t,e,m,p,s];export const TOOL_ADAPTERS=Object.fromEntries(n.map(o=>[o.name,o]));export function getAdapter(o){return TOOL_ADAPTERS[o]}export function listAdapters(){return[...n]}
1
+ import{claudeCodeAdapter as o}from"./claude-code.js";import{opencodeAdapter as r}from"./opencode.js";import{codexAdapter as t}from"./codex.js";import{openclawAdapter as e}from"./openclaw.js";import{hermesAdapter as m}from"./hermes.js";import{atomcodeAdapter as p}from"./atomcode.js";import{grokAdapter as s}from"./grok.js";import{claudeDesktopAdapter as c}from"./claude-desktop.js";const i=[o,r,t,e,m,p,s,c];export const TOOL_ADAPTERS=Object.fromEntries(i.map(o=>[o.name,o]));export function getAdapter(o){return TOOL_ADAPTERS[o]}export function listAdapters(){return[...i]}
@@ -1,4 +1,5 @@
1
1
  import type { VibecliModels, VibecliToolSettings } from '../config.js';
2
+ import type { ModelCatalog } from '../model-catalog.js';
2
3
  /**
3
4
  * Home directory the tool adapters write under. Honors `VIBECLI_TEST_HOME` (the
4
5
  * same override the config layer uses) so tests can redirect ~/.codex, ~/.hermes,
@@ -15,6 +16,11 @@ export interface ToolApplyContext {
15
16
  baseUrl: string;
16
17
  models: VibecliModels;
17
18
  settings?: VibecliToolSettings;
19
+ /**
20
+ * Everything the key can reach on the gateway, when it could be fetched.
21
+ * Only resolved for adapters that set `wantsModelCatalog`; absent offline.
22
+ */
23
+ catalog?: ModelCatalog;
18
24
  }
19
25
  /**
20
26
  * One self-contained integration for a downstream coding tool.
@@ -36,6 +42,22 @@ export interface ToolAdapter {
36
42
  configPath: string;
37
43
  /** Hidden tools are still usable by id but omitted from menus. */
38
44
  hidden?: boolean;
45
+ /**
46
+ * Ask ToolManager to resolve `ctx.catalog` (network) before applying — for
47
+ * tools that advertise a model list rather than a single pinned model.
48
+ */
49
+ wantsModelCatalog?: boolean;
50
+ /**
51
+ * Custom installed-probe for tools without a CLI binary (GUI apps). When
52
+ * omitted, ToolManager runs `which`/`where` on `command`.
53
+ */
54
+ isInstalled?(): boolean;
55
+ /**
56
+ * Human-readable follow-ups to print after a successful `loadPrivateConfig`
57
+ * (e.g. "quit and reopen the app", "a managed policy overrides this").
58
+ * Already localized; callers just print them.
59
+ */
60
+ applyNotes?(): string[];
39
61
  /** Write this CLI's provider config into the tool. */
40
62
  loadPrivateConfig(ctx: ToolApplyContext): void;
41
63
  /** Remove this CLI's provider config from the tool, leaving other config intact. */
@@ -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 _}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as w}from"./tools/codex.js";import{readFileSync as f}from"fs";import{dirname as y,join as h}from"path";import{fileURLToPath as z}from"url";const v=y(z(import.meta.url)),x=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(x.bold("\n"+m("╔","╗","═",this.BOX_WIDTH))),console.log(x.bold(u(e,"║","║",this.BOX_WIDTH,"center"))),console.log(x.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(f(h(v,"..","..","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(x.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(x.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:f}=await this.promptWithHints([{type:"list",name:"choice",message:c.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===f.type)return!1;if("default"===f.type)o.setToolModel(i,null);else if("preset"===f.type)o.setToolModel(i,r(f.preset));else if("custom"===f.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"===f.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)}if("codex"===i){const t=o.getToolSetting(i,w),{enableOneMillionContext:a}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:c.t("wizard.codex_enable_1m_context"),default:!1!==t}]);o.setToolSetting(i,w,Boolean(a))}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(x("── "+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();
1
+ import e from"inquirer";import t from"chalk";import o from"ora";import{configManager as a,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 c}from"./tool-manager.js";import{i18n as d,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{CODEX_ONE_MILLION_CONTEXT_SETTING as _}from"./tools/codex.js";import{readFileSync as f}from"fs";import{dirname as y,join as h}from"path";import{fileURLToPath as z}from"url";const v=y(z(import.meta.url)),x=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(x.bold("\n"+m("╔","╗","═",this.BOX_WIDTH))),console.log(x.bold(u(e,"║","║",this.BOX_WIDTH,"center"))),console.log(x.bold(m("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(d.t("wizard.hint_navigate")),t.gray(d.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const o=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(o)}printBanner(){const e=65,t=d.t("wizard.banner_subtitle"),o=u(t,"║","║",e,"center"),a=u("","║","║",e,"center"),i=u(`API Helper v${function(){try{return JSON.parse(f(h(v,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[" ▄▀▄ █▀▄ █ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," █▀█ █▀ █ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(t=>u(t,"║","║",e,"center")),r=[m("╔","╗","═",e),a,...n,a,i,o,m("╚","╝","═",e)];console.log(x.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 o;if(e.length>0){const t=e.map(e=>e.command).join(" / ");o=d.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(o+=" "+d.t("wizard.goodbye_claude_hint"))}else o=d.t("wizard.goodbye_message");console.log(t.green("\n👋 "+o))}async runFirstTimeSetup(){const e="zh_CN"===p();e&&(a.setLang("zh_CN"),d.setLocale("zh_CN")),this.resetScreen(),console.log(x.bold("\n"+d.t("wizard.welcome"))),console.log(t.gray(d.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(d.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(d.t("wizard.select_language"));const o=d.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+d.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===o?t.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===o?t.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return a.setLang(i),void d.setLocale(i)}}buildPresetChoices(){const o=n(),a=o.filter(e=>e.recommended),i=o.filter(e=>!e.recommended),r=e=>({name:(d.t(e.name)!==e.name?d.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 a.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+d.t("wizard.recommended")+" ──"))),s.push(...a.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+d.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const o=a.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:d.t("wizard.custom_model_mode"),choices:[{name:d.t("wizard.custom_model_single"),value:"single"},{name:d.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:d.t("wizard.input_custom_model"),default:i.sonnet}]);a.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:d.t("wizard.model_haiku"),default:o.haiku},{type:"input",name:"sonnet",message:d.t("wizard.model_sonnet"),default:o.sonnet},{type:"input",name:"opus",message:d.t("wizard.model_opus"),default:o.opus},{type:"input",name:"reasoning",message:d.t("wizard.model_reasoning"),default:o.reasoning}]);a.setModels(t)}console.log(t.green("\n✓ "+d.t("wizard.models_saved")))}async configModels(o){const i=a.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(d.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+d.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+d.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+d.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:d.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(o?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return a.setModels(e),console.log(t.green("\n✓ "+d.t("wizard.models_saved"))),void(o?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:d.t("wizard.load_config_now"),choices:[{name:d.t("wizard.load_config_yes"),value:!0},{name:d.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configApiKey(){for(;;){this.resetScreen(),this.createBox(d.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+d.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+d.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:d.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:d.t("wizard.select_action"),choices:[{name:d.t("wizard.retry_input"),value:"retry"},{name:"<- "+d.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=o(d.t("wizard.validating_api_key")).start(),r=await g(i.trim());if(n.stop(),r.valid)return a.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+d.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+d.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+d.t("wizard.api_key_network_error")));const{action:o}=await e.prompt([{type:"list",name:"action",message:d.t("wizard.select_action"),choices:[{name:d.t("wizard.retry_input"),value:"retry"},{name:"<- "+d.t("wizard.nav_return"),value:"back"}]}]);if("back"===o)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(d.t("wizard.select_tool"));const o=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:a}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+d.t("wizard.select_tool"),choices:[...o,new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===a)this.printGoodbye(),process.exit(0);else if("back"===a)return;await this.configureTool(a,t)}}async configureTool(o,a=!1){const i=c[o];if(i){if(!l.isToolInstalled(o)){console.log(t.yellow("\n"+d.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:a}=await e.prompt([{type:"confirm",name:"install",message:d.t("wizard.install_tool_confirm"),default:!0}]);if(!a)return;try{await l.installTool(o)}catch{return void console.log(t.red(d.t("wizard.install_failed")))}}a?await this.configureToolModel(o)&&await this.loadConfig(o):await this.showToolMenu(o)}}async showToolMenu(t){for(;;){this.resetScreen();const o=c[t];if(!o)return;this.createBox(o.displayName+" "+d.t("wizard.menu_title"));const a=[{name:"📥 "+d.t("wizard.action_load_config",{tool:o.displayName}),value:"load"},{name:"📤 "+d.t("wizard.action_unload_config",{tool:o.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&a.push({name:"🧹 "+d.t("wizard.action_clear_model",{tool:o.displayName}),value:"clear-model"}),a.push({name:"↩️ "+d.t("wizard.action_restore_config",{tool:o.displayName}),value:"restore"},new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+d.t("wizard.select_action"),choices:a}]);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!!a.getApiKey()?.trim()||a.listToolApiKeys().some(e=>Boolean(a.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const a=e?.trim();if(!a)return console.log(t.red("\n"+d.t("wizard.api_key_required"))),console.log(t.yellow(d.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(a))return a;const i=o(d.t("wizard.validating_api_key")).start(),n=await g(a);if(n.valid)return this.validatedApiKeys.add(a),i.succeed(d.t("wizard.api_key_valid")),a;this.validatedApiKeys.delete(a);const r="invalid_api_key"===n.error?d.t("wizard.api_key_invalid"):d.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+d.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=c[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(a.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}]),!1;const p="claude-code"===i,m=s(i),u=p||!m?d.t("wizard.model_builtin_default"):m;this.resetScreen(),this.createBox(n.displayName+" · "+d.t("wizard.config_models"));const g=[{name:"✅ "+d.t("wizard.use_default_model",{model:u}),value:{type:"default"}},{name:"🌐 "+d.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+d.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:{type:"back"}}],{choice:f}=await this.promptWithHints([{type:"list",name:"choice",message:d.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===f.type)return!1;if("default"===f.type)a.setToolModel(i,null);else if("preset"===f.type)a.setToolModel(i,r(f.preset));else if("custom"===f.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:d.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]),o=(t||"").trim();a.setToolModel(i,o?this.singleModelOverride(o):null)}else if("fetch"===f.type){const n=o(d.t("wizard.fetching_models")).start(),r=await w(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:d.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+d.t("wizard.fetch_models_failed")));const{model:o}=await e.prompt([{type:"input",name:"model",message:d.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]);s=(o||"").trim()}a.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=a.getToolSetting(i,_),{enableOneMillionContext:o}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:d.t("wizard.codex_enable_1m_context"),default:!1!==t}]);a.setToolSetting(i,_,Boolean(o))}return console.log(t.green("\n✓ "+d.t("wizard.models_saved"))),p&&console.log(t.gray("💡 "+d.t("wizard.claude_model_hint"))),!0}async loadConfig(i){const n=a.getModelsForTool(i),r=await this.validatedKeyForConfiguration(a.getApiKeyForTool(i));if(!r)return void await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}]);const s=o(d.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,r,n),s.succeed(d.t("wizard.config_loaded",{tool:c[i]?.displayName||i}));const o=c[i]?.command;o&&console.log(t.green("\n"+d.t("wizard.run_tool_hint",{command:o})));for(const e of l.applyNotes(i))console.log(t.yellow("💡 "+e));const{nextAction:a}=await e.prompt([{type:"list",name:"nextAction",message:d.t("wizard.what_next"),choices:[{name:d.t("wizard.exit_and_start"),value:"exit"},{name:d.t("wizard.continue_config"),value:"continue"}]}]);"exit"===a&&(this.printGoodbye(),process.exit(0))}catch(o){s.fail(d.t("wizard.config_failed")),console.log(t.red(o instanceof Error?o.message:String(o))),await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}])}}async clearPinnedModels(o){const a=c[o]?.displayName||o,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:d.t("wizard.confirm_clear_model",{tool:a}),default:!1}]);if(i){try{const e=l.clearPinnedModels(o);0===e.length?console.log(t.yellow("\n"+d.t("config.clear_model_none",{tool:a}))):(console.log(t.green("\n✓ "+d.t("config.clear_model_done",{tool:a}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(d.t("config.clear_model_restart_hint",{tool:a}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}])}}async unloadConfig(a){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:d.t("wizard.confirm_unload_config",{tool:c[a]?.displayName||a}),default:!1}]);if(!i)return;const n=o(d.t("wizard.unloading_config")).start();try{l.unloadPrivateConfig(a),n.succeed(d.t("wizard.config_unloaded"));const{nextAction:t}=await e.prompt([{type:"list",name:"nextAction",message:d.t("wizard.what_next"),choices:[{name:d.t("wizard.exit_and_start"),value:"exit"},{name:d.t("wizard.continue_config"),value:"continue"}]}]);"exit"===t&&(this.printGoodbye(),process.exit(0))}catch(o){n.fail(d.t("wizard.config_unload_failed")),console.log(t.red(o instanceof Error?o.message:String(o))),await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}])}}async restoreConfig(a){const i=c[a]?.displayName||a,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:d.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=o(d.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(a).filter(e=>e.result.restored);0===e.length?r.warn(d.t("wizard.restore_no_backup")):(r.succeed(d.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))))}catch(e){r.fail(d.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const o=a.getApiKey()||"",i=o.length>4?"sk-****"+o.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(x("── "+d.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+d.t("wizard.settings")+" ──")),{name:"🔑 "+d.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🌐 "+d.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+d.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+d.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();
@@ -313,21 +313,34 @@
313
313
  "non_interactive_aborted": "Aborted - missing required input"
314
314
  },
315
315
  "setup": {
316
- "usage": "Usage: vibecli setup --key sk-xxx or vibecli setup --claudekey sk-a --codexkey sk-b or vibecli setup --1m|--no-1m",
316
+ "usage": "Usage: vibecli setup --key sk-xxx or vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c --desktopkey sk-d or vibecli setup --1m|--no-1m",
317
317
  "codex_1m_enabled": "Codex 1M context is enabled",
318
318
  "codex_1m_disabled": "Codex 1M context is disabled",
319
319
  "codex_1m_missing_key": "No saved Codex API key was found; configure one with --key or --codexkey first",
320
320
  "validating": "Validating API key for {{label}}...",
321
321
  "invalid_key": "API key for {{label}} is invalid: {{reason}}",
322
322
  "probe_failed": "Capability probe for {{label}} failed (network); continuing as if all families are available",
323
- "key_no_claude": "{{label}} cannot access Claude models (token group excludes Claude); skipped Claude Code",
324
- "key_no_gpt": "{{label}} cannot access GPT models (token group excludes GPT); skipped Codex",
325
- "key_no_family": "This key exposes neither Claude nor GPT models; check the token group and retry",
323
+ "key_no_claude": "{{label}} cannot access Claude models (token group excludes Claude); skipped {{tool}}",
324
+ "key_no_gpt": "{{label}} cannot access GPT models (token group excludes GPT); skipped {{tool}}",
325
+ "key_no_grok": "{{label}} cannot access Grok models (token group excludes Grok); skipped {{tool}}",
326
+ "key_no_family": "This key exposes no Claude, GPT or Grok models; check the token group and retry",
326
327
  "skip_no_claude": "This key has no Claude model group; skipped Claude Code",
327
328
  "skip_no_gpt": "This key has no GPT model group; skipped Codex",
329
+ "skip_no_grok": "This key has no Grok model group; skipped Grok Build",
328
330
  "configured": "√ {{tool}} configured (run `{{command}}` to start)",
329
331
  "apply_failed": "Failed to write {{tool}} config: {{reason}}",
330
332
  "install_hint": " Hint: {{tool}} not detected yet; install via {{install}}",
331
- "done": "Done: {{count}} tool(s) configured. Type {{commands}} in your terminal to start coding!"
333
+ "done": "Done: {{count}} tool(s) configured. Type {{commands}} in your terminal to start coding!",
334
+ "skip_desktop_not_installed": "Claude Desktop is not installed; skipped (after installing, run vibecli setup --desktopkey sk-xxx or use the wizard)"
335
+ },
336
+ "tools": {
337
+ "claude_desktop": {
338
+ "restart_hint": "Claude Desktop reads its configuration at launch only: fully quit it (⌘Q on macOS) and reopen for the change to take effect",
339
+ "managed_conflict": "Managed configuration found at {{path}}; Claude Desktop prefers it and ignores local settings. Remove that profile / policy first, then relaunch",
340
+ "registry_written": "Also written to the registry policy {{key}} as a backstop (same as the downloadable .reg); the in-app \"Configure Third-Party Inference\" window will show as managed, and unloading removes it too",
341
+ "models_written_gateway": "Wrote {{count}} models (1M flags from the gateway group table, matched groups: {{groups}}): {{list}}",
342
+ "models_written_fallback": "Wrote {{count}} models (gateway group table unavailable or no group matched; 1M flags from built-in rules): {{list}}",
343
+ "models_written_slots": "Wrote {{count}} models (offline: from the profile's model slots, gateway model list not fetched): {{list}}"
344
+ }
332
345
  }
333
346
  }
@@ -313,21 +313,34 @@
313
313
  "non_interactive_aborted": "已中止 - 缺少必要输入"
314
314
  },
315
315
  "setup": {
316
- "usage": "用法: vibecli setup --key sk-xxx 或 vibecli setup --claudekey sk-a --codexkey sk-b 或 vibecli setup --1m|--no-1m",
316
+ "usage": "用法: vibecli setup --key sk-xxx 或 vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c --desktopkey sk-d 或 vibecli setup --1m|--no-1m",
317
317
  "codex_1m_enabled": "Codex 1M 上下文已开启",
318
318
  "codex_1m_disabled": "Codex 1M 上下文已关闭",
319
319
  "codex_1m_missing_key": "未找到已保存的 Codex API Key;请先用 --key 或 --codexkey 完成配置",
320
320
  "validating": "正在验证 {{label}} 的 API Key...",
321
321
  "invalid_key": "{{label}} 的 API Key 无效: {{reason}}",
322
322
  "probe_failed": "{{label}} 的模型能力探测失败(网络原因),按全部可用继续",
323
- "key_no_claude": "{{label}} 无法访问 Claude 模型(令牌分组不含 Claude),已跳过 Claude Code",
324
- "key_no_gpt": "{{label}} 无法访问 GPT 模型(令牌分组不含 GPT),已跳过 Codex",
325
- "key_no_family": "该 Key 探测不到 Claude 或 GPT 模型,请确认令牌分组后重试",
323
+ "key_no_claude": "{{label}} 无法访问 Claude 模型(令牌分组不含 Claude),已跳过 {{tool}}",
324
+ "key_no_gpt": "{{label}} 无法访问 GPT 模型(令牌分组不含 GPT),已跳过 {{tool}}",
325
+ "key_no_grok": "{{label}} 无法访问 Grok 模型(令牌分组不含 Grok),已跳过 {{tool}}",
326
+ "key_no_family": "该 Key 探测不到 Claude / GPT / Grok 模型,请确认令牌分组后重试",
326
327
  "skip_no_claude": "该 Key 未开通 Claude 模型分组,已跳过 Claude Code",
327
328
  "skip_no_gpt": "该 Key 未开通 GPT 模型分组,已跳过 Codex",
329
+ "skip_no_grok": "该 Key 未开通 Grok 模型分组,已跳过 Grok Build",
328
330
  "configured": "√ {{tool}} 配置完成(运行 `{{command}}` 即可使用)",
329
331
  "apply_failed": "{{tool}} 配置写入失败: {{reason}}",
330
332
  "install_hint": " 提示: 尚未检测到 {{tool}},可运行 {{install}} 安装",
331
- "done": "完成: 已配置 {{count}} 个工具。终端输入 {{commands}} 即可开始编码!"
333
+ "done": "完成: 已配置 {{count}} 个工具。终端输入 {{commands}} 即可开始编码!",
334
+ "skip_desktop_not_installed": "未检测到已安装的 Claude Desktop,已跳过(装好后运行 vibecli setup --desktopkey sk-xxx 或在向导里配置)"
335
+ },
336
+ "tools": {
337
+ "claude_desktop": {
338
+ "restart_hint": "Claude Desktop 只在启动时读取配置:请完全退出(macOS 按 ⌘Q)再重新打开才会生效",
339
+ "managed_conflict": "检测到托管配置 {{path}},Claude Desktop 会优先使用它并忽略本地配置;请先移除该描述文件 / 策略再重启",
340
+ "registry_written": "已同步写入注册表 {{key}} 作为兜底(与官网下载的 .reg 等效);应用内「Configure Third-Party Inference」窗口会显示为托管状态,移除配置时会一并清理",
341
+ "models_written_gateway": "已写入 {{count}} 个模型(1M 标记依据网关分组表,匹配分组:{{groups}}):{{list}}",
342
+ "models_written_fallback": "已写入 {{count}} 个模型(网关分组表不可用或未匹配到分组,1M 标记按内置规则):{{list}}",
343
+ "models_written_slots": "已写入 {{count}} 个模型(离线:按当前 profile 的模型槽位,未拉取网关模型列表):{{list}}"
344
+ }
332
345
  }
333
346
  }
package/dist/models.json CHANGED
@@ -16,11 +16,46 @@
16
16
  "grok": "grok-4.6"
17
17
  },
18
18
  "presets": [
19
- { "name": "GPT-5.5", "model": "gpt-5.5" },
20
- { "name": "GLM-5.2", "model": "glm-5.2" },
21
- { "name": "MiniMax-M3", "model": "MiniMax-M3" },
22
- { "name": "Kimi-K2.6", "model": "kimi-k2.6" },
23
- { "name": "Qwen3.7-Max", "model": "qwen3.7-max" },
24
- { "name": "DeepSeek-V4-Pro", "model": "deepseek-v4-pro" }
25
- ]
19
+ {
20
+ "name": "GPT-5.5",
21
+ "model": "gpt-5.5"
22
+ },
23
+ {
24
+ "name": "GLM-5.2",
25
+ "model": "glm-5.2"
26
+ },
27
+ {
28
+ "name": "MiniMax-M3",
29
+ "model": "MiniMax-M3"
30
+ },
31
+ {
32
+ "name": "Kimi-K2.6",
33
+ "model": "kimi-k2.6"
34
+ },
35
+ {
36
+ "name": "Qwen3.7-Max",
37
+ "model": "qwen3.7-max"
38
+ },
39
+ {
40
+ "name": "DeepSeek-V4-Pro",
41
+ "model": "deepseek-v4-pro"
42
+ }
43
+ ],
44
+ "claudeDesktop": {
45
+ "oneMillionFallback": [
46
+ "claude-opus-4-8",
47
+ "claude-opus-4-7",
48
+ "claude-opus-4-6",
49
+ "claude-opus-5",
50
+ "claude-sonnet-5",
51
+ "claude-fable-5",
52
+ "claude-fable-5-1"
53
+ ],
54
+ "oneMillionExclusions": [
55
+ {
56
+ "whenPrefixPresent": "claude-fable-",
57
+ "models": ["claude-opus-4-6"]
58
+ }
59
+ ]
60
+ }
26
61
  }
package/models.json CHANGED
@@ -16,11 +16,46 @@
16
16
  "grok": "grok-4.6"
17
17
  },
18
18
  "presets": [
19
- { "name": "GPT-5.5", "model": "gpt-5.5" },
20
- { "name": "GLM-5.2", "model": "glm-5.2" },
21
- { "name": "MiniMax-M3", "model": "MiniMax-M3" },
22
- { "name": "Kimi-K2.6", "model": "kimi-k2.6" },
23
- { "name": "Qwen3.7-Max", "model": "qwen3.7-max" },
24
- { "name": "DeepSeek-V4-Pro", "model": "deepseek-v4-pro" }
25
- ]
19
+ {
20
+ "name": "GPT-5.5",
21
+ "model": "gpt-5.5"
22
+ },
23
+ {
24
+ "name": "GLM-5.2",
25
+ "model": "glm-5.2"
26
+ },
27
+ {
28
+ "name": "MiniMax-M3",
29
+ "model": "MiniMax-M3"
30
+ },
31
+ {
32
+ "name": "Kimi-K2.6",
33
+ "model": "kimi-k2.6"
34
+ },
35
+ {
36
+ "name": "Qwen3.7-Max",
37
+ "model": "qwen3.7-max"
38
+ },
39
+ {
40
+ "name": "DeepSeek-V4-Pro",
41
+ "model": "deepseek-v4-pro"
42
+ }
43
+ ],
44
+ "claudeDesktop": {
45
+ "oneMillionFallback": [
46
+ "claude-opus-4-8",
47
+ "claude-opus-4-7",
48
+ "claude-opus-4-6",
49
+ "claude-opus-5",
50
+ "claude-sonnet-5",
51
+ "claude-fable-5",
52
+ "claude-fable-5-1"
53
+ ],
54
+ "oneMillionExclusions": [
55
+ {
56
+ "whenPrefixPresent": "claude-fable-",
57
+ "models": ["claude-opus-4-6"]
58
+ }
59
+ ]
60
+ }
26
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode, Grok Build) with one unified config",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",