@vibeapi/api-helper 0.0.122 → 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.
- package/dist/commands/codex-sessions.js +1 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/lib/codex-sessions.d.ts +2 -0
- package/dist/lib/codex-sessions.js +1 -1
- package/dist/lib/post-apply.d.ts +3 -2
- package/dist/lib/post-apply.js +1 -1
- package/dist/lib/tools/codex.d.ts +2 -0
- package/dist/lib/tools/codex.js +1 -1
- package/dist/lib/tools/types.d.ts +16 -0
- package/dist/locales/en_US.json +6 -0
- package/dist/locales/zh_CN.json +6 -0
- package/package.json +1 -1
|
@@ -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,
|
|
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??""}))))}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{refreshClientConfig as o}from"../lib/client-config-sync.js";import
|
|
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)}
|
|
@@ -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
|
|
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}}
|
package/dist/lib/post-apply.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What setup and the wizard ask right after writing configs
|
|
3
|
-
*
|
|
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>;
|
package/dist/lib/post-apply.js
CHANGED
|
@@ -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
|
|
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,6 +1,8 @@
|
|
|
1
1
|
import { type ToolAdapter } from './types.js';
|
|
2
2
|
import type { ModelCatalog } from '../model-catalog.js';
|
|
3
3
|
import { type CatalogRejection } from './codex-catalog.js';
|
|
4
|
+
/** The desktop app is in ChatGPT Work mode, which cannot start a thread on an API key. */
|
|
5
|
+
export declare function codexWorkModeOn(): boolean;
|
|
4
6
|
export declare const CODEX_ONE_MILLION_CONTEXT_SETTING = "one_million_context";
|
|
5
7
|
/** For doctor: a local Codex that cannot load the catalog `config.toml` points at (checked afresh, no cache). */
|
|
6
8
|
export declare function codexCatalogRejection(): CatalogRejection | null;
|
package/dist/lib/tools/codex.js
CHANGED
|
@@ -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
|
|
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.
|
package/dist/locales/en_US.json
CHANGED
|
@@ -202,6 +202,7 @@
|
|
|
202
202
|
"claude_request_failed": "Request failed: {{reason}}",
|
|
203
203
|
"deep_hint": "Add --deep to send one real request through Claude Code (uses a little credit) and confirm the whole chain works",
|
|
204
204
|
"codex_auth": "Codex token",
|
|
205
|
+
"codex_work_mode": "Codex desktop mode",
|
|
205
206
|
"codex_catalog": "Codex model menu",
|
|
206
207
|
"codex_catalog_rejected": "Codex ({{version}}, {{path}}) cannot load the model menu: {{error}}. Run {{cli}} refresh to rewrite it; if it still fails, update that Codex (the desktop app's copy updates with the ChatGPT app)",
|
|
207
208
|
"codex_stale_bearer": "~/.codex/config.toml has experimental_bearer_token under [model_providers.custom] (usually PROXY_MANAGED left by cc-switch's proxy mode); Codex sends it as the token and gets 401 Invalid token. {{cli}} setup --key <your key> removes it",
|
|
@@ -410,6 +411,11 @@
|
|
|
410
411
|
"list_separator": ", "
|
|
411
412
|
},
|
|
412
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",
|
|
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",
|
|
413
419
|
"catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen the Codex desktop app to see them",
|
|
414
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",
|
|
415
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",
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -202,6 +202,7 @@
|
|
|
202
202
|
"claude_request_failed": "请求失败:{{reason}}",
|
|
203
203
|
"deep_hint": "加上 --deep 会用 Claude Code 实际发一条请求(消耗少量额度),确认整条链路能用",
|
|
204
204
|
"codex_auth": "Codex 令牌",
|
|
205
|
+
"codex_work_mode": "Codex 桌面版模式",
|
|
205
206
|
"codex_catalog": "Codex 模型菜单",
|
|
206
207
|
"codex_catalog_rejected": "Codex({{version}},{{path}})读不了模型菜单:{{error}}。运行 {{cli}} refresh 重写;仍然失败就升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)",
|
|
207
208
|
"codex_stale_bearer": "~/.codex/config.toml 的 [model_providers.custom] 里有 experimental_bearer_token(多半是 cc-switch 等工具代理模式留下的 PROXY_MANAGED),Codex 会把它当令牌发出去,导致 401 Invalid token;运行 {{cli}} setup --key <你的 Key> 会删掉它",
|
|
@@ -410,6 +411,11 @@
|
|
|
410
411
|
"list_separator": "、"
|
|
411
412
|
},
|
|
412
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 搬过来",
|
|
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 模式",
|
|
413
419
|
"catalog_written": "模型菜单 {{count}} 个({{breakdown}}),Codex 桌面版重开后生效",
|
|
414
420
|
"catalog_skipped": "没读到 Codex 自带的模型列表(未安装或版本低于 0.122),模型菜单只有 GPT;运行 {{cli}} upgrade codex 后再运行 {{cli}} config codex --non-interactive",
|
|
415
421
|
"catalog_rejected": "Codex({{version}},{{path}})读不了 {{brand}} 的模型菜单,已改回 Codex 自带的菜单(只有 GPT);升级这份 Codex(桌面版自带的随 ChatGPT 应用升级)后运行 {{cli}} refresh",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/api-helper",
|
|
3
|
-
"version": "0.0.
|
|
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",
|