mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor(gateway): consolidate client contracts (#101012)
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type {
|
||||
ConnectParams,
|
||||
ErrorShape,
|
||||
EventFrame,
|
||||
HelloOk,
|
||||
RequestFrame,
|
||||
ResponseFrame,
|
||||
} from "@openclaw/gateway-protocol";
|
||||
import {
|
||||
GATEWAY_CLIENT_MODES,
|
||||
@@ -21,6 +21,10 @@ import {
|
||||
readPairingConnectErrorDetails,
|
||||
type ConnectErrorRecoveryAdvice,
|
||||
} from "@openclaw/gateway-protocol/connect-error-details";
|
||||
import {
|
||||
isGatewayEventFrame,
|
||||
isGatewayResponseFrame,
|
||||
} from "@openclaw/gateway-protocol/frame-guards";
|
||||
import { resolveGatewayStartupRetryAfterMs } from "@openclaw/gateway-protocol/startup-unavailable";
|
||||
import { MIN_CLIENT_PROTOCOL_VERSION, PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
|
||||
import ipaddr from "ipaddr.js";
|
||||
@@ -78,63 +82,6 @@ function normalizeOptionalString(value: unknown): string | undefined {
|
||||
return trimmed || undefined;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function isNonEmptyString(value: unknown): value is string {
|
||||
return typeof value === "string" && value.length > 0;
|
||||
}
|
||||
|
||||
function isNonNegativeInteger(value: unknown): value is number {
|
||||
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
||||
}
|
||||
|
||||
function isGatewayClientErrorShape(value: unknown): boolean {
|
||||
if (!isRecord(value)) {
|
||||
return false;
|
||||
}
|
||||
if (!isNonEmptyString(value.code) || !isNonEmptyString(value.message)) {
|
||||
return false;
|
||||
}
|
||||
if (value.retryable !== undefined && typeof value.retryable !== "boolean") {
|
||||
return false;
|
||||
}
|
||||
if (value.retryAfterMs !== undefined && !isNonNegativeInteger(value.retryAfterMs)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isGatewayEventFrame(value: unknown): value is EventFrame {
|
||||
if (!isRecord(value) || value.type !== "event" || !isNonEmptyString(value.event)) {
|
||||
return false;
|
||||
}
|
||||
return value.seq === undefined || isNonNegativeInteger(value.seq);
|
||||
}
|
||||
|
||||
function isGatewayResponseFrame(value: unknown): value is ResponseFrame {
|
||||
if (
|
||||
!isRecord(value) ||
|
||||
value.type !== "res" ||
|
||||
!isNonEmptyString(value.id) ||
|
||||
typeof value.ok !== "boolean"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return value.error === undefined || isGatewayClientErrorShape(value.error);
|
||||
}
|
||||
|
||||
function validateClientRequestFrame(frame: RequestFrame): string | null {
|
||||
if (!isNonEmptyString(frame.id)) {
|
||||
return "id must be a non-empty string";
|
||||
}
|
||||
if (!isNonEmptyString(frame.method)) {
|
||||
return "method must be a non-empty string";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeLowercaseStringOrEmpty(value: unknown): string {
|
||||
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
||||
}
|
||||
@@ -314,14 +261,6 @@ export type GatewayClientRequestOptions = {
|
||||
onAccepted?: (payload: unknown) => void;
|
||||
};
|
||||
|
||||
type GatewayClientErrorShape = {
|
||||
code?: string;
|
||||
message?: string;
|
||||
details?: unknown;
|
||||
retryable?: boolean;
|
||||
retryAfterMs?: number;
|
||||
};
|
||||
|
||||
type SelectedConnectAuth = {
|
||||
authToken?: string;
|
||||
authBootstrapToken?: string;
|
||||
@@ -376,7 +315,7 @@ export class GatewayClientRequestError extends Error {
|
||||
readonly retryable: boolean;
|
||||
readonly retryAfterMs?: number;
|
||||
|
||||
constructor(error: GatewayClientErrorShape) {
|
||||
constructor(error: Partial<ErrorShape>) {
|
||||
super(formatConnectErrorMessage({ message: error.message, details: error.details }));
|
||||
this.name = "GatewayClientRequestError";
|
||||
this.gatewayCode = error.code ?? "UNAVAILABLE";
|
||||
@@ -461,6 +400,13 @@ export type GatewayClientOptions = {
|
||||
onGap?: (info: { expected: number; received: number }) => void;
|
||||
};
|
||||
|
||||
export type GatewayClientConnectionMetadata = {
|
||||
clientName?: GatewayClientName;
|
||||
hasDeviceIdentity: boolean;
|
||||
mode?: GatewayClientMode;
|
||||
preauthHandshakeTimeoutMs?: number;
|
||||
};
|
||||
|
||||
export const GATEWAY_CLOSE_CODE_HINTS: Readonly<Record<number, string>> = {
|
||||
1000: "normal closure",
|
||||
1006: "abnormal closure (no close frame)",
|
||||
@@ -595,6 +541,15 @@ export class GatewayClient {
|
||||
: 30_000;
|
||||
}
|
||||
|
||||
getConnectionMetadata(): GatewayClientConnectionMetadata {
|
||||
return {
|
||||
clientName: this.opts.clientName,
|
||||
hasDeviceIdentity: Boolean(this.opts.deviceIdentity),
|
||||
mode: this.opts.mode,
|
||||
preauthHandshakeTimeoutMs: this.opts.preauthHandshakeTimeoutMs,
|
||||
};
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
@@ -1639,12 +1594,11 @@ export class GatewayClient {
|
||||
if (opts?.signal?.aborted) {
|
||||
throw createGatewayRequestAbortError(method);
|
||||
}
|
||||
if (typeof method !== "string" || method.length === 0) {
|
||||
throw new Error("invalid request frame: method must be a non-empty string");
|
||||
}
|
||||
const id = randomUUID();
|
||||
const frame: RequestFrame = { type: "req", id, method, params };
|
||||
const requestFrameError = validateClientRequestFrame(frame);
|
||||
if (requestFrameError) {
|
||||
throw new Error(`invalid request frame: ${requestFrameError}`);
|
||||
}
|
||||
const expectFinal = opts?.expectFinal === true;
|
||||
const timeoutMs =
|
||||
opts?.timeoutMs === null
|
||||
|
||||
@@ -149,6 +149,26 @@ describe("GatewayClient", () => {
|
||||
).toBe(6_000);
|
||||
});
|
||||
|
||||
test("returns non-sensitive connection metadata", () => {
|
||||
const client = new GatewayClient({
|
||||
clientName: "cli",
|
||||
mode: "backend",
|
||||
preauthHandshakeTimeoutMs: 30_000,
|
||||
deviceIdentity: {
|
||||
deviceId: "device-1",
|
||||
privateKeyPem: "private-key",
|
||||
publicKeyPem: "public-key",
|
||||
},
|
||||
});
|
||||
|
||||
expect(client.getConnectionMetadata()).toEqual({
|
||||
clientName: "cli",
|
||||
hasDeviceIdentity: true,
|
||||
mode: "backend",
|
||||
preauthHandshakeTimeoutMs: 30_000,
|
||||
});
|
||||
});
|
||||
|
||||
test("closes on missing ticks", async () => {
|
||||
const port = await getFreePort();
|
||||
wss = new WebSocketServer({ port, host: "127.0.0.1" });
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
"import": "./dist/connect-error-details.mjs",
|
||||
"default": "./dist/connect-error-details.mjs"
|
||||
},
|
||||
"./frame-guards": {
|
||||
"types": "./dist/frame-guards.d.mts",
|
||||
"import": "./dist/frame-guards.mjs",
|
||||
"default": "./dist/frame-guards.mjs"
|
||||
},
|
||||
"./schema": {
|
||||
"types": "./dist/schema.d.mts",
|
||||
"import": "./dist/schema.mjs",
|
||||
@@ -41,7 +46,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown src/index.ts src/client-info.ts src/connect-error-details.ts src/schema.ts src/startup-unavailable.ts src/version.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
|
||||
"build": "tsdown src/index.ts src/client-info.ts src/connect-error-details.ts src/frame-guards.ts src/schema.ts src/startup-unavailable.ts src/version.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"typebox": "1.3.3"
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isGatewayEventFrame, isGatewayResponseFrame } from "./frame-guards.js";
|
||||
|
||||
describe("gateway frame guards", () => {
|
||||
it("accepts additive event fields while validating dispatch fields", () => {
|
||||
expect(
|
||||
isGatewayEventFrame({
|
||||
type: "event",
|
||||
event: "tick",
|
||||
seq: 0,
|
||||
payload: { future: true },
|
||||
futureEnvelopeField: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(isGatewayEventFrame({ type: "event", event: "", seq: 0 })).toBe(false);
|
||||
expect(isGatewayEventFrame({ type: "event", event: "tick", seq: -1 })).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts additive response fields while validating errors", () => {
|
||||
expect(
|
||||
isGatewayResponseFrame({
|
||||
type: "res",
|
||||
id: "request-1",
|
||||
ok: false,
|
||||
error: {
|
||||
code: "UNAVAILABLE",
|
||||
message: "try later",
|
||||
retryable: true,
|
||||
retryAfterMs: 10,
|
||||
},
|
||||
futureEnvelopeField: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isGatewayResponseFrame({
|
||||
type: "res",
|
||||
id: "request-1",
|
||||
ok: false,
|
||||
error: { code: "UNAVAILABLE", message: "", retryAfterMs: 10 },
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isGatewayResponseFrame({
|
||||
type: "res",
|
||||
id: "request-1",
|
||||
ok: false,
|
||||
error: { code: "UNAVAILABLE", message: "try later", retryAfterMs: -1 },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { EventFrame, ResponseFrame } from "./schema/types.js";
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function isNonEmptyString(value: unknown): value is string {
|
||||
return typeof value === "string" && value.length > 0;
|
||||
}
|
||||
|
||||
function isNonNegativeInteger(value: unknown): value is number {
|
||||
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
||||
}
|
||||
|
||||
function isGatewayErrorShape(value: unknown): boolean {
|
||||
if (!isRecord(value)) {
|
||||
return false;
|
||||
}
|
||||
if (!isNonEmptyString(value.code) || !isNonEmptyString(value.message)) {
|
||||
return false;
|
||||
}
|
||||
if (value.retryable !== undefined && typeof value.retryable !== "boolean") {
|
||||
return false;
|
||||
}
|
||||
return value.retryAfterMs === undefined || isNonNegativeInteger(value.retryAfterMs);
|
||||
}
|
||||
|
||||
// These lightweight guards validate dispatch-critical envelope fields without
|
||||
// compiling the full schemas or rejecting additive payload fields.
|
||||
export function isGatewayEventFrame(value: unknown): value is EventFrame {
|
||||
if (!isRecord(value) || value.type !== "event" || !isNonEmptyString(value.event)) {
|
||||
return false;
|
||||
}
|
||||
return value.seq === undefined || isNonNegativeInteger(value.seq);
|
||||
}
|
||||
|
||||
export function isGatewayResponseFrame(value: unknown): value is ResponseFrame {
|
||||
if (
|
||||
!isRecord(value) ||
|
||||
value.type !== "res" ||
|
||||
!isNonEmptyString(value.id) ||
|
||||
typeof value.ok !== "boolean"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return value.error === undefined || isGatewayErrorShape(value.error);
|
||||
}
|
||||
+25
-167
@@ -1,24 +1,17 @@
|
||||
// OpenClaw Gateway client facade.
|
||||
// Wraps the shared gateway-client package with OpenClaw host dependencies.
|
||||
import {
|
||||
GatewayClient as BaseGatewayClient,
|
||||
GATEWAY_CLOSE_CODE_HINTS as BASE_GATEWAY_CLOSE_CODE_HINTS,
|
||||
GatewayClientRequestError as BaseGatewayClientRequestError,
|
||||
describeGatewayCloseCode as baseDescribeGatewayCloseCode,
|
||||
isGatewayConnectAssemblyError as baseIsGatewayConnectAssemblyError,
|
||||
resolveGatewayClientConnectChallengeTimeoutMs as baseResolveGatewayClientConnectChallengeTimeoutMs,
|
||||
} from "../../packages/gateway-client/src/index.js";
|
||||
// Injects OpenClaw host dependencies into the shared gateway-client package.
|
||||
import { GatewayClient as BaseGatewayClient } from "../../packages/gateway-client/src/index.js";
|
||||
import type {
|
||||
GatewayClientMode,
|
||||
GatewayClientName,
|
||||
} from "../../packages/gateway-protocol/src/client-info.js";
|
||||
import type { EventFrame, HelloOk } from "../../packages/gateway-protocol/src/index.js";
|
||||
GatewayClientConnectionMetadata,
|
||||
GatewayClientHostDeps,
|
||||
GatewayClientOptions,
|
||||
GatewayClientRequestOptions,
|
||||
} from "../../packages/gateway-client/src/index.js";
|
||||
import {
|
||||
clearDeviceAuthToken,
|
||||
loadDeviceAuthToken,
|
||||
storeDeviceAuthToken,
|
||||
} from "../infra/device-auth-store.js";
|
||||
import type { DeviceIdentity } from "../infra/device-identity.js";
|
||||
import {
|
||||
loadOrCreateDeviceIdentity,
|
||||
publicKeyRawBase64UrlFromPem,
|
||||
@@ -33,139 +26,23 @@ import { logDebug, logError } from "../logger.js";
|
||||
import { redactToolPayloadText } from "../logging/redact.js";
|
||||
import { VERSION } from "../version.js";
|
||||
|
||||
export type DeviceAuthTokenRecord = {
|
||||
token?: string;
|
||||
scopes?: string[];
|
||||
};
|
||||
|
||||
export type GatewayClientHostDeps = {
|
||||
loadOrCreateDeviceIdentity?: () => DeviceIdentity | undefined;
|
||||
signDevicePayload?: (privateKeyPem: string, payload: string) => string;
|
||||
publicKeyRawBase64UrlFromPem?: (publicKeyPem: string) => string;
|
||||
loadDeviceAuthToken?: (params: {
|
||||
deviceId: string;
|
||||
role: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}) => DeviceAuthTokenRecord | null;
|
||||
storeDeviceAuthToken?: (params: {
|
||||
deviceId: string;
|
||||
role: string;
|
||||
token: string;
|
||||
scopes: string[];
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}) => void;
|
||||
clearDeviceAuthToken?: (params: {
|
||||
deviceId: string;
|
||||
role: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}) => void;
|
||||
beforeConnect?: () => void;
|
||||
registerGatewayLoopbackBypass?: (url: string) => (() => void) | undefined;
|
||||
logDebug?: (message: string) => void;
|
||||
logError?: (message: string) => void;
|
||||
redactForLog?: (message: string) => string;
|
||||
normalizeTlsFingerprint?: (fingerprint: string | undefined) => string;
|
||||
};
|
||||
|
||||
export type GatewayClientRequestOptions = {
|
||||
expectFinal?: boolean;
|
||||
timeoutMs?: number | null;
|
||||
signal?: AbortSignal;
|
||||
onAccepted?: (payload: unknown) => void;
|
||||
};
|
||||
|
||||
export type GatewayReconnectPausedInfo = {
|
||||
code: number;
|
||||
reason: string;
|
||||
detailCode: string | null;
|
||||
};
|
||||
|
||||
export type GatewayClientCloseInfo = {
|
||||
phase: "pre-hello" | "post-hello";
|
||||
socketOpened: boolean;
|
||||
transportValidated: boolean;
|
||||
transientPreHelloCleanClose: boolean;
|
||||
};
|
||||
|
||||
type GatewayClientErrorShape = {
|
||||
message: string;
|
||||
code?: string;
|
||||
details?: unknown;
|
||||
retryable?: boolean;
|
||||
retryAfterMs?: number;
|
||||
};
|
||||
|
||||
export const GATEWAY_CLOSE_CODE_HINTS: Readonly<Record<number, string>> =
|
||||
BASE_GATEWAY_CLOSE_CODE_HINTS;
|
||||
|
||||
export const GatewayClientRequestError = BaseGatewayClientRequestError as unknown as {
|
||||
new (error: GatewayClientErrorShape): Error & {
|
||||
readonly gatewayCode: string;
|
||||
readonly details?: unknown;
|
||||
readonly retryable: boolean;
|
||||
readonly retryAfterMs?: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type GatewayClientRequestError = InstanceType<typeof GatewayClientRequestError>;
|
||||
|
||||
export function describeGatewayCloseCode(code: number): string | undefined {
|
||||
return baseDescribeGatewayCloseCode(code);
|
||||
}
|
||||
|
||||
export function isGatewayConnectAssemblyError(value: unknown): value is Error {
|
||||
return baseIsGatewayConnectAssemblyError(value);
|
||||
}
|
||||
|
||||
export type GatewayClientOptions = {
|
||||
url?: string;
|
||||
origin?: string;
|
||||
connectChallengeTimeoutMs?: number;
|
||||
/** @deprecated Use connectChallengeTimeoutMs. */
|
||||
connectDelayMs?: number;
|
||||
preauthHandshakeTimeoutMs?: number;
|
||||
tickWatchMinIntervalMs?: number;
|
||||
tickWatchTimeoutMs?: number;
|
||||
requestTimeoutMs?: number;
|
||||
token?: string;
|
||||
bootstrapToken?: string;
|
||||
deviceToken?: string;
|
||||
password?: string;
|
||||
approvalRuntimeToken?: string;
|
||||
agentRuntimeIdentityToken?: string;
|
||||
instanceId?: string;
|
||||
clientName?: GatewayClientName;
|
||||
clientDisplayName?: string;
|
||||
clientVersion?: string;
|
||||
platform?: string;
|
||||
deviceFamily?: string;
|
||||
mode?: GatewayClientMode;
|
||||
role?: string;
|
||||
scopes?: string[];
|
||||
caps?: string[];
|
||||
commands?: string[];
|
||||
permissions?: Record<string, boolean>;
|
||||
pathEnv?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
deviceIdentity?: DeviceIdentity | null;
|
||||
hostDeps?: GatewayClientHostDeps;
|
||||
minProtocol?: number;
|
||||
maxProtocol?: number;
|
||||
tlsFingerprint?: string;
|
||||
onEvent?: (evt: EventFrame) => void;
|
||||
onHelloOk?: (hello: HelloOk) => void;
|
||||
onConnectError?: (err: Error) => void;
|
||||
onReconnectPaused?: (info: GatewayReconnectPausedInfo) => void;
|
||||
onClose?: (code: number, reason: string, info?: GatewayClientCloseInfo) => void;
|
||||
onGap?: (info: { expected: number; received: number }) => void;
|
||||
};
|
||||
|
||||
export type GatewayClientConnectionMetadata = {
|
||||
clientName?: GatewayClientName;
|
||||
hasDeviceIdentity: boolean;
|
||||
mode?: GatewayClientMode;
|
||||
preauthHandshakeTimeoutMs?: number;
|
||||
};
|
||||
export {
|
||||
GATEWAY_CLOSE_CODE_HINTS,
|
||||
GatewayClientRequestError,
|
||||
describeGatewayCloseCode,
|
||||
isGatewayConnectAssemblyError,
|
||||
resolveGatewayClientConnectChallengeTimeoutMs,
|
||||
} from "../../packages/gateway-client/src/index.js";
|
||||
export type {
|
||||
DeviceAuthTokenRecord,
|
||||
DeviceIdentity,
|
||||
GatewayClientCloseInfo,
|
||||
GatewayClientConnectionMetadata,
|
||||
GatewayClientHostDeps,
|
||||
GatewayClientOptions,
|
||||
GatewayClientRequestOptions,
|
||||
GatewayReconnectPausedInfo,
|
||||
} from "../../packages/gateway-client/src/index.js";
|
||||
|
||||
function createOpenClawGatewayClientHostDeps(
|
||||
overrides?: GatewayClientHostDeps,
|
||||
@@ -189,21 +66,10 @@ function createOpenClawGatewayClientHostDeps(
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGatewayClientConnectChallengeTimeoutMs(
|
||||
opts: Pick<
|
||||
GatewayClientOptions,
|
||||
"connectChallengeTimeoutMs" | "connectDelayMs" | "env" | "preauthHandshakeTimeoutMs"
|
||||
>,
|
||||
): number {
|
||||
return baseResolveGatewayClientConnectChallengeTimeoutMs(opts);
|
||||
}
|
||||
|
||||
export class GatewayClient {
|
||||
#client: BaseGatewayClient;
|
||||
|
||||
constructor(opts: GatewayClientOptions) {
|
||||
// Inject host deps here so the reusable package stays decoupled from
|
||||
// OpenClaw device identity, token storage, proxy routing, and logging.
|
||||
this.#client = new BaseGatewayClient({
|
||||
...opts,
|
||||
clientVersion: opts.clientVersion ?? VERSION,
|
||||
@@ -232,14 +98,6 @@ export class GatewayClient {
|
||||
}
|
||||
|
||||
getConnectionMetadata(): GatewayClientConnectionMetadata {
|
||||
const opts = (this.#client as unknown as { opts: GatewayClientOptions }).opts;
|
||||
return {
|
||||
clientName: opts.clientName,
|
||||
hasDeviceIdentity: Boolean(opts.deviceIdentity),
|
||||
mode: opts.mode,
|
||||
preauthHandshakeTimeoutMs: opts.preauthHandshakeTimeoutMs,
|
||||
};
|
||||
return this.#client.getConnectionMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
export type { DeviceIdentity };
|
||||
|
||||
@@ -1509,6 +1509,12 @@ describe("plugin sdk alias helpers", () => {
|
||||
srcFile: "schema.ts",
|
||||
distFile: "schema.mjs",
|
||||
});
|
||||
const gatewayProtocolFrameGuards = writeWorkspacePackageEntry({
|
||||
root: fixture.root,
|
||||
packageDir: "gateway-protocol",
|
||||
srcFile: "frame-guards.ts",
|
||||
distFile: "frame-guards.mjs",
|
||||
});
|
||||
const netPolicy = writeWorkspacePackageEntry({
|
||||
root: fixture.root,
|
||||
packageDir: "net-policy",
|
||||
@@ -1615,6 +1621,7 @@ describe("plugin sdk alias helpers", () => {
|
||||
fs.rmSync(gatewayClientTimeouts.distFile);
|
||||
fs.rmSync(gatewayProtocol.distFile);
|
||||
fs.rmSync(gatewayProtocolSchema.distFile);
|
||||
fs.rmSync(gatewayProtocolFrameGuards.distFile);
|
||||
fs.rmSync(markdownCore.distFile);
|
||||
fs.rmSync(markdownCoreTables.distFile);
|
||||
fs.rmSync(mediaGenerationCore.distFile);
|
||||
@@ -1653,6 +1660,9 @@ describe("plugin sdk alias helpers", () => {
|
||||
expect(fs.realpathSync(aliases["@openclaw/gateway-protocol/schema"] ?? "")).toBe(
|
||||
fs.realpathSync(gatewayProtocolSchema.srcFile),
|
||||
);
|
||||
expect(fs.realpathSync(aliases["@openclaw/gateway-protocol/frame-guards"] ?? "")).toBe(
|
||||
fs.realpathSync(gatewayProtocolFrameGuards.srcFile),
|
||||
);
|
||||
expect(fs.realpathSync(aliases["@openclaw/markdown-core"] ?? "")).toBe(
|
||||
fs.realpathSync(markdownCore.srcFile),
|
||||
);
|
||||
@@ -1720,6 +1730,12 @@ describe("plugin sdk alias helpers", () => {
|
||||
srcFile: "connect-error-details.ts",
|
||||
distFile: "connect-error-details.mjs",
|
||||
});
|
||||
const gatewayProtocolFrameGuards = writeWorkspacePackageEntry({
|
||||
root: fixture.root,
|
||||
packageDir: "gateway-protocol",
|
||||
srcFile: "frame-guards.ts",
|
||||
distFile: "frame-guards.mjs",
|
||||
});
|
||||
const mediaGenerationCore = writeWorkspacePackageEntry({
|
||||
root: fixture.root,
|
||||
packageDir: "media-generation-core",
|
||||
@@ -1792,6 +1808,9 @@ describe("plugin sdk alias helpers", () => {
|
||||
expect(fs.realpathSync(aliases["@openclaw/gateway-protocol/connect-error-details"] ?? "")).toBe(
|
||||
fs.realpathSync(gatewayProtocol.distFile),
|
||||
);
|
||||
expect(fs.realpathSync(aliases["@openclaw/gateway-protocol/frame-guards"] ?? "")).toBe(
|
||||
fs.realpathSync(gatewayProtocolFrameGuards.distFile),
|
||||
);
|
||||
expect(fs.realpathSync(aliases["@openclaw/markdown-core/render"] ?? "")).toBe(
|
||||
fs.realpathSync(markdownCore.distFile),
|
||||
);
|
||||
|
||||
@@ -572,6 +572,13 @@ const WORKSPACE_PACKAGE_ALIAS_ENTRIES: WorkspacePackageAliasEntry[] = [
|
||||
srcFile: "connect-error-details.ts",
|
||||
distFile: "connect-error-details.mjs",
|
||||
},
|
||||
{
|
||||
packageName: "@openclaw/gateway-protocol",
|
||||
packageDir: "gateway-protocol",
|
||||
subpath: "frame-guards",
|
||||
srcFile: "frame-guards.ts",
|
||||
distFile: "frame-guards.mjs",
|
||||
},
|
||||
{
|
||||
packageName: "@openclaw/gateway-protocol",
|
||||
packageDir: "gateway-protocol",
|
||||
|
||||
@@ -213,6 +213,10 @@ export const sharedVitestConfig = {
|
||||
"connect-error-details.ts",
|
||||
),
|
||||
},
|
||||
{
|
||||
find: "@openclaw/gateway-protocol/frame-guards",
|
||||
replacement: path.join(repoRoot, "packages", "gateway-protocol", "src", "frame-guards.ts"),
|
||||
},
|
||||
{
|
||||
find: "@openclaw/gateway-protocol/schema",
|
||||
replacement: path.join(repoRoot, "packages", "gateway-protocol", "src", "schema.ts"),
|
||||
|
||||
@@ -73,6 +73,9 @@
|
||||
"@openclaw/gateway-protocol/connect-error-details": [
|
||||
"./packages/gateway-protocol/src/connect-error-details.ts"
|
||||
],
|
||||
"@openclaw/gateway-protocol/frame-guards": [
|
||||
"./packages/gateway-protocol/src/frame-guards.ts"
|
||||
],
|
||||
"@openclaw/gateway-protocol/schema": ["./packages/gateway-protocol/src/schema.ts"],
|
||||
"@openclaw/gateway-protocol/startup-unavailable": [
|
||||
"./packages/gateway-protocol/src/startup-unavailable.ts"
|
||||
|
||||
@@ -367,6 +367,7 @@ function buildGatewayProtocolDistEntries(): Record<string, string> {
|
||||
index: "packages/gateway-protocol/src/index.ts",
|
||||
"client-info": "packages/gateway-protocol/src/client-info.ts",
|
||||
"connect-error-details": "packages/gateway-protocol/src/connect-error-details.ts",
|
||||
"frame-guards": "packages/gateway-protocol/src/frame-guards.ts",
|
||||
schema: "packages/gateway-protocol/src/schema.ts",
|
||||
"startup-unavailable": "packages/gateway-protocol/src/startup-unavailable.ts",
|
||||
version: "packages/gateway-protocol/src/version.ts",
|
||||
|
||||
Reference in New Issue
Block a user