@marsapi/cli 0.0.148 → 0.0.153

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{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();
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 O}from"../commands/base.js";import{readFileSync as P}from"fs";import{dirname as W,join as j}from"path";import{fileURLToPath as H}from"url";import{brand as L}from"./brand.js";const $=W(H(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(P(j($,"..","..","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 O(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,takeOver:!0}),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();
@@ -1 +1 @@
1
- import o from"chalk";import t from"inquirer";import{getAdapter as r}from"./tools/registry.js";import{SUPPORTED_TOOLS as n}from"./tool-manager.js";import{i18n as e}from"./i18n.js";import{isColorEnabled as l,isDryRun as i,isJsonMode as s,isNonInteractive as a,shouldAutoConfirm as c}from"./runtime-options.js";export function toolWritePlan(o){return o.map(o=>{const t=r(o);return{tool:o,files:t?.backupPaths?.()??(t?[t.configPath]:[])}})}export function printWritePlan(t,r){const i=(l()?o.cyan:o=>o)(r.label),s=l()?o.gray:o=>o;for(const{tool:o,files:r}of t)console.log(`${i} ${e.t("setup.dry_run_tool",{tool:n[o]?.displayName??o})}`),r.forEach(o=>console.log(s(` ${o}`)));for(const o of r.installFirst??[])console.log(`${i} ${e.t("plan.install_first",{tool:n[o]?.displayName??o})}`);r.keyFile&&console.log(`${i} ${e.t("setup.dry_run_key",{file:r.keyFile})}`)}export function asksBeforeWriting(){return Boolean(process.stdin.isTTY&&process.stdout.isTTY)&&!c()&&!a()&&!s()&&!i()}export async function confirmWritePlan(o,r={}){if(!asksBeforeWriting())return!0;if(0===o.length&&!r.keyFile&&!r.installFirst?.length)return!0;console.log("\n"+e.t("plan.title")),printWritePlan(toolWritePlan(o),{label:" •",...r});const{go:n}=await t.prompt([{type:"confirm",name:"go",message:e.t("plan.confirm"),default:!0}]);return n||console.log(e.t("plan.cancelled")),n}
1
+ import o from"chalk";import t from"inquirer";import{getAdapter as n}from"./tools/registry.js";import{SUPPORTED_TOOLS as e}from"./tool-manager.js";import{i18n as r}from"./i18n.js";import{isColorEnabled as l,isDryRun as i,isJsonMode as s,isNonInteractive as a,shouldAutoConfirm as c}from"./runtime-options.js";export function toolWritePlan(o){return o.map(o=>{const t=n(o);let e=[];try{e=t?.planNotes?.()??[]}catch{e=[]}return{tool:o,files:t?.backupPaths?.()??(t?[t.configPath]:[]),notes:e}})}export function printWritePlan(t,n){const i=(l()?o.cyan:o=>o)(n.label),s=l()?o.gray:o=>o,a=l()?o.yellow:o=>o;for(const{tool:o,files:n,notes:l}of t)console.log(`${i} ${r.t("setup.dry_run_tool",{tool:e[o]?.displayName??o})}`),n.forEach(o=>console.log(s(` ${o}`))),l.forEach(o=>console.log(a(` ! ${o}`)));for(const o of n.installFirst??[])console.log(`${i} ${r.t("plan.install_first",{tool:e[o]?.displayName??o})}`);n.keyFile&&console.log(`${i} ${r.t("setup.dry_run_key",{file:n.keyFile})}`)}export function asksBeforeWriting(){return Boolean(process.stdin.isTTY&&process.stdout.isTTY)&&!c()&&!a()&&!s()&&!i()}export async function confirmWritePlan(o,n={}){if(!asksBeforeWriting())return!0;if(0===o.length&&!n.keyFile&&!n.installFirst?.length)return!0;console.log("\n"+r.t("plan.title")),printWritePlan(toolWritePlan(o),{label:" •",...n});const{go:e}=await t.prompt([{type:"confirm",name:"go",message:r.t("plan.confirm"),default:!0}]);return e||console.log(r.t("plan.cancelled")),e}
@@ -0,0 +1 @@
1
+ import{mkdirSync as e,readFileSync as t,writeFileSync as n}from"fs";import{join as r}from"path";import{configManager as i}from"./config.js";import{logger as o}from"../utils/logger.js";function s(){return r(i.getConfigDir(),"written-values.json")}function l(){try{const e=JSON.parse(t(s(),"utf-8"));return e.values&&"object"==typeof e.values?e.values:{}}catch{return{}}}function u(t){try{e(i.getConfigDir(),{recursive:!0}),n(s(),JSON.stringify({version:1,values:t},null,2)+"\n",{mode:384})}catch(e){o.logError("writtenValues.save",e)}}export function ownsValue(e,t,n=[]){if("string"!=typeof t)return!1;const r=l()[e];return r?r.written===t:n.includes(t)}export function claimField(e,t,n,r={}){const i=l(),o=i[e];let s;return s=null==t||""===t||t===n||"string"==typeof t&&(o?o.written===t||Boolean(r.explicit)&&o.desired!==n:Boolean(r.explicit)||(r.legacy??[]).includes(t)),i[e]=s?{written:n,desired:n}:{desired:n},u(i),s}export function forgetField(e){const t=l();e in t&&(delete t[e],u(t))}const c="gateway_urls";export function noteGatewayUrl(e){const t=l(),n=(t[c]?.desired??"").split("\n").filter(Boolean);n.includes(e)||(t[c]={desired:[...n,e].join("\n")},u(t))}export function wroteGatewayUrl(e){return(l()[c]?.desired??"").split("\n").includes(e)}
@@ -4,6 +4,7 @@
4
4
  "unknown_hint": "Next: run {{cli}} --help to see the available commands.",
5
5
  "unknown_hint_suggestion": "Did you mean {{cli}} {{command}}?",
6
6
  "err_unknown_option": "Unknown option: {{name}}",
7
+ "err_glued_option": "Unknown option: {{name}}. Put a space between {{option}} and its value",
7
8
  "err_missing_argument": "Missing argument: {{name}}",
8
9
  "err_missing_option": "Missing required option: {{name}}",
9
10
  "unknown_option_hint": "Next: run {{cli}} {{command}} --help to see the correct usage.",
@@ -380,6 +381,10 @@
380
381
  "list_separator": ", "
381
382
  },
382
383
  "codex": {
384
+ "kept_user_settings": "Kept what you set yourself: {{values}}",
385
+ "plan_replace_provider": "Replace model_providers.custom (now pointing at {{url}}, not written by {{product}})",
386
+ "plan_switch_provider": "Codex now uses {{provider}}; it will switch to {{brand}}",
387
+ "provider_replaced": "Replaced the previous custom provider {{url}}; the old file is backed up",
383
388
  "sessions_offer": "Move {{count}} earlier Codex sessions over? They're hidden from the list after switching to {{brand}}. They're backed up first and this can be undone.",
384
389
  "sessions_hidden": "{{count}} earlier Codex sessions are hidden from the list after switching to {{brand}}: run {{cli}} codex sessions to move them over (backed up first, can be undone)",
385
390
  "sessions_hidden_running": "{{count}} earlier Codex sessions are hidden from the list after switching to {{brand}}: quit Codex completely (desktop app included), then run {{cli}} codex sessions",
@@ -393,6 +398,12 @@
393
398
  "unload_other_key": "Codex is still signed in with another API key (not the {{brand}} API key, left as is). To use your ChatGPT subscription, run codex logout, then codex login"
394
399
  },
395
400
  "claude_desktop": {
401
+ "current_official": "the official sign-in",
402
+ "current_entry": "the \"{{name}}\" configuration",
403
+ "plan_switch": "Claude Desktop now uses {{current}}; it will switch to {{brand}}",
404
+ "not_switched": "Claude Desktop uses {{current}}: the {{brand}} configuration is saved but not switched on. To switch, run {{cli}} config claude-desktop --yes",
405
+ "foreign_policy": "The registry key {{key}} holds another service's settings ({{url}}); Claude Desktop uses those and ignores what {{product}} writes. {{product}} left them alone; ask your administrator if needed",
406
+ "plan_foreign_policy": "The registry key {{key}} holds another service's settings ({{url}}); they are kept and Claude Desktop keeps using them",
396
407
  "code_cli_downloading": "Downloading Claude Code {{version}} for Claude Desktop's Code tab from the China mirror (about 100 MB)…",
397
408
  "code_cli_installed": "Claude Desktop's Code tab has Claude Code {{version}} and no longer needs downloads.claude.ai",
398
409
  "code_cli_failed": "Couldn't prepare Claude Code for Claude Desktop's Code tab ({{reason}}); the tab may say it can't download it. Fix the cause above, then run {{cli}} setup --desktopkey <your API key>",
@@ -414,7 +425,16 @@
414
425
  "registry_removed": "Removed the old values under {{key}}; Claude Desktop now reads the local config"
415
426
  },
416
427
  "claude_code": {
428
+ "plan_env_remove": "Remove {{vars}} pointing at this site from {{location}} (backed up first)",
429
+ "env_removed": "Removed {{vars}} pointing at this site from {{location}}; new terminals pick this up (backed up)",
430
+ "env_foreign": "Found the environment variables {{vars}} in {{location}}; they override Claude Code's settings. To use {{brand}} reliably, remove them and open a new terminal",
431
+ "env_foreign_found": "Found the environment variables {{vars}} in {{location}}; they override Claude Code's settings",
432
+ "env_foreign_offer": "Found the environment variables {{where}}; they override Claude Code's settings, and {{brand}} only works reliably without them. Remove them (backed up first)?",
433
+ "env_removed_foreign": "Removed {{vars}} from {{location}} (backed up); open a new terminal for it to take effect",
434
+ "plan_replace_provider": "Replace Claude Code's current endpoint {{url}} (not written by {{product}})",
435
+ "provider_replaced": "Replaced the previous endpoint {{url}}; the old file is backed up",
417
436
  "gateway_models": "{{count}} more models added to /model ({{vendors}})",
437
+ "kept_user_models": "Kept the models you set yourself: {{values}}",
418
438
  "tier_fallback": "This API key can't use Claude Code's default {{from}}; the {{tier}} alias now uses {{to}}",
419
439
  "stable_channel": "Your update channel in settings.json is stable, which may not have reached {{min}} yet; set autoUpdatesChannel to latest, then upgrade",
420
440
  "managed_override": "Claude Code managed settings found ({{source}}); {{keys}} there override what {{product}} writes, so this config won't take effect. Managed settings usually come from your company's IT; ask your administrator",
@@ -462,6 +482,83 @@
462
482
  "native_mirror_failed": "Couldn't replace from the mirror: {{reason}}",
463
483
  "install_label": "{{kind}} {{version}} ({{path}})"
464
484
  },
485
+ "components": {
486
+ "category": {
487
+ "runtime": "Runtimes",
488
+ "cli": "Command-line tools",
489
+ "app": "Desktop apps"
490
+ },
491
+ "col": {
492
+ "name": "Component",
493
+ "current": "Installed",
494
+ "latest": "Latest",
495
+ "managed": "Installed by",
496
+ "state": "State"
497
+ },
498
+ "by": {
499
+ "fnm": "fnm",
500
+ "brew": "Homebrew",
501
+ "nvm": "nvm",
502
+ "volta": "Volta",
503
+ "scoop": "Scoop",
504
+ "asdf": "asdf",
505
+ "mise": "mise",
506
+ "system": "system",
507
+ "standalone": "standalone",
508
+ "pipx": "pipx",
509
+ "pip": "pip",
510
+ "winget": "winget",
511
+ "git-for-windows-user": "Git for Windows (this user)",
512
+ "git-for-windows": "Git for Windows",
513
+ "xcode": "Apple Command Line Tools",
514
+ "native": "native",
515
+ "npm": "npm",
516
+ "app": "app",
517
+ "other": "other"
518
+ },
519
+ "state": {
520
+ "unsupported": "not available on this system",
521
+ "missing": "not installed: {{command}}",
522
+ "outdated_auto": "upgrade available: {{command}}",
523
+ "outdated_manual": "upgrade available: {{how}}",
524
+ "latest": "up to date",
525
+ "unknown_latest": "installed"
526
+ },
527
+ "source": "Source: {{source}}",
528
+ "latest_unknown": "Couldn't check the latest version of: {{tools}} (try {{cli}} list --refresh later)",
529
+ "unknown": "Unknown component: {{names}}",
530
+ "known": "Components: {{ids}}",
531
+ "unsupported_platform": "{{tool}} isn't available on this system",
532
+ "blocked_sudo": "Don't install {{tool}} with sudo: it would go into root's home directory and belong to root. Run it again without sudo",
533
+ "blocked_system": "This is the SYSTEM account; {{tool}} would go into the system profile. Run it again signed in as the person who uses it",
534
+ "blocked_unsupported_platform": "{{tool}} isn't available on this system",
535
+ "reason_sudo": "not with sudo",
536
+ "reason_system": "SYSTEM account",
537
+ "reason_unsupported_platform": "not available on this system",
538
+ "no_mirror": "Can't install {{tool}}: no install script location is configured",
539
+ "already_installed": "{{tool}} {{current}} is installed",
540
+ "up_to_date": "{{tool}} {{current}} is up to date",
541
+ "force_hint": "To reinstall it, run {{command}}",
542
+ "outdated_auto": "{{tool}} {{current}} can be upgraded to {{latest}}: run {{command}}",
543
+ "outdated_manual": "{{tool}} {{current}} can be upgraded to {{latest}}. It was installed with {{by}}, which {{cli}} leaves alone: run {{how}}; to add a copy {{cli}} manages, run {{command}}",
544
+ "no_latest": "{{tool}} {{current}} is managed by the system, {{cli}} doesn't upgrade it: {{how}}",
545
+ "how_system": "upgrade it the way it was installed (system package manager or the official installer), or add --force for a copy {{cli}} manages",
546
+ "how_xcode": "update the Command Line Tools in System Settings → General → Software Update",
547
+ "how_other": "upgrade it the way it was installed",
548
+ "force_beside": "{{tool}} is already at {{path}} (installed with {{by}}); --force adds a copy {{cli}} manages",
549
+ "installing": "Installing {{tool}} {{latest}} ({{source}})…",
550
+ "upgrading": "Upgrading {{tool}} to {{latest}} ({{source}})…",
551
+ "script_failed": "{{tool}} wasn't installed; see above. Then run {{command}}",
552
+ "installed": "Installed {{tool}} {{version}}",
553
+ "upgraded": "Upgraded {{tool}} from {{from}} to {{to}}",
554
+ "reinstalled": "Reinstalled {{tool}} {{version}}",
555
+ "reinstalling": "Reinstalling {{tool}} (--force; now {{current}})",
556
+ "path_shadowed": "Your terminal finds {{active}} first, not the {{tool}} {{version}} just installed ({{path}}): put the latter's directory earlier on PATH, or remove the former",
557
+ "capped": "{{tool}} {{latest}} doesn't run on this computer; the install script put in the newest one that does ({{version}}). Upgrades are offered again once a newer version is out",
558
+ "new_terminal": "Open a new terminal window to use it",
559
+ "migrating_globals": "The new Node doesn't have the packages installed globally before; installing the same versions: {{packages}}",
560
+ "migrate_failed": "Not all global packages made it to the new Node; run: {{command}}"
561
+ },
465
562
  "install": {
466
563
  "permission_unix": "npm probably cannot write its global directory. Either ① sudo {{command}}; or ② run npm config set prefix ~/.npm-global, add export PATH=~/.npm-global/bin:$PATH to ~/.zshrc or ~/.bashrc, open a new terminal and run {{command}}",
467
564
  "permission_windows": "Probably missing permissions or a file in use: close the tool if it is running, open an elevated terminal and run {{command}}",
@@ -851,18 +948,58 @@
851
948
  "doctor": "Check the common problems first"
852
949
  }
853
950
  },
951
+ "list": {
952
+ "summary": "List the runtimes, command-line tools and desktop apps {{cli}} can install, and which versions are installed",
953
+ "description": "Lists Node.js, Git, uv, Claude Code, Codex, Grok Build, Antigravity CLI, Claude Desktop and ChatGPT: installed or not, the installed and the latest version, how it was installed and where it comes from. Latest versions are cached for an hour; --refresh checks again now.",
954
+ "when": "To see which tools this computer has and which can be upgraded.",
955
+ "alt": {
956
+ "doctor": "Check the API key and each tool's config"
957
+ },
958
+ "opt": {
959
+ "refresh": "Check the latest versions again instead of using the cache"
960
+ },
961
+ "ex": {
962
+ "all": "List every component",
963
+ "refresh": "Check the latest versions again",
964
+ "json": "JSON output for scripts"
965
+ }
966
+ },
967
+ "install": {
968
+ "summary": "Install runtimes (Node.js, Git, uv), command-line tools or desktop apps",
969
+ "description": "Installs missing components from China-reachable mirrors, checking checksums (and, for desktop apps, the vendor's signature). One that is already installed is left as it is: its version is shown, with the upgrade command when it is behind; --force reinstalls it.\nNode.js, Git and uv are installed into your own user directory by {{brand}}'s install scripts, no administrator needed; don't use sudo. Several components can be given; what they depend on is installed first.",
970
+ "arg": {
971
+ "components": "node, git, uv, claude-code, codex, grok, agy, claude-desktop, chatgpt; several allowed"
972
+ },
973
+ "opt": {
974
+ "force": "Reinstall even when it is already installed"
975
+ },
976
+ "when": "To add a tool to this computer; other apps' install scripts can call it too (npx {{package}} install …).",
977
+ "needs": "Node.js on this computer ({{cli}} itself runs on Node); without Node, use the one-line installer first",
978
+ "alt": {
979
+ "upgrade": "Upgrade components that are installed",
980
+ "setup": "Then save your API key and configure the tools"
981
+ },
982
+ "ex": {
983
+ "runtimes": "Install Node.js and uv",
984
+ "clis": "Install Claude Code and Codex",
985
+ "app": "Install the ChatGPT desktop app (contains Codex)",
986
+ "force": "Reinstall uv",
987
+ "json": "Install from a script, JSON output"
988
+ }
989
+ },
854
990
  "upgrade": {
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.",
991
+ "summary": "Upgrade coding tools, desktop apps and runtimes (Node.js, Git, uv)",
992
+ "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.\nNode.js, Git and uv are only upgraded where {{brand}}'s install scripts manage them (Node under fnm, uv in ~/.local/bin, Git for Windows installed for the current user); a copy from Homebrew, nvm, a system package manager … is left alone and the command that upgrades it is shown; --force adds a copy {{cli}} manages. After fnm's Node is upgraded, the npm packages installed globally before are installed into the new one at the same versions.\nWith nothing named, upgrades every installed coding tool and desktop app, and points out runtimes that are behind.\nTo update {{product}} itself, use {{cli}} update.",
857
993
  "arg": {
858
- "tools": "claude-code, codex, grok, agy, claude-desktop, chatgpt; one or more"
994
+ "tools": "claude-code, codex, grok, agy, claude-desktop, chatgpt, node, git, uv; one or more"
859
995
  },
860
996
  "ex": {
861
997
  "all": "Upgrade every installed tool",
862
998
  "one": "Upgrade only Claude Code",
863
999
  "many": "Upgrade Codex and Antigravity CLI",
864
1000
  "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)"
1001
+ "force": "Reinstall ChatGPT even though it is the latest (when the app is broken)",
1002
+ "runtime": "Upgrade Node.js (fnm-managed; otherwise the command that upgrades it)"
866
1003
  },
867
1004
  "when": "When {{cli}} doctor reports an old version, or the tool's own updater doesn't finish.",
868
1005
  "needs": "Without tool names, only installed tools are upgraded",
@@ -870,7 +1007,7 @@
870
1007
  "update": "Update {{product}} itself"
871
1008
  },
872
1009
  "opt": {
873
- "force": "Download and reinstall desktop apps (claude-desktop, chatgpt) even when already the latest"
1010
+ "force": "Reinstall desktop apps and runtimes that are already the latest; for a runtime installed another way, add a copy {{cli}} manages"
874
1011
  }
875
1012
  },
876
1013
  "refresh": {
@@ -1074,6 +1211,8 @@
1074
1211
  "offer_quit": "Quit {{tool}} and upgrade to {{latest}}?",
1075
1212
  "declined_hint": "Not upgraded. Run {{command}} later",
1076
1213
  "quit_failed": "Couldn't quit {{tool}}, so it wasn't upgraded: quit it yourself, then run {{command}}",
1214
+ "quit_confirm_in_app": "{{tool}} didn't quit: it may have a task running or be asking you to confirm. Switch to {{tool}} and confirm the quit there (or quit it once the task is done); the upgrade goes on here (waiting up to {{seconds}} s)…",
1215
+ "quit_refused": "{{tool}} didn't quit, so it wasn't upgraded: it has a task running or is waiting for you to confirm. Confirm the quit in {{tool}} (or quit it once the task is done), then run {{command}}",
1077
1216
  "upgraded": "Upgraded {{tool}} from {{from}} to {{to}}",
1078
1217
  "installed": "Installed {{tool}} {{version}}",
1079
1218
  "upgrade_failed": "Couldn't upgrade {{tool}}: {{reason}}. {{current}} still works; fix the cause, then run {{command}}",
@@ -1110,6 +1249,10 @@
1110
1249
  "reinstalled": "Reinstalled {{tool}} {{version}}",
1111
1250
  "force_hint": "To reinstall it, run {{command}} --force",
1112
1251
  "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"
1252
+ "reason_remote_session": "Windows apps can't be installed from a remote (SSH) session",
1253
+ "error_needs_admin": "Windows requires administrator rights for this install (0x80073D28); right-click PowerShell, choose \"Run as administrator\" and run it there",
1254
+ "error_other_account": "the prompt was approved with another administrator account, so the app was installed for that account: approve with the current account (make it an administrator), or use the app from that account",
1255
+ "elevate_offer": "Windows requires administrator rights to install {{tool}}. Show the permission prompt now?",
1256
+ "elevating": "Requesting administrator rights: click Yes in the Windows prompt…"
1114
1257
  }
1115
1258
  }
@@ -4,6 +4,7 @@
4
4
  "unknown_hint": "下一步:运行 {{cli}} --help 查看可用命令。",
5
5
  "unknown_hint_suggestion": "你是不是想运行 {{cli}} {{command}}?",
6
6
  "err_unknown_option": "未知选项:{{name}}",
7
+ "err_glued_option": "未知选项:{{name}}。{{option}} 和后面的值之间要有空格",
7
8
  "err_missing_argument": "缺少参数:{{name}}",
8
9
  "err_missing_option": "缺少必填选项:{{name}}",
9
10
  "unknown_option_hint": "下一步:运行 {{cli}} {{command}} --help 查看正确用法。",
@@ -380,6 +381,10 @@
380
381
  "list_separator": "、"
381
382
  },
382
383
  "codex": {
384
+ "kept_user_settings": "已保留你自己设的:{{values}}",
385
+ "plan_replace_provider": "替换 model_providers.custom(现在指向 {{url}},不是 {{product}} 写的)",
386
+ "plan_switch_provider": "Codex 现在用的是 {{provider}},将改用 {{brand}}",
387
+ "provider_replaced": "已替换原来的 custom 提供方 {{url}},修改前的文件已备份",
383
388
  "sessions_offer": "把 {{count}} 个以前的 Codex 会话移过来?切换到 {{brand}} 后它们不在会话列表里显示。会先备份,可以撤销。",
384
389
  "sessions_hidden": "有 {{count}} 个以前的 Codex 会话在切换到 {{brand}} 后不在列表里显示。运行 {{cli}} codex sessions 可以把它们移过来(会先备份,可以撤销)",
385
390
  "sessions_hidden_running": "有 {{count}} 个以前的 Codex 会话在切换到 {{brand}} 后不在列表里显示。完全退出 Codex(包括桌面版)后,运行 {{cli}} codex sessions 把它们移过来",
@@ -393,6 +398,12 @@
393
398
  "unload_other_key": "Codex 仍在用另一个令牌登录(不是 {{brand}} 的令牌,没有改动)。要改用 ChatGPT 订阅:运行 codex logout,再运行 codex login"
394
399
  },
395
400
  "claude_desktop": {
401
+ "current_official": "官方账号登录",
402
+ "current_entry": "配置「{{name}}」",
403
+ "plan_switch": "Claude Desktop 现在用的是{{current}},将切换到 {{brand}}",
404
+ "not_switched": "Claude Desktop 现在用的是{{current}},已写好 {{brand}} 的配置但没有切换;要切换,运行 {{cli}} config claude-desktop --yes",
405
+ "foreign_policy": "注册表 {{key}} 里是别的服务的配置({{url}}),Claude Desktop 会用它、不读 {{product}} 写的配置;{{product}} 没有改它,需要时请联系管理员",
406
+ "plan_foreign_policy": "注册表 {{key}} 里是别的服务的配置({{url}}),不会修改,Claude Desktop 会继续用它",
396
407
  "code_cli_downloading": "正在从国内镜像下载 Claude Code {{version}},供 Claude Desktop 的 Code 标签页使用(约 100 MB)…",
397
408
  "code_cli_installed": "Claude Desktop 的 Code 标签页已准备好 Claude Code {{version}},不必再从 downloads.claude.ai 下载",
398
409
  "code_cli_failed": "无法为 Claude Desktop 的 Code 标签页准备 Claude Code({{reason}}),Code 标签页可能提示无法下载。排除上面的原因后,运行 {{cli}} setup --desktopkey <你的令牌> 重试",
@@ -414,7 +425,16 @@
414
425
  "registry_removed": "已删除注册表 {{key}} 里的旧配置,Claude Desktop 改为读取本地配置"
415
426
  },
416
427
  "claude_code": {
428
+ "plan_env_remove": "删除 {{location}} 里指向本站的 {{vars}}(先备份)",
429
+ "env_removed": "已删除 {{location}} 里指向本站的 {{vars}},新开的终端生效(已备份)",
430
+ "env_foreign": "检测到 {{location}} 里已有环境变量 {{vars}},它会盖过 Claude Code 的配置;要稳定使用 {{brand}},请删掉后重新打开终端",
431
+ "env_foreign_found": "检测到 {{location}} 里已有环境变量 {{vars}},它会盖过 Claude Code 的配置",
432
+ "env_foreign_offer": "检测到环境变量 {{where}},它会盖过 Claude Code 的配置,删掉后才能稳定使用 {{brand}}。删除这些环境变量(会先备份)?",
433
+ "env_removed_foreign": "已删除 {{location}} 里的 {{vars}}(已备份),重新打开终端后生效",
434
+ "plan_replace_provider": "替换 Claude Code 现在的接入地址 {{url}}(不是 {{product}} 写的)",
435
+ "provider_replaced": "已替换原来的接入地址 {{url}},修改前的文件已备份",
417
436
  "gateway_models": "/model 菜单新增 {{count}} 个其他模型({{vendors}})",
437
+ "kept_user_models": "已保留你自己设的模型:{{values}}",
418
438
  "tier_fallback": "当前令牌无法使用 Claude Code 默认的 {{from}},{{tier}} 档已改用 {{to}}",
419
439
  "stable_channel": "settings.json 里的更新渠道是 stable,这个渠道可能还没有更新到 {{min}}。请把 autoUpdatesChannel 改成 latest 后再升级",
420
440
  "managed_override": "发现 Claude Code 的托管配置({{source}}),其中 {{keys}} 优先于 {{product}} 写入的设置,这里的配置不会生效。托管配置通常由公司电脑统一下发,请联系公司 IT 管理员",
@@ -462,6 +482,83 @@
462
482
  "native_mirror_failed": "无法从国内镜像替换:{{reason}}",
463
483
  "install_label": "{{kind}} {{version}}({{path}})"
464
484
  },
485
+ "components": {
486
+ "category": {
487
+ "runtime": "运行环境",
488
+ "cli": "命令行工具",
489
+ "app": "桌面应用"
490
+ },
491
+ "col": {
492
+ "name": "组件",
493
+ "current": "已装",
494
+ "latest": "最新",
495
+ "managed": "安装方式",
496
+ "state": "状态"
497
+ },
498
+ "by": {
499
+ "fnm": "fnm",
500
+ "brew": "Homebrew",
501
+ "nvm": "nvm",
502
+ "volta": "Volta",
503
+ "scoop": "Scoop",
504
+ "asdf": "asdf",
505
+ "mise": "mise",
506
+ "system": "系统自带",
507
+ "standalone": "独立安装",
508
+ "pipx": "pipx",
509
+ "pip": "pip",
510
+ "winget": "winget",
511
+ "git-for-windows-user": "Git for Windows(当前用户)",
512
+ "git-for-windows": "Git for Windows",
513
+ "xcode": "苹果命令行工具",
514
+ "native": "原生安装",
515
+ "npm": "npm",
516
+ "app": "应用",
517
+ "other": "其他"
518
+ },
519
+ "state": {
520
+ "unsupported": "这个系统不提供",
521
+ "missing": "未安装:{{command}}",
522
+ "outdated_auto": "可升级:{{command}}",
523
+ "outdated_manual": "可升级:{{how}}",
524
+ "latest": "已是最新",
525
+ "unknown_latest": "已安装"
526
+ },
527
+ "source": "来源:{{source}}",
528
+ "latest_unknown": "暂时查不到最新版本:{{tools}}(稍后运行 {{cli}} list --refresh 再查)",
529
+ "unknown": "不认识的组件:{{names}}",
530
+ "known": "可以安装的组件:{{ids}}",
531
+ "unsupported_platform": "这个系统不提供 {{tool}}",
532
+ "blocked_sudo": "不要用 sudo 安装 {{tool}}:会装进 root 的用户目录,归 root 所有。去掉 sudo,用平常的账号再运行一次",
533
+ "blocked_system": "当前是系统账号(SYSTEM),{{tool}} 会装进系统配置目录:登录要用它的账号后再运行",
534
+ "blocked_unsupported_platform": "这个系统不提供 {{tool}}",
535
+ "reason_sudo": "不能用 sudo 安装",
536
+ "reason_system": "当前是系统账号(SYSTEM)",
537
+ "reason_unsupported_platform": "这个系统不提供",
538
+ "no_mirror": "无法安装 {{tool}}:没有配置安装脚本的下载地址",
539
+ "already_installed": "已安装 {{tool}} {{current}}",
540
+ "up_to_date": "{{tool}} {{current}} 已是最新",
541
+ "force_hint": "要重新安装就运行 {{command}}",
542
+ "outdated_auto": "{{tool}} 当前 {{current}},可升到 {{latest}}:运行 {{command}}",
543
+ "outdated_manual": "{{tool}} 当前 {{current}},可升到 {{latest}}。这份是用 {{by}} 安装的,{{cli}} 不改动它:运行 {{how}} 升级;要另装一份由 {{cli}} 管理的,运行 {{command}}",
544
+ "no_latest": "{{tool}} {{current}} 由系统管理,{{cli}} 不升级它:{{how}}",
545
+ "how_system": "用安装它的方式升级(系统包管理器或官方安装包),或加 --force 另装一份由 {{cli}} 管理的",
546
+ "how_xcode": "在「系统设置 → 通用 → 软件更新」里更新苹果命令行工具",
547
+ "how_other": "用安装它的方式升级",
548
+ "force_beside": "{{tool}} 已有一份({{path}},{{by}} 安装),按 --force 另装一份由 {{cli}} 管理的",
549
+ "installing": "正在安装 {{tool}} {{latest}}({{source}})…",
550
+ "upgrading": "正在升级 {{tool}} 到 {{latest}}({{source}})…",
551
+ "script_failed": "{{tool}} 没有装好,原因见上方。排除原因后运行 {{command}}",
552
+ "installed": "已安装 {{tool}} {{version}}",
553
+ "upgraded": "已把 {{tool}} 从 {{from}} 升到 {{to}}",
554
+ "reinstalled": "已重新安装 {{tool}} {{version}}",
555
+ "reinstalling": "按 --force 重新安装 {{tool}}(当前 {{current}})",
556
+ "path_shadowed": "终端里先找到的是 {{active}},不是刚装好的 {{tool}} {{version}}({{path}}):把后者所在目录放到 PATH 前面,或删掉前者",
557
+ "capped": "{{tool}} {{latest}} 不能在这台电脑上运行,安装脚本装的是能运行的最新版 {{version}};有更新的版本时再提示升级",
558
+ "new_terminal": "新开一个终端窗口后可以直接使用",
559
+ "migrating_globals": "新版 Node 里还没有原来全局安装的这些包,正在装上同一版本:{{packages}}",
560
+ "migrate_failed": "原来全局安装的包没有全部装到新版 Node 里,可以手动运行:{{command}}"
561
+ },
465
562
  "install": {
466
563
  "permission_unix": "多半是 npm 没有权限写入全局目录。可以任选一种:① sudo {{command}};② 运行 npm config set prefix ~/.npm-global,把 export PATH=~/.npm-global/bin:$PATH 加入 ~/.zshrc 或 ~/.bashrc,重新打开终端后再运行 {{command}}",
467
564
  "permission_windows": "多半是权限不足或文件被占用:请关闭正在运行的这个工具,以管理员身份打开终端,再运行 {{command}}",
@@ -851,18 +948,58 @@
851
948
  "doctor": "先自行检查常见问题"
852
949
  }
853
950
  },
951
+ "list": {
952
+ "summary": "列出 {{cli}} 能装的运行环境、命令行工具和桌面应用,以及装了哪个版本",
953
+ "description": "列出 Node.js、Git、uv、Claude Code、Codex、Grok Build、Antigravity CLI、Claude Desktop、ChatGPT:装了没有、当前版本、最新版本、用什么方式装的、从哪下载。最新版本缓存一小时,加 --refresh 立即重新查。",
954
+ "when": "想知道这台电脑上装了哪些工具、哪些可以升级时。",
955
+ "alt": {
956
+ "doctor": "检查令牌和各工具的配置"
957
+ },
958
+ "opt": {
959
+ "refresh": "不用缓存,重新查最新版本"
960
+ },
961
+ "ex": {
962
+ "all": "列出全部组件",
963
+ "refresh": "重新查最新版本",
964
+ "json": "输出 JSON,给脚本用"
965
+ }
966
+ },
967
+ "install": {
968
+ "summary": "安装运行环境(Node.js、Git、uv)、命令行工具或桌面应用",
969
+ "description": "从国内镜像安装缺少的组件,下载后核对校验值(桌面应用还核对厂商签名)。已经装过的不覆盖:只显示当前版本,落后时给出升级命令;加 --force 重新安装。\nNode.js、Git、uv 用 {{brand}} 的安装脚本装进当前用户目录,不需要管理员权限;不要加 sudo。可以写多个组件,有依赖的先装依赖。",
970
+ "arg": {
971
+ "components": "node、git、uv、claude-code、codex、grok、agy、claude-desktop、chatgpt,可以填写多个"
972
+ },
973
+ "opt": {
974
+ "force": "已经装过也重新安装"
975
+ },
976
+ "when": "要在这台电脑上补装某个工具时;别的应用的安装脚本也可以调用它(npx {{package}} install …)。",
977
+ "needs": "这台电脑已有 Node.js({{cli}} 本身要 Node);还没有 Node 时先用一键安装脚本",
978
+ "alt": {
979
+ "upgrade": "升级已经装好的组件",
980
+ "setup": "装好后写入令牌并配置各工具"
981
+ },
982
+ "ex": {
983
+ "runtimes": "安装 Node.js 和 uv",
984
+ "clis": "安装 Claude Code 和 Codex",
985
+ "app": "安装 ChatGPT 桌面版(内含 Codex)",
986
+ "force": "uv 已经装过也重新安装",
987
+ "json": "在脚本里安装,输出 JSON"
988
+ }
989
+ },
854
990
  "upgrade": {
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。",
991
+ "summary": "升级编码工具、桌面应用和运行环境(Node.js、Git、uv)",
992
+ "description": "按每份工具当初的安装方式升级:原生安装用工具自带的更新,npm 安装从国内镜像(npmmirror)重新安装,Homebrew 安装用 brew upgrade;原生或 Homebrew 方式无法升级时,改用 {{brand}} 一键安装脚本(程序取自国内镜像)。指定了但还没有安装的工具,也用这个脚本安装。\nClaude Desktop 和 ChatGPT(内含 Codex;macOS / Windows)比高速下载站上的版本旧时,从高速下载站下载官方安装包,核对校验值和厂商(Anthropic / OpenAI)的签名后安装;它们自带的自动更新照常开着,{{cli}} 只在没跟上时补上。应用正在运行时,在终端里问过才关掉它升级,带 --yes 或没有终端时只提示、不关。已是最新时加 --force 重新安装。ChatGPT 的 Mac 版只支持 Apple 芯片。\nNode.js、Git、uv 只升级由 {{brand}} 安装脚本管理的那一份(fnm 装的 Node、~/.local/bin 里的 uv、装在当前用户下的 Git for Windows);用 Homebrew、nvm、系统包管理器等装的,给出对应的升级命令、不改动它,加 --force 另装一份由 {{cli}} 管理的。fnm 的 Node 升级后,原来全局安装的 npm 包会按原版本装到新版里。\n不填名字则升级已安装的全部编码工具和桌面应用,并提示可以升级的运行环境。\n升级 {{product}} 本身请用 {{cli}} update。",
857
993
  "arg": {
858
- "tools": "claude-code、codex、grok、agy、claude-desktop、chatgpt,可以填写多个"
994
+ "tools": "claude-code、codex、grok、agy、claude-desktop、chatgpt、node、git、uv,可以填写多个"
859
995
  },
860
996
  "ex": {
861
997
  "all": "升级已安装的全部工具",
862
998
  "one": "只升级 Claude Code",
863
999
  "many": "升级 Codex 和 Antigravity CLI",
864
1000
  "desktop": "安装 Claude Desktop,或升到高速下载站上的最新版",
865
- "force": "ChatGPT 已是最新也重新安装一遍(应用损坏时)"
1001
+ "force": "ChatGPT 已是最新也重新安装一遍(应用损坏时)",
1002
+ "runtime": "升级 Node.js(fnm 安装的;别的方式装的给出升级命令)"
866
1003
  },
867
1004
  "when": "{{cli}} doctor 提示版本过低,或工具自带的更新无法完成时。",
868
1005
  "needs": "不填工具名时,只升级已经安装的工具",
@@ -870,7 +1007,7 @@
870
1007
  "update": "升级 {{product}} 本身"
871
1008
  },
872
1009
  "opt": {
873
- "force": "桌面应用(claude-desktop、chatgpt)已是最新也重新下载安装"
1010
+ "force": "桌面应用和运行环境已是最新也重新安装;运行环境由别的方式装的,另装一份由 {{cli}} 管理的"
874
1011
  }
875
1012
  },
876
1013
  "refresh": {
@@ -1074,6 +1211,8 @@
1074
1211
  "offer_quit": "关掉 {{tool}} 并升到 {{latest}}?",
1075
1212
  "declined_hint": "没有升级。之后可以运行 {{command}}",
1076
1213
  "quit_failed": "无法退出 {{tool}},这次没有升级:手动退出后运行 {{command}}",
1214
+ "quit_confirm_in_app": "{{tool}} 没有退出:它可能有任务正在运行,或弹出了确认框。请切到 {{tool}},在它的提示里确认退出(或等任务结束后退出),这里会接着升级(最多等 {{seconds}} 秒)…",
1215
+ "quit_refused": "{{tool}} 没有退出,这次没有升级:它有任务正在运行或在等你确认。在 {{tool}} 里确认退出(或等任务结束后退出),再运行 {{command}}",
1077
1216
  "upgraded": "已把 {{tool}} 从 {{from}} 升到 {{to}}",
1078
1217
  "installed": "已安装 {{tool}} {{version}}",
1079
1218
  "upgrade_failed": "无法升级 {{tool}}:{{reason}}。当前的 {{current}} 照常可用;排除原因后运行 {{command}}",
@@ -1110,6 +1249,10 @@
1110
1249
  "reinstalled": "已重新安装 {{tool}} {{version}}",
1111
1250
  "force_hint": "要重新安装就运行 {{command}} --force",
1112
1251
  "blocked_remote_session": "这是远程(SSH)登录的会话,Windows 不允许在这里安装 {{tool}}:在这台电脑本机的终端里运行 {{command}}",
1113
- "reason_remote_session": "远程(SSH)会话里不能安装 Windows 应用"
1252
+ "reason_remote_session": "远程(SSH)会话里不能安装 Windows 应用",
1253
+ "error_needs_admin": "Windows 要求用管理员权限安装(0x80073D28),先右键 PowerShell 选「以管理员身份运行」,在那个窗口里运行",
1254
+ "error_other_account": "授权时用的是另一个管理员账号,应用装给了那个账号:用当前账号授权(把当前账号设为管理员),或在那个账号下使用",
1255
+ "elevate_offer": "Windows 要求用管理员权限安装 {{tool}}。现在弹出授权窗口?",
1256
+ "elevating": "正在请求管理员权限:在弹出的 Windows 窗口里点「是」…"
1114
1257
  }
1115
1258
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marsapi/cli",
3
- "version": "0.0.148",
3
+ "version": "0.0.153",
4
4
  "description": "Configure Claude Code, Codex, Grok Build and other coding tools for Mars AI with one command",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist",
12
12
  "models.json",
13
+ "components.json",
13
14
  "client-config.snapshot.json",
14
15
  "settings-template.json",
15
16
  "codex-base-instructions.md",