@vibeapi/api-helper 0.0.118 → 0.0.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/post-apply.js +1 -1
- package/dist/lib/tools/claude-desktop-cli.d.ts +46 -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/types.d.ts +6 -0
- package/dist/locales/en_US.json +3 -0
- package/dist/locales/zh_CN.json +3 -0
- package/package.json +1 -1
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})))}}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface DesktopCliPin {
|
|
2
|
+
version: string;
|
|
3
|
+
/** sha256 of what the app would download (`claude.exe.zst`, or `claude.app.tar.zst` for a bundle), as `.verified` records it. */
|
|
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, bundle?: boolean): 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
|
+
/** macOS: the app runs `claude.app` extracted from `claude.app.tar.zst`. */
|
|
25
|
+
bundle?: boolean;
|
|
26
|
+
/** Single-binary platforms: the CLI's native `versions/` dir; a file named after the pinned version there is copied instead of downloading. */
|
|
27
|
+
localVersionsDir?: string;
|
|
28
|
+
/** Called before a download (not for a local copy), e.g. to tell the user why setup pauses. */
|
|
29
|
+
onDownload?: (version: string) => void;
|
|
30
|
+
/** Writes the binary of `version` (a bundle: its `.tar.zst`) to the given path. Default: npmmirror for binaries; required for bundles. */
|
|
31
|
+
download?: (version: string, outPath: string) => Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
/** Puts the pinned CLI build where the app looks; does nothing when it is already there. */
|
|
34
|
+
export declare function installDesktopCli(options: DesktopCliOptions): Promise<DesktopCliResult>;
|
|
35
|
+
/** Tests replace the network. */
|
|
36
|
+
export declare function setDesktopCliFetchForTests(fake: typeof fetch): void;
|
|
37
|
+
export interface EnsureDesktopCliOptions {
|
|
38
|
+
userDataDir: string;
|
|
39
|
+
onDownload?: (version: string) => void;
|
|
40
|
+
/** macOS: the installed app's version, naming the mirror release that carries its bundle. */
|
|
41
|
+
desktopVersion?: string | null;
|
|
42
|
+
/** macOS: the CNB desktop mirror repository (`https://cnb.cool/<group>/claude-desktop`). */
|
|
43
|
+
mirrorRepo?: string | null;
|
|
44
|
+
}
|
|
45
|
+
/** Give the installed Claude Desktop the CLI build its Code tab needs, from China-reachable sources. */
|
|
46
|
+
export declare function ensureDesktopCli(options: EnsureDesktopCliOptions): Promise<DesktopCliResult>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execFileSync as e}from"child_process";import{createHash as t}from"crypto";import{createReadStream as r,createWriteStream as s,existsSync as i,readdirSync as n,readFileSync as o}from"fs";import{copyFile as a,mkdir as l,readFile as c,rename as u,rm as f,writeFile as p}from"fs/promises";import{isAbsolute as d,join as h,normalize as m,sep as w}from"path";import{Readable as g,Writable as b}from"stream";import{pipeline as y}from"stream/promises";import*as v from"zlib";import{logger as k}from"../../utils/logger.js";import{downloadNativeBinary as D,nativeLayout as $,nativePlatformKey as C}from"./claude-native-mirror.js";import{WINDOWS_PACKAGE_FAMILIES as N}from"./desktop-process.js";import{toolHome as x}from"./types.js";export function readDesktopCliPin(e,t,r=!1){const s=e.indexOf('claude-code-releases","sdkWrapperVersion"');if(s<0)return null;const i=e.lastIndexOf("JSON.parse('",s),n=e.indexOf("')",s);if(i<0||n<0)return null;try{const s=JSON.parse(e.subarray(i+12,n).toString("utf-8")),o="string"==typeof s.version?s.version:"",a=s.manifest?.platforms?.[t],l=r?a?.bundle?.checksum:a?.checksum;return/^\d+\.\d+\.\d+$/.test(o)&&"string"==typeof l&&/^[0-9a-f]{64}$/.test(l)?{version:o,checksum:l}:null}catch{return null}}class A extends b{dest;buffer=Buffer.alloc(0);remaining=0;padding=0;file=null;longName=null;collectingLongName=null;constructor(e){super(),this.dest=e}_write(e,t,r){this.buffer=this.buffer.length?Buffer.concat([this.buffer,e]):e,this.drain().then(()=>r()).catch(r)}_final(e){this.remaining>0?e(new Error("archive ends inside an entry")):e()}target(e){const t=m(e);if(!t||d(t)||".."===t||t.startsWith(`..${w}`))throw new Error(`unsafe path in archive: ${e}`);return h(this.dest,t)}async closeFile(){const e=this.file;this.file=null,e&&await new Promise((t,r)=>e.end(e=>e?r(e):t()))}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const e=this.buffer.subarray(0,Math.min(this.remaining,this.buffer.length));this.buffer=this.buffer.subarray(e.length),this.remaining-=e.length,this.collectingLongName?this.collectingLongName.push(e):this.file&&!this.file.write(e)&&await new Promise(e=>this.file.once("drain",()=>e())),0===this.remaining&&(this.collectingLongName&&(this.longName=Buffer.concat(this.collectingLongName).toString("utf-8").replace(/\0.*$/s,""),this.collectingLongName=null),await this.closeFile());continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const e=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),e.every(e=>0===e))continue;const t=(t,r)=>e.subarray(t,t+r).toString("utf-8").replace(/\0.*$/s,""),r=t(345,155);let i=r?`${r}/${t(0,100)}`:t(0,100);null!==this.longName&&(i=this.longName,this.longName=null);const n=parseInt(t(124,12).trim()||"0",8),o=511&parseInt(t(100,8).trim()||"644",8),a=t(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,"L"===a)this.collectingLongName=[];else if("x"===a||"g"===a);else if("5"===a)await l(this.target(i),{recursive:!0});else{if("0"!==a)throw new Error(`unexpected entry type ${a} in archive: ${i}`);{const e=this.target(i);await l(h(e,".."),{recursive:!0}),this.file=s(e,{mode:o||420}),0===n&&await this.closeFile()}}}}}export async function installDesktopCli(e){const s=readDesktopCliPin(e.asar,e.platformKey,e.bundle);if(!s)return{status:"skipped",reason:"unknown app layout"};if(e.bundle&&!e.download)return{status:"skipped",reason:"no bundle mirror"};const n=e.platformKey.startsWith("win32")?"claude.exe":"claude",o=h(e.userDataDir,"claude-code",s.version),d=h(o,s.checksum.slice(0,12)),m=e.bundle?h(d,"claude.app","Contents","MacOS","claude"):h(d,n),w=h(d,".verified");try{if(i(m)&&(await c(w,"utf-8")).trim()===s.checksum)return{status:"present",version:s.version}}catch{}await l(o,{recursive:!0});const g=h(o,`.partial-${s.checksum.slice(0,12)}`),k=`${g}.download`;try{await f(w,{force:!0}),await f(g,{recursive:!0,force:!0});let n="mirror";if(e.bundle){if(e.onDownload?.(s.version),await e.download(s.version,k),await async function(e,s,i){const n=t("sha256");if(await y(r(e),new b({write(e,t,r){n.update(e),r()}})),n.digest("hex")!==s)throw new Error("downloaded bundle does not match the app's checksum");const o=v.createZstdDecompress;if(!o)throw new Error(`Node ${process.version} cannot decompress zstd (needs 22.15 or newer)`);await y(r(e),o(),new A(i))}(k,s.checksum,g),!i(h(g,"claude.app","Contents","MacOS","claude")))throw new Error("bundle has no claude.app");await f(d,{recursive:!0,force:!0}),await u(g,d)}else{const t=e.localVersionsDir?h(e.localVersionsDir,s.version):null;if(t&&i(t))await a(t,g),n="local";else{e.onDownload?.(s.version);const t=e.download??((t,r)=>D(e.platformKey,t,r));await t(s.version,g)}await l(d,{recursive:!0}),await u(g,m)}return await p(w,s.checksum),{status:"installed",version:s.version,source:n}}finally{await f(g,{recursive:!0,force:!0}),await f(k,{force:!0}),await f(`${g}.download`,{force:!0})}}let L=(...e)=>fetch(...e);export function setDesktopCliFetchForTests(e){L=e}export async function ensureDesktopCli(t){const r=C();if(!r)return{status:"skipped",reason:"unsupported architecture"};if("win32"===process.platform){const s=function(){const t=h(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&i(t)){const e=n(t).filter(e=>/^app-\d+\.\d+\.\d+$/.test(e)).sort((e,t)=>t.localeCompare(e,void 0,{numeric:!0}));for(const r of e){const e=h(t,r,"resources","app.asar");if(i(e))return e}}try{const t=e("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${N.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]});for(const e of t.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)){const t=h(e,"app","resources","app.asar");if(i(t))return t}}catch(e){k.logError("claude-desktop-cli.windowsAsarPath",e)}return null}();return s?installDesktopCli({userDataDir:t.userDataDir,asar:o(s),platformKey:r,localVersionsDir:$().versionsDir,onDownload:t.onDownload}):{status:"skipped",reason:"app.asar not found"}}if("darwin"===process.platform){const e=function(){for(const e of["/Applications/Claude.app",h(x(),"Applications","Claude.app")]){const t=h(e,"Contents","Resources","app.asar");if(i(t))return t}return null}();if(!e)return{status:"skipped",reason:"app.asar not found"};const{mirrorRepo:n,desktopVersion:a}=t;return installDesktopCli({userDataDir:t.userDataDir,asar:o(e),platformKey:r,bundle:!0,onDownload:t.onDownload,download:n&&a?(e,t)=>async function(e,t){const r=await L(e,{signal:AbortSignal.timeout(9e5)});if(!r.ok||!r.body)throw new Error(`${e}: HTTP ${r.status}`);await y(g.fromWeb(r.body),s(t))}(`${n}/-/releases/download/v${a}/claude-code-${e}-${r}.app.tar.zst`,t):void 0})}return{status:"skipped",reason:"unsupported platform"}}
|
|
@@ -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 D}from"./backup.js";import{toolHome as A}from"./types.js";import{canRestartDesktop as C,isDesktopRunning as w,restartDesktop as E,WINDOWS_PACKAGE_FAMILIES as M}from"./desktop-process.js";import{setUserRegistryElevated as v}from"./elevated-reg.js";import{UserActionNeeded as O}from"./types.js";import{baseModelId as 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=A(),n=Boolean(k("TEST_HOME")?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return M.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const T=()=>a(claudeDesktopUserDataDir(),"configLibrary"),$=()=>a(T(),"_meta.json"),R=e=>a(T(),`${e}.json`),G=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function N(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return I(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function U(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),b(n),D(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){u.logError("claude-desktop.writeJson.chmod",e)}}function K(){const e=N($());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>I(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function F(e){return e?.entries.find(e=>e.name===j&&L.test(e.id))}const H={opus:0,fable:1,sonnet:2,haiku:3};function B(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=B(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function 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 V=null,W=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=A();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...M.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))}const 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}function me(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(A(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?ue.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(A(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return 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}const ge="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",ye="darwin"!==process.platform&&"win32"!==process.platform||!h().installMirror?.claudeDesktop?"https://claude.ai/download":h().installMirror.claudeDesktop,he="darwin"===process.platform?`open ${ye}`:"win32"===process.platform?`start ${ye}`:`xdg-open ${ye}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:ge,installCommand:he,configPath:$(),isInstalled:pe,installedVersion:me,prepareRuntime:async function(){if("win32"!==process.platform&&"darwin"!==process.platform||!pe())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(p.gray(" "+g.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?me():null,mirrorRepo:h().installMirror?.claudeDesktop?.replace(/\/-\/releases\/latest\/?$/,"")??null});return f("desktop-code-cli","skipped"===t.status?"skip":"ok",{duration_ms:Date.now()-e,detail:{...t}}),"installed"!==t.status?[]:[{kind:"summary",text:g.t("tools.claude_desktop.code_cli_installed",{version:t.version})}]}catch(t){return u.logError("claude-desktop.prepareCodeTabCli",t),f("desktop-code-cli","fail",{duration_ms:Date.now()-e,error:t}),[{kind:"action",text:g.t("tools.claude_desktop.code_cli_failed",{reason:t instanceof Error?t.message:String(t)})}]}},runningApp:C()?{isRunning:w,restart:E}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const o=K()??{appliedId:"",entries:[]};let n=F(o);n||(n={id:l(),name:j},o.entries.push(n));const r=X(e);W=!0===r.disableDeploymentModeChooser,V={models:r.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const s=N(R(n.id))??{};U(R(n.id),{...Y(e=>e in s),...s,...r}),o.appliedId=n.id,U($(),o);const i=N(G())??{};if("3p"!==i.deploymentMode&&U(G(),{...i,deploymentMode:"3p"}),z=null,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(A(),"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(R(t.id)),s(R(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(b($()),s($(),{force:!0})):U($(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=K(),t=F(e);if(!t||e?.appliedId!==t.id)return null;const o=N(R(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=K(),o=F(t);if(!o||t?.appliedId!==o.id)return;const n=N(R(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)){if(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(R(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=N(R(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return u.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=K(),t=F(e);return{meta:e,applied:t?N(R(t.id)):null,desktop:N(G()),...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?N(R(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;U(R(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(V&&V.models.length>0){const{models:t,source:o,groups:n}=V;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!==W&&e.push({kind:"detail",text:g.t(W?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),C()&&!w()||e.push({kind:"action",text:g.t("tools.claude_desktop.restart_hint")}),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=[$(),G()];try{const t=F(K());t&&e.push(R(t.id))}catch{}return e},backupPaths(){const e=[$(),G()];se()&&e.push(a(claudeDesktopUserDataDir(),oe));try{const t=F(K());t&&e.push(R(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})}}
|
|
@@ -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. */
|
package/dist/locales/en_US.json
CHANGED
|
@@ -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 100 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",
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -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}}(约 100 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.
|
|
3
|
+
"version": "0.0.120",
|
|
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",
|