@vibeapi/cli 0.0.57 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,11 +6,10 @@ import { type GatewayModelSpec } from './model-registry.js';
6
6
  */
7
7
  export declare const MODEL_ENV_VARS: readonly ["ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_FABLE_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_REASONING_MODEL"];
8
8
  /**
9
- * Add the gateway's other models (domestic models, …) to Claude Code's `/model`
10
- * menu: `modelPicker` rows after the built-in lineup, `modelSettings.<id>.autoCompactWindow`
11
- * at 80% of each window, and `CLAUDE_CODE_MAX_CONTEXT_TOKENS` — Claude Code's only
12
- * window setting for non-Claude ids, one value for all of them — at the smallest
13
- * listed window. `models` undefined (offline) keeps what is there; empty removes ours.
9
+ * Add the gateway's other models (GPT, Gemini, Grok, domestic …) to Claude Code's
10
+ * `/model` menu: `modelPicker` rows after the built-in lineup (`<id>[1m]` for 1M
11
+ * models), `modelSettings.<id>.autoCompactWindow` at 80% of each window, and
12
+ * `CLAUDE_CODE_MAX_CONTEXT_TOKENS` at the smallest window among the models below 1M. `models` undefined (offline) keeps what is there; empty removes ours.
14
13
  */
15
14
  export declare function applyGatewayModels(settings: ClaudeCodeSettingsConfig, models: GatewayModelSpec[] | undefined): ClaudeCodeSettingsConfig;
16
15
  export interface TierFallback {
@@ -1 +1 @@
1
- import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as o,renameSync as s,unlinkSync as i}from"fs";import{join as r,dirname as a}from"path";import{toolHome as l}from"./tools/types.js";import{logger as c}from"../utils/logger.js";import{configManager as d,DEFAULT_MODELS as g}from"./config.js";import{loadSettingsTemplate as m}from"./settings-template.js";import{cleanupAnthropicEnvVars as C}from"./env-cleanup.js";import{getClaudeCodeTierChains as f,getGatewayModelRules as O}from"./model-registry.js";import{autoCompactTokens as h,prettyModelName as u}from"./gateway-models.js";import{baseModelId as _}from"./model-catalog.js";export const MODEL_ENV_VARS=["ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];const p={opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL",fable:"ANTHROPIC_DEFAULT_FABLE_MODEL"},E="CLAUDE_CODE_MAX_CONTEXT_TOKENS";function T(e,t=[]){if(!e||"object"!=typeof e)return!1;const{options:n,replaceBuiltInOptions:o}=e;if(!Array.isArray(n)||o)return!1;const s=new Set([...O().models.map(e=>e.id),...t]);return n.every(e=>e&&"object"==typeof e&&s.has(e.model))}export function applyGatewayModels(e,t){if(void 0===t)return e;const n={...e};if(void 0!==n.modelPicker&&!T(n.modelPicker,t.map(e=>e.id)))return n;const o=T(n.modelPicker)?n.modelPicker.options.map(e=>e.model):[],s={...n.env||{}},i={...n.modelSettings||{}};for(const e of o){if(!i[e])continue;const{autoCompactWindow:t,...n}=i[e];Object.keys(n).length>0?i[e]=n:delete i[e]}const r=t.map(e=>e.contextWindow).filter(e=>"number"==typeof e&&e>0);if(0===t.length)delete n.modelPicker,delete s[E];else{n.modelPicker={options:t.map(e=>({model:e.id,label:e.name||u(e.id),...e.description?{description:e.description}:{}}))},r.length>0&&(s[E]=String(Math.min(...r)));for(const e of t){if(!e.contextWindow)continue;const t=h(e.contextWindow,e.autoCompactTokens);i[e.id]={...i[e.id],autoCompactWindow:Math.min(Math.max(t,1e5),1e6)}}}return Object.keys(i).length>0?n.modelSettings=i:delete n.modelSettings,Object.keys(s).length>0?n.env=s:delete n.env,n}export function resolveTierFallbacks(e){if(!e||0===e.length)return[];const t=new Set(e.map(_)),n=[];for(const[e,o]of Object.entries(f())){const s=p[e],[i,...r]=o;if(!s||!i||t.has(i))continue;const a=r.find(e=>t.has(e));a&&n.push({tier:e,envVar:s,from:i,to:a})}return n}export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;lastTierFallbacks=[];constructor(){this.settingsPath=r(l(),".claude","settings.json"),this.mcpConfigPath=r(l(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const o=a(n);e(o)||t(o,{recursive:!0})}atomicWrite(t,n){const r=`${t}.tmp`;try{o(r,n,"utf-8"),s(r,t)}catch(t){if(e(r))try{i(r)}catch{}throw t}}getSettings(){try{if(e(this.settingsPath)){const e=n(this.settingsPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code settings:",e),c.logError("ClaudeCodeManager.getSettings",e)}return{}}saveSettings(e){try{this.ensureConfigDir(this.settingsPath),this.atomicWrite(this.settingsPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code settings: ${e}`)}}getMCPConfig(){try{if(e(this.mcpConfigPath)){const e=n(this.mcpConfigPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code MCP config:",e),c.logError("ClaudeCodeManager.getMCPConfig",e)}return{}}saveMCPConfig(e){try{this.ensureConfigDir(this.mcpConfigPath),this.atomicWrite(this.mcpConfigPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code MCP config: ${e}`)}}loadPrivateConfig(e,t,n,o){this.ensureOnboardingCompleted(),C();const s=m(),i=s.env||{},{env:r,...a}=s,l=this.getSettings(),{ANTHROPIC_API_KEY:c,...f}=l.env||{},O={...f};for(const e of MODEL_ENV_VARS)delete O[e];const h={},u=e=>{const n=(t[e]||"").trim();return n&&n!==g[e]?n:null},_=u("haiku"),p=u("sonnet"),E=u("opus"),T=new Set([_&&"haiku",p&&"sonnet",E&&"opus"].filter(Boolean));this.lastTierFallbacks=resolveTierFallbacks(n).filter(e=>!T.has(e.tier));for(const e of this.lastTierFallbacks)h[e.envVar]=e.to;_&&(h.ANTHROPIC_DEFAULT_HAIKU_MODEL=_),p&&(h.ANTHROPIC_DEFAULT_SONNET_MODEL=p),E&&(h.ANTHROPIC_DEFAULT_OPUS_MODEL=E,h.ANTHROPIC_MODEL=E);const A={...l};for(const[e,t]of Object.entries(a))e in A||(A[e]=t);A.showThinkingSummaries=!0;const P={...A,env:{...O,...Object.fromEntries(Object.entries(i).filter(([e])=>!(e in O))),ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:d.getBaseUrl(),...h}};E?P.model=E:Object.prototype.hasOwnProperty.call(a,"model")?P.model=a.model:delete P.model,this.saveSettings(applyGatewayModels(P,o))}ensureOnboardingCompleted(){try{const e=this.getMCPConfig();e.hasCompletedOnboarding||this.saveMCPConfig({...e,hasCompletedOnboarding:!0})}catch(e){c.warn("Failed to ensure onboarding completed:",e),c.logError("ClaudeCodeManager.ensureOnboardingCompleted",e)}}unloadPrivateConfig(){const e=this.getSettings();if(!e.env){if(void 0!==e.model){const{model:t,...n}=e;this.saveSettings(n)}return}const{ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:n,API_TIMEOUT_MS:o,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:s,CLAUDE_CODE_AUTO_MODE_SERVER:i,...r}=e.env,a={...r};for(const e of MODEL_ENV_VARS)delete a[e];const l={...applyGatewayModels({...e,env:a},[])};l.env=l.env||{},delete l.model,l.env&&0===Object.keys(l.env).length&&delete l.env,this.saveSettings(l)}clearPinnedModels(e){const t=this.getSettings(),n=t.env,o=n?.ANTHROPIC_AUTH_TOKEN?Object.fromEntries(resolveTierFallbacks(e).map(e=>[e.envVar,e.to])):{},s=[];for(const e of MODEL_ENV_VARS)n&&e in n&&n[e]!==o[e]&&s.push(e);void 0!==t.model&&s.push("model");const i=Object.entries(o).some(([e,t])=>n?.[e]!==t);if(0===s.length&&!i)return[];const r={...t};if(delete r.model,n){const e={...n};for(const t of MODEL_ENV_VARS)delete e[t];Object.assign(e,o),0===Object.keys(e).length?delete r.env:r.env=e}return this.saveSettings(r),s}getLastTierFallbacks(){return[...this.lastTierFallbacks]}detectCurrentConfig(){try{const e=this.getSettings();return e.env&&e.env.ANTHROPIC_AUTH_TOKEN?{apiKey:e.env.ANTHROPIC_AUTH_TOKEN}:{apiKey:null}}catch{return{apiKey:null}}}getExperimentalFeature(e){const t=this.getSettings();return t.env?.[e]}setExperimentalFeature(e,t){const n=this.getSettings(),o=n.env||{},s={...n,env:{...o,[e]:t}};this.saveSettings(s)}removeExperimentalFeature(e){const t=this.getSettings();if(!t.env)return;const{[e]:n,...o}=t.env,s={...t,env:o};this.saveSettings(s)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
1
+ import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as o,renameSync as i,unlinkSync as r}from"fs";import{join as s,dirname as a}from"path";import{toolHome as c}from"./tools/types.js";import{logger as l}from"../utils/logger.js";import{configManager as d,DEFAULT_MODELS as g}from"./config.js";import{loadSettingsTemplate as f}from"./settings-template.js";import{cleanupAnthropicEnvVars as m}from"./env-cleanup.js";import{getClaudeCodeTierChains as u,getGatewayModelRules as C}from"./model-registry.js";import{autoCompactTokens as O,prettyModelName as h}from"./gateway-models.js";import{baseModelId as p}from"./model-catalog.js";export const MODEL_ENV_VARS=["ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];const _={opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL",fable:"ANTHROPIC_DEFAULT_FABLE_MODEL"},E="CLAUDE_CODE_MAX_CONTEXT_TOKENS";function A(){return s(d.getConfigDir(),"claude-code-picker.json")}function T(){try{const e=JSON.parse(n(A(),"utf-8")).ids;return Array.isArray(e)?e.map(String):[]}catch{return[]}}function P(e,t=[]){if(!e||"object"!=typeof e)return!1;const{options:n,replaceBuiltInOptions:o}=e;if(!Array.isArray(n)||o)return!1;const i=C(),r=new Set([...i.models.map(e=>e.id),...i.retired,...T(),...t]);return n.every(e=>e&&"object"==typeof e&&r.has(p(String(e.model))))}export function applyGatewayModels(e,n){if(void 0===n)return e;const i={...e};if(void 0!==i.modelPicker&&!P(i.modelPicker,n.map(e=>e.id)))return i;const r=P(i.modelPicker)?i.modelPicker.options.map(e=>p(e.model)):[],s={...i.env||{}},a={...i.modelSettings||{}};for(const e of r){if(!a[e])continue;const{autoCompactWindow:t,...n}=a[e];Object.keys(n).length>0?a[e]=n:delete a[e]}const c=e=>(e.contextWindow??0)>=1e6,g=n.filter(e=>!c(e)).map(e=>e.contextWindow).filter(e=>"number"==typeof e&&e>0);if(function(e){try{t(d.getConfigDir(),{recursive:!0}),o(A(),JSON.stringify({ids:e})+"\n")}catch(e){l.logError("claudeCode.recordWrittenPickerIds",e)}}(n.map(e=>e.id)),0===n.length)delete i.modelPicker,delete s[E];else{i.modelPicker={options:n.map(e=>({model:c(e)?`${e.id}[1m]`:e.id,label:e.name||h(e.id),...e.description?{description:e.description}:{}}))},g.length>0?s[E]=String(Math.min(...g)):delete s[E];for(const e of n){if(!e.contextWindow)continue;const t=O(e.contextWindow,e.autoCompactTokens);a[e.id]={...a[e.id],autoCompactWindow:Math.min(Math.max(t,1e5),1e6)}}}return Object.keys(a).length>0?i.modelSettings=a:delete i.modelSettings,Object.keys(s).length>0?i.env=s:delete i.env,i}export function resolveTierFallbacks(e){if(!e||0===e.length)return[];const t=new Set(e.map(p)),n=[];for(const[e,o]of Object.entries(u())){const i=_[e],[r,...s]=o;if(!i||!r||t.has(r))continue;const a=s.find(e=>t.has(e));a&&n.push({tier:e,envVar:i,from:r,to:a})}return n}export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;lastTierFallbacks=[];constructor(){this.settingsPath=s(c(),".claude","settings.json"),this.mcpConfigPath=s(c(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const o=a(n);e(o)||t(o,{recursive:!0})}atomicWrite(t,n){const s=`${t}.tmp`;try{o(s,n,"utf-8"),i(s,t)}catch(t){if(e(s))try{r(s)}catch{}throw t}}getSettings(){try{if(e(this.settingsPath)){const e=n(this.settingsPath,"utf-8");return JSON.parse(e)}}catch(e){l.warn("Failed to read Claude Code settings:",e),l.logError("ClaudeCodeManager.getSettings",e)}return{}}saveSettings(e){try{this.ensureConfigDir(this.settingsPath),this.atomicWrite(this.settingsPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code settings: ${e}`)}}getMCPConfig(){try{if(e(this.mcpConfigPath)){const e=n(this.mcpConfigPath,"utf-8");return JSON.parse(e)}}catch(e){l.warn("Failed to read Claude Code MCP config:",e),l.logError("ClaudeCodeManager.getMCPConfig",e)}return{}}saveMCPConfig(e){try{this.ensureConfigDir(this.mcpConfigPath),this.atomicWrite(this.mcpConfigPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code MCP config: ${e}`)}}loadPrivateConfig(e,t,n,o){this.ensureOnboardingCompleted(),m();const i=f(),r=i.env||{},{env:s,...a}=i,c=this.getSettings(),{ANTHROPIC_API_KEY:l,...u}=c.env||{},C={...u};for(const e of MODEL_ENV_VARS)delete C[e];const O={},h=e=>{const n=(t[e]||"").trim();return n&&n!==g[e]?n:null},p=h("haiku"),_=h("sonnet"),E=h("opus"),A=new Set([p&&"haiku",_&&"sonnet",E&&"opus"].filter(Boolean));this.lastTierFallbacks=resolveTierFallbacks(n).filter(e=>!A.has(e.tier));for(const e of this.lastTierFallbacks)O[e.envVar]=e.to;p&&(O.ANTHROPIC_DEFAULT_HAIKU_MODEL=p),_&&(O.ANTHROPIC_DEFAULT_SONNET_MODEL=_),E&&(O.ANTHROPIC_DEFAULT_OPUS_MODEL=E,O.ANTHROPIC_MODEL=E);const T={...c};for(const[e,t]of Object.entries(a))e in T||(T[e]=t);T.showThinkingSummaries=!0;const P={...T,env:{...C,...Object.fromEntries(Object.entries(r).filter(([e])=>!(e in C))),ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:d.getBaseUrl(),...O}};E?P.model=E:Object.prototype.hasOwnProperty.call(a,"model")?P.model=a.model:delete P.model,this.saveSettings(applyGatewayModels(P,o))}ensureOnboardingCompleted(){try{const e=this.getMCPConfig();e.hasCompletedOnboarding||this.saveMCPConfig({...e,hasCompletedOnboarding:!0})}catch(e){l.warn("Failed to ensure onboarding completed:",e),l.logError("ClaudeCodeManager.ensureOnboardingCompleted",e)}}unloadPrivateConfig(){const e=this.getSettings();if(!e.env){if(void 0!==e.model){const{model:t,...n}=e;this.saveSettings(n)}return}const{ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:n,API_TIMEOUT_MS:o,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:i,CLAUDE_CODE_AUTO_MODE_SERVER:r,...s}=e.env,a={...s};for(const e of MODEL_ENV_VARS)delete a[e];const c={...applyGatewayModels({...e,env:a},[])};c.env=c.env||{},delete c.model,c.env&&0===Object.keys(c.env).length&&delete c.env,this.saveSettings(c)}clearPinnedModels(e){const t=this.getSettings(),n=t.env,o=n?.ANTHROPIC_AUTH_TOKEN?Object.fromEntries(resolveTierFallbacks(e).map(e=>[e.envVar,e.to])):{},i=[];for(const e of MODEL_ENV_VARS)n&&e in n&&n[e]!==o[e]&&i.push(e);void 0!==t.model&&i.push("model");const r=Object.entries(o).some(([e,t])=>n?.[e]!==t);if(0===i.length&&!r)return[];const s={...t};if(delete s.model,n){const e={...n};for(const t of MODEL_ENV_VARS)delete e[t];Object.assign(e,o),0===Object.keys(e).length?delete s.env:s.env=e}return this.saveSettings(s),i}getLastTierFallbacks(){return[...this.lastTierFallbacks]}detectCurrentConfig(){try{const e=this.getSettings();return e.env&&e.env.ANTHROPIC_AUTH_TOKEN?{apiKey:e.env.ANTHROPIC_AUTH_TOKEN}:{apiKey:null}}catch{return{apiKey:null}}}getExperimentalFeature(e){const t=this.getSettings();return t.env?.[e]}setExperimentalFeature(e,t){const n=this.getSettings(),o=n.env||{},i={...n,env:{...o,[e]:t}};this.saveSettings(i)}removeExperimentalFeature(e){const t=this.getSettings();if(!t.env)return;const{[e]:n,...o}=t.env,i={...t,env:o};this.saveSettings(i)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
@@ -1 +1 @@
1
- import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as l}from"../commands/logs.js";import{completionCommand as p}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{upgradeCommand as u}from"../commands/upgrade.js";import{setupCommand as f}from"../commands/setup.js";import h from"chalk";import{readFileSync as y}from"fs";import{join as k,dirname as b}from"path";import{fileURLToPath as v}from"url";import{setRuntimeOptions as w,detectColorPreferenceFromEnv as $,isColorEnabled as C,isJsonMode as x}from"./runtime-options.js";import{cleanupAnthropicEnvVars as j,machineEnvVarsLeft as A}from"./env-cleanup.js";import{runStartupUpdate as I}from"./auto-update.js";export class Command{program;constructor(){const i=t.isFirstRun()?a():t.getLang();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=v(import.meta.url),o=b(e),a=k(o,"../../package.json");return JSON.parse(y(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await f({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,force:e.force})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),this.program.command("doctor").description(o.t("commands.doctor")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await l(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await u(e)}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),await d(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${h.bold(o.t("cli.examples"))}:\n ${h.gray("$ vibecli # Interactive main menu")}\n ${h.gray("$ vibecli init # Run first-time setup wizard")}\n ${h.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${h.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${h.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${h.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${h.gray("$ vibecli config codex --force # Same, for one tool")}\n ${h.gray("$ vibecli status --json # Machine-readable status")}\n ${h.gray("$ vibecli config profile list")}\n ${h.gray("$ vibecli config profile create work")}\n ${h.gray("$ vibecli config profile use work")}\n ${h.gray("$ vibecli config preset save fast")}\n ${h.gray("$ vibecli config backup create snapshot")}\n ${h.gray("$ vibecli auth <token> --non-interactive")}\n ${h.gray("$ vibecli auth revoke --yes")}\n ${h.gray("$ vibecli logs path")}\n ${h.gray("$ vibecli completion bash")}\n ${h.gray("$ vibecli bench --samples 3")}\n ${h.gray("$ vibecli update")}\n ${h.gray("$ vibecli upgrade claude-code")}\n ${h.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=j();if(0===e.length||x())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(C()?h.yellow(a):a);const t=A();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(C()?h.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&$();let i;w({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(h.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await I(e)).handled)return;await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=C()?h.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
1
+ import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as l}from"../commands/logs.js";import{completionCommand as p}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{runModelRefresh as u}from"./model-refresh.js";import{upgradeCommand as f}from"../commands/upgrade.js";import{setupCommand as h}from"../commands/setup.js";import y from"chalk";import{readFileSync as k}from"fs";import{join as b,dirname as v}from"path";import{fileURLToPath as w}from"url";import{setRuntimeOptions as $,detectColorPreferenceFromEnv as C,isColorEnabled as x,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as A,machineEnvVarsLeft as I}from"./env-cleanup.js";import{runStartupUpdate as P}from"./auto-update.js";export class Command{program;constructor(){const i=t.isLangChosen()?t.getLang():a();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=w(import.meta.url),o=v(e),a=b(o,"../../package.json");return JSON.parse(k(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await h({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,force:e.force})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),this.program.command("doctor").description(o.t("commands.doctor")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await l(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await f(e)}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),await d(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${y.bold(o.t("cli.examples"))}:\n ${y.gray("$ vibecli # Interactive main menu")}\n ${y.gray("$ vibecli init # Run first-time setup wizard")}\n ${y.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${y.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${y.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${y.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${y.gray("$ vibecli config codex --force # Same, for one tool")}\n ${y.gray("$ vibecli status --json # Machine-readable status")}\n ${y.gray("$ vibecli config profile list")}\n ${y.gray("$ vibecli config profile create work")}\n ${y.gray("$ vibecli config profile use work")}\n ${y.gray("$ vibecli config preset save fast")}\n ${y.gray("$ vibecli config backup create snapshot")}\n ${y.gray("$ vibecli auth <token> --non-interactive")}\n ${y.gray("$ vibecli auth revoke --yes")}\n ${y.gray("$ vibecli logs path")}\n ${y.gray("$ vibecli completion bash")}\n ${y.gray("$ vibecli bench --samples 3")}\n ${y.gray("$ vibecli update")}\n ${y.gray("$ vibecli upgrade claude-code")}\n ${y.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=A();if(0===e.length||j())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(x()?y.yellow(a):a);const t=I();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(x()?y.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&C();let i;$({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(y.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await P(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=x()?y.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -33,6 +33,8 @@ export interface ConfigBackupInfo {
33
33
  }
34
34
  export interface VibecliConfig {
35
35
  lang: string;
36
+ /** Set once the user picked a language (`vibecli lang set`, the wizard); until then the system locale decides. */
37
+ lang_chosen?: boolean;
36
38
  credential_store?: 'plaintext';
37
39
  active_profile?: string;
38
40
  profiles?: Record<string, VibecliProfile>;
@@ -86,6 +88,8 @@ export declare class ConfigManager {
86
88
  updateConfig(updates: Partial<VibecliConfig>): void;
87
89
  isFirstRun(): boolean;
88
90
  getLang(): string;
91
+ /** Whether the stored language is the user's choice rather than the `en_US` every new config starts with. */
92
+ isLangChosen(): boolean;
89
93
  setLang(lang: string): void;
90
94
  getActiveProfileName(): string;
91
95
  listProfiles(): string[];
@@ -126,6 +130,8 @@ export declare class ConfigManager {
126
130
  listBackups(): ConfigBackupInfo[];
127
131
  createBackup(label?: string): string;
128
132
  restoreBackup(name: string): void;
133
+ /** vibecli's own directory (`~/.vibecli`, or the test / VIBECLI_CONFIG_DIR override). */
134
+ getConfigDir(): string;
129
135
  getBaseUrl(): string;
130
136
  setBaseUrl(url: string, persist?: boolean): void;
131
137
  resetBaseUrl(): void;
@@ -1 +1 @@
1
- import{existsSync as e,mkdirSync as t,readFileSync as i,writeFileSync as o,renameSync as s,unlinkSync as r,readdirSync as n,rmdirSync as a,statSync as c,chmodSync as l}from"fs";import{join as f,dirname as g}from"path";import{homedir as h}from"os";import{createHash as p}from"crypto";import*as m from"js-yaml";import{logger as u}from"../utils/logger.js";import{getDefaultModels as y,isRetiredDefaultModel as d}from"./model-registry.js";import{readLegacySecretsFile as _}from"./secret-store.js";export const DEFAULT_BASE_URL="https://www.vibeapi.cn";export const DEFAULT_MODELS=y();export const DEFAULT_PROFILE_NAME="default";const k="plaintext";export class ConfigManager{static instance;configDir;configPath;config;runtimeBaseUrl;constructor(){this.configDir=this.resolveConfigDir(),this.migrateLegacyConfigDir(),this.configPath=f(this.configDir,"config.yaml"),this.config=this.loadConfig()}migrateLegacyConfigDir(){try{const t=f(g(this.configDir),".chelper");if(t===this.configDir||!e(t))return;if(["config.yaml","secrets.enc"].some(t=>e(f(this.configDir,t))))return;if(e(this.configDir)){for(const i of n(t)){const o=f(this.configDir,i);e(o)||s(f(t,i),o)}0===n(t).length&&a(t)}else s(t,this.configDir);u.record(`Migrated config dir: ${t} -> ${this.configDir}`)}catch(e){u.warn("Failed to migrate legacy .chelper config dir:",e)}}resolveConfigDir(){const e=process.env.VIBECLI_CONFIG_DIR;if(e&&e.trim())return e.trim();const t=process.env.VIBECLI_TEST_HOME;return t&&t.trim()?f(t.trim(),".vibecli"):f(h(),".vibecli")}createDefaultConfig(){return this.syncLegacyFields({lang:"en_US",active_profile:"default",profiles:{[DEFAULT_PROFILE_NAME]:{}},presets:{}})}normalizeModels(e){const t=t=>{const i=e?.[t]?.trim();return i&&!d(t,i)?i:DEFAULT_MODELS[t]},i={haiku:t("haiku"),sonnet:t("sonnet"),opus:t("opus"),reasoning:t("reasoning")};if(e&&"object"==typeof e)for(const[t,o]of Object.entries(e))t in i||"string"==typeof o&&o.trim()&&(i[t]=o.trim());return i}normalizeToolModels(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i={};for(const[e,o]of Object.entries(t))"string"==typeof o&&o.trim()&&(i[e]=o.trim());return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeToolSettings(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||!t||"object"!=typeof t||Array.isArray(t))return[];const i=Object.fromEntries(Object.entries(t).filter(([,e])=>"string"==typeof e||"boolean"==typeof e||"number"==typeof e&&Number.isFinite(e)));return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeStringRecord(e){return!e||"object"!=typeof e||Array.isArray(e)?{}:Object.fromEntries(Object.entries(e).filter(e=>"string"==typeof e[1]&&Boolean(e[1].trim())).map(([e,t])=>[e,t.trim()]))}normalizeProfile(e){if(!e)return{};const t=this.normalizeToolModels(e.tool_models),i=this.normalizeToolSettings(e.tool_settings),o=this.normalizeStringRecord(e.tool_api_keys);return{..."string"==typeof e.api_key&&e.api_key?{api_key:e.api_key}:{},...Object.keys(o).length>0?{tool_api_keys:o}:{},..."string"==typeof e.base_url&&e.base_url?{base_url:e.base_url}:{},...e.models?{models:this.normalizeModels(e.models)}:{},...Object.keys(t).length>0?{tool_models:t}:{},...Object.keys(i).length>0?{tool_settings:i}:{}}}normalizeProfiles(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>e&&"string"==typeof e&&t&&"object"==typeof t?[[e,this.normalizeProfile(t)]]:[]);return Object.fromEntries(t)}normalizePresets(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i=t;return i.models?[[e,{models:this.normalizeModels(i.models),updated_at:i.updated_at||new Date(0).toISOString()}]]:[]});return Object.fromEntries(t)}getResolvedActiveProfile(e){const t=e.active_profile||"default";return e.profiles?.[t]||{}}syncLegacyFields(e){const t=this.normalizeProfiles(e.profiles),i=e.active_profile?.trim()||Object.keys(t)[0]||"default",o=t[i]?t:{...t,[i]:{}},s=this.getResolvedActiveProfile({...e,profiles:o,active_profile:i});return{...e,lang:e.lang||"en_US",active_profile:i,profiles:o,presets:this.normalizePresets(e.presets),api_key:s.api_key,base_url:s.base_url,models:s.models?this.normalizeModels(s.models):DEFAULT_MODELS}}sanitizeBackupLabel(e){return(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}getBackupDir(){return f(this.configDir,"backups")}getLegacySecretsPath(){return f(this.configDir,"secrets.enc")}getSecretsBackupPath(e){return e.replace(/\.yaml$/,".secrets.enc")}static getInstance(){return ConfigManager.instance||(ConfigManager.instance=new ConfigManager),ConfigManager.instance}ensureConfigDir(){e(this.configDir)||t(this.configDir,{recursive:!0})}loadConfig(){try{if(e(this.configPath)){const t=i(this.configPath,"utf-8"),o=m.load(t);let s=this.migrateOldConfig(o||{lang:"en_US"}),n=!1;const a=this.getLegacySecretsPath();if(e(a)&&o?.credential_store!==k)try{s=this.mergeLegacySecrets(s,_(a)),n=!0}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecrets",e)}if((this.needsMigration(o)||n)&&(this.backupConfig(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(s))),n))try{r(a)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}try{l(this.configPath,384)}catch{}return s}const t=this.getLegacySecretsPath();if(e(t))try{const e=this.mergeLegacySecrets(this.createDefaultConfig(),_(t));this.ensureConfigDir(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(e)));try{r(t)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}return e}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecretsWithoutConfig",e)}}catch(e){u.warn("Failed to load config, using defaults:",e),u.logError("ConfigManager.loadConfig",e)}return this.createDefaultConfig()}needsMigration(e){return!(!e||"object"!=typeof e)&&("plan"in e||!("profiles"in e)||!("active_profile"in e)||!("presets"in e))}migrateOldConfig(e){if(!e||"object"!=typeof e)return this.createDefaultConfig();const t=e,{plan:i,...o}=t,s=this.normalizeProfiles(o.profiles),r=this.normalizeProfile({api_key:"string"==typeof o.api_key?o.api_key:void 0,base_url:"string"==typeof o.base_url?o.base_url:void 0,models:o.models}),n=Object.keys(s).length>0?s:{[DEFAULT_PROFILE_NAME]:r},a="string"==typeof o.active_profile&&o.active_profile.trim()?o.active_profile.trim():Object.keys(n)[0]||"default";return this.syncLegacyFields({lang:"string"==typeof o.lang&&o.lang?o.lang:"en_US",...o.credential_store===k?{credential_store:k}:{},active_profile:a,profiles:n,presets:this.normalizePresets(o.presets)})}atomicWrite(t,i){const n=`${t}.tmp`;try{o(n,i,{encoding:"utf-8",mode:384}),s(n,t);try{l(t,384)}catch{}}catch(t){if(e(n))try{r(n)}catch{}throw t}}backupConfig(){if(!e(this.configPath))return;const s=this.getBackupDir();e(s)||t(s,{recursive:!0});const r=(new Date).toISOString().replace(/[:.]/g,"-"),n=f(s,`config-${r}.yaml`);try{const e=i(this.configPath,"utf-8");o(n,e,{encoding:"utf-8",mode:384}),this.rotateBackups(s,10,30)}catch(e){u.logError("ConfigManager.backupConfig",e)}}rotateBackups(t,i,o){try{const s=Date.now()-24*o*60*60*1e3,a=n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>({name:e,path:f(t,e),mtime:c(f(t,e)).mtime})).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()),l=new Set;a.slice(i).forEach(e=>l.add(e.path)),a.forEach(e=>{e.mtime.getTime()<s&&l.add(e.path)});for(const t of l){let i=!1;try{r(t),i=!0}catch(e){u.logError("ConfigManager.rotateBackups",e)}if(i){const i=this.getSecretsBackupPath(t);if(e(i))try{r(i)}catch(e){u.logError("ConfigManager.rotateBackups.secrets",e)}}}}catch(e){u.logError("ConfigManager.rotateBackups",e)}}saveConfig(e){try{this.ensureConfigDir(),this.backupConfig();const t=this.syncLegacyFields(this.migrateOldConfig(e||this.config)),i=this.prepareForPersistence(t),o=m.dump(i);this.atomicWrite(this.configPath,o),this.config=t}catch(e){throw u.error("Failed to save config:",e),u.logError("ConfigManager.saveConfig",e),e}}prepareForPersistence(e){const t={...e,credential_store:k};return delete t.api_key,delete t.base_url,delete t.models,t}mergeLegacySecrets(e,t){const i={...e.profiles||{}},o=new Set([...Object.keys(t.api_keys||{}),...Object.keys(t.tool_api_keys||{})]);for(const e of o){const o=i[e]||{},s=o.api_key||t.api_keys?.[e],r={...t.tool_api_keys?.[e]||{},...o.tool_api_keys||{}};i[e]={...o,...s?{api_key:s}:{},...Object.keys(r).length>0?{tool_api_keys:r}:{}}}return this.syncLegacyFields({...e,profiles:i})}getConfig(){return this.syncLegacyFields({...this.config})}updateConfig(e){this.saveConfig({...this.config,...e})}isFirstRun(){return!e(this.configPath)}getLang(){return this.config.lang||"en_US"}setLang(e){this.updateConfig({lang:e})}getActiveProfileName(){return this.config.active_profile||"default"}listProfiles(){return Object.keys(this.config.profiles||{}).sort()}getProfile(e=this.getActiveProfileName()){const t=this.config.profiles?.[e]||{};return{...t.api_key?{api_key:t.api_key}:{},...t.tool_api_keys&&Object.keys(t.tool_api_keys).length>0?{tool_api_keys:this.normalizeStringRecord(t.tool_api_keys)}:{},...t.base_url?{base_url:t.base_url}:{},models:t.models?this.normalizeModels(t.models):DEFAULT_MODELS,...t.tool_models&&Object.keys(t.tool_models).length>0?{tool_models:this.normalizeToolModels(t.tool_models)}:{},...t.tool_settings&&Object.keys(t.tool_settings).length>0?{tool_settings:this.normalizeToolSettings(t.tool_settings)}:{}}}createProfile(e,t=this.getActiveProfileName()){const i=this.getProfile(t),o={...this.config.profiles||{},[e]:{...i.api_key?{api_key:i.api_key}:{},...i.base_url?{base_url:i.base_url}:{},models:this.normalizeModels(i.models)}};this.saveConfig({...this.config,profiles:o})}setActiveProfile(e){if(!this.config.profiles?.[e])throw new Error(`Profile not found: ${e}`);this.saveConfig({...this.config,active_profile:e})}updateProfile(e,t){const i=this.config.profiles?.[e]||{},o=t.models?this.normalizeModels({...i.models,...t.models}):i.models?this.normalizeModels(i.models):void 0,s={...i,...void 0!==t.api_key?{api_key:t.api_key}:{},...void 0!==t.base_url?{base_url:t.base_url}:{},...o?{models:o}:{}},r={...this.config.profiles||{},[e]:s};this.saveConfig({...this.config,profiles:r})}deleteProfile(e){const t={...this.config.profiles||{}};if(!t[e])return;const i=Object.keys(t).filter(t=>t!==e);if(0===i.length)throw new Error("At least one profile must remain");delete t[e];const o=this.getActiveProfileName()===e?i[0]:this.getActiveProfileName();this.saveConfig({...this.config,profiles:t,active_profile:o})}getApiKey(){return this.config.profiles?.[this.getActiveProfileName()]?.api_key}setApiKey(e){this.updateProfile(this.getActiveProfileName(),{api_key:e})}revokeApiKey(){const e=this.config.profiles?.[this.getActiveProfileName()]||{},{api_key:t,tool_api_keys:i,...o}=e,s={...this.config.profiles||{},[this.getActiveProfileName()]:o};this.saveConfig({...this.config,profiles:s})}getApiKeyFingerprint(){const e=this.getApiKey();return e?p("sha256").update(e).digest("hex").slice(0,12):""}getApiKeyForTool(e){const t=this.config.profiles?.[this.getActiveProfileName()];return t?.tool_api_keys?.[e]||t?.api_key||void 0}setToolApiKey(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...this.config.profiles||{},[i]:{...o,tool_api_keys:{...o.tool_api_keys||{},[e]:t}}};this.saveConfig({...this.config,profiles:s})}clearToolApiKey(e){const t=this.getActiveProfileName(),i=this.config.profiles?.[t]||{},{[e]:o,...s}=i.tool_api_keys||{},r={...i};Object.keys(s).length>0?r.tool_api_keys=s:delete r.tool_api_keys,this.saveConfig({...this.config,profiles:{...this.config.profiles||{},[t]:r}})}listToolApiKeys(){return Object.keys(this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys||{}).sort()}getToolApiKeyFingerprint(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys?.[e];return t?p("sha256").update(t).digest("hex").slice(0,12):""}getModels(){const e=this.config.profiles?.[this.getActiveProfileName()]?.models;return e?this.normalizeModels(e):DEFAULT_MODELS}setModels(e){this.updateProfile(this.getActiveProfileName(),{models:this.normalizeModels(e)})}updateModels(e){this.setModels(this.normalizeModels({...this.getModels(),...e}))}getModelsForTool(e){const t=this.getModels(),i=this.config.profiles?.[this.getActiveProfileName()]?.tool_models?.[e];return i?this.normalizeModels({...t,...i}):t}setToolModel(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...o.tool_models||{}};t&&0!==Object.keys(t).length?s[e]=t:delete s[e];const r={...o};Object.keys(s).length>0?r.tool_models=s:delete r.tool_models;const n={...this.config.profiles||{},[i]:r};this.saveConfig({...this.config,profiles:n})}getToolSettings(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_settings?.[e];return t?{...t}:{}}getToolSetting(e,t){return this.config.profiles?.[this.getActiveProfileName()]?.tool_settings?.[e]?.[t]}setToolSetting(e,t,i){if(this.getToolSetting(e,t)===i)return;const o=this.getActiveProfileName(),s=this.config.profiles?.[o]||{},r={...s.tool_settings||{}};r[e]={...r[e]||{},[t]:i};const n={...this.config.profiles||{},[o]:{...s,tool_settings:r}};this.saveConfig({...this.config,profiles:n})}getCustomPresets(){return{...this.config.presets||{}}}saveCustomPreset(e,t){const i={...this.config.presets||{},[e]:{models:this.normalizeModels(t),updated_at:(new Date).toISOString()}};this.saveConfig({...this.config,presets:i})}deleteCustomPreset(e){const t={...this.config.presets||{}};t[e]&&(delete t[e],this.saveConfig({...this.config,presets:t}))}listBackups(){const t=this.getBackupDir();return e(t)?n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>{const i=f(t,e);return{name:e,path:i,mtimeMs:c(i).mtimeMs}}).sort((e,t)=>t.mtimeMs-e.mtimeMs):[]}createBackup(s){this.ensureConfigDir();const r=this.getBackupDir();e(r)||t(r,{recursive:!0});const n=(new Date).toISOString().replace(/[:.]/g,"-"),a=this.sanitizeBackupLabel(s),c=f(r,a?`config-${n}-${a}.yaml`:`config-${n}.yaml`),l=e(this.configPath)?i(this.configPath,"utf-8"):m.dump(this.prepareForPersistence(this.getConfig()));return o(c,l,{encoding:"utf-8",mode:384}),this.rotateBackups(r,10,30),c}restoreBackup(t){if(!t||"."===t||".."===t||/[/\\]|\.\./.test(t))throw new Error(`Invalid backup name: ${t}`);const o=this.getBackupDir(),s=f(o,t);if(!e(s))throw new Error(`Backup not found: ${t}`);const r=i(s,"utf-8"),n=m.load(r),a=this.getSecretsBackupPath(s);let c=this.migrateOldConfig(n||this.createDefaultConfig());if(n?.credential_store!==k&&e(a))try{c=this.mergeLegacySecrets(c,_(a))}catch(e){throw new Error(`Legacy backup credentials could not be decrypted: ${a}`,{cause:e})}this.saveConfig(c)}getBaseUrl(){return this.runtimeBaseUrl||this.config.profiles?.[this.getActiveProfileName()]?.base_url||DEFAULT_BASE_URL}setBaseUrl(e,t=!1){this.runtimeBaseUrl=e,t&&this.updateProfile(this.getActiveProfileName(),{base_url:e})}resetBaseUrl(){this.runtimeBaseUrl=void 0;const e=this.config.profiles?.[this.getActiveProfileName()]||{},{base_url:t,...i}=e,o={...this.config.profiles||{},[this.getActiveProfileName()]:i};this.saveConfig({...this.config,profiles:o})}}export const configManager=ConfigManager.getInstance();
1
+ import{existsSync as e,mkdirSync as t,readFileSync as i,writeFileSync as o,renameSync as s,unlinkSync as r,readdirSync as n,rmdirSync as a,statSync as c,chmodSync as l}from"fs";import{join as f,dirname as g}from"path";import{homedir as h}from"os";import{createHash as p}from"crypto";import*as m from"js-yaml";import{logger as u}from"../utils/logger.js";import{getDefaultModels as y,isRetiredDefaultModel as d}from"./model-registry.js";import{readLegacySecretsFile as _}from"./secret-store.js";export const DEFAULT_BASE_URL="https://www.vibeapi.cn";export const DEFAULT_MODELS=y();export const DEFAULT_PROFILE_NAME="default";const k="plaintext";export class ConfigManager{static instance;configDir;configPath;config;runtimeBaseUrl;constructor(){this.configDir=this.resolveConfigDir(),this.migrateLegacyConfigDir(),this.configPath=f(this.configDir,"config.yaml"),this.config=this.loadConfig()}migrateLegacyConfigDir(){try{const t=f(g(this.configDir),".chelper");if(t===this.configDir||!e(t))return;if(["config.yaml","secrets.enc"].some(t=>e(f(this.configDir,t))))return;if(e(this.configDir)){for(const i of n(t)){const o=f(this.configDir,i);e(o)||s(f(t,i),o)}0===n(t).length&&a(t)}else s(t,this.configDir);u.record(`Migrated config dir: ${t} -> ${this.configDir}`)}catch(e){u.warn("Failed to migrate legacy .chelper config dir:",e)}}resolveConfigDir(){const e=process.env.VIBECLI_CONFIG_DIR;if(e&&e.trim())return e.trim();const t=process.env.VIBECLI_TEST_HOME;return t&&t.trim()?f(t.trim(),".vibecli"):f(h(),".vibecli")}createDefaultConfig(){return this.syncLegacyFields({lang:"en_US",active_profile:"default",profiles:{[DEFAULT_PROFILE_NAME]:{}},presets:{}})}normalizeModels(e){const t=t=>{const i=e?.[t]?.trim();return i&&!d(t,i)?i:DEFAULT_MODELS[t]},i={haiku:t("haiku"),sonnet:t("sonnet"),opus:t("opus"),reasoning:t("reasoning")};if(e&&"object"==typeof e)for(const[t,o]of Object.entries(e))t in i||"string"==typeof o&&o.trim()&&(i[t]=o.trim());return i}normalizeToolModels(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i={};for(const[e,o]of Object.entries(t))"string"==typeof o&&o.trim()&&(i[e]=o.trim());return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeToolSettings(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||!t||"object"!=typeof t||Array.isArray(t))return[];const i=Object.fromEntries(Object.entries(t).filter(([,e])=>"string"==typeof e||"boolean"==typeof e||"number"==typeof e&&Number.isFinite(e)));return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeStringRecord(e){return!e||"object"!=typeof e||Array.isArray(e)?{}:Object.fromEntries(Object.entries(e).filter(e=>"string"==typeof e[1]&&Boolean(e[1].trim())).map(([e,t])=>[e,t.trim()]))}normalizeProfile(e){if(!e)return{};const t=this.normalizeToolModels(e.tool_models),i=this.normalizeToolSettings(e.tool_settings),o=this.normalizeStringRecord(e.tool_api_keys);return{..."string"==typeof e.api_key&&e.api_key?{api_key:e.api_key}:{},...Object.keys(o).length>0?{tool_api_keys:o}:{},..."string"==typeof e.base_url&&e.base_url?{base_url:e.base_url}:{},...e.models?{models:this.normalizeModels(e.models)}:{},...Object.keys(t).length>0?{tool_models:t}:{},...Object.keys(i).length>0?{tool_settings:i}:{}}}normalizeProfiles(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>e&&"string"==typeof e&&t&&"object"==typeof t?[[e,this.normalizeProfile(t)]]:[]);return Object.fromEntries(t)}normalizePresets(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i=t;return i.models?[[e,{models:this.normalizeModels(i.models),updated_at:i.updated_at||new Date(0).toISOString()}]]:[]});return Object.fromEntries(t)}getResolvedActiveProfile(e){const t=e.active_profile||"default";return e.profiles?.[t]||{}}syncLegacyFields(e){const t=this.normalizeProfiles(e.profiles),i=e.active_profile?.trim()||Object.keys(t)[0]||"default",o=t[i]?t:{...t,[i]:{}},s=this.getResolvedActiveProfile({...e,profiles:o,active_profile:i});return{...e,lang:e.lang||"en_US",active_profile:i,profiles:o,presets:this.normalizePresets(e.presets),api_key:s.api_key,base_url:s.base_url,models:s.models?this.normalizeModels(s.models):DEFAULT_MODELS}}sanitizeBackupLabel(e){return(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}getBackupDir(){return f(this.configDir,"backups")}getLegacySecretsPath(){return f(this.configDir,"secrets.enc")}getSecretsBackupPath(e){return e.replace(/\.yaml$/,".secrets.enc")}static getInstance(){return ConfigManager.instance||(ConfigManager.instance=new ConfigManager),ConfigManager.instance}ensureConfigDir(){e(this.configDir)||t(this.configDir,{recursive:!0})}loadConfig(){try{if(e(this.configPath)){const t=i(this.configPath,"utf-8"),o=m.load(t);let s=this.migrateOldConfig(o||{lang:"en_US"}),n=!1;const a=this.getLegacySecretsPath();if(e(a)&&o?.credential_store!==k)try{s=this.mergeLegacySecrets(s,_(a)),n=!0}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecrets",e)}if((this.needsMigration(o)||n)&&(this.backupConfig(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(s))),n))try{r(a)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}try{l(this.configPath,384)}catch{}return s}const t=this.getLegacySecretsPath();if(e(t))try{const e=this.mergeLegacySecrets(this.createDefaultConfig(),_(t));this.ensureConfigDir(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(e)));try{r(t)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}return e}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecretsWithoutConfig",e)}}catch(e){u.warn("Failed to load config, using defaults:",e),u.logError("ConfigManager.loadConfig",e)}return this.createDefaultConfig()}needsMigration(e){return!(!e||"object"!=typeof e)&&("plan"in e||!("profiles"in e)||!("active_profile"in e)||!("presets"in e))}migrateOldConfig(e){if(!e||"object"!=typeof e)return this.createDefaultConfig();const t=e,{plan:i,...o}=t,s=this.normalizeProfiles(o.profiles),r=this.normalizeProfile({api_key:"string"==typeof o.api_key?o.api_key:void 0,base_url:"string"==typeof o.base_url?o.base_url:void 0,models:o.models}),n=Object.keys(s).length>0?s:{[DEFAULT_PROFILE_NAME]:r},a="string"==typeof o.active_profile&&o.active_profile.trim()?o.active_profile.trim():Object.keys(n)[0]||"default";return this.syncLegacyFields({lang:"string"==typeof o.lang&&o.lang?o.lang:"en_US",...!0===o.lang_chosen?{lang_chosen:!0}:{},...o.credential_store===k?{credential_store:k}:{},active_profile:a,profiles:n,presets:this.normalizePresets(o.presets)})}atomicWrite(t,i){const n=`${t}.tmp`;try{o(n,i,{encoding:"utf-8",mode:384}),s(n,t);try{l(t,384)}catch{}}catch(t){if(e(n))try{r(n)}catch{}throw t}}backupConfig(){if(!e(this.configPath))return;const s=this.getBackupDir();e(s)||t(s,{recursive:!0});const r=(new Date).toISOString().replace(/[:.]/g,"-"),n=f(s,`config-${r}.yaml`);try{const e=i(this.configPath,"utf-8");o(n,e,{encoding:"utf-8",mode:384}),this.rotateBackups(s,10,30)}catch(e){u.logError("ConfigManager.backupConfig",e)}}rotateBackups(t,i,o){try{const s=Date.now()-24*o*60*60*1e3,a=n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>({name:e,path:f(t,e),mtime:c(f(t,e)).mtime})).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()),l=new Set;a.slice(i).forEach(e=>l.add(e.path)),a.forEach(e=>{e.mtime.getTime()<s&&l.add(e.path)});for(const t of l){let i=!1;try{r(t),i=!0}catch(e){u.logError("ConfigManager.rotateBackups",e)}if(i){const i=this.getSecretsBackupPath(t);if(e(i))try{r(i)}catch(e){u.logError("ConfigManager.rotateBackups.secrets",e)}}}}catch(e){u.logError("ConfigManager.rotateBackups",e)}}saveConfig(e){try{this.ensureConfigDir(),this.backupConfig();const t=this.syncLegacyFields(this.migrateOldConfig(e||this.config)),i=this.prepareForPersistence(t),o=m.dump(i);this.atomicWrite(this.configPath,o),this.config=t}catch(e){throw u.error("Failed to save config:",e),u.logError("ConfigManager.saveConfig",e),e}}prepareForPersistence(e){const t={...e,credential_store:k};return delete t.api_key,delete t.base_url,delete t.models,t}mergeLegacySecrets(e,t){const i={...e.profiles||{}},o=new Set([...Object.keys(t.api_keys||{}),...Object.keys(t.tool_api_keys||{})]);for(const e of o){const o=i[e]||{},s=o.api_key||t.api_keys?.[e],r={...t.tool_api_keys?.[e]||{},...o.tool_api_keys||{}};i[e]={...o,...s?{api_key:s}:{},...Object.keys(r).length>0?{tool_api_keys:r}:{}}}return this.syncLegacyFields({...e,profiles:i})}getConfig(){return this.syncLegacyFields({...this.config})}updateConfig(e){this.saveConfig({...this.config,...e})}isFirstRun(){return!e(this.configPath)}getLang(){return this.config.lang||"en_US"}isLangChosen(){return!0===this.config.lang_chosen}setLang(e){this.updateConfig({lang:e,lang_chosen:!0})}getActiveProfileName(){return this.config.active_profile||"default"}listProfiles(){return Object.keys(this.config.profiles||{}).sort()}getProfile(e=this.getActiveProfileName()){const t=this.config.profiles?.[e]||{};return{...t.api_key?{api_key:t.api_key}:{},...t.tool_api_keys&&Object.keys(t.tool_api_keys).length>0?{tool_api_keys:this.normalizeStringRecord(t.tool_api_keys)}:{},...t.base_url?{base_url:t.base_url}:{},models:t.models?this.normalizeModels(t.models):DEFAULT_MODELS,...t.tool_models&&Object.keys(t.tool_models).length>0?{tool_models:this.normalizeToolModels(t.tool_models)}:{},...t.tool_settings&&Object.keys(t.tool_settings).length>0?{tool_settings:this.normalizeToolSettings(t.tool_settings)}:{}}}createProfile(e,t=this.getActiveProfileName()){const i=this.getProfile(t),o={...this.config.profiles||{},[e]:{...i.api_key?{api_key:i.api_key}:{},...i.base_url?{base_url:i.base_url}:{},models:this.normalizeModels(i.models)}};this.saveConfig({...this.config,profiles:o})}setActiveProfile(e){if(!this.config.profiles?.[e])throw new Error(`Profile not found: ${e}`);this.saveConfig({...this.config,active_profile:e})}updateProfile(e,t){const i=this.config.profiles?.[e]||{},o=t.models?this.normalizeModels({...i.models,...t.models}):i.models?this.normalizeModels(i.models):void 0,s={...i,...void 0!==t.api_key?{api_key:t.api_key}:{},...void 0!==t.base_url?{base_url:t.base_url}:{},...o?{models:o}:{}},r={...this.config.profiles||{},[e]:s};this.saveConfig({...this.config,profiles:r})}deleteProfile(e){const t={...this.config.profiles||{}};if(!t[e])return;const i=Object.keys(t).filter(t=>t!==e);if(0===i.length)throw new Error("At least one profile must remain");delete t[e];const o=this.getActiveProfileName()===e?i[0]:this.getActiveProfileName();this.saveConfig({...this.config,profiles:t,active_profile:o})}getApiKey(){return this.config.profiles?.[this.getActiveProfileName()]?.api_key}setApiKey(e){this.updateProfile(this.getActiveProfileName(),{api_key:e})}revokeApiKey(){const e=this.config.profiles?.[this.getActiveProfileName()]||{},{api_key:t,tool_api_keys:i,...o}=e,s={...this.config.profiles||{},[this.getActiveProfileName()]:o};this.saveConfig({...this.config,profiles:s})}getApiKeyFingerprint(){const e=this.getApiKey();return e?p("sha256").update(e).digest("hex").slice(0,12):""}getApiKeyForTool(e){const t=this.config.profiles?.[this.getActiveProfileName()];return t?.tool_api_keys?.[e]||t?.api_key||void 0}setToolApiKey(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...this.config.profiles||{},[i]:{...o,tool_api_keys:{...o.tool_api_keys||{},[e]:t}}};this.saveConfig({...this.config,profiles:s})}clearToolApiKey(e){const t=this.getActiveProfileName(),i=this.config.profiles?.[t]||{},{[e]:o,...s}=i.tool_api_keys||{},r={...i};Object.keys(s).length>0?r.tool_api_keys=s:delete r.tool_api_keys,this.saveConfig({...this.config,profiles:{...this.config.profiles||{},[t]:r}})}listToolApiKeys(){return Object.keys(this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys||{}).sort()}getToolApiKeyFingerprint(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys?.[e];return t?p("sha256").update(t).digest("hex").slice(0,12):""}getModels(){const e=this.config.profiles?.[this.getActiveProfileName()]?.models;return e?this.normalizeModels(e):DEFAULT_MODELS}setModels(e){this.updateProfile(this.getActiveProfileName(),{models:this.normalizeModels(e)})}updateModels(e){this.setModels(this.normalizeModels({...this.getModels(),...e}))}getModelsForTool(e){const t=this.getModels(),i=this.config.profiles?.[this.getActiveProfileName()]?.tool_models?.[e];return i?this.normalizeModels({...t,...i}):t}setToolModel(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...o.tool_models||{}};t&&0!==Object.keys(t).length?s[e]=t:delete s[e];const r={...o};Object.keys(s).length>0?r.tool_models=s:delete r.tool_models;const n={...this.config.profiles||{},[i]:r};this.saveConfig({...this.config,profiles:n})}getToolSettings(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_settings?.[e];return t?{...t}:{}}getToolSetting(e,t){return this.config.profiles?.[this.getActiveProfileName()]?.tool_settings?.[e]?.[t]}setToolSetting(e,t,i){if(this.getToolSetting(e,t)===i)return;const o=this.getActiveProfileName(),s=this.config.profiles?.[o]||{},r={...s.tool_settings||{}};r[e]={...r[e]||{},[t]:i};const n={...this.config.profiles||{},[o]:{...s,tool_settings:r}};this.saveConfig({...this.config,profiles:n})}getCustomPresets(){return{...this.config.presets||{}}}saveCustomPreset(e,t){const i={...this.config.presets||{},[e]:{models:this.normalizeModels(t),updated_at:(new Date).toISOString()}};this.saveConfig({...this.config,presets:i})}deleteCustomPreset(e){const t={...this.config.presets||{}};t[e]&&(delete t[e],this.saveConfig({...this.config,presets:t}))}listBackups(){const t=this.getBackupDir();return e(t)?n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>{const i=f(t,e);return{name:e,path:i,mtimeMs:c(i).mtimeMs}}).sort((e,t)=>t.mtimeMs-e.mtimeMs):[]}createBackup(s){this.ensureConfigDir();const r=this.getBackupDir();e(r)||t(r,{recursive:!0});const n=(new Date).toISOString().replace(/[:.]/g,"-"),a=this.sanitizeBackupLabel(s),c=f(r,a?`config-${n}-${a}.yaml`:`config-${n}.yaml`),l=e(this.configPath)?i(this.configPath,"utf-8"):m.dump(this.prepareForPersistence(this.getConfig()));return o(c,l,{encoding:"utf-8",mode:384}),this.rotateBackups(r,10,30),c}restoreBackup(t){if(!t||"."===t||".."===t||/[/\\]|\.\./.test(t))throw new Error(`Invalid backup name: ${t}`);const o=this.getBackupDir(),s=f(o,t);if(!e(s))throw new Error(`Backup not found: ${t}`);const r=i(s,"utf-8"),n=m.load(r),a=this.getSecretsBackupPath(s);let c=this.migrateOldConfig(n||this.createDefaultConfig());if(n?.credential_store!==k&&e(a))try{c=this.mergeLegacySecrets(c,_(a))}catch(e){throw new Error(`Legacy backup credentials could not be decrypted: ${a}`,{cause:e})}this.saveConfig(c)}getConfigDir(){return this.configDir}getBaseUrl(){return this.runtimeBaseUrl||this.config.profiles?.[this.getActiveProfileName()]?.base_url||DEFAULT_BASE_URL}setBaseUrl(e,t=!1){this.runtimeBaseUrl=e,t&&this.updateProfile(this.getActiveProfileName(),{base_url:e})}resetBaseUrl(){this.runtimeBaseUrl=void 0;const e=this.config.profiles?.[this.getActiveProfileName()]||{},{base_url:t,...i}=e,o={...this.config.profiles||{},[this.getActiveProfileName()]:i};this.saveConfig({...this.config,profiles:o})}}export const configManager=ConfigManager.getInstance();
@@ -1,7 +1,7 @@
1
1
  import { type GatewayModelSpec } from './model-registry.js';
2
2
  import type { GatewayClientModel } from './api-validator.js';
3
3
  /**
4
- * Gateway models (Claude, domestic, Grok) vibecli adds to the coding tools'
4
+ * Gateway models (Claude, GPT, Gemini, domestic, Grok) vibecli adds to the coding tools'
5
5
  * model menus, and the shared context rules: each model compacts at
6
6
  * `autoCompactRatio` (80%) of its window unless the gateway sets a point.
7
7
  */
@@ -1 +1 @@
1
- import{getGatewayModelRules as t}from"./model-registry.js";export function autoCompactTokens(e,o){return o&&o>0?Math.min(o,e):Math.floor(e*t().autoCompactRatio)}export function gatewayModelSpecs(e,o){const n=t().models.filter(t=>function(t,e){return"codex"===t||!e.id.startsWith("claude-")&&!1!==e.claudeCode}(e,t));if(!o)return n;const r=new Map(n.map(t=>[t.id,t])),s="codex"===e?[...o.codex??[],...(o.claude??[]).filter(t=>t.id.startsWith("claude-")&&r.has(t.id))]:(o.claude??[]).filter(t=>!t.id.startsWith("claude-")&&(r.get(t.id)?.claudeCode??!0));if(0===s.length)return n;const a=new Set,i=[];for(const t of s)a.has(t.id)||(a.add(t.id),i.push({...r.get(t.id),...t}));return i}const e={gpt:"GPT",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function prettyModelName(t){const o=t.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),n=[];for(const t of o){const o=n[n.length-1];if(/^\d+$/.test(t)&&o&&/^\d+$/.test(o)&&t.length<=2&&!/^\d{8}$/.test(t)){n[n.length-1]=`${o}.${t}`;continue}const r=t.toLowerCase();n.push(e[r]??(/^[a-z]\d/i.test(t)?t.toUpperCase():t[0].toUpperCase()+t.slice(1)))}return n.join(" ")}export function vendorNames(t){return[...new Set(t.map(t=>prettyModelName(t).split(" ")[0]))]}export function modelFamily(t){const e=t.toLowerCase();return e.startsWith("claude-")?"claude":e.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(e)?"gpt":"other"}
1
+ import{getGatewayModelRules as e}from"./model-registry.js";export function autoCompactTokens(t,o){return o&&o>0?Math.min(o,t):Math.floor(t*e().autoCompactRatio)}export function gatewayModelSpecs(t,o){const i=e().models,n=i.filter(e=>function(e,t){return"codex"===e?!1!==t.codex:!t.id.startsWith("claude-")&&!1!==t.claudeCode}(t,e));if(!o)return n;const r=new Map(("claude-code"===t?i:n).map(e=>[e.id,e])),d="codex"===t?[...o.codex??[],...(o.claude??[]).filter(e=>e.id.startsWith("claude-")&&r.has(e.id))]:function(e,t){return[...(e.codex??[]).filter(e=>"gpt"===modelFamily(e.id)),...e.gemini??[],...e.grok??[],...(e.claude??[]).filter(e=>!e.id.startsWith("claude-"))].filter(e=>t.get(e.id)?.claudeCode??!0)}(o,r);if(0===d.length)return n;const a=new Set,s=[];for(const e of d)a.has(e.id)||(a.add(e.id),s.push({...r.get(e.id),...e}));return s}const t={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function prettyModelName(e){const o=e.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),i=[];for(const e of o){const o=i[i.length-1];if(/^\d+$/.test(e)&&o&&/^\d+$/.test(o)&&e.length<=2&&!/^\d{8}$/.test(e)){i[i.length-1]=`${o}.${e}`;continue}const n=e.toLowerCase();i.push(t[n]??(/^[a-z]\d/i.test(e)?e.toUpperCase():e[0].toUpperCase()+e.slice(1)))}return i.join(" ")}export function vendorNames(e){return[...new Set(e.map(e=>prettyModelName(e).split(" ")[0]))]}export function modelFamily(e){const t=e.toLowerCase();return t.startsWith("claude-")?"claude":t.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(t)?"gpt":"other"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Any vibecli command also brings the coding tools' model menus up to date with
3
+ * the gateway's client config, so models the gateway adds or hides reach
4
+ * customers who never run `setup` again. Only menus vibecli wrote and that still
5
+ * use the key vibecli saved are touched; offline nothing changes.
6
+ */
7
+ export declare const MODEL_REFRESH_INTERVAL_MS: number;
8
+ export interface ModelMenuChange {
9
+ tool: string;
10
+ added: string[];
11
+ removed: string[];
12
+ }
13
+ export declare function shouldSkipModelRefresh(args: string[], env?: NodeJS.ProcessEnv): boolean;
14
+ /** Rewrites the menus that differ from the gateway's current list; returns what changed per tool. */
15
+ export declare function refreshModelMenus(): Promise<ModelMenuChange[]>;
16
+ /** Startup hook: at most once an hour, never fails the command it rides along with. */
17
+ export declare function runModelRefresh(args: string[], options?: {
18
+ env?: NodeJS.ProcessEnv;
19
+ now?: number;
20
+ output?: (line: string) => void;
21
+ }): Promise<ModelMenuChange[]>;
@@ -0,0 +1 @@
1
+ import{existsSync as e,mkdirSync as o,readFileSync as t,writeFileSync as r}from"fs";import{dirname as n,join as s}from"path";import i from"chalk";import{configManager as l}from"./config.js";import{i18n as d}from"./i18n.js";import{resolveModelCatalog as f}from"./model-catalog.js";import{listAdapters as c}from"./tools/registry.js";import{logger as a}from"../utils/logger.js";import{resolveUpdateCachePath as u}from"./auto-update.js";export const MODEL_REFRESH_INTERVAL_MS=36e5;const m=new Set(["setup","init","config","auth","enter","completion","update","lang","logs"]),h=["--json","--dry-run","--help","-h","--version","-v"];export function shouldSkipModelRefresh(e,o=process.env){if("1"===o.VIBECLI_DISABLE_MODEL_REFRESH)return!0;if(h.some(o=>e.includes(o)))return!0;const t=e.find(e=>!e.startsWith("-"));return void 0===t||m.has(t)}export async function refreshModelMenus(){const e=new Map,o=[];for(const t of c())if(t.refreshModelMenu&&t.listedModels)try{const r=l.getApiKeyForTool(t.name);if(!r||t.detectCurrentConfig().apiKey!==r)continue;const n=t.listedModels();if(null===n)continue;e.has(r)||e.set(r,await f(r));const s=e.get(r);if(!s)continue;t.refreshModelMenu({apiKey:r,baseUrl:l.getBaseUrl(),models:l.getModelsForTool(t.name),settings:l.getToolSettings(t.name),catalog:s});const i=t.listedModels()??[],d=i.filter(e=>!n.includes(e)),c=n.filter(e=>!i.includes(e));(d.length>0||c.length>0)&&o.push({tool:t.displayName,added:d,removed:c})}catch(e){a.logError(`modelRefresh(${t.name})`,e)}return o}export async function runModelRefresh(l,f={}){const c=f.env??process.env;if(shouldSkipModelRefresh(l,c))return[];const m=f.now??Date.now(),h=function(e){return s(n(u(e)),"model-refresh.json")}(c);if(m-function(e){try{const o=JSON.parse(t(e,"utf-8")).refreshed_at;return"number"==typeof o?o:0}catch{return 0}}(h)<36e5)return[];let p=[];try{p=await refreshModelMenus(),e(n(h))||o(n(h),{recursive:!0}),r(h,JSON.stringify({refreshed_at:m})+"\n")}catch(e){a.logError("runModelRefresh",e)}const g=f.output??(e=>console.log(i.gray(e)));for(const e of p){const o=[e.added.length>0?d.t("model_refresh.added",{models:e.added.join(", ")}):"",e.removed.length>0?d.t("model_refresh.removed",{models:e.removed.join(", ")}):""].filter(Boolean);g(d.t("model_refresh.changed",{tool:e.tool,changes:o.join(";")}))}return p}
@@ -47,11 +47,15 @@ export interface GatewayModelSpec {
47
47
  autoCompactTokens?: number;
48
48
  /** `false` keeps the model out of Claude Code's picker (its window differs from the rest). */
49
49
  claudeCode?: boolean;
50
+ /** `false` keeps the model out of Codex's catalog (GPT comes from Codex itself). */
51
+ codex?: boolean;
50
52
  }
51
53
  export interface GatewayModelRules {
52
54
  /** Auto-compact at this fraction of the context window unless a model sets its own limit. */
53
55
  autoCompactRatio: number;
54
56
  models: GatewayModelSpec[];
57
+ /** Ids vibecli used to add and has since taken off its lists; menus still holding them are vibecli's to update. */
58
+ retired: string[];
55
59
  }
56
60
  /** Codex-only catalog defaults. */
57
61
  export interface CodexRules {
@@ -1 +1 @@
1
- import{dirname as e,join as o}from"path";import{fileURLToPath as t}from"url";import{existsSync as n,readFileSync as r}from"fs";const l=e(t(import.meta.url));let s=null;function u(){return s||(s=function(){const e=[o(l,"..","..","models.json"),o(l,"..","models.json"),o(l,"models.json")];for(const o of e)if(n(o))return JSON.parse(r(o,"utf-8"));throw new Error(`models.json not found, searched: ${e.join(", ")}`)}()),s}export function getDefaultModels(){return{...u().defaultModels}}export function isRetiredDefaultModel(e,o){return u().retiredDefaultModels?.[e]?.includes(o)??!1}export function getDefaultToolModel(){return u().defaultToolModel?.trim()||"gpt-5.5"}export function getToolDefaultModel(e){const o=u().toolDefaults;return o&&Object.prototype.hasOwnProperty.call(o,e)?(o[e]??"").trim():getDefaultToolModel()}export function getPresets(){return[...u().presets]}export function resolvePreset(e){return e.models?{...e.models}:e.model?{haiku:e.model,sonnet:e.model,opus:e.model,reasoning:e.model}:getDefaultModels()}export function getClaudeCodeTierChains(){const e=u().claudeCode?.tierChains??{};return Object.fromEntries(Object.entries(e).map(([e,o])=>[e,[...o]]))}export function getClaudeDesktopRules(){const e=u().claudeDesktop??{};return{oneMillionFallback:[...e.oneMillionFallback??[]],oneMillionExclusions:(e.oneMillionExclusions??[]).map(e=>({...e,models:[...e.models]}))}}export function getGatewayModelRules(){const e=u().gatewayModels??{};return{autoCompactRatio:e.autoCompactRatio??.8,models:(e.models??[]).map(e=>({...e}))}}export function getCodexRules(){return{standardContextWindow:u().codex?.standardContextWindow??272e3}}export function compareVersions(e,o){const t=e.split(".").map(e=>parseInt(e,10)||0),n=o.split(".").map(e=>parseInt(e,10)||0);for(let e=0;e<Math.max(t.length,n.length);e++){const o=(t[e]??0)-(n[e]??0);if(0!==o)return o}return 0}export function getToolMinVersion(e){return u().toolMinVersions?.[e]?.trim()||null}export function reloadRegistry(){s=null}
1
+ import{dirname as e,join as o}from"path";import{fileURLToPath as t}from"url";import{existsSync as n,readFileSync as r}from"fs";const l=e(t(import.meta.url));let s=null;function i(){return s||(s=function(){const e=[o(l,"..","..","models.json"),o(l,"..","models.json"),o(l,"models.json")];for(const o of e)if(n(o))return JSON.parse(r(o,"utf-8"));throw new Error(`models.json not found, searched: ${e.join(", ")}`)}()),s}export function getDefaultModels(){return{...i().defaultModels}}export function isRetiredDefaultModel(e,o){return i().retiredDefaultModels?.[e]?.includes(o)??!1}export function getDefaultToolModel(){return i().defaultToolModel?.trim()||"gpt-5.5"}export function getToolDefaultModel(e){const o=i().toolDefaults;return o&&Object.prototype.hasOwnProperty.call(o,e)?(o[e]??"").trim():getDefaultToolModel()}export function getPresets(){return[...i().presets]}export function resolvePreset(e){return e.models?{...e.models}:e.model?{haiku:e.model,sonnet:e.model,opus:e.model,reasoning:e.model}:getDefaultModels()}export function getClaudeCodeTierChains(){const e=i().claudeCode?.tierChains??{};return Object.fromEntries(Object.entries(e).map(([e,o])=>[e,[...o]]))}export function getClaudeDesktopRules(){const e=i().claudeDesktop??{};return{oneMillionFallback:[...e.oneMillionFallback??[]],oneMillionExclusions:(e.oneMillionExclusions??[]).map(e=>({...e,models:[...e.models]}))}}export function getGatewayModelRules(){const e=i().gatewayModels??{};return{autoCompactRatio:e.autoCompactRatio??.8,models:(e.models??[]).map(e=>({...e})),retired:[...e.retired??[]]}}export function getCodexRules(){return{standardContextWindow:i().codex?.standardContextWindow??272e3}}export function compareVersions(e,o){const t=e.split(".").map(e=>parseInt(e,10)||0),n=o.split(".").map(e=>parseInt(e,10)||0);for(let e=0;e<Math.max(t.length,n.length);e++){const o=(t[e]??0)-(n[e]??0);if(0!==o)return o}return 0}export function getToolMinVersion(e){return i().toolMinVersions?.[e]?.trim()||null}export function reloadRegistry(){s=null}
@@ -3,7 +3,8 @@ export declare function setUpgradeRunnerForTests(fake: (command: string, args: s
3
3
  /**
4
4
  * `vibecli upgrade <tool>`: upgrade the copy PATH runs the way it was installed
5
5
  * (vendor updater / npm via npmmirror / Homebrew). When the vendor updater
6
- * can't connect, fall back to the npm mirror, then leave a single copy on PATH.
6
+ * can't connect, a native install gets the same binary from the mirror in place;
7
+ * failing that, install from the npm mirror and leave a single copy on PATH.
7
8
  * Returns whether the tool ends up at or above `toolMinVersions`.
8
9
  */
9
10
  export declare function upgradeTool(toolName: string): Promise<boolean>;
@@ -1 +1 @@
1
- import{spawnSync as e}from"child_process";import{rmSync as o}from"fs";import{join as r}from"path";import t from"chalk";import n from"inquirer";import{i18n as s}from"./i18n.js";import{logger as l}from"../utils/logger.js";import{compareVersions as i,getToolMinVersion as a}from"./model-registry.js";import{getAdapter as p}from"./tools/registry.js";import{findInstalls as c,forgetInstalls as u,installLabel as m,npmMirrorInstallCommand as d,npmUninstallCommand as g}from"./tools/tool-installs.js";import{toolHome as f}from"./tools/types.js";import{isNonInteractive as h,shouldAutoConfirm as w}from"./runtime-options.js";let y=(o,r)=>{console.log(t.gray("$ ")+[o,...r].join(" "));const n="win32"!==process.platform||/\.exe$/i.test(o)?e(o,r,{stdio:"inherit"}):e([o,...r].map(e=>/\s/.test(e)?`"${e}"`:e).join(" "),{stdio:"inherit",shell:!0});return n.error&&l.logError(`upgrade: ${o}`,n.error),0===n.status};export function setUpgradeRunnerForTests(e){y=e}function v(e){const[o,...r]=d(e).split(" "),n=y(o,r);return n||"win32"===process.platform||console.log(t.yellow(" ! "+s.t("upgrade.npm_permission",{command:d(e)}))),n}function b(e,t){switch(t.kind){case"npm":y("npm",["uninstall","-g",e.npmPackage]);break;case"brew":e.brew&&y("brew",["uninstall",e.brew]);break;case"native":try{o(t.path,{force:!0});for(const t of e.native?.dataDirs??[])o(r(f(),...t),{recursive:!0,force:!0})}catch(e){l.logError("upgrade: remove native copy",e)}break;default:return!1}return u(),!c(e).some(e=>e.path===t.path)}function k(e,o){switch(o.kind){case"npm":return g(e);case"brew":return`brew uninstall ${e.brew}`;case"native":return s.t("upgrade.delete_paths",{paths:[o.path,...(e.native?.dataDirs??[]).map(e=>r(f(),...e))].join("、")});default:return s.t("upgrade.delete_paths",{paths:o.path})}}export async function upgradeTool(e){const o=p(e),r=o?.installSpec;if(!o||!r)throw new Error(s.t("upgrade.unsupported",{tool:e}));const l=o.displayName;u();const d=c(r);if(console.log(t.bold(`\n${l}`)),0===d.length)console.log(s.t("upgrade.not_installed_installing",{tool:l})),v(r);else{const e=d[0];console.log(t.gray(" "+s.t("upgrade.current",{copy:m(e)})));const o=function(e,o){switch(o.kind){case"native":return y(o.path,e.native?.updateArgs??["update"]);case"npm":return v(e);case"brew":return!!e.brew&&y("brew",["upgrade",e.brew]);default:return!1}}(r,e);o||"other"!==e.kind?o||"npm"===e.kind||(console.log(t.yellow(" ! "+s.t("upgrade.fallback_mirror"))),v(r)):console.log(t.yellow(" ! "+s.t("upgrade.unknown_install",{path:e.path})))}u(),await async function(e,o){const r=c(e);if(r.length<2)return;const l=function(e){return e.reduce((e,o)=>o.version&&(!e.version||i(o.version,e.version)>0)?o:e)}(r),a=r.filter(e=>e!==l);console.log(t.yellow(" ! "+s.t("upgrade.duplicates",{tool:o,keep:m(l),others:a.map(m).join("、")})));let p=w();p||h()||!process.stdin.isTTY||(p=(await n.prompt([{type:"confirm",name:"remove",message:s.t("upgrade.confirm_remove"),default:!0}])).remove);for(const o of a)p&&b(e,o)?console.log(t.green(" √ "+s.t("upgrade.removed",{copy:m(o)}))):console.log(t.yellow(" ! "+s.t("upgrade.remove_by_hand",{how:k(e,o)})))}(r,l),u();const g=c(r)[0];if(!g)return console.log(t.red(" × "+s.t("upgrade.still_missing",{tool:l}))),!1;const f=a(e);if(f&&g.version&&i(g.version,f)<0){console.log(t.yellow(" ! "+s.t("upgrade.still_old",{copy:m(g),min:f})));for(const e of o.upgradeHints?.(f)??[])console.log(t.yellow(" ! "+e));return!1}return console.log(t.green(" √ "+s.t("upgrade.done",{copy:m(g)}))),!0}
1
+ import{spawnSync as e}from"child_process";import{rmSync as r}from"fs";import{join as o}from"path";import t from"chalk";import n from"inquirer";import{i18n as s}from"./i18n.js";import{logger as i}from"../utils/logger.js";import{compareVersions as a,getToolMinVersion as l}from"./model-registry.js";import{getAdapter as p}from"./tools/registry.js";import{findInstalls as c,forgetInstalls as u,installLabel as g,npmMirrorInstallCommand as d,npmUninstallCommand as m}from"./tools/tool-installs.js";import{toolHome as f}from"./tools/types.js";import{isNonInteractive as w,shouldAutoConfirm as h}from"./runtime-options.js";let v=(r,o)=>{console.log(t.gray("$ ")+[r,...o].join(" "));const n="win32"!==process.platform||/\.exe$/i.test(r)?e(r,o,{stdio:"inherit"}):e([r,...o].map(e=>/\s/.test(e)?`"${e}"`:e).join(" "),{stdio:"inherit",shell:!0});return n.error&&i.logError(`upgrade: ${r}`,n.error),0===n.status};export function setUpgradeRunnerForTests(e){v=e}function y(e){const[r,...o]=d(e).split(" "),n=v(r,o);return n||"win32"===process.platform||console.log(t.yellow(" ! "+s.t("upgrade.npm_permission",{command:d(e)}))),n}function _(e,t){switch(t.kind){case"npm":v("npm",["uninstall","-g",e.npmPackage]);break;case"brew":e.brew&&v("brew",["uninstall",e.brew]);break;case"native":try{r(t.path,{force:!0});for(const t of e.native?.dataDirs??[])r(o(f(),...t),{recursive:!0,force:!0})}catch(e){i.logError("upgrade: remove native copy",e)}break;default:return!1}return u(),!c(e).some(e=>e.path===t.path)}function b(e,r){switch(r.kind){case"npm":return m(e);case"brew":return`brew uninstall ${e.brew}`;case"native":return s.t("upgrade.delete_paths",{paths:[r.path,...(e.native?.dataDirs??[]).map(e=>o(f(),...e))].join("、")});default:return s.t("upgrade.delete_paths",{paths:r.path})}}export async function upgradeTool(e){const r=p(e),o=r?.installSpec;if(!r||!o)throw new Error(s.t("upgrade.unsupported",{tool:e}));const d=r.displayName;u();const m=c(o);if(console.log(t.bold(`\n${d}`)),0===m.length)console.log(s.t("upgrade.not_installed_installing",{tool:d})),y(o);else{const e=m[0];console.log(t.gray(" "+s.t("upgrade.current",{copy:g(e)})));const r=function(e,r){switch(r.kind){case"native":return v(r.path,e.native?.updateArgs??["update"]);case"npm":return y(e);case"brew":return!!e.brew&&v("brew",["upgrade",e.brew]);default:return!1}}(o,e);r||"other"!==e.kind?r||"npm"===e.kind||"native"===e.kind&&await async function(e){const r=e.native?.mirrorUpdate;if(!r)return!1;console.log(t.yellow(" ! "+s.t("upgrade.native_mirror_trying")));try{const{version:e,manifestVerified:o}=await r();return console.log(t.green(" √ "+s.t(o?"upgrade.native_mirror_done_verified":"upgrade.native_mirror_done",{version:e}))),!0}catch(e){return i.logError("upgrade: native mirror",e),console.log(t.yellow(" ! "+s.t("upgrade.native_mirror_failed",{reason:e instanceof Error?e.message:String(e)}))),!1}}(o)||(console.log(t.yellow(" ! "+s.t("upgrade.fallback_mirror"))),y(o)):console.log(t.yellow(" ! "+s.t("upgrade.unknown_install",{path:e.path})))}u(),await async function(e,r){const o=c(e);if(o.length<2)return;const i=function(e){return e.reduce((e,r)=>r.version&&(!e.version||a(r.version,e.version)>0)?r:e)}(o),l=o.filter(e=>e!==i);console.log(t.yellow(" ! "+s.t("upgrade.duplicates",{tool:r,keep:g(i),others:l.map(g).join("、")})));let p=h();p||w()||!process.stdin.isTTY||(p=(await n.prompt([{type:"confirm",name:"remove",message:s.t("upgrade.confirm_remove"),default:!0}])).remove);for(const r of l)p&&_(e,r)?console.log(t.green(" √ "+s.t("upgrade.removed",{copy:g(r)}))):console.log(t.yellow(" ! "+s.t("upgrade.remove_by_hand",{how:b(e,r)})))}(o,d),u();const f=c(o)[0];if(!f)return console.log(t.red(" × "+s.t("upgrade.still_missing",{tool:d}))),!1;const k=l(e);if(k&&f.version&&a(f.version,k)<0){console.log(t.yellow(" ! "+s.t("upgrade.still_old",{copy:g(f),min:k})));for(const e of r.upgradeHints?.(k)??[])console.log(t.yellow(" ! "+e));return!1}return console.log(t.green(" √ "+s.t("upgrade.done",{copy:g(f)}))),!0}
@@ -1 +1 @@
1
- import{join as e}from"path";import{claudeCodeManager as t}from"../claude-code-manager.js";import{backupBeforeWrite as o}from"./backup.js";import{i18n as a}from"../i18n.js";import{gatewayModelSpecs as n,prettyModelName as s,vendorNames as d}from"../gateway-models.js";import{toolHome as i}from"./types.js";import{spawnSync as c}from"child_process";import{readFileSync as l}from"fs";import{detectManagedOverrides as r}from"./claude-managed.js";const u=e(i(),".claude","settings.json"),m=e(i(),".claude.json");let p;export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",upgradeCommand:"claude update",installSpec:{command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]]},brew:"claude-code"},upgradeHints:t=>"stable"===function(){try{const t=JSON.parse(l(e(i(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}()?[a.t("tools.claude_code.stable_channel",{min:t})]:[],configPath:u,wantsModelCatalog:!0,loadPrivateConfig(e){o(u),o(m),p=function(e){if(!e)return;const t=new Set(e.models);return n("claude-code",e.clientModels).filter(e=>t.has(e.id))}(e.catalog),t.loadPrivateConfig(e.apiKey,e.models,e.catalog?.models,p)},unloadPrivateConfig(){o(u),t.unloadPrivateConfig()},detectCurrentConfig:()=>t.detectCurrentConfig(),getConfig:()=>({settings:t.getSettings(),mcp:t.getMCPConfig()}),updateConfig(e){const o=e||{};o.settings&&t.saveSettings(o.settings),o.mcp&&t.saveMCPConfig(o.mcp)},clearPinnedModels:e=>(o(u),t.clearPinnedModels(e?.models)),applyNotes(){const e=[];p&&p.length>0&&(e.push({kind:"summary",text:a.t("tools.claude_code.gateway_models",{count:String(p.length),vendors:d(p.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:p.map(e=>e.name||s(e.id)).join(", ")})),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===c("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:a.t("tools.claude_code.no_git_bash")});for(const t of r())e.push({kind:"action",text:a.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const o of t.getLastTierFallbacks())e.push({kind:"detail",text:a.t("tools.claude_code.tier_fallback",{tier:o.tier,from:o.from,to:o.to})});return e},backupPaths:()=>[u,m],forceResetPaths:()=>[u]};
1
+ import{join as e}from"path";import{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as a}from"./backup.js";import{i18n as n}from"../i18n.js";import{baseModelId as s}from"../model-catalog.js";import{gatewayModelSpecs as i,prettyModelName as r,vendorNames as d}from"../gateway-models.js";import{toolHome as l}from"./types.js";import{spawnSync as c}from"child_process";import{readFileSync as m}from"fs";import{detectManagedOverrides as u}from"./claude-managed.js";import{updateNativeFromMirror as p}from"./claude-native-mirror.js";const g=e(l(),".claude","settings.json"),f=e(l(),".claude.json");let C;function h(){try{const t=JSON.parse(m(e(l(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function _(e){if(!e)return;const t=new Set(e.models);return i("claude-code",e.clientModels).filter(e=>t.has(e.id))}export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",upgradeCommand:"claude update",installSpec:{command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>p(h())},brew:"claude-code"},upgradeHints:e=>"stable"===h()?[n.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:g,wantsModelCatalog:!0,loadPrivateConfig(e){a(g),a(f),C=_(e.catalog),o.loadPrivateConfig(e.apiKey,e.models,e.catalog?.models,C)},unloadPrivateConfig(){a(g),o.unloadPrivateConfig()},detectCurrentConfig:()=>o.detectCurrentConfig(),getConfig:()=>({settings:o.getSettings(),mcp:o.getMCPConfig()}),updateConfig(e){const t=e||{};t.settings&&o.saveSettings(t.settings),t.mcp&&o.saveMCPConfig(t.mcp)},clearPinnedModels:e=>(a(g),o.clearPinnedModels(e?.models)),listedModels(){const e=o.getSettings().modelPicker;return Array.isArray(e?.options)?e.options.map(e=>s(String(e.model))):null},refreshModelMenu(e){const n=o.getSettings(),s=t(n,_(e.catalog));JSON.stringify(s)!==JSON.stringify(n)&&(a(g),o.saveSettings(s))},applyNotes(){const e=[];C&&C.length>0&&(e.push({kind:"summary",text:n.t("tools.claude_code.gateway_models",{count:String(C.length),vendors:d(C.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:C.map(e=>e.name||r(e.id)).join(", ")})),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===c("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:n.t("tools.claude_code.no_git_bash")});for(const t of u())e.push({kind:"action",text:n.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:n.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[g,f],forceResetPaths:()=>[g]};
@@ -1 +1 @@
1
- import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{canRestartDesktop as y,isDesktopRunning as k,restartDesktop as b,WINDOWS_PACKAGE_FAMILIES as C}from"./desktop-process.js";import{baseModelId as A,isClaudeModel as _}from"../model-catalog.js";const w=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const D=/^[a-f0-9-]{36}$/;function E(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return C.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const M=()=>a(claudeDesktopUserDataDir(),"configLibrary"),v=()=>a(M(),"_meta.json"),x=e=>a(M(),`${e}.json`),P=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function S(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return E(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function O(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function I(){const e=S(v());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>E(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function L(e){return e?.entries.find(e=>e.name===w&&D.test(e.id))}const j={opus:0,fable:1,sonnet:2,haiku:3};function T(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=T(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function $(e){return T(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>A(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(_)){i=[...new Set(t.models.filter(_).map(A))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=A(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>j[$(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=T(e),s=T(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=$(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=$(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let G=null,N=null,U=null;function R(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const F={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function K(e){return Object.fromEntries(Object.entries(F).filter(([t])=>!e(t)))}const H="HKCU\\SOFTWARE\\Policies\\Claude",B="HKLM\\SOFTWARE\\Policies\\Claude",V="vibecli-claude-policy.reg",W=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let J=W;export function setRegRunnerForTests(e){J=e??W}function z(){return"win32"===process.platform}function Z(e){let t;try{t=J(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function X(){if(null===Z(H))return;const e=a(claudeDesktopUserDataDir(),V);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),J(["export",H,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}const q=[...Object.keys(R({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(F)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return Z(B)?B:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const Q=/\d+\.\d+\.\d+/;function Y(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const ee="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",te="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",oe="darwin"===process.platform?`open ${te}`:"win32"===process.platform?`start ${te}`:`xdg-open ${te}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:ee,installCommand:oe,configPath:v(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...C.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?Q.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return Y(t)}const o=C.map(e=>`'${e}'`).join(",");return Y(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>Q.exec(e)?.[0]??[]))}return null},runningApp:y()?{isRunning:k,restart:b}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const t=I()??{appliedId:"",entries:[]};let o=L(t);o||(o={id:l(),name:w},t.entries.push(o));const n=R(e);N=!0===n.disableDeploymentModeChooser,G={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=S(x(o.id))??{};O(x(o.id),{...K(e=>e in r),...r,...n}),t.appliedId=o.id,O(v(),t);const s=S(P())??{};if("3p"!==s.deploymentMode&&O(P(),{...s,deploymentMode:"3p"}),U=null,z())try{const e=Z(H);!function(e){X();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];J(["add",H,"/v",t,"/t",e,"/d",n,"/f"])}}({...K(t=>e?.has(t)??!1),...n})}catch(e){p.logError("claude-desktop.writePolicy",e),U=e instanceof Error?e.message.trim():String(e)}},unloadPrivateConfig(){if(z())try{!function(e){const t=Z(H);if(!t)return;X();let o=t.size;for(const n of e)t.has(n)&&(J(["delete",H,"/v",n,"/f"]),o-=1);o<=0&&J(["delete",H,"/f"])}(q)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=I(),t=L(e);if(!e||!t)return;m(x(t.id)),s(x(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(v()),s(v(),{force:!0})):O(v(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(z()){const e=Z(H)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=L(I());if(!e)return{apiKey:null};const t=S(x(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=I(),t=L(e);return{meta:e,applied:t?S(x(t.id)):null,desktop:S(P()),...z()?{policy:Object.fromEntries(Z(H)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;z()&&Z(H)?.has("inferenceModels")&&(X(),J(["delete",H,"/v","inferenceModels","/f"]),e=!0);const t=L(I()),o=t?S(x(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;O(x(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(G&&G.models.length>0){const{models:t,source:o,groups:n}=G;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==N&&e.push({kind:"detail",text:u.t(N?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),y()&&!k()||e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),U?(e.push({kind:"action",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:U})):z()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:H})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[v(),P()];try{const t=L(I());t&&e.push(x(t.id))}catch{}return e},backupPaths(){const e=[v(),P()];z()&&e.push(a(claudeDesktopUserDataDir(),V));try{const t=L(I());t&&e.push(x(t.id))}catch{}return e}};
1
+ import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{canRestartDesktop as y,isDesktopRunning as k,restartDesktop as b,WINDOWS_PACKAGE_FAMILIES as C}from"./desktop-process.js";import{baseModelId as A,isClaudeModel as _}from"../model-catalog.js";const w=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const D=/^[a-f0-9-]{36}$/;function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return C.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const E=()=>a(claudeDesktopUserDataDir(),"configLibrary"),S=()=>a(E(),"_meta.json"),v=e=>a(E(),`${e}.json`),x=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function O(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return M(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function P(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function I(){const e=O(S());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>M(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function L(e){return e?.entries.find(e=>e.name===w&&D.test(e.id))}const j={opus:0,fable:1,sonnet:2,haiku:3};function T(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=T(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function $(e){return T(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>A(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(_)){i=[...new Set(t.models.filter(_).map(A))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=A(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>j[$(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=T(e),s=T(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=$(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=$(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let N=null,G=null,U=null;function R(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const F={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function K(e){return Object.fromEntries(Object.entries(F).filter(([t])=>!e(t)))}const H="HKCU\\SOFTWARE\\Policies\\Claude",B="HKLM\\SOFTWARE\\Policies\\Claude",J="vibecli-claude-policy.reg",V=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let W=V;export function setRegRunnerForTests(e){W=e??V}function z(){return"win32"===process.platform}function Z(e){let t;try{t=W(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function X(){if(null===Z(H))return;const e=a(claudeDesktopUserDataDir(),J);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),W(["export",H,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function q(e){X();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];W(["add",H,"/v",t,"/t",e,"/d",n,"/f"])}}const Q=[...Object.keys(R({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(F)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return Z(B)?B:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const Y=/\d+\.\d+\.\d+/;function ee(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const te="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",oe="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",ne="darwin"===process.platform?`open ${oe}`:"win32"===process.platform?`start ${oe}`:`xdg-open ${oe}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:te,installCommand:ne,configPath:S(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...C.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?Y.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return ee(t)}const o=C.map(e=>`'${e}'`).join(",");return ee(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>Y.exec(e)?.[0]??[]))}return null},runningApp:y()?{isRunning:k,restart:b}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const t=I()??{appliedId:"",entries:[]};let o=L(t);o||(o={id:l(),name:w},t.entries.push(o));const n=R(e);G=!0===n.disableDeploymentModeChooser,N={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=O(v(o.id))??{};P(v(o.id),{...K(e=>e in r),...r,...n}),t.appliedId=o.id,P(S(),t);const s=O(x())??{};if("3p"!==s.deploymentMode&&P(x(),{...s,deploymentMode:"3p"}),U=null,z())try{const e=Z(H);q({...K(t=>e?.has(t)??!1),...n})}catch(e){p.logError("claude-desktop.writePolicy",e),U=e instanceof Error?e.message.trim():String(e)}},unloadPrivateConfig(){if(z())try{!function(e){const t=Z(H);if(!t)return;X();let o=t.size;for(const n of e)t.has(n)&&(W(["delete",H,"/v",n,"/f"]),o-=1);o<=0&&W(["delete",H,"/f"])}(Q)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=I(),t=L(e);if(!e||!t)return;m(v(t.id)),s(v(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(S()),s(S(),{force:!0})):P(S(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=I(),t=L(e);if(!t||e?.appliedId!==t.id)return null;const o=O(v(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=I(),o=L(t);if(!o||t?.appliedId!==o.id)return;const n=O(v(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)&&(P(v(o.id),{...n,inferenceModels:r}),z()&&Z(H)?.has("inferenceModels")))try{q({inferenceModels:r})}catch(e){p.logError("claude-desktop.refreshModelMenu registry",e)}},detectCurrentConfig(){try{if(z()){const e=Z(H)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=L(I());if(!e)return{apiKey:null};const t=O(v(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=I(),t=L(e);return{meta:e,applied:t?O(v(t.id)):null,desktop:O(x()),...z()?{policy:Object.fromEntries(Z(H)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;z()&&Z(H)?.has("inferenceModels")&&(X(),W(["delete",H,"/v","inferenceModels","/f"]),e=!0);const t=L(I()),o=t?O(v(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;P(v(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(N&&N.models.length>0){const{models:t,source:o,groups:n}=N;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==G&&e.push({kind:"detail",text:u.t(G?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),y()&&!k()||e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),U?(e.push({kind:"action",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:U})):z()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:H})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[S(),x()];try{const t=L(I());t&&e.push(v(t.id))}catch{}return e},backupPaths(){const e=[S(),x()];z()&&e.push(a(claudeDesktopUserDataDir(),J));try{const t=L(I());t&&e.push(v(t.id))}catch{}return e}};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Update Claude Code's native install from npmmirror when `claude update` can't
3
+ * reach downloads.claude.ai. The per-platform npm packages
4
+ * (`@anthropic-ai/claude-code-<platform>`) carry the very same single-file
5
+ * binary the native installer downloads (identical sha256), so the update keeps
6
+ * the user's native install instead of adding an npm copy next to it.
7
+ *
8
+ * Mirrors what the native installer itself does (Claude Code 2.1.295):
9
+ * - binary at `$XDG_DATA_HOME/claude/versions/<version>` (default `~/.local/share`),
10
+ * written through a temp file, mode 755, then renamed into place;
11
+ * - macOS / Linux: `~/.local/bin/claude` is a symlink into versions/, swapped by
12
+ * renaming a temp symlink over it; a launcher that isn't such a symlink belongs
13
+ * to someone else and is left alone (the installer refuses it too);
14
+ * - Windows: `~/.local/bin/claude.exe` is a copy; the old one (possibly running)
15
+ * is renamed to `claude.exe.old.<ms>.<pid>`, which the installer cleans up later.
16
+ *
17
+ * Integrity: the tarball must match the registry's sha512 `integrity`; when
18
+ * Anthropic's release manifest is reachable, the binary must also match its sha256.
19
+ */
20
+ export declare const NPM_MIRROR = "https://registry.npmmirror.com";
21
+ export interface NativeMirrorResult {
22
+ version: string;
23
+ /** Whether Anthropic's manifest checksum was also verified (it is skipped when unreachable). */
24
+ manifestVerified: boolean;
25
+ }
26
+ /** Tests replace the network. */
27
+ export declare function setNativeMirrorFetchForTests(fake: typeof fetch): void;
28
+ /** `linux-x64`, `linux-arm64-musl`, `darwin-arm64`, `win32-x64`…; null where there is no native build. */
29
+ export declare function nativePlatformKey(): string | null;
30
+ export declare function nativeLayout(): {
31
+ versionsDir: string;
32
+ launcher: string;
33
+ };
34
+ /**
35
+ * Download the channel's current version from npmmirror and make it the native
36
+ * install's active version. Throws with a readable reason on any failure, having
37
+ * left the existing install untouched.
38
+ */
39
+ export declare function updateNativeFromMirror(channel: string): Promise<NativeMirrorResult>;
@@ -0,0 +1 @@
1
+ import{createHash as t,randomBytes as r}from"crypto";import{createReadStream as i,createWriteStream as e}from"fs";import{chmod as a,copyFile as n,lstat as s,mkdir as o,readlink as c,rename as f,rm as h,symlink as u}from"fs/promises";import{dirname as l,join as w,resolve as d}from"path";import{pipeline as p}from"stream/promises";import{Readable as m,Transform as g,Writable as b}from"stream";import{createGunzip as $}from"zlib";import{toolHome as y}from"./types.js";export const NPM_MIRROR="https://registry.npmmirror.com";let v=(...t)=>fetch(...t);export function setNativeMirrorFetchForTests(t){v=t}export function nativePlatformKey(){const t="x64"===process.arch||"arm64"===process.arch?process.arch:null;if(!t)return null;if("darwin"===process.platform||"win32"===process.platform)return`${process.platform}-${t}`;if("linux"!==process.platform)return null;const r=process.report?.getReport()?.header;return r?.glibcVersionRuntime?`linux-${t}`:`linux-${t}-musl`}export function nativeLayout(){const t=y(),r=process.env.XDG_DATA_HOME?.trim()||w(t,".local","share");return{versionsDir:w(r,"claude","versions"),launcher:w(t,".local","bin","win32"===process.platform?"claude.exe":"claude")}}async function x(t,r){const i=await v(t,{signal:AbortSignal.timeout(r)});if(!i.ok)throw new Error(`${t}: HTTP ${i.status}`);return await i.json()}class E extends b{names;outPath;buffer=Buffer.alloc(0);remaining=0;padding=0;writing=null;found=!1;constructor(t,r){super(),this.names=t,this.outPath=r}_write(t,r,i){this.buffer=this.buffer.length?Buffer.concat([this.buffer,t]):t,this.drain().then(()=>i()).catch(i)}_final(t){this.writing?this.writing.end(()=>t()):t()}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const t=Math.min(this.remaining,this.buffer.length),r=this.buffer.subarray(0,t);if(this.buffer=this.buffer.subarray(t),this.remaining-=t,this.writing&&!this.writing.write(r)&&await new Promise(t=>this.writing.once("drain",()=>t())),0===this.remaining&&this.writing){const t=this.writing;this.writing=null,await new Promise((r,i)=>t.end(t=>t?i(t):r()))}continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const t=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),t.every(t=>0===t))continue;const r=(r,i)=>t.subarray(r,r+i).toString("utf-8").replace(/\0.*$/s,""),i=r(345,155),a=i?`${i}/${r(0,100)}`:r(0,100),n=parseInt(r(124,12).trim()||"0",8),s=r(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,("0"===s||""===s)&&this.names.includes(a)&&!this.found&&(this.found=!0,this.writing=e(this.outPath,{mode:493}),0===n)){const t=this.writing;this.writing=null,await new Promise(r=>t.end(()=>r()))}}}}export async function updateNativeFromMirror(b){const y=nativePlatformKey();if(!y)throw new Error(`no native Claude Code build for ${process.platform}-${process.arch}`);const{versionsDir:P,launcher:R}=nativeLayout();if("win32"!==process.platform&&!await async function(t,r){try{return!!(await s(t)).isSymbolicLink()&&d(l(t),await c(t)).startsWith(d(r)+"/")}catch{return!0}}(R,P))throw new Error(`${R} was not created by the native installer; not replacing it`);const k=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code`,2e4),M=k["dist-tags"]?.[b]??k["dist-tags"]?.latest;if(!M||!/^\d+\.\d+\.\d+$/.test(M))throw new Error("npmmirror returned no usable version");const D=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code-${y}/${M}`,2e4),N=D.dist?.tarball,T=D.dist?.integrity;if(!N||!T?.startsWith("sha512-"))throw new Error("npmmirror returned no tarball or integrity");await o(P,{recursive:!0});const _=`${process.pid}.${Date.now()}`,A=w(P,`${M}.download.${_}`),O=w(P,`${M}.tmp.${_}`);try{const c=await v(N,{signal:AbortSignal.timeout(9e5)});if(!c.ok||!c.body)throw new Error(`${N}: HTTP ${c.status}`);const d=t("sha512"),b=new g({transform(t,r,i){d.update(t),i(null,t)}});if(await p(m.fromWeb(c.body),b,e(A)),`sha512-${d.digest("base64")}`!==T)throw new Error("downloaded package does not match its sha512 integrity");const k=new E(["package/claude","package/claude.exe"],O);if(await p(i(A),$(),k),!k.found)throw new Error("package has no claude binary");await a(O,493);let D=!1;try{const r=await x(`https://downloads.claude.ai/claude-code-releases/${M}/manifest.json`,8e3),e=r.platforms?.[y]?.checksum;if(e){if(await async function(r){const e=t("sha256");return await p(i(r),e),e.digest("hex")}(O)!==e)throw new Error("binary does not match Anthropic's release checksum");D=!0}}catch(t){if(t instanceof Error&&t.message.includes("release checksum"))throw t}const _=w(P,M);return await f(O,_),await async function(t,i){await o(l(i),{recursive:!0});const e=`${process.pid}.${Date.now()}`;if("win32"!==process.platform){const r=`${i}.tmp.${e}`;await u(t,r);try{await f(r,i)}catch(t){throw await h(r,{force:!0}),t}return}const a=`${i}.new.${e}.${r(4).toString("hex")}`;await n(t,a);let c=null;try{await s(i),c=`${i}.old.${Date.now()}.${process.pid}`,await f(i,c)}catch(t){if("ENOENT"!==t.code)throw await h(a,{force:!0}),t}try{await f(a,i)}catch(t){throw c&&await f(c,i).catch(()=>{}),await h(a,{force:!0}),t}}(_,R),{version:M,manifestVerified:D}}finally{await h(A,{force:!0}),await h(O,{force:!0})}}
@@ -1 +1 @@
1
- import{existsSync as o,readFileSync as e}from"fs";import{join as t}from"path";import*as n from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as r}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as d,toolHome as s,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as p,codexCatalogPath as u,loadBundledCodexModels as g,loadPreviousCodexModels as f,previousGatewayModelIds as _}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(s(),".codex"),k=()=>t(w(),"config.toml"),C=()=>t(w(),"auth.json"),h="OPENAI_API_KEY",j="custom",E=1e6,b=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function v(o){return!!o&&"object"==typeof o&&!Array.isArray(o)}function O(o,e){return v(o[e])||(o[e]={}),o[e]}function M(){if(!o(k()))return{};const t=e(k(),"utf-8");if(!t.trim())return{};try{const o=n.parse(t);if(!v(o))throw new Error("config.toml root must be a TOML table");return o}catch(o){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${o instanceof Error?o.message:String(o)}`)}}function N(){if(!o(C()))return{};try{const o=JSON.parse(e(C(),"utf-8"));return v(o)?o:{}}catch{return{}}}let S=null;function A(t,n,r){const d=!1!==n.settings?.one_million_context;!function(o){o.model_context_window===E&&delete o.model_context_window,o.model_auto_compact_token_limit===b&&delete o.model_auto_compact_token_limit}(t);const s=g()??f();if(!s)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===u()&&delete t.model_catalog_json,void(d&&(t.model_context_window=E,t.model_auto_compact_token_limit=b));const c=n.catalog,m=p({bundled:s,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??_(),oneMillion:d,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(o=>"list"===o.visibility).map(o=>({id:String(o.slug),name:String(o.display_name??o.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=u();o(w)&&e(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(o,e){const t=M(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:o},catalog:e},r);const d=n.stringify(t);d!==i&&(l(k()),a(k(),d))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:k(),wantsModelCatalog:!0,applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const o=new Map;for(const e of S.listed)o.set(y(e.id),(o.get(y(e.id))??0)+1);const e=["gpt","claude","other","grok"].filter(e=>o.has(e)).map(e=>`${r.t(`tools.model_family.${e}`)} ${o.get(e)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:e})},{kind:"detail",text:S.listed.map(o=>o.name).join(", ")}]},loadPrivateConfig(o){const e=m("codex"),t=d(o.models,e),i=function(o){return/(?:^|\/)claude(?:-|$)/i.test(o)}(t)?e:t,r=c(o.baseUrl),s=M();s.model=i,s.model_provider=j,s.model_reasoning_effort="xhigh",A(s,o,i);const p=O(s,"model_providers"),u=v(p[j])?{...p[j]}:{};delete u.env_key,p[j]={...u,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(s,"profiles"),f=v(g[i])?{...g[i]}:{};g[i]={...f,model_provider:j,model:i},O(O(s,"tui"),"model_availability_nux")[i]=4;const _=O(s,"features");_.goals=!0,_.js_repl=!1,l(k()),a(k(),n.stringify(s)),function(o){const e=o.trim();if(!e)return;const t={...N(),auth_mode:"apikey",[h]:e};l(C()),a(C(),JSON.stringify(t,null,2)+"\n")}(o.apiKey)},unloadPrivateConfig(){if(!o(k()))return;let e;try{e=M()}catch(o){return void i.logError("codexAdapter.unloadPrivateConfig",o)}const t=e.model_providers;v(t)&&(delete t[j],0===Object.keys(t).length&&delete e.model_providers),e.model_provider===j&&delete e.model_provider,e.model_catalog_json===u()&&delete e.model_catalog_json,l(k()),a(k(),n.stringify(e))},detectCurrentConfig(){const o=N()[h];return{apiKey:"string"==typeof o&&o.trim()?o.trim():null}},getConfig(){try{return M()}catch(o){return i.logError("codexAdapter.getConfig",o),null}},backupPaths:()=>[k(),C(),u()]};
1
+ import{existsSync as e,readFileSync as o}from"fs";import{join as t}from"path";import*as n from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as r}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as u,codexCatalogPath as p,loadBundledCodexModels as g,loadPreviousCodexModels as _,previousGatewayModelIds as f}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(d(),".codex"),C=()=>t(w(),"config.toml"),h=()=>t(w(),"auth.json"),j="OPENAI_API_KEY",k="custom",b=1e6,E=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function O(e,o){return M(e[o])||(e[o]={}),e[o]}function v(){if(!e(C()))return{};const t=o(C(),"utf-8");if(!t.trim())return{};try{const e=n.parse(t);if(!M(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function N(){if(!e(h()))return{};try{const e=JSON.parse(o(h(),"utf-8"));return M(e)?e:{}}catch{return{}}}let S=null;function A(t,n,r){const s=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===b&&delete e.model_context_window,e.model_auto_compact_token_limit===E&&delete e.model_auto_compact_token_limit}(t);const d=g()??_();if(!d)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===p()&&delete t.model_catalog_json,void(s&&(t.model_context_window=b,t.model_auto_compact_token_limit=E));const c=n.catalog,m=u({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??f(),oneMillion:s,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=p();e(w)&&o(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(e,o){const t=v(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:o},r);const s=n.stringify(t);s!==i&&(l(C()),a(C(),s))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:C(),wantsModelCatalog:!0,listedModels(){if(v().model_catalog_json!==p())return null;try{return(JSON.parse(o(p(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){v().model_catalog_json===p()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const e=new Map;for(const o of S.listed)e.set(y(o.id),(e.get(y(o.id))??0)+1);const o=["gpt","claude","other","grok"].filter(o=>e.has(o)).map(o=>`${r.t(`tools.model_family.${o}`)} ${e.get(o)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:o})},{kind:"detail",text:S.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const o=m("codex"),t=s(e.models,o),i=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(t)?o:t,r=c(e.baseUrl),d=v();d.model=i,d.model_provider=k,d.model_reasoning_effort="xhigh",A(d,e,i);const u=O(d,"model_providers"),p=M(u[k])?{...u[k]}:{};delete p.env_key,u[k]={...p,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(d,"profiles"),_=M(g[i])?{...g[i]}:{};g[i]={..._,model_provider:k,model:i},O(O(d,"tui"),"model_availability_nux")[i]=4;const f=O(d,"features");f.goals=!0,f.js_repl=!1,l(C()),a(C(),n.stringify(d)),function(e){const o=e.trim();if(!o)return;const t={...N(),auth_mode:"apikey",[j]:o};l(h()),a(h(),JSON.stringify(t,null,2)+"\n")}(e.apiKey)},unloadPrivateConfig(){if(!e(C()))return;let o;try{o=v()}catch(e){return void i.logError("codexAdapter.unloadPrivateConfig",e)}const t=o.model_providers;M(t)&&(delete t[k],0===Object.keys(t).length&&delete o.model_providers),o.model_provider===k&&delete o.model_provider,o.model_catalog_json===p()&&delete o.model_catalog_json,l(C()),a(C(),n.stringify(o))},detectCurrentConfig(){const e=N()[j];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return v()}catch(e){return i.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[C(),h(),p()]};
@@ -18,6 +18,14 @@ export interface ToolInstallSpec {
18
18
  updateArgs: string[];
19
19
  /** Directories under home that belong to it, removed together with the launcher. */
20
20
  dataDirs: string[][];
21
+ /**
22
+ * Replace the native binary from a mirror when the vendor updater can't connect,
23
+ * keeping the native install. Resolves to the installed version; throws with a reason.
24
+ */
25
+ mirrorUpdate?(): Promise<{
26
+ version: string;
27
+ manifestVerified: boolean;
28
+ }>;
21
29
  };
22
30
  /** Homebrew formula / cask name. */
23
31
  brew?: string;
@@ -56,6 +56,16 @@ export interface ToolAdapter {
56
56
  * are reported, and the upgrade runs the way the active copy was installed.
57
57
  */
58
58
  installSpec?: ToolInstallSpec;
59
+ /**
60
+ * Models currently in the tool's menu as vibecli wrote it (null when vibecli
61
+ * doesn't manage the menu right now), and a rewrite of just that menu from a
62
+ * fresh catalog — used to follow the gateway's list without a full setup.
63
+ * The refresh leaves credentials, provider and the user's model choice alone.
64
+ */
65
+ listedModels?(): string[] | null;
66
+ refreshModelMenu?(ctx: ToolApplyContext & {
67
+ catalog: ModelCatalog;
68
+ }): void;
59
69
  /**
60
70
  * GUI apps that only read their config at start: setup and the wizard offer
61
71
  * to restart a running app after writing it.
@@ -375,7 +375,7 @@
375
375
  "registry_denied": "Not elevated, so the registry backstop was skipped (the local config is written and usually enough); if the reopened app doesn't use VibeAPI, run this again from an administrator terminal"
376
376
  },
377
377
  "claude_code": {
378
- "gateway_models": "{{count}} Chinese models added to /model ({{vendors}})",
378
+ "gateway_models": "{{count}} more models added to /model ({{vendors}})",
379
379
  "tier_fallback": "This key can't use Claude Code's default {{from}}; the {{tier}} alias now uses {{to}}",
380
380
  "stable_channel": "Your update channel in settings.json is stable, which may not have reached {{min}} yet; set autoUpdatesChannel to latest, then upgrade",
381
381
  "managed_override": "Claude Code managed settings found ({{source}}); {{keys}} there override what vibecli writes, so this config won't take effect. Managed settings usually come from your company's IT; ask your administrator",
@@ -411,7 +411,11 @@
411
411
  "delete_paths": "delete {{paths}}",
412
412
  "still_missing": "{{tool}} is still not installed; see the output above",
413
413
  "still_old": "After upgrading: {{copy}}, still below {{min}}",
414
- "done": "Now {{copy}}"
414
+ "done": "Now {{copy}}",
415
+ "native_mirror_trying": "The official download server is unreachable; fetching the same native binary from the npm mirror to replace it in place",
416
+ "native_mirror_done": "Replaced with {{version}} from the mirror (npm integrity verified; Anthropic's manifest unreachable)",
417
+ "native_mirror_done_verified": "Replaced with {{version}} from the mirror (npm integrity and Anthropic's release checksum verified)",
418
+ "native_mirror_failed": "Replacing from the mirror failed: {{reason}}"
415
419
  },
416
420
  "install": {
417
421
  "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}}",
@@ -423,5 +427,10 @@
423
427
  "still_not_installed": "{{tool}} still not found; make sure it installed and open a new terminal",
424
428
  "verified_success": "Found {{tool}}, continuing",
425
429
  "install_failed_detail": "Installation did not finish; see the output above or vibecli logs"
430
+ },
431
+ "model_refresh": {
432
+ "changed": "{{tool}} model menu synced with VibeAPI: {{changes}}",
433
+ "added": "added {{models}}",
434
+ "removed": "removed {{models}}"
426
435
  }
427
436
  }
@@ -375,7 +375,7 @@
375
375
  "registry_denied": "没有管理员权限,跳过了注册表兜底(本地配置已写好,通常不影响);如果重开桌面端后没有走 VibeAPI,用管理员身份打开终端再运行一次"
376
376
  },
377
377
  "claude_code": {
378
- "gateway_models": "/model 菜单加入 {{count}} 个国产模型({{vendors}})",
378
+ "gateway_models": "/model 菜单加入 {{count}} 个其他模型({{vendors}})",
379
379
  "tier_fallback": "当前 Key 用不了 Claude Code 默认的 {{from}},{{tier}} 档已改用 {{to}}",
380
380
  "stable_channel": "settings.json 里的更新渠道是 stable,这个渠道可能还没到 {{min}};把 autoUpdatesChannel 改成 latest 后再升级",
381
381
  "managed_override": "检测到 Claude Code 的托管配置({{source}}),其中 {{keys}} 优先于 vibecli 写入的设置,会让这里的配置不生效;托管配置一般是公司电脑统一下发的,请联系管理员",
@@ -411,7 +411,11 @@
411
411
  "delete_paths": "删除 {{paths}}",
412
412
  "still_missing": "{{tool}} 仍未安装成功,请看上面的输出",
413
413
  "still_old": "升级后是 {{copy}},仍低于 {{min}}",
414
- "done": "已是 {{copy}}"
414
+ "done": "已是 {{copy}}",
415
+ "native_mirror_trying": "官方下载服务器连不上,改从国内镜像下载同一个原生程序,原地替换",
416
+ "native_mirror_done": "已从国内镜像换成 {{version}}(已核对 npm 校验值;官方清单连不上,未额外核对)",
417
+ "native_mirror_done_verified": "已从国内镜像换成 {{version}}(已核对 npm 与官方发布清单的校验值)",
418
+ "native_mirror_failed": "从国内镜像替换失败:{{reason}}"
415
419
  },
416
420
  "install": {
417
421
  "permission_unix": "多半是 npm 没有权限写全局目录。可以二选一:① sudo {{command}};② 运行 npm config set prefix ~/.npm-global,把 export PATH=~/.npm-global/bin:$PATH 加进 ~/.zshrc 或 ~/.bashrc,重开终端后再运行 {{command}}",
@@ -423,5 +427,10 @@
423
427
  "still_not_installed": "还是没找到 {{tool}},请确认安装成功、重开终端后再试",
424
428
  "verified_success": "已找到 {{tool}},继续配置",
425
429
  "install_failed_detail": "安装没有完成,详情见上面的输出或 vibecli logs"
430
+ },
431
+ "model_refresh": {
432
+ "changed": "已按 VibeAPI 最新模型清单更新 {{tool}}:{{changes}}",
433
+ "added": "新增 {{models}}",
434
+ "removed": "移除 {{models}}"
426
435
  }
427
436
  }
package/dist/models.json CHANGED
@@ -26,20 +26,20 @@
26
26
  "model": "gpt-5.5"
27
27
  },
28
28
  {
29
- "name": "GLM-5.2",
30
- "model": "glm-5.2"
29
+ "name": "GLM-5.3",
30
+ "model": "glm-5.3"
31
31
  },
32
32
  {
33
33
  "name": "MiniMax-M3",
34
34
  "model": "MiniMax-M3"
35
35
  },
36
36
  {
37
- "name": "Kimi-K2.6",
38
- "model": "kimi-k2.6"
37
+ "name": "Kimi-K3",
38
+ "model": "kimi-k3"
39
39
  },
40
40
  {
41
- "name": "Qwen3.7-Max",
42
- "model": "qwen3.7-max"
41
+ "name": "Qwen3.8-Max",
42
+ "model": "qwen3.8-max"
43
43
  },
44
44
  {
45
45
  "name": "DeepSeek-V4-Pro",
@@ -76,6 +76,7 @@
76
76
  },
77
77
  "gatewayModels": {
78
78
  "autoCompactRatio": 0.8,
79
+ "retired": ["deepseek-v4-flash", "glm-5.2", "qwen3.7-max", "qwen3.7-plus"],
79
80
  "models": [
80
81
  {
81
82
  "id": "claude-opus-5-5",
@@ -137,6 +138,69 @@
137
138
  "description": "20 万上下文(部分分组无 1M)| Agent 编程",
138
139
  "contextWindow": 200000
139
140
  },
141
+ {
142
+ "id": "gpt-6-astra",
143
+ "name": "GPT-6 Astra",
144
+ "description": "105 万上下文 | 编码 / 计算机操作",
145
+ "contextWindow": 1050000,
146
+ "codex": false
147
+ },
148
+ {
149
+ "id": "gpt-6.1-sol",
150
+ "name": "GPT-6.1 Sol",
151
+ "description": "105 万上下文 | 工具调用 | 结构化输出",
152
+ "contextWindow": 1050000,
153
+ "codex": false
154
+ },
155
+ {
156
+ "id": "gpt-6-sol",
157
+ "name": "GPT-6 Sol",
158
+ "description": "105 万上下文 | 复杂编程与 Agent 工作流",
159
+ "contextWindow": 1050000,
160
+ "codex": false
161
+ },
162
+ {
163
+ "id": "gpt-5.6-sol",
164
+ "name": "GPT-5.6 Sol",
165
+ "description": "105 万上下文 | 前沿档",
166
+ "contextWindow": 1050000,
167
+ "codex": false
168
+ },
169
+ {
170
+ "id": "gpt-5.6-terra",
171
+ "name": "GPT-5.6 Terra",
172
+ "description": "105 万上下文 | 均衡生产档",
173
+ "contextWindow": 1050000,
174
+ "codex": false
175
+ },
176
+ {
177
+ "id": "gpt-5.6-luna",
178
+ "name": "GPT-5.6 Luna",
179
+ "description": "105 万上下文 | 大批量低成本档",
180
+ "contextWindow": 1050000,
181
+ "codex": false
182
+ },
183
+ {
184
+ "id": "gemini-3.8-flash",
185
+ "name": "Gemini 3.8 Flash",
186
+ "description": "105 万上下文 | 推理与编码",
187
+ "contextWindow": 1048576,
188
+ "codex": false
189
+ },
190
+ {
191
+ "id": "gemini-3.1-pro",
192
+ "name": "Gemini 3.1 Pro",
193
+ "description": "100 万上下文 | 深度推理 | 长文档",
194
+ "contextWindow": 1000000,
195
+ "codex": false
196
+ },
197
+ {
198
+ "id": "gemini-3.7-flash",
199
+ "name": "Gemini 3.7 Flash",
200
+ "description": "100 万上下文 | Agent | 软件工程",
201
+ "contextWindow": 1000000,
202
+ "codex": false
203
+ },
140
204
  {
141
205
  "id": "deepseek-v4-pro",
142
206
  "name": "DeepSeek V4 Pro",
@@ -149,12 +213,6 @@
149
213
  "description": "100 万上下文 | 高速档",
150
214
  "contextWindow": 1000000
151
215
  },
152
- {
153
- "id": "deepseek-v4-flash",
154
- "name": "DeepSeek V4 Flash",
155
- "description": "100 万上下文 | 高速档",
156
- "contextWindow": 1000000
157
- },
158
216
  {
159
217
  "id": "glm-5.3",
160
218
  "name": "GLM 5.3",
@@ -167,12 +225,6 @@
167
225
  "description": "105 万上下文 | 高速档",
168
226
  "contextWindow": 1048576
169
227
  },
170
- {
171
- "id": "glm-5.2",
172
- "name": "GLM 5.2",
173
- "description": "100 万上下文 | 编程",
174
- "contextWindow": 1000000
175
- },
176
228
  {
177
229
  "id": "kimi-k3",
178
230
  "name": "Kimi K3",
@@ -191,18 +243,6 @@
191
243
  "description": "100 万上下文 | 高速档",
192
244
  "contextWindow": 1000000
193
245
  },
194
- {
195
- "id": "qwen3.7-max",
196
- "name": "Qwen 3.7 Max",
197
- "description": "100 万上下文 | 推理 | 编程",
198
- "contextWindow": 1000000
199
- },
200
- {
201
- "id": "qwen3.7-plus",
202
- "name": "Qwen 3.7 Plus",
203
- "description": "100 万上下文 | 均衡档",
204
- "contextWindow": 1000000
205
- },
206
246
  {
207
247
  "id": "MiniMax-M3",
208
248
  "name": "MiniMax M3",
@@ -213,8 +253,14 @@
213
253
  "id": "grok-4.7",
214
254
  "name": "Grok 4.7",
215
255
  "description": "50 万上下文 | 长时 Agent | 编码",
256
+ "contextWindow": 500000
257
+ },
258
+ {
259
+ "id": "grok-4.6",
260
+ "name": "Grok 4.6",
261
+ "description": "50 万上下文 | 编码",
216
262
  "contextWindow": 500000,
217
- "claudeCode": false
263
+ "codex": false
218
264
  }
219
265
  ]
220
266
  },
package/models.json CHANGED
@@ -26,20 +26,20 @@
26
26
  "model": "gpt-5.5"
27
27
  },
28
28
  {
29
- "name": "GLM-5.2",
30
- "model": "glm-5.2"
29
+ "name": "GLM-5.3",
30
+ "model": "glm-5.3"
31
31
  },
32
32
  {
33
33
  "name": "MiniMax-M3",
34
34
  "model": "MiniMax-M3"
35
35
  },
36
36
  {
37
- "name": "Kimi-K2.6",
38
- "model": "kimi-k2.6"
37
+ "name": "Kimi-K3",
38
+ "model": "kimi-k3"
39
39
  },
40
40
  {
41
- "name": "Qwen3.7-Max",
42
- "model": "qwen3.7-max"
41
+ "name": "Qwen3.8-Max",
42
+ "model": "qwen3.8-max"
43
43
  },
44
44
  {
45
45
  "name": "DeepSeek-V4-Pro",
@@ -76,6 +76,7 @@
76
76
  },
77
77
  "gatewayModels": {
78
78
  "autoCompactRatio": 0.8,
79
+ "retired": ["deepseek-v4-flash", "glm-5.2", "qwen3.7-max", "qwen3.7-plus"],
79
80
  "models": [
80
81
  {
81
82
  "id": "claude-opus-5-5",
@@ -137,6 +138,69 @@
137
138
  "description": "20 万上下文(部分分组无 1M)| Agent 编程",
138
139
  "contextWindow": 200000
139
140
  },
141
+ {
142
+ "id": "gpt-6-astra",
143
+ "name": "GPT-6 Astra",
144
+ "description": "105 万上下文 | 编码 / 计算机操作",
145
+ "contextWindow": 1050000,
146
+ "codex": false
147
+ },
148
+ {
149
+ "id": "gpt-6.1-sol",
150
+ "name": "GPT-6.1 Sol",
151
+ "description": "105 万上下文 | 工具调用 | 结构化输出",
152
+ "contextWindow": 1050000,
153
+ "codex": false
154
+ },
155
+ {
156
+ "id": "gpt-6-sol",
157
+ "name": "GPT-6 Sol",
158
+ "description": "105 万上下文 | 复杂编程与 Agent 工作流",
159
+ "contextWindow": 1050000,
160
+ "codex": false
161
+ },
162
+ {
163
+ "id": "gpt-5.6-sol",
164
+ "name": "GPT-5.6 Sol",
165
+ "description": "105 万上下文 | 前沿档",
166
+ "contextWindow": 1050000,
167
+ "codex": false
168
+ },
169
+ {
170
+ "id": "gpt-5.6-terra",
171
+ "name": "GPT-5.6 Terra",
172
+ "description": "105 万上下文 | 均衡生产档",
173
+ "contextWindow": 1050000,
174
+ "codex": false
175
+ },
176
+ {
177
+ "id": "gpt-5.6-luna",
178
+ "name": "GPT-5.6 Luna",
179
+ "description": "105 万上下文 | 大批量低成本档",
180
+ "contextWindow": 1050000,
181
+ "codex": false
182
+ },
183
+ {
184
+ "id": "gemini-3.8-flash",
185
+ "name": "Gemini 3.8 Flash",
186
+ "description": "105 万上下文 | 推理与编码",
187
+ "contextWindow": 1048576,
188
+ "codex": false
189
+ },
190
+ {
191
+ "id": "gemini-3.1-pro",
192
+ "name": "Gemini 3.1 Pro",
193
+ "description": "100 万上下文 | 深度推理 | 长文档",
194
+ "contextWindow": 1000000,
195
+ "codex": false
196
+ },
197
+ {
198
+ "id": "gemini-3.7-flash",
199
+ "name": "Gemini 3.7 Flash",
200
+ "description": "100 万上下文 | Agent | 软件工程",
201
+ "contextWindow": 1000000,
202
+ "codex": false
203
+ },
140
204
  {
141
205
  "id": "deepseek-v4-pro",
142
206
  "name": "DeepSeek V4 Pro",
@@ -149,12 +213,6 @@
149
213
  "description": "100 万上下文 | 高速档",
150
214
  "contextWindow": 1000000
151
215
  },
152
- {
153
- "id": "deepseek-v4-flash",
154
- "name": "DeepSeek V4 Flash",
155
- "description": "100 万上下文 | 高速档",
156
- "contextWindow": 1000000
157
- },
158
216
  {
159
217
  "id": "glm-5.3",
160
218
  "name": "GLM 5.3",
@@ -167,12 +225,6 @@
167
225
  "description": "105 万上下文 | 高速档",
168
226
  "contextWindow": 1048576
169
227
  },
170
- {
171
- "id": "glm-5.2",
172
- "name": "GLM 5.2",
173
- "description": "100 万上下文 | 编程",
174
- "contextWindow": 1000000
175
- },
176
228
  {
177
229
  "id": "kimi-k3",
178
230
  "name": "Kimi K3",
@@ -191,18 +243,6 @@
191
243
  "description": "100 万上下文 | 高速档",
192
244
  "contextWindow": 1000000
193
245
  },
194
- {
195
- "id": "qwen3.7-max",
196
- "name": "Qwen 3.7 Max",
197
- "description": "100 万上下文 | 推理 | 编程",
198
- "contextWindow": 1000000
199
- },
200
- {
201
- "id": "qwen3.7-plus",
202
- "name": "Qwen 3.7 Plus",
203
- "description": "100 万上下文 | 均衡档",
204
- "contextWindow": 1000000
205
- },
206
246
  {
207
247
  "id": "MiniMax-M3",
208
248
  "name": "MiniMax M3",
@@ -213,8 +253,14 @@
213
253
  "id": "grok-4.7",
214
254
  "name": "Grok 4.7",
215
255
  "description": "50 万上下文 | 长时 Agent | 编码",
256
+ "contextWindow": 500000
257
+ },
258
+ {
259
+ "id": "grok-4.6",
260
+ "name": "Grok 4.6",
261
+ "description": "50 万上下文 | 编码",
216
262
  "contextWindow": 500000,
217
- "claudeCode": false
263
+ "codex": false
218
264
  }
219
265
  ]
220
266
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.57",
3
+ "version": "0.0.58",
4
4
  "description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode, Grok Build) with one unified config",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",