mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-13 06:03:39 -06:00
b363d5a293
* feat(linux): canvas via CLI-node + Tauri app IPC bridge * refactor: extract gateway helper modules * build(linux-canvas): register plugin package in lockfile * fix(linux-canvas): move canvas advertise test out of core, regen docs/protocol/deadcode * fix(gateway): break node-catalog/registry import cycle via leaf normalize module; add canvas glossary term * style: oxfmt invoke.ts and runtime.ts after buildNodeEventParams extraction * fix(linux): load Canvas WebView via dedicated data_directory context Wry's Linux/WebKitGTK incognito mode discards Tauri's registered WebContext (wry webkitgtk/mod.rs), so the Canvas window got a fresh ephemeral context without the openclaw-canvas:// scheme handler — the bundled A2UI page never committed (stayed about:blank) and every A2UI command timed out. Use an isolated cache-backed data_directory instead, which keeps the protocol handler while still isolating Canvas storage from the dashboard window. * fix(linux): keep Canvas WebView ephemeral via incognito + data_directory Autoreview flagged that a dedicated data_directory alone persists Canvas browser state (cookies, localStorage, IndexedDB, service workers) across restarts, so an agent that navigates Canvas to a site could leak an authenticated session into a later session. iOS uses a non-persistent store; Linux should match. Add .incognito(true) alongside .data_directory(): the distinct directory gives Tauri a fresh WebContext key so it still attaches the openclaw-canvas:// protocol closure, and incognito makes Wry swap in a fresh *ephemeral* context carrying those protocols. Live-verified on a Wayland/WebKitGTK box: the bundled page still loads (location.href=openclaw-canvas://localhost/index.html, openclawA2UI present, A2UI renders) and the canvas-webview dir holds no persistent cookie/storage files.
35 lines
1.2 KiB
TypeScript
35 lines
1.2 KiB
TypeScript
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
|
|
|
function normalizeBrowserProxyPath(value: string): string {
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return trimmed;
|
|
}
|
|
const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
if (withLeadingSlash.length <= 1) {
|
|
return withLeadingSlash;
|
|
}
|
|
return withLeadingSlash.replace(/\/+$/, "");
|
|
}
|
|
|
|
function isPersistentBrowserProxyMutation(method: string, path: string): boolean {
|
|
const normalizedPath = normalizeBrowserProxyPath(path);
|
|
if (
|
|
method === "POST" &&
|
|
(normalizedPath === "/profiles/create" || normalizedPath === "/reset-profile")
|
|
) {
|
|
return true;
|
|
}
|
|
return method === "DELETE" && /^\/profiles\/[^/]+$/.test(normalizedPath);
|
|
}
|
|
|
|
export function isForbiddenBrowserProxyMutation(params: unknown): boolean {
|
|
if (!params || typeof params !== "object") {
|
|
return false;
|
|
}
|
|
const candidate = params as { method?: unknown; path?: unknown };
|
|
const method = (normalizeOptionalString(candidate.method) ?? "").toUpperCase();
|
|
const path = normalizeOptionalString(candidate.path) ?? "";
|
|
return Boolean(method && path && isPersistentBrowserProxyMutation(method, path));
|
|
}
|