@vibeapi/cli 0.0.156 → 0.0.158

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.
@@ -76,7 +76,11 @@ export type { GatewayClientModel } from './client-config.js';
76
76
  export declare function fetchClientModelConfig(options?: {
77
77
  timeoutMs?: number;
78
78
  fetchFn?: typeof fetch;
79
- }): Promise<Record<string, GatewayClientModel[]> | null>;
79
+ apiKey?: string;
80
+ }): Promise<{
81
+ models: Record<string, GatewayClientModel[]>;
82
+ personal?: string[];
83
+ } | null>;
80
84
  /**
81
85
  * 验证 API Key 的有效性和网络连通性,遇到网络错误时按指数退避重试
82
86
  */
@@ -1 +1 @@
1
- import{KEY_INFO_PATH as r}from"./gateway-protocol.js";import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as o}from"./i18n.js";import{rememberPublishedAddresses as a}from"./gateway-addresses.js";import{eligibleLists as n}from"./client-config.js";import{track as s}from"./telemetry.js";import{refreshClientConfig as i}from"./client-config-sync.js";const l=r=>new Promise(e=>setTimeout(e,r));async function c(r,e,a){const n=`${t.getBaseUrl()}/v1/models`,s=new AbortController,i=setTimeout(()=>s.abort(),e);try{const e=await a(n,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:s.signal});if(401===e.status)return{valid:!1,error:"invalid_api_key",message:o.t("validator.rejected")};if(e.ok){try{const r=await e.json();if(r&&"list"===r.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${e.status}: ${e.statusText}`}}catch(r){return r instanceof Error&&"AbortError"===r.name?{valid:!1,error:"network_error",message:o.t("validator.timeout")}:function(r){const e=r?.cause?.code;return"string"==typeof e&&u.has(e)}(r)?{valid:!1,error:"network_error",message:o.t("messages.certificate_intercepted",{url:n})}:{valid:!1,error:"network_error",message:r instanceof Error?r.message:o.t("validator.network")}}finally{clearTimeout(i)}}const u=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:a=3e4,fetchFn:n=fetch}=o,s=`${t.getBaseUrl()}/v1/models`,i=new AbortController,l=setTimeout(()=>i.abort(),a);try{const e=await n(s,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:i.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(r=>"string"==typeof r?r:r?.id).filter(r=>"string"==typeof r&&r.trim().length>0).map(r=>r.trim())}}catch(r){return e.logError("fetchModels",r),{ok:!1,models:[],error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}function f(r){return r.filter(r=>!!r&&"object"==typeof r&&"string"==typeof r.name).map(r=>({name:r.name.trim(),..."string"==typeof r.labelOverride?{labelOverride:r.labelOverride}:{},..."boolean"==typeof r.supports1m?{supports1m:r.supports1m}:{}})).filter(r=>r.name.length>0)}export async function fetchGatewayStatus(r={}){const{timeoutMs:o=15e3,fetchFn:n=fetch}=r,s=`${t.getBaseUrl()}/api/status`,i=new AbortController,l=setTimeout(()=>i.abort(),o);try{const r=await n(s,{method:"GET",signal:i.signal});if(!r.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${r.status}`};const e=await r.json();a(e?.data,t.getBaseUrl());let o=e?.data?.claude_desktop_group_models;if("string"==typeof o)try{o=JSON.parse(o)}catch{o=void 0}const l={};if(o&&"object"==typeof o&&!Array.isArray(o))for(const[r,e]of Object.entries(o))Array.isArray(e)&&(l[r]=f(e));return{ok:!0,claudeDesktopGroupModels:l}}catch(r){return e.logError("fetchGatewayStatus",r),{ok:!1,claudeDesktopGroupModels:{},error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}export async function fetchKeyGroupInfo(o,a={}){const{timeoutMs:n=1e4,fetchFn:s=fetch}=a,i=`${t.getBaseUrl()}${r}`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const r=await s(i,{method:"GET",headers:{Authorization:`Bearer ${o}`},signal:l.signal});if(!r.ok)return null;const e=await r.json(),t=e?.data;if(!t||"string"!=typeof t.group||!Array.isArray(t.claude_desktop_models))return null;const a=Array.isArray(t.groups)?t.groups.filter(r=>"string"==typeof r&&!!r):[];return{group:t.group,groups:a.length>0?a:[t.group],claudeDesktopModels:f(t.claude_desktop_models)}}catch(r){return e.logError("fetchKeyGroupInfo",r),null}finally{clearTimeout(c)}}export async function fetchClientModelConfig(r={}){const e=await i(r);return e.config?n(e.config.models):null}export async function validateApiKey(r,t={}){const a=Date.now(),n=await async function(r,t){const{timeoutMs:a=3e4,retries:n=2,backoffMs:s=500,fetchFn:i=fetch,sleepFn:u=l}=t;let f={valid:!1,error:"network_error",message:o.t("validator.network")};for(let t=0;t<=n;t++){const o=await c(r,a,i);if(o.valid||"invalid_api_key"===o.error)return o.valid||e.logError("validateApiKey",new Error(o.message||"invalid_api_key")),o;f=o,t<n&&await u(s*Math.pow(2,t))}return e.logError("validateApiKey",new Error(f.message||"network_error")),f}(r,t);return s("validate-key",n.valid?"ok":"fail",{duration_ms:Date.now()-a,...n.valid?{}:{error:n.message??n.error,detail:{reason:n.error}}}),n}
1
+ import{KEY_INFO_PATH as r}from"./gateway-protocol.js";import{logger as e}from"../utils/logger.js";import{configManager as t}from"./config.js";import{i18n as o}from"./i18n.js";import{rememberPublishedAddresses as a}from"./gateway-addresses.js";import{eligibleLists as n}from"./client-config.js";import{track as s}from"./telemetry.js";import{refreshClientConfig as i}from"./client-config-sync.js";const l=r=>new Promise(e=>setTimeout(e,r));async function c(r,e,a){const n=`${t.getBaseUrl()}/v1/models`,s=new AbortController,i=setTimeout(()=>s.abort(),e);try{const e=await a(n,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:s.signal});if(401===e.status)return{valid:!1,error:"invalid_api_key",message:o.t("validator.rejected")};if(e.ok){try{const r=await e.json();if(r&&"list"===r.object)return{valid:!0}}catch{return{valid:!0}}return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${e.status}: ${e.statusText}`}}catch(r){return r instanceof Error&&"AbortError"===r.name?{valid:!1,error:"network_error",message:o.t("validator.timeout")}:function(r){const e=r?.cause?.code;return"string"==typeof e&&u.has(e)}(r)?{valid:!1,error:"network_error",message:o.t("messages.certificate_intercepted",{url:n})}:{valid:!1,error:"network_error",message:r instanceof Error?r.message:o.t("validator.network")}}finally{clearTimeout(i)}}const u=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:a=3e4,fetchFn:n=fetch}=o,s=`${t.getBaseUrl()}/v1/models`,i=new AbortController,l=setTimeout(()=>i.abort(),a);try{const e=await n(s,{method:"GET",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},signal:i.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(r=>"string"==typeof r?r:r?.id).filter(r=>"string"==typeof r&&r.trim().length>0).map(r=>r.trim())}}catch(r){return e.logError("fetchModels",r),{ok:!1,models:[],error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}function f(r){return r.filter(r=>!!r&&"object"==typeof r&&"string"==typeof r.name).map(r=>({name:r.name.trim(),..."string"==typeof r.labelOverride?{labelOverride:r.labelOverride}:{},..."boolean"==typeof r.supports1m?{supports1m:r.supports1m}:{}})).filter(r=>r.name.length>0)}export async function fetchGatewayStatus(r={}){const{timeoutMs:o=15e3,fetchFn:n=fetch}=r,s=`${t.getBaseUrl()}/api/status`,i=new AbortController,l=setTimeout(()=>i.abort(),o);try{const r=await n(s,{method:"GET",signal:i.signal});if(!r.ok)return{ok:!1,claudeDesktopGroupModels:{},error:`HTTP ${r.status}`};const e=await r.json();a(e?.data,t.getBaseUrl());let o=e?.data?.claude_desktop_group_models;if("string"==typeof o)try{o=JSON.parse(o)}catch{o=void 0}const l={};if(o&&"object"==typeof o&&!Array.isArray(o))for(const[r,e]of Object.entries(o))Array.isArray(e)&&(l[r]=f(e));return{ok:!0,claudeDesktopGroupModels:l}}catch(r){return e.logError("fetchGatewayStatus",r),{ok:!1,claudeDesktopGroupModels:{},error:r instanceof Error?r.message:"network error"}}finally{clearTimeout(l)}}export async function fetchKeyGroupInfo(o,a={}){const{timeoutMs:n=1e4,fetchFn:s=fetch}=a,i=`${t.getBaseUrl()}${r}`,l=new AbortController,c=setTimeout(()=>l.abort(),n);try{const r=await s(i,{method:"GET",headers:{Authorization:`Bearer ${o}`},signal:l.signal});if(!r.ok)return null;const e=await r.json(),t=e?.data;if(!t||"string"!=typeof t.group||!Array.isArray(t.claude_desktop_models))return null;const a=Array.isArray(t.groups)?t.groups.filter(r=>"string"==typeof r&&!!r):[];return{group:t.group,groups:a.length>0?a:[t.group],claudeDesktopModels:f(t.claude_desktop_models)}}catch(r){return e.logError("fetchKeyGroupInfo",r),null}finally{clearTimeout(c)}}export async function fetchClientModelConfig(r={}){const e=await i(r);if(!e.config)return null;const{personal:t}=e.config;return{models:n(e.config.models),...t?{personal:t}:{}}}export async function validateApiKey(r,t={}){const a=Date.now(),n=await async function(r,t){const{timeoutMs:a=3e4,retries:n=2,backoffMs:s=500,fetchFn:i=fetch,sleepFn:u=l}=t;let f={valid:!1,error:"network_error",message:o.t("validator.network")};for(let t=0;t<=n;t++){const o=await c(r,a,i);if(o.valid||"invalid_api_key"===o.error)return o.valid||e.logError("validateApiKey",new Error(o.message||"invalid_api_key")),o;f=o,t<n&&await u(s*Math.pow(2,t))}return e.logError("validateApiKey",new Error(f.message||"network_error")),f}(r,t);return s("validate-key",n.valid?"ok":"fail",{duration_ms:Date.now()-a,...n.valid?{}:{error:n.message??n.error,detail:{reason:n.error}}}),n}
@@ -52,6 +52,11 @@ export interface Brand {
52
52
  claudeDesktop?: string;
53
53
  /** ChatGPT desktop app (contains Codex) download mirror for macOS / Windows (see mirror-url.ts). */
54
54
  chatgptDesktop?: string;
55
+ /**
56
+ * Download counter in front of cnb.cool (logs the download, then 302s to the same path): desktop app downloads
57
+ * try `<counter><path>` first and fall back to cnb.cool (see mirror-url.ts `downloadUrls`). Omitted = direct.
58
+ */
59
+ downloadCounter?: string;
55
60
  };
56
61
  /** Claude Code plugin marketplaces tried before Anthropic's GitHub repo. */
57
62
  pluginMarketplaces: BrandPluginMarketplace[];
@@ -23,4 +23,5 @@ export declare function refreshClientConfig(options?: {
23
23
  timeoutMs?: number;
24
24
  fetchFn?: typeof fetch;
25
25
  now?: number;
26
+ apiKey?: string;
26
27
  }): Promise<ClientConfigStatus>;
@@ -1 +1 @@
1
- import{CLIENT_CONFIG_PATH as e}from"./gateway-protocol.js";import{logger as t}from"../utils/logger.js";import{bundledClientConfig as o,parseClientConfig as n,readCachedClientConfig as r,setClientConfigSiteResolver as i,writeCachedClientConfig as c}from"./client-config.js";import{configManager as f}from"./config.js";import{getBuiltinGatewayAddresses as s,reloadRegistry as l}from"./model-registry.js";import{brandEnv as g,brand as a}from"./brand.js";export const CLIENT_CONFIG_TTL_MS=3e5;function u(e){try{return new URL(e).origin}catch{return e.replace(/\/+$/,"")}}export function clientConfigSite(e=f.getBaseUrl()){const t=u(e);return s().some(e=>u(e.url)===t)?a().id:t}i(()=>clientConfigSite());let d=null;export function lastClientConfigStatus(){return d}function m(e){const t=r();if(t&&t.site===clientConfigSite())return{config:t.config,source:"cache",revision:t.config.revision,fetchedAt:t.fetchedAt,problem:e};const n=o();return n?{config:n,source:"bundled",revision:n.revision,problem:e}:{config:null,source:null,problem:e}}export async function refreshClientConfig(i={}){const{force:s=!1,timeoutMs:u=8e3,fetchFn:h=fetch,now:p=Date.now()}=i;if("1"===g("DISABLE_CLIENT_CONFIG_FETCH"))return d=m(),d;const C=clientConfigSite(),v=r(),w=v&&v.site===C?v:null;if(!s&&w&&p-w.fetchedAt<3e5)return d={config:w.config,source:"cache",revision:w.config.revision,fetchedAt:w.fetchedAt},d;const A=w?.config??(C===a().id?o():null),T=new AbortController,b=setTimeout(()=>T.abort(),u);try{const o={};w?.etag&&(o["If-None-Match"]=w.etag);const r=await h(`${f.getBaseUrl()}${e}`,{method:"GET",headers:o,signal:T.signal});if(304===r.status&&w)return c({...w,fetchedAt:p}),d={config:w.config,source:"cloud",revision:w.config.revision,fetchedAt:p},d;if(!r.ok)return d=m(`HTTP ${r.status}`),d;const i=await r.json(),{config:s,problem:g}=n(i?.data,A);if(!s)return t.warn(`Gateway client config refused: ${g}`),d=m(`refused: ${g}`),d;g&&t.warn(`Gateway client config: ${g}`);const a=r.headers.get("etag")??void 0;return c({site:C,fetchedAt:p,config:s,...a?{etag:a}:{}}),l(),d={config:s,source:"cloud",revision:s.revision,fetchedAt:p,...g?{problem:g}:{}},d}catch(e){return t.logError("refreshClientConfig",e),d=m(e instanceof Error?e.message:String(e)),d}finally{clearTimeout(b)}}
1
+ import{createHash as e}from"node:crypto";import{CLIENT_CONFIG_PATH as t}from"./gateway-protocol.js";import{logger as o}from"../utils/logger.js";import{bundledClientConfig as n,parseClientConfig as i,readCachedClientConfig as r,setClientConfigSiteResolver as c,writeCachedClientConfig as s}from"./client-config.js";import{configManager as f}from"./config.js";import{getBuiltinGatewayAddresses as a,reloadRegistry as l}from"./model-registry.js";import{brandEnv as u,brand as g}from"./brand.js";export const CLIENT_CONFIG_TTL_MS=3e5;function d(e){try{return new URL(e).origin}catch{return e.replace(/\/+$/,"")}}export function clientConfigSite(e=f.getBaseUrl()){const t=d(e);return a().some(e=>d(e.url)===t)?g().id:t}c(()=>clientConfigSite());let h=null;export function lastClientConfigStatus(){return h}function m(e){const t=r();if(t&&t.site===clientConfigSite())return{config:t.config,source:"cache",revision:t.config.revision,fetchedAt:t.fetchedAt,problem:e};const o=n();return o?{config:o,source:"bundled",revision:o.revision,problem:e}:{config:null,source:null,problem:e}}export async function refreshClientConfig(c={}){const{force:a=!1,timeoutMs:d=8e3,fetchFn:p=fetch,now:C=Date.now()}=c,y=c.apiKey??f.getApiKey(),A=function(t){return t?e("sha256").update(t).digest("hex").slice(0,16):void 0}(y);if("1"===u("DISABLE_CLIENT_CONFIG_FETCH"))return h=m(),h;const v=clientConfigSite(),w=r(),T=w&&w.site===v?w:null,b=T&&T.keyHash===A?T:null;if(!a&&b&&C-b.fetchedAt<3e5)return h={config:b.config,source:"cache",revision:b.config.revision,fetchedAt:b.fetchedAt},h;const S=T?.config??(v===g().id?n():null),$=new AbortController,j=setTimeout(()=>$.abort(),d);try{const e={};b?.etag&&(e["If-None-Match"]=b.etag),y&&(e.Authorization=`Bearer ${y}`);const n=await p(`${f.getBaseUrl()}${t}`,{method:"GET",headers:e,signal:$.signal});if(304===n.status&&b)return s({...b,fetchedAt:C}),h={config:b.config,source:"cloud",revision:b.config.revision,fetchedAt:C},h;if(!n.ok)return h=m(`HTTP ${n.status}`),h;const r=await n.json(),{config:c,problem:a}=i(r?.data,S);if(!c)return o.warn(`Gateway client config refused: ${a}`),h=m(`refused: ${a}`),h;a&&o.warn(`Gateway client config: ${a}`);const u=n.headers.get("etag")??void 0;return s({site:v,fetchedAt:C,config:c,...A?{keyHash:A}:{},...u?{etag:u}:{}}),l(),h={config:c,source:"cloud",revision:c.revision,fetchedAt:C,...a?{problem:a}:{}},h}catch(e){return o.logError("refreshClientConfig",e),h=m(e instanceof Error?e.message:String(e)),h}finally{clearTimeout(j)}}
@@ -45,11 +45,19 @@ export interface ClientConfig {
45
45
  models: Record<string, GatewayClientModel[]>;
46
46
  /** The gateway's section for this CLI (`CLIENT_SECTION` on the wire). */
47
47
  client?: ClientSection;
48
+ /**
49
+ * Lists the key's owner trimmed or reordered in the console (「编码工具模型」); the gateway
50
+ * only sends this when the request carried the key. Those lists are complete: no built-in
51
+ * extras join them and the shrink check skips them.
52
+ */
53
+ personal?: string[];
48
54
  }
49
55
  export type ClientConfigSource = 'cloud' | 'cache' | 'bundled';
50
56
  export interface CachedClientConfig {
51
57
  /** `vibeapi` for the built-in addresses (one site), else the gateway origin. */
52
58
  site: string;
59
+ /** Fingerprint of the key the config was fetched with (the gateway merges its owner's choices). */
60
+ keyHash?: string;
53
61
  etag?: string;
54
62
  fetchedAt: number;
55
63
  config: ClientConfig;
@@ -1 +1 @@
1
- import{createHash as t,randomUUID as e}from"crypto";import{existsSync as n,mkdirSync as o,readFileSync as i,renameSync as r,writeFileSync as s}from"fs";import{homedir as l}from"os";import{dirname as c,join as f}from"path";import{fileURLToPath as a}from"url";import{CLIENT_SECTION as u,MIN_CLIENT_VERSION as d}from"./gateway-protocol.js";import{brand as m,brandEnv as p}from"./brand.js";export const CLIENT_CONFIG_SCHEMA=1;const h=new Set(["haiku","sonnet","opus","reasoning","fable"]),g=/^[A-Za-z0-9._:@/\-[\]]{1,200}$/,_=/^[a-z][a-z0-9-]{0,31}$/,b=/^\d+(\.\d+){0,3}$/,y=t=>!!t&&"object"==typeof t&&!Array.isArray(t),C=t=>"number"==typeof t&&Number.isFinite(t)&&t>0?Math.floor(t):void 0;function $(t){if(!y(t)||"string"!=typeof t.id||!t.id.trim())return null;const e={id:t.id.trim()};if(!0===t.hidden)return{...e,hidden:!0};"string"==typeof t.name&&t.name.trim()&&(e.name=t.name.trim()),"string"==typeof t.description&&t.description.trim()&&(e.description=t.description.trim());const n=C(t.context_window);n&&(e.contextWindow=n);const o=C(t.auto_compact_tokens);if(o&&(e.autoCompactTokens=o),y(t.clients)){const n={};for(const[e,o]of Object.entries(t.clients))_.test(e)&&"boolean"==typeof o&&(n[e]=o);Object.keys(n).length>0&&(e.clients=n)}const i=t[d];return"string"==typeof i&&b.test(i)&&(e.minClientVersion=i),"number"==typeof t.rollout_percent&&t.rollout_percent>=0&&t.rollout_percent<=100&&(e.rolloutPercent=t.rollout_percent),e}function k(t,e,n,o){if(void 0===t)return{};if(!y(t))throw new Error(`${u}.${o} must be an object`);const i={};for(const[r,s]of Object.entries(t)){if(!e.test(r)||"string"!=typeof s||!n(s))throw new Error(`${u}.${o}.${r} is invalid`);i[r]=s}return i}function v(t,e,{nonEmpty:n=!1}={}){if(void 0===t)return[];if(!Array.isArray(t)||t.some(t=>"string"!=typeof t||!g.test(t)))throw new Error(`${u}.${e} must be a list of model ids`);if(n&&0===t.length)throw new Error(`${u}.${e} must not be empty`);return[...t]}function w(t,e,n){if(void 0===t)return{};if(!y(t))throw new Error(`${u}.${e} must be an object`);const o={};for(const[i,r]of Object.entries(t)){if(!h.has(i))throw new Error(`${u}.${e}.${i} is not a tier slot`);o[i]=v(r,`${e}.${i}`,{nonEmpty:n})}return o}const j=t=>(t??[]).filter(t=>!t.hidden).length;export function parseClientConfig(t,e){if(!y(t))return{config:null,problem:"not an object"};if(1!==t.schema)return{config:null,problem:`unsupported schema ${String(t.schema)}`};if(!y(t.models))return{config:null,problem:"models is not an object"};const n={};for(const[e,o]of Object.entries(t.models)){if(!Array.isArray(o))return{config:null,problem:`models.${e} is not a list`};const t=o.map($);if(t.some(t=>null===t))return{config:null,problem:`models.${e} has a row without an id`};n[e]=t}let o,i;try{o=function(t){if(null==t)return;if(!y(t))throw new Error(`${u} must be an object`);const e={};if(void 0!==t.min_version){if("string"!=typeof t.min_version||t.min_version&&!b.test(t.min_version))throw new Error(`${u}.min_version is invalid`);t.min_version&&(e.minVersion=t.min_version)}const n=k(t.default_models,/^(haiku|sonnet|opus|reasoning|fable)$/,t=>g.test(t),"default_models");Object.keys(n).length&&(e.defaultModels=n);const o=w(t.retired_default_models,"retired_default_models",!1);Object.keys(o).length&&(e.retiredDefaultModels=o);const i=k(t.tool_defaults,_,t=>""===t||g.test(t),"tool_defaults");Object.keys(i).length&&(e.toolDefaults=i);const r=w(t.tier_chains,"tier_chains",!0);Object.keys(r).length&&(e.tierChains=r);const s=v(t.retired_models,"retired_models");s.length&&(e.retiredModels=s);const l=k(t.tool_min_versions,_,t=>""===t||b.test(t),"tool_min_versions");Object.keys(l).length&&(e.toolMinVersions=l);const c=v(t.agy_models,"agy_models");c.length&&(e.agyModels=c);const f=v(t.one_million_fallback,"one_million_fallback");return f.length&&(e.oneMillionFallback=f),!0===t.accept_shrink&&(e.acceptShrink=!0),e}(t[u])}catch(t){i=`${u} section dropped: ${t instanceof Error?t.message:String(t)}`}const r=t[u];if(e&&(!y(r)||!0!==r.accept_shrink))for(const[t,o]of Object.entries(e.models)){const e=j(o),i=j(n[t]);if(e>0&&0===i||e>=4&&2*i<e)return{config:null,problem:`models.${t} shrank from ${e} to ${i}`}}return{config:{schema:1,revision:"string"==typeof t.revision?t.revision:"",models:n,...o?{client:o}:{}},problem:i}}export function serializeClientConfig(t){const e=Object.fromEntries(Object.entries(t.models).map(([t,e])=>[t,e.map(t=>({id:t.id,...t.hidden?{hidden:!0}:{},...t.name?{name:t.name}:{},...t.description?{description:t.description}:{},...t.contextWindow?{context_window:t.contextWindow}:{},...t.autoCompactTokens?{auto_compact_tokens:t.autoCompactTokens}:{},...t.clients?{clients:t.clients}:{},...t.minClientVersion?{[d]:t.minClientVersion}:{},...void 0!==t.rolloutPercent?{rollout_percent:t.rolloutPercent}:{}}))])),n=t.client,o=n?{...n.minVersion?{min_version:n.minVersion}:{},...n.defaultModels?{default_models:n.defaultModels}:{},...n.retiredDefaultModels?{retired_default_models:n.retiredDefaultModels}:{},...n.toolDefaults?{tool_defaults:n.toolDefaults}:{},...n.tierChains?{tier_chains:n.tierChains}:{},...n.retiredModels?{retired_models:n.retiredModels}:{},...n.toolMinVersions?{tool_min_versions:n.toolMinVersions}:{},...n.agyModels?{agy_models:n.agyModels}:{},...n.oneMillionFallback?{one_million_fallback:n.oneMillionFallback}:{},...n.acceptShrink?{accept_shrink:!0}:{}}:void 0;return{schema:t.schema,revision:t.revision,models:e,...o?{[u]:o}:{}}}function O(){const t=p("CONFIG_DIR")?.trim();if(t)return t;const e=p("TEST_HOME")?.trim();return f(e||l(),m().configDirName)}export const cachePath=()=>f(O(),"client-config.json");function x(t,e){o(c(t),{recursive:!0});const n=`${t}.${process.pid}.tmp`;s(n,e,"utf-8"),r(n,t)}export function readCachedClientConfig(){try{if(!n(cachePath()))return null;const t=JSON.parse(i(cachePath(),"utf-8"));if("string"!=typeof t.site||"number"!=typeof t.fetchedAt)return null;const{config:e}=parseClientConfig(t.config);return e?{site:t.site,fetchedAt:t.fetchedAt,config:e,..."string"==typeof t.etag?{etag:t.etag}:{}}:null}catch{return null}}export function writeCachedClientConfig(t){x(cachePath(),JSON.stringify({site:t.site,etag:t.etag,fetchedAt:t.fetchedAt,config:serializeClientConfig(t.config)},null,2)+"\n")}let E;export function bundledClientConfig(){if("1"===p("NO_BUNDLED_CLIENT_CONFIG"))return null;if(void 0!==E)return E;E=null;const t=c(a(import.meta.url));for(const e of[f(t,"..","..","client-config.snapshot.json"),f(t,"..","client-config.snapshot.json")])try{if(!n(e))continue;E=parseClientConfig(JSON.parse(i(e,"utf-8"))).config;break}catch{}return E}let M=null;export function setClientConfigSiteResolver(t){M=t}export function currentClientConfig(){const t=readCachedClientConfig();let e=null;try{e=M?M():null}catch{e=null}if(t&&null!==e&&t.site===e)return{config:t.config,source:"cache",fetchedAt:t.fetchedAt};const n=bundledClientConfig();return n?{config:n,source:"bundled"}:null}let V=null,A=null;export function setClientIdentityForTests(t,e){V=t,A=e}export const clientVersion=()=>V??function(){try{const t=c(a(import.meta.url));for(const e of[f(t,"..","..","package.json"),f(t,"..","package.json")])if(n(e))return String(JSON.parse(i(e,"utf-8")).version??"0.0.0")}catch{}return"0.0.0"}();function S(t,e){const n=t.split(".").map(t=>parseInt(t,10)||0),o=e.split(".").map(t=>parseInt(t,10)||0);for(let t=0;t<Math.max(n.length,o.length);t++){const e=(n[t]??0)-(o[t]??0);if(e)return e}return 0}export function installId(){if(A)return A;const t=f(O(),"install-id");try{if(n(t)){const e=i(t,"utf-8").trim();if(e)return e}const o=e();return x(t,o+"\n"),o}catch{return"unknown"}}export function rolloutBucket(e){return t("sha256").update(`${installId()}:${e}`).digest().readUInt32BE(0)%100}export function isEligible(t){return!(t.minClientVersion&&S(clientVersion(),t.minClientVersion)<0||void 0!==t.rolloutPercent&&rolloutBucket(t.id)>=t.rolloutPercent)}export function eligibleLists(t){return Object.fromEntries(Object.entries(t).map(([t,e])=>[t,e.map(t=>t.hidden||isEligible(t)?t:{id:t.id,hidden:!0})]))}export function usableClientSection(t){const e=t?.client;if(e&&!(e.minVersion&&S(clientVersion(),e.minVersion)<0))return e}
1
+ import{createHash as e,randomUUID as t}from"crypto";import{existsSync as n,mkdirSync as o,readFileSync as i,renameSync as r,writeFileSync as s}from"fs";import{homedir as l}from"os";import{dirname as c,join as a}from"path";import{fileURLToPath as f}from"url";import{CLIENT_SECTION as u,MIN_CLIENT_VERSION as d}from"./gateway-protocol.js";import{brand as m,brandEnv as p}from"./brand.js";export const CLIENT_CONFIG_SCHEMA=1;const h=new Set(["haiku","sonnet","opus","reasoning","fable"]),g=/^[A-Za-z0-9._:@/\-[\]]{1,200}$/,_=/^[a-z][a-z0-9-]{0,31}$/,y=/^\d+(\.\d+){0,3}$/,b=e=>!!e&&"object"==typeof e&&!Array.isArray(e),C=e=>"number"==typeof e&&Number.isFinite(e)&&e>0?Math.floor(e):void 0;function k(e){if(!b(e)||"string"!=typeof e.id||!e.id.trim())return null;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 n=C(e.context_window);n&&(t.contextWindow=n);const o=C(e.auto_compact_tokens);if(o&&(t.autoCompactTokens=o),b(e.clients)){const n={};for(const[t,o]of Object.entries(e.clients))_.test(t)&&"boolean"==typeof o&&(n[t]=o);Object.keys(n).length>0&&(t.clients=n)}const i=e[d];return"string"==typeof i&&y.test(i)&&(t.minClientVersion=i),"number"==typeof e.rollout_percent&&e.rollout_percent>=0&&e.rollout_percent<=100&&(t.rolloutPercent=e.rollout_percent),t}function $(e,t,n,o){if(void 0===e)return{};if(!b(e))throw new Error(`${u}.${o} must be an object`);const i={};for(const[r,s]of Object.entries(e)){if(!t.test(r)||"string"!=typeof s||!n(s))throw new Error(`${u}.${o}.${r} is invalid`);i[r]=s}return i}function v(e,t,{nonEmpty:n=!1}={}){if(void 0===e)return[];if(!Array.isArray(e)||e.some(e=>"string"!=typeof e||!g.test(e)))throw new Error(`${u}.${t} must be a list of model ids`);if(n&&0===e.length)throw new Error(`${u}.${t} must not be empty`);return[...e]}function w(e,t,n){if(void 0===e)return{};if(!b(e))throw new Error(`${u}.${t} must be an object`);const o={};for(const[i,r]of Object.entries(e)){if(!h.has(i))throw new Error(`${u}.${t}.${i} is not a tier slot`);o[i]=v(r,`${t}.${i}`,{nonEmpty:n})}return o}const j=e=>(e??[]).filter(e=>!e.hidden).length;export function parseClientConfig(e,t){if(!b(e))return{config:null,problem:"not an object"};if(1!==e.schema)return{config:null,problem:`unsupported schema ${String(e.schema)}`};if(!b(e.models))return{config:null,problem:"models is not an object"};const n={};for(const[t,o]of Object.entries(e.models)){if(!Array.isArray(o))return{config:null,problem:`models.${t} is not a list`};const e=o.map(k);if(e.some(e=>null===e))return{config:null,problem:`models.${t} has a row without an id`};n[t]=e}let o,i;try{o=function(e){if(null==e)return;if(!b(e))throw new Error(`${u} must be an object`);const t={};if(void 0!==e.min_version){if("string"!=typeof e.min_version||e.min_version&&!y.test(e.min_version))throw new Error(`${u}.min_version is invalid`);e.min_version&&(t.minVersion=e.min_version)}const n=$(e.default_models,/^(haiku|sonnet|opus|reasoning|fable)$/,e=>g.test(e),"default_models");Object.keys(n).length&&(t.defaultModels=n);const o=w(e.retired_default_models,"retired_default_models",!1);Object.keys(o).length&&(t.retiredDefaultModels=o);const i=$(e.tool_defaults,_,e=>""===e||g.test(e),"tool_defaults");Object.keys(i).length&&(t.toolDefaults=i);const r=w(e.tier_chains,"tier_chains",!0);Object.keys(r).length&&(t.tierChains=r);const s=v(e.retired_models,"retired_models");s.length&&(t.retiredModels=s);const l=$(e.tool_min_versions,_,e=>""===e||y.test(e),"tool_min_versions");Object.keys(l).length&&(t.toolMinVersions=l);const c=v(e.agy_models,"agy_models");c.length&&(t.agyModels=c);const a=v(e.one_million_fallback,"one_million_fallback");return a.length&&(t.oneMillionFallback=a),!0===e.accept_shrink&&(t.acceptShrink=!0),t}(e[u])}catch(e){i=`${u} section dropped: ${e instanceof Error?e.message:String(e)}`}const r=b(e.personal)&&Array.isArray(e.personal.lists)?e.personal.lists.filter(e=>"string"==typeof e&&Array.isArray(n[e])):[],s=e[u];if(t&&(!b(s)||!0!==s.accept_shrink))for(const[e,o]of Object.entries(t.models)){if(r.includes(e))continue;const t=j(o),i=j(n[e]);if(t>0&&0===i||t>=4&&2*i<t)return{config:null,problem:`models.${e} shrank from ${t} to ${i}`}}return{config:{schema:1,revision:"string"==typeof e.revision?e.revision:"",models:n,...o?{client:o}:{},...r.length?{personal:r}:{}},problem:i}}export function serializeClientConfig(e){const t=Object.fromEntries(Object.entries(e.models).map(([e,t])=>[e,t.map(e=>({id:e.id,...e.hidden?{hidden:!0}:{},...e.name?{name:e.name}:{},...e.description?{description:e.description}:{},...e.contextWindow?{context_window:e.contextWindow}:{},...e.autoCompactTokens?{auto_compact_tokens:e.autoCompactTokens}:{},...e.clients?{clients:e.clients}:{},...e.minClientVersion?{[d]:e.minClientVersion}:{},...void 0!==e.rolloutPercent?{rollout_percent:e.rolloutPercent}:{}}))])),n=e.client,o=n?{...n.minVersion?{min_version:n.minVersion}:{},...n.defaultModels?{default_models:n.defaultModels}:{},...n.retiredDefaultModels?{retired_default_models:n.retiredDefaultModels}:{},...n.toolDefaults?{tool_defaults:n.toolDefaults}:{},...n.tierChains?{tier_chains:n.tierChains}:{},...n.retiredModels?{retired_models:n.retiredModels}:{},...n.toolMinVersions?{tool_min_versions:n.toolMinVersions}:{},...n.agyModels?{agy_models:n.agyModels}:{},...n.oneMillionFallback?{one_million_fallback:n.oneMillionFallback}:{},...n.acceptShrink?{accept_shrink:!0}:{}}:void 0;return{schema:e.schema,revision:e.revision,models:t,...o?{[u]:o}:{},...e.personal?{personal:{lists:e.personal}}:{}}}function O(){const e=p("CONFIG_DIR")?.trim();if(e)return e;const t=p("TEST_HOME")?.trim();return a(t||l(),m().configDirName)}export const cachePath=()=>a(O(),"client-config.json");function x(e,t){o(c(e),{recursive:!0});const n=`${e}.${process.pid}.tmp`;s(n,t,"utf-8"),r(n,e)}export function readCachedClientConfig(){try{if(!n(cachePath()))return null;const e=JSON.parse(i(cachePath(),"utf-8"));if("string"!=typeof e.site||"number"!=typeof e.fetchedAt)return null;const{config:t}=parseClientConfig(e.config);return t?{site:e.site,fetchedAt:e.fetchedAt,config:t,..."string"==typeof e.keyHash?{keyHash:e.keyHash}:{},..."string"==typeof e.etag?{etag:e.etag}:{}}:null}catch{return null}}export function writeCachedClientConfig(e){x(cachePath(),JSON.stringify({site:e.site,keyHash:e.keyHash,etag:e.etag,fetchedAt:e.fetchedAt,config:serializeClientConfig(e.config)},null,2)+"\n")}let E;export function bundledClientConfig(){if("1"===p("NO_BUNDLED_CLIENT_CONFIG"))return null;if(void 0!==E)return E;E=null;const e=c(f(import.meta.url));for(const t of[a(e,"..","..","client-config.snapshot.json"),a(e,"..","client-config.snapshot.json")])try{if(!n(t))continue;E=parseClientConfig(JSON.parse(i(t,"utf-8"))).config;break}catch{}return E}let M=null;export function setClientConfigSiteResolver(e){M=e}export function currentClientConfig(){const e=readCachedClientConfig();let t=null;try{t=M?M():null}catch{t=null}if(e&&null!==t&&e.site===t)return{config:e.config,source:"cache",fetchedAt:e.fetchedAt};const n=bundledClientConfig();return n?{config:n,source:"bundled"}:null}let A=null,V=null;export function setClientIdentityForTests(e,t){A=e,V=t}export const clientVersion=()=>A??function(){try{const e=c(f(import.meta.url));for(const t of[a(e,"..","..","package.json"),a(e,"..","package.json")])if(n(t))return String(JSON.parse(i(t,"utf-8")).version??"0.0.0")}catch{}return"0.0.0"}();function S(e,t){const n=e.split(".").map(e=>parseInt(e,10)||0),o=t.split(".").map(e=>parseInt(e,10)||0);for(let e=0;e<Math.max(n.length,o.length);e++){const t=(n[e]??0)-(o[e]??0);if(t)return t}return 0}export function installId(){if(V)return V;const e=a(O(),"install-id");try{if(n(e)){const t=i(e,"utf-8").trim();if(t)return t}const o=t();return x(e,o+"\n"),o}catch{return"unknown"}}export function rolloutBucket(t){return e("sha256").update(`${installId()}:${t}`).digest().readUInt32BE(0)%100}export function isEligible(e){return!(e.minClientVersion&&S(clientVersion(),e.minClientVersion)<0||void 0!==e.rolloutPercent&&rolloutBucket(e.id)>=e.rolloutPercent)}export function eligibleLists(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t.map(e=>e.hidden||isEligible(e)?e:{id:e.id,hidden:!0})]))}export function usableClientSection(e){const t=e?.client;if(t&&!(t.minVersion&&S(clientVersion(),t.minVersion)<0))return t}
@@ -15,9 +15,11 @@ export declare function autoCompactTokens(window: number, explicit?: number): nu
15
15
  * them too (so a model removed there, like Haiku for Codex, stays out); Codex also
16
16
  * gets the `gemini` list, Grok Build only the `grok` list. Built-in entries the lists lack are added (see
17
17
  * `withBuiltInExtras`); a `hidden` row on the gateway keeps one out. Without the
18
- * config, the built-in list from `models.json` → `gatewayModels`.
18
+ * config, the built-in list from `models.json` → `gatewayModels`. When the key's owner chose
19
+ * the models of a list this tool reads (`personal`, 「编码工具模型」 in the console), the lists are
20
+ * complete and no built-in extras are added.
19
21
  */
20
- export declare function gatewayModelSpecs(tool: GatewayModelTool, clientModels?: Record<string, GatewayClientModel[]>): GatewayModelSpec[];
22
+ export declare function gatewayModelSpecs(tool: GatewayModelTool, clientModels?: Record<string, GatewayClientModel[]>, personal?: string[]): GatewayModelSpec[];
21
23
  /**
22
24
  * Menu subtitle from the gateway's description without its figures: output limits
23
25
  * and context sizes don't hold for every relayed model. With `millionContext` (the
@@ -1 +1 @@
1
- import{getGatewayModelRules as e}from"./model-registry.js";export function autoCompactTokens(t,i){return i&&i>0?Math.min(i,t):Math.floor(t*e().autoCompactRatio)}export function gatewayModelSpecs(t,i){const o=e().models,n=o.filter(e=>function(e,t){return"codex"===e?!1!==t.codex:"grok"===e?"grok"===modelFamily(t.id):!t.id.startsWith("claude-")&&!1!==t.claudeCode}(t,e));if(!i)return n;const d=new Set(Object.values(i).flat().filter(e=>e.hidden).map(e=>e.id)),r=Object.fromEntries(Object.entries(i).map(([e,t])=>[e,t.filter(e=>!e.hidden)])),s=new Map;for(const e of Object.values(r).flat()){const i=e.clients?.[t];void 0===i||s.has(e.id)||s.set(e.id,i)}for(const[e,t]of s)t||d.add(e);const l=new Map(("claude-code"===t?o:n).map(e=>[e.id,e])),c=(r.gemini??[]).filter(e=>s.get(e.id)??!1!==o.find(t=>t.id===e.id)?.codex),a=("grok"===t?r.grok??[]:"codex"===t?[...r.codex??[],...c,...(r.claude??[]).filter(e=>e.id.startsWith("claude-")&&(s.get(e.id)??l.has(e.id)))]:function(e,t){return[...(e.codex??[]).filter(e=>"gpt"===modelFamily(e.id)),...e.gemini??[],...e.grok??[],...(e.claude??[]).filter(e=>!e.id.startsWith("claude-"))].filter(e=>e.clients?.["claude-code"]??t.get(e.id)?.claudeCode??!0)}(r,l)).filter(e=>!(e=>!1===s.get(e.id))(e));if(0===a.length)return n.filter(e=>!d.has(e.id));const u=new Set,f=[];for(const e of a){if(u.has(e.id))continue;u.add(e.id);const{clients:t,minClientVersion:i,rolloutPercent:o,...n}=e,d={...l.get(e.id),...n};void 0!==t?.codex&&(d.codex=t.codex),void 0!==t?.["claude-code"]&&(d.claudeCode=t["claude-code"]),f.push(d)}return function(e,t,i){const o=[...e];return t.forEach((e,n)=>{if(i.has(e.id)||o.some(t=>t.id===e.id))return;const d=modelFamily(e.id),r=new Set(t.slice(n+1).filter(e=>modelFamily(e.id)===d).map(e=>e.id));let s=o.findIndex(e=>r.has(e.id));if(s<0){s=o.length;for(let e=o.length-1;e>=0;e--)if(modelFamily(o[e].id)===d){s=e+1;break}}o.splice(s,0,e)}),o}(f,n,d)}const t={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function modelBlurb(e,t={}){const i=[];for(const o of(e??"").split("|")){const e=o.trim();e&&(/\d\s*万?\s*输出|output\D*\d|\d\D*output/i.test(e)||(/[\d百千]\s*万?\s*上下文|context\D*\d|\d\D*context/i.test(e)?t.millionContext&&!i.includes("百万上下文")&&i.unshift("百万上下文"):i.push(e)))}return t.millionContext&&!i.includes("百万上下文")&&i.unshift("百万上下文"),i.length>0?i.join(" | "):void 0}export function prettyModelName(e){const i=e.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),o=[];for(const e of i){const i=o[o.length-1];if(/^\d+$/.test(e)&&i&&/^\d+$/.test(i)&&e.length<=2&&!/^\d{8}$/.test(e)){o[o.length-1]=`${i}.${e}`;continue}const n=e.toLowerCase();o.push(t[n]??(/^[a-z]\d/i.test(e)?e.toUpperCase():e[0].toUpperCase()+e.slice(1)))}return o.join(" ")}export function vendorNames(e){return[...new Set(e.map(e=>prettyModelName(e).split(" ")[0]))]}export function modelFamily(e){const t=e.toLowerCase();return t.startsWith("claude-")?"claude":t.startsWith("gemini")?"gemini":t.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(t)?"gpt":"other"}
1
+ import{getGatewayModelRules as e}from"./model-registry.js";export function autoCompactTokens(t,o){return o&&o>0?Math.min(o,t):Math.floor(t*e().autoCompactRatio)}export function gatewayModelSpecs(o,i,d=[]){const n=e().models,r=n.filter(e=>function(e,t){return"codex"===e?!1!==t.codex:"grok"===e?"grok"===modelFamily(t.id):!t.id.startsWith("claude-")&&!1!==t.claudeCode}(o,e));if(!i)return r;const c=new Set(Object.values(i).flat().filter(e=>e.hidden).map(e=>e.id)),s=Object.fromEntries(Object.entries(i).map(([e,t])=>[e,t.filter(e=>!e.hidden)])),l=new Map;for(const e of Object.values(s).flat()){const t=e.clients?.[o];void 0===t||l.has(e.id)||l.set(e.id,t)}for(const[e,t]of l)t||c.add(e);const a=new Map(("claude-code"===o?n:r).map(e=>[e.id,e])),u=(s.gemini??[]).filter(e=>l.get(e.id)??!1!==n.find(t=>t.id===e.id)?.codex),f=("grok"===o?s.grok??[]:"codex"===o?[...s.codex??[],...u,...(s.claude??[]).filter(e=>e.id.startsWith("claude-")&&(l.get(e.id)??a.has(e.id)))]:function(e,t){return[...(e.codex??[]).filter(e=>"gpt"===modelFamily(e.id)),...e.gemini??[],...e.grok??[],...(e.claude??[]).filter(e=>!e.id.startsWith("claude-"))].filter(e=>e.clients?.["claude-code"]??t.get(e.id)?.claudeCode??!0)}(s,a)).filter(e=>!(e=>!1===l.get(e.id))(e));if(0===f.length)return r.filter(e=>!c.has(e.id));const m=new Set,p=[];for(const e of f){if(m.has(e.id))continue;m.add(e.id);const{clients:t,minClientVersion:o,rolloutPercent:i,...d}=e,n={...a.get(e.id),...d};void 0!==t?.codex&&(n.codex=t.codex),void 0!==t?.["claude-code"]&&(n.claudeCode=t["claude-code"]),p.push(n)}return t[o].some(e=>d.includes(e))?p:function(e,t,o){const i=[...e];return t.forEach((e,d)=>{if(o.has(e.id)||i.some(t=>t.id===e.id))return;const n=modelFamily(e.id),r=new Set(t.slice(d+1).filter(e=>modelFamily(e.id)===n).map(e=>e.id));let c=i.findIndex(e=>r.has(e.id));if(c<0){c=i.length;for(let e=i.length-1;e>=0;e--)if(modelFamily(i[e].id)===n){c=e+1;break}}i.splice(c,0,e)}),i}(p,r,c)}const t={grok:["grok"],codex:["codex","gemini","claude"],"claude-code":["codex","gemini","grok","claude"]},o={gpt:"GPT",gemini:"Gemini",glm:"GLM",deepseek:"DeepSeek",minimax:"MiniMax",qwen:"Qwen"};export function modelBlurb(e,t={}){const o=[];for(const i of(e??"").split("|")){const e=i.trim();e&&(/\d\s*万?\s*输出|output\D*\d|\d\D*output/i.test(e)||(/[\d百千]\s*万?\s*上下文|context\D*\d|\d\D*context/i.test(e)?t.millionContext&&!o.includes("百万上下文")&&o.unshift("百万上下文"):o.push(e)))}return t.millionContext&&!o.includes("百万上下文")&&o.unshift("百万上下文"),o.length>0?o.join(" | "):void 0}export function prettyModelName(e){const t=e.replace(/^([a-z]+)(\d)/i,"$1 $2").split(/[-_\s]+/).filter(Boolean),i=[];for(const e of t){const t=i[i.length-1];if(/^\d+$/.test(e)&&t&&/^\d+$/.test(t)&&e.length<=2&&!/^\d{8}$/.test(e)){i[i.length-1]=`${t}.${e}`;continue}const d=e.toLowerCase();i.push(o[d]??(/^[a-z]\d/i.test(e)?e.toUpperCase():e[0].toUpperCase()+e.slice(1)))}return i.join(" ")}export function vendorNames(e){return[...new Set(e.map(e=>prettyModelName(e).split(" ")[0]))]}export function modelFamily(e){const t=e.toLowerCase();return t.startsWith("claude-")?"claude":t.startsWith("gemini")?"gemini":t.startsWith("grok")?"grok":/^(gpt|o\d|codex)/.test(t)?"gpt":"other"}
@@ -5,3 +5,14 @@
5
5
  export declare function mirrorFileUrl(mirror: string, file: string, oldReleaseTag?: string): string;
6
6
  /** A page people can open in a browser for the mirror: the release page of its tag. */
7
7
  export declare function mirrorPageUrl(mirror: string): string;
8
+ /**
9
+ * Where to fetch a mirror file from, in order. With a download counter (`installMirror.downloadCounter`: it logs the
10
+ * download, then redirects to the same path on cnb.cool) a cnb.cool release file is tried through it first, then
11
+ * directly; anything else (no counter, another host, git raw files) is fetched directly.
12
+ */
13
+ export declare function downloadUrls(url: string, counter: string | undefined): string[];
14
+ /**
15
+ * Runs `fetchOne` on each of `downloadUrls(url, counter)` until one succeeds; a failure of the counter (unreachable,
16
+ * an error status, a broken transfer) falls back to the direct URL. Throws the direct URL's error.
17
+ */
18
+ export declare function withDownloadFallback<T>(url: string, counter: string | undefined, fetchOne: (url: string) => Promise<T>): Promise<T>;
@@ -1 +1 @@
1
- const e=/\/-\/releases\/latest$/;export function mirrorFileUrl(r,t,l){const s=r.replace(/\/+$/,"");if(e.test(s)){const r=s.replace(e,"");return l?`${r}/-/releases/download/${l}/${t}`:`${r}/-/releases/latest/download/${t}`}return`${s}/${t}`}export function mirrorPageUrl(e){const r=e.replace(/\/+$/,""),t=/^(.*\/-\/releases)\/download\/([^/]+)$/.exec(r);return t?`${t[1]}/tag/${t[2]}`:r.replace(/\/-\/releases\/latest\/download$/,"/-/releases/latest")}
1
+ const e=/\/-\/releases\/latest$/;export function mirrorFileUrl(t,r,o){const l=t.replace(/\/+$/,"");if(e.test(l)){const t=l.replace(e,"");return o?`${t}/-/releases/download/${o}/${r}`:`${t}/-/releases/latest/download/${r}`}return`${l}/${r}`}export function mirrorPageUrl(e){const t=e.replace(/\/+$/,""),r=/^(.*\/-\/releases)\/download\/([^/]+)$/.exec(t);return r?`${r[1]}/tag/${r[2]}`:t.replace(/\/-\/releases\/latest\/download$/,"/-/releases/latest")}const t=/^\/[^?#]+\/-\/releases\/(?:latest\/download|download\/[^/]+)\/[^/]+$/;export function downloadUrls(e,r){if(!r||!e.startsWith("https://cnb.cool/"))return[e];const o=e.slice(16);return t.test(o)?[`${r.replace(/\/+$/,"")}${o}`,e]:[e]}export async function withDownloadFallback(e,t,r){const o=downloadUrls(e,t);for(const e of o.slice(0,-1))try{return await r(e)}catch{}return r(o[o.length-1])}
@@ -20,6 +20,8 @@ export interface ModelCatalog {
20
20
  * windows, compaction points). Absent when the gateway has no such endpoint.
21
21
  */
22
22
  clientModels?: Record<string, GatewayClientModel[]>;
23
+ /** Lists of `clientModels` the key's owner chose in the console (see `ClientConfig.personal`). */
24
+ clientPersonal?: string[];
23
25
  }
24
26
  /** Strip a bracketed context-variant suffix: `claude-opus-4-8[1m]` → `claude-opus-4-8`. */
25
27
  export declare function baseModelId(id: string): string;
@@ -1 +1 @@
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
+ 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({apiKey:r}),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.models}:{},...a?.personal?{clientPersonal:a.personal}:{}}}catch(o){return e.logError("resolveModelCatalog",o),null}}
@@ -1 +1 @@
1
- import{join as e}from"path";import{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as n}from"./backup.js";import{i18n as s}from"../i18n.js";import{baseModelId as a}from"../model-catalog.js";import{gatewayModelSpecs as r,prettyModelName as i,vendorNames as l}from"../gateway-models.js";import{toolHome as c}from"./types.js";import{gatewayAddresses as d,normalizeBaseUrl as u}from"../gateway-addresses.js";import{configManager as m,savedApiKeys as p}from"../config.js";import{envCleanupPlan as f,lastEnvCleanup as g,removeEnvSources as _}from"../env-cleanup.js";import{canPrompt as h,hasExplicitYes as v}from"../runtime-options.js";import{maskHome as C}from"../telemetry.js";import{spawnSync as j}from"child_process";import{existsSync as y,readFileSync as k}from"fs";import{detectManagedOverrides as A}from"./claude-managed.js";import{installOfficialPlugins as O}from"./claude-plugins.js";import{findInstalls as S,mirrorInstallCommand as P,brandInstallScripts as x}from"./tool-installs.js";import{updateNativeFromMirror as b}from"./claude-native-mirror.js";import{brandEnv as T}from"../brand.js";import{claudeFormat as N,claudeProjectFiles as w}from"../sessions/formats.js";const H=e(c(),".claude","settings.json"),L=e(c(),".claude.json");let R,U=null,E=()=>0===j("security",["find-generic-password","-s","Claude Code-credentials"],{stdio:"ignore"}).status;export function setClaudeKeychainProbeForTests(e){E=e}export function claudeAccountLoginPresent(){const t=process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude");if(y(e(t,".credentials.json")))return!0;if("darwin"!==process.platform||T("TEST_HOME"))return!1;try{return E()}catch{return!1}}function I(){try{const t=JSON.parse(k(e(c(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function M(e){if(!e)return;const t=new Set(e.models);return r("claude-code",e.clientModels).filter(e=>t.has(e.id))}function D(){const e=o.getSettings().env?.ANTHROPIC_BASE_URL;return"string"==typeof e&&e.trim()?claudeCodeAdapter.holdsOurConfig(p())?null:e.trim():null}let B=null;const K={command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>b(I())},mirrorScript:x("claude-code"),brew:"claude-code"};function F(){return h()&&!v()}export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:P(K),upgradeCommand:"claude update",installSpec:K,upgradeHints:e=>"stable"===I()?[s.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:H,sessions:{format:N,files:()=>w(e(process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude"),"projects")),resumeCommand:e=>({command:"claude",args:["--resume",e]})},wantsModelCatalog:!0,loadPrivateConfig(t){B=D(),n(H),n(L),R=M(t.catalog),o.loadPrivateConfig(t.apiKey,t.models,t.catalog?.models,R);const s=e(c(),".local","bin","win32"===process.platform?"claude.exe":"claude"),a=S(claudeCodeAdapter.installSpec)[0]?.path??(y(s)?s:null);U=a?O(a):null},holdsOurConfig(e){const t=o.getSettings().env??{},n="string"==typeof t.ANTHROPIC_AUTH_TOKEN?t.ANTHROPIC_AUTH_TOKEN.trim():"",s="string"==typeof t.ANTHROPIC_BASE_URL?u(t.ANTHROPIC_BASE_URL):null;return!(!n||!e.includes(n))||null!==s&&(d().some(e=>e.url===s)||s===u(m.getBaseUrl()))},planNotes(){const e=[],t=D();t&&e.push(s.t("tools.claude_code.plan_replace_provider",{url:t}));for(const t of f())e.push(s.t("tools.claude_code.plan_env_remove",{location:C(t.location),vars:Object.keys(t.vars).join(", ")}));return e},unloadPrivateConfig(e){const t=o.getSettings().env??{};Boolean(t.ANTHROPIC_AUTH_TOKEN||t.ANTHROPIC_BASE_URL)&&!claudeCodeAdapter.holdsOurConfig(e?.savedKeys??[])||(n(H),o.unloadPrivateConfig())},detectCurrentConfig:()=>o.detectCurrentConfig(),getConfig:()=>({settings:o.getSettings(),mcp:o.getMCPConfig()}),updateConfig(e){const t=e||{};t.settings&&o.saveSettings(t.settings),t.mcp&&o.saveMCPConfig(t.mcp)},clearPinnedModels:e=>(n(H),o.clearPinnedModels(e?.models)),listedModels(){const e=o.getSettings().modelPicker;return Array.isArray(e?.options)?e.options.map(e=>a(String(e.model))):null},refreshModelMenu(e){const s=o.getSettings(),a=t(s,M(e.catalog));JSON.stringify(a)!==JSON.stringify(s)&&(n(H),o.saveSettings(a))},async followUp(){const e=g().foreign;if(0===e.length||!F())return null;const t=e.map(e=>`${C(e.location)}(${Object.keys(e.vars).join(", ")})`).join("、");return{prompt:s.t("tools.claude_code.env_foreign_offer",{where:t}),hint:s.t("tools.claude_code.env_foreign",{location:e.map(e=>C(e.location)).join("、"),vars:[...new Set(e.flatMap(e=>Object.keys(e.vars)))].join(", ")}),async run(){const t=_(e),o=t.removed.map(e=>({kind:"summary",text:s.t("tools.claude_code.env_removed_foreign",{location:C(e.location),vars:Object.keys(e.vars).join(", ")})}));return t.machineLeft.length>0&&o.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})}),o},decline(){}}},applyNotes(){const e=[];U?.installed.length&&(e.push({kind:"summary",text:s.t("tools.claude_code.plugins_installed",{count:String(U.installed.length)})}),e.push({kind:"detail",text:U.installed.join(", ")})),"git"===U?.skipped?e.push({kind:"action",text:s.t("tools.claude_code.plugins_need_git")}):("marketplace"===U?.skipped||U?.failed.length)&&e.push({kind:"action",text:s.t("tools.claude_code.plugins_failed")}),R&&R.length>0&&(e.push({kind:"summary",text:s.t("tools.claude_code.gateway_models",{count:String(R.length),vendors:l(R.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:R.map(e=>e.name||i(e.id)).join(", ")})),B&&e.push({kind:"summary",text:s.t("tools.claude_code.provider_replaced",{url:B})});const t=g();for(const o of t.removed)e.push({kind:"summary",text:s.t("tools.claude_code.env_removed",{location:C(o.location),vars:Object.keys(o.vars).join(", ")})});for(const o of t.foreign){const t={location:C(o.location),vars:Object.keys(o.vars).join(", ")};e.push(F()?{kind:"detail",text:s.t("tools.claude_code.env_foreign_found",t)}:{kind:"action",text:s.t("tools.claude_code.env_foreign",t)})}t.machineLeft.length>0&&e.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})});const n=o.getLastReplaced();n.length>0&&e.push({kind:"summary",text:s.t("tools.claude_code.replaced_user_models",{values:n.join(", ")})}),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===j("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:s.t("tools.claude_code.no_git_bash")}),claudeAccountLoginPresent()&&e.push({kind:"detail",text:s.t("tools.claude_code.account_login_overridden")});for(const t of A())e.push({kind:"action",text:s.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:s.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[H,L],forceResetPaths:()=>[H]};
1
+ import{join as e}from"path";import{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as n}from"./backup.js";import{i18n as s}from"../i18n.js";import{baseModelId as a}from"../model-catalog.js";import{gatewayModelSpecs as r,prettyModelName as i,vendorNames as l}from"../gateway-models.js";import{toolHome as c}from"./types.js";import{gatewayAddresses as d,normalizeBaseUrl as u}from"../gateway-addresses.js";import{configManager as m,savedApiKeys as p}from"../config.js";import{envCleanupPlan as f,lastEnvCleanup as g,removeEnvSources as _}from"../env-cleanup.js";import{canPrompt as h,hasExplicitYes as v}from"../runtime-options.js";import{maskHome as C}from"../telemetry.js";import{spawnSync as j}from"child_process";import{existsSync as y,readFileSync as k}from"fs";import{detectManagedOverrides as A}from"./claude-managed.js";import{installOfficialPlugins as O}from"./claude-plugins.js";import{findInstalls as P,mirrorInstallCommand as S,brandInstallScripts as x}from"./tool-installs.js";import{updateNativeFromMirror as b}from"./claude-native-mirror.js";import{brandEnv as T}from"../brand.js";import{claudeFormat as N,claudeProjectFiles as w}from"../sessions/formats.js";const H=e(c(),".claude","settings.json"),L=e(c(),".claude.json");let R,U=null,E=()=>0===j("security",["find-generic-password","-s","Claude Code-credentials"],{stdio:"ignore"}).status;export function setClaudeKeychainProbeForTests(e){E=e}export function claudeAccountLoginPresent(){const t=process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude");if(y(e(t,".credentials.json")))return!0;if("darwin"!==process.platform||T("TEST_HOME"))return!1;try{return E()}catch{return!1}}function I(){try{const t=JSON.parse(k(e(c(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function M(e){if(!e)return;const t=new Set(e.models);return r("claude-code",e.clientModels,e.clientPersonal).filter(e=>t.has(e.id))}function D(){const e=o.getSettings().env?.ANTHROPIC_BASE_URL;return"string"==typeof e&&e.trim()?claudeCodeAdapter.holdsOurConfig(p())?null:e.trim():null}let B=null;const K={command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>b(I())},mirrorScript:x("claude-code"),brew:"claude-code"};function F(){return h()&&!v()}export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:S(K),upgradeCommand:"claude update",installSpec:K,upgradeHints:e=>"stable"===I()?[s.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:H,sessions:{format:N,files:()=>w(e(process.env.CLAUDE_CONFIG_DIR?.trim()||e(c(),".claude"),"projects")),resumeCommand:e=>({command:"claude",args:["--resume",e]})},wantsModelCatalog:!0,loadPrivateConfig(t){B=D(),n(H),n(L),R=M(t.catalog),o.loadPrivateConfig(t.apiKey,t.models,t.catalog?.models,R);const s=e(c(),".local","bin","win32"===process.platform?"claude.exe":"claude"),a=P(claudeCodeAdapter.installSpec)[0]?.path??(y(s)?s:null);U=a?O(a):null},holdsOurConfig(e){const t=o.getSettings().env??{},n="string"==typeof t.ANTHROPIC_AUTH_TOKEN?t.ANTHROPIC_AUTH_TOKEN.trim():"",s="string"==typeof t.ANTHROPIC_BASE_URL?u(t.ANTHROPIC_BASE_URL):null;return!(!n||!e.includes(n))||null!==s&&(d().some(e=>e.url===s)||s===u(m.getBaseUrl()))},planNotes(){const e=[],t=D();t&&e.push(s.t("tools.claude_code.plan_replace_provider",{url:t}));for(const t of f())e.push(s.t("tools.claude_code.plan_env_remove",{location:C(t.location),vars:Object.keys(t.vars).join(", ")}));return e},unloadPrivateConfig(e){const t=o.getSettings().env??{};Boolean(t.ANTHROPIC_AUTH_TOKEN||t.ANTHROPIC_BASE_URL)&&!claudeCodeAdapter.holdsOurConfig(e?.savedKeys??[])||(n(H),o.unloadPrivateConfig())},detectCurrentConfig:()=>o.detectCurrentConfig(),getConfig:()=>({settings:o.getSettings(),mcp:o.getMCPConfig()}),updateConfig(e){const t=e||{};t.settings&&o.saveSettings(t.settings),t.mcp&&o.saveMCPConfig(t.mcp)},clearPinnedModels:e=>(n(H),o.clearPinnedModels(e?.models)),listedModels(){const e=o.getSettings().modelPicker;return Array.isArray(e?.options)?e.options.map(e=>a(String(e.model))):null},refreshModelMenu(e){const s=o.getSettings(),a=t(s,M(e.catalog));JSON.stringify(a)!==JSON.stringify(s)&&(n(H),o.saveSettings(a))},async followUp(){const e=g().foreign;if(0===e.length||!F())return null;const t=e.map(e=>`${C(e.location)}(${Object.keys(e.vars).join(", ")})`).join("、");return{prompt:s.t("tools.claude_code.env_foreign_offer",{where:t}),hint:s.t("tools.claude_code.env_foreign",{location:e.map(e=>C(e.location)).join("、"),vars:[...new Set(e.flatMap(e=>Object.keys(e.vars)))].join(", ")}),async run(){const t=_(e),o=t.removed.map(e=>({kind:"summary",text:s.t("tools.claude_code.env_removed_foreign",{location:C(e.location),vars:Object.keys(e.vars).join(", ")})}));return t.machineLeft.length>0&&o.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})}),o},decline(){}}},applyNotes(){const e=[];U?.installed.length&&(e.push({kind:"summary",text:s.t("tools.claude_code.plugins_installed",{count:String(U.installed.length)})}),e.push({kind:"detail",text:U.installed.join(", ")})),"git"===U?.skipped?e.push({kind:"action",text:s.t("tools.claude_code.plugins_need_git")}):("marketplace"===U?.skipped||U?.failed.length)&&e.push({kind:"action",text:s.t("tools.claude_code.plugins_failed")}),R&&R.length>0&&(e.push({kind:"summary",text:s.t("tools.claude_code.gateway_models",{count:String(R.length),vendors:l(R.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:R.map(e=>e.name||i(e.id)).join(", ")})),B&&e.push({kind:"summary",text:s.t("tools.claude_code.provider_replaced",{url:B})});const t=g();for(const o of t.removed)e.push({kind:"summary",text:s.t("tools.claude_code.env_removed",{location:C(o.location),vars:Object.keys(o.vars).join(", ")})});for(const o of t.foreign){const t={location:C(o.location),vars:Object.keys(o.vars).join(", ")};e.push(F()?{kind:"detail",text:s.t("tools.claude_code.env_foreign_found",t)}:{kind:"action",text:s.t("tools.claude_code.env_foreign",t)})}t.machineLeft.length>0&&e.push({kind:"action",text:s.t("messages.env_vars_machine_left",{vars:t.machineLeft.join(", ")})});const n=o.getLastReplaced();n.length>0&&e.push({kind:"summary",text:s.t("tools.claude_code.replaced_user_models",{values:n.join(", ")})}),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===j("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:s.t("tools.claude_code.no_git_bash")}),claudeAccountLoginPresent()&&e.push({kind:"detail",text:s.t("tools.claude_code.account_login_overridden")});for(const t of A())e.push({kind:"action",text:s.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:s.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[H,L],forceResetPaths:()=>[H]};
@@ -1 +1 @@
1
- import{execFileSync as e}from"child_process";import{createHash as r}from"crypto";import{createReadStream as t,createWriteStream as s,existsSync as o,readdirSync as i,readFileSync as n}from"fs";import{copyFile as a,mkdir as l,readFile as c,rename as u,rm as f,writeFile as p}from"fs/promises";import{isAbsolute as d,join as m,normalize as h,sep as w}from"path";import{Readable as g,Writable as b}from"stream";import{pipeline as D}from"stream/promises";import*as v from"zlib";import{logger as y}from"../../utils/logger.js";import{brandEnv as k}from"../brand.js";import{downloadNativeBinary as $,nativeLayout as C,nativePlatformKey as x}from"./claude-native-mirror.js";import{WINDOWS_PACKAGE_FAMILIES as N}from"./desktop-process.js";import{toolHome as A}from"./types.js";import{mirrorFileUrl as P}from"../mirror-url.js";import{powerShellEnv as L}from"../powershell-env.js";export function readDesktopCliPin(e,r,t=!1){const s=e.indexOf('claude-code-releases","sdkWrapperVersion"');if(s<0)return null;const o=e.lastIndexOf("JSON.parse('",s),i=e.indexOf("')",s);if(o<0||i<0)return null;try{const s=JSON.parse(e.subarray(o+12,i).toString("utf-8")),n="string"==typeof s.version?s.version:"",a=s.manifest?.platforms?.[r],l=t?a?.bundle?.checksum:a?.checksum;return/^\d+\.\d+\.\d+$/.test(n)&&"string"==typeof l&&/^[0-9a-f]{64}$/.test(l)?{version:n,checksum:l}:null}catch{return null}}function O(){const r=m(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&o(r)){const e=i(r).filter(e=>/^app-\d+\.\d+\.\d+$/.test(e)).sort((e,r)=>r.localeCompare(e,void 0,{numeric:!0}));for(const t of e){const e=m(r,t,"resources","app.asar");if(o(e))return e}}try{const r=e("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${N.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"],env:L()});for(const e of r.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)){const r=m(e,"app","resources","app.asar");if(o(r))return r}}catch(e){y.logError("claude-desktop-cli.windowsAsarPath",e)}return null}function E(){if(k("TEST_HOME")?.trim())return null;for(const e of["/usr/lib/claude-desktop","/opt/Claude"]){const r=m(e,"resources","app.asar");if(o(r))return r}return null}function j(){for(const e of["/Applications/Claude.app",m(A(),"Applications","Claude.app")]){const r=m(e,"Contents","Resources","app.asar");if(o(r))return r}return null}export function installedDesktopAsarPath(){return"win32"===process.platform?O():"darwin"===process.platform?j():"linux"===process.platform?E():null}class S extends b{dest;buffer=Buffer.alloc(0);remaining=0;padding=0;file=null;longName=null;collectingLongName=null;constructor(e){super(),this.dest=e}_write(e,r,t){this.buffer=this.buffer.length?Buffer.concat([this.buffer,e]):e,this.drain().then(()=>t()).catch(t)}_final(e){this.remaining>0?e(new Error("archive ends inside an entry")):e()}target(e){const r=h(e);if(!r||d(r)||".."===r||r.startsWith(`..${w}`))throw new Error(`unsafe path in archive: ${e}`);return m(this.dest,r)}async closeFile(){const e=this.file;this.file=null,e&&await new Promise((r,t)=>e.end(e=>e?t(e):r()))}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const e=this.buffer.subarray(0,Math.min(this.remaining,this.buffer.length));this.buffer=this.buffer.subarray(e.length),this.remaining-=e.length,this.collectingLongName?this.collectingLongName.push(e):this.file&&!this.file.write(e)&&await new Promise(e=>this.file.once("drain",()=>e())),0===this.remaining&&(this.collectingLongName&&(this.longName=Buffer.concat(this.collectingLongName).toString("utf-8").replace(/\0.*$/s,""),this.collectingLongName=null),await this.closeFile());continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const e=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),e.every(e=>0===e))continue;const r=(r,t)=>e.subarray(r,r+t).toString("utf-8").replace(/\0.*$/s,""),t=r(345,155);let o=t?`${t}/${r(0,100)}`:r(0,100);null!==this.longName&&(o=this.longName,this.longName=null);const i=parseInt(r(124,12).trim()||"0",8),n=511&parseInt(r(100,8).trim()||"644",8),a=r(156,1)||"0";if(this.remaining=i,this.padding=(512-i%512)%512,"L"===a)this.collectingLongName=[];else if("x"===a||"g"===a);else if("5"===a)await l(this.target(o),{recursive:!0});else{if("0"!==a)throw new Error(`unexpected entry type ${a} in archive: ${o}`);{const e=this.target(o);await l(m(e,".."),{recursive:!0}),this.file=s(e,{mode:n||420}),0===i&&await this.closeFile()}}}}}export async function installDesktopCli(e){const s=readDesktopCliPin(e.asar,e.platformKey,e.bundle);if(!s)return{status:"skipped",reason:"unknown app layout"};if(e.bundle&&!e.download)return{status:"skipped",reason:"no bundle mirror"};const i=e.platformKey.startsWith("win32")?"claude.exe":"claude",n=m(e.userDataDir,"claude-code",s.version),d=m(n,s.checksum.slice(0,12)),h=e.bundle?m(d,"claude.app","Contents","MacOS","claude"):m(d,i),w=m(d,".verified");try{if(o(h)&&(await c(w,"utf-8")).trim()===s.checksum)return{status:"present",version:s.version}}catch{}await l(n,{recursive:!0});const g=m(n,`.partial-${s.checksum.slice(0,12)}`),y=`${g}.download`;try{await f(w,{force:!0}),await f(g,{recursive:!0,force:!0});let i="mirror";if(e.bundle){if(e.onDownload?.(s.version),await e.download(s.version,y),await async function(e,s,o){const i=r("sha256");if(await D(t(e),new b({write(e,r,t){i.update(e),t()}})),i.digest("hex")!==s)throw new Error("downloaded bundle does not match the app's checksum");const n=v.createZstdDecompress;if(!n)throw new Error(`Node ${process.version} cannot decompress zstd (needs 22.15 or newer)`);await D(t(e),n(),new S(o))}(y,s.checksum,g),!o(m(g,"claude.app","Contents","MacOS","claude")))throw new Error("bundle has no claude.app");await f(d,{recursive:!0,force:!0}),await u(g,d)}else{const r=e.localVersionsDir?m(e.localVersionsDir,s.version):null;if(r&&o(r))await a(r,g),i="local";else{e.onDownload?.(s.version);const r=e.download??((r,t)=>$(e.platformKey,r,t));await r(s.version,g)}await l(d,{recursive:!0}),await u(g,h)}return await p(w,s.checksum),{status:"installed",version:s.version,source:i}}finally{await f(g,{recursive:!0,force:!0}),await f(y,{force:!0}),await f(`${g}.download`,{force:!0})}}let F=(...e)=>fetch(...e);export function setDesktopCliFetchForTests(e){F=e}export async function ensureDesktopCli(e){const r=x();if(!r)return{status:"skipped",reason:"unsupported architecture"};if("win32"===process.platform){const t=O();return t?installDesktopCli({userDataDir:e.userDataDir,asar:n(t),platformKey:r,localVersionsDir:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}if("darwin"===process.platform){const t=j();if(!t)return{status:"skipped",reason:"app.asar not found"};const{mirror:o,desktopVersion:i}=e;return installDesktopCli({userDataDir:e.userDataDir,asar:n(t),platformKey:r,bundle:!0,onDownload:e.onDownload,download:o&&i?(e,t)=>async function(e,r){const t=await F(e,{signal:AbortSignal.timeout(9e5)});if(!t.ok||!t.body)throw new Error(`${e}: HTTP ${t.status}`);await D(g.fromWeb(t.body),s(r))}(P(o,`claude-code-${e}-${r}.app.tar.zst`,`v${i}`),t):void 0})}if("linux"===process.platform){const t=E();return t?installDesktopCli({userDataDir:e.userDataDir,asar:n(t),platformKey:r.replace(/-musl$/,""),localVersionsDir:r.endsWith("-musl")?void 0:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}return{status:"skipped",reason:"unsupported platform"}}
1
+ import{execFileSync as e}from"child_process";import{createHash as r}from"crypto";import{createReadStream as t,createWriteStream as s,existsSync as o,readdirSync as n,readFileSync as i}from"fs";import{copyFile as a,mkdir as l,readFile as c,rename as u,rm as f,writeFile as p}from"fs/promises";import{isAbsolute as d,join as m,normalize as h,sep as w}from"path";import{Readable as g,Writable as b}from"stream";import{pipeline as D}from"stream/promises";import*as v from"zlib";import{logger as y}from"../../utils/logger.js";import{brand as k,brandEnv as $}from"../brand.js";import{downloadNativeBinary as C,nativeLayout as x,nativePlatformKey as N}from"./claude-native-mirror.js";import{WINDOWS_PACKAGE_FAMILIES as A}from"./desktop-process.js";import{toolHome as P}from"./types.js";import{mirrorFileUrl as L,withDownloadFallback as O}from"../mirror-url.js";import{powerShellEnv as E}from"../powershell-env.js";export function readDesktopCliPin(e,r,t=!1){const s=e.indexOf('claude-code-releases","sdkWrapperVersion"');if(s<0)return null;const o=e.lastIndexOf("JSON.parse('",s),n=e.indexOf("')",s);if(o<0||n<0)return null;try{const s=JSON.parse(e.subarray(o+12,n).toString("utf-8")),i="string"==typeof s.version?s.version:"",a=s.manifest?.platforms?.[r],l=t?a?.bundle?.checksum:a?.checksum;return/^\d+\.\d+\.\d+$/.test(i)&&"string"==typeof l&&/^[0-9a-f]{64}$/.test(l)?{version:i,checksum:l}:null}catch{return null}}function j(){const r=m(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&o(r)){const e=n(r).filter(e=>/^app-\d+\.\d+\.\d+$/.test(e)).sort((e,r)=>r.localeCompare(e,void 0,{numeric:!0}));for(const t of e){const e=m(r,t,"resources","app.asar");if(o(e))return e}}try{const r=e("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${A.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"],env:E()});for(const e of r.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)){const r=m(e,"app","resources","app.asar");if(o(r))return r}}catch(e){y.logError("claude-desktop-cli.windowsAsarPath",e)}return null}function S(){if($("TEST_HOME")?.trim())return null;for(const e of["/usr/lib/claude-desktop","/opt/Claude"]){const r=m(e,"resources","app.asar");if(o(r))return r}return null}function F(){for(const e of["/Applications/Claude.app",m(P(),"Applications","Claude.app")]){const r=m(e,"Contents","Resources","app.asar");if(o(r))return r}return null}export function installedDesktopAsarPath(){return"win32"===process.platform?j():"darwin"===process.platform?F():"linux"===process.platform?S():null}class T extends b{dest;buffer=Buffer.alloc(0);remaining=0;padding=0;file=null;longName=null;collectingLongName=null;constructor(e){super(),this.dest=e}_write(e,r,t){this.buffer=this.buffer.length?Buffer.concat([this.buffer,e]):e,this.drain().then(()=>t()).catch(t)}_final(e){this.remaining>0?e(new Error("archive ends inside an entry")):e()}target(e){const r=h(e);if(!r||d(r)||".."===r||r.startsWith(`..${w}`))throw new Error(`unsafe path in archive: ${e}`);return m(this.dest,r)}async closeFile(){const e=this.file;this.file=null,e&&await new Promise((r,t)=>e.end(e=>e?t(e):r()))}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const e=this.buffer.subarray(0,Math.min(this.remaining,this.buffer.length));this.buffer=this.buffer.subarray(e.length),this.remaining-=e.length,this.collectingLongName?this.collectingLongName.push(e):this.file&&!this.file.write(e)&&await new Promise(e=>this.file.once("drain",()=>e())),0===this.remaining&&(this.collectingLongName&&(this.longName=Buffer.concat(this.collectingLongName).toString("utf-8").replace(/\0.*$/s,""),this.collectingLongName=null),await this.closeFile());continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const e=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),e.every(e=>0===e))continue;const r=(r,t)=>e.subarray(r,r+t).toString("utf-8").replace(/\0.*$/s,""),t=r(345,155);let o=t?`${t}/${r(0,100)}`:r(0,100);null!==this.longName&&(o=this.longName,this.longName=null);const n=parseInt(r(124,12).trim()||"0",8),i=511&parseInt(r(100,8).trim()||"644",8),a=r(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,"L"===a)this.collectingLongName=[];else if("x"===a||"g"===a);else if("5"===a)await l(this.target(o),{recursive:!0});else{if("0"!==a)throw new Error(`unexpected entry type ${a} in archive: ${o}`);{const e=this.target(o);await l(m(e,".."),{recursive:!0}),this.file=s(e,{mode:i||420}),0===n&&await this.closeFile()}}}}}export async function installDesktopCli(e){const s=readDesktopCliPin(e.asar,e.platformKey,e.bundle);if(!s)return{status:"skipped",reason:"unknown app layout"};if(e.bundle&&!e.download)return{status:"skipped",reason:"no bundle mirror"};const n=e.platformKey.startsWith("win32")?"claude.exe":"claude",i=m(e.userDataDir,"claude-code",s.version),d=m(i,s.checksum.slice(0,12)),h=e.bundle?m(d,"claude.app","Contents","MacOS","claude"):m(d,n),w=m(d,".verified");try{if(o(h)&&(await c(w,"utf-8")).trim()===s.checksum)return{status:"present",version:s.version}}catch{}await l(i,{recursive:!0});const g=m(i,`.partial-${s.checksum.slice(0,12)}`),y=`${g}.download`;try{await f(w,{force:!0}),await f(g,{recursive:!0,force:!0});let n="mirror";if(e.bundle){if(e.onDownload?.(s.version),await e.download(s.version,y),await async function(e,s,o){const n=r("sha256");if(await D(t(e),new b({write(e,r,t){n.update(e),t()}})),n.digest("hex")!==s)throw new Error("downloaded bundle does not match the app's checksum");const i=v.createZstdDecompress;if(!i)throw new Error(`Node ${process.version} cannot decompress zstd (needs 22.15 or newer)`);await D(t(e),i(),new T(o))}(y,s.checksum,g),!o(m(g,"claude.app","Contents","MacOS","claude")))throw new Error("bundle has no claude.app");await f(d,{recursive:!0,force:!0}),await u(g,d)}else{const r=e.localVersionsDir?m(e.localVersionsDir,s.version):null;if(r&&o(r))await a(r,g),n="local";else{e.onDownload?.(s.version);const r=e.download??((r,t)=>C(e.platformKey,r,t));await r(s.version,g)}await l(d,{recursive:!0}),await u(g,h)}return await p(w,s.checksum),{status:"installed",version:s.version,source:n}}finally{await f(g,{recursive:!0,force:!0}),await f(y,{force:!0}),await f(`${g}.download`,{force:!0})}}let K=(...e)=>fetch(...e);export function setDesktopCliFetchForTests(e){K=e}export async function ensureDesktopCli(e){const r=N();if(!r)return{status:"skipped",reason:"unsupported architecture"};if("win32"===process.platform){const t=j();return t?installDesktopCli({userDataDir:e.userDataDir,asar:i(t),platformKey:r,localVersionsDir:x().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}if("darwin"===process.platform){const t=F();if(!t)return{status:"skipped",reason:"app.asar not found"};const{mirror:o,desktopVersion:n}=e;return installDesktopCli({userDataDir:e.userDataDir,asar:i(t),platformKey:r,bundle:!0,onDownload:e.onDownload,download:o&&n?(e,t)=>O(L(o,`claude-code-${e}-${r}.app.tar.zst`,`v${n}`),k().installMirror?.downloadCounter,e=>async function(e,r){const t=await K(e,{signal:AbortSignal.timeout(9e5)});if(!t.ok||!t.body)throw new Error(`${e}: HTTP ${t.status}`);await D(g.fromWeb(t.body),s(r))}(e,t)):void 0})}if("linux"===process.platform){const t=S();return t?installDesktopCli({userDataDir:e.userDataDir,asar:i(t),platformKey:r.replace(/-musl$/,""),localVersionsDir:r.endsWith("-musl")?void 0:x().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}return{status:"skipped",reason:"unsupported platform"}}
@@ -48,6 +48,11 @@ export interface BuildCodexCatalogOptions {
48
48
  * table). When set, other Claude models get 200K whatever `models.json` says.
49
49
  */
50
50
  claudeOneMillion?: string[];
51
+ /**
52
+ * The key's owner chose the `codex` list in the console: Codex's own GPT entries outside it
53
+ * stay out of the menu too (the selected model always stays).
54
+ */
55
+ chosenGpt?: string[];
51
56
  }
52
57
  /**
53
58
  * Catalog entries with every field an older Codex's own entries carry and they
@@ -1 +1 @@
1
- import{spawnSync as t}from"child_process";import{existsSync as o,mkdtempSync as e,readFileSync as n,realpathSync as i,rmSync as r,statSync as s,writeFileSync as c}from"fs";import{tmpdir as l}from"os";import{delimiter as u,dirname as a,isAbsolute as d,join as p}from"path";import*as f from"@iarna/toml";import{fileURLToPath as m}from"url";import{logger as _}from"../../utils/logger.js";import{getCodexRules as x,compareVersions as g}from"../model-registry.js";import{autoCompactTokens as h,modelBlurb as w,prettyModelName as y}from"../gateway-models.js";import{codexHome as b,toolHome as v}from"./types.js";import{brandEnv as C,CLI_NAME as M,PROVIDER_KEY as j}from"../brand.js";const A=a(m(import.meta.url));export const CODEX_CATALOG_FILE=`${j}-model-catalog.json`;export const codexCatalogPath=()=>p(b(),CODEX_CATALOG_FILE);function O(){const t=C("CODEX_BIN");if(void 0!==t)return t.trim()?[t.trim()]:[];const o=["codex"];if("win32"===process.platform){const t=process.env.LOCALAPPDATA||p(v(),"AppData","Local");o.push(p(t,"Programs","OpenAI","Codex","bin","codex.exe"))}else o.push(p(v(),".local","bin","codex"));if("darwin"===process.platform)for(const t of["/Applications",p(v(),"Applications")])o.push(p(t,"ChatGPT.app/Contents/Resources/codex-cli/bin/codex")),o.push(p(t,"Codex.app/Contents/Resources/codex"));return o}function E(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function $(t){try{const o=JSON.parse(t),e=E(o)&&Array.isArray(o.models)?o.models.filter(E):[];return e.length>0?e:null}catch{return null}}function k(o,e,n){const i={encoding:"utf-8",timeout:2e4,maxBuffer:67108864,windowsHide:!0,stdio:["ignore","pipe","pipe"],...n?{env:n}:{}};return"win32"!==process.platform||"codex"!==o&&!/\.cmd$/i.test(o)?t(o,e,i):t(["codex"===o?"codex":`"${o}"`,...e].join(" "),{...i,shell:!0})}function P(t){const o=k(t,["debug","models","--bundled"]);if(0===o.status&&o.stdout){const t=$(o.stdout);if(t)return t}return(o.error||0!==o.status)&&_.logError("loadBundledCodexModels",o.error??new Error(`${t} exited ${o.status}`)),null}export function loadBundledCodexModels(t){if(t)return P(t);for(const t of O()){if("codex"!==t&&!o(t))continue;const e=P(t);if(e)return e}return null}function B(t){try{return s(t).isFile()}catch{return!1}}export function installedCodexBinaries(){const t=[],o=O();if(o.includes("codex")){const o="win32"===process.platform?["codex.exe","codex.cmd"]:["codex"];for(const e of(process.env.PATH??"").split(u))if(e&&d(e))for(const n of o)t.push(p(e,n))}t.push(...o.filter(t=>"codex"!==t));const e=new Set,n=[];for(const o of t){if(!B(o))continue;let t=o;try{t=i(o)}catch{}const r="win32"===process.platform?t.toLowerCase():t;e.has(r)||(e.add(r),n.push(o))}return n}export function codexBinaryVersion(t){const o=`${k(t,["--version"]).stdout??""}`.match(/(\d+)\.(\d+)\.(\d+)/);return o?o[0]:null}export function catalogRejections(t,o=installedCodexBinaries()){if(0===o.length)return[];const n=e(p(l(),`${M}-codex-`)),i=[];try{c(p(n,"config.toml"),f.stringify({model_catalog_json:t}));for(const t of o){const o=k(t,["debug","models"],{...process.env,CODEX_HOME:n});if(0===o.status)continue;const e=`${o.stderr??""}\n${o.stdout??""}`,r=e.split(/\r?\n/).find(t=>t.includes("model_catalog_json"));r?i.push({bin:t,version:codexBinaryVersion(t),error:r.trim()}):_.logError("catalogRejections",o.error??new Error(`${t} exited ${o.status}: ${e.trim().slice(0,500)}`))}}finally{r(n,{recursive:!0,force:!0})}return i}export function loadPreviousCodexModels(){const t=L()?.filter(t=>!S(t));return t&&t.length>0?t:null}export function previousGatewayModelIds(){return(L()??[]).filter(S).map(t=>String(t.slug))}function L(){return o(codexCatalogPath())?$(n(codexCatalogPath(),"utf-8")):null}function S(t){return"number"==typeof t.priority&&t.priority>=1e3}let T=null;function W(){if(null!==T)return T;const t=[p(A,"..","..","..","codex-base-instructions.md"),p(A,"..","..","codex-base-instructions.md"),p(A,"codex-base-instructions.md")],e=t.find(t=>o(t));if(!e)throw new Error(`codex-base-instructions.md not found, searched: ${t.join(", ")}`);return T=n(e,"utf-8"),T}function D(t){return"number"==typeof t&&Number.isFinite(t)&&t>0?t:void 0}function F(t,o,e,n){const i=e?t:Math.min(t,o);return{window:i,compact:h(i,n)}}function I(t,o,e,n){const{standardContextWindow:i}=x();let r=D(t.contextWindow)??i;n&&t.id.startsWith("claude-")&&(r=n.has(t.id)?Math.max(r,1e6):Math.min(r,2e5));const{window:s,compact:c}=F(r,i,e,D(t.autoCompactTokens));return{slug:t.id,display_name:t.name||y(t.id),description:w(t.description,{millionContext:s>=1e6})??null,supported_reasoning_levels:[],shell_type:"unified_exec",visibility:"list",supported_in_api:!0,priority:o,additional_speed_tiers:[],service_tiers:[],availability_nux:null,upgrade:null,model_messages:{instructions_template:W()},base_instructions:W(),supports_reasoning_summaries:!0,supports_parallel_tool_calls:!1,prefer_websockets:!1,include_skills_usage_instructions:!1,include_plugin_usage_instructions:!1,include_apps_usage_instructions:!1,default_reasoning_summary:"auto",support_verbosity:!1,default_verbosity:null,apply_patch_tool_type:null,web_search_tool_type:"text",truncation_policy:{mode:"bytes",limit:1e4},supports_image_detail_original:!1,context_window:s,max_context_window:s,auto_compact_token_limit:c,effective_context_window_percent:95,experimental_supported_tools:[],input_modalities:["text","image"],supports_search_tool:!1,supports_experimental_context:!1,use_responses_lite:!1,supports_reasoning_effort_updates:!1,node_repl_auto_review_required:!1,node_repl_disabled:!1}}const G={supports_parallel_tool_calls:!0,supports_reasoning_summaries:!0};export function fillFromOlderEntries(t,o){const e=new Map(o.map(t=>[t.slug,t])),n=new Map;for(const t of o)for(const[o,e]of Object.entries(t))n.has(o)||n.set(o,e);return t.map(t=>{const o=e.get(t.slug),i={...t};for(const[t,e]of n)t in i||(i[t]=o&&t in o?o[t]:e);return i})}function R(t,o,e){if("list"!==(t=function(t){const o=Object.keys(G).filter(o=>!(o in t));return 0===o.length?t:{...t,...Object.fromEntries(o.map(t=>[t,G[t]]))}}(t)).visibility)return t;const n=D(t.context_window);if(!n)return t;const i=D(t.max_context_window)??n,r=e?Math.min(D(o?.contextWindow)??i,i):n,{window:s,compact:c}=F(r,n,!0,D(o?.autoCompactTokens));return{...t,context_window:s,auto_compact_token_limit:c}}function X(t,o){if(!t.startsWith("gpt-"))return;let e,n=4;for(const i of o){const o="string"==typeof i.slug?i.slug:"";if(!o.startsWith("gpt-")||"list"!==i.visibility)continue;let r=0;for(;r<o.length&&o[r]===t[r];)r++;r>n&&(e=i,n=r)}return e}function H(t,o,e,n){return R({...t,...Object.fromEntries(["slug","display_name","description","priority"].map(t=>[t,o[t]])),upgrade:null,availability_nux:null},e,n)}export function buildCodexCatalog(t){const{bundled:o,specs:e,oneMillion:n,selectedModel:i}=t,r=t.claudeOneMillion?new Set(t.claudeOneMillion):void 0,s=t.keyModels?new Set(t.keyModels):null,c=new Set(t.gatewayAvailable),l=t=>!s||s.has(t)||t===i,u=t=>c.has(t)||t===i,a=new Map(e.map(t=>[t.id,t])),d=[],p=new Set;for(const t of o){const o="string"==typeof t.slug?t.slug:"";o&&!p.has(o)&&("list"!==t.visibility||l(o))&&(p.add(o),d.push(R(t,a.get(o),n)))}const f=[...e];i&&!a.has(i)&&f.push({id:i});const m=function(t){const o=new Map;for(const e of t){const t=/^claude-([a-z]+)-(.+)$/.exec(e);if(!t)continue;const n=t[2].split("-").filter(t=>/^\d{1,2}$/.test(t)).join("."),i=o.get(t[1]);(!i||g(n,i.version)>0)&&o.set(t[1],{id:e,version:n})}return new Set([...o.values()].map(t=>t.id))}(f.map(t=>t.id).filter(u));for(const t of f){if(p.has(t.id)||!u(t.id))continue;if(t.id.startsWith("claude-")&&!m.has(t.id)&&t.id!==i)continue;p.add(t.id);const e=I(t,1e3+d.length,n,r),s=X(t.id,o);d.push(s?H(s,e,t,n):e)}return{models:d}}
1
+ import{spawnSync as t}from"child_process";import{existsSync as o,mkdtempSync as e,readFileSync as n,realpathSync as i,rmSync as s,statSync as r,writeFileSync as c}from"fs";import{tmpdir as l}from"os";import{delimiter as u,dirname as a,isAbsolute as d,join as p}from"path";import*as f from"@iarna/toml";import{fileURLToPath as m}from"url";import{logger as _}from"../../utils/logger.js";import{getCodexRules as x,compareVersions as g}from"../model-registry.js";import{autoCompactTokens as h,modelBlurb as w,prettyModelName as y}from"../gateway-models.js";import{codexHome as b,toolHome as v}from"./types.js";import{brandEnv as C,CLI_NAME as M,PROVIDER_KEY as j}from"../brand.js";const A=a(m(import.meta.url));export const CODEX_CATALOG_FILE=`${j}-model-catalog.json`;export const codexCatalogPath=()=>p(b(),CODEX_CATALOG_FILE);function O(){const t=C("CODEX_BIN");if(void 0!==t)return t.trim()?[t.trim()]:[];const o=["codex"];if("win32"===process.platform){const t=process.env.LOCALAPPDATA||p(v(),"AppData","Local");o.push(p(t,"Programs","OpenAI","Codex","bin","codex.exe"))}else o.push(p(v(),".local","bin","codex"));if("darwin"===process.platform)for(const t of["/Applications",p(v(),"Applications")])o.push(p(t,"ChatGPT.app/Contents/Resources/codex-cli/bin/codex")),o.push(p(t,"Codex.app/Contents/Resources/codex"));return o}function E(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function $(t){try{const o=JSON.parse(t),e=E(o)&&Array.isArray(o.models)?o.models.filter(E):[];return e.length>0?e:null}catch{return null}}function k(o,e,n){const i={encoding:"utf-8",timeout:2e4,maxBuffer:67108864,windowsHide:!0,stdio:["ignore","pipe","pipe"],...n?{env:n}:{}};return"win32"!==process.platform||"codex"!==o&&!/\.cmd$/i.test(o)?t(o,e,i):t(["codex"===o?"codex":`"${o}"`,...e].join(" "),{...i,shell:!0})}function P(t){const o=k(t,["debug","models","--bundled"]);if(0===o.status&&o.stdout){const t=$(o.stdout);if(t)return t}return(o.error||0!==o.status)&&_.logError("loadBundledCodexModels",o.error??new Error(`${t} exited ${o.status}`)),null}export function loadBundledCodexModels(t){if(t)return P(t);for(const t of O()){if("codex"!==t&&!o(t))continue;const e=P(t);if(e)return e}return null}function S(t){try{return r(t).isFile()}catch{return!1}}export function installedCodexBinaries(){const t=[],o=O();if(o.includes("codex")){const o="win32"===process.platform?["codex.exe","codex.cmd"]:["codex"];for(const e of(process.env.PATH??"").split(u))if(e&&d(e))for(const n of o)t.push(p(e,n))}t.push(...o.filter(t=>"codex"!==t));const e=new Set,n=[];for(const o of t){if(!S(o))continue;let t=o;try{t=i(o)}catch{}const s="win32"===process.platform?t.toLowerCase():t;e.has(s)||(e.add(s),n.push(o))}return n}export function codexBinaryVersion(t){const o=`${k(t,["--version"]).stdout??""}`.match(/(\d+)\.(\d+)\.(\d+)/);return o?o[0]:null}export function catalogRejections(t,o=installedCodexBinaries()){if(0===o.length)return[];const n=e(p(l(),`${M}-codex-`)),i=[];try{c(p(n,"config.toml"),f.stringify({model_catalog_json:t}));for(const t of o){const o=k(t,["debug","models"],{...process.env,CODEX_HOME:n});if(0===o.status)continue;const e=`${o.stderr??""}\n${o.stdout??""}`,s=e.split(/\r?\n/).find(t=>t.includes("model_catalog_json"));s?i.push({bin:t,version:codexBinaryVersion(t),error:s.trim()}):_.logError("catalogRejections",o.error??new Error(`${t} exited ${o.status}: ${e.trim().slice(0,500)}`))}}finally{s(n,{recursive:!0,force:!0})}return i}export function loadPreviousCodexModels(){const t=B()?.filter(t=>!L(t));return t&&t.length>0?t:null}export function previousGatewayModelIds(){return(B()??[]).filter(L).map(t=>String(t.slug))}function B(){return o(codexCatalogPath())?$(n(codexCatalogPath(),"utf-8")):null}function L(t){return"number"==typeof t.priority&&t.priority>=1e3}let W=null;function T(){if(null!==W)return W;const t=[p(A,"..","..","..","codex-base-instructions.md"),p(A,"..","..","codex-base-instructions.md"),p(A,"codex-base-instructions.md")],e=t.find(t=>o(t));if(!e)throw new Error(`codex-base-instructions.md not found, searched: ${t.join(", ")}`);return W=n(e,"utf-8"),W}function D(t){return"number"==typeof t&&Number.isFinite(t)&&t>0?t:void 0}function G(t,o,e,n){const i=e?t:Math.min(t,o);return{window:i,compact:h(i,n)}}function F(t,o,e,n){const{standardContextWindow:i}=x();let s=D(t.contextWindow)??i;n&&t.id.startsWith("claude-")&&(s=n.has(t.id)?Math.max(s,1e6):Math.min(s,2e5));const{window:r,compact:c}=G(s,i,e,D(t.autoCompactTokens));return{slug:t.id,display_name:t.name||y(t.id),description:w(t.description,{millionContext:r>=1e6})??null,supported_reasoning_levels:[],shell_type:"unified_exec",visibility:"list",supported_in_api:!0,priority:o,additional_speed_tiers:[],service_tiers:[],availability_nux:null,upgrade:null,model_messages:{instructions_template:T()},base_instructions:T(),supports_reasoning_summaries:!0,supports_parallel_tool_calls:!1,prefer_websockets:!1,include_skills_usage_instructions:!1,include_plugin_usage_instructions:!1,include_apps_usage_instructions:!1,default_reasoning_summary:"auto",support_verbosity:!1,default_verbosity:null,apply_patch_tool_type:null,web_search_tool_type:"text",truncation_policy:{mode:"bytes",limit:1e4},supports_image_detail_original:!1,context_window:r,max_context_window:r,auto_compact_token_limit:c,effective_context_window_percent:95,experimental_supported_tools:[],input_modalities:["text","image"],supports_search_tool:!1,supports_experimental_context:!1,use_responses_lite:!1,supports_reasoning_effort_updates:!1,node_repl_auto_review_required:!1,node_repl_disabled:!1}}const I={supports_parallel_tool_calls:!0,supports_reasoning_summaries:!0};export function fillFromOlderEntries(t,o){const e=new Map(o.map(t=>[t.slug,t])),n=new Map;for(const t of o)for(const[o,e]of Object.entries(t))n.has(o)||n.set(o,e);return t.map(t=>{const o=e.get(t.slug),i={...t};for(const[t,e]of n)t in i||(i[t]=o&&t in o?o[t]:e);return i})}function R(t,o,e){if("list"!==(t=function(t){const o=Object.keys(I).filter(o=>!(o in t));return 0===o.length?t:{...t,...Object.fromEntries(o.map(t=>[t,I[t]]))}}(t)).visibility)return t;const n=D(t.context_window);if(!n)return t;const i=D(t.max_context_window)??n,s=e?Math.min(D(o?.contextWindow)??i,i):n,{window:r,compact:c}=G(s,n,!0,D(o?.autoCompactTokens));return{...t,context_window:r,auto_compact_token_limit:c}}function X(t,o){if(!t.startsWith("gpt-"))return;let e,n=4;for(const i of o){const o="string"==typeof i.slug?i.slug:"";if(!o.startsWith("gpt-")||"list"!==i.visibility)continue;let s=0;for(;s<o.length&&o[s]===t[s];)s++;s>n&&(e=i,n=s)}return e}function H(t,o,e,n){return R({...t,...Object.fromEntries(["slug","display_name","description","priority"].map(t=>[t,o[t]])),upgrade:null,availability_nux:null},e,n)}export function buildCodexCatalog(t){const{bundled:o,specs:e,oneMillion:n,selectedModel:i}=t,s=t.claudeOneMillion?new Set(t.claudeOneMillion):void 0,r=t.keyModels?new Set(t.keyModels):null,c=new Set(t.gatewayAvailable),l=t=>!r||r.has(t)||t===i,u=t=>c.has(t)||t===i,a=new Map(e.map(t=>[t.id,t])),d=t.chosenGpt?new Set(t.chosenGpt):null,p=t=>!!d&&t.startsWith("gpt-")&&!d.has(t)&&t!==i,f=[],m=new Set;for(const t of o){const o="string"==typeof t.slug?t.slug:"";o&&!m.has(o)&&("list"!==t.visibility||l(o)&&!p(o))&&(m.add(o),f.push(R(t,a.get(o),n)))}const _=[...e];i&&!a.has(i)&&_.push({id:i});const x=function(t){const o=new Map;for(const e of t){const t=/^claude-([a-z]+)-(.+)$/.exec(e);if(!t)continue;const n=t[2].split("-").filter(t=>/^\d{1,2}$/.test(t)).join("."),i=o.get(t[1]);(!i||g(n,i.version)>0)&&o.set(t[1],{id:e,version:n})}return new Set([...o.values()].map(t=>t.id))}(_.map(t=>t.id).filter(u));for(const t of _){if(m.has(t.id)||!u(t.id))continue;if(t.id.startsWith("claude-")&&!x.has(t.id)&&t.id!==i)continue;m.add(t.id);const e=F(t,1e3+f.length,n,s),r=X(t.id,o);f.push(r?H(r,e,t,n):e)}return{models:f}}
@@ -1 +1 @@
1
- import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readdirSync as r,readFileSync as n,rmSync as s,statSync as i,writeFileSync as l}from"fs";import{join as a}from"path";import*as d from"@iarna/toml";import{logger as c}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as p,atomicWriteFile as m}from"./backup.js";import{codexHome as f,pickModel as _,toolHome as g,withV1 as x}from"./types.js";import{moveSessionsToProvider as h,NO_STATE_DB as y,runningCodexPids as k}from"../codex-sessions.js";import{codexFormat as b,codexRolloutFiles as v}from"../sessions/formats.js";import{getGatewayModelRules as w,getToolDefaultModel as j}from"../model-registry.js";import{ownsValue as S,takeField as C}from"../written-values.js";import{configManager as O}from"../config.js";import{maskHome as M,track as A}from"../telemetry.js";import{buildCodexCatalog as E,catalogRejections as N,codexCatalogPath as P,fillFromOlderEntries as T,installedCodexBinaries as D,loadBundledCodexModels as $,loadPreviousCodexModels as I,previousGatewayModelIds as L}from"./codex-catalog.js";import{gatewayModelSpecs as J,modelFamily as G}from"../gateway-models.js";import{isOurGatewayUrl as R,noteWrittenGatewayUrl as K}from"../gateway-addresses.js";import{findInstalls as X,mirrorInstallCommand as B,brandInstallScripts as U}from"./tool-installs.js";import{chatgptDesktopApp as W}from"./chatgpt-desktop.js";const q=()=>f(),z=()=>a(q(),"config.toml"),Y="sessions_move_declined",F=["browser_use","browser_use_external","computer_use"],H="desktop_features_restored";function Q(e){if(!0===O.getToolSetting("codex",H))return!1;const t=e.features,o=de(t)&&F.every(e=>!1===t[e]);if(O.setToolSetting("codex",H,!0),!o)return!1;for(const e of F)delete t[e];return!0}export function codexWorkModeOn(){try{const e=ue();return e.model_provider===te&&de(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function V(){try{if(de(ue().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",a(g(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(a(e,o))));if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(g(),"AppData","Local"),"Packages");try{return r(e).some(e=>/^OpenAI\.(Codex|ChatGPT)/i.test(e))}catch{return!1}}return t("/usr/lib/chatgpt/ChatGPT")}export function codexConfigPath(){return z()}const Z=()=>a(q(),"auth.json"),ee="OPENAI_API_KEY",te="custom",oe="high",re=["gpt-5.5","gpt-5.6-sol","gpt-6-astra"],ne=["high","xhigh"],se={goals:!0,js_repl:!1};let ie=[];const le=1e6,ae=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function de(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function ce(e,t){return de(e[t])||(e[t]={}),e[t]}function ue(){if(!t(z()))return{};const e=n(z(),"utf-8");if(!e.trim())return{};try{const t=d.parse(e);if(!de(t))throw new Error("config.toml root must be a TOML table");return t}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed; back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function pe(){if(!t(Z()))return{};try{const e=JSON.parse(n(Z(),"utf-8"));return de(e)?e:{}}catch{return{}}}let me=null;function fe(e){const t=e.profiles;if(de(t)){for(const[e,o]of Object.entries(t))de(o)&&2===Object.keys(o).length&&o.model_provider===te&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let _e=null;const ge=()=>a(O.getConfigDir(),"codex-catalog-check.json");function xe(e){const o=P();t(o)&&n(o,"utf-8")===e||(p(o),m(o,e))}function he(t,r){if(0===r.length)return[];const s=function(t,o){const r=e("sha256").update(t);for(const e of o)try{const t=i(e);r.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{r.update(`\0${e}`)}return r.digest("hex")}(t,r);if(function(e){try{return JSON.parse(n(ge(),"utf-8")).signature===e}catch{return!1}}(s))return[];const a=N(P(),r);return 0===a.length&&function(e){try{o(O.getConfigDir(),{recursive:!0}),l(ge(),JSON.stringify({signature:e})+"\n")}catch(e){c.logError("codexAdapter.rememberAccepted",e)}}(s),a}function ye(e,t,o){const r=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===le&&delete e.model_context_window,e.model_auto_compact_token_limit===ae&&delete e.model_auto_compact_token_limit}(e);const n=()=>{e.model_catalog_json===P()&&delete e.model_catalog_json,r&&(e.model_context_window=le,e.model_auto_compact_token_limit=ae)},i=$()??I();if(!i)return c.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),_e="skipped",void n();const l=t.catalog,a=e=>E({bundled:e,specs:J("codex",l?.clientModels),...l?{keyModels:l.models}:{},gatewayAvailable:l?.models??L(),oneMillion:r,selectedModel:o,..."gateway"===l?.oneMillionSource?{claudeOneMillion:l.oneMillion}:{}}),d=e=>JSON.stringify(e,null,2)+"\n";let u=a(i);xe(d(u));const p=D(),m=he(d(u),p);if(m.length>0){const e=Date.now();let t=!1;for(const e of m){const o=$(e.bin);if(o){for(const e of[{models:T(u.models,o)},{models:T(a(o).models,o)}])if(xe(d(e)),0===he(d(e),p).length){u=e,t=!0;break}if(t)break}}if(A("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:m[0].error,detail:{rejected_by:m.map(e=>({version:e.version,path:M(e.bin)}))}}),!t)return _e={rejectedBy:m[0]},s(P(),{force:!0}),void n()}_e={listed:u.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))},e.model_catalog_json=P()}export function codexCatalogRejection(){let e;try{e=ue()}catch{return null}return e.model_catalog_json===P()&&t(P())?N(P())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=ue(),r=d.stringify(o),n="string"==typeof o.model?o.model:j("codex");ye(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},n);const s=d.stringify(o);s!==r&&(p(z()),m(z(),s))}const ke={command:"codex",npmPackage:"@openai/codex",native:{binDir:"win32"===process.platform?["AppData","Local","Programs","OpenAI","Codex","bin"]:[".local","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".codex","packages","standalone"]]},mirrorScript:U("codex"),brew:"codex"};function be(e){const t=de(e.model_providers)?e.model_providers[te]:void 0;if(!de(t))return null;const o="string"==typeof t.base_url?t.base_url.trim():"";return o&&!R(o)?o:null}let ve=null;function we(){if("skipped"===_e)return 0===X(ke).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!_e)return[];if("rejectedBy"in _e){const{bin:e,version:t}=_e.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of _e.listed)e.set(G(t.id),(e.get(G(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${u.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:u.t("tools.codex.catalog_written",{count:String(_e.listed.length),breakdown:t})},{kind:"detail",text:_e.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||V()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]}export const codexAdapter={name:"codex",companionApps:[W],displayName:"Codex",command:"codex",installCommand:B(ke),installSpec:ke,configPath:z(),sessions:{format:b,files:()=>[...v(a(f(),"sessions")),...v(a(f(),"archived_sessions"))],resumeCommand:e=>({command:"codex",args:["resume",e]})},wantsModelCatalog:!0,listedModels(){if(ue().model_catalog_json!==P())return null;try{return(JSON.parse(n(P(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(ue().model_catalog_json!==P())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=ue(),o=function(e,t){const o=de(e.model_providers)?e.model_providers[te]:void 0;return!(!de(o)||o.base_url!==t||!0===o.supports_standalone_web_search||(o.supports_standalone_web_search=!0,0))}(t,x(e.baseUrl));(Q(t)||o)&&(p(z()),m(z(),d.stringify(t)))},async followUp(){if(ue().model_provider!==te)return null;const e=await h(te,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=O.getToolSettings("codex")[Y];if("number"==typeof o&&t<=o)return null;const r={count:String(t)};return k().length>0?{hint:u.t("tools.codex.sessions_hidden_running",r)}:{prompt:u.t("tools.codex.sessions_offer",r),hint:u.t("tools.codex.sessions_hidden",r),async run(){const e=await h(te),t=[...new Set(e.moved.map(e=>e.from))].join("、")||"-",o=String(Math.max(e.moved.length,e.dbRows)),r=[{kind:"summary",text:u.t("codex_sessions.moved",{count:o,from:t,target:te})},{kind:"summary",text:u.t("codex_sessions.backup",{dir:e.backupDir??""})}];return e.dbSkipped&&e.dbSkipped!==y&&r.push({kind:"action",text:u.t("codex_sessions.db_skipped",{reason:e.dbSkipped})}),r},decline(){O.setToolSetting("codex",Y,t)}}},applyNotes(){const e=[];return ve&&e.push({kind:"summary",text:u.t("tools.codex.provider_replaced",{url:ve})}),ie.length>0&&e.push({kind:"summary",text:u.t("tools.codex.replaced_user_settings",{values:ie.join(", ")})}),[...e,...we()]},planNotes(){let e;try{e=ue()}catch{return[]}const t=[],o=be(e);return o?t.push(u.t("tools.codex.plan_replace_provider",{url:o})):"string"==typeof e.model_provider&&e.model_provider!==te&&t.push(u.t("tools.codex.plan_switch_provider",{provider:e.model_provider})),t},loadPrivateConfig(e){const t=j("codex"),o=_(e.models,t),r=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,n=x(e.baseUrl),s=ue();ie=[];const i=w().retired,l=(e,t,o,r,n)=>{void 0===t||t===o||"string"==typeof t&&i.includes(t)||S(e,t,r)||ie.push(`${n} = ${String(t)}`),C(e,o)};l("codex.model",s.model,r,re,"model"),s.model=r,s.model_provider=te,l("codex.model_reasoning_effort",s.model_reasoning_effort,oe,ne,"model_reasoning_effort"),s.model_reasoning_effort=oe;const a="string"==typeof s.model?s.model:r;ye(s,e,a);const c=ce(s,"model_providers");ve=be(s),K(n);const u=de(c[te])?{...c[te]}:{};delete u.env_key;const{experimental_bearer_token:f,...g}=u;c[te]={...g,name:"custom",wire_api:"responses",requires_openai_auth:!0,supports_standalone_web_search:!0,base_url:n},fe(s),ce(ce(s,"tui"),"model_availability_nux")[a]=4;const h=ce(s,"features");for(const[e,t]of Object.entries(se))l(`codex.features.${e}`,void 0===h[e]?void 0:String(h[e]),String(t),[String(t)],`features.${e}`),h[e]=t;Q(s),p(z()),m(z(),d.stringify(s)),function(e){const t=e.trim();if(!t)return;const o={...pe(),auth_mode:"apikey",[ee]:t};p(Z()),m(Z(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},holdsOurConfig(e){const t=pe()[ee];return"string"==typeof t&&e.includes(t.trim())},unloadPrivateConfig(e){if(me=function(e){const t=pe(),o="string"==typeof t[ee]?t[ee].trim():"";if(!o)return de(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[ee]:r,auth_mode:n,...i}=t;return p(Z()),de(i.tokens)?(m(Z(),JSON.stringify({...i,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(s(Z(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(z()))return;let o;try{o=ue()}catch(e){return void c.logError("codexAdapter.unloadPrivateConfig",e)}const r=o.model_providers;de(r)&&de(r[te])&&R(r[te].base_url)&&de(r)&&(delete r[te],0===Object.keys(r).length&&delete o.model_providers,o.model_provider===te&&delete o.model_provider),o.model_catalog_json===P()&&delete o.model_catalog_json,Q(o),fe(o),p(z()),m(z(),d.stringify(o))},unloadNotes(){switch(me){case"chatgpt":return[{kind:"action",text:u.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:u.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:u.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=pe()[ee];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return ue()}catch(e){return c.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[z(),Z(),P()]};export function codexAuthProblem(){try{const e=ue(),t=e.model_providers?.[te];if(e.model_provider!==te||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=pe();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
1
+ import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as i,statSync as s,writeFileSync as l}from"fs";import{join as d}from"path";import*as a from"@iarna/toml";import{logger as c}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as p,atomicWriteFile as m}from"./backup.js";import{codexHome as f,pickModel as _,toolHome as g,withV1 as x}from"./types.js";import{moveSessionsToProvider as h,NO_STATE_DB as y,runningCodexPids as k}from"../codex-sessions.js";import{codexFormat as b,codexRolloutFiles as v}from"../sessions/formats.js";import{getGatewayModelRules as w,getToolDefaultModel as j}from"../model-registry.js";import{ownsValue as S,takeField as C}from"../written-values.js";import{configManager as O}from"../config.js";import{maskHome as M,track as A}from"../telemetry.js";import{buildCodexCatalog as E,catalogRejections as P,codexCatalogPath as N,fillFromOlderEntries as T,installedCodexBinaries as D,loadBundledCodexModels as $,loadPreviousCodexModels as I,previousGatewayModelIds as L}from"./codex-catalog.js";import{gatewayModelSpecs as J,modelFamily as G}from"../gateway-models.js";import{isOurGatewayUrl as R,noteWrittenGatewayUrl as K}from"../gateway-addresses.js";import{findInstalls as X,mirrorInstallCommand as B,brandInstallScripts as U}from"./tool-installs.js";import{chatgptDesktopApp as W}from"./chatgpt-desktop.js";const q=()=>f(),z=()=>d(q(),"config.toml"),Y="sessions_move_declined",F=["browser_use","browser_use_external","computer_use"],H="desktop_features_restored";function Q(e){if(!0===O.getToolSetting("codex",H))return!1;const t=e.features,o=ae(t)&&F.every(e=>!1===t[e]);if(O.setToolSetting("codex",H,!0),!o)return!1;for(const e of F)delete t[e];return!0}export function codexWorkModeOn(){try{const e=ue();return e.model_provider===te&&ae(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function V(){try{if(ae(ue().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",d(g(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(d(e,o))));if("win32"===process.platform){const e=d(process.env.LOCALAPPDATA||d(g(),"AppData","Local"),"Packages");try{return n(e).some(e=>/^OpenAI\.(Codex|ChatGPT)/i.test(e))}catch{return!1}}return t("/usr/lib/chatgpt/ChatGPT")}export function codexConfigPath(){return z()}const Z=()=>d(q(),"auth.json"),ee="OPENAI_API_KEY",te="custom",oe="high",ne=["gpt-5.5","gpt-5.6-sol","gpt-6-astra"],re=["high","xhigh"],ie={goals:!0,js_repl:!1};let se=[];const le=1e6,de=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function ae(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function ce(e,t){return ae(e[t])||(e[t]={}),e[t]}function ue(){if(!t(z()))return{};const e=r(z(),"utf-8");if(!e.trim())return{};try{const t=a.parse(e);if(!ae(t))throw new Error("config.toml root must be a TOML table");return t}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed; back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function pe(){if(!t(Z()))return{};try{const e=JSON.parse(r(Z(),"utf-8"));return ae(e)?e:{}}catch{return{}}}let me=null;function fe(e){const t=e.profiles;if(ae(t)){for(const[e,o]of Object.entries(t))ae(o)&&2===Object.keys(o).length&&o.model_provider===te&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let _e=null;const ge=()=>d(O.getConfigDir(),"codex-catalog-check.json");function xe(e){const o=N();t(o)&&r(o,"utf-8")===e||(p(o),m(o,e))}function he(t,n){if(0===n.length)return[];const i=function(t,o){const n=e("sha256").update(t);for(const e of o)try{const t=s(e);n.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{n.update(`\0${e}`)}return n.digest("hex")}(t,n);if(function(e){try{return JSON.parse(r(ge(),"utf-8")).signature===e}catch{return!1}}(i))return[];const d=P(N(),n);return 0===d.length&&function(e){try{o(O.getConfigDir(),{recursive:!0}),l(ge(),JSON.stringify({signature:e})+"\n")}catch(e){c.logError("codexAdapter.rememberAccepted",e)}}(i),d}function ye(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===le&&delete e.model_context_window,e.model_auto_compact_token_limit===de&&delete e.model_auto_compact_token_limit}(e);const r=()=>{e.model_catalog_json===N()&&delete e.model_catalog_json,n&&(e.model_context_window=le,e.model_auto_compact_token_limit=de)},s=$()??I();if(!s)return c.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),_e="skipped",void r();const l=t.catalog,d=e=>E({bundled:e,specs:J("codex",l?.clientModels,l?.clientPersonal),...l?.clientPersonal?.includes("codex")?{chosenGpt:(l.clientModels?.codex??[]).filter(e=>!e.hidden).map(e=>e.id)}:{},...l?{keyModels:l.models}:{},gatewayAvailable:l?.models??L(),oneMillion:n,selectedModel:o,..."gateway"===l?.oneMillionSource?{claudeOneMillion:l.oneMillion}:{}}),a=e=>JSON.stringify(e,null,2)+"\n";let u=d(s);xe(a(u));const p=D(),m=he(a(u),p);if(m.length>0){const e=Date.now();let t=!1;for(const e of m){const o=$(e.bin);if(o){for(const e of[{models:T(u.models,o)},{models:T(d(o).models,o)}])if(xe(a(e)),0===he(a(e),p).length){u=e,t=!0;break}if(t)break}}if(A("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:m[0].error,detail:{rejected_by:m.map(e=>({version:e.version,path:M(e.bin)}))}}),!t)return _e={rejectedBy:m[0]},i(N(),{force:!0}),void r()}_e={listed:u.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))},e.model_catalog_json=N()}export function codexCatalogRejection(){let e;try{e=ue()}catch{return null}return e.model_catalog_json===N()&&t(N())?P(N())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=ue(),n=a.stringify(o),r="string"==typeof o.model?o.model:j("codex");ye(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=a.stringify(o);i!==n&&(p(z()),m(z(),i))}const ke={command:"codex",npmPackage:"@openai/codex",native:{binDir:"win32"===process.platform?["AppData","Local","Programs","OpenAI","Codex","bin"]:[".local","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".codex","packages","standalone"]]},mirrorScript:U("codex"),brew:"codex"};function be(e){const t=ae(e.model_providers)?e.model_providers[te]:void 0;if(!ae(t))return null;const o="string"==typeof t.base_url?t.base_url.trim():"";return o&&!R(o)?o:null}let ve=null;function we(){if("skipped"===_e)return 0===X(ke).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!_e)return[];if("rejectedBy"in _e){const{bin:e,version:t}=_e.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of _e.listed)e.set(G(t.id),(e.get(G(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${u.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:u.t("tools.codex.catalog_written",{count:String(_e.listed.length),breakdown:t})},{kind:"detail",text:_e.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||V()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]}export const codexAdapter={name:"codex",companionApps:[W],displayName:"Codex",command:"codex",installCommand:B(ke),installSpec:ke,configPath:z(),sessions:{format:b,files:()=>[...v(d(f(),"sessions")),...v(d(f(),"archived_sessions"))],resumeCommand:e=>({command:"codex",args:["resume",e]})},wantsModelCatalog:!0,listedModels(){if(ue().model_catalog_json!==N())return null;try{return(JSON.parse(r(N(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(ue().model_catalog_json!==N())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=ue(),o=function(e,t){const o=ae(e.model_providers)?e.model_providers[te]:void 0;return!(!ae(o)||o.base_url!==t||!0===o.supports_standalone_web_search||(o.supports_standalone_web_search=!0,0))}(t,x(e.baseUrl));(Q(t)||o)&&(p(z()),m(z(),a.stringify(t)))},async followUp(){if(ue().model_provider!==te)return null;const e=await h(te,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=O.getToolSettings("codex")[Y];if("number"==typeof o&&t<=o)return null;const n={count:String(t)};return k().length>0?{hint:u.t("tools.codex.sessions_hidden_running",n)}:{prompt:u.t("tools.codex.sessions_offer",n),hint:u.t("tools.codex.sessions_hidden",n),async run(){const e=await h(te),t=[...new Set(e.moved.map(e=>e.from))].join("、")||"-",o=String(Math.max(e.moved.length,e.dbRows)),n=[{kind:"summary",text:u.t("codex_sessions.moved",{count:o,from:t,target:te})},{kind:"summary",text:u.t("codex_sessions.backup",{dir:e.backupDir??""})}];return e.dbSkipped&&e.dbSkipped!==y&&n.push({kind:"action",text:u.t("codex_sessions.db_skipped",{reason:e.dbSkipped})}),n},decline(){O.setToolSetting("codex",Y,t)}}},applyNotes(){const e=[];return ve&&e.push({kind:"summary",text:u.t("tools.codex.provider_replaced",{url:ve})}),se.length>0&&e.push({kind:"summary",text:u.t("tools.codex.replaced_user_settings",{values:se.join(", ")})}),[...e,...we()]},planNotes(){let e;try{e=ue()}catch{return[]}const t=[],o=be(e);return o?t.push(u.t("tools.codex.plan_replace_provider",{url:o})):"string"==typeof e.model_provider&&e.model_provider!==te&&t.push(u.t("tools.codex.plan_switch_provider",{provider:e.model_provider})),t},loadPrivateConfig(e){const t=j("codex"),o=_(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=x(e.baseUrl),i=ue();se=[];const s=w().retired,l=(e,t,o,n,r)=>{void 0===t||t===o||"string"==typeof t&&s.includes(t)||S(e,t,n)||se.push(`${r} = ${String(t)}`),C(e,o)};l("codex.model",i.model,n,ne,"model"),i.model=n,i.model_provider=te,l("codex.model_reasoning_effort",i.model_reasoning_effort,oe,re,"model_reasoning_effort"),i.model_reasoning_effort=oe;const d="string"==typeof i.model?i.model:n;ye(i,e,d);const c=ce(i,"model_providers");ve=be(i),K(r);const u=ae(c[te])?{...c[te]}:{};delete u.env_key;const{experimental_bearer_token:f,...g}=u;c[te]={...g,name:"custom",wire_api:"responses",requires_openai_auth:!0,supports_standalone_web_search:!0,base_url:r},fe(i),ce(ce(i,"tui"),"model_availability_nux")[d]=4;const h=ce(i,"features");for(const[e,t]of Object.entries(ie))l(`codex.features.${e}`,void 0===h[e]?void 0:String(h[e]),String(t),[String(t)],`features.${e}`),h[e]=t;Q(i),p(z()),m(z(),a.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...pe(),auth_mode:"apikey",[ee]:t};p(Z()),m(Z(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},holdsOurConfig(e){const t=pe()[ee];return"string"==typeof t&&e.includes(t.trim())},unloadPrivateConfig(e){if(me=function(e){const t=pe(),o="string"==typeof t[ee]?t[ee].trim():"";if(!o)return ae(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[ee]:n,auth_mode:r,...s}=t;return p(Z()),ae(s.tokens)?(m(Z(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(i(Z(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(z()))return;let o;try{o=ue()}catch(e){return void c.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;ae(n)&&ae(n[te])&&R(n[te].base_url)&&ae(n)&&(delete n[te],0===Object.keys(n).length&&delete o.model_providers,o.model_provider===te&&delete o.model_provider),o.model_catalog_json===N()&&delete o.model_catalog_json,Q(o),fe(o),p(z()),m(z(),a.stringify(o))},unloadNotes(){switch(me){case"chatgpt":return[{kind:"action",text:u.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:u.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:u.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=pe()[ee];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return ue()}catch(e){return c.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[z(),Z(),N()]};export function codexAuthProblem(){try{const e=ue(),t=e.model_providers?.[te];if(e.model_provider!==te||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=pe();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -1 +1 @@
1
- import{spawnSync as t}from"child_process";import{createHash as e}from"crypto";import{accessSync as r,constants as o,copyFileSync as n,createReadStream as s,createWriteStream as i,existsSync as a,mkdirSync as c,mkdtempSync as l,readFileSync as p,renameSync as u,rmSync as d}from"fs";import{cpus as f,tmpdir as m}from"os";import{basename as g,dirname as h,join as w}from"path";import{Readable as S,Transform as A}from"stream";import{pipeline as x}from"stream/promises";import{logger as y}from"../../utils/logger.js";import{i18n as E}from"../i18n.js";import{powerShellEnv as $}from"../powershell-env.js";import{toolHome as v}from"./types.js";import{configManager as k}from"../config.js";import{mirrorFileUrl as _}from"../mirror-url.js";const P=(e,r,o={})=>{const n=t(e,r,{encoding:"utf-8",timeout:o.timeout??12e4,windowsHide:!0,input:o.input,stdio:[void 0===o.input?"ignore":"pipe","pipe","pipe"],maxBuffer:16777216,env:$()});return n.error&&y.logError(`desktop-app: ${e}`,n.error),{status:n.status,stdout:`${n.stdout??""}`,stderr:`${n.stderr??""}`}};let I=P,b=(...t)=>fetch(...t),C=null,T=null,M=null,F=null;export function setDesktopAppDepsForTests(t){void 0!==t.run&&(I=t.run??P),void 0!==t.fetch&&(b=t.fetch??((...t)=>fetch(...t))),void 0!==t.platform&&(C=t.platform),void 0!==t.arm64&&(T=t.arm64),void 0!==t.privileged&&(M=t.privileged),void 0!==t.remote&&(F=t.remote)}export function appPlatform(){return C??process.platform}export function isArm64Machine(){return null!==T?T:"arm64"===process.arch||("win32"===appPlatform()?"ARM64"===process.env.PROCESSOR_ARCHITECTURE||"ARM64"===process.env.PROCESSOR_ARCHITEW6432:"darwin"===appPlatform()&&/\bApple\b/.test(f()[0]?.model??""))}export class DesktopInstallError extends Error{key;constructor(t,e){super(t),this.name="DesktopInstallError",this.key=e}}export const fail=(t,e={})=>{const r={};for(const[t,o]of Object.entries(e))r[t]=o.trim()||E.t("app_update.reason_unknown");throw new DesktopInstallError(E.t(`app_update.error_${t}`,r),t)};export function parseSha256Sums(t){const e=new Map;for(const r of t.split(/\r?\n/)){const t=/^([0-9a-f]{64})\s+\*?(\S.*)$/i.exec(r.trim());t&&e.set(t[2].trim(),t[1].toLowerCase())}return e}async function R(t){const r=e("sha256");for await(const e of s(t))r.update(e);return r.digest("hex")}async function j(t,e){const r=await b(t,{signal:AbortSignal.timeout(e)});if(!r.ok)throw new Error(`${t}: HTTP ${r.status}`);return(await r.text()).trim()}function O(t,e){try{const r=p(w(t,"Contents","Info.plist"),"utf-8");return new RegExp(`<key>${e}</key>\\s*<string>([^<]*)</string>`).exec(r)?.[1]??null}catch{return null}}export function macBundleId(t){return O(t,"CFBundleIdentifier")}export function macAppVersion(t){return/^\d+\.\d+\.\d+/.exec(O(t,"CFBundleShortVersionString")??"")?.[0]??null}function N(t){try{return r(t,o.W_OK),!0}catch{return!1}}const B=t=>`'${t.replace(/'/g,"''")}'`;function D(t,e=12e4){return I("powershell.exe",["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command","[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; "+t],{timeout:e})}export function powerShellError(t){return t.replace(/#< CLIXML/g,"").replace(/_x000D__x000A_/g,"\n").replace(/<\/S>/g,"\n").replace(/<[^>]+>/g,"").replace(/_x[0-9A-F]{4}_/g,"").replace(/&apos;/g,"'").replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").split(/\r?\n/).map(t=>t.trim()).find(t=>t&&!/^(At line|At char|所在位置|\+|~|CategoryInfo|FullyQualifiedErrorId)/i.test(t))??""}export function createDesktopAppInstaller(t){const e=()=>t.mirror()||null,r=()=>t.asset(appPlatform(),isArm64Machine()),o=()=>{const o=appPlatform();return"darwin"!==o&&"win32"!==o?"unsupported_platform":r()?e()?"darwin"===o&&(M??0===process.getuid?.())?"root":"win32"===o&&(null!==M?M:/\\system32\\config\\systemprofile/i.test(process.env.USERPROFILE??"")||!0===process.env.USERNAME?.endsWith("$"))?"system_account":"win32"===o&&(null!==F?F:Boolean(process.env.SSH_CONNECTION||process.env.SSH_CLIENT))?"remote_session":t.extraBlocker?.(o)??null:"no_mirror":"darwin"===o?"unsupported_arch":"unsupported_platform"},s=async(r=8e3)=>{const o=e();if(!o)throw new Error("no mirror");const n=t.versionFile(appPlatform(),isArm64Machine()),s=await j(_(o,n),r),i=/^(\d+\.\d+\.\d+)(\.\d+)?$/.exec(s)?.[1];if(!i)throw new Error(`unexpected ${n}: ${s.slice(0,40)}`);return i},f=()=>{for(const e of["/Applications",w(v(),"Applications")])for(const r of t.macApp.installedNames){const o=w(e,r);if(!a(w(o,"Contents","Info.plist")))continue;const n=macBundleId(o);if(null===n||n===t.macApp.bundleId)return o}return null},$=e=>{const r=I("codesign",["--verify","--deep","--strict",e],{timeout:6e5});0!==r.status&&fail("signature",{reason:(r.stderr||r.stdout).trim().split("\n")[0]||`codesign ${r.status}`});const o=I("codesign",["-dv",e]),n=`${o.stdout}\n${o.stderr}`,s=/^TeamIdentifier=(\S+)/m.exec(n)?.[1],i=/^Identifier=(\S+)/m.exec(n)?.[1];s===t.macApp.teamId&&i===t.macApp.bundleId||fail("signature",{reason:`TeamIdentifier=${s??"?"} Identifier=${i??"?"}`});const a=I("spctl",["-a","-t","exec","-vv",e],{timeout:3e5});0!==a.status&&fail("signature",{reason:(a.stderr||a.stdout).trim().split("\n")[0]||`spctl ${a.status}`})},P=()=>{const e=f();let r=e;if(!r){const e=N("/Applications")?"/Applications":w(v(),"Applications");r=w(e,t.macApp.installedNames[0])}const o=h(r);return(a(o)&&!N(o)||e&&!N(e))&&fail("no_permission",{path:r}),{target:r,existing:e}},C=e=>{const r=D(`$s = Get-AuthenticodeSignature -LiteralPath ${B(e)}; [pscustomobject]@{ status = [string]$s.Status; subject = [string]$s.SignerCertificate.Subject } | ConvertTo-Json -Compress`);let o={};try{o=JSON.parse(r.stdout.trim()||"{}")}catch{}if("Valid"!==o.status||!t.windows.signer.test(o.subject??"")){const t=o.status?`${o.status} ${o.subject??""}`:powerShellError(r.stderr)||powerShellError(r.stdout.trim().startsWith("{")?"":r.stdout)||`exit ${r.status}`;fail("signature",{reason:t.trim()})}},T=()=>{const e=D(`Get-AppxPackage | Where-Object { ${t.windows.packageFilter} } | ForEach-Object { $_.Version.ToString() }`,3e4);return/\d+\.\d+\.\d+/.exec(e.stdout)?.[0]??null};return{assetName:r,blocker:o,latestVersion:s,installedMacApp:f,windowsPackageVersion:T,verifyMacApp:$,verifyWindowsPackage:C,install:async(f={})=>{const v=o();v&&fail("blocked",{reason:E.t(`app_update.reason_${v}`)});const M=e(),F=r();let O;try{O=await s(15e3)}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}const N=e=>_(M,e,t.releaseTag?.(O));let H;try{H=parseSha256Sums(await j(N("SHA256SUMS"),15e3)).get(F)}catch(e){y.logError(`${t.id}: SHA256SUMS`,e)}H||fail("no_checksum",{file:F}),"darwin"===appPlatform()&&P();const W=w(k.getConfigDir(),"downloads",t.id),V=w(W,F),L=l(w(m(),`${t.id}-`));let U=w(L,F),G=!1;try{a(V)&&await R(V)===H?(U=V,f.reusing?.(O)):(f.downloading?.(O),await async function(t,e,r){let o;try{o=await b(t,{signal:AbortSignal.timeout(18e5)})}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}o.ok&&o.body||fail("download",{reason:`HTTP ${o.status}`});const n=Number(o.headers.get("content-length"))||0;let s=0,a=-1;const c=new A({transform(t,e,o){s+=t.length;const i=n?Math.floor(100*s/n):-1;i>=0&&i!==a&&(a=i,r?.(i)),o(null,t)}});try{await x(S.fromWeb(o.body),c,i(e))}catch(t){d(e,{force:!0}),fail("download",{reason:t instanceof Error?t.message:String(t)})}}(N(F),U,f.percent)),f.verifying?.();const e=await R(U);e!==H&&fail("checksum",{file:F,expected:H.slice(0,12),actual:e.slice(0,12)}),f.installing?.();const r="darwin"===appPlatform()?await(async(e,r)=>{const o=w(r,"mnt");c(o,{recursive:!0});const n=I("hdiutil",["attach","-nobrowse","-readonly","-noautoopen","-mountpoint",o,e],{timeout:3e5,input:"Y\n"});0!==n.status&&fail("mount",{reason:(n.stderr||n.stdout).trim().split("\n").pop()??""});let s=null;try{const e=w(o,t.macApp.source);a(e)||fail("bad_package",{reason:t.macApp.source});const{target:r,existing:n}=P(),i=h(r),l=g(r);c(i,{recursive:!0}),s=w(i,`.${l}.${process.pid}.new`),d(s,{recursive:!0,force:!0});const p=I("ditto",[e,s],{timeout:9e5});0!==p.status&&fail("copy",{reason:(p.stderr||p.stdout).trim().split("\n")[0]??""}),$(s);const f=macAppVersion(s);f||fail("bad_package",{reason:"Info.plist"});const m=w(i,`.${l}.${process.pid}.old`);try{n&&u(n,m)}catch(t){throw["EPERM","EACCES"].includes(t.code??"")&&fail("app_management",{path:n}),t}try{u(s,r)}catch(t){throw n&&u(m,n),t}return s=null,d(m,{recursive:!0,force:!0}),{version:f,location:r}}finally{s&&d(s,{recursive:!0,force:!0}),I("hdiutil",["detach",o,"-force"],{timeout:6e4})}})(U,L):await(async(t,e)=>{C(t);const r=D(`$ErrorActionPreference = 'Stop'; Add-AppxPackage -Path ${B(t)} -ForceApplicationShutdown`,9e5);if(0!==r.status){const o=powerShellError(r.stderr)||powerShellError(r.stdout)||`Add-AppxPackage ${r.status}`;/0x80073D28/i.test(`${r.stderr}\n${r.stdout}`)||fail("install",{reason:o}),await(e.elevate?.())||fail("needs_admin");const n=(t=>{const e=w(h(t),"elevated-install.log"),r=`$ErrorActionPreference = 'Stop'; try { Add-AppxPackage -Path ${B(t)} -ForceApplicationShutdown; exit 0 } catch { [IO.File]::WriteAllText(${B(e)}, $_.Exception.Message, [Text.Encoding]::UTF8); exit 1 }`,o=D(`$ErrorActionPreference = 'Stop'; $p = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '${Buffer.from(r,"utf16le").toString("base64")}') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`,12e5);if(0===o.status)return null;let n="";try{n=p(e,"utf-8").trim()}catch{}return n||powerShellError(o.stderr)||powerShellError(o.stdout)||`exit ${o.status}`})(t);n&&fail("install",{reason:n}),T()||fail("other_account")}const o=T();return o||fail("install",{reason:"Get-AppxPackage"}),{version:o,location:"MSIX"}})(U,f);return G=!0,r}catch(t){const e=t instanceof DesktopInstallError?t.key:void 0;throw U!==V&&a(U)&&!["checksum","signature","bad_package","mount"].includes(e??"")&&function(t,e,r){try{d(e,{recursive:!0,force:!0}),c(e,{recursive:!0});try{u(t,r)}catch{n(t,r)}}catch(t){y.logError("desktop-app: keep package",t),d(e,{recursive:!0,force:!0})}}(U,W,V),U===V&&["signature","bad_package","mount"].includes(e??"")&&d(W,{recursive:!0,force:!0}),t}finally{G&&d(W,{recursive:!0,force:!0}),d(L,{recursive:!0,force:!0})}}}}
1
+ import{spawnSync as t}from"child_process";import{createHash as e}from"crypto";import{accessSync as r,constants as o,copyFileSync as n,createReadStream as s,createWriteStream as i,existsSync as a,mkdirSync as c,mkdtempSync as l,readFileSync as p,renameSync as u,rmSync as d}from"fs";import{cpus as f,tmpdir as m}from"os";import{basename as g,dirname as h,join as w}from"path";import{Readable as S,Transform as A}from"stream";import{pipeline as x}from"stream/promises";import{logger as y}from"../../utils/logger.js";import{i18n as E}from"../i18n.js";import{powerShellEnv as $}from"../powershell-env.js";import{toolHome as v}from"./types.js";import{configManager as k}from"../config.js";import{brand as _}from"../brand.js";import{mirrorFileUrl as P,withDownloadFallback as I}from"../mirror-url.js";const b=(e,r,o={})=>{const n=t(e,r,{encoding:"utf-8",timeout:o.timeout??12e4,windowsHide:!0,input:o.input,stdio:[void 0===o.input?"ignore":"pipe","pipe","pipe"],maxBuffer:16777216,env:$()});return n.error&&y.logError(`desktop-app: ${e}`,n.error),{status:n.status,stdout:`${n.stdout??""}`,stderr:`${n.stderr??""}`}};let C=b,T=(...t)=>fetch(...t),M=null,j=null,F=null,R=null;export function setDesktopAppDepsForTests(t){void 0!==t.run&&(C=t.run??b),void 0!==t.fetch&&(T=t.fetch??((...t)=>fetch(...t))),void 0!==t.platform&&(M=t.platform),void 0!==t.arm64&&(j=t.arm64),void 0!==t.privileged&&(F=t.privileged),void 0!==t.remote&&(R=t.remote)}export function appPlatform(){return M??process.platform}export function isArm64Machine(){return null!==j?j:"arm64"===process.arch||("win32"===appPlatform()?"ARM64"===process.env.PROCESSOR_ARCHITECTURE||"ARM64"===process.env.PROCESSOR_ARCHITEW6432:"darwin"===appPlatform()&&/\bApple\b/.test(f()[0]?.model??""))}export class DesktopInstallError extends Error{key;constructor(t,e){super(t),this.name="DesktopInstallError",this.key=e}}export const fail=(t,e={})=>{const r={};for(const[t,o]of Object.entries(e))r[t]=o.trim()||E.t("app_update.reason_unknown");throw new DesktopInstallError(E.t(`app_update.error_${t}`,r),t)};export function parseSha256Sums(t){const e=new Map;for(const r of t.split(/\r?\n/)){const t=/^([0-9a-f]{64})\s+\*?(\S.*)$/i.exec(r.trim());t&&e.set(t[2].trim(),t[1].toLowerCase())}return e}async function O(t){const r=e("sha256");for await(const e of s(t))r.update(e);return r.digest("hex")}async function N(t,e){const r=await T(t,{signal:AbortSignal.timeout(e)});if(!r.ok)throw new Error(`${t}: HTTP ${r.status}`);return(await r.text()).trim()}function B(t,e){try{const r=p(w(t,"Contents","Info.plist"),"utf-8");return new RegExp(`<key>${e}</key>\\s*<string>([^<]*)</string>`).exec(r)?.[1]??null}catch{return null}}export function macBundleId(t){return B(t,"CFBundleIdentifier")}export function macAppVersion(t){return/^\d+\.\d+\.\d+/.exec(B(t,"CFBundleShortVersionString")??"")?.[0]??null}function D(t){try{return r(t,o.W_OK),!0}catch{return!1}}const H=t=>`'${t.replace(/'/g,"''")}'`;function W(t,e=12e4){return C("powershell.exe",["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command","[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; "+t],{timeout:e})}export function powerShellError(t){return t.replace(/#< CLIXML/g,"").replace(/_x000D__x000A_/g,"\n").replace(/<\/S>/g,"\n").replace(/<[^>]+>/g,"").replace(/_x[0-9A-F]{4}_/g,"").replace(/&apos;/g,"'").replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").split(/\r?\n/).map(t=>t.trim()).find(t=>t&&!/^(At line|At char|所在位置|\+|~|CategoryInfo|FullyQualifiedErrorId)/i.test(t))??""}export function createDesktopAppInstaller(t){const e=()=>t.mirror()||null,r=()=>t.asset(appPlatform(),isArm64Machine()),o=()=>{const o=appPlatform();return"darwin"!==o&&"win32"!==o?"unsupported_platform":r()?e()?"darwin"===o&&(F??0===process.getuid?.())?"root":"win32"===o&&(null!==F?F:/\\system32\\config\\systemprofile/i.test(process.env.USERPROFILE??"")||!0===process.env.USERNAME?.endsWith("$"))?"system_account":"win32"===o&&(null!==R?R:Boolean(process.env.SSH_CONNECTION||process.env.SSH_CLIENT))?"remote_session":t.extraBlocker?.(o)??null:"no_mirror":"darwin"===o?"unsupported_arch":"unsupported_platform"},s=async(r=8e3)=>{const o=e();if(!o)throw new Error("no mirror");const n=t.versionFile(appPlatform(),isArm64Machine()),s=await N(P(o,n),r),i=/^(\d+\.\d+\.\d+)(\.\d+)?$/.exec(s)?.[1];if(!i)throw new Error(`unexpected ${n}: ${s.slice(0,40)}`);return i},f=()=>{for(const e of["/Applications",w(v(),"Applications")])for(const r of t.macApp.installedNames){const o=w(e,r);if(!a(w(o,"Contents","Info.plist")))continue;const n=macBundleId(o);if(null===n||n===t.macApp.bundleId)return o}return null},$=e=>{const r=C("codesign",["--verify","--deep","--strict",e],{timeout:6e5});0!==r.status&&fail("signature",{reason:(r.stderr||r.stdout).trim().split("\n")[0]||`codesign ${r.status}`});const o=C("codesign",["-dv",e]),n=`${o.stdout}\n${o.stderr}`,s=/^TeamIdentifier=(\S+)/m.exec(n)?.[1],i=/^Identifier=(\S+)/m.exec(n)?.[1];s===t.macApp.teamId&&i===t.macApp.bundleId||fail("signature",{reason:`TeamIdentifier=${s??"?"} Identifier=${i??"?"}`});const a=C("spctl",["-a","-t","exec","-vv",e],{timeout:3e5});0!==a.status&&fail("signature",{reason:(a.stderr||a.stdout).trim().split("\n")[0]||`spctl ${a.status}`})},b=()=>{const e=f();let r=e;if(!r){const e=D("/Applications")?"/Applications":w(v(),"Applications");r=w(e,t.macApp.installedNames[0])}const o=h(r);return(a(o)&&!D(o)||e&&!D(e))&&fail("no_permission",{path:r}),{target:r,existing:e}},M=e=>{const r=W(`$s = Get-AuthenticodeSignature -LiteralPath ${H(e)}; [pscustomobject]@{ status = [string]$s.Status; subject = [string]$s.SignerCertificate.Subject } | ConvertTo-Json -Compress`);let o={};try{o=JSON.parse(r.stdout.trim()||"{}")}catch{}if("Valid"!==o.status||!t.windows.signer.test(o.subject??"")){const t=o.status?`${o.status} ${o.subject??""}`:powerShellError(r.stderr)||powerShellError(r.stdout.trim().startsWith("{")?"":r.stdout)||`exit ${r.status}`;fail("signature",{reason:t.trim()})}},j=()=>{const e=W(`Get-AppxPackage | Where-Object { ${t.windows.packageFilter} } | ForEach-Object { $_.Version.ToString() }`,3e4);return/\d+\.\d+\.\d+/.exec(e.stdout)?.[0]??null};return{assetName:r,blocker:o,latestVersion:s,installedMacApp:f,windowsPackageVersion:j,verifyMacApp:$,verifyWindowsPackage:M,install:async(f={})=>{const v=o();v&&fail("blocked",{reason:E.t(`app_update.reason_${v}`)});const F=e(),R=r();let B;try{B=await s(15e3)}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}const D=e=>P(F,e,t.releaseTag?.(B)),V=_().installMirror?.downloadCounter;let L;try{L=parseSha256Sums(await I(D("SHA256SUMS"),V,t=>N(t,15e3))).get(R)}catch(e){y.logError(`${t.id}: SHA256SUMS`,e)}L||fail("no_checksum",{file:R}),"darwin"===appPlatform()&&b();const U=w(k.getConfigDir(),"downloads",t.id),G=w(U,R),J=l(w(m(),`${t.id}-`));let X=w(J,R),q=!1;try{a(G)&&await O(G)===L?(X=G,f.reusing?.(B)):(f.downloading?.(B),await I(D(R),V,t=>async function(t,e,r){let o;try{o=await T(t,{signal:AbortSignal.timeout(18e5)})}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}o.ok&&o.body||fail("download",{reason:`HTTP ${o.status}`});const n=Number(o.headers.get("content-length"))||0;let s=0,a=-1;const c=new A({transform(t,e,o){s+=t.length;const i=n?Math.floor(100*s/n):-1;i>=0&&i!==a&&(a=i,r?.(i)),o(null,t)}});try{await x(S.fromWeb(o.body),c,i(e))}catch(t){d(e,{force:!0}),fail("download",{reason:t instanceof Error?t.message:String(t)})}}(t,X,f.percent))),f.verifying?.();const e=await O(X);e!==L&&fail("checksum",{file:R,expected:L.slice(0,12),actual:e.slice(0,12)}),f.installing?.();const r="darwin"===appPlatform()?await(async(e,r)=>{const o=w(r,"mnt");c(o,{recursive:!0});const n=C("hdiutil",["attach","-nobrowse","-readonly","-noautoopen","-mountpoint",o,e],{timeout:3e5,input:"Y\n"});0!==n.status&&fail("mount",{reason:(n.stderr||n.stdout).trim().split("\n").pop()??""});let s=null;try{const e=w(o,t.macApp.source);a(e)||fail("bad_package",{reason:t.macApp.source});const{target:r,existing:n}=b(),i=h(r),l=g(r);c(i,{recursive:!0}),s=w(i,`.${l}.${process.pid}.new`),d(s,{recursive:!0,force:!0});const p=C("ditto",[e,s],{timeout:9e5});0!==p.status&&fail("copy",{reason:(p.stderr||p.stdout).trim().split("\n")[0]??""}),$(s);const f=macAppVersion(s);f||fail("bad_package",{reason:"Info.plist"});const m=w(i,`.${l}.${process.pid}.old`);try{n&&u(n,m)}catch(t){throw["EPERM","EACCES"].includes(t.code??"")&&fail("app_management",{path:n}),t}try{u(s,r)}catch(t){throw n&&u(m,n),t}return s=null,d(m,{recursive:!0,force:!0}),{version:f,location:r}}finally{s&&d(s,{recursive:!0,force:!0}),C("hdiutil",["detach",o,"-force"],{timeout:6e4})}})(X,J):await(async(t,e)=>{M(t);const r=W(`$ErrorActionPreference = 'Stop'; Add-AppxPackage -Path ${H(t)} -ForceApplicationShutdown`,9e5);if(0!==r.status){const o=powerShellError(r.stderr)||powerShellError(r.stdout)||`Add-AppxPackage ${r.status}`;/0x80073D28/i.test(`${r.stderr}\n${r.stdout}`)||fail("install",{reason:o}),await(e.elevate?.())||fail("needs_admin");const n=(t=>{const e=w(h(t),"elevated-install.log"),r=`$ErrorActionPreference = 'Stop'; try { Add-AppxPackage -Path ${H(t)} -ForceApplicationShutdown; exit 0 } catch { [IO.File]::WriteAllText(${H(e)}, $_.Exception.Message, [Text.Encoding]::UTF8); exit 1 }`,o=W(`$ErrorActionPreference = 'Stop'; $p = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '${Buffer.from(r,"utf16le").toString("base64")}') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`,12e5);if(0===o.status)return null;let n="";try{n=p(e,"utf-8").trim()}catch{}return n||powerShellError(o.stderr)||powerShellError(o.stdout)||`exit ${o.status}`})(t);n&&fail("install",{reason:n}),j()||fail("other_account")}const o=j();return o||fail("install",{reason:"Get-AppxPackage"}),{version:o,location:"MSIX"}})(X,f);return q=!0,r}catch(t){const e=t instanceof DesktopInstallError?t.key:void 0;throw X!==G&&a(X)&&!["checksum","signature","bad_package","mount"].includes(e??"")&&function(t,e,r){try{d(e,{recursive:!0,force:!0}),c(e,{recursive:!0});try{u(t,r)}catch{n(t,r)}}catch(t){y.logError("desktop-app: keep package",t),d(e,{recursive:!0,force:!0})}}(X,U,G),X===G&&["signature","bad_package","mount"].includes(e??"")&&d(U,{recursive:!0,force:!0}),t}finally{q&&d(U,{recursive:!0,force:!0}),d(J,{recursive:!0,force:!0})}}}}
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as t}from"fs";import{join as o}from"path";import*as r from"@iarna/toml";import{logger as n}from"../../utils/logger.js";import{PROVIDER_KEY as i}from"../brand.js";import{backupBeforeWrite as s,atomicWriteFile as l}from"./backup.js";import{pickModel as a,toolHome as d,withV1 as m}from"./types.js";import{i18n as f}from"../i18n.js";import{getToolDefaultModel as g}from"../model-registry.js";import{gatewayModelSpecs as u,modelBlurb as c,modelFamily as p,prettyModelName as y}from"../gateway-models.js";import{brandInstallScripts as k,mirrorInstallCommand as b}from"./tool-installs.js";import{OFFICIAL_PLUGINS as h}from"./claude-plugins.js";const j=()=>o(o(d(),".grok"),"config.toml"),_=i,w=`${i}-`,A=["low","medium","high","xhigh"];let v=null;const C=e=>`${w}${e}`,M=e=>e===_||e.startsWith(w),x={command:"grok",npmPackage:"@xai-official/grok",npmMirrorTagStale:!0,mirrorScript:k("grok"),native:{binDir:[".grok","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".grok","downloads"]]}};function S(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function E(e,t){return S(e[t])||(e[t]={}),e[t]}function O(){if(!e(j()))return{};const o=t(j(),"utf-8");if(!o.trim())return{};try{const e=r.parse(o);if(!S(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.grok/config.toml cannot be parsed; back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function P(e){return S(e.model)?Object.entries(e.model).filter(e=>M(e[0])&&S(e[1])):[]}function N(e,t){let o=u("grok",e.catalog?.clientModels);if(e.catalog){const t=new Set(e.catalog.models);o=o.filter(e=>t.has(e.id))}return o.some(e=>e.id===t)?o:[{id:t},...o]}function $(e,t,o){E(e,"endpoints").models_base_url=m(t.baseUrl);const r=E(e,"model"),n=new Map(P(e));for(const e of n.keys())delete r[e];for(const e of o){const o=C(e.id),i={...n.get(o)??{},name:e.name??y(e.id),model:e.id,base_url:m(t.baseUrl),api_key:t.apiKey,context_window:e.contextWindow??5e5,api_backend:"responses",reasoning_summary:"concise"};"grok"===p(e.id)&&(i.supports_reasoning_effort=!0,i.reasoning_efforts=A);const s=c(e.description);s?i.description=s:delete i.description,r[o]=i}}export const grokAdapter={name:"grok",displayName:"Grok Build",command:"grok",installCommand:b(x),installSpec:x,configPath:j(),wantsModelCatalog:!0,loadPrivateConfig(e){const t=O(),o=a(e.models,g("grok")),n=N(e,o);$(t,e,n);const i=e=>e.name??y(e.id);v={names:n.map(i),defaultName:i(n.find(e=>e.id===o))},E(t,"models").default=C(o),function(e){const t=E(e,"plugins"),o=e=>Array.isArray(e)?e.map(String):[],r=o(t.enabled),n=o(t.disabled),i=h.filter(e=>!r.includes(e)&&!n.includes(e));i.length>0&&(t.disabled=[...n,...i])}(t),s(j()),l(j(),r.stringify(t))},unloadPrivateConfig(){if(!e(j()))return;let t;try{t=O()}catch(e){return void n.logError("grokAdapter.unloadPrivateConfig",e)}if(S(t.model)){for(const[e]of P(t))delete t.model[e];0===Object.keys(t.model).length&&delete t.model}S(t.models)&&"string"==typeof t.models.default&&M(t.models.default)&&(delete t.models.default,0===Object.keys(t.models).length&&delete t.models),S(t.endpoints)&&(delete t.endpoints.models_base_url,0===Object.keys(t.endpoints).length&&delete t.endpoints),function(e){if(!S(e.plugins)||!Array.isArray(e.plugins.disabled))return;const t=e.plugins.disabled.filter(e=>!h.includes(e));t.length>0?e.plugins.disabled=t:delete e.plugins.disabled,0===Object.keys(e.plugins).length&&delete e.plugins}(t),s(j()),l(j(),r.stringify(t))},detectCurrentConfig(){try{const e=O(),t=P(e),o=S(e.models)?e.models.default:void 0,r=t.find(([e])=>e===o)?.[1]??t[0]?.[1],n=r?r.api_key:null;return{apiKey:"string"==typeof n&&n.trim()?n:null}}catch{return{apiKey:null}}},listedModels(){try{const e=P(O());return e.length>0?e.map(([,e])=>String(e.model)):null}catch{return null}},refreshModelMenu(e){const t=O(),o=E(t,"models"),n="string"==typeof o.default?o.default:void 0,i=n?P(t).find(([e])=>e===n)?.[1].model:void 0,d="string"==typeof i&&n!==_&&e.catalog.models.includes(i)?i:a(e.models,g("grok")),m=r.stringify(t);$(t,e,N(e,d)),(void 0===n||M(n))&&(o.default=C(d)),r.stringify(t)!==m&&(s(j()),l(j(),r.stringify(t)))},applyNotes:()=>v?[{kind:"summary",text:f.t("tools.grok.menu_written",{count:String(v.names.length),list:v.names.join(f.t("tools.grok.list_separator")),default:v.defaultName})}]:[],getConfig(){try{return O()}catch(e){return n.logError("grokAdapter.getConfig",e),null}}};
1
+ import{existsSync as e,readFileSync as t}from"fs";import{join as o}from"path";import*as r from"@iarna/toml";import{logger as n}from"../../utils/logger.js";import{PROVIDER_KEY as i}from"../brand.js";import{backupBeforeWrite as l,atomicWriteFile as s}from"./backup.js";import{pickModel as a,toolHome as d,withV1 as m}from"./types.js";import{i18n as f}from"../i18n.js";import{getToolDefaultModel as g}from"../model-registry.js";import{gatewayModelSpecs as c,modelBlurb as u,modelFamily as p,prettyModelName as y}from"../gateway-models.js";import{brandInstallScripts as k,mirrorInstallCommand as b}from"./tool-installs.js";import{OFFICIAL_PLUGINS as h}from"./claude-plugins.js";const j=()=>o(o(d(),".grok"),"config.toml"),_=i,w=`${i}-`,A=["low","medium","high","xhigh"];let v=null;const C=e=>`${w}${e}`,M=e=>e===_||e.startsWith(w),x={command:"grok",npmPackage:"@xai-official/grok",npmMirrorTagStale:!0,mirrorScript:k("grok"),native:{binDir:[".grok","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".grok","downloads"]]}};function S(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function E(e,t){return S(e[t])||(e[t]={}),e[t]}function O(){if(!e(j()))return{};const o=t(j(),"utf-8");if(!o.trim())return{};try{const e=r.parse(o);if(!S(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.grok/config.toml cannot be parsed; back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function P(e){return S(e.model)?Object.entries(e.model).filter(e=>M(e[0])&&S(e[1])):[]}function N(e,t){let o=c("grok",e.catalog?.clientModels,e.catalog?.clientPersonal);if(e.catalog){const t=new Set(e.catalog.models);o=o.filter(e=>t.has(e.id))}return o.some(e=>e.id===t)?o:[{id:t},...o]}function $(e,t,o){E(e,"endpoints").models_base_url=m(t.baseUrl);const r=E(e,"model"),n=new Map(P(e));for(const e of n.keys())delete r[e];for(const e of o){const o=C(e.id),i={...n.get(o)??{},name:e.name??y(e.id),model:e.id,base_url:m(t.baseUrl),api_key:t.apiKey,context_window:e.contextWindow??5e5,api_backend:"responses",reasoning_summary:"concise"};"grok"===p(e.id)&&(i.supports_reasoning_effort=!0,i.reasoning_efforts=A);const l=u(e.description);l?i.description=l:delete i.description,r[o]=i}}export const grokAdapter={name:"grok",displayName:"Grok Build",command:"grok",installCommand:b(x),installSpec:x,configPath:j(),wantsModelCatalog:!0,loadPrivateConfig(e){const t=O(),o=a(e.models,g("grok")),n=N(e,o);$(t,e,n);const i=e=>e.name??y(e.id);v={names:n.map(i),defaultName:i(n.find(e=>e.id===o))},E(t,"models").default=C(o),function(e){const t=E(e,"plugins"),o=e=>Array.isArray(e)?e.map(String):[],r=o(t.enabled),n=o(t.disabled),i=h.filter(e=>!r.includes(e)&&!n.includes(e));i.length>0&&(t.disabled=[...n,...i])}(t),l(j()),s(j(),r.stringify(t))},unloadPrivateConfig(){if(!e(j()))return;let t;try{t=O()}catch(e){return void n.logError("grokAdapter.unloadPrivateConfig",e)}if(S(t.model)){for(const[e]of P(t))delete t.model[e];0===Object.keys(t.model).length&&delete t.model}S(t.models)&&"string"==typeof t.models.default&&M(t.models.default)&&(delete t.models.default,0===Object.keys(t.models).length&&delete t.models),S(t.endpoints)&&(delete t.endpoints.models_base_url,0===Object.keys(t.endpoints).length&&delete t.endpoints),function(e){if(!S(e.plugins)||!Array.isArray(e.plugins.disabled))return;const t=e.plugins.disabled.filter(e=>!h.includes(e));t.length>0?e.plugins.disabled=t:delete e.plugins.disabled,0===Object.keys(e.plugins).length&&delete e.plugins}(t),l(j()),s(j(),r.stringify(t))},detectCurrentConfig(){try{const e=O(),t=P(e),o=S(e.models)?e.models.default:void 0,r=t.find(([e])=>e===o)?.[1]??t[0]?.[1],n=r?r.api_key:null;return{apiKey:"string"==typeof n&&n.trim()?n:null}}catch{return{apiKey:null}}},listedModels(){try{const e=P(O());return e.length>0?e.map(([,e])=>String(e.model)):null}catch{return null}},refreshModelMenu(e){const t=O(),o=E(t,"models"),n="string"==typeof o.default?o.default:void 0,i=n?P(t).find(([e])=>e===n)?.[1].model:void 0,d="string"==typeof i&&n!==_&&e.catalog.models.includes(i)?i:a(e.models,g("grok")),m=r.stringify(t);$(t,e,N(e,d)),(void 0===n||M(n))&&(o.default=C(d)),r.stringify(t)!==m&&(l(j()),s(j(),r.stringify(t)))},applyNotes:()=>v?[{kind:"summary",text:f.t("tools.grok.menu_written",{count:String(v.names.length),list:v.names.join(f.t("tools.grok.list_separator")),default:v.defaultName})}]:[],getConfig(){try{return O()}catch(e){return n.logError("grokAdapter.getConfig",e),null}}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.156",
3
+ "version": "0.0.158",
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",