@vibeapi/cli 0.0.56 → 0.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,7 +94,8 @@ vibecli config <tool> restore # 还原该工具到上次更改前
94
94
  vibecli config <tool> clear-model # 清掉钉死的模型设置,回到工具自带默认(保留 API Key)
95
95
  vibecli config tool-model set codex gpt-6-astra # 单独给某工具指定模型
96
96
  vibecli config profile use work # 切换 profile
97
- vibecli doctor # 健康巡检
97
+ vibecli doctor # 健康巡检(--deep 实发一条请求)
98
+ vibecli upgrade # 升级 Claude Code / Codex,官方渠道连不上自动改走国内镜像
98
99
  ```
99
100
 
100
101
  ## 支持
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as r}from"./lib/command.js";import{i18n as o}from"./lib/i18n.js";import{logger as e}from"./utils/logger.js";!async function(){const s=process.argv.slice(2),i=new r;try{await i.execute(s)}catch(r){e.logError("CLI",r),console.error(`${o.t("cli.error_general")}`,r instanceof Error?r.message:String(r)),process.exit(1)}}();
2
+ Number(process.versions.node.split(".")[0])<20&&(console.error(`vibecli 需要 Node.js 20 或更高版本,当前是 v${process.versions.node}。\n请安装 Node.js LTS(国内镜像:https://registry.npmmirror.com/binary.html?path=node/ ,或用 nvm install --lts),装好后重开终端再运行。\nvibecli needs Node.js 20 or later (found v${process.versions.node}); install the LTS release and open a new terminal.`),process.exit(1));const{Command:e}=await import("./lib/command.js"),{i18n:r}=await import("./lib/i18n.js"),{logger:o}=await import("./utils/logger.js");try{await(new e).execute(process.argv.slice(2))}catch(e){o.logError("CLI",e),console.error(`${r.t("cli.error_general")}`,e instanceof Error?e.message:String(e)),process.exit(1)}export{};
@@ -1 +1 @@
1
- import{i18n as n}from"../lib/i18n.js";import{isJsonMode as e,emitJson as o}from"../lib/runtime-options.js";const i=["init","setup","lang","auth","doctor","status","logs","completion","update","bench","config","enter"],c=["profile","preset","backup","claude-code","codex"],s=["list","create","use","delete"],l=["list","save","use","delete"],t=["list","create","restore"],r=["revoke","reload"],m=["show","set"],a=["path","list","tail"],u=["bash","zsh","fish"],p=["--base","--non-interactive","--json","--yes","--dry-run","--no-color","--help","--version"];export async function completionCommand(_){const b=(_[0]||"").toLowerCase();if(!u.includes(b))return e()?void o({ok:!1,error:"unsupported_shell",shell:b}):void console.log(n.t("completion.unsupported_shell",{shell:b}));const $="bash"===b?`# vibecli bash completion\n_vibecli_completions() {\n local cur prev\n COMPREPLY=()\n cur="\${COMP_WORDS[COMP_CWORD]}"\n prev="\${COMP_WORDS[COMP_CWORD-1]}"\n\n case "\${prev}" in\n vibecli)\n COMPREPLY=( $(compgen -W "${i.join(" ")} ${p.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n config)\n COMPREPLY=( $(compgen -W "${c.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n profile)\n COMPREPLY=( $(compgen -W "${s.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n preset)\n COMPREPLY=( $(compgen -W "${l.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n backup)\n COMPREPLY=( $(compgen -W "${t.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n auth)\n COMPREPLY=( $(compgen -W "${r.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n lang)\n COMPREPLY=( $(compgen -W "${m.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n logs)\n COMPREPLY=( $(compgen -W "${a.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n completion)\n COMPREPLY=( $(compgen -W "${u.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n esac\n\n if [[ "\${cur}" == --* ]]; then\n COMPREPLY=( $(compgen -W "${p.join(" ")}" -- "\${cur}") )\n return 0\n fi\n}\ncomplete -F _vibecli_completions vibecli\n`:"zsh"===b?`#compdef vibecli\n_vibecli() {\n local -a commands subcommands\n local context state line\n\n _arguments -C \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --version)'{-v,--version}'[Show version]' \\\n '--base[Custom API base URL]:url:' \\\n '--non-interactive[Disable interactive prompts]' \\\n '--json[Emit JSON output]' \\\n '(-y --yes)'{-y,--yes}'[Auto-confirm prompts]' \\\n '--dry-run[Show planned changes without writing]' \\\n '--no-color[Disable colored output]' \\\n '1:command:->command' \\\n '*::arg:->args'\n\n case $state in\n command)\n commands=(\n${i.map(n=>` '${n}:vibecli ${n}'`).join("\n")}\n )\n _describe 'command' commands\n ;;\n args)\n case $line[1] in\n config)\n subcommands=(${c.map(n=>`'${n}'`).join(" ")})\n _describe 'config sub' subcommands\n ;;\n auth) _values 'auth sub' ${r.join(" ")} ;;\n lang) _values 'lang sub' ${m.join(" ")} ;;\n logs) _values 'logs action' ${a.join(" ")} ;;\n completion) _values 'shell' ${u.join(" ")} ;;\n esac\n ;;\n esac\n}\ncompdef _vibecli vibecli\n`:`# vibecli fish completion\ncomplete -c vibecli -n "__fish_use_subcommand" -a "${i.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from config" -a "${c.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from profile" -a "${s.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from preset" -a "${l.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from backup" -a "${t.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from auth" -a "${r.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from lang" -a "${m.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from logs" -a "${a.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from completion" -a "${u.join(" ")}"\ncomplete -c vibecli -l base -d 'Custom API base URL'\ncomplete -c vibecli -l non-interactive -d 'Disable interactive prompts'\ncomplete -c vibecli -l json -d 'Emit JSON output'\ncomplete -c vibecli -l yes -s y -d 'Auto-confirm prompts'\ncomplete -c vibecli -l dry-run -d 'Show planned changes without writing'\ncomplete -c vibecli -l no-color -d 'Disable colored output'\n`;e()?o({shell:b,script:$}):(console.log($),"bash"===b?console.log("# "+n.t("completion.instructions_bash")):"zsh"===b?console.log("# "+n.t("completion.instructions_zsh")):console.log("# "+n.t("completion.instructions_fish")))}
1
+ import{i18n as n}from"../lib/i18n.js";import{isJsonMode as e,emitJson as o}from"../lib/runtime-options.js";const i=["init","setup","lang","auth","doctor","status","logs","completion","update","upgrade","bench","config","enter"],c=["profile","preset","backup","claude-code","codex"],s=["list","create","use","delete"],l=["list","save","use","delete"],t=["list","create","restore"],r=["revoke","reload"],m=["show","set"],a=["path","list","tail"],u=["bash","zsh","fish"],p=["--base","--non-interactive","--json","--yes","--dry-run","--no-color","--help","--version"];export async function completionCommand(_){const b=(_[0]||"").toLowerCase();if(!u.includes(b))return e()?void o({ok:!1,error:"unsupported_shell",shell:b}):void console.log(n.t("completion.unsupported_shell",{shell:b}));const $="bash"===b?`# vibecli bash completion\n_vibecli_completions() {\n local cur prev\n COMPREPLY=()\n cur="\${COMP_WORDS[COMP_CWORD]}"\n prev="\${COMP_WORDS[COMP_CWORD-1]}"\n\n case "\${prev}" in\n vibecli)\n COMPREPLY=( $(compgen -W "${i.join(" ")} ${p.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n config)\n COMPREPLY=( $(compgen -W "${c.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n profile)\n COMPREPLY=( $(compgen -W "${s.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n preset)\n COMPREPLY=( $(compgen -W "${l.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n backup)\n COMPREPLY=( $(compgen -W "${t.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n auth)\n COMPREPLY=( $(compgen -W "${r.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n lang)\n COMPREPLY=( $(compgen -W "${m.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n logs)\n COMPREPLY=( $(compgen -W "${a.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n completion)\n COMPREPLY=( $(compgen -W "${u.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n esac\n\n if [[ "\${cur}" == --* ]]; then\n COMPREPLY=( $(compgen -W "${p.join(" ")}" -- "\${cur}") )\n return 0\n fi\n}\ncomplete -F _vibecli_completions vibecli\n`:"zsh"===b?`#compdef vibecli\n_vibecli() {\n local -a commands subcommands\n local context state line\n\n _arguments -C \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --version)'{-v,--version}'[Show version]' \\\n '--base[Custom API base URL]:url:' \\\n '--non-interactive[Disable interactive prompts]' \\\n '--json[Emit JSON output]' \\\n '(-y --yes)'{-y,--yes}'[Auto-confirm prompts]' \\\n '--dry-run[Show planned changes without writing]' \\\n '--no-color[Disable colored output]' \\\n '1:command:->command' \\\n '*::arg:->args'\n\n case $state in\n command)\n commands=(\n${i.map(n=>` '${n}:vibecli ${n}'`).join("\n")}\n )\n _describe 'command' commands\n ;;\n args)\n case $line[1] in\n config)\n subcommands=(${c.map(n=>`'${n}'`).join(" ")})\n _describe 'config sub' subcommands\n ;;\n auth) _values 'auth sub' ${r.join(" ")} ;;\n lang) _values 'lang sub' ${m.join(" ")} ;;\n logs) _values 'logs action' ${a.join(" ")} ;;\n completion) _values 'shell' ${u.join(" ")} ;;\n esac\n ;;\n esac\n}\ncompdef _vibecli vibecli\n`:`# vibecli fish completion\ncomplete -c vibecli -n "__fish_use_subcommand" -a "${i.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from config" -a "${c.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from profile" -a "${s.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from preset" -a "${l.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from backup" -a "${t.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from auth" -a "${r.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from lang" -a "${m.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from logs" -a "${a.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from completion" -a "${u.join(" ")}"\ncomplete -c vibecli -l base -d 'Custom API base URL'\ncomplete -c vibecli -l non-interactive -d 'Disable interactive prompts'\ncomplete -c vibecli -l json -d 'Emit JSON output'\ncomplete -c vibecli -l yes -s y -d 'Auto-confirm prompts'\ncomplete -c vibecli -l dry-run -d 'Show planned changes without writing'\ncomplete -c vibecli -l no-color -d 'Disable colored output'\n`;e()?o({shell:b,script:$}):(console.log($),"bash"===b?console.log("# "+n.t("completion.instructions_bash")):"zsh"===b?console.log("# "+n.t("completion.instructions_zsh")):console.log("# "+n.t("completion.instructions_fish")))}
@@ -1 +1,3 @@
1
- export declare function doctorCommand(): Promise<void>;
1
+ export declare function doctorCommand(options?: {
2
+ deep?: boolean;
3
+ }): Promise<void>;
@@ -1 +1 @@
1
- import o from"chalk";import e from"ora";import{configManager as t}from"../lib/config.js";import{toolManager as s}from"../lib/tool-manager.js";import{i18n as n}from"../lib/i18n.js";import{validateApiKey as l}from"../lib/api-validator.js";export async function doctorCommand(){const a=e(n.t("doctor.checking")).start(),r=[];a.text=n.t("doctor.check_path");try{const o=process.env.PATH||"";r.push({name:"PATH",passed:o.length>0,message:o.length>0?void 0:"PATH is empty"})}catch(o){r.push({name:"PATH",passed:!1,message:String(o)})}a.text=n.t("doctor.check_api_key_network");const i=t.getApiKey();if(i){const e=await l(i);e.valid?r.push({name:"API Key & Network",passed:!0}):"invalid_api_key"===e.error?r.push({name:"API Key & Network",passed:!1,message:o.yellow(n.t("doctor.api_key_invalid"))}):r.push({name:"API Key & Network",passed:!1,message:o.yellow(n.t("doctor.network_error"))})}else r.push({name:"API Key & Network",passed:!1,message:o.yellow(n.t("doctor.api_key_missing"))});if("win32"===process.platform&&s.isToolInstalled("claude-code")){a.text=n.t("doctor.check_git_env");const e=s.isGitInstalled();r.push({name:"Git Environment",passed:e,message:e?void 0:o.yellow(n.t("doctor.git_not_installed"))})}a.text=n.t("doctor.check_tools");const c=s.getInstalledTools();r.push({name:"Installed Tools",passed:c.length>0,message:c.length>0?`${c.map(o=>s.getSupportedTools().find(e=>e.name===o)?.displayName||o).join(", ")}`:o.yellow(n.t("doctor.no_tools_installed"))}),a.stop(),console.log("\n"+o.bold(n.t("doctor.results_title"))+"\n");for(const e of r){const t=e.passed?o.green("✓"):o.red("✗"),s=e.passed?o.green("PASS"):o.red("FAIL");console.log(`${t} ${e.name}: ${s}`),e.message&&console.log(` ${e.message}`)}r.every(o=>o.passed)?console.log("\n"+o.green.bold(n.t("doctor.all_good"))):(console.log("\n"+o.yellow.bold(n.t("doctor.suggestions"))),console.log(o.gray(" - "+n.t("doctor.suggestion_auth"))),console.log(o.gray(" - "+n.t("doctor.suggestion_interactive"))))}
1
+ import t from"chalk";import o from"ora";import{spawnSync as s}from"child_process";import{readFileSync as e}from"fs";import{connect as a}from"net";import{join as n}from"path";import{configManager as r}from"../lib/config.js";import{toolManager as c}from"../lib/tool-manager.js";import{i18n as i}from"../lib/i18n.js";import{validateApiKey as l}from"../lib/api-validator.js";import{getAdapter as d}from"../lib/tools/registry.js";import{findInstalls as p,installLabel as u}from"../lib/tools/tool-installs.js";import{detectManagedOverrides as m}from"../lib/tools/claude-managed.js";import{toolHome as g}from"../lib/tools/types.js";const _=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function f(t,o,s=3e3){return new Promise(e=>{const n=a({host:t,port:o}),r=t=>{n.destroy(),e(t)};n.setTimeout(s,()=>r(!1)),n.once("connect",()=>r(!0)),n.once("error",()=>r(!1))})}export async function doctorCommand(a={}){const h=o(i.t("doctor.checking")).start(),y=[],w=Number(process.versions.node.split(".")[0]);y.push({name:"Node.js",status:w>=20?"pass":"fail",message:`v${process.versions.node}`}),h.text=i.t("doctor.check_proxy"),y.push(await async function(){const t=i.t("doctor.proxy"),o=new Map;for(const t of _){const s=process.env[t]?.trim();s&&!o.has(s)&&o.set(s,t)}if(0===o.size)return{name:t,status:"pass",message:i.t("doctor.proxy_none")};const s=[];for(const[t,e]of o){let o;try{o=new URL(t.includes("://")?t:`http://${t}`)}catch{s.push(`${e}=${t}`);continue}const a=Number(o.port)||("https:"===o.protocol?443:o.protocol.startsWith("socks")?1080:80);await f(o.hostname,a)||s.push(`${e}=${t}`)}return 0===s.length?{name:t,status:"pass",message:[...o.keys()].join(", ")}:{name:t,status:"fail",message:i.t("doctor.proxy_dead",{proxies:s.join(", ")})}}()),h.text=i.t("doctor.check_api_key_network");const $=r.getApiKey(),T=i.t("doctor.api_key_network");if($){const t=await l($);t.valid?y.push({name:T,status:"pass"}):"invalid_api_key"===t.error?y.push({name:T,status:"fail",message:i.t("doctor.api_key_invalid")}):y.push({name:T,status:"fail",message:`${i.t("doctor.network_error")}: ${t.message??t.error}`})}else y.push({name:T,status:"fail",message:i.t("doctor.api_key_missing")});h.text=i.t("doctor.check_tools");const j=c.getInstalledTools();y.push({name:i.t("doctor.installed_tools"),status:j.length>0?"pass":"warn",message:j.length>0?j.map(t=>d(t)?.displayName??t).join(", "):i.t("doctor.no_tools_installed")}),y.push(...function(){const t=[];for(const o of c.getInstalledTools()){const s=d(o);if(!s?.installSpec)continue;const e=p(s.installSpec);if(0===e.length)continue;const a=[],n=c.outdatedVersion(o);n&&a.push(i.t("doctor.tool_outdated",{min:n.min})),e.length>1&&a.push(i.t("doctor.tool_duplicates",{others:e.slice(1).map(u).join("、")})),t.push({name:s.displayName,status:a.length>0?"warn":"pass",message:[u(e[0]),...a,...a.length>0?[i.t("doctor.run_upgrade",{tool:o})]:[]].join("\n ")})}return t}());const k=function(){if(!c.isToolInstalled("claude-code"))return null;const t=i.t("doctor.claude_config"),o=m();if(o.length>0){const s=o.map(t=>`${t.source}: ${t.keys.join(", ")}`).join("; ");return{name:t,status:"fail",message:i.t("doctor.claude_managed",{detail:s})}}let s={};try{s=JSON.parse(e(n(g(),".claude","settings.json"),"utf-8")).env??{}}catch{}const a=r.getApiKeyForTool("claude-code");return s.ANTHROPIC_AUTH_TOKEN&&s.ANTHROPIC_BASE_URL?s.ANTHROPIC_BASE_URL!==r.getBaseUrl()||a&&s.ANTHROPIC_AUTH_TOKEN!==a?{name:t,status:"warn",message:i.t("doctor.claude_config_changed")}:{name:t,status:"pass"}:{name:t,status:"warn",message:i.t("doctor.claude_not_configured")}}();k&&y.push(k),"win32"===process.platform&&j.includes("claude-code")&&!process.env.CLAUDE_CODE_GIT_BASH_PATH&&y.push({name:"Git for Windows",status:c.isGitInstalled()?"pass":"warn",message:c.isGitInstalled()?void 0:i.t("tools.claude_code.no_git_bash")}),a.deep&&j.includes("claude-code")&&(h.text=i.t("doctor.check_claude_request"),y.push(function(){const t=i.t("doctor.claude_request"),o=p(d("claude-code").installSpec)[0];if(!o)return{name:t,status:"warn",message:i.t("doctor.claude_request_skipped")};const e={encoding:"utf-8",timeout:12e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},a=["-p","Reply with the single word OK."],n=/\.cmd$/i.test(o.path)?s(`"${o.path}" ${a.map(t=>`"${t}"`).join(" ")}`,{...e,shell:!0}):s(o.path,a,e),r=`${n.stdout??""}`.trim();if(0===n.status&&r)return{name:t,status:"pass",message:r.slice(0,80)};const c=(`${n.stderr??""}`.trim()||r||n.error?.message||`exit ${n.status}`).slice(0,300);return{name:t,status:"fail",message:i.t("doctor.claude_request_failed",{reason:c})}}())),h.stop(),console.log("\n"+t.bold(i.t("doctor.results_title"))+"\n");const x={pass:t.green("√"),warn:t.yellow("!"),fail:t.red("×")};for(const o of y)console.log(`${x[o.status]} ${o.name}`),o.message&&console.log(t.gray(` ${o.message}`));y.every(t=>"pass"===t.status)&&console.log("\n"+t.green.bold(i.t("doctor.all_good"))),!a.deep&&j.includes("claude-code")&&console.log(t.gray("\n"+i.t("doctor.deep_hint"))),y.some(t=>"fail"===t.status)&&(process.exitCode=1)}
@@ -1 +1 @@
1
- import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{i18n as t}from"../lib/i18n.js";import{configManager as s}from"../lib/config.js";import{toolManager as l,SUPPORTED_TOOLS as a}from"../lib/tool-manager.js";import{validateApiKey as i,fetchModels as n}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as r}from"../lib/tools/codex.js";import{resolveModelCatalog as p}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as c}from"../lib/tools/claude-desktop.js";async function u(e){const o=await n(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 f(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 m(o){console.log(e.green(o))}function g(o){console.log(e.yellow(o))}function y(o){console.error(e.red(o))}async function k(o,s){console.log(e.gray(t.t("setup.validating",{label:o})));const l=await i(s);return!!l.valid||(y(t.t("setup.invalid_key",{label:o,reason:l.message||l.error||"invalid"})),!1)}async function _(o,i,n,r,p){const u=a[o];try{"codex"===o&&void 0!==n&&s.setToolSetting("codex",d,n),"claude-desktop"===o&&void 0!==r&&s.setToolSetting("claude-desktop",c,!r),await l.applyPrivateConfig(o,i,s.getModelsForTool(o),{force:p})}catch(e){const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return y("× "+t.t("setup.apply_failed",{tool:u.displayName,reason:o})),!1}return l.printApplied(o),p&&console.log(e.gray(" "+t.t("config.force_done",{tool:u.displayName}))),"codex"===o&&void 0!==n&&console.log(e.gray(" "+t.t(n?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),l.isToolInstalled(o)||console.log(e.yellow(" ! "+t.t("setup.install_hint",{tool:u.displayName,install:u.installCommand}))),!0}export async function setupCommand(e){const i=e.key?.trim(),n=e.claudekey?.trim(),c=e.codexkey?.trim(),b=e.grokkey?.trim(),h=e.desktopkey?.trim(),x=void 0!==e.oneMillion,v=void 0!==e.claudeOfficial;let C;if(v&&(C=parseOnOff(e.claudeOfficial),void 0===C))return y(t.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const w=Boolean(i||n||c||b||h);if(!w&&(x||v)){if(x){const o=s.getApiKeyForTool("codex");if(o)try{const l=!0===e.oneMillion;s.setToolSetting("codex",d,l);const a=await p(o);r(l,a??void 0),m(t.t(l?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){y(t.t("setup.apply_failed",{tool:a.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else y(t.t("setup.codex_1m_missing_key")),process.exitCode=1}if(v){const o=s.getApiKeyForTool("claude-desktop");o?await _("claude-desktop",o,void 0,C,e.force)||(process.exitCode=1):(y(t.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!w)return y(t.t("setup.usage")),void(process.exitCode=1);let K=0,G=0;const M=new Set,j=[];n&&j.push({tool:"claude-code",label:"--claudekey",apiKey:n,need:"claude"}),c&&j.push({tool:"codex",label:"--codexkey",apiKey:c,need:"gpt"}),b&&j.push({tool:"grok",label:"--grokkey",apiKey:b,need:"grok"}),h&&j.push({tool:"claude-desktop",label:"--desktopkey",apiKey:h,need:"claude"});for(const o of j){if(!await k(o.label,o.apiKey)){G+=1;continue}const l=await u(o.apiKey);!l.probed||f(l,o.need)?(l.probed||g(t.t("setup.probe_failed",{label:o.label})),s.setToolApiKey(o.tool,o.apiKey),await _(o.tool,o.apiKey,e.oneMillion,C,e.force)?(K+=1,M.add(o.tool)):G+=1):(y(t.t(`setup.key_no_${o.need}`,{label:o.label,tool:a[o.tool].displayName})),G+=1)}if(i){if(!await k("--key",i))return void(process.exitCode=1);s.setApiKey(i);const o=await u(i);if(o.probed||g(t.t("setup.probe_failed",{label:"--key"})),o.probed&&!o.hasClaude&&!o.hasGpt&&!o.hasGrok)return y(t.t("setup.key_no_family")),void(process.exitCode=1);const a=[{tool:"claude-code",capable:o.hasClaude,skipMsg:t.t("setup.skip_no_claude")},{tool:"codex",capable:o.hasGpt,skipMsg:t.t("setup.skip_no_gpt")},{tool:"grok",capable:o.hasGrok,skipMsg:t.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:o.hasClaude&&l.isToolInstalled("claude-desktop"),skipMsg:t.t("setup.skip_desktop_not_installed"),quiet:!o.hasClaude}];for(const o of a)M.has(o.tool)||(o.capable?await _(o.tool,i,e.oneMillion,C,e.force)?(K+=1,M.add(o.tool)):G+=1:o.quiet||g(o.skipMsg))}if(K>0){const e=[...M].filter(e=>!o(e)?.isInstalled).map(e=>a[e]?.command).filter(Boolean).join(" / "),s=t.t("setup.done",{count:String(K)});m("\n"+(e?`${s}${t.t("setup.done_commands",{commands:e})}`:s))}G>0&&y(t.t("setup.failed_summary",{count:String(G)})),(G>0||0===K)&&(process.exitCode=1)}
1
+ import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{i18n as s}from"../lib/i18n.js";import{configManager as l}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as i}from"../lib/tool-manager.js";import{validateApiKey as n,fetchModels as d}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as r,setCodexOneMillionContext as p}from"../lib/tools/codex.js";import{resolveModelCatalog as c}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as u}from"../lib/tools/claude-desktop.js";async function f(e){const o=await d(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 m(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 g(o){console.log(e.green(o))}function y(o){console.log(e.yellow(o))}function k(o){console.error(e.red(o))}async function _(o,t){console.log(e.gray(s.t("setup.validating",{label:o})));const l=await n(t);return!!l.valid||(k(s.t("setup.invalid_key",{label:o,reason:l.message||l.error||"invalid"})),!1)}async function b(o,t,n,d,p){const c=i[o];try{"codex"===o&&void 0!==n&&l.setToolSetting("codex",r,n),"claude-desktop"===o&&void 0!==d&&l.setToolSetting("claude-desktop",u,!d),await a.applyPrivateConfig(o,t,l.getModelsForTool(o),{force:p})}catch(e){const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return k("× "+s.t("setup.apply_failed",{tool:c.displayName,reason:o})),!1}return a.printApplied(o),p&&console.log(e.gray(" "+s.t("config.force_done",{tool:c.displayName}))),"codex"===o&&void 0!==n&&console.log(e.gray(" "+s.t(n?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),a.isToolInstalled(o)||console.log(e.yellow(" ! "+s.t("setup.install_hint",{tool:c.displayName,install:c.installCommand}))),!0}export async function setupCommand(e){const n=e.key?.trim(),d=e.claudekey?.trim(),u=e.codexkey?.trim(),h=e.grokkey?.trim(),x=e.desktopkey?.trim(),v=void 0!==e.oneMillion,C=void 0!==e.claudeOfficial;let w;if(C&&(w=parseOnOff(e.claudeOfficial),void 0===w))return k(s.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const K=Boolean(n||d||u||h||x);if(!K&&(v||C)){if(v){const o=l.getApiKeyForTool("codex");if(o)try{const t=!0===e.oneMillion;l.setToolSetting("codex",r,t);const a=await c(o);p(t,a??void 0),g(s.t(t?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){k(s.t("setup.apply_failed",{tool:i.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else k(s.t("setup.codex_1m_missing_key")),process.exitCode=1}if(C){const o=l.getApiKeyForTool("claude-desktop");o?await b("claude-desktop",o,void 0,w,e.force)||(process.exitCode=1):(k(s.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!K)return k(s.t("setup.usage")),void(process.exitCode=1);let j=0,G=0;const M=new Set,S=[];d&&S.push({tool:"claude-code",label:"--claudekey",apiKey:d,need:"claude"}),u&&S.push({tool:"codex",label:"--codexkey",apiKey:u,need:"gpt"}),h&&S.push({tool:"grok",label:"--grokkey",apiKey:h,need:"grok"}),x&&S.push({tool:"claude-desktop",label:"--desktopkey",apiKey:x,need:"claude"});for(const o of S){if(!await _(o.label,o.apiKey)){G+=1;continue}const t=await f(o.apiKey);!t.probed||m(t,o.need)?(t.probed||y(s.t("setup.probe_failed",{label:o.label})),l.setToolApiKey(o.tool,o.apiKey),await b(o.tool,o.apiKey,e.oneMillion,w,e.force)?(j+=1,M.add(o.tool)):G+=1):(k(s.t(`setup.key_no_${o.need}`,{label:o.label,tool:i[o.tool].displayName})),G+=1)}if(n){if(!await _("--key",n))return void(process.exitCode=1);l.setApiKey(n);const o=await f(n);if(o.probed||y(s.t("setup.probe_failed",{label:"--key"})),o.probed&&!o.hasClaude&&!o.hasGpt&&!o.hasGrok)return k(s.t("setup.key_no_family")),void(process.exitCode=1);const t=[{tool:"claude-code",capable:o.hasClaude,skipMsg:s.t("setup.skip_no_claude")},{tool:"codex",capable:o.hasGpt,skipMsg:s.t("setup.skip_no_gpt")},{tool:"grok",capable:o.hasGrok,skipMsg:s.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:o.hasClaude&&a.isToolInstalled("claude-desktop"),skipMsg:s.t("setup.skip_desktop_not_installed"),quiet:!o.hasClaude}];for(const o of t)M.has(o.tool)||(o.capable?await b(o.tool,n,e.oneMillion,w,e.force)?(j+=1,M.add(o.tool)):G+=1:o.quiet||y(o.skipMsg))}if(j>0){const e=[...M].filter(e=>!o(e)?.isInstalled).map(e=>i[e]?.command).filter(Boolean).join(" / "),t=s.t("setup.done",{count:String(j)});g("\n"+(e?`${t}${s.t("setup.done_commands",{commands:e})}`:t))}G>0&&k(s.t("setup.failed_summary",{count:String(G)})),await t([...M]),(G>0||0===j)&&(process.exitCode=1)}
@@ -0,0 +1,2 @@
1
+ /** `vibecli upgrade [tool...]`: no argument = every installed tool vibecli knows how to upgrade. */
2
+ export declare function upgradeCommand(tools: string[]): Promise<void>;
@@ -0,0 +1 @@
1
+ import o from"chalk";import{i18n as r}from"../lib/i18n.js";import{toolManager as t}from"../lib/tool-manager.js";import{listAdapters as e}from"../lib/tools/registry.js";import{upgradeTool as i}from"../lib/tool-upgrade.js";export async function upgradeCommand(l){const n=e().filter(o=>o.installSpec).map(o=>o.name),s=l.length>0?l:n.filter(o=>t.isToolInstalled(o)),p=s.filter(o=>!n.includes(o));if(p.length>0)return console.error(o.red(r.t("upgrade.unsupported",{tool:p.join(", ")}))),console.error(r.t("upgrade.supported",{tools:n.join(" / ")})),void(process.exitCode=1);if(0===s.length)return void console.log(r.t("upgrade.nothing_installed",{tools:n.join(" / ")}));let a=0;for(const o of s)await i(o)||(a+=1);a>0&&(process.exitCode=1)}
@@ -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 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}}
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{upgradeCommand as u}from"../commands/upgrade.js";import{setupCommand as f}from"../commands/setup.js";import h from"chalk";import{readFileSync as y}from"fs";import{join as k,dirname as b}from"path";import{fileURLToPath as v}from"url";import{setRuntimeOptions as w,detectColorPreferenceFromEnv as $,isColorEnabled as C,isJsonMode as x}from"./runtime-options.js";import{cleanupAnthropicEnvVars as j,machineEnvVarsLeft as A}from"./env-cleanup.js";import{runStartupUpdate as I}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=v(import.meta.url),o=b(e),a=k(o,"../../package.json");return JSON.parse(y(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--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 f({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")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),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("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await u(e)}),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${h.bold(o.t("cli.examples"))}:\n ${h.gray("$ vibecli # Interactive main menu")}\n ${h.gray("$ vibecli init # Run first-time setup wizard")}\n ${h.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${h.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${h.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${h.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${h.gray("$ vibecli config codex --force # Same, for one tool")}\n ${h.gray("$ vibecli status --json # Machine-readable status")}\n ${h.gray("$ vibecli config profile list")}\n ${h.gray("$ vibecli config profile create work")}\n ${h.gray("$ vibecli config profile use work")}\n ${h.gray("$ vibecli config preset save fast")}\n ${h.gray("$ vibecli config backup create snapshot")}\n ${h.gray("$ vibecli auth <token> --non-interactive")}\n ${h.gray("$ vibecli auth revoke --yes")}\n ${h.gray("$ vibecli logs path")}\n ${h.gray("$ vibecli completion bash")}\n ${h.gray("$ vibecli bench --samples 3")}\n ${h.gray("$ vibecli update")}\n ${h.gray("$ vibecli upgrade claude-code")}\n ${h.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=j();if(0===e.length||x())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(C()?h.yellow(a):a);const t=A();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(C()?h.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&$();let i;w({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(h.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){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 I(e)).handled)return;await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=C()?h.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -14,6 +14,16 @@ export declare function getShellRcFilePath(): string | null;
14
14
  * 返回 rc 文件是否被修改。
15
15
  */
16
16
  export declare function cleanupShellRcEnvVars(): boolean;
17
+ /** 测试注入假的 PowerShell。 */
18
+ export declare function setPowerShellRunnerForTests(fake: (script: string) => string): void;
19
+ /**
20
+ * Windows 上这些变量的持久来源是「用户环境变量」「系统环境变量」和 PowerShell 配置文件里的
21
+ * `$env:NAME = ...`。用户级用 .NET 接口删除(会广播变更,新开的终端立即生效,不需要管理员);
22
+ * 系统级需要管理员,删不掉的名字返回给调用方提示用户。
23
+ */
24
+ export declare function cleanupWindowsEnvVars(names: readonly string[]): {
25
+ machineLeft: string[];
26
+ };
17
27
  /**
18
28
  * 检测并清除环境中的 ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL:
19
29
  * 1. 从当前 shell 的 rc 文件删掉对应的 export 行(持久来源);
@@ -22,3 +32,5 @@ export declare function cleanupShellRcEnvVars(): boolean;
22
32
  * 返回被清除的变量名(未检测到时为空数组)。
23
33
  */
24
34
  export declare function cleanupAnthropicEnvVars(): string[];
35
+ /** Windows 系统级环境变量里删不掉(需要管理员)的名字,来自最近一次 `cleanupAnthropicEnvVars()`。 */
36
+ export declare function machineEnvVarsLeft(): string[];
@@ -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_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]}
1
+ import{execFileSync as e}from"child_process";import{existsSync as n,readFileSync as r,writeFileSync as o}from"fs";import{join as t}from"path";import{logger as i}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 e=getShellRcFilePath();if(!e||!n(e))return!1;let t=r(e,"utf-8");const s=t,l=[...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 l)t=t.replace(e,"");if(t!==s)return o(e,t,"utf-8"),i.info(`Cleaned up ANTHROPIC_* environment variables from ${e}`),!0}catch(e){i.warn("Failed to cleanup shell environment variables:",e),i.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}let l=n=>e("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf-8",windowsHide:!0,timeout:2e4});export function setPowerShellRunnerForTests(e){l=e}export function cleanupWindowsEnvVars(e){const c=[];if("win32"!==process.platform||0===e.length)return{machineLeft:c};try{const n=e.map(e=>`'${e}'`).join(","),r=l(`foreach($n in @(${n})){if($null -ne [Environment]::GetEnvironmentVariable($n,'User')){[Environment]::SetEnvironmentVariable($n,$null,'User');"user:$n"};if($null -ne [Environment]::GetEnvironmentVariable($n,'Machine')){try{[Environment]::SetEnvironmentVariable($n,$null,'Machine');"machine-removed:$n"}catch{"machine:$n"}}}`);for(const e of r.split(/\r?\n/)){const n=e.trim().match(/^machine:(\w+)$/);n&&c.push(n[1])}i.info(`Windows env cleanup: ${r.replace(/\s+/g," ").trim()}`)}catch(e){i.logError("envCleanup.cleanupWindowsEnvVars",e)}for(const l of function(){const e=s(),n=[];for(const r of[t(e,"Documents"),t(e,"OneDrive","Documents"),t(e,"OneDrive","文档")])for(const e of["PowerShell","WindowsPowerShell"])n.push(t(r,e,"Microsoft.PowerShell_profile.ps1"),t(r,e,"profile.ps1"));return n}())try{if(!n(l))continue;const t=r(l,"utf-8");let s=t;for(const n of e)s=s.replace(new RegExp(String.raw`^[ \t]*\$env:${n}[ \t]*=.*(\r?\n)?`,"gim"),"");s!==t&&(o(l,s,"utf-8"),i.info(`Removed ANTHROPIC_* assignments from ${l}`))}catch(e){i.logError("envCleanup.powerShellProfile",e)}return{machineLeft:c}}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars(),c=cleanupWindowsEnvVars(e).machineLeft;for(const n of e)delete process.env[n];return[...e]}let c=[];export function machineEnvVarsLeft(){return c}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * What setup and the wizard ask right after writing configs, in interactive
3
+ * terminals only: upgrade tools too old for the config (`vibecli upgrade`), and
4
+ * restart GUI apps that are running and would otherwise keep the old config.
5
+ */
6
+ export declare function postApplyPrompts(tools: string[]): Promise<void>;
@@ -0,0 +1 @@
1
+ import o from"chalk";import t from"inquirer";import{i18n as r}from"./i18n.js";import{isNonInteractive as e}from"./runtime-options.js";import{toolManager as i}from"./tool-manager.js";import{getAdapter as n}from"./tools/registry.js";import{upgradeTool as s}from"./tool-upgrade.js";async function a(o){const{ok:r}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return r}export async function postApplyPrompts(t){if(e()||!process.stdin.isTTY)return;const p=t.filter(o=>n(o)?.installSpec&&i.outdatedVersion(o));if(p.length>0){const o=p.map(o=>n(o)?.displayName??o).join("、");if(await a(r.t("setup.offer_upgrade",{tools:o})))for(const o of p)await s(o)}for(const e of t){const t=n(e);if(!t?.runningApp?.isRunning())continue;if(!await a(r.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+r.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+r.t("setup.restart_failed",{tool:t.displayName})))}}
@@ -1 +1 @@
1
- import{execSync as o,spawnSync as t}from"child_process";import{i18n as n}from"./i18n.js";import l from"inquirer";import e from"chalk";import s from"terminal-link";import i from"ora";import{logger as r}from"../utils/logger.js";import{configManager as a}from"./config.js";import{getAdapter as c,listAdapters as d,TOOL_ADAPTERS as u}from"./tools/registry.js";import{backupBeforeWrite as g,listBackups as m,restoreBackup as p}from"./tools/backup.js";import{existsSync as f,rmSync as w}from"fs";import{stringWidth as h}from"../utils/string-width.js";import{resolveModelCatalog as _}from"./model-catalog.js";import{compareVersions as y,getToolMinVersion as P}from"./model-registry.js";export{y as compareVersions};function C(o){const t=u[o];return{name:t.name,command:t.command,installCommand:t.installCommand,configPath:t.configPath,displayName:t.displayName,...t.hidden?{hidden:!0}:{}}}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(u).map(o=>[o,C(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(t){const n=SUPPORTED_TOOLS[t];if(!n)return!1;const l=c(t);if(l?.isInstalled)try{return l.isInstalled()}catch{return!1}try{const t="win32"===process.platform?`where ${n.command}`:`which ${n.command}`;return o(t,{stdio:"pipe"}),!0}catch{return!1}}async installTool(t){const a=SUPPORTED_TOOLS[t];if(!a)throw new Error(`Unknown tool: ${t}`);const c=i(n.t("wizard.installing_tool")).start();try{c.info(e.gray("$ ")+e.white(a.installCommand)),c.stop(),o(a.installCommand,{stdio:"inherit"}),c.succeed(n.t("wizard.tool_installed"))}catch(i){c.fail(n.t("wizard.install_failed"));const d=(i.message||"")+(i.stderr?.toString()||"")+(i.stdout?.toString()||"");if(!(d.includes("EACCES")||d.includes("permission denied")||d.includes("EPERM")||243===i.status))throw new Error(`Failed to install ${a.displayName}: ${i}`);if(console.log("\n⚠️ "+n.t("install.permission_detected")+"\n"),"win32"===process.platform)try{const t=a.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(e.gray("$ ")+e.white(t)),o(t,{stdio:"inherit"}),void console.log("\n✅ "+n.t("install.permission_fixed"))}catch(o){r.warn("Retry install failed:",o),console.log("\n❌ "+n.t("install.auto_fix_failed")),console.log(e.yellow("\n📌 "+n.t("install.windows_solutions"))),console.log(""),console.log(e.cyan.bold(n.t("install.windows_solution_1_title"))),console.log(e.gray(" "+n.t("install.windows_solution_1_step1"))),console.log(e.gray(" "+n.t("install.windows_solution_1_step2"))),console.log(e.gray(" "+n.t("install.windows_solution_1_step3"))),console.log(e.white(` ${a.installCommand}`)),console.log(""),console.log(e.cyan.bold(n.t("install.windows_solution_2_title"))),console.log(e.gray(" "+n.t("install.windows_solution_2_command"))),console.log(e.white(` ${a.installCommand.replace("npm install -g","npm install -g --prefix=%APPDATA%\\npm")}`)),console.log("");const{action:s}=await l.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"===s)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(t))throw console.log(e.red("\n❌ "+n.t("install.still_not_installed",{tool:a.displayName}))),new Error(`${a.displayName} is not installed`);return void console.log(e.green("\n✅ "+n.t("install.verified_success",{tool:a.displayName})))}console.log(e.yellow("\n📌 "+n.t("install.unix_solutions"))),console.log(""),console.log(e.cyan.bold(n.t("install.unix_solution_1_title"))),console.log(e.gray(" "+n.t("install.unix_solution_1_desc"))),console.log(e.white(` sudo ${a.installCommand}`)),console.log("");const u="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally",g=s(u,u,{fallback:()=>u});console.log(e.cyan.bold(n.t("install.unix_solution_2_title"))),console.log(e.gray(" "+n.t("install.unix_solution_2_desc"))),console.log(e.blue(" 📖 "+n.t("install.npm_docs_link")+": ")+g),console.log("");const{action:m}=await l.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"===m)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(t))throw console.log(e.red("\n❌ "+n.t("install.still_not_installed",{tool:a.displayName}))),new Error(`${a.displayName} is not installed`);console.log(e.green("\n✅ "+n.t("install.verified_success",{tool:a.displayName})))}}getToolConfig(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);try{return t.getConfig()}catch(t){return r.warn(`Failed to read config for ${o}:`,t),r.logError(`ToolManager.getToolConfig(${o})`,t),null}}updateToolConfig(o,t){const n=c(o);if(!n)throw new Error(`Unknown tool: ${o}`);if(!n.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);n.updateConfig(t)}forceResetConfig(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);const n=t.forceResetPaths?t.forceResetPaths():this.backupPathsFor(t),l=[];for(const o of n)f(o)&&(g(o),w(o,{force:!0}),l.push(o));return l}loadPrivateConfig(o,t,n,l,e={}){const s=c(o);if(!s)throw new Error(`Unknown tool: ${o}`);e.force&&this.forceResetConfig(o),s.loadPrivateConfig({apiKey:t,baseUrl:a.getBaseUrl(),models:n,settings:a.getToolSettings(o),...l?{catalog:l}:{}})}async applyPrivateConfig(o,t,n,l={}){const e=c(o);if(!e)throw new Error(`Unknown tool: ${o}`);const s=e.wantsModelCatalog?await _(t):null;l.force?this.loadPrivateConfig(o,t,n,s??void 0,{force:!0}):s?this.loadPrivateConfig(o,t,n,s):this.loadPrivateConfig(o,t,n)}applyNotes(o){const t=c(o);if(!t)return[];const l=[];try{t.applyNotes&&l.push(...t.applyNotes())}catch(t){r.logError(`ToolManager.applyNotes(${o})`,t)}const e=this.outdatedVersion(o);if(e){l.push({kind:"action",text:n.t("tools.version_outdated",{current:e.current,min:e.min,upgrade:t.currentUpgradeCommand?.()||t.upgradeCommand||t.installCommand})});for(const o of t.upgradeHints?.(e.min)??[])l.push({kind:"action",text:o})}return l}printApplied(o,t=16){const n=SUPPORTED_TOOLS[o]?.displayName??o,l=this.applyNotes(o);for(const o of l.filter(o=>"detail"===o.kind))r.record(`${n}: ${o.text}`);const s=l.filter(o=>"summary"===o.kind).map(o=>o.text).join(";"),i=" ".repeat(Math.max(1,t-h(n)));console.log(e.green("√ "+n)+(s?i+s:""));for(const o of l.filter(o=>"action"===o.kind))console.log(e.yellow(" ! "+o.text))}installedVersion(o){const n=c(o);if(!n)return null;if(n.installedVersion)try{return n.installedVersion()}catch(t){return r.logError(`ToolManager.installedVersion(${o})`,t),null}if(n.isInstalled)return null;try{const o={encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},l=`${("win32"===process.platform?t(`${n.command} --version`,{...o,shell:!0}):t(n.command,["--version"],o)).stdout??""}`.match(/(\d+)\.(\d+)\.(\d+)/);return l?l[0]:null}catch{return null}}outdatedVersion(o){const t=P(o);if(!t)return null;const n=this.installedVersion(o);return n&&y(n,t)<0?{current:n,min:t}:null}unloadPrivateConfig(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);t.unloadPrivateConfig()}async clearPinnedModels(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(a.setToolModel(o,null),!t.clearPinnedModels)return[];const n=a.getApiKeyForTool(o),l=t.wantsModelCatalog&&n?await _(n):null;return t.clearPinnedModels(l??void 0)}supportsClearPinnedModels(o){return Boolean(c(o)?.clearPinnedModels)}detectCurrentConfig(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);return t.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(t).map(o=>({file:o,backups:m(o)}))}restorePrivateConfig(o){const t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(t).map(o=>({file:o,result:p(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return d().filter(o=>!o.hidden).map(o=>C(o.name))}isGitInstalled(){try{const t="win32"===process.platform?"where git":"which git";return o(t,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
1
+ import{execSync as o,spawnSync as t}from"child_process";import{i18n as n}from"./i18n.js";import e from"inquirer";import r from"chalk";import{logger as i}from"../utils/logger.js";import{configManager as l}from"./config.js";import{getAdapter as s,listAdapters as a,TOOL_ADAPTERS as c}from"./tools/registry.js";import{backupBeforeWrite as d,listBackups as p,restoreBackup as u}from"./tools/backup.js";import{existsSync as f,rmSync as m}from"fs";import{stringWidth as g}from"../utils/string-width.js";import{resolveModelCatalog as h}from"./model-catalog.js";import{compareVersions as w,getToolMinVersion as P}from"./model-registry.js";import{findInstalls as y,forgetInstalls as T,installLabel as C,npmMirrorInstallCommand as k}from"./tools/tool-installs.js";export{w as compareVersions};function $(o){const t=c[o];return{name:t.name,command:t.command,installCommand:t.installCommand,configPath:t.configPath,displayName:t.displayName,...t.hidden?{hidden:!0}:{}}}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(c).map(o=>[o,$(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(t){const n=SUPPORTED_TOOLS[t];if(!n)return!1;const e=s(t);if(e?.isInstalled)try{return e.isInstalled()}catch{return!1}try{const t="win32"===process.platform?`where ${n.command}`:`which ${n.command}`;return o(t,{stdio:"pipe"}),!0}catch{return!1}}async installTool(t){const i=SUPPORTED_TOOLS[t];if(!i)throw new Error(`Unknown tool: ${t}`);const l=s(t)?.installSpec,a=l?k(l):i.installCommand;console.log(r.gray("$ ")+r.white(a));try{return o(a,{stdio:"inherit"}),T(),void console.log(r.green("√ "+n.t("wizard.tool_installed")))}catch(o){if(console.log(r.red("× "+n.t("wizard.install_failed"))),!/EACCES|EPERM|permission denied/i.test(`${o?.message??""}${o?.stderr??""}`)&&243!==o?.status&&!a.startsWith("npm "))throw new Error(`Failed to install ${i.displayName}: ${o}`);console.log(r.yellow(" ! "+("win32"===process.platform?n.t("install.permission_windows",{command:a}):n.t("install.permission_unix",{command:a}))))}const{action:c}=await e.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"===c)throw new Error(n.t("install.user_cancelled"));if(T(),!this.isToolInstalled(t))throw console.log(r.red("× "+n.t("install.still_not_installed",{tool:i.displayName}))),new Error(`${i.displayName} is not installed`);console.log(r.green("√ "+n.t("install.verified_success",{tool:i.displayName})))}getToolConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);try{return t.getConfig()}catch(t){return i.warn(`Failed to read config for ${o}:`,t),i.logError(`ToolManager.getToolConfig(${o})`,t),null}}updateToolConfig(o,t){const n=s(o);if(!n)throw new Error(`Unknown tool: ${o}`);if(!n.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);n.updateConfig(t)}forceResetConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);const n=t.forceResetPaths?t.forceResetPaths():this.backupPathsFor(t),e=[];for(const o of n)f(o)&&(d(o),m(o,{force:!0}),e.push(o));return e}loadPrivateConfig(o,t,n,e,r={}){const i=s(o);if(!i)throw new Error(`Unknown tool: ${o}`);r.force&&this.forceResetConfig(o),i.loadPrivateConfig({apiKey:t,baseUrl:l.getBaseUrl(),models:n,settings:l.getToolSettings(o),...e?{catalog:e}:{}})}async applyPrivateConfig(o,t,n,e={}){const r=s(o);if(!r)throw new Error(`Unknown tool: ${o}`);const i=r.wantsModelCatalog?await h(t):null;e.force?this.loadPrivateConfig(o,t,n,i??void 0,{force:!0}):i?this.loadPrivateConfig(o,t,n,i):this.loadPrivateConfig(o,t,n)}applyNotes(o){const t=s(o);if(!t)return[];const e=[];try{t.applyNotes&&e.push(...t.applyNotes())}catch(t){i.logError(`ToolManager.applyNotes(${o})`,t)}const r=this.outdatedVersion(o);if(r){e.push({kind:"action",text:n.t("tools.version_outdated",{current:r.current,min:r.min,upgrade:t.installSpec?`vibecli upgrade ${o}`:t.upgradeCommand||t.installCommand})});for(const o of t.upgradeHints?.(r.min)??[])e.push({kind:"action",text:o})}const l=t.installSpec?y(t.installSpec):[];return l.length>1&&e.push({kind:"action",text:n.t("upgrade.duplicates_note",{count:String(l.length),active:C(l[0]),others:l.slice(1).map(C).join("、"),tool:o})}),e}printApplied(o,t=16){const n=SUPPORTED_TOOLS[o]?.displayName??o,e=this.applyNotes(o);for(const o of e.filter(o=>"detail"===o.kind))i.record(`${n}: ${o.text}`);const l=e.filter(o=>"summary"===o.kind).map(o=>o.text).join(";"),s=" ".repeat(Math.max(1,t-g(n)));console.log(r.green("√ "+n)+(l?s+l:""));for(const o of e.filter(o=>"action"===o.kind))console.log(r.yellow(" ! "+o.text))}installedVersion(o){const n=s(o);if(!n)return null;if(n.installSpec)return y(n.installSpec)[0]?.version??null;if(n.installedVersion)try{return n.installedVersion()}catch(t){return i.logError(`ToolManager.installedVersion(${o})`,t),null}if(n.isInstalled)return null;try{const o={encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},e=`${("win32"===process.platform?t(`${n.command} --version`,{...o,shell:!0}):t(n.command,["--version"],o)).stdout??""}`.match(/(\d+)\.(\d+)\.(\d+)/);return e?e[0]:null}catch{return null}}outdatedVersion(o){const t=P(o);if(!t)return null;const n=this.installedVersion(o);return n&&w(n,t)<0?{current:n,min:t}:null}unloadPrivateConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);t.unloadPrivateConfig()}async clearPinnedModels(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(l.setToolModel(o,null),!t.clearPinnedModels)return[];const n=l.getApiKeyForTool(o),e=t.wantsModelCatalog&&n?await h(n):null;return t.clearPinnedModels(e??void 0)}supportsClearPinnedModels(o){return Boolean(s(o)?.clearPinnedModels)}detectCurrentConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);return t.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(t).map(o=>({file:o,backups:p(o)}))}restorePrivateConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(t).map(o=>({file:o,result:u(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return a().filter(o=>!o.hidden).map(o=>$(o.name))}isGitInstalled(){try{const t="win32"===process.platform?"where git":"which git";return o(t,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
@@ -0,0 +1,9 @@
1
+ /** Tests replace the process runner. */
2
+ export declare function setUpgradeRunnerForTests(fake: (command: string, args: string[]) => boolean): void;
3
+ /**
4
+ * `vibecli upgrade <tool>`: upgrade the copy PATH runs the way it was installed
5
+ * (vendor updater / npm via npmmirror / Homebrew). When the vendor updater
6
+ * can't connect, fall back to the npm mirror, then leave a single copy on PATH.
7
+ * Returns whether the tool ends up at or above `toolMinVersions`.
8
+ */
9
+ export declare function upgradeTool(toolName: string): Promise<boolean>;
@@ -0,0 +1 @@
1
+ import{spawnSync as e}from"child_process";import{rmSync as o}from"fs";import{join as r}from"path";import t from"chalk";import n from"inquirer";import{i18n as s}from"./i18n.js";import{logger as l}from"../utils/logger.js";import{compareVersions as i,getToolMinVersion as a}from"./model-registry.js";import{getAdapter as p}from"./tools/registry.js";import{findInstalls as c,forgetInstalls as u,installLabel as m,npmMirrorInstallCommand as d,npmUninstallCommand as g}from"./tools/tool-installs.js";import{toolHome as f}from"./tools/types.js";import{isNonInteractive as h,shouldAutoConfirm as w}from"./runtime-options.js";let y=(o,r)=>{console.log(t.gray("$ ")+[o,...r].join(" "));const n="win32"!==process.platform||/\.exe$/i.test(o)?e(o,r,{stdio:"inherit"}):e([o,...r].map(e=>/\s/.test(e)?`"${e}"`:e).join(" "),{stdio:"inherit",shell:!0});return n.error&&l.logError(`upgrade: ${o}`,n.error),0===n.status};export function setUpgradeRunnerForTests(e){y=e}function v(e){const[o,...r]=d(e).split(" "),n=y(o,r);return n||"win32"===process.platform||console.log(t.yellow(" ! "+s.t("upgrade.npm_permission",{command:d(e)}))),n}function b(e,t){switch(t.kind){case"npm":y("npm",["uninstall","-g",e.npmPackage]);break;case"brew":e.brew&&y("brew",["uninstall",e.brew]);break;case"native":try{o(t.path,{force:!0});for(const t of e.native?.dataDirs??[])o(r(f(),...t),{recursive:!0,force:!0})}catch(e){l.logError("upgrade: remove native copy",e)}break;default:return!1}return u(),!c(e).some(e=>e.path===t.path)}function k(e,o){switch(o.kind){case"npm":return g(e);case"brew":return`brew uninstall ${e.brew}`;case"native":return s.t("upgrade.delete_paths",{paths:[o.path,...(e.native?.dataDirs??[]).map(e=>r(f(),...e))].join("、")});default:return s.t("upgrade.delete_paths",{paths:o.path})}}export async function upgradeTool(e){const o=p(e),r=o?.installSpec;if(!o||!r)throw new Error(s.t("upgrade.unsupported",{tool:e}));const l=o.displayName;u();const d=c(r);if(console.log(t.bold(`\n${l}`)),0===d.length)console.log(s.t("upgrade.not_installed_installing",{tool:l})),v(r);else{const e=d[0];console.log(t.gray(" "+s.t("upgrade.current",{copy:m(e)})));const o=function(e,o){switch(o.kind){case"native":return y(o.path,e.native?.updateArgs??["update"]);case"npm":return v(e);case"brew":return!!e.brew&&y("brew",["upgrade",e.brew]);default:return!1}}(r,e);o||"other"!==e.kind?o||"npm"===e.kind||(console.log(t.yellow(" ! "+s.t("upgrade.fallback_mirror"))),v(r)):console.log(t.yellow(" ! "+s.t("upgrade.unknown_install",{path:e.path})))}u(),await async function(e,o){const r=c(e);if(r.length<2)return;const l=function(e){return e.reduce((e,o)=>o.version&&(!e.version||i(o.version,e.version)>0)?o:e)}(r),a=r.filter(e=>e!==l);console.log(t.yellow(" ! "+s.t("upgrade.duplicates",{tool:o,keep:m(l),others:a.map(m).join("、")})));let p=w();p||h()||!process.stdin.isTTY||(p=(await n.prompt([{type:"confirm",name:"remove",message:s.t("upgrade.confirm_remove"),default:!0}])).remove);for(const o of a)p&&b(e,o)?console.log(t.green(" √ "+s.t("upgrade.removed",{copy:m(o)}))):console.log(t.yellow(" ! "+s.t("upgrade.remove_by_hand",{how:k(e,o)})))}(r,l),u();const g=c(r)[0];if(!g)return console.log(t.red(" × "+s.t("upgrade.still_missing",{tool:l}))),!1;const f=a(e);if(f&&g.version&&i(g.version,f)<0){console.log(t.yellow(" ! "+s.t("upgrade.still_old",{copy:m(g),min:f})));for(const e of o.upgradeHints?.(f)??[])console.log(t.yellow(" ! "+e));return!1}return console.log(t.green(" √ "+s.t("upgrade.done",{copy:m(g)}))),!0}
@@ -1 +1 @@
1
- import{join as e}from"path";import{claudeCodeManager as t}from"../claude-code-manager.js";import{backupBeforeWrite as o}from"./backup.js";import{i18n as n}from"../i18n.js";import{gatewayModelSpecs as a,prettyModelName as i,vendorNames as l}from"../gateway-models.js";import{compareVersions as s}from"../model-registry.js";import{toolHome as d}from"./types.js";import{CLAUDE_NPM_MIRROR_INSTALL as r,CLAUDE_NPM_UNINSTALL as c,claudeUpdateChannel as m,findClaudeInstalls as u}from"./claude-installs.js";const p=e(d(),".claude","settings.json"),g=e(d(),".claude.json");let f;function C(e){return`${n.t(`tools.claude_code.install_kind.${e.kind}`)} ${e.version??"?"} (${e.path})`}export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",upgradeCommand:"claude update",installedVersion:()=>u()[0]?.version??null,currentUpgradeCommand:()=>"npm"===u()[0]?.kind?r:"claude update",upgradeHints(e){const t=[];return"npm"!==u()[0]?.kind&&t.push(n.t("tools.claude_code.upgrade_fallback",{mirror:r})),"stable"===m()&&t.push(n.t("tools.claude_code.stable_channel",{min:e})),t},configPath:p,wantsModelCatalog:!0,loadPrivateConfig(e){o(p),o(g),f=function(e){if(!e)return;const t=new Set(e.models);return a("claude-code",e.clientModels).filter(e=>t.has(e.id))}(e.catalog),t.loadPrivateConfig(e.apiKey,e.models,e.catalog?.models,f)},unloadPrivateConfig(){o(p),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=>(o(p),t.clearPinnedModels(e?.models)),applyNotes(){const e=[],o=u();if(o.length>1){e.push({kind:"action",text:n.t("tools.claude_code.multiple_installs",{count:String(o.length),active:C(o[0]),others:o.slice(1).map(C).join("、")})});const t=o.find(e=>"native"===e.kind),a=o.find(e=>"npm"===e.kind);if(t&&a){const o=!!a.version&&s(a.version,t.version??"0")>0;e.push({kind:"action",text:o?n.t("tools.claude_code.keep_npm",{mirror:r}):n.t("tools.claude_code.keep_native",{uninstall:c})})}}f&&f.length>0&&(e.push({kind:"summary",text:n.t("tools.claude_code.gateway_models",{count:String(f.length),vendors:l(f.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:f.map(e=>e.name||i(e.id)).join(", ")}));for(const o of t.getLastTierFallbacks())e.push({kind:"detail",text:n.t("tools.claude_code.tier_fallback",{tier:o.tier,from:o.from,to:o.to})});return e},backupPaths:()=>[p,g],forceResetPaths:()=>[p]};
1
+ import{join as e}from"path";import{claudeCodeManager as t}from"../claude-code-manager.js";import{backupBeforeWrite as o}from"./backup.js";import{i18n as a}from"../i18n.js";import{gatewayModelSpecs as n,prettyModelName as s,vendorNames as d}from"../gateway-models.js";import{toolHome as i}from"./types.js";import{spawnSync as c}from"child_process";import{readFileSync as l}from"fs";import{detectManagedOverrides as r}from"./claude-managed.js";const u=e(i(),".claude","settings.json"),m=e(i(),".claude.json");let p;export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",upgradeCommand:"claude update",installSpec:{command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]]},brew:"claude-code"},upgradeHints:t=>"stable"===function(){try{const t=JSON.parse(l(e(i(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}()?[a.t("tools.claude_code.stable_channel",{min:t})]:[],configPath:u,wantsModelCatalog:!0,loadPrivateConfig(e){o(u),o(m),p=function(e){if(!e)return;const t=new Set(e.models);return n("claude-code",e.clientModels).filter(e=>t.has(e.id))}(e.catalog),t.loadPrivateConfig(e.apiKey,e.models,e.catalog?.models,p)},unloadPrivateConfig(){o(u),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=>(o(u),t.clearPinnedModels(e?.models)),applyNotes(){const e=[];p&&p.length>0&&(e.push({kind:"summary",text:a.t("tools.claude_code.gateway_models",{count:String(p.length),vendors:d(p.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:p.map(e=>e.name||s(e.id)).join(", ")})),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===c("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:a.t("tools.claude_code.no_git_bash")});for(const t of r())e.push({kind:"action",text:a.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const o of t.getLastTierFallbacks())e.push({kind:"detail",text:a.t("tools.claude_code.tier_fallback",{tier:o.tier,from:o.from,to:o.to})});return e},backupPaths:()=>[u,m],forceResetPaths:()=>[u]};
@@ -1 +1 @@
1
- import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{baseModelId as y,isClaudeModel as k}from"../model-catalog.js";const b=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const C=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"],_=/^[a-f0-9-]{36}$/;function A(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=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return C.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const w=()=>a(claudeDesktopUserDataDir(),"configLibrary"),D=()=>a(w(),"_meta.json"),E=e=>a(w(),`${e}.json`),M=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function x(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return A(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 s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function P(){const e=x(D());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>A(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function S(e){return e?.entries.find(e=>e.name===b&&_.test(e.id))}const O={opus:0,fable:1,sonnet:2,haiku:3};function j(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=j(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function I(e){return j(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(k)){i=[...new Set(t.models.filter(k).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=>O[I(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=j(e),s=j(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=I(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=I(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!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 L=null,T=null,$=null;function G(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:!1}}const N={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function U(e){return Object.fromEntries(Object.entries(N).filter(([t])=>!e(t)))}const F="HKCU\\SOFTWARE\\Policies\\Claude",R="HKLM\\SOFTWARE\\Policies\\Claude",K="vibecli-claude-policy.reg",H=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let B=H;export function setRegRunnerForTests(e){B=e??H}function V(){return"win32"===process.platform}function W(e){let t;try{t=B(["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 z(){if(null===W(F))return;const e=a(claudeDesktopUserDataDir(),K);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),B(["export",F,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}const J=[...Object.keys(G({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(N)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().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 W(R)?R:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const Z=/\d+\.\d+\.\d+/;function X(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const q="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",Q="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",Y="darwin"===process.platform?`open ${Q}`:"win32"===process.platform?`start ${Q}`:`xdg-open ${Q}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:q,installCommand:Y,configPath:D(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...C.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?Z.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return X(t)}const o=C.map(e=>`'${e}'`).join(",");return X(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>Z.exec(e)?.[0]??[]))}return null},wantsModelCatalog:!0,loadPrivateConfig(e){const t=P()??{appliedId:"",entries:[]};let o=S(t);o||(o={id:l(),name:b},t.entries.push(o));const n=G(e);T=!0===n.disableDeploymentModeChooser,L={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=x(E(o.id))??{};v(E(o.id),{...U(e=>e in r),...r,...n}),t.appliedId=o.id,v(D(),t);const s=x(M())??{};if("3p"!==s.deploymentMode&&v(M(),{...s,deploymentMode:"3p"}),$=null,V())try{const e=W(F);!function(e){z();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];B(["add",F,"/v",t,"/t",e,"/d",n,"/f"])}}({...U(t=>e?.has(t)??!1),...n})}catch(e){p.logError("claude-desktop.writePolicy",e),$=e instanceof Error?e.message.trim():String(e)}},unloadPrivateConfig(){if(V())try{!function(e){const t=W(F);if(!t)return;z();let o=t.size;for(const n of e)t.has(n)&&(B(["delete",F,"/v",n,"/f"]),o-=1);o<=0&&B(["delete",F,"/f"])}(J)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=P(),t=S(e);if(!e||!t)return;m(E(t.id)),s(E(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(D()),s(D(),{force:!0})):v(D(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(V()){const e=W(F)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=S(P());if(!e)return{apiKey:null};const t=x(E(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=P(),t=S(e);return{meta:e,applied:t?x(E(t.id)):null,desktop:x(M()),...V()?{policy:Object.fromEntries(W(F)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;V()&&W(F)?.has("inferenceModels")&&(z(),B(["delete",F,"/v","inferenceModels","/f"]),e=!0);const t=S(P()),o=t?x(E(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;v(E(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(L&&L.models.length>0){const{models:t,source:o,groups:n}=L;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==T&&e.push({kind:"detail",text:u.t(T?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),$?(e.push({kind:"action",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:$})):V()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:F})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[D(),M()];try{const t=S(P());t&&e.push(E(t.id))}catch{}return e},backupPaths(){const e=[D(),M()];V()&&e.push(a(claudeDesktopUserDataDir(),K));try{const t=S(P());t&&e.push(E(t.id))}catch{}return e}};
1
+ import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{canRestartDesktop as y,isDesktopRunning as k,restartDesktop as b,WINDOWS_PACKAGE_FAMILIES as C}from"./desktop-process.js";import{baseModelId as A,isClaudeModel as _}from"../model-catalog.js";const w=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const D=/^[a-f0-9-]{36}$/;function E(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=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return C.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const M=()=>a(claudeDesktopUserDataDir(),"configLibrary"),v=()=>a(M(),"_meta.json"),x=e=>a(M(),`${e}.json`),P=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function S(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return E(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 O(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function I(){const e=S(v());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>E(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function L(e){return e?.entries.find(e=>e.name===w&&D.test(e.id))}const j={opus:0,fable:1,sonnet:2,haiku:3};function T(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=T(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function $(e){return T(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>A(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(_)){i=[...new Set(t.models.filter(_).map(A))];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=A(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>j[$(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=T(e),s=T(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=$(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=$(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!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 G=null,N=null,U=null;function R(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:!1}}const F={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function K(e){return Object.fromEntries(Object.entries(F).filter(([t])=>!e(t)))}const H="HKCU\\SOFTWARE\\Policies\\Claude",B="HKLM\\SOFTWARE\\Policies\\Claude",V="vibecli-claude-policy.reg",W=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let J=W;export function setRegRunnerForTests(e){J=e??W}function z(){return"win32"===process.platform}function Z(e){let t;try{t=J(["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 X(){if(null===Z(H))return;const e=a(claudeDesktopUserDataDir(),V);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),J(["export",H,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}const q=[...Object.keys(R({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(F)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().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 Z(B)?B:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const Q=/\d+\.\d+\.\d+/;function Y(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const ee="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",te="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",oe="darwin"===process.platform?`open ${te}`:"win32"===process.platform?`start ${te}`:`xdg-open ${te}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:ee,installCommand:oe,configPath:v(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...C.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?Q.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return Y(t)}const o=C.map(e=>`'${e}'`).join(",");return Y(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>Q.exec(e)?.[0]??[]))}return null},runningApp:y()?{isRunning:k,restart:b}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const t=I()??{appliedId:"",entries:[]};let o=L(t);o||(o={id:l(),name:w},t.entries.push(o));const n=R(e);N=!0===n.disableDeploymentModeChooser,G={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=S(x(o.id))??{};O(x(o.id),{...K(e=>e in r),...r,...n}),t.appliedId=o.id,O(v(),t);const s=S(P())??{};if("3p"!==s.deploymentMode&&O(P(),{...s,deploymentMode:"3p"}),U=null,z())try{const e=Z(H);!function(e){X();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];J(["add",H,"/v",t,"/t",e,"/d",n,"/f"])}}({...K(t=>e?.has(t)??!1),...n})}catch(e){p.logError("claude-desktop.writePolicy",e),U=e instanceof Error?e.message.trim():String(e)}},unloadPrivateConfig(){if(z())try{!function(e){const t=Z(H);if(!t)return;X();let o=t.size;for(const n of e)t.has(n)&&(J(["delete",H,"/v",n,"/f"]),o-=1);o<=0&&J(["delete",H,"/f"])}(q)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=I(),t=L(e);if(!e||!t)return;m(x(t.id)),s(x(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(v()),s(v(),{force:!0})):O(v(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(z()){const e=Z(H)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=L(I());if(!e)return{apiKey:null};const t=S(x(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=I(),t=L(e);return{meta:e,applied:t?S(x(t.id)):null,desktop:S(P()),...z()?{policy:Object.fromEntries(Z(H)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;z()&&Z(H)?.has("inferenceModels")&&(X(),J(["delete",H,"/v","inferenceModels","/f"]),e=!0);const t=L(I()),o=t?S(x(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;O(x(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(G&&G.models.length>0){const{models:t,source:o,groups:n}=G;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==N&&e.push({kind:"detail",text:u.t(N?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),y()&&!k()||e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),U?(e.push({kind:"action",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:U})):z()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:H})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[v(),P()];try{const t=L(I());t&&e.push(x(t.id))}catch{}return e},backupPaths(){const e=[v(),P()];z()&&e.push(a(claudeDesktopUserDataDir(),V));try{const t=L(I());t&&e.push(x(t.id))}catch{}return e}};
@@ -0,0 +1,8 @@
1
+ export interface ManagedOverride {
2
+ source: string;
3
+ keys: string[];
4
+ }
5
+ /** Tests point the probes at fixtures. */
6
+ export declare function setManagedSourcesForTests(files: () => string[], policy: (key: string) => string | null): void;
7
+ /** Managed sources that override something vibecli configures. */
8
+ export declare function detectManagedOverrides(): ManagedOverride[];
@@ -0,0 +1 @@
1
+ import{execFileSync as e}from"child_process";import{existsSync as o,readFileSync as t}from"fs";import{join as r}from"path";import{logger as s}from"../../utils/logger.js";import{MODEL_ENV_VARS as n}from"../claude-code-manager.js";const i=["apiKeyHelper","forceLoginMethod","model","availableModels"],c=["ANTHROPIC_BASE_URL","ANTHROPIC_AUTH_TOKEN","ANTHROPIC_API_KEY",...n],a=["HKLM\\SOFTWARE\\Policies\\ClaudeCode","HKCU\\SOFTWARE\\Policies\\ClaudeCode"];let d=()=>"darwin"===process.platform?["/Library/Application Support/ClaudeCode/managed-settings.json"]:"win32"===process.platform?[r(process.env.ProgramFiles||"C:\\Program Files","ClaudeCode","managed-settings.json")]:["/etc/claude-code/managed-settings.json"],l=o=>{try{const t=e("reg.exe",["query",o,"/v","Settings"],{encoding:"utf-8",windowsHide:!0,timeout:1e4,stdio:["ignore","pipe","ignore"]});return/Settings\s+REG_\w+\s+(.*)/.exec(t)?.[1]?.trim()??null}catch{return null}};export function setManagedSourcesForTests(e,o){d=e,l=o}function p(e){let o;try{o=JSON.parse(e)}catch{return[]}const t=i.filter(e=>void 0!==o[e]),r=o.env;return r&&"object"==typeof r&&t.push(...c.filter(e=>void 0!==r[e]).map(e=>`env.${e}`)),t}export function detectManagedOverrides(){const e=[];try{for(const r of d()){if(!o(r))continue;const s=p(t(r,"utf-8"));s.length>0&&e.push({source:r,keys:s})}if("win32"===process.platform)for(const o of a){const t=l(o),r=t?p(t):[];r.length>0&&e.push({source:o,keys:r})}}catch(e){s.logError("claude-managed.detectManagedOverrides",e)}return e}
@@ -1 +1 @@
1
- import{existsSync as o,readFileSync as t}from"fs";import{join as e}from"path";import*as n from"@iarna/toml";import{logger as r}from"../../utils/logger.js";import{i18n as i}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as p,codexCatalogPath as u,loadBundledCodexModels as f,loadPreviousCodexModels as g,previousGatewayModelIds as _}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>e(d(),".codex"),k=()=>e(w(),"config.toml"),C=()=>e(w(),"auth.json"),h="OPENAI_API_KEY",j="custom",E=1e6,b=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function v(o){return!!o&&"object"==typeof o&&!Array.isArray(o)}function O(o,t){return v(o[t])||(o[t]={}),o[t]}function M(){if(!o(k()))return{};const e=t(k(),"utf-8");if(!e.trim())return{};try{const o=n.parse(e);if(!v(o))throw new Error("config.toml root must be a TOML table");return o}catch(o){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${o instanceof Error?o.message:String(o)}`)}}function N(){if(!o(C()))return{};try{const o=JSON.parse(t(C(),"utf-8"));return v(o)?o:{}}catch{return{}}}let S=null;function A(e,n,i){const s=!1!==n.settings?.one_million_context;!function(o){o.model_context_window===E&&delete o.model_context_window,o.model_auto_compact_token_limit===b&&delete o.model_auto_compact_token_limit}(e);const d=f()??g();if(!d)return r.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",e.model_catalog_json===u()&&delete e.model_catalog_json,void(s&&(e.model_context_window=E,e.model_auto_compact_token_limit=b));const c=n.catalog,m=p({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??_(),oneMillion:s,selectedModel:i,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(o=>"list"===o.visibility).map(o=>({id:String(o.slug),name:String(o.display_name??o.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=u();o(w)&&t(w,"utf-8")===y||(l(w),a(w,y)),e.model_catalog_json=w}export function setCodexOneMillionContext(o,t){const e=M(),r=n.stringify(e),i="string"==typeof e.model?e.model:m("codex");A(e,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:o},catalog:t},i);const s=n.stringify(e);s!==r&&(l(k()),a(k(),s))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",configPath:k(),wantsModelCatalog:!0,applyNotes(){if("skipped"===S)return[{kind:"action",text:i.t("tools.codex.catalog_skipped")}];if(!S)return[];const o=new Map;for(const t of S.listed)o.set(y(t.id),(o.get(y(t.id))??0)+1);const t=["gpt","claude","other","grok"].filter(t=>o.has(t)).map(t=>`${i.t(`tools.model_family.${t}`)} ${o.get(t)}`).join(" · ");return[{kind:"summary",text:i.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:t})},{kind:"detail",text:S.listed.map(o=>o.name).join(", ")}]},loadPrivateConfig(o){const t=m("codex"),e=s(o.models,t),r=function(o){return/(?:^|\/)claude(?:-|$)/i.test(o)}(e)?t:e,i=c(o.baseUrl),d=M();d.model=r,d.model_provider=j,d.model_reasoning_effort="xhigh",A(d,o,r);const p=O(d,"model_providers"),u=v(p[j])?{...p[j]}:{};delete u.env_key,p[j]={...u,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:i};const f=O(d,"profiles"),g=v(f[r])?{...f[r]}:{};f[r]={...g,model_provider:j,model:r},O(O(d,"tui"),"model_availability_nux")[r]=4;const _=O(d,"features");_.goals=!0,_.js_repl=!1,l(k()),a(k(),n.stringify(d)),function(o){const t=o.trim();if(!t)return;const e={...N(),auth_mode:"apikey",[h]:t};l(C()),a(C(),JSON.stringify(e,null,2)+"\n")}(o.apiKey)},unloadPrivateConfig(){if(!o(k()))return;let t;try{t=M()}catch(o){return void r.logError("codexAdapter.unloadPrivateConfig",o)}const e=t.model_providers;v(e)&&(delete e[j],0===Object.keys(e).length&&delete t.model_providers),t.model_provider===j&&delete t.model_provider,t.model_catalog_json===u()&&delete t.model_catalog_json,l(k()),a(k(),n.stringify(t))},detectCurrentConfig(){const o=N()[h];return{apiKey:"string"==typeof o&&o.trim()?o.trim():null}},getConfig(){try{return M()}catch(o){return r.logError("codexAdapter.getConfig",o),null}},backupPaths:()=>[k(),C(),u()]};
1
+ import{existsSync as o,readFileSync as e}from"fs";import{join as t}from"path";import*as n from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as r}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as d,toolHome as s,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as p,codexCatalogPath as u,loadBundledCodexModels as g,loadPreviousCodexModels as f,previousGatewayModelIds as _}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(s(),".codex"),k=()=>t(w(),"config.toml"),C=()=>t(w(),"auth.json"),h="OPENAI_API_KEY",j="custom",E=1e6,b=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function v(o){return!!o&&"object"==typeof o&&!Array.isArray(o)}function O(o,e){return v(o[e])||(o[e]={}),o[e]}function M(){if(!o(k()))return{};const t=e(k(),"utf-8");if(!t.trim())return{};try{const o=n.parse(t);if(!v(o))throw new Error("config.toml root must be a TOML table");return o}catch(o){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${o instanceof Error?o.message:String(o)}`)}}function N(){if(!o(C()))return{};try{const o=JSON.parse(e(C(),"utf-8"));return v(o)?o:{}}catch{return{}}}let S=null;function A(t,n,r){const d=!1!==n.settings?.one_million_context;!function(o){o.model_context_window===E&&delete o.model_context_window,o.model_auto_compact_token_limit===b&&delete o.model_auto_compact_token_limit}(t);const s=g()??f();if(!s)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===u()&&delete t.model_catalog_json,void(d&&(t.model_context_window=E,t.model_auto_compact_token_limit=b));const c=n.catalog,m=p({bundled:s,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??_(),oneMillion:d,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(o=>"list"===o.visibility).map(o=>({id:String(o.slug),name:String(o.display_name??o.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=u();o(w)&&e(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(o,e){const t=M(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:o},catalog:e},r);const d=n.stringify(t);d!==i&&(l(k()),a(k(),d))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:k(),wantsModelCatalog:!0,applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const o=new Map;for(const e of S.listed)o.set(y(e.id),(o.get(y(e.id))??0)+1);const e=["gpt","claude","other","grok"].filter(e=>o.has(e)).map(e=>`${r.t(`tools.model_family.${e}`)} ${o.get(e)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:e})},{kind:"detail",text:S.listed.map(o=>o.name).join(", ")}]},loadPrivateConfig(o){const e=m("codex"),t=d(o.models,e),i=function(o){return/(?:^|\/)claude(?:-|$)/i.test(o)}(t)?e:t,r=c(o.baseUrl),s=M();s.model=i,s.model_provider=j,s.model_reasoning_effort="xhigh",A(s,o,i);const p=O(s,"model_providers"),u=v(p[j])?{...p[j]}:{};delete u.env_key,p[j]={...u,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(s,"profiles"),f=v(g[i])?{...g[i]}:{};g[i]={...f,model_provider:j,model:i},O(O(s,"tui"),"model_availability_nux")[i]=4;const _=O(s,"features");_.goals=!0,_.js_repl=!1,l(k()),a(k(),n.stringify(s)),function(o){const e=o.trim();if(!e)return;const t={...N(),auth_mode:"apikey",[h]:e};l(C()),a(C(),JSON.stringify(t,null,2)+"\n")}(o.apiKey)},unloadPrivateConfig(){if(!o(k()))return;let e;try{e=M()}catch(o){return void i.logError("codexAdapter.unloadPrivateConfig",o)}const t=e.model_providers;v(t)&&(delete t[j],0===Object.keys(t).length&&delete e.model_providers),e.model_provider===j&&delete e.model_provider,e.model_catalog_json===u()&&delete e.model_catalog_json,l(k()),a(k(),n.stringify(e))},detectCurrentConfig(){const o=N()[h];return{apiKey:"string"==typeof o&&o.trim()?o.trim():null}},getConfig(){try{return M()}catch(o){return i.logError("codexAdapter.getConfig",o),null}},backupPaths:()=>[k(),C(),u()]};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Whether Claude Desktop is running, and quitting + relaunching it so a new
3
+ * config library is read (the app only reads it at start, and closing the
4
+ * window leaves it running in the tray / menu bar).
5
+ *
6
+ * Windows: Claude Code's native build is also `claude.exe`, so processes are
7
+ * matched by install path (Squirrel `AnthropicClaude`, `Programs\Claude`, MSIX
8
+ * `WindowsApps`). The MSIX build can't be started from its exe; it is launched
9
+ * through its AppsFolder id read from the package manifest.
10
+ */
11
+ /** MSIX package family names of the Windows Store / MSIX builds. */
12
+ export declare const WINDOWS_PACKAGE_FAMILIES: string[];
13
+ declare let run: (command: string, args: string[], timeout?: number) => {
14
+ status: number | null;
15
+ stdout: string;
16
+ };
17
+ /** Tests replace the process runner. */
18
+ export declare function setDesktopProcessRunnerForTests(fake: typeof run): void;
19
+ export declare function isDesktopRunning(): boolean;
20
+ /** Whether this platform supports `restartDesktop()`. */
21
+ export declare function canRestartDesktop(): boolean;
22
+ /** Quit (gracefully, then forcibly) and start again; true when it is running afterwards. */
23
+ export declare function restartDesktop(): boolean;
24
+ export {};
@@ -0,0 +1 @@
1
+ import{spawnSync as e}from"child_process";import{logger as t}from"../../utils/logger.js";export const WINDOWS_PACKAGE_FAMILIES=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"];const r="Get-Process -Name claude -ErrorAction SilentlyContinue | Where-Object { $_.Path -and ($_.Path -like '*\\AnthropicClaude\\*' -or $_.Path -like '*\\Programs\\Claude\\*' -or $_.Path -like '*\\WindowsApps\\*Claude*') }";let o=(r,o,s=3e4)=>{const n=e(r,o,{encoding:"utf-8",timeout:s,windowsHide:!0,stdio:["ignore","pipe","pipe"]});return n.error&&t.logError(`desktop-process: ${r}`,n.error),{status:n.status,stdout:`${n.stdout??""}`}};export function setDesktopProcessRunnerForTests(e){o=e}function s(e,t){return o("powershell.exe",["-NoProfile","-NonInteractive","-Command",e],t)}function n(e){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}export function isDesktopRunning(){return"darwin"===process.platform?0===o("pgrep",["-x","Claude"]).status:"win32"===process.platform&&Number(s(`@(${r}).Count`).stdout.trim())>0}export function canRestartDesktop(){return"darwin"===process.platform||"win32"===process.platform}export function restartDesktop(){if("darwin"===process.platform){o("osascript",["-e",'quit app "Claude"']);for(let e=0;e<30&&isDesktopRunning();e++)n(500);isDesktopRunning()&&o("pkill",["-x","Claude"]),n(1e3),o("open",["-a","Claude"])}else{if("win32"!==process.platform)return!1;s([`$p = @(${r})`,"$p | ForEach-Object { [void]$_.CloseMainWindow() }","Start-Sleep -Seconds 3","$p | Where-Object { -not $_.HasExited } | Stop-Process -Force -ErrorAction SilentlyContinue","Start-Sleep -Seconds 1","$squirrel = Join-Path $env:LOCALAPPDATA 'AnthropicClaude\\claude.exe'","$programs = Join-Path $env:LOCALAPPDATA 'Programs\\Claude\\Claude.exe'","if (Test-Path $squirrel) { Start-Process $squirrel }","elseif (Test-Path $programs) { Start-Process $programs }",`else { $pkg = Get-AppxPackage | Where-Object { @(${WINDOWS_PACKAGE_FAMILIES.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | Select-Object -First 1`," if ($pkg) { $id = (Get-AppxPackageManifest $pkg).Package.Applications.Application.Id | Select-Object -First 1",' Start-Process "shell:AppsFolder\\$($pkg.PackageFamilyName)!$id" } }'].join("\n"),6e4)}for(let e=0;e<20;e++){if(isDesktopRunning())return!0;n(500)}return!1}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * How a CLI tool can be installed, so vibecli can tell the copies apart and
3
+ * upgrade each the way it was installed. Users often end up with two copies
4
+ * (Claude Code's official native install plus an npm global one, Codex from npm
5
+ * plus Homebrew); only the first on PATH runs, and an upgrade only touches the
6
+ * copy it is run against.
7
+ */
8
+ export interface ToolInstallSpec {
9
+ /** Executable name without extension (`claude`). */
10
+ command: string;
11
+ /** npm package of the global install (`@anthropic-ai/claude-code`). */
12
+ npmPackage: string;
13
+ /** The vendor's self-updating install, when there is one. */
14
+ native?: {
15
+ /** Directory under the home directory holding the launcher (`.local/bin`). */
16
+ binDir: string[];
17
+ /** Arguments that make the launcher update itself (`update`). */
18
+ updateArgs: string[];
19
+ /** Directories under home that belong to it, removed together with the launcher. */
20
+ dataDirs: string[][];
21
+ };
22
+ /** Homebrew formula / cask name. */
23
+ brew?: string;
24
+ }
25
+ export type InstallKind = 'native' | 'npm' | 'brew' | 'other';
26
+ export interface ToolInstall {
27
+ path: string;
28
+ kind: InstallKind;
29
+ version: string | null;
30
+ }
31
+ /** npmmirror carries the same packages (including the per-platform native binaries) and is reachable from mainland China. */
32
+ export declare const NPM_MIRROR = "https://registry.npmmirror.com";
33
+ export declare function npmMirrorInstallCommand(spec: ToolInstallSpec): string;
34
+ export declare function npmUninstallCommand(spec: ToolInstallSpec): string;
35
+ /** `原生安装 2.1.295 (C:\\Users\\me\\.local\\bin\\claude.exe)` */
36
+ export declare function installLabel(install: ToolInstall): string;
37
+ /** Tests swap in a fake `<path> --version`. */
38
+ export declare function setVersionRunnerForTests(fake: (path: string) => string): void;
39
+ /** Drop cached probes (after an install, upgrade or removal changed what is on disk). */
40
+ export declare function forgetInstalls(): void;
41
+ /** In PATH order (the first one is what the command starts), one entry per real file. Probed once per run. */
42
+ export declare function findInstalls(spec: ToolInstallSpec): ToolInstall[];
@@ -0,0 +1 @@
1
+ import{spawnSync as t}from"child_process";import{existsSync as n,realpathSync as r,statSync as o}from"fs";import{delimiter as e,dirname as s,isAbsolute as i,join as c}from"path";import{i18n as a}from"../i18n.js";import{toolHome as m}from"./types.js";export const NPM_MIRROR="https://registry.npmmirror.com";export function npmMirrorInstallCommand(t){return`npm install -g ${t.npmPackage}@latest --registry=${NPM_MIRROR}`}export function npmUninstallCommand(t){return`npm uninstall -g ${t.npmPackage}`}export function installLabel(t){return`${a.t(`upgrade.kind.${t.kind}`)} ${t.version??"?"} (${t.path})`}let p=n=>{const r={encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]};return`${(/\.cmd$/i.test(n)?t(`"${n}" --version`,{...r,shell:!0}):t(n,["--version"],r)).stdout??""}`};const l=new Map;export function setVersionRunnerForTests(t){p=t,l.clear()}export function forgetInstalls(){l.clear()}function u(t){try{return o(t).isFile()}catch{return!1}}function f(t,o){if(t.native&&(e=s(o),i=c(m(),...t.native.binDir),"win32"===process.platform?e.toLowerCase()===i.toLowerCase():e===i))return"native";var e,i;const a=c("node_modules",...t.npmPackage.split("/"));if(n(c(s(o),a)))return"npm";let p=o;try{p=r(o)}catch{return"other"}return p.includes(a)?"npm":/[\\/](Cellar|Caskroom)[\\/]/.test(p)?"brew":"other"}export function findInstalls(t){const n=l.get(t.command);if(n)return n;const o="win32"===process.platform?[`${t.command}.exe`,`${t.command}.cmd`]:[t.command],s=new Set,a=[];for(const n of(process.env.PATH??"").split(e))if(n&&i(n))for(const e of o){const o=c(n,e);if(!u(o))continue;let i=o;try{i=r(o)}catch{}const m="win32"===process.platform?i.toLowerCase():i;if(s.has(m))continue;s.add(m);const l=p(o).match(/(\d+)\.(\d+)\.(\d+)/);a.push({path:o,kind:f(t,o),version:l?l[0]:null})}return l.set(t.command,a),a}
@@ -1,5 +1,6 @@
1
1
  import type { VibecliModels, VibecliToolSettings } from '../config.js';
2
2
  import type { ModelCatalog } from '../model-catalog.js';
3
+ import type { ToolInstallSpec } from './tool-installs.js';
3
4
  /**
4
5
  * Home directory the tool adapters write under. Honors `VIBECLI_TEST_HOME` (the
5
6
  * same override the config layer uses) so tests can redirect ~/.codex, ~/.hermes,
@@ -49,9 +50,21 @@ export interface ToolAdapter {
49
50
  installCommand: string;
50
51
  /** Shell command that upgrades an installed copy; defaults to `installCommand`. */
51
52
  upgradeCommand?: string;
52
- /** Upgrade command for the copy that actually runs, when it depends on how it was installed; else `upgradeCommand`. */
53
- currentUpgradeCommand?(): string;
54
- /** Extra lines shown under the "too old" note (fallback when the upgrade can't connect, channel caveats). */
53
+ /**
54
+ * How the CLI can be installed (npm / vendor native / Homebrew). Set for tools
55
+ * `vibecli upgrade` handles: versions are read per copy on PATH, duplicate copies
56
+ * are reported, and the upgrade runs the way the active copy was installed.
57
+ */
58
+ installSpec?: ToolInstallSpec;
59
+ /**
60
+ * GUI apps that only read their config at start: setup and the wizard offer
61
+ * to restart a running app after writing it.
62
+ */
63
+ runningApp?: {
64
+ isRunning(): boolean;
65
+ restart(): boolean;
66
+ };
67
+ /** Extra lines shown under the "too old" note (e.g. a release channel that hasn't reached it yet). */
55
68
  upgradeHints?(min: string): string[];
56
69
  /**
57
70
  * Installed version as `x.y.z`, for tools without a `--version` CLI (GUI
@@ -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{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.stop(),l.printApplied(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})));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();
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{postApplyPrompts as d}from"./post-apply.js";import{i18n as p,detectSystemLang as m}from"./i18n.js";import{createBorderLine as u,createContentLine as g}from"../utils/string-width.js";import{validateApiKey as w,fetchModels as _}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as f}from"./tools/codex.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as y}from"./tools/claude-desktop.js";import{readFileSync as h}from"fs";import{dirname as z,join as v}from"path";import{fileURLToPath as k}from"url";const x=z(k(import.meta.url)),C=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(C.bold("\n"+u("╔","╗","═",this.BOX_WIDTH))),console.log(C.bold(g(e,"║","║",this.BOX_WIDTH,"center"))),console.log(C.bold(u("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(p.t("wizard.hint_navigate")),t.gray(p.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=p.t("wizard.banner_subtitle"),o=g(t,"║","║",e,"center"),a=g("","║","║",e,"center"),i=g(`API Helper v${function(){try{return JSON.parse(h(v(x,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[" ▄▀▄ █▀▄ █ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," █▀█ █▀ █ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(t=>g(t,"║","║",e,"center")),r=[u("╔","╗","═",e),a,...n,a,i,o,u("╚","╝","═",e)];console.log(C.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=p.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(o+=" "+p.t("wizard.goodbye_claude_hint"))}else o=p.t("wizard.goodbye_message");console.log(t.green("\n👋 "+o))}async runFirstTimeSetup(){const e="zh_CN"===m();e&&(a.setLang("zh_CN"),p.setLocale("zh_CN")),this.resetScreen(),console.log(C.bold("\n"+p.t("wizard.welcome"))),console.log(t.gray(p.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(p.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(p.t("wizard.select_language"));const o=p.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+p.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===o?t.green(" ✓ ("+p.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===o?t.green(" ✓ ("+p.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"back"},{name:"x "+p.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 p.setLocale(i)}}buildPresetChoices(){const o=n(),a=o.filter(e=>e.recommended),i=o.filter(e=>!e.recommended),r=e=>({name:(p.t(e.name)!==e.name?p.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")("── "+p.t("wizard.recommended")+" ──"))),s.push(...a.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+p.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:p.t("wizard.custom_model_mode"),choices:[{name:p.t("wizard.custom_model_single"),value:"single"},{name:p.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:p.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:p.t("wizard.model_haiku"),default:o.haiku},{type:"input",name:"sonnet",message:p.t("wizard.model_sonnet"),default:o.sonnet},{type:"input",name:"opus",message:p.t("wizard.model_opus"),default:o.opus},{type:"input",name:"reasoning",message:p.t("wizard.model_reasoning"),default:o.reasoning}]);a.setModels(t)}console.log(t.green("\n✓ "+p.t("wizard.models_saved")))}async configModels(o){const i=a.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(p.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+p.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+p.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+p.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:p.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✓ "+p.t("wizard.models_saved"))),void(o?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:p.t("wizard.load_config_now"),choices:[{name:p.t("wizard.load_config_yes"),value:!0},{name:p.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configApiKey(){for(;;){this.resetScreen(),this.createBox(p.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+p.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+p.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:p.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:p.t("wizard.select_action"),choices:[{name:p.t("wizard.retry_input"),value:"retry"},{name:"<- "+p.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=o(p.t("wizard.validating_api_key")).start(),r=await w(i.trim());if(n.stop(),r.valid)return a.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+p.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+p.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+p.t("wizard.api_key_network_error")));const{action:o}=await e.prompt([{type:"list",name:"action",message:p.t("wizard.select_action"),choices:[{name:p.t("wizard.retry_input"),value:"retry"},{name:"<- "+p.t("wizard.nav_return"),value:"back"}]}]);if("back"===o)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(p.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:"🛠️ "+p.t("wizard.select_tool"),choices:[...o,new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"back"},{name:"x "+p.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"+p.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:a}=await e.prompt([{type:"confirm",name:"install",message:p.t("wizard.install_tool_confirm"),default:!0}]);if(!a)return;try{await l.installTool(o)}catch{return void console.log(t.red(p.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+" "+p.t("wizard.menu_title"));const a=[{name:"📥 "+p.t("wizard.action_load_config",{tool:o.displayName}),value:"load"},{name:"📤 "+p.t("wizard.action_unload_config",{tool:o.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&a.push({name:"🧹 "+p.t("wizard.action_clear_model",{tool:o.displayName}),value:"clear-model"}),a.push({name:"🧨 "+p.t("wizard.action_force_config",{tool:o.displayName}),value:"force"},{name:"↩️ "+p.t("wizard.action_restore_config",{tool:o.displayName}),value:"restore"},new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"back"},{name:"x "+p.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+p.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"+p.t("wizard.api_key_required"))),console.log(t.yellow(p.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(a))return a;const i=o(p.t("wizard.validating_api_key")).start(),n=await w(a);if(n.valid)return this.validatedApiKeys.add(a),i.succeed(p.t("wizard.api_key_valid")),a;this.validatedApiKeys.delete(a);const r="invalid_api_key"===n.error?p.t("wizard.api_key_invalid"):p.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+p.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:p.t("wizard.press_enter")}]),!1;const d="claude-code"===i,m=s(i),u=d||!m?p.t("wizard.model_builtin_default"):m;this.resetScreen(),this.createBox(n.displayName+" · "+p.t("wizard.config_models"));const g=[{name:"✅ "+p.t("wizard.use_default_model",{model:u}),value:{type:"default"}},{name:"🌐 "+p.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+p.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:{type:"back"}}],{choice:w}=await this.promptWithHints([{type:"list",name:"choice",message:p.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===w.type)return!1;if("default"===w.type)a.setToolModel(i,null);else if("preset"===w.type)a.setToolModel(i,r(w.preset));else if("custom"===w.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:p.t("wizard.input_custom_model"),default:!d&&m?m:void 0}]),o=(t||"").trim();a.setToolModel(i,o?this.singleModelOverride(o):null)}else if("fetch"===w.type){const n=o(p.t("wizard.fetching_models")).start(),r=await _(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:p.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+p.t("wizard.fetch_models_failed")));const{model:o}=await e.prompt([{type:"input",name:"model",message:p.t("wizard.input_custom_model"),default:!d&&m?m:void 0}]);s=(o||"").trim()}a.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=a.getToolSetting(i,f),{enableOneMillionContext:o}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:p.t("wizard.codex_enable_1m_context"),default:!1!==t}]);a.setToolSetting(i,f,Boolean(o))}if("claude-desktop"===i){const t=a.getToolSetting(i,y),{hideOfficialLogin:o}=await e.prompt([{type:"confirm",name:"hideOfficialLogin",message:p.t("wizard.desktop_hide_official_login"),default:!1!==t}]);a.setToolSetting(i,y,Boolean(o))}return console.log(t.green("\n✓ "+p.t("wizard.models_saved"))),d&&console.log(t.gray("💡 "+p.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:p.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:p.t("wizard.press_enter")}]);const m=o(p.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,s,r,n),m.stop(),l.printApplied(i),await d([i]),n.force&&console.log(t.yellow(p.t("config.force_done",{tool:c[i]?.displayName||i})));const o=c[i]?.command;o&&console.log(t.green("\n"+p.t("wizard.run_tool_hint",{command:o})));const{nextAction:a}=await e.prompt([{type:"list",name:"nextAction",message:p.t("wizard.what_next"),choices:[{name:p.t("wizard.exit_and_start"),value:"exit"},{name:p.t("wizard.continue_config"),value:"continue"}]}]);"exit"===a&&(this.printGoodbye(),process.exit(0))}catch(o){m.fail(p.t("wizard.config_failed")),console.log(t.red(o instanceof Error?o.message:String(o))),await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}}async clearPinnedModels(o){const a=c[o]?.displayName||o,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:p.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"+p.t("config.clear_model_none",{tool:a}))):(console.log(t.green("\n✓ "+p.t("config.clear_model_done",{tool:a}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(p.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:p.t("wizard.press_enter")}])}}async unloadConfig(a){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:p.t("wizard.confirm_unload_config",{tool:c[a]?.displayName||a}),default:!1}]);if(!i)return;const n=o(p.t("wizard.unloading_config")).start();try{l.unloadPrivateConfig(a),n.succeed(p.t("wizard.config_unloaded"));const{nextAction:t}=await e.prompt([{type:"list",name:"nextAction",message:p.t("wizard.what_next"),choices:[{name:p.t("wizard.exit_and_start"),value:"exit"},{name:p.t("wizard.continue_config"),value:"continue"}]}]);"exit"===t&&(this.printGoodbye(),process.exit(0))}catch(o){n.fail(p.t("wizard.config_unload_failed")),console.log(t.red(o instanceof Error?o.message:String(o))),await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}}async restoreConfig(a){const i=c[a]?.displayName||a,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:p.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=o(p.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(a).filter(e=>e.result.restored);0===e.length?r.warn(p.t("wizard.restore_no_backup")):(r.succeed(p.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))))}catch(e){r.fail(p.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:p.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(C("── "+p.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+p.t("wizard.settings")+" ──")),{name:"🔑 "+p.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🌐 "+p.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+p.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+p.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();
@@ -17,7 +17,8 @@
17
17
  "enter": "Configuration management",
18
18
  "tool": "Tool management",
19
19
  "status": "Show current configuration status",
20
- "setup": "One-shot setup (--key auto-detects capable tools / --claudekey --codexkey for per-tool keys)"
20
+ "setup": "One-shot setup (--key auto-detects capable tools / --claudekey --codexkey for per-tool keys)",
21
+ "upgrade": "Upgrade Claude Code, Codex and other coding tools (falls back to the npm mirror)"
21
22
  },
22
23
  "lang": {
23
24
  "current": "Current language",
@@ -35,7 +36,12 @@
35
36
  "revoke_confirm": "Are you sure you want to revoke the API key?",
36
37
  "revoked": "API key revoked",
37
38
  "not_revoked": "Revoke operation cancelled",
38
- "get_api_key_hint": "To obtain API Key, please visit: {{url}}"
39
+ "get_api_key_hint": "To obtain API Key, please visit: {{url}}",
40
+ "reload_tool_not_supported": "Reloading {{tool}} is not supported",
41
+ "reload_missing_config": "No saved API key yet; run vibecli setup --key <your key> first",
42
+ "reloading": "Writing the config to {{tool}} again…",
43
+ "reloaded": "Config written to {{tool}} again",
44
+ "reload_failed": "Failed to write the config; see vibecli logs"
39
45
  },
40
46
  "wizard": {
41
47
  "banner_subtitle": "Manage Your Claude Code and Other Coding Tools",
@@ -177,26 +183,32 @@
177
183
  },
178
184
  "doctor": {
179
185
  "checking": "Running health check...",
180
- "check_path": "Check PATH environment variable",
181
186
  "check_api_key_network": "Check API Key and network connection",
182
- "check_api_key": "Check API key",
183
187
  "check_tools": "Check installed tools",
184
188
  "all_good": "All checks passed!",
185
- "tool_installed": "Installed",
186
- "tool_not_found": "Tool not found: {{tool}}",
187
189
  "network_error": "Network connection failed",
188
190
  "api_key_invalid": "API Key is invalid or expired",
189
- "api_key_network_ok": "API Key and network validation passed",
190
191
  "api_key_missing": "API key not configured",
191
- "claude_code_installed": "Claude Code is installed",
192
- "claude_code_not_installed": "Claude Code is not installed. Please download from https://claude.ai/download",
193
- "suggestions": "Suggestions",
194
- "check_git_env": "Check Git environment",
195
- "git_not_installed": "Git is not installed. Please download from https://git-scm.com/downloads",
196
192
  "results_title": "Health Check Results:",
197
193
  "no_tools_installed": "No tools installed",
198
- "suggestion_auth": "Run \"vibecli auth\" to configure API Key",
199
- "suggestion_interactive": "Run \"vibecli\" to enter interactive setup"
194
+ "proxy": "Proxy",
195
+ "check_proxy": "Checking proxy settings",
196
+ "proxy_none": "No proxy variables set",
197
+ "proxy_dead": "Cannot reach {{proxies}}: the proxy app is probably not running. Claude Code and Codex go through it; start the proxy app or remove the variable and open a new terminal",
198
+ "api_key_network": "API key & network",
199
+ "installed_tools": "Installed tools",
200
+ "tool_outdated": "below the minimum version {{min}}",
201
+ "tool_duplicates": "other copies: {{others}}",
202
+ "run_upgrade": "run vibecli upgrade {{tool}} to fix",
203
+ "claude_config": "Claude Code config",
204
+ "claude_managed": "Managed settings override what vibecli writes ({{detail}}); they usually come from your company's IT, ask your administrator",
205
+ "claude_not_configured": "~/.claude/settings.json has no VibeAPI address or key; run vibecli setup --key <your key>",
206
+ "claude_config_changed": "The address or key in ~/.claude/settings.json differs from what vibecli saved (another tool may have changed it); run vibecli setup again",
207
+ "claude_request": "Claude Code request",
208
+ "check_claude_request": "Sending a test request through Claude Code",
209
+ "claude_request_skipped": "claude command not found",
210
+ "claude_request_failed": "Request failed: {{reason}}",
211
+ "deep_hint": "Add --deep to send one real request through Claude Code (uses a little credit) and confirm the whole chain works"
200
212
  },
201
213
  "preset": {
202
214
  "claude_mix": "Claude Mix",
@@ -224,7 +236,8 @@
224
236
  "first_run": "First run detected, starting initialization wizard...",
225
237
  "config_saved": "Configuration saved",
226
238
  "env_vars_cleared": "Detected {{vars}} in your shell environment and removed them (they would override the config vibecli writes). Restart your terminal to apply the shell profile change.",
227
- "certificate_intercepted": "The certificate presented for {{url}} is not the gateway's: the local network is most likely hijacking DNS (common on campus / hotel networks), not a key problem. Switch networks, set the computer's DNS to a public resolver, or use a proxy, then retry"
239
+ "certificate_intercepted": "The certificate presented for {{url}} is not the gateway's: the local network is most likely hijacking DNS (common on campus / hotel networks), not a key problem. Switch networks, set the computer's DNS to a public resolver, or use a proxy, then retry",
240
+ "env_vars_machine_left": "System environment variables still define {{vars}}; removing them needs admin rights: run vibecli again from an elevated terminal, or delete them under System Properties → Environment Variables → System variables"
228
241
  },
229
242
  "config": {
230
243
  "profile_list_title": "Profiles",
@@ -337,7 +350,11 @@
337
350
  "desktop_official_login_missing_key": "No saved Claude Desktop API key found; run --key or --desktopkey first",
338
351
  "claude_official_invalid": "Invalid value for --claude-official: {{value}} (expected on or off)",
339
352
  "done_commands": "Type {{commands}} in your terminal to start",
340
- "failed_summary": "{{count}} tool(s) failed; see above"
353
+ "failed_summary": "{{count}} tool(s) failed; see above",
354
+ "offer_upgrade": "{{tools}} is too old; upgrade now?",
355
+ "offer_restart": "{{tool}} is running; restart it so the new config takes effect? (closes its windows)",
356
+ "restarted": "{{tool}} restarted",
357
+ "restart_failed": "Could not restart {{tool}}; quit it completely and open it again"
341
358
  },
342
359
  "tools": {
343
360
  "version_outdated": "version {{current}} is too old (needs {{min}} or later); upgrade with {{upgrade}}",
@@ -360,16 +377,9 @@
360
377
  "claude_code": {
361
378
  "gateway_models": "{{count}} Chinese models added to /model ({{vendors}})",
362
379
  "tier_fallback": "This key can't use Claude Code's default {{from}}; the {{tier}} alias now uses {{to}}",
363
- "multiple_installs": "{{count}} copies of Claude Code found; typing claude runs only {{active}}; also present: {{others}}",
364
- "keep_native": "Keep only the official native install: run {{uninstall}} to remove the npm copy, then upgrade with claude update",
365
- "upgrade_fallback": "If claude update cannot reach the download server, install from the npm mirror instead: {{mirror}}",
366
380
  "stable_channel": "Your update channel in settings.json is stable, which may not have reached {{min}} yet; set autoUpdatesChannel to latest, then upgrade",
367
- "install_kind": {
368
- "native": "native",
369
- "npm": "npm",
370
- "other": "other"
371
- },
372
- "keep_npm": "Keep only the npm copy: delete the native install (~/.local/bin/claude and ~/.local/share/claude), then upgrade with {{mirror}}"
381
+ "managed_override": "Claude Code managed settings found ({{source}}); {{keys}} there override what vibecli writes, so this config won't take effect. Managed settings usually come from your company's IT; ask your administrator",
382
+ "no_git_bash": "Git for Windows not found: Claude Code falls back to PowerShell for commands, which works, but bash commands and scripts won't; install Git (winget install Git.Git, or download from https://registry.npmmirror.com/binary.html?path=git-for-windows/)"
373
383
  },
374
384
  "model_family": {
375
385
  "gpt": "GPT",
@@ -377,5 +387,41 @@
377
387
  "other": "Chinese",
378
388
  "grok": "Grok"
379
389
  }
390
+ },
391
+ "upgrade": {
392
+ "kind": {
393
+ "native": "native",
394
+ "npm": "npm",
395
+ "brew": "Homebrew",
396
+ "other": "other"
397
+ },
398
+ "unsupported": "vibecli upgrade does not support {{tool}}",
399
+ "supported": "Supported tools: {{tools}}",
400
+ "nothing_installed": "None of {{tools}} is installed",
401
+ "current": "Current: {{copy}}",
402
+ "not_installed_installing": "{{tool}} not found; installing from the npm mirror",
403
+ "fallback_mirror": "The official upgrade failed; installing from the npm mirror instead",
404
+ "unknown_install": "Unknown install source ({{path}}); upgrade it the way it was installed",
405
+ "npm_permission": "npm cannot write the global directory: use sudo {{command}}, or run npm config set prefix ~/.npm-global, add ~/.npm-global/bin to PATH and retry",
406
+ "duplicates": "Several copies of {{tool}}: keeping {{keep}}; extra: {{others}}",
407
+ "duplicates_note": "{{count}} copies found; the terminal runs {{active}}; also present: {{others}}. Run vibecli upgrade {{tool}} to upgrade and keep one",
408
+ "confirm_remove": "Remove the extra copies?",
409
+ "removed": "Removed {{copy}}",
410
+ "remove_by_hand": "Remove by hand: {{how}}",
411
+ "delete_paths": "delete {{paths}}",
412
+ "still_missing": "{{tool}} is still not installed; see the output above",
413
+ "still_old": "After upgrading: {{copy}}, still below {{min}}",
414
+ "done": "Now {{copy}}"
415
+ },
416
+ "install": {
417
+ "permission_unix": "npm probably cannot write its global directory. Either ① sudo {{command}}; or ② run npm config set prefix ~/.npm-global, add export PATH=~/.npm-global/bin:$PATH to ~/.zshrc or ~/.bashrc, open a new terminal and run {{command}}",
418
+ "permission_windows": "Probably missing permissions or a file in use: close running Claude Code / Codex, open an elevated terminal and run {{command}}",
419
+ "what_next": "What next?",
420
+ "installed_continue": "I installed it by hand, continue",
421
+ "cancel_install": "Cancel",
422
+ "user_cancelled": "Installation cancelled",
423
+ "still_not_installed": "{{tool}} still not found; make sure it installed and open a new terminal",
424
+ "verified_success": "Found {{tool}}, continuing",
425
+ "install_failed_detail": "Installation did not finish; see the output above or vibecli logs"
380
426
  }
381
427
  }
@@ -17,7 +17,8 @@
17
17
  "enter": "配置管理",
18
18
  "tool": "工具管理",
19
19
  "status": "显示当前配置状态",
20
- "setup": "一条命令完成配置(--key 按能力自动选工具 / --claudekey --codexkey 双令牌分别配置)"
20
+ "setup": "一条命令完成配置(--key 按能力自动选工具 / --claudekey --codexkey 双令牌分别配置)",
21
+ "upgrade": "升级 Claude Code、Codex 等编码工具(官方渠道连不上时改走国内镜像)"
21
22
  },
22
23
  "lang": {
23
24
  "current": "当前语言",
@@ -35,7 +36,12 @@
35
36
  "revoke_confirm": "确认要删除已保存的API密钥吗?",
36
37
  "revoked": "API密钥已删除",
37
38
  "not_revoked": "取消删除操作",
38
- "get_api_key_hint": "如需获取 API Key,请访问: {{url}}"
39
+ "get_api_key_hint": "如需获取 API Key,请访问: {{url}}",
40
+ "reload_tool_not_supported": "不支持重新加载 {{tool}}",
41
+ "reload_missing_config": "还没有保存的 API Key,先运行 vibecli setup --key <你的 Key>",
42
+ "reloading": "正在把配置重新写入 {{tool}}…",
43
+ "reloaded": "{{tool}} 的配置已重新写入",
44
+ "reload_failed": "重新写入配置失败,详情见 vibecli logs"
39
45
  },
40
46
  "wizard": {
41
47
  "banner_subtitle": "统一管理您的 Claude Code 等编码工具",
@@ -177,26 +183,32 @@
177
183
  },
178
184
  "doctor": {
179
185
  "checking": "正在进行健康检查...",
180
- "check_path": "检查PATH环境变量",
181
186
  "check_api_key_network": "检查 API Key 和网络连接",
182
- "check_api_key": "检查API密钥",
183
187
  "check_tools": "检查已安装的工具",
184
188
  "all_good": "所有检查通过!",
185
- "tool_installed": "已安装",
186
- "tool_not_found": "工具未找到: {{tool}}",
187
189
  "network_error": "网络连接失败",
188
190
  "api_key_invalid": "API Key 无效或已过期",
189
- "api_key_network_ok": "API Key 和网络验证通过",
190
191
  "api_key_missing": "API密钥未配置",
191
- "claude_code_installed": "Claude Code 已安装",
192
- "claude_code_not_installed": "Claude Code 未安装。请从 https://claude.ai/download 下载安装",
193
- "suggestions": "建议",
194
- "check_git_env": "检查 Git 环境",
195
- "git_not_installed": "Git 未安装。请从 https://git-scm.com/downloads 下载安装",
196
192
  "results_title": "健康检查结果:",
197
193
  "no_tools_installed": "未安装任何工具",
198
- "suggestion_auth": "运行 \"vibecli auth\" 配置 API Key",
199
- "suggestion_interactive": "运行 \"vibecli\" 进入交互式配置"
194
+ "proxy": "代理设置",
195
+ "check_proxy": "检查代理设置",
196
+ "proxy_none": "未设置代理环境变量",
197
+ "proxy_dead": "连不上 {{proxies}}:代理软件可能没开。Claude Code、Codex 都会走这个代理,请打开代理软件,或删掉这个环境变量后重开终端",
198
+ "api_key_network": "API Key 与网络",
199
+ "installed_tools": "已安装的工具",
200
+ "tool_outdated": "低于最低版本 {{min}}",
201
+ "tool_duplicates": "还有另外的安装:{{others}}",
202
+ "run_upgrade": "运行 vibecli upgrade {{tool}} 处理",
203
+ "claude_config": "Claude Code 配置",
204
+ "claude_managed": "托管配置会盖掉 vibecli 写入的设置({{detail}}),一般是公司电脑统一下发的,请联系管理员",
205
+ "claude_not_configured": "~/.claude/settings.json 里没有 VibeAPI 的地址或 Key,运行 vibecli setup --key <你的 Key> 重新配置",
206
+ "claude_config_changed": "~/.claude/settings.json 里的地址或 Key 和 vibecli 保存的不一致(可能被别的工具改过),运行 vibecli setup 重新写入",
207
+ "claude_request": "Claude Code 实际请求",
208
+ "check_claude_request": "用 Claude Code 发一条测试请求",
209
+ "claude_request_skipped": "没找到 claude 命令",
210
+ "claude_request_failed": "请求失败:{{reason}}",
211
+ "deep_hint": "加上 --deep 会用 Claude Code 实际发一条请求(消耗少量额度),确认整条链路能用"
200
212
  },
201
213
  "preset": {
202
214
  "claude_mix": "Claude 混合",
@@ -224,7 +236,8 @@
224
236
  "first_run": "检测到首次运行,启动初始化向导...",
225
237
  "config_saved": "配置已保存",
226
238
  "env_vars_cleared": "检测到 shell 环境变量 {{vars}} 并已自动清除(它们会覆盖 vibecli 写入的配置)。如已写入 shell 配置文件,请重开终端生效。",
227
- "certificate_intercepted": "连接 {{url}} 时证书不对:通常是当前网络的 DNS 被劫持(校园网、酒店网络常见),而不是 Key 有问题。请换个网络、把电脑的 DNS 改成 119.29.29.29 / 223.5.5.5,或开代理后重试"
239
+ "certificate_intercepted": "连接 {{url}} 时证书不对:通常是当前网络的 DNS 被劫持(校园网、酒店网络常见),而不是 Key 有问题。请换个网络、把电脑的 DNS 改成 119.29.29.29 / 223.5.5.5,或开代理后重试",
240
+ "env_vars_machine_left": "系统环境变量里还有 {{vars}},需要管理员权限才能删:用管理员身份打开终端再运行一次 vibecli,或在「系统属性 → 环境变量 → 系统变量」里手动删除"
228
241
  },
229
242
  "config": {
230
243
  "profile_list_title": "Profile 列表",
@@ -337,7 +350,11 @@
337
350
  "desktop_official_login_missing_key": "未找到已保存的 Claude Desktop API Key;请先用 --key 或 --desktopkey 完成配置",
338
351
  "claude_official_invalid": "--claude-official 的值无效: {{value}}(只接受 on / off)",
339
352
  "done_commands": "在终端输入 {{commands}} 开始使用",
340
- "failed_summary": "{{count}} 个工具没有配好,原因见上方"
353
+ "failed_summary": "{{count}} 个工具没有配好,原因见上方",
354
+ "offer_upgrade": "{{tools}} 版本过旧,现在升级吗?",
355
+ "offer_restart": "{{tool}} 正在运行,要重启它让新配置生效吗?(会关掉当前窗口)",
356
+ "restarted": "{{tool}} 已重启",
357
+ "restart_failed": "{{tool}} 没能自动重启,请完全退出后手动打开"
341
358
  },
342
359
  "tools": {
343
360
  "version_outdated": "版本 {{current}} 过旧,需要 {{min}} 及以上,运行 {{upgrade}} 升级",
@@ -360,16 +377,9 @@
360
377
  "claude_code": {
361
378
  "gateway_models": "/model 菜单加入 {{count}} 个国产模型({{vendors}})",
362
379
  "tier_fallback": "当前 Key 用不了 Claude Code 默认的 {{from}},{{tier}} 档已改用 {{to}}",
363
- "multiple_installs": "本机有 {{count}} 份 Claude Code,终端里输入 claude 只会运行 {{active}},另外还有 {{others}}",
364
- "keep_native": "建议只留官方原生安装:运行 {{uninstall}} 删掉 npm 那份,之后用 claude update 升级",
365
- "upgrade_fallback": "claude update 连不上下载服务器时,改用国内 npm 镜像安装:{{mirror}}",
366
380
  "stable_channel": "settings.json 里的更新渠道是 stable,这个渠道可能还没到 {{min}};把 autoUpdatesChannel 改成 latest 后再升级",
367
- "install_kind": {
368
- "native": "原生安装",
369
- "npm": "npm 安装",
370
- "other": "其他安装"
371
- },
372
- "keep_npm": "建议只留 npm 那份:删除原生安装(~/.local/bin/claude 与 ~/.local/share/claude),之后用 {{mirror}} 升级"
381
+ "managed_override": "检测到 Claude Code 的托管配置({{source}}),其中 {{keys}} 优先于 vibecli 写入的设置,会让这里的配置不生效;托管配置一般是公司电脑统一下发的,请联系管理员",
382
+ "no_git_bash": "没找到 Git for Windows:Claude Code 会改用 PowerShell 执行命令,可以用,但 bash 写法的命令和脚本跑不了;建议安装 Git(winget install Git.Git,或从国内镜像 https://registry.npmmirror.com/binary.html?path=git-for-windows/ 下载)"
373
383
  },
374
384
  "model_family": {
375
385
  "gpt": "GPT",
@@ -377,5 +387,41 @@
377
387
  "other": "国产",
378
388
  "grok": "Grok"
379
389
  }
390
+ },
391
+ "upgrade": {
392
+ "kind": {
393
+ "native": "原生安装",
394
+ "npm": "npm 安装",
395
+ "brew": "Homebrew 安装",
396
+ "other": "其他安装"
397
+ },
398
+ "unsupported": "vibecli upgrade 不支持 {{tool}}",
399
+ "supported": "支持的工具:{{tools}}",
400
+ "nothing_installed": "没有找到已安装的 {{tools}}",
401
+ "current": "当前:{{copy}}",
402
+ "not_installed_installing": "没有找到 {{tool}},用国内 npm 镜像安装",
403
+ "fallback_mirror": "官方渠道升级失败,改用国内 npm 镜像安装",
404
+ "unknown_install": "不认识这份安装的来源({{path}}),没法自动升级,请按当初的安装方式升级",
405
+ "npm_permission": "npm 没有权限写全局目录:可以用 sudo {{command}},或运行 npm config set prefix ~/.npm-global 并把 ~/.npm-global/bin 加进 PATH 后重试",
406
+ "duplicates": "本机有多份 {{tool}},保留 {{keep}},多余的是 {{others}}",
407
+ "duplicates_note": "本机有 {{count}} 份,终端里实际运行的是 {{active}},另外还有 {{others}};运行 vibecli upgrade {{tool}} 升级并只留一份",
408
+ "confirm_remove": "删掉多余的那几份吗?",
409
+ "removed": "已删除 {{copy}}",
410
+ "remove_by_hand": "请手动删除:{{how}}",
411
+ "delete_paths": "删除 {{paths}}",
412
+ "still_missing": "{{tool}} 仍未安装成功,请看上面的输出",
413
+ "still_old": "升级后是 {{copy}},仍低于 {{min}}",
414
+ "done": "已是 {{copy}}"
415
+ },
416
+ "install": {
417
+ "permission_unix": "多半是 npm 没有权限写全局目录。可以二选一:① sudo {{command}};② 运行 npm config set prefix ~/.npm-global,把 export PATH=~/.npm-global/bin:$PATH 加进 ~/.zshrc 或 ~/.bashrc,重开终端后再运行 {{command}}",
418
+ "permission_windows": "多半是权限不足或文件被占用:关掉正在运行的 Claude Code / Codex,用管理员身份打开终端,再运行 {{command}}",
419
+ "what_next": "接下来怎么做?",
420
+ "installed_continue": "我已经手动装好了,继续",
421
+ "cancel_install": "取消",
422
+ "user_cancelled": "已取消安装",
423
+ "still_not_installed": "还是没找到 {{tool}},请确认安装成功、重开终端后再试",
424
+ "verified_success": "已找到 {{tool}},继续配置",
425
+ "install_failed_detail": "安装没有完成,详情见上面的输出或 vibecli logs"
380
426
  }
381
427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.56",
3
+ "version": "0.0.57",
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",
@@ -1,20 +0,0 @@
1
- /**
2
- * Every copy of Claude Code a terminal could start. Users often end up with both
3
- * the official native install (`~/.local/bin/claude`, updated by `claude update`)
4
- * and an npm global one; only the first on PATH runs, and `claude update` only
5
- * updates that one.
6
- */
7
- export interface ClaudeInstall {
8
- path: string;
9
- kind: 'native' | 'npm' | 'other';
10
- version: string | null;
11
- }
12
- /** Same package, same binary (npm ships the native build as an optional dependency); npmmirror is reachable from mainland China. */
13
- export declare const CLAUDE_NPM_MIRROR_INSTALL = "npm install -g @anthropic-ai/claude-code@latest --registry=https://registry.npmmirror.com";
14
- export declare const CLAUDE_NPM_UNINSTALL = "npm uninstall -g @anthropic-ai/claude-code";
15
- /** Tests swap in a fake `<path> --version`. */
16
- export declare function setClaudeVersionRunnerForTests(fake: (path: string) => string): void;
17
- /** In PATH order (the first one is what `claude` starts), one entry per real file. Probed once per run. */
18
- export declare function findClaudeInstalls(): ClaudeInstall[];
19
- /** `autoUpdatesChannel` in ~/.claude/settings.json; unset means `latest`. */
20
- export declare function claudeUpdateChannel(): string;
@@ -1 +0,0 @@
1
- import{spawnSync as t}from"child_process";import{existsSync as e,readFileSync as n,realpathSync as o,statSync as r}from"fs";import{delimiter as s,dirname as i,isAbsolute as c,join as a}from"path";import{toolHome as l}from"./types.js";export const CLAUDE_NPM_MIRROR_INSTALL="npm install -g @anthropic-ai/claude-code@latest --registry=https://registry.npmmirror.com";export const CLAUDE_NPM_UNINSTALL="npm uninstall -g @anthropic-ai/claude-code";let u=e=>{const n={encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]};return`${(/\.cmd$/i.test(e)?t(`"${e}" --version`,{...n,shell:!0}):t(e,["--version"],n)).stdout??""}`};export function setClaudeVersionRunnerForTests(t){u=t,f=null}function p(t){try{return r(t).isFile()}catch{return!1}}function d(t){if(n=i(t),r=a(l(),".local","bin"),"win32"===process.platform?n.toLowerCase()===r.toLowerCase():n===r)return"native";var n,r;const s=a("node_modules","@anthropic-ai","claude-code");if(e(a(i(t),s)))return"npm";try{if(o(t).includes(s))return"npm"}catch{}return"other"}let f=null;export function findClaudeInstalls(){if(f)return f;const t="win32"===process.platform?["claude.exe","claude.cmd"]:["claude"],e=new Set,n=[];for(const r of(process.env.PATH??"").split(s))if(r&&c(r))for(const s of t){const t=a(r,s);if(!p(t))continue;let i=t;try{i=o(t)}catch{}const c="win32"===process.platform?i.toLowerCase():i;if(e.has(c))continue;e.add(c);const l=u(t).match(/(\d+)\.(\d+)\.(\d+)/);n.push({path:t,kind:d(t),version:l?l[0]:null})}return f=n,n}export function claudeUpdateChannel(){try{const t=JSON.parse(n(a(l(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}