@vibeapi/cli 0.0.45 → 0.0.47
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 +2 -1
- package/dist/commands/auth.js +1 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/setup.d.ts +14 -0
- package/dist/commands/setup.js +1 -1
- package/dist/lib/claude-code-manager.d.ts +26 -4
- package/dist/lib/claude-code-manager.js +1 -1
- package/dist/lib/command.js +1 -1
- package/dist/lib/env-cleanup.d.ts +1 -1
- package/dist/lib/env-cleanup.js +1 -1
- package/dist/lib/model-registry.d.ts +11 -0
- package/dist/lib/model-registry.js +1 -1
- package/dist/lib/tool-manager.d.ts +16 -3
- package/dist/lib/tool-manager.js +1 -1
- package/dist/lib/tools/claude-code.js +1 -1
- package/dist/lib/tools/claude-desktop.d.ts +7 -0
- package/dist/lib/tools/claude-desktop.js +1 -1
- package/dist/lib/tools/types.d.ts +10 -2
- package/dist/lib/wizard.d.ts +8 -1
- package/dist/lib/wizard.js +1 -1
- package/dist/locales/en_US.json +16 -5
- package/dist/locales/zh_CN.json +16 -5
- package/dist/models.json +8 -0
- package/models.json +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ vibecli setup --1m
|
|
|
50
50
|
vibecli setup --no-1m
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
配完直接运行 `claude` / `codex` / `grok` 即可。
|
|
53
|
+
配完直接运行 `claude` / `codex` / `grok` 即可。Claude Desktop 默认隐藏官方(Claude.ai 账号)登录入口、只走网关,想保留就加 `--claude-official on`。
|
|
54
54
|
|
|
55
55
|
### 方式 B:交互式向导
|
|
56
56
|
|
|
@@ -81,6 +81,7 @@ vibecli config claude-code clear-model # 模型选择被钉住时,交还给
|
|
|
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 时可取消。
|
|
83
83
|
- **不动你的其它配置**:只改本工具的 provider 字段,你的 hooks、MCP、插件、权限设置原样保留。
|
|
84
|
+
- **配置改坏了一键重置**:`vibecli config <tool> --force`(或 `setup … --force`)先快照再整份重写为推荐配置,坏掉的 JSON/TOML 也能救回;不加 `--force` 时只幂等合并、不碰坏文件。
|
|
84
85
|
- **改前自动备份 + 一键还原**:覆盖配置前先快照(每文件留 10 份),`vibecli config <tool> restore` 随时回退;还原本身也可再撤销。
|
|
85
86
|
- **多 Profile + 可迁移凭据**:work/personal 各自独立;API Key 存在权限为 `0600` 的 `~/.vibecli/config.yaml`,与 Claude/Codex 本身的明文凭据方式一致,换机不会被主机绑定加密阻断。
|
|
86
87
|
- **CI 友好**:所有命令支持 `--json` / `--non-interactive` / `--yes` / `--dry-run` / `--no-color`。
|
package/dist/commands/auth.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"chalk";import e from"inquirer";import t from"ora";import{configManager as r}from"../lib/config.js";import{i18n as i}from"../lib/i18n.js";import{validateApiKey as a}from"../lib/api-validator.js";import{toolManager as n}from"../lib/tool-manager.js";import{isJsonMode as d,isNonInteractive as l,shouldAutoConfirm as c,emitJson as s,isColorEnabled as u}from"../lib/runtime-options.js";const p=["claude","claude-code","opencode"];function m(o){return u()?o:o=>o}function f(o,e=o=>o){d()||console.log(m(e)(o))}async function k(e){if(d()){const o=await a(e);return o.valid?(r.setApiKey(e),void s({ok:!0,action:"auth"})):void s({ok:!1,action:"auth",error:o.error||"invalid_api_key"})}const n=t(i.t("wizard.validating_api_key")).start(),l=await a(e);l.valid?(r.setApiKey(e),n.succeed(m(o.green)(i.t("auth.saved")))):"invalid_api_key"===l.error?n.fail(m(o.red)(i.t("wizard.api_key_invalid"))):n.fail(m(o.red)(i.t("wizard.api_key_network_error")))}export async function authCommand(t){0!==t.length?"revoke"!==t[0]?"reload"!==t[0]?await k(t[0]):await async function(e){const t=e.toLowerCase();if(!p.includes(t))return d()?void s({ok:!1,action:"reload",error:"unsupported_tool",tool:e}):void f(i.t("auth.reload_tool_not_supported",{tool:e}),o.red);const a=r.getApiKey();if(!a)return d()?void s({ok:!1,action:"reload",error:"missing_api_key",tool:e}):void f(i.t("auth.reload_missing_config"),o.red);const l="opencode"===t?"OpenCode":"Claude Code",c="opencode"===t?"opencode":"claude-code";d()||f(i.t("auth.reloading",{tool:l}),o.blue);try{n.
|
|
1
|
+
import o from"chalk";import e from"inquirer";import t from"ora";import{configManager as r}from"../lib/config.js";import{i18n as i}from"../lib/i18n.js";import{validateApiKey as a}from"../lib/api-validator.js";import{toolManager as n}from"../lib/tool-manager.js";import{isJsonMode as d,isNonInteractive as l,shouldAutoConfirm as c,emitJson as s,isColorEnabled as u}from"../lib/runtime-options.js";const p=["claude","claude-code","opencode"];function m(o){return u()?o:o=>o}function f(o,e=o=>o){d()||console.log(m(e)(o))}async function k(e){if(d()){const o=await a(e);return o.valid?(r.setApiKey(e),void s({ok:!0,action:"auth"})):void s({ok:!1,action:"auth",error:o.error||"invalid_api_key"})}const n=t(i.t("wizard.validating_api_key")).start(),l=await a(e);l.valid?(r.setApiKey(e),n.succeed(m(o.green)(i.t("auth.saved")))):"invalid_api_key"===l.error?n.fail(m(o.red)(i.t("wizard.api_key_invalid"))):n.fail(m(o.red)(i.t("wizard.api_key_network_error")))}export async function authCommand(t){0!==t.length?"revoke"!==t[0]?"reload"!==t[0]?await k(t[0]):await async function(e){const t=e.toLowerCase();if(!p.includes(t))return d()?void s({ok:!1,action:"reload",error:"unsupported_tool",tool:e}):void f(i.t("auth.reload_tool_not_supported",{tool:e}),o.red);const a=r.getApiKey();if(!a)return d()?void s({ok:!1,action:"reload",error:"missing_api_key",tool:e}):void f(i.t("auth.reload_missing_config"),o.red);const l="opencode"===t?"OpenCode":"Claude Code",c="opencode"===t?"opencode":"claude-code";d()||f(i.t("auth.reloading",{tool:l}),o.blue);try{await n.applyPrivateConfig(c,r.getApiKeyForTool(c)||a,r.getModelsForTool(c)),d()?s({ok:!0,action:"reload",tool:c}):f(i.t("auth.reloaded",{tool:l}),o.green)}catch(e){if(d())return void s({ok:!1,action:"reload",error:"reload_failed",message:e instanceof Error?e.message:String(e)});f(i.t("auth.reload_failed"),o.red),e instanceof Error&&f(e.message,o.red)}}(t[1]||""):await async function(){if(!c()){const{confirm:t}=await e.prompt([{type:"confirm",name:"confirm",message:i.t("auth.revoke_confirm"),default:!1}]);if(!t)return d()?void s({ok:!1,action:"revoke",cancelled:!0}):void f(i.t("auth.not_revoked"),o.yellow)}r.revokeApiKey(),d()?s({ok:!0,action:"revoke"}):f(i.t("auth.revoked"),o.green)}():await async function(){if(l())return d()?void s({ok:!1,action:"auth",error:"token_required"}):void f(i.t("runtime.non_interactive_required_token"),o.red);const{token:t}=await e.prompt([{type:"password",name:"token",message:i.t("auth.token_prompt"),validate:o=>!(!o||0===o.trim().length)||i.t("auth.token_required")}]);await k(t.trim())}()}
|
package/dist/commands/config.js
CHANGED
|
@@ -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
|
|
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 f,shouldAutoConfirm as d}from"../lib/runtime-options.js";const g=/^[a-zA-Z0-9._-]+$/;function m(o,e){const t=(o||"").trim();if(!t||!g.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,f,d){if(!l[s])return void console.log(e.red(i.t("config.unknown_tool",{tool:s})));const g=(f[0]||"").toLowerCase();if("backups"!==g&&"list-backups"!==g){if("restore"===g){if(!await a({message:i.t("config.confirm_restore",{tool:l[s].displayName}),defaultValue:!1}))return;return void p(s,"restore")}if("clear-model"===g||"clear-models"===g){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=await 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(d.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(d.nonInteractive||d.force){const o=r.getApiKeyForTool(s);if(!o)return void console.log(e.red(i.t("wizard.api_key_required")));if(d.force&&!d.nonInteractive&&!await a({message:i.t("config.confirm_force",{tool:l[s].displayName}),defaultValue:!1}))return;return await n.applyPrivateConfig(s,o,r.getModelsForTool(s),{force:d.force}),d.force&&console.log(e.yellow(i.t("config.force_done",{tool:l[s].displayName}))),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)))}await t.showToolMenu(s)}else p(s,"backups")}export async function configCommand(t){const{positional:n,options:p}=function(o){const e=[],t={};(f()||d())&&(t.nonInteractive=!0);for(const n of o)"--non-interactive"===n||"-y"===n?t.nonInteractive=!0:"--force"===n||"-f"===n?t.force=!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&&g.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)}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -18,6 +18,14 @@
|
|
|
18
18
|
* vibecli setup --no-1m
|
|
19
19
|
* 复用当前 profile 已保存的 Codex Key,关闭 1M 上下文且不改写凭据。
|
|
20
20
|
*
|
|
21
|
+
* vibecli setup ... --force
|
|
22
|
+
* 任何一种用法都可加:先快照、再整份删掉工具原有配置文件,用推荐配置重写——给把配置
|
|
23
|
+
* 改坏(乃至改成不合法 JSON/TOML)的用户兜底;不加时是幂等合并,遇到坏文件会拒绝。
|
|
24
|
+
*
|
|
25
|
+
* vibecli setup --claude-official on|off
|
|
26
|
+
* Claude Desktop 里是否保留官方(Claude.ai 账号)登录入口;默认 off(隐藏)。可与 --desktopkey /
|
|
27
|
+
* --key 同用,单独给则复用已保存的桌面端 Key 重新应用。
|
|
28
|
+
*
|
|
21
29
|
* 重复执行即换令牌(幂等)。模型一律用默认组合,之后可在向导 / tool-model 里改。
|
|
22
30
|
*/
|
|
23
31
|
export interface SetupCommandOptions {
|
|
@@ -28,5 +36,11 @@ export interface SetupCommandOptions {
|
|
|
28
36
|
desktopkey?: string;
|
|
29
37
|
all?: boolean;
|
|
30
38
|
oneMillion?: boolean;
|
|
39
|
+
/** `on` = keep the Claude.ai sign-in entry visible in Claude Desktop; `off` = hide it. */
|
|
40
|
+
claudeOfficial?: string;
|
|
41
|
+
/** Replace each tool's config files wholesale (snapshotted first) instead of merging. */
|
|
42
|
+
force?: boolean;
|
|
31
43
|
}
|
|
44
|
+
/** `--claude-official on|off` (also true/false, show/hide, 1/0, yes/no); undefined when unrecognized. */
|
|
45
|
+
export declare function parseOnOff(raw: string | undefined): boolean | undefined;
|
|
32
46
|
export declare function setupCommand(options: SetupCommandOptions): Promise<void>;
|
package/dist/commands/setup.js
CHANGED
|
@@ -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
|
|
1
|
+
import e from"chalk";import{i18n as o}from"../lib/i18n.js";import{configManager as t}from"../lib/config.js";import{toolManager as s,SUPPORTED_TOOLS as a}from"../lib/tool-manager.js";import{validateApiKey as l,fetchModels as i}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as n}from"../lib/tools/codex.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as r}from"../lib/tools/claude-desktop.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 c(e,o){return"claude"===o?e.hasClaude:"gpt"===o?e.hasGpt:e.hasGrok}export function parseOnOff(e){const o=(e??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(o)||!["off","false","hide","0","no","n"].includes(o)&&void 0}function u(o){console.log(e.green(o))}function f(o){console.log(e.yellow(o))}function m(o){console.error(e.red(o))}async function k(t,s){console.log(e.gray(o.t("setup.validating",{label:t})));const a=await l(s);return!!a.valid||(m(o.t("setup.invalid_key",{label:t,reason:a.message||a.error||"invalid"})),!1)}async function y(l,i,n,p,c){const k=a[l];try{"codex"===l&&void 0!==n&&t.setToolSetting("codex",d,n),"claude-desktop"===l&&void 0!==p&&t.setToolSetting("claude-desktop",r,!p),await s.applyPrivateConfig(l,i,t.getModelsForTool(l),{force:c})}catch(e){return m(o.t("setup.apply_failed",{tool:k.displayName,reason:e instanceof Error?e.message:String(e)})),!1}return c&&f(o.t("config.force_done",{tool:k.displayName})),u(o.t("setup.configured",{tool:k.displayName,command:k.command})),"codex"===l&&void 0!==n&&u(o.t(n?"setup.codex_1m_enabled":"setup.codex_1m_disabled")),s.applyNotes(l).forEach(e=>f(" "+e)),s.isToolInstalled(l)||console.log(e.gray(o.t("setup.install_hint",{tool:k.displayName,install:k.installCommand}))),!0}export async function setupCommand(e){const l=e.key?.trim(),i=e.claudekey?.trim(),r=e.codexkey?.trim(),g=e.grokkey?.trim(),_=e.desktopkey?.trim(),b=void 0!==e.oneMillion,h=void 0!==e.claudeOfficial;let x;if(h&&(x=parseOnOff(e.claudeOfficial),void 0===x))return m(o.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const C=Boolean(l||i||r||g||_);if(!C&&(b||h)){if(b)if(t.getApiKeyForTool("codex"))try{const s=!0===e.oneMillion;t.setToolSetting("codex",d,s),n(s),u(o.t(s?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){m(o.t("setup.apply_failed",{tool:a.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else m(o.t("setup.codex_1m_missing_key")),process.exitCode=1;if(h){const s=t.getApiKeyForTool("claude-desktop");s?await y("claude-desktop",s,void 0,x,e.force)||(process.exitCode=1):(m(o.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!C)return m(o.t("setup.usage")),void(process.exitCode=1);let v=0,w=0;const K=new Set,G=[];i&&G.push({tool:"claude-code",label:"--claudekey",apiKey:i,need:"claude"}),r&&G.push({tool:"codex",label:"--codexkey",apiKey:r,need:"gpt"}),g&&G.push({tool:"grok",label:"--grokkey",apiKey:g,need:"grok"}),_&&G.push({tool:"claude-desktop",label:"--desktopkey",apiKey:_,need:"claude"});for(const s of G){if(!await k(s.label,s.apiKey)){w+=1;continue}const l=await p(s.apiKey);!l.probed||c(l,s.need)?(l.probed||f(o.t("setup.probe_failed",{label:s.label})),t.setToolApiKey(s.tool,s.apiKey),await y(s.tool,s.apiKey,e.oneMillion,x,e.force)?(v+=1,K.add(s.tool)):w+=1):(m(o.t(`setup.key_no_${s.need}`,{label:s.label,tool:a[s.tool].displayName})),w+=1)}if(l){if(!await k("--key",l))return void(process.exitCode=1);t.setApiKey(l);const a=await p(l);if(a.probed||f(o.t("setup.probe_failed",{label:"--key"})),a.probed&&!a.hasClaude&&!a.hasGpt&&!a.hasGrok)return m(o.t("setup.key_no_family")),void(process.exitCode=1);const i=[{tool:"claude-code",capable:a.hasClaude,skipMsg:o.t("setup.skip_no_claude")},{tool:"codex",capable:a.hasGpt,skipMsg:o.t("setup.skip_no_gpt")},{tool:"grok",capable:a.hasGrok,skipMsg:o.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:a.hasClaude&&s.isToolInstalled("claude-desktop"),skipMsg:o.t("setup.skip_desktop_not_installed"),quiet:!a.hasClaude}];for(const o of i)K.has(o.tool)||(o.capable?await y(o.tool,l,e.oneMillion,x,e.force)?(v+=1,K.add(o.tool)):w+=1:o.quiet||f(o.skipMsg))}if(v>0){const e=[...K].map(e=>a[e]?.command).filter(Boolean).join(" / ");u("\n"+o.t("setup.done",{count:String(v),commands:e}))}(w>0||0===v)&&(process.exitCode=1)}
|
|
@@ -3,7 +3,22 @@ import { DEFAULT_MODELS } from './config.js';
|
|
|
3
3
|
* settings.json 里所有"钉死模型"的 env 变量。写入、卸载与 clearPinnedModels
|
|
4
4
|
* 共用这一份清单——新增模型槽时只改这里,避免某条路径漏清导致残留。
|
|
5
5
|
*/
|
|
6
|
-
export declare const MODEL_ENV_VARS: readonly ["ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
|
|
6
|
+
export declare const MODEL_ENV_VARS: readonly ["ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_FABLE_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
|
|
7
|
+
export interface TierFallback {
|
|
8
|
+
tier: string;
|
|
9
|
+
envVar: string;
|
|
10
|
+
/** Claude Code 该档位内置解析到的模型(Key 用不了)。 */
|
|
11
|
+
from: string;
|
|
12
|
+
/** 实际钉上的替代模型。 */
|
|
13
|
+
to: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Claude Code 的档位别名会解析成它内置的模型(如 opus → claude-opus-5-5),Key 所在
|
|
17
|
+
* 分组没有这个模型时请求直接失败。按 models.json 的 `claudeCode.tierChains` 找出第一个
|
|
18
|
+
* Key 能用的替代模型钉到对应 env。内置模型可用、链上没有可用模型、或拿不到模型列表
|
|
19
|
+
* (离线)时都不钉;只会钉链上的 Claude 模型,不会把 GPT 等其它模型配给 Claude Code。
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveTierFallbacks(available?: string[]): TierFallback[];
|
|
7
22
|
export interface ClaudeCodeSettingsConfig {
|
|
8
23
|
env?: {
|
|
9
24
|
ANTHROPIC_AUTH_TOKEN?: string;
|
|
@@ -11,6 +26,7 @@ export interface ClaudeCodeSettingsConfig {
|
|
|
11
26
|
ANTHROPIC_DEFAULT_HAIKU_MODEL?: string;
|
|
12
27
|
ANTHROPIC_DEFAULT_SONNET_MODEL?: string;
|
|
13
28
|
ANTHROPIC_DEFAULT_OPUS_MODEL?: string;
|
|
29
|
+
ANTHROPIC_DEFAULT_FABLE_MODEL?: string;
|
|
14
30
|
ANTHROPIC_MODEL?: string;
|
|
15
31
|
ANTHROPIC_REASONING_MODEL?: string;
|
|
16
32
|
API_TIMEOUT_MS?: string;
|
|
@@ -38,6 +54,7 @@ export declare class ClaudeCodeManager {
|
|
|
38
54
|
private static instance;
|
|
39
55
|
private settingsPath;
|
|
40
56
|
private mcpConfigPath;
|
|
57
|
+
private lastTierFallbacks;
|
|
41
58
|
private constructor();
|
|
42
59
|
static getInstance(): ClaudeCodeManager;
|
|
43
60
|
/**
|
|
@@ -63,8 +80,9 @@ export declare class ClaudeCodeManager {
|
|
|
63
80
|
saveMCPConfig(config: ClaudeCodeMCPConfig): void;
|
|
64
81
|
/**
|
|
65
82
|
* 加载私有 NewAPI 配置到 Claude Code
|
|
83
|
+
* @param availableModels Key 在网关 `/v1/models` 能看到的模型;缺省(离线)时不做档位回退
|
|
66
84
|
*/
|
|
67
|
-
loadPrivateConfig(apiKey: string, models: typeof DEFAULT_MODELS): void;
|
|
85
|
+
loadPrivateConfig(apiKey: string, models: typeof DEFAULT_MODELS, availableModels?: string[]): void;
|
|
68
86
|
/**
|
|
69
87
|
* 确保 .claude.json 中有 hasCompletedOnboarding: true
|
|
70
88
|
*/
|
|
@@ -76,10 +94,14 @@ export declare class ClaudeCodeManager {
|
|
|
76
94
|
/**
|
|
77
95
|
* 只摘掉钉死的模型(env 里的 ANTHROPIC_*_MODEL + 顶层 model),保留凭据与
|
|
78
96
|
* 用户的 hooks / MCP / 插件配置。用于把 Claude Code 交还给自带默认模型,
|
|
79
|
-
* 让 /model 列表恢复完整(新上线的模型不再被旧配置挡住)。
|
|
97
|
+
* 让 /model 列表恢复完整(新上线的模型不再被旧配置挡住)。Key 用不了的内置档位
|
|
98
|
+
* 模型仍按 {@link resolveTierFallbacks} 保留回退钉子,否则清完就不能用了。
|
|
99
|
+
* @param availableModels Key 在网关能看到的模型;缺省(离线)时不保留回退
|
|
80
100
|
* @returns 实际被清除的键名(未改动时为空数组)
|
|
81
101
|
*/
|
|
82
|
-
clearPinnedModels(): string[];
|
|
102
|
+
clearPinnedModels(availableModels?: string[]): string[];
|
|
103
|
+
/** 最近一次 loadPrivateConfig 实际写入的档位回退(用于应用后的提示)。 */
|
|
104
|
+
getLastTierFallbacks(): TierFallback[];
|
|
83
105
|
/**
|
|
84
106
|
* 检测当前 Claude Code 配置的 API Key
|
|
85
107
|
* @returns 包含 API Key 的对象
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as s,renameSync as o,unlinkSync as
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as s,renameSync as o,unlinkSync as r}from"fs";import{join as i,dirname as a}from"path";import{homedir as l}from"os";import{logger as c}from"../utils/logger.js";import{configManager as g,DEFAULT_MODELS as C}from"./config.js";import{loadSettingsTemplate as d}from"./settings-template.js";import{cleanupAnthropicEnvVars as O}from"./env-cleanup.js";import{getClaudeCodeTierChains as h}from"./model-registry.js";import{baseModelId as _}from"./model-catalog.js";export const MODEL_ENV_VARS=["ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];const f={opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL",fable:"ANTHROPIC_DEFAULT_FABLE_MODEL"};export function resolveTierFallbacks(e){if(!e||0===e.length)return[];const t=new Set(e.map(_)),n=[];for(const[e,s]of Object.entries(h())){const o=f[e],[r,...i]=s;if(!o||!r||t.has(r))continue;const a=i.find(e=>t.has(e));a&&n.push({tier:e,envVar:o,from:r,to:a})}return n}export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;lastTierFallbacks=[];constructor(){this.settingsPath=i(l(),".claude","settings.json"),this.mcpConfigPath=i(l(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const s=a(n);e(s)||t(s,{recursive:!0})}atomicWrite(t,n){const i=`${t}.tmp`;try{s(i,n,"utf-8"),o(i,t)}catch(t){if(e(i))try{r(i)}catch{}throw t}}getSettings(){try{if(e(this.settingsPath)){const e=n(this.settingsPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code settings:",e),c.logError("ClaudeCodeManager.getSettings",e)}return{}}saveSettings(e){try{this.ensureConfigDir(this.settingsPath),this.atomicWrite(this.settingsPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code settings: ${e}`)}}getMCPConfig(){try{if(e(this.mcpConfigPath)){const e=n(this.mcpConfigPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code MCP config:",e),c.logError("ClaudeCodeManager.getMCPConfig",e)}return{}}saveMCPConfig(e){try{this.ensureConfigDir(this.mcpConfigPath),this.atomicWrite(this.mcpConfigPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code MCP config: ${e}`)}}loadPrivateConfig(e,t,n){this.ensureOnboardingCompleted(),O();const s=d(),o=s.env||{},{env:r,...i}=s,a=this.getSettings(),{ANTHROPIC_API_KEY:l,...c}=a.env||{},h={...c};for(const e of MODEL_ENV_VARS)delete h[e];const _={},f=e=>{const n=(t[e]||"").trim();return n&&n!==C[e]?n:null},u=f("haiku"),E=f("sonnet"),m=f("opus"),T=new Set([u&&"haiku",E&&"sonnet",m&&"opus"].filter(Boolean));this.lastTierFallbacks=resolveTierFallbacks(n).filter(e=>!T.has(e.tier));for(const e of this.lastTierFallbacks)_[e.envVar]=e.to;u&&(_.ANTHROPIC_DEFAULT_HAIKU_MODEL=u),E&&(_.ANTHROPIC_DEFAULT_SONNET_MODEL=E),m&&(_.ANTHROPIC_DEFAULT_OPUS_MODEL=m,_.ANTHROPIC_MODEL=m);const A={...a};for(const[e,t]of Object.entries(i))e in A||(A[e]=t);A.showThinkingSummaries=!0;const P={...A,env:{...h,...Object.fromEntries(Object.entries(o).filter(([e])=>!(e in h))),ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:g.getBaseUrl(),..._}};m?P.model=m:Object.prototype.hasOwnProperty.call(i,"model")?P.model=i.model:delete P.model,this.saveSettings(P)}ensureOnboardingCompleted(){try{const e=this.getMCPConfig();e.hasCompletedOnboarding||this.saveMCPConfig({...e,hasCompletedOnboarding:!0})}catch(e){c.warn("Failed to ensure onboarding completed:",e),c.logError("ClaudeCodeManager.ensureOnboardingCompleted",e)}}unloadPrivateConfig(){const e=this.getSettings();if(!e.env){if(void 0!==e.model){const{model:t,...n}=e;this.saveSettings(n)}return}const{ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:n,API_TIMEOUT_MS:s,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:o,...r}=e.env,i={...r};for(const e of MODEL_ENV_VARS)delete i[e];const a={...e,env:i};delete a.model,a.env&&0===Object.keys(a.env).length&&delete a.env,this.saveSettings(a)}clearPinnedModels(e){const t=this.getSettings(),n=t.env,s=n?.ANTHROPIC_AUTH_TOKEN?Object.fromEntries(resolveTierFallbacks(e).map(e=>[e.envVar,e.to])):{},o=[];for(const e of MODEL_ENV_VARS)n&&e in n&&n[e]!==s[e]&&o.push(e);void 0!==t.model&&o.push("model");const r=Object.entries(s).some(([e,t])=>n?.[e]!==t);if(0===o.length&&!r)return[];const i={...t};if(delete i.model,n){const e={...n};for(const t of MODEL_ENV_VARS)delete e[t];Object.assign(e,s),0===Object.keys(e).length?delete i.env:i.env=e}return this.saveSettings(i),o}getLastTierFallbacks(){return[...this.lastTierFallbacks]}detectCurrentConfig(){try{const e=this.getSettings();return e.env&&e.env.ANTHROPIC_AUTH_TOKEN?{apiKey:e.env.ANTHROPIC_AUTH_TOKEN}:{apiKey:null}}catch{return{apiKey:null}}}getExperimentalFeature(e){const t=this.getSettings();return t.env?.[e]}setExperimentalFeature(e,t){const n=this.getSettings(),s=n.env||{},o={...n,env:{...s,[e]:t}};this.saveSettings(o)}removeExperimentalFeature(e){const t=this.getSettings();if(!t.env)return;const{[e]:n,...s}=t.env,o={...t,env:s};this.saveSettings(o)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
|
package/dist/lib/command.js
CHANGED
|
@@ -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 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
|
|
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 l}from"../commands/logs.js";import{completionCommand as p}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 f from"chalk";import{readFileSync as h}from"fs";import{join as y,dirname as k}from"path";import{fileURLToPath as b}from"url";import{setRuntimeOptions as v,detectColorPreferenceFromEnv as w,isColorEnabled as $,isJsonMode as x}from"./runtime-options.js";import{cleanupAnthropicEnvVars as C}from"./env-cleanup.js";import{runStartupUpdate as j}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=y(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("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").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"],claudeOfficial:e.claudeOfficial,force:e.force})}),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 l(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([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${f.bold(o.t("cli.examples"))}:\n ${f.gray("$ vibecli # Interactive main menu")}\n ${f.gray("$ vibecli init # Run first-time setup wizard")}\n ${f.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${f.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${f.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${f.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${f.gray("$ vibecli config codex --force # Same, for one tool")}\n ${f.gray("$ vibecli status --json # Machine-readable status")}\n ${f.gray("$ vibecli config profile list")}\n ${f.gray("$ vibecli config profile create work")}\n ${f.gray("$ vibecli config profile use work")}\n ${f.gray("$ vibecli config preset save fast")}\n ${f.gray("$ vibecli config backup create snapshot")}\n ${f.gray("$ vibecli auth <token> --non-interactive")}\n ${f.gray("$ vibecli auth revoke --yes")}\n ${f.gray("$ vibecli logs path")}\n ${f.gray("$ vibecli completion bash")}\n ${f.gray("$ vibecli bench --samples 3")}\n ${f.gray("$ vibecli update")}\n ${f.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=C();if(0===e.length||x())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log($()?f.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||(f.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 j(e)).handled)return;await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=$()?f.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 会盖住模型选择,让 /model 里选不到新模型。这里统一负责把它们清掉:
|
|
5
5
|
* CLI 启动时检测一次(见 command.ts),给 Claude Code 写配置前再兜底一次。
|
|
6
6
|
*/
|
|
7
|
-
export declare const ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
|
|
7
|
+
export declare const ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_FABLE_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
|
|
8
8
|
/**
|
|
9
9
|
* 获取当前 shell 的 rc 文件路径(Windows 不使用 rc 文件,返回 null)。
|
|
10
10
|
*/
|
package/dist/lib/env-cleanup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readFileSync as r,writeFileSync as n}from"fs";import{join as t}from"path";import{logger as o}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const t=getShellRcFilePath();if(!t||!e(t))return!1;let s=r(t,"utf-8");const l=s,c=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of c)s=s.replace(e,"");if(s!==l)return n(t,s,"utf-8"),o.info(`Cleaned up ANTHROPIC_* environment variables from ${t}`),!0}catch(e){o.warn("Failed to cleanup shell environment variables:",e),o.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars();for(const r of e)delete process.env[r];return[...e]}
|
|
1
|
+
import{existsSync as e,readFileSync as r,writeFileSync as n}from"fs";import{join as t}from"path";import{logger as o}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const t=getShellRcFilePath();if(!t||!e(t))return!1;let s=r(t,"utf-8");const l=s,c=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of c)s=s.replace(e,"");if(s!==l)return n(t,s,"utf-8"),o.info(`Cleaned up ANTHROPIC_* environment variables from ${t}`),!0}catch(e){o.warn("Failed to cleanup shell environment variables:",e),o.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars();for(const r of e)delete process.env[r];return[...e]}
|
|
@@ -25,6 +25,15 @@ export interface ClaudeDesktopRules {
|
|
|
25
25
|
models: string[];
|
|
26
26
|
}>;
|
|
27
27
|
}
|
|
28
|
+
/** Claude Code tier aliases (`opus` / `sonnet` / `haiku` / `fable`) and how they degrade. */
|
|
29
|
+
export interface ClaudeCodeRules {
|
|
30
|
+
/**
|
|
31
|
+
* Per tier: the model Claude Code's alias resolves to by default, then the
|
|
32
|
+
* replacements to pin (in order) when the key cannot reach it — mirrors
|
|
33
|
+
* Claude Code's own `fallback_3p` chain.
|
|
34
|
+
*/
|
|
35
|
+
tierChains: Record<string, string[]>;
|
|
36
|
+
}
|
|
28
37
|
export interface ModelRegistryData {
|
|
29
38
|
defaultModels: Models;
|
|
30
39
|
/** Default model for non-Claude tools (Codex/OpenCode/OpenClaw/Hermes/AtomCode/Grok). */
|
|
@@ -32,6 +41,7 @@ export interface ModelRegistryData {
|
|
|
32
41
|
/** Per-tool default model. Empty string (e.g. claude-code) means "use the tool's built-in default". */
|
|
33
42
|
toolDefaults?: Record<string, string>;
|
|
34
43
|
presets: ModelPreset[];
|
|
44
|
+
claudeCode?: Partial<ClaudeCodeRules>;
|
|
35
45
|
claudeDesktop?: Partial<ClaudeDesktopRules>;
|
|
36
46
|
}
|
|
37
47
|
export declare function getDefaultModels(): Models;
|
|
@@ -45,5 +55,6 @@ export declare function getDefaultToolModel(): string;
|
|
|
45
55
|
export declare function getToolDefaultModel(toolName: string): string;
|
|
46
56
|
export declare function getPresets(): ModelPreset[];
|
|
47
57
|
export declare function resolvePreset(preset: ModelPreset): Models;
|
|
58
|
+
export declare function getClaudeCodeTierChains(): Record<string, string[]>;
|
|
48
59
|
export declare function getClaudeDesktopRules(): ClaudeDesktopRules;
|
|
49
60
|
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
|
|
1
|
+
import{dirname as o,join as e}from"path";import{fileURLToPath as t}from"url";import{existsSync as n,readFileSync as l}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(n(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 getClaudeCodeTierChains(){const o=u().claudeCode?.tierChains??{};return Object.fromEntries(Object.entries(o).map(([o,e])=>[o,[...e]]))}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}
|
|
@@ -30,13 +30,26 @@ export declare class ToolManager {
|
|
|
30
30
|
installTool(toolName: string): Promise<void>;
|
|
31
31
|
getToolConfig(toolName: string): any;
|
|
32
32
|
updateToolConfig(toolName: string, config: any): void;
|
|
33
|
-
|
|
33
|
+
/**
|
|
34
|
+
* `--force`: snapshot then delete every file the adapter would otherwise
|
|
35
|
+
* merge into, so the following apply starts from a clean slate. Meant for
|
|
36
|
+
* users who hand-edited a config into an unusable state — the merge path
|
|
37
|
+
* refuses to touch unparsable files, this path replaces them (backups keep
|
|
38
|
+
* the broken original recoverable via `restore`).
|
|
39
|
+
* @returns the files that were removed
|
|
40
|
+
*/
|
|
41
|
+
forceResetConfig(toolName: string): string[];
|
|
42
|
+
loadPrivateConfig(toolName: string, apiKey: string, models: VibecliModels, catalog?: ModelCatalog, options?: {
|
|
43
|
+
force?: boolean;
|
|
44
|
+
}): void;
|
|
34
45
|
/**
|
|
35
46
|
* `loadPrivateConfig` plus the network lookup adapters with
|
|
36
47
|
* `wantsModelCatalog` need (what the key can reach, which models are 1M).
|
|
37
48
|
* A failed lookup degrades to the profile's slots instead of failing the apply.
|
|
38
49
|
*/
|
|
39
|
-
applyPrivateConfig(toolName: string, apiKey: string, models: VibecliModels
|
|
50
|
+
applyPrivateConfig(toolName: string, apiKey: string, models: VibecliModels, options?: {
|
|
51
|
+
force?: boolean;
|
|
52
|
+
}): Promise<void>;
|
|
40
53
|
/** Post-apply follow-ups the adapter wants shown to the user (may be empty). */
|
|
41
54
|
applyNotes(toolName: string): string[];
|
|
42
55
|
unloadPrivateConfig(toolName: string): void;
|
|
@@ -45,7 +58,7 @@ export declare class ToolManager {
|
|
|
45
58
|
* CLI's own per-tool model override so a later re-apply doesn't pin it back.
|
|
46
59
|
* @returns the config keys cleared in the tool's own config file
|
|
47
60
|
*/
|
|
48
|
-
clearPinnedModels(toolName: string): string[]
|
|
61
|
+
clearPinnedModels(toolName: string): Promise<string[]>;
|
|
49
62
|
/** Whether this tool supports clearing a pinned model. */
|
|
50
63
|
supportsClearPinnedModels(toolName: string): boolean;
|
|
51
64
|
detectCurrentConfig(toolName: string): {
|
package/dist/lib/tool-manager.js
CHANGED
|
@@ -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{
|
|
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{backupBeforeWrite as g,listBackups as m,restoreBackup as p}from"./tools/backup.js";import{existsSync as u,rmSync as f}from"fs";import{resolveModelCatalog as w}from"./model-catalog.js";function h(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,h(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)}forceResetConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);const t=n.forceResetPaths?n.forceResetPaths():this.backupPathsFor(n),l=[];for(const o of t)u(o)&&(g(o),f(o,{force:!0}),l.push(o));return l}loadPrivateConfig(o,n,t,l,e={}){const s=r(o);if(!s)throw new Error(`Unknown tool: ${o}`);e.force&&this.forceResetConfig(o),s.loadPrivateConfig({apiKey:n,baseUrl:a.getBaseUrl(),models:t,settings:a.getToolSettings(o),...l?{catalog:l}:{}})}async applyPrivateConfig(o,n,t,l={}){const e=r(o);if(!e)throw new Error(`Unknown tool: ${o}`);const s=e.wantsModelCatalog?await w(n):null;l.force?this.loadPrivateConfig(o,n,t,s??void 0,{force:!0}):s?this.loadPrivateConfig(o,n,t,s):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()}async clearPinnedModels(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);if(a.setToolModel(o,null),!n.clearPinnedModels)return[];const t=a.getApiKeyForTool(o),l=n.wantsModelCatalog&&t?await w(t):null;return n.clearPinnedModels(l??void 0)}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:m(o)}))}restorePrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,result:p(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return c().filter(o=>!o.hidden).map(o=>h(o.name))}isGitInstalled(){try{const n="win32"===process.platform?"where git":"which git";return o(n,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as e}from"path";import{homedir as o}from"os";import{claudeCodeManager as t}from"../claude-code-manager.js";import{backupBeforeWrite as a}from"./backup.js";const
|
|
1
|
+
import{join as e}from"path";import{homedir as o}from"os";import{claudeCodeManager as t}from"../claude-code-manager.js";import{backupBeforeWrite as a}from"./backup.js";import{i18n as n}from"../i18n.js";const i=e(o(),".claude","settings.json"),s=e(o(),".claude.json");export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",configPath:i,wantsModelCatalog:!0,loadPrivateConfig(e){a(i),a(s),t.loadPrivateConfig(e.apiKey,e.models,e.catalog?.models)},unloadPrivateConfig(){a(i),t.unloadPrivateConfig()},detectCurrentConfig:()=>t.detectCurrentConfig(),getConfig:()=>({settings:t.getSettings(),mcp:t.getMCPConfig()}),updateConfig(e){const o=e||{};o.settings&&t.saveSettings(o.settings),o.mcp&&t.saveMCPConfig(o.mcp)},clearPinnedModels:e=>(a(i),t.clearPinnedModels(e?.models)),applyNotes:()=>t.getLastTierFallbacks().map(e=>n.t("tools.claude_code.tier_fallback",{tier:e.tier,from:e.from,to:e.to})),backupPaths:()=>[i,s],forceResetPaths:()=>[i]};
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { type ToolAdapter } from './types.js';
|
|
2
2
|
import type { VibecliModels } from '../config.js';
|
|
3
3
|
import { type ModelCatalog } from '../model-catalog.js';
|
|
4
|
+
/**
|
|
5
|
+
* Tool setting (config.yaml `tool_settings['claude-desktop'].hide_official_login`).
|
|
6
|
+
* `true` (default, same as the console's Desktop Assistant page) writes
|
|
7
|
+
* `disableDeploymentModeChooser: true`, hiding the Claude.ai sign-in choice so
|
|
8
|
+
* the app is gateway-only; `false` leaves the chooser visible.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING = "hide_official_login";
|
|
4
11
|
/** One model as advertised to Claude Desktop's picker (`inferenceModels[]`). */
|
|
5
12
|
export interface InferenceModel {
|
|
6
13
|
name: string;
|
|
@@ -1 +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
|
|
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 i}from"path";import{randomUUID as a}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;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const k=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"],_=/^[a-f0-9-]{36}$/;function b(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 i(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:i(o,"AppData","Local");return k.map(t=>i(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??i(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:i(o,".config");return i(e,"Claude-3p")}}}const D=()=>i(claudeDesktopUserDataDir(),"configLibrary"),A=()=>i(D(),"_meta.json"),w=e=>i(D(),`${e}.json`),M=()=>i(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 b(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 i=s(n);t(i)||o(i,{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 I(){const e=E(A());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>b(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&&_.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 i;if(t&&t.models.some(h)){i=[...new Set(t.models.filter(h).map(y))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=y(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>S[O(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(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&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.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 j=null,x=null;function T(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!0}}const G="HKCU\\SOFTWARE\\Policies\\Claude",U="HKLM\\SOFTWARE\\Policies\\Claude",$="vibecli-claude-policy.reg",K=e=>l("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let N=K;export function setRegRunnerForTests(e){N=e??K}function R(){return"win32"===process.platform}function F(e){let t;try{t=N(["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 H(){if(null===F(G))return;const e=i(claudeDesktopUserDataDir(),$);try{t(s(e))||o(s(e),{recursive:!0,mode:448}),f(e),N(["export",G,e,"/y"])}catch(e){d.logError("claude-desktop.snapshotPolicy",e)}}const B=Object.keys(T({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 F(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 z="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",J="https://claude.ai/download",W="darwin"===process.platform?`open ${J}`:"win32"===process.platform?`start ${J}`:`xdg-open ${J}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:z,installCommand:W,configPath:A(),isInstalled:function(){const e=g();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",i(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||i(e,"AppData","Local");o=[i(t,"AnthropicClaude"),i(t,"Programs","Claude"),...k.map(e=>i(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",i(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),i(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},wantsModelCatalog:!0,loadPrivateConfig(e){const t=I()??{appliedId:"",entries:[]};let o=P(t);o||(o={id:a(),name:C},t.entries.push(o));const n=T(e);x=!0===n.disableDeploymentModeChooser,j={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=E(w(o.id))??{};v(w(o.id),{...r,...n}),t.appliedId=o.id,v(A(),t);const s=E(M())??{};"3p"!==s.deploymentMode&&v(M(),{...s,deploymentMode:"3p"}),R()&&function(e){H();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)];N(["add",G,"/v",t,"/t",e,"/d",n,"/f"])}}(n)},unloadPrivateConfig(){R()&&function(e){const t=F(G);if(!t)return;H();let o=t.size;for(const n of e)t.has(n)&&(N(["delete",G,"/v",n,"/f"]),o-=1);o<=0&&N(["delete",G,"/f"])}(B);const e=I(),t=P(e);if(!e||!t)return;f(w(t.id)),r(w(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(f(A()),r(A(),{force:!0})):v(A(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(R()){const e=F(G)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=P(I());if(!e)return{apiKey:null};const t=E(w(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=I(),t=P(e);return{meta:e,applied:t?E(w(t.id)):null,desktop:E(M()),...R()?{policy:Object.fromEntries(F(G)??[])}:{}}}catch(e){return d.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;R()&&F(G)?.has("inferenceModels")&&(H(),N(["delete",G,"/v","inferenceModels","/f"]),e=!0);const t=P(I()),o=t?E(w(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;v(w(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(j){const t=j.models.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", ");e.push(p.t(`tools.claude_desktop.models_written_${j.source}`,{count:String(j.models.length),list:t,groups:j.groups.join(" / ")}))}null!==x&&e.push(p.t(x?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")),e.push(p.t("tools.claude_desktop.restart_hint")),R()&&e.push(p.t("tools.claude_desktop.registry_written",{key:G}));const t=detectManagedConfig();return t&&e.push(p.t("tools.claude_desktop.managed_conflict",{path:t})),e},forceResetPaths(){const e=[A(),M()];try{const t=P(I());t&&e.push(w(t.id))}catch{}return e},backupPaths(){const e=[A(),M()];R()&&e.push(i(claudeDesktopUserDataDir(),$));try{const t=P(I());t&&e.push(w(t.id))}catch{}return e}};
|
|
@@ -73,16 +73,24 @@ export interface ToolAdapter {
|
|
|
73
73
|
/**
|
|
74
74
|
* Drop any pinned model selection while keeping credentials and the user's
|
|
75
75
|
* own config intact, handing the tool back to its built-in default model.
|
|
76
|
-
* Optional — only tools that can pin a model implement it.
|
|
76
|
+
* Optional — only tools that can pin a model implement it. Adapters with
|
|
77
|
+
* `wantsModelCatalog` also get the key's catalog (absent offline).
|
|
77
78
|
* @returns the config keys actually cleared (empty when nothing was pinned)
|
|
78
79
|
*/
|
|
79
|
-
clearPinnedModels?(): string[];
|
|
80
|
+
clearPinnedModels?(catalog?: ModelCatalog): string[];
|
|
80
81
|
/**
|
|
81
82
|
* Every file this adapter writes (and therefore snapshots) — used by restore.
|
|
82
83
|
* Defaults to `[configPath]` when omitted; multi-file tools (Codex auth.json,
|
|
83
84
|
* Hermes .env, Claude Code .claude.json) override it.
|
|
84
85
|
*/
|
|
85
86
|
backupPaths?(): string[];
|
|
87
|
+
/**
|
|
88
|
+
* Files `--force` wipes (after snapshotting) before re-applying, so a config
|
|
89
|
+
* the user broke by hand is replaced wholesale by the recommended one.
|
|
90
|
+
* Defaults to `backupPaths()`; override to keep files that hold unrelated
|
|
91
|
+
* user state (Claude Code's `.claude.json` carries onboarding + MCP servers).
|
|
92
|
+
*/
|
|
93
|
+
forceResetPaths?(): string[];
|
|
86
94
|
}
|
|
87
95
|
/**
|
|
88
96
|
* Pick the single "main" model for tools that only take one model id.
|
package/dist/lib/wizard.d.ts
CHANGED
|
@@ -46,7 +46,14 @@ export declare class Wizard {
|
|
|
46
46
|
* the user backed out.
|
|
47
47
|
*/
|
|
48
48
|
configureToolModel(toolName: string): Promise<boolean>;
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* 「重置为推荐配置」:先确认,再整份删掉工具原有配置文件(已快照)并用当前
|
|
51
|
+
* profile 重写——给把配置改坏的用户兜底;随后走和 loadConfig 一样的收尾。
|
|
52
|
+
*/
|
|
53
|
+
forceConfig(toolName: string): Promise<void>;
|
|
54
|
+
loadConfig(toolName: string, options?: {
|
|
55
|
+
force?: boolean;
|
|
56
|
+
}): Promise<void>;
|
|
50
57
|
/**
|
|
51
58
|
* 只摘掉钉死的模型、保留 API Key 与用户自有配置(hooks / MCP / 插件),
|
|
52
59
|
* 让工具回到自带默认模型。
|
package/dist/lib/wizard.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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();
|
|
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{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as f}from"./tools/claude-desktop.js";import{readFileSync as y}from"fs";import{dirname as h,join as z}from"path";import{fileURLToPath as v}from"url";const k=h(v(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(y(z(k,"..","..","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_force_config",{tool:o.displayName}),value:"force"},{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):"force"===i?await this.forceConfig(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:y}=await this.promptWithHints([{type:"list",name:"choice",message:d.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===y.type)return!1;if("default"===y.type)a.setToolModel(i,null);else if("preset"===y.type)a.setToolModel(i,r(y.preset));else if("custom"===y.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"===y.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))}if("claude-desktop"===i){const t=a.getToolSetting(i,f),{hideOfficialLogin:o}=await e.prompt([{type:"confirm",name:"hideOfficialLogin",message:d.t("wizard.desktop_hide_official_login"),default:!1!==t}]);a.setToolSetting(i,f,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 forceConfig(t){const o=c[t]?.displayName||t,{confirm:a}=await e.prompt([{type:"confirm",name:"confirm",message:d.t("config.confirm_force",{tool:o}),default:!1}]);a&&await this.loadConfig(t,{force:!0})}async loadConfig(i,n={}){const r=a.getModelsForTool(i),s=await this.validatedKeyForConfiguration(a.getApiKeyForTool(i));if(!s)return void await e.prompt([{type:"input",name:"continue",message:d.t("wizard.press_enter")}]);const p=o(d.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,s,r,n),p.succeed(d.t("wizard.config_loaded",{tool:c[i]?.displayName||i})),n.force&&console.log(t.yellow(d.t("config.force_done",{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){p.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=await 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();
|
package/dist/locales/en_US.json
CHANGED
|
@@ -172,7 +172,9 @@
|
|
|
172
172
|
"update_failed": "Update failed",
|
|
173
173
|
"custom_model_mode": "Select custom model mode",
|
|
174
174
|
"custom_model_single": "Single model for all roles",
|
|
175
|
-
"custom_model_per_role": "Configure each role separately"
|
|
175
|
+
"custom_model_per_role": "Configure each role separately",
|
|
176
|
+
"desktop_hide_official_login": "Hide the official Claude.ai sign-in entry in Claude Desktop (gateway only)?",
|
|
177
|
+
"action_force_config": "Reset to recommended - (delete {{tool}}'s config files and rewrite them; repairs a broken config)"
|
|
176
178
|
},
|
|
177
179
|
"doctor": {
|
|
178
180
|
"checking": "Running health check...",
|
|
@@ -268,7 +270,9 @@
|
|
|
268
270
|
"clear_model_done": "Cleared the pinned model settings for {{tool}}; it now uses its built-in default:",
|
|
269
271
|
"clear_model_none": "{{tool}} has no pinned model settings — nothing to clear",
|
|
270
272
|
"clear_model_unsupported": "{{tool}} does not support clearing model settings",
|
|
271
|
-
"clear_model_restart_hint": "Restart {{tool}} to apply (your API key, hooks and MCP config were left untouched)"
|
|
273
|
+
"clear_model_restart_hint": "Restart {{tool}} to apply (your API key, hooks and MCP config were left untouched)",
|
|
274
|
+
"confirm_force": "Reset {{tool}}? Its config files are snapshotted, deleted, and rewritten from the recommended configuration (fields you added by hand are lost; undo with restore)",
|
|
275
|
+
"force_done": "Reset {{tool}}'s config files (originals snapshotted; undo with `vibecli config {{tool}} restore`)"
|
|
272
276
|
},
|
|
273
277
|
"logs": {
|
|
274
278
|
"directory": "Logs directory: {{dir}}",
|
|
@@ -313,7 +317,7 @@
|
|
|
313
317
|
"non_interactive_aborted": "Aborted - missing required input"
|
|
314
318
|
},
|
|
315
319
|
"setup": {
|
|
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",
|
|
320
|
+
"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 or vibecli setup --claude-official on|off",
|
|
317
321
|
"codex_1m_enabled": "Codex 1M context is enabled",
|
|
318
322
|
"codex_1m_disabled": "Codex 1M context is disabled",
|
|
319
323
|
"codex_1m_missing_key": "No saved Codex API key was found; configure one with --key or --codexkey first",
|
|
@@ -331,7 +335,9 @@
|
|
|
331
335
|
"apply_failed": "Failed to write {{tool}} config: {{reason}}",
|
|
332
336
|
"install_hint": " Hint: {{tool}} not detected yet; install via {{install}}",
|
|
333
337
|
"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)"
|
|
338
|
+
"skip_desktop_not_installed": "Claude Desktop is not installed; skipped (after installing, run vibecli setup --desktopkey sk-xxx or use the wizard)",
|
|
339
|
+
"desktop_official_login_missing_key": "No saved Claude Desktop API key found; run --key or --desktopkey first",
|
|
340
|
+
"claude_official_invalid": "Invalid value for --claude-official: {{value}} (expected on or off)"
|
|
335
341
|
},
|
|
336
342
|
"tools": {
|
|
337
343
|
"claude_desktop": {
|
|
@@ -340,7 +346,12 @@
|
|
|
340
346
|
"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
347
|
"models_written_gateway": "Wrote {{count}} models (1M flags from the gateway group table, matched groups: {{groups}}): {{list}}",
|
|
342
348
|
"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}}"
|
|
349
|
+
"models_written_slots": "Wrote {{count}} models (offline: from the profile's model slots, gateway model list not fetched): {{list}}",
|
|
350
|
+
"official_login_hidden": "The official Claude.ai sign-in entry is hidden; the desktop is gateway-only. Run vibecli setup --claude-official on to bring it back",
|
|
351
|
+
"official_login_shown": "The official Claude.ai sign-in entry stays visible, so users can switch between the gateway and their own account. Run vibecli setup --claude-official off to hide it"
|
|
352
|
+
},
|
|
353
|
+
"claude_code": {
|
|
354
|
+
"tier_fallback": "This key can't use Claude Code's default {{from}}; the {{tier}} alias now uses {{to}}"
|
|
344
355
|
}
|
|
345
356
|
}
|
|
346
357
|
}
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -172,7 +172,9 @@
|
|
|
172
172
|
"update_failed": "更新失败",
|
|
173
173
|
"custom_model_mode": "选择自定义模型模式",
|
|
174
174
|
"custom_model_single": "所有角色使用同一模型",
|
|
175
|
-
"custom_model_per_role": "分别配置每个角色"
|
|
175
|
+
"custom_model_per_role": "分别配置每个角色",
|
|
176
|
+
"desktop_hide_official_login": "隐藏 Claude Desktop 里的官方登录(Claude.ai 账号)入口,只走网关?",
|
|
177
|
+
"action_force_config": "重置为推荐配置 - (删掉 {{tool}} 原配置文件后整份重写,修复改坏的配置)"
|
|
176
178
|
},
|
|
177
179
|
"doctor": {
|
|
178
180
|
"checking": "正在进行健康检查...",
|
|
@@ -268,7 +270,9 @@
|
|
|
268
270
|
"clear_model_done": "已清除 {{tool}} 钉死的模型设置,恢复使用工具自带默认模型:",
|
|
269
271
|
"clear_model_none": "{{tool}} 当前没有钉死的模型设置,无需清理",
|
|
270
272
|
"clear_model_unsupported": "{{tool}} 不支持清除模型设置",
|
|
271
|
-
"clear_model_restart_hint": "请重启 {{tool}} 后生效(API Key 与你的 hooks / MCP 配置未改动)"
|
|
273
|
+
"clear_model_restart_hint": "请重启 {{tool}} 后生效(API Key 与你的 hooks / MCP 配置未改动)",
|
|
274
|
+
"confirm_force": "确认重置 {{tool}} 的配置吗?会先快照、再删掉它的配置文件,用推荐配置整份重写(您自行添加的字段会丢失,可用 restore 撤销)",
|
|
275
|
+
"force_done": "已重置 {{tool}} 的配置文件(原文件已快照,`vibecli config {{tool}} restore` 可撤销)"
|
|
272
276
|
},
|
|
273
277
|
"logs": {
|
|
274
278
|
"directory": "日志目录: {{dir}}",
|
|
@@ -313,7 +317,7 @@
|
|
|
313
317
|
"non_interactive_aborted": "已中止 - 缺少必要输入"
|
|
314
318
|
},
|
|
315
319
|
"setup": {
|
|
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",
|
|
320
|
+
"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 或 vibecli setup --claude-official on|off",
|
|
317
321
|
"codex_1m_enabled": "Codex 1M 上下文已开启",
|
|
318
322
|
"codex_1m_disabled": "Codex 1M 上下文已关闭",
|
|
319
323
|
"codex_1m_missing_key": "未找到已保存的 Codex API Key;请先用 --key 或 --codexkey 完成配置",
|
|
@@ -331,7 +335,9 @@
|
|
|
331
335
|
"apply_failed": "{{tool}} 配置写入失败: {{reason}}",
|
|
332
336
|
"install_hint": " 提示: 尚未检测到 {{tool}},可运行 {{install}} 安装",
|
|
333
337
|
"done": "完成: 已配置 {{count}} 个工具。终端输入 {{commands}} 即可开始编码!",
|
|
334
|
-
"skip_desktop_not_installed": "未检测到已安装的 Claude Desktop,已跳过(装好后运行 vibecli setup --desktopkey sk-xxx 或在向导里配置)"
|
|
338
|
+
"skip_desktop_not_installed": "未检测到已安装的 Claude Desktop,已跳过(装好后运行 vibecli setup --desktopkey sk-xxx 或在向导里配置)",
|
|
339
|
+
"desktop_official_login_missing_key": "未找到已保存的 Claude Desktop API Key;请先用 --key 或 --desktopkey 完成配置",
|
|
340
|
+
"claude_official_invalid": "--claude-official 的值无效: {{value}}(只接受 on / off)"
|
|
335
341
|
},
|
|
336
342
|
"tools": {
|
|
337
343
|
"claude_desktop": {
|
|
@@ -340,7 +346,12 @@
|
|
|
340
346
|
"registry_written": "已同步写入注册表 {{key}} 作为兜底(与官网下载的 .reg 等效);应用内「Configure Third-Party Inference」窗口会显示为托管状态,移除配置时会一并清理",
|
|
341
347
|
"models_written_gateway": "已写入 {{count}} 个模型(1M 标记依据网关分组表,匹配分组:{{groups}}):{{list}}",
|
|
342
348
|
"models_written_fallback": "已写入 {{count}} 个模型(网关分组表不可用或未匹配到分组,1M 标记按内置规则):{{list}}",
|
|
343
|
-
"models_written_slots": "已写入 {{count}} 个模型(离线:按当前 profile 的模型槽位,未拉取网关模型列表):{{list}}"
|
|
349
|
+
"models_written_slots": "已写入 {{count}} 个模型(离线:按当前 profile 的模型槽位,未拉取网关模型列表):{{list}}",
|
|
350
|
+
"official_login_hidden": "官方登录(Claude.ai 账号)入口已隐藏,桌面端只走网关;要恢复运行 vibecli setup --claude-official on",
|
|
351
|
+
"official_login_shown": "官方登录(Claude.ai 账号)入口保持可见,用户可在网关与官方账号间切换;要隐藏运行 vibecli setup --claude-official off"
|
|
352
|
+
},
|
|
353
|
+
"claude_code": {
|
|
354
|
+
"tier_fallback": "当前 Key 用不了 Claude Code 默认的 {{from}},{{tier}} 档已改用 {{to}}"
|
|
344
355
|
}
|
|
345
356
|
}
|
|
346
357
|
}
|
package/dist/models.json
CHANGED
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"model": "deepseek-v4-pro"
|
|
42
42
|
}
|
|
43
43
|
],
|
|
44
|
+
"claudeCode": {
|
|
45
|
+
"tierChains": {
|
|
46
|
+
"opus": ["claude-opus-5-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6"],
|
|
47
|
+
"sonnet": ["claude-sonnet-5", "claude-sonnet-4-6"],
|
|
48
|
+
"haiku": ["claude-haiku-4-5-20251001"],
|
|
49
|
+
"fable": ["claude-fable-5-1", "claude-fable-5", "claude-opus-5-5", "claude-opus-5", "claude-opus-4-8"]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
44
52
|
"claudeDesktop": {
|
|
45
53
|
"oneMillionFallback": [
|
|
46
54
|
"claude-opus-4-8",
|
package/models.json
CHANGED
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"model": "deepseek-v4-pro"
|
|
42
42
|
}
|
|
43
43
|
],
|
|
44
|
+
"claudeCode": {
|
|
45
|
+
"tierChains": {
|
|
46
|
+
"opus": ["claude-opus-5-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6"],
|
|
47
|
+
"sonnet": ["claude-sonnet-5", "claude-sonnet-4-6"],
|
|
48
|
+
"haiku": ["claude-haiku-4-5-20251001"],
|
|
49
|
+
"fable": ["claude-fable-5-1", "claude-fable-5", "claude-opus-5-5", "claude-opus-5", "claude-opus-4-8"]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
44
52
|
"claudeDesktop": {
|
|
45
53
|
"oneMillionFallback": [
|
|
46
54
|
"claude-opus-4-8",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.47",
|
|
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",
|