fix: stabilize gateway ws + iOS
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { WebSocket } from "ws";
|
||||
import { WebSocket, type ClientOptions, type CertMeta } from "ws";
|
||||
import { rawDataToString } from "../infra/ws.js";
|
||||
import { logDebug, logError } from "../logger.js";
|
||||
import type { DeviceIdentity } from "../infra/device-identity.js";
|
||||
@@ -85,18 +85,21 @@ export class GatewayClient {
|
||||
if (this.closed) return;
|
||||
const url = this.opts.url ?? "ws://127.0.0.1:18789";
|
||||
// Allow node screen snapshots and other large responses.
|
||||
const wsOptions: ConstructorParameters<typeof WebSocket>[1] = {
|
||||
const wsOptions: ClientOptions = {
|
||||
maxPayload: 25 * 1024 * 1024,
|
||||
};
|
||||
if (url.startsWith("wss://") && this.opts.tlsFingerprint) {
|
||||
wsOptions.rejectUnauthorized = false;
|
||||
wsOptions.checkServerIdentity = (_host, cert) => {
|
||||
wsOptions.checkServerIdentity = (_host: string, cert: CertMeta) => {
|
||||
const fingerprintValue =
|
||||
typeof cert === "object" && cert && "fingerprint256" in cert
|
||||
? (cert as { fingerprint256?: string }).fingerprint256 ?? ""
|
||||
: "";
|
||||
const fingerprint = normalizeFingerprint(
|
||||
typeof cert?.fingerprint256 === "string" ? cert.fingerprint256 : "",
|
||||
typeof fingerprintValue === "string" ? fingerprintValue : "",
|
||||
);
|
||||
const expected = normalizeFingerprint(this.opts.tlsFingerprint ?? "");
|
||||
if (fingerprint && fingerprint === expected) return undefined;
|
||||
return new Error("gateway tls fingerprint mismatch");
|
||||
return Boolean(fingerprint && fingerprint === expected);
|
||||
};
|
||||
}
|
||||
this.ws = new WebSocket(url, wsOptions);
|
||||
|
||||
@@ -119,7 +119,7 @@ export class NodeRegistry {
|
||||
timeoutMs: params.timeoutMs,
|
||||
idempotencyKey: params.idempotencyKey,
|
||||
};
|
||||
const ok = this.sendEvent(node, "node.invoke.request", payload);
|
||||
const ok = this.sendEventToSession(node, "node.invoke.request", payload);
|
||||
if (!ok) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -172,7 +172,7 @@ export class NodeRegistry {
|
||||
return this.sendEventToSession(node, event, payload);
|
||||
}
|
||||
|
||||
private sendEvent(node: NodeSession, event: string, payload: unknown): boolean {
|
||||
private sendEventInternal(node: NodeSession, event: string, payload: unknown): boolean {
|
||||
try {
|
||||
node.client.socket.send(
|
||||
JSON.stringify({
|
||||
@@ -188,6 +188,6 @@ export class NodeRegistry {
|
||||
}
|
||||
|
||||
private sendEventToSession(node: NodeSession, event: string, payload: unknown): boolean {
|
||||
return this.sendEvent(node, event, payload);
|
||||
return this.sendEventInternal(node, event, payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +451,6 @@ export const nodeHandlers: GatewayRequestHandlers = {
|
||||
nodeContext,
|
||||
"node",
|
||||
{
|
||||
type: "event",
|
||||
event: p.event,
|
||||
payloadJSON,
|
||||
},
|
||||
|
||||
@@ -356,13 +356,15 @@ export async function startGatewayServer(
|
||||
const execApprovalManager = new ExecApprovalManager();
|
||||
const execApprovalHandlers = createExecApprovalHandlers(execApprovalManager);
|
||||
|
||||
const canvasHostServerPort = (canvasHostServer as CanvasHostServer | null)?.port;
|
||||
|
||||
attachGatewayWsHandlers({
|
||||
wss,
|
||||
clients,
|
||||
port,
|
||||
gatewayHost: bindHost ?? undefined,
|
||||
canvasHostEnabled: Boolean(canvasHost),
|
||||
canvasHostServerPort: canvasHostServer?.port ?? undefined,
|
||||
canvasHostServerPort,
|
||||
resolvedAuth,
|
||||
gatewayMethods,
|
||||
events: GATEWAY_EVENTS,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
rpcReq,
|
||||
startServerWithClient,
|
||||
} from "./test-helpers.js";
|
||||
import { GATEWAY_CLIENT_MODES } from "../utils/message-channel.js";
|
||||
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
||||
|
||||
installGatewayTestHooks();
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("gateway server models + voicewake", () => {
|
||||
await connectOk(nodeWs, {
|
||||
role: "node",
|
||||
client: {
|
||||
id: "n1",
|
||||
id: GATEWAY_CLIENT_NAMES.NODE_HOST,
|
||||
version: "1.0.0",
|
||||
platform: "ios",
|
||||
mode: GATEWAY_CLIENT_MODES.NODE,
|
||||
|
||||
@@ -32,6 +32,7 @@ describe("sessions_send gateway loopback", () => {
|
||||
it("returns reply when lifecycle ends before agent.wait", async () => {
|
||||
const port = await getFreePort();
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_PORT", String(port));
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "test-token");
|
||||
|
||||
const server = await startGatewayServer(port);
|
||||
const spy = vi.mocked(agentCommand);
|
||||
@@ -105,6 +106,7 @@ describe("sessions_send label lookup", () => {
|
||||
it("finds session by label and sends message", { timeout: 60_000 }, async () => {
|
||||
const port = await getFreePort();
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_PORT", String(port));
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "test-token");
|
||||
|
||||
const server = await startGatewayServer(port);
|
||||
servers.push(server);
|
||||
@@ -171,6 +173,7 @@ describe("sessions_send label lookup", () => {
|
||||
it("returns error when label not found", { timeout: 60_000 }, async () => {
|
||||
const port = await getFreePort();
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_PORT", String(port));
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "test-token");
|
||||
|
||||
const server = await startGatewayServer(port);
|
||||
servers.push(server);
|
||||
@@ -191,6 +194,7 @@ describe("sessions_send label lookup", () => {
|
||||
it("returns error when neither sessionKey nor label provided", { timeout: 60_000 }, async () => {
|
||||
const port = await getFreePort();
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_PORT", String(port));
|
||||
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "test-token");
|
||||
|
||||
const server = await startGatewayServer(port);
|
||||
servers.push(server);
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
loadGatewayTlsRuntime as loadGatewayTlsRuntimeConfig,
|
||||
} from "../../infra/tls/gateway.js";
|
||||
|
||||
export type { GatewayTlsRuntime } from "../../infra/tls/gateway.js";
|
||||
|
||||
export async function loadGatewayTlsRuntime(
|
||||
cfg: GatewayTlsConfig | undefined,
|
||||
log?: { info?: (msg: string) => void; warn?: (msg: string) => void },
|
||||
|
||||
Reference in New Issue
Block a user