@vibeapi/api-helper 0.0.146 → 0.0.148

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.
Files changed (37) hide show
  1. package/dist/brand.json +2 -1
  2. package/dist/commands/doctor.js +1 -1
  3. package/dist/commands/setup.d.ts +7 -0
  4. package/dist/commands/setup.js +1 -1
  5. package/dist/commands/update.js +1 -1
  6. package/dist/commands/upgrade.d.ts +8 -2
  7. package/dist/commands/upgrade.js +1 -1
  8. package/dist/lib/app-update.d.ts +74 -0
  9. package/dist/lib/app-update.js +1 -0
  10. package/dist/lib/brand.d.ts +2 -0
  11. package/dist/lib/codex-sessions.js +1 -1
  12. package/dist/lib/i18n.js +1 -1
  13. package/dist/lib/post-apply.d.ts +5 -1
  14. package/dist/lib/post-apply.js +1 -1
  15. package/dist/lib/powershell-env.d.ts +12 -0
  16. package/dist/lib/powershell-env.js +1 -0
  17. package/dist/lib/program.js +1 -1
  18. package/dist/lib/tool-manager.js +1 -1
  19. package/dist/lib/tool-upgrade.js +1 -1
  20. package/dist/lib/tools/chatgpt-desktop.d.ts +32 -0
  21. package/dist/lib/tools/chatgpt-desktop.js +1 -0
  22. package/dist/lib/tools/claude-desktop-app.d.ts +31 -0
  23. package/dist/lib/tools/claude-desktop-app.js +1 -0
  24. package/dist/lib/tools/claude-desktop-cli.js +1 -1
  25. package/dist/lib/tools/claude-desktop.js +1 -1
  26. package/dist/lib/tools/codex.js +1 -1
  27. package/dist/lib/tools/desktop-app-installer.d.ts +102 -0
  28. package/dist/lib/tools/desktop-app-installer.js +1 -0
  29. package/dist/lib/tools/desktop-process.d.ts +4 -0
  30. package/dist/lib/tools/desktop-process.js +1 -1
  31. package/dist/lib/tools/elevated-reg.js +1 -1
  32. package/dist/lib/tools/types.d.ts +50 -0
  33. package/dist/lib/windows-env.js +1 -1
  34. package/dist/lib/wizard.js +1 -1
  35. package/dist/locales/en_US.json +69 -7
  36. package/dist/locales/zh_CN.json +69 -7
  37. package/package.json +1 -1
@@ -1 +1 @@
1
- import{spawn as t,spawnSync as e}from"child_process";import{rmSync as o}from"fs";import{join as r}from"path";import n from"chalk";import i from"inquirer";import{i18n as a}from"./i18n.js";import{logger as s}from"../utils/logger.js";import{compareVersions as l,getToolMinVersion as c}from"./model-registry.js";import{getAdapter as p}from"./tools/registry.js";import{findInstalls as u,forgetInstalls as m,installLabel as d,npmMirrorInstallCommand as g,npmMirrorVersion as f,npmUninstallCommand as w}from"./tools/tool-installs.js";import{toolHome as y}from"./tools/types.js";import{isNonInteractive as h,shouldAutoConfirm as _}from"./runtime-options.js";import{track as v}from"./telemetry.js";let k="ok",b=async(t,o,r={})=>(console.log(n.gray("$ ")+[t,...o].join(" ")),k=r.idleMs?await runWatched(t,o,r):function(t,o,r){const n=$(t)?e(T(t,o),{stdio:"inherit",shell:!0,timeout:r}):e(t,o,{stdio:"inherit",timeout:r});return n.error&&s.logError(`upgrade: ${t}`,n.error),"ETIMEDOUT"===n.error?.code?"timeout":0===n.status?"ok":"fail"}(t,o,r.timeoutMs),"ok"===k);const $=t=>"win32"===process.platform&&!/\.exe$/i.test(t),T=(t,e)=>[t,...e].map(t=>/\s/.test(t)?`"${t}"`:t).join(" ");export function runWatched(e,o,r){return new Promise(n=>{const i=$(e)?t(T(e,o),{stdio:["inherit","pipe","pipe"],shell:!0}):t(e,o,{stdio:["inherit","pipe","pipe"]});let a;const l=t=>{a=t,i.kill()};let c;const p=()=>{clearTimeout(c),r.idleMs&&(c=setTimeout(()=>l("stalled"),r.idleMs))},u=r.timeoutMs?setTimeout(()=>l("timeout"),r.timeoutMs):void 0;p(),i.stdout?.on("data",t=>{process.stdout.write(t),p()}),i.stderr?.on("data",t=>{process.stderr.write(t),p()});const m=t=>{clearTimeout(c),clearTimeout(u),n(t)};i.on("error",t=>{s.logError(`upgrade: ${e}`,t),m(a??"fail")}),i.on("close",t=>{a&&process.stdout.write("\n"),m(a??(0===t?"ok":"fail"))})})}export function setUpgradeRunnerForTests(t){b=async(e,o,r)=>{const n=await t(e,o,r);return k="string"==typeof n?n:n?"ok":"fail","ok"===k}}const j={ok:"install.script_failed_npm",fail:"install.script_failed_npm",stalled:"install.script_stalled_npm",timeout:"install.script_timeout_npm"};export async function mirrorInstall(t){if(!t.mirrorScript)return M(t);console.log(n.gray(a.t("install.downloading")));const e=`install-script:${t.command}`,o=Date.now();v(e,"start");const r={idleMs:18e4,timeoutMs:36e5},i="win32"===process.platform?await b("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",`irm ${t.mirrorScript.ps1} -TimeoutSec 30 | iex`],r):await b("sh",["-c",`script=$(curl -fsSL --connect-timeout 15 --max-time 60 ${t.mirrorScript.sh}) && printf '%s\\n' "$script" | sh`],r),s=k;return v(e,"stalled"===s?"timeout":s,{duration_ms:Date.now()-o,..."stalled"===s||"timeout"===s?{detail:{reason:s}}:{}}),i||!t.npmPackage?i:(console.log(n.yellow(" ! "+a.t(j[s],{minutes:String(3)}))),v(e,"fallback"),M(t))}async function M(t){const e=g(t,await f(t)),[o,...r]=e.split(" "),i=`install-npm:${t.command}`,s=Date.now();v(i,"start");const l=await b(o,r);return v(i,l?"ok":"fail",{duration_ms:Date.now()-s}),l||"win32"===process.platform||console.log(n.yellow(" ! "+a.t("upgrade.npm_permission",{command:e}))),l}async function D(t,e){switch(e.kind){case"npm":t.npmPackage&&await b("npm",["uninstall","-g",t.npmPackage]);break;case"brew":t.brew&&await b("brew",["uninstall",t.brew]);break;case"native":try{o(e.path,{force:!0});for(const e of t.native?.dataDirs??[])o(r(y(),...e),{recursive:!0,force:!0})}catch(t){s.logError("upgrade: remove native copy",t)}break;default:return!1}return m(),!u(t).some(t=>t.path===e.path)}function x(t,e){switch(e.kind){case"npm":return w(t);case"brew":return`brew uninstall ${t.brew}`;case"native":return a.t("upgrade.delete_paths",{paths:[e.path,...(t.native?.dataDirs??[]).map(t=>r(y(),...t))].join("、")});default:return a.t("upgrade.delete_paths",{paths:e.path})}}export async function upgradeTool(t){const e=Date.now();v(`upgrade:${t}`,"start");try{const o=await async function(t){const e=p(t),o=e?.installSpec;if(!e||!o)throw new Error(a.t("upgrade.unsupported",{tool:t}));const r=e.displayName;m();const g=u(o);if(console.log(n.bold(`\n${r}`)),0===g.length)console.log(a.t("upgrade.not_installed_installing",{tool:r})),await mirrorInstall(o);else{const t=g[0];console.log(n.gray(" "+a.t("upgrade.current",{copy:d(t)})));const e=await async function(t,e){switch(e.kind){case"native":return b(e.path,t.native?.updateArgs??["update"],{timeoutMs:t.native?.updateTimeoutMs});case"npm":return M(t);case"brew":return!!t.brew&&b("brew",["upgrade",t.brew]);default:return!1}}(o,t);e||"other"!==t.kind?e||"npm"===t.kind||"native"===t.kind&&await async function(t){const e=t.native?.mirrorUpdate;if(!e)return!1;console.log(n.yellow(" ! "+a.t("upgrade.native_mirror_trying")));try{const{version:t,manifestVerified:o}=await e();return console.log(n.green(" √ "+a.t(o?"upgrade.native_mirror_done_verified":"upgrade.native_mirror_done",{version:t}))),!0}catch(t){return s.logError("upgrade: native mirror",t),console.log(n.yellow(" ! "+a.t("upgrade.native_mirror_failed",{reason:t instanceof Error?t.message:String(t)}))),!1}}(o)||(console.log(n.yellow(" ! "+a.t("upgrade.fallback_mirror"))),await mirrorInstall(o)):console.log(n.yellow(" ! "+a.t("upgrade.unknown_install",{path:t.path})))}m(),await async function(t,e){const o=u(t);if(o.length<2)return;const r=function(t){return t.reduce((t,e)=>e.version&&(!t.version||l(e.version,t.version)>0)?e:t)}(o),s=o.filter(t=>t!==r);console.log(n.yellow(" ! "+a.t("upgrade.duplicates",{tool:e,keep:d(r),others:s.map(d).join("、")})));let c=_();c||h()||!process.stdin.isTTY||(c=(await i.prompt([{type:"confirm",name:"remove",message:a.t("upgrade.confirm_remove"),default:!0}])).remove);for(const e of s)c&&await D(t,e)?console.log(n.green(" √ "+a.t("upgrade.removed",{copy:d(e)}))):console.log(n.yellow(" ! "+a.t("upgrade.remove_by_hand",{how:x(t,e)})))}(o,r),m();const f=u(o)[0];if(!f)return console.log(n.red(" × "+a.t("upgrade.still_missing",{tool:r}))),!1;const w=c(t);if(w&&f.version&&l(f.version,w)<0){console.log(n.yellow(" ! "+a.t("upgrade.still_old",{copy:d(f),min:w})));for(const t of e.upgradeHints?.(w)??[])console.log(n.yellow(" ! "+t));return!1}return console.log(n.green(" √ "+a.t("upgrade.done",{copy:d(f)}))),!0}(t);return v(`upgrade:${t}`,o?"ok":"fail",{duration_ms:Date.now()-e}),o}catch(o){throw v(`upgrade:${t}`,"fail",{duration_ms:Date.now()-e,error:o}),o}}
1
+ import{spawn as t,spawnSync as e}from"child_process";import{rmSync as o}from"fs";import{join as r}from"path";import n from"chalk";import i from"inquirer";import{i18n as s}from"./i18n.js";import{logger as a}from"../utils/logger.js";import{compareVersions as l,getToolMinVersion as c}from"./model-registry.js";import{getAdapter as p}from"./tools/registry.js";import{findInstalls as u,forgetInstalls as m,installLabel as d,npmMirrorInstallCommand as g,npmMirrorVersion as f,npmUninstallCommand as w}from"./tools/tool-installs.js";import{toolHome as h}from"./tools/types.js";import{isNonInteractive as y,shouldAutoConfirm as v}from"./runtime-options.js";import{track as _}from"./telemetry.js";import{isWindowsPowerShell as k,powerShellEnv as b}from"./powershell-env.js";let $="ok",j=async(t,o,r={})=>(console.log(n.gray("$ ")+[t,...o].join(" ")),$=r.idleMs?await runWatched(t,o,r):function(t,o,r){const n=T(t)?e(D(t,o),{stdio:"inherit",shell:!0,timeout:r}):e(t,o,{stdio:"inherit",timeout:r,env:M(t)});return n.error&&a.logError(`upgrade: ${t}`,n.error),"ETIMEDOUT"===n.error?.code?"timeout":0===n.status?"ok":"fail"}(t,o,r.timeoutMs),"ok"===$);const T=t=>"win32"===process.platform&&!/\.exe$/i.test(t),M=t=>k(t)?b():process.env,D=(t,e)=>[t,...e].map(t=>/\s/.test(t)?`"${t}"`:t).join(" ");export function runWatched(e,o,r){return new Promise(n=>{const i=T(e)?t(D(e,o),{stdio:["inherit","pipe","pipe"],shell:!0}):t(e,o,{stdio:["inherit","pipe","pipe"],env:M(e)});let s;const l=t=>{s=t,i.kill()};let c;const p=()=>{clearTimeout(c),r.idleMs&&(c=setTimeout(()=>l("stalled"),r.idleMs))},u=r.timeoutMs?setTimeout(()=>l("timeout"),r.timeoutMs):void 0;p(),i.stdout?.on("data",t=>{process.stdout.write(t),p()}),i.stderr?.on("data",t=>{process.stderr.write(t),p()});const m=t=>{clearTimeout(c),clearTimeout(u),n(t)};i.on("error",t=>{a.logError(`upgrade: ${e}`,t),m(s??"fail")}),i.on("close",t=>{s&&process.stdout.write("\n"),m(s??(0===t?"ok":"fail"))})})}export function setUpgradeRunnerForTests(t){j=async(e,o,r)=>{const n=await t(e,o,r);return $="string"==typeof n?n:n?"ok":"fail","ok"===$}}const x={ok:"install.script_failed_npm",fail:"install.script_failed_npm",stalled:"install.script_stalled_npm",timeout:"install.script_timeout_npm"};export async function mirrorInstall(t){if(!t.mirrorScript)return E(t);console.log(n.gray(s.t("install.downloading")));const e=`install-script:${t.command}`,o=Date.now();_(e,"start");const r={idleMs:18e4,timeoutMs:36e5},i="win32"===process.platform?await j("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",`irm ${t.mirrorScript.ps1} -TimeoutSec 30 | iex`],r):await j("sh",["-c",`script=$(curl -fsSL --connect-timeout 15 --max-time 60 ${t.mirrorScript.sh}) && printf '%s\\n' "$script" | sh`],r),a=$;return _(e,"stalled"===a?"timeout":a,{duration_ms:Date.now()-o,..."stalled"===a||"timeout"===a?{detail:{reason:a}}:{}}),i||!t.npmPackage?i:(console.log(n.yellow(" ! "+s.t(x[a],{minutes:String(3)}))),_(e,"fallback"),E(t))}async function E(t){const e=g(t,await f(t)),[o,...r]=e.split(" "),i=`install-npm:${t.command}`,a=Date.now();_(i,"start");const l=await j(o,r);return _(i,l?"ok":"fail",{duration_ms:Date.now()-a}),l||"win32"===process.platform||console.log(n.yellow(" ! "+s.t("upgrade.npm_permission",{command:e}))),l}async function S(t,e){switch(e.kind){case"npm":t.npmPackage&&await j("npm",["uninstall","-g",t.npmPackage]);break;case"brew":t.brew&&await j("brew",["uninstall",t.brew]);break;case"native":try{o(e.path,{force:!0});for(const e of t.native?.dataDirs??[])o(r(h(),...e),{recursive:!0,force:!0})}catch(t){a.logError("upgrade: remove native copy",t)}break;default:return!1}return m(),!u(t).some(t=>t.path===e.path)}function P(t,e){switch(e.kind){case"npm":return w(t);case"brew":return`brew uninstall ${t.brew}`;case"native":return s.t("upgrade.delete_paths",{paths:[e.path,...(t.native?.dataDirs??[]).map(t=>r(h(),...t))].join("、")});default:return s.t("upgrade.delete_paths",{paths:e.path})}}export async function upgradeTool(t){const e=Date.now();_(`upgrade:${t}`,"start");try{const o=await async function(t){const e=p(t),o=e?.installSpec;if(!e||!o)throw new Error(s.t("upgrade.unsupported",{tool:t}));const r=e.displayName;m();const g=u(o);if(console.log(n.bold(`\n${r}`)),0===g.length)console.log(s.t("upgrade.not_installed_installing",{tool:r})),await mirrorInstall(o);else{const t=g[0];console.log(n.gray(" "+s.t("upgrade.current",{copy:d(t)})));const e=await async function(t,e){switch(e.kind){case"native":return j(e.path,t.native?.updateArgs??["update"],{timeoutMs:t.native?.updateTimeoutMs});case"npm":return E(t);case"brew":return!!t.brew&&j("brew",["upgrade",t.brew]);default:return!1}}(o,t);e||"other"!==t.kind?e||"npm"===t.kind||"native"===t.kind&&await async function(t){const e=t.native?.mirrorUpdate;if(!e)return!1;console.log(n.yellow(" ! "+s.t("upgrade.native_mirror_trying")));try{const{version:t,manifestVerified:o}=await e();return console.log(n.green(" √ "+s.t(o?"upgrade.native_mirror_done_verified":"upgrade.native_mirror_done",{version:t}))),!0}catch(t){return a.logError("upgrade: native mirror",t),console.log(n.yellow(" ! "+s.t("upgrade.native_mirror_failed",{reason:t instanceof Error?t.message:String(t)}))),!1}}(o)||(console.log(n.yellow(" ! "+s.t("upgrade.fallback_mirror"))),await mirrorInstall(o)):console.log(n.yellow(" ! "+s.t("upgrade.unknown_install",{path:t.path})))}m(),await async function(t,e){const o=u(t);if(o.length<2)return;const r=function(t){return t.reduce((t,e)=>e.version&&(!t.version||l(e.version,t.version)>0)?e:t)}(o),a=o.filter(t=>t!==r);console.log(n.yellow(" ! "+s.t("upgrade.duplicates",{tool:e,keep:d(r),others:a.map(d).join("、")})));let c=v();c||y()||!process.stdin.isTTY||(c=(await i.prompt([{type:"confirm",name:"remove",message:s.t("upgrade.confirm_remove"),default:!0}])).remove);for(const e of a)c&&await S(t,e)?console.log(n.green(" √ "+s.t("upgrade.removed",{copy:d(e)}))):console.log(n.yellow(" ! "+s.t("upgrade.remove_by_hand",{how:P(t,e)})))}(o,r),m();const f=u(o)[0];if(!f)return console.log(n.red(" × "+s.t("upgrade.still_missing",{tool:r}))),!1;const w=c(t);if(w&&f.version&&l(f.version,w)<0){console.log(n.yellow(" ! "+s.t("upgrade.still_old",{copy:d(f),min:w})));for(const t of e.upgradeHints?.(w)??[])console.log(n.yellow(" ! "+t));return!1}return console.log(n.green(" √ "+s.t("upgrade.done",{copy:d(f)}))),!0}(t);return _(`upgrade:${t}`,o?"ok":"fail",{duration_ms:Date.now()-e}),o}catch(o){throw _(`upgrade:${t}`,"fail",{duration_ms:Date.now()-e,error:o}),o}}
@@ -0,0 +1,32 @@
1
+ import type { CompanionApp } from './types.js';
2
+ /**
3
+ * OpenAI's ChatGPT desktop app (it contains Codex: the Codex mode runs a bundled Codex on `CODEX_HOME`, so the
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
6
+ * persistent.oaistatic.com/codex-app-prod, re-published hourly with a `SHA256SUMS`).
7
+ *
8
+ * - macOS: Apple silicon only (`ChatGPT.dmg`; the Sparkle feed has no Intel build). Bundle id `com.openai.codex`,
9
+ * team `2DC432GLL2`. Older installs are named `Codex.app` with the same id; one named `ChatGPT.app` with another
10
+ * id (the earlier ChatGPT app) isn't touched.
11
+ * - Windows: MSIX `OpenAI.Codex` (family `OpenAI.Codex_2p2nqsd0c76g0`), the Microsoft Store package itself: signed
12
+ * through the Store with OpenAI's publisher id, so `Add-AppxPackage` upgrades a Store install in place.
13
+ * The package version (x.y.z.0) is numbered differently from macOS: the mirror has one version file per build.
14
+ */
15
+ /** Apple Developer Team ID of OpenAI (`codesign -dv /Applications/ChatGPT.app`). */
16
+ export declare const OPENAI_TEAM_ID = "2DC432GLL2";
17
+ export declare const CHATGPT_PACKAGE_FAMILY = "OpenAI.Codex_2p2nqsd0c76g0";
18
+ export declare const chatgptInstaller: import("./desktop-app-installer.js").DesktopAppInstaller;
19
+ declare let run: (command: string, args: string[], timeout?: number) => {
20
+ status: number | null;
21
+ stdout: string;
22
+ };
23
+ /** Tests replace the process runner. */
24
+ export declare function setChatgptProcessRunnerForTests(fake: typeof run): void;
25
+ /** Only the installed copy's main process counts (not another app that happens to be called ChatGPT). */
26
+ export declare function isChatgptRunning(): boolean;
27
+ /** Quit (gracefully, then forcibly); true when no instance is left. */
28
+ export declare function quitChatgpt(): boolean;
29
+ /** Start the installed app; true when it is running afterwards. */
30
+ export declare function launchChatgpt(): boolean;
31
+ export declare const chatgptDesktopApp: CompanionApp;
32
+ export {};
@@ -0,0 +1 @@
1
+ import{spawnSync as t}from"child_process";import{logger as n}from"../../utils/logger.js";import{brand as e}from"../brand.js";import{powerShellEnv as o}from"../powershell-env.js";import{existsSync as r}from"fs";import{join as i}from"path";import{appPlatform as a,createDesktopAppInstaller as s,isArm64Machine as p,macAppVersion as l,macBundleId as c}from"./desktop-app-installer.js";import{toolHome as u}from"./types.js";export const OPENAI_TEAM_ID="2DC432GLL2";const d="com.openai.codex";export const CHATGPT_PACKAGE_FAMILY="OpenAI.Codex_2p2nqsd0c76g0";const g=s({id:"chatgpt-desktop",mirror:()=>e().installMirror?.chatgptDesktop,asset:(t,n)=>"darwin"===t?n?"ChatGPT-mac-arm64.dmg":null:"win32"===t?n?"ChatGPT-win-arm64.msix":"ChatGPT-win-x64.msix":null,versionFile:(t,n)=>"win32"===t?`version-win-${n?"arm64":"x64"}.txt`:"version.txt",macApp:{source:"ChatGPT.app",installedNames:["ChatGPT.app","Codex.app"],bundleId:d,teamId:"2DC432GLL2"},windows:{signer:new RegExp("^CN=50BDFD77-8903-4850-9FFE-6E8522F64D5B$"),packageFilter:`$_.PackageFamilyName -eq '${CHATGPT_PACKAGE_FAMILY}'`},extraBlocker:t=>"darwin"===t&&!g.installedMacApp()&&["/Applications",i(u(),"Applications")].some(t=>{const n=i(t,"ChatGPT.app");return r(i(n,"Contents","Info.plist"))&&c(n)!==d})?"other_app":null});export const chatgptInstaller=g;let h=(e,r,i=3e4)=>{const a=t(e,r,{encoding:"utf-8",timeout:i,windowsHide:!0,stdio:["ignore","pipe","pipe"],env:o()});return a.error&&n.logError(`chatgpt-desktop: ${e}`,a.error),{status:a.status,stdout:`${a.stdout??""}`}};export function setChatgptProcessRunnerForTests(t){h=t}function m(t,n){return h("powershell.exe",["-NoProfile","-NonInteractive","-Command",t],n)}const C=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),w="Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*\\WindowsApps\\OpenAI.Codex_*\\app\\ChatGPT.exe' }";function A(t){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,t)}export function isChatgptRunning(){if("darwin"===a()){const t=g.installedMacApp();return Boolean(t)&&0===h("pgrep",["-f",`^${C(t)}/Contents/MacOS/`]).status}return"win32"===a()&&Number(m(`@(${w}).Count`).stdout.trim())>0}export function quitChatgpt(){if("darwin"===a()){const t=g.installedMacApp();h("osascript",["-e",`tell application id "${d}" to quit`]);for(let t=0;t<30&&isChatgptRunning();t++)A(500);t&&isChatgptRunning()&&h("pkill",["-f",`^${C(t)}/Contents/MacOS/`]),A(1e3)}else{if("win32"!==a())return!1;m([`$p = @(${w})`,"$p | ForEach-Object { [void]$_.CloseMainWindow() }","Start-Sleep -Seconds 3",`@(${w}) | Stop-Process -Force -ErrorAction SilentlyContinue`,"Start-Sleep -Seconds 1"].join("\n"),6e4)}return!isChatgptRunning()}export function launchChatgpt(){if("darwin"===a()){const t=g.installedMacApp();if(!t)return!1;h("open",[t])}else{if("win32"!==a())return!1;m(`Start-Process 'shell:AppsFolder\\${CHATGPT_PACKAGE_FAMILY}!App'`,6e4)}for(let t=0;t<20;t++){if(isChatgptRunning())return!0;A(500)}return!1}export const chatgptDesktopApp={name:"chatgpt",displayName:"ChatGPT",installedVersion:function(){if("darwin"===a()){const t=g.installedMacApp();return t?l(t):null}return"win32"===a()?g.windowsPackageVersion():null},runningApp:{isRunning:isChatgptRunning,quit:quitChatgpt,launch:launchChatgpt},appInstaller:{vendor:"OpenAI",sizeMb:()=>"darwin"===a()?800:p()?1050:1e3,downloadPage:"https://chatgpt.com/download",blocker:g.blocker,latestVersion:()=>g.latestVersion(),installedPath:()=>"darwin"===a()?g.installedMacApp():"win32"!==a()?null:g.windowsPackageVersion()?"MSIX":null,install:t=>g.install(t)}};
@@ -0,0 +1,31 @@
1
+ import { type DesktopInstallProgress } from './desktop-app-installer.js';
2
+ /**
3
+ * Claude Desktop from the brand's download mirror (`installMirror.claudeDesktop`, e.g. CNB `lib/claude-desktop`:
4
+ * Anthropic's installers re-published byte for byte every hour with a `SHA256SUMS`); the download, checks and
5
+ * install are the shared ones in `desktop-app-installer.ts`. The Windows mirror only carries the MSIX build, so the
6
+ * classic Squirrel install (`%LOCALAPPDATA%\AnthropicClaude`) isn't touched.
7
+ */
8
+ /** Apple Developer Team ID of Anthropic PBC (`codesign -dv /Applications/Claude.app`). */
9
+ export declare const ANTHROPIC_TEAM_ID = "Q6L2SF6YDW";
10
+ export { DesktopInstallError, parseSha256Sums, setDesktopAppDepsForTests } from './desktop-app-installer.js';
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
+ /** The release file for this machine; null where the mirror has none (Linux). */
15
+ export declare const desktopAssetName: () => string | null;
16
+ /** Why vibecli can't install or upgrade the app here (a locale key under `app_update.reason_*`), or null. */
17
+ export declare const desktopInstallBlocker: () => string | null;
18
+ /** The mirror's newest Claude Desktop version (its `version.txt`). */
19
+ export declare const fetchLatestDesktopVersion: (timeoutMs?: number) => Promise<string>;
20
+ /** The installed bundle (the one vibecli upgrades in place), else null. */
21
+ export declare const installedMacApp: () => string | null;
22
+ export declare const verifyMacApp: (app: string) => void;
23
+ export declare const verifyWindowsPackage: (msix: string) => void;
24
+ /**
25
+ * Downloads the mirror's latest build, checks it and installs it over the current copy (or as a new one).
26
+ * The app must not be running. Throws `DesktopInstallError` (localized) and leaves the installed copy as it was.
27
+ */
28
+ export declare function installLatestDesktop(progress?: DesktopInstallProgress): Promise<{
29
+ version: string;
30
+ location: string;
31
+ }>;
@@ -0,0 +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 +1 @@
1
- import{execFileSync as e}from"child_process";import{createHash as t}from"crypto";import{createReadStream as r,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 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 y from"zlib";import{logger as v}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";export function readDesktopCliPin(e,t,r=!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?.[t],l=r?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 P(){const t=m(process.env.LOCALAPPDATA||"","AnthropicClaude");if(process.env.LOCALAPPDATA&&o(t)){const e=n(t).filter(e=>/^app-\d+\.\d+\.\d+$/.test(e)).sort((e,t)=>t.localeCompare(e,void 0,{numeric:!0}));for(const r of e){const e=m(t,r,"resources","app.asar");if(o(e))return e}}try{const t=e("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${N.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | ForEach-Object { $_.InstallLocation }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]});for(const e of t.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)){const t=m(e,"app","resources","app.asar");if(o(t))return t}}catch(e){v.logError("claude-desktop-cli.windowsAsarPath",e)}return null}function L(){if(k("TEST_HOME")?.trim())return null;for(const e of["/usr/lib/claude-desktop","/opt/Claude"]){const t=m(e,"resources","app.asar");if(o(t))return t}return null}function O(){for(const e of["/Applications/Claude.app",m(A(),"Applications","Claude.app")]){const t=m(e,"Contents","Resources","app.asar");if(o(t))return t}return null}export function installedDesktopAsarPath(){return"win32"===process.platform?P():"darwin"===process.platform?O():"linux"===process.platform?L():null}class E extends b{dest;buffer=Buffer.alloc(0);remaining=0;padding=0;file=null;longName=null;collectingLongName=null;constructor(e){super(),this.dest=e}_write(e,t,r){this.buffer=this.buffer.length?Buffer.concat([this.buffer,e]):e,this.drain().then(()=>r()).catch(r)}_final(e){this.remaining>0?e(new Error("archive ends inside an entry")):e()}target(e){const t=h(e);if(!t||d(t)||".."===t||t.startsWith(`..${w}`))throw new Error(`unsafe path in archive: ${e}`);return m(this.dest,t)}async closeFile(){const e=this.file;this.file=null,e&&await new Promise((t,r)=>e.end(e=>e?r(e):t()))}async drain(){for(;;){if(this.remaining>0){if(0===this.buffer.length)return;const e=this.buffer.subarray(0,Math.min(this.remaining,this.buffer.length));this.buffer=this.buffer.subarray(e.length),this.remaining-=e.length,this.collectingLongName?this.collectingLongName.push(e):this.file&&!this.file.write(e)&&await new Promise(e=>this.file.once("drain",()=>e())),0===this.remaining&&(this.collectingLongName&&(this.longName=Buffer.concat(this.collectingLongName).toString("utf-8").replace(/\0.*$/s,""),this.collectingLongName=null),await this.closeFile());continue}if(this.padding>0){if(this.buffer.length<this.padding)return;this.buffer=this.buffer.subarray(this.padding),this.padding=0}if(this.buffer.length<512)return;const e=this.buffer.subarray(0,512);if(this.buffer=this.buffer.subarray(512),e.every(e=>0===e))continue;const t=(t,r)=>e.subarray(t,t+r).toString("utf-8").replace(/\0.*$/s,""),r=t(345,155);let o=r?`${r}/${t(0,100)}`:t(0,100);null!==this.longName&&(o=this.longName,this.longName=null);const n=parseInt(t(124,12).trim()||"0",8),i=511&parseInt(t(100,8).trim()||"644",8),a=t(156,1)||"0";if(this.remaining=n,this.padding=(512-n%512)%512,"L"===a)this.collectingLongName=[];else if("x"===a||"g"===a);else if("5"===a)await l(this.target(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)}`),v=`${g}.download`;try{await f(w,{force:!0}),await f(g,{recursive:!0,force:!0});let n="mirror";if(e.bundle){if(e.onDownload?.(s.version),await e.download(s.version,v),await async function(e,s,o){const n=t("sha256");if(await D(r(e),new b({write(e,t,r){n.update(e),r()}})),n.digest("hex")!==s)throw new Error("downloaded bundle does not match the app's checksum");const i=y.createZstdDecompress;if(!i)throw new Error(`Node ${process.version} cannot decompress zstd (needs 22.15 or newer)`);await D(r(e),i(),new E(o))}(v,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 t=e.localVersionsDir?m(e.localVersionsDir,s.version):null;if(t&&o(t))await a(t,g),n="local";else{e.onDownload?.(s.version);const t=e.download??((t,r)=>$(e.platformKey,t,r));await t(s.version,g)}await l(d,{recursive:!0}),await u(g,h)}return await p(w,s.checksum),{status:"installed",version:s.version,source:n}}finally{await f(g,{recursive:!0,force:!0}),await f(v,{force:!0}),await f(`${g}.download`,{force:!0})}}let S=(...e)=>fetch(...e);export function setDesktopCliFetchForTests(e){S=e}export async function ensureDesktopCli(e){const t=x();if(!t)return{status:"skipped",reason:"unsupported architecture"};if("win32"===process.platform){const r=P();return r?installDesktopCli({userDataDir:e.userDataDir,asar:i(r),platformKey:t,localVersionsDir:C().versionsDir,onDownload:e.onDownload}):{status:"skipped",reason:"app.asar not found"}}if("darwin"===process.platform){const r=O();if(!r)return{status:"skipped",reason:"app.asar not found"};const{mirrorRepo:o,desktopVersion:n}=e;return installDesktopCli({userDataDir:e.userDataDir,asar:i(r),platformKey:t,bundle:!0,onDownload:e.onDownload,download:o&&n?(e,r)=>async function(e,t){const r=await S(e,{signal:AbortSignal.timeout(9e5)});if(!r.ok||!r.body)throw new Error(`${e}: HTTP ${r.status}`);await D(g.fromWeb(r.body),s(t))}(`${o}/-/releases/download/v${n}/claude-code-${e}-${t}.app.tar.zst`,r):void 0})}if("linux"===process.platform){const r=L();return r?installDesktopCli({userDataDir:e.userDataDir,asar:i(r),platformKey:t.replace(/-musl$/,""),localVersionsDir:t.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 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 +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 p from"chalk";import{logger as u}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 D}from"../brand.js";import{backupBeforeWrite as b,atomicWriteFile as A}from"./backup.js";import{toolHome as C}from"./types.js";import{canRestartDesktop as w,isDesktopRunning as E,restartDesktop as v,WINDOWS_PACKAGE_FAMILIES as M}from"./desktop-process.js";import{setUserRegistryElevated as O}from"./elevated-reg.js";import{UserActionNeeded as S}from"./types.js";import{baseModelId as x,isClaudeModel as j}from"../model-catalog.js";import{getDefaultModels as P}from"../model-registry.js";import{claudeFormat as L,nestedClaudeProjectFiles as T}from"../sessions/formats.js";const I=h;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const $=/^[a-f0-9-]{36}$/;function N(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 M.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 R=()=>l(claudeDesktopUserDataDir(),"configLibrary"),G=()=>l(R(),"_meta.json"),K=e=>l(R(),`${e}.json`),U=()=>l(claudeDesktopUserDataDir(),"claude_desktop_config.json");function F(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return N(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 H(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),b(n),A(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){u.logError("claude-desktop.writeJson.chmod",e)}}function J(){const e=F(G());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>N(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function B(e){return e?.entries.find(e=>e.name===I&&$.test(e.id))}const V={opus:0,fable:1,sonnet:2,haiku:3};function W(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=W(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function z(e){return W(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>x(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(j)){i=[...new Set(t.models.filter(j).map(x))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=x(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const l=e=>V[z(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=W(e),s=W(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const a=new Set,c=new Map,d=new Set(Object.values(P()).map(e=>x(e||"")));for(const e of n){const t=z(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=z(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 Z=null,q=null,X=null,Y=null,Q=null;function ee(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 te={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}},oe=()=>{if(_("TEST_HOME")?.trim())return null;const e=g();return e?r(e):null};let ne=oe,re=null;export function setDesktopAsarForTests(e){ne=void 0===e?oe:()=>e,re=null}function se(){if(re)return re;let e=null;try{e=ne()}catch(e){u.logError("claude-desktop.unsupportedDefaults",e)}return re=new Set(e?Object.keys(te).filter(t=>!e.includes(t)):[]),re}function ie(e){const t=se();return Object.fromEntries(Object.entries(te).filter(([o])=>!e(o)&&!t.has(o)))}function le(e){const t={...ie(t=>t in e),...e};for(const e of se())e in t&&JSON.stringify(t[e])===JSON.stringify(te[e])&&delete t[e];return t}const ae="SOFTWARE\\Policies\\Claude",ce=`HKCU\\${ae}`,de="HKLM\\SOFTWARE\\Policies\\Claude",pe=`${D}-claude-policy.reg`,ue=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let fe=ue;export function setRegRunnerForTests(e){fe=e??ue}function me(){return"win32"===process.platform}function ge(e){let t;try{t=fe(["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 ye(){if(null===ge(ce))return;const e=l(claudeDesktopUserDataDir(),pe);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),b(e),fe(["export",ce,e,"/y"])}catch(e){u.logError("claude-desktop.snapshotPolicy",e)}}function he(e){ye();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)];fe(["add",ce,"/v",t,"/t",e,"/d",n,"/f"])}}function ke(e){const t=ge(ce);if(!t)return;ye();let o=t.size;for(const n of e)t.has(n)&&(fe(["delete",ce,"/v",n,"/f"]),o-=1);o<=0&&fe(["delete",ce,"/f"])}function _e(){const e=!Boolean(_("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:l(C(),"AppData","Local");return!!t(l(e,"AnthropicClaude"))||M.some(o=>t(l(e,"Packages",o,"LocalCache","Local","Claude-3p")))}const De=[...Object.keys(ee({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(te)];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 ge(de)?de:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return u.logError("claude-desktop.detectManagedConfig",e),null}}function be(){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"),...M.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 Ae=/\d+\.\d+\.\d+/;function Ce(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}function we(){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?Ae.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 Ce(t)}const o=M.map(e=>`'${e}'`).join(",");return Ce(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>Ae.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 Ae.exec(e)?.[0]??null}catch{return null}return null}const Ee="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",ve="darwin"!==process.platform&&"win32"!==process.platform||!k().installMirror?.claudeDesktop?"https://claude.ai/download":k().installMirror.claudeDesktop,Me="darwin"===process.platform?`open ${ve}`:"win32"===process.platform?`start ${ve}`:`xdg-open ${ve}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:Ee,installCommand:Me,configPath:G(),sessions:{format:L,files:()=>T(l(claudeDesktopUserDataDir(),"local-agent-mode-sessions"))},isInstalled:be,installedVersion:we,prepareRuntime:async function(){if(!["win32","darwin","linux"].includes(process.platform)||!be())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(p.gray(" "+y.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?we():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 u.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:w()?{isRunning:E,restart:v}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const t=J()??{appliedId:"",entries:[]};let o=B(t);o||(o={id:a(),name:I},t.entries.push(o));const n=ee(e);q=!0===n.disableDeploymentModeChooser,Z={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=F(K(o.id))??{};H(K(o.id),{...le(r),...n}),t.appliedId=o.id,H(G(),t);const s=F(U())??{};if("3p"!==s.deploymentMode&&H(U(),{...s,deploymentMode:"3p"}),Y=null,Q=null,X=null,me()){const t=ge(ce),o=De.filter(e=>t?.has(e)),r={...ie(e=>t?.has(e)??!1),...n},s=!0===e.settings?.force_registry,i=!s&&_e();try{i?ke(De):he(r),X=i?"removed":"written"}catch(e){u.logError("claude-desktop.policy",e),Y=e instanceof Error?e.message.trim():String(e),(o.length>0||s)&&(Q=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=Q;return e?{prompt:y.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!O(ae,e.values,e.deleteKey))return!1;const t=ge(ce),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(Q=null,Y=null),o}}:null},unloadPrivateConfig(){if(me())try{ke(De)}catch(e){u.logError("claude-desktop.removePolicy",e)}const e=J(),t=B(e);if(!e||!t)return;b(K(t.id)),s(K(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(b(G()),s(G(),{force:!0})):H(G(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=J(),t=B(e);if(!t||e?.appliedId!==t.id)return null;const o=F(K(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=J(),o=B(t);if(!o||t?.appliedId!==o.id)return;const n=F(K(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog),s=JSON.stringify(n.inferenceModels)!==JSON.stringify(r),i={...le(n),inferenceModels:r},l=JSON.stringify(i)!==JSON.stringify(n);Q=null;const a=me()?ge(ce):null;if(!a?.has("inferenceModels"))return void(l&&H(K(o.id),i));const c=De.filter(e=>a.has(e)),d=!0!==e.settings?.force_registry&&_e();if(l&&H(K(o.id),i),d||s)try{d?ke(De):he({inferenceModels:r})}catch(e){throw u.logError("claude-desktop.refreshModelMenu registry",e),Q=d?{action:"remove",values:Object.fromEntries(c.map(e=>[e,null])),deleteKey:c.length===a.size}:{action:"write",values:{inferenceModels:r},deleteKey:!1},new S(y.t("tools.claude_desktop.refresh_needs_admin"))}},detectCurrentConfig(){try{if(me()){const e=ge(ce)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=B(J());if(!e)return{apiKey:null};const t=F(K(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return u.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=J(),t=B(e);return{meta:e,applied:t?F(K(t.id)):null,desktop:F(U()),...me()?{policy:Object.fromEntries(ge(ce)??[])}:{}}}catch(e){return u.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;me()&&ge(ce)?.has("inferenceModels")&&(ye(),fe(["delete",ce,"/v","inferenceModels","/f"]),e=!0);const t=B(J()),o=t?F(K(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;H(K(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(Z&&Z.models.length>0){const{models:t,source:o,groups:n}=Z;e.push({kind:"summary",text: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!==q&&e.push({kind:"detail",text:y.t(q?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),w()&&!E()||e.push({kind:"action",text:y.t("tools.claude_desktop.restart_hint")}),Q?(e.push({kind:"action",text:y.t(`tools.claude_desktop.policy_stale_${Q.action}`)}),e.push({kind:"detail",text:Y??""})):Y?(e.push({kind:"detail",text:y.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:Y})):me()&&X&&e.push({kind:"detail",text:y.t(`tools.claude_desktop.registry_${X}`,{key:ce})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:y.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[G(),U()];try{const t=B(J());t&&e.push(K(t.id))}catch{}return e},backupPaths(){const e=[G(),U()];me()&&e.push(l(claudeDesktopUserDataDir(),pe));try{const t=B(J());t&&e.push(K(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 p from"chalk";import{logger as u}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 b}from"../brand.js";import{backupBeforeWrite as D,atomicWriteFile as w}from"./backup.js";import{toolHome as A}from"./types.js";import{canRestartDesktop as C,isDesktopRunning as E,launchDesktop as v,quitDesktop as M,restartDesktop as O,WINDOWS_PACKAGE_FAMILIES as S}from"./desktop-process.js";import{desktopInstallBlocker as x,fetchLatestDesktopVersion as P,installedMacApp as j,installLatestDesktop as I}from"./claude-desktop-app.js";import{setUserRegistryElevated as L}from"./elevated-reg.js";import{UserActionNeeded as T}from"./types.js";import{baseModelId as $,isClaudeModel as N}from"../model-catalog.js";import{getDefaultModels as R}from"../model-registry.js";import{claudeFormat as G,nestedClaudeProjectFiles as K}from"../sessions/formats.js";import{powerShellEnv as U}from"../powershell-env.js";const F=h;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const H=/^[a-f0-9-]{36}$/;function J(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 S.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 V=()=>l(claudeDesktopUserDataDir(),"configLibrary"),B=()=>l(V(),"_meta.json"),W=e=>l(V(),`${e}.json`),z=()=>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 J(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 X(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),D(n),w(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){u.logError("claude-desktop.writeJson.chmod",e)}}function Z(){const e=q(B());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>J(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function Y(e){return e?.entries.find(e=>e.name===F&&H.test(e.id))}const Q={opus:0,fable:1,sonnet:2,haiku:3};function ee(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=ee(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function te(e){return ee(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>$(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(N)){i=[...new Set(t.models.filter(N).map($))];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=$(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const l=e=>Q[te(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=ee(e),s=ee(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(R()).map(e=>$(e||"")));for(const e of n){const t=te(e);t&&i.includes(e)&&!d.has(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=te(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 oe=null,ne=null,re=null,se=null,ie=null;function le(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 ae={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}},ce=()=>{if(_("TEST_HOME")?.trim())return null;const e=g();return e?r(e):null};let de=ce,pe=null;export function setDesktopAsarForTests(e){de=void 0===e?ce:()=>e,pe=null}function ue(){if(pe)return pe;let e=null;try{e=de()}catch(e){u.logError("claude-desktop.unsupportedDefaults",e)}return pe=new Set(e?Object.keys(ae).filter(t=>!e.includes(t)):[]),pe}function fe(e){const t=ue();return Object.fromEntries(Object.entries(ae).filter(([o])=>!e(o)&&!t.has(o)))}function me(e){const t={...fe(t=>t in e),...e};for(const e of ue())e in t&&JSON.stringify(t[e])===JSON.stringify(ae[e])&&delete t[e];return t}const ge="SOFTWARE\\Policies\\Claude",ye=`HKCU\\${ge}`,he="HKLM\\SOFTWARE\\Policies\\Claude",ke=`${b}-claude-policy.reg`,_e=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let be=_e;export function setRegRunnerForTests(e){be=e??_e}function De(){return"win32"===process.platform}function we(e){let t;try{t=be(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function Ae(){if(null===we(ye))return;const e=l(claudeDesktopUserDataDir(),ke);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),D(e),be(["export",ye,e,"/y"])}catch(e){u.logError("claude-desktop.snapshotPolicy",e)}}function Ce(e){Ae();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];be(["add",ye,"/v",t,"/t",e,"/d",n,"/f"])}}function Ee(e){const t=we(ye);if(!t)return;Ae();let o=t.size;for(const n of e)t.has(n)&&(be(["delete",ye,"/v",n,"/f"]),o-=1);o<=0&&be(["delete",ye,"/f"])}function ve(){const e=!Boolean(_("TEST_HOME")?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:l(A(),"AppData","Local");return!!t(l(e,"AnthropicClaude"))||S.some(o=>t(l(e,"Packages",o,"LocalCache","Local","Claude-3p")))}const Me=[...Object.keys(le({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(ae)];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 we(he)?he:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return u.logError("claude-desktop.detectManagedConfig",e),null}}function Oe(){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"),...S.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 Se=/\d+\.\d+\.\d+/;function xe(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 Pe(){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?Se.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 xe(t)}const o=S.map(e=>`'${e}'`).join(",");return xe(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:U()}).split(/\r?\n/).flatMap(e=>Se.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 Se.exec(e)?.[0]??null}catch{return null}return null}const je="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",Ie="darwin"!==process.platform&&"win32"!==process.platform||!k().installMirror?.claudeDesktop?"https://claude.ai/download":k().installMirror.claudeDesktop,Le="darwin"===process.platform?`open ${Ie}`:"win32"===process.platform?`start ${Ie}`:`xdg-open ${Ie}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:je,installCommand:Le,configPath:B(),sessions:{format:G,files:()=>K(l(claudeDesktopUserDataDir(),"local-agent-mode-sessions"))},isInstalled:Oe,installedVersion:Pe,prepareRuntime:async function(){if(!["win32","darwin","linux"].includes(process.platform)||!Oe())return[];const e=Date.now();try{const t=await m({userDataDir:claudeDesktopUserDataDir(),onDownload:e=>console.log(p.gray(" "+y.t("tools.claude_desktop.code_cli_downloading",{version:e}))),desktopVersion:"darwin"===process.platform?Pe():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 u.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:O,quit:M,launch:v}:void 0,appInstaller:{vendor:"Anthropic",sizeMb:()=>300,downloadPage:"https://claude.ai/download",blocker:x,latestVersion:()=>P(),installedPath(){if("darwin"===process.platform)return j();if("win32"!==process.platform)return null;try{return Pe()?"MSIX":null}catch(e){return u.logError("claude-desktop.installedPath",e),null}},install:e=>I(e)},wantsModelCatalog:!0,loadPrivateConfig(e){const t=Z()??{appliedId:"",entries:[]};let o=Y(t);o||(o={id:a(),name:F},t.entries.push(o));const n=le(e);ne=!0===n.disableDeploymentModeChooser,oe={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=q(W(o.id))??{};X(W(o.id),{...me(r),...n}),t.appliedId=o.id,X(B(),t);const s=q(z())??{};if("3p"!==s.deploymentMode&&X(z(),{...s,deploymentMode:"3p"}),se=null,ie=null,re=null,De()){const t=we(ye),o=Me.filter(e=>t?.has(e)),r={...fe(e=>t?.has(e)??!1),...n},s=!0===e.settings?.force_registry,i=!s&&ve();try{i?Ee(Me):Ce(r),re=i?"removed":"written"}catch(e){u.logError("claude-desktop.policy",e),se=e instanceof Error?e.message.trim():String(e),(o.length>0||s)&&(ie=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=ie;return e?{prompt:y.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!L(ge,e.values,e.deleteKey))return!1;const t=we(ye),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(ie=null,se=null),o}}:null},unloadPrivateConfig(){if(De())try{Ee(Me)}catch(e){u.logError("claude-desktop.removePolicy",e)}const e=Z(),t=Y(e);if(!e||!t)return;D(W(t.id)),s(W(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(D(B()),s(B(),{force:!0})):X(B(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=Z(),t=Y(e);if(!t||e?.appliedId!==t.id)return null;const o=q(W(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=Z(),o=Y(t);if(!o||t?.appliedId!==o.id)return;const n=q(W(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog),s=JSON.stringify(n.inferenceModels)!==JSON.stringify(r),i={...me(n),inferenceModels:r},l=JSON.stringify(i)!==JSON.stringify(n);ie=null;const a=De()?we(ye):null;if(!a?.has("inferenceModels"))return void(l&&X(W(o.id),i));const c=Me.filter(e=>a.has(e)),d=!0!==e.settings?.force_registry&&ve();if(l&&X(W(o.id),i),d||s)try{d?Ee(Me):Ce({inferenceModels:r})}catch(e){throw u.logError("claude-desktop.refreshModelMenu registry",e),ie=d?{action:"remove",values:Object.fromEntries(c.map(e=>[e,null])),deleteKey:c.length===a.size}:{action:"write",values:{inferenceModels:r},deleteKey:!1},new T(y.t("tools.claude_desktop.refresh_needs_admin"))}},detectCurrentConfig(){try{if(De()){const e=we(ye)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=Y(Z());if(!e)return{apiKey:null};const t=q(W(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return u.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=Z(),t=Y(e);return{meta:e,applied:t?q(W(t.id)):null,desktop:q(z()),...De()?{policy:Object.fromEntries(we(ye)??[])}:{}}}catch(e){return u.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;De()&&we(ye)?.has("inferenceModels")&&(Ae(),be(["delete",ye,"/v","inferenceModels","/f"]),e=!0);const t=Y(Z()),o=t?q(W(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;X(W(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(oe&&oe.models.length>0){const{models:t,source:o,groups:n}=oe;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!==ne&&e.push({kind:"detail",text:y.t(ne?"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")}),ie?(e.push({kind:"action",text:y.t(`tools.claude_desktop.policy_stale_${ie.action}`)}),e.push({kind:"detail",text:se??""})):se?(e.push({kind:"detail",text:y.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:se})):De()&&re&&e.push({kind:"detail",text:y.t(`tools.claude_desktop.registry_${re}`,{key:ye})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:y.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[B(),z()];try{const t=Y(Z());t&&e.push(W(t.id))}catch{}return e},backupPaths(){const e=[B(),z()];De()&&e.push(l(claudeDesktopUserDataDir(),ke));try{const t=Y(Z());t&&e.push(W(t.id))}catch{}return e}};
@@ -1 +1 @@
1
- import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as i,statSync as s,writeFileSync as a}from"fs";import{join as l}from"path";import*as c from"@iarna/toml";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as m,atomicWriteFile as p}from"./backup.js";import{codexHome as f,pickModel as _,toolHome as g,withV1 as x}from"./types.js";import{moveSessionsToProvider as h,NO_STATE_DB as y,runningCodexPids as k}from"../codex-sessions.js";import{codexFormat as b,codexRolloutFiles as v}from"../sessions/formats.js";import{getToolDefaultModel as w}from"../model-registry.js";import{configManager as j}from"../config.js";import{maskHome as C,track as O}from"../telemetry.js";import{buildCodexCatalog as S,catalogRejections as M,codexCatalogPath as A,fillFromOlderEntries as E,installedCodexBinaries as N,loadBundledCodexModels as P,loadPreviousCodexModels as T,previousGatewayModelIds as D}from"./codex-catalog.js";import{gatewayModelSpecs as I,modelFamily as L}from"../gateway-models.js";import{findInstalls as $,mirrorInstallCommand as J,brandInstallScripts as G}from"./tool-installs.js";const R=()=>f(),K=()=>l(R(),"config.toml"),X="sessions_move_declined",B=["browser_use","browser_use_external","computer_use"];function U(e){const t=e.features;if(!Q(t)||!B.every(e=>!1===t[e]))return!1;for(const e of B)delete t[e];return!0}export function codexWorkModeOn(){try{const e=Z();return e.model_provider===Y&&Q(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function W(){try{if(Q(Z().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",l(g(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(l(e,o))));if("win32"===process.platform){const e=l(process.env.LOCALAPPDATA||l(g(),"AppData","Local"),"Packages");try{return n(e).some(e=>/^OpenAI\.(Codex|ChatGPT)/i.test(e))}catch{return!1}}return t("/usr/lib/chatgpt/ChatGPT")}export function codexConfigPath(){return K()}const q=()=>l(R(),"auth.json"),z="OPENAI_API_KEY",Y="custom",F=1e6,H=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function Q(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function V(e,t){return Q(e[t])||(e[t]={}),e[t]}function Z(){if(!t(K()))return{};const e=r(K(),"utf-8");if(!e.trim())return{};try{const t=c.parse(e);if(!Q(t))throw new Error("config.toml root must be a TOML table");return t}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed; back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function ee(){if(!t(q()))return{};try{const e=JSON.parse(r(q(),"utf-8"));return Q(e)?e:{}}catch{return{}}}let te=null;function oe(e){const t=e.profiles;if(Q(t)){for(const[e,o]of Object.entries(t))Q(o)&&2===Object.keys(o).length&&o.model_provider===Y&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let ne=null;const re=()=>l(j.getConfigDir(),"codex-catalog-check.json");function ie(e){const o=A();t(o)&&r(o,"utf-8")===e||(m(o),p(o,e))}function se(t,n){if(0===n.length)return[];const i=function(t,o){const n=e("sha256").update(t);for(const e of o)try{const t=s(e);n.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{n.update(`\0${e}`)}return n.digest("hex")}(t,n);if(function(e){try{return JSON.parse(r(re(),"utf-8")).signature===e}catch{return!1}}(i))return[];const l=M(A(),n);return 0===l.length&&function(e){try{o(j.getConfigDir(),{recursive:!0}),a(re(),JSON.stringify({signature:e})+"\n")}catch(e){d.logError("codexAdapter.rememberAccepted",e)}}(i),l}function ae(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===F&&delete e.model_context_window,e.model_auto_compact_token_limit===H&&delete e.model_auto_compact_token_limit}(e);const r=()=>{e.model_catalog_json===A()&&delete e.model_catalog_json,n&&(e.model_context_window=F,e.model_auto_compact_token_limit=H)},s=P()??T();if(!s)return d.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),ne="skipped",void r();const a=t.catalog,l=e=>S({bundled:e,specs:I("codex",a?.clientModels),...a?{keyModels:a.models}:{},gatewayAvailable:a?.models??D(),oneMillion:n,selectedModel:o,..."gateway"===a?.oneMillionSource?{claudeOneMillion:a.oneMillion}:{}}),c=e=>JSON.stringify(e,null,2)+"\n";let u=l(s);ie(c(u));const m=N(),p=se(c(u),m);if(p.length>0){const e=Date.now();let t=!1;for(const e of p){const o=P(e.bin);if(o){for(const e of[{models:E(u.models,o)},{models:E(l(o).models,o)}])if(ie(c(e)),0===se(c(e),m).length){u=e,t=!0;break}if(t)break}}if(O("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:p[0].error,detail:{rejected_by:p.map(e=>({version:e.version,path:C(e.bin)}))}}),!t)return ne={rejectedBy:p[0]},i(A(),{force:!0}),void r()}ne={listed:u.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))},e.model_catalog_json=A()}export function codexCatalogRejection(){let e;try{e=Z()}catch{return null}return e.model_catalog_json===A()&&t(A())?M(A())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=Z(),n=c.stringify(o),r="string"==typeof o.model?o.model:w("codex");ae(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=c.stringify(o);i!==n&&(m(K()),p(K(),i))}const le={command:"codex",npmPackage:"@openai/codex",native:{binDir:"win32"===process.platform?["AppData","Local","Programs","OpenAI","Codex","bin"]:[".local","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".codex","packages","standalone"]]},mirrorScript:G("codex"),brew:"codex"};export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:J(le),installSpec:le,configPath:K(),sessions:{format:b,files:()=>[...v(l(f(),"sessions")),...v(l(f(),"archived_sessions"))],resumeCommand:e=>({command:"codex",args:["resume",e]})},wantsModelCatalog:!0,listedModels(){if(Z().model_catalog_json!==A())return null;try{return(JSON.parse(r(A(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(Z().model_catalog_json!==A())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=Z(),o=function(e,t){const o=Q(e.model_providers)?e.model_providers[Y]:void 0;return!(!Q(o)||o.base_url!==t||!0===o.supports_standalone_web_search||(o.supports_standalone_web_search=!0,0))}(t,x(e.baseUrl));(U(t)||o)&&(m(K()),p(K(),c.stringify(t)))},async followUp(){if(Z().model_provider!==Y)return null;const e=await h(Y,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=j.getToolSettings("codex")[X];if("number"==typeof o&&t<=o)return null;const n={count:String(t)};return k().length>0?{hint:u.t("tools.codex.sessions_hidden_running",n)}:{prompt:u.t("tools.codex.sessions_offer",n),hint:u.t("tools.codex.sessions_hidden",n),async run(){const e=await h(Y),t=[...new Set(e.moved.map(e=>e.from))].join("、")||"-",o=String(Math.max(e.moved.length,e.dbRows)),n=[{kind:"summary",text:u.t("codex_sessions.moved",{count:o,from:t,target:Y})},{kind:"summary",text:u.t("codex_sessions.backup",{dir:e.backupDir??""})}];return e.dbSkipped&&e.dbSkipped!==y&&n.push({kind:"action",text:u.t("codex_sessions.db_skipped",{reason:e.dbSkipped})}),n},decline(){j.setToolSetting("codex",X,t)}}},applyNotes(){if("skipped"===ne)return 0===$(le).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!ne)return[];if("rejectedBy"in ne){const{bin:e,version:t}=ne.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of ne.listed)e.set(L(t.id),(e.get(L(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${u.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:u.t("tools.codex.catalog_written",{count:String(ne.listed.length),breakdown:t})},{kind:"detail",text:ne.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||W()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]},loadPrivateConfig(e){const t=w("codex"),o=_(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=x(e.baseUrl),i=Z();i.model=n,i.model_provider=Y,i.model_reasoning_effort="high",ae(i,e,n);const s=V(i,"model_providers"),a=Q(s[Y])?{...s[Y]}:{};delete a.env_key;const{experimental_bearer_token:l,...d}=a;s[Y]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,supports_standalone_web_search:!0,base_url:r},oe(i),V(V(i,"tui"),"model_availability_nux")[n]=4;const u=V(i,"features");u.goals=!0,u.js_repl=!1,U(i),m(K()),p(K(),c.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...ee(),auth_mode:"apikey",[z]:t};m(q()),p(q(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},holdsOurConfig(e){const t=ee()[z];return"string"==typeof t&&e.includes(t.trim())},unloadPrivateConfig(e){if(te=function(e){const t=ee(),o="string"==typeof t[z]?t[z].trim():"";if(!o)return Q(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[z]:n,auth_mode:r,...s}=t;return m(q()),Q(s.tokens)?(p(q(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(i(q(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(K()))return;let o;try{o=Z()}catch(e){return void d.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;Q(n)&&(delete n[Y],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===Y&&delete o.model_provider,o.model_catalog_json===A()&&delete o.model_catalog_json,U(o),oe(o),m(K()),p(K(),c.stringify(o))},unloadNotes(){switch(te){case"chatgpt":return[{kind:"action",text:u.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:u.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:u.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=ee()[z];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return Z()}catch(e){return d.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[K(),q(),A()]};export function codexAuthProblem(){try{const e=Z(),t=e.model_providers?.[Y];if(e.model_provider!==Y||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=ee();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
1
+ import{createHash as e}from"crypto";import{existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as i,statSync as s,writeFileSync as a}from"fs";import{join as l}from"path";import*as c from"@iarna/toml";import{logger as d}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{backupBeforeWrite as m,atomicWriteFile as p}from"./backup.js";import{codexHome as f,pickModel as _,toolHome as g,withV1 as x}from"./types.js";import{moveSessionsToProvider as h,NO_STATE_DB as y,runningCodexPids as k}from"../codex-sessions.js";import{codexFormat as b,codexRolloutFiles as v}from"../sessions/formats.js";import{getToolDefaultModel as w}from"../model-registry.js";import{configManager as j}from"../config.js";import{maskHome as C,track as O}from"../telemetry.js";import{buildCodexCatalog as S,catalogRejections as M,codexCatalogPath as A,fillFromOlderEntries as E,installedCodexBinaries as N,loadBundledCodexModels as P,loadPreviousCodexModels as T,previousGatewayModelIds as D}from"./codex-catalog.js";import{gatewayModelSpecs as I,modelFamily as L}from"../gateway-models.js";import{findInstalls as $,mirrorInstallCommand as J,brandInstallScripts as G}from"./tool-installs.js";import{chatgptDesktopApp as R}from"./chatgpt-desktop.js";const K=()=>f(),X=()=>l(K(),"config.toml"),B="sessions_move_declined",U=["browser_use","browser_use_external","computer_use"];function W(e){const t=e.features;if(!V(t)||!U.every(e=>!1===t[e]))return!1;for(const e of U)delete t[e];return!0}export function codexWorkModeOn(){try{const e=ee();return e.model_provider===F&&V(e.desktop)&&"STEPS_PROSE"===e.desktop.conversationDetailMode}catch{return!1}}function q(){try{if(V(ee().desktop))return!0}catch{}if("darwin"===process.platform)return["/Applications",l(g(),"Applications")].some(e=>["ChatGPT.app","Codex.app"].some(o=>t(l(e,o))));if("win32"===process.platform){const e=l(process.env.LOCALAPPDATA||l(g(),"AppData","Local"),"Packages");try{return n(e).some(e=>/^OpenAI\.(Codex|ChatGPT)/i.test(e))}catch{return!1}}return t("/usr/lib/chatgpt/ChatGPT")}export function codexConfigPath(){return X()}const z=()=>l(K(),"auth.json"),Y="OPENAI_API_KEY",F="custom",H=1e6,Q=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function V(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function Z(e,t){return V(e[t])||(e[t]={}),e[t]}function ee(){if(!t(X()))return{};const e=r(X(),"utf-8");if(!e.trim())return{};try{const t=c.parse(e);if(!V(t))throw new Error("config.toml root must be a TOML table");return t}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed; back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function te(){if(!t(z()))return{};try{const e=JSON.parse(r(z(),"utf-8"));return V(e)?e:{}}catch{return{}}}let oe=null;function ne(e){const t=e.profiles;if(V(t)){for(const[e,o]of Object.entries(t))V(o)&&2===Object.keys(o).length&&o.model_provider===F&&o.model===e&&delete t[e];0===Object.keys(t).length&&delete e.profiles}}let re=null;const ie=()=>l(j.getConfigDir(),"codex-catalog-check.json");function se(e){const o=A();t(o)&&r(o,"utf-8")===e||(m(o),p(o,e))}function ae(t,n){if(0===n.length)return[];const i=function(t,o){const n=e("sha256").update(t);for(const e of o)try{const t=s(e);n.update(`\0${e}\0${t.size}\0${t.mtimeMs}`)}catch{n.update(`\0${e}`)}return n.digest("hex")}(t,n);if(function(e){try{return JSON.parse(r(ie(),"utf-8")).signature===e}catch{return!1}}(i))return[];const l=M(A(),n);return 0===l.length&&function(e){try{o(j.getConfigDir(),{recursive:!0}),a(ie(),JSON.stringify({signature:e})+"\n")}catch(e){d.logError("codexAdapter.rememberAccepted",e)}}(i),l}function le(e,t,o){const n=!1!==t.settings?.one_million_context;!function(e){e.model_context_window===H&&delete e.model_context_window,e.model_auto_compact_token_limit===Q&&delete e.model_auto_compact_token_limit}(e);const r=()=>{e.model_catalog_json===A()&&delete e.model_catalog_json,n&&(e.model_context_window=H,e.model_auto_compact_token_limit=Q)},s=P()??T();if(!s)return d.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),re="skipped",void r();const a=t.catalog,l=e=>S({bundled:e,specs:I("codex",a?.clientModels),...a?{keyModels:a.models}:{},gatewayAvailable:a?.models??D(),oneMillion:n,selectedModel:o,..."gateway"===a?.oneMillionSource?{claudeOneMillion:a.oneMillion}:{}}),c=e=>JSON.stringify(e,null,2)+"\n";let u=l(s);se(c(u));const m=N(),p=ae(c(u),m);if(p.length>0){const e=Date.now();let t=!1;for(const e of p){const o=P(e.bin);if(o){for(const e of[{models:E(u.models,o)},{models:E(l(o).models,o)}])if(se(c(e)),0===ae(c(e),m).length){u=e,t=!0;break}if(t)break}}if(O("codex-catalog",t?"fallback":"fail",{duration_ms:Date.now()-e,error:p[0].error,detail:{rejected_by:p.map(e=>({version:e.version,path:C(e.bin)}))}}),!t)return re={rejectedBy:p[0]},i(A(),{force:!0}),void r()}re={listed:u.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))},e.model_catalog_json=A()}export function codexCatalogRejection(){let e;try{e=ee()}catch{return null}return e.model_catalog_json===A()&&t(A())?M(A())[0]??null:null}export function setCodexOneMillionContext(e,t){const o=ee(),n=c.stringify(o),r="string"==typeof o.model?o.model:w("codex");le(o,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:t},r);const i=c.stringify(o);i!==n&&(m(X()),p(X(),i))}const ce={command:"codex",npmPackage:"@openai/codex",native:{binDir:"win32"===process.platform?["AppData","Local","Programs","OpenAI","Codex","bin"]:[".local","bin"],updateArgs:["update"],updateTimeoutMs:12e4,dataDirs:[[".codex","packages","standalone"]]},mirrorScript:G("codex"),brew:"codex"};export const codexAdapter={name:"codex",companionApps:[R],displayName:"Codex",command:"codex",installCommand:J(ce),installSpec:ce,configPath:X(),sessions:{format:b,files:()=>[...v(l(f(),"sessions")),...v(l(f(),"archived_sessions"))],resumeCommand:e=>({command:"codex",args:["resume",e]})},wantsModelCatalog:!0,listedModels(){if(ee().model_catalog_json!==A())return null;try{return(JSON.parse(r(A(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){if(ee().model_catalog_json!==A())return;setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog);const t=ee(),o=function(e,t){const o=V(e.model_providers)?e.model_providers[F]:void 0;return!(!V(o)||o.base_url!==t||!0===o.supports_standalone_web_search||(o.supports_standalone_web_search=!0,0))}(t,x(e.baseUrl));(W(t)||o)&&(m(X()),p(X(),c.stringify(t)))},async followUp(){if(ee().model_provider!==F)return null;const e=await h(F,{dryRun:!0}),t=Math.max(e.moved.length,e.dbRows);if(0===t)return null;const o=j.getToolSettings("codex")[B];if("number"==typeof o&&t<=o)return null;const n={count:String(t)};return k().length>0?{hint:u.t("tools.codex.sessions_hidden_running",n)}:{prompt:u.t("tools.codex.sessions_offer",n),hint:u.t("tools.codex.sessions_hidden",n),async run(){const e=await h(F),t=[...new Set(e.moved.map(e=>e.from))].join("、")||"-",o=String(Math.max(e.moved.length,e.dbRows)),n=[{kind:"summary",text:u.t("codex_sessions.moved",{count:o,from:t,target:F})},{kind:"summary",text:u.t("codex_sessions.backup",{dir:e.backupDir??""})}];return e.dbSkipped&&e.dbSkipped!==y&&n.push({kind:"action",text:u.t("codex_sessions.db_skipped",{reason:e.dbSkipped})}),n},decline(){j.setToolSetting("codex",B,t)}}},applyNotes(){if("skipped"===re)return 0===$(ce).length?[]:[{kind:"action",text:u.t("tools.codex.catalog_skipped")}];if(!re)return[];if("rejectedBy"in re){const{bin:e,version:t}=re.rejectedBy;return[{kind:"action",text:u.t("tools.codex.catalog_rejected",{path:e,version:t??"?"})}]}const e=new Map;for(const t of re.listed)e.set(L(t.id),(e.get(L(t.id))??0)+1);const t=["gpt","claude","gemini","other","grok"].filter(t=>e.has(t)).map(t=>`${u.t(`tools.model_family.${t}`)} ${e.get(t)}`).join(" · ");return[{kind:"summary",text:u.t("tools.codex.catalog_written",{count:String(re.listed.length),breakdown:t})},{kind:"detail",text:re.listed.map(e=>e.name).join(", ")},...codexWorkModeOn()||q()?[{kind:"action",text:u.t(codexWorkModeOn()?"tools.codex.work_mode_on":"tools.codex.work_mode_hint")}]:[]]},loadPrivateConfig(e){const t=w("codex"),o=_(e.models,t),n=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(o)?t:o,r=x(e.baseUrl),i=ee();i.model=n,i.model_provider=F,i.model_reasoning_effort="high",le(i,e,n);const s=Z(i,"model_providers"),a=V(s[F])?{...s[F]}:{};delete a.env_key;const{experimental_bearer_token:l,...d}=a;s[F]={...d,name:"custom",wire_api:"responses",requires_openai_auth:!0,supports_standalone_web_search:!0,base_url:r},ne(i),Z(Z(i,"tui"),"model_availability_nux")[n]=4;const u=Z(i,"features");u.goals=!0,u.js_repl=!1,W(i),m(X()),p(X(),c.stringify(i)),function(e){const t=e.trim();if(!t)return;const o={...te(),auth_mode:"apikey",[Y]:t};m(z()),p(z(),JSON.stringify(o,null,2)+"\n",384)}(e.apiKey)},holdsOurConfig(e){const t=te()[Y];return"string"==typeof t&&e.includes(t.trim())},unloadPrivateConfig(e){if(oe=function(e){const t=te(),o="string"==typeof t[Y]?t[Y].trim():"";if(!o)return V(t.tokens)?"chatgpt":null;if(!e.includes(o))return"other_key";const{[Y]:n,auth_mode:r,...s}=t;return m(z()),V(s.tokens)?(p(z(),JSON.stringify({...s,auth_mode:"chatgpt"},null,2)+"\n",384),"chatgpt"):(i(z(),{force:!0}),"signed_out")}(e?.savedKeys??[]),!t(X()))return;let o;try{o=ee()}catch(e){return void d.logError("codexAdapter.unloadPrivateConfig",e)}const n=o.model_providers;V(n)&&(delete n[F],0===Object.keys(n).length&&delete o.model_providers),o.model_provider===F&&delete o.model_provider,o.model_catalog_json===A()&&delete o.model_catalog_json,W(o),ne(o),m(X()),p(X(),c.stringify(o))},unloadNotes(){switch(oe){case"chatgpt":return[{kind:"action",text:u.t("tools.codex.unload_chatgpt_restored")}];case"other_key":return[{kind:"action",text:u.t("tools.codex.unload_other_key")}];default:return[{kind:"action",text:u.t("tools.codex.unload_sign_in")}]}},detectCurrentConfig(){const e=te()[Y];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return ee()}catch(e){return d.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[X(),z(),A()]};export function codexAuthProblem(){try{const e=ee(),t=e.model_providers?.[F];if(e.model_provider!==F||!t)return null;if("string"==typeof t.experimental_bearer_token&&t.experimental_bearer_token)return"stale_bearer";const o=te();return!0===t.requires_openai_auth&&"chatgpt"===o.auth_mode?"chatgpt_login":null}catch{return null}}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Installs and upgrades a vendor's desktop app (Claude Desktop, ChatGPT) on macOS / Windows from the brand's
3
+ * download mirror: a CNB repo that re-publishes the vendor's own installers byte for byte every hour with a
4
+ * `SHA256SUMS`. The apps have their own updaters (Squirrel / Sparkle on macOS, App Installer / Store on
5
+ * Windows) that talk to vendor hosts often unreachable from mainland China; vibecli only steps in when the
6
+ * installed copy is older than the mirror's (or when asked to reinstall), and leaves those updaters on.
7
+ *
8
+ * Every download is checked twice before anything is replaced: its sha256 against the release's SHA256SUMS,
9
+ * then the vendor's code signature (macOS: `codesign --verify --deep --strict` of the copied bundle with the
10
+ * vendor's team id and bundle id, plus Gatekeeper `spctl -a`; Windows: Authenticode of the .msix, valid and
11
+ * signed by the vendor's publisher). A failed step leaves the installed copy as it was.
12
+ *
13
+ * - macOS: the .dmg is attached read-only, the app copied with `ditto` next to the target and swapped in with
14
+ * two renames (the old bundle stays until the new one is in place). A new install goes to /Applications, or
15
+ * ~/Applications when /Applications isn't writable for this user. Write access is checked before downloading.
16
+ * - Windows: `Add-AppxPackage` (per user, no administrator).
17
+ * - Not as root (sudo) on macOS, not as SYSTEM on Windows: the app would belong to the wrong account. Not over
18
+ * SSH on Windows: a network logon can't deploy MSIX packages.
19
+ */
20
+ export interface DesktopAppSpec {
21
+ /** Log label, e.g. `claude-desktop`. */
22
+ id: string;
23
+ /** The brand's download page `https://cnb.cool/<group>/<repo>/-/releases/latest`, undefined without a mirror. */
24
+ mirror(): string | undefined;
25
+ /** Release file for this machine, null where the vendor has none (Linux, Intel Mac for ChatGPT). */
26
+ asset(platform: NodeJS.Platform, arm64: boolean): string | null;
27
+ /** Release text file holding the version of `asset` (macOS and Windows builds may be numbered differently). */
28
+ versionFile(platform: NodeJS.Platform, arm64: boolean): string;
29
+ /**
30
+ * Release tag holding `version` (`v<version>` for Claude Desktop); absent when the tag follows another
31
+ * platform's numbering (ChatGPT's follows macOS), then files are read from `releases/latest/download`.
32
+ */
33
+ releaseTag?(version: string): string;
34
+ /** The bundle inside the .dmg, and the names an installed copy may have (first = name of a new install). */
35
+ macApp: {
36
+ source: string;
37
+ installedNames: string[];
38
+ bundleId: string;
39
+ teamId: string;
40
+ };
41
+ /** Authenticode subject of the .msix, and a `Where-Object` filter for the installed package. */
42
+ windows: {
43
+ signer: RegExp;
44
+ packageFilter: string;
45
+ };
46
+ /** Why this machine can't be served beyond platform / mirror (a key under `app_update.reason_*`), else null. */
47
+ extraBlocker?(platform: NodeJS.Platform): string | null;
48
+ }
49
+ type Runner = (command: string, args: string[], options?: {
50
+ timeout?: number;
51
+ input?: string;
52
+ }) => {
53
+ status: number | null;
54
+ stdout: string;
55
+ stderr: string;
56
+ };
57
+ /** Tests replace the process runner, the network, the platform, the CPU architecture and root / SYSTEM. */
58
+ export declare function setDesktopAppDepsForTests(deps: {
59
+ run?: Runner | null;
60
+ fetch?: typeof fetch | null;
61
+ platform?: NodeJS.Platform | null;
62
+ arm64?: boolean | null;
63
+ privileged?: boolean | null;
64
+ remote?: boolean | null;
65
+ }): void;
66
+ export declare function appPlatform(): NodeJS.Platform;
67
+ /** An ARM machine, also when this node runs under Rosetta / x64 emulation. */
68
+ export declare function isArm64Machine(): boolean;
69
+ export declare class DesktopInstallError extends Error {
70
+ constructor(message: string);
71
+ }
72
+ /** Localized failure; an empty detail (a tool that printed nothing) reads "reason unknown", never `{{reason}}`. */
73
+ export declare const fail: (key: string, vars?: Record<string, string>) => never;
74
+ /** `sha256sum` lines → file name → hex digest. */
75
+ export declare function parseSha256Sums(text: string): Map<string, string>;
76
+ /** `CFBundleIdentifier` of a bundle, null when unreadable. */
77
+ export declare function macBundleId(app: string): string | null;
78
+ export declare function macAppVersion(app: string): string | null;
79
+ /** First line of a PowerShell error worth showing (stderr is CLIXML when the output is redirected). */
80
+ export declare function powerShellError(text: string): string;
81
+ export interface DesktopInstallProgress {
82
+ downloading?(version: string): void;
83
+ percent?(percent: number): void;
84
+ verifying?(): void;
85
+ installing?(): void;
86
+ }
87
+ export interface DesktopAppInstaller {
88
+ mirrorRepo(): string | null;
89
+ assetName(): string | null;
90
+ blocker(): string | null;
91
+ latestVersion(timeoutMs?: number): Promise<string>;
92
+ installedMacApp(): string | null;
93
+ windowsPackageVersion(): string | null;
94
+ verifyMacApp(app: string): void;
95
+ verifyWindowsPackage(msix: string): void;
96
+ install(progress?: DesktopInstallProgress): Promise<{
97
+ version: string;
98
+ location: string;
99
+ }>;
100
+ }
101
+ export declare function createDesktopAppInstaller(spec: DesktopAppSpec): DesktopAppInstaller;
102
+ export {};
@@ -0,0 +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 w}from"path";import{Readable as h,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 y}from"../powershell-env.js";import{toolHome as E}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:y()});return n.error&&A.logError(`desktop-app: ${e}`,n.error),{status:n.status,stdout:`${n.stdout??""}`,stderr:`${n.stderr??""}`}};let _=v,I=(...t)=>fetch(...t),k=null,P=null,b=null,C=null;export function setDesktopAppDepsForTests(t){void 0!==t.run&&(_=t.run??v),void 0!==t.fetch&&(I=t.fetch??((...t)=>fetch(...t))),void 0!==t.platform&&(k=t.platform),void 0!==t.arm64&&(P=t.arm64),void 0!==t.privileged&&(b=t.privileged),void 0!==t.remote&&(C=t.remote)}export function appPlatform(){return k??process.platform}export function isArm64Machine(){return null!==P?P:"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 I(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(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 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 O=t=>`'${t.replace(/'/g,"''")}'`;function j(t,e=12e4){return _("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()),l=()=>{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"},d=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},y=()=>{for(const e of["/Applications",w(E(),"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},v=e=>{const r=_("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=_("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=_("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=y();let r=e;if(!r){const e=R("/Applications")?"/Applications":w(E(),"Applications");r=w(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}},P=e=>{const r=j(`$s = Get-AuthenticodeSignature -LiteralPath ${O(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()})}},M=()=>{const e=j(`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:l,latestVersion:d,installedMacApp:y,windowsPackageVersion:M,verifyMacApp:v,verifyWindowsPackage:P,install:async(y={})=>{const E=l();E&&fail("blocked",{reason:x.t(`app_update.reason_${E}`)});const b=r(),C=o();let R;try{R=await d(15e3)}catch(t){fail("download",{reason:t instanceof Error?t.message:String(t)})}const N=t.releaseTag?`${b}/-/releases/download/${t.releaseTag(R)}`:`${b}/-/releases/latest/download`;let F;try{F=parseSha256Sums(await T(`${N}/SHA256SUMS`,15e3)).get(C)}catch(e){A.logError(`${t.id}: SHA256SUMS`,e)}F||fail("no_checksum",{file:C}),"darwin"===appPlatform()&&k();const H=c(w(f(),`${t.id}-`));try{const r=w(H,C);y.downloading?.(R),await async function(t,e,r){let o;try{o=await I(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(h.fromWeb(o.body),c,s(e))}catch(t){u(e,{force:!0}),fail("download",{reason:t instanceof Error?t.message:String(t)})}}(`${N}/${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!==F&&fail("checksum",{file:C,expected:F.slice(0,12),actual:o.slice(0,12)}),y.installing?.(),"darwin"===appPlatform()?await(async(e,r)=>{const o=w(r,"mnt");i(o,{recursive:!0});const n=_("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}=k(),c=g(r),l=m(r);i(c,{recursive:!0}),s=w(c,`.${l}.${process.pid}.new`),u(s,{recursive:!0,force:!0});const d=_("ditto",[e,s],{timeout:9e5});0!==d.status&&fail("copy",{reason:(d.stderr||d.stdout).trim().split("\n")[0]??""}),v(s);const f=macAppVersion(s);f||fail("bad_package",{reason:"Info.plist"});const h=w(c,`.${l}.${process.pid}.old`);try{n&&p(n,h)}catch(t){throw["EPERM","EACCES"].includes(t.code??"")&&fail("app_management",{path:n}),t}try{p(s,r)}catch(t){throw n&&p(h,n),t}return s=null,u(h,{recursive:!0,force:!0}),{version:f,location:r}}finally{s&&u(s,{recursive:!0,force:!0}),_("hdiutil",["detach",o,"-force"],{timeout:6e4})}})(r,H):(t=>{P(t);const e=j(`$ErrorActionPreference = 'Stop'; Add-AppxPackage -Path ${O(t)} -ForceApplicationShutdown`,9e5);0!==e.status&&fail("install",{reason:powerShellError(e.stderr)||powerShellError(e.stdout)||`Add-AppxPackage ${e.status}`});const r=M();return r||fail("install",{reason:"Get-AppxPackage"}),{version:r,location:"MSIX"}})(r)}finally{u(H,{recursive:!0,force:!0})}}}}
@@ -19,6 +19,10 @@ export declare function setDesktopProcessRunnerForTests(fake: typeof run): void;
19
19
  export declare function isDesktopRunning(): boolean;
20
20
  /** Whether this platform supports `restartDesktop()`. */
21
21
  export declare function canRestartDesktop(): boolean;
22
+ /** Quit (gracefully, then forcibly); true when no instance is left. */
23
+ export declare function quitDesktop(): boolean;
24
+ /** Start the installed app; true when it is running afterwards. */
25
+ export declare function launchDesktop(): boolean;
22
26
  /** Quit (gracefully, then forcibly) and start again; true when it is running afterwards. */
23
27
  export declare function restartDesktop(): boolean;
24
28
  export {};
@@ -1 +1 @@
1
- import{spawnSync as e}from"child_process";import{logger as t}from"../../utils/logger.js";export const WINDOWS_PACKAGE_FAMILIES=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"];const r="Get-Process -Name claude -ErrorAction SilentlyContinue | Where-Object { $_.Path -and ($_.Path -like '*\\AnthropicClaude\\*' -or $_.Path -like '*\\Programs\\Claude\\*' -or $_.Path -like '*\\WindowsApps\\*Claude*') }";let o=(r,o,s=3e4)=>{const n=e(r,o,{encoding:"utf-8",timeout:s,windowsHide:!0,stdio:["ignore","pipe","pipe"]});return n.error&&t.logError(`desktop-process: ${r}`,n.error),{status:n.status,stdout:`${n.stdout??""}`}};export function setDesktopProcessRunnerForTests(e){o=e}function s(e,t){return o("powershell.exe",["-NoProfile","-NonInteractive","-Command",e],t)}function n(e){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}export function isDesktopRunning(){return"darwin"===process.platform?0===o("pgrep",["-x","Claude"]).status:"win32"===process.platform&&Number(s(`@(${r}).Count`).stdout.trim())>0}export function canRestartDesktop(){return"darwin"===process.platform||"win32"===process.platform}export function restartDesktop(){if("darwin"===process.platform){o("osascript",["-e",'quit app "Claude"']);for(let e=0;e<30&&isDesktopRunning();e++)n(500);isDesktopRunning()&&o("pkill",["-x","Claude"]),n(1e3),o("open",["-a","Claude"])}else{if("win32"!==process.platform)return!1;s([`$p = @(${r})`,"$p | ForEach-Object { [void]$_.CloseMainWindow() }","Start-Sleep -Seconds 3","$p | Where-Object { -not $_.HasExited } | Stop-Process -Force -ErrorAction SilentlyContinue","Start-Sleep -Seconds 1","$squirrel = Join-Path $env:LOCALAPPDATA 'AnthropicClaude\\claude.exe'","$programs = Join-Path $env:LOCALAPPDATA 'Programs\\Claude\\Claude.exe'","if (Test-Path $squirrel) { Start-Process $squirrel }","elseif (Test-Path $programs) { Start-Process $programs }",`else { $pkg = Get-AppxPackage | Where-Object { @(${WINDOWS_PACKAGE_FAMILIES.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | Select-Object -First 1`," if ($pkg) { $id = (Get-AppxPackageManifest $pkg).Package.Applications.Application.Id | Select-Object -First 1",' Start-Process "shell:AppsFolder\\$($pkg.PackageFamilyName)!$id" } }'].join("\n"),6e4)}for(let e=0;e<20;e++){if(isDesktopRunning())return!0;n(500)}return!1}
1
+ import{spawnSync as e}from"child_process";import{logger as t}from"../../utils/logger.js";import{powerShellEnv as r}from"../powershell-env.js";export const WINDOWS_PACKAGE_FAMILIES=["Claude_pzs8sxrjxfjjc","AnthropicPBC.Claude_fnn82j28hfe8t"];const o="Get-Process -Name claude -ErrorAction SilentlyContinue | Where-Object { $_.Path -and ($_.Path -like '*\\AnthropicClaude\\*' -or $_.Path -like '*\\Programs\\Claude\\*' -or $_.Path -like '*\\WindowsApps\\*Claude*') }";let s=(o,s,n=3e4)=>{const i=e(o,s,{encoding:"utf-8",timeout:n,windowsHide:!0,stdio:["ignore","pipe","pipe"],env:r()});return i.error&&t.logError(`desktop-process: ${o}`,i.error),{status:i.status,stdout:`${i.stdout??""}`}};export function setDesktopProcessRunnerForTests(e){s=e}function n(e,t){return s("powershell.exe",["-NoProfile","-NonInteractive","-Command",e],t)}function i(e){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}export function isDesktopRunning(){return"darwin"===process.platform?0===s("pgrep",["-x","Claude"]).status:"win32"===process.platform&&Number(n(`@(${o}).Count`).stdout.trim())>0}export function canRestartDesktop(){return"darwin"===process.platform||"win32"===process.platform}export function quitDesktop(){if("darwin"===process.platform){s("osascript",["-e",'quit app "Claude"']);for(let e=0;e<30&&isDesktopRunning();e++)i(500);isDesktopRunning()&&s("pkill",["-x","Claude"]),i(1e3)}else{if("win32"!==process.platform)return!1;n([`$p = @(${o})`,"$p | ForEach-Object { [void]$_.CloseMainWindow() }","Start-Sleep -Seconds 3","$p | Where-Object { -not $_.HasExited } | Stop-Process -Force -ErrorAction SilentlyContinue","Start-Sleep -Seconds 1"].join("\n"),6e4)}return!isDesktopRunning()}export function launchDesktop(){if("darwin"===process.platform)s("open",["-a","Claude"]);else{if("win32"!==process.platform)return!1;n(["$squirrel = Join-Path $env:LOCALAPPDATA 'AnthropicClaude\\claude.exe'","$programs = Join-Path $env:LOCALAPPDATA 'Programs\\Claude\\Claude.exe'","if (Test-Path $squirrel) { Start-Process $squirrel }","elseif (Test-Path $programs) { Start-Process $programs }",`else { $pkg = Get-AppxPackage | Where-Object { @(${WINDOWS_PACKAGE_FAMILIES.map(e=>`'${e}'`).join(",")}) -contains $_.PackageFamilyName } | Select-Object -First 1`," if ($pkg) { $id = (Get-AppxPackageManifest $pkg).Package.Applications.Application.Id | Select-Object -First 1",' Start-Process "shell:AppsFolder\\$($pkg.PackageFamilyName)!$id" } }'].join("\n"),6e4)}for(let e=0;e<20;e++){if(isDesktopRunning())return!0;i(500)}return!1}export function restartDesktop(){return!!canRestartDesktop()&&(quitDesktop(),launchDesktop())}
@@ -1 +1 @@
1
- import{spawnSync as r}from"child_process";import{mkdtempSync as e,rmSync as t,writeFileSync as o}from"fs";import{tmpdir as n}from"os";import{join as i}from"path";import{logger as s}from"../../utils/logger.js";import{CLI_NAME as u}from"../brand.js";function f(r){return`"${r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}export function buildRegFile(r,e,t,o=!1){const n=`HKEY_USERS\\${r}\\${e}`;return o?`Windows Registry Editor Version 5.00\r\n\r\n[-${n}]\r\n`:`Windows Registry Editor Version 5.00\r\n\r\n[${n}]\r\n${Object.entries(t).map(([r,e])=>`${f(r)}=${null===e?"-":function(r){return"boolean"==typeof r?"dword:"+(r?"00000001":"00000000"):"number"==typeof r?`dword:${(r>>>0).toString(16).padStart(8,"0")}`:f("string"==typeof r?r:JSON.stringify(r))}(e)}`).join("\r\n")}\r\n`}let d=()=>{const e=r("whoami",["/user","/fo","csv","/nh"],{encoding:"utf-8",windowsHide:!0});return/"(S-1-[\d-]+)"/.exec(`${e.stdout??""}`)?.[1]??null},l=e=>{const t=r("powershell.exe",["-NoProfile","-NonInteractive","-Command",`$p = Start-Process -FilePath 'reg.exe' -ArgumentList @('import', '"${e}"') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`],{encoding:"utf-8",windowsHide:!0,timeout:3e5});return 0!==t.status&&s.logError("elevatedImport",new Error(`${t.stderr??""}`.trim()||`exit ${t.status}`)),0===t.status};export function setElevationForTests(r,e){d=r,l=e}export function setUserRegistryElevated(r,s,f=!1){const c=d();if(!c)return!1;const m=e(i(n(),`${u}-reg-`)),a=i(m,`${u}.reg`);try{return o(a,Buffer.concat([Buffer.from([255,254]),Buffer.from(buildRegFile(c,r,s,f),"utf16le")])),l(a)}finally{t(m,{recursive:!0,force:!0})}}
1
+ import{spawnSync as r}from"child_process";import{mkdtempSync as e,rmSync as t,writeFileSync as o}from"fs";import{tmpdir as n}from"os";import{join as i}from"path";import{logger as s}from"../../utils/logger.js";import{CLI_NAME as u}from"../brand.js";import{powerShellEnv as f}from"../powershell-env.js";function d(r){return`"${r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}export function buildRegFile(r,e,t,o=!1){const n=`HKEY_USERS\\${r}\\${e}`;return o?`Windows Registry Editor Version 5.00\r\n\r\n[-${n}]\r\n`:`Windows Registry Editor Version 5.00\r\n\r\n[${n}]\r\n${Object.entries(t).map(([r,e])=>`${d(r)}=${null===e?"-":function(r){return"boolean"==typeof r?"dword:"+(r?"00000001":"00000000"):"number"==typeof r?`dword:${(r>>>0).toString(16).padStart(8,"0")}`:d("string"==typeof r?r:JSON.stringify(r))}(e)}`).join("\r\n")}\r\n`}let l=()=>{const e=r("whoami",["/user","/fo","csv","/nh"],{encoding:"utf-8",windowsHide:!0});return/"(S-1-[\d-]+)"/.exec(`${e.stdout??""}`)?.[1]??null},m=e=>{const t=r("powershell.exe",["-NoProfile","-NonInteractive","-Command",`$p = Start-Process -FilePath 'reg.exe' -ArgumentList @('import', '"${e}"') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`],{encoding:"utf-8",windowsHide:!0,timeout:3e5,env:f()});return 0!==t.status&&s.logError("elevatedImport",new Error(`${t.stderr??""}`.trim()||`exit ${t.status}`)),0===t.status};export function setElevationForTests(r,e){l=r,m=e}export function setUserRegistryElevated(r,s,f=!1){const d=l();if(!d)return!1;const c=e(i(n(),`${u}-reg-`)),p=i(c,`${u}.reg`);try{return o(p,Buffer.concat([Buffer.from([255,254]),Buffer.from(buildRegFile(d,r,s,f),"utf16le")])),m(p)}finally{t(c,{recursive:!0,force:!0})}}
@@ -70,6 +70,50 @@ export interface ToolSessionStore {
70
70
  args: string[];
71
71
  };
72
72
  }
73
+ /**
74
+ * A GUI app vibecli installs and upgrades itself from the brand's download mirror (Claude Desktop), next to the
75
+ * app's own updater: only when the installed copy is older than the mirror's. Driven by `lib/app-update.ts`.
76
+ */
77
+ export interface AppInstaller {
78
+ /** Whose signature is checked (`Anthropic`, `OpenAI`), for progress lines. */
79
+ vendor: string;
80
+ /** About how big the download is here, in MB. */
81
+ sizeMb(): number;
82
+ /** The vendor's download page, named where vibecli can't install the app. */
83
+ downloadPage: string;
84
+ /** Why this machine can't be served (a locale key under `app_update.reason_*`), null when it can. */
85
+ blocker(): string | null;
86
+ /** The mirror's newest version; throws when it can't be read. */
87
+ latestVersion(): Promise<string>;
88
+ /** Path of the installed app, null when there is none (a user-data dir alone doesn't count). */
89
+ installedPath(): string | null;
90
+ /** Download, verify and install the newest build while the app is not running; throws a localized error, leaving the old copy. */
91
+ install(progress: {
92
+ downloading(version: string): void;
93
+ percent(percent: number): void;
94
+ verifying(): void;
95
+ installing(): void;
96
+ }): Promise<{
97
+ version: string;
98
+ location: string;
99
+ }>;
100
+ }
101
+ /**
102
+ * A desktop app that uses another tool's config and that vibecli installs / upgrades alongside it (ChatGPT, which
103
+ * runs Codex on `~/.codex`, for the `codex` adapter). Same handling as an adapter's own `appInstaller`.
104
+ */
105
+ export interface CompanionApp {
106
+ /** Id on the command line (`vibecli upgrade chatgpt`). */
107
+ name: string;
108
+ displayName: string;
109
+ appInstaller: AppInstaller;
110
+ installedVersion(): string | null;
111
+ runningApp?: {
112
+ isRunning(): boolean;
113
+ quit?(): boolean;
114
+ launch?(): boolean;
115
+ };
116
+ }
73
117
  export interface ToolAdapter {
74
118
  /** Stable id used on the CLI and as the config key (e.g. `codex`). */
75
119
  name: string;
@@ -121,7 +165,13 @@ export interface ToolAdapter {
121
165
  runningApp?: {
122
166
  isRunning(): boolean;
123
167
  restart(): boolean;
168
+ quit?(): boolean;
169
+ launch?(): boolean;
124
170
  };
171
+ /** The app itself can be installed and upgraded by vibecli; see {@link AppInstaller}. */
172
+ appInstaller?: AppInstaller;
173
+ /** Desktop apps that run on this tool's config; installed by setup and upgraded with it. */
174
+ companionApps?: CompanionApp[];
125
175
  /** Extra lines shown under the "too old" note (e.g. a release channel that hasn't reached it yet). */
126
176
  upgradeHints?(min: string): string[];
127
177
  /**
@@ -1 +1 @@
1
- import{execFileSync as e,spawn as r}from"child_process";import{logger as n}from"../utils/logger.js";import{brandEnvName as t}from"./brand.js";const o={user:"HKCU\\Environment",machine:"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"},s=r=>e("reg.exe",r,{encoding:"utf-8",windowsHide:!0,timeout:15e3,stdio:["ignore","pipe","pipe"]}),i=()=>{const e=r("powershell.exe",["-NoProfile","-NonInteractive","-Command",`[Environment]::SetEnvironmentVariable('${t("ENV_REFRESH")}',$null,'User')`],{detached:!0,stdio:"ignore",windowsHide:!0});e.on("error",e=>n.logError("windowsEnv.broadcast",e)),e.unref()};let a=s,d=i;export function setWindowsEnvRunnersForTests(e,r){a=e??s,d=e?r??(()=>{}):i}export function readWindowsEnv(e,r){let n;try{n=a(["query",o[e],"/v",r])}catch{return}const t=n.match(new RegExp(String.raw`^\s*${r}\s+REG_(?:EXPAND_)?SZ\s+(.*?)\r?$`,"im"));return t?t[1]:void 0}export function addUserPathDir(e){let r="REG_EXPAND_SZ",n="";try{const e=a(["query",o.user,"/v","Path"]).match(/^\s*Path\s+(REG_(?:EXPAND_)?SZ)\s+(.*?)\r?$/im);e&&([,r,n]=e)}catch{}const t=e=>(e=>e.replace(/%([^%]+)%/g,(e,r)=>process.env[r]??e))(e).replace(/[\\/]+$/,"").toLowerCase();return!n.split(";").some(r=>r&&t(r)===t(e))&&(a(["add",o.user,"/v","Path","/t",r,"/d",n?`${n.replace(/;+$/,"")};${e}`:e,"/f"]),!0)}export function setUserEnv(e,r){a(["add",o.user,"/v",e,"/t","REG_SZ","/d",r,"/f"])}export function deleteWindowsEnv(e,r){a(["delete",o[e],"/v",r,"/f"])}export function broadcastEnvChange(){d()}
1
+ import{execFileSync as e,spawn as r}from"child_process";import{logger as n}from"../utils/logger.js";import{brandEnvName as t}from"./brand.js";import{powerShellEnv as o}from"./powershell-env.js";const s={user:"HKCU\\Environment",machine:"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"},i=r=>e("reg.exe",r,{encoding:"utf-8",windowsHide:!0,timeout:15e3,stdio:["ignore","pipe","pipe"]}),a=()=>{const e=r("powershell.exe",["-NoProfile","-NonInteractive","-Command",`[Environment]::SetEnvironmentVariable('${t("ENV_REFRESH")}',$null,'User')`],{detached:!0,stdio:"ignore",windowsHide:!0,env:o()});e.on("error",e=>n.logError("windowsEnv.broadcast",e)),e.unref()};let d=i,c=a;export function setWindowsEnvRunnersForTests(e,r){d=e??i,c=e?r??(()=>{}):a}export function readWindowsEnv(e,r){let n;try{n=d(["query",s[e],"/v",r])}catch{return}const t=n.match(new RegExp(String.raw`^\s*${r}\s+REG_(?:EXPAND_)?SZ\s+(.*?)\r?$`,"im"));return t?t[1]:void 0}export function addUserPathDir(e){let r="REG_EXPAND_SZ",n="";try{const e=d(["query",s.user,"/v","Path"]).match(/^\s*Path\s+(REG_(?:EXPAND_)?SZ)\s+(.*?)\r?$/im);e&&([,r,n]=e)}catch{}const t=e=>(e=>e.replace(/%([^%]+)%/g,(e,r)=>process.env[r]??e))(e).replace(/[\\/]+$/,"").toLowerCase();return!n.split(";").some(r=>r&&t(r)===t(e))&&(d(["add",s.user,"/v","Path","/t",r,"/d",n?`${n.replace(/;+$/,"")};${e}`:e,"/f"]),!0)}export function setUserEnv(e,r){d(["add",s.user,"/v",e,"/t","REG_SZ","/d",r,"/f"])}export function deleteWindowsEnv(e,r){d(["delete",s[e],"/v",r,"/f"])}export function broadcastEnvChange(){c()}