@vibeapi/cli 0.0.56 → 0.0.58

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.
Files changed (46) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +1 -1
  3. package/dist/commands/completion.js +1 -1
  4. package/dist/commands/doctor.d.ts +3 -1
  5. package/dist/commands/doctor.js +1 -1
  6. package/dist/commands/setup.js +1 -1
  7. package/dist/commands/upgrade.d.ts +2 -0
  8. package/dist/commands/upgrade.js +1 -0
  9. package/dist/lib/claude-code-manager.d.ts +4 -5
  10. package/dist/lib/claude-code-manager.js +1 -1
  11. package/dist/lib/command.js +1 -1
  12. package/dist/lib/config.d.ts +6 -0
  13. package/dist/lib/config.js +1 -1
  14. package/dist/lib/env-cleanup.d.ts +12 -0
  15. package/dist/lib/env-cleanup.js +1 -1
  16. package/dist/lib/gateway-models.d.ts +1 -1
  17. package/dist/lib/gateway-models.js +1 -1
  18. package/dist/lib/model-refresh.d.ts +21 -0
  19. package/dist/lib/model-refresh.js +1 -0
  20. package/dist/lib/model-registry.d.ts +4 -0
  21. package/dist/lib/model-registry.js +1 -1
  22. package/dist/lib/post-apply.d.ts +6 -0
  23. package/dist/lib/post-apply.js +1 -0
  24. package/dist/lib/tool-manager.js +1 -1
  25. package/dist/lib/tool-upgrade.d.ts +10 -0
  26. package/dist/lib/tool-upgrade.js +1 -0
  27. package/dist/lib/tools/claude-code.js +1 -1
  28. package/dist/lib/tools/claude-desktop.js +1 -1
  29. package/dist/lib/tools/claude-managed.d.ts +8 -0
  30. package/dist/lib/tools/claude-managed.js +1 -0
  31. package/dist/lib/tools/claude-native-mirror.d.ts +39 -0
  32. package/dist/lib/tools/claude-native-mirror.js +1 -0
  33. package/dist/lib/tools/codex.js +1 -1
  34. package/dist/lib/tools/desktop-process.d.ts +24 -0
  35. package/dist/lib/tools/desktop-process.js +1 -0
  36. package/dist/lib/tools/tool-installs.d.ts +50 -0
  37. package/dist/lib/tools/tool-installs.js +1 -0
  38. package/dist/lib/tools/types.d.ts +26 -3
  39. package/dist/lib/wizard.js +1 -1
  40. package/dist/locales/en_US.json +81 -26
  41. package/dist/locales/zh_CN.json +81 -26
  42. package/dist/models.json +77 -31
  43. package/models.json +77 -31
  44. package/package.json +1 -1
  45. package/dist/lib/tools/claude-installs.d.ts +0 -20
  46. package/dist/lib/tools/claude-installs.js +0 -1
@@ -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{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as a}from"./backup.js";import{i18n as n}from"../i18n.js";import{baseModelId as s}from"../model-catalog.js";import{gatewayModelSpecs as i,prettyModelName as r,vendorNames as d}from"../gateway-models.js";import{toolHome as l}from"./types.js";import{spawnSync as c}from"child_process";import{readFileSync as m}from"fs";import{detectManagedOverrides as u}from"./claude-managed.js";import{updateNativeFromMirror as p}from"./claude-native-mirror.js";const g=e(l(),".claude","settings.json"),f=e(l(),".claude.json");let C;function h(){try{const t=JSON.parse(m(e(l(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function _(e){if(!e)return;const t=new Set(e.models);return i("claude-code",e.clientModels).filter(e=>t.has(e.id))}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"]],mirrorUpdate:()=>p(h())},brew:"claude-code"},upgradeHints:e=>"stable"===h()?[n.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:g,wantsModelCatalog:!0,loadPrivateConfig(e){a(g),a(f),C=_(e.catalog),o.loadPrivateConfig(e.apiKey,e.models,e.catalog?.models,C)},unloadPrivateConfig(){a(g),o.unloadPrivateConfig()},detectCurrentConfig:()=>o.detectCurrentConfig(),getConfig:()=>({settings:o.getSettings(),mcp:o.getMCPConfig()}),updateConfig(e){const t=e||{};t.settings&&o.saveSettings(t.settings),t.mcp&&o.saveMCPConfig(t.mcp)},clearPinnedModels:e=>(a(g),o.clearPinnedModels(e?.models)),listedModels(){const e=o.getSettings().modelPicker;return Array.isArray(e?.options)?e.options.map(e=>s(String(e.model))):null},refreshModelMenu(e){const n=o.getSettings(),s=t(n,_(e.catalog));JSON.stringify(s)!==JSON.stringify(n)&&(a(g),o.saveSettings(s))},applyNotes(){const e=[];C&&C.length>0&&(e.push({kind:"summary",text:n.t("tools.claude_code.gateway_models",{count:String(C.length),vendors:d(C.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:C.map(e=>e.name||r(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:n.t("tools.claude_code.no_git_bash")});for(const t of u())e.push({kind:"action",text:n.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:n.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[g,f],forceResetPaths:()=>[g]};
@@ -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 M(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 E=()=>a(claudeDesktopUserDataDir(),"configLibrary"),S=()=>a(E(),"_meta.json"),v=e=>a(E(),`${e}.json`),x=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function O(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return M(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 P(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=O(S());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>M(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 N=null,G=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",J="vibecli-claude-policy.reg",V=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let W=V;export function setRegRunnerForTests(e){W=e??V}function z(){return"win32"===process.platform}function Z(e){let t;try{t=W(["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(),J);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),W(["export",H,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function q(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)];W(["add",H,"/v",t,"/t",e,"/d",n,"/f"])}}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 Y=/\d+\.\d+\.\d+/;function ee(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 te="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",oe="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",ne="darwin"===process.platform?`open ${oe}`:"win32"===process.platform?`start ${oe}`:`xdg-open ${oe}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:te,installCommand:ne,configPath:S(),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?Y.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 ee(t)}const o=C.map(e=>`'${e}'`).join(",");return ee(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=>Y.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);G=!0===n.disableDeploymentModeChooser,N={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=O(v(o.id))??{};P(v(o.id),{...K(e=>e in r),...r,...n}),t.appliedId=o.id,P(S(),t);const s=O(x())??{};if("3p"!==s.deploymentMode&&P(x(),{...s,deploymentMode:"3p"}),U=null,z())try{const e=Z(H);q({...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)&&(W(["delete",H,"/v",n,"/f"]),o-=1);o<=0&&W(["delete",H,"/f"])}(Q)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=I(),t=L(e);if(!e||!t)return;m(v(t.id)),s(v(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(S()),s(S(),{force:!0})):P(S(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=I(),t=L(e);if(!t||e?.appliedId!==t.id)return null;const o=O(v(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=I(),o=L(t);if(!o||t?.appliedId!==o.id)return;const n=O(v(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)&&(P(v(o.id),{...n,inferenceModels:r}),z()&&Z(H)?.has("inferenceModels")))try{q({inferenceModels:r})}catch(e){p.logError("claude-desktop.refreshModelMenu registry",e)}},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=O(v(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?O(v(t.id)):null,desktop:O(x()),...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(),W(["delete",H,"/v","inferenceModels","/f"]),e=!0);const t=L(I()),o=t?O(v(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;P(v(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(N&&N.models.length>0){const{models:t,source:o,groups:n}=N;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!==G&&e.push({kind:"detail",text:u.t(G?"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=[S(),x()];try{const t=L(I());t&&e.push(v(t.id))}catch{}return e},backupPaths(){const e=[S(),x()];z()&&e.push(a(claudeDesktopUserDataDir(),J));try{const t=L(I());t&&e.push(v(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}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Update Claude Code's native install from npmmirror when `claude update` can't
3
+ * reach downloads.claude.ai. The per-platform npm packages
4
+ * (`@anthropic-ai/claude-code-<platform>`) carry the very same single-file
5
+ * binary the native installer downloads (identical sha256), so the update keeps
6
+ * the user's native install instead of adding an npm copy next to it.
7
+ *
8
+ * Mirrors what the native installer itself does (Claude Code 2.1.295):
9
+ * - binary at `$XDG_DATA_HOME/claude/versions/<version>` (default `~/.local/share`),
10
+ * written through a temp file, mode 755, then renamed into place;
11
+ * - macOS / Linux: `~/.local/bin/claude` is a symlink into versions/, swapped by
12
+ * renaming a temp symlink over it; a launcher that isn't such a symlink belongs
13
+ * to someone else and is left alone (the installer refuses it too);
14
+ * - Windows: `~/.local/bin/claude.exe` is a copy; the old one (possibly running)
15
+ * is renamed to `claude.exe.old.<ms>.<pid>`, which the installer cleans up later.
16
+ *
17
+ * Integrity: the tarball must match the registry's sha512 `integrity`; when
18
+ * Anthropic's release manifest is reachable, the binary must also match its sha256.
19
+ */
20
+ export declare const NPM_MIRROR = "https://registry.npmmirror.com";
21
+ export interface NativeMirrorResult {
22
+ version: string;
23
+ /** Whether Anthropic's manifest checksum was also verified (it is skipped when unreachable). */
24
+ manifestVerified: boolean;
25
+ }
26
+ /** Tests replace the network. */
27
+ export declare function setNativeMirrorFetchForTests(fake: typeof fetch): void;
28
+ /** `linux-x64`, `linux-arm64-musl`, `darwin-arm64`, `win32-x64`…; null where there is no native build. */
29
+ export declare function nativePlatformKey(): string | null;
30
+ export declare function nativeLayout(): {
31
+ versionsDir: string;
32
+ launcher: string;
33
+ };
34
+ /**
35
+ * Download the channel's current version from npmmirror and make it the native
36
+ * install's active version. Throws with a readable reason on any failure, having
37
+ * left the existing install untouched.
38
+ */
39
+ export declare function updateNativeFromMirror(channel: string): Promise<NativeMirrorResult>;
@@ -0,0 +1 @@
1
+ import{createHash as t,randomBytes as r}from"crypto";import{createReadStream as i,createWriteStream as e}from"fs";import{chmod as a,copyFile as n,lstat as s,mkdir as o,readlink as c,rename as f,rm as h,symlink as u}from"fs/promises";import{dirname as l,join as w,resolve as d}from"path";import{pipeline as p}from"stream/promises";import{Readable as m,Transform as g,Writable as b}from"stream";import{createGunzip as $}from"zlib";import{toolHome as y}from"./types.js";export const NPM_MIRROR="https://registry.npmmirror.com";let v=(...t)=>fetch(...t);export function setNativeMirrorFetchForTests(t){v=t}export function nativePlatformKey(){const t="x64"===process.arch||"arm64"===process.arch?process.arch:null;if(!t)return null;if("darwin"===process.platform||"win32"===process.platform)return`${process.platform}-${t}`;if("linux"!==process.platform)return null;const r=process.report?.getReport()?.header;return r?.glibcVersionRuntime?`linux-${t}`:`linux-${t}-musl`}export function nativeLayout(){const t=y(),r=process.env.XDG_DATA_HOME?.trim()||w(t,".local","share");return{versionsDir:w(r,"claude","versions"),launcher:w(t,".local","bin","win32"===process.platform?"claude.exe":"claude")}}async function x(t,r){const i=await v(t,{signal:AbortSignal.timeout(r)});if(!i.ok)throw new Error(`${t}: HTTP ${i.status}`);return await i.json()}class E extends b{names;outPath;buffer=Buffer.alloc(0);remaining=0;padding=0;writing=null;found=!1;constructor(t,r){super(),this.names=t,this.outPath=r}_write(t,r,i){this.buffer=this.buffer.length?Buffer.concat([this.buffer,t]):t,this.drain().then(()=>i()).catch(i)}_final(t){this.writing?this.writing.end(()=>t()):t()}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const t=Math.min(this.remaining,this.buffer.length),r=this.buffer.subarray(0,t);if(this.buffer=this.buffer.subarray(t),this.remaining-=t,this.writing&&!this.writing.write(r)&&await new Promise(t=>this.writing.once("drain",()=>t())),0===this.remaining&&this.writing){const t=this.writing;this.writing=null,await new Promise((r,i)=>t.end(t=>t?i(t):r()))}continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const t=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),t.every(t=>0===t))continue;const r=(r,i)=>t.subarray(r,r+i).toString("utf-8").replace(/\0.*$/s,""),i=r(345,155),a=i?`${i}/${r(0,100)}`:r(0,100),n=parseInt(r(124,12).trim()||"0",8),s=r(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,("0"===s||""===s)&&this.names.includes(a)&&!this.found&&(this.found=!0,this.writing=e(this.outPath,{mode:493}),0===n)){const t=this.writing;this.writing=null,await new Promise(r=>t.end(()=>r()))}}}}export async function updateNativeFromMirror(b){const y=nativePlatformKey();if(!y)throw new Error(`no native Claude Code build for ${process.platform}-${process.arch}`);const{versionsDir:P,launcher:R}=nativeLayout();if("win32"!==process.platform&&!await async function(t,r){try{return!!(await s(t)).isSymbolicLink()&&d(l(t),await c(t)).startsWith(d(r)+"/")}catch{return!0}}(R,P))throw new Error(`${R} was not created by the native installer; not replacing it`);const k=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code`,2e4),M=k["dist-tags"]?.[b]??k["dist-tags"]?.latest;if(!M||!/^\d+\.\d+\.\d+$/.test(M))throw new Error("npmmirror returned no usable version");const D=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code-${y}/${M}`,2e4),N=D.dist?.tarball,T=D.dist?.integrity;if(!N||!T?.startsWith("sha512-"))throw new Error("npmmirror returned no tarball or integrity");await o(P,{recursive:!0});const _=`${process.pid}.${Date.now()}`,A=w(P,`${M}.download.${_}`),O=w(P,`${M}.tmp.${_}`);try{const c=await v(N,{signal:AbortSignal.timeout(9e5)});if(!c.ok||!c.body)throw new Error(`${N}: HTTP ${c.status}`);const d=t("sha512"),b=new g({transform(t,r,i){d.update(t),i(null,t)}});if(await p(m.fromWeb(c.body),b,e(A)),`sha512-${d.digest("base64")}`!==T)throw new Error("downloaded package does not match its sha512 integrity");const k=new E(["package/claude","package/claude.exe"],O);if(await p(i(A),$(),k),!k.found)throw new Error("package has no claude binary");await a(O,493);let D=!1;try{const r=await x(`https://downloads.claude.ai/claude-code-releases/${M}/manifest.json`,8e3),e=r.platforms?.[y]?.checksum;if(e){if(await async function(r){const e=t("sha256");return await p(i(r),e),e.digest("hex")}(O)!==e)throw new Error("binary does not match Anthropic's release checksum");D=!0}}catch(t){if(t instanceof Error&&t.message.includes("release checksum"))throw t}const _=w(P,M);return await f(O,_),await async function(t,i){await o(l(i),{recursive:!0});const e=`${process.pid}.${Date.now()}`;if("win32"!==process.platform){const r=`${i}.tmp.${e}`;await u(t,r);try{await f(r,i)}catch(t){throw await h(r,{force:!0}),t}return}const a=`${i}.new.${e}.${r(4).toString("hex")}`;await n(t,a);let c=null;try{await s(i),c=`${i}.old.${Date.now()}.${process.pid}`,await f(i,c)}catch(t){if("ENOENT"!==t.code)throw await h(a,{force:!0}),t}try{await f(a,i)}catch(t){throw c&&await f(c,i).catch(()=>{}),await h(a,{force:!0}),t}}(_,R),{version:M,manifestVerified:D}}finally{await h(A,{force:!0}),await h(O,{force:!0})}}
@@ -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 e,readFileSync as o}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 s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as u,codexCatalogPath as p,loadBundledCodexModels as g,loadPreviousCodexModels as _,previousGatewayModelIds as f}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(d(),".codex"),C=()=>t(w(),"config.toml"),h=()=>t(w(),"auth.json"),j="OPENAI_API_KEY",k="custom",b=1e6,E=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function O(e,o){return M(e[o])||(e[o]={}),e[o]}function v(){if(!e(C()))return{};const t=o(C(),"utf-8");if(!t.trim())return{};try{const e=n.parse(t);if(!M(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function N(){if(!e(h()))return{};try{const e=JSON.parse(o(h(),"utf-8"));return M(e)?e:{}}catch{return{}}}let S=null;function A(t,n,r){const s=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===b&&delete e.model_context_window,e.model_auto_compact_token_limit===E&&delete e.model_auto_compact_token_limit}(t);const d=g()??_();if(!d)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===p()&&delete t.model_catalog_json,void(s&&(t.model_context_window=b,t.model_auto_compact_token_limit=E));const c=n.catalog,m=u({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??f(),oneMillion:s,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=p();e(w)&&o(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(e,o){const t=v(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:o},r);const s=n.stringify(t);s!==i&&(l(C()),a(C(),s))}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:C(),wantsModelCatalog:!0,listedModels(){if(v().model_catalog_json!==p())return null;try{return(JSON.parse(o(p(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){v().model_catalog_json===p()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const e=new Map;for(const o of S.listed)e.set(y(o.id),(e.get(y(o.id))??0)+1);const o=["gpt","claude","other","grok"].filter(o=>e.has(o)).map(o=>`${r.t(`tools.model_family.${o}`)} ${e.get(o)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:o})},{kind:"detail",text:S.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const o=m("codex"),t=s(e.models,o),i=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(t)?o:t,r=c(e.baseUrl),d=v();d.model=i,d.model_provider=k,d.model_reasoning_effort="xhigh",A(d,e,i);const u=O(d,"model_providers"),p=M(u[k])?{...u[k]}:{};delete p.env_key,u[k]={...p,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(d,"profiles"),_=M(g[i])?{...g[i]}:{};g[i]={..._,model_provider:k,model:i},O(O(d,"tui"),"model_availability_nux")[i]=4;const f=O(d,"features");f.goals=!0,f.js_repl=!1,l(C()),a(C(),n.stringify(d)),function(e){const o=e.trim();if(!o)return;const t={...N(),auth_mode:"apikey",[j]:o};l(h()),a(h(),JSON.stringify(t,null,2)+"\n")}(e.apiKey)},unloadPrivateConfig(){if(!e(C()))return;let o;try{o=v()}catch(e){return void i.logError("codexAdapter.unloadPrivateConfig",e)}const t=o.model_providers;M(t)&&(delete t[k],0===Object.keys(t).length&&delete o.model_providers),o.model_provider===k&&delete o.model_provider,o.model_catalog_json===p()&&delete o.model_catalog_json,l(C()),a(C(),n.stringify(o))},detectCurrentConfig(){const e=N()[j];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return v()}catch(e){return i.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[C(),h(),p()]};
@@ -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,50 @@
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
+ * Replace the native binary from a mirror when the vendor updater can't connect,
23
+ * keeping the native install. Resolves to the installed version; throws with a reason.
24
+ */
25
+ mirrorUpdate?(): Promise<{
26
+ version: string;
27
+ manifestVerified: boolean;
28
+ }>;
29
+ };
30
+ /** Homebrew formula / cask name. */
31
+ brew?: string;
32
+ }
33
+ export type InstallKind = 'native' | 'npm' | 'brew' | 'other';
34
+ export interface ToolInstall {
35
+ path: string;
36
+ kind: InstallKind;
37
+ version: string | null;
38
+ }
39
+ /** npmmirror carries the same packages (including the per-platform native binaries) and is reachable from mainland China. */
40
+ export declare const NPM_MIRROR = "https://registry.npmmirror.com";
41
+ export declare function npmMirrorInstallCommand(spec: ToolInstallSpec): string;
42
+ export declare function npmUninstallCommand(spec: ToolInstallSpec): string;
43
+ /** `原生安装 2.1.295 (C:\\Users\\me\\.local\\bin\\claude.exe)` */
44
+ export declare function installLabel(install: ToolInstall): string;
45
+ /** Tests swap in a fake `<path> --version`. */
46
+ export declare function setVersionRunnerForTests(fake: (path: string) => string): void;
47
+ /** Drop cached probes (after an install, upgrade or removal changed what is on disk). */
48
+ export declare function forgetInstalls(): void;
49
+ /** In PATH order (the first one is what the command starts), one entry per real file. Probed once per run. */
50
+ 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,31 @@ 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
+ * Models currently in the tool's menu as vibecli wrote it (null when vibecli
61
+ * doesn't manage the menu right now), and a rewrite of just that menu from a
62
+ * fresh catalog — used to follow the gateway's list without a full setup.
63
+ * The refresh leaves credentials, provider and the user's model choice alone.
64
+ */
65
+ listedModels?(): string[] | null;
66
+ refreshModelMenu?(ctx: ToolApplyContext & {
67
+ catalog: ModelCatalog;
68
+ }): void;
69
+ /**
70
+ * GUI apps that only read their config at start: setup and the wizard offer
71
+ * to restart a running app after writing it.
72
+ */
73
+ runningApp?: {
74
+ isRunning(): boolean;
75
+ restart(): boolean;
76
+ };
77
+ /** Extra lines shown under the "too old" note (e.g. a release channel that hasn't reached it yet). */
55
78
  upgradeHints?(min: string): string[];
56
79
  /**
57
80
  * 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();