fix(qa): restore embedded control ui gateway startup

This commit is contained in:
Peter Steinberger 2026-04-05 21:49:01 +01:00
parent 8e1c81e707
commit b5fc435bd5
No known key found for this signature in database
5 changed files with 41 additions and 14 deletions

View File

@ -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",
},
});

View File

@ -1 +1,2 @@
export * from "./src/runtime-api.js";
export * from "./src/runtime.js";

View File

@ -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,

View File

@ -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("<!doctype html><title>control-ui</title><h1>Control UI</h1>");
});
await new Promise<void>((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");
});

View File

@ -150,6 +150,22 @@ function rewriteControlUiProxyPath(pathname: string, search: string) {
return `${stripped}${search}`;
}
function rewriteEmbeddedControlUiHeaders(
headers: IncomingMessage["headers"],
): Record<string, string | string[] | number | undefined> {
const rewritten: Record<string, string | string[] | number | undefined> = { ...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);
},
);