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.
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/runtime/.next/BUILD_ID +1 -1
- package/runtime/.next/app-build-manifest.json +14 -14
- package/runtime/.next/app-path-routes-manifest.json +1 -1
- package/runtime/.next/build-manifest.json +6 -6
- package/runtime/.next/prerender-manifest.json +1 -1
- package/runtime/.next/react-loadable-manifest.json +3 -3
- package/runtime/.next/required-server-files.json +1 -1
- package/runtime/.next/server/app/_not-found/page.js +1 -1
- package/runtime/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/runtime/.next/server/app/_not-found.html +1 -1
- package/runtime/.next/server/app/_not-found.rsc +1 -1
- package/runtime/.next/server/app/api/agent/abort/route.js +1 -1
- package/runtime/.next/server/app/api/agent/answer/route.js +1 -1
- package/runtime/.next/server/app/api/agent/harness/status/route.js +1 -1
- package/runtime/.next/server/app/api/agent/model/route.js +1 -1
- package/runtime/.next/server/app/api/agent/models/route.js +1 -1
- package/runtime/.next/server/app/api/agent/run/route.js +2 -2
- package/runtime/.next/server/app/api/agent/runtime-health/route.js +1 -1
- package/runtime/.next/server/app/api/agent/steer/route.js +1 -1
- package/runtime/.next/server/app/api/agent/stream/route.js +3 -3
- package/runtime/.next/server/app/api/agent-workspaces/[workspaceId]/sessions/route.js +1 -1
- package/runtime/.next/server/app/api/agent-workspaces/route.js +1 -1
- package/runtime/.next/server/app/api/auth/verify/route.js +1 -1
- package/runtime/.next/server/app/api/business/route.js +1 -1
- package/runtime/.next/server/app/api/client-logs/route.js +1 -1
- package/runtime/.next/server/app/api/flow/v1/catalog/route.js +1 -1
- package/runtime/.next/server/app/api/flow/v1/runs/[runId]/cancel/route.js +1 -1
- package/runtime/.next/server/app/api/flow/v1/runs/[runId]/events/route.js +2 -2
- package/runtime/.next/server/app/api/flow/v1/runs/route.js +1 -1
- package/runtime/.next/server/app/api/flow-studio/config/route.js +1 -1
- package/runtime/.next/server/app/api/mcp/connect/route.js +1 -1
- package/runtime/.next/server/app/api/mcp/disconnect/route.js +1 -1
- package/runtime/.next/server/app/api/mcp/servers/route.js +1 -1
- package/runtime/.next/server/app/api/memory/[key]/route.js +1 -1
- package/runtime/.next/server/app/api/memory/route.js +1 -1
- package/runtime/.next/server/app/api/plugins/load/route.js +1 -1
- package/runtime/.next/server/app/api/projects/[id]/route.js +1 -1
- package/runtime/.next/server/app/api/projects/check/route.js +1 -1
- package/runtime/.next/server/app/api/projects/directories/route.js +1 -1
- package/runtime/.next/server/app/api/projects/roots/route.js +1 -1
- package/runtime/.next/server/app/api/projects/route.js +1 -1
- package/runtime/.next/server/app/api/push/subscribe/route.js +2 -2
- package/runtime/.next/server/app/api/push/unsubscribe/route.js +3 -3
- package/runtime/.next/server/app/api/push/vapid/route.js +2 -2
- package/runtime/.next/server/app/api/push/vapid.body +1 -1
- package/runtime/.next/server/app/api/remote-tools/register/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/changes/route.js +2 -2
- package/runtime/.next/server/app/api/sessions/[id]/fork/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/fork-at/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/goals/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/handoff/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/query-index/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/release/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/thread-goal/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/[id]/tool-result/route.js +1 -1
- package/runtime/.next/server/app/api/sessions/route.js +1 -1
- package/runtime/.next/server/app/api/settings/route.js +1 -1
- package/runtime/.next/server/app/api/skills/route.js +1 -1
- package/runtime/.next/server/app/api/tool-policies/[policyId]/route.js +1 -1
- package/runtime/.next/server/app/api/tool-policies/route.js +1 -1
- package/runtime/.next/server/app/api/tools/route.js +1 -1
- package/runtime/.next/server/app/api/update/check/route.js +1 -1
- package/runtime/.next/server/app/api/update/install/route.js +1 -1
- package/runtime/.next/server/app/api/update/status/route.js +1 -1
- package/runtime/.next/server/app/api/web-auth/login/route.js +1 -1
- package/runtime/.next/server/app/api/web-auth/logout/route.js +1 -1
- package/runtime/.next/server/app/api/web-auth/revoke-all/route.js +1 -1
- package/runtime/.next/server/app/api/web-auth/setup/route.js +1 -1
- package/runtime/.next/server/app/api/web-auth/status/route.js +1 -1
- package/runtime/.next/server/app/api/web-console/bootstrap/route.js +1 -1
- package/runtime/.next/server/app/api/web-console/device-state/route.js +1 -1
- package/runtime/.next/server/app/api/web-console/history/route.js +1 -1
- package/runtime/.next/server/app/api/web-console/preferences/route.js +1 -1
- package/runtime/.next/server/app/web/page.js +28 -8
- package/runtime/.next/server/app/web/page_client-reference-manifest.js +1 -1
- package/runtime/.next/server/app-paths-manifest.json +23 -23
- package/runtime/.next/server/chunks/1322.js +8 -8
- package/runtime/.next/server/chunks/1773.js +1 -1
- package/runtime/.next/server/chunks/5303.js +2 -2
- package/runtime/.next/server/chunks/6244.js +1 -1
- package/runtime/.next/server/chunks/6703.js +2 -2
- package/runtime/.next/server/middleware-build-manifest.js +1 -1
- package/runtime/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/runtime/.next/server/pages/404.html +1 -1
- package/runtime/.next/server/pages/500.html +1 -1
- package/runtime/.next/server/pages-manifest.json +1 -1
- package/runtime/.next/server/server-reference-manifest.json +1 -1
- package/runtime/.next/static/chunks/20.7bd5212774091396.js +1 -0
- package/runtime/.next/static/chunks/33-a1c48bbbfec958bb.js +1 -0
- package/runtime/.next/static/chunks/630.8377565e177db746.js +1 -0
- package/runtime/.next/static/chunks/app/web/{error-f69dbd3718cb2107.js → error-06b5df319f2c7fc7.js} +1 -1
- package/runtime/.next/static/chunks/app/web/layout-610834461f279f9b.js +1 -0
- package/runtime/.next/static/chunks/app/web/page-4aa41938174b8948.js +1 -0
- package/runtime/.next/static/chunks/{main-app-d527fa0b25a0c755.js → main-app-adc9b288f2f7795e.js} +1 -1
- package/runtime/.next/static/chunks/webpack-503c1f5d8fb747b2.js +1 -0
- package/runtime/lib/pairing-page.mjs +29 -8
- package/runtime/lib/pwa-assets.mjs +2 -0
- package/runtime/node_modules/.package-lock.json +115 -115
- package/runtime/package-lock.json +132 -132
- package/runtime/public/pwa/computer-directory.js +97 -0
- package/runtime/public/pwa/offline-computers.js +15 -0
- package/runtime/public/pwa/offline.html +1 -1
- package/runtime/public/pwa/pairing-scan.js +74 -50
- package/runtime/public/pwa/service-worker.js +8 -2
- package/runtime/server.js +1 -1
- package/runtime/webapp/dist/assets/{App-COIcZZvf.js → App-Bnv7-jUZ.js} +16 -16
- package/runtime/webapp/dist/assets/{abnfDiagram-VCTEODGH-d4urtMQU.js → abnfDiagram-VCTEODGH-CyHhjdFC.js} +1 -1
- package/runtime/webapp/dist/assets/{arc-DiQEpEJQ.js → arc-YykeV-yA.js} +1 -1
- package/runtime/webapp/dist/assets/{architectureDiagram-5GKGNRK7-CWYKGqND.js → architectureDiagram-5GKGNRK7-CPXGrnO0.js} +1 -1
- package/runtime/webapp/dist/assets/{blockDiagram-I7D4REHJ-J2I0b5Zn.js → blockDiagram-I7D4REHJ-DY5fPlHV.js} +1 -1
- package/runtime/webapp/dist/assets/{c4Diagram-7LVT6UL2-C-0xWeN3.js → c4Diagram-7LVT6UL2-D9IqQaEZ.js} +1 -1
- package/runtime/webapp/dist/assets/channel-DQNFzknQ.js +1 -0
- package/runtime/webapp/dist/assets/{chunk-2Q5K7J3B-CqodmK6L.js → chunk-2Q5K7J3B-Dq03q8Ff.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-5VM5RSS4-DClUnQcr.js → chunk-5VM5RSS4-V40WX1nt.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-F27PBJKO-CpaDg7Bg.js → chunk-F27PBJKO-CiCQWcQf.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-IMKFNOWR-CTIf9EjK.js → chunk-IMKFNOWR-EJT0DATb.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-JWPE2WC7-DKalSWMm.js → chunk-JWPE2WC7-B0T3KRl6.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-POPQ4Y6H-69MmeyRW.js → chunk-POPQ4Y6H-CBoqnL3A.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-SVP7TREG-DCXWXzhU.js → chunk-SVP7TREG-vc6mvklc.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-TICWLB2K-B5nbTEng.js → chunk-TICWLB2K-Co7ACWND.js} +1 -1
- package/runtime/webapp/dist/assets/{chunk-XXDRQBXY-DsJvkUuN.js → chunk-XXDRQBXY-CvByXr6J.js} +1 -1
- package/runtime/webapp/dist/assets/classDiagram-ZZMXUADV-DjmQeVYo.js +1 -0
- package/runtime/webapp/dist/assets/classDiagram-v2-VYDZK3BY-DjmQeVYo.js +1 -0
- package/runtime/webapp/dist/assets/{codeSyntax-DJH88FPL.js → codeSyntax-CEVVRDzr.js} +1 -1
- package/runtime/webapp/dist/assets/{cose-bilkent-JH36ORCC-BamzQ_e_.js → cose-bilkent-JH36ORCC-B6ijVOa6.js} +1 -1
- package/runtime/webapp/dist/assets/{cynefin-OW5HDTMX-BI_j1tbW.js → cynefin-OW5HDTMX-DNC_aald.js} +1 -1
- package/runtime/webapp/dist/assets/{cynefinDiagram-5FMLGOSQ-DuxMrVM2.js → cynefinDiagram-5FMLGOSQ-DMwAVRmf.js} +1 -1
- package/runtime/webapp/dist/assets/{dagre-GXQ25YYZ-Y9_ne36w.js → dagre-GXQ25YYZ-DtA0NmZw.js} +1 -1
- package/runtime/webapp/dist/assets/{diagram-S7CK7UJ4-DIIjO29m.js → diagram-S7CK7UJ4-B7SEmc9A.js} +1 -1
- package/runtime/webapp/dist/assets/{diagram-UQ7AKVKN-C4m2VMIy.js → diagram-UQ7AKVKN-CP_Xy1AP.js} +1 -1
- package/runtime/webapp/dist/assets/{diagram-VSXAHHWV-Cw5aPZ0o.js → diagram-VSXAHHWV-CMq6cPW9.js} +1 -1
- package/runtime/webapp/dist/assets/{diagram-VX7I27RA-W8nmVcOl.js → diagram-VX7I27RA-KIUvVPO4.js} +1 -1
- package/runtime/webapp/dist/assets/{diagram-Z3DM3KII-TksnHlQo.js → diagram-Z3DM3KII-UiMEplzj.js} +1 -1
- package/runtime/webapp/dist/assets/{ebnfDiagram-PWID7BFC-B3WM1b4a.js → ebnfDiagram-PWID7BFC-D221JIeM.js} +1 -1
- package/runtime/webapp/dist/assets/{erDiagram-RLTQ6QDP-BWctPuya.js → erDiagram-RLTQ6QDP-XKInLFA3.js} +1 -1
- package/runtime/webapp/dist/assets/{flowDiagram-HODETNUW-BG5RMrLN.js → flowDiagram-HODETNUW-BeT9GYtM.js} +1 -1
- package/runtime/webapp/dist/assets/{ganttDiagram-EL5Y4UJY-CPcR7Kn_.js → ganttDiagram-EL5Y4UJY-Dk8Uwf7E.js} +1 -1
- package/runtime/webapp/dist/assets/{gitGraphDiagram-WWUBYQGX-Sg6KChEd.js → gitGraphDiagram-WWUBYQGX-DiVf0w01.js} +1 -1
- package/runtime/webapp/dist/assets/{index-tQnPGo1_.js → index-D1I5RKVk.js} +3 -3
- package/runtime/webapp/dist/assets/{infoDiagram-27XIBGKW-BvrtvnrY.js → infoDiagram-27XIBGKW-DpVFXIO9.js} +1 -1
- package/runtime/webapp/dist/assets/{ishikawaDiagram-5VMMS53U-Bz-dZQLJ.js → ishikawaDiagram-5VMMS53U-ChWOHTzh.js} +1 -1
- package/runtime/webapp/dist/assets/{journeyDiagram-3NMN7TZE-Cb4s9jRE.js → journeyDiagram-3NMN7TZE-CTm0xDi4.js} +1 -1
- package/runtime/webapp/dist/assets/{kanban-definition-UXKFOSKX-IjMPUHg9.js → kanban-definition-UXKFOSKX-zVtWbTTX.js} +1 -1
- package/runtime/webapp/dist/assets/{layout-DHjqr0PJ.js → layout-C8tlpprW.js} +1 -1
- package/runtime/webapp/dist/assets/{linear-COctZkFy.js → linear-CArf8t7h.js} +1 -1
- package/runtime/webapp/dist/assets/{mermaid.core-BnonXfHT.js → mermaid.core-BVUH1a8m.js} +5 -5
- package/runtime/webapp/dist/assets/{mindmap-definition-YA3MSWOX-hxBlXJJE.js → mindmap-definition-YA3MSWOX-Cx7RFt-X.js} +1 -1
- package/runtime/webapp/dist/assets/{pegDiagram-XKGWAZYB-D6KJ8Ftm.js → pegDiagram-XKGWAZYB-BvEhVZgX.js} +1 -1
- package/runtime/webapp/dist/assets/{pieDiagram-E7YTZNPT-BzKWZmHN.js → pieDiagram-E7YTZNPT-U0qmhh14.js} +1 -1
- package/runtime/webapp/dist/assets/{quadrantDiagram-AXDQQJYC-ZhiPKt96.js → quadrantDiagram-AXDQQJYC-BbUHWejy.js} +1 -1
- package/runtime/webapp/dist/assets/{railroadDiagram-O6MQD6OU-BtKhiOPG.js → railroadDiagram-O6MQD6OU-SXZBMND2.js} +1 -1
- package/runtime/webapp/dist/assets/{requirementDiagram-BXWQKSXE-OFr_TA8V.js → requirementDiagram-BXWQKSXE-Cv9h1clg.js} +1 -1
- package/runtime/webapp/dist/assets/{sankeyDiagram-P5KCCOFB-qNTDFxAX.js → sankeyDiagram-P5KCCOFB-CSWUhEDJ.js} +1 -1
- package/runtime/webapp/dist/assets/{sequenceDiagram-WJ2MYXX4-B7s5-R4s.js → sequenceDiagram-WJ2MYXX4-Cy7gfOPU.js} +1 -1
- package/runtime/webapp/dist/assets/{sizeCapture-INFHLROL-DdFabbN6.js → sizeCapture-INFHLROL-DEJ8653D.js} +1 -1
- package/runtime/webapp/dist/assets/{stateDiagram-D77RDMKH-CmAN4pX7.js → stateDiagram-D77RDMKH-C9znHj1A.js} +1 -1
- package/runtime/webapp/dist/assets/stateDiagram-v2-MP3YSRHH-D4PTr_9g.js +1 -0
- package/runtime/webapp/dist/assets/{swimlanes-42K2YHIH-BQlIJAhf.js → swimlanes-42K2YHIH-D-1OGDuC.js} +1 -1
- package/runtime/webapp/dist/assets/swimlanesDiagram-VR7AAH4N-BnGs6oif.js +8 -0
- package/runtime/webapp/dist/assets/{timeline-definition-24CTP7MA--Y1zJ9mc.js → timeline-definition-24CTP7MA-fmk9SWID.js} +1 -1
- package/runtime/webapp/dist/assets/{vennDiagram-4TSXK5OY-CUn6FScx.js → vennDiagram-4TSXK5OY-CoJrdpIF.js} +1 -1
- package/runtime/webapp/dist/assets/{wardleyDiagram-VM6X3IG4-Bbz-WBd5.js → wardleyDiagram-VM6X3IG4-CQd2ig95.js} +1 -1
- package/runtime/webapp/dist/assets/{web-DzBEnE5q.js → web-CICIlrjx.js} +1 -1
- package/runtime/webapp/dist/assets/{web-harvpkJj.js → web-DdNvGACS.js} +1 -1
- package/runtime/webapp/dist/assets/{xychartDiagram-S5SC5T6Z-RA9edGdQ.js → xychartDiagram-S5SC5T6Z-Slg0Vzat.js} +1 -1
- package/runtime/webapp/dist/index.html +1 -1
- package/runtime/.next/static/chunks/20.b1b61df1a3c1499c.js +0 -1
- package/runtime/.next/static/chunks/33.4372b0479992e5e4.js +0 -1
- package/runtime/.next/static/chunks/693-41d7c8e27a8513d1.js +0 -1
- package/runtime/.next/static/chunks/app/web/layout-e0dd41d18df8ee9e.js +0 -1
- package/runtime/.next/static/chunks/app/web/page-f9926cbf4c1badd4.js +0 -1
- package/runtime/.next/static/chunks/webpack-d4e32619d2ce6d30.js +0 -1
- package/runtime/webapp/dist/assets/channel-B6S3kcST.js +0 -1
- package/runtime/webapp/dist/assets/classDiagram-ZZMXUADV-CyUA2YaV.js +0 -1
- package/runtime/webapp/dist/assets/classDiagram-v2-VYDZK3BY-CyUA2YaV.js +0 -1
- package/runtime/webapp/dist/assets/stateDiagram-v2-MP3YSRHH-DsxpCqde.js +0 -1
- package/runtime/webapp/dist/assets/swimlanesDiagram-VR7AAH4N-tji3GZVz.js +0 -8
- /package/runtime/.next/static/{BmUH3xLHQfGkG3yzEK37i → 8hkoN4pqLlKY59lLXLBS-}/_buildManifest.js +0 -0
- /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
|
|
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
|
-
|
|
3
|
-
if (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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("
|
|
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 (
|
|
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 {
|
|
84
|
+
timer = setTimeout(tick, 250);
|
|
85
|
+
} catch (error) { stop(); onError(error?.message || "二维码识别失败,请重试。"); }
|
|
60
86
|
};
|
|
61
87
|
tick();
|
|
62
|
-
} catch (error) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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-
|
|
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(
|
|
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":"/
|
|
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
|
|