@vibeapi/cli 0.0.77 → 0.0.80
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 +4 -4
- package/dist/commands/setup.js +1 -1
- package/dist/lib/api-validator.d.ts +4 -1
- package/dist/lib/api-validator.js +1 -1
- package/dist/lib/env-cleanup.d.ts +2 -0
- package/dist/lib/env-cleanup.js +1 -1
- package/dist/lib/gateway-models.d.ts +5 -3
- package/dist/lib/gateway-models.js +1 -1
- package/dist/lib/persistent-env.d.ts +13 -0
- package/dist/lib/persistent-env.js +1 -0
- package/dist/lib/tools/antigravity.d.ts +2 -0
- package/dist/lib/tools/antigravity.js +1 -0
- package/dist/lib/tools/backup.d.ts +1 -1
- package/dist/lib/tools/backup.js +1 -1
- package/dist/lib/tools/registry.js +1 -1
- package/dist/locales/en_US.json +5 -0
- package/dist/locales/zh_CN.json +5 -0
- package/dist/models.json +2 -1
- package/models.json +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @vibeapi/api-helper
|
|
2
2
|
|
|
3
|
-
> 一条命令,在 Claude Code / Claude Desktop / Codex / OpenClaw / Hermes / AtomCode / Grok Build 之间统一配置 API Key 与模型。
|
|
3
|
+
> 一条命令,在 Claude Code / Claude Desktop / Codex / OpenClaw / Hermes / AtomCode / Grok Build / Antigravity CLI 之间统一配置 API Key 与模型。
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@vibeapi/api-helper)
|
|
6
6
|
[](https://nodejs.org)
|
|
@@ -36,7 +36,7 @@ npx --yes --registry https://npm.cnb.cool/vibeapi/npm/-/packages/ @vibeapi/cli@l
|
|
|
36
36
|
vibecli setup --key sk-xxx
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
`--key` 会按令牌能访问的模型分组自动配置:有 Claude 模型配 Claude Code(机器上装了 Claude Desktop 也一并配好)、有 GPT 模型配 Codex、有 Grok 模型配 Grok Build。拿到的是多个分组的令牌(如 Claude 分组 + GPT 分组 + Grok 分组),就各配各的:
|
|
39
|
+
`--key` 会按令牌能访问的模型分组自动配置:有 Claude 模型配 Claude Code(机器上装了 Claude Desktop 也一并配好)、有 GPT 模型配 Codex、有 Grok 模型配 Grok Build、机器上装了 Antigravity CLI(`agy`)且有 Gemini 模型就配 agy。拿到的是多个分组的令牌(如 Claude 分组 + GPT 分组 + Grok 分组),就各配各的:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c --desktopkey sk-d
|
|
@@ -50,7 +50,7 @@ vibecli setup --1m
|
|
|
50
50
|
vibecli setup --no-1m
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
配完直接运行 `claude` / `codex` / `grok`
|
|
53
|
+
配完直接运行 `claude` / `codex` / `grok` 即可(agy 要新开一个终端)。Claude Desktop 默认隐藏官方(Claude.ai 账号)登录入口、只走网关,想保留就加 `--claude-official on`。
|
|
54
54
|
|
|
55
55
|
### 方式 B:交互式向导
|
|
56
56
|
|
|
@@ -76,7 +76,7 @@ vibecli config claude-code clear-model # 模型选择被钉住时,交还给
|
|
|
76
76
|
|
|
77
77
|
## 能做什么
|
|
78
78
|
|
|
79
|
-
- **多工具一键配置**:把当前 API Key / 模型注入 Claude Code、Claude Desktop(桌面端第三方推理模式,写应用本地配置库,无需管理员 / 描述文件)、Codex、OpenClaw、Hermes、AtomCode、Grok Build(OpenCode 暂时下架,`vibecli config opencode` 仍可用)。
|
|
79
|
+
- **多工具一键配置**:把当前 API Key / 模型注入 Claude Code、Claude Desktop(桌面端第三方推理模式,写应用本地配置库,无需管理员 / 描述文件)、Codex、OpenClaw、Hermes、AtomCode、Grok Build、Antigravity CLI(OpenCode 暂时下架,`vibecli config opencode` 仍可用)。
|
|
80
80
|
- **按工具选模型**:使用默认 / 实时从 API 拉取 / 内置预设 / 手动输入;每个工具可用不同模型。
|
|
81
81
|
- Claude Code 用默认模型时不写 `ANTHROPIC_*_MODEL` 环境变量,顶层 `model` 由 `settings-template.json` 提供(当前为 `opus[1m]`);显式选择其它模型时会覆盖该模板值。Codex 默认 `gpt-6-astra`,Grok Build 默认 `grok-4.6`,其余工具默认 `gpt-5.5`。
|
|
82
82
|
- Codex(CLI 与桌面版)的模型菜单除自带的 GPT 模型外,还列出网关的 Claude、Gemini 与国产模型(只列 Key 能用的);Claude Code 的 `/model` 菜单也会在内置档位后列出 Key 能用的国产模型;每个模型按自己的上下文窗口设置,窗口用到 80% 时自动压缩。默认启用 1M 上下文,交互配置 Codex 时可取消(取消后窗口不超过 272K)。需要 Codex 0.122 及以上;Codex、Claude Code 版本过旧时,配置完成会提示升级命令。
|
package/dist/commands/setup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as s}from"./base.js";import{i18n as i}from"../lib/i18n.js";import{configManager as a}from"../lib/config.js";import{toolManager as l,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as d,fetchModels as r}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as p,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as f,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as m}from"../lib/tools/claude-desktop.js";async function g(e){const o=await r(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),hasGrok:t.some(e=>e.includes("grok")),probed:!0}}function k(e,o){return"claude"===o?e.hasClaude:"gpt"===o?e.hasGpt:e.hasGrok}export function parseOnOff(e){const o=(e??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(o)||!["off","false","hide","0","no","n"].includes(o)&&void 0}function y(o){console.log(e.green(o))}function _(o){console.log(e.yellow(o))}function b(o){console.error(e.red(o))}async function h(o,t){console.log(e.gray(i.t("setup.validating",{label:o})));const s=await d(t);return!!s.valid||(b(i.t("setup.invalid_key",{label:o,reason:s.message||s.error||"invalid"})),"network_error"===s.error&&_(i.t("base.network_hint")),!1)}async function x(o,t,s={}){const{oneMillion:d,officialLogin:r,desktopRegistry:c,force:u}=s,g=n[o];try{"claude-desktop"===o&&void 0!==c&&a.setToolSetting("claude-desktop",f,c),"codex"===o&&void 0!==d&&a.setToolSetting("codex",p,d),"claude-desktop"===o&&void 0!==r&&a.setToolSetting("claude-desktop",m,!r),await l.applyPrivateConfig(o,t,a.getModelsForTool(o),{force:u})}catch(e){const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return b("× "+i.t("setup.apply_failed",{tool:g.displayName,reason:o})),!1}return l.printApplied(o),u&&console.log(e.gray(" "+i.t("config.force_done",{tool:g.displayName}))),"codex"===o&&void 0!==d&&console.log(e.gray(" "+i.t(d?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),l.isToolInstalled(o)||console.log(e.yellow(" ! "+i.t("setup.install_hint",{tool:g.displayName,install:g.installCommand}))),!0}export async function setupCommand(e){const d=e.key?.trim(),r=e.claudekey?.trim(),f=e.codexkey?.trim(),m=e.grokkey?.trim(),v=e.desktopkey?.trim(),w=void 0!==e.oneMillion,C=void 0!==e.claudeOfficial;let
|
|
1
|
+
import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as s}from"./base.js";import{i18n as i}from"../lib/i18n.js";import{configManager as a}from"../lib/config.js";import{toolManager as l,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as d,fetchModels as r}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as p,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as f,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as m}from"../lib/tools/claude-desktop.js";async function g(e){const o=await r(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),hasGrok:t.some(e=>e.includes("grok")),hasGemini:t.some(e=>e.startsWith("gemini")),probed:!0}}function k(e,o){return"claude"===o?e.hasClaude:"gpt"===o?e.hasGpt:e.hasGrok}export function parseOnOff(e){const o=(e??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(o)||!["off","false","hide","0","no","n"].includes(o)&&void 0}function y(o){console.log(e.green(o))}function _(o){console.log(e.yellow(o))}function b(o){console.error(e.red(o))}async function h(o,t){console.log(e.gray(i.t("setup.validating",{label:o})));const s=await d(t);return!!s.valid||(b(i.t("setup.invalid_key",{label:o,reason:s.message||s.error||"invalid"})),"network_error"===s.error&&_(i.t("base.network_hint")),!1)}async function x(o,t,s={}){const{oneMillion:d,officialLogin:r,desktopRegistry:c,force:u}=s,g=n[o];try{"claude-desktop"===o&&void 0!==c&&a.setToolSetting("claude-desktop",f,c),"codex"===o&&void 0!==d&&a.setToolSetting("codex",p,d),"claude-desktop"===o&&void 0!==r&&a.setToolSetting("claude-desktop",m,!r),await l.applyPrivateConfig(o,t,a.getModelsForTool(o),{force:u})}catch(e){const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return b("× "+i.t("setup.apply_failed",{tool:g.displayName,reason:o})),!1}return l.printApplied(o),u&&console.log(e.gray(" "+i.t("config.force_done",{tool:g.displayName}))),"codex"===o&&void 0!==d&&console.log(e.gray(" "+i.t(d?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),l.isToolInstalled(o)||console.log(e.yellow(" ! "+i.t("setup.install_hint",{tool:g.displayName,install:g.installCommand}))),!0}export async function setupCommand(e){const d=e.key?.trim(),r=e.claudekey?.trim(),f=e.codexkey?.trim(),m=e.grokkey?.trim(),v=e.desktopkey?.trim(),w=void 0!==e.oneMillion,C=void 0!==e.claudeOfficial;let G;if(C&&(G=parseOnOff(e.claudeOfficial),void 0===G))return b(i.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const M=void 0!==e.desktopRegistry;let K;if(M&&(K=parseOnOff(e.desktopRegistry),void 0===K))return b(i.t("setup.desktop_registry_invalid",{value:String(e.desktopRegistry)})),void(process.exitCode=1);const T=C||M,j=Boolean(d||r||f||m||v),S={oneMillion:e.oneMillion,officialLogin:G,desktopRegistry:K,force:e.force};if(!j&&(w||T)){if(w){const o=a.getApiKeyForTool("codex");if(o)try{const t=!0===e.oneMillion;a.setToolSetting("codex",p,t);const s=await u(o);c(t,s??void 0),y(i.t(t?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){b(i.t("setup.apply_failed",{tool:n.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else b(i.t("setup.codex_1m_missing_key")),process.exitCode=1}if(T){const e=a.getApiKeyForTool("claude-desktop");e?await x("claude-desktop",e,{...S,oneMillion:void 0})?await t(["claude-desktop"]):process.exitCode=1:(b(i.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!j)return b(i.t("setup.usage")),void(process.exitCode=1);await s(d||r||f||m||v);let O=0,A=0;const I=new Set,N=[];r&&N.push({tool:"claude-code",label:"--claudekey",apiKey:r,need:"claude"}),f&&N.push({tool:"codex",label:"--codexkey",apiKey:f,need:"gpt"}),m&&N.push({tool:"grok",label:"--grokkey",apiKey:m,need:"grok"}),v&&N.push({tool:"claude-desktop",label:"--desktopkey",apiKey:v,need:"claude"});for(const e of N){if(!await h(e.label,e.apiKey)){A+=1;continue}const o=await g(e.apiKey);!o.probed||k(o,e.need)?(o.probed||_(i.t("setup.probe_failed",{label:e.label})),a.setToolApiKey(e.tool,e.apiKey),await x(e.tool,e.apiKey,S)?(O+=1,I.add(e.tool)):A+=1):(b(i.t(`setup.key_no_${e.need}`,{label:e.label,tool:n[e.tool].displayName})),A+=1)}if(d){if(!await h("--key",d))return void(process.exitCode=1);a.setApiKey(d);const e=await g(d);if(e.probed||_(i.t("setup.probe_failed",{label:"--key"})),e.probed&&!e.hasClaude&&!e.hasGpt&&!e.hasGrok)return b(i.t("setup.key_no_family")),void(process.exitCode=1);const o=[{tool:"claude-code",capable:e.hasClaude,skipMsg:i.t("setup.skip_no_claude")},{tool:"codex",capable:e.hasGpt,skipMsg:i.t("setup.skip_no_gpt")},{tool:"grok",capable:e.hasGrok,skipMsg:i.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:e.hasClaude&&l.isToolInstalled("claude-desktop"),skipMsg:i.t("setup.skip_desktop_not_installed"),quiet:!e.hasClaude},{tool:"agy",capable:e.hasGemini&&l.isToolInstalled("agy"),skipMsg:i.t("setup.skip_no_gemini"),quiet:!l.isToolInstalled("agy")}];for(const e of o)I.has(e.tool)||(e.capable?await x(e.tool,d,S)?(O+=1,I.add(e.tool)):A+=1:e.quiet||_(e.skipMsg))}if(O>0){const e=[...I].filter(e=>!o(e)?.isInstalled).map(e=>n[e]?.command).filter(Boolean).join(" / "),t=i.t("setup.done",{count:String(O)});y("\n"+(e?`${t}${i.t("setup.done_commands",{commands:e})}`:t))}A>0&&b(i.t("setup.failed_summary",{count:String(A)})),await t([...I]),(A>0||0===O)&&(process.exitCode=1)}
|
|
@@ -72,11 +72,14 @@ export interface GatewayClientModel {
|
|
|
72
72
|
description?: string;
|
|
73
73
|
contextWindow?: number;
|
|
74
74
|
autoCompactTokens?: number;
|
|
75
|
+
/** Taken off by the gateway: not listed, and keeps the built-in entry of the same id out too. */
|
|
76
|
+
hidden?: boolean;
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
79
|
* 拉取网关下发给编码客户端的模型清单(公开接口,无需鉴权):每个工具的模型、名称、说明、
|
|
78
80
|
* 上下文窗口与压缩阈值,站长改了即时生效。老网关没有这个接口,任何失败都返回 null,
|
|
79
|
-
* 调用方回退到 models.json
|
|
81
|
+
* 调用方回退到 models.json 里的内置清单。隐藏的模型只留 id 和 `hidden` 标记:不列出,
|
|
82
|
+
* 也挡住内置清单里的同名条目。
|
|
80
83
|
*/
|
|
81
84
|
export declare function fetchClientModelConfig(options?: {
|
|
82
85
|
timeoutMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as r}from"./i18n.js";import{rememberPublishedAddresses as o}from"./gateway-addresses.js";const n=e=>new Promise(t=>setTimeout(t,e));async function a(e,o,n){const a=`${t.getBaseUrl()}/v1/models`,s=new AbortController,l=setTimeout(()=>s.abort(),o);try{const t=await n(a,{method:"GET",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},signal:s.signal});if(401===t.status)return{valid:!1,error:"invalid_api_key",message:"API Key is invalid or expired"};if(t.ok){try{const e=await t.json();if(e&&"list"===e.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${t.status}: ${t.statusText}`}}catch(e){return e instanceof Error&&"AbortError"===e.name?{valid:!1,error:"network_error",message:"Request timeout"}:function(e){const t=e?.cause?.code;return"string"==typeof t&&i.has(t)}(e)?{valid:!1,error:"network_error",message:r.t("messages.certificate_intercepted",{url:a})}:{valid:!1,error:"network_error",message:e instanceof Error?e.message:"Network connection failed"}}finally{clearTimeout(l)}}const i=new Set(["DEPTH_ZERO_SELF_SIGNED_CERT","SELF_SIGNED_CERT_IN_CHAIN","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_UNTRUSTED","ERR_TLS_CERT_ALTNAME_INVALID"]);export async function fetchModels(r,o={}){const{timeoutMs:n=3e4,fetchFn:a=fetch}=o,i=`${t.getBaseUrl()}/v1/models`,s=new AbortController,l=setTimeout(()=>s.abort(),n);try{const e=await a(i,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:s.signal});if(!e.ok)return{ok:!1,models:[],error:`HTTP ${e.status}`};const t=await e.json();return{ok:!0,models:(Array.isArray(t?.data)?t.data:[]).map(e=>"string"==typeof e?e:e?.id).filter(e=>"string"==typeof e&&e.trim().length>0).map(e=>e.trim())}}catch(t){return e.logError("fetchModels",t),{ok:!1,models:[],error:t instanceof Error?t.message:"network error"}}finally{clearTimeout(l)}}function s(e){return e.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.name).map(e=>({name:e.name.trim(),..."string"==typeof e.labelOverride?{labelOverride:e.labelOverride}:{},..."boolean"==typeof e.supports1m?{supports1m:e.supports1m}:{}})).filter(e=>e.name.length>0)}export async function fetchGatewayStatus(r={}){const{timeoutMs:n=15e3,fetchFn:a=fetch}=r,i=`${t.getBaseUrl()}/api/status`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const e=await a(i,{method:"GET",signal:l.signal});if(!e.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${e.status}`};const r=await e.json();o(r?.data,t.getBaseUrl());let n=r?.data?.claude_desktop_group_models;if("string"==typeof n)try{n=JSON.parse(n)}catch{n=void 0}const c={};if(n&&"object"==typeof n&&!Array.isArray(n))for(const[e,t]of Object.entries(n))Array.isArray(t)&&(c[e]=s(t));return{ok:!0,claudeDesktopGroupModels:c}}catch(t){return e.logError("fetchGatewayStatus",t),{ok:!1,claudeDesktopGroupModels:{},error:t instanceof Error?t.message:"network error"}}finally{clearTimeout(c)}}export async function fetchKeyGroupInfo(r,o={}){const{timeoutMs:n=1e4,fetchFn:a=fetch}=o,i=`${t.getBaseUrl()}/api/platform/vibecode/key`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const e=await a(i,{method:"GET",headers:{Authorization:`Bearer ${r}`},signal:l.signal});if(!e.ok)return null;const t=await e.json(),o=t?.data;if(!o||"string"!=typeof o.group||!Array.isArray(o.claude_desktop_models))return null;const n=Array.isArray(o.groups)?o.groups.filter(e=>"string"==typeof e&&!!e):[];return{group:o.group,groups:n.length>0?n:[o.group],claudeDesktopModels:s(o.claude_desktop_models)}}catch(t){return e.logError("fetchKeyGroupInfo",t),null}finally{clearTimeout(c)}}function l(e){return"number"==typeof e&&Number.isFinite(e)&&e>0?Math.floor(e):void 0}export async function fetchClientModelConfig(r={}){const{timeoutMs:o=1e4,fetchFn:n=fetch}=r,a=`${t.getBaseUrl()}/api/platform/vibecode/config`,i=new AbortController,s=setTimeout(()=>i.abort(),o);try{const e=await n(a,{method:"GET",signal:i.signal});if(!e.ok)return null;const t=await e.json(),r=t?.data?.models;if(!r||"object"!=typeof r||Array.isArray(r))return null;const o={};for(const[e,t]of Object.entries(r))Array.isArray(t)&&(o[e]=t.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.id).filter(e
|
|
1
|
+
import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as r}from"./i18n.js";import{rememberPublishedAddresses as o}from"./gateway-addresses.js";const n=e=>new Promise(t=>setTimeout(t,e));async function a(e,o,n){const a=`${t.getBaseUrl()}/v1/models`,s=new AbortController,l=setTimeout(()=>s.abort(),o);try{const t=await n(a,{method:"GET",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},signal:s.signal});if(401===t.status)return{valid:!1,error:"invalid_api_key",message:"API Key is invalid or expired"};if(t.ok){try{const e=await t.json();if(e&&"list"===e.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${t.status}: ${t.statusText}`}}catch(e){return e instanceof Error&&"AbortError"===e.name?{valid:!1,error:"network_error",message:"Request timeout"}:function(e){const t=e?.cause?.code;return"string"==typeof t&&i.has(t)}(e)?{valid:!1,error:"network_error",message:r.t("messages.certificate_intercepted",{url:a})}:{valid:!1,error:"network_error",message:e instanceof Error?e.message:"Network connection failed"}}finally{clearTimeout(l)}}const i=new Set(["DEPTH_ZERO_SELF_SIGNED_CERT","SELF_SIGNED_CERT_IN_CHAIN","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_UNTRUSTED","ERR_TLS_CERT_ALTNAME_INVALID"]);export async function fetchModels(r,o={}){const{timeoutMs:n=3e4,fetchFn:a=fetch}=o,i=`${t.getBaseUrl()}/v1/models`,s=new AbortController,l=setTimeout(()=>s.abort(),n);try{const e=await a(i,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:s.signal});if(!e.ok)return{ok:!1,models:[],error:`HTTP ${e.status}`};const t=await e.json();return{ok:!0,models:(Array.isArray(t?.data)?t.data:[]).map(e=>"string"==typeof e?e:e?.id).filter(e=>"string"==typeof e&&e.trim().length>0).map(e=>e.trim())}}catch(t){return e.logError("fetchModels",t),{ok:!1,models:[],error:t instanceof Error?t.message:"network error"}}finally{clearTimeout(l)}}function s(e){return e.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.name).map(e=>({name:e.name.trim(),..."string"==typeof e.labelOverride?{labelOverride:e.labelOverride}:{},..."boolean"==typeof e.supports1m?{supports1m:e.supports1m}:{}})).filter(e=>e.name.length>0)}export async function fetchGatewayStatus(r={}){const{timeoutMs:n=15e3,fetchFn:a=fetch}=r,i=`${t.getBaseUrl()}/api/status`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const e=await a(i,{method:"GET",signal:l.signal});if(!e.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${e.status}`};const r=await e.json();o(r?.data,t.getBaseUrl());let n=r?.data?.claude_desktop_group_models;if("string"==typeof n)try{n=JSON.parse(n)}catch{n=void 0}const c={};if(n&&"object"==typeof n&&!Array.isArray(n))for(const[e,t]of Object.entries(n))Array.isArray(t)&&(c[e]=s(t));return{ok:!0,claudeDesktopGroupModels:c}}catch(t){return e.logError("fetchGatewayStatus",t),{ok:!1,claudeDesktopGroupModels:{},error:t instanceof Error?t.message:"network error"}}finally{clearTimeout(c)}}export async function fetchKeyGroupInfo(r,o={}){const{timeoutMs:n=1e4,fetchFn:a=fetch}=o,i=`${t.getBaseUrl()}/api/platform/vibecode/key`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const e=await a(i,{method:"GET",headers:{Authorization:`Bearer ${r}`},signal:l.signal});if(!e.ok)return null;const t=await e.json(),o=t?.data;if(!o||"string"!=typeof o.group||!Array.isArray(o.claude_desktop_models))return null;const n=Array.isArray(o.groups)?o.groups.filter(e=>"string"==typeof e&&!!e):[];return{group:o.group,groups:n.length>0?n:[o.group],claudeDesktopModels:s(o.claude_desktop_models)}}catch(t){return e.logError("fetchKeyGroupInfo",t),null}finally{clearTimeout(c)}}function l(e){return"number"==typeof e&&Number.isFinite(e)&&e>0?Math.floor(e):void 0}export async function fetchClientModelConfig(r={}){const{timeoutMs:o=1e4,fetchFn:n=fetch}=r,a=`${t.getBaseUrl()}/api/platform/vibecode/config`,i=new AbortController,s=setTimeout(()=>i.abort(),o);try{const e=await n(a,{method:"GET",signal:i.signal});if(!e.ok)return null;const t=await e.json(),r=t?.data?.models;if(!r||"object"!=typeof r||Array.isArray(r))return null;const o={};for(const[e,t]of Object.entries(r))Array.isArray(t)&&(o[e]=t.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.id).filter(e=>e.id.trim().length>0).map(e=>{const t={id:e.id.trim()};if(!0===e.hidden)return{...t,hidden:!0};"string"==typeof e.name&&e.name.trim()&&(t.name=e.name.trim()),"string"==typeof e.description&&e.description.trim()&&(t.description=e.description.trim());const r=l(e.context_window);r&&(t.contextWindow=r);const o=l(e.auto_compact_tokens);return o&&(t.autoCompactTokens=o),t}));return o}catch(t){return e.logError("fetchClientModelConfig",t),null}finally{clearTimeout(s)}}export async function validateApiKey(t,r={}){const{timeoutMs:o=3e4,retries:i=2,backoffMs:s=500,fetchFn:l=fetch,sleepFn:c=n}=r;let u={valid:!1,error:"network_error",message:"Network connection failed"};for(let r=0;r<=i;r++){const n=await a(t,o,l);if(n.valid||"invalid_api_key"===n.error)return n.valid||e.logError("validateApiKey",new Error(n.message||"invalid_api_key")),n;u=n,r<i&&await c(s*Math.pow(2,r))}return e.logError("validateApiKey",new Error(u.message||"network_error")),u}
|
|
@@ -16,6 +16,8 @@ export declare function getShellRcFilePath(): string | null;
|
|
|
16
16
|
export declare function cleanupShellRcEnvVars(): boolean;
|
|
17
17
|
/** 测试注入假的 PowerShell。 */
|
|
18
18
|
export declare function setPowerShellRunnerForTests(fake: (script: string) => string): void;
|
|
19
|
+
/** 经可注入的 PowerShell 执行一段脚本(测试里由 `setPowerShellRunnerForTests` 替换)。 */
|
|
20
|
+
export declare function runPowerShell(script: string): string;
|
|
19
21
|
/**
|
|
20
22
|
* Windows 上这些变量的持久来源是「用户环境变量」「系统环境变量」和 PowerShell 配置文件里的
|
|
21
23
|
* `$env:NAME = ...`。用户级用 .NET 接口删除(会广播变更,新开的终端立即生效,不需要管理员);
|
package/dist/lib/env-cleanup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as e}from"child_process";import{existsSync as n,readFileSync as r,writeFileSync as o}from"fs";import{join as t}from"path";import{logger as i}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const e=getShellRcFilePath();if(!e||!n(e))return!1;let t=r(e,"utf-8");const s=t,l=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of l)t=t.replace(e,"");if(t!==s)return o(e,t,"utf-8"),i.info(`Cleaned up ANTHROPIC_* environment variables from ${e}`),!0}catch(e){i.warn("Failed to cleanup shell environment variables:",e),i.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}let l=n=>e("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf-8",windowsHide:!0,timeout:2e4});export function setPowerShellRunnerForTests(e){l=e}export function cleanupWindowsEnvVars(e){const c=[];if("win32"!==process.platform||0===e.length)return{machineLeft:c};try{const n=e.map(e=>`'${e}'`).join(","),r=l(`foreach($n in @(${n})){if($null -ne [Environment]::GetEnvironmentVariable($n,'User')){[Environment]::SetEnvironmentVariable($n,$null,'User');"user:$n"};if($null -ne [Environment]::GetEnvironmentVariable($n,'Machine')){try{[Environment]::SetEnvironmentVariable($n,$null,'Machine');"machine-removed:$n"}catch{"machine:$n"}}}`);for(const e of r.split(/\r?\n/)){const n=e.trim().match(/^machine:(\w+)$/);n&&c.push(n[1])}i.info(`Windows env cleanup: ${r.replace(/\s+/g," ").trim()}`)}catch(e){i.logError("envCleanup.cleanupWindowsEnvVars",e)}for(const l of function(){const e=s(),n=[];for(const r of[t(e,"Documents"),t(e,"OneDrive","Documents"),t(e,"OneDrive","文档")])for(const e of["PowerShell","WindowsPowerShell"])n.push(t(r,e,"Microsoft.PowerShell_profile.ps1"),t(r,e,"profile.ps1"));return n}())try{if(!n(l))continue;const t=r(l,"utf-8");let s=t;for(const n of e)s=s.replace(new RegExp(String.raw`^[ \t]*\$env:${n}[ \t]*=.*(\r?\n)?`,"gim"),"");s!==t&&(o(l,s,"utf-8"),i.info(`Removed ANTHROPIC_* assignments from ${l}`))}catch(e){i.logError("envCleanup.powerShellProfile",e)}return{machineLeft:c}}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars(),c=cleanupWindowsEnvVars(e).machineLeft;for(const n of e)delete process.env[n];return[...e]}let c=[];export function machineEnvVarsLeft(){return c}
|
|
1
|
+
import{execFileSync as e}from"child_process";import{existsSync as n,readFileSync as r,writeFileSync as o}from"fs";import{join as t}from"path";import{logger as i}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const e=getShellRcFilePath();if(!e||!n(e))return!1;let t=r(e,"utf-8");const s=t,l=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of l)t=t.replace(e,"");if(t!==s)return o(e,t,"utf-8"),i.info(`Cleaned up ANTHROPIC_* environment variables from ${e}`),!0}catch(e){i.warn("Failed to cleanup shell environment variables:",e),i.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}let l=n=>e("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf-8",windowsHide:!0,timeout:2e4});export function setPowerShellRunnerForTests(e){l=e}export function runPowerShell(e){return l(e)}export function cleanupWindowsEnvVars(e){const c=[];if("win32"!==process.platform||0===e.length)return{machineLeft:c};try{const n=e.map(e=>`'${e}'`).join(","),r=l(`foreach($n in @(${n})){if($null -ne [Environment]::GetEnvironmentVariable($n,'User')){[Environment]::SetEnvironmentVariable($n,$null,'User');"user:$n"};if($null -ne [Environment]::GetEnvironmentVariable($n,'Machine')){try{[Environment]::SetEnvironmentVariable($n,$null,'Machine');"machine-removed:$n"}catch{"machine:$n"}}}`);for(const e of r.split(/\r?\n/)){const n=e.trim().match(/^machine:(\w+)$/);n&&c.push(n[1])}i.info(`Windows env cleanup: ${r.replace(/\s+/g," ").trim()}`)}catch(e){i.logError("envCleanup.cleanupWindowsEnvVars",e)}for(const l of function(){const e=s(),n=[];for(const r of[t(e,"Documents"),t(e,"OneDrive","Documents"),t(e,"OneDrive","文档")])for(const e of["PowerShell","WindowsPowerShell"])n.push(t(r,e,"Microsoft.PowerShell_profile.ps1"),t(r,e,"profile.ps1"));return n}())try{if(!n(l))continue;const t=r(l,"utf-8");let s=t;for(const n of e)s=s.replace(new RegExp(String.raw`^[ \t]*\$env:${n}[ \t]*=.*(\r?\n)?`,"gim"),"");s!==t&&(o(l,s,"utf-8"),i.info(`Removed ANTHROPIC_* assignments from ${l}`))}catch(e){i.logError("envCleanup.powerShellProfile",e)}return{machineLeft:c}}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars(),c=cleanupWindowsEnvVars(e).machineLeft;for(const n of e)delete process.env[n];return[...e]}let c=[];export function machineEnvVarsLeft(){return c}
|
|
@@ -10,10 +10,12 @@ export type GatewayModelTool = 'codex' | 'claude-code';
|
|
|
10
10
|
export declare function autoCompactTokens(window: number, explicit?: number): number;
|
|
11
11
|
/**
|
|
12
12
|
* The models to add for `tool`. When the gateway publishes its client config
|
|
13
|
-
* (`/api/platform/vibecode/config`) its lists
|
|
14
|
-
* built-in values, and Claude models only come from it when `models.json` lists
|
|
13
|
+
* (`/api/platform/vibecode/config`) its lists come first — fields it leaves out keep
|
|
14
|
+
* the built-in values, and Claude models only come from it when `models.json` lists
|
|
15
15
|
* them too (so a model removed there, like Haiku for Codex, stays out); Codex also
|
|
16
|
-
* gets the `gemini` list.
|
|
16
|
+
* gets the `gemini` list. Built-in entries the lists lack are added (see
|
|
17
|
+
* `withBuiltInExtras`); a `hidden` row on the gateway keeps one out. Without the
|
|
18
|
+
* config, the built-in list from `models.json` → `gatewayModels`.
|
|
17
19
|
*/
|
|
18
20
|
export declare function gatewayModelSpecs(tool: GatewayModelTool, clientModels?: Record<string, GatewayClientModel[]>): GatewayModelSpec[];
|
|
19
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getGatewayModelRules as t}from"./model-registry.js";export function autoCompactTokens(e,
|
|
1
|
+
import{getGatewayModelRules as t}from"./model-registry.js";export function autoCompactTokens(e,i){return i&&i>0?Math.min(i,e):Math.floor(e*t().autoCompactRatio)}export function gatewayModelSpecs(e,i){const o=t().models,n=o.filter(t=>function(t,e){return"codex"===t?!1!==e.codex:!e.id.startsWith("claude-")&&!1!==e.claudeCode}(e,t));if(!i)return n;const d=new Set(Object.values(i).flat().filter(t=>t.hidden).map(t=>t.id)),r=Object.fromEntries(Object.entries(i).map(([t,e])=>[t,e.filter(t=>!t.hidden)])),s=new Map(("claude-code"===e?o:n).map(t=>[t.id,t])),l=(r.gemini??[]).filter(t=>!1!==o.find(e=>e.id===t.id)?.codex),a="codex"===e?[...r.codex??[],...l,...(r.claude??[]).filter(t=>t.id.startsWith("claude-")&&s.has(t.id))]:function(t,e){return[...(t.codex??[]).filter(t=>"gpt"===modelFamily(t.id)),...t.gemini??[],...t.grok??[],...(t.claude??[]).filter(t=>!t.id.startsWith("claude-"))].filter(t=>e.get(t.id)?.claudeCode??!0)}(r,s);if(0===a.length)return n.filter(t=>!d.has(t.id));const c=new Set,u=[];for(const t of a)c.has(t.id)||(c.add(t.id),u.push({...s.get(t.id),...t}));return function(t,e,i){const o=[...t];return e.forEach((t,n)=>{if(i.has(t.id)||o.some(e=>e.id===t.id))return;const d=modelFamily(t.id),r=new Set(e.slice(n+1).filter(t=>modelFamily(t.id)===d).map(t=>t.id));let s=o.findIndex(t=>r.has(t.id));if(s<0){s=o.length;for(let t=o.length-1;t>=0;t--)if(modelFamily(o[t].id)===d){s=t+1;break}}o.splice(s,0,t)}),o}(u,n,d)}const e={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function modelBlurb(t,e={}){const i=[];for(const o of(t??"").split("|")){const t=o.trim();t&&(/\d\s*万?\s*输出|output\D*\d|\d\D*output/i.test(t)||(/[\d百千]\s*万?\s*上下文|context\D*\d|\d\D*context/i.test(t)?e.millionContext&&!i.includes("百万上下文")&&i.unshift("百万上下文"):i.push(t)))}return e.millionContext&&!i.includes("百万上下文")&&i.unshift("百万上下文"),i.length>0?i.join(" | "):void 0}export function prettyModelName(t){const i=t.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),o=[];for(const t of i){const i=o[o.length-1];if(/^\d+$/.test(t)&&i&&/^\d+$/.test(i)&&t.length<=2&&!/^\d{8}$/.test(t)){o[o.length-1]=`${i}.${t}`;continue}const n=t.toLowerCase();o.push(e[n]??(/^[a-z]\d/i.test(t)?t.toUpperCase():t[0].toUpperCase()+t.slice(1)))}return o.join(" ")}export function vendorNames(t){return[...new Set(t.map(t=>prettyModelName(t).split(" ")[0]))]}export function modelFamily(t){const e=t.toLowerCase();return e.startsWith("claude-")?"claude":e.startsWith("gemini")?"gemini":e.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(e)?"gpt":"other"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface PersistentEnvResult {
|
|
2
|
+
/** rc 文件里加了加载行(新开终端才生效);Windows 为空。 */
|
|
3
|
+
rcFiles: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* 写入(覆盖)env 文件并确保各 rc / Windows 用户环境变量指向这些值。
|
|
7
|
+
* 同时更新本进程的 process.env,让 vibecli 随后启动的子进程立即可用。
|
|
8
|
+
*/
|
|
9
|
+
export declare function writePersistentEnv(id: string, envFile: string, vars: Record<string, string>): PersistentEnvResult;
|
|
10
|
+
/** 删掉 env 文件、rc 里我们加的那一行,以及 Windows 用户环境变量里我们写的值。 */
|
|
11
|
+
export declare function removePersistentEnv(id: string, envFile: string, names: readonly string[]): void;
|
|
12
|
+
/** 读回 env 文件里的变量;文件不存在或没有内容时为空对象。 */
|
|
13
|
+
export declare function readPersistentEnvFile(envFile: string): Record<string, string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{chmodSync as n,existsSync as t,readFileSync as e,realpathSync as o,unlinkSync as r}from"fs";import{join as i}from"path";import{logger as s}from"../utils/logger.js";import{getShellRcFilePath as c,runPowerShell as f}from"./env-cleanup.js";import{atomicWriteFile as l,backupBeforeWrite as u}from"./tools/backup.js";import{toolHome as p}from"./tools/types.js";const a=n=>`# vibecli:${n}`;function m(){const n=p(),e=new Set,o=c();o&&e.add(o);for(const o of[".zshrc",".bashrc"]){const r=i(n,o);t(r)&&e.add(r)}return[...e]}function $(n,t,e){const o=`"${function(n){const t=p();return n.startsWith(t+"/")?`$HOME/${n.slice(t.length+1)}`:n}(t)}"`;return n.endsWith(".fish")?`test -f ${o}; and source ${o} ${a(e)}`:`[ -f ${o} ] && . ${o} ${a(e)}`}function v(n,e){const r=t(n)?o(n):n;u(r),l(r,e)}function E(n,t){return n.split("\n").filter(n=>!n.trimEnd().endsWith(a(t))).join("\n")}function d(n,t){if(/['\r\n]/.test(t))throw new Error(`${n} contains a quote or newline and cannot be written to a shell env file`)}export function writePersistentEnv(o,r,i){for(const[n,t]of Object.entries(i))d(n,t);const s=Object.entries(i).map(([n,t])=>`export ${n}='${t}'`).join("\n");u(r),l(r,`# Written by vibecli. Loaded from your shell profile.\n${s}\n`,384),n(r,384);for(const[n,t]of Object.entries(i))process.env[n]=t;if("win32"===process.platform){const n=Object.entries(i).map(([n,t])=>`[Environment]::SetEnvironmentVariable('${n}','${t}','User')`).join(";");return f(n),{rcFiles:[]}}const c=[];for(const n of m()){const i=t(n)?e(n,"utf-8"):"",s=$(n,r,o);if(i.split("\n").some(n=>n.trimEnd()===s)){c.push(n);continue}const f=E(i,o).replace(/\n*$/,"");v(n,`${f?f+"\n\n":""}${s}\n`),c.push(n)}return{rcFiles:c}}export function removePersistentEnv(n,o,i){const c=readPersistentEnvFile(o);if(t(o)&&(u(o),r(o)),"win32"===process.platform){const n=i.filter(n=>void 0!==c[n]).map(n=>`if([Environment]::GetEnvironmentVariable('${n}','User') -eq '${c[n]}'){[Environment]::SetEnvironmentVariable('${n}',$null,'User')}`).join(";");if(n)try{f(n)}catch(n){s.logError("persistentEnv.remove",n)}return}for(const o of m()){if(!t(o))continue;const r=e(o,"utf-8"),i=E(r,n);i!==r&&v(o,i)}}export function readPersistentEnvFile(n){const o={};if(!t(n))return o;for(const t of e(n,"utf-8").split("\n")){const n=t.match(/^export ([A-Z_][A-Z0-9_]*)='([^']*)'$/);n&&(o[n[1]]=n[2])}return o}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as t,readFileSync as r}from"fs";import{join as i}from"path";import{i18n as e}from"../i18n.js";import{logger as n}from"../../utils/logger.js";import{readPersistentEnvFile as o,removePersistentEnv as a,writePersistentEnv as s}from"../persistent-env.js";import{backupBeforeWrite as l,atomicWriteFile as g}from"./backup.js";import{toolHome as c}from"./types.js";const p="GEMINI_API_KEY",m="GOOGLE_GEMINI_BASE_URL",f="gemini",y=()=>i(c(),".gemini","antigravity-cli"),d=()=>i(y(),"settings.json"),u=()=>i(y(),"vibeapi.env"),v="win32"===process.platform?'powershell -c "irm https://antigravity.google/cli/install.ps1 | iex"':"curl -fsSL https://antigravity.google/cli/install.sh | bash";function h(){if(!t(d()))return{};const i=r(d(),"utf-8");if(!i.trim())return{};try{const t=JSON.parse(i);if(!(e=t)||"object"!=typeof e||Array.isArray(e))throw new Error("settings.json root must be an object");return t}catch(t){throw new Error(`Existing ~/.gemini/antigravity-cli/settings.json cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}var e}function P(t){l(d()),g(d(),JSON.stringify(t,null,2)+"\n")}let b=null;export const antigravityAdapter={name:"agy",displayName:"Antigravity CLI",command:"agy",installCommand:v,upgradeCommand:"agy update",configPath:d(),loadPrivateConfig(t){const r=h();var i;b=s("agy",u(),{[p]:t.apiKey,[m]:(i=t.baseUrl,(i||"").replace(/\/+$/,"").replace(/\/v1(beta)?$/,""))}).rcFiles,r.modelProvider!==f&&(r.modelProvider=f,P(r))},unloadPrivateConfig(){try{const t=h();t.modelProvider===f&&(delete t.modelProvider,P(t))}catch(t){n.logError("antigravityAdapter.unloadPrivateConfig",t)}a("agy",u(),[p,m])},detectCurrentConfig(){try{if(h().modelProvider!==f)return{apiKey:null}}catch{return{apiKey:null}}const t=o(u())[p];return{apiKey:t&&t.trim()?t:null}},getConfig(){try{return h()}catch(t){return n.logError("antigravityAdapter.getConfig",t),null}},applyNotes:()=>null===b?[]:0===b.length?[{kind:"action",text:e.t("tools.agy.reopen_terminal_windows")}]:[{kind:"action",text:e.t("tools.agy.reopen_terminal",{rc:b.join(", ")})}],backupPaths:()=>[d(),u()],forceResetPaths:()=>[u()]};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* leaves the original intact instead of a half-written config. Cleans up the
|
|
5
5
|
* temp file on failure and rethrows.
|
|
6
6
|
*/
|
|
7
|
-
export declare function atomicWriteFile(filePath: string, content: string): void;
|
|
7
|
+
export declare function atomicWriteFile(filePath: string, content: string, mode?: number): void;
|
|
8
8
|
/** How many timestamped backups to keep per file before the oldest are pruned. */
|
|
9
9
|
export declare const MAX_TOOL_BACKUPS = 10;
|
|
10
10
|
/**
|
package/dist/lib/tools/backup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as t,copyFileSync as r,readdirSync as e,statSync as o,unlinkSync as s,writeFileSync as c,renameSync as n,mkdirSync as a}from"fs";import{dirname as i,basename as u,join as m}from"path";import{logger as p}from"../../utils/logger.js";export function atomicWriteFile(r,e){const
|
|
1
|
+
import{existsSync as t,copyFileSync as r,readdirSync as e,statSync as o,unlinkSync as s,writeFileSync as c,renameSync as n,mkdirSync as a}from"fs";import{dirname as i,basename as u,join as m}from"path";import{logger as p}from"../../utils/logger.js";export function atomicWriteFile(r,e,o){const u=i(r);t(u)||a(u,{recursive:!0});const m=`${r}.tmp-${process.pid}-${Date.now()}`;try{c(m,e,void 0===o?"utf-8":{encoding:"utf-8",mode:o}),n(m,r)}catch(r){try{t(m)&&s(m)}catch{}throw r}}export const MAX_TOOL_BACKUPS=10;const f=".bak-";function h(r){const e=`${r}${f}${(new Date).toISOString().replace(/[:.]/g,"-")}`;let o=e,s=1;for(;t(o);)o=`${e}-${s++}`;return o}export function backupBeforeWrite(e,o=10){try{if(!t(e))return;r(e,h(e)),b(e,o)}catch(t){p.logError("backupBeforeWrite",t)}}function d(t){const r=t.match(/Z-(\d+)$/);return r?Number(r[1]):0}function l(t){const r=t.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z(?:-\d+)?$/);if(!r)return 0;const[,e,o,s,c,n,a,i]=r;return Date.UTC(Number(e),Number(o)-1,Number(s),Number(c),Number(n),Number(a),Number(i))}export function listBackups(t){const r=i(t),s=u(t)+f;try{return e(r).filter(t=>t.startsWith(s)).map(t=>{const e=m(r,t);return{path:e,name:t,mtimeMs:o(e).mtimeMs,seq:d(t),timestampMs:l(t)}}).sort((t,r)=>r.timestampMs-t.timestampMs||r.seq-t.seq||r.mtimeMs-t.mtimeMs)}catch{return[]}}export function restoreBackup(e,o){const s=o?listBackups(e).find(t=>t.path===o||t.name===o)?.path:listBackups(e)[0]?.path;if(!s||!t(s))return{restored:!1,reason:"no_backup"};let c;if(t(e)){const t=h(e);try{r(e,t),c=u(t)}catch(t){p.logError("restoreBackup.snapshot",t)}}return r(s,e),b(e,10),{restored:!0,from:u(s),snapshot:c}}function b(t,r){for(const e of listBackups(t).slice(Math.max(0,r)))try{s(e.path)}catch(t){p.logError("backupBeforeWrite.rotate",t)}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{claudeCodeAdapter as o}from"./claude-code.js";import{opencodeAdapter as r}from"./opencode.js";import{codexAdapter as t}from"./codex.js";import{openclawAdapter as
|
|
1
|
+
import{claudeCodeAdapter as o}from"./claude-code.js";import{opencodeAdapter as r}from"./opencode.js";import{codexAdapter as t}from"./codex.js";import{openclawAdapter as m}from"./openclaw.js";import{hermesAdapter as e}from"./hermes.js";import{atomcodeAdapter as p}from"./atomcode.js";import{grokAdapter as s}from"./grok.js";import{claudeDesktopAdapter as i}from"./claude-desktop.js";import{antigravityAdapter as n}from"./antigravity.js";const c=[o,r,t,m,e,p,s,i,n];export const TOOL_ADAPTERS=Object.fromEntries(c.map(o=>[o.name,o]));export function getAdapter(o){return TOOL_ADAPTERS[o]}export function listAdapters(){return[...c]}
|
package/dist/locales/en_US.json
CHANGED
|
@@ -357,6 +357,7 @@
|
|
|
357
357
|
"skip_no_claude": "This key has no Claude model group; skipped Claude Code",
|
|
358
358
|
"skip_no_gpt": "This key has no GPT model group; skipped Codex",
|
|
359
359
|
"skip_no_grok": "This key has no Grok model group; skipped Grok Build",
|
|
360
|
+
"skip_no_gemini": "This key has no Gemini model group; skipped Antigravity CLI",
|
|
360
361
|
"apply_failed": "Failed to write {{tool}} config: {{reason}}",
|
|
361
362
|
"install_hint": "{{tool}} not found; install it with {{install}}",
|
|
362
363
|
"done": "Done: {{count}} tool(s) configured. ",
|
|
@@ -376,6 +377,10 @@
|
|
|
376
377
|
},
|
|
377
378
|
"tools": {
|
|
378
379
|
"version_outdated": "version {{current}} is too old (needs {{min}} or later); upgrade with {{upgrade}}",
|
|
380
|
+
"agy": {
|
|
381
|
+
"reopen_terminal": "Key and address were added to {{rc}}; open a new terminal before running agy",
|
|
382
|
+
"reopen_terminal_windows": "Key and address were saved as user environment variables; reopen the terminal before running agy"
|
|
383
|
+
},
|
|
379
384
|
"codex": {
|
|
380
385
|
"catalog_written": "{{count}} models in the menu ({{breakdown}}); reopen the Codex desktop app to see them",
|
|
381
386
|
"catalog_skipped": "Couldn't read Codex's built-in model list (not installed, or older than 0.122), so the menu has GPT models only; install or upgrade Codex and run this again"
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -357,6 +357,7 @@
|
|
|
357
357
|
"skip_no_claude": "该 Key 未开通 Claude 模型分组,已跳过 Claude Code",
|
|
358
358
|
"skip_no_gpt": "该 Key 未开通 GPT 模型分组,已跳过 Codex",
|
|
359
359
|
"skip_no_grok": "该 Key 未开通 Grok 模型分组,已跳过 Grok Build",
|
|
360
|
+
"skip_no_gemini": "该 Key 未开通 Gemini 模型分组,已跳过 Antigravity CLI",
|
|
360
361
|
"apply_failed": "{{tool}} 配置写入失败: {{reason}}",
|
|
361
362
|
"install_hint": "没检测到 {{tool}},运行 {{install}} 安装",
|
|
362
363
|
"done": "完成:已配置 {{count}} 个工具。",
|
|
@@ -376,6 +377,10 @@
|
|
|
376
377
|
},
|
|
377
378
|
"tools": {
|
|
378
379
|
"version_outdated": "版本 {{current}} 过旧,需要 {{min}} 及以上,运行 {{upgrade}} 升级",
|
|
380
|
+
"agy": {
|
|
381
|
+
"reopen_terminal": "Key 与地址已写入 {{rc}},新开一个终端再运行 agy",
|
|
382
|
+
"reopen_terminal_windows": "Key 与地址已写入用户环境变量,重新打开终端再运行 agy"
|
|
383
|
+
},
|
|
379
384
|
"codex": {
|
|
380
385
|
"catalog_written": "模型菜单 {{count}} 个({{breakdown}}),Codex 桌面版重开后生效",
|
|
381
386
|
"catalog_skipped": "没读到 Codex 自带的模型列表(未安装或版本低于 0.122),模型菜单只有 GPT;安装或升级 Codex 后再运行一次"
|
package/dist/models.json
CHANGED
package/models.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.80",
|
|
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",
|