@vibeapi/api-helper 0.0.128 → 0.0.130

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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * One real streaming request, the way the coding tools talk to the gateway: how long until the first
3
+ * token arrives, and whether the stream ends the way the protocol says it should. A stream that stops
4
+ * without its terminator (`message_stop` / `[DONE]`) is what users see as answers cut off halfway.
5
+ */
6
+ export interface StreamCheckResult {
7
+ protocol: 'anthropic' | 'openai';
8
+ model: string;
9
+ ok: boolean;
10
+ status?: number;
11
+ /** Request sent → first text token. */
12
+ first_token_ms?: number;
13
+ total_ms: number;
14
+ /** Saw the protocol's end marker. */
15
+ completed: boolean;
16
+ chunks: number;
17
+ /** Why it is not ok; `detail` carries the gateway's own message when there is one. */
18
+ failure?: 'http' | 'stream_error' | 'incomplete' | 'no_text' | 'timeout' | 'network';
19
+ detail?: string;
20
+ }
21
+ export declare const STREAM_TIMEOUT_MS = 60000;
22
+ /** Anthropic Messages for Claude models (what Claude Code sends), Chat Completions for the rest. */
23
+ export declare function streamProtocol(model: string): StreamCheckResult['protocol'];
24
+ export declare function runStreamCheck(options: {
25
+ baseUrl: string;
26
+ apiKey: string;
27
+ model: string;
28
+ fetchFn?: typeof fetch;
29
+ timeoutMs?: number;
30
+ now?: () => number;
31
+ }): Promise<StreamCheckResult>;
@@ -0,0 +1 @@
1
+ import{isClaudeModel as e}from"./model-catalog.js";const t="Reply with the single word OK.";export const STREAM_TIMEOUT_MS=6e4;export function streamProtocol(t){return e(t)?"anthropic":"openai"}export async function runStreamCheck(e){const{baseUrl:o,apiKey:r,fetchFn:n=fetch,timeoutMs:a=STREAM_TIMEOUT_MS,now:s=Date.now}=e,i=e.model.replace(/\[[^\]]*\]$/,""),c=streamProtocol(i),l=function(e,o,r,n){return"anthropic"===e?{url:`${o}/v1/messages`,headers:{"x-api-key":r,Authorization:`Bearer ${r}`,"anthropic-version":"2023-06-01","content-type":"application/json"},body:{model:n,max_tokens:16,stream:!0,messages:[{role:"user",content:t}]}}:{url:`${o}/v1/chat/completions`,headers:{Authorization:`Bearer ${r}`,"content-type":"application/json"},body:{model:n,max_tokens:16,stream:!0,messages:[{role:"user",content:t}]}}}(c,o.replace(/\/+$/,""),r,i),d={protocol:c,model:i,ok:!1,total_ms:0,completed:!1,chunks:0},u=new AbortController,m=setTimeout(()=>u.abort(),a),p=s();try{const e=await n(l.url,{method:"POST",headers:l.headers,body:JSON.stringify(l.body),signal:u.signal});if(d.status=e.status,!e.ok||!e.body){const t=await e.text().catch(()=>"");return d.failure="http",d.detail=`HTTP ${e.status}${t?`: ${t.slice(0,300)}`:""}`,d}const t=new TextDecoder,o=e.body.getReader();let r="";const a=e=>{const t=e.trim();if(!t.startsWith("data:"))return;d.chunks+=1;const o=function(e,t){if("openai"===e&&"[DONE]"===t)return{text:"",end:!0};let o;try{o=JSON.parse(t)}catch{return{text:"",end:!1}}const r=o.error??("error"===o.type?o:void 0);if(r)return{text:"",end:!1,error:"string"==typeof r.message?r.message:JSON.stringify(r)};if("anthropic"===e){if("message_stop"===o.type)return{text:"",end:!0};const e=o.delta;return"content_block_delta"===o.type&&e?{text:"string"==typeof e.text?e.text:"string"==typeof e.thinking?e.thinking:"",end:!1}:{text:"",end:!1}}const n=o.choices?.[0],a=n?.delta?.content??n?.delta?.reasoning_content;return{text:"string"==typeof a?a:"",end:!1}}(c,t.slice(5).trim());o.error&&(d.failure="stream_error",d.detail=o.error.slice(0,300)),o.text&&void 0===d.first_token_ms&&(d.first_token_ms=s()-p),o.end&&(d.completed=!0)};for(;;){const{value:e,done:n}=await o.read();if(n)break;let s;for(r+=t.decode(e,{stream:!0});(s=r.indexOf("\n"))>=0;)a(r.slice(0,s)),r=r.slice(s+1)}return a(r+t.decode()),d.failure||d.completed||(d.failure="incomplete"),d.failure||void 0!==d.first_token_ms||(d.failure="no_text"),d.ok=!d.failure,d}catch(e){return d.failure=u.signal.aborted?"timeout":"network",u.signal.aborted||(d.detail=e.message),d}finally{clearTimeout(m),d.total_ms=s()-p}}
@@ -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 i,prettyModelName as r,vendorNames as l}from"../gateway-models.js";import{toolHome as d}from"./types.js";import{gatewayAddresses as c,normalizeBaseUrl as u}from"../gateway-addresses.js";import{configManager as p}from"../config.js";import{spawnSync as m}from"child_process";import{existsSync as g,readFileSync as f}from"fs";import{detectManagedOverrides as _}from"./claude-managed.js";import{installOfficialPlugins as C}from"./claude-plugins.js";import{findInstalls as h,mirrorInstallCommand as A,brandInstallScripts as y}from"./tool-installs.js";import{updateNativeFromMirror as P}from"./claude-native-mirror.js";import{brandEnv as S}from"../brand.js";const j=e(d(),".claude","settings.json"),k=e(d(),".claude.json");let T,v=null,O=()=>0===m("security",["find-generic-password","-s","Claude Code-credentials"],{stdio:"ignore"}).status;export function setClaudeKeychainProbeForTests(e){O=e}export function claudeAccountLoginPresent(){const t=process.env.CLAUDE_CONFIG_DIR?.trim()||e(d(),".claude");if(g(e(t,".credentials.json")))return!0;if("darwin"!==process.platform||S("TEST_HOME"))return!1;try{return O()}catch{return!1}}function N(){try{const t=JSON.parse(f(e(d(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function x(e){if(!e)return;const t=new Set(e.models);return i("claude-code",e.clientModels).filter(e=>t.has(e.id))}const H={command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>P(N())},mirrorScript:y("claude-code"),brew:"claude-code"};export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:A(H),upgradeCommand:"claude update",installSpec:H,upgradeHints:e=>"stable"===N()?[s.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:j,wantsModelCatalog:!0,loadPrivateConfig(t){n(j),n(k),T=x(t.catalog),o.loadPrivateConfig(t.apiKey,t.models,t.catalog?.models,T);const s=e(d(),".local","bin","win32"===process.platform?"claude.exe":"claude"),a=h(claudeCodeAdapter.installSpec)[0]?.path??(g(s)?s:null);v=a?C(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&&(c().some(e=>e.url===s)||s===u(p.getBaseUrl()))},unloadPrivateConfig(e){const t=o.getSettings().env??{};Boolean(t.ANTHROPIC_AUTH_TOKEN||t.ANTHROPIC_BASE_URL)&&!claudeCodeAdapter.holdsOurConfig(e?.savedKeys??[])||(n(j),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(j),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,x(e.catalog));JSON.stringify(a)!==JSON.stringify(s)&&(n(j),o.saveSettings(a))},applyNotes(){const e=[];v?.installed.length&&(e.push({kind:"summary",text:s.t("tools.claude_code.plugins_installed",{count:String(v.installed.length)})}),e.push({kind:"detail",text:v.installed.join(", ")})),"git"===v?.skipped?e.push({kind:"action",text:s.t("tools.claude_code.plugins_need_git")}):("marketplace"===v?.skipped||v?.failed.length)&&e.push({kind:"action",text:s.t("tools.claude_code.plugins_failed")}),T&&T.length>0&&(e.push({kind:"summary",text:s.t("tools.claude_code.gateway_models",{count:String(T.length),vendors:l(T.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:T.map(e=>e.name||r(e.id)).join(", ")})),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===m("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 _())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:()=>[j,k],forceResetPaths:()=>[j]};
1
+ import{join as e}from"path";import{applyGatewayModels as t,claudeCodeManager as o}from"../claude-code-manager.js";import{backupBeforeWrite as s}from"./backup.js";import{i18n as n}from"../i18n.js";import{baseModelId as a}from"../model-catalog.js";import{gatewayModelSpecs as i,prettyModelName as r,vendorNames as l}from"../gateway-models.js";import{toolHome as d}from"./types.js";import{gatewayAddresses as c,normalizeBaseUrl as u}from"../gateway-addresses.js";import{configManager as m}from"../config.js";import{spawnSync as p}from"child_process";import{existsSync as g,readFileSync as f}from"fs";import{detectManagedOverrides as _}from"./claude-managed.js";import{installOfficialPlugins as C}from"./claude-plugins.js";import{findInstalls as h,mirrorInstallCommand as A,brandInstallScripts as j}from"./tool-installs.js";import{updateNativeFromMirror as y}from"./claude-native-mirror.js";import{brandEnv as P}from"../brand.js";import{claudeFormat as S,claudeProjectFiles as k}from"../sessions/formats.js";const v=e(d(),".claude","settings.json"),O=e(d(),".claude.json");let T,N=null,x=()=>0===p("security",["find-generic-password","-s","Claude Code-credentials"],{stdio:"ignore"}).status;export function setClaudeKeychainProbeForTests(e){x=e}export function claudeAccountLoginPresent(){const t=process.env.CLAUDE_CONFIG_DIR?.trim()||e(d(),".claude");if(g(e(t,".credentials.json")))return!0;if("darwin"!==process.platform||P("TEST_HOME"))return!1;try{return x()}catch{return!1}}function H(){try{const t=JSON.parse(f(e(d(),".claude","settings.json"),"utf-8"));return"string"==typeof t.autoUpdatesChannel?t.autoUpdatesChannel:"latest"}catch{return"latest"}}function b(e){if(!e)return;const t=new Set(e.models);return i("claude-code",e.clientModels).filter(e=>t.has(e.id))}const w={command:"claude",npmPackage:"@anthropic-ai/claude-code",native:{binDir:[".local","bin"],updateArgs:["update"],dataDirs:[[".local","share","claude"]],mirrorUpdate:()=>y(H())},mirrorScript:j("claude-code"),brew:"claude-code"};export const claudeCodeAdapter={name:"claude-code",displayName:"Claude Code",command:"claude",installCommand:A(w),upgradeCommand:"claude update",installSpec:w,upgradeHints:e=>"stable"===H()?[n.t("tools.claude_code.stable_channel",{min:e})]:[],configPath:v,sessions:{format:S,files:()=>k(e(process.env.CLAUDE_CONFIG_DIR?.trim()||e(d(),".claude"),"projects")),resumeCommand:e=>({command:"claude",args:["--resume",e]})},wantsModelCatalog:!0,loadPrivateConfig(t){s(v),s(O),T=b(t.catalog),o.loadPrivateConfig(t.apiKey,t.models,t.catalog?.models,T);const n=e(d(),".local","bin","win32"===process.platform?"claude.exe":"claude"),a=h(claudeCodeAdapter.installSpec)[0]?.path??(g(n)?n:null);N=a?C(a):null},holdsOurConfig(e){const t=o.getSettings().env??{},s="string"==typeof t.ANTHROPIC_AUTH_TOKEN?t.ANTHROPIC_AUTH_TOKEN.trim():"",n="string"==typeof t.ANTHROPIC_BASE_URL?u(t.ANTHROPIC_BASE_URL):null;return!(!s||!e.includes(s))||null!==n&&(c().some(e=>e.url===n)||n===u(m.getBaseUrl()))},unloadPrivateConfig(e){const t=o.getSettings().env??{};Boolean(t.ANTHROPIC_AUTH_TOKEN||t.ANTHROPIC_BASE_URL)&&!claudeCodeAdapter.holdsOurConfig(e?.savedKeys??[])||(s(v),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=>(s(v),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 n=o.getSettings(),a=t(n,b(e.catalog));JSON.stringify(a)!==JSON.stringify(n)&&(s(v),o.saveSettings(a))},applyNotes(){const e=[];N?.installed.length&&(e.push({kind:"summary",text:n.t("tools.claude_code.plugins_installed",{count:String(N.installed.length)})}),e.push({kind:"detail",text:N.installed.join(", ")})),"git"===N?.skipped?e.push({kind:"action",text:n.t("tools.claude_code.plugins_need_git")}):("marketplace"===N?.skipped||N?.failed.length)&&e.push({kind:"action",text:n.t("tools.claude_code.plugins_failed")}),T&&T.length>0&&(e.push({kind:"summary",text:n.t("tools.claude_code.gateway_models",{count:String(T.length),vendors:l(T.map(e=>e.id)).join(" / ")})}),e.push({kind:"detail",text:T.map(e=>e.name||r(e.id)).join(", ")})),"win32"!==process.platform||process.env.CLAUDE_CODE_GIT_BASH_PATH||0===p("where",["git"],{windowsHide:!0,stdio:"ignore"}).status||e.push({kind:"action",text:n.t("tools.claude_code.no_git_bash")}),claudeAccountLoginPresent()&&e.push({kind:"detail",text:n.t("tools.claude_code.account_login_overridden")});for(const t of _())e.push({kind:"action",text:n.t("tools.claude_code.managed_override",{source:t.source,keys:t.keys.join(", ")})});for(const t of o.getLastTierFallbacks())e.push({kind:"detail",text:n.t("tools.claude_code.tier_fallback",{tier:t.tier,from:t.from,to:t.to})});return e},backupPaths:()=>[v,O],forceResetPaths:()=>[v]};
@@ -1 +1 @@
1
- import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import p from"chalk";import{logger as u}from"../../utils/logger.js";import{track as f}from"../telemetry.js";import{ensureDesktopCli as m}from"./claude-desktop-cli.js";import{i18n as g}from"../i18n.js";import{BRAND_NAME as y,brand as h,brandEnv as k,CLI_NAME as _}from"../brand.js";import{backupBeforeWrite as b,atomicWriteFile as D}from"./backup.js";import{toolHome as A}from"./types.js";import{canRestartDesktop as C,isDesktopRunning as w,restartDesktop as E,WINDOWS_PACKAGE_FAMILIES as M}from"./desktop-process.js";import{setUserRegistryElevated as v}from"./elevated-reg.js";import{UserActionNeeded as O}from"./types.js";import{baseModelId as x,isClaudeModel as S}from"../model-catalog.js";import{getDefaultModels as P}from"../model-registry.js";const j=y;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const L=/^[a-f0-9-]{36}$/;function I(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=A(),n=Boolean(k("TEST_HOME")?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return M.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const T=()=>a(claudeDesktopUserDataDir(),"configLibrary"),$=()=>a(T(),"_meta.json"),R=e=>a(T(),`${e}.json`),G=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function N(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return I(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function U(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),b(n),D(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){u.logError("claude-desktop.writeJson.chmod",e)}}function K(){const e=N($());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>I(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function F(e){return e?.entries.find(e=>e.name===j&&L.test(e.id))}const H={opus:0,fable:1,sonnet:2,haiku:3};function B(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=B(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function V(e){return B(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>x(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(S)){i=[...new Set(t.models.filter(S).map(x))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=x(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>H[V(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=B(e),s=B(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map,d=new Set(Object.values(P()).map(e=>x(e||"")));for(const e of n){const t=V(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=V(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let W=null,J=null,z=null,Z=null,q=null;function X(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const Y={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0,banner:{enabled:!1}};function Q(e){return Object.fromEntries(Object.entries(Y).filter(([t])=>!e(t)))}const ee="SOFTWARE\\Policies\\Claude",te=`HKCU\\${ee}`,oe="HKLM\\SOFTWARE\\Policies\\Claude",ne=`${_}-claude-policy.reg`,re=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let se=re;export function setRegRunnerForTests(e){se=e??re}function ie(){return"win32"===process.platform}function ae(e){let t;try{t=se(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function le(){if(null===ae(te))return;const e=a(claudeDesktopUserDataDir(),ne);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),b(e),se(["export",te,e,"/y"])}catch(e){u.logError("claude-desktop.snapshotPolicy",e)}}function ce(e){le();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];se(["add",te,"/v",t,"/t",e,"/d",n,"/f"])}}function de(e){const t=ae(te);if(!t)return;le();let o=t.size;for(const n of e)t.has(n)&&(se(["delete",te,"/v",n,"/f"]),o-=1);o<=0&&se(["delete",te,"/f"])}const pe=[...Object.keys(X({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(Y)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return ae(oe)?oe:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return u.logError("claude-desktop.detectManagedConfig",e),null}}function ue(){const e=A();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...M.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))}const fe=/\d+\.\d+\.\d+/;function me(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}function ge(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(A(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?fe.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(A(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return me(t)}const o=M.map(e=>`'${e}'`).join(",");return me(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>fe.exec(e)?.[0]??[]))}if("linux"===process.platform)try{const e=c("dpkg-query",["-W","-f=${Version}","claude-desktop"],{encoding:"utf-8",timeout:5e3,stdio:["ignore","pipe","ignore"]});return fe.exec(e)?.[0]??null}catch{return null}return null}const ye="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",he="darwin"!==process.platform&&"win32"!==process.platform||!h().installMirror?.claudeDesktop?"https://claude.ai/download":h().installMirror.claudeDesktop,ke="darwin"===process.platform?`open ${he}`:"win32"===process.platform?`start ${he}`:`xdg-open ${he}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:ye,installCommand:ke,configPath:$(),isInstalled:ue,installedVersion:ge,prepareRuntime:async function(){if(!["win32","darwin","linux"].includes(process.platform)||!ue())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(p.gray(" "+g.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?ge():null,mirrorRepo:h().installMirror?.claudeDesktop?.replace(/\/-\/releases\/latest\/?$/,"")??null});return f("desktop-code-cli","skipped"===t.status?"skip":"ok",{duration_ms:Date.now()-e,detail:{...t}}),"installed"!==t.status?[]:[{kind:"summary",text:g.t("tools.claude_desktop.code_cli_installed",{version:t.version})}]}catch(t){return u.logError("claude-desktop.prepareCodeTabCli",t),f("desktop-code-cli","fail",{duration_ms:Date.now()-e,error:t}),[{kind:"action",text:g.t("tools.claude_desktop.code_cli_failed",{reason:t instanceof Error?t.message:String(t)})}]}},runningApp:C()?{isRunning:w,restart:E}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const o=K()??{appliedId:"",entries:[]};let n=F(o);n||(n={id:l(),name:j},o.entries.push(n));const r=X(e);J=!0===r.disableDeploymentModeChooser,W={models:r.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const s=N(R(n.id))??{};U(R(n.id),{...Q(e=>e in s),...s,...r}),o.appliedId=n.id,U($(),o);const i=N(G())??{};if("3p"!==i.deploymentMode&&U(G(),{...i,deploymentMode:"3p"}),Z=null,q=null,z=null,ie()){const o=ae(te),n=pe.filter(e=>o?.has(e)),s={...Q(e=>o?.has(e)??!1),...r},i=!0===e.settings?.force_registry,l=!i&&function(){const e=!Boolean(k("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(A(),"AppData","Local");return!!t(a(e,"AnthropicClaude"))||M.some(o=>t(a(e,"Packages",o,"LocalCache","Local","Claude-3p")))}();try{l?de(pe):ce(s),z=l?"removed":"written"}catch(e){u.logError("claude-desktop.policy",e),Z=e instanceof Error?e.message.trim():String(e),(n.length>0||i)&&(q=l?{action:"remove",values:Object.fromEntries(n.map(e=>[e,null])),deleteKey:n.length===o.size}:{action:"write",values:s,deleteKey:!1})}}},pendingElevation(){const e=q;return e?{prompt:g.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!v(ee,e.values,e.deleteKey))return!1;const t=ae(te),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(q=null,Z=null),o}}:null},unloadPrivateConfig(){if(ie())try{de(pe)}catch(e){u.logError("claude-desktop.removePolicy",e)}const e=K(),t=F(e);if(!e||!t)return;b(R(t.id)),s(R(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(b($()),s($(),{force:!0})):U($(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=K(),t=F(e);if(!t||e?.appliedId!==t.id)return null;const o=N(R(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=K(),o=F(t);if(!o||t?.appliedId!==o.id)return;const n=N(R(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)){if(ie()&&ae(te)?.has("inferenceModels"))try{ce({inferenceModels:r})}catch(e){throw u.logError("claude-desktop.refreshModelMenu registry",e),new O(g.t("tools.claude_desktop.refresh_needs_admin"))}U(R(o.id),{...n,inferenceModels:r})}},detectCurrentConfig(){try{if(ie()){const e=ae(te)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=F(K());if(!e)return{apiKey:null};const t=N(R(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return u.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=K(),t=F(e);return{meta:e,applied:t?N(R(t.id)):null,desktop:N(G()),...ie()?{policy:Object.fromEntries(ae(te)??[])}:{}}}catch(e){return u.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;ie()&&ae(te)?.has("inferenceModels")&&(le(),se(["delete",te,"/v","inferenceModels","/f"]),e=!0);const t=F(K()),o=t?N(R(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;U(R(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(W&&W.models.length>0){const{models:t,source:o,groups:n}=W;e.push({kind:"summary",text:g.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:g.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==J&&e.push({kind:"detail",text:g.t(J?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),C()&&!w()||e.push({kind:"action",text:g.t("tools.claude_desktop.restart_hint")}),q?(e.push({kind:"action",text:g.t(`tools.claude_desktop.policy_stale_${q.action}`)}),e.push({kind:"detail",text:Z??""})):Z?(e.push({kind:"detail",text:g.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:Z})):ie()&&z&&e.push({kind:"detail",text:g.t(`tools.claude_desktop.registry_${z}`,{key:te})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:g.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[$(),G()];try{const t=F(K());t&&e.push(R(t.id))}catch{}return e},backupPaths(){const e=[$(),G()];ie()&&e.push(a(claudeDesktopUserDataDir(),ne));try{const t=F(K());t&&e.push(R(t.id))}catch{}return e}};
1
+ import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import p from"chalk";import{logger as u}from"../../utils/logger.js";import{track as f}from"../telemetry.js";import{ensureDesktopCli as m}from"./claude-desktop-cli.js";import{i18n as g}from"../i18n.js";import{BRAND_NAME as y,brand as h,brandEnv as k,CLI_NAME as _}from"../brand.js";import{backupBeforeWrite as D,atomicWriteFile as b}from"./backup.js";import{toolHome as A}from"./types.js";import{canRestartDesktop as C,isDesktopRunning as w,restartDesktop as E,WINDOWS_PACKAGE_FAMILIES as M}from"./desktop-process.js";import{setUserRegistryElevated as v}from"./elevated-reg.js";import{UserActionNeeded as O}from"./types.js";import{baseModelId as x,isClaudeModel as S}from"../model-catalog.js";import{getDefaultModels as P}from"../model-registry.js";import{claudeFormat as j,nestedClaudeProjectFiles as L}from"../sessions/formats.js";const I=y;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const T=/^[a-f0-9-]{36}$/;function $(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=A(),n=Boolean(k("TEST_HOME")?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return M.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const R=()=>a(claudeDesktopUserDataDir(),"configLibrary"),G=()=>a(R(),"_meta.json"),N=e=>a(R(),`${e}.json`),U=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function K(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return $(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function F(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),D(n),b(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){u.logError("claude-desktop.writeJson.chmod",e)}}function H(){const e=K(G());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>$(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function B(e){return e?.entries.find(e=>e.name===I&&T.test(e.id))}const V={opus:0,fable:1,sonnet:2,haiku:3};function W(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=W(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function J(e){return W(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>x(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(S)){i=[...new Set(t.models.filter(S).map(x))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=x(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>V[J(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=W(e),s=W(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map,d=new Set(Object.values(P()).map(e=>x(e||"")));for(const e of n){const t=J(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=J(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let z=null,Z=null,q=null,X=null,Y=null;function Q(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const ee={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0,banner:{enabled:!1}};function te(e){return Object.fromEntries(Object.entries(ee).filter(([t])=>!e(t)))}const oe="SOFTWARE\\Policies\\Claude",ne=`HKCU\\${oe}`,re="HKLM\\SOFTWARE\\Policies\\Claude",se=`${_}-claude-policy.reg`,ie=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let ae=ie;export function setRegRunnerForTests(e){ae=e??ie}function le(){return"win32"===process.platform}function ce(e){let t;try{t=ae(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function de(){if(null===ce(ne))return;const e=a(claudeDesktopUserDataDir(),se);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),D(e),ae(["export",ne,e,"/y"])}catch(e){u.logError("claude-desktop.snapshotPolicy",e)}}function pe(e){de();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];ae(["add",ne,"/v",t,"/t",e,"/d",n,"/f"])}}function ue(e){const t=ce(ne);if(!t)return;de();let o=t.size;for(const n of e)t.has(n)&&(ae(["delete",ne,"/v",n,"/f"]),o-=1);o<=0&&ae(["delete",ne,"/f"])}const fe=[...Object.keys(Q({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(ee)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return ce(re)?re:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return u.logError("claude-desktop.detectManagedConfig",e),null}}function me(){const e=A();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...M.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))}const ge=/\d+\.\d+\.\d+/;function ye(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}function he(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(A(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?ge.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(A(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return ye(t)}const o=M.map(e=>`'${e}'`).join(",");return ye(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>ge.exec(e)?.[0]??[]))}if("linux"===process.platform)try{const e=c("dpkg-query",["-W","-f=${Version}","claude-desktop"],{encoding:"utf-8",timeout:5e3,stdio:["ignore","pipe","ignore"]});return ge.exec(e)?.[0]??null}catch{return null}return null}const ke="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",_e="darwin"!==process.platform&&"win32"!==process.platform||!h().installMirror?.claudeDesktop?"https://claude.ai/download":h().installMirror.claudeDesktop,De="darwin"===process.platform?`open ${_e}`:"win32"===process.platform?`start ${_e}`:`xdg-open ${_e}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:ke,installCommand:De,configPath:G(),sessions:{format:j,files:()=>L(a(claudeDesktopUserDataDir(),"local-agent-mode-sessions"))},isInstalled:me,installedVersion:he,prepareRuntime:async function(){if(!["win32","darwin","linux"].includes(process.platform)||!me())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(p.gray(" "+g.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?he():null,mirrorRepo:h().installMirror?.claudeDesktop?.replace(/\/-\/releases\/latest\/?$/,"")??null});return f("desktop-code-cli","skipped"===t.status?"skip":"ok",{duration_ms:Date.now()-e,detail:{...t}}),"installed"!==t.status?[]:[{kind:"summary",text:g.t("tools.claude_desktop.code_cli_installed",{version:t.version})}]}catch(t){return u.logError("claude-desktop.prepareCodeTabCli",t),f("desktop-code-cli","fail",{duration_ms:Date.now()-e,error:t}),[{kind:"action",text:g.t("tools.claude_desktop.code_cli_failed",{reason:t instanceof Error?t.message:String(t)})}]}},runningApp:C()?{isRunning:w,restart:E}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const o=H()??{appliedId:"",entries:[]};let n=B(o);n||(n={id:l(),name:I},o.entries.push(n));const r=Q(e);Z=!0===r.disableDeploymentModeChooser,z={models:r.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const s=K(N(n.id))??{};F(N(n.id),{...te(e=>e in s),...s,...r}),o.appliedId=n.id,F(G(),o);const i=K(U())??{};if("3p"!==i.deploymentMode&&F(U(),{...i,deploymentMode:"3p"}),X=null,Y=null,q=null,le()){const o=ce(ne),n=fe.filter(e=>o?.has(e)),s={...te(e=>o?.has(e)??!1),...r},i=!0===e.settings?.force_registry,l=!i&&function(){const e=!Boolean(k("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(A(),"AppData","Local");return!!t(a(e,"AnthropicClaude"))||M.some(o=>t(a(e,"Packages",o,"LocalCache","Local","Claude-3p")))}();try{l?ue(fe):pe(s),q=l?"removed":"written"}catch(e){u.logError("claude-desktop.policy",e),X=e instanceof Error?e.message.trim():String(e),(n.length>0||i)&&(Y=l?{action:"remove",values:Object.fromEntries(n.map(e=>[e,null])),deleteKey:n.length===o.size}:{action:"write",values:s,deleteKey:!1})}}},pendingElevation(){const e=Y;return e?{prompt:g.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!v(oe,e.values,e.deleteKey))return!1;const t=ce(ne),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(Y=null,X=null),o}}:null},unloadPrivateConfig(){if(le())try{ue(fe)}catch(e){u.logError("claude-desktop.removePolicy",e)}const e=H(),t=B(e);if(!e||!t)return;D(N(t.id)),s(N(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(D(G()),s(G(),{force:!0})):F(G(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=H(),t=B(e);if(!t||e?.appliedId!==t.id)return null;const o=K(N(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=H(),o=B(t);if(!o||t?.appliedId!==o.id)return;const n=K(N(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)){if(le()&&ce(ne)?.has("inferenceModels"))try{pe({inferenceModels:r})}catch(e){throw u.logError("claude-desktop.refreshModelMenu registry",e),new O(g.t("tools.claude_desktop.refresh_needs_admin"))}F(N(o.id),{...n,inferenceModels:r})}},detectCurrentConfig(){try{if(le()){const e=ce(ne)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=B(H());if(!e)return{apiKey:null};const t=K(N(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return u.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=H(),t=B(e);return{meta:e,applied:t?K(N(t.id)):null,desktop:K(U()),...le()?{policy:Object.fromEntries(ce(ne)??[])}:{}}}catch(e){return u.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;le()&&ce(ne)?.has("inferenceModels")&&(de(),ae(["delete",ne,"/v","inferenceModels","/f"]),e=!0);const t=B(H()),o=t?K(N(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;F(N(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(z&&z.models.length>0){const{models:t,source:o,groups:n}=z;e.push({kind:"summary",text:g.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:g.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==Z&&e.push({kind:"detail",text:g.t(Z?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),C()&&!w()||e.push({kind:"action",text:g.t("tools.claude_desktop.restart_hint")}),Y?(e.push({kind:"action",text:g.t(`tools.claude_desktop.policy_stale_${Y.action}`)}),e.push({kind:"detail",text:X??""})):X?(e.push({kind:"detail",text:g.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:X})):le()&&q&&e.push({kind:"detail",text:g.t(`tools.claude_desktop.registry_${q}`,{key:ne})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:g.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[G(),U()];try{const t=B(H());t&&e.push(N(t.id))}catch{}return e},backupPaths(){const e=[G(),U()];le()&&e.push(a(claudeDesktopUserDataDir(),se));try{const t=B(H());t&&e.push(N(t.id))}catch{}return e}};
@@ -1 +1 @@
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 c}from"path";import*as a from"@iarna/toml";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as m,atomicWriteFile as p}from"./backup.js";import{codexHome as f,pickModel as g,toolHome as _,withV1 as x}from"./types.js";import{moveSessionsToProvider as h,NO_STATE_DB as y,runningCodexPids as k}from"../codex-sessions.js";import{getToolDefaultModel as b}from"../model-registry.js";import{configManager as v}from"../config.js";import{maskHome as w,track as j}from"../telemetry.js";import{buildCodexCatalog as C,catalogRejections as O,codexCatalogPath as S,fillFromOlderEntries as M,installedCodexBinaries as A,loadBundledCodexModels as E,loadPreviousCodexModels as N,previousGatewayModelIds as P}from"./codex-catalog.js";import{gatewayModelSpecs as T,modelFamily as D}from"../gateway-models.js";import{findInstalls as I,mirrorInstallCommand as L,brandInstallScripts as $}from"./tool-installs.js";const J=()=>f(),G=()=>c(J(),"config.toml"),R="sessions_move_declined",K=["browser_use","browser_use_external","computer_use"];function X(e){const t=e.features;if(!F(t)||!K.every(e=>!1===t[e]))return!1;for(const e of K)delete t[e];return!0}export function codexWorkModeOn(){try{const e=Q();return e.model_provider===U&&F(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function B(){try{if(F(Q().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",c(_(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(c(e,o))));if("win32"===process.platform){const e=c(process.env.LOCALAPPDATA||c(_(),"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 G()}const W=()=>c(J(),"auth.json"),q="OPENAI_API_KEY",U="custom",z=1e6,Y=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function F(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function H(e,t){return F(e[t])||(e[t]={}),e[t]}function Q(){if(!t(G()))return{};const e=r(G(),"utf-8");if(!e.trim())return{};try{const t=a.parse(e);if(!F(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 V(){if(!t(W()))return{};try{const e=JSON.parse(r(W(),"utf-8"));return F(e)?e:{}}catch{return{}}}let Z=null;function ee(e){const t=e.profiles;if(F(t)){for(const[e,o]of Object.entries(t))F(o)&&2===Object.keys(o).length&&o.model_provider===U&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let te=null;const oe=()=>c(v.getConfigDir(),"codex-catalog-check.json");function ne(e){const o=S();t(o)&&r(o,"utf-8")===e||(m(o),p(o,e))}function re(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(oe(),"utf-8")).signature===e}catch{return!1}}(i))return[];const c=O(S(),n);return 0===c.length&&function(e){try{o(v.getConfigDir(),{recursive:!0}),l(oe(),JSON.stringify({signature:e})+"\n")}catch(e){d.logError("codexAdapter.rememberAccepted",e)}}(i),c}function ie(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===z&&delete e.model_context_window,e.model_auto_compact_token_limit===Y&&delete e.model_auto_compact_token_limit}(e);const r=()=>{e.model_catalog_json===S()&&delete e.model_catalog_json,n&&(e.model_context_window=z,e.model_auto_compact_token_limit=Y)},s=E()??N();if(!s)return d.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),te="skipped",void r();const l=t.catalog,c=e=>C({bundled:e,specs:T("codex",l?.clientModels),...l?{keyModels:l.models}:{},gatewayAvailable:l?.models??P(),oneMillion:n,selectedModel:o,..."gateway"===l?.oneMillionSource?{claudeOneMillion:l.oneMillion}:{}}),a=e=>JSON.stringify(e,null,2)+"\n";let u=c(s);ne(a(u));const m=A(),p=re(a(u),m);if(p.length>0){const e=Date.now();let t=!1;for(const e of p){const o=E(e.bin);if(o){for(const e of[{models:M(u.models,o)},{models:M(c(o).models,o)}])if(ne(a(e)),0===re(a(e),m).length){u=e,t=!0;break}if(t)break}}if(j("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:p[0].error,detail:{rejected_by:p.map(e=>({version:e.version,path:w(e.bin)}))}}),!t)return te={rejectedBy:p[0]},i(S(),{force:!0}),void r()}te={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=S()}export function codexCatalogRejection(){let e;try{e=Q()}catch{return null}return e.model_catalog_json===S()&&t(S())?O(S())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=Q(),n=a.stringify(o),r="string"==typeof o.model?o.model:b("codex");ie(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=a.stringify(o);i!==n&&(m(G()),p(G(),i))}const se={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:$("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:L(se),installSpec:se,configPath:G(),wantsModelCatalog:!0,listedModels(){if(Q().model_catalog_json!==S())return null;try{return(JSON.parse(r(S(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(Q().model_catalog_json!==S())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=Q();X(t)&&(m(G()),p(G(),a.stringify(t)))},async followUp(){if(Q().model_provider!==U)return null;const e=await h(U,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=v.getToolSettings("codex")[R];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(U),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:U})},{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(){v.setToolSetting("codex",R,t)}}},applyNotes(){if("skipped"===te)return 0===I(se).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!te)return[];if("rejectedBy"in te){const{bin:e,version:t}=te.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of te.listed)e.set(D(t.id),(e.get(D(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(te.listed.length),breakdown:t})},{kind:"detail",text:te.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||B()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]},loadPrivateConfig(e){const t=b("codex"),o=g(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=x(e.baseUrl),i=Q();i.model=n,i.model_provider=U,i.model_reasoning_effort="high",ie(i,e,n);const s=H(i,"model_providers"),l=F(s[U])?{...s[U]}:{};delete l.env_key;const{experimental_bearer_token:c,...d}=l;s[U]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},ee(i),H(H(i,"tui"),"model_availability_nux")[n]=4;const u=H(i,"features");u.goals=!0,u.js_repl=!1,X(i),m(G()),p(G(),a.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...V(),auth_mode:"apikey",[q]:t};m(W()),p(W(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},holdsOurConfig(e){const t=V()[q];return"string"==typeof t&&e.includes(t.trim())},unloadPrivateConfig(e){if(Z=function(e){const t=V(),o="string"==typeof t[q]?t[q].trim():"";if(!o)return F(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[q]:n,auth_mode:r,...s}=t;return m(W()),F(s.tokens)?(p(W(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(i(W(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(G()))return;let o;try{o=Q()}catch(e){return void d.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;F(n)&&(delete n[U],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===U&&delete o.model_provider,o.model_catalog_json===S()&&delete o.model_catalog_json,X(o),ee(o),m(G()),p(G(),a.stringify(o))},unloadNotes(){switch(Z){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=V()[q];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return Q()}catch(e){return d.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[G(),W(),S()]};export function codexAuthProblem(){try{const e=Q(),t=e.model_providers?.[U];if(e.model_provider!==U||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=V();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 a}from"path";import*as c from"@iarna/toml";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as m,atomicWriteFile as p}from"./backup.js";import{codexHome as f,pickModel as g,toolHome as _,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{getToolDefaultModel as j}from"../model-registry.js";import{configManager as w}from"../config.js";import{maskHome as C,track as O}from"../telemetry.js";import{buildCodexCatalog as S,catalogRejections as M,codexCatalogPath as A,fillFromOlderEntries as E,installedCodexBinaries as N,loadBundledCodexModels as P,loadPreviousCodexModels as T,previousGatewayModelIds as D}from"./codex-catalog.js";import{gatewayModelSpecs as I,modelFamily as L}from"../gateway-models.js";import{findInstalls as $,mirrorInstallCommand as J,brandInstallScripts as G}from"./tool-installs.js";const R=()=>f(),K=()=>a(R(),"config.toml"),X="sessions_move_declined",B=["browser_use","browser_use_external","computer_use"];function W(e){const t=e.features;if(!Q(t)||!B.every(e=>!1===t[e]))return!1;for(const e of B)delete t[e];return!0}export function codexWorkModeOn(){try{const e=Z();return e.model_provider===Y&&Q(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function q(){try{if(Q(Z().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",a(_(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(a(e,o))));if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(_(),"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 K()}const U=()=>a(R(),"auth.json"),z="OPENAI_API_KEY",Y="custom",F=1e6,H=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function Q(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function V(e,t){return Q(e[t])||(e[t]={}),e[t]}function Z(){if(!t(K()))return{};const e=r(K(),"utf-8");if(!e.trim())return{};try{const t=c.parse(e);if(!Q(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 ee(){if(!t(U()))return{};try{const e=JSON.parse(r(U(),"utf-8"));return Q(e)?e:{}}catch{return{}}}let te=null;function oe(e){const t=e.profiles;if(Q(t)){for(const[e,o]of Object.entries(t))Q(o)&&2===Object.keys(o).length&&o.model_provider===Y&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let ne=null;const re=()=>a(w.getConfigDir(),"codex-catalog-check.json");function ie(e){const o=A();t(o)&&r(o,"utf-8")===e||(m(o),p(o,e))}function se(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(re(),"utf-8")).signature===e}catch{return!1}}(i))return[];const a=M(A(),n);return 0===a.length&&function(e){try{o(w.getConfigDir(),{recursive:!0}),l(re(),JSON.stringify({signature:e})+"\n")}catch(e){d.logError("codexAdapter.rememberAccepted",e)}}(i),a}function le(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===F&&delete e.model_context_window,e.model_auto_compact_token_limit===H&&delete e.model_auto_compact_token_limit}(e);const r=()=>{e.model_catalog_json===A()&&delete e.model_catalog_json,n&&(e.model_context_window=F,e.model_auto_compact_token_limit=H)},s=P()??T();if(!s)return d.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),ne="skipped",void r();const l=t.catalog,a=e=>S({bundled:e,specs:I("codex",l?.clientModels),...l?{keyModels:l.models}:{},gatewayAvailable:l?.models??D(),oneMillion:n,selectedModel:o,..."gateway"===l?.oneMillionSource?{claudeOneMillion:l.oneMillion}:{}}),c=e=>JSON.stringify(e,null,2)+"\n";let u=a(s);ie(c(u));const m=N(),p=se(c(u),m);if(p.length>0){const e=Date.now();let t=!1;for(const e of p){const o=P(e.bin);if(o){for(const e of[{models:E(u.models,o)},{models:E(a(o).models,o)}])if(ie(c(e)),0===se(c(e),m).length){u=e,t=!0;break}if(t)break}}if(O("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:p[0].error,detail:{rejected_by:p.map(e=>({version:e.version,path:C(e.bin)}))}}),!t)return ne={rejectedBy:p[0]},i(A(),{force:!0}),void r()}ne={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=A()}export function codexCatalogRejection(){let e;try{e=Z()}catch{return null}return e.model_catalog_json===A()&&t(A())?M(A())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=Z(),n=c.stringify(o),r="string"==typeof o.model?o.model:j("codex");le(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=c.stringify(o);i!==n&&(m(K()),p(K(),i))}const ae={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:G("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:J(ae),installSpec:ae,configPath:K(),sessions:{format:b,files:()=>[...v(a(f(),"sessions")),...v(a(f(),"archived_sessions"))],resumeCommand:e=>({command:"codex",args:["resume",e]})},wantsModelCatalog:!0,listedModels(){if(Z().model_catalog_json!==A())return null;try{return(JSON.parse(r(A(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(Z().model_catalog_json!==A())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=Z();W(t)&&(m(K()),p(K(),c.stringify(t)))},async followUp(){if(Z().model_provider!==Y)return null;const e=await h(Y,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=w.getToolSettings("codex")[X];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(Y),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:Y})},{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(){w.setToolSetting("codex",X,t)}}},applyNotes(){if("skipped"===ne)return 0===$(ae).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!ne)return[];if("rejectedBy"in ne){const{bin:e,version:t}=ne.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of ne.listed)e.set(L(t.id),(e.get(L(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(ne.listed.length),breakdown:t})},{kind:"detail",text:ne.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||q()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]},loadPrivateConfig(e){const t=j("codex"),o=g(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=x(e.baseUrl),i=Z();i.model=n,i.model_provider=Y,i.model_reasoning_effort="high",le(i,e,n);const s=V(i,"model_providers"),l=Q(s[Y])?{...s[Y]}:{};delete l.env_key;const{experimental_bearer_token:a,...d}=l;s[Y]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},oe(i),V(V(i,"tui"),"model_availability_nux")[n]=4;const u=V(i,"features");u.goals=!0,u.js_repl=!1,W(i),m(K()),p(K(),c.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...ee(),auth_mode:"apikey",[z]:t};m(U()),p(U(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},holdsOurConfig(e){const t=ee()[z];return"string"==typeof t&&e.includes(t.trim())},unloadPrivateConfig(e){if(te=function(e){const t=ee(),o="string"==typeof t[z]?t[z].trim():"";if(!o)return Q(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[z]:n,auth_mode:r,...s}=t;return m(U()),Q(s.tokens)?(p(U(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(i(U(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(K()))return;let o;try{o=Z()}catch(e){return void d.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;Q(n)&&(delete n[Y],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===Y&&delete o.model_provider,o.model_catalog_json===A()&&delete o.model_catalog_json,W(o),oe(o),m(K()),p(K(),c.stringify(o))},unloadNotes(){switch(te){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=ee()[z];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return Z()}catch(e){return d.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[K(),U(),A()]};export function codexAuthProblem(){try{const e=Z(),t=e.model_providers?.[Y];if(e.model_provider!==Y||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=ee();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -1 +1 @@
1
- import{join as o}from"path";import{homedir as e}from"os";import{openCodeManager as n}from"../opencode-manager.js";import{backupBeforeWrite as i}from"./backup.js";import{pickModel as t}from"./types.js";import{getToolDefaultModel as a}from"../model-registry.js";const d=o(e(),".config","opencode","opencode.json");export const opencodeAdapter={name:"opencode",displayName:"OpenCode",command:"opencode",installCommand:"npm install -g opencode-ai",configPath:d,hidden:!0,loadPrivateConfig(o){i(d);const e=t(o.models,a("opencode"));n.loadPrivateConfig(o.apiKey,{...o.models,opus:e,haiku:e})},unloadPrivateConfig(){i(d),n.unloadPrivateConfig()},detectCurrentConfig:()=>n.detectCurrentConfig(),getConfig:()=>n.getConfig(),updateConfig(o){n.saveConfig(o)}};
1
+ import{join as o}from"path";import{openCodeManager as e}from"../opencode-manager.js";import{backupBeforeWrite as n}from"./backup.js";import{pickModel as t,toolHome as i}from"./types.js";import{getToolDefaultModel as a}from"../model-registry.js";const d=()=>o(i(),".config","opencode","opencode.json");export const opencodeAdapter={name:"opencode",displayName:"OpenCode",command:"opencode",installCommand:"npm install -g opencode-ai",get configPath(){return d()},hidden:!0,loadPrivateConfig(o){n(d());const i=t(o.models,a("opencode"));e.loadPrivateConfig(o.apiKey,{...o.models,opus:i,haiku:i})},unloadPrivateConfig(){n(d()),e.unloadPrivateConfig()},detectCurrentConfig:()=>e.detectCurrentConfig(),getConfig:()=>e.getConfig(),updateConfig(o){e.saveConfig(o)}};
@@ -1,6 +1,7 @@
1
1
  import type { VibecliModels, VibecliToolSettings } from '../config.js';
2
2
  import type { ModelCatalog } from '../model-catalog.js';
3
3
  import type { ToolInstallSpec } from './tool-installs.js';
4
+ import type { SessionFormat } from '../sessions/formats.js';
4
5
  /**
5
6
  * Home directory the tool adapters write under. Honors `VIBECLI_TEST_HOME` (the
6
7
  * same override the config layer uses) so tests can redirect ~/.codex, ~/.hermes,
@@ -56,6 +57,19 @@ export interface ToolUnloadContext {
56
57
  /** Every API key this CLI has saved (all profiles, default and tool-specific): a key in the tool equal to one of these is ours to remove. */
57
58
  savedKeys: string[];
58
59
  }
60
+ /**
61
+ * Where a tool keeps its conversation logs on this machine, for `vibecli sessions` (read-only:
62
+ * list, show, export, resume). Files come back unsorted; the format parses them.
63
+ */
64
+ export interface ToolSessionStore {
65
+ files(): string[];
66
+ format: SessionFormat;
67
+ /** The command that continues a session in the tool, run from the session's directory; absent when the tool can't. */
68
+ resumeCommand?(id: string): {
69
+ command: string;
70
+ args: string[];
71
+ };
72
+ }
59
73
  export interface ToolAdapter {
60
74
  /** Stable id used on the CLI and as the config key (e.g. `codex`). */
61
75
  name: string;
@@ -115,6 +129,8 @@ export interface ToolAdapter {
115
129
  * apps); null when unknown. Checked against `models.json` → `toolMinVersions`.
116
130
  */
117
131
  installedVersion?(): string | null;
132
+ /** Local conversation logs, for `vibecli sessions`. */
133
+ sessions?: ToolSessionStore;
118
134
  /** Primary config file path (informational; used for status/doctor output). */
119
135
  configPath: string;
120
136
  /** Hidden tools are still usable by id but omitted from menus. */
@@ -1 +1 @@
1
- import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as c}from"./tool-manager.js";import{postApplyPrompts as d}from"./post-apply.js";import{i18n as p,detectSystemLang as m}from"./i18n.js";import{createBorderLine as u,createContentLine as _}from"../utils/string-width.js";import{validateApiKey as g,fetchModels as w}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as f}from"./tools/codex.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as y}from"./tools/claude-desktop.js";import{addressesWithCurrent as h,ensureReachableGatewayAddress as z,fastestIndex as v,probeAll as k,probeSummary as x,reasonText as b,savedApiKey as S,switchGatewayAddress as C}from"../commands/base.js";import{readFileSync as M}from"fs";import{dirname as T,join as A}from"path";import{fileURLToPath as K}from"url";import{brand as N}from"./brand.js";const B=T(K(import.meta.url)),P=t.hex("#FF8700");export class Wizard{static instance;BOX_WIDTH=63;validatedApiKeys=new Set;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(P.bold("\n"+u("╔","╗","═",this.BOX_WIDTH))),console.log(P.bold(_(e,"║","║",this.BOX_WIDTH,"center"))),console.log(P.bold(u("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(p.t("wizard.hint_navigate")),t.gray(p.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=60,t=p.t("wizard.banner_subtitle"),a=_(t,"║","║",e,"center"),o=_("","║","║",e,"center"),i=_(`${N().productName} v${function(){try{return JSON.parse(M(A(B,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[String.raw`__ ___ _ ____ _ ___ `,String.raw`\ \ / (_) |__ ___ / ___| | |_ _|`,String.raw` \ \ / /| | '_ \ / _ \ | | | | | `,String.raw` \ V / | | |_) | __/ |___| |___ | | `,String.raw` \_/ |_|_.__/ \___|\____|_____|___|`],r=Math.max(...n.map(e=>e.length)),s=" ".repeat(Math.floor((58-r)/2)),l=n.map(t=>_(s+t,"║","║",e,"left")),c=[u("╔","╗","═",e),o,...l,o,i,a,u("╚","╝","═",e)];console.log(P.bold("\n"+c.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{if(e.gui)return!1;try{return Boolean(l.detectCurrentConfig(e.name).apiKey)&&l.isToolInstalled(e.name)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=p.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+p.t("wizard.goodbye_claude_hint"))}else a=p.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a)),this.printPathReloadHint(e.map(e=>e.name))}printPathReloadHint(e){const a=e.filter(e=>!l.isOnPath(e));if(0===a.length)return;const o=a.map(e=>c[e].command).join(" / ");console.log(t.yellow(" ! "+p.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:o})))}async runFirstTimeSetup(){const e="zh_CN"===m();e&&(o.setLang("zh_CN"),p.setLocale("zh_CN")),this.resetScreen(),console.log(P.bold("\n"+p.t("wizard.welcome"))),console.log(t.gray(p.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(p.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await z(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(p.t("wizard.select_language"));const a=p.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+p.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+p.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+p.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"back"},{name:"x "+p.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void p.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(p.t(e.name)!==e.name?p.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+p.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+p.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:p.t("wizard.custom_model_mode"),choices:[{name:p.t("wizard.custom_model_single"),value:"single"},{name:p.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:p.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:p.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:p.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:p.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:p.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+p.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(p.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+p.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+p.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+p.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:p.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+p.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:p.t("wizard.load_config_now"),choices:[{name:p.t("wizard.load_config_yes"),value:!0},{name:p.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configBaseUrl(){const i=o.getBaseUrl(),n=h(),r=a(p.t("wizard.base_testing")).start(),s=await k(n,{apiKey:S(),samples:3});r.stop();const l=v(s),c=n.map((e,a)=>{const o=e.url===i?t.cyan(` ← ${p.t("base.current_mark")}`):"";return{name:`${e.url} ${t.gray(e.label)} ${x(s[a],a===l)}${o}`,value:e.url}});c.push(new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"__back__"});const{url:d}=await this.promptWithHints([{type:"list",name:"url",message:p.t("wizard.base_select"),choices:c,default:i}]);if("__back__"===d)return;const m=s[n.findIndex(e=>e.url===d)];m.ok?await C(d):console.log(t.red(p.t("base.unreachable",{url:d,reason:b(m)}))),await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}async configApiKey(){for(;;){this.resetScreen(),this.createBox(p.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+p.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+p.t("wizard.api_key_get_hint",{url:N().links.tokenPage}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:p.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:p.t("wizard.select_action"),choices:[{name:p.t("wizard.retry_input"),value:"retry"},{name:"<- "+p.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(p.t("wizard.validating_api_key")).start(),r=await g(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+p.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+p.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+p.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:p.t("wizard.select_action"),choices:[{name:p.t("wizard.retry_input"),value:"retry"},{name:"<- "+p.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(p.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+p.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"back"},{name:"x "+p.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=c[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+p.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:p.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(p.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=c[t];if(!a)return;this.createBox(a.displayName+" "+p.t("wizard.menu_title"));const o=[{name:"📥 "+p.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+p.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+p.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"}),o.push({name:"🧨 "+p.t("wizard.action_force_config",{tool:a.displayName}),value:"force"},{name:"↩️ "+p.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"back"},{name:"x "+p.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+p.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"force"===i?await this.forceConfig(t):"unload"===i?await this.unloadConfig(t):"clear-model"===i?await this.clearPinnedModels(t):"restore"===i&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}hasConfiguredApiKey(){return!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+p.t("wizard.api_key_required"))),console.log(t.yellow(p.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(p.t("wizard.validating_api_key")).start(),n=await g(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(p.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?p.t("wizard.api_key_invalid"):p.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+p.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=c[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}]),!1;const d="claude-code"===i,m=d||"claude-desktop"===i||"agy"===i,u=s(i),_=m||!u?p.t("wizard.model_builtin_default"):u;this.resetScreen(),this.createBox(n.displayName+" · "+p.t("wizard.config_models"));const g=[{name:"✅ "+p.t("wizard.use_default_model",{model:_}),value:{type:"default"}},{name:"🌐 "+p.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+p.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:{type:"back"}}],{choice:h}=await this.promptWithHints([{type:"list",name:"choice",message:p.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===h.type)return!1;if("default"===h.type)o.setToolModel(i,null);else if("preset"===h.type)o.setToolModel(i,r(h.preset));else if("custom"===h.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:p.t("wizard.input_custom_model"),default:!m&&u?u:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===h.type){const n=a(p.t("wizard.fetching_models")).start(),r=await w(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:p.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+p.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+p.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:p.t("wizard.input_custom_model"),default:!m&&u?u:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=o.getToolSetting(i,f),{enableOneMillionContext:a}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:p.t("wizard.codex_enable_1m_context"),default:!1!==t}]);o.setToolSetting(i,f,Boolean(a))}if("claude-desktop"===i){const t=o.getToolSetting(i,y),{hideOfficialLogin:a}=await e.prompt([{type:"confirm",name:"hideOfficialLogin",message:p.t("wizard.desktop_hide_official_login"),default:!1!==t}]);o.setToolSetting(i,y,Boolean(a))}return console.log(t.green("\n✓ "+p.t("wizard.models_saved"))),d&&console.log(t.gray("💡 "+p.t("wizard.claude_model_hint"))),!0}async forceConfig(t){const a=c[t]?.displayName||t,{confirm:o}=await e.prompt([{type:"confirm",name:"confirm",message:p.t("config.confirm_force",{tool:a}),default:!1}]);o&&await this.loadConfig(t,{force:!0})}async loadConfig(i,n={}){const r=o.getModelsForTool(i),s=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!s)return void await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}]);const m=a(p.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,s,r,n),m.stop(),l.printApplied(i),await d([i]),n.force&&console.log(t.yellow(p.t("config.force_done",{tool:c[i]?.displayName||i,name:i})));const a=c[i];a&&!a.gui&&(l.isOnPath(i)&&console.log(t.green("\n"+p.t("wizard.run_tool_hint",{command:a.command}))),this.printPathReloadHint([i]));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:p.t("wizard.what_next"),choices:[{name:p.t("wizard.exit_and_start"),value:"exit"},{name:p.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){m.fail(p.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=c[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:p.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=await l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+p.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+p.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(p.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:p.t("wizard.confirm_unload_config",{tool:c[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(p.t("wizard.unloading_config")).start();try{const a=l.holdsOurConfig(o),i=l.unloadPrivateConfig(o);if(!a)return void n.info(p.t("config.remove_nothing",{tool:c[o]?.displayName||o}));n.succeed(p.t("wizard.config_unloaded")),i.forEach(e=>console.log(t.yellow(" ! "+e.text)));const{nextAction:r}=await e.prompt([{type:"list",name:"nextAction",message:p.t("wizard.what_next"),choices:[{name:p.t("wizard.nav_exit"),value:"exit"},{name:p.t("wizard.continue_config"),value:"continue"}]}]);"exit"===r&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(p.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}}async restoreConfig(o){const i=c[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:p.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(p.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(p.t("wizard.restore_no_backup")):(r.succeed(p.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))),console.log(t.yellow(" ! "+p.t("config.reopen_hint",{tool:i}))))}catch(e){r.fail(p.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:p.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(P("── "+p.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+p.t("wizard.settings")+" ──")),{name:"🔑 "+p.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🔗 "+p.t("wizard.menu_base_url",{url:o.getBaseUrl()}),value:{type:"base"}},{name:"🌐 "+p.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+p.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+p.t("wizard.select_tool"),choices:r}]);"exit"===s.type?(this.printGoodbye(),process.exit(0)):"tool"===s.type?await this.configureTool(s.tool):"apikey"===s.type?await this.configApiKey():"base"===s.type?await this.configBaseUrl():"language"===s.type&&await this.configLanguage()}else await this.runFirstTimeSetup()}}export const wizard=Wizard.getInstance();
1
+ import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as c}from"./tool-manager.js";import{confirmWritePlan as d}from"./write-plan.js";import{postApplyPrompts as p}from"./post-apply.js";import{i18n as m,detectSystemLang as u}from"./i18n.js";import{createBorderLine as _,createContentLine as g}from"../utils/string-width.js";import{validateApiKey as w,fetchModels as f}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as y}from"./tools/codex.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as h}from"./tools/claude-desktop.js";import{addressesWithCurrent as z,ensureReachableGatewayAddress as v,fastestIndex as k,probeAll as x,probeSummary as b,reasonText as S,savedApiKey as C,switchGatewayAddress as M}from"../commands/base.js";import{readFileSync as T}from"fs";import{dirname as A,join as K}from"path";import{fileURLToPath as N}from"url";import{brand as B}from"./brand.js";const P=A(N(import.meta.url)),H=t.hex("#FF8700");export class Wizard{static instance;BOX_WIDTH=63;validatedApiKeys=new Set;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(H.bold("\n"+_("╔","╗","═",this.BOX_WIDTH))),console.log(H.bold(g(e,"║","║",this.BOX_WIDTH,"center"))),console.log(H.bold(_("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(m.t("wizard.hint_navigate")),t.gray(m.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=60,t=m.t("wizard.banner_subtitle"),a=g(t,"║","║",e,"center"),o=g("","║","║",e,"center"),i=g(`${B().productName} v${function(){try{return JSON.parse(T(K(P,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[String.raw`__ ___ _ ____ _ ___ `,String.raw`\ \ / (_) |__ ___ / ___| | |_ _|`,String.raw` \ \ / /| | '_ \ / _ \ | | | | | `,String.raw` \ V / | | |_) | __/ |___| |___ | | `,String.raw` \_/ |_|_.__/ \___|\____|_____|___|`],r=Math.max(...n.map(e=>e.length)),s=" ".repeat(Math.floor((58-r)/2)),l=n.map(t=>g(s+t,"║","║",e,"left")),c=[_("╔","╗","═",e),o,...l,o,i,a,_("╚","╝","═",e)];console.log(H.bold("\n"+c.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{if(e.gui)return!1;try{return Boolean(l.detectCurrentConfig(e.name).apiKey)&&l.isToolInstalled(e.name)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=m.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+m.t("wizard.goodbye_claude_hint"))}else a=m.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a)),this.printPathReloadHint(e.map(e=>e.name))}printPathReloadHint(e){const a=e.filter(e=>!l.isOnPath(e));if(0===a.length)return;const o=a.map(e=>c[e].command).join(" / ");console.log(t.yellow(" ! "+m.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:o})))}async runFirstTimeSetup(){const e="zh_CN"===u();e&&(o.setLang("zh_CN"),m.setLocale("zh_CN")),this.resetScreen(),console.log(H.bold("\n"+m.t("wizard.welcome"))),console.log(t.gray(m.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(m.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await v(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(m.t("wizard.select_language"));const a=m.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+m.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+m.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+m.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"back"},{name:"x "+m.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void m.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(m.t(e.name)!==e.name?m.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+m.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+m.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:m.t("wizard.custom_model_mode"),choices:[{name:m.t("wizard.custom_model_single"),value:"single"},{name:m.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:m.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:m.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:m.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:m.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:m.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+m.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(m.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+m.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+m.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+m.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:m.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+m.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:m.t("wizard.load_config_now"),choices:[{name:m.t("wizard.load_config_yes"),value:!0},{name:m.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configBaseUrl(){const i=o.getBaseUrl(),n=z(),r=a(m.t("wizard.base_testing")).start(),s=await x(n,{apiKey:C(),samples:3});r.stop();const l=k(s),c=n.map((e,a)=>{const o=e.url===i?t.cyan(` ← ${m.t("base.current_mark")}`):"";return{name:`${e.url} ${t.gray(e.label)} ${b(s[a],a===l)}${o}`,value:e.url}});c.push(new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"__back__"});const{url:d}=await this.promptWithHints([{type:"list",name:"url",message:m.t("wizard.base_select"),choices:c,default:i}]);if("__back__"===d)return;const p=s[n.findIndex(e=>e.url===d)];p.ok?await M(d):console.log(t.red(m.t("base.unreachable",{url:d,reason:S(p)}))),await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}async configApiKey(){for(;;){this.resetScreen(),this.createBox(m.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+m.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+m.t("wizard.api_key_get_hint",{url:B().links.tokenPage}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:m.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:m.t("wizard.select_action"),choices:[{name:m.t("wizard.retry_input"),value:"retry"},{name:"<- "+m.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(m.t("wizard.validating_api_key")).start(),r=await w(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+m.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+m.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+m.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:m.t("wizard.select_action"),choices:[{name:m.t("wizard.retry_input"),value:"retry"},{name:"<- "+m.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(m.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+m.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"back"},{name:"x "+m.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=c[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+m.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:m.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(m.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=c[t];if(!a)return;this.createBox(a.displayName+" "+m.t("wizard.menu_title"));const o=[{name:"📥 "+m.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+m.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+m.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"}),o.push({name:"🧨 "+m.t("wizard.action_force_config",{tool:a.displayName}),value:"force"},{name:"↩️ "+m.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"back"},{name:"x "+m.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+m.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"force"===i?await this.forceConfig(t):"unload"===i?await this.unloadConfig(t):"clear-model"===i?await this.clearPinnedModels(t):"restore"===i&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}hasConfiguredApiKey(){return!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+m.t("wizard.api_key_required"))),console.log(t.yellow(m.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(m.t("wizard.validating_api_key")).start(),n=await w(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(m.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?m.t("wizard.api_key_invalid"):m.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+m.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=c[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}]),!1;const d="claude-code"===i,p=d||"claude-desktop"===i||"agy"===i,u=s(i),_=p||!u?m.t("wizard.model_builtin_default"):u;this.resetScreen(),this.createBox(n.displayName+" · "+m.t("wizard.config_models"));const g=[{name:"✅ "+m.t("wizard.use_default_model",{model:_}),value:{type:"default"}},{name:"🌐 "+m.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+m.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:{type:"back"}}],{choice:w}=await this.promptWithHints([{type:"list",name:"choice",message:m.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===w.type)return!1;if("default"===w.type)o.setToolModel(i,null);else if("preset"===w.type)o.setToolModel(i,r(w.preset));else if("custom"===w.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:m.t("wizard.input_custom_model"),default:!p&&u?u:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===w.type){const n=a(m.t("wizard.fetching_models")).start(),r=await f(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:m.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+m.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+m.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:m.t("wizard.input_custom_model"),default:!p&&u?u:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=o.getToolSetting(i,y),{enableOneMillionContext:a}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:m.t("wizard.codex_enable_1m_context"),default:!1!==t}]);o.setToolSetting(i,y,Boolean(a))}if("claude-desktop"===i){const t=o.getToolSetting(i,h),{hideOfficialLogin:a}=await e.prompt([{type:"confirm",name:"hideOfficialLogin",message:m.t("wizard.desktop_hide_official_login"),default:!1!==t}]);o.setToolSetting(i,h,Boolean(a))}return console.log(t.green("\n✓ "+m.t("wizard.models_saved"))),d&&console.log(t.gray("💡 "+m.t("wizard.claude_model_hint"))),!0}async forceConfig(t){const a=c[t]?.displayName||t,{confirm:o}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("config.confirm_force",{tool:a}),default:!1}]);o&&await this.loadConfig(t,{force:!0})}async loadConfig(i,n={}){const r=o.getModelsForTool(i),s=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!s)return void await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}]);if(!n.force&&!await d([i]))return;const u=a(m.t("wizard.loading_config")).start();try{await l.applyPrivateConfig(i,s,r,n),u.stop(),l.printApplied(i),await p([i]),n.force&&console.log(t.yellow(m.t("config.force_done",{tool:c[i]?.displayName||i,name:i})));const a=c[i];a&&!a.gui&&(l.isOnPath(i)&&console.log(t.green("\n"+m.t("wizard.run_tool_hint",{command:a.command}))),this.printPathReloadHint([i]));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:m.t("wizard.what_next"),choices:[{name:m.t("wizard.exit_and_start"),value:"exit"},{name:m.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){u.fail(m.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=c[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=await l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+m.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+m.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(m.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("wizard.confirm_unload_config",{tool:c[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(m.t("wizard.unloading_config")).start();try{const a=l.holdsOurConfig(o),i=l.unloadPrivateConfig(o);if(!a)return void n.info(m.t("config.remove_nothing",{tool:c[o]?.displayName||o}));n.succeed(m.t("wizard.config_unloaded")),i.forEach(e=>console.log(t.yellow(" ! "+e.text)));const{nextAction:r}=await e.prompt([{type:"list",name:"nextAction",message:m.t("wizard.what_next"),choices:[{name:m.t("wizard.nav_exit"),value:"exit"},{name:m.t("wizard.continue_config"),value:"continue"}]}]);"exit"===r&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(m.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}}async restoreConfig(o){const i=c[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:m.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(m.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(m.t("wizard.restore_no_backup")):(r.succeed(m.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))),console.log(t.yellow(" ! "+m.t("config.reopen_hint",{tool:i}))))}catch(e){r.fail(m.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:m.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(H("── "+m.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+m.t("wizard.settings")+" ──")),{name:"🔑 "+m.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🔗 "+m.t("wizard.menu_base_url",{url:o.getBaseUrl()}),value:{type:"base"}},{name:"🌐 "+m.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+m.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+m.t("wizard.select_tool"),choices:r}]);"exit"===s.type?(this.printGoodbye(),process.exit(0)):"tool"===s.type?await this.configureTool(s.tool):"apikey"===s.type?await this.configApiKey():"base"===s.type?await this.configBaseUrl():"language"===s.type&&await this.configLanguage()}else await this.runFirstTimeSetup()}}export const wizard=Wizard.getInstance();
@@ -0,0 +1,26 @@
1
+ /**
2
+ * What a configuring command is about to write, per tool: shared by `--dry-run` (print and stop) and by
3
+ * the confirmation an interactive run asks before writing anything. Non-interactive runs (`--yes`,
4
+ * `--non-interactive`, `--json`, no terminal) never ask.
5
+ */
6
+ export interface ToolWritePlan {
7
+ tool: string;
8
+ files: string[];
9
+ }
10
+ export declare function toolWritePlan(tools: string[]): ToolWritePlan[];
11
+ /** `<label> Claude Code` + its files, one block per tool; then the key file and tools installed first. */
12
+ export declare function printWritePlan(plan: ToolWritePlan[], options: {
13
+ label: string;
14
+ keyFile?: string;
15
+ installFirst?: string[];
16
+ }): void;
17
+ /** A person at a terminal who has not said yes up front. */
18
+ export declare function asksBeforeWriting(): boolean;
19
+ /**
20
+ * Interactive runs: list the files about to change and ask (default yes). Everything else: true
21
+ * without printing, so scripts behave exactly as before.
22
+ */
23
+ export declare function confirmWritePlan(tools: string[], options?: {
24
+ keyFile?: string;
25
+ installFirst?: string[];
26
+ }): Promise<boolean>;
@@ -0,0 +1 @@
1
+ import o from"chalk";import t from"inquirer";import{getAdapter as r}from"./tools/registry.js";import{SUPPORTED_TOOLS as n}from"./tool-manager.js";import{i18n as e}from"./i18n.js";import{isColorEnabled as l,isDryRun as i,isJsonMode as s,isNonInteractive as a,shouldAutoConfirm as c}from"./runtime-options.js";export function toolWritePlan(o){return o.map(o=>{const t=r(o);return{tool:o,files:t?.backupPaths?.()??(t?[t.configPath]:[])}})}export function printWritePlan(t,r){const i=(l()?o.cyan:o=>o)(r.label),s=l()?o.gray:o=>o;for(const{tool:o,files:r}of t)console.log(`${i} ${e.t("setup.dry_run_tool",{tool:n[o]?.displayName??o})}`),r.forEach(o=>console.log(s(` ${o}`)));for(const o of r.installFirst??[])console.log(`${i} ${e.t("plan.install_first",{tool:n[o]?.displayName??o})}`);r.keyFile&&console.log(`${i} ${e.t("setup.dry_run_key",{file:r.keyFile})}`)}export function asksBeforeWriting(){return Boolean(process.stdin.isTTY&&process.stdout.isTTY)&&!c()&&!a()&&!s()&&!i()}export async function confirmWritePlan(o,r={}){if(!asksBeforeWriting())return!0;if(0===o.length&&!r.keyFile&&!r.installFirst?.length)return!0;console.log("\n"+e.t("plan.title")),printWritePlan(toolWritePlan(o),{label:" •",...r});const{go:n}=await t.prompt([{type:"confirm",name:"go",message:e.t("plan.confirm"),default:!0}]);return n||console.log(e.t("plan.cancelled")),n}
@@ -160,6 +160,9 @@
160
160
  "other_addresses": "Other gateway addresses",
161
161
  "other_addresses_found": "These addresses work: {{urls}}. Run {{cli}} base auto to switch; tools {{product}} configured move with it",
162
162
  "other_addresses_none": "None of the other gateway addresses can be reached either; check your network or proxy",
163
+ "balance": "Balance",
164
+ "balance_tool": "Balance ({{tool}} dedicated key)",
165
+ "check_balance": "Checking balance",
163
166
  "check_client_config": "Checking the gateway model config...",
164
167
  "client_config": "Gateway model config",
165
168
  "client_config_revision": "revision {{revision}}",
@@ -177,7 +180,12 @@
177
180
  "tools": "Tools",
178
181
  "config_loaded": "Config applied",
179
182
  "config_not_loaded": "Config not applied",
180
- "tool_key_label": "{{tool}} dedicated key"
183
+ "tool_key_label": "{{tool}} dedicated key",
184
+ "balance": "Balance",
185
+ "balance_account_low": "{{amount}} (key quota), but the account balance cannot cover it — top up in the console",
186
+ "balance_token_limited": "{{amount}} (key quota)",
187
+ "balance_empty": "{{amount}}, used up — top up in the console to keep using it",
188
+ "balance_token_empty": "{{amount}}, this key's quota is used up — raise it in the console"
181
189
  },
182
190
  "messages": {
183
191
  "env_vars_cleared": "Detected {{vars}} in your shell environment and removed them (they would override the config {{product}} writes). Restart your terminal to apply the shell profile change.",
@@ -268,7 +276,23 @@
268
276
  "min": "min",
269
277
  "max": "max",
270
278
  "errors": "errors",
271
- "missing_api_key": "No API key saved yet"
279
+ "missing_api_key": "No API key saved yet",
280
+ "stream_running": "Sending one streaming request with {{model}}…",
281
+ "stream_title": "Streaming request",
282
+ "stream_model": "Model",
283
+ "stream_first_token": "First token",
284
+ "stream_total": "Total",
285
+ "stream_ok": "Stream ended normally",
286
+ "stream_hint": "Add --stream to also send one real streaming request and see the first-token time and whether the stream ends properly (costs a few tokens): {{cli}} bench --stream",
287
+ "stream_fail": {
288
+ "http": "The request was rejected",
289
+ "stream_error": "The stream carried an error",
290
+ "incomplete": "The stream stopped without its end marker (answers get cut off)",
291
+ "no_text": "The stream ended without any text",
292
+ "timeout": "Did not finish within 60 seconds",
293
+ "network": "Connection failed",
294
+ "no_model": "Could not list this key's models; pick one with --model: {{cli}} bench --stream --model <model>"
295
+ }
272
296
  },
273
297
  "update": {
274
298
  "checking": "Checking for updates...",
@@ -328,7 +352,7 @@
328
352
  "desktop_registry_invalid": "--desktop-registry takes on or off, got {{value}}",
329
353
  "install_confirm": "{{tools}} not installed yet. Install from the China mirrors now?",
330
354
  "install_all_hint": "Or run {{cli}} setup --key <your key> --install to install them all",
331
- "still_missing": "{{tools}} still not installed; run the commands above to install",
355
+ "still_missing": "{{tools}} not installed, so their config was not written; after installing them with the commands above, run {{commands}}",
332
356
  "path_reload": "Run exec \"$SHELL\" -l in this window (or open a new terminal) to use {{commands}}",
333
357
  "path_added": "{{tool}} is installed in {{dir}}, which was not on PATH; added it to your user PATH",
334
358
  "path_reload_windows": "Open a new terminal to use {{commands}}",
@@ -460,6 +484,51 @@
460
484
  "backup": "Backup and undo record in {{dir}}; undo with {{cli}} codex sessions --undo <that dir>",
461
485
  "undone": "Restored {{files}} session files and {{rows}} database rows"
462
486
  },
487
+ "plan": {
488
+ "title": "About to change (backed up first):",
489
+ "confirm": "Write these changes?",
490
+ "cancelled": "Cancelled; no file was changed",
491
+ "install_first": "{{tool}} is not installed: install it first, then write its config"
492
+ },
493
+ "sessions": {
494
+ "unknown_tool": "Unknown tool: {{tool}} (tools with sessions: {{tools}})",
495
+ "unknown_tool_hint": "Drop --tool to see all, or run {{cli}} sessions --tool codex",
496
+ "id_too_short": "Session ID \"{{id}}\" is too short; give at least 4 characters",
497
+ "list_hint": "Run {{cli}} sessions to see sessions and their IDs",
498
+ "not_found": "No session on this machine has an ID starting with \"{{id}}\"",
499
+ "ambiguous": "Several sessions have IDs starting with \"{{id}}\": {{ids}}",
500
+ "ambiguous_hint": "Give more characters of the ID",
501
+ "bad_limit": "Not a valid count: {{value}}",
502
+ "bad_limit_hint": "Use a positive whole number, e.g. {{cli}} sessions --limit 50; --all lists everything",
503
+ "none": "No matching sessions on this machine",
504
+ "list_footer": "{{count}} listed. Details: {{cli}} sessions show <ID>; continue: {{cli}} sessions resume <ID>; export: {{cli}} sessions export <ID> -o <dir>",
505
+ "more_messages": "{{more}} more messages; export them all: {{cli}} sessions export {{id}} -o <dir>",
506
+ "no_resume": "No resume command; open it in the app",
507
+ "cannot_resume": "{{tool}} sessions have no resume command",
508
+ "cannot_resume_hint": "Open the session in the {{tool}} app",
509
+ "cwd_missing": "The session's directory {{dir}} no longer exists; starting in the current directory",
510
+ "run_failed": "Could not start {{command}}: {{error}}",
511
+ "run_failed_hint": "Check that the tool is installed, or run it yourself: {{line}}",
512
+ "export_needs_output": "Export needs an output directory",
513
+ "export_bad_path": "Unexpected export file name: {{path}}",
514
+ "export_hint": "For example {{cli}} sessions export <ID> -o ./sessions",
515
+ "role": {
516
+ "user": "User",
517
+ "assistant": "Assistant",
518
+ "tool": "Tool"
519
+ },
520
+ "field": {
521
+ "tool": "Tool",
522
+ "id": "Session ID",
523
+ "started": "Started",
524
+ "updated": "Last update",
525
+ "cwd": "Directory",
526
+ "provider": "Provider",
527
+ "messages": "Messages",
528
+ "file": "File",
529
+ "resume": "Resume"
530
+ }
531
+ },
463
532
  "base": {
464
533
  "current": "Current gateway address: {{url}}",
465
534
  "current_mark": "current",
@@ -701,7 +770,7 @@
701
770
  },
702
771
  "status": {
703
772
  "summary": "Show the current configuration",
704
- "description": "Shows the key, gateway address, language, models, and whether each tool is configured.",
773
+ "description": "Shows the key and its balance, gateway address, language, models, and whether each tool is configured.",
705
774
  "ex": {
706
775
  "show": "Show the current configuration",
707
776
  "json": "Print JSON for scripts"
@@ -713,7 +782,7 @@
713
782
  },
714
783
  "doctor": {
715
784
  "summary": "Find configuration and network problems",
716
- "description": "Checks the key, network, proxy, installed tools and their configs, and says how to fix what it finds.",
785
+ "description": "Checks the key, balance, network, proxy, installed tools and their configs, and says how to fix what it finds.",
717
786
  "opt": {
718
787
  "deep": "If Claude Code is installed, also send one real request through it (uses a little quota)",
719
788
  "report": "After the checks, upload diagnostics (results, tool versions, settings and recent run history; no keys or conversations) and get a code for support"
@@ -731,13 +800,17 @@
731
800
  },
732
801
  "bench": {
733
802
  "summary": "Measure gateway latency",
734
- "description": "Requests the model list from the current gateway address a few times with the saved key and reports timings.",
803
+ "description": "Requests the model list from the current gateway address a few times with the saved key and reports timings; with --stream it also sends one real streaming request and reports the first-token time and whether the stream ends properly.",
735
804
  "opt": {
736
- "samples": "Number of requests (1–50)"
805
+ "samples": "Number of requests (1–50)",
806
+ "stream": "Also send one real streaming request: first-token time and whether the stream ends properly (costs a few tokens)",
807
+ "model": "Model for the streaming request (default: picked from what the key can use)"
737
808
  },
738
809
  "ex": {
739
810
  "run": "Send 5 requests",
740
811
  "samples": "Send only 3 requests",
812
+ "stream": "Latency, then one streaming request",
813
+ "stream_model": "Streaming request with a given model",
741
814
  "json": "Print JSON"
742
815
  },
743
816
  "when": "When things feel slow, or to see the current address latency.",
@@ -833,6 +906,41 @@
833
906
  "undo": "Revert a move you do not want"
834
907
  }
835
908
  },
909
+ "sessions": {
910
+ "summary": "Browse the coding sessions on this machine",
911
+ "description": "Lists the sessions Claude Code, Codex and Claude Desktop (Cowork / Code) keep on this machine, most recently updated first.\nShow a session, print or run the command that continues it, or export it as Markdown. Read-only: no session file is changed.",
912
+ "opt": {
913
+ "tool": "Only this tool's sessions (claude-code / codex / claude-desktop)",
914
+ "dir": "Only sessions started in this directory or below",
915
+ "here": "Only sessions started in the current directory or below",
916
+ "limit": "How many to list (default 20)",
917
+ "all": "List all of them",
918
+ "messages": "How many messages to show (default 6)",
919
+ "run": "Run the resume command (default: print it)",
920
+ "output": "Directory to export into (created if missing; existing files are kept)",
921
+ "tools": "Include tool call arguments and outputs (each clipped to 2000 characters)"
922
+ },
923
+ "list": "List sessions (default)",
924
+ "show": "Show a session's details and first messages",
925
+ "resume": "Print or run the command that continues a session",
926
+ "export": "Export sessions as Markdown",
927
+ "ex": {
928
+ "list": "List the 20 most recent sessions",
929
+ "codex": "Only Codex sessions",
930
+ "here": "Only sessions of the current directory",
931
+ "show": "Show a session (the first few ID characters are enough)",
932
+ "resume": "Continue the session in its original directory",
933
+ "export": "Export two sessions into ./sessions",
934
+ "json": "JSON output for scripts",
935
+ "show_more": "Show the first 20 messages",
936
+ "resume_print": "Only print the resume command to run yourself",
937
+ "export_tools": "Include tool call arguments and outputs in the export"
938
+ },
939
+ "when": "To find an earlier conversation, continue it, or keep a copy.",
940
+ "alt": {
941
+ "codex_sessions": "Old sessions missing from the Codex list: move them to the current provider"
942
+ }
943
+ },
836
944
  "lang": {
837
945
  "summary": "Change the interface language",
838
946
  "description": "Show or set the language {{product}} uses (zh_CN / en_US).",