@vibeapi/api-helper 0.0.111 → 0.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{refreshClientConfig as o}from"../lib/client-config-sync.js";import t from"chalk";import e from"ora";import{spawnSync as s}from"child_process";import{existsSync as r,readdirSync as n,readFileSync as a}from"fs";import{connect as i}from"net";import{join as c}from"path";import{configManager as l}from"../lib/config.js";import{toolManager as d}from"../lib/tool-manager.js";import{i18n as p}from"../lib/i18n.js";import{validateApiKey as u}from"../lib/api-validator.js";import{getAdapter as m}from"../lib/tools/registry.js";import{findInstalls as f,installLabel as g}from"../lib/tools/tool-installs.js";import{detectManagedOverrides as _}from"../lib/tools/claude-managed.js";import{codexAuthProblem as h}from"../lib/tools/codex.js";import{claudeAccountLoginPresent as y}from"../lib/tools/claude-code.js";import{toolHome as v}from"../lib/tools/types.js";import{gatewayAddresses as w,probeGatewayAddress as j}from"../lib/gateway-addresses.js";import{listAdapters as k}from"../lib/tools/registry.js";import{logger as b}from"../utils/logger.js";import{maskSecrets as $,uploadReport as x}from"../lib/telemetry.js";import{CLI_NAME as T}from"../lib/brand.js";const A=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function P(o,t,e=3e3){return new Promise(s=>{const r=i({host:o,port:t}),n=o=>{r.destroy(),s(o)};r.setTimeout(e,()=>n(!1)),r.once("connect",()=>n(!0)),r.once("error",()=>n(!1))})}function R(){const o=b.getLogDir();if(!r(o))return"";const t=n(o).filter(o=>/^\d{4}-\d{2}-\d{2}\.log$/.test(o)).sort().slice(-2).map(t=>`== ${t}\n`+a(c(o,t),"utf-8")).join("\n");return $(t.slice(-15e4))}function O(){const o={};for(const t of A){const e=process.env[t]?.trim();if(e)try{const s=new URL(e.includes("://")?e:`http://${e}`);o[t]=`${s.protocol}//${s.host}`}catch{o[t]="(unparsable)"}}return o}export async function doctorCommand(r={}){const n=e(p.t("doctor.checking")).start(),i=[],b=Number(process.versions.node.split(".")[0]);i.push({name:"Node.js",status:b>=20?"pass":"fail",message:`v${process.versions.node}`}),n.text=p.t("doctor.check_proxy"),i.push(await async function(){const o=p.t("doctor.proxy"),t=new Map;for(const o of A){const e=process.env[o]?.trim();e&&!t.has(e)&&t.set(e,o)}if(0===t.size)return{name:o,status:"pass",message:p.t("doctor.proxy_none")};const e=[];for(const[o,s]of t){let t;try{t=new URL(o.includes("://")?o:`http://${o}`)}catch{e.push(`${s}=${o}`);continue}const r=Number(t.port)||("https:"===t.protocol?443:t.protocol.startsWith("socks")?1080:80);await P(t.hostname,r)||e.push(`${s}=${o}`)}return 0===e.length?{name:o,status:"pass",message:[...t.keys()].join(", ")}:{name:o,status:"fail",message:p.t("doctor.proxy_dead",{proxies:e.join(", ")})}}()),n.text=p.t("doctor.check_api_key_network");const $=l.getApiKey(),N=p.t("doctor.api_key_network");if($){const o=await u($);o.valid?i.push({name:N,status:"pass"}):"invalid_api_key"===o.error?i.push({name:N,status:"fail",message:p.t("doctor.api_key_invalid")}):(i.push({name:N,status:"fail",message:`${p.t("doctor.network_error")}: ${o.message??o.error}`}),n.text=p.t("doctor.check_other_addresses"),i.push(await async function(){const o=p.t("doctor.other_addresses"),t=l.getBaseUrl(),e=w().filter(o=>o.url!==t),s=l.getApiKey(),r=await Promise.all(e.map(o=>j(o.url,{apiKey:s}))),n=e.filter((o,t)=>r[t].ok).map(o=>o.url);return 0===n.length?{name:o,status:"fail",message:p.t("doctor.other_addresses_none")}:{name:o,status:"warn",message:p.t("doctor.other_addresses_found",{urls:n.join(", ")})}}()))}else i.push({name:N,status:"fail",message:p.t("doctor.api_key_missing")});n.text=p.t("doctor.check_client_config");const H=await o();i.push({name:p.t("doctor.client_config"),status:"cloud"===H.source||"cache"===H.source&&!H.problem?"pass":"warn",message:[H.source?p.t(`refresh.source.${H.source}`):p.t("refresh.source.none"),H.revision?p.t("doctor.client_config_revision",{revision:H.revision}):"",H.problem??""].filter(Boolean).join(" · ")}),n.text=p.t("doctor.check_tools");const S=d.getInstalledTools();i.push({name:p.t("doctor.installed_tools"),status:S.length>0?"pass":"warn",message:S.length>0?S.map(o=>m(o)?.displayName??o).join(", "):p.t("doctor.no_tools_installed")}),i.push(...function(){const o=[];for(const t of d.getInstalledTools()){const e=m(t);if(!e?.installSpec)continue;const s=f(e.installSpec);if(0===s.length)continue;const r=[],n=d.outdatedVersion(t);n&&r.push(p.t("doctor.tool_outdated",{min:n.min})),s.length>1&&r.push(p.t("doctor.tool_duplicates",{others:s.slice(1).map(g).join("、")})),o.push({name:e.displayName,status:r.length>0?"warn":"pass",message:[g(s[0]),...r,...r.length>0?[p.t("doctor.run_upgrade",{tool:t})]:[]].join("\n ")})}return o}());const C=function(){if(!d.isToolInstalled("claude-code"))return null;const o=p.t("doctor.claude_config"),t=_();if(t.length>0){const e=t.map(o=>`${o.source}: ${o.keys.join(", ")}`).join("; ");return{name:o,status:"fail",message:p.t("doctor.claude_managed",{detail:e})}}let e={};try{e=JSON.parse(a(c(v(),".claude","settings.json"),"utf-8")).env??{}}catch{}const s=l.getApiKeyForTool("claude-code");return e.ANTHROPIC_AUTH_TOKEN&&e.ANTHROPIC_BASE_URL?e.ANTHROPIC_BASE_URL!==l.getBaseUrl()||s&&e.ANTHROPIC_AUTH_TOKEN!==s?{name:o,status:"warn",message:p.t("doctor.claude_config_changed")}:{name:o,status:"pass"}:{name:o,status:"warn",message:p.t("doctor.claude_not_configured")}}();C&&i.push(C),S.includes("claude-code")&&y()&&i.push({name:p.t("doctor.claude_login"),status:"pass",message:p.t("tools.claude_code.account_login_overridden")});const I=S.includes("codex")?h():null;S.includes("codex")&&i.push({name:p.t("doctor.codex_auth"),status:I?"fail":"pass",message:I?p.t(`doctor.codex_${I}`):void 0}),"win32"===process.platform&&S.includes("claude-code")&&!process.env.CLAUDE_CODE_GIT_BASH_PATH&&i.push({name:"Git for Windows",status:d.isGitInstalled()?"pass":"warn",message:d.isGitInstalled()?void 0:p.t("tools.claude_code.no_git_bash")}),r.deep&&S.includes("claude-code")&&(n.text=p.t("doctor.check_claude_request"),i.push(function(){const o=p.t("doctor.claude_request"),t=f(m("claude-code").installSpec)[0];if(!t)return{name:o,status:"warn",message:p.t("doctor.claude_request_skipped")};const e={encoding:"utf-8",timeout:12e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},r=["-p","Reply with the single word OK."],n=/\.cmd$/i.test(t.path)?s(`"${t.path}" ${r.map(o=>`"${o}"`).join(" ")}`,{...e,shell:!0}):s(t.path,r,e),a=`${n.stdout??""}`.trim();if(0===n.status&&a)return{name:o,status:"pass",message:a.slice(0,80)};const i=(`${n.stderr??""}`.trim()||a||n.error?.message||`exit ${n.status}`).slice(0,300);return{name:o,status:"fail",message:p.t("doctor.claude_request_failed",{reason:i})}}())),n.stop(),console.log("\n"+t.bold(p.t("doctor.results_title"))+"\n");const L={pass:t.green("√"),warn:t.yellow("!"),fail:t.red("×")};for(const o of i)console.log(`${L[o.status]} ${o.name}`),o.message&&console.log(t.gray(` ${o.message}`));if(i.every(o=>"pass"===o.status)&&console.log("\n"+t.green.bold(p.t("doctor.all_good"))),!r.deep&&S.includes("claude-code")&&console.log(t.gray("\n"+p.t("doctor.deep_hint"))),r.report){const o=e(p.t("doctor.report_uploading")).start(),t=await x(function(o,t){const e={};for(const o of k()){if(o.hidden)continue;let t=null;try{t=d.detectCurrentConfig(o.name).apiKey?"has_key":"no_key"}catch{t="unreadable"}e[o.name]={installed:d.isToolInstalled(o.name),version:d.installedVersion(o.name),copies:o.installSpec?f(o.installSpec).map(o=>({kind:o.kind,version:o.version,path:o.path})):void 0,config:t}}const s=l.getProfile();return{checks:o,tools:e,settings:{base_url:l.getBaseUrl(),profile:l.getActiveProfileName(),profiles:l.listProfiles().length,key_fingerprint:l.getApiKeyFingerprint()||null,tool_keys:Object.keys(s.tool_api_keys??{}),models:s.models,tool_models:s.tool_models??{},tool_settings:s.tool_settings??{},lang:l.getLang()},client_config:t,env:{shell:process.env.SHELL??process.env.ComSpec??null,term:process.env.TERM_PROGRAM??null,proxies:O()},log:R()}}(i,{source:H.source??void 0,revision:H.revision,problem:H.problem}));t?o.succeed(p.t("doctor.report_done",{code:t})):o.fail(p.t("doctor.report_failed",{cli:T}))}else i.some(o=>"fail"===o.status)&&console.log(t.gray("\n"+p.t("doctor.report_hint",{cli:T})));i.some(o=>"fail"===o.status)&&(process.exitCode=1)}
1
+ import{refreshClientConfig as o}from"../lib/client-config-sync.js";import t from"chalk";import e from"ora";import{spawnSync as s}from"child_process";import{existsSync as r,readdirSync as n,readFileSync as a}from"fs";import{connect as i}from"net";import{join as c}from"path";import{configManager as l}from"../lib/config.js";import{toolManager as d}from"../lib/tool-manager.js";import{i18n as p}from"../lib/i18n.js";import{validateApiKey as u}from"../lib/api-validator.js";import{getAdapter as m}from"../lib/tools/registry.js";import{findInstalls as g,installLabel as f}from"../lib/tools/tool-installs.js";import{detectManagedOverrides as _}from"../lib/tools/claude-managed.js";import{codexAuthProblem as h,codexCatalogRejection as y}from"../lib/tools/codex.js";import{claudeAccountLoginPresent as v}from"../lib/tools/claude-code.js";import{toolHome as j}from"../lib/tools/types.js";import{gatewayAddresses as w,probeGatewayAddress as b}from"../lib/gateway-addresses.js";import{listAdapters as k}from"../lib/tools/registry.js";import{logger as $}from"../utils/logger.js";import{maskSecrets as x,uploadReport as T}from"../lib/telemetry.js";import{CLI_NAME as A}from"../lib/brand.js";const P=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function R(o,t,e=3e3){return new Promise(s=>{const r=i({host:o,port:t}),n=o=>{r.destroy(),s(o)};r.setTimeout(e,()=>n(!1)),r.once("connect",()=>n(!0)),r.once("error",()=>n(!1))})}function O(){const o=$.getLogDir();if(!r(o))return"";const t=n(o).filter(o=>/^\d{4}-\d{2}-\d{2}\.log$/.test(o)).sort().slice(-2).map(t=>`== ${t}\n`+a(c(o,t),"utf-8")).join("\n");return x(t.slice(-15e4))}function N(){const o={};for(const t of P){const e=process.env[t]?.trim();if(e)try{const s=new URL(e.includes("://")?e:`http://${e}`);o[t]=`${s.protocol}//${s.host}`}catch{o[t]="(unparsable)"}}return o}export async function doctorCommand(r={}){const n=e(p.t("doctor.checking")).start(),i=[],$=Number(process.versions.node.split(".")[0]);i.push({name:"Node.js",status:$>=20?"pass":"fail",message:`v${process.versions.node}`}),n.text=p.t("doctor.check_proxy"),i.push(await async function(){const o=p.t("doctor.proxy"),t=new Map;for(const o of P){const e=process.env[o]?.trim();e&&!t.has(e)&&t.set(e,o)}if(0===t.size)return{name:o,status:"pass",message:p.t("doctor.proxy_none")};const e=[];for(const[o,s]of t){let t;try{t=new URL(o.includes("://")?o:`http://${o}`)}catch{e.push(`${s}=${o}`);continue}const r=Number(t.port)||("https:"===t.protocol?443:t.protocol.startsWith("socks")?1080:80);await R(t.hostname,r)||e.push(`${s}=${o}`)}return 0===e.length?{name:o,status:"pass",message:[...t.keys()].join(", ")}:{name:o,status:"fail",message:p.t("doctor.proxy_dead",{proxies:e.join(", ")})}}()),n.text=p.t("doctor.check_api_key_network");const x=l.getApiKey(),H=p.t("doctor.api_key_network");if(x){const o=await u(x);o.valid?i.push({name:H,status:"pass"}):"invalid_api_key"===o.error?i.push({name:H,status:"fail",message:p.t("doctor.api_key_invalid")}):(i.push({name:H,status:"fail",message:`${p.t("doctor.network_error")}: ${o.message??o.error}`}),n.text=p.t("doctor.check_other_addresses"),i.push(await async function(){const o=p.t("doctor.other_addresses"),t=l.getBaseUrl(),e=w().filter(o=>o.url!==t),s=l.getApiKey(),r=await Promise.all(e.map(o=>b(o.url,{apiKey:s}))),n=e.filter((o,t)=>r[t].ok).map(o=>o.url);return 0===n.length?{name:o,status:"fail",message:p.t("doctor.other_addresses_none")}:{name:o,status:"warn",message:p.t("doctor.other_addresses_found",{urls:n.join(", ")})}}()))}else i.push({name:H,status:"fail",message:p.t("doctor.api_key_missing")});n.text=p.t("doctor.check_client_config");const S=await o();i.push({name:p.t("doctor.client_config"),status:"cloud"===S.source||"cache"===S.source&&!S.problem?"pass":"warn",message:[S.source?p.t(`refresh.source.${S.source}`):p.t("refresh.source.none"),S.revision?p.t("doctor.client_config_revision",{revision:S.revision}):"",S.problem??""].filter(Boolean).join(" · ")}),n.text=p.t("doctor.check_tools");const C=d.getInstalledTools();i.push({name:p.t("doctor.installed_tools"),status:C.length>0?"pass":"warn",message:C.length>0?C.map(o=>m(o)?.displayName??o).join(", "):p.t("doctor.no_tools_installed")}),i.push(...function(){const o=[];for(const t of d.getInstalledTools()){const e=m(t);if(!e?.installSpec)continue;const s=g(e.installSpec);if(0===s.length)continue;const r=[],n=d.outdatedVersion(t);n&&r.push(p.t("doctor.tool_outdated",{min:n.min})),s.length>1&&r.push(p.t("doctor.tool_duplicates",{others:s.slice(1).map(f).join("、")})),o.push({name:e.displayName,status:r.length>0?"warn":"pass",message:[f(s[0]),...r,...r.length>0?[p.t("doctor.run_upgrade",{tool:t})]:[]].join("\n ")})}return o}());const I=function(){if(!d.isToolInstalled("claude-code"))return null;const o=p.t("doctor.claude_config"),t=_();if(t.length>0){const e=t.map(o=>`${o.source}: ${o.keys.join(", ")}`).join("; ");return{name:o,status:"fail",message:p.t("doctor.claude_managed",{detail:e})}}let e={};try{e=JSON.parse(a(c(j(),".claude","settings.json"),"utf-8")).env??{}}catch{}const s=l.getApiKeyForTool("claude-code");return e.ANTHROPIC_AUTH_TOKEN&&e.ANTHROPIC_BASE_URL?e.ANTHROPIC_BASE_URL!==l.getBaseUrl()||s&&e.ANTHROPIC_AUTH_TOKEN!==s?{name:o,status:"warn",message:p.t("doctor.claude_config_changed")}:{name:o,status:"pass"}:{name:o,status:"warn",message:p.t("doctor.claude_not_configured")}}();I&&i.push(I),C.includes("claude-code")&&v()&&i.push({name:p.t("doctor.claude_login"),status:"pass",message:p.t("tools.claude_code.account_login_overridden")});const L=C.includes("codex")?h():null;if(C.includes("codex")&&i.push({name:p.t("doctor.codex_auth"),status:L?"fail":"pass",message:L?p.t(`doctor.codex_${L}`):void 0}),C.includes("codex")){const o=y();i.push({name:p.t("doctor.codex_catalog"),status:o?"fail":"pass",message:o?p.t("doctor.codex_catalog_rejected",{path:o.bin,version:o.version??"?",error:o.error}):void 0})}"win32"===process.platform&&C.includes("claude-code")&&!process.env.CLAUDE_CODE_GIT_BASH_PATH&&i.push({name:"Git for Windows",status:d.isGitInstalled()?"pass":"warn",message:d.isGitInstalled()?void 0:p.t("tools.claude_code.no_git_bash")}),r.deep&&C.includes("claude-code")&&(n.text=p.t("doctor.check_claude_request"),i.push(function(){const o=p.t("doctor.claude_request"),t=g(m("claude-code").installSpec)[0];if(!t)return{name:o,status:"warn",message:p.t("doctor.claude_request_skipped")};const e={encoding:"utf-8",timeout:12e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},r=["-p","Reply with the single word OK."],n=/\.cmd$/i.test(t.path)?s(`"${t.path}" ${r.map(o=>`"${o}"`).join(" ")}`,{...e,shell:!0}):s(t.path,r,e),a=`${n.stdout??""}`.trim();if(0===n.status&&a)return{name:o,status:"pass",message:a.slice(0,80)};const i=(`${n.stderr??""}`.trim()||a||n.error?.message||`exit ${n.status}`).slice(0,300);return{name:o,status:"fail",message:p.t("doctor.claude_request_failed",{reason:i})}}())),n.stop(),console.log("\n"+t.bold(p.t("doctor.results_title"))+"\n");const U={pass:t.green("√"),warn:t.yellow("!"),fail:t.red("×")};for(const o of i)console.log(`${U[o.status]} ${o.name}`),o.message&&console.log(t.gray(` ${o.message}`));if(i.every(o=>"pass"===o.status)&&console.log("\n"+t.green.bold(p.t("doctor.all_good"))),!r.deep&&C.includes("claude-code")&&console.log(t.gray("\n"+p.t("doctor.deep_hint"))),r.report){const o=e(p.t("doctor.report_uploading")).start(),t=await T(function(o,t){const e={};for(const o of k()){if(o.hidden)continue;let t=null;try{t=d.detectCurrentConfig(o.name).apiKey?"has_key":"no_key"}catch{t="unreadable"}e[o.name]={installed:d.isToolInstalled(o.name),version:d.installedVersion(o.name),copies:o.installSpec?g(o.installSpec).map(o=>({kind:o.kind,version:o.version,path:o.path})):void 0,config:t}}const s=l.getProfile();return{checks:o,tools:e,settings:{base_url:l.getBaseUrl(),profile:l.getActiveProfileName(),profiles:l.listProfiles().length,key_fingerprint:l.getApiKeyFingerprint()||null,tool_keys:Object.keys(s.tool_api_keys??{}),models:s.models,tool_models:s.tool_models??{},tool_settings:s.tool_settings??{},lang:l.getLang()},client_config:t,env:{shell:process.env.SHELL??process.env.ComSpec??null,term:process.env.TERM_PROGRAM??null,proxies:N()},log:O()}}(i,{source:S.source??void 0,revision:S.revision,problem:S.problem}));t?o.succeed(p.t("doctor.report_done",{code:t})):o.fail(p.t("doctor.report_failed",{cli:A}))}else i.some(o=>"fail"===o.status)&&console.log(t.gray("\n"+p.t("doctor.report_hint",{cli:A})));i.some(o=>"fail"===o.status)&&(process.exitCode=1)}
@@ -3,7 +3,28 @@ type Dict = Record<string, unknown>;
3
3
  export declare const CODEX_CATALOG_FILE: string;
4
4
  export declare const codexCatalogPath: () => string;
5
5
  /** The catalog built into the installed Codex binary; null when no binary answers. */
6
- export declare function loadBundledCodexModels(): Dict[] | null;
6
+ export declare function loadBundledCodexModels(bin?: string): Dict[] | null;
7
+ /**
8
+ * Every Codex executable on this machine, one per real file: each one on PATH and
9
+ * the known install spots, including the copy the desktop app ships — often an
10
+ * older release than the CLI, and the one that reads the catalog when the user
11
+ * opens the app.
12
+ */
13
+ export declare function installedCodexBinaries(): string[];
14
+ export declare function codexBinaryVersion(bin: string): string | null;
15
+ export interface CatalogRejection {
16
+ bin: string;
17
+ version: string | null;
18
+ error: string;
19
+ }
20
+ /**
21
+ * Has every Codex on this machine load `catalogPath` the way it will at startup
22
+ * (`codex debug models`, read-only), in a scratch CODEX_HOME holding only the
23
+ * catalog setting so the user's own config, login and network play no part.
24
+ * Returns the ones that reject the catalog itself; a Codex failing for another
25
+ * reason (broken install, its own policy files) is logged, not counted.
26
+ */
27
+ export declare function catalogRejections(catalogPath: string, bins?: string[]): CatalogRejection[];
7
28
  /**
8
29
  * Codex's own entries from the catalog vibecli wrote last time — the fallback
9
30
  * when no Codex binary can be run (e.g. it moved), so GPT models stay listed.
@@ -28,6 +49,12 @@ export interface BuildCodexCatalogOptions {
28
49
  */
29
50
  claudeOneMillion?: string[];
30
51
  }
52
+ /**
53
+ * Catalog entries with every field an older Codex's own entries carry and they
54
+ * lack — what that release requires but newer ones dropped. Values come from the
55
+ * older entry for the same model, else the first older entry that has the field.
56
+ */
57
+ export declare function fillFromOlderEntries(entries: Dict[], older: Dict[]): Dict[];
31
58
  export declare function buildCodexCatalog(options: BuildCodexCatalogOptions): {
32
59
  models: Dict[];
33
60
  };
@@ -1 +1 @@
1
- import{spawnSync as t}from"child_process";import{existsSync as e,readFileSync as o}from"fs";import{dirname as n,join as i}from"path";import{fileURLToPath as s}from"url";import{logger as r}from"../../utils/logger.js";import{getCodexRules as l,compareVersions as a}from"../model-registry.js";import{autoCompactTokens as u,modelBlurb as c,prettyModelName as d}from"../gateway-models.js";import{toolHome as p}from"./types.js";import{brandEnv as _,PROVIDER_KEY as m}from"../brand.js";const f=n(s(import.meta.url));export const CODEX_CATALOG_FILE=`${m}-model-catalog.json`;export const codexCatalogPath=()=>i(p(),".codex",CODEX_CATALOG_FILE);function x(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function g(t){try{const e=JSON.parse(t),o=x(e)&&Array.isArray(e.models)?e.models.filter(x):[];return o.length>0?o:null}catch{return null}}export function loadBundledCodexModels(){for(const o of function(){const t=_("CODEX_BIN");if(void 0!==t)return t.trim()?[t.trim()]:[];const e=["codex"];if("win32"===process.platform){const t=process.env.LOCALAPPDATA||i(p(),"AppData","Local");e.push(i(t,"Programs","OpenAI","Codex","bin","codex.exe"))}else e.push(i(p(),".local","bin","codex"));if("darwin"===process.platform)for(const t of["/Applications",i(p(),"Applications")])e.push(i(t,"ChatGPT.app/Contents/Resources/codex-cli/bin/codex")),e.push(i(t,"Codex.app/Contents/Resources/codex"));return e}()){if("codex"!==o&&!e(o))continue;const n={encoding:"utf-8",timeout:2e4,maxBuffer:67108864,windowsHide:!0,stdio:["ignore","pipe","pipe"]},i="win32"===process.platform&&"codex"===o?t("codex debug models --bundled",{...n,shell:!0}):t(o,["debug","models","--bundled"],n);if(0===i.status&&i.stdout){const t=g(i.stdout);if(t)return t}(i.error||0!==i.status)&&r.logError("loadBundledCodexModels",i.error??new Error(`${o} exited ${i.status}`))}return null}export function loadPreviousCodexModels(){const t=w()?.filter(t=>!h(t));return t&&t.length>0?t:null}export function previousGatewayModelIds(){return(w()??[]).filter(h).map(t=>String(t.slug))}function w(){return e(codexCatalogPath())?g(o(codexCatalogPath(),"utf-8")):null}function h(t){return"number"==typeof t.priority&&t.priority>=1e3}let y=null;function b(){if(null!==y)return y;const t=[i(f,"..","..","..","codex-base-instructions.md"),i(f,"..","..","codex-base-instructions.md"),i(f,"codex-base-instructions.md")],n=t.find(t=>e(t));if(!n)throw new Error(`codex-base-instructions.md not found, searched: ${t.join(", ")}`);return y=o(n,"utf-8"),y}function C(t){return"number"==typeof t&&Number.isFinite(t)&&t>0?t:void 0}function v(t,e,o,n){const i=o?t:Math.min(t,e);return{window:i,compact:u(i,n)}}function A(t,e,o,n){const{standardContextWindow:i}=l();let s=C(t.contextWindow)??i;n&&t.id.startsWith("claude-")&&(s=n.has(t.id)?Math.max(s,1e6):Math.min(s,2e5));const{window:r,compact:a}=v(s,i,o,C(t.autoCompactTokens));return{slug:t.id,display_name:t.name||d(t.id),description:c(t.description,{millionContext:r>=1e6})??null,supported_reasoning_levels:[],shell_type:"unified_exec",visibility:"list",supported_in_api:!0,priority:e,additional_speed_tiers:[],service_tiers:[],availability_nux:null,upgrade:null,model_messages:{instructions_template:b()},base_instructions:b(),supports_reasoning_summaries:!0,supports_parallel_tool_calls:!1,prefer_websockets:!1,include_skills_usage_instructions:!1,include_plugin_usage_instructions:!1,include_apps_usage_instructions:!1,default_reasoning_summary:"auto",support_verbosity:!1,default_verbosity:null,apply_patch_tool_type:null,web_search_tool_type:"text",truncation_policy:{mode:"bytes",limit:1e4},supports_image_detail_original:!1,context_window:r,max_context_window:r,auto_compact_token_limit:a,effective_context_window_percent:95,experimental_supported_tools:[],input_modalities:["text","image"],supports_search_tool:!1,supports_experimental_context:!1,use_responses_lite:!1,supports_reasoning_effort_updates:!1,node_repl_auto_review_required:!1,node_repl_disabled:!1}}const M={supports_parallel_tool_calls:!0,supports_reasoning_summaries:!0};function O(t,e,o){if("list"!==(t=function(t){const e=Object.keys(M).filter(e=>!(e in t));return 0===e.length?t:{...t,...Object.fromEntries(e.map(t=>[t,M[t]]))}}(t)).visibility)return t;const n=C(t.context_window);if(!n)return t;const i=C(t.max_context_window)??n,s=o?Math.min(C(e?.contextWindow)??i,i):n,{window:r,compact:l}=v(s,n,!0,C(e?.autoCompactTokens));return{...t,context_window:r,auto_compact_token_limit:l}}export function buildCodexCatalog(t){const{bundled:e,specs:o,oneMillion:n,selectedModel:i}=t,s=t.claudeOneMillion?new Set(t.claudeOneMillion):void 0,r=t.keyModels?new Set(t.keyModels):null,l=new Set(t.gatewayAvailable),u=t=>!r||r.has(t)||t===i,c=t=>l.has(t)||t===i,d=new Map(o.map(t=>[t.id,t])),p=[],_=new Set;for(const t of e){const e="string"==typeof t.slug?t.slug:"";e&&!_.has(e)&&("list"!==t.visibility||u(e))&&(_.add(e),p.push(O(t,d.get(e),n)))}const m=[...o];i&&!d.has(i)&&m.push({id:i});const f=function(t){const e=new Map;for(const o of t){const t=/^claude-([a-z]+)-(.+)$/.exec(o);if(!t)continue;const n=t[2].split("-").filter(t=>/^\d{1,2}$/.test(t)).join("."),i=e.get(t[1]);(!i||a(n,i.version)>0)&&e.set(t[1],{id:o,version:n})}return new Set([...e.values()].map(t=>t.id))}(m.map(t=>t.id).filter(c));for(const t of m)!_.has(t.id)&&c(t.id)&&(t.id.startsWith("claude-")&&!f.has(t.id)&&t.id!==i||(_.add(t.id),p.push(A(t,1e3+p.length,n,s))));return{models:p}}
1
+ import{spawnSync as t}from"child_process";import{existsSync as o,mkdtempSync as e,readFileSync as n,realpathSync as s,rmSync as r,statSync as i,writeFileSync as c}from"fs";import{tmpdir as l}from"os";import{delimiter as a,dirname as u,isAbsolute as d,join as p}from"path";import*as f from"@iarna/toml";import{fileURLToPath as m}from"url";import{logger as _}from"../../utils/logger.js";import{getCodexRules as x,compareVersions as g}from"../model-registry.js";import{autoCompactTokens as w,modelBlurb as h,prettyModelName as y}from"../gateway-models.js";import{toolHome as b}from"./types.js";import{brandEnv as v,PROVIDER_KEY as C}from"../brand.js";const M=u(m(import.meta.url));export const CODEX_CATALOG_FILE=`${C}-model-catalog.json`;export const codexCatalogPath=()=>p(b(),".codex",CODEX_CATALOG_FILE);function A(){const t=v("CODEX_BIN");if(void 0!==t)return t.trim()?[t.trim()]:[];const o=["codex"];if("win32"===process.platform){const t=process.env.LOCALAPPDATA||p(b(),"AppData","Local");o.push(p(t,"Programs","OpenAI","Codex","bin","codex.exe"))}else o.push(p(b(),".local","bin","codex"));if("darwin"===process.platform)for(const t of["/Applications",p(b(),"Applications")])o.push(p(t,"ChatGPT.app/Contents/Resources/codex-cli/bin/codex")),o.push(p(t,"Codex.app/Contents/Resources/codex"));return o}function j(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function O(t){try{const o=JSON.parse(t),e=j(o)&&Array.isArray(o.models)?o.models.filter(j):[];return e.length>0?e:null}catch{return null}}function E(o,e,n){const s={encoding:"utf-8",timeout:2e4,maxBuffer:67108864,windowsHide:!0,stdio:["ignore","pipe","pipe"],...n?{env:n}:{}};return"win32"!==process.platform||"codex"!==o&&!/\.cmd$/i.test(o)?t(o,e,s):t(["codex"===o?"codex":`"${o}"`,...e].join(" "),{...s,shell:!0})}function $(t){const o=E(t,["debug","models","--bundled"]);if(0===o.status&&o.stdout){const t=O(o.stdout);if(t)return t}return(o.error||0!==o.status)&&_.logError("loadBundledCodexModels",o.error??new Error(`${t} exited ${o.status}`)),null}export function loadBundledCodexModels(t){if(t)return $(t);for(const t of A()){if("codex"!==t&&!o(t))continue;const e=$(t);if(e)return e}return null}function k(t){try{return i(t).isFile()}catch{return!1}}export function installedCodexBinaries(){const t=[],o=A();if(o.includes("codex")){const o="win32"===process.platform?["codex.exe","codex.cmd"]:["codex"];for(const e of(process.env.PATH??"").split(a))if(e&&d(e))for(const n of o)t.push(p(e,n))}t.push(...o.filter(t=>"codex"!==t));const e=new Set,n=[];for(const o of t){if(!k(o))continue;let t=o;try{t=s(o)}catch{}const r="win32"===process.platform?t.toLowerCase():t;e.has(r)||(e.add(r),n.push(o))}return n}export function codexBinaryVersion(t){const o=`${E(t,["--version"]).stdout??""}`.match(/(\d+)\.(\d+)\.(\d+)/);return o?o[0]:null}export function catalogRejections(t,o=installedCodexBinaries()){if(0===o.length)return[];const n=e(p(l(),"vibecli-codex-")),s=[];try{c(p(n,"config.toml"),f.stringify({model_catalog_json:t}));for(const t of o){const o=E(t,["debug","models"],{...process.env,CODEX_HOME:n});if(0===o.status)continue;const e=`${o.stderr??""}\n${o.stdout??""}`,r=e.split(/\r?\n/).find(t=>t.includes("model_catalog_json"));r?s.push({bin:t,version:codexBinaryVersion(t),error:r.trim()}):_.logError("catalogRejections",o.error??new Error(`${t} exited ${o.status}: ${e.trim().slice(0,500)}`))}}finally{r(n,{recursive:!0,force:!0})}return s}export function loadPreviousCodexModels(){const t=P()?.filter(t=>!B(t));return t&&t.length>0?t:null}export function previousGatewayModelIds(){return(P()??[]).filter(B).map(t=>String(t.slug))}function P(){return o(codexCatalogPath())?O(n(codexCatalogPath(),"utf-8")):null}function B(t){return"number"==typeof t.priority&&t.priority>=1e3}let L=null;function S(){if(null!==L)return L;const t=[p(M,"..","..","..","codex-base-instructions.md"),p(M,"..","..","codex-base-instructions.md"),p(M,"codex-base-instructions.md")],e=t.find(t=>o(t));if(!e)throw new Error(`codex-base-instructions.md not found, searched: ${t.join(", ")}`);return L=n(e,"utf-8"),L}function T(t){return"number"==typeof t&&Number.isFinite(t)&&t>0?t:void 0}function D(t,o,e,n){const s=e?t:Math.min(t,o);return{window:s,compact:w(s,n)}}function F(t,o,e,n){const{standardContextWindow:s}=x();let r=T(t.contextWindow)??s;n&&t.id.startsWith("claude-")&&(r=n.has(t.id)?Math.max(r,1e6):Math.min(r,2e5));const{window:i,compact:c}=D(r,s,e,T(t.autoCompactTokens));return{slug:t.id,display_name:t.name||y(t.id),description:h(t.description,{millionContext:i>=1e6})??null,supported_reasoning_levels:[],shell_type:"unified_exec",visibility:"list",supported_in_api:!0,priority:o,additional_speed_tiers:[],service_tiers:[],availability_nux:null,upgrade:null,model_messages:{instructions_template:S()},base_instructions:S(),supports_reasoning_summaries:!0,supports_parallel_tool_calls:!1,prefer_websockets:!1,include_skills_usage_instructions:!1,include_plugin_usage_instructions:!1,include_apps_usage_instructions:!1,default_reasoning_summary:"auto",support_verbosity:!1,default_verbosity:null,apply_patch_tool_type:null,web_search_tool_type:"text",truncation_policy:{mode:"bytes",limit:1e4},supports_image_detail_original:!1,context_window:i,max_context_window:i,auto_compact_token_limit:c,effective_context_window_percent:95,experimental_supported_tools:[],input_modalities:["text","image"],supports_search_tool:!1,supports_experimental_context:!1,use_responses_lite:!1,supports_reasoning_effort_updates:!1,node_repl_auto_review_required:!1,node_repl_disabled:!1}}const I={supports_parallel_tool_calls:!0,supports_reasoning_summaries:!0};export function fillFromOlderEntries(t,o){const e=new Map(o.map(t=>[t.slug,t])),n=new Map;for(const t of o)for(const[o,e]of Object.entries(t))n.has(o)||n.set(o,e);return t.map(t=>{const o=e.get(t.slug),s={...t};for(const[t,e]of n)t in s||(s[t]=o&&t in o?o[t]:e);return s})}function W(t,o,e){if("list"!==(t=function(t){const o=Object.keys(I).filter(o=>!(o in t));return 0===o.length?t:{...t,...Object.fromEntries(o.map(t=>[t,I[t]]))}}(t)).visibility)return t;const n=T(t.context_window);if(!n)return t;const s=T(t.max_context_window)??n,r=e?Math.min(T(o?.contextWindow)??s,s):n,{window:i,compact:c}=D(r,n,!0,T(o?.autoCompactTokens));return{...t,context_window:i,auto_compact_token_limit:c}}export function buildCodexCatalog(t){const{bundled:o,specs:e,oneMillion:n,selectedModel:s}=t,r=t.claudeOneMillion?new Set(t.claudeOneMillion):void 0,i=t.keyModels?new Set(t.keyModels):null,c=new Set(t.gatewayAvailable),l=t=>!i||i.has(t)||t===s,a=t=>c.has(t)||t===s,u=new Map(e.map(t=>[t.id,t])),d=[],p=new Set;for(const t of o){const o="string"==typeof t.slug?t.slug:"";o&&!p.has(o)&&("list"!==t.visibility||l(o))&&(p.add(o),d.push(W(t,u.get(o),n)))}const f=[...e];s&&!u.has(s)&&f.push({id:s});const m=function(t){const o=new Map;for(const e of t){const t=/^claude-([a-z]+)-(.+)$/.exec(e);if(!t)continue;const n=t[2].split("-").filter(t=>/^\d{1,2}$/.test(t)).join("."),s=o.get(t[1]);(!s||g(n,s.version)>0)&&o.set(t[1],{id:e,version:n})}return new Set([...o.values()].map(t=>t.id))}(f.map(t=>t.id).filter(a));for(const t of f)!p.has(t.id)&&a(t.id)&&(t.id.startsWith("claude-")&&!m.has(t.id)&&t.id!==s||(p.add(t.id),d.push(F(t,1e3+d.length,n,r))));return{models:d}}
@@ -1,6 +1,9 @@
1
1
  import { type ToolAdapter } from './types.js';
2
2
  import type { ModelCatalog } from '../model-catalog.js';
3
+ import { type CatalogRejection } from './codex-catalog.js';
3
4
  export declare const CODEX_ONE_MILLION_CONTEXT_SETTING = "one_million_context";
5
+ /** For doctor: a local Codex that cannot load the catalog `config.toml` points at (checked afresh, no cache). */
6
+ export declare function codexCatalogRejection(): CatalogRejection | null;
4
7
  /**
5
8
  * Re-apply only the context limits (model catalog + context keys) for the 1M
6
9
  * switch. Credentials, provider and model selection are untouched; files are only
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as t,rmSync as o}from"fs";import{join as n}from"path";import*as r from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as l}from"../i18n.js";import{backupBeforeWrite as a,atomicWriteFile as s}from"./backup.js";import{pickModel as d,toolHome as c,withV1 as m}from"./types.js";import{getToolDefaultModel as u}from"../model-registry.js";import{buildCodexCatalog as p,codexCatalogPath as _,loadBundledCodexModels as g,loadPreviousCodexModels as f,previousGatewayModelIds as x}from"./codex-catalog.js";import{gatewayModelSpecs as y,modelFamily as h}from"../gateway-models.js";import{mirrorInstallCommand as k,brandInstallScripts as b}from"./tool-installs.js";const j=()=>n(c(),".codex"),w=()=>n(j(),"config.toml"),O=()=>n(j(),"auth.json"),v="OPENAI_API_KEY",C="custom",M=1e6,E=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function N(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function S(e,t){return N(e[t])||(e[t]={}),e[t]}function A(){if(!e(w()))return{};const o=t(w(),"utf-8");if(!o.trim())return{};try{const e=r.parse(o);if(!N(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function P(){if(!e(O()))return{};try{const e=JSON.parse(t(O(),"utf-8"));return N(e)?e:{}}catch{return{}}}let T=null;function I(e){const t=e.profiles;if(N(t)){for(const[e,o]of Object.entries(t))N(o)&&2===Object.keys(o).length&&o.model_provider===C&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let L=null;function D(o,n,r){const l=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===M&&delete e.model_context_window,e.model_auto_compact_token_limit===E&&delete e.model_auto_compact_token_limit}(o);const d=g()??f();if(!d)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),L="skipped",o.model_catalog_json===_()&&delete o.model_catalog_json,void(l&&(o.model_context_window=M,o.model_auto_compact_token_limit=E));const c=n.catalog,m=p({bundled:d,specs:y("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??x(),oneMillion:l,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});L={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const u=JSON.stringify(m,null,2)+"\n",h=_();e(h)&&t(h,"utf-8")===u||(a(h),s(h,u)),o.model_catalog_json=h}export function setCodexOneMillionContext(e,t){const o=A(),n=r.stringify(o),i="string"==typeof o.model?o.model:u("codex");D(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},i);const l=r.stringify(o);l!==n&&(a(w()),s(w(),l))}const J={command:"codex",npmPackage:"@openai/codex",native:{binDir:"win32"===process.platform?["AppData","Local","Programs","OpenAI","Codex","bin"]:[".local","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".codex","packages","standalone"]]},mirrorScript:b("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:k(J),installSpec:J,configPath:w(),wantsModelCatalog:!0,listedModels(){if(A().model_catalog_json!==_())return null;try{return(JSON.parse(t(_(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){A().model_catalog_json===_()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===L)return[{kind:"action",text:l.t("tools.codex.catalog_skipped")}];if(!L)return[];const e=new Map;for(const t of L.listed)e.set(h(t.id),(e.get(h(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${l.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:l.t("tools.codex.catalog_written",{count:String(L.listed.length),breakdown:t})},{kind:"detail",text:L.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const t=u("codex"),o=d(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,i=m(e.baseUrl),l=A();l.model=n,l.model_provider=C,l.model_reasoning_effort="high",D(l,e,n);const c=S(l,"model_providers"),p=N(c[C])?{...c[C]}:{};delete p.env_key;const{experimental_bearer_token:_,...g}=p;c[C]={...g,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:i},I(l),S(S(l,"tui"),"model_availability_nux")[n]=4;const f=S(l,"features");f.goals=!0,f.js_repl=!1,a(w()),s(w(),r.stringify(l)),function(e){const t=e.trim();if(!t)return;const o={...P(),auth_mode:"apikey",[v]:t};a(O()),s(O(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(t){if(T=function(e){const t=P(),n="string"==typeof t[v]?t[v].trim():"";if(!n)return N(t.tokens)?"chatgpt":null;if(!e.includes(n))return"other_key";const{[v]:r,auth_mode:i,...l}=t;return a(O()),N(l.tokens)?(s(O(),JSON.stringify({...l,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(o(O(),{force:!0}),"signed_out")}(t?.savedKeys??[]),!e(w()))return;let n;try{n=A()}catch(e){return void i.logError("codexAdapter.unloadPrivateConfig",e)}const l=n.model_providers;N(l)&&(delete l[C],0===Object.keys(l).length&&delete n.model_providers),n.model_provider===C&&delete n.model_provider,n.model_catalog_json===_()&&delete n.model_catalog_json,I(n),a(w()),s(w(),r.stringify(n))},unloadNotes(){switch(T){case"chatgpt":return[{kind:"action",text:l.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:l.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:l.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=P()[v];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return A()}catch(e){return i.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[w(),O(),_()]};export function codexAuthProblem(){try{const e=A(),t=e.model_providers?.[C];if(e.model_provider!==C||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=P();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
1
+ import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readFileSync as n,rmSync as r,statSync as i,writeFileSync as l}from"fs";import{join as a}from"path";import*as s from"@iarna/toml";import{logger as c}from"../../utils/logger.js";import{i18n as d}from"../i18n.js";import{backupBeforeWrite as u,atomicWriteFile as m}from"./backup.js";import{pickModel as f,toolHome as p,withV1 as g}from"./types.js";import{getToolDefaultModel as _}from"../model-registry.js";import{configManager as x}from"../config.js";import{track as y}from"../telemetry.js";import{buildCodexCatalog as h,catalogRejections as k,codexCatalogPath as b,fillFromOlderEntries as j,installedCodexBinaries as v,loadBundledCodexModels as w,loadPreviousCodexModels as O,previousGatewayModelIds as C}from"./codex-catalog.js";import{gatewayModelSpecs as M,modelFamily as E}from"../gateway-models.js";import{mirrorInstallCommand as N,brandInstallScripts as S}from"./tool-installs.js";const A=()=>a(p(),".codex"),P=()=>a(A(),"config.toml"),T=()=>a(A(),"auth.json"),D="OPENAI_API_KEY",I="custom",$=1e6,J=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function L(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function K(e,t){return L(e[t])||(e[t]={}),e[t]}function X(){if(!t(P()))return{};const e=n(P(),"utf-8");if(!e.trim())return{};try{const t=s.parse(e);if(!L(t))throw new Error("config.toml root must be a TOML table");return t}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function B(){if(!t(T()))return{};try{const e=JSON.parse(n(T(),"utf-8"));return L(e)?e:{}}catch{return{}}}let q=null;function G(e){const t=e.profiles;if(L(t)){for(const[e,o]of Object.entries(t))L(o)&&2===Object.keys(o).length&&o.model_provider===I&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let z=null;const R=()=>a(x.getConfigDir(),"codex-catalog-check.json");function U(e){const o=b();t(o)&&n(o,"utf-8")===e||(u(o),m(o,e))}function Y(t,r){if(0===r.length)return[];const a=function(t,o){const n=e("sha256").update(t);for(const e of o)try{const t=i(e);n.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{n.update(`\0${e}`)}return n.digest("hex")}(t,r);if(function(e){try{return JSON.parse(n(R(),"utf-8")).signature===e}catch{return!1}}(a))return[];const s=k(b(),r);return 0===s.length&&function(e){try{o(x.getConfigDir(),{recursive:!0}),l(R(),JSON.stringify({signature:e})+"\n")}catch(e){c.logError("codexAdapter.rememberAccepted",e)}}(a),s}function F(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===$&&delete e.model_context_window,e.model_auto_compact_token_limit===J&&delete e.model_auto_compact_token_limit}(e);const i=()=>{e.model_catalog_json===b()&&delete e.model_catalog_json,n&&(e.model_context_window=$,e.model_auto_compact_token_limit=J)},l=w()??O();if(!l)return c.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),z="skipped",void i();const a=t.catalog,s=e=>h({bundled:e,specs:M("codex",a?.clientModels),...a?{keyModels:a.models}:{},gatewayAvailable:a?.models??C(),oneMillion:n,selectedModel:o,..."gateway"===a?.oneMillionSource?{claudeOneMillion:a.oneMillion}:{}}),d=e=>JSON.stringify(e,null,2)+"\n";let u=s(l);U(d(u));const m=v(),f=Y(d(u),m);if(f.length>0){const e=Date.now();let t=!1;for(const e of f){const o=w(e.bin);if(o){for(const e of[{models:j(u.models,o)},{models:j(s(o).models,o)}])if(U(d(e)),0===Y(d(e),m).length){u=e,t=!0;break}if(t)break}}if(y("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:f[0].error,detail:{rejected_by:f.map(e=>({version:e.version,path:e.bin}))}}),!t)return z={rejectedBy:f[0]},r(b(),{force:!0}),void i()}z={listed:u.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))},e.model_catalog_json=b()}export function codexCatalogRejection(){let e;try{e=X()}catch{return null}return e.model_catalog_json===b()&&t(b())?k(b())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=X(),n=s.stringify(o),r="string"==typeof o.model?o.model:_("codex");F(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=s.stringify(o);i!==n&&(u(P()),m(P(),i))}const H={command:"codex",npmPackage:"@openai/codex",native:{binDir:"win32"===process.platform?["AppData","Local","Programs","OpenAI","Codex","bin"]:[".local","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".codex","packages","standalone"]]},mirrorScript:S("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:N(H),installSpec:H,configPath:P(),wantsModelCatalog:!0,listedModels(){if(X().model_catalog_json!==b())return null;try{return(JSON.parse(n(b(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){X().model_catalog_json===b()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===z)return[{kind:"action",text:d.t("tools.codex.catalog_skipped")}];if(!z)return[];if("rejectedBy"in z){const{bin:e,version:t}=z.rejectedBy;return[{kind:"action",text:d.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of z.listed)e.set(E(t.id),(e.get(E(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${d.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:d.t("tools.codex.catalog_written",{count:String(z.listed.length),breakdown:t})},{kind:"detail",text:z.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const t=_("codex"),o=f(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=g(e.baseUrl),i=X();i.model=n,i.model_provider=I,i.model_reasoning_effort="high",F(i,e,n);const l=K(i,"model_providers"),a=L(l[I])?{...l[I]}:{};delete a.env_key;const{experimental_bearer_token:c,...d}=a;l[I]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},G(i),K(K(i,"tui"),"model_availability_nux")[n]=4;const p=K(i,"features");p.goals=!0,p.js_repl=!1,u(P()),m(P(),s.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...B(),auth_mode:"apikey",[D]:t};u(T()),m(T(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(e){if(q=function(e){const t=B(),o="string"==typeof t[D]?t[D].trim():"";if(!o)return L(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[D]:n,auth_mode:i,...l}=t;return u(T()),L(l.tokens)?(m(T(),JSON.stringify({...l,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(r(T(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(P()))return;let o;try{o=X()}catch(e){return void c.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;L(n)&&(delete n[I],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===I&&delete o.model_provider,o.model_catalog_json===b()&&delete o.model_catalog_json,G(o),u(P()),m(P(),s.stringify(o))},unloadNotes(){switch(q){case"chatgpt":return[{kind:"action",text:d.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:d.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:d.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=B()[D];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return X()}catch(e){return c.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[P(),T(),b()]};export function codexAuthProblem(){try{const e=X(),t=e.model_providers?.[I];if(e.model_provider!==I||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=B();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -202,6 +202,8 @@
202
202
  "claude_request_failed": "Request failed: {{reason}}",
203
203
  "deep_hint": "Add --deep to send one real request through Claude Code (uses a little credit) and confirm the whole chain works",
204
204
  "codex_auth": "Codex token",
205
+ "codex_catalog": "Codex model menu",
206
+ "codex_catalog_rejected": "Codex ({{version}}, {{path}}) cannot load the model menu: {{error}}. Run {{cli}} refresh to rewrite it; if it still fails, update that Codex (the desktop app's copy updates with the ChatGPT app)",
205
207
  "codex_stale_bearer": "~/.codex/config.toml has experimental_bearer_token under [model_providers.custom] (usually PROXY_MANAGED left by cc-switch's proxy mode); Codex sends it as the token and gets 401 Invalid token. {{cli}} setup --key <your key> removes it",
206
208
  "codex_chatgpt_login": "Codex is signed in with a ChatGPT account and sends that credential to {{brand}} (401 Invalid token); run {{cli}} setup --key <your key> to switch back to the key",
207
209
  "claude_login": "Claude account login",
@@ -393,6 +395,7 @@
393
395
  "codex": {
394
396
  "catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen the Codex desktop app to see them",
395
397
  "catalog_skipped": "Couldn't read Codex's built-in model list (not installed, or older than 0.122), so the menu has GPT models only; run {{cli}} upgrade codex, then {{cli}} config codex --non-interactive",
398
+ "catalog_rejected": "Codex ({{version}}, {{path}}) cannot load the {{brand}} model menu, so Codex's own menu (GPT only) is back; update that Codex (the desktop app's copy updates with the ChatGPT app), then run {{cli}} refresh",
396
399
  "unload_sign_in": "Codex is no longer signed in with an API key. To use your ChatGPT subscription, run codex login; quit the Codex desktop app completely, reopen it and sign in",
397
400
  "unload_chatgpt_restored": "Codex is back on your ChatGPT sign-in; quit the Codex desktop app completely and reopen it",
398
401
  "unload_other_key": "Codex is still signed in with another API key (not the {{brand}} key, left as is). To use your ChatGPT subscription, run codex logout, then codex login"
@@ -202,6 +202,8 @@
202
202
  "claude_request_failed": "请求失败:{{reason}}",
203
203
  "deep_hint": "加上 --deep 会用 Claude Code 实际发一条请求(消耗少量额度),确认整条链路能用",
204
204
  "codex_auth": "Codex 令牌",
205
+ "codex_catalog": "Codex 模型菜单",
206
+ "codex_catalog_rejected": "Codex({{version}},{{path}})读不了模型菜单:{{error}}。运行 {{cli}} refresh 重写;仍然失败就升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)",
205
207
  "codex_stale_bearer": "~/.codex/config.toml 的 [model_providers.custom] 里有 experimental_bearer_token(多半是 cc-switch 等工具代理模式留下的 PROXY_MANAGED),Codex 会把它当令牌发出去,导致 401 Invalid token;运行 {{cli}} setup --key <你的 Key> 会删掉它",
206
208
  "codex_chatgpt_login": "Codex 当前是 ChatGPT 账号登录状态,会把 ChatGPT 的凭据发给 {{brand}},导致 401 Invalid token;运行 {{cli}} setup --key <你的 Key> 改回令牌登录",
207
209
  "claude_login": "Claude 官方登录",
@@ -393,6 +395,7 @@
393
395
  "codex": {
394
396
  "catalog_written": "模型菜单 {{count}} 个({{breakdown}}),Codex 桌面版重开后生效",
395
397
  "catalog_skipped": "没读到 Codex 自带的模型列表(未安装或版本低于 0.122),模型菜单只有 GPT;运行 {{cli}} upgrade codex 后再运行 {{cli}} config codex --non-interactive",
398
+ "catalog_rejected": "Codex({{version}},{{path}})读不了 {{brand}} 的模型菜单,已改回 Codex 自带的菜单(只有 GPT);升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)后运行 {{cli}} refresh",
396
399
  "unload_sign_in": "Codex 已退出 API Key 登录。改用 ChatGPT 订阅:终端运行 codex login;Codex 桌面版完全退出后重开,按提示登录",
397
400
  "unload_chatgpt_restored": "Codex 已改回你的 ChatGPT 登录;Codex 桌面版完全退出后重开即可",
398
401
  "unload_other_key": "Codex 仍在用另一个 API Key 登录(不是 {{brand}} 的 Key,没有动它)。改用 ChatGPT 订阅:运行 codex logout,再运行 codex login"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/api-helper",
3
- "version": "0.0.111",
3
+ "version": "0.0.112",
4
4
  "description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode, Grok Build) with one unified config",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",