






















@@ -4,13 +4,16 @@ import path from "node:path";
44import { fileURLToPath } from "node:url";
55import { detectMime } from "../media/mime.js";
66import { lowercasePreservingWhitespace } from "../shared/string-coerce.js";
7+import { A2UI_PATH, injectCanvasLiveReload, isA2uiPath } from "./a2ui-shared.js";
78import { resolveFileWithinRoot } from "./file-resolver.js";
899-export const A2UI_PATH = "/__openclaw__/a2ui";
10-11-export const CANVAS_HOST_PATH = "/__openclaw__/canvas";
12-13-export const CANVAS_WS_PATH = "/__openclaw__/ws";
10+export {
11+A2UI_PATH,
12+CANVAS_HOST_PATH,
13+CANVAS_WS_PATH,
14+injectCanvasLiveReload,
15+isA2uiPath,
16+} from "./a2ui-shared.js";
14171518let cachedA2uiRootReal: string | null | undefined;
1619let resolvingA2uiRoot: Promise<string | null> | null = null;
@@ -79,67 +82,6 @@ async function resolveA2uiRootReal(): Promise<string | null> {
7982return resolvingA2uiRoot;
8083}
818482-export function injectCanvasLiveReload(html: string): string {
83-const snippet = `
84-<script>
85-(() => {
86- // Cross-platform action bridge helper.
87- // Works on:
88- // - iOS: window.webkit.messageHandlers.openclawCanvasA2UIAction.postMessage(...)
89- // - Android: window.openclawCanvasA2UIAction.postMessage(...)
90- const handlerNames = ["openclawCanvasA2UIAction"];
91- function postToNode(payload) {
92- try {
93- const raw = typeof payload === "string" ? payload : JSON.stringify(payload);
94- for (const name of handlerNames) {
95- const iosHandler = globalThis.webkit?.messageHandlers?.[name];
96- if (iosHandler && typeof iosHandler.postMessage === "function") {
97- iosHandler.postMessage(raw);
98- return true;
99- }
100- const androidHandler = globalThis[name];
101- if (androidHandler && typeof androidHandler.postMessage === "function") {
102- // Important: call as a method on the interface object (binding matters on Android WebView).
103- androidHandler.postMessage(raw);
104- return true;
105- }
106- }
107- } catch {}
108- return false;
109- }
110- function sendUserAction(userAction) {
111- const id =
112- (userAction && typeof userAction.id === "string" && userAction.id.trim()) ||
113- (globalThis.crypto?.randomUUID?.() ?? String(Date.now()));
114- const action = { ...userAction, id };
115- return postToNode({ userAction: action });
116- }
117- globalThis.OpenClaw = globalThis.OpenClaw ?? {};
118- globalThis.OpenClaw.postMessage = postToNode;
119- globalThis.OpenClaw.sendUserAction = sendUserAction;
120- globalThis.openclawPostMessage = postToNode;
121- globalThis.openclawSendUserAction = sendUserAction;
122-123- try {
124- const cap = new URLSearchParams(location.search).get("oc_cap");
125- const proto = location.protocol === "https:" ? "wss" : "ws";
126- const capQuery = cap ? "?oc_cap=" + encodeURIComponent(cap) : "";
127- const ws = new WebSocket(proto + "://" + location.host + ${JSON.stringify(CANVAS_WS_PATH)} + capQuery);
128- ws.onmessage = (ev) => {
129- if (String(ev.data || "") === "reload") location.reload();
130- };
131- } catch {}
132-})();
133-</script>
134-`.trim();
135-136-const idx = lowercasePreservingWhitespace(html).lastIndexOf("</body>");
137-if (idx >= 0) {
138-return `${html.slice(0, idx)}\n${snippet}\n${html.slice(idx)}`;
139-}
140-return `${html}\n${snippet}\n`;
141-}
142-14385export async function handleA2uiHttpRequest(
14486req: IncomingMessage,
14587res: ServerResponse,
@@ -150,8 +92,7 @@ export async function handleA2uiHttpRequest(
15092}
1519315294const url = new URL(urlRaw, "http://localhost");
153-const basePath =
154-url.pathname === A2UI_PATH || url.pathname.startsWith(`${A2UI_PATH}/`) ? A2UI_PATH : undefined;
95+const basePath = isA2uiPath(url.pathname) ? A2UI_PATH : undefined;
15596if (!basePath) {
15697return false;
15798}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。