@vibeapi/cli 0.0.70 → 0.0.72

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
 
@@ -16,14 +16,19 @@ export declare function normalizeBaseUrl(input: string): string | null;
16
16
  /** Valid, de-duplicated entries of the gateway's `api_info` list; empty when it has none. */
17
17
  export declare function parsePublishedAddresses(apiInfo: unknown): GatewayAddress[];
18
18
  /**
19
- * Caches the list from the `/api/status` payload `source` answered with. Only an
20
- * address already trusted (built in, or on the cached list) may publish one: a
21
- * profile pointed at some other gateway must not get to steer `vibecli base auto`,
22
- * and with it the user's key, to addresses of its choosing. A gateway that
23
- * publishes no list leaves the cache alone.
19
+ * Caches the list from the `/api/status` payload `source` answered with. Only the
20
+ * built-in addresses may publish one, never an address that merely came from an
21
+ * earlier list: a profile pointed at some other gateway must not get to steer
22
+ * `vibecli base auto`, and with it the user's key, to addresses of its choosing.
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(!gatewayAddresses().some(r=>r.url===t))return;const s=r;if(!1===s.api_info_enabled)return;const i=parsePublishedAddresses(s.api_info);if(0!==i.length)try{e(n.getConfigDir(),{recursive:!0}),o(u(),JSON.stringify({addresses:i},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)}}
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.70",
3
+ "version": "0.0.72",
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",