@vibeapi/api-helper 0.0.118 → 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.
@@ -1 +1 @@
1
- import o from"chalk";import t from"inquirer";import{i18n as n}from"./i18n.js";import{isNonInteractive as e,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:n}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return n}export async function postApplyPrompts(t){const l=!e()&&process.stdin.isTTY;for(const e of t){const t=s(e)?.pendingElevation?.();if(!t)continue;if(!(i()||l&&await p(t.prompt)))continue;const r=s(e)?.displayName??e;console.log(o.gray(" "+n.t("setup.elevation_running"))),console.log(t.run()?o.green(" √ "+n.t("setup.elevation_done",{tool:r})):o.yellow(" ! "+n.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(n.t("setup.offer_upgrade",{tools:o})))for(const o of f)await a(o)}for(const e of t){const t=s(e);if(!t?.runningApp?.isRunning())continue;if(!await p(n.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+n.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+n.t("setup.restart_failed",{tool:t.displayName})))}}
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})))}}
@@ -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 e}from"fs";import{chmod as a,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 b}from"stream";import{createGunzip as $}from"zlib";import{toolHome as y}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=y(),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 b{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),a=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(a)&&!this.found&&(this.found=!0,this.writing=e(this.outPath,{mode:493}),0===n)){const t=this.writing;this.writing=null,await new Promise(r=>t.end(()=>r()))}}}}export async function updateNativeFromMirror(b){const y=nativePlatformKey();if(!y)throw new Error(`no native Claude Code build for ${process.platform}-${process.arch}`);const{versionsDir:P,launcher:R}=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}}(R,P))throw new Error(`${R} was not created by the native installer; not replacing it`);const k=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code`,2e4),M=k["dist-tags"]?.[b]??k["dist-tags"]?.latest;if(!M||!/^\d+\.\d+\.\d+$/.test(M))throw new Error("npmmirror returned no usable version");const D=await x(`${NPM_MIRROR}/@anthropic-ai/claude-code-${y}/${M}`,2e4),N=D.dist?.tarball,T=D.dist?.integrity;if(!N||!T?.startsWith("sha512-"))throw new Error("npmmirror returned no tarball or integrity");await o(P,{recursive:!0});const _=`${process.pid}.${Date.now()}`,A=w(P,`${M}.download.${_}`),O=w(P,`${M}.tmp.${_}`);try{const c=await v(N,{signal:AbortSignal.timeout(9e5)});if(!c.ok||!c.body)throw new Error(`${N}: HTTP ${c.status}`);const d=t("sha512"),b=new g({transform(t,r,i){d.update(t),i(null,t)}});if(await p(m.fromWeb(c.body),b,e(A)),`sha512-${d.digest("base64")}`!==T)throw new Error("downloaded package does not match its sha512 integrity");const k=new E(["package/claude","package/claude.exe"],O);if(await p(i(A),$(),k),!k.found)throw new Error("package has no claude binary");await a(O,493);let D=!1;try{const r=await x(`https://downloads.claude.ai/claude-code-releases/${M}/manifest.json`,8e3),e=r.platforms?.[y]?.checksum;if(e){if(await async function(r){const e=t("sha256");return await p(i(r),e),e.digest("hex")}(O)!==e)throw new Error("binary does not match Anthropic's release checksum");D=!0}}catch(t){if(t instanceof Error&&t.message.includes("release checksum"))throw t}const _=w(P,M);return await f(O,_),await async function(t,i){await o(l(i),{recursive:!0});const e=`${process.pid}.${Date.now()}`;if("win32"!==process.platform){const r=`${i}.tmp.${e}`;await u(t,r);try{await f(r,i)}catch(t){throw await h(r,{force:!0}),t}return}const a=`${i}.new.${e}.${r(4).toString("hex")}`;await n(t,a);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(a,{force:!0}),t}try{await f(a,i)}catch(t){throw c&&await f(c,i).catch(()=>{}),await h(a,{force:!0}),t}}(_,R),{version:M,manifestVerified:D}}finally{await h(A,{force:!0}),await h(O,{force:!0})}}
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})}}
@@ -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. */
@@ -402,6 +402,9 @@
402
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"
403
403
  },
404
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",
405
408
  "restart_hint": "Fully quit Claude Desktop and reopen it for the change to take effect",
406
409
  "managed_conflict": "Managed configuration found at {{path}}; Claude Desktop prefers it and ignores local settings. Remove that profile / policy first, then relaunch",
407
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",
@@ -402,6 +402,9 @@
402
402
  "unload_other_key": "Codex 仍在用另一个 API Key 登录(不是 {{brand}} 的 Key,没有动它)。改用 ChatGPT 订阅:运行 codex logout,再运行 codex login"
403
403
  },
404
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 即可",
405
408
  "restart_hint": "完全退出 Claude 桌面端再重新打开才会生效",
406
409
  "managed_conflict": "检测到托管配置 {{path}},Claude Desktop 会优先使用它并忽略本地配置;请先移除该描述文件 / 策略再重启",
407
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.118",
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",