@vibeapi/api-helper 0.0.122 → 0.0.123

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 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)}
1
+ import{refreshClientConfig as o}from"../lib/client-config-sync.js";import e from"chalk";import t 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 u}from"../lib/i18n.js";import{validateApiKey as p}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,codexWorkModeOn as v}from"../lib/tools/codex.js";import{claudeAccountLoginPresent as w}from"../lib/tools/claude-code.js";import{toolHome as k}from"../lib/tools/types.js";import{gatewayAddresses as j,probeGatewayAddress as b}from"../lib/gateway-addresses.js";import{listAdapters as x}from"../lib/tools/registry.js";import{logger as $}from"../utils/logger.js";import{maskSecrets as T,uploadReport as A}from"../lib/telemetry.js";import{CLI_NAME as P}from"../lib/brand.js";const R=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function O(o,e,t=3e3){return new Promise(s=>{const r=i({host:o,port:e}),n=o=>{r.destroy(),s(o)};r.setTimeout(t,()=>n(!1)),r.once("connect",()=>n(!0)),r.once("error",()=>n(!1))})}function N(){const o=$.getLogDir();if(!r(o))return"";const e=n(o).filter(o=>/^\d{4}-\d{2}-\d{2}\.log$/.test(o)).sort().slice(-2).map(e=>`== ${e}\n`+a(c(o,e),"utf-8")).join("\n");return T(e.slice(-15e4))}function H(){const o={};for(const e of R){const t=process.env[e]?.trim();if(t)try{const s=new URL(t.includes("://")?t:`http://${t}`);o[e]=`${s.protocol}//${s.host}`}catch{o[e]="(unparsable)"}}return o}export async function doctorCommand(r={}){const n=t(u.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=u.t("doctor.check_proxy"),i.push(await async function(){const o=u.t("doctor.proxy"),e=new Map;for(const o of R){const t=process.env[o]?.trim();t&&!e.has(t)&&e.set(t,o)}if(0===e.size)return{name:o,status:"pass",message:u.t("doctor.proxy_none")};const t=[];for(const[o,s]of e){let e;try{e=new URL(o.includes("://")?o:`http://${o}`)}catch{t.push(`${s}=${o}`);continue}const r=Number(e.port)||("https:"===e.protocol?443:e.protocol.startsWith("socks")?1080:80);await O(e.hostname,r)||t.push(`${s}=${o}`)}return 0===t.length?{name:o,status:"pass",message:[...e.keys()].join(", ")}:{name:o,status:"fail",message:u.t("doctor.proxy_dead",{proxies:t.join(", ")})}}()),n.text=u.t("doctor.check_api_key_network");const T=l.getApiKey(),S=u.t("doctor.api_key_network");if(T){const o=await p(T);o.valid?i.push({name:S,status:"pass"}):"invalid_api_key"===o.error?i.push({name:S,status:"fail",message:u.t("doctor.api_key_invalid")}):(i.push({name:S,status:"fail",message:`${u.t("doctor.network_error")}: ${o.message??o.error}`}),n.text=u.t("doctor.check_other_addresses"),i.push(await async function(){const o=u.t("doctor.other_addresses"),e=l.getBaseUrl(),t=j().filter(o=>o.url!==e),s=l.getApiKey(),r=await Promise.all(t.map(o=>b(o.url,{apiKey:s}))),n=t.filter((o,e)=>r[e].ok).map(o=>o.url);return 0===n.length?{name:o,status:"fail",message:u.t("doctor.other_addresses_none")}:{name:o,status:"warn",message:u.t("doctor.other_addresses_found",{urls:n.join(", ")})}}()))}else i.push({name:S,status:"fail",message:u.t("doctor.api_key_missing")});n.text=u.t("doctor.check_client_config");const C=await o();i.push({name:u.t("doctor.client_config"),status:"cloud"===C.source||"cache"===C.source&&!C.problem?"pass":"warn",message:[C.source?u.t(`refresh.source.${C.source}`):u.t("refresh.source.none"),C.revision?u.t("doctor.client_config_revision",{revision:C.revision}):"",C.problem??""].filter(Boolean).join(" · ")}),n.text=u.t("doctor.check_tools");const I=d.getInstalledTools();i.push({name:u.t("doctor.installed_tools"),status:I.length>0?"pass":"warn",message:I.length>0?I.map(o=>m(o)?.displayName??o).join(", "):u.t("doctor.no_tools_installed")}),i.push(...function(){const o=[];for(const e of d.getInstalledTools()){const t=m(e);if(!t?.installSpec)continue;const s=g(t.installSpec);if(0===s.length)continue;const r=[],n=d.outdatedVersion(e);n&&r.push(u.t("doctor.tool_outdated",{min:n.min})),s.length>1&&r.push(u.t("doctor.tool_duplicates",{others:s.slice(1).map(f).join("、")})),o.push({name:t.displayName,status:r.length>0?"warn":"pass",message:[f(s[0]),...r,...r.length>0?[u.t("doctor.run_upgrade",{tool:e})]:[]].join("\n ")})}return o}());const L=function(){if(!d.isToolInstalled("claude-code"))return null;const o=u.t("doctor.claude_config"),e=_();if(e.length>0){const t=e.map(o=>`${o.source}: ${o.keys.join(", ")}`).join("; ");return{name:o,status:"fail",message:u.t("doctor.claude_managed",{detail:t})}}let t={};try{t=JSON.parse(a(c(k(),".claude","settings.json"),"utf-8")).env??{}}catch{}const s=l.getApiKeyForTool("claude-code");return t.ANTHROPIC_AUTH_TOKEN&&t.ANTHROPIC_BASE_URL?t.ANTHROPIC_BASE_URL!==l.getBaseUrl()||s&&t.ANTHROPIC_AUTH_TOKEN!==s?{name:o,status:"warn",message:u.t("doctor.claude_config_changed")}:{name:o,status:"pass"}:{name:o,status:"warn",message:u.t("doctor.claude_not_configured")}}();L&&i.push(L),I.includes("claude-code")&&w()&&i.push({name:u.t("doctor.claude_login"),status:"pass",message:u.t("tools.claude_code.account_login_overridden")});const U=I.includes("codex")?h():null;if(I.includes("codex")&&i.push({name:u.t("doctor.codex_auth"),status:U?"fail":"pass",message:U?u.t(`doctor.codex_${U}`):void 0}),I.includes("codex")){const o=y();i.push({name:u.t("doctor.codex_catalog"),status:o?"fail":"pass",message:o?u.t("doctor.codex_catalog_rejected",{path:o.bin,version:o.version??"?",error:o.error}):void 0})}I.includes("codex")&&v()&&i.push({name:u.t("doctor.codex_work_mode"),status:"warn",message:u.t("tools.codex.work_mode_on")}),"win32"===process.platform&&I.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:u.t("tools.claude_code.no_git_bash")}),r.deep&&I.includes("claude-code")&&(n.text=u.t("doctor.check_claude_request"),i.push(function(){const o=u.t("doctor.claude_request"),e=g(m("claude-code").installSpec)[0];if(!e)return{name:o,status:"warn",message:u.t("doctor.claude_request_skipped")};const t={encoding:"utf-8",timeout:12e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},r=["-p","Reply with the single word OK."],n=/\.cmd$/i.test(e.path)?s(`"${e.path}" ${r.map(o=>`"${o}"`).join(" ")}`,{...t,shell:!0}):s(e.path,r,t),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:u.t("doctor.claude_request_failed",{reason:i})}}())),n.stop(),console.log("\n"+e.bold(u.t("doctor.results_title"))+"\n");const K={pass:e.green("√"),warn:e.yellow("!"),fail:e.red("×")};for(const o of i)console.log(`${K[o.status]} ${o.name}`),o.message&&console.log(e.gray(` ${o.message}`));if(i.every(o=>"pass"===o.status)&&console.log("\n"+e.green.bold(u.t("doctor.all_good"))),!r.deep&&I.includes("claude-code")&&console.log(e.gray("\n"+u.t("doctor.deep_hint"))),r.report){const o=t(u.t("doctor.report_uploading")).start(),e=await A(function(o,e){const t={};for(const o of x()){if(o.hidden)continue;let e=null;try{e=d.detectCurrentConfig(o.name).apiKey?"has_key":"no_key"}catch{e="unreadable"}t[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:e}}const s=l.getProfile();return{checks:o,tools:t,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:e,env:{shell:process.env.SHELL??process.env.ComSpec??null,term:process.env.TERM_PROGRAM??null,proxies:H()},log:N()}}(i,{source:C.source??void 0,revision:C.revision,problem:C.problem}));e?o.succeed(u.t("doctor.report_done",{code:e})):o.fail(u.t("doctor.report_failed",{cli:P}))}else i.some(o=>"fail"===o.status)&&console.log(e.gray("\n"+u.t("doctor.report_hint",{cli:P})));i.some(o=>"fail"===o.status)&&(process.exitCode=1)}
@@ -1,6 +1,8 @@
1
1
  import { type ToolAdapter } from './types.js';
2
2
  import type { ModelCatalog } from '../model-catalog.js';
3
3
  import { type CatalogRejection } from './codex-catalog.js';
4
+ /** The desktop app is in ChatGPT Work mode, which cannot start a thread on an API key. */
5
+ export declare function codexWorkModeOn(): boolean;
4
6
  export declare const CODEX_ONE_MILLION_CONTEXT_SETTING = "one_million_context";
5
7
  /** For doctor: a local Codex that cannot load the catalog `config.toml` points at (checked afresh, no cache). */
6
8
  export declare function codexCatalogRejection(): CatalogRejection | null;
@@ -1 +1 @@
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 s}from"path";import*as a 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{codexHome as f,pickModel as p,withV1 as g}from"./types.js";import{getToolDefaultModel as _}from"../model-registry.js";import{configManager as x}from"../config.js";import{maskHome as y,track as h}from"../telemetry.js";import{buildCodexCatalog as b,catalogRejections as k,codexCatalogPath as j,fillFromOlderEntries as v,installedCodexBinaries as w,loadBundledCodexModels as O,loadPreviousCodexModels as C,previousGatewayModelIds as M}from"./codex-catalog.js";import{gatewayModelSpecs as E,modelFamily as N}from"../gateway-models.js";import{findInstalls as S,mirrorInstallCommand as A,brandInstallScripts as P}from"./tool-installs.js";const T=()=>f(),D=()=>s(T(),"config.toml"),I=["browser_use","browser_use_external","computer_use"];function $(e){const t=G(e,"features");let o=!1;for(const e of I)!1!==t[e]&&(t[e]=!1,o=!0);return o}const J=()=>s(T(),"auth.json"),L="OPENAI_API_KEY",K="custom",X=1e6,B=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function q(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function G(e,t){return q(e[t])||(e[t]={}),e[t]}function z(){if(!t(D()))return{};const e=n(D(),"utf-8");if(!e.trim())return{};try{const t=a.parse(e);if(!q(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 R(){if(!t(J()))return{};try{const e=JSON.parse(n(J(),"utf-8"));return q(e)?e:{}}catch{return{}}}let U=null;function Y(e){const t=e.profiles;if(q(t)){for(const[e,o]of Object.entries(t))q(o)&&2===Object.keys(o).length&&o.model_provider===K&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let F=null;const H=()=>s(x.getConfigDir(),"codex-catalog-check.json");function Q(e){const o=j();t(o)&&n(o,"utf-8")===e||(u(o),m(o,e))}function V(t,r){if(0===r.length)return[];const s=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(H(),"utf-8")).signature===e}catch{return!1}}(s))return[];const a=k(j(),r);return 0===a.length&&function(e){try{o(x.getConfigDir(),{recursive:!0}),l(H(),JSON.stringify({signature:e})+"\n")}catch(e){c.logError("codexAdapter.rememberAccepted",e)}}(s),a}function W(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===X&&delete e.model_context_window,e.model_auto_compact_token_limit===B&&delete e.model_auto_compact_token_limit}(e);const i=()=>{e.model_catalog_json===j()&&delete e.model_catalog_json,n&&(e.model_context_window=X,e.model_auto_compact_token_limit=B)},l=O()??C();if(!l)return c.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),F="skipped",void i();const s=t.catalog,a=e=>b({bundled:e,specs:E("codex",s?.clientModels),...s?{keyModels:s.models}:{},gatewayAvailable:s?.models??M(),oneMillion:n,selectedModel:o,..."gateway"===s?.oneMillionSource?{claudeOneMillion:s.oneMillion}:{}}),d=e=>JSON.stringify(e,null,2)+"\n";let u=a(l);Q(d(u));const m=w(),f=V(d(u),m);if(f.length>0){const e=Date.now();let t=!1;for(const e of f){const o=O(e.bin);if(o){for(const e of[{models:v(u.models,o)},{models:v(a(o).models,o)}])if(Q(d(e)),0===V(d(e),m).length){u=e,t=!0;break}if(t)break}}if(h("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:f[0].error,detail:{rejected_by:f.map(e=>({version:e.version,path:y(e.bin)}))}}),!t)return F={rejectedBy:f[0]},r(j(),{force:!0}),void i()}F={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=j()}export function codexCatalogRejection(){let e;try{e=z()}catch{return null}return e.model_catalog_json===j()&&t(j())?k(j())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=z(),n=a.stringify(o),r="string"==typeof o.model?o.model:_("codex");W(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=a.stringify(o);i!==n&&(u(D()),m(D(),i))}const Z={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:P("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:A(Z),installSpec:Z,configPath:D(),wantsModelCatalog:!0,listedModels(){if(z().model_catalog_json!==j())return null;try{return(JSON.parse(n(j(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(z().model_catalog_json!==j())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=z();$(t)&&(u(D()),m(D(),a.stringify(t)))},applyNotes(){if("skipped"===F)return 0===S(Z).length?[]:[{kind:"action",text:d.t("tools.codex.catalog_skipped")}];if(!F)return[];if("rejectedBy"in F){const{bin:e,version:t}=F.rejectedBy;return[{kind:"action",text:d.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of F.listed)e.set(N(t.id),(e.get(N(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(F.listed.length),breakdown:t})},{kind:"detail",text:F.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const t=_("codex"),o=p(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=g(e.baseUrl),i=z();i.model=n,i.model_provider=K,i.model_reasoning_effort="high",W(i,e,n);const l=G(i,"model_providers"),s=q(l[K])?{...l[K]}:{};delete s.env_key;const{experimental_bearer_token:c,...d}=s;l[K]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},Y(i),G(G(i,"tui"),"model_availability_nux")[n]=4;const f=G(i,"features");f.goals=!0,f.js_repl=!1,$(i),u(D()),m(D(),a.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...R(),auth_mode:"apikey",[L]:t};u(J()),m(J(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(e){if(U=function(e){const t=R(),o="string"==typeof t[L]?t[L].trim():"";if(!o)return q(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[L]:n,auth_mode:i,...l}=t;return u(J()),q(l.tokens)?(m(J(),JSON.stringify({...l,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(r(J(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(D()))return;let o;try{o=z()}catch(e){return void c.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;q(n)&&(delete n[K],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===K&&delete o.model_provider,o.model_catalog_json===j()&&delete o.model_catalog_json;const i=o.features;if(q(i))for(const e of I)!1===i[e]&&delete i[e];Y(o),u(D()),m(D(),a.stringify(o))},unloadNotes(){switch(U){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=R()[L];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return z()}catch(e){return c.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[D(),J(),j()]};export function codexAuthProblem(){try{const e=z(),t=e.model_providers?.[K];if(e.model_provider!==K||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=R();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 s}from"path";import*as a 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{codexHome as f,pickModel as p,withV1 as g}from"./types.js";import{getToolDefaultModel as _}from"../model-registry.js";import{configManager as x}from"../config.js";import{maskHome as y,track as h}from"../telemetry.js";import{buildCodexCatalog as k,catalogRejections as b,codexCatalogPath as j,fillFromOlderEntries as v,installedCodexBinaries as w,loadBundledCodexModels as O,loadPreviousCodexModels as C,previousGatewayModelIds as M}from"./codex-catalog.js";import{gatewayModelSpecs as E,modelFamily as S}from"../gateway-models.js";import{findInstalls as N,mirrorInstallCommand as A,brandInstallScripts as P}from"./tool-installs.js";const T=()=>f(),D=()=>s(T(),"config.toml"),I=["browser_use","browser_use_external","computer_use"];function $(e){const t=e.features;if(!q(t)||!I.every(e=>!1===t[e]))return!1;for(const e of I)delete t[e];return!0}export function codexWorkModeOn(){try{const e=R().desktop;return q(e)&&"STEPS_PROSE"===e.conversationDetailMode}catch{return!1}}const J=()=>s(T(),"auth.json"),L="OPENAI_API_KEY",K="custom",X=1e6,B=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function q(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function G(e,t){return q(e[t])||(e[t]={}),e[t]}function R(){if(!t(D()))return{};const e=n(D(),"utf-8");if(!e.trim())return{};try{const t=a.parse(e);if(!q(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 W(){if(!t(J()))return{};try{const e=JSON.parse(n(J(),"utf-8"));return q(e)?e:{}}catch{return{}}}let z=null;function U(e){const t=e.profiles;if(q(t)){for(const[e,o]of Object.entries(t))q(o)&&2===Object.keys(o).length&&o.model_provider===K&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let Y=null;const F=()=>s(x.getConfigDir(),"codex-catalog-check.json");function H(e){const o=j();t(o)&&n(o,"utf-8")===e||(u(o),m(o,e))}function Q(t,r){if(0===r.length)return[];const s=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(F(),"utf-8")).signature===e}catch{return!1}}(s))return[];const a=b(j(),r);return 0===a.length&&function(e){try{o(x.getConfigDir(),{recursive:!0}),l(F(),JSON.stringify({signature:e})+"\n")}catch(e){c.logError("codexAdapter.rememberAccepted",e)}}(s),a}function V(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===X&&delete e.model_context_window,e.model_auto_compact_token_limit===B&&delete e.model_auto_compact_token_limit}(e);const i=()=>{e.model_catalog_json===j()&&delete e.model_catalog_json,n&&(e.model_context_window=X,e.model_auto_compact_token_limit=B)},l=O()??C();if(!l)return c.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),Y="skipped",void i();const s=t.catalog,a=e=>k({bundled:e,specs:E("codex",s?.clientModels),...s?{keyModels:s.models}:{},gatewayAvailable:s?.models??M(),oneMillion:n,selectedModel:o,..."gateway"===s?.oneMillionSource?{claudeOneMillion:s.oneMillion}:{}}),d=e=>JSON.stringify(e,null,2)+"\n";let u=a(l);H(d(u));const m=w(),f=Q(d(u),m);if(f.length>0){const e=Date.now();let t=!1;for(const e of f){const o=O(e.bin);if(o){for(const e of[{models:v(u.models,o)},{models:v(a(o).models,o)}])if(H(d(e)),0===Q(d(e),m).length){u=e,t=!0;break}if(t)break}}if(h("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:f[0].error,detail:{rejected_by:f.map(e=>({version:e.version,path:y(e.bin)}))}}),!t)return Y={rejectedBy:f[0]},r(j(),{force:!0}),void i()}Y={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=j()}export function codexCatalogRejection(){let e;try{e=R()}catch{return null}return e.model_catalog_json===j()&&t(j())?b(j())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=R(),n=a.stringify(o),r="string"==typeof o.model?o.model:_("codex");V(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=a.stringify(o);i!==n&&(u(D()),m(D(),i))}const Z={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:P("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:A(Z),installSpec:Z,configPath:D(),wantsModelCatalog:!0,listedModels(){if(R().model_catalog_json!==j())return null;try{return(JSON.parse(n(j(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(R().model_catalog_json!==j())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=R();$(t)&&(u(D()),m(D(),a.stringify(t)))},applyNotes(){if("skipped"===Y)return 0===N(Z).length?[]:[{kind:"action",text:d.t("tools.codex.catalog_skipped")}];if(!Y)return[];if("rejectedBy"in Y){const{bin:e,version:t}=Y.rejectedBy;return[{kind:"action",text:d.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of Y.listed)e.set(S(t.id),(e.get(S(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(Y.listed.length),breakdown:t})},{kind:"detail",text:Y.listed.map(e=>e.name).join(", ")},{kind:"action",text:d.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]},loadPrivateConfig(e){const t=_("codex"),o=p(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=g(e.baseUrl),i=R();i.model=n,i.model_provider=K,i.model_reasoning_effort="high",V(i,e,n);const l=G(i,"model_providers"),s=q(l[K])?{...l[K]}:{};delete s.env_key;const{experimental_bearer_token:c,...d}=s;l[K]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},U(i),G(G(i,"tui"),"model_availability_nux")[n]=4;const f=G(i,"features");f.goals=!0,f.js_repl=!1,$(i),u(D()),m(D(),a.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...W(),auth_mode:"apikey",[L]:t};u(J()),m(J(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(e){if(z=function(e){const t=W(),o="string"==typeof t[L]?t[L].trim():"";if(!o)return q(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[L]:n,auth_mode:i,...l}=t;return u(J()),q(l.tokens)?(m(J(),JSON.stringify({...l,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(r(J(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(D()))return;let o;try{o=R()}catch(e){return void c.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;q(n)&&(delete n[K],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===K&&delete o.model_provider,o.model_catalog_json===j()&&delete o.model_catalog_json,$(o),U(o),u(D()),m(D(),a.stringify(o))},unloadNotes(){switch(z){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=W()[L];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return R()}catch(e){return c.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[D(),J(),j()]};export function codexAuthProblem(){try{const e=R(),t=e.model_providers?.[K];if(e.model_provider!==K||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=W();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -202,6 +202,7 @@
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_work_mode": "Codex desktop mode",
205
206
  "codex_catalog": "Codex model menu",
206
207
  "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)",
207
208
  "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",
@@ -410,6 +411,8 @@
410
411
  "list_separator": ", "
411
412
  },
412
413
  "codex": {
414
+ "work_mode_hint": "In the Codex desktop app use Codex mode (switch at the top left): on an API key, ChatGPT Work mode cannot send messages and shows \"Could not start this chat\", a known OpenAI issue",
415
+ "work_mode_on": "The Codex desktop app is in ChatGPT Work mode, which cannot send messages on an API key (\"Could not start this chat\", a known OpenAI issue): switch back to Codex mode at the top left",
413
416
  "catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen the Codex desktop app to see them",
414
417
  "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",
415
418
  "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",
@@ -202,6 +202,7 @@
202
202
  "claude_request_failed": "请求失败:{{reason}}",
203
203
  "deep_hint": "加上 --deep 会用 Claude Code 实际发一条请求(消耗少量额度),确认整条链路能用",
204
204
  "codex_auth": "Codex 令牌",
205
+ "codex_work_mode": "Codex 桌面版模式",
205
206
  "codex_catalog": "Codex 模型菜单",
206
207
  "codex_catalog_rejected": "Codex({{version}},{{path}})读不了模型菜单:{{error}}。运行 {{cli}} refresh 重写;仍然失败就升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)",
207
208
  "codex_stale_bearer": "~/.codex/config.toml 的 [model_providers.custom] 里有 experimental_bearer_token(多半是 cc-switch 等工具代理模式留下的 PROXY_MANAGED),Codex 会把它当令牌发出去,导致 401 Invalid token;运行 {{cli}} setup --key <你的 Key> 会删掉它",
@@ -410,6 +411,8 @@
410
411
  "list_separator": "、"
411
412
  },
412
413
  "codex": {
414
+ "work_mode_hint": "Codex 桌面版请用 Codex 模式(左上角切换):用 API Key 时 ChatGPT Work 模式发不出消息,会提示「Could not start this chat」,这是 OpenAI 的已知问题",
415
+ "work_mode_on": "Codex 桌面版现在是 ChatGPT Work 模式,用 API Key 时发不出消息(提示「Could not start this chat」,OpenAI 的已知问题):请在左上角切回 Codex 模式",
413
416
  "catalog_written": "模型菜单 {{count}} 个({{breakdown}}),Codex 桌面版重开后生效",
414
417
  "catalog_skipped": "没读到 Codex 自带的模型列表(未安装或版本低于 0.122),模型菜单只有 GPT;运行 {{cli}} upgrade codex 后再运行 {{cli}} config codex --non-interactive",
415
418
  "catalog_rejected": "Codex({{version}},{{path}})读不了 {{brand}} 的模型菜单,已改回 Codex 自带的菜单(只有 GPT);升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)后运行 {{cli}} refresh",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/api-helper",
3
- "version": "0.0.122",
3
+ "version": "0.0.123",
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",