diff --git a/extensions/qa-channel/index.ts b/extensions/qa-channel/index.ts index f444df63c19..b9cc59f5e72 100644 --- a/extensions/qa-channel/index.ts +++ b/extensions/qa-channel/index.ts @@ -1,15 +1,16 @@ -import type { ChannelPlugin } from "openclaw/plugin-sdk/core"; -import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; -import { qaChannelPlugin } from "./src/channel.js"; -import { setQaChannelRuntime } from "./src/runtime.js"; +import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract"; -export { qaChannelPlugin } from "./src/channel.js"; -export { setQaChannelRuntime } from "./src/runtime.js"; - -export default defineChannelPluginEntry({ +export default defineBundledChannelEntry({ id: "qa-channel", name: "QA Channel", description: "Synthetic QA channel plugin", - plugin: qaChannelPlugin as ChannelPlugin, - setRuntime: setQaChannelRuntime, + importMetaUrl: import.meta.url, + plugin: { + specifier: "./api.js", + exportName: "qaChannelPlugin", + }, + runtime: { + specifier: "./runtime-api.js", + exportName: "setQaChannelRuntime", + }, }); diff --git a/extensions/qa-channel/runtime-api.ts b/extensions/qa-channel/runtime-api.ts index 801051438fb..0548753cc4f 100644 --- a/extensions/qa-channel/runtime-api.ts +++ b/extensions/qa-channel/runtime-api.ts @@ -1 +1,2 @@ export * from "./src/runtime-api.js"; +export * from "./src/runtime.js"; diff --git a/extensions/qa-channel/src/channel.ts b/extensions/qa-channel/src/channel.ts index cbe9e6e45e6..ea941c59d79 100644 --- a/extensions/qa-channel/src/channel.ts +++ b/extensions/qa-channel/src/channel.ts @@ -1,8 +1,8 @@ import { buildChannelOutboundSessionRoute, createChatChannelPlugin, - getChatChannelMeta, -} from "openclaw/plugin-sdk/core"; +} from "openclaw/plugin-sdk/channel-core"; +import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common"; import { DEFAULT_ACCOUNT_ID, listQaChannelAccountIds, diff --git a/extensions/qa-lab/src/lab-server.test.ts b/extensions/qa-lab/src/lab-server.test.ts index 0e8544c2881..3250696d2c1 100644 --- a/extensions/qa-lab/src/lab-server.test.ts +++ b/extensions/qa-lab/src/lab-server.test.ts @@ -122,7 +122,11 @@ describe("qa-lab server", () => { res.end(JSON.stringify({ ok: true, status: "live" })); return; } - res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); + res.writeHead(200, { + "content-type": "text/html; charset=utf-8", + "x-frame-options": "DENY", + "content-security-policy": "default-src 'self'; frame-ancestors 'none';", + }); res.end("control-ui

Control UI

"); }); await new Promise((resolve, reject) => { @@ -168,6 +172,8 @@ describe("qa-lab server", () => { const rootResponse = await fetch(`${lab.listenUrl}/control-ui/`); expect(rootResponse.status).toBe(200); + expect(rootResponse.headers.get("x-frame-options")).toBeNull(); + expect(rootResponse.headers.get("content-security-policy")).toContain("frame-ancestors 'self'"); expect(await rootResponse.text()).toContain("Control UI"); }); diff --git a/extensions/qa-lab/src/lab-server.ts b/extensions/qa-lab/src/lab-server.ts index 001458c94dc..09553528bb6 100644 --- a/extensions/qa-lab/src/lab-server.ts +++ b/extensions/qa-lab/src/lab-server.ts @@ -150,6 +150,22 @@ function rewriteControlUiProxyPath(pathname: string, search: string) { return `${stripped}${search}`; } +function rewriteEmbeddedControlUiHeaders( + headers: IncomingMessage["headers"], +): Record { + const rewritten: Record = { ...headers }; + delete rewritten["x-frame-options"]; + + const csp = headers["content-security-policy"]; + if (typeof csp === "string") { + rewritten["content-security-policy"] = csp.includes("frame-ancestors") + ? csp.replace(/frame-ancestors\s+[^;]+/i, "frame-ancestors 'self'") + : `${csp}; frame-ancestors 'self'`; + } + + return rewritten; +} + async function proxyHttpRequest(params: { req: IncomingMessage; res: ServerResponse; @@ -171,7 +187,10 @@ async function proxyHttpRequest(params: { }, }, (upstreamRes) => { - params.res.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.headers); + params.res.writeHead( + upstreamRes.statusCode ?? 502, + rewriteEmbeddedControlUiHeaders(upstreamRes.headers), + ); upstreamRes.pipe(params.res); }, );