fix: use bridge canvas host for nodes

This commit is contained in:
Peter Steinberger
2025-12-20 22:24:59 +01:00
parent e53442d983
commit 6a30452b4a
4 changed files with 55 additions and 12 deletions

View File

@@ -34,7 +34,9 @@ describe("canvas host", () => {
}); });
try { try {
const res = await fetch(`http://127.0.0.1:${server.port}/`); const res = await fetch(
`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`,
);
const html = await res.text(); const html = await res.text();
expect(res.status).toBe(200); expect(res.status).toBe(200);
expect(html).toContain("Interactive test page"); expect(html).toContain("Interactive test page");
@@ -111,7 +113,9 @@ describe("canvas host", () => {
}); });
try { try {
const res = await fetch(`http://127.0.0.1:${server.port}/`); const res = await fetch(
`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`,
);
const html = await res.text(); const html = await res.text();
expect(res.status).toBe(200); expect(res.status).toBe(200);
expect(html).toContain("v1"); expect(html).toContain("v1");

View File

@@ -377,7 +377,7 @@ export async function startCanvasHost(
const handler = await createCanvasHostHandler({ const handler = await createCanvasHostHandler({
runtime: opts.runtime, runtime: opts.runtime,
rootDir: opts.rootDir, rootDir: opts.rootDir,
basePath: "/", basePath: CANVAS_HOST_PATH,
allowInTests: opts.allowInTests, allowInTests: opts.allowInTests,
}); });

View File

@@ -1584,7 +1584,7 @@ describe("gateway server", () => {
await new Promise<void>((resolve) => ws.once("open", resolve)); await new Promise<void>((resolve) => ws.once("open", resolve));
const hello = await connectOk(ws, { token: "secret" }); const hello = await connectOk(ws, { token: "secret" });
expect(hello.canvasHostUrl).toBe(`http://100.64.0.1:${port}`); expect(hello.canvasHostUrl).toBe(`http://100.64.0.1:18793`);
ws.close(); ws.close();
await server.close(); await server.close();

View File

@@ -24,7 +24,9 @@ import {
} from "../canvas-host/a2ui.js"; } from "../canvas-host/a2ui.js";
import { import {
type CanvasHostHandler, type CanvasHostHandler,
type CanvasHostServer,
createCanvasHostHandler, createCanvasHostHandler,
startCanvasHost,
} from "../canvas-host/server.js"; } from "../canvas-host/server.js";
import { createDefaultDeps } from "../cli/deps.js"; import { createDefaultDeps } from "../cli/deps.js";
import { agentCommand } from "../commands/agent.js"; import { agentCommand } from "../commands/agent.js";
@@ -395,6 +397,7 @@ const MAX_BUFFERED_BYTES = 1.5 * 1024 * 1024; // per-connection send buffer limi
function deriveCanvasHostUrl( function deriveCanvasHostUrl(
req: IncomingMessage | undefined, req: IncomingMessage | undefined,
canvasPort: number | undefined, canvasPort: number | undefined,
hostOverride?: string,
) { ) {
if (!req || !canvasPort) return undefined; if (!req || !canvasPort) return undefined;
const hostHeader = req.headers.host?.trim(); const hostHeader = req.headers.host?.trim();
@@ -406,8 +409,9 @@ function deriveCanvasHostUrl(
: undefined; : undefined;
const scheme = forwardedProto === "https" ? "https" : "http"; const scheme = forwardedProto === "https" ? "https" : "http";
let host = ""; let host = (hostOverride ?? "").trim();
if (hostHeader) { if (host === "0.0.0.0" || host === "::") host = "";
if (!host && hostHeader) {
try { try {
const parsed = new URL(`http://${hostHeader}`); const parsed = new URL(`http://${hostHeader}`);
host = parsed.hostname; host = parsed.hostname;
@@ -1025,6 +1029,7 @@ export async function startGatewayServer(
} }
let canvasHost: CanvasHostHandler | null = null; let canvasHost: CanvasHostHandler | null = null;
let canvasHostServer: CanvasHostServer | null = null;
if (canvasHostEnabled) { if (canvasHostEnabled) {
try { try {
const handler = await createCanvasHostHandler({ const handler = await createCanvasHostHandler({
@@ -1318,6 +1323,31 @@ export async function startGatewayServer(
return "0.0.0.0"; return "0.0.0.0";
})(); })();
const canvasHostPort = (() => {
const configured = cfgAtStart.canvasHost?.port;
if (typeof configured === "number" && configured > 0) return configured;
return 18793;
})();
if (canvasHostEnabled && bridgeEnabled && bridgeHost) {
try {
const started = await startCanvasHost({
runtime: defaultRuntime,
rootDir: cfgAtStart.canvasHost?.root,
port: canvasHostPort,
listenHost: bridgeHost,
allowInTests: opts.allowCanvasHostInTests,
});
if (started.port > 0) {
canvasHostServer = started;
}
} catch (err) {
logWarn(
`gateway: canvas host failed to start on ${bridgeHost}:${canvasHostPort}: ${String(err)}`,
);
}
}
const bridgeSubscribe = (nodeId: string, sessionKey: string) => { const bridgeSubscribe = (nodeId: string, sessionKey: string) => {
const normalizedNodeId = nodeId.trim(); const normalizedNodeId = nodeId.trim();
const normalizedSessionKey = sessionKey.trim(); const normalizedSessionKey = sessionKey.trim();
@@ -2079,11 +2109,7 @@ export async function startGatewayServer(
}; };
const machineDisplayName = await getMachineDisplayName(); const machineDisplayName = await getMachineDisplayName();
const bridgeHostIsLoopback = bridgeHost ? isLoopbackHost(bridgeHost) : false; const canvasHostPortForBridge = canvasHostServer?.port;
const canvasHostPortForBridge =
canvasHost && (!isLoopbackHost(bindHost) || bridgeHostIsLoopback)
? port
: undefined;
if (bridgeEnabled && bridgePort > 0 && bridgeHost) { if (bridgeEnabled && bridgePort > 0 && bridgeHost) {
try { try {
@@ -2381,9 +2407,15 @@ export async function startGatewayServer(
const remoteAddr = ( const remoteAddr = (
socket as WebSocket & { _socket?: { remoteAddress?: string } } socket as WebSocket & { _socket?: { remoteAddress?: string } }
)._socket?.remoteAddress; )._socket?.remoteAddress;
const canvasHostPortForWs = canvasHostServer?.port ?? (canvasHost ? port : undefined);
const canvasHostOverride =
bridgeHost && bridgeHost !== "0.0.0.0" && bridgeHost !== "::"
? bridgeHost
: undefined;
const canvasHostUrl = deriveCanvasHostUrl( const canvasHostUrl = deriveCanvasHostUrl(
req, req,
canvasHost ? port : undefined, canvasHostPortForWs,
canvasHostServer ? canvasHostOverride : undefined,
); );
logWs("in", "open", { connId, remoteAddr }); logWs("in", "open", { connId, remoteAddr });
const isWebchatConnect = (params: ConnectParams | null | undefined) => const isWebchatConnect = (params: ConnectParams | null | undefined) =>
@@ -4398,6 +4430,13 @@ export async function startGatewayServer(
/* ignore */ /* ignore */
} }
} }
if (canvasHostServer) {
try {
await canvasHostServer.close();
} catch {
/* ignore */
}
}
if (bridge) { if (bridge) {
try { try {
await bridge.close(); await bridge.close();