agentroam-runtime-darwin-arm64 0.2.0-preview.56 → 0.2.0-preview.57

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 (116) hide show
  1. package/manifest.json +2 -2
  2. package/package.json +1 -1
  3. package/runtime/.next/BUILD_ID +1 -1
  4. package/runtime/.next/app-path-routes-manifest.json +1 -1
  5. package/runtime/.next/build-manifest.json +2 -2
  6. package/runtime/.next/prerender-manifest.json +1 -1
  7. package/runtime/.next/server/app/_not-found.html +1 -1
  8. package/runtime/.next/server/app/_not-found.rsc +1 -1
  9. package/runtime/.next/server/app/api/push/vapid.body +1 -1
  10. package/runtime/.next/server/app-paths-manifest.json +27 -27
  11. package/runtime/.next/server/pages/404.html +1 -1
  12. package/runtime/.next/server/pages/500.html +1 -1
  13. package/runtime/.next/server/server-reference-manifest.json +1 -1
  14. package/runtime/lib/remote-control/component-version.mjs +13 -0
  15. package/runtime/lib/remote-control/macos-helper-app.mjs +86 -35
  16. package/runtime/lib/remote-control/remote-authorization.mjs +1 -1
  17. package/runtime/lib/remote-control/windows-helper-app.mjs +201 -0
  18. package/runtime/lib/remote-control/windows-helper.mjs +12 -12
  19. package/runtime/native/Support.noindex/AgentRoam Remote Desktop.app/Contents/MacOS/agentroam-remote-desktop +0 -0
  20. package/runtime/native/Support.noindex/AgentRoam Remote Desktop.app/Contents/Resources/agentroam-component.json +1 -1
  21. package/runtime/native/Support.noindex/AgentRoam Remote Desktop.app/Contents/_CodeSignature/CodeResources +2 -2
  22. package/runtime/node_modules/.package-lock.json +117 -117
  23. package/runtime/node_modules/asn1js/README.md +50 -32
  24. package/runtime/node_modules/asn1js/build/index.es.js +860 -353
  25. package/runtime/node_modules/asn1js/build/index.js +860 -353
  26. package/runtime/node_modules/asn1js/package.json +20 -17
  27. package/runtime/package-lock.json +134 -134
  28. package/runtime/webapp/dist/assets/{AiHubPane-Cu92elLn.js → AiHubPane-ChqCR_y0.js} +1 -1
  29. package/runtime/webapp/dist/assets/{App-CIoUFqUR.js → App-KFs0NxCF.js} +3 -3
  30. package/runtime/webapp/dist/assets/{BrowserLivePanel-BsthwY1a.js → BrowserLivePanel-CsScRv23.js} +1 -1
  31. package/runtime/webapp/dist/assets/{CollaborationDashboard-DG7Y0h1s.js → CollaborationDashboard-HF8ltJSE.js} +1 -1
  32. package/runtime/webapp/dist/assets/{CollaborationPage-DA902wqi.js → CollaborationPage-BgLQAQjm.js} +1 -1
  33. package/runtime/webapp/dist/assets/{FilePreview-CGjiY6Oh.js → FilePreview-DiHfafCP.js} +3 -3
  34. package/runtime/webapp/dist/assets/{FileTree-BC45MKRL.js → FileTree-PvPJgMg5.js} +1 -1
  35. package/runtime/webapp/dist/assets/{FlowStudioPane-9TrQcObf.js → FlowStudioPane-C5GM9JC-.js} +1 -1
  36. package/runtime/webapp/dist/assets/{RemoteDesktopPage-DMnDxASQ.js → RemoteDesktopPage-BDpFrmKy.js} +1 -1
  37. package/runtime/webapp/dist/assets/{TerminalPane-Ds9JxVMt.js → TerminalPane-D-Yj-chg.js} +1 -1
  38. package/runtime/webapp/dist/assets/{abnfDiagram-VCTEODGH-2YNJ76WG.js → abnfDiagram-VCTEODGH-Cw_sGqxb.js} +1 -1
  39. package/runtime/webapp/dist/assets/{arc-CcKuP0ye.js → arc-CCCMJyI8.js} +1 -1
  40. package/runtime/webapp/dist/assets/{architectureDiagram-5GKGNRK7-Bw49xhZn.js → architectureDiagram-5GKGNRK7-DlkkDiCM.js} +1 -1
  41. package/runtime/webapp/dist/assets/{artifact-links-OG5Ksgyd.js → artifact-links-BHjZ3nlR.js} +1 -1
  42. package/runtime/webapp/dist/assets/{blockDiagram-I7D4REHJ-Dqm_qfY7.js → blockDiagram-I7D4REHJ-Ci_dfCdR.js} +1 -1
  43. package/runtime/webapp/dist/assets/{c4Diagram-7LVT6UL2-CASULDQ9.js → c4Diagram-7LVT6UL2-DI10DGne.js} +1 -1
  44. package/runtime/webapp/dist/assets/channel-NIixBBfi.js +1 -0
  45. package/runtime/webapp/dist/assets/{chunk-2Q5K7J3B-yEnUXBZC.js → chunk-2Q5K7J3B-CT0BORhC.js} +1 -1
  46. package/runtime/webapp/dist/assets/{chunk-5VM5RSS4-Djs5IDQf.js → chunk-5VM5RSS4-hg-DY-5r.js} +1 -1
  47. package/runtime/webapp/dist/assets/{chunk-F27PBJKO-CdLh9ewF.js → chunk-F27PBJKO-DngPgytW.js} +1 -1
  48. package/runtime/webapp/dist/assets/{chunk-IMKFNOWR-I-pRrlxR.js → chunk-IMKFNOWR-5CWl6SEz.js} +1 -1
  49. package/runtime/webapp/dist/assets/{chunk-JWPE2WC7-BD2Gv69T.js → chunk-JWPE2WC7-D3j6hMZe.js} +1 -1
  50. package/runtime/webapp/dist/assets/{chunk-POPQ4Y6H-BhuerIJs.js → chunk-POPQ4Y6H-C8GSJtEY.js} +1 -1
  51. package/runtime/webapp/dist/assets/{chunk-SVP7TREG-AWEtUvjM.js → chunk-SVP7TREG-C5HQHSea.js} +1 -1
  52. package/runtime/webapp/dist/assets/{chunk-TICWLB2K-BiO_dQcW.js → chunk-TICWLB2K-B2AwbLWh.js} +1 -1
  53. package/runtime/webapp/dist/assets/{chunk-XXDRQBXY-zkjO5GCv.js → chunk-XXDRQBXY-_j-3Zf7f.js} +1 -1
  54. package/runtime/webapp/dist/assets/classDiagram-ZZMXUADV-CGa6hDyt.js +1 -0
  55. package/runtime/webapp/dist/assets/classDiagram-v2-VYDZK3BY-CGa6hDyt.js +1 -0
  56. package/runtime/webapp/dist/assets/{clipboard-CvdeFLww.js → clipboard-Cwf2Psxb.js} +1 -1
  57. package/runtime/webapp/dist/assets/{codeSyntax-ByG7L1fi.js → codeSyntax-Dnkv_4aa.js} +1 -1
  58. package/runtime/webapp/dist/assets/{collaboration-Be_9WEiY.js → collaboration-DHDzNCPO.js} +1 -1
  59. package/runtime/webapp/dist/assets/{cose-bilkent-JH36ORCC-CAxuJw-v.js → cose-bilkent-JH36ORCC-DTE5n0M0.js} +1 -1
  60. package/runtime/webapp/dist/assets/{cynefin-OW5HDTMX-hLWhgC57.js → cynefin-OW5HDTMX-mEPaurNe.js} +1 -1
  61. package/runtime/webapp/dist/assets/{cynefinDiagram-5FMLGOSQ-uelhySIx.js → cynefinDiagram-5FMLGOSQ-Ci281Jxk.js} +1 -1
  62. package/runtime/webapp/dist/assets/{dagre-GXQ25YYZ-DPO7KxIX.js → dagre-GXQ25YYZ-DTnnlrAX.js} +1 -1
  63. package/runtime/webapp/dist/assets/{diagram-S7CK7UJ4-B8jQa5Lm.js → diagram-S7CK7UJ4-2UuWvA7i.js} +1 -1
  64. package/runtime/webapp/dist/assets/{diagram-UQ7AKVKN-DdymRFf0.js → diagram-UQ7AKVKN-DaWqSqMe.js} +1 -1
  65. package/runtime/webapp/dist/assets/{diagram-VSXAHHWV-BHAv1Uzp.js → diagram-VSXAHHWV-DSZtnJiB.js} +1 -1
  66. package/runtime/webapp/dist/assets/{diagram-VX7I27RA-DlRev6E7.js → diagram-VX7I27RA-3T-5Agty.js} +1 -1
  67. package/runtime/webapp/dist/assets/{diagram-Z3DM3KII-DspqqK0b.js → diagram-Z3DM3KII-xmDPknxw.js} +1 -1
  68. package/runtime/webapp/dist/assets/{ebnfDiagram-PWID7BFC-BwStWg-O.js → ebnfDiagram-PWID7BFC-JQEYWOvZ.js} +1 -1
  69. package/runtime/webapp/dist/assets/{erDiagram-RLTQ6QDP-jUH66GeO.js → erDiagram-RLTQ6QDP-B6narI72.js} +1 -1
  70. package/runtime/webapp/dist/assets/{file-types-Bbr1oDan.js → file-types-Cs4tHNVf.js} +1 -1
  71. package/runtime/webapp/dist/assets/{flowDiagram-HODETNUW-b2JhyYZh.js → flowDiagram-HODETNUW-CK7DK3Gk.js} +1 -1
  72. package/runtime/webapp/dist/assets/{ganttDiagram-EL5Y4UJY-k6xQA8nM.js → ganttDiagram-EL5Y4UJY-BcDmlNwh.js} +1 -1
  73. package/runtime/webapp/dist/assets/{gitGraphDiagram-WWUBYQGX-Cg8O3XcX.js → gitGraphDiagram-WWUBYQGX-RdFAuhWE.js} +1 -1
  74. package/runtime/webapp/dist/assets/{index-CQepZy15.js → index-CFGPpdBY.js} +4 -4
  75. package/runtime/webapp/dist/assets/{infoDiagram-27XIBGKW-BEZuUiUV.js → infoDiagram-27XIBGKW-ElVqnYMm.js} +1 -1
  76. package/runtime/webapp/dist/assets/{ishikawaDiagram-5VMMS53U-D8cPBWA9.js → ishikawaDiagram-5VMMS53U-CTeLVA0R.js} +1 -1
  77. package/runtime/webapp/dist/assets/{journeyDiagram-3NMN7TZE-BiCIZoXl.js → journeyDiagram-3NMN7TZE-ByxrFxTv.js} +1 -1
  78. package/runtime/webapp/dist/assets/{kanban-definition-UXKFOSKX-DBGsdTe3.js → kanban-definition-UXKFOSKX-BqiTQeMN.js} +1 -1
  79. package/runtime/webapp/dist/assets/{layout-CESwBcoQ.js → layout-BZCWCzvh.js} +1 -1
  80. package/runtime/webapp/dist/assets/{linear-meicA11o.js → linear-DXrM6MzH.js} +1 -1
  81. package/runtime/webapp/dist/assets/{loader-circle-gCGomGeo.js → loader-circle-CRhbj_Kf.js} +1 -1
  82. package/runtime/webapp/dist/assets/{mermaid.core-BsSrKkY-.js → mermaid.core-DTDkmQ6m.js} +5 -5
  83. package/runtime/webapp/dist/assets/{mindmap-definition-YA3MSWOX-Ci76EHnj.js → mindmap-definition-YA3MSWOX-DqCPnFQf.js} +1 -1
  84. package/runtime/webapp/dist/assets/{page-CaBcebHM.js → page-qprcLw0l.js} +3 -3
  85. package/runtime/webapp/dist/assets/{pegDiagram-XKGWAZYB-BJ9fzsRy.js → pegDiagram-XKGWAZYB-D63yrQuq.js} +1 -1
  86. package/runtime/webapp/dist/assets/{pieDiagram-E7YTZNPT-Dr_g_s7V.js → pieDiagram-E7YTZNPT-DCzTnhwu.js} +1 -1
  87. package/runtime/webapp/dist/assets/{quadrantDiagram-AXDQQJYC-DRUIkYzL.js → quadrantDiagram-AXDQQJYC-B3Bb7MB-.js} +1 -1
  88. package/runtime/webapp/dist/assets/{railroadDiagram-O6MQD6OU-z_f3jTzL.js → railroadDiagram-O6MQD6OU-Cknh6Ei1.js} +1 -1
  89. package/runtime/webapp/dist/assets/{requirementDiagram-BXWQKSXE-BZ9192zw.js → requirementDiagram-BXWQKSXE-BYtbQioD.js} +1 -1
  90. package/runtime/webapp/dist/assets/{rotate-ccw-CU6Ofovh.js → rotate-ccw-B-nRINvb.js} +1 -1
  91. package/runtime/webapp/dist/assets/{rotate-cw-CI-k2c-H.js → rotate-cw-DVGqAFQz.js} +1 -1
  92. package/runtime/webapp/dist/assets/{sankeyDiagram-P5KCCOFB-D_bYtKCw.js → sankeyDiagram-P5KCCOFB-k_Vb3RIX.js} +1 -1
  93. package/runtime/webapp/dist/assets/{send-B_L3JVTV.js → send-C4oDLFFm.js} +1 -1
  94. package/runtime/webapp/dist/assets/{sequenceDiagram-WJ2MYXX4-BRPkyVwo.js → sequenceDiagram-WJ2MYXX4-Bc9Pcv0e.js} +1 -1
  95. package/runtime/webapp/dist/assets/{sizeCapture-INFHLROL-D0IXlEUO.js → sizeCapture-INFHLROL-0YYAh6Pi.js} +1 -1
  96. package/runtime/webapp/dist/assets/{smartphone-BaM-cilR.js → smartphone-0wJ6lsYD.js} +1 -1
  97. package/runtime/webapp/dist/assets/{stateDiagram-D77RDMKH-DOPcJmKu.js → stateDiagram-D77RDMKH-CIqzUhPM.js} +1 -1
  98. package/runtime/webapp/dist/assets/stateDiagram-v2-MP3YSRHH-DLwWK8OY.js +1 -0
  99. package/runtime/webapp/dist/assets/{swimlanes-42K2YHIH-DkWPxpZR.js → swimlanes-42K2YHIH-D1NpIz9q.js} +1 -1
  100. package/runtime/webapp/dist/assets/swimlanesDiagram-VR7AAH4N-C4Bc5Vem.js +8 -0
  101. package/runtime/webapp/dist/assets/{timeline-definition-24CTP7MA-DSSrwQZc.js → timeline-definition-24CTP7MA-Dxw_7egm.js} +1 -1
  102. package/runtime/webapp/dist/assets/{trash-2-CCZMuVfC.js → trash-2-CVH0gGlm.js} +1 -1
  103. package/runtime/webapp/dist/assets/{vennDiagram-4TSXK5OY-QRwqQ2e4.js → vennDiagram-4TSXK5OY-DhGn0ZVr.js} +1 -1
  104. package/runtime/webapp/dist/assets/{wardleyDiagram-VM6X3IG4-BOSS_TSY.js → wardleyDiagram-VM6X3IG4-CZmbL_Wo.js} +1 -1
  105. package/runtime/webapp/dist/assets/{web-DX0wjWUS.js → web-BK27Wi1i.js} +1 -1
  106. package/runtime/webapp/dist/assets/{web-ebzGWnSw.js → web-BPW_Ymbe.js} +1 -1
  107. package/runtime/webapp/dist/assets/{x-CbdLQm7m.js → x-Pd8Ro-NN.js} +1 -1
  108. package/runtime/webapp/dist/assets/{xychartDiagram-S5SC5T6Z-D9Rp3n6B.js → xychartDiagram-S5SC5T6Z-Cd1HAnc7.js} +1 -1
  109. package/runtime/webapp/dist/index.html +1 -1
  110. package/runtime/webapp/dist/assets/channel-xKavBx6z.js +0 -1
  111. package/runtime/webapp/dist/assets/classDiagram-ZZMXUADV-B6zj-gda.js +0 -1
  112. package/runtime/webapp/dist/assets/classDiagram-v2-VYDZK3BY-B6zj-gda.js +0 -1
  113. package/runtime/webapp/dist/assets/stateDiagram-v2-MP3YSRHH-CosKh0dU.js +0 -1
  114. package/runtime/webapp/dist/assets/swimlanesDiagram-VR7AAH4N-eLjpgpHz.js +0 -8
  115. /package/runtime/.next/static/{1ar2wXugWliv3TmyVCQiY → 3znfZrYYbguBkCBygHPcf}/_buildManifest.js +0 -0
  116. /package/runtime/.next/static/{1ar2wXugWliv3TmyVCQiY → 3znfZrYYbguBkCBygHPcf}/_ssgManifest.js +0 -0
@@ -103,7 +103,7 @@ export class RemoteAuthorization {
103
103
  }
104
104
  async status(local = true) {
105
105
  const unlock = this.platform === 'win32' && this.system ? await this.system.probe().then(p => p.available ? 'available' : 'missing').catch(() => 'missing') : 'unsupported';
106
- const component = local && this.platform === 'darwin' ? await this.helper.describe?.() : undefined;
106
+ const component = local && ['darwin', 'win32'].includes(this.platform) ? await this.helper.describe?.() : undefined;
107
107
  return { local, platform: this.platform, supported: this.supported, installed: this.supported && await this.helper.available(), enabled: this.enabled, screen: this.screen, accessibility: this.accessibility, online: this.online, error: this.error, locked: this.locked, unlock, viewerCount: this.viewerCount, controlState: this.controlState, ...(component ? { component } : {}) };
108
108
  }
109
109
  /** Lock / wake / unlock entry point for paired remote viewers (`browser:system`). */
@@ -0,0 +1,201 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync } from 'node:fs';
4
+ import { access, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
5
+ import { createServer } from 'node:net';
6
+ import { homedir } from 'node:os';
7
+ import { dirname, join, win32 } from 'node:path';
8
+ import { setTimeout as delay } from 'node:timers/promises';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { promisify } from 'node:util';
11
+ import { componentVersionPattern, compareComponentVersions } from './component-version.mjs';
12
+
13
+ const run = promisify(execFile);
14
+ export const WINDOWS_HELPER_EXECUTABLE = 'agentroam-remote-desktop.exe';
15
+ export const WINDOWS_HELPER_METADATA = 'agentroam-remote-component.json';
16
+ const payloadFiles = [WINDOWS_HELPER_EXECUTABLE, 'remote-helper-NOTICES.txt'];
17
+ const component = 'agentroam-remote-desktop';
18
+ const hash = bytes => createHash('sha256').update(bytes).digest('hex');
19
+ const canonical = path => win32.normalize(path).toLowerCase();
20
+
21
+ export function bundledWindowsHelperPath() {
22
+ const native = fileURLToPath(new URL('../../native/', import.meta.url));
23
+ const bundled = join(native, WINDOWS_HELPER_EXECUTABLE), development = join(native, '.build-remote/windows', WINDOWS_HELPER_EXECUTABLE);
24
+ return existsSync(bundled) || !existsSync(development) ? bundled : development;
25
+ }
26
+
27
+ export function windowsHelperInstallDirectory({ localAppData = process.env.LOCALAPPDATA, home = homedir() } = {}) {
28
+ if (localAppData && !win32.isAbsolute(localAppData)) throw new Error('LOCALAPPDATA 必须是绝对路径');
29
+ return win32.join(localAppData || win32.join(home, 'AppData/Local'), 'AgentRoam/RemoteDesktop');
30
+ }
31
+
32
+ /** A Windows pipe disappears on process exit, so no stale PID file is removed. */
33
+ export async function acquireWindowsInstallLock(directory, timeoutMs = 10000) {
34
+ const pipe = '\\\\.\\pipe\\agentroam-remote-install-' + hash(canonical(directory));
35
+ const deadline = Date.now() + timeoutMs;
36
+ while (true) {
37
+ const server = createServer(socket => socket.destroy());
38
+ try {
39
+ await new Promise((resolve, reject) => { server.once('error', reject); server.listen(pipe, resolve); });
40
+ return () => new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
41
+ } catch (error) {
42
+ if (!['EADDRINUSE', 'EACCES'].includes(error.code)) throw error;
43
+ if (Date.now() >= deadline) throw new Error('另一实例正在更新远控组件,请稍后重试');
44
+ await delay(Math.min(100, deadline - Date.now()));
45
+ }
46
+ }
47
+ }
48
+
49
+ export async function windowsHelperProcesses() {
50
+ const script = `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
51
+ $ErrorActionPreference = 'Stop'
52
+ $items = @(Get-CimInstance Win32_Process -Filter "Name = 'agentroam-remote-desktop.exe'" | ForEach-Object {
53
+ if ([string]::IsNullOrWhiteSpace($_.ExecutablePath)) { throw 'Cannot inspect remote helper executable' }
54
+ @{ pid = $_.ProcessId; path = $_.ExecutablePath }
55
+ })
56
+ ConvertTo-Json -InputObject $items -Compress`;
57
+ const { stdout } = await runPowerShell(script);
58
+ const processes = JSON.parse(stdout.replace(/^\uFEFF/, '').trim());
59
+ if (!Array.isArray(processes) || processes.some(item => !Number.isSafeInteger(item.pid) || item.pid <= 0 || typeof item.path !== 'string' || !win32.isAbsolute(item.path))) throw new Error('无法确认旧远控组件是否仍在使用,请稍后重试');
60
+ return processes;
61
+ }
62
+
63
+ function runPowerShell(script, options = {}) {
64
+ const powershell = win32.join(process.env.SystemRoot || 'C:\\Windows', 'System32/WindowsPowerShell/v1.0/powershell.exe');
65
+ return run(powershell, ['-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(script, 'utf16le').toString('base64')], { timeout: 20000, maxBuffer: 1024 * 1024, windowsHide: true, ...options });
66
+ }
67
+
68
+ export async function stopWindowsHelperProcesses(processes) {
69
+ if (!processes.length) return;
70
+ const script = `$ErrorActionPreference = 'Stop'
71
+ $items = ConvertFrom-Json $env:AGENTROAM_RETIRE_HELPERS
72
+ foreach ($item in $items) {
73
+ $current = Get-CimInstance Win32_Process -Filter "ProcessId = $($item.pid)"
74
+ if ($null -eq $current) { continue }
75
+ if ($current.Name -ine 'agentroam-remote-desktop.exe' -or $current.ExecutablePath -ine $item.path) { throw 'Remote helper process identity changed' }
76
+ try { $process = [System.Diagnostics.Process]::GetProcessById($item.pid) } catch [System.ArgumentException] { continue }
77
+ try {
78
+ $handle = $process.Handle
79
+ $again = Get-CimInstance Win32_Process -Filter "ProcessId = $($item.pid)"
80
+ if ($null -eq $again) { continue }
81
+ if ($again.CreationDate -ne $current.CreationDate -or $again.ExecutablePath -ine $item.path) { throw 'Remote helper process identity changed' }
82
+ $process.Kill()
83
+ if (-not $process.WaitForExit(10000)) { throw 'Remote helper did not exit' }
84
+ } finally { $process.Dispose() }
85
+ }`;
86
+ await runPowerShell(script, { env: { ...process.env, AGENTROAM_RETIRE_HELPERS: JSON.stringify(processes) } });
87
+ }
88
+
89
+ async function entry(path) { return lstat(path).catch(error => { if (error.code !== 'ENOENT') throw error; return null; }); }
90
+
91
+ async function readPayload(directory, managed = false) {
92
+ const root = await entry(directory);
93
+ if (!root) return null;
94
+ if (!root.isDirectory() || root.isSymbolicLink()) throw new Error('远控组件安装位置不是普通目录,未覆盖');
95
+ if (managed && (await readdir(directory)).some(name => ![...payloadFiles, WINDOWS_HELPER_METADATA].includes(name))) throw new Error('远控组件安装位置含有其他文件,未覆盖');
96
+ const files = {};
97
+ for (const name of payloadFiles) {
98
+ const path = join(directory, name), info = await entry(path);
99
+ if (!info?.isFile() || info.isSymbolicLink()) throw new Error(`远控组件缺少普通文件:${name}`);
100
+ const bytes = await readFile(path);
101
+ if (name === WINDOWS_HELPER_EXECUTABLE) {
102
+ const offset = bytes.length >= 64 ? bytes.readUInt32LE(0x3c) : bytes.length;
103
+ if (bytes.toString('ascii', 0, 2) !== 'MZ' || offset + 6 > bytes.length || bytes.readUInt32LE(offset) !== 0x4550 || bytes.readUInt16LE(offset + 4) !== 0x8664) throw new Error('远控组件不是 Windows x64 程序');
104
+ }
105
+ files[name] = hash(bytes);
106
+ }
107
+ let version = 'legacy';
108
+ const metadataPath = join(directory, WINDOWS_HELPER_METADATA), metadata = await entry(metadataPath);
109
+ if (metadata) {
110
+ if (!metadata.isFile() || metadata.isSymbolicLink()) throw new Error('远控组件版本信息无效');
111
+ const value = JSON.parse(await readFile(metadataPath, 'utf8'));
112
+ if (value.component !== component || !(value.version === 'legacy' || componentVersionPattern.test(value.version))) throw new Error('远控组件版本信息无效');
113
+ if (!value.files || Object.keys(value.files).length !== payloadFiles.length || payloadFiles.some(name => value.files[name] !== files[name])) throw new Error('远控组件文件校验失败');
114
+ version = value.version;
115
+ } else if (managed) throw new Error('安装位置不是 AgentRoam 管理的远控组件,未覆盖');
116
+ const manifest = { component, version, files };
117
+ return { manifest, version, hash: hash(JSON.stringify(manifest)) };
118
+ }
119
+
120
+ /** The user helper is separate from the administrator-installed unlock service. */
121
+ export class WindowsRemoteHelperApp {
122
+ constructor({ sourcePath = bundledWindowsHelperPath(), installDir = windowsHelperInstallDirectory(), processes = windowsHelperProcesses, stopProcesses = stopWindowsHelperProcesses, acquireLock = acquireWindowsInstallLock, lockTimeoutMs = 10000 } = {}) {
123
+ Object.assign(this, { sourcePath, installDir, processes, stopProcesses, acquireLock, lockTimeoutMs });
124
+ this.executable = join(installDir, WINDOWS_HELPER_EXECUTABLE);
125
+ }
126
+ async available() { try { await access(this.sourcePath); return true; } catch { return false; } }
127
+ async describe() {
128
+ const installed = await readPayload(this.installDir, true), source = await readPayload(dirname(this.sourcePath));
129
+ return { name: 'AgentRoam Remote Desktop', path: this.executable, version: installed?.version ?? source?.version ?? 'unknown', fingerprint: (installed ?? source)?.hash.slice(0, 12), installed: Boolean(installed), updatePending: false };
130
+ }
131
+ prepare() { return this.withPreparedApp(path => path); }
132
+ async withPreparedApp(use) {
133
+ const parent = dirname(this.installDir);
134
+ await mkdir(parent, { recursive: true });
135
+ const unlock = await this.acquireLock(this.installDir, this.lockTimeoutMs);
136
+ try {
137
+ const backup = join(parent, '.RemoteDesktop.previous');
138
+ let installed = await readPayload(this.installDir, true);
139
+ const previous = await readPayload(backup, true);
140
+ if (previous) {
141
+ if (!installed) { await rename(backup, this.installDir); installed = previous; }
142
+ else await rm(backup, { recursive: true });
143
+ }
144
+ const source = await readPayload(dirname(this.sourcePath));
145
+ if (!source) throw new Error('当前 CLI 未包含 Windows 远程桌面组件,请升级 CLI');
146
+ const keepInstalled = installed && (installed.hash === source.hash || installed.version !== 'legacy' && compareComponentVersions(installed.version, source.version) >= 0);
147
+ const targetVersion = keepInstalled ? installed.version : source.version;
148
+ const running = await this.processes();
149
+ const retired = [];
150
+ for (const item of running.filter(item => canonical(item.path) !== canonical(this.executable))) {
151
+ const directory = dirname(item.path);
152
+ if (canonical(item.path) === canonical(this.sourcePath)) { retired.push({ ...item, directory, remove: false }); continue; }
153
+ // Old npm runtime payloads can be identified without touching their
154
+ // gateway, sessions or separately installed privileged unlock service.
155
+ const packagePath = join(directory, '../../package.json');
156
+ const pkg = JSON.parse(await readFile(packagePath, 'utf8').catch(() => { throw new Error('无法确认旧远控组件归属,未退出该进程'); }));
157
+ if (pkg.name !== 'agentroam-runtime-win32-x64' || !canonical(directory).endsWith('\\runtime\\native') || !componentVersionPattern.test(pkg.version)) throw new Error('无法确认旧远控组件归属,未退出该进程');
158
+ const payload = await readPayload(directory);
159
+ if (compareComponentVersions(payload.version === 'legacy' ? pkg.version : payload.version, targetVersion) > 0) throw new Error('检测到较新的远控组件,请使用最新 CLI 更新');
160
+ retired.push({ ...item, directory, remove: canonical(directory) !== canonical(dirname(this.sourcePath)), hash: payload.hash });
161
+ }
162
+ const stop = [...retired, ...(!keepInstalled ? running.filter(item => canonical(item.path) === canonical(this.executable)) : [])];
163
+ const retire = async () => {
164
+ await this.stopProcesses(stop.map(({ pid, path }) => ({ pid, path })));
165
+ };
166
+ const cleanup = async () => {
167
+ for (const item of new Map(retired.filter(item => item.remove).map(item => [canonical(item.directory), item])).values()) {
168
+ if ((await readPayload(item.directory))?.hash !== item.hash) throw new Error('旧远控组件已变化,未删除');
169
+ await rm(item.path);
170
+ await rm(join(item.directory, WINDOWS_HELPER_METADATA), { force: true });
171
+ }
172
+ };
173
+ if (keepInstalled) {
174
+ await retire(); await cleanup();
175
+ return await use(this.executable);
176
+ }
177
+ const staging = await mkdtemp(join(parent, '.remote-helper-update-'));
178
+ const staged = join(staging, 'RemoteDesktop');
179
+ let moved = false;
180
+ try {
181
+ await mkdir(staged);
182
+ for (const name of payloadFiles) await copyFile(join(dirname(this.sourcePath), name), join(staged, name));
183
+ await writeFile(join(staged, WINDOWS_HELPER_METADATA), JSON.stringify(source.manifest) + '\n');
184
+ if ((await readPayload(staged, true))?.hash !== source.hash) throw new Error('远控组件复制校验失败');
185
+ await retire();
186
+ if (installed) { await rename(this.installDir, backup); moved = true; }
187
+ try { await rename(staged, this.installDir); }
188
+ catch (error) {
189
+ if (moved) {
190
+ try { await rename(backup, this.installDir); }
191
+ catch { throw new Error(`远控组件更新失败,旧组件保留在 ${backup}`); }
192
+ }
193
+ throw error;
194
+ }
195
+ if (moved) await rm(backup, { recursive: true });
196
+ await cleanup();
197
+ } finally { await rm(staging, { recursive: true, force: true }); }
198
+ return await use(this.executable);
199
+ } finally { await unlock(); }
200
+ }
201
+ }
@@ -1,21 +1,17 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { access } from 'node:fs/promises';
3
- import { existsSync } from 'node:fs';
4
- import { fileURLToPath } from 'node:url';
3
+ import { WindowsRemoteHelperApp, bundledWindowsHelperPath } from './windows-helper-app.mjs';
5
4
 
6
5
  const MAX_MESSAGE = 8 * 1024 * 1024;
7
- function defaultExecutable() {
8
- const bundled = fileURLToPath(new URL('../../native/agentroam-remote-desktop.exe', import.meta.url));
9
- const development = fileURLToPath(new URL('../../native/.build-remote/windows/agentroam-remote-desktop.exe', import.meta.url));
10
- return existsSync(bundled) || !existsSync(development) ? bundled : development;
11
- }
12
- // Inherited pipes are private to this process tree; there is no named pipe or TCP listener.
6
+ // Control and media use private inherited pipes, separate from the install lock.
13
7
  export class WindowsRemoteHelper {
14
- constructor({ executable = defaultExecutable(), launch = spawn } = {}) {
15
- this.executable = executable; this.launch = launch; this.sequence = 0;
8
+ constructor({ executable, launch = spawn, application = executable || launch !== spawn ? null : new WindowsRemoteHelperApp() } = {}) {
9
+ this.application = application;
10
+ this.executable = executable ?? application?.executable ?? bundledWindowsHelperPath(); this.launch = launch; this.sequence = 0;
16
11
  this.pending = new Map(); this.listeners = new Set(); this.audioListeners = new Set(); this.generation = 0;
17
12
  }
18
- async available() { try { await access(this.executable); return true; } catch { return false; } }
13
+ async available() { if (this.application) return this.application.available(); try { await access(this.executable); return true; } catch { return false; } }
14
+ async describe() { return this.application ? this.application.describe() : { name: 'AgentRoam Remote Desktop', path: this.executable, version: 'unknown' }; }
19
15
  onVideo(listener) { this.listeners.add(listener); return () => this.listeners.delete(listener); }
20
16
  onAudio(listener) { this.audioListeners.add(listener); return () => this.audioListeners.delete(listener); }
21
17
  start() {
@@ -27,8 +23,12 @@ export class WindowsRemoteHelper {
27
23
  }
28
24
  async open(generation) {
29
25
  if (!await this.available()) throw new Error('当前 CLI 未包含 Windows 远程桌面组件,请升级 CLI');
26
+ if (this.application) return this.application.withPreparedApp(executable => this.openPrepared(generation, executable));
27
+ return this.openPrepared(generation, this.executable);
28
+ }
29
+ async openPrepared(generation, executable) {
30
30
  if (generation !== this.generation) throw new Error('远程桌面组件启动已取消');
31
- const child = this.launch(this.executable, ['--parent', String(process.pid)], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
31
+ const child = this.launch(executable, ['--parent', String(process.pid)], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
32
32
  this.child = child;
33
33
  let buffer = '';
34
34
  const fail = (error) => {
@@ -1 +1 @@
1
- {"bundleId":"com.agentroam.remote-desktop","version":"0.2.0-preview.56"}
1
+ {"bundleId":"com.agentroam.remote-desktop","version":"0.2.0-preview.57"}
@@ -6,7 +6,7 @@
6
6
  <dict>
7
7
  <key>Resources/agentroam-component.json</key>
8
8
  <data>
9
- i6c+paViWQ1yozhUEV9ggnWlWsE=
9
+ 16P/bUZutIfFc/aFvWp/xd7fDqU=
10
10
  </data>
11
11
  <key>Resources/app-icon.icns</key>
12
12
  <data>
@@ -19,7 +19,7 @@
19
19
  <dict>
20
20
  <key>hash2</key>
21
21
  <data>
22
- 3E7wcwJoJszIfVEaC2oDnUeqUgBwJAeEpl+Ap1TvefI=
22
+ U8ASez4BjgM9JQlpg/I9yqxIRe6kiR6ihMj/N5fCTaY=
23
23
  </data>
24
24
  </dict>
25
25
  <key>Resources/app-icon.icns</key>