@vibeapi/cli 0.0.153 → 0.0.155

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/components.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "about": "Components the CLI installs and upgrades (its list / install / upgrade commands). Read by src/lib/components.ts and by scripts/gen-components-doc.mjs (docs, the install scripts' README). {scripts} = the brand's installMirror.scripts, {agy} = installMirror.agy, {devtools} = the neutral CNB mirror of fnm / uv / Git for Windows.",
4
- "devtools": "https://cnb.cool/lib/devtools/-/releases/latest/download",
4
+ "devtools": "https://cnb.cool/lib/mirrors/-/releases/download/devtools",
5
5
  "components": [
6
6
  {
7
7
  "id": "node",
@@ -124,8 +124,8 @@
124
124
  "app": "claude-desktop",
125
125
  "latest": { "type": "app" },
126
126
  "sources": {
127
- "darwin": "高速下载站 lib/claude-desktop(官方 dmg)",
128
- "win32": "高速下载站 lib/claude-desktop(官方 MSIX)"
127
+ "darwin": "高速下载站 lib/mirrors 的 claude-desktop(官方 dmg)",
128
+ "win32": "高速下载站 lib/mirrors 的 claude-desktop(官方 MSIX)"
129
129
  },
130
130
  "verify": {
131
131
  "checksum": "SHA256SUMS",
@@ -143,8 +143,8 @@
143
143
  "app": "chatgpt",
144
144
  "latest": { "type": "app" },
145
145
  "sources": {
146
- "darwin": "高速下载站 lib/chatgpt-desktop(官方 dmg,只有 Apple 芯片版)",
147
- "win32": "高速下载站 lib/chatgpt-desktop(Microsoft Store 同一个 MSIX)"
146
+ "darwin": "高速下载站 lib/mirrors 的 chatgpt-desktop(官方 dmg,只有 Apple 芯片版)",
147
+ "win32": "高速下载站 lib/mirrors 的 chatgpt-desktop(Microsoft Store 同一个 MSIX)"
148
148
  },
149
149
  "verify": {
150
150
  "checksum": "SHA256SUMS",
package/dist/brand.json CHANGED
@@ -41,9 +41,9 @@
41
41
  },
42
42
  "installMirror": {
43
43
  "scripts": "https://cnb.cool/vibeapi/install/-/git/raw/main",
44
- "agy": "https://cnb.cool/lib/agy/-/releases/latest/download",
45
- "claudeDesktop": "https://cnb.cool/lib/claude-desktop/-/releases/latest",
46
- "chatgptDesktop": "https://cnb.cool/lib/chatgpt-desktop/-/releases/latest"
44
+ "agy": "https://cnb.cool/lib/mirrors/-/releases/download/agy",
45
+ "claudeDesktop": "https://cnb.cool/lib/mirrors/-/releases/download/claude-desktop",
46
+ "chatgptDesktop": "https://cnb.cool/lib/mirrors/-/releases/download/chatgpt-desktop"
47
47
  },
48
48
  "pluginMarketplaces": [
49
49
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "about": "Components the CLI installs and upgrades (its list / install / upgrade commands). Read by src/lib/components.ts and by scripts/gen-components-doc.mjs (docs, the install scripts' README). {scripts} = the brand's installMirror.scripts, {agy} = installMirror.agy, {devtools} = the neutral CNB mirror of fnm / uv / Git for Windows.",
4
- "devtools": "https://cnb.cool/lib/devtools/-/releases/latest/download",
4
+ "devtools": "https://cnb.cool/lib/mirrors/-/releases/download/devtools",
5
5
  "components": [
6
6
  {
7
7
  "id": "node",
@@ -124,8 +124,8 @@
124
124
  "app": "claude-desktop",
125
125
  "latest": { "type": "app" },
126
126
  "sources": {
127
- "darwin": "高速下载站 lib/claude-desktop(官方 dmg)",
128
- "win32": "高速下载站 lib/claude-desktop(官方 MSIX)"
127
+ "darwin": "高速下载站 lib/mirrors 的 claude-desktop(官方 dmg)",
128
+ "win32": "高速下载站 lib/mirrors 的 claude-desktop(官方 MSIX)"
129
129
  },
130
130
  "verify": {
131
131
  "checksum": "SHA256SUMS",
@@ -143,8 +143,8 @@
143
143
  "app": "chatgpt",
144
144
  "latest": { "type": "app" },
145
145
  "sources": {
146
- "darwin": "高速下载站 lib/chatgpt-desktop(官方 dmg,只有 Apple 芯片版)",
147
- "win32": "高速下载站 lib/chatgpt-desktop(Microsoft Store 同一个 MSIX)"
146
+ "darwin": "高速下载站 lib/mirrors 的 chatgpt-desktop(官方 dmg,只有 Apple 芯片版)",
147
+ "win32": "高速下载站 lib/mirrors 的 chatgpt-desktop(Microsoft Store 同一个 MSIX)"
148
148
  },
149
149
  "verify": {
150
150
  "checksum": "SHA256SUMS",
@@ -1 +1 @@
1
- import t from"chalk";import e from"inquirer";import{existsSync as r,mkdirSync as n,readFileSync as a,writeFileSync as o}from"fs";import{join as p}from"path";import{CLI_NAME as l}from"./brand.js";import{configManager as i}from"./config.js";import{i18n as s}from"./i18n.js";import{canPrompt as u,hasExplicitYes as d,isJsonMode as c}from"./runtime-options.js";import{track as f}from"./telemetry.js";import{compareVersions as m}from"./model-registry.js";import{listAdapters as g}from"./tools/registry.js";import{logger as _}from"../utils/logger.js";function y(){return p(i.getConfigDir(),"app-releases.json")}function w(){try{if(!r(y()))return{};const t=JSON.parse(a(y(),"utf-8"));return t&&"object"==typeof t?t:{}}catch{return{}}}function h(t,e){try{n(i.getConfigDir(),{recursive:!0}),o(y(),JSON.stringify({...w(),[t]:e},null,2))}catch(t){_.logError("app-update.writeCache",t)}}function v(){return g().flatMap(t=>[...t.appInstaller?[{name:t.name,displayName:t.displayName,appInstaller:t.appInstaller,installedVersion:t.installedVersion,runningApp:t.runningApp,tool:t.name}]:[],...(t.companionApps??[]).map(e=>({...e,tool:t.name}))])}export function appTools(){return v().map(t=>t.name)}export function getApp(t){return v().find(e=>e.name===t)}export function appsForTools(t){return v().filter(e=>t.includes(e.tool)).map(t=>t.name)}function k(t){const e=getApp(t);if(!e)throw new Error(`no app installer for ${t}`);return e}function b(t,e,r){try{return e()}catch(e){return _.logError(`app-update.${t}`,e),r}}function q(t){const e=k(t),r=e.appInstaller,n=b("installedPath",()=>null!==r.installedPath(),!1);return{tool:t,name:e.displayName,installed:n,current:n?b("installedVersion",()=>e.installedVersion?.()??null,null):null,running:!!n&&b("isRunning",()=>e.runningApp?.isRunning()??!1,!1),blocker:b("blocker",()=>r.blocker(),"unsupported_platform")}}function x(t,e,r){const n=Boolean(t.installed&&t.current&&e&&m(t.current,e)<0);return{...t,latest:e,outdated:n,...r?{error:r}:{}}}export async function appLatest(t,e={}){const r=w()[t],n=r&&Date.now()-r.checked_at<(r.version?36e5:6e5);if(r&&n&&!e.refresh)return{version:r.version??null,...r.error?{error:r.error}:{}};try{const e=await k(t).appInstaller.latestVersion();return h(t,{version:e,checked_at:Date.now()}),{version:e}}catch(e){const n=e instanceof Error?e.message:String(e);return _.logError(`app-update.latest(${t})`,e),h(t,{error:n,checked_at:Date.now()}),{version:r?.version??null,error:n}}}export async function appStatus(t,e={}){const r=q(t);if(r.blocker&&"classic_install"!==r.blocker)return x(r,null);const{version:n,error:a}=await appLatest(t,e);return x(r,n,a)}export function appStatusWith(t,e){return x(q(t),e.version,e.error)}export function cachedAppStatus(t){const e=q(t);return x(e,e.blocker&&"classic_install"!==e.blocker?null:w()[t]?.version??null)}export function appStatusText(t){return t.installed?t.outdated?s.t("app_update.outdated",{current:t.current,latest:t.latest}):t.current&&t.latest?s.t("app_update.up_to_date",{current:t.current}):t.current??"":t.latest?s.t("app_update.not_installed",{latest:t.latest}):""}export function appUpgradeCommand(t){return`${l} upgrade ${t}`}function C(t){c()?process.stderr.write(t+"\n"):console.log(t)}async function S(t,r=!0){const{ok:n}=await e.prompt([{type:"confirm",name:"ok",message:t,default:r}]);return n}let U={totalMs:12e4,stepMs:1e3};export function setQuitWaitForTests(t){U=t}export async function upgradeApp(e,r={}){const n=Date.now(),a=await appStatus(e,{refresh:r.refresh??!0}),o=a.name,p=(t,r={})=>{const o={...a,result:t,upgraded:"upgraded"===t||"installed"===t,version_after:null,...r};return["up_to_date","unavailable","not_installed","offered"].includes(t)||f(`app-upgrade:${e}`,"failed"===t?"fail":"ok",{duration_ms:Date.now()-n,detail:{result:t,current:a.current,latest:a.latest,...r.reason?{reason:r.reason}:{}}}),o};if(a.blocker)return!r.quiet&&a.installed&&a.outdated&&C(t.yellow(" ! "+s.t("app_update.outdated",{current:a.current,latest:a.latest}))),r.quiet||C(t.yellow(" ! "+s.t(`app_update.blocked_${a.blocker}`,{tool:o,url:k(e).appInstaller.downloadPage,command:appUpgradeCommand(e)}))),p("unavailable",{reason:a.blocker});if(!a.latest)return r.quiet||C(t.yellow(" ! "+s.t("app_update.latest_unknown",{tool:o,reason:a.error??""}))),p("unavailable",{reason:a.error});if(!a.installed&&!r.install)return p("not_installed");const l=Boolean(r.force&&a.installed&&!a.outdated);if(a.installed&&!a.outdated&&!l)return r.quiet||(C(t.green(" √ "+s.t("app_update.already_latest",{tool:o,current:a.current??a.latest}))),C(t.gray(" "+s.t("app_update.force_hint",{command:appUpgradeCommand(e)})))),p("up_to_date");if(l?C(t.yellow(" ! "+s.t("app_update.reinstalling",{tool:o,latest:a.latest,current:a.current??"?"}))):!a.installed||r.askBeforeUpgrade&&!a.running||C(t.yellow(` ! ${o} `+appStatusText(a))),a.installed&&r.askBeforeUpgrade&&!a.running){if(!u()||d())return C(t.gray(" "+s.t("app_update.upgrade_hint",{tool:o,current:a.current??"?",latest:a.latest,command:appUpgradeCommand(e)}))),p("offered");if(!await S(s.t("app_update.upgrade_offer",{tool:o,current:a.current??"?",latest:a.latest})))return C(t.gray(" "+s.t("app_update.declined_hint",{command:appUpgradeCommand(e)}))),p("declined")}let i=!1;if(a.running){if(!u()||d())return C(t.yellow(" ! "+s.t("app_update.running_hint",{tool:o,command:appUpgradeCommand(e)}))),p("left_running");if(!await S(s.t("app_update.offer_quit",{tool:o,latest:a.latest})))return C(t.gray(" "+s.t("app_update.declined_hint",{command:appUpgradeCommand(e)}))),p("declined");const r=k(e).runningApp;if(r?.quit?.())i=!0;else{if(!r?.quitRefused?.()||!await async function(e,r){C(t.yellow(" ! "+s.t("app_update.quit_confirm_in_app",{tool:e,seconds:String(Math.round(U.totalMs/1e3))})));const n=Date.now()+U.totalMs;for(;Date.now()<n;)if(await new Promise(t=>setTimeout(t,U.stepMs)),!r.isRunning())return await new Promise(t=>setTimeout(t,Math.min(1e3,U.stepMs))),!0;return!1}(o,r)){const n=r?.quitRefused?.()?"app_update.quit_refused":"app_update.quit_failed";return C(t.yellow(" ! "+s.t(n,{tool:o,command:appUpgradeCommand(e)}))),p(r?.quitRefused?.()?"left_running":"failed",{reason:r?.quitRefused?.()?"quit_refused":"quit_failed"})}i=!0}}try{const{version:r}=await async function(e){const r=k(e).appInstaller,n=!c()&&Boolean(process.stdout.isTTY);let a=-1;try{return await r.install({downloading:n=>C(t.gray(" "+s.t("app_update.downloading",{tool:k(e).displayName,version:n,size:String(r.sizeMb())}))),percent:e=>{!n||e-a<5||(a=e,process.stdout.write(t.gray(`\r ${e}%`)),e>=100&&process.stdout.write("\n"))},verifying:()=>{n&&a>=0&&a<100&&process.stdout.write("\n"),a=100,C(t.gray(" "+s.t("app_update.verifying",{vendor:r.vendor})))},installing:()=>C(t.gray(" "+s.t("app_update.installing"))),elevate:async()=>!(!u()||!d()&&!await S(s.t("app_update.elevate_offer",{tool:k(e).displayName}))||(C(t.gray(" "+s.t("app_update.elevating"))),0))})}finally{n&&a>=0&&a<100&&process.stdout.write("\n")}}(e);return C(t.green(" √ "+(l?s.t("app_update.reinstalled",{tool:o,version:r}):a.installed?s.t("app_update.upgraded",{tool:o,from:a.current??"?",to:r}):s.t("app_update.installed",{tool:o,version:r})))),i&&!k(e).runningApp?.launch?.()&&C(t.yellow(" ! "+s.t("setup.reopen_hint",{tool:o}))),p(a.installed?"upgraded":"installed",{version_after:r,current:a.current})}catch(r){const n=r instanceof Error?r.message:String(r);return _.logError(`app-update.install(${e})`,r),C(t.yellow(" ! "+(a.installed?s.t("app_update.upgrade_failed",{tool:o,reason:n,current:a.current??"?",command:appUpgradeCommand(e)}):s.t("app_update.install_failed",{tool:o,reason:n,command:appUpgradeCommand(e)})))),i&&k(e).runningApp?.launch?.(),p("failed",{reason:n})}}
1
+ import t from"chalk";import e from"inquirer";import{existsSync as r,mkdirSync as n,readFileSync as a,writeFileSync as o}from"fs";import{join as p}from"path";import{CLI_NAME as l}from"./brand.js";import{configManager as i}from"./config.js";import{i18n as s}from"./i18n.js";import{canPrompt as u,hasExplicitYes as d,isJsonMode as c}from"./runtime-options.js";import{track as f}from"./telemetry.js";import{compareVersions as m}from"./model-registry.js";import{listAdapters as g}from"./tools/registry.js";import{logger as _}from"../utils/logger.js";function y(){return p(i.getConfigDir(),"app-releases.json")}function w(){try{if(!r(y()))return{};const t=JSON.parse(a(y(),"utf-8"));return t&&"object"==typeof t?t:{}}catch{return{}}}function h(t,e){try{n(i.getConfigDir(),{recursive:!0}),o(y(),JSON.stringify({...w(),[t]:e},null,2))}catch(t){_.logError("app-update.writeCache",t)}}function v(){return g().flatMap(t=>[...t.appInstaller?[{name:t.name,displayName:t.displayName,appInstaller:t.appInstaller,installedVersion:t.installedVersion,runningApp:t.runningApp,tool:t.name}]:[],...(t.companionApps??[]).map(e=>({...e,tool:t.name}))])}export function appTools(){return v().map(t=>t.name)}export function getApp(t){return v().find(e=>e.name===t)}export function appsForTools(t){return v().filter(e=>t.includes(e.tool)).map(t=>t.name)}function k(t){const e=getApp(t);if(!e)throw new Error(`no app installer for ${t}`);return e}function b(t,e,r){try{return e()}catch(e){return _.logError(`app-update.${t}`,e),r}}function q(t){const e=k(t),r=e.appInstaller,n=b("installedPath",()=>null!==r.installedPath(),!1);return{tool:t,name:e.displayName,installed:n,current:n?b("installedVersion",()=>e.installedVersion?.()??null,null):null,running:!!n&&b("isRunning",()=>e.runningApp?.isRunning()??!1,!1),blocker:b("blocker",()=>r.blocker(),"unsupported_platform")}}function x(t,e,r){const n=Boolean(t.installed&&t.current&&e&&m(t.current,e)<0);return{...t,latest:e,outdated:n,...r?{error:r}:{}}}export async function appLatest(t,e={}){const r=w()[t],n=r&&Date.now()-r.checked_at<(r.version?36e5:6e5);if(r&&n&&!e.refresh)return{version:r.version??null,...r.error?{error:r.error}:{}};try{const e=await k(t).appInstaller.latestVersion();return h(t,{version:e,checked_at:Date.now()}),{version:e}}catch(e){const n=e instanceof Error?e.message:String(e);return _.logError(`app-update.latest(${t})`,e),h(t,{error:n,checked_at:Date.now()}),{version:r?.version??null,error:n}}}export async function appStatus(t,e={}){const r=q(t);if(r.blocker&&"classic_install"!==r.blocker)return x(r,null);const{version:n,error:a}=await appLatest(t,e);return x(r,n,a)}export function appStatusWith(t,e){return x(q(t),e.version,e.error)}export function cachedAppStatus(t){const e=q(t);return x(e,e.blocker&&"classic_install"!==e.blocker?null:w()[t]?.version??null)}export function appStatusText(t){return t.installed?t.outdated?s.t("app_update.outdated",{current:t.current,latest:t.latest}):t.current&&t.latest?s.t("app_update.up_to_date",{current:t.current}):t.current??"":t.latest?s.t("app_update.not_installed",{latest:t.latest}):""}export function appUpgradeCommand(t){return`${l} upgrade ${t}`}function C(t){c()?process.stderr.write(t+"\n"):console.log(t)}async function S(t,r=!0){const{ok:n}=await e.prompt([{type:"confirm",name:"ok",message:t,default:r}]);return n}let U={totalMs:12e4,stepMs:1e3};export function setQuitWaitForTests(t){U=t}export async function upgradeApp(e,r={}){const n=Date.now(),a=await appStatus(e,{refresh:r.refresh??!0}),o=a.name,p=(t,r={})=>{const o={...a,result:t,upgraded:"upgraded"===t||"installed"===t,version_after:null,...r};return["up_to_date","unavailable","not_installed","offered"].includes(t)||f(`app-upgrade:${e}`,"failed"===t?"fail":"ok",{duration_ms:Date.now()-n,detail:{result:t,current:a.current,latest:a.latest,...r.reason?{reason:r.reason}:{}}}),o};if(a.blocker)return!r.quiet&&a.installed&&a.outdated&&C(t.yellow(" ! "+s.t("app_update.outdated",{current:a.current,latest:a.latest}))),r.quiet||C(t.yellow(" ! "+s.t(`app_update.blocked_${a.blocker}`,{tool:o,url:k(e).appInstaller.downloadPage,command:appUpgradeCommand(e)}))),p("unavailable",{reason:a.blocker});if(!a.latest)return r.quiet||C(t.yellow(" ! "+s.t("app_update.latest_unknown",{tool:o,reason:a.error??""}))),p("unavailable",{reason:a.error});if(!a.installed&&!r.install)return p("not_installed");const l=Boolean(r.force&&a.installed&&!a.outdated);if(a.installed&&!a.outdated&&!l)return r.quiet||(C(t.green(" √ "+s.t("app_update.already_latest",{tool:o,current:a.current??a.latest}))),C(t.gray(" "+s.t("app_update.force_hint",{command:appUpgradeCommand(e)})))),p("up_to_date");if(l?C(t.yellow(" ! "+s.t("app_update.reinstalling",{tool:o,latest:a.latest,current:a.current??"?"}))):!a.installed||r.askBeforeUpgrade&&!a.running||C(t.yellow(` ! ${o} `+appStatusText(a))),a.installed&&r.askBeforeUpgrade&&!a.running){if(!u()||d())return C(t.gray(" "+s.t("app_update.upgrade_hint",{tool:o,current:a.current??"?",latest:a.latest,command:appUpgradeCommand(e)}))),p("offered");if(!await S(s.t("app_update.upgrade_offer",{tool:o,current:a.current??"?",latest:a.latest})))return C(t.gray(" "+s.t("app_update.declined_hint",{command:appUpgradeCommand(e)}))),p("declined")}let i=!1;if(a.running){if(!u()||d())return C(t.yellow(" ! "+s.t("app_update.running_hint",{tool:o,command:appUpgradeCommand(e)}))),p("left_running");if(!await S(s.t("app_update.offer_quit",{tool:o,latest:a.latest})))return C(t.gray(" "+s.t("app_update.declined_hint",{command:appUpgradeCommand(e)}))),p("declined");const r=k(e).runningApp;if(r?.quit?.())i=!0;else{if(!r?.quitRefused?.()||!await async function(e,r){C(t.yellow(" ! "+s.t("app_update.quit_confirm_in_app",{tool:e,seconds:String(Math.round(U.totalMs/1e3))})));const n=Date.now()+U.totalMs;for(;Date.now()<n;)if(await new Promise(t=>setTimeout(t,U.stepMs)),!r.isRunning())return await new Promise(t=>setTimeout(t,Math.min(1e3,U.stepMs))),!0;return!1}(o,r)){const n=r?.quitRefused?.()?"app_update.quit_refused":"app_update.quit_failed";return C(t.yellow(" ! "+s.t(n,{tool:o,command:appUpgradeCommand(e)}))),p(r?.quitRefused?.()?"left_running":"failed",{reason:r?.quitRefused?.()?"quit_refused":"quit_failed"})}i=!0}}try{const{version:r}=await async function(e){const r=k(e).appInstaller,n=!c()&&Boolean(process.stdout.isTTY);let a=-1;try{return await r.install({downloading:n=>C(t.gray(" "+s.t("app_update.downloading",{tool:k(e).displayName,version:n,size:String(r.sizeMb())}))),percent:e=>{!n||e-a<5||(a=e,process.stdout.write(t.gray(`\r ${e}%`)),e>=100&&process.stdout.write("\n"))},verifying:()=>{n&&a>=0&&a<100&&process.stdout.write("\n"),a=100,C(t.gray(" "+s.t("app_update.verifying",{vendor:r.vendor})))},installing:()=>C(t.gray(" "+s.t("app_update.installing"))),reusing:r=>C(t.gray(" "+s.t("app_update.reusing",{tool:k(e).displayName,version:r}))),elevate:async()=>!(!u()||!d()&&!await S(s.t("app_update.elevate_offer",{tool:k(e).displayName}))||(C(t.gray(" "+s.t("app_update.elevating"))),0))})}finally{n&&a>=0&&a<100&&process.stdout.write("\n")}}(e);return C(t.green(" √ "+(l?s.t("app_update.reinstalled",{tool:o,version:r}):a.installed?s.t("app_update.upgraded",{tool:o,from:a.current??"?",to:r}):s.t("app_update.installed",{tool:o,version:r})))),i&&!k(e).runningApp?.launch?.()&&C(t.yellow(" ! "+s.t("setup.reopen_hint",{tool:o}))),p(a.installed?"upgraded":"installed",{version_after:r,current:a.current})}catch(r){const n=r instanceof Error?r.message:String(r);return _.logError(`app-update.install(${e})`,r),C(t.yellow(" ! "+(a.installed?s.t("app_update.upgrade_failed",{tool:o,reason:n,current:a.current??"?",command:appUpgradeCommand(e)}):s.t("app_update.install_failed",{tool:o,reason:n,command:appUpgradeCommand(e)})))),i&&k(e).runningApp?.launch?.(),p("failed",{reason:n})}}
@@ -46,11 +46,11 @@ export interface Brand {
46
46
  installMirror?: {
47
47
  /** Base URL of `<tool>.sh` / `<tool>.ps1` one-line installers. */
48
48
  scripts?: string;
49
- /** Antigravity CLI download mirror carrying install.sh / install.ps1. */
49
+ /** Antigravity CLI download mirror carrying install.sh / install.ps1 (a download directory, see mirror-url.ts). */
50
50
  agy?: string;
51
- /** Claude Desktop download page for macOS / Windows. */
51
+ /** Claude Desktop download mirror for macOS / Windows (a download directory, see mirror-url.ts). */
52
52
  claudeDesktop?: string;
53
- /** ChatGPT desktop app (contains Codex) download page for macOS / Windows. */
53
+ /** ChatGPT desktop app (contains Codex) download mirror for macOS / Windows (see mirror-url.ts). */
54
54
  chatgptDesktop?: string;
55
55
  };
56
56
  /** Claude Code plugin marketplaces tried before Anthropic's GitHub repo. */
@@ -0,0 +1,7 @@
1
+ /**
2
+ * URL of `file` on the mirror. `oldReleaseTag` is only used for an old per-tool repository, whose files of a
3
+ * given version sit in that version's release (`v<version>` for Claude Desktop); a fixed tag holds them itself.
4
+ */
5
+ export declare function mirrorFileUrl(mirror: string, file: string, oldReleaseTag?: string): string;
6
+ /** A page people can open in a browser for the mirror: the release page of its tag. */
7
+ export declare function mirrorPageUrl(mirror: string): string;
@@ -0,0 +1 @@
1
+ const e=/\/-\/releases\/latest$/;export function mirrorFileUrl(r,t,l){const s=r.replace(/\/+$/,"");if(e.test(s)){const r=s.replace(e,"");return l?`${r}/-/releases/download/${l}/${t}`:`${r}/-/releases/latest/download/${t}`}return`${s}/${t}`}export function mirrorPageUrl(e){const r=e.replace(/\/+$/,""),t=/^(.*\/-\/releases)\/download\/([^/]+)$/.exec(r);return t?`${t[1]}/tag/${t[2]}`:r.replace(/\/-\/releases\/latest\/download$/,"/-/releases/latest")}
@@ -2,7 +2,7 @@ import type { CompanionApp } from './types.js';
2
2
  /**
3
3
  * OpenAI's ChatGPT desktop app (it contains Codex: the Codex mode runs a bundled Codex on `CODEX_HOME`, so the
4
4
  * config `vibecli` writes for the `codex` tool is what it uses). Installed / upgraded from the brand's download
5
- * mirror (`installMirror.chatgptDesktop`, e.g. CNB `lib/chatgpt-desktop`: OpenAI's own files from
5
+ * mirror (`installMirror.chatgptDesktop`, e.g. CNB `lib/mirrors` tag `chatgpt-desktop`: OpenAI's own files from
6
6
  * persistent.oaistatic.com/codex-app-prod, re-published hourly with a `SHA256SUMS`).
7
7
  *
8
8
  * - macOS: Apple silicon only (`ChatGPT.dmg`; the Sparkle feed has no Intel build). Bundle id `com.openai.codex`,
@@ -1,6 +1,6 @@
1
1
  import { type DesktopInstallProgress } from './desktop-app-installer.js';
2
2
  /**
3
- * Claude Desktop from the brand's download mirror (`installMirror.claudeDesktop`, e.g. CNB `lib/claude-desktop`:
3
+ * Claude Desktop from the brand's download mirror (`installMirror.claudeDesktop`, e.g. CNB `lib/mirrors` tag `claude-desktop`:
4
4
  * Anthropic's installers re-published byte for byte every hour with a `SHA256SUMS`); the download, checks and
5
5
  * install are the shared ones in `desktop-app-installer.ts`. The Windows mirror only carries the MSIX build, so the
6
6
  * classic Squirrel install (`%LOCALAPPDATA%\AnthropicClaude`) isn't touched.
@@ -9,8 +9,6 @@ import { type DesktopInstallProgress } from './desktop-app-installer.js';
9
9
  export declare const ANTHROPIC_TEAM_ID = "Q6L2SF6YDW";
10
10
  export { DesktopInstallError, parseSha256Sums, setDesktopAppDepsForTests } from './desktop-app-installer.js';
11
11
  export type { DesktopInstallProgress } from './desktop-app-installer.js';
12
- /** `https://cnb.cool/<group>/claude-desktop`, from the brand's download page `…/-/releases/latest`; null without a mirror. */
13
- export declare const desktopMirrorRepo: () => string | null;
14
12
  /** The release file for this machine; null where the mirror has none (Linux). */
15
13
  export declare const desktopAssetName: () => string | null;
16
14
  /** Why vibecli can't install or upgrade the app here (a locale key under `app_update.reason_*`), or null. */
@@ -1 +1 @@
1
- import{existsSync as e}from"fs";import{join as t}from"path";import{brand as s}from"../brand.js";import{WINDOWS_PACKAGE_FAMILIES as o}from"./desktop-process.js";import{createDesktopAppInstaller as r}from"./desktop-app-installer.js";import{toolHome as a}from"./types.js";export const ANTHROPIC_TEAM_ID="Q6L2SF6YDW";export{DesktopInstallError,parseSha256Sums,setDesktopAppDepsForTests}from"./desktop-app-installer.js";const p=r({id:"claude-desktop",mirror:()=>s().installMirror?.claudeDesktop,asset:(e,t)=>"darwin"===e?"Claude-mac-universal.dmg":"win32"===e?t?"Claude-win-arm64.msix":"Claude-win-x64.msix":null,versionFile:()=>"version.txt",releaseTag:e=>`v${e}`,macApp:{source:"Claude.app",installedNames:["Claude.app"],bundleId:"com.anthropic.claudefordesktop",teamId:"Q6L2SF6YDW"},windows:{signer:/(^|,\s*)O="?Anthropic, PBC"?(,|$)/,packageFilter:`@(${o.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName`},extraBlocker:s=>"win32"===s&&function(){const s=process.env.LOCALAPPDATA||t(a(),"AppData","Local");return e(t(s,"AnthropicClaude","Update.exe"))}()?"classic_install":null});export const desktopMirrorRepo=p.mirrorRepo;export const desktopAssetName=p.assetName;export const desktopInstallBlocker=p.blocker;export const fetchLatestDesktopVersion=(e=8e3)=>p.latestVersion(e);export const installedMacApp=p.installedMacApp;export const verifyMacApp=p.verifyMacApp;export const verifyWindowsPackage=p.verifyWindowsPackage;export function installLatestDesktop(e={}){return p.install(e)}
1
+ import{existsSync as e}from"fs";import{join as s}from"path";import{brand as t}from"../brand.js";import{WINDOWS_PACKAGE_FAMILIES as o}from"./desktop-process.js";import{createDesktopAppInstaller as a}from"./desktop-app-installer.js";import{toolHome as r}from"./types.js";export const ANTHROPIC_TEAM_ID="Q6L2SF6YDW";export{DesktopInstallError,parseSha256Sums,setDesktopAppDepsForTests}from"./desktop-app-installer.js";const p=a({id:"claude-desktop",mirror:()=>t().installMirror?.claudeDesktop,asset:(e,s)=>"darwin"===e?"Claude-mac-universal.dmg":"win32"===e?s?"Claude-win-arm64.msix":"Claude-win-x64.msix":null,versionFile:()=>"version.txt",releaseTag:e=>`v${e}`,macApp:{source:"Claude.app",installedNames:["Claude.app"],bundleId:"com.anthropic.claudefordesktop",teamId:"Q6L2SF6YDW"},windows:{signer:/(^|,\s*)O="?Anthropic, PBC"?(,|$)/,packageFilter:`@(${o.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName`},extraBlocker:t=>"win32"===t&&function(){const t=process.env.LOCALAPPDATA||s(r(),"AppData","Local");return e(s(t,"AnthropicClaude","Update.exe"))}()?"classic_install":null});export const desktopAssetName=p.assetName;export const desktopInstallBlocker=p.blocker;export const fetchLatestDesktopVersion=(e=8e3)=>p.latestVersion(e);export const installedMacApp=p.installedMacApp;export const verifyMacApp=p.verifyMacApp;export const verifyWindowsPackage=p.verifyWindowsPackage;export function installLatestDesktop(e={}){return p.install(e)}
@@ -39,10 +39,10 @@ export declare function setDesktopCliFetchForTests(fake: typeof fetch): void;
39
39
  export interface EnsureDesktopCliOptions {
40
40
  userDataDir: string;
41
41
  onDownload?: (version: string) => void;
42
- /** macOS: the installed app's version, naming the mirror release that carries its bundle. */
42
+ /** macOS: the installed app's version (on an old per-tool mirror repository it names the release carrying its bundle). */
43
43
  desktopVersion?: string | null;
44
- /** macOS: the CNB desktop mirror repository (`https://cnb.cool/<group>/claude-desktop`). */
45
- mirrorRepo?: string | null;
44
+ /** macOS: the brand's Claude Desktop download mirror (`installMirror.claudeDesktop`, see mirror-url.ts). */
45
+ mirror?: string | null;
46
46
  }
47
47
  /** Give the installed Claude Desktop the CLI build its Code tab needs, from China-reachable sources. */
48
48
  export declare function ensureDesktopCli(options: EnsureDesktopCliOptions): Promise<DesktopCliResult>;
@@ -1 +1 @@
1
- import{execFileSync as e}from"child_process";import{createHash as r}from"crypto";import{createReadStream as t,createWriteStream as s,existsSync as o,readdirSync as n,readFileSync as i}from"fs";import{copyFile as a,mkdir as l,readFile as c,rename as u,rm as p,writeFile as f}from"fs/promises";import{isAbsolute as d,join as m,normalize as h,sep as w}from"path";import{Readable as g,Writable as b}from"stream";import{pipeline as D}from"stream/promises";import*as v from"zlib";import{logger as y}from"../../utils/logger.js";import{brandEnv as k}from"../brand.js";import{downloadNativeBinary as $,nativeLayout as C,nativePlatformKey as x}from"./claude-native-mirror.js";import{WINDOWS_PACKAGE_FAMILIES as N}from"./desktop-process.js";import{toolHome as A}from"./types.js";import{powerShellEnv as P}from"../powershell-env.js";export function readDesktopCliPin(e,r,t=!1){const s=e.indexOf('claude-code-releases","sdkWrapperVersion"');if(s<0)return null;const o=e.lastIndexOf("JSON.parse('",s),n=e.indexOf("')",s);if(o<0||n<0)return null;try{const s=JSON.parse(e.subarray(o+12,n).toString("utf-8")),i="string"==typeof s.version?s.version:"",a=s.manifest?.platforms?.[r],l=t?a?.bundle?.checksum:a?.checksum;return/^\d+\.\d+\.\d+$/.test(i)&&"string"==typeof l&&/^[0-9a-f]{64}$/.test(l)?{version:i,checksum:l}:null}catch{return null}}function L(){const r=m(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&o(r)){const e=n(r).filter(e=>/^app-\d+\.\d+\.\d+$/.test(e)).sort((e,r)=>r.localeCompare(e,void 0,{numeric:!0}));for(const t of e){const e=m(r,t,"resources","app.asar");if(o(e))return e}}try{const r=e("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${N.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"],env:P()});for(const e of r.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)){const r=m(e,"app","resources","app.asar");if(o(r))return r}}catch(e){y.logError("claude-desktop-cli.windowsAsarPath",e)}return null}function O(){if(k("TEST_HOME")?.trim())return null;for(const e of["/usr/lib/claude-desktop","/opt/Claude"]){const r=m(e,"resources","app.asar");if(o(r))return r}return null}function E(){for(const e of["/Applications/Claude.app",m(A(),"Applications","Claude.app")]){const r=m(e,"Contents","Resources","app.asar");if(o(r))return r}return null}export function installedDesktopAsarPath(){return"win32"===process.platform?L():"darwin"===process.platform?E():"linux"===process.platform?O():null}class j extends b{dest;buffer=Buffer.alloc(0);remaining=0;padding=0;file=null;longName=null;collectingLongName=null;constructor(e){super(),this.dest=e}_write(e,r,t){this.buffer=this.buffer.length?Buffer.concat([this.buffer,e]):e,this.drain().then(()=>t()).catch(t)}_final(e){this.remaining>0?e(new Error("archive ends inside an entry")):e()}target(e){const r=h(e);if(!r||d(r)||".."===r||r.startsWith(`..${w}`))throw new Error(`unsafe path in archive: ${e}`);return m(this.dest,r)}async closeFile(){const e=this.file;this.file=null,e&&await new Promise((r,t)=>e.end(e=>e?t(e):r()))}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const e=this.buffer.subarray(0,Math.min(this.remaining,this.buffer.length));this.buffer=this.buffer.subarray(e.length),this.remaining-=e.length,this.collectingLongName?this.collectingLongName.push(e):this.file&&!this.file.write(e)&&await new Promise(e=>this.file.once("drain",()=>e())),0===this.remaining&&(this.collectingLongName&&(this.longName=Buffer.concat(this.collectingLongName).toString("utf-8").replace(/\0.*$/s,""),this.collectingLongName=null),await this.closeFile());continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const e=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),e.every(e=>0===e))continue;const r=(r,t)=>e.subarray(r,r+t).toString("utf-8").replace(/\0.*$/s,""),t=r(345,155);let o=t?`${t}/${r(0,100)}`:r(0,100);null!==this.longName&&(o=this.longName,this.longName=null);const n=parseInt(r(124,12).trim()||"0",8),i=511&parseInt(r(100,8).trim()||"644",8),a=r(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,"L"===a)this.collectingLongName=[];else if("x"===a||"g"===a);else if("5"===a)await l(this.target(o),{recursive:!0});else{if("0"!==a)throw new Error(`unexpected entry type ${a} in archive: ${o}`);{const e=this.target(o);await l(m(e,".."),{recursive:!0}),this.file=s(e,{mode:i||420}),0===n&&await this.closeFile()}}}}}export async function installDesktopCli(e){const s=readDesktopCliPin(e.asar,e.platformKey,e.bundle);if(!s)return{status:"skipped",reason:"unknown app layout"};if(e.bundle&&!e.download)return{status:"skipped",reason:"no bundle mirror"};const n=e.platformKey.startsWith("win32")?"claude.exe":"claude",i=m(e.userDataDir,"claude-code",s.version),d=m(i,s.checksum.slice(0,12)),h=e.bundle?m(d,"claude.app","Contents","MacOS","claude"):m(d,n),w=m(d,".verified");try{if(o(h)&&(await c(w,"utf-8")).trim()===s.checksum)return{status:"present",version:s.version}}catch{}await l(i,{recursive:!0});const g=m(i,`.partial-${s.checksum.slice(0,12)}`),y=`${g}.download`;try{await p(w,{force:!0}),await p(g,{recursive:!0,force:!0});let n="mirror";if(e.bundle){if(e.onDownload?.(s.version),await e.download(s.version,y),await async function(e,s,o){const n=r("sha256");if(await D(t(e),new b({write(e,r,t){n.update(e),t()}})),n.digest("hex")!==s)throw new Error("downloaded bundle does not match the app's checksum");const i=v.createZstdDecompress;if(!i)throw new Error(`Node ${process.version} cannot decompress zstd (needs 22.15 or newer)`);await D(t(e),i(),new j(o))}(y,s.checksum,g),!o(m(g,"claude.app","Contents","MacOS","claude")))throw new Error("bundle has no claude.app");await p(d,{recursive:!0,force:!0}),await u(g,d)}else{const r=e.localVersionsDir?m(e.localVersionsDir,s.version):null;if(r&&o(r))await a(r,g),n="local";else{e.onDownload?.(s.version);const r=e.download??((r,t)=>$(e.platformKey,r,t));await r(s.version,g)}await l(d,{recursive:!0}),await u(g,h)}return await f(w,s.checksum),{status:"installed",version:s.version,source:n}}finally{await p(g,{recursive:!0,force:!0}),await p(y,{force:!0}),await p(`${g}.download`,{force:!0})}}let S=(...e)=>fetch(...e);export function setDesktopCliFetchForTests(e){S=e}export async function ensureDesktopCli(e){const r=x();if(!r)return{status:"skipped",reason:"unsupported architecture"};if("win32"===process.platform){const t=L();return t?installDesktopCli({userDataDir:e.userDataDir,asar:i(t),platformKey:r,localVersionsDir:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}if("darwin"===process.platform){const t=E();if(!t)return{status:"skipped",reason:"app.asar not found"};const{mirrorRepo:o,desktopVersion:n}=e;return installDesktopCli({userDataDir:e.userDataDir,asar:i(t),platformKey:r,bundle:!0,onDownload:e.onDownload,download:o&&n?(e,t)=>async function(e,r){const t=await S(e,{signal:AbortSignal.timeout(9e5)});if(!t.ok||!t.body)throw new Error(`${e}: HTTP ${t.status}`);await D(g.fromWeb(t.body),s(r))}(`${o}/-/releases/download/v${n}/claude-code-${e}-${r}.app.tar.zst`,t):void 0})}if("linux"===process.platform){const t=O();return t?installDesktopCli({userDataDir:e.userDataDir,asar:i(t),platformKey:r.replace(/-musl$/,""),localVersionsDir:r.endsWith("-musl")?void 0:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}return{status:"skipped",reason:"unsupported platform"}}
1
+ import{execFileSync as e}from"child_process";import{createHash as r}from"crypto";import{createReadStream as t,createWriteStream as s,existsSync as o,readdirSync as i,readFileSync as n}from"fs";import{copyFile as a,mkdir as l,readFile as c,rename as u,rm as f,writeFile as p}from"fs/promises";import{isAbsolute as d,join as m,normalize as h,sep as w}from"path";import{Readable as g,Writable as b}from"stream";import{pipeline as D}from"stream/promises";import*as v from"zlib";import{logger as y}from"../../utils/logger.js";import{brandEnv as k}from"../brand.js";import{downloadNativeBinary as $,nativeLayout as C,nativePlatformKey as x}from"./claude-native-mirror.js";import{WINDOWS_PACKAGE_FAMILIES as N}from"./desktop-process.js";import{toolHome as A}from"./types.js";import{mirrorFileUrl as P}from"../mirror-url.js";import{powerShellEnv as L}from"../powershell-env.js";export function readDesktopCliPin(e,r,t=!1){const s=e.indexOf('claude-code-releases","sdkWrapperVersion"');if(s<0)return null;const o=e.lastIndexOf("JSON.parse('",s),i=e.indexOf("')",s);if(o<0||i<0)return null;try{const s=JSON.parse(e.subarray(o+12,i).toString("utf-8")),n="string"==typeof s.version?s.version:"",a=s.manifest?.platforms?.[r],l=t?a?.bundle?.checksum:a?.checksum;return/^\d+\.\d+\.\d+$/.test(n)&&"string"==typeof l&&/^[0-9a-f]{64}$/.test(l)?{version:n,checksum:l}:null}catch{return null}}function O(){const r=m(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&o(r)){const e=i(r).filter(e=>/^app-\d+\.\d+\.\d+$/.test(e)).sort((e,r)=>r.localeCompare(e,void 0,{numeric:!0}));for(const t of e){const e=m(r,t,"resources","app.asar");if(o(e))return e}}try{const r=e("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${N.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"],env:L()});for(const e of r.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)){const r=m(e,"app","resources","app.asar");if(o(r))return r}}catch(e){y.logError("claude-desktop-cli.windowsAsarPath",e)}return null}function E(){if(k("TEST_HOME")?.trim())return null;for(const e of["/usr/lib/claude-desktop","/opt/Claude"]){const r=m(e,"resources","app.asar");if(o(r))return r}return null}function j(){for(const e of["/Applications/Claude.app",m(A(),"Applications","Claude.app")]){const r=m(e,"Contents","Resources","app.asar");if(o(r))return r}return null}export function installedDesktopAsarPath(){return"win32"===process.platform?O():"darwin"===process.platform?j():"linux"===process.platform?E():null}class S extends b{dest;buffer=Buffer.alloc(0);remaining=0;padding=0;file=null;longName=null;collectingLongName=null;constructor(e){super(),this.dest=e}_write(e,r,t){this.buffer=this.buffer.length?Buffer.concat([this.buffer,e]):e,this.drain().then(()=>t()).catch(t)}_final(e){this.remaining>0?e(new Error("archive ends inside an entry")):e()}target(e){const r=h(e);if(!r||d(r)||".."===r||r.startsWith(`..${w}`))throw new Error(`unsafe path in archive: ${e}`);return m(this.dest,r)}async closeFile(){const e=this.file;this.file=null,e&&await new Promise((r,t)=>e.end(e=>e?t(e):r()))}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const e=this.buffer.subarray(0,Math.min(this.remaining,this.buffer.length));this.buffer=this.buffer.subarray(e.length),this.remaining-=e.length,this.collectingLongName?this.collectingLongName.push(e):this.file&&!this.file.write(e)&&await new Promise(e=>this.file.once("drain",()=>e())),0===this.remaining&&(this.collectingLongName&&(this.longName=Buffer.concat(this.collectingLongName).toString("utf-8").replace(/\0.*$/s,""),this.collectingLongName=null),await this.closeFile());continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const e=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),e.every(e=>0===e))continue;const r=(r,t)=>e.subarray(r,r+t).toString("utf-8").replace(/\0.*$/s,""),t=r(345,155);let o=t?`${t}/${r(0,100)}`:r(0,100);null!==this.longName&&(o=this.longName,this.longName=null);const i=parseInt(r(124,12).trim()||"0",8),n=511&parseInt(r(100,8).trim()||"644",8),a=r(156,1)||"0";if(this.remaining=i,this.padding=(512-i%512)%512,"L"===a)this.collectingLongName=[];else if("x"===a||"g"===a);else if("5"===a)await l(this.target(o),{recursive:!0});else{if("0"!==a)throw new Error(`unexpected entry type ${a} in archive: ${o}`);{const e=this.target(o);await l(m(e,".."),{recursive:!0}),this.file=s(e,{mode:n||420}),0===i&&await this.closeFile()}}}}}export async function installDesktopCli(e){const s=readDesktopCliPin(e.asar,e.platformKey,e.bundle);if(!s)return{status:"skipped",reason:"unknown app layout"};if(e.bundle&&!e.download)return{status:"skipped",reason:"no bundle mirror"};const i=e.platformKey.startsWith("win32")?"claude.exe":"claude",n=m(e.userDataDir,"claude-code",s.version),d=m(n,s.checksum.slice(0,12)),h=e.bundle?m(d,"claude.app","Contents","MacOS","claude"):m(d,i),w=m(d,".verified");try{if(o(h)&&(await c(w,"utf-8")).trim()===s.checksum)return{status:"present",version:s.version}}catch{}await l(n,{recursive:!0});const g=m(n,`.partial-${s.checksum.slice(0,12)}`),y=`${g}.download`;try{await f(w,{force:!0}),await f(g,{recursive:!0,force:!0});let i="mirror";if(e.bundle){if(e.onDownload?.(s.version),await e.download(s.version,y),await async function(e,s,o){const i=r("sha256");if(await D(t(e),new b({write(e,r,t){i.update(e),t()}})),i.digest("hex")!==s)throw new Error("downloaded bundle does not match the app's checksum");const n=v.createZstdDecompress;if(!n)throw new Error(`Node ${process.version} cannot decompress zstd (needs 22.15 or newer)`);await D(t(e),n(),new S(o))}(y,s.checksum,g),!o(m(g,"claude.app","Contents","MacOS","claude")))throw new Error("bundle has no claude.app");await f(d,{recursive:!0,force:!0}),await u(g,d)}else{const r=e.localVersionsDir?m(e.localVersionsDir,s.version):null;if(r&&o(r))await a(r,g),i="local";else{e.onDownload?.(s.version);const r=e.download??((r,t)=>$(e.platformKey,r,t));await r(s.version,g)}await l(d,{recursive:!0}),await u(g,h)}return await p(w,s.checksum),{status:"installed",version:s.version,source:i}}finally{await f(g,{recursive:!0,force:!0}),await f(y,{force:!0}),await f(`${g}.download`,{force:!0})}}let F=(...e)=>fetch(...e);export function setDesktopCliFetchForTests(e){F=e}export async function ensureDesktopCli(e){const r=x();if(!r)return{status:"skipped",reason:"unsupported architecture"};if("win32"===process.platform){const t=O();return t?installDesktopCli({userDataDir:e.userDataDir,asar:n(t),platformKey:r,localVersionsDir:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}if("darwin"===process.platform){const t=j();if(!t)return{status:"skipped",reason:"app.asar not found"};const{mirror:o,desktopVersion:i}=e;return installDesktopCli({userDataDir:e.userDataDir,asar:n(t),platformKey:r,bundle:!0,onDownload:e.onDownload,download:o&&i?(e,t)=>async function(e,r){const t=await F(e,{signal:AbortSignal.timeout(9e5)});if(!t.ok||!t.body)throw new Error(`${e}: HTTP ${t.status}`);await D(g.fromWeb(t.body),s(r))}(P(o,`claude-code-${e}-${r}.app.tar.zst`,`v${i}`),t):void 0})}if("linux"===process.platform){const t=E();return t?installDesktopCli({userDataDir:e.userDataDir,asar:n(t),platformKey:r.replace(/-musl$/,""),localVersionsDir:r.endsWith("-musl")?void 0:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}return{status:"skipped",reason:"unsupported platform"}}
@@ -1 +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 l}from"path";import{randomUUID as a}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import u from"chalk";import{logger as p}from"../../utils/logger.js";import{track as f}from"../telemetry.js";import{ensureDesktopCli as m,installedDesktopAsarPath as g}from"./claude-desktop-cli.js";import{i18n as y}from"../i18n.js";import{BRAND_NAME as h,brand as k,brandEnv as _,CLI_NAME as w}from"../brand.js";import{backupBeforeWrite as b,atomicWriteFile as D}from"./backup.js";import{toolHome as A}from"./types.js";import{canRestartDesktop as C,desktopQuitRefused as v,isDesktopRunning as E,launchDesktop as M,quitDesktop as O,restartDesktop as S,WINDOWS_PACKAGE_FAMILIES as x}from"./desktop-process.js";import{desktopInstallBlocker as j,fetchLatestDesktopVersion as P,installedMacApp as I,installLatestDesktop as L}from"./claude-desktop-app.js";import{setUserRegistryElevated as T}from"./elevated-reg.js";import{UserActionNeeded as $}from"./types.js";import{savedApiKeys as G}from"../config.js";import{isOurGatewayUrl as N,noteWrittenGatewayUrl as U}from"../gateway-addresses.js";import{baseModelId as R,isClaudeModel as K}from"../model-catalog.js";import{getDefaultModels as F}from"../model-registry.js";import{claudeFormat as B,nestedClaudeProjectFiles as H}from"../sessions/formats.js";import{powerShellEnv as J}from"../powershell-env.js";const V=h;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const W=/^[a-f0-9-]{36}$/;function z(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(_("TEST_HOME")?.trim());switch(process.platform){case"darwin":return l(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:l(o,"AppData","Local");return x.map(t=>l(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??l(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:l(o,".config");return l(e,"Claude-3p")}}}const q=()=>l(claudeDesktopUserDataDir(),"configLibrary"),X=()=>l(q(),"_meta.json"),Z=e=>l(q(),`${e}.json`),Y=()=>l(claudeDesktopUserDataDir(),"claude_desktop_config.json");function Q(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return z(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 ee(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){p.logError("claude-desktop.writeJson.chmod",e)}}function te(){const e=Q(X());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>z(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function oe(e){return e?.entries.find(e=>e.name===V&&W.test(e.id))}const ne={opus:0,fable:1,sonnet:2,haiku:3};function re(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=re(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function se(e){return re(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>R(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(K)){i=[...new Set(t.models.filter(K).map(R))];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=R(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const l=e=>ne[se(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=l(e)-l(t);if(0!==n)return n;const r=re(e),s=re(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 a=new Set,c=new Map,d=new Set(Object.values(F()).map(e=>R(e||"")));for(const e of n){const t=se(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=se(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||a.has(t)||(o.isFamilyDefault=!0,a.add(t))}return o})}let ie=null,le=null,ae=null,ce=null,de=null;function ue(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 pe={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}},fe=()=>{if(_("TEST_HOME")?.trim())return null;const e=g();return e?r(e):null};let me=fe,ge=null;export function setDesktopAsarForTests(e){me=void 0===e?fe:()=>e,ge=null}function ye(){if(ge)return ge;let e=null;try{e=me()}catch(e){p.logError("claude-desktop.unsupportedDefaults",e)}return ge=new Set(e?Object.keys(pe).filter(t=>!e.includes(t)):[]),ge}function he(e){const t=ye();return Object.fromEntries(Object.entries(pe).filter(([o])=>!e(o)&&!t.has(o)))}function ke(e){const t={...he(t=>t in e),...e};for(const e of ye())e in t&&JSON.stringify(t[e])===JSON.stringify(pe[e])&&delete t[e];return t}const _e="SOFTWARE\\Policies\\Claude",we=`HKCU\\${_e}`,be="HKLM\\SOFTWARE\\Policies\\Claude",De=`${w}-claude-policy.reg`,Ae=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let Ce=Ae;export function setRegRunnerForTests(e){Ce=e??Ae}function ve(){return"win32"===process.platform}function Ee(e){let t;try{t=Ce(["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 Me(){if(null===Ee(we))return;const e=l(claudeDesktopUserDataDir(),De);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),b(e),Ce(["export",we,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function Oe(e){Me();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)];Ce(["add",we,"/v",t,"/t",e,"/d",n,"/f"])}}function Se(e){const t=Ee(we);if(!t)return;Me();let o=t.size;for(const n of e)t.has(n)&&(Ce(["delete",we,"/v",n,"/f"]),o-=1);o<=0&&Ce(["delete",we,"/f"])}function xe(){const e=!Boolean(_("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:l(A(),"AppData","Local");return!!t(l(e,"AnthropicClaude"))||x.some(o=>t(l(e,"Packages",o,"LocalCache","Local","Claude-3p")))}const je=[...Object.keys(ue({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(pe)];function Pe(e){if(!e||!je.some(t=>e.has(t)))return"none";const t=e.get("inferenceGatewayApiKey")?.trim();return N(e.get("inferenceGatewayBaseUrl"))||t&&G().includes(t)?"ours":"foreign"}function Ie(){const e=Q(Y())?.deploymentMode;if(void 0!==e&&"3p"!==e)return{kind:"mode"};const t=te(),o=t?.entries.find(e=>e.id===t.appliedId);if(!o||o.id===oe(t)?.id)return null;const n=Q(Z(o.id));return n&&(n.inferenceProvider||n.inferenceGatewayBaseUrl)?{kind:"entry",name:o.name}:null}function Le(e){return"mode"===e.kind?y.t("tools.claude_desktop.current_official"):y.t("tools.claude_desktop.current_entry",{name:e.name})}let Te=null,$e=null;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 Ee(be)?be:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}function Ge(){const e=A();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",l(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||l(e,"AppData","Local");o=[l(t,"AnthropicClaude"),l(t,"Programs","Claude"),...x.map(e=>l(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",l(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),l(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))}const Ne=/\d+\.\d+\.\d+/;function Ue(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 Re(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",l(A(),"Applications","Claude.app")]){const o=l(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?Ne.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=l(process.env.LOCALAPPDATA||l(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 Ue(t)}const o=x.map(e=>`'${e}'`).join(",");return Ue(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"],env:J()}).split(/\r?\n/).flatMap(e=>Ne.exec(e)?.[0]??[]))}if("linux"===process.platform)try{const e=c("dpkg-query",["-W","-f=${Version}","claude-desktop"],{encoding:"utf-8",timeout:5e3,stdio:["ignore","pipe","ignore"]});return Ne.exec(e)?.[0]??null}catch{return null}return null}const Ke="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",Fe="darwin"!==process.platform&&"win32"!==process.platform||!k().installMirror?.claudeDesktop?"https://claude.ai/download":k().installMirror.claudeDesktop,Be="darwin"===process.platform?`open ${Fe}`:"win32"===process.platform?`start ${Fe}`:`xdg-open ${Fe}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:Ke,installCommand:Be,configPath:X(),sessions:{format:B,files:()=>H(l(claudeDesktopUserDataDir(),"local-agent-mode-sessions"))},isInstalled:Ge,installedVersion:Re,prepareRuntime:async function(){if(!["win32","darwin","linux"].includes(process.platform)||!Ge())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(u.gray(" "+y.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?Re():null,mirrorRepo:k().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:y.t("tools.claude_desktop.code_cli_installed",{version:t.version})}]}catch(t){return p.logError("claude-desktop.prepareCodeTabCli",t),f("desktop-code-cli","fail",{duration_ms:Date.now()-e,error:t}),[{kind:"action",text:y.t("tools.claude_desktop.code_cli_failed",{reason:t instanceof Error?t.message:String(t)})}]}},runningApp:C()?{isRunning:E,restart:S,quit:O,quitRefused:v,launch:M}:void 0,appInstaller:{vendor:"Anthropic",sizeMb:()=>300,downloadPage:"https://claude.ai/download",blocker:j,latestVersion:()=>P(),installedPath(){if("darwin"===process.platform)return I();if("win32"!==process.platform)return null;try{return Re()?"MSIX":null}catch(e){return p.logError("claude-desktop.installedPath",e),null}},install:e=>L(e)},wantsModelCatalog:!0,loadPrivateConfig(e){const t=te()??{appliedId:"",entries:[]};let o=oe(t);o||(o={id:a(),name:V},t.entries.push(o));const n=ue(e);U(String(n.inferenceGatewayBaseUrl)),le=!0===n.disableDeploymentModeChooser,ie={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=Q(Z(o.id))??{};ee(Z(o.id),{...ke(r),...n});const s=Ie();Te=s&&!e.takeOver?Le(s):null,Te||(t.appliedId=o.id),ee(X(),t);const i=Q(Y())??{};Te||"3p"===i.deploymentMode||ee(Y(),{...i,deploymentMode:"3p"}),ce=null,de=null,ae=null,$e=null;const l=!0===e.settings?.force_registry,c=ve()?Ee(we):null;if(ve()&&"foreign"===Pe(c)&&!l)$e=c?.get("inferenceGatewayBaseUrl")||we;else if(ve()){const t=Ee(we),o=je.filter(e=>t?.has(e)),r={...he(e=>t?.has(e)??!1),...n},s=!0===e.settings?.force_registry,i=!s&&xe();try{i?Se(je):Oe(r),ae=i?"removed":"written"}catch(e){p.logError("claude-desktop.policy",e),ce=e instanceof Error?e.message.trim():String(e),(o.length>0||s)&&(de=i?{action:"remove",values:Object.fromEntries(o.map(e=>[e,null])),deleteKey:o.length===t.size}:{action:"write",values:r,deleteKey:!1})}}},pendingElevation(){const e=de;return e?{prompt:y.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!T(_e,e.values,e.deleteKey))return!1;const t=Ee(we),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(de=null,ce=null),o}}:null},unloadPrivateConfig(){if(ve()&&"ours"===Pe(Ee(we)))try{Se(je)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=te(),t=oe(e);if(!e||!t)return;b(Z(t.id)),s(Z(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(b(X()),s(X(),{force:!0})):ee(X(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=te(),t=oe(e);if(!t||e?.appliedId!==t.id)return null;const o=Q(Z(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=te(),o=oe(t);if(!o||t?.appliedId!==o.id)return;const n=Q(Z(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog),s=JSON.stringify(n.inferenceModels)!==JSON.stringify(r),i={...ke(n),inferenceModels:r},l=JSON.stringify(i)!==JSON.stringify(n);de=null;const a=ve()?Ee(we):null,c="foreign"===Pe(a)&&!0!==e.settings?.force_registry;if(!a?.has("inferenceModels")||c)return void(l&&ee(Z(o.id),i));const d=je.filter(e=>a.has(e)),u=!0!==e.settings?.force_registry&&xe();if(l&&ee(Z(o.id),i),u||s)try{u?Se(je):Oe({inferenceModels:r})}catch(e){throw p.logError("claude-desktop.refreshModelMenu registry",e),de=u?{action:"remove",values:Object.fromEntries(d.map(e=>[e,null])),deleteKey:d.length===a.size}:{action:"write",values:{inferenceModels:r},deleteKey:!1},new $(y.t("tools.claude_desktop.refresh_needs_admin"))}},detectCurrentConfig(){try{if(ve()){const e=Ee(we)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=oe(te());if(!e)return{apiKey:null};const t=Q(Z(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=te(),t=oe(e);return{meta:e,applied:t?Q(Z(t.id)):null,desktop:Q(Y()),...ve()?{policy:Object.fromEntries(Ee(we)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;ve()&&Ee(we)?.has("inferenceModels")&&(Me(),Ce(["delete",we,"/v","inferenceModels","/f"]),e=!0);const t=oe(te()),o=t?Q(Z(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;ee(Z(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(ie&&ie.models.length>0){const{models:t,source:o,groups:n}=ie;e.push({kind:"summary",text:y.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:y.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!==le&&e.push({kind:"detail",text:y.t(le?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),C()&&!E()||e.push({kind:"action",text:y.t("tools.claude_desktop.restart_hint")}),de?(e.push({kind:"action",text:y.t(`tools.claude_desktop.policy_stale_${de.action}`)}),e.push({kind:"detail",text:ce??""})):ce?(e.push({kind:"detail",text:y.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:ce})):ve()&&ae&&e.push({kind:"detail",text:y.t(`tools.claude_desktop.registry_${ae}`,{key:we})}),Te&&e.push({kind:"action",text:y.t("tools.claude_desktop.not_switched",{current:Te})}),$e&&e.push({kind:"action",text:y.t("tools.claude_desktop.foreign_policy",{key:we,url:$e})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:y.t("tools.claude_desktop.managed_conflict",{path:t})}),e},planNotes(){const e=[],t=Ie();if(t&&e.push(y.t("tools.claude_desktop.plan_switch",{current:Le(t)})),ve()){const t=Ee(we);"foreign"===Pe(t)&&e.push(y.t("tools.claude_desktop.plan_foreign_policy",{key:we,url:t?.get("inferenceGatewayBaseUrl")||"-"}))}return e},forceResetPaths(){const e=[X(),Y()];try{const t=oe(te());t&&e.push(Z(t.id))}catch{}return e},backupPaths(){const e=[X(),Y()];ve()&&e.push(l(claudeDesktopUserDataDir(),De));try{const t=oe(te());t&&e.push(Z(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 l}from"path";import{randomUUID as a}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import u from"chalk";import{logger as p}from"../../utils/logger.js";import{track as f}from"../telemetry.js";import{ensureDesktopCli as m,installedDesktopAsarPath as g}from"./claude-desktop-cli.js";import{i18n as y}from"../i18n.js";import{BRAND_NAME as h,brand as k,brandEnv as _,CLI_NAME as w}from"../brand.js";import{mirrorPageUrl as b}from"../mirror-url.js";import{backupBeforeWrite as D,atomicWriteFile as A}from"./backup.js";import{toolHome as C}from"./types.js";import{canRestartDesktop as v,desktopQuitRefused as E,isDesktopRunning as M,launchDesktop as O,quitDesktop as S,restartDesktop as x,WINDOWS_PACKAGE_FAMILIES as j}from"./desktop-process.js";import{desktopInstallBlocker as P,fetchLatestDesktopVersion as I,installedMacApp as L,installLatestDesktop as T}from"./claude-desktop-app.js";import{setUserRegistryElevated as $}from"./elevated-reg.js";import{UserActionNeeded as G}from"./types.js";import{savedApiKeys as N}from"../config.js";import{isOurGatewayUrl as U,noteWrittenGatewayUrl as K}from"../gateway-addresses.js";import{baseModelId as R,isClaudeModel as F}from"../model-catalog.js";import{getDefaultModels as B}from"../model-registry.js";import{claudeFormat as H,nestedClaudeProjectFiles as J}from"../sessions/formats.js";import{powerShellEnv as V}from"../powershell-env.js";const W=h;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const z=/^[a-f0-9-]{36}$/;function q(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(_("TEST_HOME")?.trim());switch(process.platform){case"darwin":return l(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:l(o,"AppData","Local");return j.map(t=>l(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??l(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:l(o,".config");return l(e,"Claude-3p")}}}const X=()=>l(claudeDesktopUserDataDir(),"configLibrary"),Z=()=>l(X(),"_meta.json"),Y=e=>l(X(),`${e}.json`),Q=()=>l(claudeDesktopUserDataDir(),"claude_desktop_config.json");function ee(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return q(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 te(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),D(n),A(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function oe(){const e=ee(Z());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>q(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function ne(e){return e?.entries.find(e=>e.name===W&&z.test(e.id))}const re={opus:0,fable:1,sonnet:2,haiku:3};function se(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=se(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function ie(e){return se(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>R(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(F)){i=[...new Set(t.models.filter(F).map(R))];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=R(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const l=e=>re[ie(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=l(e)-l(t);if(0!==n)return n;const r=se(e),s=se(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 a=new Set,c=new Map,d=new Set(Object.values(B()).map(e=>R(e||"")));for(const e of n){const t=ie(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=ie(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||a.has(t)||(o.isFamilyDefault=!0,a.add(t))}return o})}let le=null,ae=null,ce=null,de=null,ue=null;function pe(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 fe={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}},me=()=>{if(_("TEST_HOME")?.trim())return null;const e=g();return e?r(e):null};let ge=me,ye=null;export function setDesktopAsarForTests(e){ge=void 0===e?me:()=>e,ye=null}function he(){if(ye)return ye;let e=null;try{e=ge()}catch(e){p.logError("claude-desktop.unsupportedDefaults",e)}return ye=new Set(e?Object.keys(fe).filter(t=>!e.includes(t)):[]),ye}function ke(e){const t=he();return Object.fromEntries(Object.entries(fe).filter(([o])=>!e(o)&&!t.has(o)))}function _e(e){const t={...ke(t=>t in e),...e};for(const e of he())e in t&&JSON.stringify(t[e])===JSON.stringify(fe[e])&&delete t[e];return t}const we="SOFTWARE\\Policies\\Claude",be=`HKCU\\${we}`,De="HKLM\\SOFTWARE\\Policies\\Claude",Ae=`${w}-claude-policy.reg`,Ce=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let ve=Ce;export function setRegRunnerForTests(e){ve=e??Ce}function Ee(){return"win32"===process.platform}function Me(e){let t;try{t=ve(["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 Oe(){if(null===Me(be))return;const e=l(claudeDesktopUserDataDir(),Ae);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),D(e),ve(["export",be,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function Se(e){Oe();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)];ve(["add",be,"/v",t,"/t",e,"/d",n,"/f"])}}function xe(e){const t=Me(be);if(!t)return;Oe();let o=t.size;for(const n of e)t.has(n)&&(ve(["delete",be,"/v",n,"/f"]),o-=1);o<=0&&ve(["delete",be,"/f"])}function je(){const e=!Boolean(_("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:l(C(),"AppData","Local");return!!t(l(e,"AnthropicClaude"))||j.some(o=>t(l(e,"Packages",o,"LocalCache","Local","Claude-3p")))}const Pe=[...Object.keys(pe({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(fe)];function Ie(e){if(!e||!Pe.some(t=>e.has(t)))return"none";const t=e.get("inferenceGatewayApiKey")?.trim();return U(e.get("inferenceGatewayBaseUrl"))||t&&N().includes(t)?"ours":"foreign"}function Le(){const e=ee(Q())?.deploymentMode;if(void 0!==e&&"3p"!==e)return{kind:"mode"};const t=oe(),o=t?.entries.find(e=>e.id===t.appliedId);if(!o||o.id===ne(t)?.id)return null;const n=ee(Y(o.id));return n&&(n.inferenceProvider||n.inferenceGatewayBaseUrl)?{kind:"entry",name:o.name}:null}function Te(e){return"mode"===e.kind?y.t("tools.claude_desktop.current_official"):y.t("tools.claude_desktop.current_entry",{name:e.name})}let $e=null,Ge=null;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 Me(De)?De:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}function Ne(){const e=C();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",l(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||l(e,"AppData","Local");o=[l(t,"AnthropicClaude"),l(t,"Programs","Claude"),...j.map(e=>l(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",l(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),l(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))}const Ue=/\d+\.\d+\.\d+/;function Ke(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 Re(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",l(C(),"Applications","Claude.app")]){const o=l(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=l(process.env.LOCALAPPDATA||l(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 Ke(t)}const o=j.map(e=>`'${e}'`).join(",");return Ke(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"],env:V()}).split(/\r?\n/).flatMap(e=>Ue.exec(e)?.[0]??[]))}if("linux"===process.platform)try{const e=c("dpkg-query",["-W","-f=${Version}","claude-desktop"],{encoding:"utf-8",timeout:5e3,stdio:["ignore","pipe","ignore"]});return Ue.exec(e)?.[0]??null}catch{return null}return null}const Fe="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",Be="darwin"!==process.platform&&"win32"!==process.platform||!k().installMirror?.claudeDesktop?"https://claude.ai/download":b(k().installMirror.claudeDesktop),He="darwin"===process.platform?`open ${Be}`:"win32"===process.platform?`start ${Be}`:`xdg-open ${Be}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:Fe,installCommand:He,configPath:Z(),sessions:{format:H,files:()=>J(l(claudeDesktopUserDataDir(),"local-agent-mode-sessions"))},isInstalled:Ne,installedVersion:Re,prepareRuntime:async function(){if(!["win32","darwin","linux"].includes(process.platform)||!Ne())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(u.gray(" "+y.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?Re():null,mirror:k().installMirror?.claudeDesktop??null});return f("desktop-code-cli","skipped"===t.status?"skip":"ok",{duration_ms:Date.now()-e,detail:{...t}}),"installed"!==t.status?[]:[{kind:"summary",text:y.t("tools.claude_desktop.code_cli_installed",{version:t.version})}]}catch(t){return p.logError("claude-desktop.prepareCodeTabCli",t),f("desktop-code-cli","fail",{duration_ms:Date.now()-e,error:t}),[{kind:"action",text:y.t("tools.claude_desktop.code_cli_failed",{reason:t instanceof Error?t.message:String(t)})}]}},runningApp:v()?{isRunning:M,restart:x,quit:S,quitRefused:E,launch:O}:void 0,appInstaller:{vendor:"Anthropic",sizeMb:()=>300,downloadPage:"https://claude.ai/download",blocker:P,latestVersion:()=>I(),installedPath(){if("darwin"===process.platform)return L();if("win32"!==process.platform)return null;try{return Re()?"MSIX":null}catch(e){return p.logError("claude-desktop.installedPath",e),null}},install:e=>T(e)},wantsModelCatalog:!0,loadPrivateConfig(e){const t=oe()??{appliedId:"",entries:[]};let o=ne(t);o||(o={id:a(),name:W},t.entries.push(o));const n=pe(e);K(String(n.inferenceGatewayBaseUrl)),ae=!0===n.disableDeploymentModeChooser,le={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=ee(Y(o.id))??{};te(Y(o.id),{..._e(r),...n});const s=Le();$e=s&&!e.takeOver?Te(s):null,$e||(t.appliedId=o.id),te(Z(),t);const i=ee(Q())??{};$e||"3p"===i.deploymentMode||te(Q(),{...i,deploymentMode:"3p"}),de=null,ue=null,ce=null,Ge=null;const l=!0===e.settings?.force_registry,c=Ee()?Me(be):null;if(Ee()&&"foreign"===Ie(c)&&!l)Ge=c?.get("inferenceGatewayBaseUrl")||be;else if(Ee()){const t=Me(be),o=Pe.filter(e=>t?.has(e)),r={...ke(e=>t?.has(e)??!1),...n},s=!0===e.settings?.force_registry,i=!s&&je();try{i?xe(Pe):Se(r),ce=i?"removed":"written"}catch(e){p.logError("claude-desktop.policy",e),de=e instanceof Error?e.message.trim():String(e),(o.length>0||s)&&(ue=i?{action:"remove",values:Object.fromEntries(o.map(e=>[e,null])),deleteKey:o.length===t.size}:{action:"write",values:r,deleteKey:!1})}}},pendingElevation(){const e=ue;return e?{prompt:y.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!$(we,e.values,e.deleteKey))return!1;const t=Me(be),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(ue=null,de=null),o}}:null},unloadPrivateConfig(){if(Ee()&&"ours"===Ie(Me(be)))try{xe(Pe)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=oe(),t=ne(e);if(!e||!t)return;D(Y(t.id)),s(Y(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(D(Z()),s(Z(),{force:!0})):te(Z(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=oe(),t=ne(e);if(!t||e?.appliedId!==t.id)return null;const o=ee(Y(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=oe(),o=ne(t);if(!o||t?.appliedId!==o.id)return;const n=ee(Y(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog),s=JSON.stringify(n.inferenceModels)!==JSON.stringify(r),i={..._e(n),inferenceModels:r},l=JSON.stringify(i)!==JSON.stringify(n);ue=null;const a=Ee()?Me(be):null,c="foreign"===Ie(a)&&!0!==e.settings?.force_registry;if(!a?.has("inferenceModels")||c)return void(l&&te(Y(o.id),i));const d=Pe.filter(e=>a.has(e)),u=!0!==e.settings?.force_registry&&je();if(l&&te(Y(o.id),i),u||s)try{u?xe(Pe):Se({inferenceModels:r})}catch(e){throw p.logError("claude-desktop.refreshModelMenu registry",e),ue=u?{action:"remove",values:Object.fromEntries(d.map(e=>[e,null])),deleteKey:d.length===a.size}:{action:"write",values:{inferenceModels:r},deleteKey:!1},new G(y.t("tools.claude_desktop.refresh_needs_admin"))}},detectCurrentConfig(){try{if(Ee()){const e=Me(be)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=ne(oe());if(!e)return{apiKey:null};const t=ee(Y(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=oe(),t=ne(e);return{meta:e,applied:t?ee(Y(t.id)):null,desktop:ee(Q()),...Ee()?{policy:Object.fromEntries(Me(be)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;Ee()&&Me(be)?.has("inferenceModels")&&(Oe(),ve(["delete",be,"/v","inferenceModels","/f"]),e=!0);const t=ne(oe()),o=t?ee(Y(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;te(Y(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(le&&le.models.length>0){const{models:t,source:o,groups:n}=le;e.push({kind:"summary",text:y.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:y.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!==ae&&e.push({kind:"detail",text:y.t(ae?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),v()&&!M()||e.push({kind:"action",text:y.t("tools.claude_desktop.restart_hint")}),ue?(e.push({kind:"action",text:y.t(`tools.claude_desktop.policy_stale_${ue.action}`)}),e.push({kind:"detail",text:de??""})):de?(e.push({kind:"detail",text:y.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:de})):Ee()&&ce&&e.push({kind:"detail",text:y.t(`tools.claude_desktop.registry_${ce}`,{key:be})}),$e&&e.push({kind:"action",text:y.t("tools.claude_desktop.not_switched",{current:$e})}),Ge&&e.push({kind:"action",text:y.t("tools.claude_desktop.foreign_policy",{key:be,url:Ge})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:y.t("tools.claude_desktop.managed_conflict",{path:t})}),e},planNotes(){const e=[],t=Le();if(t&&e.push(y.t("tools.claude_desktop.plan_switch",{current:Te(t)})),Ee()){const t=Me(be);"foreign"===Ie(t)&&e.push(y.t("tools.claude_desktop.plan_foreign_policy",{key:be,url:t?.get("inferenceGatewayBaseUrl")||"-"}))}return e},forceResetPaths(){const e=[Z(),Q()];try{const t=ne(oe());t&&e.push(Y(t.id))}catch{}return e},backupPaths(){const e=[Z(),Q()];Ee()&&e.push(l(claudeDesktopUserDataDir(),Ae));try{const t=ne(oe());t&&e.push(Y(t.id))}catch{}return e}};
@@ -22,15 +22,16 @@
22
22
  export interface DesktopAppSpec {
23
23
  /** Log label, e.g. `claude-desktop`. */
24
24
  id: string;
25
- /** The brand's download page `https://cnb.cool/<group>/<repo>/-/releases/latest`, undefined without a mirror. */
25
+ /** The brand's download mirror (see mirror-url.ts: a fixed-tag download directory, or an old `…/<repo>/-/releases/latest`), undefined without one. */
26
26
  mirror(): string | undefined;
27
27
  /** Release file for this machine, null where the vendor has none (Linux, Intel Mac for ChatGPT). */
28
28
  asset(platform: NodeJS.Platform, arm64: boolean): string | null;
29
29
  /** Release text file holding the version of `asset` (macOS and Windows builds may be numbered differently). */
30
30
  versionFile(platform: NodeJS.Platform, arm64: boolean): string;
31
31
  /**
32
- * Release tag holding `version` (`v<version>` for Claude Desktop); absent when the tag follows another
33
- * platform's numbering (ChatGPT's follows macOS), then files are read from `releases/latest/download`.
32
+ * On an old per-tool mirror repository, the release tag holding `version` (`v<version>` for Claude Desktop);
33
+ * absent when the tag follows another platform's numbering (ChatGPT's follows macOS), then files are read
34
+ * from `releases/latest/download`. A fixed-tag mirror holds every file itself and ignores this.
34
35
  */
35
36
  releaseTag?(version: string): string;
36
37
  /** The bundle inside the .dmg, and the names an installed copy may have (first = name of a new install). */
@@ -69,7 +70,9 @@ export declare function appPlatform(): NodeJS.Platform;
69
70
  /** An ARM machine, also when this node runs under Rosetta / x64 emulation. */
70
71
  export declare function isArm64Machine(): boolean;
71
72
  export declare class DesktopInstallError extends Error {
72
- constructor(message: string);
73
+ /** Which `app_update.error_*` this is (absent for others). */
74
+ readonly key?: string;
75
+ constructor(message: string, key?: string);
73
76
  }
74
77
  /** Localized failure; an empty detail (a tool that printed nothing) reads "reason unknown", never `{{reason}}`. */
75
78
  export declare const fail: (key: string, vars?: Record<string, string>) => never;
@@ -85,11 +88,12 @@ export interface DesktopInstallProgress {
85
88
  percent?(percent: number): void;
86
89
  verifying?(): void;
87
90
  installing?(): void;
91
+ /** A package downloaded by an earlier, failed attempt is used again (its checksum matched). */
92
+ reusing?(version: string): void;
88
93
  /** Windows asked for an administrator: true = install through a UAC prompt (asked / announced by the caller). */
89
94
  elevate?(): Promise<boolean>;
90
95
  }
91
96
  export interface DesktopAppInstaller {
92
- mirrorRepo(): string | null;
93
97
  assetName(): string | null;
94
98
  blocker(): string | null;
95
99
  latestVersion(timeoutMs?: number): Promise<string>;
@@ -1 +1 @@
1
- import{spawnSync as t}from"child_process";import{createHash as e}from"crypto";import{accessSync as r,constants as o,createReadStream as n,createWriteStream as s,existsSync as a,mkdirSync as i,mkdtempSync as c,readFileSync as l,renameSync as p,rmSync as u}from"fs";import{cpus as d,tmpdir as f}from"os";import{basename as m,dirname as g,join as h}from"path";import{Readable as w,Transform as $}from"stream";import{pipeline as S}from"stream/promises";import{logger as A}from"../../utils/logger.js";import{i18n as x}from"../i18n.js";import{powerShellEnv as E}from"../powershell-env.js";import{toolHome as y}from"./types.js";const v=(e,r,o={})=>{const n=t(e,r,{encoding:"utf-8",timeout:o.timeout??12e4,windowsHide:!0,input:o.input,stdio:[void 0===o.input?"ignore":"pipe","pipe","pipe"],maxBuffer:16777216,env:E()});return n.error&&A.logError(`desktop-app: ${e}`,n.error),{status:n.status,stdout:`${n.stdout??""}`,stderr:`${n.stderr??""}`}};let P=v,_=(...t)=>fetch(...t),I=null,k=null,b=null,C=null;export function setDesktopAppDepsForTests(t){void 0!==t.run&&(P=t.run??v),void 0!==t.fetch&&(_=t.fetch??((...t)=>fetch(...t))),void 0!==t.platform&&(I=t.platform),void 0!==t.arm64&&(k=t.arm64),void 0!==t.privileged&&(b=t.privileged),void 0!==t.remote&&(C=t.remote)}export function appPlatform(){return I??process.platform}export function isArm64Machine(){return null!==k?k:"arm64"===process.arch||("win32"===appPlatform()?"ARM64"===process.env.PROCESSOR_ARCHITECTURE||"ARM64"===process.env.PROCESSOR_ARCHITEW6432:"darwin"===appPlatform()&&/\bApple\b/.test(d()[0]?.model??""))}export class DesktopInstallError extends Error{constructor(t){super(t),this.name="DesktopInstallError"}}export const fail=(t,e={})=>{const r={};for(const[t,o]of Object.entries(e))r[t]=o.trim()||x.t("app_update.reason_unknown");throw new DesktopInstallError(x.t(`app_update.error_${t}`,r))};export function parseSha256Sums(t){const e=new Map;for(const r of t.split(/\r?\n/)){const t=/^([0-9a-f]{64})\s+\*?(\S.*)$/i.exec(r.trim());t&&e.set(t[2].trim(),t[1].toLowerCase())}return e}async function T(t,e){const r=await _(t,{signal:AbortSignal.timeout(e)});if(!r.ok)throw new Error(`${t}: HTTP ${r.status}`);return(await r.text()).trim()}function M(t,e){try{const r=l(h(t,"Contents","Info.plist"),"utf-8");return new RegExp(`<key>${e}</key>\\s*<string>([^<]*)</string>`).exec(r)?.[1]??null}catch{return null}}export function macBundleId(t){return M(t,"CFBundleIdentifier")}export function macAppVersion(t){return/^\d+\.\d+\.\d+/.exec(M(t,"CFBundleShortVersionString")??"")?.[0]??null}function R(t){try{return r(t,o.W_OK),!0}catch{return!1}}const F=t=>`'${t.replace(/'/g,"''")}'`;function O(t,e=12e4){return P("powershell.exe",["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command","[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; "+t],{timeout:e})}export function powerShellError(t){return t.replace(/#< CLIXML/g,"").replace(/_x000D__x000A_/g,"\n").replace(/<\/S>/g,"\n").replace(/<[^>]+>/g,"").replace(/_x[0-9A-F]{4}_/g,"").replace(/&apos;/g,"'").replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").split(/\r?\n/).map(t=>t.trim()).find(t=>t&&!/^(At line|At char|所在位置|\+|~|CategoryInfo|FullyQualifiedErrorId)/i.test(t))??""}export function createDesktopAppInstaller(t){const r=()=>t.mirror()?.replace(/\/-\/releases\/latest\/?$/,"")??null,o=()=>t.asset(appPlatform(),isArm64Machine()),d=()=>{const e=appPlatform();return"darwin"!==e&&"win32"!==e?"unsupported_platform":o()?r()?"darwin"===e&&(b??0===process.getuid?.())?"root":"win32"===e&&(null!==b?b:/\\system32\\config\\systemprofile/i.test(process.env.USERPROFILE??"")||!0===process.env.USERNAME?.endsWith("$"))?"system_account":"win32"===e&&(null!==C?C:Boolean(process.env.SSH_CONNECTION||process.env.SSH_CLIENT))?"remote_session":t.extraBlocker?.(e)??null:"no_mirror":"darwin"===e?"unsupported_arch":"unsupported_platform"},E=async(e=8e3)=>{const o=r();if(!o)throw new Error("no mirror");const n=t.versionFile(appPlatform(),isArm64Machine()),s=await T(`${o}/-/releases/latest/download/${n}`,e),a=/^(\d+\.\d+\.\d+)(\.\d+)?$/.exec(s)?.[1];if(!a)throw new Error(`unexpected ${n}: ${s.slice(0,40)}`);return a},v=()=>{for(const e of["/Applications",h(y(),"Applications")])for(const r of t.macApp.installedNames){const o=h(e,r);if(!a(h(o,"Contents","Info.plist")))continue;const n=macBundleId(o);if(null===n||n===t.macApp.bundleId)return o}return null},I=e=>{const r=P("codesign",["--verify","--deep","--strict",e],{timeout:6e5});0!==r.status&&fail("signature",{reason:(r.stderr||r.stdout).trim().split("\n")[0]||`codesign ${r.status}`});const o=P("codesign",["-dv",e]),n=`${o.stdout}\n${o.stderr}`,s=/^TeamIdentifier=(\S+)/m.exec(n)?.[1],a=/^Identifier=(\S+)/m.exec(n)?.[1];s===t.macApp.teamId&&a===t.macApp.bundleId||fail("signature",{reason:`TeamIdentifier=${s??"?"} Identifier=${a??"?"}`});const i=P("spctl",["-a","-t","exec","-vv",e],{timeout:3e5});0!==i.status&&fail("signature",{reason:(i.stderr||i.stdout).trim().split("\n")[0]||`spctl ${i.status}`})},k=()=>{const e=v();let r=e;if(!r){const e=R("/Applications")?"/Applications":h(y(),"Applications");r=h(e,t.macApp.installedNames[0])}const o=g(r);return(a(o)&&!R(o)||e&&!R(e))&&fail("no_permission",{path:r}),{target:r,existing:e}},M=e=>{const r=O(`$s = Get-AuthenticodeSignature -LiteralPath ${F(e)}; [pscustomobject]@{ status = [string]$s.Status; subject = [string]$s.SignerCertificate.Subject } | ConvertTo-Json -Compress`);let o={};try{o=JSON.parse(r.stdout.trim()||"{}")}catch{}if("Valid"!==o.status||!t.windows.signer.test(o.subject??"")){const t=o.status?`${o.status} ${o.subject??""}`:powerShellError(r.stderr)||powerShellError(r.stdout.trim().startsWith("{")?"":r.stdout)||`exit ${r.status}`;fail("signature",{reason:t.trim()})}},N=()=>{const e=O(`Get-AppxPackage | Where-Object { ${t.windows.packageFilter} } | ForEach-Object { $_.Version.ToString() }`,3e4);return/\d+\.\d+\.\d+/.exec(e.stdout)?.[0]??null};return{mirrorRepo:r,assetName:o,blocker:d,latestVersion:E,installedMacApp:v,windowsPackageVersion:N,verifyMacApp:I,verifyWindowsPackage:M,install:async(y={})=>{const v=d();v&&fail("blocked",{reason:x.t(`app_update.reason_${v}`)});const b=r(),C=o();let R;try{R=await E(15e3)}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}const j=t.releaseTag?`${b}/-/releases/download/${t.releaseTag(R)}`:`${b}/-/releases/latest/download`;let B;try{B=parseSha256Sums(await T(`${j}/SHA256SUMS`,15e3)).get(C)}catch(e){A.logError(`${t.id}: SHA256SUMS`,e)}B||fail("no_checksum",{file:C}),"darwin"===appPlatform()&&k();const H=c(h(f(),`${t.id}-`));try{const r=h(H,C);y.downloading?.(R),await async function(t,e,r){let o;try{o=await _(t,{signal:AbortSignal.timeout(18e5)})}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}o.ok&&o.body||fail("download",{reason:`HTTP ${o.status}`});const n=Number(o.headers.get("content-length"))||0;let a=0,i=-1;const c=new $({transform(t,e,o){a+=t.length;const s=n?Math.floor(100*a/n):-1;s>=0&&s!==i&&(i=s,r?.(s)),o(null,t)}});try{await S(w.fromWeb(o.body),c,s(e))}catch(t){u(e,{force:!0}),fail("download",{reason:t instanceof Error?t.message:String(t)})}}(`${j}/${C}`,r,y.percent),y.verifying?.();const o=await async function(t){const r=e("sha256");for await(const e of n(t))r.update(e);return r.digest("hex")}(r);return o!==B&&fail("checksum",{file:C,expected:B.slice(0,12),actual:o.slice(0,12)}),y.installing?.(),"darwin"===appPlatform()?await(async(e,r)=>{const o=h(r,"mnt");i(o,{recursive:!0});const n=P("hdiutil",["attach","-nobrowse","-readonly","-noautoopen","-mountpoint",o,e],{timeout:3e5,input:"Y\n"});0!==n.status&&fail("mount",{reason:(n.stderr||n.stdout).trim().split("\n").pop()??""});let s=null;try{const e=h(o,t.macApp.source);a(e)||fail("bad_package",{reason:t.macApp.source});const{target:r,existing:n}=k(),c=g(r),l=m(r);i(c,{recursive:!0}),s=h(c,`.${l}.${process.pid}.new`),u(s,{recursive:!0,force:!0});const d=P("ditto",[e,s],{timeout:9e5});0!==d.status&&fail("copy",{reason:(d.stderr||d.stdout).trim().split("\n")[0]??""}),I(s);const f=macAppVersion(s);f||fail("bad_package",{reason:"Info.plist"});const w=h(c,`.${l}.${process.pid}.old`);try{n&&p(n,w)}catch(t){throw["EPERM","EACCES"].includes(t.code??"")&&fail("app_management",{path:n}),t}try{p(s,r)}catch(t){throw n&&p(w,n),t}return s=null,u(w,{recursive:!0,force:!0}),{version:f,location:r}}finally{s&&u(s,{recursive:!0,force:!0}),P("hdiutil",["detach",o,"-force"],{timeout:6e4})}})(r,H):await(async(t,e)=>{M(t);const r=O(`$ErrorActionPreference = 'Stop'; Add-AppxPackage -Path ${F(t)} -ForceApplicationShutdown`,9e5);if(0!==r.status){const o=powerShellError(r.stderr)||powerShellError(r.stdout)||`Add-AppxPackage ${r.status}`;/0x80073D28/i.test(`${r.stderr}\n${r.stdout}`)||fail("install",{reason:o}),await(e.elevate?.())||fail("needs_admin");const n=(t=>{const e=h(g(t),"elevated-install.log"),r=`$ErrorActionPreference = 'Stop'; try { Add-AppxPackage -Path ${F(t)} -ForceApplicationShutdown; exit 0 } catch { [IO.File]::WriteAllText(${F(e)}, $_.Exception.Message, [Text.Encoding]::UTF8); exit 1 }`,o=O(`$ErrorActionPreference = 'Stop'; $p = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '${Buffer.from(r,"utf16le").toString("base64")}') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`,12e5);if(0===o.status)return null;let n="";try{n=l(e,"utf-8").trim()}catch{}return n||powerShellError(o.stderr)||powerShellError(o.stdout)||`exit ${o.status}`})(t);n&&fail("install",{reason:n}),N()||fail("other_account")}const o=N();return o||fail("install",{reason:"Get-AppxPackage"}),{version:o,location:"MSIX"}})(r,y)}finally{u(H,{recursive:!0,force:!0})}}}}
1
+ import{spawnSync as t}from"child_process";import{createHash as e}from"crypto";import{accessSync as r,constants as o,copyFileSync as n,createReadStream as s,createWriteStream as i,existsSync as a,mkdirSync as c,mkdtempSync as l,readFileSync as p,renameSync as u,rmSync as d}from"fs";import{cpus as f,tmpdir as m}from"os";import{basename as g,dirname as h,join as w}from"path";import{Readable as S,Transform as A}from"stream";import{pipeline as x}from"stream/promises";import{logger as y}from"../../utils/logger.js";import{i18n as E}from"../i18n.js";import{powerShellEnv as $}from"../powershell-env.js";import{toolHome as v}from"./types.js";import{configManager as k}from"../config.js";import{mirrorFileUrl as _}from"../mirror-url.js";const P=(e,r,o={})=>{const n=t(e,r,{encoding:"utf-8",timeout:o.timeout??12e4,windowsHide:!0,input:o.input,stdio:[void 0===o.input?"ignore":"pipe","pipe","pipe"],maxBuffer:16777216,env:$()});return n.error&&y.logError(`desktop-app: ${e}`,n.error),{status:n.status,stdout:`${n.stdout??""}`,stderr:`${n.stderr??""}`}};let I=P,b=(...t)=>fetch(...t),C=null,T=null,M=null,F=null;export function setDesktopAppDepsForTests(t){void 0!==t.run&&(I=t.run??P),void 0!==t.fetch&&(b=t.fetch??((...t)=>fetch(...t))),void 0!==t.platform&&(C=t.platform),void 0!==t.arm64&&(T=t.arm64),void 0!==t.privileged&&(M=t.privileged),void 0!==t.remote&&(F=t.remote)}export function appPlatform(){return C??process.platform}export function isArm64Machine(){return null!==T?T:"arm64"===process.arch||("win32"===appPlatform()?"ARM64"===process.env.PROCESSOR_ARCHITECTURE||"ARM64"===process.env.PROCESSOR_ARCHITEW6432:"darwin"===appPlatform()&&/\bApple\b/.test(f()[0]?.model??""))}export class DesktopInstallError extends Error{key;constructor(t,e){super(t),this.name="DesktopInstallError",this.key=e}}export const fail=(t,e={})=>{const r={};for(const[t,o]of Object.entries(e))r[t]=o.trim()||E.t("app_update.reason_unknown");throw new DesktopInstallError(E.t(`app_update.error_${t}`,r),t)};export function parseSha256Sums(t){const e=new Map;for(const r of t.split(/\r?\n/)){const t=/^([0-9a-f]{64})\s+\*?(\S.*)$/i.exec(r.trim());t&&e.set(t[2].trim(),t[1].toLowerCase())}return e}async function R(t){const r=e("sha256");for await(const e of s(t))r.update(e);return r.digest("hex")}async function j(t,e){const r=await b(t,{signal:AbortSignal.timeout(e)});if(!r.ok)throw new Error(`${t}: HTTP ${r.status}`);return(await r.text()).trim()}function O(t,e){try{const r=p(w(t,"Contents","Info.plist"),"utf-8");return new RegExp(`<key>${e}</key>\\s*<string>([^<]*)</string>`).exec(r)?.[1]??null}catch{return null}}export function macBundleId(t){return O(t,"CFBundleIdentifier")}export function macAppVersion(t){return/^\d+\.\d+\.\d+/.exec(O(t,"CFBundleShortVersionString")??"")?.[0]??null}function N(t){try{return r(t,o.W_OK),!0}catch{return!1}}const B=t=>`'${t.replace(/'/g,"''")}'`;function D(t,e=12e4){return I("powershell.exe",["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command","[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; "+t],{timeout:e})}export function powerShellError(t){return t.replace(/#< CLIXML/g,"").replace(/_x000D__x000A_/g,"\n").replace(/<\/S>/g,"\n").replace(/<[^>]+>/g,"").replace(/_x[0-9A-F]{4}_/g,"").replace(/&apos;/g,"'").replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").split(/\r?\n/).map(t=>t.trim()).find(t=>t&&!/^(At line|At char|所在位置|\+|~|CategoryInfo|FullyQualifiedErrorId)/i.test(t))??""}export function createDesktopAppInstaller(t){const e=()=>t.mirror()||null,r=()=>t.asset(appPlatform(),isArm64Machine()),o=()=>{const o=appPlatform();return"darwin"!==o&&"win32"!==o?"unsupported_platform":r()?e()?"darwin"===o&&(M??0===process.getuid?.())?"root":"win32"===o&&(null!==M?M:/\\system32\\config\\systemprofile/i.test(process.env.USERPROFILE??"")||!0===process.env.USERNAME?.endsWith("$"))?"system_account":"win32"===o&&(null!==F?F:Boolean(process.env.SSH_CONNECTION||process.env.SSH_CLIENT))?"remote_session":t.extraBlocker?.(o)??null:"no_mirror":"darwin"===o?"unsupported_arch":"unsupported_platform"},s=async(r=8e3)=>{const o=e();if(!o)throw new Error("no mirror");const n=t.versionFile(appPlatform(),isArm64Machine()),s=await j(_(o,n),r),i=/^(\d+\.\d+\.\d+)(\.\d+)?$/.exec(s)?.[1];if(!i)throw new Error(`unexpected ${n}: ${s.slice(0,40)}`);return i},f=()=>{for(const e of["/Applications",w(v(),"Applications")])for(const r of t.macApp.installedNames){const o=w(e,r);if(!a(w(o,"Contents","Info.plist")))continue;const n=macBundleId(o);if(null===n||n===t.macApp.bundleId)return o}return null},$=e=>{const r=I("codesign",["--verify","--deep","--strict",e],{timeout:6e5});0!==r.status&&fail("signature",{reason:(r.stderr||r.stdout).trim().split("\n")[0]||`codesign ${r.status}`});const o=I("codesign",["-dv",e]),n=`${o.stdout}\n${o.stderr}`,s=/^TeamIdentifier=(\S+)/m.exec(n)?.[1],i=/^Identifier=(\S+)/m.exec(n)?.[1];s===t.macApp.teamId&&i===t.macApp.bundleId||fail("signature",{reason:`TeamIdentifier=${s??"?"} Identifier=${i??"?"}`});const a=I("spctl",["-a","-t","exec","-vv",e],{timeout:3e5});0!==a.status&&fail("signature",{reason:(a.stderr||a.stdout).trim().split("\n")[0]||`spctl ${a.status}`})},P=()=>{const e=f();let r=e;if(!r){const e=N("/Applications")?"/Applications":w(v(),"Applications");r=w(e,t.macApp.installedNames[0])}const o=h(r);return(a(o)&&!N(o)||e&&!N(e))&&fail("no_permission",{path:r}),{target:r,existing:e}},C=e=>{const r=D(`$s = Get-AuthenticodeSignature -LiteralPath ${B(e)}; [pscustomobject]@{ status = [string]$s.Status; subject = [string]$s.SignerCertificate.Subject } | ConvertTo-Json -Compress`);let o={};try{o=JSON.parse(r.stdout.trim()||"{}")}catch{}if("Valid"!==o.status||!t.windows.signer.test(o.subject??"")){const t=o.status?`${o.status} ${o.subject??""}`:powerShellError(r.stderr)||powerShellError(r.stdout.trim().startsWith("{")?"":r.stdout)||`exit ${r.status}`;fail("signature",{reason:t.trim()})}},T=()=>{const e=D(`Get-AppxPackage | Where-Object { ${t.windows.packageFilter} } | ForEach-Object { $_.Version.ToString() }`,3e4);return/\d+\.\d+\.\d+/.exec(e.stdout)?.[0]??null};return{assetName:r,blocker:o,latestVersion:s,installedMacApp:f,windowsPackageVersion:T,verifyMacApp:$,verifyWindowsPackage:C,install:async(f={})=>{const v=o();v&&fail("blocked",{reason:E.t(`app_update.reason_${v}`)});const M=e(),F=r();let O;try{O=await s(15e3)}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}const N=e=>_(M,e,t.releaseTag?.(O));let H;try{H=parseSha256Sums(await j(N("SHA256SUMS"),15e3)).get(F)}catch(e){y.logError(`${t.id}: SHA256SUMS`,e)}H||fail("no_checksum",{file:F}),"darwin"===appPlatform()&&P();const W=w(k.getConfigDir(),"downloads",t.id),V=w(W,F),L=l(w(m(),`${t.id}-`));let U=w(L,F),G=!1;try{a(V)&&await R(V)===H?(U=V,f.reusing?.(O)):(f.downloading?.(O),await async function(t,e,r){let o;try{o=await b(t,{signal:AbortSignal.timeout(18e5)})}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}o.ok&&o.body||fail("download",{reason:`HTTP ${o.status}`});const n=Number(o.headers.get("content-length"))||0;let s=0,a=-1;const c=new A({transform(t,e,o){s+=t.length;const i=n?Math.floor(100*s/n):-1;i>=0&&i!==a&&(a=i,r?.(i)),o(null,t)}});try{await x(S.fromWeb(o.body),c,i(e))}catch(t){d(e,{force:!0}),fail("download",{reason:t instanceof Error?t.message:String(t)})}}(N(F),U,f.percent)),f.verifying?.();const e=await R(U);e!==H&&fail("checksum",{file:F,expected:H.slice(0,12),actual:e.slice(0,12)}),f.installing?.();const r="darwin"===appPlatform()?await(async(e,r)=>{const o=w(r,"mnt");c(o,{recursive:!0});const n=I("hdiutil",["attach","-nobrowse","-readonly","-noautoopen","-mountpoint",o,e],{timeout:3e5,input:"Y\n"});0!==n.status&&fail("mount",{reason:(n.stderr||n.stdout).trim().split("\n").pop()??""});let s=null;try{const e=w(o,t.macApp.source);a(e)||fail("bad_package",{reason:t.macApp.source});const{target:r,existing:n}=P(),i=h(r),l=g(r);c(i,{recursive:!0}),s=w(i,`.${l}.${process.pid}.new`),d(s,{recursive:!0,force:!0});const p=I("ditto",[e,s],{timeout:9e5});0!==p.status&&fail("copy",{reason:(p.stderr||p.stdout).trim().split("\n")[0]??""}),$(s);const f=macAppVersion(s);f||fail("bad_package",{reason:"Info.plist"});const m=w(i,`.${l}.${process.pid}.old`);try{n&&u(n,m)}catch(t){throw["EPERM","EACCES"].includes(t.code??"")&&fail("app_management",{path:n}),t}try{u(s,r)}catch(t){throw n&&u(m,n),t}return s=null,d(m,{recursive:!0,force:!0}),{version:f,location:r}}finally{s&&d(s,{recursive:!0,force:!0}),I("hdiutil",["detach",o,"-force"],{timeout:6e4})}})(U,L):await(async(t,e)=>{C(t);const r=D(`$ErrorActionPreference = 'Stop'; Add-AppxPackage -Path ${B(t)} -ForceApplicationShutdown`,9e5);if(0!==r.status){const o=powerShellError(r.stderr)||powerShellError(r.stdout)||`Add-AppxPackage ${r.status}`;/0x80073D28/i.test(`${r.stderr}\n${r.stdout}`)||fail("install",{reason:o}),await(e.elevate?.())||fail("needs_admin");const n=(t=>{const e=w(h(t),"elevated-install.log"),r=`$ErrorActionPreference = 'Stop'; try { Add-AppxPackage -Path ${B(t)} -ForceApplicationShutdown; exit 0 } catch { [IO.File]::WriteAllText(${B(e)}, $_.Exception.Message, [Text.Encoding]::UTF8); exit 1 }`,o=D(`$ErrorActionPreference = 'Stop'; $p = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '${Buffer.from(r,"utf16le").toString("base64")}') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`,12e5);if(0===o.status)return null;let n="";try{n=p(e,"utf-8").trim()}catch{}return n||powerShellError(o.stderr)||powerShellError(o.stdout)||`exit ${o.status}`})(t);n&&fail("install",{reason:n}),T()||fail("other_account")}const o=T();return o||fail("install",{reason:"Get-AppxPackage"}),{version:o,location:"MSIX"}})(U,f);return G=!0,r}catch(t){const e=t instanceof DesktopInstallError?t.key:void 0;throw U!==V&&a(U)&&!["checksum","signature","bad_package","mount"].includes(e??"")&&function(t,e,r){try{d(e,{recursive:!0,force:!0}),c(e,{recursive:!0});try{u(t,r)}catch{n(t,r)}}catch(t){y.logError("desktop-app: keep package",t),d(e,{recursive:!0,force:!0})}}(U,W,V),U===V&&["signature","bad_package","mount"].includes(e??"")&&d(W,{recursive:!0,force:!0}),t}finally{G&&d(W,{recursive:!0,force:!0}),d(L,{recursive:!0,force:!0})}}}}
@@ -100,6 +100,8 @@ export interface AppInstaller {
100
100
  percent(percent: number): void;
101
101
  verifying(): void;
102
102
  installing(): void;
103
+ /** A package kept from an earlier failed attempt is used again. */
104
+ reusing?(version: string): void;
103
105
  /** Windows wants an administrator for this install: true = go ahead with a UAC prompt. */
104
106
  elevate?(): Promise<boolean>;
105
107
  }): Promise<{
@@ -1203,6 +1203,7 @@
1203
1203
  "outdated": "{{current}} installed, {{latest}} available",
1204
1204
  "up_to_date": "{{current}} (latest)",
1205
1205
  "downloading": "Downloading {{tool}} {{version}} from the download site (about {{size}} MB)…",
1206
+ "reusing": "Using the {{tool}} {{version}} package downloaded last time (checksum matches, no new download)",
1206
1207
  "verifying": "Checking the checksum and {{vendor}}'s signature…",
1207
1208
  "installing": "Installing…",
1208
1209
  "already_latest": "{{tool}} is up to date ({{current}})",
@@ -1203,6 +1203,7 @@
1203
1203
  "outdated": "当前 {{current}},可升到 {{latest}}",
1204
1204
  "up_to_date": "{{current}}(已是最新)",
1205
1205
  "downloading": "正在从高速下载站下载 {{tool}} {{version}}(约 {{size}} MB)…",
1206
+ "reusing": "使用上次下载好的 {{tool}} {{version}} 安装包(校验值对得上,不再重新下载)",
1206
1207
  "verifying": "正在核对校验值和 {{vendor}} 的签名…",
1207
1208
  "installing": "正在安装…",
1208
1209
  "already_latest": "{{tool}} 已是最新({{current}})",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.153",
3
+ "version": "0.0.155",
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",