Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions apps/server/src/openclawGatewayTest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,32 @@ type GatewayRequestFrame = {
type?: unknown;
id?: unknown;
method?: unknown;
params?: {
client?: {
id?: unknown;
displayName?: unknown;
mode?: unknown;
deviceFamily?: unknown;
};
auth?: {
password?: unknown;
token?: unknown;
deviceToken?: unknown;
};
device?: {
id?: unknown;
publicKey?: unknown;
signature?: unknown;
signedAt?: unknown;
nonce?: unknown;
};
};
};

function isBase64Url(value: unknown): value is string {
return typeof value === "string" && /^[A-Za-z0-9_-]+$/.test(value);
}

afterEach(async () => {
await Promise.all(
[...servers].map(
Expand Down Expand Up @@ -82,11 +106,14 @@ describe("runOpenclawGatewayTest", () => {
});

it("passes when the modern connect handshake succeeds", async () => {
let connectParams: GatewayRequestFrame["params"];

const gateway = await createGatewayServer((socket) => {
sendChallenge(socket);
socket.on("message", (data) => {
const message = JSON.parse(data.toString()) as GatewayRequestFrame;
if (message.type === "req" && message.method === "connect") {
connectParams = message.params;
socket.send(
JSON.stringify({
type: "res",
Expand All @@ -108,6 +135,21 @@ describe("runOpenclawGatewayTest", () => {
expect(result.steps.find((step) => step.name === "WebSocket connect")?.status).toBe("pass");
expect(result.steps.find((step) => step.name === "Gateway handshake")?.status).toBe("pass");
expect(result.diagnostics?.observedNotifications).toContain("connect.challenge");

expect(connectParams?.client?.id).toBe("gateway-client");
expect(connectParams?.client?.mode).toBe("backend");
expect(connectParams?.client?.displayName).toBe("OK Code gateway test");
expect(connectParams?.client?.deviceFamily).toBe("server");
expect(connectParams?.auth?.password).toBe("topsecret");
expect(connectParams?.auth?.token).toBeUndefined();
expect(connectParams?.auth?.deviceToken).toBeUndefined();
expect(connectParams?.device?.id).toMatch(/^[a-f0-9]{64}$/);
expect(connectParams?.device?.id).not.toMatch(/^device_/);
expect(isBase64Url(connectParams?.device?.publicKey)).toBe(true);
expect(String(connectParams?.device?.publicKey)).not.toContain("BEGIN");
expect(isBase64Url(connectParams?.device?.signature)).toBe(true);
expect(connectParams?.device?.nonce).toBe("nonce-123");
expect(typeof connectParams?.device?.signedAt).toBe("number");
});

it("reports pairing-required detail codes from the connect handshake", async () => {
Expand Down
23 changes: 20 additions & 3 deletions apps/server/src/openclawGatewayTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import type {
TestOpenclawGatewayStepStatus,
} from "@okcode/contracts";
import { serverBuildInfo } from "./buildInfo.ts";
import { connectOpenClawGateway } from "./provider/Layers/OpenClawGatewayClient.ts";
import {
OPENCLAW_GATEWAY_CLIENT_IDS,
OPENCLAW_GATEWAY_CLIENT_MODES,
connectOpenClawGateway,
} from "./provider/Layers/OpenClawGatewayClient.ts";

const OPENCLAW_TEST_CONNECT_TIMEOUT_MS = 10_000;
const OPENCLAW_TEST_RPC_TIMEOUT_MS = 10_000;
Expand Down Expand Up @@ -371,6 +375,17 @@ function buildHints(
);
}

if (
errorLower.includes("/client/id") ||
errorLower.includes("/client/mode") ||
errorLower.includes("client id") ||
errorLower.includes("client mode")
) {
hints.push(
"The gateway rejected the advertised client identity. That usually means the gateway expects a newer OpenClaw `connect.params.client` allowlist than this OK Code build is using.",
);
}

if (
diagnostics.hostKind === "tailscale" &&
(detailCode === "PAIRING_REQUIRED" ||
Expand Down Expand Up @@ -523,15 +538,17 @@ export async function runOpenclawGatewayTest(
role: "operator",
scopes: [...OPENCLAW_OPERATOR_SCOPES],
client: {
id: "okcode",
id: OPENCLAW_GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
displayName: "OK Code gateway test",
version: serverBuildInfo.version,
platform:
process.platform === "darwin"
? "macos"
: process.platform === "win32"
? "windows"
: process.platform,
mode: "operator",
deviceFamily: "server",
mode: OPENCLAW_GATEWAY_CLIENT_MODES.BACKEND,
},
userAgent: `okcode/${serverBuildInfo.version}`,
locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
Expand Down
Loading
Loading