mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-23 13:41:12 +00:00
* 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.
45 lines
1.5 KiB
TypeScript
45 lines
1.5 KiB
TypeScript
import fs from "node:fs";
|
|
import net from "node:net";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { linuxCanvasSocketExists } from "./socket-path.js";
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const dir of tempDirs.splice(0)) {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
describe("Linux Canvas socket availability", () => {
|
|
it.runIf(process.platform === "linux")(
|
|
"requires a live, user-only socket instead of a stale inode or symlink",
|
|
async () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-linux-canvas-path-"));
|
|
tempDirs.push(dir);
|
|
const socketPath = path.join(dir, "canvas.sock");
|
|
const symlinkPath = path.join(dir, "canvas-link.sock");
|
|
const server = net.createServer();
|
|
await new Promise<void>((resolve, reject) => {
|
|
server.once("error", reject);
|
|
server.listen(socketPath, resolve);
|
|
});
|
|
fs.chmodSync(socketPath, 0o600);
|
|
|
|
expect(linuxCanvasSocketExists(socketPath)).toBe(true);
|
|
fs.symlinkSync(socketPath, symlinkPath);
|
|
expect(linuxCanvasSocketExists(symlinkPath)).toBe(false);
|
|
fs.chmodSync(socketPath, 0o666);
|
|
expect(linuxCanvasSocketExists(socketPath)).toBe(false);
|
|
fs.chmodSync(socketPath, 0o600);
|
|
|
|
await new Promise<void>((resolve) => {
|
|
server.close(() => resolve());
|
|
});
|
|
expect(linuxCanvasSocketExists(socketPath)).toBe(false);
|
|
},
|
|
);
|
|
});
|