@vibeapi/api-helper 0.0.124 → 0.0.125

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 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
+ 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 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,codexConfigPath as v,codexWorkModeOn as w}from"../lib/tools/codex.js";import{claudeAccountLoginPresent as k}from"../lib/tools/claude-code.js";import{toolHome as j}from"../lib/tools/types.js";import{gatewayAddresses as b,probeGatewayAddress as x}from"../lib/gateway-addresses.js";import{listAdapters as $}from"../lib/tools/registry.js";import{logger as T}from"../utils/logger.js";import{maskHome as A,maskSecrets as P,uploadReport as R}from"../lib/telemetry.js";import{CLI_NAME as O}from"../lib/brand.js";const N=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function H(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 S(){const o=T.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 P(e.slice(-15e4))}function C(){const o={};for(const e of N){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(p.t("doctor.checking")).start(),i=[],T=Number(process.versions.node.split(".")[0]);i.push({name:"Node.js",status:T>=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"),e=new Map;for(const o of N){const t=process.env[o]?.trim();t&&!e.has(t)&&e.set(t,o)}if(0===e.size)return{name:o,status:"pass",message:p.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 H(e.hostname,r)||t.push(`${s}=${o}`)}return 0===t.length?{name:o,status:"pass",message:[...e.keys()].join(", ")}:{name:o,status:"fail",message:p.t("doctor.proxy_dead",{proxies:t.join(", ")})}}()),n.text=p.t("doctor.check_api_key_network");const P=l.getApiKey(),I=p.t("doctor.api_key_network");if(P){const o=await u(P);o.valid?i.push({name:I,status:"pass"}):"invalid_api_key"===o.error?i.push({name:I,status:"fail",message:p.t("doctor.api_key_invalid")}):(i.push({name:I,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"),e=l.getBaseUrl(),t=b().filter(o=>o.url!==e),s=l.getApiKey(),r=await Promise.all(t.map(o=>x(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:p.t("doctor.other_addresses_none")}:{name:o,status:"warn",message:p.t("doctor.other_addresses_found",{urls:n.join(", ")})}}()))}else i.push({name:I,status:"fail",message:p.t("doctor.api_key_missing")});n.text=p.t("doctor.check_client_config");const L=await o();i.push({name:p.t("doctor.client_config"),status:"cloud"===L.source||"cache"===L.source&&!L.problem?"pass":"warn",message:[L.source?p.t(`refresh.source.${L.source}`):p.t("refresh.source.none"),L.revision?p.t("doctor.client_config_revision",{revision:L.revision}):"",L.problem??""].filter(Boolean).join(" · ")}),n.text=p.t("doctor.check_tools");const U=d.getInstalledTools();i.push({name:p.t("doctor.installed_tools"),status:U.length>0?"pass":"warn",message:U.length>0?U.map(o=>m(o)?.displayName??o).join(", "):p.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(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:t.displayName,status:r.length>0?"warn":"pass",message:[f(s[0]),...r,...r.length>0?[p.t("doctor.run_upgrade",{tool:e})]:[]].join("\n ")})}return o}());const K=function(){if(!d.isToolInstalled("claude-code"))return null;const o=p.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:p.t("doctor.claude_managed",{detail:t})}}let t={};try{t=JSON.parse(a(c(j(),".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:p.t("doctor.claude_config_changed")}:{name:o,status:"pass"}:{name:o,status:"warn",message:p.t("doctor.claude_not_configured")}}();K&&i.push(K),U.includes("claude-code")&&k()&&i.push({name:p.t("doctor.claude_login"),status:"pass",message:p.t("tools.claude_code.account_login_overridden")});const E=U.includes("codex")?h():null;if(U.includes("codex")&&i.push({name:p.t("doctor.codex_auth"),status:E?"fail":"pass",message:E?p.t(`doctor.codex_${E}`,{path:A(v())}):void 0}),U.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})}U.includes("codex")&&w()&&i.push({name:p.t("doctor.codex_work_mode"),status:"warn",message:p.t("tools.codex.work_mode_on")}),"win32"===process.platform&&U.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&&U.includes("claude-code")&&(n.text=p.t("doctor.check_claude_request"),i.push(function(){const o=p.t("doctor.claude_request"),e=g(m("claude-code").installSpec)[0];if(!e)return{name:o,status:"warn",message:p.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:p.t("doctor.claude_request_failed",{reason:i})}}())),n.stop(),console.log("\n"+e.bold(p.t("doctor.results_title"))+"\n");const B={pass:e.green("√"),warn:e.yellow("!"),fail:e.red("×")};for(const o of i)console.log(`${B[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(p.t("doctor.all_good"))),!r.deep&&U.includes("claude-code")&&console.log(e.gray("\n"+p.t("doctor.deep_hint"))),r.report){const o=t(p.t("doctor.report_uploading")).start(),e=await R(function(o,e){const t={};for(const o of $()){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:C()},log:S()}}(i,{source:L.source??void 0,revision:L.revision,problem:L.problem}));e?o.succeed(p.t("doctor.report_done",{code:e})):o.fail(p.t("doctor.report_failed",{cli:O}))}else i.some(o=>"fail"===o.status)&&console.log(e.gray("\n"+p.t("doctor.report_hint",{cli:O})));i.some(o=>"fail"===o.status)&&(process.exitCode=1)}
@@ -27,7 +27,7 @@ export { codexHome };
27
27
  export declare function currentCodexProvider(): string;
28
28
  /** Tests replace the process probe. */
29
29
  export declare function setCodexProcessCheckForTests(fake: () => number[]): void;
30
- /** PIDs of running Codex processes (CLI, desktop app, background app-server); they would overwrite the edits. */
30
+ /** PIDs of this user's running Codex processes (CLI, desktop app, background app-server) on this CODEX_HOME; they would overwrite the edits. */
31
31
  export declare function runningCodexPids(): number[];
32
32
  /** `dbSkipped` when Codex has no state DB yet: nothing to update, not a problem to report. */
33
33
  export declare const NO_STATE_DB = "no state DB";
@@ -1 +1 @@
1
- import{spawnSync as e}from"child_process";import{closeSync as t,copyFileSync as o,createReadStream as r,createWriteStream as n,existsSync as s,mkdirSync as i,openSync as a,readFileSync as f,readSync as c,readdirSync as l,renameSync as d,statSync as p,utimesSync as m,writeFileSync as u}from"fs";import{join as h}from"path";import{pipeline as y}from"stream/promises";import*as g from"@iarna/toml";import{codexHome as b}from"./tools/types.js";import{CLI_NAME as v}from"./brand.js";export{b as codexHome};export function currentCodexProvider(){try{const e=g.parse(f(h(b(),"config.toml"),"utf-8"));return"string"==typeof e.model_provider&&e.model_provider.trim()?e.model_provider.trim():"openai"}catch{return"openai"}}let S=()=>"win32"===process.platform?`${e("powershell.exe",["-NoProfile","-NonInteractive","-Command","(Get-Process -Name codex -ErrorAction SilentlyContinue).Id"],{encoding:"utf-8",windowsHide:!0}).stdout??""}`.split(/\s+/).filter(Boolean).map(Number):`${e("pgrep",["-x","-i","codex"],{encoding:"utf-8"}).stdout??""}`.split(/\s+/).filter(Boolean).map(Number);export function setCodexProcessCheckForTests(e){S=e}export function runningCodexPids(){try{return S()}catch{return[]}}function _(e){const o=a(e,"r");try{const e=[],t=Buffer.alloc(65536);for(;;){const r=c(o,t,0,t.length,null);if(r<=0)break;const n=t.subarray(0,r).indexOf(10);if(n>=0){e.push(Buffer.from(t.subarray(0,n)));break}e.push(Buffer.from(t.subarray(0,r)))}return Buffer.concat(e).toString("utf-8")}finally{t(o)}}async function w(e,t,o){const{atime:s,mtime:i}=p(e),a=`${e}.${v}-tmp`,f=n(a);f.write(t),await y(r(e,{start:o}),f),d(a,e),m(e,s,i)}async function N(e){try{return new((await import("node:sqlite")).DatabaseSync)(e)}catch(e){return`node:sqlite unavailable (Node ${process.versions.node}; needs 22.13+): ${e instanceof Error?e.message:String(e)}`}}export const NO_STATE_DB="no state DB";export async function moveSessionsToProvider(e,t={}){const r=function(){const e=[],t=o=>{if(s(o))for(const r of l(o,{withFileTypes:!0})){const n=h(o,r.name);r.isDirectory()?t(n):r.isFile()&&r.name.endsWith(".jsonl")&&e.push(n)}};return t(h(b(),"sessions")),t(h(b(),"archived_sessions")),e}(),n=[];for(const t of r){let o,r;try{o=_(t)}catch{continue}try{r=JSON.parse(o)}catch{continue}const s=r.payload?.model_provider;"session_meta"===r.type&&"string"==typeof s&&s!==e&&(r.payload.model_provider=e,n.push({file:t,from:s,line:JSON.stringify(r),length:Buffer.byteLength(o,"utf-8")}))}const a=function(){const e=b();if(!s(e))return null;const t=l(e).map(e=>/^state_(\d+)\.sqlite$/.exec(e)).filter(e=>!!e).sort((e,t)=>Number(t[1])-Number(e[1]));return t.length>0?h(e,t[0][0]):null}();let f=a?null:NO_STATE_DB,c=[],d=null;if(a){const t=await N(a);"string"==typeof t?f=t:(d=t,c=d.prepare('SELECT id, model_provider AS "from" FROM threads WHERE model_provider <> ?').all(e).map(e=>({id:String(e.id),from:String(e.from)})))}const p={target:e,scanned:r.length,moved:n.map(({file:e,from:t})=>({file:e,from:t})),dbRows:c.length,dbSkipped:f,backupDir:null};if(t.dryRun||0===n.length&&0===c.length)return d?.close(),p;const m=h(b(),"backups",`${v}-sessions-${(new Date).toISOString().replace(/[:.]/g,"-")}`);if(i(m,{recursive:!0}),a)for(const e of["","-wal","-shm"])s(a+e)&&o(a+e,h(m,`state.sqlite${e}`));const y={target:e,moved:p.moved,threads:c,db:a};u(h(m,"manifest.json"),JSON.stringify(y,null,2)+"\n");for(const e of n)await w(e.file,e.line,e.length);return d&&(d.prepare("UPDATE threads SET model_provider = ? WHERE model_provider <> ?").run(e,e),d.close()),p.backupDir=m,p}export async function undoSessionMove(e){const t=JSON.parse(f(h(e,"manifest.json"),"utf-8"));let o=0;for(const{file:e,from:r}of t.moved){if(!s(e))continue;const t=_(e),n=JSON.parse(t);n.payload&&(n.payload.model_provider=r,await w(e,JSON.stringify(n),Buffer.byteLength(t,"utf-8")),o+=1)}let r=0;if(t.db&&s(t.db)&&t.threads.length>0){const e=await N(t.db);if("string"!=typeof e){const o=e.prepare("UPDATE threads SET model_provider = ? WHERE id = ?");for(const{id:e,from:n}of t.threads)r+=Number(o.run(n,e).changes);e.close()}}return{files:o,dbRows:r}}
1
+ import{spawnSync as e}from"child_process";import{closeSync as t,copyFileSync as r,createReadStream as o,createWriteStream as n,existsSync as s,mkdirSync as i,openSync as a,readFileSync as c,readSync as f,readdirSync as l,realpathSync as u,renameSync as d,statSync as p,utimesSync as m,writeFileSync as g}from"fs";import{join as h}from"path";import{pipeline as y}from"stream/promises";import*as v from"@iarna/toml";import{codexHome as b}from"./tools/types.js";import{CLI_NAME as S}from"./brand.js";export{b as codexHome};export function currentCodexProvider(){try{const e=v.parse(c(h(b(),"config.toml"),"utf-8"));return"string"==typeof e.model_provider&&e.model_provider.trim()?e.model_provider.trim():"openai"}catch{return"openai"}}let x=()=>{if("win32"===process.platform)return`${e("powershell.exe",["-NoProfile","-NonInteractive","-Command","(Get-Process -Name codex -ErrorAction SilentlyContinue).Id"],{encoding:"utf-8",windowsHide:!0}).stdout??""}`.split(/\s+/).filter(Boolean).map(Number);const t=process.getuid?.(),r=`${e("pgrep",["-x","-i",...void 0===t?[]:["-u",String(t)],"codex"],{encoding:"utf-8"}).stdout??""}`.split(/\s+/).filter(Boolean).map(Number);if("linux"!==process.platform)return r;const o=E(b());return r.filter(e=>{let t;try{t=c(`/proc/${e}/environ`,"utf-8")}catch{return!0}const r=new Map(t.split("\0").map(e=>[e.slice(0,e.indexOf("=")),e.slice(e.indexOf("=")+1)])),n=r.get("CODEX_HOME")?.trim()||(r.get("HOME")?h(r.get("HOME"),".codex"):null);return null===n||E(n)===o})};function E(e){try{return u(e)}catch{return e}}export function setCodexProcessCheckForTests(e){x=e}export function runningCodexPids(){try{return x()}catch{return[]}}function _(e){const r=a(e,"r");try{const e=[],t=Buffer.alloc(65536);for(;;){const o=f(r,t,0,t.length,null);if(o<=0)break;const n=t.subarray(0,o).indexOf(10);if(n>=0){e.push(Buffer.from(t.subarray(0,n)));break}e.push(Buffer.from(t.subarray(0,o)))}return Buffer.concat(e).toString("utf-8")}finally{t(r)}}async function w(e,t,r){const{atime:s,mtime:i}=p(e),a=`${e}.${S}-tmp`,c=n(a);c.write(t),await y(o(e,{start:r}),c),d(a,e),m(e,s,i)}async function N(e){try{return new((await import("node:sqlite")).DatabaseSync)(e)}catch(e){return`node:sqlite unavailable (Node ${process.versions.node}; needs 22.13+): ${e instanceof Error?e.message:String(e)}`}}export const NO_STATE_DB="no state DB";export async function moveSessionsToProvider(e,t={}){const o=function(){const e=[],t=r=>{if(s(r))for(const o of l(r,{withFileTypes:!0})){const n=h(r,o.name);o.isDirectory()?t(n):o.isFile()&&o.name.endsWith(".jsonl")&&e.push(n)}};return t(h(b(),"sessions")),t(h(b(),"archived_sessions")),e}(),n=[];for(const t of o){let r,o;try{r=_(t)}catch{continue}try{o=JSON.parse(r)}catch{continue}const s=o.payload?.model_provider;"session_meta"===o.type&&"string"==typeof s&&s!==e&&(o.payload.model_provider=e,n.push({file:t,from:s,line:JSON.stringify(o),length:Buffer.byteLength(r,"utf-8")}))}const a=function(){const e=b();if(!s(e))return null;const t=l(e).map(e=>/^state_(\d+)\.sqlite$/.exec(e)).filter(e=>!!e).sort((e,t)=>Number(t[1])-Number(e[1]));return t.length>0?h(e,t[0][0]):null}();let c=a?null:NO_STATE_DB,f=[],u=null;if(a){const t=await N(a);"string"==typeof t?c=t:(u=t,f=u.prepare('SELECT id, model_provider AS "from" FROM threads WHERE model_provider <> ?').all(e).map(e=>({id:String(e.id),from:String(e.from)})))}const d={target:e,scanned:o.length,moved:n.map(({file:e,from:t})=>({file:e,from:t})),dbRows:f.length,dbSkipped:c,backupDir:null};if(t.dryRun||0===n.length&&0===f.length)return u?.close(),d;const p=h(b(),"backups",`${S}-sessions-${(new Date).toISOString().replace(/[:.]/g,"-")}`);if(i(p,{recursive:!0}),a)for(const e of["","-wal","-shm"])s(a+e)&&r(a+e,h(p,`state.sqlite${e}`));const m={target:e,moved:d.moved,threads:f,db:a};g(h(p,"manifest.json"),JSON.stringify(m,null,2)+"\n");for(const e of n)await w(e.file,e.line,e.length);return u&&(u.prepare("UPDATE threads SET model_provider = ? WHERE model_provider <> ?").run(e,e),u.close()),d.backupDir=p,d}export async function undoSessionMove(e){const t=JSON.parse(c(h(e,"manifest.json"),"utf-8"));let r=0;for(const{file:e,from:o}of t.moved){if(!s(e))continue;const t=_(e),n=JSON.parse(t);n.payload&&(n.payload.model_provider=o,await w(e,JSON.stringify(n),Buffer.byteLength(t,"utf-8")),r+=1)}let o=0;if(t.db&&s(t.db)&&t.threads.length>0){const e=await N(t.db);if("string"!=typeof e){const r=e.prepare("UPDATE threads SET model_provider = ? WHERE id = ?");for(const{id:e,from:n}of t.threads)o+=Number(r.run(n,e).changes);e.close()}}return{files:r,dbRows:o}}
@@ -1 +1 @@
1
- import o from"chalk";import t from"inquirer";import{i18n as e}from"./i18n.js";import{isNonInteractive as n,shouldAutoConfirm as i}from"./runtime-options.js";import{toolManager as r}from"./tool-manager.js";import{getAdapter as s}from"./tools/registry.js";import{upgradeTool as l}from"./tool-upgrade.js";import{logger as a}from"../utils/logger.js";async function p(o){const{ok:e}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return e}export async function postApplyPrompts(t){for(const e of t){const t=await(s(e)?.prepareRuntime?.())??[];for(const e of t)console.log("action"===e.kind?o.yellow(" ! "+e.text):o.green(" √ "+e.text))}const f=!n()&&process.stdin.isTTY;for(const n of t){const t=s(n)?.pendingElevation?.();if(!t)continue;if(!(i()||f&&await p(t.prompt)))continue;const r=s(n)?.displayName??n;console.log(o.gray(" "+e.t("setup.elevation_running"))),console.log(t.run()?o.green(" √ "+e.t("setup.elevation_done",{tool:r})):o.yellow(" ! "+e.t("setup.elevation_failed",{tool:r})))}for(const e of t){let t;try{t=await(s(e)?.followUp?.())}catch(o){a.logError(`postApplyPrompts.followUp(${e})`,o);continue}if(t)if("run"in t&&(i()||f))if(i()||await p(t.prompt))for(const e of await t.run())console.log("action"===e.kind?o.yellow(" ! "+e.text):o.green(" √ "+e.text));else t.decline();else console.log(o.yellow(" ! "+t.hint))}if(!f)return;const c=t.filter(o=>s(o)?.installSpec&&r.outdatedVersion(o));if(c.length>0){const o=c.map(o=>s(o)?.displayName??o).join("、");if(await p(e.t("setup.offer_upgrade",{tools:o})))for(const o of c)await l(o)}for(const n of t){const t=s(n);if(!t?.runningApp?.isRunning())continue;if(!await p(e.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+e.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+e.t("setup.restart_failed",{tool:t.displayName})))}}
1
+ import o from"chalk";import t from"inquirer";import{i18n as e}from"./i18n.js";import{hasExplicitYes as n,isNonInteractive as i,shouldAutoConfirm as r}from"./runtime-options.js";import{toolManager as s}from"./tool-manager.js";import{getAdapter as l}from"./tools/registry.js";import{upgradeTool as a}from"./tool-upgrade.js";import{logger as p}from"../utils/logger.js";async function f(o){const{ok:e}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return e}export async function postApplyPrompts(t){for(const e of t){const t=await(l(e)?.prepareRuntime?.())??[];for(const e of t)console.log("action"===e.kind?o.yellow(" ! "+e.text):o.green(" √ "+e.text))}const c=!i()&&process.stdin.isTTY;for(const n of t){const t=l(n)?.pendingElevation?.();if(!t)continue;if(!(r()||c&&await f(t.prompt)))continue;const i=l(n)?.displayName??n;console.log(o.gray(" "+e.t("setup.elevation_running"))),console.log(t.run()?o.green(" √ "+e.t("setup.elevation_done",{tool:i})):o.yellow(" ! "+e.t("setup.elevation_failed",{tool:i})))}for(const e of t){let t;try{t=await(l(e)?.followUp?.())}catch(o){p.logError(`postApplyPrompts.followUp(${e})`,o);continue}if(!t)continue;const i=n();if("run"in t&&(i||c))if(i||await f(t.prompt))for(const e of await t.run())console.log("action"===e.kind?o.yellow(" ! "+e.text):o.green(" √ "+e.text));else t.decline();else console.log(o.yellow(" ! "+t.hint))}if(!c)return;const m=t.filter(o=>l(o)?.installSpec&&s.outdatedVersion(o));if(m.length>0){const o=m.map(o=>l(o)?.displayName??o).join("、");if(await f(e.t("setup.offer_upgrade",{tools:o})))for(const o of m)await a(o)}for(const n of t){const t=l(n);if(!t?.runningApp?.isRunning())continue;if(!await f(e.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+e.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+e.t("setup.restart_failed",{tool:t.displayName})))}}
@@ -11,6 +11,8 @@ export declare function isJsonMode(): boolean;
11
11
  export declare function isNonInteractive(): boolean;
12
12
  export declare function isDryRun(): boolean;
13
13
  export declare function shouldAutoConfirm(): boolean;
14
+ /** Only an explicit `--yes`: changes to the user's own data are not made just because nobody can be asked. */
15
+ export declare function hasExplicitYes(): boolean;
14
16
  export declare function isColorEnabled(): boolean;
15
17
  export declare function resetRuntimeOptions(): void;
16
18
  export declare function detectColorPreferenceFromEnv(env?: NodeJS.ProcessEnv): boolean;
@@ -1 +1 @@
1
- const n={nonInteractive:!1,json:!1,yes:!1,dryRun:!1,color:!0};let t={...n};export function setRuntimeOptions(n){t={...t,...n}}export function getRuntimeOptions(){return{...t}}export function isJsonMode(){return t.json}export function isNonInteractive(){return t.nonInteractive||t.json}export function isDryRun(){return t.dryRun}export function shouldAutoConfirm(){return t.yes||t.nonInteractive||t.json}export function isColorEnabled(){return t.color}export function resetRuntimeOptions(){t={...n}}export function detectColorPreferenceFromEnv(n=process.env){return(!n.NO_COLOR||"0"===n.NO_COLOR)&&"0"!==n.FORCE_COLOR&&"false"!==n.FORCE_COLOR}export function emitJson(n,t=n=>process.stdout.write(n+"\n")){t(JSON.stringify(n,null,2))}
1
+ const n={nonInteractive:!1,json:!1,yes:!1,dryRun:!1,color:!0};let t={...n};export function setRuntimeOptions(n){t={...t,...n}}export function getRuntimeOptions(){return{...t}}export function isJsonMode(){return t.json}export function isNonInteractive(){return t.nonInteractive||t.json}export function isDryRun(){return t.dryRun}export function shouldAutoConfirm(){return t.yes||t.nonInteractive||t.json}export function hasExplicitYes(){return t.yes}export function isColorEnabled(){return t.color}export function resetRuntimeOptions(){t={...n}}export function detectColorPreferenceFromEnv(n=process.env){return(!n.NO_COLOR||"0"===n.NO_COLOR)&&"0"!==n.FORCE_COLOR&&"false"!==n.FORCE_COLOR}export function emitJson(n,t=n=>process.stdout.write(n+"\n")){t(JSON.stringify(n,null,2))}
@@ -1,8 +1,10 @@
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. */
4
+ /** The desktop app is in ChatGPT Work mode on our key, where it may refuse to start a thread. */
5
5
  export declare function codexWorkModeOn(): boolean;
6
+ /** Where Codex reads its config, for messages that name the file. */
7
+ export declare function codexConfigPath(): string;
6
8
  export declare const CODEX_ONE_MILLION_CONTEXT_SETTING = "one_million_context";
7
9
  /** For doctor: a local Codex that cannot load the catalog `config.toml` points at (checked afresh, no cache). */
8
10
  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 s}from"fs";import{join as l}from"path";import*as c from"@iarna/toml";import{logger as a}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 _}from"./types.js";import{moveSessionsToProvider as g,NO_STATE_DB as x,runningCodexPids as y}from"../codex-sessions.js";import{getToolDefaultModel as h}from"../model-registry.js";import{configManager as k}from"../config.js";import{maskHome as b,track as j}from"../telemetry.js";import{buildCodexCatalog as v,catalogRejections as w,codexCatalogPath as O,fillFromOlderEntries as S,installedCodexBinaries as M,loadBundledCodexModels as C,loadPreviousCodexModels as E,previousGatewayModelIds as N}from"./codex-catalog.js";import{gatewayModelSpecs as A,modelFamily as T}from"../gateway-models.js";import{findInstalls as P,mirrorInstallCommand as D,brandInstallScripts as I}from"./tool-installs.js";const $=()=>f(),J=()=>l($(),"config.toml"),L="sessions_move_declined",R=["browser_use","browser_use_external","computer_use"];function K(e){const t=e.features;if(!W(t)||!R.every(e=>!1===t[e]))return!1;for(const e of R)delete t[e];return!0}export function codexWorkModeOn(){try{const e=Y().desktop;return W(e)&&"STEPS_PROSE"===e.conversationDetailMode}catch{return!1}}const X=()=>l($(),"auth.json"),B="OPENAI_API_KEY",q="custom",G=1e6,U=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function W(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function z(e,t){return W(e[t])||(e[t]={}),e[t]}function Y(){if(!t(J()))return{};const e=n(J(),"utf-8");if(!e.trim())return{};try{const t=c.parse(e);if(!W(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 F(){if(!t(X()))return{};try{const e=JSON.parse(n(X(),"utf-8"));return W(e)?e:{}}catch{return{}}}let H=null;function Q(e){const t=e.profiles;if(W(t)){for(const[e,o]of Object.entries(t))W(o)&&2===Object.keys(o).length&&o.model_provider===q&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let V=null;const Z=()=>l(k.getConfigDir(),"codex-catalog-check.json");function ee(e){const o=O();t(o)&&n(o,"utf-8")===e||(u(o),m(o,e))}function te(t,r){if(0===r.length)return[];const l=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(Z(),"utf-8")).signature===e}catch{return!1}}(l))return[];const c=w(O(),r);return 0===c.length&&function(e){try{o(k.getConfigDir(),{recursive:!0}),s(Z(),JSON.stringify({signature:e})+"\n")}catch(e){a.logError("codexAdapter.rememberAccepted",e)}}(l),c}function oe(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===G&&delete e.model_context_window,e.model_auto_compact_token_limit===U&&delete e.model_auto_compact_token_limit}(e);const i=()=>{e.model_catalog_json===O()&&delete e.model_catalog_json,n&&(e.model_context_window=G,e.model_auto_compact_token_limit=U)},s=C()??E();if(!s)return a.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),V="skipped",void i();const l=t.catalog,c=e=>v({bundled:e,specs:A("codex",l?.clientModels),...l?{keyModels:l.models}:{},gatewayAvailable:l?.models??N(),oneMillion:n,selectedModel:o,..."gateway"===l?.oneMillionSource?{claudeOneMillion:l.oneMillion}:{}}),d=e=>JSON.stringify(e,null,2)+"\n";let u=c(s);ee(d(u));const m=M(),f=te(d(u),m);if(f.length>0){const e=Date.now();let t=!1;for(const e of f){const o=C(e.bin);if(o){for(const e of[{models:S(u.models,o)},{models:S(c(o).models,o)}])if(ee(d(e)),0===te(d(e),m).length){u=e,t=!0;break}if(t)break}}if(j("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:f[0].error,detail:{rejected_by:f.map(e=>({version:e.version,path:b(e.bin)}))}}),!t)return V={rejectedBy:f[0]},r(O(),{force:!0}),void i()}V={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=O()}export function codexCatalogRejection(){let e;try{e=Y()}catch{return null}return e.model_catalog_json===O()&&t(O())?w(O())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=Y(),n=c.stringify(o),r="string"==typeof o.model?o.model:h("codex");oe(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=c.stringify(o);i!==n&&(u(J()),m(J(),i))}const ne={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:I("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:D(ne),installSpec:ne,configPath:J(),wantsModelCatalog:!0,listedModels(){if(Y().model_catalog_json!==O())return null;try{return(JSON.parse(n(O(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(Y().model_catalog_json!==O())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=Y();K(t)&&(u(J()),m(J(),c.stringify(t)))},async followUp(){if(Y().model_provider!==q)return null;const e=await g(q,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=k.getToolSettings("codex")[L];if("number"==typeof o&&t<=o)return null;const n={count:String(t)};return y().length>0?{hint:d.t("tools.codex.sessions_hidden_running",n)}:{prompt:d.t("tools.codex.sessions_offer",n),hint:d.t("tools.codex.sessions_hidden",n),async run(){const e=await g(q),t=[...new Set(e.moved.map(e=>e.from))].join("、")||"-",o=String(Math.max(e.moved.length,e.dbRows)),n=[{kind:"summary",text:d.t("codex_sessions.moved",{count:o,from:t,target:q})},{kind:"summary",text:d.t("codex_sessions.backup",{dir:e.backupDir??""})}];return e.dbSkipped&&e.dbSkipped!==x&&n.push({kind:"action",text:d.t("codex_sessions.db_skipped",{reason:e.dbSkipped})}),n},decline(){k.setToolSetting("codex",L,t)}}},applyNotes(){if("skipped"===V)return 0===P(ne).length?[]:[{kind:"action",text:d.t("tools.codex.catalog_skipped")}];if(!V)return[];if("rejectedBy"in V){const{bin:e,version:t}=V.rejectedBy;return[{kind:"action",text:d.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of V.listed)e.set(T(t.id),(e.get(T(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(V.listed.length),breakdown:t})},{kind:"detail",text:V.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=h("codex"),o=p(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=_(e.baseUrl),i=Y();i.model=n,i.model_provider=q,i.model_reasoning_effort="high",oe(i,e,n);const s=z(i,"model_providers"),l=W(s[q])?{...s[q]}:{};delete l.env_key;const{experimental_bearer_token:a,...d}=l;s[q]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},Q(i),z(z(i,"tui"),"model_availability_nux")[n]=4;const f=z(i,"features");f.goals=!0,f.js_repl=!1,K(i),u(J()),m(J(),c.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...F(),auth_mode:"apikey",[B]:t};u(X()),m(X(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(e){if(H=function(e){const t=F(),o="string"==typeof t[B]?t[B].trim():"";if(!o)return W(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[B]:n,auth_mode:i,...s}=t;return u(X()),W(s.tokens)?(m(X(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(r(X(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(J()))return;let o;try{o=Y()}catch(e){return void a.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;W(n)&&(delete n[q],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===q&&delete o.model_provider,o.model_catalog_json===O()&&delete o.model_catalog_json,K(o),Q(o),u(J()),m(J(),c.stringify(o))},unloadNotes(){switch(H){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=F()[B];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return Y()}catch(e){return a.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[J(),X(),O()]};export function codexAuthProblem(){try{const e=Y(),t=e.model_providers?.[q];if(e.model_provider!==q||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=F();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,readdirSync as n,readFileSync as r,rmSync as i,statSync as s,writeFileSync as l}from"fs";import{join as a}from"path";import*as c from"@iarna/toml";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as m,atomicWriteFile as p}from"./backup.js";import{codexHome as f,pickModel as g,toolHome as _,withV1 as x}from"./types.js";import{moveSessionsToProvider as h,NO_STATE_DB as y,runningCodexPids as k}from"../codex-sessions.js";import{getToolDefaultModel as b}from"../model-registry.js";import{configManager as v}from"../config.js";import{maskHome as w,track as j}from"../telemetry.js";import{buildCodexCatalog as C,catalogRejections as O,codexCatalogPath as S,fillFromOlderEntries as M,installedCodexBinaries as A,loadBundledCodexModels as E,loadPreviousCodexModels as N,previousGatewayModelIds as P}from"./codex-catalog.js";import{gatewayModelSpecs as T,modelFamily as D}from"../gateway-models.js";import{findInstalls as I,mirrorInstallCommand as L,brandInstallScripts as $}from"./tool-installs.js";const J=()=>f(),G=()=>a(J(),"config.toml"),R="sessions_move_declined",K=["browser_use","browser_use_external","computer_use"];function X(e){const t=e.features;if(!F(t)||!K.every(e=>!1===t[e]))return!1;for(const e of K)delete t[e];return!0}export function codexWorkModeOn(){try{const e=Q();return e.model_provider===U&&F(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function B(){try{if(F(Q().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",a(_(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(a(e,o))));if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(_(),"AppData","Local"),"Packages");try{return n(e).some(e=>/^OpenAI\.(Codex|ChatGPT)/i.test(e))}catch{return!1}}return t("/usr/lib/chatgpt/ChatGPT")}export function codexConfigPath(){return G()}const W=()=>a(J(),"auth.json"),q="OPENAI_API_KEY",U="custom",z=1e6,Y=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function F(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function H(e,t){return F(e[t])||(e[t]={}),e[t]}function Q(){if(!t(G()))return{};const e=r(G(),"utf-8");if(!e.trim())return{};try{const t=c.parse(e);if(!F(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 V(){if(!t(W()))return{};try{const e=JSON.parse(r(W(),"utf-8"));return F(e)?e:{}}catch{return{}}}let Z=null;function ee(e){const t=e.profiles;if(F(t)){for(const[e,o]of Object.entries(t))F(o)&&2===Object.keys(o).length&&o.model_provider===U&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let te=null;const oe=()=>a(v.getConfigDir(),"codex-catalog-check.json");function ne(e){const o=S();t(o)&&r(o,"utf-8")===e||(m(o),p(o,e))}function re(t,n){if(0===n.length)return[];const i=function(t,o){const n=e("sha256").update(t);for(const e of o)try{const t=s(e);n.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{n.update(`\0${e}`)}return n.digest("hex")}(t,n);if(function(e){try{return JSON.parse(r(oe(),"utf-8")).signature===e}catch{return!1}}(i))return[];const a=O(S(),n);return 0===a.length&&function(e){try{o(v.getConfigDir(),{recursive:!0}),l(oe(),JSON.stringify({signature:e})+"\n")}catch(e){d.logError("codexAdapter.rememberAccepted",e)}}(i),a}function ie(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===z&&delete e.model_context_window,e.model_auto_compact_token_limit===Y&&delete e.model_auto_compact_token_limit}(e);const r=()=>{e.model_catalog_json===S()&&delete e.model_catalog_json,n&&(e.model_context_window=z,e.model_auto_compact_token_limit=Y)},s=E()??N();if(!s)return d.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),te="skipped",void r();const l=t.catalog,a=e=>C({bundled:e,specs:T("codex",l?.clientModels),...l?{keyModels:l.models}:{},gatewayAvailable:l?.models??P(),oneMillion:n,selectedModel:o,..."gateway"===l?.oneMillionSource?{claudeOneMillion:l.oneMillion}:{}}),c=e=>JSON.stringify(e,null,2)+"\n";let u=a(s);ne(c(u));const m=A(),p=re(c(u),m);if(p.length>0){const e=Date.now();let t=!1;for(const e of p){const o=E(e.bin);if(o){for(const e of[{models:M(u.models,o)},{models:M(a(o).models,o)}])if(ne(c(e)),0===re(c(e),m).length){u=e,t=!0;break}if(t)break}}if(j("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:p[0].error,detail:{rejected_by:p.map(e=>({version:e.version,path:w(e.bin)}))}}),!t)return te={rejectedBy:p[0]},i(S(),{force:!0}),void r()}te={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=S()}export function codexCatalogRejection(){let e;try{e=Q()}catch{return null}return e.model_catalog_json===S()&&t(S())?O(S())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=Q(),n=c.stringify(o),r="string"==typeof o.model?o.model:b("codex");ie(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=c.stringify(o);i!==n&&(m(G()),p(G(),i))}const se={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:$("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:L(se),installSpec:se,configPath:G(),wantsModelCatalog:!0,listedModels(){if(Q().model_catalog_json!==S())return null;try{return(JSON.parse(r(S(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(Q().model_catalog_json!==S())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=Q();X(t)&&(m(G()),p(G(),c.stringify(t)))},async followUp(){if(Q().model_provider!==U)return null;const e=await h(U,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=v.getToolSettings("codex")[R];if("number"==typeof o&&t<=o)return null;const n={count:String(t)};return k().length>0?{hint:u.t("tools.codex.sessions_hidden_running",n)}:{prompt:u.t("tools.codex.sessions_offer",n),hint:u.t("tools.codex.sessions_hidden",n),async run(){const e=await h(U),t=[...new Set(e.moved.map(e=>e.from))].join("、")||"-",o=String(Math.max(e.moved.length,e.dbRows)),n=[{kind:"summary",text:u.t("codex_sessions.moved",{count:o,from:t,target:U})},{kind:"summary",text:u.t("codex_sessions.backup",{dir:e.backupDir??""})}];return e.dbSkipped&&e.dbSkipped!==y&&n.push({kind:"action",text:u.t("codex_sessions.db_skipped",{reason:e.dbSkipped})}),n},decline(){v.setToolSetting("codex",R,t)}}},applyNotes(){if("skipped"===te)return 0===I(se).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!te)return[];if("rejectedBy"in te){const{bin:e,version:t}=te.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of te.listed)e.set(D(t.id),(e.get(D(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${u.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:u.t("tools.codex.catalog_written",{count:String(te.listed.length),breakdown:t})},{kind:"detail",text:te.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||B()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]},loadPrivateConfig(e){const t=b("codex"),o=g(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=x(e.baseUrl),i=Q();i.model=n,i.model_provider=U,i.model_reasoning_effort="high",ie(i,e,n);const s=H(i,"model_providers"),l=F(s[U])?{...s[U]}:{};delete l.env_key;const{experimental_bearer_token:a,...d}=l;s[U]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},ee(i),H(H(i,"tui"),"model_availability_nux")[n]=4;const u=H(i,"features");u.goals=!0,u.js_repl=!1,X(i),m(G()),p(G(),c.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...V(),auth_mode:"apikey",[q]:t};m(W()),p(W(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(e){if(Z=function(e){const t=V(),o="string"==typeof t[q]?t[q].trim():"";if(!o)return F(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[q]:n,auth_mode:r,...s}=t;return m(W()),F(s.tokens)?(p(W(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(i(W(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(G()))return;let o;try{o=Q()}catch(e){return void d.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;F(n)&&(delete n[U],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===U&&delete o.model_provider,o.model_catalog_json===S()&&delete o.model_catalog_json,X(o),ee(o),m(G()),p(G(),c.stringify(o))},unloadNotes(){switch(Z){case"chatgpt":return[{kind:"action",text:u.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:u.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:u.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=V()[q];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return Q()}catch(e){return d.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[G(),W(),S()]};export function codexAuthProblem(){try{const e=Q(),t=e.model_providers?.[U];if(e.model_provider!==U||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=V();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -205,7 +205,7 @@
205
205
  "codex_work_mode": "Codex desktop mode",
206
206
  "codex_catalog": "Codex model menu",
207
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)",
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",
208
+ "codex_stale_bearer": "{{path}} 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",
209
209
  "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",
210
210
  "claude_login": "Claude account login",
211
211
  "check_other_addresses": "Testing other gateway addresses",
@@ -414,9 +414,9 @@
414
414
  "sessions_offer": "Found {{count}} earlier Codex sessions that are hidden from the list after switching to {{brand}}. Move them over? (backed up first, can be undone)",
415
415
  "sessions_hidden": "{{count}} earlier Codex sessions are hidden from the list after switching to {{brand}}: run {{cli}} codex sessions to move them over (backed up first, can be undone)",
416
416
  "sessions_hidden_running": "{{count}} earlier Codex sessions are hidden from the list after switching to {{brand}}: quit Codex completely (desktop app included), then run {{cli}} codex sessions",
417
- "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",
418
- "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",
419
- "catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen the Codex desktop app to see them",
417
+ "work_mode_hint": "In the Codex desktop app use Codex mode (switch at the top left): on an API key, ChatGPT Work mode may not send messages and shows \"Could not start this chat\", a known OpenAI issue",
418
+ "work_mode_on": "The Codex desktop app is in ChatGPT Work mode, which may not send messages on an API key (\"Could not start this chat\", a known OpenAI issue): switch back to Codex mode at the top left",
419
+ "catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen Codex (desktop app included) to see them",
420
420
  "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",
421
421
  "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",
422
422
  "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",
@@ -517,7 +517,7 @@
517
517
  "codex_running": "Codex is still running (PIDs {{pids}}) and would overwrite the change: quit Codex completely (desktop app and background server included), then run {{cli}} codex sessions",
518
518
  "nothing": "Scanned {{scanned}} sessions; all are already under \"{{target}}\"",
519
519
  "dry_run": "Would move {{count}} sessions from \"{{from}}\" to \"{{target}}\" (dry run, nothing changed)",
520
- "moved": "Moved {{count}} sessions from \"{{from}}\" to \"{{target}}\"; reopen Codex to see them. Run {{cli}} codex sessions again after switching login back",
520
+ "moved": "Moved {{count}} sessions from \"{{from}}\" to \"{{target}}\"; reopen Codex to see them (archived ones in the archive). Run {{cli}} codex sessions again after switching login back",
521
521
  "db_skipped": "Codex's database was not updated ({{reason}}); session files were. Upgrade Node.js to 22.13+ and run {{cli}} codex sessions again",
522
522
  "backup": "Backup and undo record in {{dir}}; undo with {{cli}} codex sessions --undo <that dir>",
523
523
  "undone": "Restored {{files}} session files and {{rows}} database rows"
@@ -205,7 +205,7 @@
205
205
  "codex_work_mode": "Codex 桌面版模式",
206
206
  "codex_catalog": "Codex 模型菜单",
207
207
  "codex_catalog_rejected": "Codex({{version}},{{path}})读不了模型菜单:{{error}}。运行 {{cli}} refresh 重写;仍然失败就升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)",
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> 会删掉它",
208
+ "codex_stale_bearer": "{{path}} 的 [model_providers.custom] 里有 experimental_bearer_token(多半是 cc-switch 等工具代理模式留下的 PROXY_MANAGED),Codex 会把它当令牌发出去,导致 401 Invalid token;运行 {{cli}} setup --key <你的 Key> 会删掉它",
209
209
  "codex_chatgpt_login": "Codex 当前是 ChatGPT 账号登录状态,会把 ChatGPT 的凭据发给 {{brand}},导致 401 Invalid token;运行 {{cli}} setup --key <你的 Key> 改回令牌登录",
210
210
  "claude_login": "Claude 官方登录",
211
211
  "check_other_addresses": "测试其他接入地址",
@@ -414,9 +414,9 @@
414
414
  "sessions_offer": "发现 {{count}} 个以前的 Codex 会话,换到 {{brand}} 后在会话列表里看不到,要搬过来吗?(会先备份,可撤回)",
415
415
  "sessions_hidden": "有 {{count}} 个以前的 Codex 会话换到 {{brand}} 后在列表里看不到:运行 {{cli}} codex sessions 搬过来(会先备份,可撤回)",
416
416
  "sessions_hidden_running": "有 {{count}} 个以前的 Codex 会话换到 {{brand}} 后在列表里看不到:完全退出 Codex(含桌面版)后运行 {{cli}} codex sessions 搬过来",
417
- "work_mode_hint": "Codex 桌面版请用 Codex 模式(左上角切换):用 API Key 时 ChatGPT Work 模式发不出消息,会提示「Could not start this chat」,这是 OpenAI 的已知问题",
418
- "work_mode_on": "Codex 桌面版现在是 ChatGPT Work 模式,用 API Key 时发不出消息(提示「Could not start this chat」,OpenAI 的已知问题):请在左上角切回 Codex 模式",
419
- "catalog_written": "模型菜单 {{count}} 个({{breakdown}}),Codex 桌面版重开后生效",
417
+ "work_mode_hint": "Codex 桌面版请用 Codex 模式(左上角切换):用 API Key 时 ChatGPT Work 模式可能发不出消息,提示「Could not start this chat」,这是 OpenAI 的已知问题",
418
+ "work_mode_on": "Codex 桌面版现在是 ChatGPT Work 模式,用 API Key 时可能发不出消息(提示「Could not start this chat」,OpenAI 的已知问题):请在左上角切回 Codex 模式",
419
+ "catalog_written": "模型菜单 {{count}} 个({{breakdown}}),重开 Codex(含桌面版)后生效",
420
420
  "catalog_skipped": "没读到 Codex 自带的模型列表(未安装或版本低于 0.122),模型菜单只有 GPT;运行 {{cli}} upgrade codex 后再运行 {{cli}} config codex --non-interactive",
421
421
  "catalog_rejected": "Codex({{version}},{{path}})读不了 {{brand}} 的模型菜单,已改回 Codex 自带的菜单(只有 GPT);升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)后运行 {{cli}} refresh",
422
422
  "unload_sign_in": "Codex 已退出 API Key 登录。改用 ChatGPT 订阅:终端运行 codex login;Codex 桌面版完全退出后重开,按提示登录",
@@ -517,7 +517,7 @@
517
517
  "codex_running": "Codex 还在运行(进程 {{pids}}),它会覆盖这次修改:请先完全退出 Codex(含桌面版和后台服务)再运行 {{cli}} codex sessions",
518
518
  "nothing": "已扫描 {{scanned}} 个会话,全都在「{{target}}」下,不用处理",
519
519
  "dry_run": "将把 {{count}} 个会话从「{{from}}」移到「{{target}}」(试运行,未修改)",
520
- "moved": "已把 {{count}} 个会话从「{{from}}」移到「{{target}}」,重开 Codex 后都能在列表里看到;以后换回另一种登录方式时再运行 {{cli}} codex sessions",
520
+ "moved": "已把 {{count}} 个会话从「{{from}}」移到「{{target}}」,重开 Codex 后都能看到(已归档的在归档里);以后换回另一种登录方式时再运行 {{cli}} codex sessions",
521
521
  "db_skipped": "Codex 的数据库没有更新({{reason}}),会话文件已改好;升级 Node.js 到 22.13 以上后再运行 {{cli}} codex sessions",
522
522
  "backup": "备份与还原记录在 {{dir}},还原用 {{cli}} codex sessions --undo <这个目录>",
523
523
  "undone": "已还原 {{files}} 个会话文件、{{rows}} 条数据库记录"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/api-helper",
3
- "version": "0.0.124",
3
+ "version": "0.0.125",
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",