@vibeapi/cli 0.0.62 → 0.0.64

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.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `vibecli codex sessions [--to <provider>] [--undo <backup dir>]`: show every local
3
+ * Codex session again by moving them all under the provider Codex uses now.
4
+ */
5
+ export declare function codexSessionsCommand(options: {
6
+ to?: string;
7
+ undo?: string;
8
+ }): Promise<void>;
@@ -0,0 +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 +1 @@
1
- import{i18n as n}from"../lib/i18n.js";import{isJsonMode as e,emitJson as o}from"../lib/runtime-options.js";const i=["init","setup","lang","auth","doctor","status","logs","completion","update","upgrade","bench","config","enter"],c=["profile","preset","backup","claude-code","codex"],s=["list","create","use","delete"],l=["list","save","use","delete"],t=["list","create","restore"],r=["revoke","reload"],m=["show","set"],a=["path","list","tail"],u=["bash","zsh","fish"],p=["--base","--non-interactive","--json","--yes","--dry-run","--no-color","--help","--version"];export async function completionCommand(_){const b=(_[0]||"").toLowerCase();if(!u.includes(b))return e()?void o({ok:!1,error:"unsupported_shell",shell:b}):void console.log(n.t("completion.unsupported_shell",{shell:b}));const $="bash"===b?`# vibecli bash completion\n_vibecli_completions() {\n local cur prev\n COMPREPLY=()\n cur="\${COMP_WORDS[COMP_CWORD]}"\n prev="\${COMP_WORDS[COMP_CWORD-1]}"\n\n case "\${prev}" in\n vibecli)\n COMPREPLY=( $(compgen -W "${i.join(" ")} ${p.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n config)\n COMPREPLY=( $(compgen -W "${c.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n profile)\n COMPREPLY=( $(compgen -W "${s.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n preset)\n COMPREPLY=( $(compgen -W "${l.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n backup)\n COMPREPLY=( $(compgen -W "${t.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n auth)\n COMPREPLY=( $(compgen -W "${r.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n lang)\n COMPREPLY=( $(compgen -W "${m.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n logs)\n COMPREPLY=( $(compgen -W "${a.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n completion)\n COMPREPLY=( $(compgen -W "${u.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n esac\n\n if [[ "\${cur}" == --* ]]; then\n COMPREPLY=( $(compgen -W "${p.join(" ")}" -- "\${cur}") )\n return 0\n fi\n}\ncomplete -F _vibecli_completions vibecli\n`:"zsh"===b?`#compdef vibecli\n_vibecli() {\n local -a commands subcommands\n local context state line\n\n _arguments -C \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --version)'{-v,--version}'[Show version]' \\\n '--base[Custom API base URL]:url:' \\\n '--non-interactive[Disable interactive prompts]' \\\n '--json[Emit JSON output]' \\\n '(-y --yes)'{-y,--yes}'[Auto-confirm prompts]' \\\n '--dry-run[Show planned changes without writing]' \\\n '--no-color[Disable colored output]' \\\n '1:command:->command' \\\n '*::arg:->args'\n\n case $state in\n command)\n commands=(\n${i.map(n=>` '${n}:vibecli ${n}'`).join("\n")}\n )\n _describe 'command' commands\n ;;\n args)\n case $line[1] in\n config)\n subcommands=(${c.map(n=>`'${n}'`).join(" ")})\n _describe 'config sub' subcommands\n ;;\n auth) _values 'auth sub' ${r.join(" ")} ;;\n lang) _values 'lang sub' ${m.join(" ")} ;;\n logs) _values 'logs action' ${a.join(" ")} ;;\n completion) _values 'shell' ${u.join(" ")} ;;\n esac\n ;;\n esac\n}\ncompdef _vibecli vibecli\n`:`# vibecli fish completion\ncomplete -c vibecli -n "__fish_use_subcommand" -a "${i.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from config" -a "${c.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from profile" -a "${s.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from preset" -a "${l.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from backup" -a "${t.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from auth" -a "${r.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from lang" -a "${m.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from logs" -a "${a.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from completion" -a "${u.join(" ")}"\ncomplete -c vibecli -l base -d 'Custom API base URL'\ncomplete -c vibecli -l non-interactive -d 'Disable interactive prompts'\ncomplete -c vibecli -l json -d 'Emit JSON output'\ncomplete -c vibecli -l yes -s y -d 'Auto-confirm prompts'\ncomplete -c vibecli -l dry-run -d 'Show planned changes without writing'\ncomplete -c vibecli -l no-color -d 'Disable colored output'\n`;e()?o({shell:b,script:$}):(console.log($),"bash"===b?console.log("# "+n.t("completion.instructions_bash")):"zsh"===b?console.log("# "+n.t("completion.instructions_zsh")):console.log("# "+n.t("completion.instructions_fish")))}
1
+ import{i18n as n}from"../lib/i18n.js";import{isJsonMode as e,emitJson as o}from"../lib/runtime-options.js";const i=["init","setup","lang","auth","doctor","status","logs","completion","update","upgrade","codex","bench","config","enter"],c=["profile","preset","backup","claude-code","codex"],s=["list","create","use","delete"],l=["list","save","use","delete"],t=["list","create","restore"],r=["revoke","reload"],m=["show","set"],a=["path","list","tail"],u=["bash","zsh","fish"],p=["--base","--non-interactive","--json","--yes","--dry-run","--no-color","--help","--version"];export async function completionCommand(_){const b=(_[0]||"").toLowerCase();if(!u.includes(b))return e()?void o({ok:!1,error:"unsupported_shell",shell:b}):void console.log(n.t("completion.unsupported_shell",{shell:b}));const $="bash"===b?`# vibecli bash completion\n_vibecli_completions() {\n local cur prev\n COMPREPLY=()\n cur="\${COMP_WORDS[COMP_CWORD]}"\n prev="\${COMP_WORDS[COMP_CWORD-1]}"\n\n case "\${prev}" in\n vibecli)\n COMPREPLY=( $(compgen -W "${i.join(" ")} ${p.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n config)\n COMPREPLY=( $(compgen -W "${c.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n profile)\n COMPREPLY=( $(compgen -W "${s.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n preset)\n COMPREPLY=( $(compgen -W "${l.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n backup)\n COMPREPLY=( $(compgen -W "${t.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n auth)\n COMPREPLY=( $(compgen -W "${r.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n lang)\n COMPREPLY=( $(compgen -W "${m.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n logs)\n COMPREPLY=( $(compgen -W "${a.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n completion)\n COMPREPLY=( $(compgen -W "${u.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n esac\n\n if [[ "\${cur}" == --* ]]; then\n COMPREPLY=( $(compgen -W "${p.join(" ")}" -- "\${cur}") )\n return 0\n fi\n}\ncomplete -F _vibecli_completions vibecli\n`:"zsh"===b?`#compdef vibecli\n_vibecli() {\n local -a commands subcommands\n local context state line\n\n _arguments -C \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --version)'{-v,--version}'[Show version]' \\\n '--base[Custom API base URL]:url:' \\\n '--non-interactive[Disable interactive prompts]' \\\n '--json[Emit JSON output]' \\\n '(-y --yes)'{-y,--yes}'[Auto-confirm prompts]' \\\n '--dry-run[Show planned changes without writing]' \\\n '--no-color[Disable colored output]' \\\n '1:command:->command' \\\n '*::arg:->args'\n\n case $state in\n command)\n commands=(\n${i.map(n=>` '${n}:vibecli ${n}'`).join("\n")}\n )\n _describe 'command' commands\n ;;\n args)\n case $line[1] in\n config)\n subcommands=(${c.map(n=>`'${n}'`).join(" ")})\n _describe 'config sub' subcommands\n ;;\n auth) _values 'auth sub' ${r.join(" ")} ;;\n lang) _values 'lang sub' ${m.join(" ")} ;;\n logs) _values 'logs action' ${a.join(" ")} ;;\n completion) _values 'shell' ${u.join(" ")} ;;\n esac\n ;;\n esac\n}\ncompdef _vibecli vibecli\n`:`# vibecli fish completion\ncomplete -c vibecli -n "__fish_use_subcommand" -a "${i.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from config" -a "${c.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from profile" -a "${s.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from preset" -a "${l.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from backup" -a "${t.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from auth" -a "${r.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from lang" -a "${m.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from logs" -a "${a.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from completion" -a "${u.join(" ")}"\ncomplete -c vibecli -l base -d 'Custom API base URL'\ncomplete -c vibecli -l non-interactive -d 'Disable interactive prompts'\ncomplete -c vibecli -l json -d 'Emit JSON output'\ncomplete -c vibecli -l yes -s y -d 'Auto-confirm prompts'\ncomplete -c vibecli -l dry-run -d 'Show planned changes without writing'\ncomplete -c vibecli -l no-color -d 'Disable colored output'\n`;e()?o({shell:b,script:$}):(console.log($),"bash"===b?console.log("# "+n.t("completion.instructions_bash")):"zsh"===b?console.log("# "+n.t("completion.instructions_zsh")):console.log("# "+n.t("completion.instructions_fish")))}
@@ -1 +1 @@
1
- import t from"chalk";import o from"ora";import{spawnSync as s}from"child_process";import{readFileSync as e}from"fs";import{connect as a}from"net";import{join as n}from"path";import{configManager as r}from"../lib/config.js";import{toolManager as c}from"../lib/tool-manager.js";import{i18n as i}from"../lib/i18n.js";import{validateApiKey as l}from"../lib/api-validator.js";import{getAdapter as d}from"../lib/tools/registry.js";import{findInstalls as p,installLabel as u}from"../lib/tools/tool-installs.js";import{detectManagedOverrides as m}from"../lib/tools/claude-managed.js";import{toolHome as g}from"../lib/tools/types.js";const _=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function f(t,o,s=3e3){return new Promise(e=>{const n=a({host:t,port:o}),r=t=>{n.destroy(),e(t)};n.setTimeout(s,()=>r(!1)),n.once("connect",()=>r(!0)),n.once("error",()=>r(!1))})}export async function doctorCommand(a={}){const h=o(i.t("doctor.checking")).start(),y=[],w=Number(process.versions.node.split(".")[0]);y.push({name:"Node.js",status:w>=20?"pass":"fail",message:`v${process.versions.node}`}),h.text=i.t("doctor.check_proxy"),y.push(await async function(){const t=i.t("doctor.proxy"),o=new Map;for(const t of _){const s=process.env[t]?.trim();s&&!o.has(s)&&o.set(s,t)}if(0===o.size)return{name:t,status:"pass",message:i.t("doctor.proxy_none")};const s=[];for(const[t,e]of o){let o;try{o=new URL(t.includes("://")?t:`http://${t}`)}catch{s.push(`${e}=${t}`);continue}const a=Number(o.port)||("https:"===o.protocol?443:o.protocol.startsWith("socks")?1080:80);await f(o.hostname,a)||s.push(`${e}=${t}`)}return 0===s.length?{name:t,status:"pass",message:[...o.keys()].join(", ")}:{name:t,status:"fail",message:i.t("doctor.proxy_dead",{proxies:s.join(", ")})}}()),h.text=i.t("doctor.check_api_key_network");const $=r.getApiKey(),T=i.t("doctor.api_key_network");if($){const t=await l($);t.valid?y.push({name:T,status:"pass"}):"invalid_api_key"===t.error?y.push({name:T,status:"fail",message:i.t("doctor.api_key_invalid")}):y.push({name:T,status:"fail",message:`${i.t("doctor.network_error")}: ${t.message??t.error}`})}else y.push({name:T,status:"fail",message:i.t("doctor.api_key_missing")});h.text=i.t("doctor.check_tools");const j=c.getInstalledTools();y.push({name:i.t("doctor.installed_tools"),status:j.length>0?"pass":"warn",message:j.length>0?j.map(t=>d(t)?.displayName??t).join(", "):i.t("doctor.no_tools_installed")}),y.push(...function(){const t=[];for(const o of c.getInstalledTools()){const s=d(o);if(!s?.installSpec)continue;const e=p(s.installSpec);if(0===e.length)continue;const a=[],n=c.outdatedVersion(o);n&&a.push(i.t("doctor.tool_outdated",{min:n.min})),e.length>1&&a.push(i.t("doctor.tool_duplicates",{others:e.slice(1).map(u).join("、")})),t.push({name:s.displayName,status:a.length>0?"warn":"pass",message:[u(e[0]),...a,...a.length>0?[i.t("doctor.run_upgrade",{tool:o})]:[]].join("\n ")})}return t}());const k=function(){if(!c.isToolInstalled("claude-code"))return null;const t=i.t("doctor.claude_config"),o=m();if(o.length>0){const s=o.map(t=>`${t.source}: ${t.keys.join(", ")}`).join("; ");return{name:t,status:"fail",message:i.t("doctor.claude_managed",{detail:s})}}let s={};try{s=JSON.parse(e(n(g(),".claude","settings.json"),"utf-8")).env??{}}catch{}const a=r.getApiKeyForTool("claude-code");return s.ANTHROPIC_AUTH_TOKEN&&s.ANTHROPIC_BASE_URL?s.ANTHROPIC_BASE_URL!==r.getBaseUrl()||a&&s.ANTHROPIC_AUTH_TOKEN!==a?{name:t,status:"warn",message:i.t("doctor.claude_config_changed")}:{name:t,status:"pass"}:{name:t,status:"warn",message:i.t("doctor.claude_not_configured")}}();k&&y.push(k),"win32"===process.platform&&j.includes("claude-code")&&!process.env.CLAUDE_CODE_GIT_BASH_PATH&&y.push({name:"Git for Windows",status:c.isGitInstalled()?"pass":"warn",message:c.isGitInstalled()?void 0:i.t("tools.claude_code.no_git_bash")}),a.deep&&j.includes("claude-code")&&(h.text=i.t("doctor.check_claude_request"),y.push(function(){const t=i.t("doctor.claude_request"),o=p(d("claude-code").installSpec)[0];if(!o)return{name:t,status:"warn",message:i.t("doctor.claude_request_skipped")};const e={encoding:"utf-8",timeout:12e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},a=["-p","Reply with the single word OK."],n=/\.cmd$/i.test(o.path)?s(`"${o.path}" ${a.map(t=>`"${t}"`).join(" ")}`,{...e,shell:!0}):s(o.path,a,e),r=`${n.stdout??""}`.trim();if(0===n.status&&r)return{name:t,status:"pass",message:r.slice(0,80)};const c=(`${n.stderr??""}`.trim()||r||n.error?.message||`exit ${n.status}`).slice(0,300);return{name:t,status:"fail",message:i.t("doctor.claude_request_failed",{reason:c})}}())),h.stop(),console.log("\n"+t.bold(i.t("doctor.results_title"))+"\n");const x={pass:t.green("√"),warn:t.yellow("!"),fail:t.red("×")};for(const o of y)console.log(`${x[o.status]} ${o.name}`),o.message&&console.log(t.gray(` ${o.message}`));y.every(t=>"pass"===t.status)&&console.log("\n"+t.green.bold(i.t("doctor.all_good"))),!a.deep&&j.includes("claude-code")&&console.log(t.gray("\n"+i.t("doctor.deep_hint"))),y.some(t=>"fail"===t.status)&&(process.exitCode=1)}
1
+ import t from"chalk";import o from"ora";import{spawnSync as s}from"child_process";import{readFileSync as e}from"fs";import{connect as a}from"net";import{join as n}from"path";import{configManager as r}from"../lib/config.js";import{toolManager as c}from"../lib/tool-manager.js";import{i18n as i}from"../lib/i18n.js";import{validateApiKey as l}from"../lib/api-validator.js";import{getAdapter as d}from"../lib/tools/registry.js";import{findInstalls as p,installLabel as u}from"../lib/tools/tool-installs.js";import{detectManagedOverrides as m}from"../lib/tools/claude-managed.js";import{codexAuthProblem as _}from"../lib/tools/codex.js";import{toolHome as g}from"../lib/tools/types.js";const f=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy","ALL_PROXY","all_proxy"];function h(t,o,s=3e3){return new Promise(e=>{const n=a({host:t,port:o}),r=t=>{n.destroy(),e(t)};n.setTimeout(s,()=>r(!1)),n.once("connect",()=>r(!0)),n.once("error",()=>r(!1))})}export async function doctorCommand(a={}){const y=o(i.t("doctor.checking")).start(),w=[],$=Number(process.versions.node.split(".")[0]);w.push({name:"Node.js",status:$>=20?"pass":"fail",message:`v${process.versions.node}`}),y.text=i.t("doctor.check_proxy"),w.push(await async function(){const t=i.t("doctor.proxy"),o=new Map;for(const t of f){const s=process.env[t]?.trim();s&&!o.has(s)&&o.set(s,t)}if(0===o.size)return{name:t,status:"pass",message:i.t("doctor.proxy_none")};const s=[];for(const[t,e]of o){let o;try{o=new URL(t.includes("://")?t:`http://${t}`)}catch{s.push(`${e}=${t}`);continue}const a=Number(o.port)||("https:"===o.protocol?443:o.protocol.startsWith("socks")?1080:80);await h(o.hostname,a)||s.push(`${e}=${t}`)}return 0===s.length?{name:t,status:"pass",message:[...o.keys()].join(", ")}:{name:t,status:"fail",message:i.t("doctor.proxy_dead",{proxies:s.join(", ")})}}()),y.text=i.t("doctor.check_api_key_network");const x=r.getApiKey(),j=i.t("doctor.api_key_network");if(x){const t=await l(x);t.valid?w.push({name:j,status:"pass"}):"invalid_api_key"===t.error?w.push({name:j,status:"fail",message:i.t("doctor.api_key_invalid")}):w.push({name:j,status:"fail",message:`${i.t("doctor.network_error")}: ${t.message??t.error}`})}else w.push({name:j,status:"fail",message:i.t("doctor.api_key_missing")});y.text=i.t("doctor.check_tools");const T=c.getInstalledTools();w.push({name:i.t("doctor.installed_tools"),status:T.length>0?"pass":"warn",message:T.length>0?T.map(t=>d(t)?.displayName??t).join(", "):i.t("doctor.no_tools_installed")}),w.push(...function(){const t=[];for(const o of c.getInstalledTools()){const s=d(o);if(!s?.installSpec)continue;const e=p(s.installSpec);if(0===e.length)continue;const a=[],n=c.outdatedVersion(o);n&&a.push(i.t("doctor.tool_outdated",{min:n.min})),e.length>1&&a.push(i.t("doctor.tool_duplicates",{others:e.slice(1).map(u).join("、")})),t.push({name:s.displayName,status:a.length>0?"warn":"pass",message:[u(e[0]),...a,...a.length>0?[i.t("doctor.run_upgrade",{tool:o})]:[]].join("\n ")})}return t}());const k=function(){if(!c.isToolInstalled("claude-code"))return null;const t=i.t("doctor.claude_config"),o=m();if(o.length>0){const s=o.map(t=>`${t.source}: ${t.keys.join(", ")}`).join("; ");return{name:t,status:"fail",message:i.t("doctor.claude_managed",{detail:s})}}let s={};try{s=JSON.parse(e(n(g(),".claude","settings.json"),"utf-8")).env??{}}catch{}const a=r.getApiKeyForTool("claude-code");return s.ANTHROPIC_AUTH_TOKEN&&s.ANTHROPIC_BASE_URL?s.ANTHROPIC_BASE_URL!==r.getBaseUrl()||a&&s.ANTHROPIC_AUTH_TOKEN!==a?{name:t,status:"warn",message:i.t("doctor.claude_config_changed")}:{name:t,status:"pass"}:{name:t,status:"warn",message:i.t("doctor.claude_not_configured")}}();k&&w.push(k);const b=T.includes("codex")?_():null;T.includes("codex")&&w.push({name:i.t("doctor.codex_auth"),status:b?"fail":"pass",message:b?i.t(`doctor.codex_${b}`):void 0}),"win32"===process.platform&&T.includes("claude-code")&&!process.env.CLAUDE_CODE_GIT_BASH_PATH&&w.push({name:"Git for Windows",status:c.isGitInstalled()?"pass":"warn",message:c.isGitInstalled()?void 0:i.t("tools.claude_code.no_git_bash")}),a.deep&&T.includes("claude-code")&&(y.text=i.t("doctor.check_claude_request"),w.push(function(){const t=i.t("doctor.claude_request"),o=p(d("claude-code").installSpec)[0];if(!o)return{name:t,status:"warn",message:i.t("doctor.claude_request_skipped")};const e={encoding:"utf-8",timeout:12e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},a=["-p","Reply with the single word OK."],n=/\.cmd$/i.test(o.path)?s(`"${o.path}" ${a.map(t=>`"${t}"`).join(" ")}`,{...e,shell:!0}):s(o.path,a,e),r=`${n.stdout??""}`.trim();if(0===n.status&&r)return{name:t,status:"pass",message:r.slice(0,80)};const c=(`${n.stderr??""}`.trim()||r||n.error?.message||`exit ${n.status}`).slice(0,300);return{name:t,status:"fail",message:i.t("doctor.claude_request_failed",{reason:c})}}())),y.stop(),console.log("\n"+t.bold(i.t("doctor.results_title"))+"\n");const A={pass:t.green("√"),warn:t.yellow("!"),fail:t.red("×")};for(const o of w)console.log(`${A[o.status]} ${o.name}`),o.message&&console.log(t.gray(` ${o.message}`));w.every(t=>"pass"===t.status)&&console.log("\n"+t.green.bold(i.t("doctor.all_good"))),!a.deep&&T.includes("claude-code")&&console.log(t.gray("\n"+i.t("doctor.deep_hint"))),w.some(t=>"fail"===t.status)&&(process.exitCode=1)}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Codex lists only the threads whose `model_provider` matches the provider it is
3
+ * using now (`threads.model_provider IN (…)` in its state DB, source:
4
+ * codex-rs/state/src/runtime/threads.rs). Switching between the official login
5
+ * (`openai`) and VibeAPI (`custom`) therefore hides every earlier session. This
6
+ * moves all local sessions under one provider: the first line of each rollout
7
+ * (`session_meta.payload.model_provider`, which Codex re-syncs the DB from) and
8
+ * the `threads` table. Before anything changes, the DB files are copied and every
9
+ * moved session's previous provider is recorded so `--undo` can put it back.
10
+ */
11
+ export interface MovedSession {
12
+ file: string;
13
+ from: string;
14
+ }
15
+ export interface SessionMoveResult {
16
+ target: string;
17
+ scanned: number;
18
+ moved: MovedSession[];
19
+ dbRows: number;
20
+ /** Why the state DB was left alone (no DB, no node:sqlite); null when updated. */
21
+ dbSkipped: string | null;
22
+ backupDir: string | null;
23
+ }
24
+ export declare function codexHome(): string;
25
+ /** The provider Codex uses now: `model_provider` in config.toml, else the built-in `openai`. */
26
+ export declare function currentCodexProvider(): string;
27
+ /** Tests replace the process probe. */
28
+ export declare function setCodexProcessCheckForTests(fake: () => number[]): void;
29
+ /** PIDs of running Codex processes (CLI, desktop app, background app-server); they would overwrite the edits. */
30
+ export declare function runningCodexPids(): number[];
31
+ /** Move every local session under `target`. `dryRun` only counts. */
32
+ export declare function moveSessionsToProvider(target: string, options?: {
33
+ dryRun?: boolean;
34
+ }): Promise<SessionMoveResult>;
35
+ /** Put every session recorded in a backup back under the provider it had. */
36
+ export declare function undoSessionMove(backupDir: string): Promise<{
37
+ files: number;
38
+ dbRows: number;
39
+ }>;
@@ -0,0 +1 @@
1
+ import{spawnSync as e}from"child_process";import{closeSync as o,copyFileSync as t,createReadStream as r,createWriteStream as n,existsSync as s,mkdirSync as i,openSync as a,readFileSync as c,readSync as f,readdirSync as l,renameSync as d,statSync as m,utimesSync as p,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{toolHome as v}from"./tools/types.js";export function codexHome(){return process.env.CODEX_HOME?.trim()||h(v(),".codex")}export function currentCodexProvider(){try{const e=g.parse(c(h(codexHome(),"config.toml"),"utf-8"));return"string"==typeof e.model_provider&&e.model_provider.trim()?e.model_provider.trim():"openai"}catch{return"openai"}}let b=()=>"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){b=e}export function runningCodexPids(){try{return b()}catch{return[]}}function x(e){const t=a(e,"r");try{const e=[],o=Buffer.alloc(65536);for(;;){const r=f(t,o,0,o.length,null);if(r<=0)break;const n=o.subarray(0,r).indexOf(10);if(n>=0){e.push(Buffer.from(o.subarray(0,n)));break}e.push(Buffer.from(o.subarray(0,r)))}return Buffer.concat(e).toString("utf-8")}finally{o(t)}}async function S(e,o,t){const{atime:s,mtime:i}=m(e),a=`${e}.vibecli-tmp`,c=n(a);c.write(o),await y(r(e,{start:t}),c),d(a,e),p(e,s,i)}async function w(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,o={}){const r=function(){const e=[],o=t=>{if(s(t))for(const r of l(t,{withFileTypes:!0})){const n=h(t,r.name);r.isDirectory()?o(n):r.isFile()&&r.name.endsWith(".jsonl")&&e.push(n)}};return o(h(codexHome(),"sessions")),o(h(codexHome(),"archived_sessions")),e}(),n=[];for(const o of r){let t,r;try{t=x(o)}catch{continue}try{r=JSON.parse(t)}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:o,from:s,line:JSON.stringify(r),length:Buffer.byteLength(t,"utf-8")}))}const a=function(){const e=codexHome();if(!s(e))return null;const o=l(e).map(e=>/^state_(\d+)\.sqlite$/.exec(e)).filter(e=>!!e).sort((e,o)=>Number(o[1])-Number(e[1]));return o.length>0?h(e,o[0][0]):null}();let c=a?null:"no state DB",f=[],d=null;if(a){const o=await w(a);"string"==typeof o?c=o:(d=o,f=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 m={target:e,scanned:r.length,moved:n.map(({file:e,from:o})=>({file:e,from:o})),dbRows:f.length,dbSkipped:c,backupDir:null};if(o.dryRun||0===n.length&&0===f.length)return d?.close(),m;const p=h(codexHome(),"backups",`vibecli-sessions-${(new Date).toISOString().replace(/[:.]/g,"-")}`);if(i(p,{recursive:!0}),a)for(const e of["","-wal","-shm"])s(a+e)&&t(a+e,h(p,`state.sqlite${e}`));const y={target:e,moved:m.moved,threads:f,db:a};u(h(p,"manifest.json"),JSON.stringify(y,null,2)+"\n");for(const e of n)await S(e.file,e.line,e.length);return d&&(d.prepare("UPDATE threads SET model_provider = ? WHERE model_provider <> ?").run(e,e),d.close()),m.backupDir=p,m}export async function undoSessionMove(e){const o=JSON.parse(c(h(e,"manifest.json"),"utf-8"));let t=0;for(const{file:e,from:r}of o.moved){if(!s(e))continue;const o=x(e),n=JSON.parse(o);n.payload&&(n.payload.model_provider=r,await S(e,JSON.stringify(n),Buffer.byteLength(o,"utf-8")),t+=1)}let r=0;if(o.db&&s(o.db)&&o.threads.length>0){const e=await w(o.db);if("string"!=typeof e){const t=e.prepare("UPDATE threads SET model_provider = ? WHERE id = ?");for(const{id:e,from:n}of o.threads)r+=Number(t.run(n,e).changes);e.close()}}return{files:t,dbRows:r}}
@@ -1 +1 @@
1
- import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as l}from"../commands/logs.js";import{completionCommand as p}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{runModelRefresh as u}from"./model-refresh.js";import{upgradeCommand as f}from"../commands/upgrade.js";import{setupCommand as h}from"../commands/setup.js";import y from"chalk";import{readFileSync as k}from"fs";import{join as b,dirname as w}from"path";import{fileURLToPath as v}from"url";import{setRuntimeOptions as $,detectColorPreferenceFromEnv as C,isColorEnabled as x,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as A,machineEnvVarsLeft as I}from"./env-cleanup.js";import{runStartupUpdate as P}from"./auto-update.js";export class Command{program;constructor(){const i=t.isLangChosen()?t.getLang():a();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=v(import.meta.url),o=w(e),a=b(o,"../../package.json");return JSON.parse(k(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--desktop-registry <on|off>","Windows Claude Desktop: always write the config to the registry policy (on, asks for admin once if needed) or decide automatically (off)").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await h({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,desktopRegistry:e.desktopRegistry,force:e.force})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),this.program.command("doctor").description(o.t("commands.doctor")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await l(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await f(e)}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),await d(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${y.bold(o.t("cli.examples"))}:\n ${y.gray("$ vibecli # Interactive main menu")}\n ${y.gray("$ vibecli init # Run first-time setup wizard")}\n ${y.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${y.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${y.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${y.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${y.gray("$ vibecli config codex --force # Same, for one tool")}\n ${y.gray("$ vibecli status --json # Machine-readable status")}\n ${y.gray("$ vibecli config profile list")}\n ${y.gray("$ vibecli config profile create work")}\n ${y.gray("$ vibecli config profile use work")}\n ${y.gray("$ vibecli config preset save fast")}\n ${y.gray("$ vibecli config backup create snapshot")}\n ${y.gray("$ vibecli auth <token> --non-interactive")}\n ${y.gray("$ vibecli auth revoke --yes")}\n ${y.gray("$ vibecli logs path")}\n ${y.gray("$ vibecli completion bash")}\n ${y.gray("$ vibecli bench --samples 3")}\n ${y.gray("$ vibecli update")}\n ${y.gray("$ vibecli upgrade claude-code")}\n ${y.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=A();if(0===e.length||j())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(x()?y.yellow(a):a);const t=I();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(x()?y.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&C();let i;$({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(y.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await P(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=x()?y.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
1
+ import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as d}from"../commands/logs.js";import{completionCommand as p}from"../commands/completion.js";import{benchCommand as l}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{runModelRefresh as u}from"./model-refresh.js";import{upgradeCommand as f}from"../commands/upgrade.js";import{codexSessionsCommand as h}from"../commands/codex-sessions.js";import{setupCommand as y}from"../commands/setup.js";import k from"chalk";import{readFileSync as b}from"fs";import{join as v,dirname as w}from"path";import{fileURLToPath as $}from"url";import{setRuntimeOptions as x,detectColorPreferenceFromEnv as C,isColorEnabled as j,isJsonMode as A}from"./runtime-options.js";import{cleanupAnthropicEnvVars as I,machineEnvVarsLeft as P}from"./env-cleanup.js";import{runStartupUpdate as D}from"./auto-update.js";export class Command{program;constructor(){const i=t.isLangChosen()?t.getLang():a();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=$(import.meta.url),o=w(e),a=v(o,"../../package.json");return JSON.parse(b(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--desktop-registry <on|off>","Windows Claude Desktop: always write the config to the registry policy (on, asks for admin once if needed) or decide automatically (off)").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await y({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,desktopRegistry:e.desktopRegistry,force:e.force})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),this.program.command("doctor").description(o.t("commands.doctor")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await d(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await f(e)}),this.program.command("codex").description(o.t("commands.codex")).command("sessions").description(o.t("commands.codex_sessions")).option("--to <provider>","Provider to move the sessions under (default: the one Codex uses now)").option("--undo <dir>","Put the sessions back as recorded in this backup directory").action(async e=>{await h(e)}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),await l(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${k.bold(o.t("cli.examples"))}:\n ${k.gray("$ vibecli # Interactive main menu")}\n ${k.gray("$ vibecli init # Run first-time setup wizard")}\n ${k.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${k.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${k.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${k.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${k.gray("$ vibecli config codex --force # Same, for one tool")}\n ${k.gray("$ vibecli status --json # Machine-readable status")}\n ${k.gray("$ vibecli config profile list")}\n ${k.gray("$ vibecli config profile create work")}\n ${k.gray("$ vibecli config profile use work")}\n ${k.gray("$ vibecli config preset save fast")}\n ${k.gray("$ vibecli config backup create snapshot")}\n ${k.gray("$ vibecli auth <token> --non-interactive")}\n ${k.gray("$ vibecli auth revoke --yes")}\n ${k.gray("$ vibecli logs path")}\n ${k.gray("$ vibecli completion bash")}\n ${k.gray("$ vibecli bench --samples 3")}\n ${k.gray("$ vibecli update")}\n ${k.gray("$ vibecli upgrade claude-code")}\n ${k.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=I();if(0===e.length||A())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(j()?k.yellow(a):a);const t=P();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(j()?k.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&C();let i;x({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(k.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await D(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=j()?k.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -8,3 +8,9 @@ export declare const CODEX_ONE_MILLION_CONTEXT_SETTING = "one_million_context";
8
8
  */
9
9
  export declare function setCodexOneMillionContext(enabled: boolean, catalog?: ModelCatalog): void;
10
10
  export declare const codexAdapter: ToolAdapter;
11
+ /**
12
+ * Why Codex would send the gateway something other than the saved key, for
13
+ * `vibecli doctor`: a bearer left on our provider (usually cc-switch's
14
+ * `PROXY_MANAGED`), or auth.json in ChatGPT-login mode. Null when neither.
15
+ */
16
+ export declare function codexAuthProblem(): 'stale_bearer' | 'chatgpt_login' | null;
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as o}from"fs";import{join as t}from"path";import*as n from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as r}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as u,codexCatalogPath as p,loadBundledCodexModels as g,loadPreviousCodexModels as _,previousGatewayModelIds as f}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(d(),".codex"),C=()=>t(w(),"config.toml"),h=()=>t(w(),"auth.json"),j="OPENAI_API_KEY",k="custom",b=1e6,E=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function O(e,o){return M(e[o])||(e[o]={}),e[o]}function v(){if(!e(C()))return{};const t=o(C(),"utf-8");if(!t.trim())return{};try{const e=n.parse(t);if(!M(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function N(){if(!e(h()))return{};try{const e=JSON.parse(o(h(),"utf-8"));return M(e)?e:{}}catch{return{}}}let S=null;function A(t,n,r){const s=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===b&&delete e.model_context_window,e.model_auto_compact_token_limit===E&&delete e.model_auto_compact_token_limit}(t);const d=g()??_();if(!d)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===p()&&delete t.model_catalog_json,void(s&&(t.model_context_window=b,t.model_auto_compact_token_limit=E));const c=n.catalog,m=u({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??f(),oneMillion:s,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=p();e(w)&&o(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(e,o){const t=v(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:o},r);const s=n.stringify(t);s!==i&&(l(C()),a(C(),s))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:C(),wantsModelCatalog:!0,listedModels(){if(v().model_catalog_json!==p())return null;try{return(JSON.parse(o(p(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){v().model_catalog_json===p()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const e=new Map;for(const o of S.listed)e.set(y(o.id),(e.get(y(o.id))??0)+1);const o=["gpt","claude","other","grok"].filter(o=>e.has(o)).map(o=>`${r.t(`tools.model_family.${o}`)} ${e.get(o)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:o})},{kind:"detail",text:S.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const o=m("codex"),t=s(e.models,o),i=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(t)?o:t,r=c(e.baseUrl),d=v();d.model=i,d.model_provider=k,d.model_reasoning_effort="high",A(d,e,i);const u=O(d,"model_providers"),p=M(u[k])?{...u[k]}:{};delete p.env_key,u[k]={...p,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(d,"profiles"),_=M(g[i])?{...g[i]}:{};g[i]={..._,model_provider:k,model:i},O(O(d,"tui"),"model_availability_nux")[i]=4;const f=O(d,"features");f.goals=!0,f.js_repl=!1,l(C()),a(C(),n.stringify(d)),function(e){const o=e.trim();if(!o)return;const t={...N(),auth_mode:"apikey",[j]:o};l(h()),a(h(),JSON.stringify(t,null,2)+"\n")}(e.apiKey)},unloadPrivateConfig(){if(!e(C()))return;let o;try{o=v()}catch(e){return void i.logError("codexAdapter.unloadPrivateConfig",e)}const t=o.model_providers;M(t)&&(delete t[k],0===Object.keys(t).length&&delete o.model_providers),o.model_provider===k&&delete o.model_provider,o.model_catalog_json===p()&&delete o.model_catalog_json,l(C()),a(C(),n.stringify(o))},detectCurrentConfig(){const e=N()[j];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return v()}catch(e){return i.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[C(),h(),p()]};
1
+ import{existsSync as e,readFileSync as t}from"fs";import{join as o}from"path";import*as n from"@iarna/toml";import{logger as r}from"../../utils/logger.js";import{i18n as i}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as u,codexCatalogPath as p,loadBundledCodexModels as _,loadPreviousCodexModels as g,previousGatewayModelIds as f}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const h=()=>o(d(),".codex"),b=()=>o(h(),"config.toml"),k=()=>o(h(),"auth.json"),w="OPENAI_API_KEY",C="custom",j=1e6,v=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function E(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function M(e,t){return E(e[t])||(e[t]={}),e[t]}function O(){if(!e(b()))return{};const o=t(b(),"utf-8");if(!o.trim())return{};try{const e=n.parse(o);if(!E(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function N(){if(!e(k()))return{};try{const e=JSON.parse(t(k(),"utf-8"));return E(e)?e:{}}catch{return{}}}let S=null;function A(o,n,i){const s=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===j&&delete e.model_context_window,e.model_auto_compact_token_limit===v&&delete e.model_auto_compact_token_limit}(o);const d=_()??g();if(!d)return r.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",o.model_catalog_json===p()&&delete o.model_catalog_json,void(s&&(o.model_context_window=j,o.model_auto_compact_token_limit=v));const c=n.catalog,m=u({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??f(),oneMillion:s,selectedModel:i,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const y=JSON.stringify(m,null,2)+"\n",h=p();e(h)&&t(h,"utf-8")===y||(l(h),a(h,y)),o.model_catalog_json=h}export function setCodexOneMillionContext(e,t){const o=O(),r=n.stringify(o),i="string"==typeof o.model?o.model:m("codex");A(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},i);const s=n.stringify(o);s!==r&&(l(b()),a(b(),s))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:b(),wantsModelCatalog:!0,listedModels(){if(O().model_catalog_json!==p())return null;try{return(JSON.parse(t(p(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){O().model_catalog_json===p()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===S)return[{kind:"action",text:i.t("tools.codex.catalog_skipped")}];if(!S)return[];const e=new Map;for(const t of S.listed)e.set(y(t.id),(e.get(y(t.id))??0)+1);const t=["gpt","claude","other","grok"].filter(t=>e.has(t)).map(t=>`${i.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:i.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:t})},{kind:"detail",text:S.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const t=m("codex"),o=s(e.models,t),r=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,i=c(e.baseUrl),d=O();d.model=r,d.model_provider=C,d.model_reasoning_effort="high",A(d,e,r);const u=M(d,"model_providers"),p=E(u[C])?{...u[C]}:{};delete p.env_key;const{experimental_bearer_token:_,...g}=p;u[C]={...g,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:i};const f=M(d,"profiles"),x=E(f[r])?{...f[r]}:{};f[r]={...x,model_provider:C,model:r},M(M(d,"tui"),"model_availability_nux")[r]=4;const y=M(d,"features");y.goals=!0,y.js_repl=!1,l(b()),a(b(),n.stringify(d)),function(e){const t=e.trim();if(!t)return;const o={...N(),auth_mode:"apikey",[w]:t};l(k()),a(k(),JSON.stringify(o,null,2)+"\n")}(e.apiKey)},unloadPrivateConfig(){if(!e(b()))return;let t;try{t=O()}catch(e){return void r.logError("codexAdapter.unloadPrivateConfig",e)}const o=t.model_providers;E(o)&&(delete o[C],0===Object.keys(o).length&&delete t.model_providers),t.model_provider===C&&delete t.model_provider,t.model_catalog_json===p()&&delete t.model_catalog_json,l(b()),a(b(),n.stringify(t))},detectCurrentConfig(){const e=N()[w];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return O()}catch(e){return r.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[b(),k(),p()]};export function codexAuthProblem(){try{const e=O(),t=e.model_providers?.[C];if(e.model_provider!==C||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=N();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -18,7 +18,9 @@
18
18
  "tool": "Tool management",
19
19
  "status": "Show current configuration status",
20
20
  "setup": "One-shot setup (--key auto-detects capable tools / --claudekey --codexkey for per-tool keys)",
21
- "upgrade": "Upgrade Claude Code, Codex and other coding tools (falls back to the npm mirror)"
21
+ "upgrade": "Upgrade Claude Code, Codex and other coding tools (falls back to the npm mirror)",
22
+ "codex": "Codex helpers",
23
+ "codex_sessions": "Show every local Codex session again (sessions from another login or provider are hidden)"
22
24
  },
23
25
  "lang": {
24
26
  "current": "Current language",
@@ -208,7 +210,10 @@
208
210
  "check_claude_request": "Sending a test request through Claude Code",
209
211
  "claude_request_skipped": "claude command not found",
210
212
  "claude_request_failed": "Request failed: {{reason}}",
211
- "deep_hint": "Add --deep to send one real request through Claude Code (uses a little credit) and confirm the whole chain works"
213
+ "deep_hint": "Add --deep to send one real request through Claude Code (uses a little credit) and confirm the whole chain works",
214
+ "codex_auth": "Codex token",
215
+ "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. vibecli setup removes it",
216
+ "codex_chatgpt_login": "Codex is signed in with a ChatGPT account and sends that credential to VibeAPI (401 Invalid token); run vibecli setup to switch back to the key"
212
217
  },
213
218
  "preset": {
214
219
  "claude_mix": "Claude Mix",
@@ -441,5 +446,14 @@
441
446
  "changed": "{{tool}} model menu synced with VibeAPI: {{changes}}",
442
447
  "added": "added {{models}}",
443
448
  "removed": "removed {{models}}"
449
+ },
450
+ "codex_sessions": {
451
+ "codex_running": "Codex is still running (PIDs {{pids}}) and would overwrite the change: quit Codex completely (desktop app and background server included) first",
452
+ "nothing": "Scanned {{scanned}} sessions; all are already under \"{{target}}\"",
453
+ "dry_run": "Would move {{count}} sessions from \"{{from}}\" to \"{{target}}\" (dry run, nothing changed)",
454
+ "moved": "Moved {{count}} sessions from \"{{from}}\" to \"{{target}}\"; reopen Codex to see them. Run again after switching login back",
455
+ "db_skipped": "Codex's database was not updated ({{reason}}); session files were. Upgrade Node.js to 22.13+ and run again",
456
+ "backup": "Backup and undo record in {{dir}}; undo with vibecli codex sessions --undo <that dir>",
457
+ "undone": "Restored {{files}} session files and {{rows}} database rows"
444
458
  }
445
459
  }
@@ -18,7 +18,9 @@
18
18
  "tool": "工具管理",
19
19
  "status": "显示当前配置状态",
20
20
  "setup": "一条命令完成配置(--key 按能力自动选工具 / --claudekey --codexkey 双令牌分别配置)",
21
- "upgrade": "升级 Claude Code、Codex 等编码工具(官方渠道连不上时改走国内镜像)"
21
+ "upgrade": "升级 Claude Code、Codex 等编码工具(官方渠道连不上时改走国内镜像)",
22
+ "codex": "Codex 辅助功能",
23
+ "codex_sessions": "让 Codex 本地所有会话都显示在列表里(换过登录方式或服务商后旧会话会被隐藏)"
22
24
  },
23
25
  "lang": {
24
26
  "current": "当前语言",
@@ -208,7 +210,10 @@
208
210
  "check_claude_request": "用 Claude Code 发一条测试请求",
209
211
  "claude_request_skipped": "没找到 claude 命令",
210
212
  "claude_request_failed": "请求失败:{{reason}}",
211
- "deep_hint": "加上 --deep 会用 Claude Code 实际发一条请求(消耗少量额度),确认整条链路能用"
213
+ "deep_hint": "加上 --deep 会用 Claude Code 实际发一条请求(消耗少量额度),确认整条链路能用",
214
+ "codex_auth": "Codex 令牌",
215
+ "codex_stale_bearer": "~/.codex/config.toml 的 [model_providers.custom] 里有 experimental_bearer_token(多半是 cc-switch 等工具代理模式留下的 PROXY_MANAGED),Codex 会把它当令牌发出去,导致 401 Invalid token;运行 vibecli setup 会删掉它",
216
+ "codex_chatgpt_login": "Codex 当前是 ChatGPT 账号登录状态,会把 ChatGPT 的凭据发给 VibeAPI,导致 401 Invalid token;运行 vibecli setup 改回令牌登录"
212
217
  },
213
218
  "preset": {
214
219
  "claude_mix": "Claude 混合",
@@ -441,5 +446,14 @@
441
446
  "changed": "已按 VibeAPI 最新模型清单更新 {{tool}}:{{changes}}",
442
447
  "added": "新增 {{models}}",
443
448
  "removed": "移除 {{models}}"
449
+ },
450
+ "codex_sessions": {
451
+ "codex_running": "Codex 还在运行(进程 {{pids}}),它会覆盖这次修改:请先完全退出 Codex(含桌面版和后台服务)再运行",
452
+ "nothing": "已扫描 {{scanned}} 个会话,全都在「{{target}}」下,不用处理",
453
+ "dry_run": "将把 {{count}} 个会话从「{{from}}」移到「{{target}}」(试运行,未修改)",
454
+ "moved": "已把 {{count}} 个会话从「{{from}}」移到「{{target}}」,重开 Codex 后都能在列表里看到;以后换回另一种登录方式时再运行一次",
455
+ "db_skipped": "Codex 的数据库没有更新({{reason}}),会话文件已改好;升级 Node.js 到 22.13 以上后再运行一次",
456
+ "backup": "备份与还原记录在 {{dir}},还原用 vibecli codex sessions --undo <这个目录>",
457
+ "undone": "已还原 {{files}} 个会话文件、{{rows}} 条数据库记录"
444
458
  }
445
459
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
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",