@openclaw/gateway-client 0.0.0 → 2026.7.2-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ - Publish the reference Gateway WebSocket client for the first time.
6
+ - Record the client extraction from the Gateway protocol implementation in #87797.
7
+ - Support the current Gateway wire protocol, protocol v4.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenClaw Foundation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,134 @@
1
- # OpenClaw Gateway Client
1
+ # `@openclaw/gateway-client`
2
2
 
3
- Reserved package name. Release artifacts are published by OpenClaw release automation.
3
+ Reference WebSocket client for the OpenClaw Gateway protocol. It provides the
4
+ connection state machine used by OpenClaw's own Node and browser clients:
5
+ challenge-based authentication, typed protocol frames, request correlation,
6
+ timeouts, reconnect backoff, device-token handling, and event delivery.
7
+
8
+ The current wire protocol is version 4. General clients must negotiate v4 with
9
+ `minProtocol: 4` and `maxProtocol: 4`. See the
10
+ [Gateway protocol specification](https://docs.openclaw.ai/gateway/protocol) for
11
+ the complete handshake, authentication, role, scope, and method contracts.
12
+
13
+ ## Versioning
14
+
15
+ Package versions follow the OpenClaw calendar release train: `YYYY.M.PATCH`,
16
+ including the OpenClaw prerelease suffix when applicable. The package version is
17
+ separate from the negotiated wire protocol number.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install @openclaw/gateway-client @openclaw/gateway-protocol
23
+ ```
24
+
25
+ Node consumers use the `ws` transport included as a runtime dependency. Browser
26
+ consumers provide their platform WebSocket through the browser-safe protocol
27
+ client surface.
28
+
29
+ ## Entry points
30
+
31
+ - `@openclaw/gateway-client` exports the Node `GatewayClient`, device-auth
32
+ helpers, readiness helpers, and timeout utilities.
33
+ - `@openclaw/gateway-client/browser` exports the browser-safe protocol client,
34
+ browser device-auth lifecycle, reconnect policy, and lightweight protocol
35
+ constants. Its module graph does not import Node built-ins or `ws`.
36
+ - `@openclaw/gateway-client/readiness` exports helpers that delay client startup
37
+ until the event loop can process Gateway IO.
38
+ - `@openclaw/gateway-client/timeouts` exports timeout constants and safe timer
39
+ resolution helpers.
40
+
41
+ ## Node quickstart
42
+
43
+ ```ts
44
+ import { GatewayClient } from "@openclaw/gateway-client";
45
+ import { PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
46
+
47
+ const connected = Promise.withResolvers<void>();
48
+ const client = new GatewayClient({
49
+ url: "ws://127.0.0.1:18789",
50
+ token: process.env.OPENCLAW_GATEWAY_TOKEN,
51
+ minProtocol: PROTOCOL_VERSION, // v4
52
+ maxProtocol: PROTOCOL_VERSION, // v4
53
+ onHelloOk: () => connected.resolve(),
54
+ onConnectError: (error) => connected.reject(error),
55
+ onEvent: (event) => {
56
+ console.log(event.event, event.payload);
57
+ },
58
+ });
59
+
60
+ client.start();
61
+ await connected.promise;
62
+
63
+ const status = await client.request("status", {});
64
+ console.log(status);
65
+
66
+ client.stop();
67
+ ```
68
+
69
+ The client waits for the Gateway's `connect.challenge` event before sending its
70
+ `connect` request. It includes the challenge nonce in device authentication and
71
+ does not fall back to a pre-challenge handshake. `onHelloOk` fires only after the
72
+ Gateway accepts the v4 connection, so requests should wait for that callback.
73
+
74
+ For remote connections, use `wss://`. Plaintext `ws://` is allowed by default
75
+ only for loopback addresses. Authentication material and Gateway traffic must
76
+ not cross an untrusted network without transport security.
77
+
78
+ ## Browser clients
79
+
80
+ Import `@openclaw/gateway-client/browser` when the host owns the WebSocket
81
+ adapter and device-key storage. The browser entry includes
82
+ `GatewayProtocolClient` and `GatewayBrowserDeviceAuthLifecycle`; it deliberately
83
+ omits the Node transport, TLS fingerprint handling, and private-network address
84
+ policy.
85
+
86
+ The host is responsible for:
87
+
88
+ - creating a `GatewayProtocolSocket` adapter around the browser WebSocket;
89
+ - loading and storing browser device identity and issued device tokens;
90
+ - signing the challenge-bound device payload;
91
+ - supplying the client identity, role, scopes, and authentication selection;
92
+ - choosing close and reconnect behavior for product-specific errors.
93
+
94
+ The shared protocol client still owns frame parsing, request correlation,
95
+ challenge ordering, timeout cleanup, sequence-gap detection, and reconnect
96
+ scheduling.
97
+
98
+ ## Defaults and reconnect behavior
99
+
100
+ The Node client starts with a 30 second request timeout, a 15 second
101
+ connect-challenge timeout, and exponential reconnect delays from 1 second to 30
102
+ seconds with a multiplier of 2. Server-provided startup retry hints may override
103
+ the next delay.
104
+
105
+ The canonical defaults table and the server policy fields that can replace
106
+ pre-handshake values are documented in the
107
+ [Gateway protocol specification](https://docs.openclaw.ai/gateway/protocol#client-constants).
108
+
109
+ Use the `./timeouts` entry point when a host must align readiness or watchdog
110
+ budgets with these defaults. Use the `./readiness` entry point when startup must
111
+ wait for an event-loop probe before opening the socket.
112
+
113
+ ## Bundled internals
114
+
115
+ The retry supervisor and the small `@openclaw/net-policy/ip` implementation are
116
+ inlined into the published JavaScript and declarations. They are implementation
117
+ details, not public exports or supported API surfaces. `ipaddr.js` remains an
118
+ external dependency because the inlined IP helpers use its public runtime and
119
+ types.
120
+
121
+ `ws`, `@openclaw/gateway-protocol`, and `ipaddr.js` remain external in the
122
+ published distribution. Consumers should import protocol types and constants
123
+ from `@openclaw/gateway-protocol`, not from bundled implementation paths.
124
+
125
+ ## Contract notes
126
+
127
+ - The client is inert at module import and construction time. `start()` opens
128
+ the socket; `stop()` closes it and rejects pending requests.
129
+ - A request uses `request(method, params)` after `hello-ok`. Passing
130
+ `timeoutMs: null` creates an intentionally unbounded request.
131
+ - Device identity persistence, signing, proxy routing, TLS formatting, and
132
+ logging stay host-owned through `GatewayClientHostDeps`.
133
+ - Protocol changes are additive first. Incompatible changes require an explicit
134
+ wire-version decision and coordinated server/client follow-through.
@@ -0,0 +1,130 @@
1
+ import { ConnectParams, HelloOk } from "@openclaw/gateway-protocol";
2
+
3
+ //#region packages/gateway-client/src/device-auth.d.ts
4
+ declare function normalizeDeviceMetadataForAuth(value?: string | null): string;
5
+ type DeviceAuthPayloadParams = {
6
+ deviceId: string;
7
+ clientId: string;
8
+ clientMode: string;
9
+ role: string;
10
+ scopes: string[];
11
+ signedAtMs: number;
12
+ token?: string | null;
13
+ nonce: string;
14
+ };
15
+ type DeviceAuthPayloadV3Params = DeviceAuthPayloadParams & {
16
+ platform?: string | null;
17
+ deviceFamily?: string | null;
18
+ };
19
+ declare function buildDeviceAuthPayload(params: DeviceAuthPayloadParams): string;
20
+ declare function buildDeviceAuthPayloadV3(params: DeviceAuthPayloadV3Params): string;
21
+ //#endregion
22
+ //#region packages/gateway-client/src/connect-auth.d.ts
23
+ type GatewayConnectAuthSelection = {
24
+ authToken?: string;
25
+ authBootstrapToken?: string;
26
+ authDeviceToken?: string;
27
+ authPassword?: string;
28
+ authApprovalRuntimeToken?: string;
29
+ authAgentRuntimeIdentityToken?: string;
30
+ signatureToken?: string;
31
+ resolvedDeviceToken?: string;
32
+ storedToken?: string;
33
+ storedScopes?: string[];
34
+ usingStoredDeviceToken?: boolean;
35
+ };
36
+ declare function selectGatewayConnectAuth(params: {
37
+ token?: string;
38
+ bootstrapToken?: string;
39
+ deviceToken?: string;
40
+ password?: string;
41
+ approvalRuntimeToken?: string;
42
+ agentRuntimeIdentityToken?: string;
43
+ storedToken?: string;
44
+ storedScopes?: string[];
45
+ pendingDeviceTokenRetry?: boolean;
46
+ trustedDeviceTokenRetry?: boolean;
47
+ preferBootstrapToken?: boolean;
48
+ }): GatewayConnectAuthSelection;
49
+ declare function buildGatewayConnectAuth(selected: GatewayConnectAuthSelection): ConnectParams["auth"];
50
+ declare function resolveGatewayConnectScopes(params: {
51
+ requestedScopes?: string[];
52
+ usingStoredDeviceToken?: boolean;
53
+ storedScopes?: string[];
54
+ defaultScopes: readonly string[];
55
+ }): string[];
56
+ declare function shouldRetryGatewayWithDeviceToken(params: {
57
+ retryBudgetUsed: boolean;
58
+ currentDeviceToken?: string;
59
+ explicitToken?: string;
60
+ storedToken?: string;
61
+ trustedEndpoint: boolean;
62
+ canRetryWithDeviceTokenHint?: boolean;
63
+ errorDetails?: unknown;
64
+ }): boolean;
65
+ //#endregion
66
+ //#region packages/gateway-client/src/browser-device-auth.d.ts
67
+ type GatewayBrowserDeviceIdentity = {
68
+ deviceId: string;
69
+ publicKey: string;
70
+ sign: (payload: string) => Promise<string>;
71
+ };
72
+ type GatewayBrowserDeviceTokenRecord = {
73
+ token: string;
74
+ scopes: string[];
75
+ };
76
+ type MaybePromise<T> = T | Promise<T>;
77
+ type GatewayBrowserDeviceTokenStore = {
78
+ load: (params: {
79
+ clientId: string;
80
+ deviceId: string;
81
+ role: string;
82
+ }) => MaybePromise<GatewayBrowserDeviceTokenRecord | null>;
83
+ store: (params: {
84
+ clientId: string;
85
+ deviceId: string;
86
+ role: string;
87
+ token: string;
88
+ scopes: string[];
89
+ }) => MaybePromise<void>;
90
+ clear: (params: {
91
+ clientId: string;
92
+ deviceId: string;
93
+ role: string;
94
+ }) => MaybePromise<void>;
95
+ };
96
+ type GatewayBrowserDeviceAuthPlan = {
97
+ clientId: string;
98
+ role: string;
99
+ identity: GatewayBrowserDeviceIdentity | null;
100
+ selectedAuth: GatewayConnectAuthSelection;
101
+ scopes: string[];
102
+ device?: NonNullable<ConnectParams["device"]>;
103
+ auth?: ConnectParams["auth"];
104
+ };
105
+ /** Browser-safe device pairing and issued-token lifecycle shared by first-party UI clients. */
106
+ declare class GatewayBrowserDeviceAuthLifecycle {
107
+ private readonly deps;
108
+ constructor(deps: {
109
+ loadIdentity: () => Promise<GatewayBrowserDeviceIdentity | null>;
110
+ tokenStore: GatewayBrowserDeviceTokenStore;
111
+ nowMs?: () => number;
112
+ });
113
+ buildPlan(params: {
114
+ client: ConnectParams["client"];
115
+ role: string;
116
+ defaultScopes: readonly string[];
117
+ bootstrapScopes?: readonly string[];
118
+ token?: string;
119
+ bootstrapToken?: string;
120
+ password?: string;
121
+ pendingDeviceTokenRetry?: boolean;
122
+ trustedDeviceTokenRetry?: boolean;
123
+ preferBootstrapToken?: boolean;
124
+ nonce: string | null;
125
+ }): Promise<GatewayBrowserDeviceAuthPlan>;
126
+ acceptHello(hello: Pick<HelloOk, "auth">, plan: GatewayBrowserDeviceAuthPlan): Promise<void>;
127
+ clearStoredToken(plan: GatewayBrowserDeviceAuthPlan): Promise<void>;
128
+ }
129
+ //#endregion
130
+ export { GatewayBrowserDeviceTokenStore as a, resolveGatewayConnectScopes as c, buildDeviceAuthPayload as d, buildDeviceAuthPayloadV3 as f, GatewayBrowserDeviceTokenRecord as i, selectGatewayConnectAuth as l, GatewayBrowserDeviceAuthPlan as n, GatewayConnectAuthSelection as o, normalizeDeviceMetadataForAuth as p, GatewayBrowserDeviceIdentity as r, buildGatewayConnectAuth as s, GatewayBrowserDeviceAuthLifecycle as t, shouldRetryGatewayWithDeviceToken as u };
@@ -0,0 +1,20 @@
1
+ import { a as GatewayBrowserDeviceTokenStore, c as resolveGatewayConnectScopes, d as buildDeviceAuthPayload, f as buildDeviceAuthPayloadV3, i as GatewayBrowserDeviceTokenRecord, l as selectGatewayConnectAuth, n as GatewayBrowserDeviceAuthPlan, o as GatewayConnectAuthSelection, p as normalizeDeviceMetadataForAuth, r as GatewayBrowserDeviceIdentity, s as buildGatewayConnectAuth, t as GatewayBrowserDeviceAuthLifecycle, u as shouldRetryGatewayWithDeviceToken } from "./browser-device-auth-Bp297Rtf.mjs";
2
+ import { a as GatewayProtocolRequestTiming, c as GatewayProtocolTiming, i as GatewayProtocolRequestOptions, n as GatewayProtocolCloseContext, o as GatewayProtocolSocket, r as GatewayProtocolRequestError, s as GatewayProtocolSocketHandlers, t as GatewayProtocolClient } from "./protocol-client-Cj1WAoMt.mjs";
3
+ import { DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS } from "./timeouts.mjs";
4
+ import { ConnectParams, ErrorShape, EventFrame, HelloOk } from "@openclaw/gateway-protocol";
5
+ export * from "@openclaw/gateway-protocol/connect-error-details";
6
+ export * from "@openclaw/gateway-protocol/client-info";
7
+ export * from "@openclaw/gateway-protocol/gateway-error-details";
8
+ export * from "@openclaw/gateway-protocol/startup-unavailable";
9
+ export * from "@openclaw/gateway-protocol/version";
10
+
11
+ //#region packages/gateway-client/src/reconnect-policy.d.ts
12
+ declare function shouldPauseGatewayReconnect(params: {
13
+ details?: unknown;
14
+ deviceTokenRetryPending?: boolean;
15
+ tokenMismatchIsTerminal?: boolean;
16
+ protocolMismatchIsTerminal?: boolean;
17
+ clientVersionMismatchIsTerminal?: boolean;
18
+ }): boolean;
19
+ //#endregion
20
+ export { type ConnectParams, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, type ErrorShape, type EventFrame, GatewayBrowserDeviceAuthLifecycle, GatewayBrowserDeviceAuthPlan, GatewayBrowserDeviceIdentity, GatewayBrowserDeviceTokenRecord, GatewayBrowserDeviceTokenStore, GatewayConnectAuthSelection, GatewayProtocolClient, GatewayProtocolCloseContext, GatewayProtocolRequestError, GatewayProtocolRequestOptions, GatewayProtocolRequestTiming, GatewayProtocolSocket, GatewayProtocolSocketHandlers, GatewayProtocolTiming, type HelloOk, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, normalizeDeviceMetadataForAuth, resolveGatewayConnectScopes, selectGatewayConnectAuth, shouldPauseGatewayReconnect, shouldRetryGatewayWithDeviceToken };
@@ -0,0 +1,8 @@
1
+ import { a as buildGatewayConnectAuth, c as shouldRetryGatewayWithDeviceToken, d as normalizeDeviceMetadataForAuth, i as GatewayBrowserDeviceAuthLifecycle, l as buildDeviceAuthPayload, n as GatewayProtocolClient, o as resolveGatewayConnectScopes, r as GatewayProtocolRequestError, s as selectGatewayConnectAuth, t as shouldPauseGatewayReconnect, u as buildDeviceAuthPayloadV3 } from "./reconnect-policy-CkzLe1_K.mjs";
2
+ import { DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS } from "./timeouts.mjs";
3
+ export * from "@openclaw/gateway-protocol/connect-error-details";
4
+ export * from "@openclaw/gateway-protocol/client-info";
5
+ export * from "@openclaw/gateway-protocol/gateway-error-details";
6
+ export * from "@openclaw/gateway-protocol/startup-unavailable";
7
+ export * from "@openclaw/gateway-protocol/version";
8
+ export { DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, GatewayBrowserDeviceAuthLifecycle, GatewayProtocolClient, GatewayProtocolRequestError, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, normalizeDeviceMetadataForAuth, resolveGatewayConnectScopes, selectGatewayConnectAuth, shouldPauseGatewayReconnect, shouldRetryGatewayWithDeviceToken };
@@ -0,0 +1,4 @@
1
+ import { a as GatewayBrowserDeviceTokenStore, c as resolveGatewayConnectScopes, d as buildDeviceAuthPayload, f as buildDeviceAuthPayloadV3, i as GatewayBrowserDeviceTokenRecord, l as selectGatewayConnectAuth, n as GatewayBrowserDeviceAuthPlan, o as GatewayConnectAuthSelection, p as normalizeDeviceMetadataForAuth, r as GatewayBrowserDeviceIdentity, s as buildGatewayConnectAuth, t as GatewayBrowserDeviceAuthLifecycle, u as shouldRetryGatewayWithDeviceToken } from "./browser-device-auth-Bp297Rtf.mjs";
2
+ import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, clampConnectChallengeTimeoutMs, getConnectChallengeTimeoutMsFromEnv, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs } from "./timeouts.mjs";
3
+ import { _ as GatewayClientRequestOptions, a as startGatewayClientWithReadinessWait, c as waitForEventLoopReady, d as GatewayClient, f as GatewayClientCloseInfo, g as GatewayClientRequestError, h as GatewayClientOptions, i as startGatewayClientWhenEventLoopReady, l as DeviceAuthTokenRecord, m as GatewayClientHostDeps, n as GatewayClientStartReadinessOptions, o as EventLoopReadyOptions, p as GatewayClientConnectionMetadata, r as GatewayClientStartable, s as EventLoopReadyResult, t as EventLoopReadyWaiter, u as DeviceIdentity, v as GatewayReconnectPausedInfo, y as isGatewayConnectAssemblyError } from "./readiness-CPiKyi1s.mjs";
4
+ export { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, DeviceAuthTokenRecord, DeviceIdentity, EventLoopReadyOptions, EventLoopReadyResult, EventLoopReadyWaiter, GatewayBrowserDeviceAuthLifecycle, GatewayBrowserDeviceAuthPlan, GatewayBrowserDeviceIdentity, GatewayBrowserDeviceTokenRecord, GatewayBrowserDeviceTokenStore, GatewayClient, GatewayClientCloseInfo, GatewayClientConnectionMetadata, GatewayClientHostDeps, GatewayClientOptions, GatewayClientRequestError, GatewayClientRequestOptions, GatewayClientStartReadinessOptions, GatewayClientStartable, GatewayConnectAuthSelection, GatewayReconnectPausedInfo, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, clampConnectChallengeTimeoutMs, getConnectChallengeTimeoutMsFromEnv, isGatewayConnectAssemblyError, normalizeDeviceMetadataForAuth, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolveGatewayConnectScopes, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait, waitForEventLoopReady };