@vibeapi/cli 0.0.52 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/api-validator.d.ts +16 -0
- package/dist/lib/api-validator.js +1 -1
- package/dist/lib/model-catalog.d.ts +8 -6
- package/dist/lib/model-catalog.js +1 -1
- package/dist/lib/tools/claude-desktop.js +1 -1
- package/dist/locales/en_US.json +2 -1
- package/dist/locales/zh_CN.json +2 -1
- package/package.json +1 -1
|
@@ -49,6 +49,22 @@ export declare function fetchGatewayStatus(options?: {
|
|
|
49
49
|
timeoutMs?: number;
|
|
50
50
|
fetchFn?: typeof fetch;
|
|
51
51
|
}): Promise<FetchGatewayStatusResult>;
|
|
52
|
+
/** What the gateway says about one key: its group(s) and their Claude Desktop model table. */
|
|
53
|
+
export interface KeyGroupInfo {
|
|
54
|
+
group: string;
|
|
55
|
+
/** Effective groups in routing order (an `auto` key expands to several). */
|
|
56
|
+
groups: string[];
|
|
57
|
+
/** The desktop model table for those groups, first group wins per model. */
|
|
58
|
+
claudeDesktopModels: GatewayGroupModel[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 按 Key 查它所在的分组和分组里每个 Claude 模型是否支持 1M(`/api/platform/vibecode/key`)。
|
|
62
|
+
* 有了它就不用按模型集合去猜分组。老网关没有这个接口,任何失败都返回 null,调用方回退到猜分组。
|
|
63
|
+
*/
|
|
64
|
+
export declare function fetchKeyGroupInfo(apiKey: string, options?: {
|
|
65
|
+
timeoutMs?: number;
|
|
66
|
+
fetchFn?: typeof fetch;
|
|
67
|
+
}): Promise<KeyGroupInfo | null>;
|
|
52
68
|
/** One model row of the gateway's client config (`/api/platform/vibecode/config` → `models.<tool>[]`). */
|
|
53
69
|
export interface GatewayClientModel {
|
|
54
70
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";const
|
|
1
|
+
import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as r}from"./i18n.js";const o=e=>new Promise(t=>setTimeout(t,e));async function n(e,o,n){const i=`${t.getBaseUrl()}/v1/models`,s=new AbortController,l=setTimeout(()=>s.abort(),o);try{const t=await n(i,{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&&a.has(t)}(e)?{valid:!1,error:"network_error",message:r.t("messages.certificate_intercepted",{url:i})}:{valid:!1,error:"network_error",message:e instanceof Error?e.message:"Network connection failed"}}finally{clearTimeout(l)}}const a=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 i(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:o=15e3,fetchFn:n=fetch}=r,a=`${t.getBaseUrl()}/api/status`,s=new AbortController,l=setTimeout(()=>s.abort(),o);try{const e=await n(a,{method:"GET",signal:s.signal});if(!e.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${e.status}`};const t=await e.json();let r=t?.data?.claude_desktop_group_models;if("string"==typeof r)try{r=JSON.parse(r)}catch{r=void 0}const o={};if(r&&"object"==typeof r&&!Array.isArray(r))for(const[e,t]of Object.entries(r))Array.isArray(t)&&(o[e]=i(t));return{ok:!0,claudeDesktopGroupModels:o}}catch(t){return e.logError("fetchGatewayStatus",t),{ok:!1,claudeDesktopGroupModels:{},error:t instanceof Error?t.message:"network error"}}finally{clearTimeout(l)}}export async function fetchKeyGroupInfo(r,o={}){const{timeoutMs:n=1e4,fetchFn:a=fetch}=o,s=`${t.getBaseUrl()}/api/platform/vibecode/key`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const e=await a(s,{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:i(o.claude_desktop_models)}}catch(t){return e.logError("fetchKeyGroupInfo",t),null}finally{clearTimeout(c)}}function s(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,l=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=>!0!==e.hidden&&e.id.trim().length>0).map(e=>{const t={id:e.id.trim()};"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=s(e.context_window);r&&(t.contextWindow=r);const o=s(e.auto_compact_tokens);return o&&(t.autoCompactTokens=o),t}));return o}catch(t){return e.logError("fetchClientModelConfig",t),null}finally{clearTimeout(l)}}export async function validateApiKey(t,r={}){const{timeoutMs:a=3e4,retries:i=2,backoffMs:s=500,fetchFn:l=fetch,sleepFn:c=o}=r;let u={valid:!1,error:"network_error",message:"Network connection failed"};for(let r=0;r<=i;r++){const o=await n(t,a,l);if(o.valid||"invalid_api_key"===o.error)return o.valid||e.logError("validateApiKey",new Error(o.message||"invalid_api_key")),o;u=o,r<i&&await c(s*Math.pow(2,r))}return e.logError("validateApiKey",new Error(u.message||"network_error")),u}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type GatewayClientModel, type GatewayGroupModel } from './api-validator.js';
|
|
1
|
+
import { type GatewayClientModel, type GatewayGroupModel, type KeyGroupInfo } from './api-validator.js';
|
|
2
2
|
/**
|
|
3
3
|
* What a key can actually reach on the gateway, resolved once per apply so
|
|
4
4
|
* adapters that advertise a model list (Claude Desktop) can offer every model
|
|
@@ -13,7 +13,7 @@ export interface ModelCatalog {
|
|
|
13
13
|
labels: Record<string, string>;
|
|
14
14
|
/** Where `oneMillion` came from — surfaced in notes / diagnostics. */
|
|
15
15
|
oneMillionSource: 'gateway' | 'fallback';
|
|
16
|
-
/** Console group names whose model table matched
|
|
16
|
+
/** Console group names this key belongs to (from the gateway) or whose model table matched it (empty when neither). */
|
|
17
17
|
matchedGroups: string[];
|
|
18
18
|
/**
|
|
19
19
|
* Per-tool model lists the gateway publishes for coding clients (names, context
|
|
@@ -33,11 +33,13 @@ export declare function isClaudeModel(id: string): boolean;
|
|
|
33
33
|
*/
|
|
34
34
|
export declare function matchGroups(keyClaudeIds: string[], groups: Record<string, GatewayGroupModel[]>): string[];
|
|
35
35
|
/**
|
|
36
|
-
* Compute the 1M set for `claudeIds`.
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Compute the 1M set for `claudeIds`. The gateway's answer for this key first
|
|
37
|
+
* (its real groups; models missing from their table use the static fallback),
|
|
38
|
+
* else the group table matched by model set, else the static `models.json`
|
|
39
|
+
* fallback; then the operator's hard exclusions (e.g. Fable groups don't serve
|
|
40
|
+
* 1M Opus 4.6) on top of all of them.
|
|
39
41
|
*/
|
|
40
|
-
export declare function resolveOneMillion(claudeIds: string[], groups: Record<string, GatewayGroupModel[]> | null): Pick<ModelCatalog, 'oneMillion' | 'labels' | 'oneMillionSource' | 'matchedGroups'>;
|
|
42
|
+
export declare function resolveOneMillion(claudeIds: string[], groups: Record<string, GatewayGroupModel[]> | null, keyInfo?: KeyGroupInfo | null): Pick<ModelCatalog, 'oneMillion' | 'labels' | 'oneMillionSource' | 'matchedGroups'>;
|
|
41
43
|
/**
|
|
42
44
|
* Fetch `/v1/models` (+ the console's group table) and build the catalog. Never
|
|
43
45
|
* throws: returns null when the model list itself cannot be fetched, so callers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as e}from"../utils/logger.js";import{fetchClientModelConfig as o,fetchModels as t,fetchGatewayStatus as
|
|
1
|
+
import{logger as e}from"../utils/logger.js";import{fetchClientModelConfig as o,fetchKeyGroupInfo as l,fetchModels as t,fetchGatewayStatus as n}from"./api-validator.js";import{getClaudeDesktopRules as r}from"./model-registry.js";export function baseModelId(e){return e.replace(/\[[^\]]*\]$/,"").trim()}export function isClaudeModel(e){return/claude/i.test(baseModelId(e))}export function matchGroups(e,o){const l=new Set(e.map(baseModelId));if(0===l.size)return[];const t=[],n=[];for(const[e,r]of Object.entries(o)){const o=new Set(r.map(e=>baseModelId(e.name)).filter(isClaudeModel));[...l].every(e=>o.has(e))&&(o.size===l.size?t:n).push(e)}return t.length>0?t:n}export function resolveOneMillion(e,o,l){const t=r(),n=[...new Set(e.map(baseModelId))],s=new Set(e.filter(e=>/\[1m\]/i.test(e)).map(baseModelId)),a={};let i,d="fallback",c=[];if(l&&l.claudeDesktopModels.length>0){d="gateway",c=o?l.groups.filter(e=>e in o):l.groups;const e=new Map(l.claudeDesktopModels.map(e=>[baseModelId(e.name),e])),r=new Set(t.oneMillionFallback);i=new Set(n.filter(o=>{const l=e.get(o);return l?.labelOverride&&(a[o]=l.labelOverride),l?!0===l.supports1m:r.has(o)}))}else if(o&&(c=matchGroups(n,o)).length>0){d="gateway",i=new Set(n);for(const e of c){const l=new Map(o[e].map(e=>[baseModelId(e.name),e]));for(const e of n){const o=l.get(e);o?.labelOverride&&!a[e]&&(a[e]=o.labelOverride),!0!==o?.supports1m&&i.delete(e)}}}else{const e=new Set(t.oneMillionFallback);i=new Set(n.filter(o=>e.has(o)))}for(const e of s)i.add(e);for(const e of t.oneMillionExclusions)if(n.some(o=>o.startsWith(e.whenPrefixPresent)))for(const o of e.models)i.delete(o);return{oneMillion:[...i],labels:a,oneMillionSource:d,matchedGroups:c}}export async function resolveModelCatalog(r){try{const[e,s,a,i]=await Promise.all([t(r),n(),o(),l(r)]);if(!e.ok||0===e.models.length)return null;const d=[...new Set(e.models.map(e=>e.trim()).filter(Boolean))],c=d.filter(isClaudeModel);return{models:d,...resolveOneMillion(c,s.ok?s.claudeDesktopGroupModels:null,i),...a?{clientModels:a}:{}}}catch(o){return e.logError("resolveModelCatalog",o),null}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as p}from"os";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{baseModelId as y,isClaudeModel as b}from"../model-catalog.js";const C=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const k=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"],A=/^[a-f0-9-]{36}$/;function _(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return k.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const D=()=>a(claudeDesktopUserDataDir(),"configLibrary"),w=()=>a(D(),"_meta.json"),E=e=>a(D(),`${e}.json`),M=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function v(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return _(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function P(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){d.logError("claude-desktop.writeJson.chmod",e)}}function O(){const e=v(w());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>_(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function S(e){return e?.entries.find(e=>e.name===C&&A.test(e.id))}const j={opus:0,fable:1,sonnet:2,haiku:3};function I(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=I(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function x(e){return I(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>y(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(b)){i=[...new Set(t.models.filter(b).map(y))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=y(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>j[x(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=I(e),s=I(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=x(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=x(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let L=null,T=null;function $(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const G={modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function N(e){return Object.fromEntries(Object.entries(G).filter(([t])=>!e(t)))}const U="HKCU\\SOFTWARE\\Policies\\Claude",F="HKLM\\SOFTWARE\\Policies\\Claude",R="vibecli-claude-policy.reg",K=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let H=K;export function setRegRunnerForTests(e){H=e??K}function B(){return"win32"===process.platform}function V(e){let t;try{t=H(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function W(){if(null===V(U))return;const e=a(claudeDesktopUserDataDir(),R);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),H(["export",U,e,"/y"])}catch(e){d.logError("claude-desktop.snapshotPolicy",e)}}const z=[...Object.keys($({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(G)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=p().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return V(F)?F:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return d.logError("claude-desktop.detectManagedConfig",e),null}}const J=/\d+\.\d+\.\d+/;function Z(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const X="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",q="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",Q="darwin"===process.platform?`open ${q}`:"win32"===process.platform?`start ${q}`:`xdg-open ${q}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:X,installCommand:Q,configPath:w(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...k.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?J.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return Z(t)}const o=k.map(e=>`'${e}'`).join(",");return Z(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>J.exec(e)?.[0]??[]))}return null},wantsModelCatalog:!0,loadPrivateConfig(e){const t=O()??{appliedId:"",entries:[]};let o=S(t);o||(o={id:l(),name:C},t.entries.push(o));const n=$(e);T=!0===n.disableDeploymentModeChooser,L={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=v(E(o.id))??{};P(E(o.id),{...N(e=>e in r),...r,...n}),t.appliedId=o.id,P(w(),t);const s=v(M())??{};if("3p"!==s.deploymentMode&&P(M(),{...s,deploymentMode:"3p"}),B()){const e=V(U);!function(e){W();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];H(["add",U,"/v",t,"/t",e,"/d",n,"/f"])}}({...N(t=>e?.has(t)??!1),...n})}},unloadPrivateConfig(){B()&&function(e){const t=V(U);if(!t)return;W();let o=t.size;for(const n of e)t.has(n)&&(H(["delete",U,"/v",n,"/f"]),o-=1);o<=0&&H(["delete",U,"/f"])}(z);const e=O(),t=S(e);if(!e||!t)return;m(E(t.id)),s(E(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(w()),s(w(),{force:!0})):P(w(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(B()){const e=V(U)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=S(O());if(!e)return{apiKey:null};const t=v(E(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return d.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=O(),t=S(e);return{meta:e,applied:t?v(E(t.id)):null,desktop:v(M()),...B()?{policy:Object.fromEntries(V(U)??[])}:{}}}catch(e){return d.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;B()&&V(U)?.has("inferenceModels")&&(W(),H(["delete",U,"/v","inferenceModels","/f"]),e=!0);const t=S(O()),o=t?v(E(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;P(E(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(L){const t=L.models.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", ");e.push(u.t(`tools.claude_desktop.models_written_${L.source}`,{count:String(L.models.length),list:t,groups:L.groups.join(" / ")}))}null!==T&&e.push(u.t(T?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")),e.push(u.t("tools.claude_desktop.restart_hint")),B()&&e.push(u.t("tools.claude_desktop.registry_written",{key:U}));const t=detectManagedConfig();return t&&e.push(u.t("tools.claude_desktop.managed_conflict",{path:t})),e},forceResetPaths(){const e=[w(),M()];try{const t=S(O());t&&e.push(E(t.id))}catch{}return e},backupPaths(){const e=[w(),M()];B()&&e.push(a(claudeDesktopUserDataDir(),R));try{const t=S(O());t&&e.push(E(t.id))}catch{}return e}};
|
|
1
|
+
import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as p}from"os";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{baseModelId as y,isClaudeModel as b}from"../model-catalog.js";import{gatewayModelSpecs as C,prettyModelName as k}from"../gateway-models.js";const A=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const w=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"],_=/^[a-f0-9-]{36}$/;function D(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return w.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const E=()=>a(claudeDesktopUserDataDir(),"configLibrary"),M=()=>a(E(),"_meta.json"),v=e=>a(E(),`${e}.json`),O=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function P(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return D(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function S(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){d.logError("claude-desktop.writeJson.chmod",e)}}function j(){const e=P(M());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>D(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function x(e){return e?.entries.find(e=>e.name===A&&_.test(e.id))}const I={opus:0,fable:1,sonnet:2,haiku:3};function L(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=L(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function T(e){return L(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>y(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(b)){i=[...new Set(t.models.filter(b).map(y))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=y(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>I[T(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=L(e),s=L(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=T(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}const p=[...i.map(e=>{const t=T(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o}),...$(t)],d=p.findIndex(e=>e.name===o);return d>0&&p.unshift(...p.splice(d,1)),p}function $(e){if(!e)return[];const t=new Set(e.models.map(y));return C("claude-code",e.clientModels).filter(e=>t.has(e.id)).map(e=>{const t={name:e.id,labelOverride:e.name||k(e.id)};return(e.contextWindow??0)>=1e6&&(t.supports1m=!0,t.prefer1m=!0),t})}let G=null,N=null;function U(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const F={modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function R(e){return Object.fromEntries(Object.entries(F).filter(([t])=>!e(t)))}const K="HKCU\\SOFTWARE\\Policies\\Claude",H="HKLM\\SOFTWARE\\Policies\\Claude",B="vibecli-claude-policy.reg",W=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let V=W;export function setRegRunnerForTests(e){V=e??W}function z(){return"win32"===process.platform}function J(e){let t;try{t=V(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function Z(){if(null===J(K))return;const e=a(claudeDesktopUserDataDir(),B);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),V(["export",K,e,"/y"])}catch(e){d.logError("claude-desktop.snapshotPolicy",e)}}const X=[...Object.keys(U({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(F)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=p().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return J(H)?H:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return d.logError("claude-desktop.detectManagedConfig",e),null}}const q=/\d+\.\d+\.\d+/;function Q(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const Y="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",ee="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",te="darwin"===process.platform?`open ${ee}`:"win32"===process.platform?`start ${ee}`:`xdg-open ${ee}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:Y,installCommand:te,configPath:M(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...w.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?q.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return Q(t)}const o=w.map(e=>`'${e}'`).join(",");return Q(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>q.exec(e)?.[0]??[]))}return null},wantsModelCatalog:!0,loadPrivateConfig(e){const t=j()??{appliedId:"",entries:[]};let o=x(t);o||(o={id:l(),name:A},t.entries.push(o));const n=U(e);N=!0===n.disableDeploymentModeChooser,G={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=P(v(o.id))??{};S(v(o.id),{...R(e=>e in r),...r,...n}),t.appliedId=o.id,S(M(),t);const s=P(O())??{};if("3p"!==s.deploymentMode&&S(O(),{...s,deploymentMode:"3p"}),z()){const e=J(K);!function(e){Z();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];V(["add",K,"/v",t,"/t",e,"/d",n,"/f"])}}({...R(t=>e?.has(t)??!1),...n})}},unloadPrivateConfig(){z()&&function(e){const t=J(K);if(!t)return;Z();let o=t.size;for(const n of e)t.has(n)&&(V(["delete",K,"/v",n,"/f"]),o-=1);o<=0&&V(["delete",K,"/f"])}(X);const e=j(),t=x(e);if(!e||!t)return;m(v(t.id)),s(v(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(M()),s(M(),{force:!0})):S(M(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},detectCurrentConfig(){try{if(z()){const e=J(K)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=x(j());if(!e)return{apiKey:null};const t=P(v(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return d.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=j(),t=x(e);return{meta:e,applied:t?P(v(t.id)):null,desktop:P(O()),...z()?{policy:Object.fromEntries(J(K)??[])}:{}}}catch(e){return d.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;z()&&J(K)?.has("inferenceModels")&&(Z(),V(["delete",K,"/v","inferenceModels","/f"]),e=!0);const t=x(j()),o=t?P(v(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;S(v(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(G){const t=G.models.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", ");e.push(u.t(`tools.claude_desktop.models_written_${G.source}`,{count:String(G.models.length),list:t,groups:G.groups.join(" / ")}))}null!==N&&e.push(u.t(N?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")),e.push(u.t("tools.claude_desktop.restart_hint")),z()&&e.push(u.t("tools.claude_desktop.registry_written",{key:K}));const t=detectManagedConfig();return t&&e.push(u.t("tools.claude_desktop.managed_conflict",{path:t})),e},forceResetPaths(){const e=[M(),O()];try{const t=x(j());t&&e.push(v(t.id))}catch{}return e},backupPaths(){const e=[M(),O()];z()&&e.push(a(claudeDesktopUserDataDir(),B));try{const t=x(j());t&&e.push(v(t.id))}catch{}return e}};
|
package/dist/locales/en_US.json
CHANGED
|
@@ -224,7 +224,8 @@
|
|
|
224
224
|
"args": "Arguments:",
|
|
225
225
|
"first_run": "First run detected, starting initialization wizard...",
|
|
226
226
|
"config_saved": "Configuration saved",
|
|
227
|
-
"env_vars_cleared": "Detected {{vars}} in your shell environment and removed them (they would override the config vibecli writes). Restart your terminal to apply the shell profile change."
|
|
227
|
+
"env_vars_cleared": "Detected {{vars}} in your shell environment and removed them (they would override the config vibecli writes). Restart your terminal to apply the shell profile change.",
|
|
228
|
+
"certificate_intercepted": "The certificate presented for {{url}} is not the gateway's: the local network is most likely hijacking DNS (common on campus / hotel networks), not a key problem. Switch networks, set the computer's DNS to a public resolver, or use a proxy, then retry"
|
|
228
229
|
},
|
|
229
230
|
"config": {
|
|
230
231
|
"profile_list_title": "Profiles",
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -224,7 +224,8 @@
|
|
|
224
224
|
"args": "参数:",
|
|
225
225
|
"first_run": "检测到首次运行,启动初始化向导...",
|
|
226
226
|
"config_saved": "配置已保存",
|
|
227
|
-
"env_vars_cleared": "检测到 shell 环境变量 {{vars}} 并已自动清除(它们会覆盖 vibecli 写入的配置)。如已写入 shell 配置文件,请重开终端生效。"
|
|
227
|
+
"env_vars_cleared": "检测到 shell 环境变量 {{vars}} 并已自动清除(它们会覆盖 vibecli 写入的配置)。如已写入 shell 配置文件,请重开终端生效。",
|
|
228
|
+
"certificate_intercepted": "连接 {{url}} 时证书不对:通常是当前网络的 DNS 被劫持(校园网、酒店网络常见),而不是 Key 有问题。请换个网络、把电脑的 DNS 改成 119.29.29.29 / 223.5.5.5,或开代理后重试"
|
|
228
229
|
},
|
|
229
230
|
"config": {
|
|
230
231
|
"profile_list_title": "Profile 列表",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.53",
|
|
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",
|