@xship/cli 0.0.146 → 0.0.148

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.
@@ -1 +1 @@
1
- import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as c}from"./tool-manager.js";import{confirmWritePlan as d}from"./write-plan.js";import{postApplyPrompts as p}from"./post-apply.js";import{i18n as m,detectSystemLang as u}from"./i18n.js";import{createBorderLine as _,createContentLine as g}from"../utils/string-width.js";import{validateApiKey as w,fetchModels as f}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as y}from"./tools/codex.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as h}from"./tools/claude-desktop.js";import{addressesWithCurrent as z,ensureReachableGatewayAddress as v,fastestIndex as k,probeAll as x,probeSummary as b,reasonText as S,savedApiKey as C,switchGatewayAddress as M}from"../commands/base.js";import{readFileSync as T}from"fs";import{dirname as A,join as K}from"path";import{fileURLToPath as N}from"url";import{brand as B}from"./brand.js";const P=A(N(import.meta.url)),H=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(H.bold("\n"+_("╔","╗","═",this.BOX_WIDTH))),console.log(H.bold(g(e,"║","║",this.BOX_WIDTH,"center"))),console.log(H.bold(_("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(m.t("wizard.hint_navigate")),t.gray(m.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=60,t=m.t("wizard.banner_subtitle"),a=g(t,"║","║",e,"center"),o=g("","║","║",e,"center"),i=g(`${B().productName} v${function(){try{return JSON.parse(T(K(P,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[String.raw`__ ___ _ ____ _ ___ `,String.raw`\ \ / (_) |__ ___ / ___| | |_ _|`,String.raw` \ \ / /| | '_ \ / _ \ | | | | | `,String.raw` \ V / | | |_) | __/ |___| |___ | | `,String.raw` \_/ |_|_.__/ \___|\____|_____|___|`],r=Math.max(...n.map(e=>e.length)),s=" ".repeat(Math.floor((58-r)/2)),l=n.map(t=>g(s+t,"║","║",e,"left")),c=[_("╔","╗","═",e),o,...l,o,i,a,_("╚","╝","═",e)];console.log(H.bold("\n"+c.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{if(e.gui)return!1;try{return Boolean(l.detectCurrentConfig(e.name).apiKey)&&l.isToolInstalled(e.name)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=m.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+m.t("wizard.goodbye_claude_hint"))}else a=m.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a)),this.printPathReloadHint(e.map(e=>e.name))}printPathReloadHint(e){const a=e.filter(e=>!l.isOnPath(e));if(0===a.length)return;const o=a.map(e=>c[e].command).join(" / ");console.log(t.yellow(" ! "+m.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:o})))}async runFirstTimeSetup(){const e="zh_CN"===u();e&&(o.setLang("zh_CN"),m.setLocale("zh_CN")),this.resetScreen(),console.log(H.bold("\n"+m.t("wizard.welcome"))),console.log(t.gray(m.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(m.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await v(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(m.t("wizard.select_language"));const a=m.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+m.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+m.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+m.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"back"},{name:"x "+m.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void m.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(m.t(e.name)!==e.name?m.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+m.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+m.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:m.t("wizard.custom_model_mode"),choices:[{name:m.t("wizard.custom_model_single"),value:"single"},{name:m.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:m.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:m.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:m.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:m.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:m.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+m.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(m.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+m.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+m.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+m.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:m.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+m.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:m.t("wizard.load_config_now"),choices:[{name:m.t("wizard.load_config_yes"),value:!0},{name:m.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configBaseUrl(){const i=o.getBaseUrl(),n=z(),r=a(m.t("wizard.base_testing")).start(),s=await x(n,{apiKey:C(),samples:3});r.stop();const l=k(s),c=n.map((e,a)=>{const o=e.url===i?t.cyan(` ← ${m.t("base.current_mark")}`):"";return{name:`${e.url} ${t.gray(e.label)} ${b(s[a],a===l)}${o}`,value:e.url}});c.push(new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"__back__"});const{url:d}=await this.promptWithHints([{type:"list",name:"url",message:m.t("wizard.base_select"),choices:c,default:i}]);if("__back__"===d)return;const p=s[n.findIndex(e=>e.url===d)];p.ok?await M(d):console.log(t.red(m.t("base.unreachable",{url:d,reason:S(p)}))),await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}async configApiKey(){for(;;){this.resetScreen(),this.createBox(m.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+m.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+m.t("wizard.api_key_get_hint",{url:B().links.tokenPage}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:m.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:m.t("wizard.select_action"),choices:[{name:m.t("wizard.retry_input"),value:"retry"},{name:"<- "+m.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(m.t("wizard.validating_api_key")).start(),r=await w(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+m.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+m.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+m.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:m.t("wizard.select_action"),choices:[{name:m.t("wizard.retry_input"),value:"retry"},{name:"<- "+m.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(m.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+m.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"back"},{name:"x "+m.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=c[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+m.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:m.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(m.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=c[t];if(!a)return;this.createBox(a.displayName+" "+m.t("wizard.menu_title"));const o=[{name:"📥 "+m.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+m.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+m.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"}),o.push({name:"🧨 "+m.t("wizard.action_force_config",{tool:a.displayName}),value:"force"},{name:"↩️ "+m.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"back"},{name:"x "+m.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+m.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"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!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+m.t("wizard.api_key_required"))),console.log(t.yellow(m.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(m.t("wizard.validating_api_key")).start(),n=await w(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(m.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?m.t("wizard.api_key_invalid"):m.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+m.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=c[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}]),!1;const d="claude-code"===i,p=d||"claude-desktop"===i||"agy"===i,u=s(i),_=p||!u?m.t("wizard.model_builtin_default"):u;this.resetScreen(),this.createBox(n.displayName+" · "+m.t("wizard.config_models"));const g=[{name:"✅ "+m.t("wizard.use_default_model",{model:_}),value:{type:"default"}},{name:"🌐 "+m.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+m.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:{type:"back"}}],{choice:w}=await this.promptWithHints([{type:"list",name:"choice",message:m.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===w.type)return!1;if("default"===w.type)o.setToolModel(i,null);else if("preset"===w.type)o.setToolModel(i,r(w.preset));else if("custom"===w.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:m.t("wizard.input_custom_model"),default:!p&&u?u:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===w.type){const n=a(m.t("wizard.fetching_models")).start(),r=await f(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:m.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+m.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:m.t("wizard.input_custom_model"),default:!p&&u?u:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=o.getToolSetting(i,y),{enableOneMillionContext:a}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:m.t("wizard.codex_enable_1m_context"),default:!1!==t}]);o.setToolSetting(i,y,Boolean(a))}if("claude-desktop"===i){const t=o.getToolSetting(i,h),{hideOfficialLogin:a}=await e.prompt([{type:"confirm",name:"hideOfficialLogin",message:m.t("wizard.desktop_hide_official_login"),default:!1!==t}]);o.setToolSetting(i,h,Boolean(a))}return console.log(t.green("\n✓ "+m.t("wizard.models_saved"))),d&&console.log(t.gray("💡 "+m.t("wizard.claude_model_hint"))),!0}async forceConfig(t){const a=c[t]?.displayName||t,{confirm:o}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("config.confirm_force",{tool:a}),default:!1}]);o&&await this.loadConfig(t,{force:!0})}async loadConfig(i,n={}){const r=o.getModelsForTool(i),s=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!s)return void await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}]);if(!n.force&&!await d([i]))return;const u=a(m.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,s,r,n),u.stop(),l.printApplied(i),await p([i]),n.force&&console.log(t.yellow(m.t("config.force_done",{tool:c[i]?.displayName||i,name:i})));const a=c[i];a&&!a.gui&&(l.isOnPath(i)&&console.log(t.green("\n"+m.t("wizard.run_tool_hint",{command:a.command}))),this.printPathReloadHint([i]));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:m.t("wizard.what_next"),choices:[{name:m.t("wizard.exit_and_start"),value:"exit"},{name:m.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){u.fail(m.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=c[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=await l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+m.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+m.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(m.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("wizard.confirm_unload_config",{tool:c[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(m.t("wizard.unloading_config")).start();try{const a=l.holdsOurConfig(o),i=l.unloadPrivateConfig(o);if(!a)return void n.info(m.t("config.remove_nothing",{tool:c[o]?.displayName||o}));n.succeed(m.t("wizard.config_unloaded")),i.forEach(e=>console.log(t.yellow(" ! "+e.text)));const{nextAction:r}=await e.prompt([{type:"list",name:"nextAction",message:m.t("wizard.what_next"),choices:[{name:m.t("wizard.nav_exit"),value:"exit"},{name:m.t("wizard.continue_config"),value:"continue"}]}]);"exit"===r&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(m.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}}async restoreConfig(o){const i=c[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(m.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(m.t("wizard.restore_no_backup")):(r.succeed(m.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))),console.log(t.yellow(" ! "+m.t("config.reopen_hint",{tool:i}))))}catch(e){r.fail(m.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(H("── "+m.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+m.t("wizard.settings")+" ──")),{name:"🔑 "+m.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🔗 "+m.t("wizard.menu_base_url",{url:o.getBaseUrl()}),value:{type:"base"}},{name:"🌐 "+m.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+m.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+m.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():"base"===s.type?await this.configBaseUrl():"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 a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as c}from"./tool-manager.js";import{confirmWritePlan as d}from"./write-plan.js";import{postApplyPrompts as p}from"./post-apply.js";import{appStatus as m,appStatusText as u,appTools as _,appsForTools as g,cachedAppStatus as w,upgradeApp as f}from"./app-update.js";import{i18n as y,detectSystemLang as h}from"./i18n.js";import{createBorderLine as z,createContentLine as v}from"../utils/string-width.js";import{validateApiKey as k,fetchModels as x}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as b}from"./tools/codex.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as S}from"./tools/claude-desktop.js";import{addressesWithCurrent as C,ensureReachableGatewayAddress as M,fastestIndex as T,probeAll as A,probeSummary as K,reasonText as N,savedApiKey as B,switchGatewayAddress as P}from"../commands/base.js";import{readFileSync as W}from"fs";import{dirname as j,join as H}from"path";import{fileURLToPath as O}from"url";import{brand as L}from"./brand.js";const $=j(O(import.meta.url)),F=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(F.bold("\n"+z("╔","╗","═",this.BOX_WIDTH))),console.log(F.bold(v(e,"║","║",this.BOX_WIDTH,"center"))),console.log(F.bold(z("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(y.t("wizard.hint_navigate")),t.gray(y.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=60,t=y.t("wizard.banner_subtitle"),a=v(t,"║","║",e,"center"),o=v("","║","║",e,"center"),i=v(`${L().productName} v${function(){try{return JSON.parse(W(H($,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[String.raw`__ ___ _ ____ _ ___ `,String.raw`\ \ / (_) |__ ___ / ___| | |_ _|`,String.raw` \ \ / /| | '_ \ / _ \ | | | | | `,String.raw` \ V / | | |_) | __/ |___| |___ | | `,String.raw` \_/ |_|_.__/ \___|\____|_____|___|`],r=Math.max(...n.map(e=>e.length)),s=" ".repeat(Math.floor((58-r)/2)),l=n.map(t=>v(s+t,"║","║",e,"left")),c=[z("╔","╗","═",e),o,...l,o,i,a,z("╚","╝","═",e)];console.log(F.bold("\n"+c.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{if(e.gui)return!1;try{return Boolean(l.detectCurrentConfig(e.name).apiKey)&&l.isToolInstalled(e.name)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=y.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+y.t("wizard.goodbye_claude_hint"))}else a=y.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a)),this.printPathReloadHint(e.map(e=>e.name))}printPathReloadHint(e){const a=e.filter(e=>!l.isOnPath(e));if(0===a.length)return;const o=a.map(e=>c[e].command).join(" / ");console.log(t.yellow(" ! "+y.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:o})))}async runFirstTimeSetup(){const e="zh_CN"===h();e&&(o.setLang("zh_CN"),y.setLocale("zh_CN")),this.resetScreen(),console.log(F.bold("\n"+y.t("wizard.welcome"))),console.log(t.gray(y.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(y.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await M(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(y.t("wizard.select_language"));const a=y.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+y.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+y.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+y.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+y.t("wizard.nav_return"),value:"back"},{name:"x "+y.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void y.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(y.t(e.name)!==e.name?y.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+y.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+y.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:y.t("wizard.custom_model_mode"),choices:[{name:y.t("wizard.custom_model_single"),value:"single"},{name:y.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:y.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:y.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:y.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:y.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:y.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+y.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(y.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+y.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+y.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+y.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:y.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+y.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:y.t("wizard.load_config_now"),choices:[{name:y.t("wizard.load_config_yes"),value:!0},{name:y.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configBaseUrl(){const i=o.getBaseUrl(),n=C(),r=a(y.t("wizard.base_testing")).start(),s=await A(n,{apiKey:B(),samples:3});r.stop();const l=T(s),c=n.map((e,a)=>{const o=e.url===i?t.cyan(` ← ${y.t("base.current_mark")}`):"";return{name:`${e.url} ${t.gray(e.label)} ${K(s[a],a===l)}${o}`,value:e.url}});c.push(new e.Separator,{name:"<- "+y.t("wizard.nav_return"),value:"__back__"});const{url:d}=await this.promptWithHints([{type:"list",name:"url",message:y.t("wizard.base_select"),choices:c,default:i}]);if("__back__"===d)return;const p=s[n.findIndex(e=>e.url===d)];p.ok?await P(d):console.log(t.red(y.t("base.unreachable",{url:d,reason:N(p)}))),await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])}async configApiKey(){for(;;){this.resetScreen(),this.createBox(y.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+y.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+y.t("wizard.api_key_get_hint",{url:L().links.tokenPage}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:y.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:y.t("wizard.select_action"),choices:[{name:y.t("wizard.retry_input"),value:"retry"},{name:"<- "+y.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(y.t("wizard.validating_api_key")).start(),r=await k(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+y.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+y.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+y.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:y.t("wizard.select_action"),choices:[{name:y.t("wizard.retry_input"),value:"retry"},{name:"<- "+y.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(a=!1){for(const e of _())await m(e).catch(()=>{});for(;;){this.resetScreen(),this.createBox(y.t("wizard.select_tool"));const o=l.getSupportedTools().map(e=>{const a=g([e.name]).map(e=>w(e)).find(e=>e.outdated);return{name:e.displayName+(a?t.yellow(" "+(a.tool===e.name?"":`${a.name} `)+u(a)):""),value:e.name}}),{tool:i}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+y.t("wizard.select_tool"),choices:[...o,new e.Separator,{name:"<- "+y.t("wizard.nav_return"),value:"back"},{name:"x "+y.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;await this.configureTool(i,a)}}async configureTool(a,o=!1){const i=c[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+y.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:y.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(y.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=c[t];if(!a)return;this.createBox(a.displayName+" "+y.t("wizard.menu_title"));const o=[{name:"📥 "+y.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+y.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+y.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"});for(const e of g([t]).map(e=>w(e)).reverse()){if(e.blocker||!e.latest||!e.outdated&&e.installed)continue;const t=e.installed?y.t("wizard.action_upgrade_app",{tool:e.name,current:e.current??"?",latest:e.latest}):y.t("wizard.action_install_app",{tool:e.name,latest:e.latest});o.unshift({name:"↑ "+t,value:`upgrade-app:${e.tool}`})}o.push({name:"🧨 "+y.t("wizard.action_force_config",{tool:a.displayName}),value:"force"},{name:"↩️ "+y.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+y.t("wizard.nav_return"),value:"back"},{name:"x "+y.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+y.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"force"===i?await this.forceConfig(t):"unload"===i?await this.unloadConfig(t):"string"==typeof i&&i.startsWith("upgrade-app:")?(await f(i.slice(12),{install:!0}),await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])):"clear-model"===i?await this.clearPinnedModels(t):"restore"===i&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}hasConfiguredApiKey(){return!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+y.t("wizard.api_key_required"))),console.log(t.yellow(y.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(y.t("wizard.validating_api_key")).start(),n=await k(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(y.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?y.t("wizard.api_key_invalid"):y.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+y.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=c[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}]),!1;const d="claude-code"===i,p=d||"claude-desktop"===i||"agy"===i,m=s(i),u=p||!m?y.t("wizard.model_builtin_default"):m;this.resetScreen(),this.createBox(n.displayName+" · "+y.t("wizard.config_models"));const _=[{name:"✅ "+y.t("wizard.use_default_model",{model:u}),value:{type:"default"}},{name:"🌐 "+y.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+y.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+y.t("wizard.nav_return"),value:{type:"back"}}],{choice:g}=await this.promptWithHints([{type:"list",name:"choice",message:y.t("wizard.select_tool_model",{tool:n.displayName}),choices:_}]);if("back"===g.type)return!1;if("default"===g.type)o.setToolModel(i,null);else if("preset"===g.type)o.setToolModel(i,r(g.preset));else if("custom"===g.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:y.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===g.type){const n=a(y.t("wizard.fetching_models")).start(),r=await x(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:y.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+y.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+y.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:y.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=o.getToolSetting(i,b),{enableOneMillionContext:a}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:y.t("wizard.codex_enable_1m_context"),default:!1!==t}]);o.setToolSetting(i,b,Boolean(a))}if("claude-desktop"===i){const t=o.getToolSetting(i,S),{hideOfficialLogin:a}=await e.prompt([{type:"confirm",name:"hideOfficialLogin",message:y.t("wizard.desktop_hide_official_login"),default:!1!==t}]);o.setToolSetting(i,S,Boolean(a))}return console.log(t.green("\n✓ "+y.t("wizard.models_saved"))),d&&console.log(t.gray("💡 "+y.t("wizard.claude_model_hint"))),!0}async forceConfig(t){const a=c[t]?.displayName||t,{confirm:o}=await e.prompt([{type:"confirm",name:"confirm",message:y.t("config.confirm_force",{tool:a}),default:!1}]);o&&await this.loadConfig(t,{force:!0})}async loadConfig(i,n={}){const r=o.getModelsForTool(i),s=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!s)return void await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}]);if(!n.force&&!await d([i]))return;const m=a(y.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,s,r,n),m.stop(),l.printApplied(i),await p([i],{apps:{install:!1}}),n.force&&console.log(t.yellow(y.t("config.force_done",{tool:c[i]?.displayName||i,name:i})));const a=c[i];a&&!a.gui&&(l.isOnPath(i)&&console.log(t.green("\n"+y.t("wizard.run_tool_hint",{command:a.command}))),this.printPathReloadHint([i]));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:y.t("wizard.what_next"),choices:[{name:y.t("wizard.exit_and_start"),value:"exit"},{name:y.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){m.fail(y.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=c[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:y.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=await l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+y.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+y.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(y.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:y.t("wizard.confirm_unload_config",{tool:c[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(y.t("wizard.unloading_config")).start();try{const a=l.holdsOurConfig(o),i=l.unloadPrivateConfig(o);if(!a)return void n.info(y.t("config.remove_nothing",{tool:c[o]?.displayName||o}));n.succeed(y.t("wizard.config_unloaded")),i.forEach(e=>console.log(t.yellow(" ! "+e.text)));const{nextAction:r}=await e.prompt([{type:"list",name:"nextAction",message:y.t("wizard.what_next"),choices:[{name:y.t("wizard.nav_exit"),value:"exit"},{name:y.t("wizard.continue_config"),value:"continue"}]}]);"exit"===r&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(y.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])}}async restoreConfig(o){const i=c[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:y.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(y.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(y.t("wizard.restore_no_backup")):(r.succeed(y.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))),console.log(t.yellow(" ! "+y.t("config.reopen_hint",{tool:i}))))}catch(e){r.fail(y.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:y.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(F("── "+y.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+y.t("wizard.settings")+" ──")),{name:"🔑 "+y.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🔗 "+y.t("wizard.menu_base_url",{url:o.getBaseUrl()}),value:{type:"base"}},{name:"🌐 "+y.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+y.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+y.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():"base"===s.type?await this.configBaseUrl():"language"===s.type&&await this.configLanguage()}else await this.runFirstTimeSetup()}}export const wizard=Wizard.getInstance();
@@ -119,7 +119,9 @@
119
119
  "action_force_config": "Reset to recommended: delete {{tool}}'s config files and rewrite them, to repair a broken config",
120
120
  "menu_base_url": "Switch gateway address (current: {{url}})",
121
121
  "base_testing": "Testing the gateway addresses…",
122
- "base_select": "Pick a gateway address (configured tools switch with it)"
122
+ "base_select": "Pick a gateway address (configured tools switch with it)",
123
+ "action_upgrade_app": "Upgrade {{tool}} ({{current}} installed, {{latest}} available)",
124
+ "action_install_app": "Install {{tool}} {{latest}}"
123
125
  },
124
126
  "doctor": {
125
127
  "checking": "Running checks…",
@@ -169,7 +171,9 @@
169
171
  "report_uploading": "Uploading diagnostics…",
170
172
  "report_done": "Diagnostic code: {{code}} (give it to support; they can see these results and recent run history with it)",
171
173
  "report_failed": "Couldn't upload the diagnostics. Send support a screenshot of the results above, and run {{cli}} doctor --report again once the network is back",
172
- "report_hint": "When you need help from support, run {{cli}} doctor --report and give them the diagnostic code"
174
+ "report_hint": "When you need help from support, run {{cli}} doctor --report and give them the diagnostic code",
175
+ "app_version": "{{tool}} version",
176
+ "run_app_upgrade": "Run {{command}}"
173
177
  },
174
178
  "status": {
175
179
  "title": "Current Status",
@@ -361,7 +365,9 @@
361
365
  "list_separator": ", ",
362
366
  "gui_preconfigured": "{{tool}} is not installed yet; its config is written already, so it works once you install and open it (download: {{install}})",
363
367
  "validate_failed": "Couldn't validate the API key for {{label}}: {{reason}}",
364
- "skip_no_claude_desktop": "This API key has no Claude model group; skipped Claude Code and Claude Desktop"
368
+ "skip_no_claude_desktop": "This API key has no Claude model group; skipped Claude Code and Claude Desktop",
369
+ "install_app_confirm": "Install {{tools}}? (about {{size}} MB from the download site)",
370
+ "install_app_hint": "{{tool}} isn't installed: run {{command}} to install it (its config is ready, so it works once opened)"
365
371
  },
366
372
  "tools": {
367
373
  "version_outdated": "version {{current}} is too old (needs {{min}} or later); upgrade with {{upgrade}}",
@@ -846,20 +852,25 @@
846
852
  }
847
853
  },
848
854
  "upgrade": {
849
- "summary": "Upgrade Claude Code, Codex and other coding tools",
850
- "description": "Upgrades each copy the way it was installed: native installs use the tool's own updater, npm installs are reinstalled from the China mirror (npmmirror), Homebrew installs use brew upgrade; if a native or Homebrew upgrade doesn't work, {{brand}}'s install script is used (programs from the China mirror). A named tool that is not installed yet is installed with that script.\nWith no tool named, upgrades every installed one.\nTo update {{product}} itself, use {{cli}} update.",
855
+ "summary": "Upgrade Claude Code, Codex, Claude Desktop, ChatGPT and other coding tools",
856
+ "description": "Upgrades each copy the way it was installed: native installs use the tool's own updater, npm installs are reinstalled from the China mirror (npmmirror), Homebrew installs use brew upgrade; if a native or Homebrew upgrade doesn't work, {{brand}}'s install script is used (programs from the China mirror). A named tool that is not installed yet is installed with that script.\nClaude Desktop and ChatGPT (with Codex inside; macOS / Windows) older than the download site's build are upgraded from the official installer there, after checking its checksum and the vendor's (Anthropic / OpenAI) signature; their own auto-update stays on, {{cli}} only steps in when they fall behind. A running app is only closed after a yes at a terminal; with --yes or without a terminal it is left running and only a hint is printed. Add --force to reinstall one that is already the latest. ChatGPT for Mac needs Apple silicon.\nWith no tool named, upgrades every installed one.\nTo update {{product}} itself, use {{cli}} update.",
851
857
  "arg": {
852
- "tools": "claude-code, codex, grok, agy; one or more"
858
+ "tools": "claude-code, codex, grok, agy, claude-desktop, chatgpt; one or more"
853
859
  },
854
860
  "ex": {
855
861
  "all": "Upgrade every installed tool",
856
862
  "one": "Upgrade only Claude Code",
857
- "many": "Upgrade Codex and Antigravity CLI"
863
+ "many": "Upgrade Codex and Antigravity CLI",
864
+ "desktop": "Install Claude Desktop, or upgrade it to the download site's latest build",
865
+ "force": "Reinstall ChatGPT even though it is the latest (when the app is broken)"
858
866
  },
859
867
  "when": "When {{cli}} doctor reports an old version, or the tool's own updater doesn't finish.",
860
868
  "needs": "Without tool names, only installed tools are upgraded",
861
869
  "alt": {
862
870
  "update": "Update {{product}} itself"
871
+ },
872
+ "opt": {
873
+ "force": "Download and reinstall desktop apps (claude-desktop, chatgpt) even when already the latest"
863
874
  }
864
875
  },
865
876
  "refresh": {
@@ -1049,5 +1060,56 @@
1049
1060
  "rejected": "the gateway rejected it; it may have been deleted, disabled or expired",
1050
1061
  "timeout": "the request timed out",
1051
1062
  "network": "couldn't connect to the gateway"
1063
+ },
1064
+ "app_update": {
1065
+ "not_installed": "not installed; the download site has {{latest}}",
1066
+ "outdated": "{{current}} installed, {{latest}} available",
1067
+ "up_to_date": "{{current}} (latest)",
1068
+ "downloading": "Downloading {{tool}} {{version}} from the download site (about {{size}} MB)…",
1069
+ "verifying": "Checking the checksum and {{vendor}}'s signature…",
1070
+ "installing": "Installing…",
1071
+ "already_latest": "{{tool}} is up to date ({{current}})",
1072
+ "latest_unknown": "Couldn't look up the latest {{tool}}: {{reason}}",
1073
+ "running_hint": "{{tool}} is running, so it wasn't upgraded: quit {{tool}}, then run {{command}}",
1074
+ "offer_quit": "Quit {{tool}} and upgrade to {{latest}}?",
1075
+ "declined_hint": "Not upgraded. Run {{command}} later",
1076
+ "quit_failed": "Couldn't quit {{tool}}, so it wasn't upgraded: quit it yourself, then run {{command}}",
1077
+ "upgraded": "Upgraded {{tool}} from {{from}} to {{to}}",
1078
+ "installed": "Installed {{tool}} {{version}}",
1079
+ "upgrade_failed": "Couldn't upgrade {{tool}}: {{reason}}. {{current}} still works; fix the cause, then run {{command}}",
1080
+ "install_failed": "Couldn't install {{tool}}: {{reason}}. Fix the cause, then run {{command}}",
1081
+ "blocked_unsupported_platform": "On this computer, install {{tool}} from {{url}}",
1082
+ "blocked_no_mirror": "Install {{tool}} from {{url}}",
1083
+ "blocked_classic_install": "This computer has the classic installer build of {{tool}}, which {{cli}} doesn't replace: check for updates in {{tool}}, or uninstall it and run {{command}} to get the current build",
1084
+ "reason_unsupported_platform": "there is no installer for this system",
1085
+ "reason_no_mirror": "no download site is configured",
1086
+ "reason_classic_install": "the classic installer build is installed",
1087
+ "error_blocked": "{{reason}}",
1088
+ "error_download": "download failed ({{reason}})",
1089
+ "error_no_checksum": "the download site has no checksum for {{file}}",
1090
+ "error_checksum": "{{file}} has the wrong checksum (expected {{expected}}…, got {{actual}}…); the download was deleted",
1091
+ "error_signature": "the package's signature is wrong or broken ({{reason}}); nothing was installed",
1092
+ "error_mount": "couldn't open the package ({{reason}})",
1093
+ "error_bad_package": "the package has no {{reason}}",
1094
+ "error_no_permission": "no permission to write {{path}}: run as an administrator, or give {{path}} to this user",
1095
+ "error_copy": "copying the app failed ({{reason}})",
1096
+ "error_install": "the system installer failed ({{reason}})",
1097
+ "blocked_unsupported_arch": "{{tool}} is only built for Apple silicon Macs; this Mac can't run it",
1098
+ "reason_unsupported_arch": "there is no installer for this chip",
1099
+ "reason_unknown": "reason unknown",
1100
+ "blocked_other_app": "The ChatGPT.app in Applications is another app (the earlier ChatGPT without Codex), which {{cli}} doesn't replace: remove or rename it, then run {{command}}",
1101
+ "reason_other_app": "another app with the same name is in the way",
1102
+ "blocked_root": "Don't install {{tool}} with sudo: the app would belong to root and its own updates would fail. Run {{command}} as your usual account",
1103
+ "blocked_system_account": "This is the system account (SYSTEM); {{tool}} would be installed for no real user. Sign in as the account that uses it and run {{command}}",
1104
+ "reason_root": "it can't be installed with sudo",
1105
+ "reason_system_account": "this is the system account (SYSTEM)",
1106
+ "error_app_management": "macOS doesn't let this terminal replace {{path}}: allow it under System Settings → Privacy & Security → App Management, or check for updates in the app",
1107
+ "upgrade_offer": "Upgrade {{tool}} from {{current}} to {{latest}}?",
1108
+ "upgrade_hint": "{{tool}} is at {{current}}, {{latest}} is available: run {{command}}",
1109
+ "reinstalling": "Reinstalling {{tool}} {{latest}} (--force; current {{current}})",
1110
+ "reinstalled": "Reinstalled {{tool}} {{version}}",
1111
+ "force_hint": "To reinstall it, run {{command}} --force",
1112
+ "blocked_remote_session": "This is a remote (SSH) session, where Windows doesn't allow installing {{tool}}: run {{command}} in a terminal on this computer",
1113
+ "reason_remote_session": "Windows apps can't be installed from a remote (SSH) session"
1052
1114
  }
1053
1115
  }
@@ -119,7 +119,9 @@
119
119
  "action_force_config": "重置为推荐配置:删除 {{tool}} 原有配置文件后整份重写,用于修复改坏的配置",
120
120
  "menu_base_url": "切换接入地址(当前:{{url}})",
121
121
  "base_testing": "正在测试各个接入地址…",
122
- "base_select": "选择接入地址(已配置的工具会一起切换)"
122
+ "base_select": "选择接入地址(已配置的工具会一起切换)",
123
+ "action_upgrade_app": "升级 {{tool}}(当前 {{current}},可升到 {{latest}})",
124
+ "action_install_app": "安装 {{tool}} {{latest}}"
123
125
  },
124
126
  "doctor": {
125
127
  "checking": "正在检查…",
@@ -169,7 +171,9 @@
169
171
  "report_uploading": "正在上传诊断信息…",
170
172
  "report_done": "诊断码:{{code}}(把它发给客服,客服就能看到这次检查的结果和最近的运行记录)",
171
173
  "report_failed": "无法上传诊断信息。可以把上面的结果截图发给客服,网络恢复后再运行 {{cli}} doctor --report",
172
- "report_hint": "需要客服帮助时,运行 {{cli}} doctor --report,把得到的诊断码发给客服"
174
+ "report_hint": "需要客服帮助时,运行 {{cli}} doctor --report,把得到的诊断码发给客服",
175
+ "app_version": "{{tool}} 版本",
176
+ "run_app_upgrade": "运行 {{command}}"
173
177
  },
174
178
  "status": {
175
179
  "title": "当前状态",
@@ -361,7 +365,9 @@
361
365
  "list_separator": "、",
362
366
  "gui_preconfigured": "{{tool}} 还没有安装,已提前写好配置,安装后打开即可使用(下载:{{install}})",
363
367
  "validate_failed": "无法验证 {{label}} 的令牌:{{reason}}",
364
- "skip_no_claude_desktop": "这个令牌没有开通 Claude 模型分组,已跳过 Claude Code 和 Claude Desktop"
368
+ "skip_no_claude_desktop": "这个令牌没有开通 Claude 模型分组,已跳过 Claude Code 和 Claude Desktop",
369
+ "install_app_confirm": "安装 {{tools}}?(从高速下载站下载约 {{size}} MB)",
370
+ "install_app_hint": "没装 {{tool}}:要安装就运行 {{command}}(已写好配置,装好打开就能用)"
365
371
  },
366
372
  "tools": {
367
373
  "version_outdated": "版本 {{current}} 过旧,需要 {{min}} 或更高版本,运行 {{upgrade}} 升级",
@@ -846,20 +852,25 @@
846
852
  }
847
853
  },
848
854
  "upgrade": {
849
- "summary": "升级 Claude Code、Codex 等编码工具",
850
- "description": "按每份工具当初的安装方式升级:原生安装用工具自带的更新,npm 安装从国内镜像(npmmirror)重新安装,Homebrew 安装用 brew upgrade;原生或 Homebrew 方式无法升级时,改用 {{brand}} 一键安装脚本(程序取自国内镜像)。指定了但还没有安装的工具,也用这个脚本安装。\n不填工具名则升级已安装的全部工具。\n升级 {{product}} 本身请用 {{cli}} update。",
855
+ "summary": "升级 Claude Code、Codex、Claude Desktop、ChatGPT 等编码工具",
856
+ "description": "按每份工具当初的安装方式升级:原生安装用工具自带的更新,npm 安装从国内镜像(npmmirror)重新安装,Homebrew 安装用 brew upgrade;原生或 Homebrew 方式无法升级时,改用 {{brand}} 一键安装脚本(程序取自国内镜像)。指定了但还没有安装的工具,也用这个脚本安装。\nClaude Desktop 和 ChatGPT(内含 Codex;macOS / Windows)比高速下载站上的版本旧时,从高速下载站下载官方安装包,核对校验值和厂商(Anthropic / OpenAI)的签名后安装;它们自带的自动更新照常开着,{{cli}} 只在没跟上时补上。应用正在运行时,在终端里问过才关掉它升级,带 --yes 或没有终端时只提示、不关。已是最新时加 --force 重新安装。ChatGPT 的 Mac 版只支持 Apple 芯片。\n不填工具名则升级已安装的全部工具。\n升级 {{product}} 本身请用 {{cli}} update。",
851
857
  "arg": {
852
- "tools": "claude-code、codex、grok、agy,可以填写多个"
858
+ "tools": "claude-code、codex、grok、agy、claude-desktop、chatgpt,可以填写多个"
853
859
  },
854
860
  "ex": {
855
861
  "all": "升级已安装的全部工具",
856
862
  "one": "只升级 Claude Code",
857
- "many": "升级 Codex 和 Antigravity CLI"
863
+ "many": "升级 Codex 和 Antigravity CLI",
864
+ "desktop": "安装 Claude Desktop,或升到高速下载站上的最新版",
865
+ "force": "ChatGPT 已是最新也重新安装一遍(应用损坏时)"
858
866
  },
859
867
  "when": "{{cli}} doctor 提示版本过低,或工具自带的更新无法完成时。",
860
868
  "needs": "不填工具名时,只升级已经安装的工具",
861
869
  "alt": {
862
870
  "update": "升级 {{product}} 本身"
871
+ },
872
+ "opt": {
873
+ "force": "桌面应用(claude-desktop、chatgpt)已是最新也重新下载安装"
863
874
  }
864
875
  },
865
876
  "refresh": {
@@ -1049,5 +1060,56 @@
1049
1060
  "rejected": "网关拒绝了这个令牌,它可能已被删除、停用或已过期",
1050
1061
  "timeout": "请求超时",
1051
1062
  "network": "无法连接网关"
1063
+ },
1064
+ "app_update": {
1065
+ "not_installed": "没有安装,高速下载站上最新是 {{latest}}",
1066
+ "outdated": "当前 {{current}},可升到 {{latest}}",
1067
+ "up_to_date": "{{current}}(已是最新)",
1068
+ "downloading": "正在从高速下载站下载 {{tool}} {{version}}(约 {{size}} MB)…",
1069
+ "verifying": "正在核对校验值和 {{vendor}} 的签名…",
1070
+ "installing": "正在安装…",
1071
+ "already_latest": "{{tool}} 已是最新({{current}})",
1072
+ "latest_unknown": "无法查到 {{tool}} 的最新版本:{{reason}}",
1073
+ "running_hint": "{{tool}} 正在运行,这次没有升级:退出 {{tool}} 后运行 {{command}}",
1074
+ "offer_quit": "关掉 {{tool}} 并升到 {{latest}}?",
1075
+ "declined_hint": "没有升级。之后可以运行 {{command}}",
1076
+ "quit_failed": "无法退出 {{tool}},这次没有升级:手动退出后运行 {{command}}",
1077
+ "upgraded": "已把 {{tool}} 从 {{from}} 升到 {{to}}",
1078
+ "installed": "已安装 {{tool}} {{version}}",
1079
+ "upgrade_failed": "无法升级 {{tool}}:{{reason}}。当前的 {{current}} 照常可用;排除原因后运行 {{command}}",
1080
+ "install_failed": "无法安装 {{tool}}:{{reason}}。排除原因后运行 {{command}}",
1081
+ "blocked_unsupported_platform": "这台电脑的 {{tool}} 要从官网下载安装:{{url}}",
1082
+ "blocked_no_mirror": "{{tool}} 要从官网下载安装:{{url}}",
1083
+ "blocked_classic_install": "这台电脑装的是旧式安装版 {{tool}},{{cli}} 不替换它:在 {{tool}} 里检查更新,或卸载后运行 {{command}} 装新版",
1084
+ "reason_unsupported_platform": "这个系统没有可用的安装包",
1085
+ "reason_no_mirror": "没有配置高速下载站",
1086
+ "reason_classic_install": "装的是旧式安装版",
1087
+ "error_blocked": "{{reason}}",
1088
+ "error_download": "下载失败({{reason}})",
1089
+ "error_no_checksum": "高速下载站上没有 {{file}} 的校验值",
1090
+ "error_checksum": "{{file}} 的校验值不对(应为 {{expected}}…,实际 {{actual}}…),下载的文件已删除",
1091
+ "error_signature": "安装包的签名不对或已损坏({{reason}}),没有安装",
1092
+ "error_mount": "无法打开安装包({{reason}})",
1093
+ "error_bad_package": "安装包里缺少 {{reason}}",
1094
+ "error_no_permission": "没有权限写入 {{path}}:用管理员账号运行,或把 {{path}} 交给当前用户",
1095
+ "error_copy": "复制应用失败({{reason}})",
1096
+ "error_install": "系统安装失败({{reason}})",
1097
+ "blocked_unsupported_arch": "{{tool}} 只有 Apple 芯片的 Mac 版,这台 Mac 装不了",
1098
+ "reason_unsupported_arch": "官方没有这种芯片的安装包",
1099
+ "reason_unknown": "原因不明",
1100
+ "blocked_other_app": "“应用程序”里的 ChatGPT.app 是另一个应用(不含 Codex 的旧版 ChatGPT),{{cli}} 不替换它:删掉或改名后运行 {{command}}",
1101
+ "reason_other_app": "同名的另一个应用已经在那里",
1102
+ "blocked_root": "不要用 sudo 安装 {{tool}}:装出来的应用归 root 所有,它自己的更新会失败。用平常的账号运行 {{command}}",
1103
+ "blocked_system_account": "当前是系统账号(SYSTEM),{{tool}} 会装给不存在的用户:登录要用它的账号后运行 {{command}}",
1104
+ "reason_root": "不能用 sudo 安装",
1105
+ "reason_system_account": "当前是系统账号(SYSTEM)",
1106
+ "error_app_management": "macOS 不允许这个终端替换 {{path}}:在「系统设置 → 隐私与安全性 → App 管理」里打开当前终端,或在应用里检查更新",
1107
+ "upgrade_offer": "把 {{tool}} 从 {{current}} 升到 {{latest}}?",
1108
+ "upgrade_hint": "{{tool}} 当前 {{current}},可升到 {{latest}}:运行 {{command}}",
1109
+ "reinstalling": "按 --force 重新安装 {{tool}} {{latest}}(当前 {{current}})",
1110
+ "reinstalled": "已重新安装 {{tool}} {{version}}",
1111
+ "force_hint": "要重新安装就运行 {{command}} --force",
1112
+ "blocked_remote_session": "这是远程(SSH)登录的会话,Windows 不允许在这里安装 {{tool}}:在这台电脑本机的终端里运行 {{command}}",
1113
+ "reason_remote_session": "远程(SSH)会话里不能安装 Windows 应用"
1052
1114
  }
1053
1115
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xship/cli",
3
- "version": "0.0.146",
3
+ "version": "0.0.148",
4
4
  "description": "Configure Claude Code, Codex, Grok Build and other coding tools for Xship AI with one command",
5
5
  "main": "dist/index.js",
6
6
  "bin": {