@vibeapi/cli 0.0.157 → 0.0.159

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.
@@ -69,6 +69,14 @@ export declare const DEDICATED_KEYS: readonly [{
69
69
  readonly tool: "atomcode";
70
70
  readonly need: "any";
71
71
  readonly hidden: true;
72
+ }, {
73
+ readonly option: "codebuddykey";
74
+ readonly tool: "codebuddy";
75
+ readonly need: "any";
76
+ }, {
77
+ readonly option: "workbuddykey";
78
+ readonly tool: "workbuddy";
79
+ readonly need: "any";
72
80
  }];
73
81
  export type DedicatedKeyOption = (typeof DEDICATED_KEYS)[number]['option'];
74
82
  export interface SetupCommandOptions extends Partial<Record<DedicatedKeyOption, string>> {
@@ -1 +1 @@
1
- import o from"chalk";import{getAdapter as e}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as i}from"./base.js";import{i18n as l}from"../lib/i18n.js";import{configManager as s}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as r,fetchModels as p}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as f,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as m}from"../lib/tools/claude-desktop.js";import{operationError as g,authenticationError as y,networkError as _,EXIT_CODES as k}from"../lib/errors.js";import{canPrompt as h,emitJson as b,isColorEnabled as v,isDryRun as w,isJsonMode as x,isNonInteractive as C,shouldAutoConfirm as j}from"../lib/runtime-options.js";import{appsForTools as K,appUpgradeCommand as $,getApp as T}from"../lib/app-update.js";import{join as M}from"path";import{asksBeforeWriting as G,confirmWritePlan as O,printWritePlan as S,toolWritePlan as I}from"../lib/write-plan.js";import N from"inquirer";import{logger as E}from"../utils/logger.js";import{forgetInstalls as A}from"../lib/tools/tool-installs.js";import{CLI_NAME as B,brandEnv as D}from"../lib/brand.js";export const DEDICATED_KEYS=[{option:"claudekey",tool:"claude-code",need:"claude"},{option:"codexkey",tool:"codex",need:"gpt"},{option:"grokkey",tool:"grok",need:"grok"},{option:"desktopkey",tool:"claude-desktop",need:"claude"},{option:"agykey",tool:"agy",need:"gemini"},{option:"openclawkey",tool:"openclaw",need:"any",hidden:!0},{option:"hermeskey",tool:"hermes",need:"any",hidden:!0},{option:"atomcodekey",tool:"atomcode",need:"any",hidden:!0}];async function F(o){const e=await p(o);if(!e.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=e.models.map(o=>o.toLowerCase());return{hasClaude:t.some(o=>o.includes("claude")),hasGpt:t.some(o=>o.startsWith("gpt")||o.includes("gpt-")||/^o\d/.test(o)),hasGrok:t.some(o=>o.includes("grok")),hasGemini:t.some(o=>o.startsWith("gemini")),probed:!0}}function L(o,e){switch(e){case"claude":return o.hasClaude;case"gpt":return o.hasGpt;case"grok":return o.hasGrok;case"gemini":return o.hasGemini;default:return!0}}export function parseOnOff(o){const e=(o??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(e)||!["off","false","hide","0","no","n"].includes(e)&&void 0}function R(e){console.log(o.green(e))}function P(e){console.log(o.yellow(e))}function Y(e){console.error(o.red(e))}let q=null;function z(o){const e={operation:1,authentication:2,network:3};(!q||e[o]>e[q])&&(q=o)}async function H(e,t){x()||console.log(o.gray(l.t("setup.validating",{label:e})));const i=await r(t);if(i.valid)return!0;const a=l.t("errors.key_placeholder"),n="--key"===e?`${B} setup --key ${a}`:`${B} setup ${e} ${a}`;let p;if("network_error"===i.error?(z("network"),p=_(l.t("setup.unreachable",{url:s.getBaseUrl(),reason:i.message||"network error"}),`${B} base auto`,"network_error")):"invalid_api_key"===i.error?(z("authentication"),p=y(l.t("setup.invalid_key",{label:e,reason:i.message||"invalid"}),n,"invalid_api_key")):(z("operation"),p=g(l.t("setup.validate_failed",{label:e,reason:i.message||i.error||"unknown"}),n,i.error)),x())throw p;return Y(p.message),console.log(o.gray(l.t("errors.next_step",{command:p.hint}))),!1}async function W(t,i,r={}){const{oneMillion:p,officialLogin:c,desktopRegistry:u,force:y,takeOver:_}=r,k=n[t];try{"claude-desktop"===t&&void 0!==u&&s.setToolSetting("claude-desktop",f,u),"codex"===t&&void 0!==p&&s.setToolSetting("codex",d,p),"claude-desktop"===t&&void 0!==c&&s.setToolSetting("claude-desktop",m,!c),await a.applyPrivateConfig(t,i,s.getModelsForTool(t),{force:y,takeOver:_})}catch(o){if(x())throw g(o instanceof Error?o.message:String(o),`${B} setup --key ${l.t("errors.key_placeholder")}`,"config_write_failed");const e=(o instanceof Error?o.message:String(o)).replace(/\s+/g," ").trim();return Y("× "+l.t("setup.apply_failed",{tool:k.displayName,reason:e})),!1}return a.printApplied(t),y&&console.log(o.gray(" "+l.t("config.force_done",{tool:k.displayName,name:t}))),"codex"===t&&void 0!==p&&console.log(o.gray(" "+l.t(p?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),e(t)?.isInstalled&&!a.isToolInstalled(t)&&console.log(o.yellow(" ! "+l.t("setup.gui_preconfigured",{tool:k.displayName,install:k.installCommand}))),!0}function U(o){return!e(o)?.isInstalled&&!a.isToolInstalled(o)}export async function installMissingApps(e,t){const i=K(e).filter(o=>{const e=T(o)?.appInstaller;try{return Boolean(e&&!e.blocker()&&!e.installedPath())}catch(e){return E.logError(`setup: app installed? ${o}`,e),!1}});if(0===i.length)return!1;const s=o=>T(o)?.displayName??o;let a=t;if(void 0===a&&!x())if(j())a=!C();else if(h()){const o=i.map(s).join(l.t("setup.list_separator")),e=i.reduce((o,e)=>o+(T(e)?.appInstaller.sizeMb()??0),0);console.log(""),a=(await N.prompt([{type:"confirm",name:"go",message:l.t("setup.install_app_confirm",{tools:o,size:String(e)}),default:!0}])).go}if(!a&&!x())for(const e of i)console.log(o.gray(l.t("setup.install_app_hint",{tool:s(e),command:$(e)})));return Boolean(a)}function J(o){P(l.t("setup.still_missing",{tools:o.map(o=>n[o].displayName).join(l.t("setup.list_separator")),commands:o.map(o=>`${B} config ${o} --yes`).join(l.t("setup.list_separator"))}))}function Q(e,t,i=[]){const a=I(e),n=M(s.getConfigDir(),"config.yaml");if(x())b({dry_run:!0,tools:a,install_first:i,key_file:t?n:null});else{if(0===a.length&&0===i.length){const e=(v()?o.cyan:o=>o)(l.t("runtime.dry_run_label"));return void console.log(`${e} ${l.t("setup.dry_run_none")}`)}S(a,{label:l.t("runtime.dry_run_label"),keyFile:t?n:void 0,installFirst:i})}}function V(){return"darwin"===process.platform||"win32"===process.platform||a.isToolInstalled("claude-desktop")}export async function setupCommand(r){q=null;const p=r.key?.trim(),f=DEDICATED_KEYS.map(o=>({...o,apiKey:r[o.option]?.trim()})).filter(o=>Boolean(o.apiKey)),m=void 0!==r.oneMillion,g=void 0!==r.claudeOfficial;let y;if(g&&(y=parseOnOff(r.claudeOfficial),void 0===y))return Y(l.t("setup.claude_official_invalid",{value:String(r.claudeOfficial)})),void(process.exitCode=1);const _=void 0!==r.desktopRegistry;let h;if(_&&(h=parseOnOff(r.desktopRegistry),void 0===h))return Y(l.t("setup.desktop_registry_invalid",{value:String(r.desktopRegistry)})),void(process.exitCode=1);const b=g||_,v=Boolean(p||f.length>0),K={oneMillion:r.oneMillion,officialLogin:y,desktopRegistry:h,force:r.force};if(!v&&(m||b)&&w())return void Q([...m?["codex"]:[],...b?["claude-desktop"]:[]],!1);if(!v&&(m||b)){if(m){const o=s.getApiKeyForTool("codex");if(o)try{const e=!0===r.oneMillion;s.setToolSetting("codex",d,e);const t=await u(o);c(e,t??void 0),R(l.t(e?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(o){Y(l.t("setup.apply_failed",{tool:n.codex.displayName,reason:o instanceof Error?o.message:String(o)})),process.exitCode=1}else Y(l.t("setup.codex_1m_missing_key")),process.exitCode=1}if(b){const o=s.getApiKeyForTool("claude-desktop");o?await W("claude-desktop",o,{...K,oneMillion:void 0,takeOver:!0})?await t(["claude-desktop"],{apps:{install:!1}}):process.exitCode=1:(Y(l.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!v)return Y(l.t("setup.usage")),void(process.exitCode=1);const $=w();$||await i(p||f[0].apiKey);const T=[],S=[];let I,z=0,X=0;const Z=new Set,oo=f.map(o=>({tool:o.tool,label:`--${o.option}`,apiKey:o.apiKey,need:o.need}));for(const o of oo){if(!await H(o.label,o.apiKey)){X+=1;continue}const e=await F(o.apiKey);!e.probed||L(e,o.need)?(e.probed||P(l.t("setup.probe_failed",{label:o.label})),T.push({tool:o.tool,apiKey:o.apiKey,dedicated:!0}),Z.add(o.tool)):(Y(l.t(`setup.key_no_${o.need}`,{label:o.label,tool:n[o.tool].displayName})),X+=1)}const eo=!!p&&await H("--key",p);p&&!eo&&(I=k[q??"operation"]);const to=p&&eo?await F(p):void 0;if(p&&to&&!to.probed&&P(l.t("setup.probe_failed",{label:"--key"})),!(p&&to&&to.probed)||to.hasClaude||to.hasGpt||to.hasGrok||to.hasGemini||(Y(l.t("setup.key_no_family")),I=1),p&&to&&void 0===I){const o=[{tool:"claude-code",capable:to.hasClaude,skipMsg:l.t(V()?"setup.skip_no_claude_desktop":"setup.skip_no_claude")},{tool:"codex",capable:to.hasGpt,skipMsg:l.t("setup.skip_no_gpt")},{tool:"grok",capable:to.hasGrok,skipMsg:l.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:to.hasClaude&&V(),skipMsg:"",quiet:!0},{tool:"agy",capable:to.hasGemini,skipMsg:l.t("setup.skip_no_gemini")}];for(const e of o)Z.has(e.tool)||(e.capable?(T.push({tool:e.tool,apiKey:p,dedicated:!1}),Z.add(e.tool)):e.quiet||P(e.skipMsg))}const io=Boolean(p&&eo&&void 0===I);if($)return Q(T.filter(o=>!U(o.tool)).map(o=>o.tool),T.length>0,T.filter(o=>U(o.tool)).map(o=>o.tool)),void((X>0||void 0!==I)&&(process.exitCode=I??k[q??"operation"]));const lo=T.filter(o=>!U(o.tool)),so=io||T.some(o=>o.dedicated),ao=!r.fromInstaller&&G();if((T.length>0||so)&&!r.fromInstaller&&!await O(lo.map(o=>o.tool),{keyFile:so?M(s.getConfigDir(),"config.yaml"):void 0,installFirst:T.filter(o=>U(o.tool)).map(o=>o.tool)}))return void(process.exitCode=k.cancelled);io&&p&&s.setApiKey(p);for(const o of T)o.dedicated&&s.setToolApiKey(o.tool,o.apiKey),U(o.tool)?S.push({tool:o.tool,apiKey:o.apiKey}):await W(o.tool,o.apiKey,{...K,takeOver:o.dedicated||ao})?z+=1:(X+=1,Z.delete(o.tool));if(void 0!==I)return S.length>0&&J(S.map(o=>o.tool)),void(process.exitCode=I);let no=[];if(S.length>0){const e=await async function(e,t,i){const s=e.map(o=>o.tool);if(0===s.length)return{still:[],applied:[],failed:[]};const r=s.map(o=>n[o].displayName).join(l.t("setup.list_separator"));let p=t;if(void 0!==p||x()||(j()?p=!C():process.stdin.isTTY&&(console.log(""),p=(await N.prompt([{type:"confirm",name:"go",message:l.t("setup.install_confirm",{tools:r}),default:!0}])).go)),!p){console.log("");for(const e of s)console.log(o.yellow("! "+l.t("setup.install_hint",{tool:n[e].displayName,install:n[e].installCommand})));return x()||console.log(o.gray(l.t("setup.install_all_hint",{cli:B}))),{still:s,applied:[],failed:[]}}const d=[];for(const e of s){console.log(o.bold(`\n${n[e].displayName}`));try{await a.installTool(e)}catch(o){E.logError(`setup: install ${e}`,o)}A(),a.isToolInstalled(e)||d.push(e)}const c=s.filter(o=>!d.includes(o));c.length>0&&console.log("");const u=[],f=[];for(const{tool:o,apiKey:t}of e.filter(o=>c.includes(o.tool)))await W(o,t,i)?u.push(o):f.push(o);return{still:d,applied:u,failed:f}}(S,r.install,K);no=e.still,z+=e.applied.length,X+=e.failed.length;for(const o of[...no,...e.failed])Z.delete(o)}if(z>0){const t=[...Z].filter(o=>!e(o)?.isInstalled&&!no.includes(o)),i=t.map(o=>n[o]?.command).filter(Boolean).join(" / "),s=l.t("setup.done",{count:String(z)});R("\n"+(i?`${s}${l.t("setup.done_commands",{commands:i})}`:s)),no.length>0&&J(no);for(const o of t){const e=a.ensureNativeOnPath(o);e&&R(l.t("setup.path_added",{tool:n[o].displayName,dir:e}))}const r=t.filter(o=>!a.isOnPath(o));if(r.length>0&&!x()&&"1"!==D("PATH_HINT_BY_CALLER")){const e=r.map(o=>n[o].command).join(" / ");console.log(o.yellow(" ! "+l.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:e})))}}0===z&&no.length>0&&J(no),X>0&&(Y(l.t("setup.failed_summary",{count:String(X)})),x()||console.error(`${B} setup --key ${l.t("errors.key_placeholder")}`)),await t([...Z],{apps:{install:await installMissingApps([...Z],r.install)}}),(X>0||0===z&&0===no.length)&&(process.exitCode=k[q??"operation"])}
1
+ import o from"chalk";import{getAdapter as e}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as i}from"./base.js";import{i18n as l}from"../lib/i18n.js";import{configManager as s}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as r,fetchModels as p}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as f,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as m}from"../lib/tools/claude-desktop.js";import{operationError as g,authenticationError as y,networkError as k,EXIT_CODES as _}from"../lib/errors.js";import{canPrompt as h,emitJson as b,isColorEnabled as w,isDryRun as v,isJsonMode as x,isNonInteractive as C,shouldAutoConfirm as j}from"../lib/runtime-options.js";import{appsForTools as K,appUpgradeCommand as T,getApp as $}from"../lib/app-update.js";import{join as G}from"path";import{asksBeforeWriting as M,confirmWritePlan as I,printWritePlan as O,toolWritePlan as S}from"../lib/write-plan.js";import N from"inquirer";import{logger as E}from"../utils/logger.js";import{forgetInstalls as A}from"../lib/tools/tool-installs.js";import{CLI_NAME as B,brandEnv as D}from"../lib/brand.js";export const DEDICATED_KEYS=[{option:"claudekey",tool:"claude-code",need:"claude"},{option:"codexkey",tool:"codex",need:"gpt"},{option:"grokkey",tool:"grok",need:"grok"},{option:"desktopkey",tool:"claude-desktop",need:"claude"},{option:"agykey",tool:"agy",need:"gemini"},{option:"openclawkey",tool:"openclaw",need:"any",hidden:!0},{option:"hermeskey",tool:"hermes",need:"any",hidden:!0},{option:"atomcodekey",tool:"atomcode",need:"any",hidden:!0},{option:"codebuddykey",tool:"codebuddy",need:"any"},{option:"workbuddykey",tool:"workbuddy",need:"any"}];async function F(o){const e=await p(o);if(!e.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=e.models.map(o=>o.toLowerCase());return{hasClaude:t.some(o=>o.includes("claude")),hasGpt:t.some(o=>o.startsWith("gpt")||o.includes("gpt-")||/^o\d/.test(o)),hasGrok:t.some(o=>o.includes("grok")),hasGemini:t.some(o=>o.startsWith("gemini")),probed:!0}}function L(o,e){switch(e){case"claude":return o.hasClaude;case"gpt":return o.hasGpt;case"grok":return o.hasGrok;case"gemini":return o.hasGemini;default:return!0}}export function parseOnOff(o){const e=(o??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(e)||!["off","false","hide","0","no","n"].includes(e)&&void 0}function R(e){console.log(o.green(e))}function q(e){console.log(o.yellow(e))}function P(e){console.error(o.red(e))}let Y=null;function z(o){const e={operation:1,authentication:2,network:3};(!Y||e[o]>e[Y])&&(Y=o)}async function H(e,t){x()||console.log(o.gray(l.t("setup.validating",{label:e})));const i=await r(t);if(i.valid)return!0;const a=l.t("errors.key_placeholder"),n="--key"===e?`${B} setup --key ${a}`:`${B} setup ${e} ${a}`;let p;if("network_error"===i.error?(z("network"),p=k(l.t("setup.unreachable",{url:s.getBaseUrl(),reason:i.message||"network error"}),`${B} base auto`,"network_error")):"invalid_api_key"===i.error?(z("authentication"),p=y(l.t("setup.invalid_key",{label:e,reason:i.message||"invalid"}),n,"invalid_api_key")):(z("operation"),p=g(l.t("setup.validate_failed",{label:e,reason:i.message||i.error||"unknown"}),n,i.error)),x())throw p;return P(p.message),console.log(o.gray(l.t("errors.next_step",{command:p.hint}))),!1}async function W(t,i,r={}){const{oneMillion:p,officialLogin:c,desktopRegistry:u,force:y,takeOver:k}=r,_=n[t];try{"claude-desktop"===t&&void 0!==u&&s.setToolSetting("claude-desktop",f,u),"codex"===t&&void 0!==p&&s.setToolSetting("codex",d,p),"claude-desktop"===t&&void 0!==c&&s.setToolSetting("claude-desktop",m,!c),await a.applyPrivateConfig(t,i,s.getModelsForTool(t),{force:y,takeOver:k})}catch(o){if(x())throw g(o instanceof Error?o.message:String(o),`${B} setup --key ${l.t("errors.key_placeholder")}`,"config_write_failed");const e=(o instanceof Error?o.message:String(o)).replace(/\s+/g," ").trim();return P("× "+l.t("setup.apply_failed",{tool:_.displayName,reason:e})),!1}return a.printApplied(t),y&&console.log(o.gray(" "+l.t("config.force_done",{tool:_.displayName,name:t}))),"codex"===t&&void 0!==p&&console.log(o.gray(" "+l.t(p?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),e(t)?.isInstalled&&!a.isToolInstalled(t)&&console.log(o.yellow(" ! "+l.t("setup.gui_preconfigured",{tool:_.displayName,install:_.installCommand}))),!0}function U(o){return!e(o)?.isInstalled&&!a.isToolInstalled(o)}export async function installMissingApps(e,t){const i=K(e).filter(o=>{const e=$(o)?.appInstaller;try{return Boolean(e&&!e.blocker()&&!e.installedPath())}catch(e){return E.logError(`setup: app installed? ${o}`,e),!1}});if(0===i.length)return!1;const s=o=>$(o)?.displayName??o;let a=t;if(void 0===a&&!x())if(j())a=!C();else if(h()){const o=i.map(s).join(l.t("setup.list_separator")),e=i.reduce((o,e)=>o+($(e)?.appInstaller.sizeMb()??0),0);console.log(""),a=(await N.prompt([{type:"confirm",name:"go",message:l.t("setup.install_app_confirm",{tools:o,size:String(e)}),default:!0}])).go}if(!a&&!x())for(const e of i)console.log(o.gray(l.t("setup.install_app_hint",{tool:s(e),command:T(e)})));return Boolean(a)}function J(o){q(l.t("setup.still_missing",{tools:o.map(o=>n[o].displayName).join(l.t("setup.list_separator")),commands:o.map(o=>`${B} config ${o} --yes`).join(l.t("setup.list_separator"))}))}function Q(e,t,i=[]){const a=S(e),n=G(s.getConfigDir(),"config.yaml");if(x())b({dry_run:!0,tools:a,install_first:i,key_file:t?n:null});else{if(0===a.length&&0===i.length){const e=(w()?o.cyan:o=>o)(l.t("runtime.dry_run_label"));return void console.log(`${e} ${l.t("setup.dry_run_none")}`)}O(a,{label:l.t("runtime.dry_run_label"),keyFile:t?n:void 0,installFirst:i})}}function V(){return"darwin"===process.platform||"win32"===process.platform||a.isToolInstalled("claude-desktop")}export async function setupCommand(r){Y=null;const p=r.key?.trim(),f=DEDICATED_KEYS.map(o=>({...o,apiKey:r[o.option]?.trim()})).filter(o=>Boolean(o.apiKey)),m=void 0!==r.oneMillion,g=void 0!==r.claudeOfficial;let y;if(g&&(y=parseOnOff(r.claudeOfficial),void 0===y))return P(l.t("setup.claude_official_invalid",{value:String(r.claudeOfficial)})),void(process.exitCode=1);const k=void 0!==r.desktopRegistry;let h;if(k&&(h=parseOnOff(r.desktopRegistry),void 0===h))return P(l.t("setup.desktop_registry_invalid",{value:String(r.desktopRegistry)})),void(process.exitCode=1);const b=g||k,w=Boolean(p||f.length>0),K={oneMillion:r.oneMillion,officialLogin:y,desktopRegistry:h,force:r.force};if(!w&&(m||b)&&v())return void Q([...m?["codex"]:[],...b?["claude-desktop"]:[]],!1);if(!w&&(m||b)){if(m){const o=s.getApiKeyForTool("codex");if(o)try{const e=!0===r.oneMillion;s.setToolSetting("codex",d,e);const t=await u(o);c(e,t??void 0),R(l.t(e?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(o){P(l.t("setup.apply_failed",{tool:n.codex.displayName,reason:o instanceof Error?o.message:String(o)})),process.exitCode=1}else P(l.t("setup.codex_1m_missing_key")),process.exitCode=1}if(b){const o=s.getApiKeyForTool("claude-desktop");o?await W("claude-desktop",o,{...K,oneMillion:void 0,takeOver:!0})?await t(["claude-desktop"],{apps:{install:!1}}):process.exitCode=1:(P(l.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!w)return P(l.t("setup.usage")),void(process.exitCode=1);const T=v();T||await i(p||f[0].apiKey);const $=[],O=[];let S,z=0,X=0;const Z=new Set,oo=f.map(o=>({tool:o.tool,label:`--${o.option}`,apiKey:o.apiKey,need:o.need}));for(const o of oo){if(!await H(o.label,o.apiKey)){X+=1;continue}const e=await F(o.apiKey);!e.probed||L(e,o.need)?(e.probed||q(l.t("setup.probe_failed",{label:o.label})),$.push({tool:o.tool,apiKey:o.apiKey,dedicated:!0}),Z.add(o.tool)):(P(l.t(`setup.key_no_${o.need}`,{label:o.label,tool:n[o.tool].displayName})),X+=1)}const eo=!!p&&await H("--key",p);p&&!eo&&(S=_[Y??"operation"]);const to=p&&eo?await F(p):void 0;if(p&&to&&!to.probed&&q(l.t("setup.probe_failed",{label:"--key"})),!(p&&to&&to.probed)||to.hasClaude||to.hasGpt||to.hasGrok||to.hasGemini||(P(l.t("setup.key_no_family")),S=1),p&&to&&void 0===S){const o=to.hasClaude||to.hasGpt||to.hasGrok||to.hasGemini,e=[{tool:"claude-code",capable:to.hasClaude,skipMsg:l.t(V()?"setup.skip_no_claude_desktop":"setup.skip_no_claude")},{tool:"codex",capable:to.hasGpt,skipMsg:l.t("setup.skip_no_gpt")},{tool:"grok",capable:to.hasGrok,skipMsg:l.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:to.hasClaude&&V(),skipMsg:"",quiet:!0},{tool:"agy",capable:to.hasGemini,skipMsg:l.t("setup.skip_no_gemini")},{tool:"codebuddy",capable:o&&a.isToolInstalled("codebuddy"),skipMsg:"",quiet:!0},{tool:"workbuddy",capable:o&&a.isToolInstalled("workbuddy"),skipMsg:"",quiet:!0}];for(const o of e)Z.has(o.tool)||(o.capable?($.push({tool:o.tool,apiKey:p,dedicated:!1}),Z.add(o.tool)):o.quiet||q(o.skipMsg))}const io=Boolean(p&&eo&&void 0===S);if(T)return Q($.filter(o=>!U(o.tool)).map(o=>o.tool),$.length>0,$.filter(o=>U(o.tool)).map(o=>o.tool)),void((X>0||void 0!==S)&&(process.exitCode=S??_[Y??"operation"]));const lo=$.filter(o=>!U(o.tool)),so=io||$.some(o=>o.dedicated),ao=!r.fromInstaller&&M();if(($.length>0||so)&&!r.fromInstaller&&!await I(lo.map(o=>o.tool),{keyFile:so?G(s.getConfigDir(),"config.yaml"):void 0,installFirst:$.filter(o=>U(o.tool)).map(o=>o.tool)}))return void(process.exitCode=_.cancelled);io&&p&&s.setApiKey(p);for(const o of $)o.dedicated&&s.setToolApiKey(o.tool,o.apiKey),U(o.tool)?O.push({tool:o.tool,apiKey:o.apiKey}):await W(o.tool,o.apiKey,{...K,takeOver:o.dedicated||ao})?z+=1:(X+=1,Z.delete(o.tool));if(void 0!==S)return O.length>0&&J(O.map(o=>o.tool)),void(process.exitCode=S);let no=[];if(O.length>0){const e=await async function(e,t,i){const s=e.map(o=>o.tool);if(0===s.length)return{still:[],applied:[],failed:[]};const r=s.map(o=>n[o].displayName).join(l.t("setup.list_separator"));let p=t;if(void 0!==p||x()||(j()?p=!C():process.stdin.isTTY&&(console.log(""),p=(await N.prompt([{type:"confirm",name:"go",message:l.t("setup.install_confirm",{tools:r}),default:!0}])).go)),!p){console.log("");for(const e of s)console.log(o.yellow("! "+l.t("setup.install_hint",{tool:n[e].displayName,install:n[e].installCommand})));return x()||console.log(o.gray(l.t("setup.install_all_hint",{cli:B}))),{still:s,applied:[],failed:[]}}const d=[];for(const e of s){console.log(o.bold(`\n${n[e].displayName}`));try{await a.installTool(e)}catch(o){E.logError(`setup: install ${e}`,o)}A(),a.isToolInstalled(e)||d.push(e)}const c=s.filter(o=>!d.includes(o));c.length>0&&console.log("");const u=[],f=[];for(const{tool:o,apiKey:t}of e.filter(o=>c.includes(o.tool)))await W(o,t,i)?u.push(o):f.push(o);return{still:d,applied:u,failed:f}}(O,r.install,K);no=e.still,z+=e.applied.length,X+=e.failed.length;for(const o of[...no,...e.failed])Z.delete(o)}if(z>0){const t=[...Z].filter(o=>!e(o)?.isInstalled&&!no.includes(o)),i=t.map(o=>n[o]?.command).filter(Boolean).join(" / "),s=l.t("setup.done",{count:String(z)});R("\n"+(i?`${s}${l.t("setup.done_commands",{commands:i})}`:s)),no.length>0&&J(no);for(const o of t){const e=a.ensureNativeOnPath(o);e&&R(l.t("setup.path_added",{tool:n[o].displayName,dir:e}))}const r=t.filter(o=>!a.isOnPath(o));if(r.length>0&&!x()&&"1"!==D("PATH_HINT_BY_CALLER")){const e=r.map(o=>n[o].command).join(" / ");console.log(o.yellow(" ! "+l.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:e})))}}0===z&&no.length>0&&J(no),X>0&&(P(l.t("setup.failed_summary",{count:String(X)})),x()||console.error(`${B} setup --key ${l.t("errors.key_placeholder")}`)),await t([...Z],{apps:{install:await installMissingApps([...Z],r.install)}}),(X>0||0===z&&0===no.length)&&(process.exitCode=_[Y??"operation"])}
@@ -116,6 +116,20 @@
116
116
  "check": ["agy", "--version"],
117
117
  "requires": []
118
118
  },
119
+ {
120
+ "id": "codebuddy",
121
+ "name": "CodeBuddy Code",
122
+ "category": "cli",
123
+ "aliases": ["codebuddy-code"],
124
+ "platforms": ["darwin", "linux", "win32"],
125
+ "adapter": "codebuddy",
126
+ "latest": { "type": "npm", "package": "@tencent-ai/codebuddy-code" },
127
+ "sources": { "all": "npmmirror 的 @tencent-ai/codebuddy-code(与 npmjs 同一份)" },
128
+ "verify": { "checksum": "npm sha512", "signature": "—" },
129
+ "install": { "type": "adapter" },
130
+ "check": ["codebuddy", "--version"],
131
+ "requires": ["node"]
132
+ },
119
133
  {
120
134
  "id": "claude-desktop",
121
135
  "name": "Claude Desktop",
@@ -76,7 +76,11 @@ export type { GatewayClientModel } from './client-config.js';
76
76
  export declare function fetchClientModelConfig(options?: {
77
77
  timeoutMs?: number;
78
78
  fetchFn?: typeof fetch;
79
- }): Promise<Record<string, GatewayClientModel[]> | null>;
79
+ apiKey?: string;
80
+ }): Promise<{
81
+ models: Record<string, GatewayClientModel[]>;
82
+ personal?: string[];
83
+ } | null>;
80
84
  /**
81
85
  * 验证 API Key 的有效性和网络连通性,遇到网络错误时按指数退避重试
82
86
  */
@@ -1 +1 @@
1
- import{KEY_INFO_PATH as r}from"./gateway-protocol.js";import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as o}from"./i18n.js";import{rememberPublishedAddresses as a}from"./gateway-addresses.js";import{eligibleLists as n}from"./client-config.js";import{track as s}from"./telemetry.js";import{refreshClientConfig as i}from"./client-config-sync.js";const l=r=>new Promise(e=>setTimeout(e,r));async function c(r,e,a){const n=`${t.getBaseUrl()}/v1/models`,s=new AbortController,i=setTimeout(()=>s.abort(),e);try{const e=await a(n,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:s.signal});if(401===e.status)return{valid:!1,error:"invalid_api_key",message:o.t("validator.rejected")};if(e.ok){try{const r=await e.json();if(r&&"list"===r.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${e.status}: ${e.statusText}`}}catch(r){return r instanceof Error&&"AbortError"===r.name?{valid:!1,error:"network_error",message:o.t("validator.timeout")}:function(r){const e=r?.cause?.code;return"string"==typeof e&&u.has(e)}(r)?{valid:!1,error:"network_error",message:o.t("messages.certificate_intercepted",{url:n})}:{valid:!1,error:"network_error",message:r instanceof Error?r.message:o.t("validator.network")}}finally{clearTimeout(i)}}const u=new Set(["DEPTH_ZERO_SELF_SIGNED_CERT","SELF_SIGNED_CERT_IN_CHAIN","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_UNTRUSTED","ERR_TLS_CERT_ALTNAME_INVALID"]);export async function fetchModels(r,o={}){const{timeoutMs:a=3e4,fetchFn:n=fetch}=o,s=`${t.getBaseUrl()}/v1/models`,i=new AbortController,l=setTimeout(()=>i.abort(),a);try{const e=await n(s,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:i.signal});if(!e.ok)return{ok:!1,models:[],error:`HTTP ${e.status}`};const t=await e.json();return{ok:!0,models:(Array.isArray(t?.data)?t.data:[]).map(r=>"string"==typeof r?r:r?.id).filter(r=>"string"==typeof r&&r.trim().length>0).map(r=>r.trim())}}catch(r){return e.logError("fetchModels",r),{ok:!1,models:[],error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}function f(r){return r.filter(r=>!!r&&"object"==typeof r&&"string"==typeof r.name).map(r=>({name:r.name.trim(),..."string"==typeof r.labelOverride?{labelOverride:r.labelOverride}:{},..."boolean"==typeof r.supports1m?{supports1m:r.supports1m}:{}})).filter(r=>r.name.length>0)}export async function fetchGatewayStatus(r={}){const{timeoutMs:o=15e3,fetchFn:n=fetch}=r,s=`${t.getBaseUrl()}/api/status`,i=new AbortController,l=setTimeout(()=>i.abort(),o);try{const r=await n(s,{method:"GET",signal:i.signal});if(!r.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${r.status}`};const e=await r.json();a(e?.data,t.getBaseUrl());let o=e?.data?.claude_desktop_group_models;if("string"==typeof o)try{o=JSON.parse(o)}catch{o=void 0}const l={};if(o&&"object"==typeof o&&!Array.isArray(o))for(const[r,e]of Object.entries(o))Array.isArray(e)&&(l[r]=f(e));return{ok:!0,claudeDesktopGroupModels:l}}catch(r){return e.logError("fetchGatewayStatus",r),{ok:!1,claudeDesktopGroupModels:{},error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}export async function fetchKeyGroupInfo(o,a={}){const{timeoutMs:n=1e4,fetchFn:s=fetch}=a,i=`${t.getBaseUrl()}${r}`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const r=await s(i,{method:"GET",headers:{Authorization:`Bearer ${o}`},signal:l.signal});if(!r.ok)return null;const e=await r.json(),t=e?.data;if(!t||"string"!=typeof t.group||!Array.isArray(t.claude_desktop_models))return null;const a=Array.isArray(t.groups)?t.groups.filter(r=>"string"==typeof r&&!!r):[];return{group:t.group,groups:a.length>0?a:[t.group],claudeDesktopModels:f(t.claude_desktop_models)}}catch(r){return e.logError("fetchKeyGroupInfo",r),null}finally{clearTimeout(c)}}export async function fetchClientModelConfig(r={}){const e=await i(r);return e.config?n(e.config.models):null}export async function validateApiKey(r,t={}){const a=Date.now(),n=await async function(r,t){const{timeoutMs:a=3e4,retries:n=2,backoffMs:s=500,fetchFn:i=fetch,sleepFn:u=l}=t;let f={valid:!1,error:"network_error",message:o.t("validator.network")};for(let t=0;t<=n;t++){const o=await c(r,a,i);if(o.valid||"invalid_api_key"===o.error)return o.valid||e.logError("validateApiKey",new Error(o.message||"invalid_api_key")),o;f=o,t<n&&await u(s*Math.pow(2,t))}return e.logError("validateApiKey",new Error(f.message||"network_error")),f}(r,t);return s("validate-key",n.valid?"ok":"fail",{duration_ms:Date.now()-a,...n.valid?{}:{error:n.message??n.error,detail:{reason:n.error}}}),n}
1
+ import{KEY_INFO_PATH as r}from"./gateway-protocol.js";import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as o}from"./i18n.js";import{rememberPublishedAddresses as a}from"./gateway-addresses.js";import{eligibleLists as n}from"./client-config.js";import{track as s}from"./telemetry.js";import{refreshClientConfig as i}from"./client-config-sync.js";const l=r=>new Promise(e=>setTimeout(e,r));async function c(r,e,a){const n=`${t.getBaseUrl()}/v1/models`,s=new AbortController,i=setTimeout(()=>s.abort(),e);try{const e=await a(n,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:s.signal});if(401===e.status)return{valid:!1,error:"invalid_api_key",message:o.t("validator.rejected")};if(e.ok){try{const r=await e.json();if(r&&"list"===r.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${e.status}: ${e.statusText}`}}catch(r){return r instanceof Error&&"AbortError"===r.name?{valid:!1,error:"network_error",message:o.t("validator.timeout")}:function(r){const e=r?.cause?.code;return"string"==typeof e&&u.has(e)}(r)?{valid:!1,error:"network_error",message:o.t("messages.certificate_intercepted",{url:n})}:{valid:!1,error:"network_error",message:r instanceof Error?r.message:o.t("validator.network")}}finally{clearTimeout(i)}}const u=new Set(["DEPTH_ZERO_SELF_SIGNED_CERT","SELF_SIGNED_CERT_IN_CHAIN","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_UNTRUSTED","ERR_TLS_CERT_ALTNAME_INVALID"]);export async function fetchModels(r,o={}){const{timeoutMs:a=3e4,fetchFn:n=fetch}=o,s=`${t.getBaseUrl()}/v1/models`,i=new AbortController,l=setTimeout(()=>i.abort(),a);try{const e=await n(s,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:i.signal});if(!e.ok)return{ok:!1,models:[],error:`HTTP ${e.status}`};const t=await e.json();return{ok:!0,models:(Array.isArray(t?.data)?t.data:[]).map(r=>"string"==typeof r?r:r?.id).filter(r=>"string"==typeof r&&r.trim().length>0).map(r=>r.trim())}}catch(r){return e.logError("fetchModels",r),{ok:!1,models:[],error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}function f(r){return r.filter(r=>!!r&&"object"==typeof r&&"string"==typeof r.name).map(r=>({name:r.name.trim(),..."string"==typeof r.labelOverride?{labelOverride:r.labelOverride}:{},..."boolean"==typeof r.supports1m?{supports1m:r.supports1m}:{}})).filter(r=>r.name.length>0)}export async function fetchGatewayStatus(r={}){const{timeoutMs:o=15e3,fetchFn:n=fetch}=r,s=`${t.getBaseUrl()}/api/status`,i=new AbortController,l=setTimeout(()=>i.abort(),o);try{const r=await n(s,{method:"GET",signal:i.signal});if(!r.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${r.status}`};const e=await r.json();a(e?.data,t.getBaseUrl());let o=e?.data?.claude_desktop_group_models;if("string"==typeof o)try{o=JSON.parse(o)}catch{o=void 0}const l={};if(o&&"object"==typeof o&&!Array.isArray(o))for(const[r,e]of Object.entries(o))Array.isArray(e)&&(l[r]=f(e));return{ok:!0,claudeDesktopGroupModels:l}}catch(r){return e.logError("fetchGatewayStatus",r),{ok:!1,claudeDesktopGroupModels:{},error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}export async function fetchKeyGroupInfo(o,a={}){const{timeoutMs:n=1e4,fetchFn:s=fetch}=a,i=`${t.getBaseUrl()}${r}`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const r=await s(i,{method:"GET",headers:{Authorization:`Bearer ${o}`},signal:l.signal});if(!r.ok)return null;const e=await r.json(),t=e?.data;if(!t||"string"!=typeof t.group||!Array.isArray(t.claude_desktop_models))return null;const a=Array.isArray(t.groups)?t.groups.filter(r=>"string"==typeof r&&!!r):[];return{group:t.group,groups:a.length>0?a:[t.group],claudeDesktopModels:f(t.claude_desktop_models)}}catch(r){return e.logError("fetchKeyGroupInfo",r),null}finally{clearTimeout(c)}}export async function fetchClientModelConfig(r={}){const e=await i(r);if(!e.config)return null;const{personal:t}=e.config;return{models:n(e.config.models),...t?{personal:t}:{}}}export async function validateApiKey(r,t={}){const a=Date.now(),n=await async function(r,t){const{timeoutMs:a=3e4,retries:n=2,backoffMs:s=500,fetchFn:i=fetch,sleepFn:u=l}=t;let f={valid:!1,error:"network_error",message:o.t("validator.network")};for(let t=0;t<=n;t++){const o=await c(r,a,i);if(o.valid||"invalid_api_key"===o.error)return o.valid||e.logError("validateApiKey",new Error(o.message||"invalid_api_key")),o;f=o,t<n&&await u(s*Math.pow(2,t))}return e.logError("validateApiKey",new Error(f.message||"network_error")),f}(r,t);return s("validate-key",n.valid?"ok":"fail",{duration_ms:Date.now()-a,...n.valid?{}:{error:n.message??n.error,detail:{reason:n.error}}}),n}
@@ -23,4 +23,5 @@ export declare function refreshClientConfig(options?: {
23
23
  timeoutMs?: number;
24
24
  fetchFn?: typeof fetch;
25
25
  now?: number;
26
+ apiKey?: string;
26
27
  }): Promise<ClientConfigStatus>;
@@ -1 +1 @@
1
- import{CLIENT_CONFIG_PATH as e}from"./gateway-protocol.js";import{logger as t}from"../utils/logger.js";import{bundledClientConfig as o,parseClientConfig as n,readCachedClientConfig as r,setClientConfigSiteResolver as i,writeCachedClientConfig as c}from"./client-config.js";import{configManager as f}from"./config.js";import{getBuiltinGatewayAddresses as s,reloadRegistry as l}from"./model-registry.js";import{brandEnv as g,brand as a}from"./brand.js";export const CLIENT_CONFIG_TTL_MS=3e5;function u(e){try{return new URL(e).origin}catch{return e.replace(/\/+$/,"")}}export function clientConfigSite(e=f.getBaseUrl()){const t=u(e);return s().some(e=>u(e.url)===t)?a().id:t}i(()=>clientConfigSite());let d=null;export function lastClientConfigStatus(){return d}function m(e){const t=r();if(t&&t.site===clientConfigSite())return{config:t.config,source:"cache",revision:t.config.revision,fetchedAt:t.fetchedAt,problem:e};const n=o();return n?{config:n,source:"bundled",revision:n.revision,problem:e}:{config:null,source:null,problem:e}}export async function refreshClientConfig(i={}){const{force:s=!1,timeoutMs:u=8e3,fetchFn:h=fetch,now:p=Date.now()}=i;if("1"===g("DISABLE_CLIENT_CONFIG_FETCH"))return d=m(),d;const C=clientConfigSite(),v=r(),w=v&&v.site===C?v:null;if(!s&&w&&p-w.fetchedAt<3e5)return d={config:w.config,source:"cache",revision:w.config.revision,fetchedAt:w.fetchedAt},d;const A=w?.config??(C===a().id?o():null),T=new AbortController,b=setTimeout(()=>T.abort(),u);try{const o={};w?.etag&&(o["If-None-Match"]=w.etag);const r=await h(`${f.getBaseUrl()}${e}`,{method:"GET",headers:o,signal:T.signal});if(304===r.status&&w)return c({...w,fetchedAt:p}),d={config:w.config,source:"cloud",revision:w.config.revision,fetchedAt:p},d;if(!r.ok)return d=m(`HTTP ${r.status}`),d;const i=await r.json(),{config:s,problem:g}=n(i?.data,A);if(!s)return t.warn(`Gateway client config refused: ${g}`),d=m(`refused: ${g}`),d;g&&t.warn(`Gateway client config: ${g}`);const a=r.headers.get("etag")??void 0;return c({site:C,fetchedAt:p,config:s,...a?{etag:a}:{}}),l(),d={config:s,source:"cloud",revision:s.revision,fetchedAt:p,...g?{problem:g}:{}},d}catch(e){return t.logError("refreshClientConfig",e),d=m(e instanceof Error?e.message:String(e)),d}finally{clearTimeout(b)}}
1
+ import{createHash as e}from"node:crypto";import{CLIENT_CONFIG_PATH as t}from"./gateway-protocol.js";import{logger as o}from"../utils/logger.js";import{bundledClientConfig as n,parseClientConfig as i,readCachedClientConfig as r,setClientConfigSiteResolver as c,writeCachedClientConfig as s}from"./client-config.js";import{configManager as f}from"./config.js";import{getBuiltinGatewayAddresses as a,reloadRegistry as l}from"./model-registry.js";import{brandEnv as u,brand as g}from"./brand.js";export const CLIENT_CONFIG_TTL_MS=3e5;function d(e){try{return new URL(e).origin}catch{return e.replace(/\/+$/,"")}}export function clientConfigSite(e=f.getBaseUrl()){const t=d(e);return a().some(e=>d(e.url)===t)?g().id:t}c(()=>clientConfigSite());let h=null;export function lastClientConfigStatus(){return h}function m(e){const t=r();if(t&&t.site===clientConfigSite())return{config:t.config,source:"cache",revision:t.config.revision,fetchedAt:t.fetchedAt,problem:e};const o=n();return o?{config:o,source:"bundled",revision:o.revision,problem:e}:{config:null,source:null,problem:e}}export async function refreshClientConfig(c={}){const{force:a=!1,timeoutMs:d=8e3,fetchFn:p=fetch,now:C=Date.now()}=c,y=c.apiKey??f.getApiKey(),A=function(t){return t?e("sha256").update(t).digest("hex").slice(0,16):void 0}(y);if("1"===u("DISABLE_CLIENT_CONFIG_FETCH"))return h=m(),h;const v=clientConfigSite(),w=r(),T=w&&w.site===v?w:null,b=T&&T.keyHash===A?T:null;if(!a&&b&&C-b.fetchedAt<3e5)return h={config:b.config,source:"cache",revision:b.config.revision,fetchedAt:b.fetchedAt},h;const S=T?.config??(v===g().id?n():null),$=new AbortController,j=setTimeout(()=>$.abort(),d);try{const e={};b?.etag&&(e["If-None-Match"]=b.etag),y&&(e.Authorization=`Bearer ${y}`);const n=await p(`${f.getBaseUrl()}${t}`,{method:"GET",headers:e,signal:$.signal});if(304===n.status&&b)return s({...b,fetchedAt:C}),h={config:b.config,source:"cloud",revision:b.config.revision,fetchedAt:C},h;if(!n.ok)return h=m(`HTTP ${n.status}`),h;const r=await n.json(),{config:c,problem:a}=i(r?.data,S);if(!c)return o.warn(`Gateway client config refused: ${a}`),h=m(`refused: ${a}`),h;a&&o.warn(`Gateway client config: ${a}`);const u=n.headers.get("etag")??void 0;return s({site:v,fetchedAt:C,config:c,...A?{keyHash:A}:{},...u?{etag:u}:{}}),l(),h={config:c,source:"cloud",revision:c.revision,fetchedAt:C,...a?{problem:a}:{}},h}catch(e){return o.logError("refreshClientConfig",e),h=m(e instanceof Error?e.message:String(e)),h}finally{clearTimeout(j)}}
@@ -10,15 +10,36 @@
10
10
  * 这个模块不依赖 config.ts(config.ts → model-registry.ts → 这里),联网刷新在 client-config-sync.ts。
11
11
  */
12
12
  export declare const CLIENT_CONFIG_SCHEMA = 1;
13
+ /**
14
+ * What the gateway's model metadata says a model can do (`capabilities` on a row, the same
15
+ * data as `/api/pricing`; filled in by the gateway when serving, never edited by hand).
16
+ */
17
+ export interface GatewayModelCapabilities {
18
+ toolCall?: boolean;
19
+ vision?: boolean;
20
+ reasoning?: boolean;
21
+ contextWindow?: number;
22
+ maxOutput?: number;
23
+ reasoningEfforts?: string[];
24
+ defaultReasoningEffort?: string;
25
+ /** Interfaces where tool calls work (`openai`, `openai-response`, …); absent = all of them. */
26
+ toolCallEndpoints?: string[];
27
+ }
13
28
  /** One model row of a list (`models.<list>[]`), normalized. */
14
29
  export interface GatewayClientModel {
15
30
  id: string;
16
31
  name?: string;
17
32
  description?: string;
18
33
  contextWindow?: number;
34
+ maxOutputTokens?: number;
19
35
  autoCompactTokens?: number;
36
+ capabilities?: GatewayModelCapabilities;
37
+ /** Interfaces the gateway serves this model on (`openai`, `openai-response`, `anthropic`, `gemini`). */
38
+ endpointTypes?: string[];
20
39
  /** Taken off by the gateway: not listed, and keeps the built-in entry of the same id out too. */
21
40
  hidden?: boolean;
41
+ /** Marked recommended in the gateway console; tools with a short menu (CodeBuddy, WorkBuddy) always list it. */
42
+ recommended?: boolean;
22
43
  /** Per-tool switches (`claude-code`, `codex`, `grok`): false keeps it out, true puts it in. */
23
44
  clients?: Record<string, boolean>;
24
45
  /** Only vibecli at or above this version sees the row. */
@@ -45,11 +66,19 @@ export interface ClientConfig {
45
66
  models: Record<string, GatewayClientModel[]>;
46
67
  /** The gateway's section for this CLI (`CLIENT_SECTION` on the wire). */
47
68
  client?: ClientSection;
69
+ /**
70
+ * Lists the key's owner trimmed or reordered in the console (「编码工具模型」); the gateway
71
+ * only sends this when the request carried the key. Those lists are complete: no built-in
72
+ * extras join them and the shrink check skips them.
73
+ */
74
+ personal?: string[];
48
75
  }
49
76
  export type ClientConfigSource = 'cloud' | 'cache' | 'bundled';
50
77
  export interface CachedClientConfig {
51
78
  /** `vibeapi` for the built-in addresses (one site), else the gateway origin. */
52
79
  site: string;
80
+ /** Fingerprint of the key the config was fetched with (the gateway merges its owner's choices). */
81
+ keyHash?: string;
53
82
  etag?: string;
54
83
  fetchedAt: number;
55
84
  config: ClientConfig;
@@ -1 +1 @@
1
- import{createHash as t,randomUUID as e}from"crypto";import{existsSync as n,mkdirSync as o,readFileSync as i,renameSync as r,writeFileSync as s}from"fs";import{homedir as l}from"os";import{dirname as c,join as f}from"path";import{fileURLToPath as a}from"url";import{CLIENT_SECTION as u,MIN_CLIENT_VERSION as d}from"./gateway-protocol.js";import{brand as m,brandEnv as p}from"./brand.js";export const CLIENT_CONFIG_SCHEMA=1;const h=new Set(["haiku","sonnet","opus","reasoning","fable"]),g=/^[A-Za-z0-9._:@/\-[\]]{1,200}$/,_=/^[a-z][a-z0-9-]{0,31}$/,b=/^\d+(\.\d+){0,3}$/,y=t=>!!t&&"object"==typeof t&&!Array.isArray(t),C=t=>"number"==typeof t&&Number.isFinite(t)&&t>0?Math.floor(t):void 0;function $(t){if(!y(t)||"string"!=typeof t.id||!t.id.trim())return null;const e={id:t.id.trim()};if(!0===t.hidden)return{...e,hidden:!0};"string"==typeof t.name&&t.name.trim()&&(e.name=t.name.trim()),"string"==typeof t.description&&t.description.trim()&&(e.description=t.description.trim());const n=C(t.context_window);n&&(e.contextWindow=n);const o=C(t.auto_compact_tokens);if(o&&(e.autoCompactTokens=o),y(t.clients)){const n={};for(const[e,o]of Object.entries(t.clients))_.test(e)&&"boolean"==typeof o&&(n[e]=o);Object.keys(n).length>0&&(e.clients=n)}const i=t[d];return"string"==typeof i&&b.test(i)&&(e.minClientVersion=i),"number"==typeof t.rollout_percent&&t.rollout_percent>=0&&t.rollout_percent<=100&&(e.rolloutPercent=t.rollout_percent),e}function k(t,e,n,o){if(void 0===t)return{};if(!y(t))throw new Error(`${u}.${o} must be an object`);const i={};for(const[r,s]of Object.entries(t)){if(!e.test(r)||"string"!=typeof s||!n(s))throw new Error(`${u}.${o}.${r} is invalid`);i[r]=s}return i}function v(t,e,{nonEmpty:n=!1}={}){if(void 0===t)return[];if(!Array.isArray(t)||t.some(t=>"string"!=typeof t||!g.test(t)))throw new Error(`${u}.${e} must be a list of model ids`);if(n&&0===t.length)throw new Error(`${u}.${e} must not be empty`);return[...t]}function w(t,e,n){if(void 0===t)return{};if(!y(t))throw new Error(`${u}.${e} must be an object`);const o={};for(const[i,r]of Object.entries(t)){if(!h.has(i))throw new Error(`${u}.${e}.${i} is not a tier slot`);o[i]=v(r,`${e}.${i}`,{nonEmpty:n})}return o}const j=t=>(t??[]).filter(t=>!t.hidden).length;export function parseClientConfig(t,e){if(!y(t))return{config:null,problem:"not an object"};if(1!==t.schema)return{config:null,problem:`unsupported schema ${String(t.schema)}`};if(!y(t.models))return{config:null,problem:"models is not an object"};const n={};for(const[e,o]of Object.entries(t.models)){if(!Array.isArray(o))return{config:null,problem:`models.${e} is not a list`};const t=o.map($);if(t.some(t=>null===t))return{config:null,problem:`models.${e} has a row without an id`};n[e]=t}let o,i;try{o=function(t){if(null==t)return;if(!y(t))throw new Error(`${u} must be an object`);const e={};if(void 0!==t.min_version){if("string"!=typeof t.min_version||t.min_version&&!b.test(t.min_version))throw new Error(`${u}.min_version is invalid`);t.min_version&&(e.minVersion=t.min_version)}const n=k(t.default_models,/^(haiku|sonnet|opus|reasoning|fable)$/,t=>g.test(t),"default_models");Object.keys(n).length&&(e.defaultModels=n);const o=w(t.retired_default_models,"retired_default_models",!1);Object.keys(o).length&&(e.retiredDefaultModels=o);const i=k(t.tool_defaults,_,t=>""===t||g.test(t),"tool_defaults");Object.keys(i).length&&(e.toolDefaults=i);const r=w(t.tier_chains,"tier_chains",!0);Object.keys(r).length&&(e.tierChains=r);const s=v(t.retired_models,"retired_models");s.length&&(e.retiredModels=s);const l=k(t.tool_min_versions,_,t=>""===t||b.test(t),"tool_min_versions");Object.keys(l).length&&(e.toolMinVersions=l);const c=v(t.agy_models,"agy_models");c.length&&(e.agyModels=c);const f=v(t.one_million_fallback,"one_million_fallback");return f.length&&(e.oneMillionFallback=f),!0===t.accept_shrink&&(e.acceptShrink=!0),e}(t[u])}catch(t){i=`${u} section dropped: ${t instanceof Error?t.message:String(t)}`}const r=t[u];if(e&&(!y(r)||!0!==r.accept_shrink))for(const[t,o]of Object.entries(e.models)){const e=j(o),i=j(n[t]);if(e>0&&0===i||e>=4&&2*i<e)return{config:null,problem:`models.${t} shrank from ${e} to ${i}`}}return{config:{schema:1,revision:"string"==typeof t.revision?t.revision:"",models:n,...o?{client:o}:{}},problem:i}}export function serializeClientConfig(t){const e=Object.fromEntries(Object.entries(t.models).map(([t,e])=>[t,e.map(t=>({id:t.id,...t.hidden?{hidden:!0}:{},...t.name?{name:t.name}:{},...t.description?{description:t.description}:{},...t.contextWindow?{context_window:t.contextWindow}:{},...t.autoCompactTokens?{auto_compact_tokens:t.autoCompactTokens}:{},...t.clients?{clients:t.clients}:{},...t.minClientVersion?{[d]:t.minClientVersion}:{},...void 0!==t.rolloutPercent?{rollout_percent:t.rolloutPercent}:{}}))])),n=t.client,o=n?{...n.minVersion?{min_version:n.minVersion}:{},...n.defaultModels?{default_models:n.defaultModels}:{},...n.retiredDefaultModels?{retired_default_models:n.retiredDefaultModels}:{},...n.toolDefaults?{tool_defaults:n.toolDefaults}:{},...n.tierChains?{tier_chains:n.tierChains}:{},...n.retiredModels?{retired_models:n.retiredModels}:{},...n.toolMinVersions?{tool_min_versions:n.toolMinVersions}:{},...n.agyModels?{agy_models:n.agyModels}:{},...n.oneMillionFallback?{one_million_fallback:n.oneMillionFallback}:{},...n.acceptShrink?{accept_shrink:!0}:{}}:void 0;return{schema:t.schema,revision:t.revision,models:e,...o?{[u]:o}:{}}}function O(){const t=p("CONFIG_DIR")?.trim();if(t)return t;const e=p("TEST_HOME")?.trim();return f(e||l(),m().configDirName)}export const cachePath=()=>f(O(),"client-config.json");function x(t,e){o(c(t),{recursive:!0});const n=`${t}.${process.pid}.tmp`;s(n,e,"utf-8"),r(n,t)}export function readCachedClientConfig(){try{if(!n(cachePath()))return null;const t=JSON.parse(i(cachePath(),"utf-8"));if("string"!=typeof t.site||"number"!=typeof t.fetchedAt)return null;const{config:e}=parseClientConfig(t.config);return e?{site:t.site,fetchedAt:t.fetchedAt,config:e,..."string"==typeof t.etag?{etag:t.etag}:{}}:null}catch{return null}}export function writeCachedClientConfig(t){x(cachePath(),JSON.stringify({site:t.site,etag:t.etag,fetchedAt:t.fetchedAt,config:serializeClientConfig(t.config)},null,2)+"\n")}let E;export function bundledClientConfig(){if("1"===p("NO_BUNDLED_CLIENT_CONFIG"))return null;if(void 0!==E)return E;E=null;const t=c(a(import.meta.url));for(const e of[f(t,"..","..","client-config.snapshot.json"),f(t,"..","client-config.snapshot.json")])try{if(!n(e))continue;E=parseClientConfig(JSON.parse(i(e,"utf-8"))).config;break}catch{}return E}let M=null;export function setClientConfigSiteResolver(t){M=t}export function currentClientConfig(){const t=readCachedClientConfig();let e=null;try{e=M?M():null}catch{e=null}if(t&&null!==e&&t.site===e)return{config:t.config,source:"cache",fetchedAt:t.fetchedAt};const n=bundledClientConfig();return n?{config:n,source:"bundled"}:null}let V=null,A=null;export function setClientIdentityForTests(t,e){V=t,A=e}export const clientVersion=()=>V??function(){try{const t=c(a(import.meta.url));for(const e of[f(t,"..","..","package.json"),f(t,"..","package.json")])if(n(e))return String(JSON.parse(i(e,"utf-8")).version??"0.0.0")}catch{}return"0.0.0"}();function S(t,e){const n=t.split(".").map(t=>parseInt(t,10)||0),o=e.split(".").map(t=>parseInt(t,10)||0);for(let t=0;t<Math.max(n.length,o.length);t++){const e=(n[t]??0)-(o[t]??0);if(e)return e}return 0}export function installId(){if(A)return A;const t=f(O(),"install-id");try{if(n(t)){const e=i(t,"utf-8").trim();if(e)return e}const o=e();return x(t,o+"\n"),o}catch{return"unknown"}}export function rolloutBucket(e){return t("sha256").update(`${installId()}:${e}`).digest().readUInt32BE(0)%100}export function isEligible(t){return!(t.minClientVersion&&S(clientVersion(),t.minClientVersion)<0||void 0!==t.rolloutPercent&&rolloutBucket(t.id)>=t.rolloutPercent)}export function eligibleLists(t){return Object.fromEntries(Object.entries(t).map(([t,e])=>[t,e.map(t=>t.hidden||isEligible(t)?t:{id:t.id,hidden:!0})]))}export function usableClientSection(t){const e=t?.client;if(e&&!(e.minVersion&&S(clientVersion(),e.minVersion)<0))return e}
1
+ import{createHash as t,randomUUID as e}from"crypto";import{existsSync as n,mkdirSync as o,readFileSync as i,renameSync as r,writeFileSync as s}from"fs";import{homedir as l}from"os";import{dirname as c,join as a}from"path";import{fileURLToPath as f}from"url";import{CLIENT_SECTION as u,MIN_CLIENT_VERSION as d}from"./gateway-protocol.js";import{brand as p,brandEnv as m}from"./brand.js";export const CLIENT_CONFIG_SCHEMA=1;const g=new Set(["haiku","sonnet","opus","reasoning","fable"]),h=/^[A-Za-z0-9._:@/\-[\]]{1,200}$/,y=/^[a-z][a-z0-9-]{0,31}$/,_=/^\d+(\.\d+){0,3}$/,b=t=>!!t&&"object"==typeof t&&!Array.isArray(t),C=t=>"number"==typeof t&&Number.isFinite(t)&&t>0?Math.floor(t):void 0,k=/^[A-Za-z0-9._-]{1,64}$/,v=t=>{if(!Array.isArray(t))return;const e=t.filter(t=>"string"==typeof t&&k.test(t));return e.length>0?e:void 0};function E(t){if(!b(t)||"string"!=typeof t.id||!t.id.trim())return null;const e={id:t.id.trim()};if(!0===t.hidden)return{...e,hidden:!0};"string"==typeof t.name&&t.name.trim()&&(e.name=t.name.trim()),"string"==typeof t.description&&t.description.trim()&&(e.description=t.description.trim()),!0===t.recommended&&(e.recommended=!0);const n=C(t.context_window);n&&(e.contextWindow=n);const o=C(t.max_output_tokens);o&&(e.maxOutputTokens=o);const i=C(t.auto_compact_tokens);i&&(e.autoCompactTokens=i);const r=function(t){if(!b(t))return;const e={};"boolean"==typeof t.functionCall&&(e.toolCall=t.functionCall),"boolean"==typeof t.vision&&(e.vision=t.vision),"boolean"==typeof t.reasoning&&(e.reasoning=t.reasoning);const n=C(t.contextWindowTokens);n&&(e.contextWindow=n);const o=C(t.maxOutput);o&&(e.maxOutput=o);const i=v(t.reasoningEfforts);i&&(e.reasoningEfforts=i),"string"==typeof t.defaultReasoningEffort&&k.test(t.defaultReasoningEffort)&&(e.defaultReasoningEffort=t.defaultReasoningEffort);const r=v(t.functionCallEndpoints);return r&&(e.toolCallEndpoints=r),Object.keys(e).length>0?e:void 0}(t.capabilities);r&&(e.capabilities=r);const s=v(t.endpoint_types);if(s&&(e.endpointTypes=s),b(t.clients)){const n={};for(const[e,o]of Object.entries(t.clients))y.test(e)&&"boolean"==typeof o&&(n[e]=o);Object.keys(n).length>0&&(e.clients=n)}const l=t[d];return"string"==typeof l&&_.test(l)&&(e.minClientVersion=l),"number"==typeof t.rollout_percent&&t.rollout_percent>=0&&t.rollout_percent<=100&&(e.rolloutPercent=t.rollout_percent),e}function x(t,e,n,o){if(void 0===t)return{};if(!b(t))throw new Error(`${u}.${o} must be an object`);const i={};for(const[r,s]of Object.entries(t)){if(!e.test(r)||"string"!=typeof s||!n(s))throw new Error(`${u}.${o}.${r} is invalid`);i[r]=s}return i}function $(t,e,{nonEmpty:n=!1}={}){if(void 0===t)return[];if(!Array.isArray(t)||t.some(t=>"string"!=typeof t||!h.test(t)))throw new Error(`${u}.${e} must be a list of model ids`);if(n&&0===t.length)throw new Error(`${u}.${e} must not be empty`);return[...t]}function w(t,e,n){if(void 0===t)return{};if(!b(t))throw new Error(`${u}.${e} must be an object`);const o={};for(const[i,r]of Object.entries(t)){if(!g.has(i))throw new Error(`${u}.${e}.${i} is not a tier slot`);o[i]=$(r,`${e}.${i}`,{nonEmpty:n})}return o}const O=t=>(t??[]).filter(t=>!t.hidden).length;export function parseClientConfig(t,e){if(!b(t))return{config:null,problem:"not an object"};if(1!==t.schema)return{config:null,problem:`unsupported schema ${String(t.schema)}`};if(!b(t.models))return{config:null,problem:"models is not an object"};const n={};for(const[e,o]of Object.entries(t.models)){if(!Array.isArray(o))return{config:null,problem:`models.${e} is not a list`};const t=o.map(E);if(t.some(t=>null===t))return{config:null,problem:`models.${e} has a row without an id`};n[e]=t}let o,i;try{o=function(t){if(null==t)return;if(!b(t))throw new Error(`${u} must be an object`);const e={};if(void 0!==t.min_version){if("string"!=typeof t.min_version||t.min_version&&!_.test(t.min_version))throw new Error(`${u}.min_version is invalid`);t.min_version&&(e.minVersion=t.min_version)}const n=x(t.default_models,/^(haiku|sonnet|opus|reasoning|fable)$/,t=>h.test(t),"default_models");Object.keys(n).length&&(e.defaultModels=n);const o=w(t.retired_default_models,"retired_default_models",!1);Object.keys(o).length&&(e.retiredDefaultModels=o);const i=x(t.tool_defaults,y,t=>""===t||h.test(t),"tool_defaults");Object.keys(i).length&&(e.toolDefaults=i);const r=w(t.tier_chains,"tier_chains",!0);Object.keys(r).length&&(e.tierChains=r);const s=$(t.retired_models,"retired_models");s.length&&(e.retiredModels=s);const l=x(t.tool_min_versions,y,t=>""===t||_.test(t),"tool_min_versions");Object.keys(l).length&&(e.toolMinVersions=l);const c=$(t.agy_models,"agy_models");c.length&&(e.agyModels=c);const a=$(t.one_million_fallback,"one_million_fallback");return a.length&&(e.oneMillionFallback=a),!0===t.accept_shrink&&(e.acceptShrink=!0),e}(t[u])}catch(t){i=`${u} section dropped: ${t instanceof Error?t.message:String(t)}`}const r=b(t.personal)&&Array.isArray(t.personal.lists)?t.personal.lists.filter(t=>"string"==typeof t&&Array.isArray(n[t])):[],s=t[u];if(e&&(!b(s)||!0!==s.accept_shrink))for(const[t,o]of Object.entries(e.models)){if(r.includes(t))continue;const e=O(o),i=O(n[t]);if(e>0&&0===i||e>=4&&2*i<e)return{config:null,problem:`models.${t} shrank from ${e} to ${i}`}}return{config:{schema:1,revision:"string"==typeof t.revision?t.revision:"",models:n,...o?{client:o}:{},...r.length?{personal:r}:{}},problem:i}}export function serializeClientConfig(t){const e=Object.fromEntries(Object.entries(t.models).map(([t,e])=>[t,e.map(t=>{return{id:t.id,...t.hidden?{hidden:!0}:{},...t.recommended?{recommended:!0}:{},...t.name?{name:t.name}:{},...t.description?{description:t.description}:{},...t.contextWindow?{context_window:t.contextWindow}:{},...t.maxOutputTokens?{max_output_tokens:t.maxOutputTokens}:{},...t.autoCompactTokens?{auto_compact_tokens:t.autoCompactTokens}:{},...t.capabilities?{capabilities:(e=t.capabilities,{...void 0!==e.toolCall?{functionCall:e.toolCall}:{},...void 0!==e.vision?{vision:e.vision}:{},...void 0!==e.reasoning?{reasoning:e.reasoning}:{},...e.contextWindow?{contextWindowTokens:e.contextWindow}:{},...e.maxOutput?{maxOutput:e.maxOutput}:{},...e.reasoningEfforts?{reasoningEfforts:e.reasoningEfforts}:{},...e.defaultReasoningEffort?{defaultReasoningEffort:e.defaultReasoningEffort}:{},...e.toolCallEndpoints?{functionCallEndpoints:e.toolCallEndpoints}:{}})}:{},...t.endpointTypes?{endpoint_types:t.endpointTypes}:{},...t.clients?{clients:t.clients}:{},...t.minClientVersion?{[d]:t.minClientVersion}:{},...void 0!==t.rolloutPercent?{rollout_percent:t.rolloutPercent}:{}};var e})])),n=t.client,o=n?{...n.minVersion?{min_version:n.minVersion}:{},...n.defaultModels?{default_models:n.defaultModels}:{},...n.retiredDefaultModels?{retired_default_models:n.retiredDefaultModels}:{},...n.toolDefaults?{tool_defaults:n.toolDefaults}:{},...n.tierChains?{tier_chains:n.tierChains}:{},...n.retiredModels?{retired_models:n.retiredModels}:{},...n.toolMinVersions?{tool_min_versions:n.toolMinVersions}:{},...n.agyModels?{agy_models:n.agyModels}:{},...n.oneMillionFallback?{one_million_fallback:n.oneMillionFallback}:{},...n.acceptShrink?{accept_shrink:!0}:{}}:void 0;return{schema:t.schema,revision:t.revision,models:e,...o?{[u]:o}:{},...t.personal?{personal:{lists:t.personal}}:{}}}function j(){const t=m("CONFIG_DIR")?.trim();if(t)return t;const e=m("TEST_HOME")?.trim();return a(e||l(),p().configDirName)}export const cachePath=()=>a(j(),"client-config.json");function A(t,e){o(c(t),{recursive:!0});const n=`${t}.${process.pid}.tmp`;s(n,e,"utf-8"),r(n,t)}export function readCachedClientConfig(){try{if(!n(cachePath()))return null;const t=JSON.parse(i(cachePath(),"utf-8"));if("string"!=typeof t.site||"number"!=typeof t.fetchedAt)return null;const{config:e}=parseClientConfig(t.config);return e?{site:t.site,fetchedAt:t.fetchedAt,config:e,..."string"==typeof t.keyHash?{keyHash:t.keyHash}:{},..."string"==typeof t.etag?{etag:t.etag}:{}}:null}catch{return null}}export function writeCachedClientConfig(t){A(cachePath(),JSON.stringify({site:t.site,keyHash:t.keyHash,etag:t.etag,fetchedAt:t.fetchedAt,config:serializeClientConfig(t.config)},null,2)+"\n")}let M;export function bundledClientConfig(){if("1"===m("NO_BUNDLED_CLIENT_CONFIG"))return null;if(void 0!==M)return M;M=null;const t=c(f(import.meta.url));for(const e of[a(t,"..","..","client-config.snapshot.json"),a(t,"..","client-config.snapshot.json")])try{if(!n(e))continue;M=parseClientConfig(JSON.parse(i(e,"utf-8"))).config;break}catch{}return M}let T=null;export function setClientConfigSiteResolver(t){T=t}export function currentClientConfig(){const t=readCachedClientConfig();let e=null;try{e=T?T():null}catch{e=null}if(t&&null!==e&&t.site===e)return{config:t.config,source:"cache",fetchedAt:t.fetchedAt};const n=bundledClientConfig();return n?{config:n,source:"bundled"}:null}let V=null,S=null;export function setClientIdentityForTests(t,e){V=t,S=e}export const clientVersion=()=>V??function(){try{const t=c(f(import.meta.url));for(const e of[a(t,"..","..","package.json"),a(t,"..","package.json")])if(n(e))return String(JSON.parse(i(e,"utf-8")).version??"0.0.0")}catch{}return"0.0.0"}();function N(t,e){const n=t.split(".").map(t=>parseInt(t,10)||0),o=e.split(".").map(t=>parseInt(t,10)||0);for(let t=0;t<Math.max(n.length,o.length);t++){const e=(n[t]??0)-(o[t]??0);if(e)return e}return 0}export function installId(){if(S)return S;const t=a(j(),"install-id");try{if(n(t)){const e=i(t,"utf-8").trim();if(e)return e}const o=e();return A(t,o+"\n"),o}catch{return"unknown"}}export function rolloutBucket(e){return t("sha256").update(`${installId()}:${e}`).digest().readUInt32BE(0)%100}export function isEligible(t){return!(t.minClientVersion&&N(clientVersion(),t.minClientVersion)<0||void 0!==t.rolloutPercent&&rolloutBucket(t.id)>=t.rolloutPercent)}export function eligibleLists(t){return Object.fromEntries(Object.entries(t).map(([t,e])=>[t,e.map(t=>t.hidden||isEligible(t)?t:{id:t.id,hidden:!0})]))}export function usableClientSection(t){const e=t?.client;if(e&&!(e.minVersion&&N(clientVersion(),e.minVersion)<0))return e}
@@ -5,7 +5,7 @@ import type { GatewayClientModel } from './client-config.js';
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
  */
8
- export type GatewayModelTool = 'codex' | 'claude-code' | 'grok';
8
+ export type GatewayModelTool = 'codex' | 'claude-code' | 'grok' | 'codebuddy';
9
9
  /** Auto-compaction point for a window: the explicit value (capped at the window) or the ratio. */
10
10
  export declare function autoCompactTokens(window: number, explicit?: number): number;
11
11
  /**
@@ -15,9 +15,11 @@ export declare function autoCompactTokens(window: number, explicit?: number): nu
15
15
  * them too (so a model removed there, like Haiku for Codex, stays out); Codex also
16
16
  * gets the `gemini` list, Grok Build only the `grok` list. Built-in entries the lists lack are added (see
17
17
  * `withBuiltInExtras`); a `hidden` row on the gateway keeps one out. Without the
18
- * config, the built-in list from `models.json` → `gatewayModels`.
18
+ * config, the built-in list from `models.json` → `gatewayModels`. When the key's owner chose
19
+ * the models of a list this tool reads (`personal`, 「编码工具模型」 in the console), the lists are
20
+ * complete and no built-in extras are added.
19
21
  */
20
- export declare function gatewayModelSpecs(tool: GatewayModelTool, clientModels?: Record<string, GatewayClientModel[]>): GatewayModelSpec[];
22
+ export declare function gatewayModelSpecs(tool: GatewayModelTool, clientModels?: Record<string, GatewayClientModel[]>, personal?: string[]): GatewayModelSpec[];
21
23
  /**
22
24
  * Menu subtitle from the gateway's description without its figures: output limits
23
25
  * and context sizes don't hold for every relayed model. With `millionContext` (the
@@ -1 +1 @@
1
- import{getGatewayModelRules as e}from"./model-registry.js";export function autoCompactTokens(t,i){return i&&i>0?Math.min(i,t):Math.floor(t*e().autoCompactRatio)}export function gatewayModelSpecs(t,i){const o=e().models,n=o.filter(e=>function(e,t){return"codex"===e?!1!==t.codex:"grok"===e?"grok"===modelFamily(t.id):!t.id.startsWith("claude-")&&!1!==t.claudeCode}(t,e));if(!i)return n;const d=new Set(Object.values(i).flat().filter(e=>e.hidden).map(e=>e.id)),r=Object.fromEntries(Object.entries(i).map(([e,t])=>[e,t.filter(e=>!e.hidden)])),s=new Map;for(const e of Object.values(r).flat()){const i=e.clients?.[t];void 0===i||s.has(e.id)||s.set(e.id,i)}for(const[e,t]of s)t||d.add(e);const l=new Map(("claude-code"===t?o:n).map(e=>[e.id,e])),c=(r.gemini??[]).filter(e=>s.get(e.id)??!1!==o.find(t=>t.id===e.id)?.codex),a=("grok"===t?r.grok??[]:"codex"===t?[...r.codex??[],...c,...(r.claude??[]).filter(e=>e.id.startsWith("claude-")&&(s.get(e.id)??l.has(e.id)))]:function(e,t){return[...(e.codex??[]).filter(e=>"gpt"===modelFamily(e.id)),...e.gemini??[],...e.grok??[],...(e.claude??[]).filter(e=>!e.id.startsWith("claude-"))].filter(e=>e.clients?.["claude-code"]??t.get(e.id)?.claudeCode??!0)}(r,l)).filter(e=>!(e=>!1===s.get(e.id))(e));if(0===a.length)return n.filter(e=>!d.has(e.id));const u=new Set,f=[];for(const e of a){if(u.has(e.id))continue;u.add(e.id);const{clients:t,minClientVersion:i,rolloutPercent:o,...n}=e,d={...l.get(e.id),...n};void 0!==t?.codex&&(d.codex=t.codex),void 0!==t?.["claude-code"]&&(d.claudeCode=t["claude-code"]),f.push(d)}return function(e,t,i){const o=[...e];return t.forEach((e,n)=>{if(i.has(e.id)||o.some(t=>t.id===e.id))return;const d=modelFamily(e.id),r=new Set(t.slice(n+1).filter(e=>modelFamily(e.id)===d).map(e=>e.id));let s=o.findIndex(e=>r.has(e.id));if(s<0){s=o.length;for(let e=o.length-1;e>=0;e--)if(modelFamily(o[e].id)===d){s=e+1;break}}o.splice(s,0,e)}),o}(f,n,d)}const t={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function modelBlurb(e,t={}){const i=[];for(const o of(e??"").split("|")){const e=o.trim();e&&(/\d\s*万?\s*输出|output\D*\d|\d\D*output/i.test(e)||(/[\d百千]\s*万?\s*上下文|context\D*\d|\d\D*context/i.test(e)?t.millionContext&&!i.includes("百万上下文")&&i.unshift("百万上下文"):i.push(e)))}return t.millionContext&&!i.includes("百万上下文")&&i.unshift("百万上下文"),i.length>0?i.join(" | "):void 0}export function prettyModelName(e){const i=e.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),o=[];for(const e of i){const i=o[o.length-1];if(/^\d+$/.test(e)&&i&&/^\d+$/.test(i)&&e.length<=2&&!/^\d{8}$/.test(e)){o[o.length-1]=`${i}.${e}`;continue}const n=e.toLowerCase();o.push(t[n]??(/^[a-z]\d/i.test(e)?e.toUpperCase():e[0].toUpperCase()+e.slice(1)))}return o.join(" ")}export function vendorNames(e){return[...new Set(e.map(e=>prettyModelName(e).split(" ")[0]))]}export function modelFamily(e){const t=e.toLowerCase();return t.startsWith("claude-")?"claude":t.startsWith("gemini")?"gemini":t.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(t)?"gpt":"other"}
1
+ import{getGatewayModelRules as e}from"./model-registry.js";export function autoCompactTokens(t,o){return o&&o>0?Math.min(o,t):Math.floor(t*e().autoCompactRatio)}export function gatewayModelSpecs(o,i,d=[]){const n=e().models,c=n.filter(e=>function(e,t){return"codebuddy"===e||("codex"===e?!1!==t.codex:"grok"===e?"grok"===modelFamily(t.id):!t.id.startsWith("claude-")&&!1!==t.claudeCode)}(o,e));if(!i)return c;const r=new Set(Object.values(i).flat().filter(e=>e.hidden).map(e=>e.id)),l=Object.fromEntries(Object.entries(i).map(([e,t])=>[e,t.filter(e=>!e.hidden)])),s=new Map;for(const e of Object.values(l).flat()){const t=e.clients?.[o];void 0===t||s.has(e.id)||s.set(e.id,t)}for(const[e,t]of s)t||r.add(e);const a=new Map(("claude-code"===o?n:c).map(e=>[e.id,e])),u=(l.gemini??[]).filter(e=>s.get(e.id)??!1!==n.find(t=>t.id===e.id)?.codex),f=("codebuddy"===o?[...l.claude??[],...l.codex??[],...l.gemini??[],...l.grok??[]]:"grok"===o?l.grok??[]:"codex"===o?[...l.codex??[],...u,...(l.claude??[]).filter(e=>e.id.startsWith("claude-")&&(s.get(e.id)??a.has(e.id)))]:function(e,t){return[...(e.codex??[]).filter(e=>"gpt"===modelFamily(e.id)),...e.gemini??[],...e.grok??[],...(e.claude??[]).filter(e=>!e.id.startsWith("claude-"))].filter(e=>e.clients?.["claude-code"]??t.get(e.id)?.claudeCode??!0)}(l,a)).filter(e=>!(e=>!1===s.get(e.id))(e));if(0===f.length)return c.filter(e=>!r.has(e.id));const m=new Set,g=[];for(const e of f){if(m.has(e.id))continue;m.add(e.id);const{clients:t,minClientVersion:o,rolloutPercent:i,...d}=e,n={...a.get(e.id),...d};void 0!==t?.codex&&(n.codex=t.codex),void 0!==t?.["claude-code"]&&(n.claudeCode=t["claude-code"]),g.push(n)}return t[o].some(e=>d.includes(e))?g:function(e,t,o){const i=[...e];return t.forEach((e,d)=>{if(o.has(e.id)||i.some(t=>t.id===e.id))return;const n=modelFamily(e.id),c=new Set(t.slice(d+1).filter(e=>modelFamily(e.id)===n).map(e=>e.id));let r=i.findIndex(e=>c.has(e.id));if(r<0){r=i.length;for(let e=i.length-1;e>=0;e--)if(modelFamily(i[e].id)===n){r=e+1;break}}i.splice(r,0,e)}),i}(g,c,r)}const t={grok:["grok"],codex:["codex","gemini","claude"],"claude-code":["codex","gemini","grok","claude"],codebuddy:["claude","codex","gemini","grok"]},o={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function modelBlurb(e,t={}){const o=[];for(const i of(e??"").split("|")){const e=i.trim();e&&(/\d\s*万?\s*输出|output\D*\d|\d\D*output/i.test(e)||(/[\d百千]\s*万?\s*上下文|context\D*\d|\d\D*context/i.test(e)?t.millionContext&&!o.includes("百万上下文")&&o.unshift("百万上下文"):o.push(e)))}return t.millionContext&&!o.includes("百万上下文")&&o.unshift("百万上下文"),o.length>0?o.join(" | "):void 0}export function prettyModelName(e){const t=e.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),i=[];for(const e of t){const t=i[i.length-1];if(/^\d+$/.test(e)&&t&&/^\d+$/.test(t)&&e.length<=2&&!/^\d{8}$/.test(e)){i[i.length-1]=`${t}.${e}`;continue}const d=e.toLowerCase();i.push(o[d]??(/^[a-z]\d/i.test(e)?e.toUpperCase():e[0].toUpperCase()+e.slice(1)))}return i.join(" ")}export function vendorNames(e){return[...new Set(e.map(e=>prettyModelName(e).split(" ")[0]))]}export function modelFamily(e){const t=e.toLowerCase();return t.startsWith("claude-")?"claude":t.startsWith("gemini")?"gemini":t.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(t)?"gpt":"other"}
@@ -20,6 +20,8 @@ export interface ModelCatalog {
20
20
  * windows, compaction points). Absent when the gateway has no such endpoint.
21
21
  */
22
22
  clientModels?: Record<string, GatewayClientModel[]>;
23
+ /** Lists of `clientModels` the key's owner chose in the console (see `ClientConfig.personal`). */
24
+ clientPersonal?: string[];
23
25
  }
24
26
  /** Strip a bracketed context-variant suffix: `claude-opus-4-8[1m]` → `claude-opus-4-8`. */
25
27
  export declare function baseModelId(id: string): string;
@@ -1 +1 @@
1
- import{logger as e}from"../utils/logger.js";import{fetchClientModelConfig as o,fetchKeyGroupInfo as l,fetchModels as t,fetchGatewayStatus as n}from"./api-validator.js";import{getClaudeDesktopRules as r}from"./model-registry.js";export function baseModelId(e){return e.replace(/\[[^\]]*\]$/,"").trim()}export function isClaudeModel(e){return/claude/i.test(baseModelId(e))}export function matchGroups(e,o){const l=new Set(e.map(baseModelId));if(0===l.size)return[];const t=[],n=[];for(const[e,r]of Object.entries(o)){const o=new Set(r.map(e=>baseModelId(e.name)).filter(isClaudeModel));[...l].every(e=>o.has(e))&&(o.size===l.size?t:n).push(e)}return t.length>0?t:n}export function resolveOneMillion(e,o,l){const t=r(),n=[...new Set(e.map(baseModelId))],s=new Set(e.filter(e=>/\[1m\]/i.test(e)).map(baseModelId)),a={};let i,d="fallback",c=[];if(l&&l.claudeDesktopModels.length>0){d="gateway",c=o?l.groups.filter(e=>e in o):l.groups;const e=new Map(l.claudeDesktopModels.map(e=>[baseModelId(e.name),e])),r=new Set(t.oneMillionFallback);i=new Set(n.filter(o=>{const l=e.get(o);return l?.labelOverride&&(a[o]=l.labelOverride),l?!0===l.supports1m:r.has(o)}))}else if(o&&(c=matchGroups(n,o)).length>0){d="gateway",i=new Set(n);for(const e of c){const l=new Map(o[e].map(e=>[baseModelId(e.name),e]));for(const e of n){const o=l.get(e);o?.labelOverride&&!a[e]&&(a[e]=o.labelOverride),!0!==o?.supports1m&&i.delete(e)}}}else{const e=new Set(t.oneMillionFallback);i=new Set(n.filter(o=>e.has(o)))}for(const e of s)i.add(e);for(const e of t.oneMillionExclusions)if(n.some(o=>o.startsWith(e.whenPrefixPresent)))for(const o of e.models)i.delete(o);return{oneMillion:[...i],labels:a,oneMillionSource:d,matchedGroups:c}}export async function resolveModelCatalog(r){try{const[e,s,a,i]=await Promise.all([t(r),n(),o(),l(r)]);if(!e.ok||0===e.models.length)return null;const d=[...new Set(e.models.map(e=>e.trim()).filter(Boolean))],c=d.filter(isClaudeModel);return{models:d,...resolveOneMillion(c,s.ok?s.claudeDesktopGroupModels:null,i),...a?{clientModels:a}:{}}}catch(o){return e.logError("resolveModelCatalog",o),null}}
1
+ import{logger as e}from"../utils/logger.js";import{fetchClientModelConfig as o,fetchKeyGroupInfo as l,fetchModels as t,fetchGatewayStatus as n}from"./api-validator.js";import{getClaudeDesktopRules as r}from"./model-registry.js";export function baseModelId(e){return e.replace(/\[[^\]]*\]$/,"").trim()}export function isClaudeModel(e){return/claude/i.test(baseModelId(e))}export function matchGroups(e,o){const l=new Set(e.map(baseModelId));if(0===l.size)return[];const t=[],n=[];for(const[e,r]of Object.entries(o)){const o=new Set(r.map(e=>baseModelId(e.name)).filter(isClaudeModel));[...l].every(e=>o.has(e))&&(o.size===l.size?t:n).push(e)}return t.length>0?t:n}export function resolveOneMillion(e,o,l){const t=r(),n=[...new Set(e.map(baseModelId))],s=new Set(e.filter(e=>/\[1m\]/i.test(e)).map(baseModelId)),a={};let i,d="fallback",c=[];if(l&&l.claudeDesktopModels.length>0){d="gateway",c=o?l.groups.filter(e=>e in o):l.groups;const e=new Map(l.claudeDesktopModels.map(e=>[baseModelId(e.name),e])),r=new Set(t.oneMillionFallback);i=new Set(n.filter(o=>{const l=e.get(o);return l?.labelOverride&&(a[o]=l.labelOverride),l?!0===l.supports1m:r.has(o)}))}else if(o&&(c=matchGroups(n,o)).length>0){d="gateway",i=new Set(n);for(const e of c){const l=new Map(o[e].map(e=>[baseModelId(e.name),e]));for(const e of n){const o=l.get(e);o?.labelOverride&&!a[e]&&(a[e]=o.labelOverride),!0!==o?.supports1m&&i.delete(e)}}}else{const e=new Set(t.oneMillionFallback);i=new Set(n.filter(o=>e.has(o)))}for(const e of s)i.add(e);for(const e of t.oneMillionExclusions)if(n.some(o=>o.startsWith(e.whenPrefixPresent)))for(const o of e.models)i.delete(o);return{oneMillion:[...i],labels:a,oneMillionSource:d,matchedGroups:c}}export async function resolveModelCatalog(r){try{const[e,s,a,i]=await Promise.all([t(r),n(),o({apiKey:r}),l(r)]);if(!e.ok||0===e.models.length)return null;const d=[...new Set(e.models.map(e=>e.trim()).filter(Boolean))],c=d.filter(isClaudeModel);return{models:d,...resolveOneMillion(c,s.ok?s.claudeDesktopGroupModels:null,i),...a?{clientModels:a.models}:{},...a?.personal?{clientPersonal:a.personal}:{}}}catch(o){return e.logError("resolveModelCatalog",o),null}}
@@ -1,4 +1,4 @@
1
- import { type ClientSection } from './client-config.js';
1
+ import { type ClientSection, type GatewayModelCapabilities } from './client-config.js';
2
2
  export interface Models {
3
3
  haiku: string;
4
4
  sonnet: string;
@@ -44,8 +44,16 @@ export interface GatewayModelSpec {
44
44
  name?: string;
45
45
  description?: string;
46
46
  contextWindow?: number;
47
+ /** Marked recommended in the gateway console. */
48
+ recommended?: boolean;
49
+ /** Output limit from the gateway (only some rows carry it). */
50
+ maxOutputTokens?: number;
47
51
  /** Explicit auto-compaction point; defaults to `autoCompactRatio` of the window. */
48
52
  autoCompactTokens?: number;
53
+ /** From the gateway's model metadata; absent for built-in entries and older gateways. */
54
+ capabilities?: GatewayModelCapabilities;
55
+ /** Interfaces the gateway serves the model on (`openai`, `openai-response`, …). */
56
+ endpointTypes?: string[];
49
57
  /** `false` keeps the model out of Claude Code's picker (its window differs from the rest). */
50
58
  claudeCode?: boolean;
51
59
  /** `false` keeps the model out of Codex's catalog (GPT comes from Codex itself). */
@@ -1 +1 @@
1
- import{Command as e,Option as o}from"commander";import{readFileSync as s}from"fs";import{join as t,dirname as n}from"path";import{fileURLToPath as a}from"url";import{i18n as i}from"./i18n.js";import{wizard as r}from"./wizard.js";import{SUPPORTED_TOOLS as c}from"./tool-manager.js";import{DEFAULT_BASE_URL as l}from"./config.js";import{attachCommandHelp as p,helpConfiguration as m}from"./cli-help.js";import{langCommand as u,authCommand as d,doctorCommand as f,configCommand as g,statusCommand as x,refreshCommand as h}from"../commands/index.js";import{logsCommand as $}from"../commands/logs.js";import{completionCommand as y}from"../commands/completion.js";import{benchCommand as b}from"../commands/bench.js";import{updateCommand as k}from"../commands/update.js";import{upgradeCommand as _}from"../commands/upgrade.js";import{installCommand as w,listCommand as v}from"../commands/components.js";import{codexSessionsCommand as j}from"../commands/codex-sessions.js";import{sessionsExportCommand as O,sessionsListCommand as S,sessionsResumeCommand as A,sessionsShowCommand as E}from"../commands/sessions.js";import{setupCommand as G,DEDICATED_KEYS as z}from"../commands/setup.js";import{baseCommand as C}from"../commands/base.js";import{usageError as R}from"./errors.js";import{requireTerminal as B}from"./confirm.js";import{brand as I,brandEnv as L,CLI_NAME as N}from"./brand.js";const P=(I().gatewayAddresses[1]??I().gatewayAddresses[0]).url,T=(I().gatewayAddresses[2]??I().gatewayAddresses[0]).url;function U(e,o){return i.t(`help.${e}`,o)}function H(e,o){return{command:e,note:U(o)}}function K(e){const o=U(e);return o===`help.${e}`?"":o}function M(e,o=[]){return{when:K(`${e}.when`),prerequisites:K(`${e}.needs`).split("\n").filter(Boolean),alternatives:o.map(([e,o])=>H(e,o))}}function q(e){return e.helpCommand("help [command]",U("help_command"))}export function buildProgram(){const I=new e;I.name(`${N}`).summary(U("root.summary")).description(U("root.description")).configureHelp(m()).allowExcessArguments(!0).optionsGroup(U("group.global_options")).helpOption("-h, --help",U("global.help")).version(function(){try{const e=n(a(import.meta.url));return JSON.parse(s(t(e,"../../package.json"),"utf-8")).version}catch{return"1.0.0"}}(),"-v, --version",U("global.version")).option("--base <url>",U("global.base")).option("--non-interactive",U("global.non_interactive")).option("--json",U("global.json")).option("-y, --yes",U("global.yes")).option("--dry-run",U("global.dry_run")).option("--no-color",U("global.no_color")),I.configureOutput({writeErr:()=>{},outputError:()=>{}}),I.showSuggestionAfterError(),I.exitOverride(e=>{throw e}),I.commandsGroup(U("group.configure"));const K=I.command("setup").summary(U("setup.summary")).description(U("setup.description")).option("--key <key>",U("setup.opt.key"));for(const e of z){const s=new o(`--${e.option} <key>`,U(`setup.opt.${e.option}`));e.hidden&&s.hideHelp(),K.addOption(s)}K.option("--1m",U("setup.opt.one_m")).option("--no-1m",U("setup.opt.no_one_m")).option("--claude-official <on|off>",U("setup.opt.claude_official")).option("--desktop-registry <on|off>",U("setup.opt.desktop_registry")).option("--force",U("setup.opt.force")).option("--install",U("setup.opt.install")).option("--no-install",U("setup.opt.no_install")).option("--all",U("setup.opt.all")).action(async e=>{e.key||z.some(o=>e[o.option])||(e.key=L("SETUP_KEY")?.trim()||void 0,e.fromInstaller=Boolean(e.key));const o=Boolean(e.key||z.some(o=>e[o.option])),s=void 0!==e["1m"]||void 0!==e.claudeOfficial||void 0!==e.desktopRegistry,t=i.t("errors.key_placeholder");if(!o&&!s)throw R(i.t("setup.usage"),`${N} setup --key ${t}`,"missing_key");if(void 0!==e.claudeOfficial&&!["on","off"].includes(String(e.claudeOfficial).toLowerCase()))throw R(i.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)}),`${N} setup --claude-official on|off`,"invalid_value");if(void 0!==e.desktopRegistry&&!["on","off"].includes(String(e.desktopRegistry).toLowerCase()))throw R(i.t("setup.desktop_registry_invalid",{value:String(e.desktopRegistry)}),`${N} setup --desktop-registry on|off`,"invalid_value");await G({key:e.key,...Object.fromEntries(z.map(o=>[o.option,e[o.option]])),all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,desktopRegistry:e.desktopRegistry,force:e.force,install:e.install,fromInstaller:e.fromInstaller})}),p(K,{...M("setup",[[`${N} init`,"setup.alt.init"],[`${N} config <tool>`,"setup.alt.config"],[`${N} auth <key>`,"setup.alt.auth"]]),sections:[{title:U("setup.autodetect_title"),rows:[["Claude",U("setup.autodetect.claude")],["GPT",U("setup.autodetect.gpt")],["Grok",U("setup.autodetect.grok")],["Gemini",U("setup.autodetect.gemini")]]}],examples:[H(`${N} setup --key sk-xxx`,"setup.ex.key"),H(`${N} setup --install --key sk-xxx`,"setup.ex.install"),H(`${N} setup --claudekey sk-a --codexkey sk-b --agykey sk-g`,"setup.ex.split"),H(`${N} setup --force --key sk-xxx`,"setup.ex.force"),H(`${N} setup --base ${P} --key sk-xxx`,"setup.ex.base")]});const D=I.command("init").summary(U("init.summary")).description(U("init.description")).action(async()=>{B(),await r.runFirstTimeSetup()});p(D,{...M("init",[[`${N} setup --key <key>`,"init.alt.setup"],[`${N}`,"init.alt.menu"]]),examples:[H(`${N} init`,"init.ex.run"),H(`${N} init --base ${P}`,"init.ex.base"),H(`${N}`,"init.ex.menu")]});const F=I.command("auth").summary(U("auth.summary")).description(U("auth.description")).argument("[token]",U("auth.arg.token")).action(async e=>{const o=[];e&&o.push(e),await d(o)});q(F);const J=F.command("revoke").description(U("auth.revoke")).action(async()=>{await d(["revoke"])});p(J,{examples:[H(`${N} auth revoke`,"auth.ex.revoke_ask"),H(`${N} auth revoke --yes`,"auth.ex.revoke")]});const Y=F.command("reload <tool>").description(U("auth.reload")).action(async e=>{await d(["reload",e])});p(Y,{examples:[H(`${N} auth reload claude-code`,"auth.ex.reload"),H(`${N} auth reload claude-code --json`,"auth.ex.reload_json")]}),p(F,{...M("auth",[[`${N} setup --key <key>`,"auth.alt.setup"]]),examples:[H(`${N} auth`,"auth.ex.prompt"),H(`${N} auth sk-xxx --non-interactive`,"auth.ex.save"),H(`${N} auth revoke --yes`,"auth.ex.revoke"),H(`${N} auth reload claude-code`,"auth.ex.reload")]});const Q=I.command("config").summary(U("config.summary")).description(U("config.description")).argument("[args...]",U("config.arg.args")).option("-f, --force",U("config.opt.force")).allowUnknownOption(!0).action(async(e=[],o={})=>{await g(o.force?[...e,"--force"]:e)});p(Q,{...M("config",[[`${N} setup --force --key <key>`,"config.alt.setup"],[`${N} status`,"config.alt.status"]]),sections:[{title:U("config.subcommands_title"),rows:[["<tool>",U("config.sub.tool")],["<tool> --force",U("config.sub.tool_force")],["<tool> --non-interactive",U("config.sub.tool_non_interactive")],["<tool> remove",U("config.sub.tool_remove")],["<tool> restore",U("config.sub.tool_restore")],["<tool> backups",U("config.sub.tool_backups")],["<tool> clear-model",U("config.sub.tool_clear_model")],["tool-model list",U("config.sub.tool_model_list")],["tool-model set <tool> [slot] <model>",U("config.sub.tool_model_set")],["tool-model clear <tool>",U("config.sub.tool_model_clear")],["profile list",U("config.sub.profile_list")],["profile create <name>",U("config.sub.profile_create")],["profile use <name>",U("config.sub.profile_use")],["profile delete <name>",U("config.sub.profile_delete")],["preset list",U("config.sub.preset_list")],["preset save <name>",U("config.sub.preset_save")],["preset use <name>",U("config.sub.preset_use")],["preset delete <name>",U("config.sub.preset_delete")],["backup list",U("config.sub.backup_list")],["backup create [label]",U("config.sub.backup_create")],["backup restore <name>",U("config.sub.backup_restore")]]},{title:U("config.tools_title"),rows:Object.values(c).filter(e=>!e.hidden).map(e=>[e.name,e.displayName])}],examples:[H(`${N} config codex`,"config.ex.menu"),H(`${N} config claude-code --force`,"config.ex.force"),H(`${N} config tool-model set codex gpt-6-astra`,"config.ex.tool_model"),H(`${N} config profile create work`,"config.ex.profile"),H(`${N} config codex remove`,"config.ex.remove")]});const V=I.command("base").summary(U("base.summary")).description(U("base.description")).argument("[target]",U("base.arg.target")).action(async e=>{await C(e)});p(V,{...M("base",[[`${N} doctor`,"base.alt.doctor"],[`${N} bench`,"base.alt.bench"]]),sections:[{title:U("base.targets_title"),rows:[[U("base.target.none_term"),U("base.target.none")],[U("base.target.index_term"),U("base.target.index")],[U("base.target.url_term"),U("base.target.url")],["auto",U("base.target.auto")],["default",U("base.target.default",{url:l})]]}],examples:[H(`${N} base`,"base.ex.list"),H(`${N} base auto`,"base.ex.auto"),H(`${N} base 2`,"base.ex.index"),H(`${N} base ${P}`,"base.ex.url"),H(`${N} base default`,"base.ex.default")]}),I.commandsGroup(U("group.diagnose"));const W=I.command("status").summary(U("status.summary")).description(U("status.description")).action(async()=>{await x()});p(W,{...M("status",[[`${N} doctor`,"status.alt.doctor"]]),examples:[H(`${N} status`,"status.ex.show"),H(`${N} status --json`,"status.ex.json")]});const X=I.command("doctor").summary(U("doctor.summary")).description(U("doctor.description")).option("--deep",U("doctor.opt.deep")).option("--report",U("doctor.opt.report")).action(async e=>{await f({deep:e.deep,report:e.report})});p(X,{...M("doctor",[[`${N} base auto`,"doctor.alt.base"],[`${N} logs tail`,"doctor.alt.logs"]]),examples:[H(`${N} doctor`,"doctor.ex.run"),H(`${N} doctor --deep`,"doctor.ex.deep"),H(`${N} doctor --report`,"doctor.ex.report")]});const Z=I.command("bench").summary(U("bench.summary")).description(U("bench.description")).option("--samples <n>",U("bench.opt.samples"),"5").option("--stream",U("bench.opt.stream")).option("--model <model>",U("bench.opt.model")).action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),e.stream&&o.push("--stream"),e.model&&o.push("--model",e.model),await b(o)});p(Z,{...M("bench",[[`${N} base`,"bench.alt.base"]]),examples:[H(`${N} bench`,"bench.ex.run"),H(`${N} bench --samples 3`,"bench.ex.samples"),H(`${N} bench --stream`,"bench.ex.stream"),H(`${N} bench --stream --model gpt-6-astra`,"bench.ex.stream_model"),H(`${N} bench --json`,"bench.ex.json")]});const ee=I.command("logs").summary(U("logs.summary")).description(U("logs.description")).argument("[action]",U("logs.arg.action")).argument("[file]",U("logs.arg.file")).action(async(e,o)=>{const s=[];e&&s.push(e),o&&s.push(o),await $(s)});p(ee,{...M("logs",[[`${N} doctor`,"logs.alt.doctor"]]),sections:[{title:U("logs.actions_title"),rows:[["list",U("logs.action.list")],["tail [file]",U("logs.action.tail")],["path",U("logs.action.path")]]}],examples:[H(`${N} logs`,"logs.ex.list"),H(`${N} logs tail`,"logs.ex.tail"),H(`${N} logs path`,"logs.ex.path")]}),I.commandsGroup(U("group.maintain"));const oe=I.command("list").summary(U("list.summary")).description(U("list.description")).option("--refresh",U("list.opt.refresh")).action(async(e={})=>{await v({refresh:e.refresh})});p(oe,{...M("list",[[`${N} doctor`,"list.alt.doctor"]]),examples:[H(`${N} list`,"list.ex.all"),H(`${N} list --refresh`,"list.ex.refresh"),H(`${N} list --json`,"list.ex.json")]});const se=I.command("install").summary(U("install.summary")).description(U("install.description")).argument("<components...>",U("install.arg.components")).option("--force",U("install.opt.force")).action(async(e,o={})=>{await w(e,{force:o.force})});p(se,{...M("install",[[`${N} upgrade`,"install.alt.upgrade"],[`${N} setup --key ${i.t("errors.key_placeholder")}`,"install.alt.setup"]]),examples:[H(`${N} install node uv`,"install.ex.runtimes"),H(`${N} install claude-code codex`,"install.ex.clis"),H(`${N} install chatgpt`,"install.ex.app"),H(`${N} install uv --force`,"install.ex.force"),H(`${N} install node uv --yes --json`,"install.ex.json")]});const te=I.command("upgrade").summary(U("upgrade.summary")).description(U("upgrade.description")).argument("[tools...]",U("upgrade.arg.tools")).option("--force",U("upgrade.opt.force")).action(async(e=[],o={})=>{await _(e,{force:o.force})});p(te,{...M("upgrade",[[`${N} update`,"upgrade.alt.update"]]),examples:[H(`${N} upgrade`,"upgrade.ex.all"),H(`${N} upgrade codex agy`,"upgrade.ex.many"),H(`${N} upgrade claude-desktop`,"upgrade.ex.desktop"),H(`${N} upgrade chatgpt --force`,"upgrade.ex.force"),H(`${N} upgrade node`,"upgrade.ex.runtime")]});const ne=I.command("refresh").summary(U("refresh.summary")).description(U("refresh.description")).action(async()=>{await h()});p(ne,{...M("refresh",[[`${N} setup --key ${i.t("errors.key_placeholder")}`,"refresh.alt.setup"]]),examples:[H(`${N} refresh`,"refresh.ex.run"),H(`${N} refresh --base ${T}`,"refresh.ex.base")]});const ae=I.command("update").summary(U("update.summary")).description(U("update.description")).action(async()=>{await k()});p(ae,{...M("update",[[`${N} upgrade`,"update.alt.upgrade"]]),examples:[H(`${N} update`,"update.ex.check"),H(`${N} update --json`,"update.ex.json")]});const ie=I.command("codex").summary(U("codex.summary")).description(U("codex.description"));q(ie);const re=ie.command("sessions").summary(U("codex_sessions.summary")).description(U("codex_sessions.description")).option("--to <provider>",U("codex_sessions.opt.to")).option("--undo <dir>",U("codex_sessions.opt.undo")).action(async e=>{await j(e)});p(ie,{...M("codex"),examples:[H(`${N} codex sessions`,"codex_sessions.ex.move"),H(`${N} codex sessions --undo <dir>`,"codex_sessions.ex.undo")]}),p(re,{...M("codex_sessions",[[`${N} codex sessions --undo <dir>`,"codex_sessions.alt.undo"]]),examples:[H(`${N} codex sessions`,"codex_sessions.ex.move"),H(`${N} codex sessions --to custom`,"codex_sessions.ex.to"),H(`${N} codex sessions --undo <dir>`,"codex_sessions.ex.undo")]});const ce=I.command("sessions").summary(U("sessions.summary")).description(U("sessions.description"));q(ce);const le=ce.command("list",{isDefault:!0}).description(U("sessions.list")).option("--tool <tool>",U("sessions.opt.tool")).option("--dir <path>",U("sessions.opt.dir")).option("--here",U("sessions.opt.here")).option("-n, --limit <count>",U("sessions.opt.limit")).option("--all",U("sessions.opt.all")).action(async e=>{await S(e)}),pe=ce.command("show <id>").description(U("sessions.show")).option("-m, --messages <count>",U("sessions.opt.messages")).action(async(e,o)=>{await E(e,o)}),me=ce.command("resume <id>").description(U("sessions.resume")).option("--run",U("sessions.opt.run")).action(async(e,o)=>{await A(e,o)}),ue=ce.command("export <ids...>").description(U("sessions.export")).option("-o, --output <dir>",U("sessions.opt.output")).option("--tools",U("sessions.opt.tools")).action(async(e,o)=>{await O(e,o)});p(le,{examples:[H(`${N} sessions list`,"sessions.ex.list"),H(`${N} sessions list --tool codex`,"sessions.ex.codex"),H(`${N} sessions list --here --all`,"sessions.ex.here"),H(`${N} sessions list --json`,"sessions.ex.json")]}),p(pe,{examples:[H(`${N} sessions show 01a0f5fe`,"sessions.ex.show"),H(`${N} sessions show 01a0f5fe -m 20`,"sessions.ex.show_more")]}),p(me,{examples:[H(`${N} sessions resume 01a0f5fe`,"sessions.ex.resume_print"),H(`${N} sessions resume 01a0f5fe --run`,"sessions.ex.resume")]}),p(ue,{examples:[H(`${N} sessions export 01a0f5fe 7c2e9d41 -o ./sessions`,"sessions.ex.export"),H(`${N} sessions export 01a0f5fe -o ./sessions --tools`,"sessions.ex.export_tools")]}),p(ce,{...M("sessions",[[`${N} codex sessions`,"sessions.alt.codex_sessions"]]),examples:[H(`${N} sessions`,"sessions.ex.list"),H(`${N} sessions --tool codex --here`,"sessions.ex.here"),H(`${N} sessions show 01a0f5fe`,"sessions.ex.show"),H(`${N} sessions resume 01a0f5fe --run`,"sessions.ex.resume"),H(`${N} sessions export 01a0f5fe 7c2e9d41 -o ./sessions`,"sessions.ex.export")]});const de=I.command("lang").summary(U("lang.summary")).description(U("lang.description"));q(de);const fe=de.command("show").description(U("lang.show")).action(async()=>{await u(["show"])}),ge=de.command("set <locale>").description(U("lang.set")).action(async e=>{await u(["set",e])}),xe=[H(`${N} lang show`,"lang.ex.show"),H(`${N} lang set en_US`,"lang.ex.set_en"),H(`${N} lang set zh_CN`,"lang.ex.set_zh")];p(de,{...M("lang",[[`${N} enter lang`,"lang.alt.enter"]]),examples:xe}),p(fe,{examples:[xe[0],H(`${N} lang show --json`,"lang.ex.show_json")]}),p(ge,{examples:xe.slice(1)});const he=I.command("completion").summary(U("completion.summary")).description(U("completion.description")).argument("<shell>",U("completion.arg.shell")).action(async e=>{await y([e])});p(he,{...M("completion"),examples:[H(`${N} completion bash > ~/.${N}-completion.bash`,"completion.ex.bash"),H(`${N} completion zsh > ~/.${N}-completion.zsh`,"completion.ex.zsh"),H(`${N} completion fish > ~/.config/fish/completions/${N}.fish`,"completion.ex.fish")]});const $e=I.command("enter").summary(U("enter.summary")).description(U("enter.description")).argument("[option]",U("enter.arg.option")).action(async e=>{if(e&&!["lang","language","apikey","api-key","models"].includes(e)||B(),e)switch(e){case"lang":case"language":await r.configLanguage();break;case"apikey":case"api-key":await r.configApiKey();break;case"models":await r.configModels();break;default:await g([e])}else await r.run()});p($e,{...M("enter",[[`${N}`,"enter.alt.menu"],[`${N} config <tool>`,"enter.alt.config"]]),sections:[{title:U("enter.options_title"),rows:[[U("enter.option.none_term"),U("enter.option.none")],["lang",U("enter.option.lang")],["apikey",U("enter.option.apikey")],["models",U("enter.option.models")],["<tool>",U("enter.option.tool")]]}],examples:[H(`${N} enter`,"enter.ex.menu"),H(`${N} enter models`,"enter.ex.models"),H(`${N} enter codex`,"enter.ex.tool")]}),q(I),I.action(async()=>{B(),await r.run()}),p(I,{examples:[H(`${N}`,"root.ex.menu"),H(`${N} setup --key sk-xxx`,"root.ex.setup"),H(`${N} doctor`,"root.ex.doctor"),H(`${N} base auto`,"root.ex.base"),H(`${N} config codex`,"root.ex.config")]});const ye=e=>{for(const o of e.commands)o.helpOption("-h, --help",U("global.help")),ye(o)};return ye(I),I}
1
+ import{Command as e,Option as o}from"commander";import{readFileSync as s}from"fs";import{join as t,dirname as n}from"path";import{fileURLToPath as a}from"url";import{i18n as i}from"./i18n.js";import{wizard as r}from"./wizard.js";import{SUPPORTED_TOOLS as c}from"./tool-manager.js";import{DEFAULT_BASE_URL as l}from"./config.js";import{attachCommandHelp as p,helpConfiguration as m}from"./cli-help.js";import{langCommand as u,authCommand as d,doctorCommand as f,configCommand as g,statusCommand as x,refreshCommand as h}from"../commands/index.js";import{logsCommand as y}from"../commands/logs.js";import{completionCommand as $}from"../commands/completion.js";import{benchCommand as b}from"../commands/bench.js";import{updateCommand as k}from"../commands/update.js";import{upgradeCommand as _}from"../commands/upgrade.js";import{installCommand as w,listCommand as v}from"../commands/components.js";import{codexSessionsCommand as j}from"../commands/codex-sessions.js";import{sessionsExportCommand as O,sessionsListCommand as S,sessionsResumeCommand as A,sessionsShowCommand as E}from"../commands/sessions.js";import{setupCommand as G,DEDICATED_KEYS as z}from"../commands/setup.js";import{baseCommand as C}from"../commands/base.js";import{usageError as R}from"./errors.js";import{requireTerminal as B}from"./confirm.js";import{brand as I,brandEnv as L,CLI_NAME as N}from"./brand.js";const P=(I().gatewayAddresses[1]??I().gatewayAddresses[0]).url,T=(I().gatewayAddresses[2]??I().gatewayAddresses[0]).url;function U(e,o){return i.t(`help.${e}`,o)}function H(e,o){return{command:e,note:U(o)}}function K(e){const o=U(e);return o===`help.${e}`?"":o}function M(e,o=[]){return{when:K(`${e}.when`),prerequisites:K(`${e}.needs`).split("\n").filter(Boolean),alternatives:o.map(([e,o])=>H(e,o))}}function q(e){return e.helpCommand("help [command]",U("help_command"))}export function buildProgram(){const I=new e;I.name(`${N}`).summary(U("root.summary")).description(U("root.description")).configureHelp(m()).allowExcessArguments(!0).optionsGroup(U("group.global_options")).helpOption("-h, --help",U("global.help")).version(function(){try{const e=n(a(import.meta.url));return JSON.parse(s(t(e,"../../package.json"),"utf-8")).version}catch{return"1.0.0"}}(),"-v, --version",U("global.version")).option("--base <url>",U("global.base")).option("--non-interactive",U("global.non_interactive")).option("--json",U("global.json")).option("-y, --yes",U("global.yes")).option("--dry-run",U("global.dry_run")).option("--no-color",U("global.no_color")),I.configureOutput({writeErr:()=>{},outputError:()=>{}}),I.showSuggestionAfterError(),I.exitOverride(e=>{throw e}),I.commandsGroup(U("group.configure"));const K=I.command("setup").summary(U("setup.summary")).description(U("setup.description")).option("--key <key>",U("setup.opt.key"));for(const e of z){const s=new o(`--${e.option} <key>`,U(`setup.opt.${e.option}`));e.hidden&&s.hideHelp(),K.addOption(s)}K.option("--1m",U("setup.opt.one_m")).option("--no-1m",U("setup.opt.no_one_m")).option("--claude-official <on|off>",U("setup.opt.claude_official")).option("--desktop-registry <on|off>",U("setup.opt.desktop_registry")).option("--force",U("setup.opt.force")).option("--install",U("setup.opt.install")).option("--no-install",U("setup.opt.no_install")).option("--all",U("setup.opt.all")).action(async e=>{e.key||z.some(o=>e[o.option])||(e.key=L("SETUP_KEY")?.trim()||void 0,e.fromInstaller=Boolean(e.key));const o=Boolean(e.key||z.some(o=>e[o.option])),s=void 0!==e["1m"]||void 0!==e.claudeOfficial||void 0!==e.desktopRegistry,t=i.t("errors.key_placeholder");if(!o&&!s)throw R(i.t("setup.usage"),`${N} setup --key ${t}`,"missing_key");if(void 0!==e.claudeOfficial&&!["on","off"].includes(String(e.claudeOfficial).toLowerCase()))throw R(i.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)}),`${N} setup --claude-official on|off`,"invalid_value");if(void 0!==e.desktopRegistry&&!["on","off"].includes(String(e.desktopRegistry).toLowerCase()))throw R(i.t("setup.desktop_registry_invalid",{value:String(e.desktopRegistry)}),`${N} setup --desktop-registry on|off`,"invalid_value");await G({key:e.key,...Object.fromEntries(z.map(o=>[o.option,e[o.option]])),all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,desktopRegistry:e.desktopRegistry,force:e.force,install:e.install,fromInstaller:e.fromInstaller})}),p(K,{...M("setup",[[`${N} init`,"setup.alt.init"],[`${N} config <tool>`,"setup.alt.config"],[`${N} auth <key>`,"setup.alt.auth"]]),sections:[{title:U("setup.autodetect_title"),rows:[["Claude",U("setup.autodetect.claude")],["GPT",U("setup.autodetect.gpt")],["Grok",U("setup.autodetect.grok")],["Gemini",U("setup.autodetect.gemini")],[U("setup.autodetect.any_label"),U("setup.autodetect.any")]]}],examples:[H(`${N} setup --key sk-xxx`,"setup.ex.key"),H(`${N} setup --install --key sk-xxx`,"setup.ex.install"),H(`${N} setup --claudekey sk-a --codexkey sk-b --agykey sk-g`,"setup.ex.split"),H(`${N} setup --force --key sk-xxx`,"setup.ex.force"),H(`${N} setup --base ${P} --key sk-xxx`,"setup.ex.base")]});const D=I.command("init").summary(U("init.summary")).description(U("init.description")).action(async()=>{B(),await r.runFirstTimeSetup()});p(D,{...M("init",[[`${N} setup --key <key>`,"init.alt.setup"],[`${N}`,"init.alt.menu"]]),examples:[H(`${N} init`,"init.ex.run"),H(`${N} init --base ${P}`,"init.ex.base"),H(`${N}`,"init.ex.menu")]});const F=I.command("auth").summary(U("auth.summary")).description(U("auth.description")).argument("[token]",U("auth.arg.token")).action(async e=>{const o=[];e&&o.push(e),await d(o)});q(F);const J=F.command("revoke").description(U("auth.revoke")).action(async()=>{await d(["revoke"])});p(J,{examples:[H(`${N} auth revoke`,"auth.ex.revoke_ask"),H(`${N} auth revoke --yes`,"auth.ex.revoke")]});const Y=F.command("reload <tool>").description(U("auth.reload")).action(async e=>{await d(["reload",e])});p(Y,{examples:[H(`${N} auth reload claude-code`,"auth.ex.reload"),H(`${N} auth reload claude-code --json`,"auth.ex.reload_json")]}),p(F,{...M("auth",[[`${N} setup --key <key>`,"auth.alt.setup"]]),examples:[H(`${N} auth`,"auth.ex.prompt"),H(`${N} auth sk-xxx --non-interactive`,"auth.ex.save"),H(`${N} auth revoke --yes`,"auth.ex.revoke"),H(`${N} auth reload claude-code`,"auth.ex.reload")]});const Q=I.command("config").summary(U("config.summary")).description(U("config.description")).argument("[args...]",U("config.arg.args")).option("-f, --force",U("config.opt.force")).allowUnknownOption(!0).action(async(e=[],o={})=>{await g(o.force?[...e,"--force"]:e)});p(Q,{...M("config",[[`${N} setup --force --key <key>`,"config.alt.setup"],[`${N} status`,"config.alt.status"]]),sections:[{title:U("config.subcommands_title"),rows:[["<tool>",U("config.sub.tool")],["<tool> --force",U("config.sub.tool_force")],["<tool> --non-interactive",U("config.sub.tool_non_interactive")],["<tool> remove",U("config.sub.tool_remove")],["<tool> restore",U("config.sub.tool_restore")],["<tool> backups",U("config.sub.tool_backups")],["<tool> clear-model",U("config.sub.tool_clear_model")],["tool-model list",U("config.sub.tool_model_list")],["tool-model set <tool> [slot] <model>",U("config.sub.tool_model_set")],["tool-model clear <tool>",U("config.sub.tool_model_clear")],["profile list",U("config.sub.profile_list")],["profile create <name>",U("config.sub.profile_create")],["profile use <name>",U("config.sub.profile_use")],["profile delete <name>",U("config.sub.profile_delete")],["preset list",U("config.sub.preset_list")],["preset save <name>",U("config.sub.preset_save")],["preset use <name>",U("config.sub.preset_use")],["preset delete <name>",U("config.sub.preset_delete")],["backup list",U("config.sub.backup_list")],["backup create [label]",U("config.sub.backup_create")],["backup restore <name>",U("config.sub.backup_restore")]]},{title:U("config.tools_title"),rows:Object.values(c).filter(e=>!e.hidden).map(e=>[e.name,e.displayName])}],examples:[H(`${N} config codex`,"config.ex.menu"),H(`${N} config claude-code --force`,"config.ex.force"),H(`${N} config tool-model set codex gpt-6-astra`,"config.ex.tool_model"),H(`${N} config profile create work`,"config.ex.profile"),H(`${N} config codex remove`,"config.ex.remove")]});const V=I.command("base").summary(U("base.summary")).description(U("base.description")).argument("[target]",U("base.arg.target")).action(async e=>{await C(e)});p(V,{...M("base",[[`${N} doctor`,"base.alt.doctor"],[`${N} bench`,"base.alt.bench"]]),sections:[{title:U("base.targets_title"),rows:[[U("base.target.none_term"),U("base.target.none")],[U("base.target.index_term"),U("base.target.index")],[U("base.target.url_term"),U("base.target.url")],["auto",U("base.target.auto")],["default",U("base.target.default",{url:l})]]}],examples:[H(`${N} base`,"base.ex.list"),H(`${N} base auto`,"base.ex.auto"),H(`${N} base 2`,"base.ex.index"),H(`${N} base ${P}`,"base.ex.url"),H(`${N} base default`,"base.ex.default")]}),I.commandsGroup(U("group.diagnose"));const W=I.command("status").summary(U("status.summary")).description(U("status.description")).action(async()=>{await x()});p(W,{...M("status",[[`${N} doctor`,"status.alt.doctor"]]),examples:[H(`${N} status`,"status.ex.show"),H(`${N} status --json`,"status.ex.json")]});const X=I.command("doctor").summary(U("doctor.summary")).description(U("doctor.description")).option("--deep",U("doctor.opt.deep")).option("--report",U("doctor.opt.report")).action(async e=>{await f({deep:e.deep,report:e.report})});p(X,{...M("doctor",[[`${N} base auto`,"doctor.alt.base"],[`${N} logs tail`,"doctor.alt.logs"]]),examples:[H(`${N} doctor`,"doctor.ex.run"),H(`${N} doctor --deep`,"doctor.ex.deep"),H(`${N} doctor --report`,"doctor.ex.report")]});const Z=I.command("bench").summary(U("bench.summary")).description(U("bench.description")).option("--samples <n>",U("bench.opt.samples"),"5").option("--stream",U("bench.opt.stream")).option("--model <model>",U("bench.opt.model")).action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),e.stream&&o.push("--stream"),e.model&&o.push("--model",e.model),await b(o)});p(Z,{...M("bench",[[`${N} base`,"bench.alt.base"]]),examples:[H(`${N} bench`,"bench.ex.run"),H(`${N} bench --samples 3`,"bench.ex.samples"),H(`${N} bench --stream`,"bench.ex.stream"),H(`${N} bench --stream --model gpt-6-astra`,"bench.ex.stream_model"),H(`${N} bench --json`,"bench.ex.json")]});const ee=I.command("logs").summary(U("logs.summary")).description(U("logs.description")).argument("[action]",U("logs.arg.action")).argument("[file]",U("logs.arg.file")).action(async(e,o)=>{const s=[];e&&s.push(e),o&&s.push(o),await y(s)});p(ee,{...M("logs",[[`${N} doctor`,"logs.alt.doctor"]]),sections:[{title:U("logs.actions_title"),rows:[["list",U("logs.action.list")],["tail [file]",U("logs.action.tail")],["path",U("logs.action.path")]]}],examples:[H(`${N} logs`,"logs.ex.list"),H(`${N} logs tail`,"logs.ex.tail"),H(`${N} logs path`,"logs.ex.path")]}),I.commandsGroup(U("group.maintain"));const oe=I.command("list").summary(U("list.summary")).description(U("list.description")).option("--refresh",U("list.opt.refresh")).action(async(e={})=>{await v({refresh:e.refresh})});p(oe,{...M("list",[[`${N} doctor`,"list.alt.doctor"]]),examples:[H(`${N} list`,"list.ex.all"),H(`${N} list --refresh`,"list.ex.refresh"),H(`${N} list --json`,"list.ex.json")]});const se=I.command("install").summary(U("install.summary")).description(U("install.description")).argument("<components...>",U("install.arg.components")).option("--force",U("install.opt.force")).action(async(e,o={})=>{await w(e,{force:o.force})});p(se,{...M("install",[[`${N} upgrade`,"install.alt.upgrade"],[`${N} setup --key ${i.t("errors.key_placeholder")}`,"install.alt.setup"]]),examples:[H(`${N} install node uv`,"install.ex.runtimes"),H(`${N} install claude-code codex`,"install.ex.clis"),H(`${N} install chatgpt`,"install.ex.app"),H(`${N} install uv --force`,"install.ex.force"),H(`${N} install node uv --yes --json`,"install.ex.json")]});const te=I.command("upgrade").summary(U("upgrade.summary")).description(U("upgrade.description")).argument("[tools...]",U("upgrade.arg.tools")).option("--force",U("upgrade.opt.force")).action(async(e=[],o={})=>{await _(e,{force:o.force})});p(te,{...M("upgrade",[[`${N} update`,"upgrade.alt.update"]]),examples:[H(`${N} upgrade`,"upgrade.ex.all"),H(`${N} upgrade codex agy`,"upgrade.ex.many"),H(`${N} upgrade claude-desktop`,"upgrade.ex.desktop"),H(`${N} upgrade chatgpt --force`,"upgrade.ex.force"),H(`${N} upgrade node`,"upgrade.ex.runtime")]});const ne=I.command("refresh").summary(U("refresh.summary")).description(U("refresh.description")).action(async()=>{await h()});p(ne,{...M("refresh",[[`${N} setup --key ${i.t("errors.key_placeholder")}`,"refresh.alt.setup"]]),examples:[H(`${N} refresh`,"refresh.ex.run"),H(`${N} refresh --base ${T}`,"refresh.ex.base")]});const ae=I.command("update").summary(U("update.summary")).description(U("update.description")).action(async()=>{await k()});p(ae,{...M("update",[[`${N} upgrade`,"update.alt.upgrade"]]),examples:[H(`${N} update`,"update.ex.check"),H(`${N} update --json`,"update.ex.json")]});const ie=I.command("codex").summary(U("codex.summary")).description(U("codex.description"));q(ie);const re=ie.command("sessions").summary(U("codex_sessions.summary")).description(U("codex_sessions.description")).option("--to <provider>",U("codex_sessions.opt.to")).option("--undo <dir>",U("codex_sessions.opt.undo")).action(async e=>{await j(e)});p(ie,{...M("codex"),examples:[H(`${N} codex sessions`,"codex_sessions.ex.move"),H(`${N} codex sessions --undo <dir>`,"codex_sessions.ex.undo")]}),p(re,{...M("codex_sessions",[[`${N} codex sessions --undo <dir>`,"codex_sessions.alt.undo"]]),examples:[H(`${N} codex sessions`,"codex_sessions.ex.move"),H(`${N} codex sessions --to custom`,"codex_sessions.ex.to"),H(`${N} codex sessions --undo <dir>`,"codex_sessions.ex.undo")]});const ce=I.command("sessions").summary(U("sessions.summary")).description(U("sessions.description"));q(ce);const le=ce.command("list",{isDefault:!0}).description(U("sessions.list")).option("--tool <tool>",U("sessions.opt.tool")).option("--dir <path>",U("sessions.opt.dir")).option("--here",U("sessions.opt.here")).option("-n, --limit <count>",U("sessions.opt.limit")).option("--all",U("sessions.opt.all")).action(async e=>{await S(e)}),pe=ce.command("show <id>").description(U("sessions.show")).option("-m, --messages <count>",U("sessions.opt.messages")).action(async(e,o)=>{await E(e,o)}),me=ce.command("resume <id>").description(U("sessions.resume")).option("--run",U("sessions.opt.run")).action(async(e,o)=>{await A(e,o)}),ue=ce.command("export <ids...>").description(U("sessions.export")).option("-o, --output <dir>",U("sessions.opt.output")).option("--tools",U("sessions.opt.tools")).action(async(e,o)=>{await O(e,o)});p(le,{examples:[H(`${N} sessions list`,"sessions.ex.list"),H(`${N} sessions list --tool codex`,"sessions.ex.codex"),H(`${N} sessions list --here --all`,"sessions.ex.here"),H(`${N} sessions list --json`,"sessions.ex.json")]}),p(pe,{examples:[H(`${N} sessions show 01a0f5fe`,"sessions.ex.show"),H(`${N} sessions show 01a0f5fe -m 20`,"sessions.ex.show_more")]}),p(me,{examples:[H(`${N} sessions resume 01a0f5fe`,"sessions.ex.resume_print"),H(`${N} sessions resume 01a0f5fe --run`,"sessions.ex.resume")]}),p(ue,{examples:[H(`${N} sessions export 01a0f5fe 7c2e9d41 -o ./sessions`,"sessions.ex.export"),H(`${N} sessions export 01a0f5fe -o ./sessions --tools`,"sessions.ex.export_tools")]}),p(ce,{...M("sessions",[[`${N} codex sessions`,"sessions.alt.codex_sessions"]]),examples:[H(`${N} sessions`,"sessions.ex.list"),H(`${N} sessions --tool codex --here`,"sessions.ex.here"),H(`${N} sessions show 01a0f5fe`,"sessions.ex.show"),H(`${N} sessions resume 01a0f5fe --run`,"sessions.ex.resume"),H(`${N} sessions export 01a0f5fe 7c2e9d41 -o ./sessions`,"sessions.ex.export")]});const de=I.command("lang").summary(U("lang.summary")).description(U("lang.description"));q(de);const fe=de.command("show").description(U("lang.show")).action(async()=>{await u(["show"])}),ge=de.command("set <locale>").description(U("lang.set")).action(async e=>{await u(["set",e])}),xe=[H(`${N} lang show`,"lang.ex.show"),H(`${N} lang set en_US`,"lang.ex.set_en"),H(`${N} lang set zh_CN`,"lang.ex.set_zh")];p(de,{...M("lang",[[`${N} enter lang`,"lang.alt.enter"]]),examples:xe}),p(fe,{examples:[xe[0],H(`${N} lang show --json`,"lang.ex.show_json")]}),p(ge,{examples:xe.slice(1)});const he=I.command("completion").summary(U("completion.summary")).description(U("completion.description")).argument("<shell>",U("completion.arg.shell")).action(async e=>{await $([e])});p(he,{...M("completion"),examples:[H(`${N} completion bash > ~/.${N}-completion.bash`,"completion.ex.bash"),H(`${N} completion zsh > ~/.${N}-completion.zsh`,"completion.ex.zsh"),H(`${N} completion fish > ~/.config/fish/completions/${N}.fish`,"completion.ex.fish")]});const ye=I.command("enter").summary(U("enter.summary")).description(U("enter.description")).argument("[option]",U("enter.arg.option")).action(async e=>{if(e&&!["lang","language","apikey","api-key","models"].includes(e)||B(),e)switch(e){case"lang":case"language":await r.configLanguage();break;case"apikey":case"api-key":await r.configApiKey();break;case"models":await r.configModels();break;default:await g([e])}else await r.run()});p(ye,{...M("enter",[[`${N}`,"enter.alt.menu"],[`${N} config <tool>`,"enter.alt.config"]]),sections:[{title:U("enter.options_title"),rows:[[U("enter.option.none_term"),U("enter.option.none")],["lang",U("enter.option.lang")],["apikey",U("enter.option.apikey")],["models",U("enter.option.models")],["<tool>",U("enter.option.tool")]]}],examples:[H(`${N} enter`,"enter.ex.menu"),H(`${N} enter models`,"enter.ex.models"),H(`${N} enter codex`,"enter.ex.tool")]}),q(I),I.action(async()=>{B(),await r.run()}),p(I,{examples:[H(`${N}`,"root.ex.menu"),H(`${N} setup --key sk-xxx`,"root.ex.setup"),H(`${N} doctor`,"root.ex.doctor"),H(`${N} base auto`,"root.ex.base"),H(`${N} config codex`,"root.ex.config")]});const $e=e=>{for(const o of e.commands)o.helpOption("-h, --help",U("global.help")),$e(o)};return $e(I),I}
@@ -1 +1 @@
1
- import{join as e}from"path";import{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as n}from"./backup.js";import{i18n as s}from"../i18n.js";import{baseModelId as a}from"../model-catalog.js";import{gatewayModelSpecs as r,prettyModelName as i,vendorNames as l}from"../gateway-models.js";import{toolHome as c}from"./types.js";import{gatewayAddresses as d,normalizeBaseUrl as u}from"../gateway-addresses.js";import{configManager as m,savedApiKeys as p}from"../config.js";import{envCleanupPlan as f,lastEnvCleanup as g,removeEnvSources as _}from"../env-cleanup.js";import{canPrompt as h,hasExplicitYes as v}from"../runtime-options.js";import{maskHome as C}from"../telemetry.js";import{spawnSync as j}from"child_process";import{existsSync as y,readFileSync as k}from"fs";import{detectManagedOverrides as A}from"./claude-managed.js";import{installOfficialPlugins as O}from"./claude-plugins.js";import{findInstalls as S,mirrorInstallCommand as P,brandInstallScripts as x}from"./tool-installs.js";import{updateNativeFromMirror as b}from"./claude-native-mirror.js";import{brandEnv as T}from"../brand.js";import{claudeFormat as N,claudeProjectFiles as w}from"../sessions/formats.js";const H=e(c(),".claude","settings.json"),L=e(c(),".claude.json");let R,U=null,E=()=>0===j("security",["find-generic-password","-s","Claude Code-credentials"],{stdio:"ignore"}).status;export function setClaudeKeychainProbeForTests(e){E=e}export function claudeAccountLoginPresent(){const t=process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude");if(y(e(t,".credentials.json")))return!0;if("darwin"!==process.platform||T("TEST_HOME"))return!1;try{return E()}catch{return!1}}function I(){try{const t=JSON.parse(k(e(c(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function M(e){if(!e)return;const t=new Set(e.models);return r("claude-code",e.clientModels).filter(e=>t.has(e.id))}function D(){const e=o.getSettings().env?.ANTHROPIC_BASE_URL;return"string"==typeof e&&e.trim()?claudeCodeAdapter.holdsOurConfig(p())?null:e.trim():null}let B=null;const K={command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>b(I())},mirrorScript:x("claude-code"),brew:"claude-code"};function F(){return h()&&!v()}export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:P(K),upgradeCommand:"claude update",installSpec:K,upgradeHints:e=>"stable"===I()?[s.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:H,sessions:{format:N,files:()=>w(e(process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude"),"projects")),resumeCommand:e=>({command:"claude",args:["--resume",e]})},wantsModelCatalog:!0,loadPrivateConfig(t){B=D(),n(H),n(L),R=M(t.catalog),o.loadPrivateConfig(t.apiKey,t.models,t.catalog?.models,R);const s=e(c(),".local","bin","win32"===process.platform?"claude.exe":"claude"),a=S(claudeCodeAdapter.installSpec)[0]?.path??(y(s)?s:null);U=a?O(a):null},holdsOurConfig(e){const t=o.getSettings().env??{},n="string"==typeof t.ANTHROPIC_AUTH_TOKEN?t.ANTHROPIC_AUTH_TOKEN.trim():"",s="string"==typeof t.ANTHROPIC_BASE_URL?u(t.ANTHROPIC_BASE_URL):null;return!(!n||!e.includes(n))||null!==s&&(d().some(e=>e.url===s)||s===u(m.getBaseUrl()))},planNotes(){const e=[],t=D();t&&e.push(s.t("tools.claude_code.plan_replace_provider",{url:t}));for(const t of f())e.push(s.t("tools.claude_code.plan_env_remove",{location:C(t.location),vars:Object.keys(t.vars).join(", ")}));return e},unloadPrivateConfig(e){const t=o.getSettings().env??{};Boolean(t.ANTHROPIC_AUTH_TOKEN||t.ANTHROPIC_BASE_URL)&&!claudeCodeAdapter.holdsOurConfig(e?.savedKeys??[])||(n(H),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=>(n(H),o.clearPinnedModels(e?.models)),listedModels(){const e=o.getSettings().modelPicker;return Array.isArray(e?.options)?e.options.map(e=>a(String(e.model))):null},refreshModelMenu(e){const s=o.getSettings(),a=t(s,M(e.catalog));JSON.stringify(a)!==JSON.stringify(s)&&(n(H),o.saveSettings(a))},async followUp(){const e=g().foreign;if(0===e.length||!F())return null;const t=e.map(e=>`${C(e.location)}(${Object.keys(e.vars).join(", ")})`).join("、");return{prompt:s.t("tools.claude_code.env_foreign_offer",{where:t}),hint:s.t("tools.claude_code.env_foreign",{location:e.map(e=>C(e.location)).join("、"),vars:[...new Set(e.flatMap(e=>Object.keys(e.vars)))].join(", ")}),async run(){const t=_(e),o=t.removed.map(e=>({kind:"summary",text:s.t("tools.claude_code.env_removed_foreign",{location:C(e.location),vars:Object.keys(e.vars).join(", ")})}));return t.machineLeft.length>0&&o.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})}),o},decline(){}}},applyNotes(){const e=[];U?.installed.length&&(e.push({kind:"summary",text:s.t("tools.claude_code.plugins_installed",{count:String(U.installed.length)})}),e.push({kind:"detail",text:U.installed.join(", ")})),"git"===U?.skipped?e.push({kind:"action",text:s.t("tools.claude_code.plugins_need_git")}):("marketplace"===U?.skipped||U?.failed.length)&&e.push({kind:"action",text:s.t("tools.claude_code.plugins_failed")}),R&&R.length>0&&(e.push({kind:"summary",text:s.t("tools.claude_code.gateway_models",{count:String(R.length),vendors:l(R.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:R.map(e=>e.name||i(e.id)).join(", ")})),B&&e.push({kind:"summary",text:s.t("tools.claude_code.provider_replaced",{url:B})});const t=g();for(const o of t.removed)e.push({kind:"summary",text:s.t("tools.claude_code.env_removed",{location:C(o.location),vars:Object.keys(o.vars).join(", ")})});for(const o of t.foreign){const t={location:C(o.location),vars:Object.keys(o.vars).join(", ")};e.push(F()?{kind:"detail",text:s.t("tools.claude_code.env_foreign_found",t)}:{kind:"action",text:s.t("tools.claude_code.env_foreign",t)})}t.machineLeft.length>0&&e.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})});const n=o.getLastReplaced();n.length>0&&e.push({kind:"summary",text:s.t("tools.claude_code.replaced_user_models",{values:n.join(", ")})}),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===j("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:s.t("tools.claude_code.no_git_bash")}),claudeAccountLoginPresent()&&e.push({kind:"detail",text:s.t("tools.claude_code.account_login_overridden")});for(const t of A())e.push({kind:"action",text:s.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:s.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[H,L],forceResetPaths:()=>[H]};
1
+ import{join as e}from"path";import{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as n}from"./backup.js";import{i18n as s}from"../i18n.js";import{baseModelId as a}from"../model-catalog.js";import{gatewayModelSpecs as r,prettyModelName as i,vendorNames as l}from"../gateway-models.js";import{toolHome as c}from"./types.js";import{gatewayAddresses as d,normalizeBaseUrl as u}from"../gateway-addresses.js";import{configManager as m,savedApiKeys as p}from"../config.js";import{envCleanupPlan as f,lastEnvCleanup as g,removeEnvSources as _}from"../env-cleanup.js";import{canPrompt as h,hasExplicitYes as v}from"../runtime-options.js";import{maskHome as C}from"../telemetry.js";import{spawnSync as j}from"child_process";import{existsSync as y,readFileSync as k}from"fs";import{detectManagedOverrides as A}from"./claude-managed.js";import{installOfficialPlugins as O}from"./claude-plugins.js";import{findInstalls as P,mirrorInstallCommand as S,brandInstallScripts as x}from"./tool-installs.js";import{updateNativeFromMirror as b}from"./claude-native-mirror.js";import{brandEnv as T}from"../brand.js";import{claudeFormat as N,claudeProjectFiles as w}from"../sessions/formats.js";const H=e(c(),".claude","settings.json"),L=e(c(),".claude.json");let R,U=null,E=()=>0===j("security",["find-generic-password","-s","Claude Code-credentials"],{stdio:"ignore"}).status;export function setClaudeKeychainProbeForTests(e){E=e}export function claudeAccountLoginPresent(){const t=process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude");if(y(e(t,".credentials.json")))return!0;if("darwin"!==process.platform||T("TEST_HOME"))return!1;try{return E()}catch{return!1}}function I(){try{const t=JSON.parse(k(e(c(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function M(e){if(!e)return;const t=new Set(e.models);return r("claude-code",e.clientModels,e.clientPersonal).filter(e=>t.has(e.id))}function D(){const e=o.getSettings().env?.ANTHROPIC_BASE_URL;return"string"==typeof e&&e.trim()?claudeCodeAdapter.holdsOurConfig(p())?null:e.trim():null}let B=null;const K={command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>b(I())},mirrorScript:x("claude-code"),brew:"claude-code"};function F(){return h()&&!v()}export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:S(K),upgradeCommand:"claude update",installSpec:K,upgradeHints:e=>"stable"===I()?[s.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:H,sessions:{format:N,files:()=>w(e(process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude"),"projects")),resumeCommand:e=>({command:"claude",args:["--resume",e]})},wantsModelCatalog:!0,loadPrivateConfig(t){B=D(),n(H),n(L),R=M(t.catalog),o.loadPrivateConfig(t.apiKey,t.models,t.catalog?.models,R);const s=e(c(),".local","bin","win32"===process.platform?"claude.exe":"claude"),a=P(claudeCodeAdapter.installSpec)[0]?.path??(y(s)?s:null);U=a?O(a):null},holdsOurConfig(e){const t=o.getSettings().env??{},n="string"==typeof t.ANTHROPIC_AUTH_TOKEN?t.ANTHROPIC_AUTH_TOKEN.trim():"",s="string"==typeof t.ANTHROPIC_BASE_URL?u(t.ANTHROPIC_BASE_URL):null;return!(!n||!e.includes(n))||null!==s&&(d().some(e=>e.url===s)||s===u(m.getBaseUrl()))},planNotes(){const e=[],t=D();t&&e.push(s.t("tools.claude_code.plan_replace_provider",{url:t}));for(const t of f())e.push(s.t("tools.claude_code.plan_env_remove",{location:C(t.location),vars:Object.keys(t.vars).join(", ")}));return e},unloadPrivateConfig(e){const t=o.getSettings().env??{};Boolean(t.ANTHROPIC_AUTH_TOKEN||t.ANTHROPIC_BASE_URL)&&!claudeCodeAdapter.holdsOurConfig(e?.savedKeys??[])||(n(H),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=>(n(H),o.clearPinnedModels(e?.models)),listedModels(){const e=o.getSettings().modelPicker;return Array.isArray(e?.options)?e.options.map(e=>a(String(e.model))):null},refreshModelMenu(e){const s=o.getSettings(),a=t(s,M(e.catalog));JSON.stringify(a)!==JSON.stringify(s)&&(n(H),o.saveSettings(a))},async followUp(){const e=g().foreign;if(0===e.length||!F())return null;const t=e.map(e=>`${C(e.location)}(${Object.keys(e.vars).join(", ")})`).join("、");return{prompt:s.t("tools.claude_code.env_foreign_offer",{where:t}),hint:s.t("tools.claude_code.env_foreign",{location:e.map(e=>C(e.location)).join("、"),vars:[...new Set(e.flatMap(e=>Object.keys(e.vars)))].join(", ")}),async run(){const t=_(e),o=t.removed.map(e=>({kind:"summary",text:s.t("tools.claude_code.env_removed_foreign",{location:C(e.location),vars:Object.keys(e.vars).join(", ")})}));return t.machineLeft.length>0&&o.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})}),o},decline(){}}},applyNotes(){const e=[];U?.installed.length&&(e.push({kind:"summary",text:s.t("tools.claude_code.plugins_installed",{count:String(U.installed.length)})}),e.push({kind:"detail",text:U.installed.join(", ")})),"git"===U?.skipped?e.push({kind:"action",text:s.t("tools.claude_code.plugins_need_git")}):("marketplace"===U?.skipped||U?.failed.length)&&e.push({kind:"action",text:s.t("tools.claude_code.plugins_failed")}),R&&R.length>0&&(e.push({kind:"summary",text:s.t("tools.claude_code.gateway_models",{count:String(R.length),vendors:l(R.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:R.map(e=>e.name||i(e.id)).join(", ")})),B&&e.push({kind:"summary",text:s.t("tools.claude_code.provider_replaced",{url:B})});const t=g();for(const o of t.removed)e.push({kind:"summary",text:s.t("tools.claude_code.env_removed",{location:C(o.location),vars:Object.keys(o.vars).join(", ")})});for(const o of t.foreign){const t={location:C(o.location),vars:Object.keys(o.vars).join(", ")};e.push(F()?{kind:"detail",text:s.t("tools.claude_code.env_foreign_found",t)}:{kind:"action",text:s.t("tools.claude_code.env_foreign",t)})}t.machineLeft.length>0&&e.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})});const n=o.getLastReplaced();n.length>0&&e.push({kind:"summary",text:s.t("tools.claude_code.replaced_user_models",{values:n.join(", ")})}),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===j("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:s.t("tools.claude_code.no_git_bash")}),claudeAccountLoginPresent()&&e.push({kind:"detail",text:s.t("tools.claude_code.account_login_overridden")});for(const t of A())e.push({kind:"action",text:s.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:s.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[H,L],forceResetPaths:()=>[H]};
@@ -0,0 +1,62 @@
1
+ import type { GatewayModelSpec } from '../model-registry.js';
2
+ import type { ModelCatalog } from '../model-catalog.js';
3
+ /**
4
+ * Custom models for CodeBuddy Code (`~/.codebuddy/models.json`) and WorkBuddy
5
+ * (`~/.workbuddy/models.json`); both run the same agent engine and read the same file format:
6
+ * either `{ "models": [...], "availableModels": [...] }` or a bare array (WorkBuddy's settings page
7
+ * writes `[]`). Each entry names its own `url` and `apiKey`; the engine speaks OpenAI Chat
8
+ * Completions, or the Responses API with `api: "openai-responses"` (then `url` is the base).
9
+ *
10
+ * Our entries carry `vendor: BRAND_NAME`; the user's own entries and other keys stay. We never
11
+ * write `availableModels`: it would hide the user's own and the built-in models.
12
+ *
13
+ * Inside the engine a custom model is `custom-local:<id>`. A bare id that is also one of the
14
+ * vendor's built-in models (gpt-6-astra, glm-5.3, …) resolves to the built-in one, which needs a
15
+ * Tencent sign-in (checked on 2.179.3); so the default model is written with the prefix.
16
+ */
17
+ export declare const CUSTOM_MODEL_PREFIX = "custom-local:";
18
+ type Dict = Record<string, unknown>;
19
+ export declare const isOurEntry: (entry: unknown) => entry is Dict;
20
+ /** The file's model entries plus how to put them back (array file vs object file). */
21
+ interface ModelsFile {
22
+ entries: unknown[];
23
+ wrap(entries: unknown[]): unknown;
24
+ }
25
+ export declare function readModelsFile(path: string): ModelsFile;
26
+ export declare function writeModelsFile(path: string, file: ModelsFile, entries: unknown[]): void;
27
+ /** Strip Claude Code's `[1m]` window suffix: the gateway's model id has none. */
28
+ export declare const bareModelId: (id: string) => string;
29
+ /**
30
+ * Chat Completions unless the gateway says tool calls only work on the Responses API (GPT-6
31
+ * Astra / 6.1 Sol: `functionCallEndpoints: ["openai-response"]`) or the model is only served
32
+ * there. Without metadata (older gateway, built-in entry) GPT goes to Responses, like Codex.
33
+ */
34
+ export declare function usesResponses(spec: GatewayModelSpec): boolean;
35
+ /** The engine's entry for one model, capabilities from the gateway's metadata. */
36
+ /**
37
+ * `nameIsId`: WorkBuddy shows a custom model as `name:id` unless the name equals the id, so it
38
+ * gets the bare id (the CLI shows the name).
39
+ */
40
+ export declare function modelEntry(spec: GatewayModelSpec, baseUrl: string, apiKey: string, nameIsId?: boolean): Dict;
41
+ /**
42
+ * The models to write, kept short (the app lists every entry in one flat menu): the default
43
+ * model, the ones the gateway marks recommended, and each vendor's first model in the gateway's
44
+ * order (newest first) — all limited to what the key can reach (everything offline) and to
45
+ * models the gateway serves over an OpenAI-style interface. The default comes first, the rest
46
+ * keep the gateway's order; reordering or marking models in the gateway console changes the menu.
47
+ * When the key's owner chose models in the console (`personal`), every chosen model is written.
48
+ */
49
+ export declare function menuSpecs(catalog: ModelCatalog | undefined, picked: string): GatewayModelSpec[];
50
+ /**
51
+ * Replace our entries with `specs`; the user's own entries keep their place. A user entry with
52
+ * the same id as one of ours is replaced (its id now means our model); returned as `replaced`.
53
+ */
54
+ export declare function mergeEntries(existing: unknown[], specs: GatewayModelSpec[], baseUrl: string, apiKey: string, nameIsId?: boolean): {
55
+ entries: unknown[];
56
+ replaced: string[];
57
+ };
58
+ /** User entries a write would replace (same id as a model we add); for the write plan. */
59
+ export declare function foreignIdsReplaced(existing: unknown[], specs: GatewayModelSpec[]): string[];
60
+ export declare function ourEntries(entries: unknown[]): Dict[];
61
+ export declare function ourApiKey(entries: unknown[]): string | null;
62
+ export {};