@vibeapi/cli 0.0.57 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/claude-code-manager.d.ts +4 -5
- package/dist/lib/claude-code-manager.js +1 -1
- package/dist/lib/command.js +1 -1
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.js +1 -1
- package/dist/lib/gateway-models.d.ts +10 -1
- package/dist/lib/gateway-models.js +1 -1
- package/dist/lib/model-refresh.d.ts +21 -0
- package/dist/lib/model-refresh.js +1 -0
- package/dist/lib/model-registry.d.ts +4 -0
- package/dist/lib/model-registry.js +1 -1
- package/dist/lib/tool-upgrade.d.ts +2 -1
- package/dist/lib/tool-upgrade.js +1 -1
- package/dist/lib/tools/claude-code.js +1 -1
- package/dist/lib/tools/claude-desktop.js +1 -1
- package/dist/lib/tools/claude-native-mirror.d.ts +39 -0
- package/dist/lib/tools/claude-native-mirror.js +1 -0
- package/dist/lib/tools/codex-catalog.js +1 -1
- package/dist/lib/tools/codex.js +1 -1
- package/dist/lib/tools/tool-installs.d.ts +8 -0
- package/dist/lib/tools/types.d.ts +10 -0
- package/dist/locales/en_US.json +11 -2
- package/dist/locales/zh_CN.json +11 -2
- package/dist/models.json +88 -30
- package/models.json +88 -30
- package/package.json +1 -1
|
@@ -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 (
|
|
10
|
-
* menu: `modelPicker` rows after the built-in lineup
|
|
11
|
-
* at 80% of each window, and
|
|
12
|
-
* window
|
|
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
|
|
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 C,getGatewayModelRules as u}from"./model-registry.js";import{autoCompactTokens as O,modelBlurb as h,prettyModelName as p}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 E={opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL",fable:"ANTHROPIC_DEFAULT_FABLE_MODEL"},A="CLAUDE_CODE_MAX_CONTEXT_TOKENS";function T(){return s(d.getConfigDir(),"claude-code-picker.json")}function P(){try{const e=JSON.parse(n(T(),"utf-8")).ids;return Array.isArray(e)?e.map(String):[]}catch{return[]}}function v(e,t=[]){if(!e||"object"!=typeof e)return!1;const{options:n,replaceBuiltInOptions:o}=e;if(!Array.isArray(n)||o)return!1;const i=u(),r=new Set([...i.models.map(e=>e.id),...i.retired,...P(),...t]);return n.every(e=>e&&"object"==typeof e&&r.has(_(String(e.model))))}export function applyGatewayModels(e,n){if(void 0===n)return e;const i={...e};if(void 0!==i.modelPicker&&!v(i.modelPicker,n.map(e=>e.id)))return i;const r=v(i.modelPicker)?i.modelPicker.options.map(e=>_(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);!function(e){try{t(d.getConfigDir(),{recursive:!0}),o(T(),JSON.stringify({ids:e})+"\n")}catch(e){l.logError("claudeCode.recordWrittenPickerIds",e)}}(n.map(e=>e.id));const f=e=>h(e.description,{millionContext:c(e)});if(0===n.length)delete i.modelPicker,delete s[A];else{i.modelPicker={options:n.map(e=>({model:c(e)?`${e.id}[1m]`:e.id,label:e.name||p(e.id),...f(e)?{description:f(e)}:{}}))},g.length>0?s[A]=String(Math.min(...g)):delete s[A];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(_)),n=[];for(const[e,o]of Object.entries(C())){const i=E[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,...C}=c.env||{},u={...C};for(const e of MODEL_ENV_VARS)delete u[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:{...u,...Object.fromEntries(Object.entries(r).filter(([e])=>!(e in u))),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();
|
package/dist/lib/command.js
CHANGED
|
@@ -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{
|
|
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}}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/config.js
CHANGED
|
@@ -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
|
*/
|
|
@@ -16,6 +16,15 @@ export declare function autoCompactTokens(window: number, explicit?: number): nu
|
|
|
16
16
|
* the built-in list from `models.json` → `gatewayModels`.
|
|
17
17
|
*/
|
|
18
18
|
export declare function gatewayModelSpecs(tool: GatewayModelTool, clientModels?: Record<string, GatewayClientModel[]>): GatewayModelSpec[];
|
|
19
|
+
/**
|
|
20
|
+
* Menu subtitle from the gateway's description without its figures: output limits
|
|
21
|
+
* and context sizes don't hold for every relayed model. With `millionContext` (the
|
|
22
|
+
* window vibecli configures is at least 1M) the context part becomes a plain
|
|
23
|
+
* 「百万上下文」; otherwise it is dropped. Undefined when nothing is left.
|
|
24
|
+
*/
|
|
25
|
+
export declare function modelBlurb(description: string | undefined, options?: {
|
|
26
|
+
millionContext?: boolean;
|
|
27
|
+
}): string | undefined;
|
|
19
28
|
/**
|
|
20
29
|
* Menu label for a model id nobody named: `glm-5.3-flash` → `GLM 5.3 Flash`,
|
|
21
30
|
* `claude-opus-5-5` → `Claude Opus 5.5`, `qwen3.8-max` → `Qwen 3.8 Max`.
|
|
@@ -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
|
|
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,i=n.filter(t=>function(t,e){return"codex"===t?!1!==e.codex:!e.id.startsWith("claude-")&&!1!==e.claudeCode}(e,t));if(!o)return i;const r=new Map(("claude-code"===e?n:i).map(t=>[t.id,t])),s="codex"===e?[...o.codex??[],...(o.claude??[]).filter(t=>t.id.startsWith("claude-")&&r.has(t.id))]:function(t,e){return[...(t.codex??[]).filter(t=>"gpt"===modelFamily(t.id)),...t.gemini??[],...t.grok??[],...(t.claude??[]).filter(t=>!t.id.startsWith("claude-"))].filter(t=>e.get(t.id)?.claudeCode??!0)}(o,r);if(0===s.length)return i;const d=new Set,c=[];for(const t of s)d.has(t.id)||(d.add(t.id),c.push({...r.get(t.id),...t}));return c}const e={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function modelBlurb(t,e={}){const o=[];for(const n of(t??"").split("|")){const t=n.trim();t&&(/\d\s*万?\s*输出|output\D*\d|\d\D*output/i.test(t)||(/[\d百千]\s*万?\s*上下文|context\D*\d|\d\D*context/i.test(t)?e.millionContext&&!o.includes("百万上下文")&&o.push("百万上下文"):o.push(t)))}return o.length>0?o.join(" | "):void 0}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 i=t.toLowerCase();n.push(e[i]??(/^[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"}
|
|
@@ -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
|
|
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,
|
|
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>;
|
package/dist/lib/tool-upgrade.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{spawnSync as e}from"child_process";import{rmSync as
|
|
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
|
|
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{spawnSync as t}from"child_process";import{existsSync as e,readFileSync as o}from"fs";import{dirname as n,join as i}from"path";import{fileURLToPath as s}from"url";import{logger as r}from"../../utils/logger.js";import{getCodexRules as l}from"../model-registry.js";import{autoCompactTokens as
|
|
1
|
+
import{spawnSync as t}from"child_process";import{existsSync as e,readFileSync as o}from"fs";import{dirname as n,join as i}from"path";import{fileURLToPath as s}from"url";import{logger as r}from"../../utils/logger.js";import{getCodexRules as l,compareVersions as u}from"../model-registry.js";import{autoCompactTokens as a,modelBlurb as d,prettyModelName as c}from"../gateway-models.js";import{toolHome as p}from"./types.js";const _=n(s(import.meta.url));export const CODEX_CATALOG_FILE="vibeapi-model-catalog.json";export const codexCatalogPath=()=>i(p(),".codex",CODEX_CATALOG_FILE);function m(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function f(t){try{const e=JSON.parse(t),o=m(e)&&Array.isArray(e.models)?e.models.filter(m):[];return o.length>0?o:null}catch{return null}}export function loadBundledCodexModels(){for(const o of function(){const t=process.env.VIBECLI_CODEX_BIN;if(void 0!==t)return t.trim()?[t.trim()]:[];const e=["codex"];if("darwin"===process.platform)for(const t of["/Applications",i(p(),"Applications")])e.push(i(t,"ChatGPT.app/Contents/Resources/codex-cli/bin/codex")),e.push(i(t,"Codex.app/Contents/Resources/codex"));return e}()){if("codex"!==o&&!e(o))continue;const n={encoding:"utf-8",timeout:2e4,maxBuffer:67108864,windowsHide:!0,stdio:["ignore","pipe","pipe"]},i="win32"===process.platform&&"codex"===o?t("codex debug models --bundled",{...n,shell:!0}):t(o,["debug","models","--bundled"],n);if(0===i.status&&i.stdout){const t=f(i.stdout);if(t)return t}(i.error||0!==i.status)&&r.logError("loadBundledCodexModels",i.error??new Error(`${o} exited ${i.status}`))}return null}export function loadPreviousCodexModels(){const t=x()?.filter(t=>!w(t));return t&&t.length>0?t:null}export function previousGatewayModelIds(){return(x()??[]).filter(w).map(t=>String(t.slug))}function x(){return e(codexCatalogPath())?f(o(codexCatalogPath(),"utf-8")):null}function w(t){return"number"==typeof t.priority&&t.priority>=1e3}let g=null;function h(){if(null!==g)return g;const t=[i(_,"..","..","..","codex-base-instructions.md"),i(_,"..","..","codex-base-instructions.md"),i(_,"codex-base-instructions.md")],n=t.find(t=>e(t));if(!n)throw new Error(`codex-base-instructions.md not found, searched: ${t.join(", ")}`);return g=o(n,"utf-8"),g}function y(t){return"number"==typeof t&&Number.isFinite(t)&&t>0?t:void 0}function b(t,e,o,n){const i=o?t:Math.min(t,e);return{window:i,compact:a(i,n)}}function C(t,e,o,n){const{standardContextWindow:i}=l();let s=y(t.contextWindow)??i;n&&t.id.startsWith("claude-")&&(s=n.has(t.id)?Math.max(s,1e6):Math.min(s,2e5));const{window:r,compact:u}=b(s,i,o,y(t.autoCompactTokens));return{slug:t.id,display_name:t.name||c(t.id),description:d(t.description,{millionContext:r>=1e6})??null,supported_reasoning_levels:[],shell_type:"unified_exec",visibility:"list",supported_in_api:!0,priority:e,additional_speed_tiers:[],service_tiers:[],availability_nux:null,upgrade:null,model_messages:{instructions_template:h()},base_instructions:h(),supports_reasoning_summaries:!0,supports_parallel_tool_calls:!1,prefer_websockets:!1,include_skills_usage_instructions:!1,include_plugin_usage_instructions:!1,include_apps_usage_instructions:!1,default_reasoning_summary:"auto",support_verbosity:!1,default_verbosity:null,apply_patch_tool_type:null,web_search_tool_type:"text",truncation_policy:{mode:"bytes",limit:1e4},supports_image_detail_original:!1,context_window:r,max_context_window:r,auto_compact_token_limit:u,effective_context_window_percent:95,experimental_supported_tools:[],input_modalities:["text","image"],supports_search_tool:!1,supports_experimental_context:!1,use_responses_lite:!1,supports_reasoning_effort_updates:!1,node_repl_auto_review_required:!1,node_repl_disabled:!1}}function v(t,e,o){if("list"!==t.visibility)return t;const n=y(t.context_window);if(!n)return t;const i=y(t.max_context_window)??n,s=o?Math.min(y(e?.contextWindow)??i,i):n,{window:r,compact:l}=b(s,n,!0,y(e?.autoCompactTokens));return{...t,context_window:r,auto_compact_token_limit:l}}export function buildCodexCatalog(t){const{bundled:e,specs:o,oneMillion:n,selectedModel:i}=t,s=t.claudeOneMillion?new Set(t.claudeOneMillion):void 0,r=t.keyModels?new Set(t.keyModels):null,l=new Set(t.gatewayAvailable),a=t=>!r||r.has(t)||t===i,d=t=>l.has(t)||t===i,c=new Map(o.map(t=>[t.id,t])),p=[],_=new Set;for(const t of e){const e="string"==typeof t.slug?t.slug:"";e&&!_.has(e)&&("list"!==t.visibility||a(e))&&(_.add(e),p.push(v(t,c.get(e),n)))}const m=[...o];i&&!c.has(i)&&m.push({id:i});const f=function(t){const e=new Map;for(const o of t){const t=/^claude-([a-z]+)-(.+)$/.exec(o);if(!t)continue;const n=t[2].split("-").filter(t=>/^\d{1,2}$/.test(t)).join("."),i=e.get(t[1]);(!i||u(n,i.version)>0)&&e.set(t[1],{id:o,version:n})}return new Set([...e.values()].map(t=>t.id))}(m.map(t=>t.id).filter(d));for(const t of m)!_.has(t.id)&&d(t.id)&&(t.id.startsWith("claude-")&&!f.has(t.id)&&t.id!==i||(_.add(t.id),p.push(C(t,1e3+p.length,n,s))));return{models:p}}
|
package/dist/lib/tools/codex.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as
|
|
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.
|
package/dist/locales/en_US.json
CHANGED
|
@@ -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}}
|
|
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
|
}
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -375,7 +375,7 @@
|
|
|
375
375
|
"registry_denied": "没有管理员权限,跳过了注册表兜底(本地配置已写好,通常不影响);如果重开桌面端后没有走 VibeAPI,用管理员身份打开终端再运行一次"
|
|
376
376
|
},
|
|
377
377
|
"claude_code": {
|
|
378
|
-
"gateway_models": "/model 菜单加入 {{count}}
|
|
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.
|
|
30
|
-
"model": "glm-5.
|
|
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-
|
|
38
|
-
"model": "kimi-
|
|
37
|
+
"name": "Kimi-K3",
|
|
38
|
+
"model": "kimi-k3"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
"name": "Qwen3.
|
|
42
|
-
"model": "qwen3.
|
|
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,80 @@
|
|
|
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-6-luna",
|
|
164
|
+
"name": "GPT-6 Luna",
|
|
165
|
+
"contextWindow": 1050000,
|
|
166
|
+
"claudeCode": false,
|
|
167
|
+
"codex": false
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "gpt-5.6-sol",
|
|
171
|
+
"name": "GPT-5.6 Sol",
|
|
172
|
+
"description": "105 万上下文 | 前沿档",
|
|
173
|
+
"contextWindow": 1050000,
|
|
174
|
+
"codex": false,
|
|
175
|
+
"claudeCode": false
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": "gpt-5.6-terra",
|
|
179
|
+
"name": "GPT-5.6 Terra",
|
|
180
|
+
"description": "105 万上下文 | 均衡生产档",
|
|
181
|
+
"contextWindow": 1050000,
|
|
182
|
+
"codex": false,
|
|
183
|
+
"claudeCode": false
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"id": "gpt-5.6-luna",
|
|
187
|
+
"name": "GPT-5.6 Luna",
|
|
188
|
+
"description": "105 万上下文 | 大批量低成本档",
|
|
189
|
+
"contextWindow": 1050000,
|
|
190
|
+
"codex": false,
|
|
191
|
+
"claudeCode": false
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "gemini-3.8-flash",
|
|
195
|
+
"name": "Gemini 3.8 Flash",
|
|
196
|
+
"description": "105 万上下文 | 推理与编码",
|
|
197
|
+
"contextWindow": 1048576,
|
|
198
|
+
"codex": false
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "gemini-3.1-pro",
|
|
202
|
+
"name": "Gemini 3.1 Pro",
|
|
203
|
+
"description": "100 万上下文 | 深度推理 | 长文档",
|
|
204
|
+
"contextWindow": 1000000,
|
|
205
|
+
"codex": false
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "gemini-3.7-flash",
|
|
209
|
+
"name": "Gemini 3.7 Flash",
|
|
210
|
+
"description": "100 万上下文 | Agent | 软件工程",
|
|
211
|
+
"contextWindow": 1000000,
|
|
212
|
+
"codex": false,
|
|
213
|
+
"claudeCode": false
|
|
214
|
+
},
|
|
140
215
|
{
|
|
141
216
|
"id": "deepseek-v4-pro",
|
|
142
217
|
"name": "DeepSeek V4 Pro",
|
|
@@ -149,12 +224,6 @@
|
|
|
149
224
|
"description": "100 万上下文 | 高速档",
|
|
150
225
|
"contextWindow": 1000000
|
|
151
226
|
},
|
|
152
|
-
{
|
|
153
|
-
"id": "deepseek-v4-flash",
|
|
154
|
-
"name": "DeepSeek V4 Flash",
|
|
155
|
-
"description": "100 万上下文 | 高速档",
|
|
156
|
-
"contextWindow": 1000000
|
|
157
|
-
},
|
|
158
227
|
{
|
|
159
228
|
"id": "glm-5.3",
|
|
160
229
|
"name": "GLM 5.3",
|
|
@@ -167,12 +236,6 @@
|
|
|
167
236
|
"description": "105 万上下文 | 高速档",
|
|
168
237
|
"contextWindow": 1048576
|
|
169
238
|
},
|
|
170
|
-
{
|
|
171
|
-
"id": "glm-5.2",
|
|
172
|
-
"name": "GLM 5.2",
|
|
173
|
-
"description": "100 万上下文 | 编程",
|
|
174
|
-
"contextWindow": 1000000
|
|
175
|
-
},
|
|
176
239
|
{
|
|
177
240
|
"id": "kimi-k3",
|
|
178
241
|
"name": "Kimi K3",
|
|
@@ -191,18 +254,6 @@
|
|
|
191
254
|
"description": "100 万上下文 | 高速档",
|
|
192
255
|
"contextWindow": 1000000
|
|
193
256
|
},
|
|
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
257
|
{
|
|
207
258
|
"id": "MiniMax-M3",
|
|
208
259
|
"name": "MiniMax M3",
|
|
@@ -213,7 +264,14 @@
|
|
|
213
264
|
"id": "grok-4.7",
|
|
214
265
|
"name": "Grok 4.7",
|
|
215
266
|
"description": "50 万上下文 | 长时 Agent | 编码",
|
|
267
|
+
"contextWindow": 500000
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"id": "grok-4.6",
|
|
271
|
+
"name": "Grok 4.6",
|
|
272
|
+
"description": "50 万上下文 | 编码",
|
|
216
273
|
"contextWindow": 500000,
|
|
274
|
+
"codex": false,
|
|
217
275
|
"claudeCode": false
|
|
218
276
|
}
|
|
219
277
|
]
|
package/models.json
CHANGED
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
"model": "gpt-5.5"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
"name": "GLM-5.
|
|
30
|
-
"model": "glm-5.
|
|
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-
|
|
38
|
-
"model": "kimi-
|
|
37
|
+
"name": "Kimi-K3",
|
|
38
|
+
"model": "kimi-k3"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
"name": "Qwen3.
|
|
42
|
-
"model": "qwen3.
|
|
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,80 @@
|
|
|
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-6-luna",
|
|
164
|
+
"name": "GPT-6 Luna",
|
|
165
|
+
"contextWindow": 1050000,
|
|
166
|
+
"claudeCode": false,
|
|
167
|
+
"codex": false
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "gpt-5.6-sol",
|
|
171
|
+
"name": "GPT-5.6 Sol",
|
|
172
|
+
"description": "105 万上下文 | 前沿档",
|
|
173
|
+
"contextWindow": 1050000,
|
|
174
|
+
"codex": false,
|
|
175
|
+
"claudeCode": false
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": "gpt-5.6-terra",
|
|
179
|
+
"name": "GPT-5.6 Terra",
|
|
180
|
+
"description": "105 万上下文 | 均衡生产档",
|
|
181
|
+
"contextWindow": 1050000,
|
|
182
|
+
"codex": false,
|
|
183
|
+
"claudeCode": false
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"id": "gpt-5.6-luna",
|
|
187
|
+
"name": "GPT-5.6 Luna",
|
|
188
|
+
"description": "105 万上下文 | 大批量低成本档",
|
|
189
|
+
"contextWindow": 1050000,
|
|
190
|
+
"codex": false,
|
|
191
|
+
"claudeCode": false
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "gemini-3.8-flash",
|
|
195
|
+
"name": "Gemini 3.8 Flash",
|
|
196
|
+
"description": "105 万上下文 | 推理与编码",
|
|
197
|
+
"contextWindow": 1048576,
|
|
198
|
+
"codex": false
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "gemini-3.1-pro",
|
|
202
|
+
"name": "Gemini 3.1 Pro",
|
|
203
|
+
"description": "100 万上下文 | 深度推理 | 长文档",
|
|
204
|
+
"contextWindow": 1000000,
|
|
205
|
+
"codex": false
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "gemini-3.7-flash",
|
|
209
|
+
"name": "Gemini 3.7 Flash",
|
|
210
|
+
"description": "100 万上下文 | Agent | 软件工程",
|
|
211
|
+
"contextWindow": 1000000,
|
|
212
|
+
"codex": false,
|
|
213
|
+
"claudeCode": false
|
|
214
|
+
},
|
|
140
215
|
{
|
|
141
216
|
"id": "deepseek-v4-pro",
|
|
142
217
|
"name": "DeepSeek V4 Pro",
|
|
@@ -149,12 +224,6 @@
|
|
|
149
224
|
"description": "100 万上下文 | 高速档",
|
|
150
225
|
"contextWindow": 1000000
|
|
151
226
|
},
|
|
152
|
-
{
|
|
153
|
-
"id": "deepseek-v4-flash",
|
|
154
|
-
"name": "DeepSeek V4 Flash",
|
|
155
|
-
"description": "100 万上下文 | 高速档",
|
|
156
|
-
"contextWindow": 1000000
|
|
157
|
-
},
|
|
158
227
|
{
|
|
159
228
|
"id": "glm-5.3",
|
|
160
229
|
"name": "GLM 5.3",
|
|
@@ -167,12 +236,6 @@
|
|
|
167
236
|
"description": "105 万上下文 | 高速档",
|
|
168
237
|
"contextWindow": 1048576
|
|
169
238
|
},
|
|
170
|
-
{
|
|
171
|
-
"id": "glm-5.2",
|
|
172
|
-
"name": "GLM 5.2",
|
|
173
|
-
"description": "100 万上下文 | 编程",
|
|
174
|
-
"contextWindow": 1000000
|
|
175
|
-
},
|
|
176
239
|
{
|
|
177
240
|
"id": "kimi-k3",
|
|
178
241
|
"name": "Kimi K3",
|
|
@@ -191,18 +254,6 @@
|
|
|
191
254
|
"description": "100 万上下文 | 高速档",
|
|
192
255
|
"contextWindow": 1000000
|
|
193
256
|
},
|
|
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
257
|
{
|
|
207
258
|
"id": "MiniMax-M3",
|
|
208
259
|
"name": "MiniMax M3",
|
|
@@ -213,7 +264,14 @@
|
|
|
213
264
|
"id": "grok-4.7",
|
|
214
265
|
"name": "Grok 4.7",
|
|
215
266
|
"description": "50 万上下文 | 长时 Agent | 编码",
|
|
267
|
+
"contextWindow": 500000
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"id": "grok-4.6",
|
|
271
|
+
"name": "Grok 4.6",
|
|
272
|
+
"description": "50 万上下文 | 编码",
|
|
216
273
|
"contextWindow": 500000,
|
|
274
|
+
"codex": false,
|
|
217
275
|
"claudeCode": false
|
|
218
276
|
}
|
|
219
277
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.59",
|
|
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",
|