@vibeapi/api-helper 0.0.123 → 0.0.124

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 o from"chalk";import{i18n as s}from"../lib/i18n.js";import{isDryRun as e}from"../lib/runtime-options.js";import{currentCodexProvider as n,moveSessionsToProvider as i,runningCodexPids as t,undoSessionMove as r}from"../lib/codex-sessions.js";export async function codexSessionsCommand(d){const c=t();if(c.length>0)return console.error(o.yellow(s.t("codex_sessions.codex_running",{pids:c.join(", ")}))),void(process.exitCode=1);if(d.undo){const{files:e,dbRows:n}=await r(d.undo);return void console.log(o.green("√ "+s.t("codex_sessions.undone",{files:String(e),rows:String(n)})))}const l=d.to?.trim()||n(),g=await i(l,{dryRun:e()}),m=Math.max(g.moved.length,g.dbRows);if(0===m)return void console.log(s.t("codex_sessions.nothing",{target:l,scanned:String(g.scanned)}));const a=[...new Set(g.moved.map(o=>o.from))].join("、")||"-";e()?console.log(s.t("codex_sessions.dry_run",{count:String(m),from:a,target:l})):(console.log(o.green("√ "+s.t("codex_sessions.moved",{count:String(m),from:a,target:l}))),g.dbSkipped&&console.log(o.yellow(" ! "+s.t("codex_sessions.db_skipped",{reason:g.dbSkipped}))),console.log(o.gray(" "+s.t("codex_sessions.backup",{dir:g.backupDir??""}))))}
1
+ import o from"chalk";import{i18n as s}from"../lib/i18n.js";import{isDryRun as e}from"../lib/runtime-options.js";import{currentCodexProvider as n,moveSessionsToProvider as i,NO_STATE_DB as t,runningCodexPids as r,undoSessionMove as d}from"../lib/codex-sessions.js";export async function codexSessionsCommand(c){const l=r();if(l.length>0)return console.error(o.yellow(s.t("codex_sessions.codex_running",{pids:l.join(", ")}))),void(process.exitCode=1);if(c.undo){const{files:e,dbRows:n}=await d(c.undo);return void console.log(o.green("√ "+s.t("codex_sessions.undone",{files:String(e),rows:String(n)})))}const g=c.to?.trim()||n(),m=await i(g,{dryRun:e()}),a=Math.max(m.moved.length,m.dbRows);if(0===a)return void console.log(s.t("codex_sessions.nothing",{target:g,scanned:String(m.scanned)}));const p=[...new Set(m.moved.map(o=>o.from))].join("、")||"-";e()?console.log(s.t("codex_sessions.dry_run",{count:String(a),from:p,target:g})):(console.log(o.green("√ "+s.t("codex_sessions.moved",{count:String(a),from:p,target:g}))),m.dbSkipped&&m.dbSkipped!==t&&console.log(o.yellow(" ! "+s.t("codex_sessions.db_skipped",{reason:m.dbSkipped}))),console.log(o.gray(" "+s.t("codex_sessions.backup",{dir:m.backupDir??""}))))}
@@ -29,6 +29,8 @@ export declare function currentCodexProvider(): string;
29
29
  export declare function setCodexProcessCheckForTests(fake: () => number[]): void;
30
30
  /** PIDs of running Codex processes (CLI, desktop app, background app-server); they would overwrite the edits. */
31
31
  export declare function runningCodexPids(): number[];
32
+ /** `dbSkipped` when Codex has no state DB yet: nothing to update, not a problem to report. */
33
+ export declare const NO_STATE_DB = "no state DB";
32
34
  /** Move every local session under `target`. `dryRun` only counts. */
33
35
  export declare function moveSessionsToProvider(target: string, options?: {
34
36
  dryRun?: boolean;
@@ -1 +1 @@
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 f,readSync as l,readdirSync as c,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 w(e){const r=a(e,"r");try{const e=[],t=Buffer.alloc(65536);for(;;){const o=l(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 x(e,t,r){const{atime:s,mtime:i}=p(e),a=`${e}.${v}-tmp`,f=n(a);f.write(t),await y(o(e,{start:r}),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 async function moveSessionsToProvider(e,t={}){const o=function(){const e=[],t=r=>{if(s(r))for(const o of c(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=w(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=c(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",l=[],d=null;if(a){const t=await N(a);"string"==typeof t?f=t:(d=t,l=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:o.length,moved:n.map(({file:e,from:t})=>({file:e,from:t})),dbRows:l.length,dbSkipped:f,backupDir:null};if(t.dryRun||0===n.length&&0===l.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)&&r(a+e,h(m,`state.sqlite${e}`));const y={target:e,moved:p.moved,threads:l,db:a};u(h(m,"manifest.json"),JSON.stringify(y,null,2)+"\n");for(const e of n)await x(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 r=0;for(const{file:e,from:o}of t.moved){if(!s(e))continue;const t=w(e),n=JSON.parse(t);n.payload&&(n.payload.model_provider=o,await x(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
+ 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,6 +1,7 @@
1
1
  /**
2
- * What setup and the wizard ask right after writing configs, in interactive
3
- * terminals only: upgrade tools too old for the config (`vibecli upgrade`), and
2
+ * What setup and the wizard ask right after writing configs: UAC changes and
3
+ * adapter follow-ups (non-interactive: only their hint), then, in interactive
4
+ * terminals only, upgrade tools too old for the config (`vibecli upgrade`) and
4
5
  * restart GUI apps that are running and would otherwise keep the old config.
5
6
  */
6
7
  export declare function postApplyPrompts(tools: string[]): Promise<void>;
@@ -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 a}from"./tool-upgrade.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 l=!n()&&process.stdin.isTTY;for(const n of t){const t=s(n)?.pendingElevation?.();if(!t)continue;if(!(i()||l&&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})))}if(!l)return;const f=t.filter(o=>s(o)?.installSpec&&r.outdatedVersion(o));if(f.length>0){const o=f.map(o=>s(o)?.displayName??o).join("、");if(await p(e.t("setup.offer_upgrade",{tools:o})))for(const o of f)await a(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{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 +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 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}}
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}}
@@ -13,6 +13,13 @@ export declare function toolHome(): string;
13
13
  * `~/.codex`.
14
14
  */
15
15
  export declare function codexHome(): string;
16
+ export interface ToolFollowUp {
17
+ prompt: string;
18
+ hint: string;
19
+ run(): Promise<ApplyNote[]>;
20
+ /** The user said no: do not ask again about the same thing. */
21
+ decline(): void;
22
+ }
16
23
  /**
17
24
  * Everything a tool adapter needs to write a working private-provider config.
18
25
  * Built by ToolManager from the active profile (api key + base url + the models
@@ -84,6 +91,15 @@ export interface ToolAdapter {
84
91
  prompt: string;
85
92
  run(): boolean;
86
93
  } | null;
94
+ /**
95
+ * A reversible change the user should agree to, offered after the config is
96
+ * written (setup, the wizard, `base`): asked with default yes in a terminal,
97
+ * done without asking under `--yes`, otherwise only `hint` is printed. A bare
98
+ * hint when it cannot be done right now; null when there is nothing to offer.
99
+ */
100
+ followUp?(): Promise<ToolFollowUp | {
101
+ hint: string;
102
+ } | null>;
87
103
  /**
88
104
  * GUI apps that only read their config at start: setup and the wizard offer
89
105
  * to restart a running app after writing it.
@@ -411,6 +411,9 @@
411
411
  "list_separator": ", "
412
412
  },
413
413
  "codex": {
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
+ "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
+ "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",
414
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",
415
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",
416
419
  "catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen the Codex desktop app to see them",
@@ -411,6 +411,9 @@
411
411
  "list_separator": "、"
412
412
  },
413
413
  "codex": {
414
+ "sessions_offer": "发现 {{count}} 个以前的 Codex 会话,换到 {{brand}} 后在会话列表里看不到,要搬过来吗?(会先备份,可撤回)",
415
+ "sessions_hidden": "有 {{count}} 个以前的 Codex 会话换到 {{brand}} 后在列表里看不到:运行 {{cli}} codex sessions 搬过来(会先备份,可撤回)",
416
+ "sessions_hidden_running": "有 {{count}} 个以前的 Codex 会话换到 {{brand}} 后在列表里看不到:完全退出 Codex(含桌面版)后运行 {{cli}} codex sessions 搬过来",
414
417
  "work_mode_hint": "Codex 桌面版请用 Codex 模式(左上角切换):用 API Key 时 ChatGPT Work 模式发不出消息,会提示「Could not start this chat」,这是 OpenAI 的已知问题",
415
418
  "work_mode_on": "Codex 桌面版现在是 ChatGPT Work 模式,用 API Key 时发不出消息(提示「Could not start this chat」,OpenAI 的已知问题):请在左上角切回 Codex 模式",
416
419
  "catalog_written": "模型菜单 {{count}} 个({{breakdown}}),Codex 桌面版重开后生效",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/api-helper",
3
- "version": "0.0.123",
3
+ "version": "0.0.124",
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",