@vibeapi/cli 0.0.71 → 0.0.73

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/README.md CHANGED
@@ -112,7 +112,7 @@ vibecli base https://www.vibeapi.si # 或指定一个地址
112
112
  vibecli base default # 网络恢复后回到主站
113
113
  ```
114
114
 
115
- 地址清单以网关控制台公布的为准(目前有 `www.vibeapi.cn`、`cf.vibeapi.cn`、`hk.vibeapi.cn`、`www.vibeapi.si`),vibecli 每次连上网关都会更新清单。切换后地址保存在当前 profile 里,Claude Code、Codex、Claude 桌面端等已用 vibecli 配置过的工具会一起改过去,以后再运行 `vibecli setup` 也不会改回主站。首次配置时就可以指定:`vibecli setup --key sk-xxx --base https://www.vibeapi.si`。向导主菜单里也有「切换接入地址」。
115
+ `auto` 按这个优先级尝试,取第一个能用的:`www.vibeapi.cn` → `www.vibeapi.si` → `hk.vibeapi.cn` → `cf.vibeapi.cn` → `token.nfeyre.top`。网关控制台以后新增的地址会排在这些后面。切换后地址保存在当前 profile 里,Claude Code、Codex、Claude 桌面端等已用 vibecli 配置过的工具会一起改过去,以后再运行 `vibecli setup` 也不会改回主站。首次配置时就可以指定:`vibecli setup --key sk-xxx --base https://www.vibeapi.si`。向导主菜单里也有「切换接入地址」。
116
116
 
117
117
  ## 支持
118
118
 
@@ -1 +1 @@
1
- import e from"chalk";import{configManager as o,DEFAULT_BASE_URL as r}from"../lib/config.js";import{i18n as t}from"../lib/i18n.js";import{toolManager as s}from"../lib/tool-manager.js";import{listAdapters as n}from"../lib/tools/registry.js";import{postApplyPrompts as a}from"../lib/post-apply.js";import{emitJson as l,isJsonMode as i}from"../lib/runtime-options.js";import{logger as c}from"../utils/logger.js";import{gatewayAddresses as u,normalizeBaseUrl as d,probeGatewayAddress as f}from"../lib/gateway-addresses.js";export async function baseCommand(s){const n=o.getBaseUrl(),a=addressesWithCurrent(),c=s?.trim().toLowerCase();if(!c)return void await async function(o,r){const s=await probeAll(o);i()?l({ok:!0,current:r,addresses:o.map((e,o)=>({...e,...s[o],current:e.url===r}))}):(console.log(e.bold(t.t("base.current",{url:r}))+"\n"),b(o,s,r),console.log("\n"+e.gray(t.t("base.usage_hint"))))}(a,n);if("auto"===c){const e=await probeAll(a),o=e.find(e=>e.ok);return o?void await switchGatewayAddress(o.url):(p("no_reachable",t.t("base.none_reachable")),void b(a,e,n))}let u;if("default"===c||"reset"===c)u=r;else if(/^\d+$/.test(c)){if(u=a[Number(c)-1]?.url??null,!u)return void p("bad_index",t.t("base.bad_index",{index:c,count:String(a.length)}))}else if(u=d(s),!u)return void p("bad_url",t.t("base.bad_url",{value:s}));const g=await f(u);g.ok?await switchGatewayAddress(u):p("unreachable",t.t("base.unreachable",{url:u,reason:reasonText(g)}))}export function addressesWithCurrent(){const e=o.getBaseUrl(),r=u();return r.some(o=>o.url===e)||r.push({url:e,label:t.t("base.custom_label")}),r}export async function probeAll(e){return Promise.all(e.map(e=>f(e.url)))}export function reasonText(e){if(!e.error)return"";const o=t.t(`base.error_${e.error}`);return e.detail?`${o}(${e.detail})`:o}function p(o,r){process.exitCode=1,i()?l({ok:!1,error:o,message:r}):console.error(e.red(r))}function b(o,r,s){i()||o.forEach((o,n)=>{const a=r[n],l=a.ok?e.green(`√ ${a.ms} ms`):e.red(`× ${reasonText(a)}`),i=o.url===s?e.cyan(` ← ${t.t("base.current_mark")}`):"";console.log(`${n+1}. ${o.url} ${e.gray(o.label)}\n ${l}${i}`)})}export async function switchGatewayAddress(u){const d=o.getBaseUrl();u===r?o.resetBaseUrl():o.setBaseUrl(u,!0);const f=[],p=[];for(const e of function(){const e=[];for(const r of n()){const t=o.getApiKeyForTool(r.name);if(t)try{r.detectCurrentConfig().apiKey===t&&e.push(r.name)}catch(e){c.logError(`base.detectCurrentConfig(${r.name})`,e)}}return e}()){const r=o.getApiKeyForTool(e);try{await s.applyPrivateConfig(e,r,o.getModelsForTool(e)),f.push(e)}catch(o){p.push({tool:e,reason:o instanceof Error?o.message:String(o)})}}if(p.length>0&&(process.exitCode=1),i())l({ok:0===p.length,previous:d,base_url:u,applied:f,failed:p});else{console.log(e.green(t.t(u===d?"base.kept":"base.switched",{url:u}))),f.length>0&&console.log(t.t("base.tools_moved"));for(const e of f)s.printApplied(e);for(const{tool:o,reason:r}of p)console.error(e.red(`× ${t.t("setup.apply_failed",{tool:o,reason:r})}`));0===f.length&&0===p.length&&console.log(e.gray(t.t("base.no_tools"))),await a(f)}}
1
+ import e from"chalk";import{configManager as o,DEFAULT_BASE_URL as r}from"../lib/config.js";import{i18n as t}from"../lib/i18n.js";import{toolManager as s}from"../lib/tool-manager.js";import{listAdapters as n}from"../lib/tools/registry.js";import{postApplyPrompts as a}from"../lib/post-apply.js";import{emitJson as l,isJsonMode as i}from"../lib/runtime-options.js";import{logger as c}from"../utils/logger.js";import{gatewayAddresses as u,normalizeBaseUrl as d,probeGatewayAddress as f}from"../lib/gateway-addresses.js";export async function baseCommand(s){const n=o.getBaseUrl(),a=addressesWithCurrent(),c=s?.trim().toLowerCase();if(!c)return void await async function(o,r){const s=await probeAll(o);i()?l({ok:!0,current:r,addresses:o.map((e,o)=>({...e,...s[o],current:e.url===r}))}):(console.log(e.bold(t.t("base.current",{url:r}))+"\n"),b(o,s,r),console.log("\n"+e.gray(t.t("base.usage_hint"))))}(a,n);if("auto"===c){const e=await probeAll(a),o=e.find(e=>e.ok);return o?void await switchGatewayAddress(o.url):(p("no_reachable",t.t("base.none_reachable")),void b(a,e,n))}let u;if("default"===c||"reset"===c)u=r;else if(/^\d+$/.test(c)){if(u=a[Number(c)-1]?.url??null,!u)return void p("bad_index",t.t("base.bad_index",{index:c,count:String(a.length)}))}else if(u=d(s),!u)return void p("bad_url",t.t("base.bad_url",{value:s}));const m=await f(u);m.ok?await switchGatewayAddress(u):p("unreachable",t.t("base.unreachable",{url:u,reason:reasonText(m)}))}export function addressesWithCurrent(){const e=o.getBaseUrl(),r=u();return r.some(o=>o.url===e)||r.push({url:e,label:t.t("base.custom_label")}),r}export async function probeAll(e){return Promise.all(e.map(e=>f(e.url)))}export function reasonText(e){if(!e.error)return"";const o=t.t(`base.error_${e.error}`);return e.detail?`${o}(${e.detail})`:o}function p(o,r){process.exitCode=1,i()?l({ok:!1,error:o,message:r}):console.error(e.red(r))}function b(o,r,s){i()||o.forEach((o,n)=>{const a=r[n],l=a.ok?e.green(`√ ${a.ms} ms`):e.red(`× ${reasonText(a)}`),i=o.url===s?e.cyan(` ← ${t.t("base.current_mark")}`):"";console.log(`${n+1}. ${o.url} ${e.gray(o.label)}\n ${l}${i}`)})}export async function switchGatewayAddress(u){const d=o.getBaseUrl();u===r?o.resetBaseUrl():o.setBaseUrl(u,!0);const f=[],p=[];for(const e of function(){const e=[];for(const r of n()){const t=o.getApiKeyForTool(r.name);if(t)try{r.detectCurrentConfig().apiKey===t&&e.push(r.name)}catch(e){c.logError(`base.detectCurrentConfig(${r.name})`,e)}}return e}()){const r=o.getApiKeyForTool(e);try{await s.applyPrivateConfig(e,r,o.getModelsForTool(e)),f.push(e)}catch(o){p.push({tool:e,reason:o instanceof Error?o.message:String(o)})}}if(p.length>0&&(process.exitCode=1),i())l({ok:0===p.length,previous:d,base_url:u,applied:f,failed:p});else{console.log(e.green(t.t(u===d?"base.kept":"base.switched",{url:u}))),f.length>0&&console.log(t.t("base.tools_moved"));for(const e of f)s.printApplied(e);for(const{tool:o,reason:r}of p)console.error(e.red(`× ${t.t("setup.apply_failed",{tool:o,reason:r})}`));0===f.length&&0===p.length&&console.log(e.yellow(t.t("base.no_tools",{url:u}))),await a(f)}}
@@ -1,13 +1,15 @@
1
1
  import { Command as Commander } from 'commander';
2
2
  export declare class Command {
3
3
  private program;
4
+ /** Set by `preflightArgs`: this run configures tools (see CONFIGURING_COMMANDS). */
5
+ private configuring;
4
6
  constructor();
5
7
  private getVersion;
6
8
  private setupProgram;
7
9
  private handleInitCommand;
8
10
  /**
9
- * 启动即检测 shell 环境里泄漏的 ANTHROPIC_BASE_URL / ANTHROPIC_API_KEY 并清除,
10
- * 否则它们会覆盖 vibecli 写进各工具配置文件的凭据。
11
+ * 配置类命令(CONFIGURING_COMMANDS 与向导)启动时检测 shell 环境里泄漏的
12
+ * ANTHROPIC_BASE_URL / ANTHROPIC_API_KEY 并清除,否则它们会覆盖 vibecli 写进各工具配置文件的凭据。
11
13
  */
12
14
  private cleanupLeakedEnvVars;
13
15
  private preflightArgs;
@@ -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 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{baseCommand as k}from"../commands/base.js";import{normalizeBaseUrl as b}from"./gateway-addresses.js";import w from"chalk";import{readFileSync as v}from"fs";import{join as $,dirname as x}from"path";import{fileURLToPath as C}from"url";import{setRuntimeOptions as j,detectColorPreferenceFromEnv as A,isColorEnabled as I,isJsonMode as P}from"./runtime-options.js";import{cleanupAnthropicEnvVars as D,machineEnvVarsLeft as R}from"./env-cleanup.js";import{runStartupUpdate as S}from"./auto-update.js";const _=new Set(["setup","init","auth","enter","config"]);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=C(import.meta.url),o=x(e),a=$(o,"../../package.json");return JSON.parse(v(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("base [target]").description(o.t("commands.base")).action(async e=>{await k(e)}),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${w.bold(o.t("cli.examples"))}:\n ${w.gray("$ vibecli # Interactive main menu")}\n ${w.gray("$ vibecli init # Run first-time setup wizard")}\n ${w.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${w.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${w.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${w.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${w.gray("$ vibecli config codex --force # Same, for one tool")}\n ${w.gray("$ vibecli status --json # Machine-readable status")}\n ${w.gray("$ vibecli config profile list")}\n ${w.gray("$ vibecli config profile create work")}\n ${w.gray("$ vibecli config profile use work")}\n ${w.gray("$ vibecli config preset save fast")}\n ${w.gray("$ vibecli config backup create snapshot")}\n ${w.gray("$ vibecli auth <token> --non-interactive")}\n ${w.gray("$ vibecli auth revoke --yes")}\n ${w.gray("$ vibecli logs path")}\n ${w.gray("$ vibecli completion bash")}\n ${w.gray("$ vibecli bench --samples 3")}\n ${w.gray("$ vibecli update")}\n ${w.gray("$ vibecli upgrade claude-code")}\n ${w.gray("$ vibecli base # List gateway addresses and test each one")}\n ${w.gray("$ vibecli base auto # Switch every tool to the first address that works")}\n ${w.gray("$ vibecli setup --key sk-xxx --base https://www.vibeapi.si # Configure on another address")}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=D();if(0===e.length||P())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(I()?w.yellow(a):a);const t=R();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(I()?w.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&A();let i,n;j({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(w.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];"--base"===a&&e[o+1]?(i=e[o+1],o+=1):a.startsWith("--base=")?i=a.slice(7):n||a.startsWith("-")||(n=a)}if(i){const e=void 0===n&&!["--help","-h","--version","-v"].some(o)||void 0!==n&&_.has(n);t.setBaseUrl(b(i)??i,e)}}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await S(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=I()?w.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{baseCommand as k}from"../commands/base.js";import{normalizeBaseUrl as b}from"./gateway-addresses.js";import w from"chalk";import{readFileSync as v}from"fs";import{join as $,dirname as x}from"path";import{fileURLToPath as C}from"url";import{setRuntimeOptions as j,detectColorPreferenceFromEnv as A,isColorEnabled as I,isJsonMode as P}from"./runtime-options.js";import{cleanupAnthropicEnvVars as D,machineEnvVarsLeft as R}from"./env-cleanup.js";import{runStartupUpdate as S}from"./auto-update.js";const _=new Set(["setup","init","auth","enter","config"]);export class Command{program;configuring=!1;constructor(){const i=t.isLangChosen()?t.getLang():a();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=C(import.meta.url),o=x(e),a=$(o,"../../package.json");return JSON.parse(v(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("base [target]").description(o.t("commands.base")).action(async e=>{await k(e)}),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${w.bold(o.t("cli.examples"))}:\n ${w.gray("$ vibecli # Interactive main menu")}\n ${w.gray("$ vibecli init # Run first-time setup wizard")}\n ${w.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${w.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${w.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${w.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${w.gray("$ vibecli config codex --force # Same, for one tool")}\n ${w.gray("$ vibecli status --json # Machine-readable status")}\n ${w.gray("$ vibecli config profile list")}\n ${w.gray("$ vibecli config profile create work")}\n ${w.gray("$ vibecli config profile use work")}\n ${w.gray("$ vibecli config preset save fast")}\n ${w.gray("$ vibecli config backup create snapshot")}\n ${w.gray("$ vibecli auth <token> --non-interactive")}\n ${w.gray("$ vibecli auth revoke --yes")}\n ${w.gray("$ vibecli logs path")}\n ${w.gray("$ vibecli completion bash")}\n ${w.gray("$ vibecli bench --samples 3")}\n ${w.gray("$ vibecli update")}\n ${w.gray("$ vibecli upgrade claude-code")}\n ${w.gray("$ vibecli base # List gateway addresses and test each one")}\n ${w.gray("$ vibecli base auto # Switch every tool to the first address that works")}\n ${w.gray("$ vibecli setup --key sk-xxx --base https://www.vibeapi.si # Configure on another address")}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=D();if(0===e.length||P())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(I()?w.yellow(a):a);const t=R();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(I()?w.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&A();let i,n;j({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(w.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];"--base"===a&&e[o+1]?(i=e[o+1],o+=1):a.startsWith("--base=")?i=a.slice(7):n||a.startsWith("-")||(n=a)}const s=void 0===n&&!["--help","-h","--version","-v"].some(o);this.configuring=s||void 0!==n&&_.has(n),i&&t.setBaseUrl(b(i)??i,this.configuring)}async execute(e){try{if(this.preflightArgs(e),this.configuring&&this.cleanupLeakedEnvVars(),(await S(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=I()?w.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -23,7 +23,12 @@ export declare function parsePublishedAddresses(apiInfo: unknown): GatewayAddres
23
23
  * A gateway that publishes no list leaves the cache alone.
24
24
  */
25
25
  export declare function rememberPublishedAddresses(statusData: unknown, source: string): void;
26
- /** Addresses to offer, in the operator's order: the gateway's published list, else the built-in one. */
26
+ /**
27
+ * Addresses to offer, in priority order: the built-in list (`models.json` →
28
+ * `gatewayAddresses`, which is also what `vibecli base auto` tries first to
29
+ * last), then addresses the gateway publishes that are not built in, in the
30
+ * gateway's order. A built-in address takes the gateway's label when it has one.
31
+ */
27
32
  export declare function gatewayAddresses(): GatewayAddress[];
28
33
  /**
29
34
  * One unauthenticated `GET /api/status`: reachable, valid certificate, and the
@@ -1 +1 @@
1
- import{existsSync as r,mkdirSync as e,readFileSync as t,writeFileSync as o}from"fs";import{join as s}from"path";import{configManager as n}from"./config.js";import{getBuiltinGatewayAddresses as i}from"./model-registry.js";import{logger as a}from"../utils/logger.js";const l=new Set(["SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_HAS_EXPIRED","CERT_NOT_YET_VALID","ERR_TLS_CERT_ALTNAME_INVALID"]);function u(){return s(n.getConfigDir(),"gateway-addresses.json")}export function normalizeBaseUrl(r){let e,t=r.trim();if(!t)return null;/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||(t=`https://${t}`);try{e=new URL(t)}catch{return null}if("https:"!==e.protocol&&"http:"!==e.protocol)return null;const o=e.pathname.replace(/\/+$/,"").replace(/\/v1$/i,"");return`${e.protocol}//${e.host}${o}`}export function parsePublishedAddresses(r){if(!Array.isArray(r))return[];const e=new Set,t=[];for(const o of r){if(!o||"object"!=typeof o)continue;const{url:r,route:s,description:n}=o,i="string"==typeof r?normalizeBaseUrl(r):null;if(!i?.startsWith("https://")||e.has(i))continue;e.add(i);const a="string"==typeof s?s.trim():"",l="string"==typeof n?n.trim():"";t.push({url:i,label:a&&l?`${a}(${l})`:a||l})}return t}export function rememberPublishedAddresses(r,t){if(!r||"object"!=typeof r)return;if(!i().some(r=>normalizeBaseUrl(r.url)===t))return;const s=r;if(!1===s.api_info_enabled)return;const l=parsePublishedAddresses(s.api_info);if(0!==l.length)try{e(n.getConfigDir(),{recursive:!0}),o(u(),JSON.stringify({addresses:l},null,2)+"\n")}catch(r){a.logError("rememberPublishedAddresses",r)}}export function gatewayAddresses(){const e=function(){const e=u();if(!r(e))return[];try{const r=JSON.parse(t(e,"utf-8"));return(Array.isArray(r.addresses)?r.addresses:[]).flatMap(r=>{const e="string"==typeof r?.url?normalizeBaseUrl(r.url):null;return e?[{url:e,label:"string"==typeof r.label?r.label:""}]:[]})}catch{return[]}}();return e.length>0?e:i().map(r=>({url:normalizeBaseUrl(r.url)??r.url,label:r.label})).filter((r,e,t)=>t.findIndex(e=>e.url===r.url)===e)}function c(r){const e=r?.cause,t="string"==typeof e?.code?e.code:"",o="string"==typeof e?.message?e.message:r instanceof Error?r.message:String(r);return r instanceof Error&&"AbortError"===r.name?{error:"timeout",detail:o}:l.has(t)||/certificate/i.test(o)?{error:"certificate",detail:t||o}:{error:"network",detail:t||o}}export async function probeGatewayAddress(r,e={}){const{timeoutMs:t=8e3,fetchFn:o=fetch}=e,s=new AbortController,n=setTimeout(()=>s.abort(),t),i=Date.now();try{const e=await o(`${r}/api/status`,{method:"GET",signal:s.signal}),t=Date.now()-i;let n;try{n=(await e.json())?.data}catch{n=void 0}return e.ok&&n&&"object"==typeof n?(rememberPublishedAddresses(n,r),{url:r,ok:!0,ms:t}):{url:r,ok:!1,ms:t,error:"not_gateway",detail:`HTTP ${e.status}`}}catch(e){return{url:r,ok:!1,ms:Date.now()-i,...c(e)}}finally{clearTimeout(n)}}
1
+ import{existsSync as r,mkdirSync as e,readFileSync as t,writeFileSync as o}from"fs";import{join as s}from"path";import{configManager as n}from"./config.js";import{getBuiltinGatewayAddresses as i}from"./model-registry.js";import{logger as a}from"../utils/logger.js";const l=new Set(["SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_HAS_EXPIRED","CERT_NOT_YET_VALID","ERR_TLS_CERT_ALTNAME_INVALID"]);function u(){return s(n.getConfigDir(),"gateway-addresses.json")}export function normalizeBaseUrl(r){let e,t=r.trim();if(!t)return null;/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||(t=`https://${t}`);try{e=new URL(t)}catch{return null}if("https:"!==e.protocol&&"http:"!==e.protocol)return null;const o=e.pathname.replace(/\/+$/,"").replace(/\/v1$/i,"");return`${e.protocol}//${e.host}${o}`}export function parsePublishedAddresses(r){if(!Array.isArray(r))return[];const e=new Set,t=[];for(const o of r){if(!o||"object"!=typeof o)continue;const{url:r,route:s,description:n}=o,i="string"==typeof r?normalizeBaseUrl(r):null;if(!i?.startsWith("https://")||e.has(i))continue;e.add(i);const a="string"==typeof s?s.trim():"",l="string"==typeof n?n.trim():"";t.push({url:i,label:a&&l?`${a}(${l})`:a||l})}return t}export function rememberPublishedAddresses(r,t){if(!r||"object"!=typeof r)return;if(!i().some(r=>normalizeBaseUrl(r.url)===t))return;const s=r;if(!1===s.api_info_enabled)return;const l=parsePublishedAddresses(s.api_info);if(0!==l.length)try{e(n.getConfigDir(),{recursive:!0}),o(u(),JSON.stringify({addresses:l},null,2)+"\n")}catch(r){a.logError("rememberPublishedAddresses",r)}}export function gatewayAddresses(){const e=function(){const e=u();if(!r(e))return[];try{const r=JSON.parse(t(e,"utf-8"));return(Array.isArray(r.addresses)?r.addresses:[]).flatMap(r=>{const e="string"==typeof r?.url?normalizeBaseUrl(r.url):null;return e?[{url:e,label:"string"==typeof r.label?r.label:""}]:[]})}catch{return[]}}(),o=[];for(const r of i()){const t=normalizeBaseUrl(r.url);if(!t||o.some(r=>r.url===t))continue;const s=e.find(r=>r.url===t)?.label||r.label;o.push({url:t,label:s})}for(const r of e)o.some(e=>e.url===r.url)||o.push(r);return o}function c(r){const e=r?.cause,t="string"==typeof e?.code?e.code:"",o="string"==typeof e?.message?e.message:r instanceof Error?r.message:String(r);return r instanceof Error&&"AbortError"===r.name?{error:"timeout",detail:o}:l.has(t)||/certificate/i.test(o)?{error:"certificate",detail:t||o}:{error:"network",detail:t||o}}export async function probeGatewayAddress(r,e={}){const{timeoutMs:t=8e3,fetchFn:o=fetch}=e,s=new AbortController,n=setTimeout(()=>s.abort(),t),i=Date.now();try{const e=await o(`${r}/api/status`,{method:"GET",signal:s.signal}),t=Date.now()-i;let n;try{n=(await e.json())?.data}catch{n=void 0}return e.ok&&n&&"object"==typeof n?(rememberPublishedAddresses(n,r),{url:r,ok:!0,ms:t}):{url:r,ok:!1,ms:t,error:"not_gateway",detail:`HTTP ${e.status}`}}catch(e){return{url:r,ok:!1,ms:Date.now()-i,...c(e)}}finally{clearTimeout(n)}}
@@ -476,7 +476,7 @@
476
476
  "usage_hint": "Switch: vibecli base <number or address>; first one that works: vibecli base auto; back to the main site: vibecli base default",
477
477
  "switched": "Switched to {{url}}",
478
478
  "kept": "{{url}} works and is saved",
479
- "no_tools": "No tool has been configured by vibecli yet; vibecli setup --key <your key> will use this address",
479
+ "no_tools": "No tool on this computer was configured by vibecli, so only the address was saved and nothing else changed. In tools you set up by hand, replace www.vibeapi.cn with {{url}}; or run vibecli setup --key <your key> to let vibecli configure them (it rewrites their VibeAPI settings, backing them up first)",
480
480
  "none_reachable": "None of the gateway addresses can be reached; check your network or proxy",
481
481
  "bad_index": "There is no address {{index}} ({{count}} in total); run vibecli base to see the list",
482
482
  "bad_url": "{{value}} is not a valid address, e.g. https://www.vibeapi.si",
@@ -476,7 +476,7 @@
476
476
  "usage_hint": "切换:vibecli base <序号或地址>;自动选第一个能用的:vibecli base auto;回到主站:vibecli base default",
477
477
  "switched": "已切换到 {{url}}",
478
478
  "kept": "当前地址 {{url}} 可用,已保存",
479
- "no_tools": "还没有用 vibecli 配置过的工具,运行 vibecli setup --key <你的 Key> 时会直接用这个地址",
479
+ "no_tools": "这台电脑上没有用 vibecli 配置过的工具,所以只保存了地址,其它配置都没动。自己手动配置的工具,请把里面的 www.vibeapi.cn 换成 {{url}};也可以运行 vibecli setup --key <你的 Key>,交给 vibecli 统一配置(会改写这些工具里的 VibeAPI 配置,改前自动备份)",
480
480
  "none_reachable": "所有接入地址都连不上,请检查网络或代理",
481
481
  "bad_index": "没有第 {{index}} 个地址(共 {{count}} 个),先运行 vibecli base 查看列表",
482
482
  "bad_url": "{{value}} 不是有效的地址,示例:https://www.vibeapi.si",
package/dist/models.json CHANGED
@@ -288,16 +288,20 @@
288
288
  "label": "主站"
289
289
  },
290
290
  {
291
- "url": "https://cf.vibeapi.cn",
292
- "label": "Cloudflare 线路"
291
+ "url": "https://www.vibeapi.si",
292
+ "label": "备用域名(证书报错时用)"
293
293
  },
294
294
  {
295
295
  "url": "https://hk.vibeapi.cn",
296
296
  "label": "香港线路"
297
297
  },
298
298
  {
299
- "url": "https://www.vibeapi.si",
300
- "label": "备用域名(证书报错时用)"
299
+ "url": "https://cf.vibeapi.cn",
300
+ "label": "Cloudflare 线路"
301
+ },
302
+ {
303
+ "url": "https://token.nfeyre.top",
304
+ "label": "全球加速"
301
305
  }
302
306
  ]
303
307
  }
package/models.json CHANGED
@@ -288,16 +288,20 @@
288
288
  "label": "主站"
289
289
  },
290
290
  {
291
- "url": "https://cf.vibeapi.cn",
292
- "label": "Cloudflare 线路"
291
+ "url": "https://www.vibeapi.si",
292
+ "label": "备用域名(证书报错时用)"
293
293
  },
294
294
  {
295
295
  "url": "https://hk.vibeapi.cn",
296
296
  "label": "香港线路"
297
297
  },
298
298
  {
299
- "url": "https://www.vibeapi.si",
300
- "label": "备用域名(证书报错时用)"
299
+ "url": "https://cf.vibeapi.cn",
300
+ "label": "Cloudflare 线路"
301
+ },
302
+ {
303
+ "url": "https://token.nfeyre.top",
304
+ "label": "全球加速"
301
305
  }
302
306
  ]
303
307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.71",
3
+ "version": "0.0.73",
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",