agentroam-runtime-darwin-arm64 0.2.0-preview.39 → 0.2.0-preview.42

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 (182) hide show
  1. package/manifest.json +1 -1
  2. package/package.json +1 -1
  3. package/runtime/.next/BUILD_ID +1 -1
  4. package/runtime/.next/app-build-manifest.json +14 -14
  5. package/runtime/.next/app-path-routes-manifest.json +1 -1
  6. package/runtime/.next/build-manifest.json +6 -6
  7. package/runtime/.next/prerender-manifest.json +1 -1
  8. package/runtime/.next/react-loadable-manifest.json +3 -3
  9. package/runtime/.next/required-server-files.json +1 -1
  10. package/runtime/.next/server/app/_not-found/page.js +1 -1
  11. package/runtime/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/runtime/.next/server/app/_not-found.html +1 -1
  13. package/runtime/.next/server/app/_not-found.rsc +1 -1
  14. package/runtime/.next/server/app/api/agent/abort/route.js +1 -1
  15. package/runtime/.next/server/app/api/agent/answer/route.js +1 -1
  16. package/runtime/.next/server/app/api/agent/harness/status/route.js +1 -1
  17. package/runtime/.next/server/app/api/agent/model/route.js +1 -1
  18. package/runtime/.next/server/app/api/agent/models/route.js +1 -1
  19. package/runtime/.next/server/app/api/agent/run/route.js +2 -2
  20. package/runtime/.next/server/app/api/agent/runtime-health/route.js +1 -1
  21. package/runtime/.next/server/app/api/agent/steer/route.js +1 -1
  22. package/runtime/.next/server/app/api/agent/stream/route.js +3 -3
  23. package/runtime/.next/server/app/api/agent-workspaces/[workspaceId]/sessions/route.js +1 -1
  24. package/runtime/.next/server/app/api/agent-workspaces/route.js +1 -1
  25. package/runtime/.next/server/app/api/auth/verify/route.js +1 -1
  26. package/runtime/.next/server/app/api/business/route.js +1 -1
  27. package/runtime/.next/server/app/api/client-logs/route.js +1 -1
  28. package/runtime/.next/server/app/api/flow/v1/catalog/route.js +1 -1
  29. package/runtime/.next/server/app/api/flow/v1/runs/[runId]/cancel/route.js +1 -1
  30. package/runtime/.next/server/app/api/flow/v1/runs/[runId]/events/route.js +2 -2
  31. package/runtime/.next/server/app/api/flow/v1/runs/route.js +1 -1
  32. package/runtime/.next/server/app/api/flow-studio/config/route.js +1 -1
  33. package/runtime/.next/server/app/api/mcp/connect/route.js +1 -1
  34. package/runtime/.next/server/app/api/mcp/disconnect/route.js +1 -1
  35. package/runtime/.next/server/app/api/mcp/servers/route.js +1 -1
  36. package/runtime/.next/server/app/api/memory/[key]/route.js +1 -1
  37. package/runtime/.next/server/app/api/memory/route.js +1 -1
  38. package/runtime/.next/server/app/api/plugins/load/route.js +1 -1
  39. package/runtime/.next/server/app/api/projects/[id]/route.js +1 -1
  40. package/runtime/.next/server/app/api/projects/check/route.js +1 -1
  41. package/runtime/.next/server/app/api/projects/directories/route.js +1 -1
  42. package/runtime/.next/server/app/api/projects/roots/route.js +1 -1
  43. package/runtime/.next/server/app/api/projects/route.js +1 -1
  44. package/runtime/.next/server/app/api/push/subscribe/route.js +2 -2
  45. package/runtime/.next/server/app/api/push/unsubscribe/route.js +3 -3
  46. package/runtime/.next/server/app/api/push/vapid/route.js +2 -2
  47. package/runtime/.next/server/app/api/push/vapid.body +1 -1
  48. package/runtime/.next/server/app/api/remote-tools/register/route.js +1 -1
  49. package/runtime/.next/server/app/api/sessions/[id]/changes/route.js +2 -2
  50. package/runtime/.next/server/app/api/sessions/[id]/fork/route.js +1 -1
  51. package/runtime/.next/server/app/api/sessions/[id]/fork-at/route.js +1 -1
  52. package/runtime/.next/server/app/api/sessions/[id]/goals/route.js +1 -1
  53. package/runtime/.next/server/app/api/sessions/[id]/handoff/route.js +1 -1
  54. package/runtime/.next/server/app/api/sessions/[id]/query-index/route.js +1 -1
  55. package/runtime/.next/server/app/api/sessions/[id]/release/route.js +1 -1
  56. package/runtime/.next/server/app/api/sessions/[id]/route.js +1 -1
  57. package/runtime/.next/server/app/api/sessions/[id]/thread-goal/route.js +1 -1
  58. package/runtime/.next/server/app/api/sessions/[id]/tool-result/route.js +1 -1
  59. package/runtime/.next/server/app/api/sessions/route.js +1 -1
  60. package/runtime/.next/server/app/api/settings/route.js +1 -1
  61. package/runtime/.next/server/app/api/skills/route.js +1 -1
  62. package/runtime/.next/server/app/api/tool-policies/[policyId]/route.js +1 -1
  63. package/runtime/.next/server/app/api/tool-policies/route.js +1 -1
  64. package/runtime/.next/server/app/api/tools/route.js +1 -1
  65. package/runtime/.next/server/app/api/update/check/route.js +1 -1
  66. package/runtime/.next/server/app/api/update/install/route.js +1 -1
  67. package/runtime/.next/server/app/api/update/status/route.js +1 -1
  68. package/runtime/.next/server/app/api/web-auth/login/route.js +1 -1
  69. package/runtime/.next/server/app/api/web-auth/logout/route.js +1 -1
  70. package/runtime/.next/server/app/api/web-auth/revoke-all/route.js +1 -1
  71. package/runtime/.next/server/app/api/web-auth/setup/route.js +1 -1
  72. package/runtime/.next/server/app/api/web-auth/status/route.js +1 -1
  73. package/runtime/.next/server/app/api/web-console/bootstrap/route.js +1 -1
  74. package/runtime/.next/server/app/api/web-console/device-state/route.js +1 -1
  75. package/runtime/.next/server/app/api/web-console/history/route.js +1 -1
  76. package/runtime/.next/server/app/api/web-console/preferences/route.js +1 -1
  77. package/runtime/.next/server/app/web/page.js +28 -8
  78. package/runtime/.next/server/app/web/page_client-reference-manifest.js +1 -1
  79. package/runtime/.next/server/app-paths-manifest.json +23 -23
  80. package/runtime/.next/server/chunks/1322.js +8 -8
  81. package/runtime/.next/server/chunks/1773.js +1 -1
  82. package/runtime/.next/server/chunks/5303.js +2 -2
  83. package/runtime/.next/server/chunks/6244.js +1 -1
  84. package/runtime/.next/server/chunks/6703.js +2 -2
  85. package/runtime/.next/server/middleware-build-manifest.js +1 -1
  86. package/runtime/.next/server/middleware-react-loadable-manifest.js +1 -1
  87. package/runtime/.next/server/pages/404.html +1 -1
  88. package/runtime/.next/server/pages/500.html +1 -1
  89. package/runtime/.next/server/pages-manifest.json +1 -1
  90. package/runtime/.next/server/server-reference-manifest.json +1 -1
  91. package/runtime/.next/static/chunks/20.7bd5212774091396.js +1 -0
  92. package/runtime/.next/static/chunks/33-a1c48bbbfec958bb.js +1 -0
  93. package/runtime/.next/static/chunks/630.8377565e177db746.js +1 -0
  94. package/runtime/.next/static/chunks/app/web/{error-f69dbd3718cb2107.js → error-06b5df319f2c7fc7.js} +1 -1
  95. package/runtime/.next/static/chunks/app/web/layout-610834461f279f9b.js +1 -0
  96. package/runtime/.next/static/chunks/app/web/page-4aa41938174b8948.js +1 -0
  97. package/runtime/.next/static/chunks/{main-app-d527fa0b25a0c755.js → main-app-adc9b288f2f7795e.js} +1 -1
  98. package/runtime/.next/static/chunks/webpack-503c1f5d8fb747b2.js +1 -0
  99. package/runtime/lib/pairing-page.mjs +29 -8
  100. package/runtime/lib/pwa-assets.mjs +2 -0
  101. package/runtime/node_modules/.package-lock.json +115 -115
  102. package/runtime/package-lock.json +132 -132
  103. package/runtime/public/pwa/computer-directory.js +97 -0
  104. package/runtime/public/pwa/offline-computers.js +15 -0
  105. package/runtime/public/pwa/offline.html +1 -1
  106. package/runtime/public/pwa/pairing-scan.js +74 -50
  107. package/runtime/public/pwa/service-worker.js +8 -2
  108. package/runtime/server.js +1 -1
  109. package/runtime/webapp/dist/assets/{App-COIcZZvf.js → App-Bnv7-jUZ.js} +16 -16
  110. package/runtime/webapp/dist/assets/{abnfDiagram-VCTEODGH-d4urtMQU.js → abnfDiagram-VCTEODGH-CyHhjdFC.js} +1 -1
  111. package/runtime/webapp/dist/assets/{arc-DiQEpEJQ.js → arc-YykeV-yA.js} +1 -1
  112. package/runtime/webapp/dist/assets/{architectureDiagram-5GKGNRK7-CWYKGqND.js → architectureDiagram-5GKGNRK7-CPXGrnO0.js} +1 -1
  113. package/runtime/webapp/dist/assets/{blockDiagram-I7D4REHJ-J2I0b5Zn.js → blockDiagram-I7D4REHJ-DY5fPlHV.js} +1 -1
  114. package/runtime/webapp/dist/assets/{c4Diagram-7LVT6UL2-C-0xWeN3.js → c4Diagram-7LVT6UL2-D9IqQaEZ.js} +1 -1
  115. package/runtime/webapp/dist/assets/channel-DQNFzknQ.js +1 -0
  116. package/runtime/webapp/dist/assets/{chunk-2Q5K7J3B-CqodmK6L.js → chunk-2Q5K7J3B-Dq03q8Ff.js} +1 -1
  117. package/runtime/webapp/dist/assets/{chunk-5VM5RSS4-DClUnQcr.js → chunk-5VM5RSS4-V40WX1nt.js} +1 -1
  118. package/runtime/webapp/dist/assets/{chunk-F27PBJKO-CpaDg7Bg.js → chunk-F27PBJKO-CiCQWcQf.js} +1 -1
  119. package/runtime/webapp/dist/assets/{chunk-IMKFNOWR-CTIf9EjK.js → chunk-IMKFNOWR-EJT0DATb.js} +1 -1
  120. package/runtime/webapp/dist/assets/{chunk-JWPE2WC7-DKalSWMm.js → chunk-JWPE2WC7-B0T3KRl6.js} +1 -1
  121. package/runtime/webapp/dist/assets/{chunk-POPQ4Y6H-69MmeyRW.js → chunk-POPQ4Y6H-CBoqnL3A.js} +1 -1
  122. package/runtime/webapp/dist/assets/{chunk-SVP7TREG-DCXWXzhU.js → chunk-SVP7TREG-vc6mvklc.js} +1 -1
  123. package/runtime/webapp/dist/assets/{chunk-TICWLB2K-B5nbTEng.js → chunk-TICWLB2K-Co7ACWND.js} +1 -1
  124. package/runtime/webapp/dist/assets/{chunk-XXDRQBXY-DsJvkUuN.js → chunk-XXDRQBXY-CvByXr6J.js} +1 -1
  125. package/runtime/webapp/dist/assets/classDiagram-ZZMXUADV-DjmQeVYo.js +1 -0
  126. package/runtime/webapp/dist/assets/classDiagram-v2-VYDZK3BY-DjmQeVYo.js +1 -0
  127. package/runtime/webapp/dist/assets/{codeSyntax-DJH88FPL.js → codeSyntax-CEVVRDzr.js} +1 -1
  128. package/runtime/webapp/dist/assets/{cose-bilkent-JH36ORCC-BamzQ_e_.js → cose-bilkent-JH36ORCC-B6ijVOa6.js} +1 -1
  129. package/runtime/webapp/dist/assets/{cynefin-OW5HDTMX-BI_j1tbW.js → cynefin-OW5HDTMX-DNC_aald.js} +1 -1
  130. package/runtime/webapp/dist/assets/{cynefinDiagram-5FMLGOSQ-DuxMrVM2.js → cynefinDiagram-5FMLGOSQ-DMwAVRmf.js} +1 -1
  131. package/runtime/webapp/dist/assets/{dagre-GXQ25YYZ-Y9_ne36w.js → dagre-GXQ25YYZ-DtA0NmZw.js} +1 -1
  132. package/runtime/webapp/dist/assets/{diagram-S7CK7UJ4-DIIjO29m.js → diagram-S7CK7UJ4-B7SEmc9A.js} +1 -1
  133. package/runtime/webapp/dist/assets/{diagram-UQ7AKVKN-C4m2VMIy.js → diagram-UQ7AKVKN-CP_Xy1AP.js} +1 -1
  134. package/runtime/webapp/dist/assets/{diagram-VSXAHHWV-Cw5aPZ0o.js → diagram-VSXAHHWV-CMq6cPW9.js} +1 -1
  135. package/runtime/webapp/dist/assets/{diagram-VX7I27RA-W8nmVcOl.js → diagram-VX7I27RA-KIUvVPO4.js} +1 -1
  136. package/runtime/webapp/dist/assets/{diagram-Z3DM3KII-TksnHlQo.js → diagram-Z3DM3KII-UiMEplzj.js} +1 -1
  137. package/runtime/webapp/dist/assets/{ebnfDiagram-PWID7BFC-B3WM1b4a.js → ebnfDiagram-PWID7BFC-D221JIeM.js} +1 -1
  138. package/runtime/webapp/dist/assets/{erDiagram-RLTQ6QDP-BWctPuya.js → erDiagram-RLTQ6QDP-XKInLFA3.js} +1 -1
  139. package/runtime/webapp/dist/assets/{flowDiagram-HODETNUW-BG5RMrLN.js → flowDiagram-HODETNUW-BeT9GYtM.js} +1 -1
  140. package/runtime/webapp/dist/assets/{ganttDiagram-EL5Y4UJY-CPcR7Kn_.js → ganttDiagram-EL5Y4UJY-Dk8Uwf7E.js} +1 -1
  141. package/runtime/webapp/dist/assets/{gitGraphDiagram-WWUBYQGX-Sg6KChEd.js → gitGraphDiagram-WWUBYQGX-DiVf0w01.js} +1 -1
  142. package/runtime/webapp/dist/assets/{index-tQnPGo1_.js → index-D1I5RKVk.js} +3 -3
  143. package/runtime/webapp/dist/assets/{infoDiagram-27XIBGKW-BvrtvnrY.js → infoDiagram-27XIBGKW-DpVFXIO9.js} +1 -1
  144. package/runtime/webapp/dist/assets/{ishikawaDiagram-5VMMS53U-Bz-dZQLJ.js → ishikawaDiagram-5VMMS53U-ChWOHTzh.js} +1 -1
  145. package/runtime/webapp/dist/assets/{journeyDiagram-3NMN7TZE-Cb4s9jRE.js → journeyDiagram-3NMN7TZE-CTm0xDi4.js} +1 -1
  146. package/runtime/webapp/dist/assets/{kanban-definition-UXKFOSKX-IjMPUHg9.js → kanban-definition-UXKFOSKX-zVtWbTTX.js} +1 -1
  147. package/runtime/webapp/dist/assets/{layout-DHjqr0PJ.js → layout-C8tlpprW.js} +1 -1
  148. package/runtime/webapp/dist/assets/{linear-COctZkFy.js → linear-CArf8t7h.js} +1 -1
  149. package/runtime/webapp/dist/assets/{mermaid.core-BnonXfHT.js → mermaid.core-BVUH1a8m.js} +5 -5
  150. package/runtime/webapp/dist/assets/{mindmap-definition-YA3MSWOX-hxBlXJJE.js → mindmap-definition-YA3MSWOX-Cx7RFt-X.js} +1 -1
  151. package/runtime/webapp/dist/assets/{pegDiagram-XKGWAZYB-D6KJ8Ftm.js → pegDiagram-XKGWAZYB-BvEhVZgX.js} +1 -1
  152. package/runtime/webapp/dist/assets/{pieDiagram-E7YTZNPT-BzKWZmHN.js → pieDiagram-E7YTZNPT-U0qmhh14.js} +1 -1
  153. package/runtime/webapp/dist/assets/{quadrantDiagram-AXDQQJYC-ZhiPKt96.js → quadrantDiagram-AXDQQJYC-BbUHWejy.js} +1 -1
  154. package/runtime/webapp/dist/assets/{railroadDiagram-O6MQD6OU-BtKhiOPG.js → railroadDiagram-O6MQD6OU-SXZBMND2.js} +1 -1
  155. package/runtime/webapp/dist/assets/{requirementDiagram-BXWQKSXE-OFr_TA8V.js → requirementDiagram-BXWQKSXE-Cv9h1clg.js} +1 -1
  156. package/runtime/webapp/dist/assets/{sankeyDiagram-P5KCCOFB-qNTDFxAX.js → sankeyDiagram-P5KCCOFB-CSWUhEDJ.js} +1 -1
  157. package/runtime/webapp/dist/assets/{sequenceDiagram-WJ2MYXX4-B7s5-R4s.js → sequenceDiagram-WJ2MYXX4-Cy7gfOPU.js} +1 -1
  158. package/runtime/webapp/dist/assets/{sizeCapture-INFHLROL-DdFabbN6.js → sizeCapture-INFHLROL-DEJ8653D.js} +1 -1
  159. package/runtime/webapp/dist/assets/{stateDiagram-D77RDMKH-CmAN4pX7.js → stateDiagram-D77RDMKH-C9znHj1A.js} +1 -1
  160. package/runtime/webapp/dist/assets/stateDiagram-v2-MP3YSRHH-D4PTr_9g.js +1 -0
  161. package/runtime/webapp/dist/assets/{swimlanes-42K2YHIH-BQlIJAhf.js → swimlanes-42K2YHIH-D-1OGDuC.js} +1 -1
  162. package/runtime/webapp/dist/assets/swimlanesDiagram-VR7AAH4N-BnGs6oif.js +8 -0
  163. package/runtime/webapp/dist/assets/{timeline-definition-24CTP7MA--Y1zJ9mc.js → timeline-definition-24CTP7MA-fmk9SWID.js} +1 -1
  164. package/runtime/webapp/dist/assets/{vennDiagram-4TSXK5OY-CUn6FScx.js → vennDiagram-4TSXK5OY-CoJrdpIF.js} +1 -1
  165. package/runtime/webapp/dist/assets/{wardleyDiagram-VM6X3IG4-Bbz-WBd5.js → wardleyDiagram-VM6X3IG4-CQd2ig95.js} +1 -1
  166. package/runtime/webapp/dist/assets/{web-DzBEnE5q.js → web-CICIlrjx.js} +1 -1
  167. package/runtime/webapp/dist/assets/{web-harvpkJj.js → web-DdNvGACS.js} +1 -1
  168. package/runtime/webapp/dist/assets/{xychartDiagram-S5SC5T6Z-RA9edGdQ.js → xychartDiagram-S5SC5T6Z-Slg0Vzat.js} +1 -1
  169. package/runtime/webapp/dist/index.html +1 -1
  170. package/runtime/.next/static/chunks/20.b1b61df1a3c1499c.js +0 -1
  171. package/runtime/.next/static/chunks/33.4372b0479992e5e4.js +0 -1
  172. package/runtime/.next/static/chunks/693-41d7c8e27a8513d1.js +0 -1
  173. package/runtime/.next/static/chunks/app/web/layout-e0dd41d18df8ee9e.js +0 -1
  174. package/runtime/.next/static/chunks/app/web/page-f9926cbf4c1badd4.js +0 -1
  175. package/runtime/.next/static/chunks/webpack-d4e32619d2ce6d30.js +0 -1
  176. package/runtime/webapp/dist/assets/channel-B6S3kcST.js +0 -1
  177. package/runtime/webapp/dist/assets/classDiagram-ZZMXUADV-CyUA2YaV.js +0 -1
  178. package/runtime/webapp/dist/assets/classDiagram-v2-VYDZK3BY-CyUA2YaV.js +0 -1
  179. package/runtime/webapp/dist/assets/stateDiagram-v2-MP3YSRHH-DsxpCqde.js +0 -1
  180. package/runtime/webapp/dist/assets/swimlanesDiagram-VR7AAH4N-tji3GZVz.js +0 -8
  181. /package/runtime/.next/static/{BmUH3xLHQfGkG3yzEK37i → 8hkoN4pqLlKY59lLXLBS-}/_buildManifest.js +0 -0
  182. /package/runtime/.next/static/{BmUH3xLHQfGkG3yzEK37i → 8hkoN4pqLlKY59lLXLBS-}/_ssgManifest.js +0 -0
@@ -0,0 +1,97 @@
1
+ /** Computer labels and addresses only. Authorization always stays on its own origin. */
2
+ export const COMPUTER_STORAGE_KEY = "agentroam.computers.v1";
3
+ const MAX_COMPUTERS = 20;
4
+ const GRANT = /^[A-Za-z0-9_-]{43}$/;
5
+
6
+ /** @typedef {{ origin: string, name: string }} Computer */
7
+
8
+ export function computerOrigin(raw) {
9
+ try {
10
+ const url = new URL(raw);
11
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
12
+ return url.origin;
13
+ } catch { return null; }
14
+ }
15
+
16
+ /** @returns {Computer[]} */
17
+ export function normalizeComputers(value) {
18
+ if (!Array.isArray(value) || value.length > MAX_COMPUTERS) return [];
19
+ const computers = new Map();
20
+ for (const entry of value) {
21
+ const origin = entry && typeof entry.origin === "string" ? computerOrigin(entry.origin) : null;
22
+ if (!origin || typeof entry.name !== "string") continue;
23
+ const name = entry.name.trim().slice(0, 60) || new URL(origin).hostname;
24
+ computers.set(origin, { origin, name });
25
+ }
26
+ return [...computers.values()];
27
+ }
28
+
29
+ /** @returns {Computer[]} */
30
+ export function readComputers(storage = localStorage) {
31
+ try { return normalizeComputers(JSON.parse(storage.getItem(COMPUTER_STORAGE_KEY) || "[]")); }
32
+ catch { return []; }
33
+ }
34
+
35
+ export function mergeComputers(...lists) {
36
+ const byOrigin = new Map();
37
+ for (const list of lists) for (const computer of normalizeComputers(list)) byOrigin.set(computer.origin, computer);
38
+ if (byOrigin.size > MAX_COMPUTERS) throw new Error("最多可添加 20 台电脑。");
39
+ return [...byOrigin.values()];
40
+ }
41
+
42
+ export function saveComputers(computers, storage = localStorage) {
43
+ const normalized = normalizeComputers(computers);
44
+ try { storage.setItem(COMPUTER_STORAGE_KEY, JSON.stringify(normalized)); }
45
+ catch { throw new Error("浏览器无法保存电脑列表,请检查浏览器存储设置。"); }
46
+ return normalized;
47
+ }
48
+
49
+ export function rememberComputer(origin, name, computers = readComputers()) {
50
+ const normalized = computerOrigin(origin);
51
+ if (!normalized) throw new Error("电脑地址无效。");
52
+ const existing = computers.find(computer => computer.origin === normalized);
53
+ return mergeComputers(computers, [{ origin: normalized, name: name?.trim() || existing?.name || new URL(normalized).hostname }]);
54
+ }
55
+
56
+ export function readComputerHandoff(fragment) {
57
+ if (fragment.length > 16_384) return { computers: [], name: "" };
58
+ try {
59
+ const value = JSON.parse(new URLSearchParams(fragment.slice(1)).get("computers") || "null");
60
+ if (value?.version !== 1) return { computers: [], name: "" };
61
+ return { computers: normalizeComputers(value.computers), name: typeof value.name === "string" ? value.name.trim().slice(0, 60) : "" };
62
+ } catch { return { computers: [], name: "" }; }
63
+ }
64
+
65
+ /** Accept only the existing CLI URL or native QR format, never an arbitrary link. */
66
+ export function parsePairingTarget(raw) {
67
+ try {
68
+ if (typeof raw !== "string" || raw.length > 2048) throw new Error();
69
+ raw = raw.trim();
70
+ if (raw.startsWith("{")) {
71
+ const value = JSON.parse(raw), origin = computerOrigin(value.server);
72
+ if (value.type !== "agentroam-pair" || value.version !== 1 || !origin || !GRANT.test(value.grant)) throw new Error();
73
+ raw = origin + "/pair#pair=" + value.grant;
74
+ }
75
+ const url = new URL(raw);
76
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/pair" || url.search || !/^#pair=[A-Za-z0-9_-]{43}$/.test(url.hash)) throw new Error();
77
+ return url;
78
+ } catch { throw new Error("这不是 AgentRoam 授权二维码,请在电脑上重新生成。"); }
79
+ }
80
+
81
+ function attachDirectory(url, computers, name = "") {
82
+ const params = new URLSearchParams(url.hash.slice(1));
83
+ params.set("computers", JSON.stringify({ version: 1, computers: normalizeComputers(computers), name: name.trim().slice(0, 60) }));
84
+ url.hash = params.toString();
85
+ return url.href;
86
+ }
87
+
88
+ export function pairingNavigation(raw, computers, name = "") {
89
+ return attachDirectory(parsePairingTarget(raw), computers, name);
90
+ }
91
+
92
+ /** /pair makes a same-origin auth check after a cross-site Strict-cookie navigation. */
93
+ export function computerNavigation(origin, computers) {
94
+ const normalized = computerOrigin(origin);
95
+ if (!normalized) throw new Error("电脑地址无效。");
96
+ return attachDirectory(new URL(normalized + "/pair"), computers);
97
+ }
@@ -0,0 +1,15 @@
1
+ import { readComputers, readComputerHandoff, mergeComputers, saveComputers, computerNavigation } from "./computer-directory.js";
2
+
3
+ let computers = readComputers();
4
+ // The target can go offline after the user starts switching. Keep its handoff
5
+ // directory locally, but never record the unconfirmed target or a QR grant.
6
+ try { computers = saveComputers(mergeComputers(computers, readComputerHandoff(location.hash).computers)); } catch {}
7
+ if (location.hash) history.replaceState(null, "", location.pathname + location.search);
8
+ const section = document.getElementById("known-computers"), selector = document.getElementById("computer");
9
+ for (const computer of computers) {
10
+ const option = document.createElement("option");
11
+ option.value = computer.origin; option.textContent = computer.name;
12
+ selector.append(option);
13
+ }
14
+ section.hidden = !computers.length;
15
+ selector.addEventListener("change", () => { if (selector.value) location.assign(computerNavigation(selector.value, computers)); });
@@ -1 +1 @@
1
- <!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#0b1015"><title>连接已断开 · AgentRoam</title><style>body{margin:0;padding:32px;min-height:80vh;display:grid;place-content:center;background:#0b1015;color:#eaf2f3;font-family:system-ui}main{max-width:380px}p{color:#9baeb7;line-height:1.8}a{color:#a1dece}</style></head><body><main><h1>暂时无法连接电脑</h1><p>请检查手机网络,并确认电脑上的 AgentRoam 正在运行。恢复连接后可以继续使用。</p><a href="/web">重新连接</a></main></body></html>
1
+ <!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#0b1015"><title>连接已断开 · AgentRoam</title><style>body{margin:0;padding:32px;min-height:80vh;display:grid;place-content:center;background:#0b1015;color:#eaf2f3;font-family:system-ui}main{max-width:380px}p{color:#9baeb7;line-height:1.8}a{color:#a1dece}select{width:100%;margin:12px 0 24px;padding:12px;border-radius:8px;background:#121c24;color:#eaf2f3;border:1px solid #34454e;font:inherit}</style></head><body><main><h1>暂时无法连接电脑</h1><p>请检查手机网络,并确认电脑上的 AgentRoam 正在运行。也可以切换到已添加的另一台电脑。</p><section id="known-computers" hidden><label for="computer">已添加的电脑</label><select id="computer"><option value="">选择另一台电脑…</option></select></section><a href="/pair">重新连接</a></main><script type="module" src="/pwa/offline-computers.js"></script></body></html>
@@ -1,33 +1,66 @@
1
- (() => {
2
- const scan = document.getElementById("scan"), cancel = document.getElementById("scan-cancel"), section = document.getElementById("scanner"), video = document.getElementById("camera");
3
- if (!scan || !video) return;
4
- let stream, running = false, generation = 0, decoder;
5
- const stopCamera = () => { generation++; running = false; stream?.getTracks().forEach(track => track.stop()); stream = null; video.srcObject = null; section.hidden = true; scan.disabled = false; };
6
- const fail = message => { stopCamera(); showForm(message); };
7
- function parse(raw) {
8
- if (raw.length > 2048) throw new Error();
9
- if (raw.startsWith("{")) {
10
- const value = JSON.parse(raw), server = new URL(value.server);
11
- if (value.type !== "agentroam-pair" || value.version !== 1 || server.username || server.password || server.search || server.hash || server.pathname !== "/" || !/^[A-Za-z0-9_-]{43}$/.test(value.grant)) throw new Error();
12
- raw = server.origin + "/pair#pair=" + value.grant;
13
- }
14
- const url = new URL(raw);
15
- if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/pair" || url.search || !/^#pair=[A-Za-z0-9_-]{43}$/.test(url.hash)) throw new Error();
16
- return url;
17
- }
18
- function loadDecoder() {
19
- if (window.jsQR) return Promise.resolve();
20
- return decoder ||= new Promise((resolve, reject) => {
21
- const script = document.createElement("script"); script.src = "/pwa/qr-decoder.js";
22
- const failed = () => { clearTimeout(timer); decoder = null; script.remove(); reject(new Error()); };
23
- const timer = setTimeout(failed, 10_000);
24
- script.onload = () => { clearTimeout(timer); resolve(); }; script.onerror = failed; document.head.append(script);
1
+ let decoder;
2
+ function loadDecoder() {
3
+ if (window.jsQR) return Promise.resolve();
4
+ return decoder ||= new Promise((resolve, reject) => {
5
+ const script = document.createElement("script"); script.src = "/pwa/qr-decoder.js";
6
+ const failed = () => { clearTimeout(timer); decoder = null; script.remove(); reject(new Error("二维码识别器加载失败,请重试。")); };
7
+ const timer = setTimeout(failed, 10_000);
8
+ script.onload = () => { clearTimeout(timer); resolve(); }; script.onerror = failed; document.head.append(script);
9
+ });
10
+ }
11
+
12
+ export function supportsLiveCamera() {
13
+ return Boolean(window.isSecureContext && navigator.mediaDevices?.getUserMedia);
14
+ }
15
+
16
+ /** A native photo capture input also works on ordinary HTTP mobile pages. */
17
+ export async function decodePairingImage(file) {
18
+ if (!file || (file.type && !file.type.startsWith("image/"))) throw new Error("请选择二维码照片。");
19
+ if (file.size > 20 * 1024 * 1024) throw new Error("照片过大,请重新拍摄二维码。");
20
+ await loadDecoder();
21
+ const image = new Image(), source = URL.createObjectURL(file);
22
+ try {
23
+ await new Promise((resolve, reject) => {
24
+ const timer = setTimeout(() => reject(new Error("照片加载超时,请重新拍摄。")), 10_000);
25
+ image.onload = () => { clearTimeout(timer); resolve(); };
26
+ image.onerror = () => { clearTimeout(timer); reject(new Error("无法读取照片,请重新拍摄二维码。")); };
27
+ image.src = source;
25
28
  });
26
- }
27
- scan.addEventListener("click", async () => {
28
- if (running || scan.disabled) return;
29
- if (!window.isSecureContext || !navigator.mediaDevices?.getUserMedia) { showForm("相机扫码需要 HTTPS。也可以用手机系统相机扫描电脑二维码,或手输配对码。"); return; }
30
- const attempt = ++generation; scan.disabled = true; section.hidden = false;
29
+ const canvas = document.createElement("canvas"), context = canvas.getContext("2d", { willReadFrequently: true });
30
+ for (const limit of [1600, 1024, 640]) {
31
+ const scale = Math.min(1, limit / Math.max(image.naturalWidth, image.naturalHeight));
32
+ canvas.width = Math.max(1, Math.round(image.naturalWidth * scale)); canvas.height = Math.max(1, Math.round(image.naturalHeight * scale));
33
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
34
+ const pixels = context.getImageData(0, 0, canvas.width, canvas.height);
35
+ const code = window.jsQR(pixels.data, pixels.width, pixels.height);
36
+ if (code) return code.data;
37
+ }
38
+ throw new Error("照片中没有识别到二维码,请靠近电脑屏幕重新拍摄。");
39
+ } finally { image.onload = null; image.onerror = null; URL.revokeObjectURL(source); }
40
+ }
41
+
42
+ /** Shared by the pairing landing page and the global computer switcher. */
43
+ export function createPairingScanner(video, onResult, onError) {
44
+ let stream, generation = 0, timer;
45
+ const stop = () => {
46
+ generation++;
47
+ clearTimeout(timer);
48
+ stream?.getTracks().forEach(track => track.stop());
49
+ stream = null;
50
+ video.srcObject = null;
51
+ };
52
+ const fail = (attempt, error) => {
53
+ if (attempt !== generation) return;
54
+ stop();
55
+ onError(error?.name === "NotAllowedError" ? "相机权限未开启,请允许相机或粘贴二维码链接。" : error?.message || "无法启动扫码,请重试。");
56
+ };
57
+ const start = async () => {
58
+ stop();
59
+ const attempt = generation;
60
+ if (!supportsLiveCamera()) {
61
+ onError("实时扫码需要 HTTPS,请使用拍照扫码。");
62
+ return;
63
+ }
31
64
  try {
32
65
  await loadDecoder();
33
66
  if (attempt !== generation) return;
@@ -35,36 +68,27 @@
35
68
  if (attempt !== generation) { camera.getTracks().forEach(track => track.stop()); return; }
36
69
  stream = camera; video.srcObject = camera;
37
70
  let playTimer;
38
- try { await Promise.race([video.play(), new Promise((_, reject) => { playTimer = setTimeout(() => reject(new Error("Camera produced no frames")), 10_000); })]); }
71
+ try { await Promise.race([video.play(), new Promise((_, reject) => { playTimer = setTimeout(() => reject(new Error("相机没有返回画面,请重试。")), 10_000); })]); }
39
72
  finally { clearTimeout(playTimer); }
40
73
  if (attempt !== generation) return;
41
- running = true;
42
74
  const canvas = document.createElement("canvas"), context = canvas.getContext("2d", { willReadFrequently: true });
43
75
  const tick = () => {
44
- if (!running || attempt !== generation) return;
76
+ if (attempt !== generation) return;
45
77
  try {
46
78
  if (video.videoWidth && video.videoHeight) {
47
79
  canvas.width = Math.min(video.videoWidth, 640); canvas.height = Math.round(video.videoHeight * canvas.width / video.videoWidth);
48
80
  context.drawImage(video, 0, 0, canvas.width, canvas.height);
49
81
  const pixels = context.getImageData(0, 0, canvas.width, canvas.height), code = window.jsQR(pixels.data, pixels.width, pixels.height);
50
- if (code) {
51
- let target; try { target = parse(code.data); } catch { fail("这不是 AgentRoam 授权二维码,请在电脑上重新生成。"); return; }
52
- stopCamera();
53
- if (target.origin === location.origin) void exchangeQr(target.hash.slice(6));
54
- else location.assign(target.href);
55
- return;
56
- }
82
+ if (code) { stop(); onResult(code.data); return; }
57
83
  }
58
- setTimeout(tick, 250);
59
- } catch { fail("二维码识别失败,请重试或手输配对码。"); }
84
+ timer = setTimeout(tick, 250);
85
+ } catch (error) { stop(); onError(error?.message || "二维码识别失败,请重试。"); }
60
86
  };
61
87
  tick();
62
- } catch (error) {
63
- if (attempt === generation) fail(error?.name === "NotAllowedError" ? "相机权限未开启,请在浏览器设置中允许相机,或手输配对码。" : "无法启动扫码,请检查相机与网络后重试。");
64
- }
65
- });
66
- cancel.addEventListener("click", stopCamera);
67
- document.getElementById("pair").addEventListener("submit", stopCamera);
68
- window.addEventListener("pagehide", stopCamera);
69
- document.addEventListener("visibilitychange", () => { if (document.hidden) stopCamera(); });
70
- })();
88
+ } catch (error) { fail(attempt, error); }
89
+ };
90
+ const visibility = () => { if (document.hidden) stop(); };
91
+ window.addEventListener("pagehide", stop);
92
+ document.addEventListener("visibilitychange", visibility);
93
+ return { start, stop, dispose() { stop(); window.removeEventListener("pagehide", stop); document.removeEventListener("visibilitychange", visibility); } };
94
+ }
@@ -1,12 +1,18 @@
1
- const CACHE = "agentroam-public-offline-v1";
1
+ const CACHE = "agentroam-public-offline-v2";
2
2
  const OFFLINE = "/pwa/offline.html";
3
+ const PUBLIC_FILES = [OFFLINE, "/pwa/computer-directory.js", "/pwa/offline-computers.js"];
3
4
  self.addEventListener("install", event => {
4
- event.waitUntil(caches.open(CACHE).then(cache => cache.add(new Request(OFFLINE, { cache: "reload", credentials: "omit" }))).then(() => self.skipWaiting()));
5
+ event.waitUntil(caches.open(CACHE).then(cache => Promise.all(PUBLIC_FILES.map(path => cache.add(new Request(path, { cache: "reload", credentials: "omit" }))))).then(() => self.skipWaiting()));
5
6
  });
6
7
  self.addEventListener("activate", event => {
7
8
  event.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(key => key.startsWith("agentroam-public-offline-") && key !== CACHE).map(key => caches.delete(key)))).then(() => self.clients.claim()));
8
9
  });
9
10
  self.addEventListener("fetch", event => {
11
+ const url = new URL(event.request.url);
12
+ if (event.request.method === "GET" && url.origin === self.location.origin && !url.search && PUBLIC_FILES.includes(url.pathname)) {
13
+ event.respondWith(fetch(event.request).catch(() => caches.match(url.pathname)));
14
+ return;
15
+ }
10
16
  // Never cache or replay authenticated pages, API responses, files or operations.
11
17
  if (event.request.mode === "navigate" && event.request.method === "GET" && new URL(event.request.url).origin === self.location.origin) {
12
18
  event.respondWith(fetch(new Request(event.request, { cache: "no-store" })).catch(async () => (await caches.match(OFFLINE)) || new Response("无法连接电脑,请检查网络后重试。", { status: 503, headers: { "content-type": "text/plain; charset=utf-8" } })));
package/runtime/server.js CHANGED
@@ -15,7 +15,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
15
15
  const hostname = process.env.HOSTNAME || '0.0.0.0'
16
16
 
17
17
  let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
18
- const nextConfig = {"env":{},"eslint":{"ignoreDuringBuilds":false},"typescript":{"ignoreBuildErrors":false,"tsconfigPath":"tsconfig.json"},"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.js","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"analyticsId":"","images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[16,32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":60,"formats":["image/webp"],"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"inline","remotePatterns":[],"unoptimized":false},"devIndicators":{"buildActivity":true,"buildActivityPosition":"bottom-right"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"amp":{"canonicalBase":""},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"optimizeFonts":true,"excludeDefaultMomentLocales":true,"serverRuntimeConfig":{},"publicRuntimeConfig":{},"reactProductionProfiling":false,"reactStrictMode":null,"httpAgentOptions":{"keepAlive":true},"outputFileTracing":true,"staticPageGenerationTimeout":60,"swcMinify":true,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"experimental":{"multiZoneDraftMode":false,"prerenderEarlyExit":false,"serverMinification":true,"serverSourceMaps":false,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","middlewarePrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":10,"memoryBasedWorkersCount":false,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"outputFileTracingRoot":"/Users/caoqu/.codex/worktrees/npm-preview-39/customer-agent","swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"adjustFontFallbacks":false,"adjustFontFallbacksWithSizeAdjust":false,"typedRoutes":false,"instrumentationHook":false,"bundlePagesExternals":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"missingSuspenseWithCSRBailout":true,"optimizeServerReact":true,"useEarlyImport":false,"staleTimes":{"dynamic":30,"static":300},"serverComponentsExternalPackages":["@anthropic-ai/claude-agent-sdk"],"optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"configFileName":"next.config.js"}
18
+ const nextConfig = {"env":{},"eslint":{"ignoreDuringBuilds":false},"typescript":{"ignoreBuildErrors":false,"tsconfigPath":"tsconfig.json"},"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.js","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"analyticsId":"","images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[16,32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":60,"formats":["image/webp"],"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"inline","remotePatterns":[],"unoptimized":false},"devIndicators":{"buildActivity":true,"buildActivityPosition":"bottom-right"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"amp":{"canonicalBase":""},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"optimizeFonts":true,"excludeDefaultMomentLocales":true,"serverRuntimeConfig":{},"publicRuntimeConfig":{},"reactProductionProfiling":false,"reactStrictMode":null,"httpAgentOptions":{"keepAlive":true},"outputFileTracing":true,"staticPageGenerationTimeout":60,"swcMinify":true,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"experimental":{"multiZoneDraftMode":false,"prerenderEarlyExit":false,"serverMinification":true,"serverSourceMaps":false,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","middlewarePrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":10,"memoryBasedWorkersCount":false,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"outputFileTracingRoot":"/private/var/folders/6r/m9bbktwj1pvbr6hbzpxqkq3h0000gn/T/agentroam-release42-6zCiAy/repository","swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"adjustFontFallbacks":false,"adjustFontFallbacksWithSizeAdjust":false,"typedRoutes":false,"instrumentationHook":false,"bundlePagesExternals":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"missingSuspenseWithCSRBailout":true,"optimizeServerReact":true,"useEarlyImport":false,"staleTimes":{"dynamic":30,"static":300},"serverComponentsExternalPackages":["@anthropic-ai/claude-agent-sdk"],"optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"configFileName":"next.config.js"}
19
19
 
20
20
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
21
21