@vibeapi/api-helper 0.0.116 → 0.0.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/setup.js +1 -1
- package/dist/lib/post-apply.js +1 -1
- package/dist/lib/tool-manager.d.ts +7 -0
- package/dist/lib/tool-manager.js +1 -1
- package/dist/lib/tools/claude-desktop-cli.d.ts +34 -0
- package/dist/lib/tools/claude-desktop-cli.js +1 -0
- package/dist/lib/tools/claude-desktop.js +1 -1
- package/dist/lib/tools/claude-native-mirror.d.ts +7 -0
- package/dist/lib/tools/claude-native-mirror.js +1 -1
- package/dist/lib/tools/codex.js +1 -1
- package/dist/lib/tools/grok.js +1 -1
- package/dist/lib/tools/types.d.ts +6 -0
- package/dist/lib/windows-env.d.ts +5 -0
- package/dist/lib/windows-env.js +1 -1
- package/dist/locales/en_US.json +4 -0
- package/dist/locales/zh_CN.json +4 -0
- package/package.json +1 -1
package/dist/commands/setup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"chalk";import{getAdapter as e}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as i}from"./base.js";import{i18n as s}from"../lib/i18n.js";import{configManager as l}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as r,fetchModels as p}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as m,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as f}from"../lib/tools/claude-desktop.js";import{operationError as g,authenticationError as y,networkError as k,EXIT_CODES as _}from"../lib/errors.js";import{isJsonMode as h,isNonInteractive as b,shouldAutoConfirm as w}from"../lib/runtime-options.js";import v from"inquirer";import{logger as x}from"../utils/logger.js";import{forgetInstalls as C}from"../lib/tools/tool-installs.js";import{CLI_NAME as j,brandEnv as K}from"../lib/brand.js";export const DEDICATED_KEYS=[{option:"claudekey",tool:"claude-code",need:"claude"},{option:"codexkey",tool:"codex",need:"gpt"},{option:"grokkey",tool:"grok",need:"grok"},{option:"desktopkey",tool:"claude-desktop",need:"claude"},{option:"agykey",tool:"agy",need:"gemini"},{option:"openclawkey",tool:"openclaw",need:"any",hidden:!0},{option:"hermeskey",tool:"hermes",need:"any",hidden:!0},{option:"atomcodekey",tool:"atomcode",need:"any",hidden:!0}];async function T(o){const e=await p(o);if(!e.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=e.models.map(o=>o.toLowerCase());return{hasClaude:t.some(o=>o.includes("claude")),hasGpt:t.some(o=>o.startsWith("gpt")||o.includes("gpt-")||/^o\d/.test(o)),hasGrok:t.some(o=>o.includes("grok")),hasGemini:t.some(o=>o.startsWith("gemini")),probed:!0}}function $(o,e){switch(e){case"claude":return o.hasClaude;case"gpt":return o.hasGpt;case"grok":return o.hasGrok;case"gemini":return o.hasGemini;default:return!0}}export function parseOnOff(o){const e=(o??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(e)||!["off","false","hide","0","no","n"].includes(e)&&void 0}function G(e){console.log(o.green(e))}function
|
|
1
|
+
import o from"chalk";import{getAdapter as e}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as i}from"./base.js";import{i18n as s}from"../lib/i18n.js";import{configManager as l}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as r,fetchModels as p}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as m,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as f}from"../lib/tools/claude-desktop.js";import{operationError as g,authenticationError as y,networkError as k,EXIT_CODES as _}from"../lib/errors.js";import{isJsonMode as h,isNonInteractive as b,shouldAutoConfirm as w}from"../lib/runtime-options.js";import v from"inquirer";import{logger as x}from"../utils/logger.js";import{forgetInstalls as C}from"../lib/tools/tool-installs.js";import{CLI_NAME as j,brandEnv as K}from"../lib/brand.js";export const DEDICATED_KEYS=[{option:"claudekey",tool:"claude-code",need:"claude"},{option:"codexkey",tool:"codex",need:"gpt"},{option:"grokkey",tool:"grok",need:"grok"},{option:"desktopkey",tool:"claude-desktop",need:"claude"},{option:"agykey",tool:"agy",need:"gemini"},{option:"openclawkey",tool:"openclaw",need:"any",hidden:!0},{option:"hermeskey",tool:"hermes",need:"any",hidden:!0},{option:"atomcodekey",tool:"atomcode",need:"any",hidden:!0}];async function T(o){const e=await p(o);if(!e.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=e.models.map(o=>o.toLowerCase());return{hasClaude:t.some(o=>o.includes("claude")),hasGpt:t.some(o=>o.startsWith("gpt")||o.includes("gpt-")||/^o\d/.test(o)),hasGrok:t.some(o=>o.includes("grok")),hasGemini:t.some(o=>o.startsWith("gemini")),probed:!0}}function $(o,e){switch(e){case"claude":return o.hasClaude;case"gpt":return o.hasGpt;case"grok":return o.hasGrok;case"gemini":return o.hasGemini;default:return!0}}export function parseOnOff(o){const e=(o??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(e)||!["off","false","hide","0","no","n"].includes(e)&&void 0}function G(e){console.log(o.green(e))}function N(e){console.log(o.yellow(e))}function M(e){console.error(o.red(e))}let S=null;function E(o){const e={operation:1,authentication:2,network:3};(!S||e[o]>e[S])&&(S=o)}async function O(e,t){h()||console.log(o.gray(s.t("setup.validating",{label:e})));const i=await r(t);if(i.valid)return!0;const a="zh_CN"===s.getLocale()?"<你的 Key>":"<your key>",n="--key"===e?`${j} setup --key ${a}`:`${j} setup ${e} ${a}`;let p;if("network_error"===i.error?(E("network"),p=k(s.t("setup.unreachable",{url:l.getBaseUrl(),reason:i.message||"network error"}),`${j} base auto`,"network_error")):"invalid_api_key"===i.error?(E("authentication"),p=y(s.t("setup.invalid_key",{label:e,reason:i.message||"invalid"}),n,"invalid_api_key")):(E("operation"),p=g(s.t("setup.invalid_key",{label:e,reason:i.message||i.error||"unknown"}),n,i.error)),h())throw p;return M(p.message),console.log(o.gray(s.t("errors.next_step",{command:p.hint}))),!1}async function A(t,i,r={}){const{oneMillion:p,officialLogin:c,desktopRegistry:u,force:y}=r,k=n[t];try{"claude-desktop"===t&&void 0!==u&&l.setToolSetting("claude-desktop",m,u),"codex"===t&&void 0!==p&&l.setToolSetting("codex",d,p),"claude-desktop"===t&&void 0!==c&&l.setToolSetting("claude-desktop",f,!c),await a.applyPrivateConfig(t,i,l.getModelsForTool(t),{force:y})}catch(o){if(h())throw g(o instanceof Error?o.message:String(o),`${j} setup --key ${"zh_CN"===s.getLocale()?"<你的 Key>":"<your key>"}`,"config_write_failed");const e=(o instanceof Error?o.message:String(o)).replace(/\s+/g," ").trim();return M("× "+s.t("setup.apply_failed",{tool:k.displayName,reason:e})),!1}return a.printApplied(t),y&&console.log(o.gray(" "+s.t("config.force_done",{tool:k.displayName}))),"codex"===t&&void 0!==p&&console.log(o.gray(" "+s.t(p?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),e(t)?.isInstalled&&!a.isToolInstalled(t)&&console.log(o.yellow(" ! "+s.t("setup.gui_preconfigured",{tool:k.displayName,install:k.installCommand}))),!0}export async function setupCommand(r){S=null;const p=r.key?.trim(),m=DEDICATED_KEYS.map(o=>({...o,apiKey:r[o.option]?.trim()})).filter(o=>Boolean(o.apiKey)),f=void 0!==r.oneMillion,g=void 0!==r.claudeOfficial;let y;if(g&&(y=parseOnOff(r.claudeOfficial),void 0===y))return M(s.t("setup.claude_official_invalid",{value:String(r.claudeOfficial)})),void(process.exitCode=1);const k=void 0!==r.desktopRegistry;let E;if(k&&(E=parseOnOff(r.desktopRegistry),void 0===E))return M(s.t("setup.desktop_registry_invalid",{value:String(r.desktopRegistry)})),void(process.exitCode=1);const I=g||k,L=Boolean(p||m.length>0),D={oneMillion:r.oneMillion,officialLogin:y,desktopRegistry:E,force:r.force};if(!L&&(f||I)){if(f){const o=l.getApiKeyForTool("codex");if(o)try{const e=!0===r.oneMillion;l.setToolSetting("codex",d,e);const t=await u(o);c(e,t??void 0),G(s.t(e?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(o){M(s.t("setup.apply_failed",{tool:n.codex.displayName,reason:o instanceof Error?o.message:String(o)})),process.exitCode=1}else M(s.t("setup.codex_1m_missing_key")),process.exitCode=1}if(I){const o=l.getApiKeyForTool("claude-desktop");o?await A("claude-desktop",o,{...D,oneMillion:void 0})?await t(["claude-desktop"]):process.exitCode=1:(M(s.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!L)return M(s.t("setup.usage")),void(process.exitCode=1);await i(p||m[0].apiKey);let R=0,B=0;const F=new Set,P=m.map(o=>({tool:o.tool,label:`--${o.option}`,apiKey:o.apiKey,need:o.need}));for(const o of P){if(!await O(o.label,o.apiKey)){B+=1;continue}const e=await T(o.apiKey);!e.probed||$(e,o.need)?(e.probed||N(s.t("setup.probe_failed",{label:o.label})),l.setToolApiKey(o.tool,o.apiKey),await A(o.tool,o.apiKey,D)?(R+=1,F.add(o.tool)):B+=1):(M(s.t(`setup.key_no_${o.need}`,{label:o.label,tool:n[o.tool].displayName})),B+=1)}if(p){if(!await O("--key",p))return void(process.exitCode=_[S??"operation"]);l.setApiKey(p);const o=await T(p);if(o.probed||N(s.t("setup.probe_failed",{label:"--key"})),o.probed&&!o.hasClaude&&!o.hasGpt&&!o.hasGrok&&!o.hasGemini)return M(s.t("setup.key_no_family")),void(process.exitCode=1);const e=[{tool:"claude-code",capable:o.hasClaude,skipMsg:s.t("setup.skip_no_claude")},{tool:"codex",capable:o.hasGpt,skipMsg:s.t("setup.skip_no_gpt")},{tool:"grok",capable:o.hasGrok,skipMsg:s.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:o.hasClaude&&("darwin"===process.platform||"win32"===process.platform),skipMsg:"",quiet:!0},{tool:"agy",capable:o.hasGemini,skipMsg:s.t("setup.skip_no_gemini")}];for(const o of e)F.has(o.tool)||(o.capable?await A(o.tool,p,D)?(R+=1,F.add(o.tool)):B+=1:o.quiet||N(o.skipMsg))}if(R>0){const t=await async function(t,i,r){const p=t.filter(o=>!e(o)?.isInstalled&&!a.isToolInstalled(o));if(0===p.length)return[];const d=p.map(o=>n[o].displayName).join(s.t("setup.list_separator"));let c=i;if(void 0!==c||h()||(w()?c=!b():process.stdin.isTTY&&(console.log(""),c=(await v.prompt([{type:"confirm",name:"go",message:s.t("setup.install_confirm",{tools:d}),default:!0}])).go)),!c){console.log("");for(const e of p)console.log(o.yellow("! "+s.t("setup.install_hint",{tool:n[e].displayName,install:n[e].installCommand})));return h()||console.log(o.gray(s.t("setup.install_all_hint",{cli:j}))),p}const u=[];for(const e of p){console.log(o.bold(`\n${n[e].displayName}`));try{await a.installTool(e)}catch(o){x.logError(`setup: install ${e}`,o)}C(),a.isToolInstalled(e)||u.push(e)}const m=p.filter(o=>!u.includes(o));m.length>0&&console.log("");for(const o of m){const e=l.getApiKeyForTool(o);e&&await A(o,e,{...r,oneMillion:void 0,force:void 0})}return u}([...F],r.install,D),i=[...F].filter(o=>!e(o)?.isInstalled&&!t.includes(o)),p=i.map(o=>n[o]?.command).filter(Boolean).join(" / "),d=s.t("setup.done",{count:String(R)});G("\n"+(p?`${d}${s.t("setup.done_commands",{commands:p})}`:d)),t.length>0&&N(s.t("setup.still_missing",{tools:t.map(o=>n[o].displayName).join(s.t("setup.list_separator"))}));for(const o of i){const e=a.ensureNativeOnPath(o);e&&G(s.t("setup.path_added",{tool:n[o].displayName,dir:e}))}const c=i.filter(o=>!a.isOnPath(o));if(c.length>0&&!h()&&"1"!==K("PATH_HINT_BY_CALLER")){const e=c.map(o=>n[o].command).join(" / ");console.log(o.yellow(" ! "+s.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:e})))}}B>0&&(M(s.t("setup.failed_summary",{count:String(B)})),h()||console.error(`${j} setup --key ${"zh_CN"===s.getLocale()?"<你的 Key>":"<your key>"}`)),await t([...F]),(B>0||0===R)&&(process.exitCode=_[S??"operation"])}
|
package/dist/lib/post-apply.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"chalk";import t from"inquirer";import{i18n as
|
|
1
|
+
import o from"chalk";import t from"inquirer";import{i18n as e}from"./i18n.js";import{isNonInteractive as n,shouldAutoConfirm as i}from"./runtime-options.js";import{toolManager as r}from"./tool-manager.js";import{getAdapter as s}from"./tools/registry.js";import{upgradeTool as a}from"./tool-upgrade.js";async function p(o){const{ok:e}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return e}export async function postApplyPrompts(t){for(const e of t){const t=await(s(e)?.prepareRuntime?.())??[];for(const e of t)console.log("action"===e.kind?o.yellow(" ! "+e.text):o.green(" √ "+e.text))}const l=!n()&&process.stdin.isTTY;for(const n of t){const t=s(n)?.pendingElevation?.();if(!t)continue;if(!(i()||l&&await p(t.prompt)))continue;const r=s(n)?.displayName??n;console.log(o.gray(" "+e.t("setup.elevation_running"))),console.log(t.run()?o.green(" √ "+e.t("setup.elevation_done",{tool:r})):o.yellow(" ! "+e.t("setup.elevation_failed",{tool:r})))}if(!l)return;const f=t.filter(o=>s(o)?.installSpec&&r.outdatedVersion(o));if(f.length>0){const o=f.map(o=>s(o)?.displayName??o).join("、");if(await p(e.t("setup.offer_upgrade",{tools:o})))for(const o of f)await a(o)}for(const n of t){const t=s(n);if(!t?.runningApp?.isRunning())continue;if(!await p(e.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+e.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+e.t("setup.restart_failed",{tool:t.displayName})))}}
|
|
@@ -40,6 +40,13 @@ export declare class ToolManager {
|
|
|
40
40
|
* `curl | sh` installers can only add the directory to PATH for new terminals.
|
|
41
41
|
*/
|
|
42
42
|
nativeLauncher(toolName: string): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Windows: a native install found by `nativeLauncher` whose directory isn't on PATH (Claude Code's own
|
|
45
|
+
* installer leaves that to the user) counts as installed but `claude` doesn't run. Put the directory on
|
|
46
|
+
* the user PATH so new terminals, and the one-command bootstrap after it refreshes PATH, find it.
|
|
47
|
+
* Returns the directory when it was added.
|
|
48
|
+
*/
|
|
49
|
+
ensureNativeOnPath(toolName: string): string | null;
|
|
43
50
|
installTool(toolName: string): Promise<void>;
|
|
44
51
|
private installToolNow;
|
|
45
52
|
getToolConfig(toolName: string): any;
|
package/dist/lib/tool-manager.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execSync as o,spawnSync as t}from"child_process";import{i18n as n}from"./i18n.js";import e from"inquirer";import r from"chalk";import{logger as
|
|
1
|
+
import{execSync as o,spawnSync as t}from"child_process";import{i18n as n}from"./i18n.js";import e from"inquirer";import r from"chalk";import{logger as i}from"../utils/logger.js";import{configManager as l}from"./config.js";import{getAdapter as s,listAdapters as a,TOOL_ADAPTERS as c}from"./tools/registry.js";import{backupBeforeWrite as d,listBackups as p,restoreBackup as u}from"./tools/backup.js";import{existsSync as m,rmSync as f}from"fs";import{dirname as g,join as h}from"path";import{toolHome as w}from"./tools/types.js";import{stringWidth as y}from"../utils/string-width.js";import{resolveModelCatalog as P}from"./model-catalog.js";import{compareVersions as $,getToolMinVersion as T}from"./model-registry.js";import{findInstalls as v,forgetInstalls as k,installLabel as C,mirrorInstallCommand as E,npmMirrorInstallCommand as _}from"./tools/tool-installs.js";import{mirrorInstall as O}from"./tool-upgrade.js";import{CLI_NAME as S}from"./brand.js";import{track as N,trackStep as M}from"./telemetry.js";import{addUserPathDir as j,broadcastEnvChange as U}from"./windows-env.js";export{$ as compareVersions};function b(o){const t=c[o];return{name:t.name,command:t.command,installCommand:t.installCommand,configPath:t.configPath,displayName:t.displayName,...t.hidden?{hidden:!0}:{},...t.isInstalled?{gui:!0}:{}}}function x(){const o=new Set;for(const t of l.listProfiles()){const n=l.getProfile(t);for(const t of[n.api_key,...Object.values(n.tool_api_keys??{})])t?.trim()&&o.add(t.trim())}return[...o]}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(c).map(o=>[o,b(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(o){if(!SUPPORTED_TOOLS[o])return!1;const t=s(o);if(t?.isInstalled)try{return t.isInstalled()}catch{return!1}return this.isOnPath(o)||null!==this.nativeLauncher(o)}isOnPath(t){const n=SUPPORTED_TOOLS[t];if(!n)return!1;try{const t="win32"===process.platform?`where ${n.command}`:`which ${n.command}`;return o(t,{stdio:"pipe"}),!0}catch{return!1}}nativeLauncher(o){const t=s(o),n=t?.installSpec?.native?.binDir;if(!t||!n)return null;const e="win32"===process.platform?`${t.command}.exe`:t.command,r=h(w(),...n,e);return m(r)?r:null}ensureNativeOnPath(o){if("win32"!==process.platform||this.isOnPath(o))return null;const t=this.nativeLauncher(o);if(!t)return null;const n=g(t);try{if(!j(n))return null}catch(t){return i.logError(`ensureNativeOnPath: ${o}`,t),null}return U(),process.env.Path=`${process.env.Path??""};${n}`,n}async installTool(o){return M(`install:${o}`,()=>this.installToolNow(o))}async installToolNow(t){const i=SUPPORTED_TOOLS[t];if(!i)throw new Error(`Unknown tool: ${t}`);const l=s(t)?.installSpec;if(l&&(l.mirrorScript||l.npmPackage)){const o=E(l);if(await O(l)&&(k(),this.isToolInstalled(t)))return void console.log(r.green("√ "+n.t("wizard.tool_installed")));throw console.log(r.red("× "+n.t("wizard.install_failed"))),console.log(r.yellow(" ! "+n.t("install.manual_command",{tool:i.displayName,command:o}))),l.mirrorScript&&l.npmPackage&&console.log(r.yellow(" ! "+n.t("install.manual_npm",{command:_(l)}))),new Error(`Failed to install ${i.displayName}`)}const a=i.installCommand;console.log(r.gray("$ ")+r.white(a));try{return o(a,{stdio:"inherit"}),k(),void console.log(r.green("√ "+n.t("wizard.tool_installed")))}catch(o){if(console.log(r.red("× "+n.t("wizard.install_failed"))),!/EACCES|EPERM|permission denied/i.test(`${o?.message??""}${o?.stderr??""}`)&&243!==o?.status&&!a.startsWith("npm "))throw new Error(`Failed to install ${i.displayName}: ${o}`);console.log(r.yellow(" ! "+("win32"===process.platform?n.t("install.permission_windows",{command:a}):n.t("install.permission_unix",{command:a}))))}const{action:c}=await e.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===c)throw new Error(n.t("install.user_cancelled"));if(k(),!this.isToolInstalled(t))throw console.log(r.red("× "+n.t("install.still_not_installed",{tool:i.displayName}))),new Error(`${i.displayName} is not installed`);console.log(r.green("√ "+n.t("install.verified_success",{tool:i.displayName})))}getToolConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);try{return t.getConfig()}catch(t){return i.warn(`Failed to read config for ${o}:`,t),i.logError(`ToolManager.getToolConfig(${o})`,t),null}}updateToolConfig(o,t){const n=s(o);if(!n)throw new Error(`Unknown tool: ${o}`);if(!n.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);n.updateConfig(t)}forceResetConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);const n=t.forceResetPaths?t.forceResetPaths():this.backupPathsFor(t),e=[];for(const o of n)m(o)&&(d(o),f(o,{force:!0}),e.push(o));return e}loadPrivateConfig(o,t,n,e,r={}){const i=s(o);if(!i)throw new Error(`Unknown tool: ${o}`);r.force&&this.forceResetConfig(o),i.loadPrivateConfig({apiKey:t,baseUrl:l.getBaseUrl(),models:n,settings:l.getToolSettings(o),...e?{catalog:e}:{}})}async applyPrivateConfig(o,t,n,e={}){const r=this.installedVersion(o);return M(`apply:${o}`,()=>this.applyPrivateConfigNow(o,t,n,e),{...r?{tool_version:r}:{},...e.force?{force:!0}:{}})}async applyPrivateConfigNow(o,t,n,e){const r=s(o);if(!r)throw new Error(`Unknown tool: ${o}`);const i=r.wantsModelCatalog?await P(t):null;e.force?this.loadPrivateConfig(o,t,n,i??void 0,{force:!0}):i?this.loadPrivateConfig(o,t,n,i):this.loadPrivateConfig(o,t,n)}applyNotes(o){const t=s(o);if(!t)return[];const e=[];try{t.applyNotes&&e.push(...t.applyNotes())}catch(t){i.logError(`ToolManager.applyNotes(${o})`,t)}const r=this.outdatedVersion(o);if(r){e.push({kind:"action",text:n.t("tools.version_outdated",{current:r.current,min:r.min,upgrade:t.installSpec?`${S} upgrade ${o}`:t.upgradeCommand||t.installCommand})});for(const o of t.upgradeHints?.(r.min)??[])e.push({kind:"action",text:o})}const l=t.installSpec?v(t.installSpec):[];return l.length>1&&e.push({kind:"action",text:n.t("upgrade.duplicates_note",{count:String(l.length),active:C(l[0]),others:l.slice(1).map(C).join("、"),tool:o})}),e}printApplied(o,t=16){const n=SUPPORTED_TOOLS[o]?.displayName??o,e=this.applyNotes(o);for(const o of e.filter(o=>"detail"===o.kind))i.record(`${n}: ${o.text}`);const l=e.filter(o=>"summary"===o.kind).map(o=>o.text).join(";"),s=" ".repeat(Math.max(1,t-y(n)));console.log(r.green("√ "+n)+(l?s+l:""));for(const o of e.filter(o=>"action"===o.kind))console.log(r.yellow(" ! "+o.text))}installedVersion(o){const n=s(o);if(!n)return null;if(n.installSpec)return v(n.installSpec)[0]?.version??null;if(n.installedVersion)try{return n.installedVersion()}catch(t){return i.logError(`ToolManager.installedVersion(${o})`,t),null}if(n.isInstalled)return null;try{const o={encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]},e=`${("win32"===process.platform?t(`${n.command} --version`,{...o,shell:!0}):t(n.command,["--version"],o)).stdout??""}`.match(/(\d+)\.(\d+)\.(\d+)/);return e?e[0]:null}catch{return null}}outdatedVersion(o){const t=T(o);if(!t)return null;const n=this.installedVersion(o);return n&&$(n,t)<0?{current:n,min:t}:null}unloadPrivateConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);try{t.unloadPrivateConfig({savedKeys:x()}),N(`remove:${o}`,"ok")}catch(t){throw N(`remove:${o}`,"fail",{error:t}),t}let e=[];try{e=t.unloadNotes?.()??[]}catch(t){i.logError(`ToolManager.unloadNotes(${o})`,t)}return e.length>0?e:[{kind:"action",text:n.t("config.reopen_hint",{tool:t.displayName})}]}async clearPinnedModels(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(l.setToolModel(o,null),!t.clearPinnedModels)return[];const n=l.getApiKeyForTool(o),e=t.wantsModelCatalog&&n?await P(n):null;return t.clearPinnedModels(e??void 0)}supportsClearPinnedModels(o){return Boolean(s(o)?.clearPinnedModels)}detectCurrentConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);return t.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(t).map(o=>({file:o,backups:p(o)}))}restorePrivateConfig(o){const t=s(o);if(!t)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(t).map(o=>({file:o,result:u(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return a().filter(o=>!o.hidden).map(o=>b(o.name))}isGitInstalled(){try{const t="win32"===process.platform?"where git":"which git";return o(t,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface DesktopCliPin {
|
|
2
|
+
version: string;
|
|
3
|
+
/** sha256 of the platform's `claude.exe.zst`, as the app records it in `.verified`. */
|
|
4
|
+
checksum: string;
|
|
5
|
+
}
|
|
6
|
+
/** The CLI build an app.asar pins for `platformKey`; null when the layout isn't the one we know. */
|
|
7
|
+
export declare function readDesktopCliPin(asar: Buffer, platformKey: string): DesktopCliPin | null;
|
|
8
|
+
export type DesktopCliResult = {
|
|
9
|
+
status: 'present';
|
|
10
|
+
version: string;
|
|
11
|
+
} | {
|
|
12
|
+
status: 'installed';
|
|
13
|
+
version: string;
|
|
14
|
+
source: 'local' | 'mirror';
|
|
15
|
+
} | {
|
|
16
|
+
status: 'skipped';
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
export interface DesktopCliOptions {
|
|
20
|
+
/** `<userData>` of the app (Claude-3p). */
|
|
21
|
+
userDataDir: string;
|
|
22
|
+
asar: Buffer;
|
|
23
|
+
platformKey: string;
|
|
24
|
+
/** The CLI's native `versions/` dir; a file named after the pinned version there is copied instead of downloading. */
|
|
25
|
+
localVersionsDir?: string;
|
|
26
|
+
/** Called before a download (not for a local copy), e.g. to tell the user why setup pauses. */
|
|
27
|
+
onDownload?: (version: string) => void;
|
|
28
|
+
/** Writes the binary of `version` to the given path (default: npmmirror). */
|
|
29
|
+
download?: (version: string, outPath: string) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/** Puts the pinned CLI build where the app looks; does nothing when it is already there. */
|
|
32
|
+
export declare function installDesktopCli(options: DesktopCliOptions): Promise<DesktopCliResult>;
|
|
33
|
+
/** Windows: give the installed Claude Desktop the CLI build its Code tab needs, from China-reachable sources. */
|
|
34
|
+
export declare function ensureDesktopCli(userDataDir: string, onDownload?: (version: string) => void): Promise<DesktopCliResult>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execFileSync as r}from"child_process";import{existsSync as e,readdirSync as o,readFileSync as t}from"fs";import{copyFile as s,mkdir as n,readFile as a,rename as i,rm as c,writeFile as l}from"fs/promises";import{join as p}from"path";import{logger as u}from"../../utils/logger.js";import{downloadNativeBinary as f,nativeLayout as d,nativePlatformKey as m}from"./claude-native-mirror.js";import{WINDOWS_PACKAGE_FAMILIES as w}from"./desktop-process.js";export function readDesktopCliPin(r,e){const o=r.indexOf('claude-code-releases","sdkWrapperVersion"');if(o<0)return null;const t=r.lastIndexOf("JSON.parse('",o),s=r.indexOf("')",o);if(t<0||s<0)return null;try{const o=JSON.parse(r.subarray(t+12,s).toString("utf-8")),n="string"==typeof o.version?o.version:"",a=o.manifest?.platforms?.[e]?.checksum;return/^\d+\.\d+\.\d+$/.test(n)&&"string"==typeof a&&/^[0-9a-f]{64}$/.test(a)?{version:n,checksum:a}:null}catch{return null}}export async function installDesktopCli(r){const o=readDesktopCliPin(r.asar,r.platformKey);if(!o)return{status:"skipped",reason:"unknown app layout"};const t=r.platformKey.startsWith("win32")?"claude.exe":"claude",u=p(r.userDataDir,"claude-code",o.version,o.checksum.slice(0,12)),d=p(u,t),m=p(u,".verified");try{if(e(d)&&(await a(m,"utf-8")).trim()===o.checksum)return{status:"present",version:o.version}}catch{}await n(u,{recursive:!0});const w=`${d}.partial`;try{await c(m,{force:!0});let t="mirror";const n=r.localVersionsDir?p(r.localVersionsDir,o.version):null;if(n&&e(n))await s(n,w),t="local";else{r.onDownload?.(o.version);const e=r.download??((e,o)=>f(r.platformKey,e,o));await e(o.version,w)}return await i(w,d),await l(m,o.checksum),{status:"installed",version:o.version,source:t}}finally{await c(w,{force:!0}),await c(`${w}.download`,{force:!0})}}export async function ensureDesktopCli(s,n){if("win32"!==process.platform)return{status:"skipped",reason:"not windows"};const a=m();if(!a)return{status:"skipped",reason:"unsupported architecture"};const i=function(){const t=p(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&e(t)){const r=o(t).filter(r=>/^app-\d+\.\d+\.\d+$/.test(r)).sort((r,e)=>e.localeCompare(r,void 0,{numeric:!0}));for(const o of r){const r=p(t,o,"resources","app.asar");if(e(r))return r}}try{const o=r("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${w.map(r=>`'${r}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]});for(const r of o.split(/\r?\n/).map(r=>r.trim()).filter(Boolean)){const o=p(r,"app","resources","app.asar");if(e(o))return o}}catch(r){u.logError("claude-desktop-cli.windowsAsarPath",r)}return null}();return i?installDesktopCli({userDataDir:s,asar:t(i),platformKey:a,localVersionsDir:d().versionsDir,onDownload:n}):{status:"skipped",reason:"app.asar not found"}}
|
|
@@ -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{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f,brand as m,brandEnv as g,CLI_NAME as h}from"../brand.js";import{backupBeforeWrite as y,atomicWriteFile as k}from"./backup.js";import{toolHome as b}from"./types.js";import{canRestartDesktop as _,isDesktopRunning as A,restartDesktop as C,WINDOWS_PACKAGE_FAMILIES as D}from"./desktop-process.js";import{setUserRegistryElevated as E}from"./elevated-reg.js";import{UserActionNeeded as w}from"./types.js";import{baseModelId as M,isClaudeModel as v}from"../model-catalog.js";import{getDefaultModels as O}from"../model-registry.js";const S=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const P=/^[a-f0-9-]{36}$/;function x(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=b(),n=Boolean(g("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 D.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 L=()=>a(claudeDesktopUserDataDir(),"configLibrary"),j=()=>a(L(),"_meta.json"),I=e=>a(L(),`${e}.json`),T=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function $(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return x(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 G(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),y(n),k(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function N(){const e=$(j());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>x(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function R(e){return e?.entries.find(e=>e.name===S&&P.test(e.id))}const K={opus:0,fable:1,sonnet:2,haiku:3};function U(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=U(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function F(e){return U(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>M(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(v)){i=[...new Set(t.models.filter(v).map(M))];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=M(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>K[F(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=U(e),s=U(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(O()).map(e=>M(e||"")));for(const e of n){const t=F(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=F(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 H=null,B=null,J=null,W=null;function V(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 z={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 Z(e){return Object.fromEntries(Object.entries(z).filter(([t])=>!e(t)))}const X="SOFTWARE\\Policies\\Claude",q=`HKCU\\${X}`,Y="HKLM\\SOFTWARE\\Policies\\Claude",Q=`${h}-claude-policy.reg`,ee=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let te=ee;export function setRegRunnerForTests(e){te=e??ee}function oe(){return"win32"===process.platform}function ne(e){let t;try{t=te(["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 re(){if(null===ne(q))return;const e=a(claudeDesktopUserDataDir(),Q);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),y(e),te(["export",q,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function se(e){re();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)];te(["add",q,"/v",t,"/t",e,"/d",n,"/f"])}}function ie(e){const t=ne(q);if(!t)return;re();let o=t.size;for(const n of e)t.has(n)&&(te(["delete",q,"/v",n,"/f"]),o-=1);o<=0&&te(["delete",q,"/f"])}const ae=[...Object.keys(V({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(z)];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 ne(Y)?Y:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const le=/\d+\.\d+\.\d+/;function ce(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const de="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",pe="darwin"!==process.platform&&"win32"!==process.platform||!m().installMirror?.claudeDesktop?"https://claude.ai/download":m().installMirror.claudeDesktop,ue="darwin"===process.platform?`open ${pe}`:"win32"===process.platform?`start ${pe}`:`xdg-open ${pe}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:de,installCommand:ue,configPath:j(),isInstalled:function(){const e=b();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"),...D.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(b(),"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?le.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(b(),"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 ce(t)}const o=D.map(e=>`'${e}'`).join(",");return ce(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=>le.exec(e)?.[0]??[]))}return null},runningApp:_()?{isRunning:A,restart:C}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const o=N()??{appliedId:"",entries:[]};let n=R(o);n||(n={id:l(),name:S},o.entries.push(n));const r=V(e);B=!0===r.disableDeploymentModeChooser,H={models:r.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const s=$(I(n.id))??{};G(I(n.id),{...Z(e=>e in s),...s,...r}),o.appliedId=n.id,G(j(),o);const i=$(T())??{};if("3p"!==i.deploymentMode&&G(T(),{...i,deploymentMode:"3p"}),J=null,W=null,oe()){const o=ne(q),n=ae.filter(e=>o?.has(e)),s={...Z(e=>o?.has(e)??!1),...r},i=!0===e.settings?.force_registry,l=!i&&function(){const e=!Boolean(g("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(b(),"AppData","Local");return!!t(a(e,"AnthropicClaude"))||D.some(o=>t(a(e,"Packages",o,"LocalCache","Local","Claude-3p")))}();try{l?ie(ae):se(s)}catch(e){p.logError("claude-desktop.policy",e),J=e instanceof Error?e.message.trim():String(e),(n.length>0||i)&&(W=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=W;return e?{prompt:u.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!E(X,e.values,e.deleteKey))return!1;const t=ne(q),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(W=null,J=null),o}}:null},unloadPrivateConfig(){if(oe())try{ie(ae)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=N(),t=R(e);if(!e||!t)return;y(I(t.id)),s(I(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(y(j()),s(j(),{force:!0})):G(j(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=N(),t=R(e);if(!t||e?.appliedId!==t.id)return null;const o=$(I(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=N(),o=R(t);if(!o||t?.appliedId!==o.id)return;const n=$(I(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)){if(oe()&&ne(q)?.has("inferenceModels"))try{se({inferenceModels:r})}catch(e){throw p.logError("claude-desktop.refreshModelMenu registry",e),new w(u.t("tools.claude_desktop.refresh_needs_admin"))}G(I(o.id),{...n,inferenceModels:r})}},detectCurrentConfig(){try{if(oe()){const e=ne(q)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=R(N());if(!e)return{apiKey:null};const t=$(I(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=N(),t=R(e);return{meta:e,applied:t?$(I(t.id)):null,desktop:$(T()),...oe()?{policy:Object.fromEntries(ne(q)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;oe()&&ne(q)?.has("inferenceModels")&&(re(),te(["delete",q,"/v","inferenceModels","/f"]),e=!0);const t=R(N()),o=t?$(I(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;G(I(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(H&&H.models.length>0){const{models:t,source:o,groups:n}=H;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.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!==B&&e.push({kind:"detail",text:u.t(B?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),_()&&!A()||e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),W?(e.push({kind:"action",text:u.t(`tools.claude_desktop.policy_stale_${W.action}`)}),e.push({kind:"detail",text:J??""})):J?(e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:J})):oe()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:q})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[j(),T()];try{const t=R(N());t&&e.push(I(t.id))}catch{}return e},backupPaths(){const e=[j(),T()];oe()&&e.push(a(claudeDesktopUserDataDir(),Q));try{const t=R(N());t&&e.push(I(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 b,atomicWriteFile as A}from"./backup.js";import{toolHome as C}from"./types.js";import{canRestartDesktop as D,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 S,isClaudeModel as x}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=C(),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"),G=e=>a(T(),`${e}.json`),N=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function R(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),A(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){u.logError("claude-desktop.writeJson.chmod",e)}}function K(){const e=R($());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 J(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=>S(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(x)){i=[...new Set(t.models.filter(x).map(S))];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=S(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>H[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=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=>S(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 W=null,V=null,z=null,Z=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 q={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 Y(e){return Object.fromEntries(Object.entries(q).filter(([t])=>!e(t)))}const Q="SOFTWARE\\Policies\\Claude",ee=`HKCU\\${Q}`,te="HKLM\\SOFTWARE\\Policies\\Claude",oe=`${_}-claude-policy.reg`,ne=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let re=ne;export function setRegRunnerForTests(e){re=e??ne}function se(){return"win32"===process.platform}function ie(e){let t;try{t=re(["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 ae(){if(null===ie(ee))return;const e=a(claudeDesktopUserDataDir(),oe);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),b(e),re(["export",ee,e,"/y"])}catch(e){u.logError("claude-desktop.snapshotPolicy",e)}}function le(e){ae();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)];re(["add",ee,"/v",t,"/t",e,"/d",n,"/f"])}}function ce(e){const t=ie(ee);if(!t)return;ae();let o=t.size;for(const n of e)t.has(n)&&(re(["delete",ee,"/v",n,"/f"]),o-=1);o<=0&&re(["delete",ee,"/f"])}const de=[...Object.keys(X({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(q)];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 ie(te)?te: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 pe(){const e=C();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 ue=/\d+\.\d+\.\d+/;function fe(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const me="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",ge="darwin"!==process.platform&&"win32"!==process.platform||!h().installMirror?.claudeDesktop?"https://claude.ai/download":h().installMirror.claudeDesktop,ye="darwin"===process.platform?`open ${ge}`:"win32"===process.platform?`start ${ge}`:`xdg-open ${ge}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:me,installCommand:ye,configPath:$(),isInstalled:pe,installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(C(),"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?ue.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(C(),"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 fe(t)}const o=M.map(e=>`'${e}'`).join(",");return fe(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=>ue.exec(e)?.[0]??[]))}return null},prepareRuntime:async function(){if("win32"!==process.platform||!pe())return[];const e=Date.now();try{const t=await m(claudeDesktopUserDataDir(),e=>console.log(p.gray(" "+g.t("tools.claude_desktop.code_cli_downloading",{version:e}))));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:D()?{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);V=!0===r.disableDeploymentModeChooser,W={models:r.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const s=R(G(n.id))??{};U(G(n.id),{...Y(e=>e in s),...s,...r}),o.appliedId=n.id,U($(),o);const i=R(N())??{};if("3p"!==i.deploymentMode&&U(N(),{...i,deploymentMode:"3p"}),z=null,Z=null,se()){const o=ie(ee),n=de.filter(e=>o?.has(e)),s={...Y(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(C(),"AppData","Local");return!!t(a(e,"AnthropicClaude"))||M.some(o=>t(a(e,"Packages",o,"LocalCache","Local","Claude-3p")))}();try{l?ce(de):le(s)}catch(e){u.logError("claude-desktop.policy",e),z=e instanceof Error?e.message.trim():String(e),(n.length>0||i)&&(Z=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=Z;return e?{prompt:g.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!v(Q,e.values,e.deleteKey))return!1;const t=ie(ee),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(Z=null,z=null),o}}:null},unloadPrivateConfig(){if(se())try{ce(de)}catch(e){u.logError("claude-desktop.removePolicy",e)}const e=K(),t=F(e);if(!e||!t)return;b(G(t.id)),s(G(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=R(G(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=R(G(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)){if(se()&&ie(ee)?.has("inferenceModels"))try{le({inferenceModels:r})}catch(e){throw u.logError("claude-desktop.refreshModelMenu registry",e),new O(g.t("tools.claude_desktop.refresh_needs_admin"))}U(G(o.id),{...n,inferenceModels:r})}},detectCurrentConfig(){try{if(se()){const e=ie(ee)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=F(K());if(!e)return{apiKey:null};const t=R(G(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?R(G(t.id)):null,desktop:R(N()),...se()?{policy:Object.fromEntries(ie(ee)??[])}:{}}}catch(e){return u.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;se()&&ie(ee)?.has("inferenceModels")&&(ae(),re(["delete",ee,"/v","inferenceModels","/f"]),e=!0);const t=F(K()),o=t?R(G(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;U(G(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!==V&&e.push({kind:"detail",text:g.t(V?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),D()&&!w()||e.push({kind:"action",text:g.t("tools.claude_desktop.restart_hint")}),Z?(e.push({kind:"action",text:g.t(`tools.claude_desktop.policy_stale_${Z.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})):se()&&e.push({kind:"detail",text:g.t("tools.claude_desktop.registry_written",{key:ee})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:g.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[$(),N()];try{const t=F(K());t&&e.push(G(t.id))}catch{}return e},backupPaths(){const e=[$(),N()];se()&&e.push(a(claudeDesktopUserDataDir(),oe));try{const t=F(K());t&&e.push(G(t.id))}catch{}return e}};
|
|
@@ -37,3 +37,10 @@ export declare function nativeLayout(): {
|
|
|
37
37
|
* left the existing install untouched.
|
|
38
38
|
*/
|
|
39
39
|
export declare function updateNativeFromMirror(channel: string): Promise<NativeMirrorResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Write Claude Code `version`'s binary for platform `key` (see nativePlatformKey) to `outPath`, from
|
|
42
|
+
* npmmirror's `@anthropic-ai/claude-code-<key>` package, checked against the registry's sha512 integrity.
|
|
43
|
+
* The file is the same one Anthropic ships (the npm package and the release `claude.zst` decompress to
|
|
44
|
+
* identical bytes). Throws with a readable reason; `outPath` may be left behind for the caller to remove.
|
|
45
|
+
*/
|
|
46
|
+
export declare function downloadNativeBinary(key: string, version: string, outPath: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as t,randomBytes as r}from"crypto";import{createReadStream as i,createWriteStream as
|
|
1
|
+
import{createHash as t,randomBytes as r}from"crypto";import{createReadStream as i,createWriteStream as a}from"fs";import{chmod as e,copyFile as n,lstat as s,mkdir as o,readlink as c,rename as f,rm as h,symlink as u}from"fs/promises";import{dirname as l,join as w,resolve as d}from"path";import{pipeline as p}from"stream/promises";import{Readable as m,Transform as g,Writable as y}from"stream";import{createGunzip as b}from"zlib";import{toolHome as $}from"./types.js";export const NPM_MIRROR="https://registry.npmmirror.com";let v=(...t)=>fetch(...t);export function setNativeMirrorFetchForTests(t){v=t}export function nativePlatformKey(){const t="x64"===process.arch||"arm64"===process.arch?process.arch:null;if(!t)return null;if("darwin"===process.platform||"win32"===process.platform)return`${process.platform}-${t}`;if("linux"!==process.platform)return null;const r=process.report?.getReport()?.header;return r?.glibcVersionRuntime?`linux-${t}`:`linux-${t}-musl`}export function nativeLayout(){const t=$(),r=process.env.XDG_DATA_HOME?.trim()||w(t,".local","share");return{versionsDir:w(r,"claude","versions"),launcher:w(t,".local","bin","win32"===process.platform?"claude.exe":"claude")}}async function x(t,r){const i=await v(t,{signal:AbortSignal.timeout(r)});if(!i.ok)throw new Error(`${t}: HTTP ${i.status}`);return await i.json()}class E extends y{names;outPath;buffer=Buffer.alloc(0);remaining=0;padding=0;writing=null;found=!1;constructor(t,r){super(),this.names=t,this.outPath=r}_write(t,r,i){this.buffer=this.buffer.length?Buffer.concat([this.buffer,t]):t,this.drain().then(()=>i()).catch(i)}_final(t){this.writing?this.writing.end(()=>t()):t()}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const t=Math.min(this.remaining,this.buffer.length),r=this.buffer.subarray(0,t);if(this.buffer=this.buffer.subarray(t),this.remaining-=t,this.writing&&!this.writing.write(r)&&await new Promise(t=>this.writing.once("drain",()=>t())),0===this.remaining&&this.writing){const t=this.writing;this.writing=null,await new Promise((r,i)=>t.end(t=>t?i(t):r()))}continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const t=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),t.every(t=>0===t))continue;const r=(r,i)=>t.subarray(r,r+i).toString("utf-8").replace(/\0.*$/s,""),i=r(345,155),e=i?`${i}/${r(0,100)}`:r(0,100),n=parseInt(r(124,12).trim()||"0",8),s=r(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,("0"===s||""===s)&&this.names.includes(e)&&!this.found&&(this.found=!0,this.writing=a(this.outPath,{mode:493}),0===n)){const t=this.writing;this.writing=null,await new Promise(r=>t.end(()=>r()))}}}}export async function updateNativeFromMirror(a){const e=nativePlatformKey();if(!e)throw new Error(`no native Claude Code build for ${process.platform}-${process.arch}`);const{versionsDir:m,launcher:g}=nativeLayout();if("win32"!==process.platform&&!await async function(t,r){try{return!!(await s(t)).isSymbolicLink()&&d(l(t),await c(t)).startsWith(d(r)+"/")}catch{return!0}}(g,m))throw new Error(`${g} was not created by the native installer; not replacing it`);const y=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code`,2e4),b=y["dist-tags"]?.[a]??y["dist-tags"]?.latest;if(!b||!/^\d+\.\d+\.\d+$/.test(b))throw new Error("npmmirror returned no usable version");await o(m,{recursive:!0});const $=w(m,`${b}.tmp.${process.pid}.${Date.now()}`);try{await downloadNativeBinary(e,b,$);let a=!1;try{const r=await x(`https://downloads.claude.ai/claude-code-releases/${b}/manifest.json`,8e3),n=r.platforms?.[e]?.checksum;if(n){if(await async function(r){const a=t("sha256");return await p(i(r),a),a.digest("hex")}($)!==n)throw new Error("binary does not match Anthropic's release checksum");a=!0}}catch(t){if(t instanceof Error&&t.message.includes("release checksum"))throw t}const c=w(m,b);return await f($,c),await async function(t,i){await o(l(i),{recursive:!0});const a=`${process.pid}.${Date.now()}`;if("win32"!==process.platform){const r=`${i}.tmp.${a}`;await u(t,r);try{await f(r,i)}catch(t){throw await h(r,{force:!0}),t}return}const e=`${i}.new.${a}.${r(4).toString("hex")}`;await n(t,e);let c=null;try{await s(i),c=`${i}.old.${Date.now()}.${process.pid}`,await f(i,c)}catch(t){if("ENOENT"!==t.code)throw await h(e,{force:!0}),t}try{await f(e,i)}catch(t){throw c&&await f(c,i).catch(()=>{}),await h(e,{force:!0}),t}}(c,g),{version:b,manifestVerified:a}}finally{await h($,{force:!0})}}export async function downloadNativeBinary(r,n,s){const o=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code-${r}/${n}`,2e4),c=o.dist?.tarball,f=o.dist?.integrity;if(!c||!f?.startsWith("sha512-"))throw new Error("npmmirror returned no tarball or integrity");const u=`${s}.download`;try{const r=await v(c,{signal:AbortSignal.timeout(9e5)});if(!r.ok||!r.body)throw new Error(`${c}: HTTP ${r.status}`);const n=t("sha512"),o=new g({transform(t,r,i){n.update(t),i(null,t)}});if(await p(m.fromWeb(r.body),o,a(u)),`sha512-${n.digest("base64")}`!==f)throw new Error("downloaded package does not match its sha512 integrity");const h=new E(["package/claude","package/claude.exe"],s);if(await p(i(u),b(),h),!h.found)throw new Error("package has no claude binary");await e(s,493)}finally{await h(u,{force:!0})}}
|
package/dist/lib/tools/codex.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readFileSync as n,rmSync as r,statSync as i,writeFileSync as l}from"fs";import{join as a}from"path";import*as s from"@iarna/toml";import{logger as c}from"../../utils/logger.js";import{i18n as d}from"../i18n.js";import{backupBeforeWrite as u,atomicWriteFile as m}from"./backup.js";import{pickModel as f,toolHome as p,withV1 as g}from"./types.js";import{getToolDefaultModel as _}from"../model-registry.js";import{configManager as x}from"../config.js";import{maskHome as y,track as h}from"../telemetry.js";import{buildCodexCatalog as k,catalogRejections as b,codexCatalogPath as j,fillFromOlderEntries as v,installedCodexBinaries as w,loadBundledCodexModels as O,loadPreviousCodexModels as C,previousGatewayModelIds as M}from"./codex-catalog.js";import{gatewayModelSpecs as E,modelFamily as N}from"../gateway-models.js";import{
|
|
1
|
+
import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readFileSync as n,rmSync as r,statSync as i,writeFileSync as l}from"fs";import{join as a}from"path";import*as s from"@iarna/toml";import{logger as c}from"../../utils/logger.js";import{i18n as d}from"../i18n.js";import{backupBeforeWrite as u,atomicWriteFile as m}from"./backup.js";import{pickModel as f,toolHome as p,withV1 as g}from"./types.js";import{getToolDefaultModel as _}from"../model-registry.js";import{configManager as x}from"../config.js";import{maskHome as y,track as h}from"../telemetry.js";import{buildCodexCatalog as k,catalogRejections as b,codexCatalogPath as j,fillFromOlderEntries as v,installedCodexBinaries as w,loadBundledCodexModels as O,loadPreviousCodexModels as C,previousGatewayModelIds as M}from"./codex-catalog.js";import{gatewayModelSpecs as E,modelFamily as N}from"../gateway-models.js";import{findInstalls as S,mirrorInstallCommand as A,brandInstallScripts as P}from"./tool-installs.js";const T=()=>a(p(),".codex"),D=()=>a(T(),"config.toml"),I=()=>a(T(),"auth.json"),$="OPENAI_API_KEY",J="custom",L=1e6,K=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function X(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function B(e,t){return X(e[t])||(e[t]={}),e[t]}function q(){if(!t(D()))return{};const e=n(D(),"utf-8");if(!e.trim())return{};try{const t=s.parse(e);if(!X(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 G(){if(!t(I()))return{};try{const e=JSON.parse(n(I(),"utf-8"));return X(e)?e:{}}catch{return{}}}let z=null;function R(e){const t=e.profiles;if(X(t)){for(const[e,o]of Object.entries(t))X(o)&&2===Object.keys(o).length&&o.model_provider===J&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let U=null;const Y=()=>a(x.getConfigDir(),"codex-catalog-check.json");function F(e){const o=j();t(o)&&n(o,"utf-8")===e||(u(o),m(o,e))}function H(t,r){if(0===r.length)return[];const a=function(t,o){const n=e("sha256").update(t);for(const e of o)try{const t=i(e);n.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{n.update(`\0${e}`)}return n.digest("hex")}(t,r);if(function(e){try{return JSON.parse(n(Y(),"utf-8")).signature===e}catch{return!1}}(a))return[];const s=b(j(),r);return 0===s.length&&function(e){try{o(x.getConfigDir(),{recursive:!0}),l(Y(),JSON.stringify({signature:e})+"\n")}catch(e){c.logError("codexAdapter.rememberAccepted",e)}}(a),s}function Q(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===L&&delete e.model_context_window,e.model_auto_compact_token_limit===K&&delete e.model_auto_compact_token_limit}(e);const i=()=>{e.model_catalog_json===j()&&delete e.model_catalog_json,n&&(e.model_context_window=L,e.model_auto_compact_token_limit=K)},l=O()??C();if(!l)return c.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),U="skipped",void i();const a=t.catalog,s=e=>k({bundled:e,specs:E("codex",a?.clientModels),...a?{keyModels:a.models}:{},gatewayAvailable:a?.models??M(),oneMillion:n,selectedModel:o,..."gateway"===a?.oneMillionSource?{claudeOneMillion:a.oneMillion}:{}}),d=e=>JSON.stringify(e,null,2)+"\n";let u=s(l);F(d(u));const m=w(),f=H(d(u),m);if(f.length>0){const e=Date.now();let t=!1;for(const e of f){const o=O(e.bin);if(o){for(const e of[{models:v(u.models,o)},{models:v(s(o).models,o)}])if(F(d(e)),0===H(d(e),m).length){u=e,t=!0;break}if(t)break}}if(h("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:f[0].error,detail:{rejected_by:f.map(e=>({version:e.version,path:y(e.bin)}))}}),!t)return U={rejectedBy:f[0]},r(j(),{force:!0}),void i()}U={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=j()}export function codexCatalogRejection(){let e;try{e=q()}catch{return null}return e.model_catalog_json===j()&&t(j())?b(j())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=q(),n=s.stringify(o),r="string"==typeof o.model?o.model:_("codex");Q(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=s.stringify(o);i!==n&&(u(D()),m(D(),i))}const V={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:P("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:A(V),installSpec:V,configPath:D(),wantsModelCatalog:!0,listedModels(){if(q().model_catalog_json!==j())return null;try{return(JSON.parse(n(j(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){q().model_catalog_json===j()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===U)return 0===S(V).length?[]:[{kind:"action",text:d.t("tools.codex.catalog_skipped")}];if(!U)return[];if("rejectedBy"in U){const{bin:e,version:t}=U.rejectedBy;return[{kind:"action",text:d.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of U.listed)e.set(N(t.id),(e.get(N(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${d.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:d.t("tools.codex.catalog_written",{count:String(U.listed.length),breakdown:t})},{kind:"detail",text:U.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const t=_("codex"),o=f(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=g(e.baseUrl),i=q();i.model=n,i.model_provider=J,i.model_reasoning_effort="high",Q(i,e,n);const l=B(i,"model_providers"),a=X(l[J])?{...l[J]}:{};delete a.env_key;const{experimental_bearer_token:c,...d}=a;l[J]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r},R(i),B(B(i,"tui"),"model_availability_nux")[n]=4;const p=B(i,"features");p.goals=!0,p.js_repl=!1,u(D()),m(D(),s.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...G(),auth_mode:"apikey",[$]:t};u(I()),m(I(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},unloadPrivateConfig(e){if(z=function(e){const t=G(),o="string"==typeof t[$]?t[$].trim():"";if(!o)return X(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[$]:n,auth_mode:i,...l}=t;return u(I()),X(l.tokens)?(m(I(),JSON.stringify({...l,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(r(I(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(D()))return;let o;try{o=q()}catch(e){return void c.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;X(n)&&(delete n[J],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===J&&delete o.model_provider,o.model_catalog_json===j()&&delete o.model_catalog_json,R(o),u(D()),m(D(),s.stringify(o))},unloadNotes(){switch(z){case"chatgpt":return[{kind:"action",text:d.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:d.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:d.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=G()[$];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return q()}catch(e){return c.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[D(),I(),j()]};export function codexAuthProblem(){try{const e=q(),t=e.model_providers?.[J];if(e.model_provider!==J||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=G();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
|
package/dist/lib/tools/grok.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readFileSync as t}from"fs";import{join as o}from"path";import*as r from"@iarna/toml";import{logger as n}from"../../utils/logger.js";import{PROVIDER_KEY as i}from"../brand.js";import{backupBeforeWrite as s,atomicWriteFile as l}from"./backup.js";import{pickModel as
|
|
1
|
+
import{existsSync as e,readFileSync as t}from"fs";import{join as o}from"path";import*as r from"@iarna/toml";import{logger as n}from"../../utils/logger.js";import{PROVIDER_KEY as i}from"../brand.js";import{backupBeforeWrite as s,atomicWriteFile as l}from"./backup.js";import{pickModel as d,toolHome as a,withV1 as m}from"./types.js";import{i18n as f}from"../i18n.js";import{getToolDefaultModel as g}from"../model-registry.js";import{gatewayModelSpecs as u,modelBlurb as c,modelFamily as p,prettyModelName as y}from"../gateway-models.js";import{brandInstallScripts as k,mirrorInstallCommand as b}from"./tool-installs.js";import{OFFICIAL_PLUGINS as h}from"./claude-plugins.js";const j=()=>o(o(a(),".grok"),"config.toml"),_=i,w=`${i}-`,A=["low","medium","high","xhigh"];let v=null;const C=e=>`${w}${e}`,x=e=>e===_||e.startsWith(w),M={command:"grok",npmPackage:"@xai-official/grok",mirrorScript:k("grok"),native:{binDir:[".grok","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".grok","downloads"]]}};function S(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function E(e,t){return S(e[t])||(e[t]={}),e[t]}function O(){if(!e(j()))return{};const o=t(j(),"utf-8");if(!o.trim())return{};try{const e=r.parse(o);if(!S(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.grok/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function P(e){return S(e.model)?Object.entries(e.model).filter(e=>x(e[0])&&S(e[1])):[]}function N(e,t){let o=u("grok",e.catalog?.clientModels);if(e.catalog){const t=new Set(e.catalog.models);o=o.filter(e=>t.has(e.id))}return o.some(e=>e.id===t)?o:[{id:t},...o]}function $(e,t,o){E(e,"endpoints").models_base_url=m(t.baseUrl);const r=E(e,"model"),n=new Map(P(e));for(const e of n.keys())delete r[e];for(const e of o){const o=C(e.id),i={...n.get(o)??{},name:e.name??y(e.id),model:e.id,base_url:m(t.baseUrl),api_key:t.apiKey,context_window:e.contextWindow??5e5,api_backend:"responses",reasoning_summary:"concise"};"grok"===p(e.id)&&(i.supports_reasoning_effort=!0,i.reasoning_efforts=A);const s=c(e.description);s?i.description=s:delete i.description,r[o]=i}}export const grokAdapter={name:"grok",displayName:"Grok Build",command:"grok",installCommand:b(M),installSpec:M,configPath:j(),wantsModelCatalog:!0,loadPrivateConfig(e){const t=O(),o=d(e.models,g("grok")),n=N(e,o);$(t,e,n);const i=e=>e.name??y(e.id);v={names:n.map(i),defaultName:i(n.find(e=>e.id===o))},E(t,"models").default=C(o),function(e){const t=E(e,"plugins"),o=e=>Array.isArray(e)?e.map(String):[],r=o(t.enabled),n=o(t.disabled),i=h.filter(e=>!r.includes(e)&&!n.includes(e));i.length>0&&(t.disabled=[...n,...i])}(t),s(j()),l(j(),r.stringify(t))},unloadPrivateConfig(){if(!e(j()))return;let t;try{t=O()}catch(e){return void n.logError("grokAdapter.unloadPrivateConfig",e)}if(S(t.model)){for(const[e]of P(t))delete t.model[e];0===Object.keys(t.model).length&&delete t.model}S(t.models)&&"string"==typeof t.models.default&&x(t.models.default)&&(delete t.models.default,0===Object.keys(t.models).length&&delete t.models),S(t.endpoints)&&(delete t.endpoints.models_base_url,0===Object.keys(t.endpoints).length&&delete t.endpoints),function(e){if(!S(e.plugins)||!Array.isArray(e.plugins.disabled))return;const t=e.plugins.disabled.filter(e=>!h.includes(e));t.length>0?e.plugins.disabled=t:delete e.plugins.disabled,0===Object.keys(e.plugins).length&&delete e.plugins}(t),s(j()),l(j(),r.stringify(t))},detectCurrentConfig(){try{const e=O(),t=P(e),o=S(e.models)?e.models.default:void 0,r=t.find(([e])=>e===o)?.[1]??t[0]?.[1],n=r?r.api_key:null;return{apiKey:"string"==typeof n&&n.trim()?n:null}}catch{return{apiKey:null}}},listedModels(){try{const e=P(O());return e.length>0?e.map(([,e])=>String(e.model)):null}catch{return null}},refreshModelMenu(e){const t=O(),o=E(t,"models"),n="string"==typeof o.default?o.default:void 0,i=n?P(t).find(([e])=>e===n)?.[1].model:void 0,a="string"==typeof i&&n!==_&&e.catalog.models.includes(i)?i:d(e.models,g("grok")),m=r.stringify(t);$(t,e,N(e,a)),(void 0===n||x(n))&&(o.default=C(a)),r.stringify(t)!==m&&(s(j()),l(j(),r.stringify(t)))},applyNotes:()=>v?[{kind:"summary",text:f.t("tools.grok.menu_written",{count:String(v.names.length),list:v.names.join(f.t("tools.grok.list_separator")),default:v.defaultName})}]:[],getConfig(){try{return O()}catch(e){return n.logError("grokAdapter.getConfig",e),null}}};
|
|
@@ -109,6 +109,12 @@ export interface ToolAdapter {
|
|
|
109
109
|
isInstalled?(): boolean;
|
|
110
110
|
/** Localized feedback about the last successful `loadPrivateConfig`; see {@link ApplyNote}. */
|
|
111
111
|
applyNotes?(): ApplyNote[];
|
|
112
|
+
/**
|
|
113
|
+
* Slow work after the config is written that the tool needs to run, such as downloading a component the
|
|
114
|
+
* tool would otherwise fetch from a host customers can't reach. Run by `post-apply.ts` (also non-interactive);
|
|
115
|
+
* returns notes to print, an empty list when there was nothing to do. Must not throw.
|
|
116
|
+
*/
|
|
117
|
+
prepareRuntime?(): Promise<ApplyNote[]>;
|
|
112
118
|
/** Write this CLI's provider config into the tool. */
|
|
113
119
|
loadPrivateConfig(ctx: ToolApplyContext): void;
|
|
114
120
|
/** Remove this CLI's provider config from the tool, leaving other config intact. */
|
|
@@ -10,6 +10,11 @@ type RegRunner = (args: string[]) => string;
|
|
|
10
10
|
export declare function setWindowsEnvRunnersForTests(runner: RegRunner | null, onBroadcast?: () => void): void;
|
|
11
11
|
/** 读注册表里的值;不存在时为 undefined(reg query 以非零退出)。 */
|
|
12
12
|
export declare function readWindowsEnv(scope: EnvScope, name: string): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* 把一个目录加到用户级 Path 末尾(已有就不动),保留原来的值类型:Path 通常是 REG_EXPAND_SZ,
|
|
15
|
+
* 写成 REG_SZ 会让里面的 %USERPROFILE% 之类不再展开。返回是否改了。
|
|
16
|
+
*/
|
|
17
|
+
export declare function addUserPathDir(dir: string): boolean;
|
|
13
18
|
export declare function setUserEnv(name: string, value: string): void;
|
|
14
19
|
/** 删除一个值;系统级没有管理员权限时 reg.exe 拒绝,异常交给调用方。 */
|
|
15
20
|
export declare function deleteWindowsEnv(scope: EnvScope, name: string): void;
|
package/dist/lib/windows-env.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as e,spawn as
|
|
1
|
+
import{execFileSync as e,spawn as r}from"child_process";import{logger as n}from"../utils/logger.js";import{brandEnvName as t}from"./brand.js";const o={user:"HKCU\\Environment",machine:"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"},s=r=>e("reg.exe",r,{encoding:"utf-8",windowsHide:!0,timeout:15e3,stdio:["ignore","pipe","pipe"]}),i=()=>{const e=r("powershell.exe",["-NoProfile","-NonInteractive","-Command",`[Environment]::SetEnvironmentVariable('${t("ENV_REFRESH")}',$null,'User')`],{detached:!0,stdio:"ignore",windowsHide:!0});e.on("error",e=>n.logError("windowsEnv.broadcast",e)),e.unref()};let a=s,d=i;export function setWindowsEnvRunnersForTests(e,r){a=e??s,d=e?r??(()=>{}):i}export function readWindowsEnv(e,r){let n;try{n=a(["query",o[e],"/v",r])}catch{return}const t=n.match(new RegExp(String.raw`^\s*${r}\s+REG_(?:EXPAND_)?SZ\s+(.*?)\r?$`,"im"));return t?t[1]:void 0}export function addUserPathDir(e){let r="REG_EXPAND_SZ",n="";try{const e=a(["query",o.user,"/v","Path"]).match(/^\s*Path\s+(REG_(?:EXPAND_)?SZ)\s+(.*?)\r?$/im);e&&([,r,n]=e)}catch{}const t=e=>(e=>e.replace(/%([^%]+)%/g,(e,r)=>process.env[r]??e))(e).replace(/[\\/]+$/,"").toLowerCase();return!n.split(";").some(r=>r&&t(r)===t(e))&&(a(["add",o.user,"/v","Path","/t",r,"/d",n?`${n.replace(/;+$/,"")};${e}`:e,"/f"]),!0)}export function setUserEnv(e,r){a(["add",o.user,"/v",e,"/t","REG_SZ","/d",r,"/f"])}export function deleteWindowsEnv(e,r){a(["delete",o[e],"/v",r,"/f"])}export function broadcastEnvChange(){d()}
|
package/dist/locales/en_US.json
CHANGED
|
@@ -378,6 +378,7 @@
|
|
|
378
378
|
"install_all_hint": "Or run {{cli}} setup --key <your key> --install to install them all",
|
|
379
379
|
"still_missing": "{{tools}} still not installed; run the commands above to install",
|
|
380
380
|
"path_reload": "Run exec \"$SHELL\" -l in this window (or open a new terminal) to use {{commands}}",
|
|
381
|
+
"path_added": "{{tool}} is installed in {{dir}}, which was not on PATH; added it to your user PATH",
|
|
381
382
|
"path_reload_windows": "Open a new terminal to use {{commands}}",
|
|
382
383
|
"list_separator": ", ",
|
|
383
384
|
"gui_preconfigured": "{{tool}} is not installed yet; its config is written already, so it works once you install and open it (download: {{install}})"
|
|
@@ -401,6 +402,9 @@
|
|
|
401
402
|
"unload_other_key": "Codex is still signed in with another API key (not the {{brand}} key, left as is). To use your ChatGPT subscription, run codex logout, then codex login"
|
|
402
403
|
},
|
|
403
404
|
"claude_desktop": {
|
|
405
|
+
"code_cli_downloading": "Downloading Claude Code {{version}} for Claude Desktop's Code tab from the China mirror (about 140 MB)",
|
|
406
|
+
"code_cli_installed": "Claude Desktop's Code tab has Claude Code {{version}} and no longer needs downloads.claude.ai",
|
|
407
|
+
"code_cli_failed": "Could not prepare Claude Code for Claude Desktop's Code tab ({{reason}}); the tab may say it cannot download it. Run setup again when the network is better",
|
|
404
408
|
"restart_hint": "Fully quit Claude Desktop and reopen it for the change to take effect",
|
|
405
409
|
"managed_conflict": "Managed configuration found at {{path}}; Claude Desktop prefers it and ignores local settings. Remove that profile / policy first, then relaunch",
|
|
406
410
|
"registry_written": "Also written to the registry policy {{key}} as a backstop (same as the downloadable .reg); the in-app \"Configure Third-Party Inference\" window will show as managed, and unloading removes it too",
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -378,6 +378,7 @@
|
|
|
378
378
|
"install_all_hint": "也可以运行 {{cli}} setup --key <你的 Key> --install 一次装好",
|
|
379
379
|
"still_missing": "{{tools}} 还没装好,按上方的命令安装后就能用",
|
|
380
380
|
"path_reload": "在当前窗口运行 exec \"$SHELL\" -l(或新开一个终端)后就能用 {{commands}}",
|
|
381
|
+
"path_added": "{{tool}} 已装在 {{dir}},但这个目录不在 PATH 里,已加入用户 PATH",
|
|
381
382
|
"path_reload_windows": "新开一个终端后就能用 {{commands}}",
|
|
382
383
|
"list_separator": "、",
|
|
383
384
|
"gui_preconfigured": "{{tool}} 还没装,配置已先写好,装好后打开就能用(下载:{{install}})"
|
|
@@ -401,6 +402,9 @@
|
|
|
401
402
|
"unload_other_key": "Codex 仍在用另一个 API Key 登录(不是 {{brand}} 的 Key,没有动它)。改用 ChatGPT 订阅:运行 codex logout,再运行 codex login"
|
|
402
403
|
},
|
|
403
404
|
"claude_desktop": {
|
|
405
|
+
"code_cli_downloading": "正在为 Claude 桌面端的 Code 标签页从国内镜像下载 Claude Code {{version}}(约 140 MB)",
|
|
406
|
+
"code_cli_installed": "Claude 桌面端的 Code 标签页已备好 Claude Code {{version}},不用再从 downloads.claude.ai 下载",
|
|
407
|
+
"code_cli_failed": "没能给 Claude 桌面端的 Code 标签页备好 Claude Code({{reason}}),Code 标签页可能提示无法下载;网络好的时候重新运行 setup 即可",
|
|
404
408
|
"restart_hint": "完全退出 Claude 桌面端再重新打开才会生效",
|
|
405
409
|
"managed_conflict": "检测到托管配置 {{path}},Claude Desktop 会优先使用它并忽略本地配置;请先移除该描述文件 / 策略再重启",
|
|
406
410
|
"registry_written": "已同步写入注册表 {{key}} 作为兜底(与官网下载的 .reg 等效);应用内「Configure Third-Party Inference」窗口会显示为托管状态,移除配置时会一并清理",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/api-helper",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.119",
|
|
4
4
|
"description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode, Grok Build) with one unified config",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|