@openclaw/gateway-client 2026.10.1-beta.2 → 2026.10.5-beta.1
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/README.md +13 -4
- package/dist/browser.d.mts +1 -1
- package/dist/browser.mjs +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +34 -70
- package/dist/{protocol-client-DT451Msj.d.mts → protocol-client-CKMLpN-S.d.mts} +1 -0
- package/dist/{readiness-CLaFwfd4.d.mts → readiness-BbuB7IzG.d.mts} +1 -2
- package/dist/{readiness-DnB0NLBq.mjs → readiness-rG6_5Ynk.mjs} +1 -1
- package/dist/readiness.d.mts +1 -1
- package/dist/readiness.mjs +1 -2
- package/dist/{session-subscriptions-D5CpDUGt.mjs → session-subscriptions-DE0w2upu.mjs} +162 -133
- package/dist/timeouts-CQTCye5U.mjs +116 -0
- package/dist/timeouts.mjs +1 -87
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@ The current wire protocol is version 4. General clients must advertise exactly v
|
|
|
9
9
|
`minProtocol: 4` and `maxProtocol: 4`. See the
|
|
10
10
|
[Gateway protocol specification](https://docs.openclaw.ai/gateway/protocol) for
|
|
11
11
|
the complete handshake, authentication, role, scope, and method contracts.
|
|
12
|
-
Exact node identities (`role: "node"` plus `mode: "node"`) and
|
|
12
|
+
Exact node identities (`role: "node"` plus `mode: "node"`) and diagnostic clients
|
|
13
13
|
can use v3. The built-in node host starts with an exact v4 envelope, then retries
|
|
14
|
-
an exact v3 envelope after a v3 Gateway rejects v4. If that legacy
|
|
14
|
+
an exact v3 envelope after a v3 Gateway rejects v4. If that legacy connection attempt reaches
|
|
15
15
|
an upgraded v4 Gateway, the client reconnects with the full v4 envelope before
|
|
16
16
|
reporting readiness. Other exact node identities default to `[3, 4]`. Explicit
|
|
17
17
|
bounds override these defaults; `[3, 4]` on the built-in node host selects the
|
|
@@ -187,10 +187,19 @@ pre-handshake values are documented in the
|
|
|
187
187
|
|
|
188
188
|
Use the `./timeouts` entry point when a host must align readiness or watchdog
|
|
189
189
|
budgets with these defaults. Use the `./readiness` entry point when startup must
|
|
190
|
-
wait for an event-loop
|
|
190
|
+
wait for an event-loop check before opening the socket.
|
|
191
191
|
|
|
192
192
|
## Streaming chat
|
|
193
193
|
|
|
194
|
+
The `runId` returned by `chat.send` identifies the submitted input even when a
|
|
195
|
+
steer falls back to a followup. Queue admission is nonterminal: keep observing
|
|
196
|
+
that ID until the consuming execution completes, or the input is rejected,
|
|
197
|
+
canceled, or dropped before consumption. Collected inputs each receive the
|
|
198
|
+
batch execution's terminal outcome under their original IDs. SDK run handles
|
|
199
|
+
use the same IDs for events, `wait()`, and cancellation; canceling an input before
|
|
200
|
+
consumption withdraws only that input. A steer accepted into the active turn
|
|
201
|
+
continues to complete after its transcript receipt while that turn remains active.
|
|
202
|
+
|
|
194
203
|
Event callbacks receive the wire payload unchanged. A `chat` delta's optional
|
|
195
204
|
`message` is an authoritative snapshot that already includes `deltaText`.
|
|
196
205
|
Without `message`, append `deltaText` to the run's existing text. `replace: true`
|
|
@@ -229,7 +238,7 @@ reports `rebaselined`, `recovered`, or `unavailable`; unavailable full text omit
|
|
|
229
238
|
have finite retention, and sessionless or unavailable transcript occurrences
|
|
230
239
|
cannot be reconstructed; unknown outcomes remain unsettled with a recovery
|
|
231
240
|
notice. Automatic recovery stops after four consecutive unavailable observations
|
|
232
|
-
(initial
|
|
241
|
+
(initial check plus three waits), releases retention protection, and stays stopped
|
|
233
242
|
across reconnects. Confirmed activity resets that budget; late terminal frames can
|
|
234
243
|
still settle the run. Queued replies use exponential backoff with the Gateway
|
|
235
244
|
client's positive 20% jitter and a 25–30 second cap. ACP reports unavailable full
|
package/dist/browser.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { A as normalizeSessionProjectionRunId, C as reduceSessionProjectionRunEvent, D as SessionMessageIdentity, E as SessionMessageEnvelope, F as gatewayOriginScope, I as mergeChatStreamMessage, L as buildDeviceAuthPayload, M as readSessionMessageIdentity, N as readSessionMessageSequence, O as SessionProjectionEntry, P as gatewayCredentialScope, R as buildDeviceAuthPayloadV3, S as SessionProjectionRunTransition, T as readSessionProjectionFinalMessageIdentity, _ as reconcileSessionProjectionSnapshot, a as GatewaySessionMessageSubscriptionOptions, b as projectSessionTerminalReplyMessage, c as resetGatewaySessionMessageSubscriptionCoordinator, d as SessionProjectionRunStatus, f as SessionProjectionScope, g as projectLiveSessionMessage, h as createSessionProjection, i as GatewaySessionMessageSubscriptionCoordinatorOptions, j as readAssistantStreamSegmentIdentity, k as isLocallyOptimisticSessionMessage, l as SessionProjectionEvent, m as SessionProjectionState, n as GatewaySessionMessageSubscription, o as getGatewaySessionMessageSubscriptionCoordinator, p as SessionProjectionSnapshotOptions, r as GatewaySessionMessageSubscriptionCoordinator, s as releaseGatewaySessionMessageSubscription, t as GatewaySessionMessageRequestClient, u as SessionProjectionRun, v as reduceSessionProjection, w as hasSessionProjectionAcceptedFinal, x as SessionProjectionGatewayRunEvent, y as isSessionProjectionErrorMessage, z as normalizeDeviceMetadataForAuth } from "./session-subscriptions-Bcq6_wtR.mjs";
|
|
2
2
|
import { a as GatewayBrowserDeviceTokenStore, c as resolveGatewayConnectScopes, i as GatewayBrowserDeviceTokenRecord, l as selectGatewayConnectAuth, n as GatewayBrowserDeviceAuthPlan, o as GatewayConnectAuthSelection, r as GatewayBrowserDeviceIdentity, s as buildGatewayConnectAuth, t as GatewayBrowserDeviceAuthLifecycle, u as shouldRetryGatewayWithDeviceToken } from "./browser-device-auth-_UweR4aN.mjs";
|
|
3
3
|
import { i as isGatewayProtocolResponseError, n as GatewayProtocolRequestOptions, r as GatewayProtocolRequestTimeoutError, t as GatewayProtocolRequestError } from "./protocol-request-CtVhe3Bj.mjs";
|
|
4
|
-
import { a as GatewayProtocolConnectAuthority, c as GatewayProtocolTiming, i as GatewayProtocolCloseContext, l as GatewayProtocolRequestTiming, o as GatewayProtocolSocket, s as GatewayProtocolSocketHandlers, t as GatewayProtocolClient } from "./protocol-client-
|
|
4
|
+
import { a as GatewayProtocolConnectAuthority, c as GatewayProtocolTiming, i as GatewayProtocolCloseContext, l as GatewayProtocolRequestTiming, o as GatewayProtocolSocket, s as GatewayProtocolSocketHandlers, t as GatewayProtocolClient } from "./protocol-client-CKMLpN-S.mjs";
|
|
5
5
|
import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, resolveSafeTimeoutDelayMs } from "./timeouts.mjs";
|
|
6
6
|
import { ConnectParams as ConnectParams$1, GATEWAY_SERVER_CAPS } from "@openclaw/gateway-protocol/frame-guards";
|
|
7
7
|
import { ChannelsStatusResult, ConnectParams, ErrorShape, EventFrame, HelloOk, QuestionRecord } from "@openclaw/gateway-protocol";
|
package/dist/browser.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import {
|
|
1
|
+
import { A as buildGatewayConnectAuth, B as normalizeDeviceMetadataForAuth, C as isGatewayProtocolResponseError, D as gatewayCredentialScope, E as resolveModelCatalogConnect, F as asNullableRecord, M as selectGatewayConnectAuth, N as shouldRetryGatewayWithDeviceToken, O as gatewayOriginScope, P as mergeChatStreamMessage, R as buildDeviceAuthPayload, S as GatewayProtocolRequestTimeoutError, _ as isSessionProjectionErrorMessage, a as createSessionProjection, b as GatewayProtocolClient, c as reduceSessionProjection, d as readSessionProjectionFinalMessageIdentity, f as isLocallyOptimisticSessionMessage, g as readSessionMessageSequence, h as readSessionMessageIdentity, i as resetGatewaySessionMessageSubscriptionCoordinator, j as resolveGatewayConnectScopes, k as GatewayBrowserDeviceAuthLifecycle, l as reduceSessionProjectionRunEvent, m as readAssistantStreamSegmentIdentity, n as getGatewaySessionMessageSubscriptionCoordinator, o as projectLiveSessionMessage, p as normalizeSessionProjectionRunId, r as releaseGatewaySessionMessageSubscription, s as reconcileSessionProjectionSnapshot, t as GatewaySessionMessageSubscriptionCoordinator, u as hasSessionProjectionAcceptedFinal, v as projectSessionTerminalReplyMessage, x as GatewayProtocolRequestError, y as shouldPauseGatewayReconnect, z as buildDeviceAuthPayloadV3 } from "./session-subscriptions-DE0w2upu.mjs";
|
|
2
|
+
import { n as DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, p as resolveSafeTimeoutDelayMs, t as DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS } from "./timeouts-CQTCye5U.mjs";
|
|
3
3
|
import { GATEWAY_SERVER_CAPS } from "@openclaw/gateway-protocol/frame-guards";
|
|
4
4
|
export * from "@openclaw/gateway-protocol/connect-error-details";
|
|
5
5
|
export * from "@openclaw/gateway-protocol/gateway-error-details";
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { A as normalizeSessionProjectionRunId, C as reduceSessionProjectionRunEvent, D as SessionMessageIdentity, E as SessionMessageEnvelope, F as gatewayOriginScope, I as mergeChatStreamMessage, L as buildDeviceAuthPayload, M as readSessionMessageIdentity, N as readSessionMessageSequence, O as SessionProjectionEntry, P as gatewayCredentialScope, R as buildDeviceAuthPayloadV3, S as SessionProjectionRunTransition, T as readSessionProjectionFinalMessageIdentity, _ as reconcileSessionProjectionSnapshot, a as GatewaySessionMessageSubscriptionOptions, b as projectSessionTerminalReplyMessage, c as resetGatewaySessionMessageSubscriptionCoordinator, d as SessionProjectionRunStatus, f as SessionProjectionScope, g as projectLiveSessionMessage, h as createSessionProjection, i as GatewaySessionMessageSubscriptionCoordinatorOptions, j as readAssistantStreamSegmentIdentity, k as isLocallyOptimisticSessionMessage, l as SessionProjectionEvent, m as SessionProjectionState, n as GatewaySessionMessageSubscription, o as getGatewaySessionMessageSubscriptionCoordinator, p as SessionProjectionSnapshotOptions, r as GatewaySessionMessageSubscriptionCoordinator, s as releaseGatewaySessionMessageSubscription, t as GatewaySessionMessageRequestClient, u as SessionProjectionRun, v as reduceSessionProjection, w as hasSessionProjectionAcceptedFinal, x as SessionProjectionGatewayRunEvent, y as isSessionProjectionErrorMessage, z as normalizeDeviceMetadataForAuth } from "./session-subscriptions-Bcq6_wtR.mjs";
|
|
2
2
|
import { a as GatewayBrowserDeviceTokenStore, c as resolveGatewayConnectScopes, i as GatewayBrowserDeviceTokenRecord, l as selectGatewayConnectAuth, n as GatewayBrowserDeviceAuthPlan, o as GatewayConnectAuthSelection, r as GatewayBrowserDeviceIdentity, s as buildGatewayConnectAuth, t as GatewayBrowserDeviceAuthLifecycle, u as shouldRetryGatewayWithDeviceToken } from "./browser-device-auth-_UweR4aN.mjs";
|
|
3
3
|
import { i as isGatewayProtocolResponseError } from "./protocol-request-CtVhe3Bj.mjs";
|
|
4
|
-
import { n as GatewayClientCloseInfo, r as GatewayClientConnectionMetadata } from "./protocol-client-
|
|
4
|
+
import { n as GatewayClientCloseInfo, r as GatewayClientConnectionMetadata } from "./protocol-client-CKMLpN-S.mjs";
|
|
5
5
|
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, clearGatewayConnectTimeout, getConnectChallengeTimeoutMsFromEnv, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, startGatewayConnectTimeout } from "./timeouts.mjs";
|
|
6
|
-
import { _ as isGatewayConnectAssemblyError, a as startGatewayClientWithReadinessWait, c as waitForEventLoopReady, d as GatewayClientHostDeps, f as GatewayClientOptions, g as GatewayClientRequestError, h as GatewayReconnectPausedInfo, i as startGatewayClientWhenEventLoopReady, l as DeviceIdentity, m as GatewayClientRequestTimeoutError, n as GatewayClientStartReadinessOptions, o as EventLoopReadyOptions, p as GatewayClientRequestOptions, r as GatewayClientStartable, s as EventLoopReadyResult, t as EventLoopReadyWaiter, u as GatewayClient, v as DeviceAuthTokenRecord } from "./readiness-
|
|
6
|
+
import { _ as isGatewayConnectAssemblyError, a as startGatewayClientWithReadinessWait, c as waitForEventLoopReady, d as GatewayClientHostDeps, f as GatewayClientOptions, g as GatewayClientRequestError, h as GatewayReconnectPausedInfo, i as startGatewayClientWhenEventLoopReady, l as DeviceIdentity, m as GatewayClientRequestTimeoutError, n as GatewayClientStartReadinessOptions, o as EventLoopReadyOptions, p as GatewayClientRequestOptions, r as GatewayClientStartable, s as EventLoopReadyResult, t as EventLoopReadyWaiter, u as GatewayClient, v as DeviceAuthTokenRecord } from "./readiness-BbuB7IzG.mjs";
|
|
7
7
|
import { GatewayScopeUpgrade, ScopeUpgradeBinding, ScopeUpgradeOptions, ScopeUpgradeOutcome } from "./scope-upgrade.mjs";
|
|
8
8
|
//#region packages/gateway-client/src/run-recovery-text.d.ts
|
|
9
9
|
export type RecoveryRequest = (method: string, params: Record<string, unknown>, signal: AbortSignal) => Promise<unknown>;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import {
|
|
1
|
+
import { A as buildGatewayConnectAuth, B as normalizeDeviceMetadataForAuth, C as isGatewayProtocolResponseError, D as gatewayCredentialScope, E as resolveModelCatalogConnect, I as asRecord, L as isRecord, M as selectGatewayConnectAuth, N as shouldRetryGatewayWithDeviceToken, O as gatewayOriginScope, P as mergeChatStreamMessage, R as buildDeviceAuthPayload, S as GatewayProtocolRequestTimeoutError, T as raceWithTimeout, _ as isSessionProjectionErrorMessage, a as createSessionProjection, b as GatewayProtocolClient, c as reduceSessionProjection, d as readSessionProjectionFinalMessageIdentity, f as isLocallyOptimisticSessionMessage, g as readSessionMessageSequence, h as readSessionMessageIdentity, i as resetGatewaySessionMessageSubscriptionCoordinator, j as resolveGatewayConnectScopes, k as GatewayBrowserDeviceAuthLifecycle, l as reduceSessionProjectionRunEvent, m as readAssistantStreamSegmentIdentity, n as getGatewaySessionMessageSubscriptionCoordinator, o as projectLiveSessionMessage, p as normalizeSessionProjectionRunId, r as releaseGatewaySessionMessageSubscription, s as reconcileSessionProjectionSnapshot, t as GatewaySessionMessageSubscriptionCoordinator, u as hasSessionProjectionAcceptedFinal, v as projectSessionTerminalReplyMessage, w as createAbortError, x as GatewayProtocolRequestError, y as shouldPauseGatewayReconnect, z as buildDeviceAuthPayloadV3 } from "./session-subscriptions-DE0w2upu.mjs";
|
|
2
|
+
import { _ as normalizeOptionalString, a as MIN_CONNECT_CHALLENGE_TIMEOUT_MS, c as clearGatewayConnectTimeout, d as resolveFiniteTimeoutDelayMs, f as resolvePreauthHandshakeTimeoutMs, h as normalizeLowercaseStringOrEmpty, i as MAX_SAFE_TIMEOUT_DELAY_MS, l as getConnectChallengeTimeoutMsFromEnv, m as startGatewayConnectTimeout, n as DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, o as addSafeTimeoutDelayGraceMs, p as resolveSafeTimeoutDelayMs, r as MAX_CONNECT_CHALLENGE_TIMEOUT_MS, s as clampConnectChallengeTimeoutMs, t as DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, u as resolveConnectChallengeTimeoutMs } from "./timeouts-CQTCye5U.mjs";
|
|
3
3
|
import { rawDataToString } from "./websocket-data.mjs";
|
|
4
|
-
import { n as startGatewayClientWithReadinessWait, r as waitForEventLoopReady, t as startGatewayClientWhenEventLoopReady } from "./readiness-
|
|
4
|
+
import { n as startGatewayClientWithReadinessWait, r as waitForEventLoopReady, t as startGatewayClientWhenEventLoopReady } from "./readiness-rG6_5Ynk.mjs";
|
|
5
5
|
import { GatewayScopeUpgrade } from "./scope-upgrade.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { ConnectErrorDetailCodes, formatConnectErrorMessage, readConnectErrorDetailCode } from "@openclaw/gateway-protocol/connect-error-details";
|
|
@@ -78,9 +78,7 @@ function normalizeUrlQueryParamName(name) {
|
|
|
78
78
|
}
|
|
79
79
|
function looksLikeNestedUrlValue(value) {
|
|
80
80
|
if (URL_SCHEME_RE.test(value)) return true;
|
|
81
|
-
const
|
|
82
|
-
const backwardAuthorityIndex = value.indexOf("\\\\");
|
|
83
|
-
const authorityIndex = forwardAuthorityIndex < 0 ? backwardAuthorityIndex : backwardAuthorityIndex < 0 ? forwardAuthorityIndex : Math.min(forwardAuthorityIndex, backwardAuthorityIndex);
|
|
81
|
+
const authorityIndex = value.search(/\/\/|\\\\/u);
|
|
84
82
|
if (authorityIndex >= 0 && value.includes("@", authorityIndex + 2)) return true;
|
|
85
83
|
const queryIndex = value.search(/[?&]/u);
|
|
86
84
|
if (queryIndex >= 0 && value.includes("=", queryIndex + 1)) return true;
|
|
@@ -89,7 +87,7 @@ function looksLikeNestedUrlValue(value) {
|
|
|
89
87
|
return /%[\da-f]{2}/iu.test(value);
|
|
90
88
|
}
|
|
91
89
|
/** True for auth-like URL query parameter names that should be redacted. */
|
|
92
|
-
function isSensitiveUrlQueryParamName
|
|
90
|
+
function isSensitiveUrlQueryParamName(name) {
|
|
93
91
|
const normalized = normalizeUrlQueryParamName(name);
|
|
94
92
|
return normalized.unresolvedEncoding || SENSITIVE_URL_QUERY_PARAM_NAMES.has(normalized.value) || SUFFIXED_OR_SCOPED_TOKEN_QUERY_PARAM_RE.test(normalized.value);
|
|
95
93
|
}
|
|
@@ -107,7 +105,7 @@ function redactDirectSensitiveUrl(value) {
|
|
|
107
105
|
parsed.password = parsed.password ? "***" : "";
|
|
108
106
|
mutated = true;
|
|
109
107
|
}
|
|
110
|
-
for (const key of Array.from(parsed.searchParams.keys())) if (isSensitiveUrlQueryParamName
|
|
108
|
+
for (const key of Array.from(parsed.searchParams.keys())) if (isSensitiveUrlQueryParamName(key)) {
|
|
111
109
|
parsed.searchParams.set(key, "***");
|
|
112
110
|
mutated = true;
|
|
113
111
|
}
|
|
@@ -118,12 +116,11 @@ function redactDirectSensitiveUrl(value) {
|
|
|
118
116
|
}
|
|
119
117
|
function redactQueryString(value, depth) {
|
|
120
118
|
const params = new URLSearchParams(value);
|
|
121
|
-
const entries = Array.from(params.entries());
|
|
122
119
|
const redactedEntries = [];
|
|
123
120
|
const seenSensitiveKeys = /* @__PURE__ */ new Set();
|
|
124
121
|
let mutated = false;
|
|
125
|
-
for (const [key, entryValue] of
|
|
126
|
-
if (isSensitiveUrlQueryParamName
|
|
122
|
+
for (const [key, entryValue] of params) {
|
|
123
|
+
if (isSensitiveUrlQueryParamName(key)) {
|
|
127
124
|
mutated = true;
|
|
128
125
|
if (!seenSensitiveKeys.has(key)) {
|
|
129
126
|
seenSensitiveKeys.add(key);
|
|
@@ -140,7 +137,7 @@ function redactQueryString(value, depth) {
|
|
|
140
137
|
return new URLSearchParams(redactedEntries).toString();
|
|
141
138
|
}
|
|
142
139
|
function redactUrlLikeFallback(value) {
|
|
143
|
-
return redactSensitiveUrlPath(redactEmbeddedUrlUserInfo(value).replace(/([?&])([^=&]+)=([^&]*)/g, (match, prefix, key) => isSensitiveUrlQueryParamName
|
|
140
|
+
return redactSensitiveUrlPath(redactEmbeddedUrlUserInfo(value).replace(/([?&])([^=&]+)=([^&]*)/g, (match, prefix, key) => isSensitiveUrlQueryParamName(key) ? `${prefix}${key}=***` : match));
|
|
144
141
|
}
|
|
145
142
|
function redactAuthorityUserInfo(candidate, authorityStart) {
|
|
146
143
|
const authority = candidate.slice(authorityStart);
|
|
@@ -312,10 +309,6 @@ function redactSensitiveUrlLikeString(value) {
|
|
|
312
309
|
return redactSensitiveUrlLikeStringAtDepth(value, 0).value;
|
|
313
310
|
}
|
|
314
311
|
ipaddr.IPv4.parse("198.18.0.0");
|
|
315
|
-
function stripIpv6Brackets(value) {
|
|
316
|
-
if (value.startsWith("[") && value.endsWith("]")) return value.slice(1, -1);
|
|
317
|
-
return value;
|
|
318
|
-
}
|
|
319
312
|
/** Type guard for parsed IPv6 addresses. */
|
|
320
313
|
function isIpv6Address(address) {
|
|
321
314
|
return address.kind() === "ipv6";
|
|
@@ -326,7 +319,7 @@ function normalizeIpv4MappedAddress(address) {
|
|
|
326
319
|
function normalizeIpParseInput(raw) {
|
|
327
320
|
const trimmed = normalizeOptionalString(raw);
|
|
328
321
|
if (!trimmed) return;
|
|
329
|
-
return
|
|
322
|
+
return trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
|
|
330
323
|
}
|
|
331
324
|
/** Parses canonical IPv4/IPv6 literals, rejecting legacy IPv4 shorthand forms. */
|
|
332
325
|
function parseCanonicalIpAddress(raw) {
|
|
@@ -349,18 +342,12 @@ function isLoopbackIpAddress(raw) {
|
|
|
349
342
|
}
|
|
350
343
|
//#endregion
|
|
351
344
|
//#region packages/gateway-client/src/client-address-utils.ts
|
|
352
|
-
function normalizeGatewayErrorText(value) {
|
|
353
|
-
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
354
|
-
}
|
|
355
|
-
function isSensitiveUrlQueryParamName(key) {
|
|
356
|
-
return /(?:token|password|secret|key|auth|credential)/iu.test(key);
|
|
357
|
-
}
|
|
358
345
|
function isGatewayClientStoppedError(err) {
|
|
359
346
|
const message = err instanceof Error ? err.message : String(err);
|
|
360
347
|
return message === "gateway client stopped" || message === "Error: gateway client stopped";
|
|
361
348
|
}
|
|
362
349
|
function formatGatewayClientErrorForLog(err) {
|
|
363
|
-
return String(err).replace(/\/\/([^@/?#\s]+)@/g, "//***:***@").replace(/(Authorization:\s*Bearer\s+)[^\s]+/giu, "$1***").replace(/([?&])([^=&\s]+)=([^&#\s"'<>)]*)/g, (match, prefix, key) =>
|
|
350
|
+
return String(err).replace(/\/\/([^@/?#\s]+)@/g, "//***:***@").replace(/(Authorization:\s*Bearer\s+)[^\s]+/giu, "$1***").replace(/([?&])([^=&\s]+)=([^&#\s"'<>)]*)/g, (match, prefix, key) => /(?:token|password|secret|key|auth|credential)/iu.test(key) ? `${prefix}${key}=***` : match);
|
|
364
351
|
}
|
|
365
352
|
const SHA256_HEX_FINGERPRINT = /^[a-fA-F0-9]{64}$/u;
|
|
366
353
|
const SHA256_COLON_FINGERPRINT = /^(?:[a-fA-F0-9]{2}:){31}[a-fA-F0-9]{2}$/u;
|
|
@@ -523,12 +510,7 @@ function isGatewayConnectAssemblyError(value) {
|
|
|
523
510
|
//#endregion
|
|
524
511
|
//#region packages/net-policy/src/url-protocol.ts
|
|
525
512
|
function parseUrl(value) {
|
|
526
|
-
|
|
527
|
-
try {
|
|
528
|
-
return new URL(value);
|
|
529
|
-
} catch {
|
|
530
|
-
return null;
|
|
531
|
-
}
|
|
513
|
+
return value instanceof URL ? value : URL.parse(value);
|
|
532
514
|
}
|
|
533
515
|
function isWssUrl(value) {
|
|
534
516
|
return parseUrl(value)?.protocol === "wss:";
|
|
@@ -567,17 +549,14 @@ function isTrustedPlaintextWebSocketHost(hostname) {
|
|
|
567
549
|
return normalized.endsWith(".local") || normalized.endsWith(".ts.net");
|
|
568
550
|
}
|
|
569
551
|
function isSecureWebSocketUrl(rawUrl, options) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
} catch {
|
|
579
|
-
return false;
|
|
580
|
-
}
|
|
552
|
+
const url = URL.parse(rawUrl);
|
|
553
|
+
if (!url) return false;
|
|
554
|
+
const protocol = url.protocol === "https:" ? "wss:" : url.protocol === "http:" ? "ws:" : url.protocol;
|
|
555
|
+
if (protocol === "wss:") return true;
|
|
556
|
+
if (protocol !== "ws:") return false;
|
|
557
|
+
if (isTrustedPlaintextWebSocketHost(url.hostname)) return true;
|
|
558
|
+
if (options?.allowPrivateWs === true) return parseGatewayIpAddress(url.hostname.startsWith("[") && url.hostname.endsWith("]") ? url.hostname.slice(1, -1) : url.hostname) === void 0;
|
|
559
|
+
return false;
|
|
581
560
|
}
|
|
582
561
|
var GatewayWebSocketTransportConfigurationError = class extends Error {};
|
|
583
562
|
var GatewayWebSocketTlsPinError = class extends Error {};
|
|
@@ -585,13 +564,7 @@ function resolveGatewayWebSocketTransport(params) {
|
|
|
585
564
|
const usesTls = isWssUrl(params.url);
|
|
586
565
|
if (params.tlsFingerprint && !usesTls) throw new GatewayWebSocketTransportConfigurationError("gateway tls fingerprint requires wss:// gateway url");
|
|
587
566
|
const allowPrivateWs = (params.env ?? process.env).OPENCLAW_ALLOW_INSECURE_PRIVATE_WS === "1";
|
|
588
|
-
if (!isSecureWebSocketUrl(params.url, { allowPrivateWs })) {
|
|
589
|
-
let displayHost = params.url;
|
|
590
|
-
try {
|
|
591
|
-
displayHost = new URL(params.url).hostname || params.url;
|
|
592
|
-
} catch {}
|
|
593
|
-
throw new GatewayWebSocketTransportConfigurationError(`SECURITY ERROR: Cannot connect to "${displayHost}" over plaintext ws://. Both credentials and chat data would be exposed to network interception. Use wss:// for remote URLs. Safe defaults: keep gateway.bind=loopback and connect via SSH tunnel (ssh -N -L 18789:127.0.0.1:18789 user@gateway-host), or use Tailscale Serve/Funnel. ` + (allowPrivateWs ? "" : "Break-glass (trusted private networks only): set OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1. ") + "Run `openclaw doctor --fix` for guidance.");
|
|
594
|
-
}
|
|
567
|
+
if (!isSecureWebSocketUrl(params.url, { allowPrivateWs })) throw new GatewayWebSocketTransportConfigurationError(`SECURITY ERROR: Cannot connect to "${URL.parse(params.url)?.hostname || params.url}" over plaintext ws://. Both credentials and chat data would be exposed to network interception. Use wss:// for remote URLs. Safe defaults: keep gateway.bind=loopback and connect via SSH tunnel (ssh -N -L 18789:127.0.0.1:18789 user@gateway-host), or use Tailscale Serve/Funnel. ` + (allowPrivateWs ? "" : "Break-glass (trusted private networks only): set OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1. ") + "Run `openclaw doctor --fix` for guidance.");
|
|
595
568
|
const normalize = params.normalizeTlsFingerprint ?? normalizeTlsFingerprint;
|
|
596
569
|
const expectedFingerprint = params.tlsFingerprint ? normalizeTlsFingerprint(params.tlsFingerprint) : void 0;
|
|
597
570
|
if (params.tlsFingerprint && !expectedFingerprint) throw new GatewayWebSocketTransportConfigurationError("gateway tls fingerprint must be a SHA-256 fingerprint");
|
|
@@ -719,7 +692,7 @@ var GatewayClient = class {
|
|
|
719
692
|
timeoutMs,
|
|
720
693
|
requestSent
|
|
721
694
|
}),
|
|
722
|
-
createRequestAbortError:
|
|
695
|
+
createRequestAbortError: (method) => createAbortError(`gateway request aborted for ${method}`),
|
|
723
696
|
buildConnectPlan: ({ nonce, challengeTs, serverCapabilities, generation, ...authority }) => {
|
|
724
697
|
if (!nonce) throw new Error("gateway connect challenge missing nonce");
|
|
725
698
|
if (this.opts.deviceIdentity && challengeTs == null) throw new Error("gateway connect challenge timestamp invalid");
|
|
@@ -954,7 +927,15 @@ var GatewayClient = class {
|
|
|
954
927
|
const ws = this.ws;
|
|
955
928
|
this.ws = null;
|
|
956
929
|
if (ws) {
|
|
957
|
-
|
|
930
|
+
let resolve = () => {};
|
|
931
|
+
const pendingStop = {
|
|
932
|
+
ws,
|
|
933
|
+
promise: new Promise((done) => {
|
|
934
|
+
resolve = done;
|
|
935
|
+
}),
|
|
936
|
+
resolve
|
|
937
|
+
};
|
|
938
|
+
this.pendingStop = pendingStop;
|
|
958
939
|
const forceTerminateTimer = setTimeout(() => {
|
|
959
940
|
try {
|
|
960
941
|
ws.terminate();
|
|
@@ -975,18 +956,6 @@ var GatewayClient = class {
|
|
|
975
956
|
this.protocol.stop();
|
|
976
957
|
return null;
|
|
977
958
|
}
|
|
978
|
-
createPendingStop(ws) {
|
|
979
|
-
let resolve = () => {};
|
|
980
|
-
const promise = new Promise((done) => {
|
|
981
|
-
resolve = done;
|
|
982
|
-
});
|
|
983
|
-
this.pendingStop = {
|
|
984
|
-
ws,
|
|
985
|
-
promise,
|
|
986
|
-
resolve
|
|
987
|
-
};
|
|
988
|
-
return this.pendingStop;
|
|
989
|
-
}
|
|
990
959
|
resolvePendingStop(ws) {
|
|
991
960
|
if (this.pendingStop?.ws !== ws) return;
|
|
992
961
|
const { resolve, terminateTimer } = this.pendingStop;
|
|
@@ -1076,7 +1045,7 @@ var GatewayClient = class {
|
|
|
1076
1045
|
shouldRetryWithAlternateNodeProtocol(error) {
|
|
1077
1046
|
if (!this.shouldNegotiateLegacyNodeProtocol() || !(error instanceof GatewayClientRequestError)) return false;
|
|
1078
1047
|
const detailCode = readConnectErrorDetailCode(error.details);
|
|
1079
|
-
return error.details?.expectedProtocol === (this.useLegacyNodeProtocolEnvelope ? PROTOCOL_VERSION : MIN_NODE_PROTOCOL_VERSION) && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH ||
|
|
1048
|
+
return error.details?.expectedProtocol === (this.useLegacyNodeProtocolEnvelope ? PROTOCOL_VERSION : MIN_NODE_PROTOCOL_VERSION) && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || normalizeLowercaseStringOrEmpty(error.message).includes("protocol mismatch"));
|
|
1080
1049
|
}
|
|
1081
1050
|
buildDeviceConnectParams(params) {
|
|
1082
1051
|
if (!this.opts.deviceIdentity) return;
|
|
@@ -1292,7 +1261,7 @@ var GatewayClient = class {
|
|
|
1292
1261
|
}
|
|
1293
1262
|
clearStaleDeviceTokenForClose(context) {
|
|
1294
1263
|
const { code, reason, generation } = context;
|
|
1295
|
-
if (code !== 1008 || !
|
|
1264
|
+
if (code !== 1008 || !normalizeLowercaseStringOrEmpty(reason).includes("device token mismatch") || !this.opts.deviceIdentity) return;
|
|
1296
1265
|
const observation = this.connectionStoredToken?.generation === generation ? this.connectionStoredToken : { token: void 0 };
|
|
1297
1266
|
this.clearDeviceToken(this.opts.deviceIdentity.deviceId, observation, () => !this.opts.token && !this.opts.password);
|
|
1298
1267
|
}
|
|
@@ -1334,7 +1303,7 @@ var GatewayClient = class {
|
|
|
1334
1303
|
}
|
|
1335
1304
|
isUnsupportedConnectAuthField(error, field) {
|
|
1336
1305
|
if (!(error instanceof GatewayClientRequestError) || error.gatewayCode !== "INVALID_REQUEST") return false;
|
|
1337
|
-
const message =
|
|
1306
|
+
const message = normalizeLowercaseStringOrEmpty(error.message);
|
|
1338
1307
|
return message.includes("invalid connect params") && message.includes(field);
|
|
1339
1308
|
}
|
|
1340
1309
|
isTrustedDeviceRetryEndpoint() {
|
|
@@ -1389,11 +1358,6 @@ var GatewayClient = class {
|
|
|
1389
1358
|
});
|
|
1390
1359
|
}
|
|
1391
1360
|
};
|
|
1392
|
-
function createGatewayRequestAbortError(method) {
|
|
1393
|
-
const err = /* @__PURE__ */ new Error(`gateway request aborted for ${method}`);
|
|
1394
|
-
err.name = "AbortError";
|
|
1395
|
-
return err;
|
|
1396
|
-
}
|
|
1397
1361
|
//#endregion
|
|
1398
1362
|
//#region packages/gateway-client/src/run-recovery-text.ts
|
|
1399
1363
|
async function recoverTerminalReply(params) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as GatewayProtocolRequestOptions, r as GatewayProtocolRequestTimeoutError, t as GatewayProtocolRequestError } from "./protocol-request-CtVhe3Bj.mjs";
|
|
2
|
-
import { l as GatewayProtocolRequestTiming, n as GatewayClientCloseInfo, r as GatewayClientConnectionMetadata } from "./protocol-client-
|
|
2
|
+
import { l as GatewayProtocolRequestTiming, n as GatewayClientCloseInfo, r as GatewayClientConnectionMetadata } from "./protocol-client-CKMLpN-S.mjs";
|
|
3
3
|
import { ConnectParams, ErrorShape, EventFrame, HelloOk } from "@openclaw/gateway-protocol/frame-guards";
|
|
4
4
|
import { GatewayClientMode, GatewayClientName } from "@openclaw/gateway-protocol/client-info";
|
|
5
5
|
import "ws";
|
|
@@ -171,7 +171,6 @@ declare class GatewayClient {
|
|
|
171
171
|
timeoutMs?: number;
|
|
172
172
|
}): Promise<void>;
|
|
173
173
|
private beginStop;
|
|
174
|
-
private createPendingStop;
|
|
175
174
|
private resolvePendingStop;
|
|
176
175
|
private logDebug;
|
|
177
176
|
private logError;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as resolveFiniteTimeoutDelayMs, u as resolveConnectChallengeTimeoutMs } from "./timeouts-CQTCye5U.mjs";
|
|
2
2
|
//#region packages/gateway-client/src/event-loop-ready.ts
|
|
3
3
|
const DEFAULT_MAX_WAIT_MS = 1e4;
|
|
4
4
|
const DEFAULT_INTERVAL_MS = 1;
|
package/dist/readiness.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as startGatewayClientWithReadinessWait, i as startGatewayClientWhenEventLoopReady, n as GatewayClientStartReadinessOptions, r as GatewayClientStartable, t as EventLoopReadyWaiter } from "./readiness-
|
|
1
|
+
import { a as startGatewayClientWithReadinessWait, i as startGatewayClientWhenEventLoopReady, n as GatewayClientStartReadinessOptions, r as GatewayClientStartable, t as EventLoopReadyWaiter } from "./readiness-BbuB7IzG.mjs";
|
|
2
2
|
export { EventLoopReadyWaiter, GatewayClientStartReadinessOptions, GatewayClientStartable, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait };
|
package/dist/readiness.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import { n as startGatewayClientWithReadinessWait, t as startGatewayClientWhenEventLoopReady } from "./readiness-DnB0NLBq.mjs";
|
|
1
|
+
import { n as startGatewayClientWithReadinessWait, t as startGatewayClientWhenEventLoopReady } from "./readiness-rG6_5Ynk.mjs";
|
|
3
2
|
export { startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as normalizeOptionalString, c as clearGatewayConnectTimeout, g as normalizeNullableString, m as startGatewayConnectTimeout, p as resolveSafeTimeoutDelayMs, t as DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS } from "./timeouts-CQTCye5U.mjs";
|
|
2
2
|
import { ConnectErrorDetailCodes, readConnectErrorDetailCode, readConnectErrorRecoveryAdvice, readPairingConnectErrorDetails } from "@openclaw/gateway-protocol/connect-error-details";
|
|
3
3
|
import { isGatewayEventFrame, isGatewayResponseFrame } from "@openclaw/gateway-protocol/frame-guards";
|
|
4
|
-
import { GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS, isGatewaySuspendUnavailableError } from "@openclaw/gateway-protocol/restart-unavailable";
|
|
4
|
+
import { GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS, isGatewayRestartUnavailableError, isGatewaySuspendUnavailableError } from "@openclaw/gateway-protocol/restart-unavailable";
|
|
5
5
|
import { GATEWAY_ASSISTANT_ERROR_FALLBACK_TEXT } from "@openclaw/gateway-protocol/gateway-error-details";
|
|
6
6
|
//#region packages/gateway-client/src/device-auth.ts
|
|
7
7
|
function normalizeDeviceMetadataForAuth(value) {
|
|
@@ -100,26 +100,6 @@ function mergeChatStreamMessage(previous, payload) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
//#endregion
|
|
103
|
-
//#region packages/normalization-core/src/string-coerce.ts
|
|
104
|
-
/** Trims string input and returns null for non-strings or empty strings. */
|
|
105
|
-
function normalizeNullableString(value) {
|
|
106
|
-
if (typeof value !== "string") return null;
|
|
107
|
-
const trimmed = value.trim();
|
|
108
|
-
return trimmed ? trimmed : null;
|
|
109
|
-
}
|
|
110
|
-
/** Trims string input and returns undefined for non-strings or empty strings. */
|
|
111
|
-
function normalizeOptionalString(value) {
|
|
112
|
-
return normalizeNullableString(value) ?? void 0;
|
|
113
|
-
}
|
|
114
|
-
/** Lowercases a normalized optional string. */
|
|
115
|
-
function normalizeOptionalLowercaseString(value) {
|
|
116
|
-
return normalizeOptionalString(value)?.toLowerCase();
|
|
117
|
-
}
|
|
118
|
-
/** Lowercases a normalized string or returns an empty string when absent. */
|
|
119
|
-
function normalizeLowercaseStringOrEmpty(value) {
|
|
120
|
-
return normalizeOptionalLowercaseString(value) ?? "";
|
|
121
|
-
}
|
|
122
|
-
//#endregion
|
|
123
103
|
//#region packages/gateway-client/src/connect-auth.ts
|
|
124
104
|
function selectGatewayConnectAuth(params) {
|
|
125
105
|
const authToken = normalizeOptionalString(params.token);
|
|
@@ -267,15 +247,12 @@ var GatewayBrowserDeviceAuthLifecycle = class {
|
|
|
267
247
|
function normalizeGatewayScope(gatewayUrl, includeSearch) {
|
|
268
248
|
const trimmed = gatewayUrl.trim();
|
|
269
249
|
if (!trimmed) return "default";
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
} catch {
|
|
277
|
-
return trimmed;
|
|
278
|
-
}
|
|
250
|
+
const browserLocation = globalThis.location;
|
|
251
|
+
const base = browserLocation ? `${browserLocation.protocol}//${browserLocation.host}${browserLocation.pathname || "/"}` : void 0;
|
|
252
|
+
const parsed = URL.parse(trimmed, base);
|
|
253
|
+
if (!parsed) return trimmed;
|
|
254
|
+
const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/+$/, "") || parsed.pathname;
|
|
255
|
+
return `${parsed.protocol}//${parsed.host}${pathname}${includeSearch ? parsed.search : ""}`;
|
|
279
256
|
}
|
|
280
257
|
/** Normalizes the gateway URL scope used for origin-bound device tokens. */
|
|
281
258
|
function gatewayOriginScope(gatewayUrl) {
|
|
@@ -309,20 +286,39 @@ function createAbortError(message, options) {
|
|
|
309
286
|
/** Bounds observation without cancelling work; a factory starts only after the timer is armed. */
|
|
310
287
|
async function raceWithTimeout(operation, timeoutMs, onTimeout, options = {}) {
|
|
311
288
|
let timer;
|
|
289
|
+
let onAbort;
|
|
312
290
|
try {
|
|
313
291
|
const deadline = new Promise((resolve, reject) => {
|
|
314
|
-
|
|
292
|
+
const settle = (result) => {
|
|
315
293
|
try {
|
|
316
|
-
resolve(
|
|
294
|
+
resolve(result());
|
|
317
295
|
} catch (error) {
|
|
318
296
|
reject(error);
|
|
319
297
|
}
|
|
320
|
-
}
|
|
298
|
+
};
|
|
299
|
+
timer = setTimeout(() => settle(onTimeout), timeoutMs);
|
|
321
300
|
if (options.ref === false) timer.unref?.();
|
|
301
|
+
const signal = options.signal;
|
|
302
|
+
if (signal) {
|
|
303
|
+
onAbort = () => settle(() => {
|
|
304
|
+
if (options.onAbort) return options.onAbort(signal);
|
|
305
|
+
throw createAbortError("Operation aborted", { cause: signal.reason });
|
|
306
|
+
});
|
|
307
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
308
|
+
if (signal.aborted) onAbort();
|
|
309
|
+
}
|
|
322
310
|
});
|
|
323
|
-
|
|
311
|
+
let source;
|
|
312
|
+
try {
|
|
313
|
+
source = typeof operation === "function" ? operation() : operation;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
deadline.catch(() => {});
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
return await Promise.race([source, deadline]);
|
|
324
319
|
} finally {
|
|
325
320
|
clearTimeout(timer);
|
|
321
|
+
if (onAbort) options.signal?.removeEventListener("abort", onAbort);
|
|
326
322
|
}
|
|
327
323
|
}
|
|
328
324
|
function computeBackoff(policy, attempt) {
|
|
@@ -577,17 +573,35 @@ var GatewayPendingRequests = class {
|
|
|
577
573
|
this.requestSequence = 0;
|
|
578
574
|
}
|
|
579
575
|
setSuspensionPhase(phase) {
|
|
580
|
-
if (phase === "accepting") this.
|
|
581
|
-
else if (phase === "preparing" || phase === "draining" || phase === "prepared") this.
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
this.
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
576
|
+
if (phase === "accepting") this.resumeBootstrapRequests();
|
|
577
|
+
else if (phase === "preparing" || phase === "draining" || phase === "prepared") this.pauseBootstrapRequests(void 0, true);
|
|
578
|
+
}
|
|
579
|
+
pauseBootstrapRequests(retryAfterMs = GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS, announced = false) {
|
|
580
|
+
const nowMs = this.opts.nowMs();
|
|
581
|
+
const delayMs = resolveSafeTimeoutDelayMs(Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? retryAfterMs : GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS);
|
|
582
|
+
const pause = this.bootstrapPause ??= {
|
|
583
|
+
retryAtMs: nowMs,
|
|
584
|
+
revision: 0,
|
|
585
|
+
announced
|
|
586
|
+
};
|
|
587
|
+
pause.announced ||= announced;
|
|
588
|
+
pause.revision += 1;
|
|
589
|
+
pause.retryAtMs = Math.max(pause.retryAtMs, nowMs + delayMs);
|
|
590
|
+
clearTimeout(pause.timer);
|
|
591
|
+
pause.timer = void 0;
|
|
592
|
+
if (pause.announced) return;
|
|
593
|
+
pause.timer = setTimeout(() => {
|
|
594
|
+
pause.timer = void 0;
|
|
595
|
+
this.drainBootstrapRequests();
|
|
596
|
+
}, resolveSafeTimeoutDelayMs(pause.retryAtMs - nowMs));
|
|
597
|
+
pause.timer.unref?.();
|
|
598
|
+
}
|
|
599
|
+
resumeBootstrapRequests() {
|
|
600
|
+
clearTimeout(this.bootstrapPause?.timer);
|
|
601
|
+
this.bootstrapPause = void 0;
|
|
602
|
+
this.drainBootstrapRequests();
|
|
603
|
+
}
|
|
604
|
+
drainBootstrapRequests() {
|
|
591
605
|
for (const pending of this.pending.values()) if (pending.waitingForResume) pending.resend?.();
|
|
592
606
|
}
|
|
593
607
|
get hasPending() {
|
|
@@ -622,6 +636,10 @@ var GatewayPendingRequests = class {
|
|
|
622
636
|
const cleanup = () => {
|
|
623
637
|
if (timeout !== void 0) clearTimeout(timeout);
|
|
624
638
|
options?.signal?.removeEventListener("abort", onAbort);
|
|
639
|
+
if (this.bootstrapPause?.probe?.pending === pending) {
|
|
640
|
+
this.bootstrapPause.probe = void 0;
|
|
641
|
+
this.drainBootstrapRequests();
|
|
642
|
+
}
|
|
625
643
|
};
|
|
626
644
|
const retire = (errorCode) => {
|
|
627
645
|
if (this.pending.get(id) !== pending) return false;
|
|
@@ -660,8 +678,13 @@ var GatewayPendingRequests = class {
|
|
|
660
678
|
params
|
|
661
679
|
});
|
|
662
680
|
const send = () => {
|
|
663
|
-
if (this.pending.get(id) !== pending) return;
|
|
664
|
-
|
|
681
|
+
if (this.pending.get(id) !== pending || options?.signal?.aborted || pending.waitingForResume && timeoutMs !== void 0 && this.opts.nowMs() - pending.startedAtMs >= timeoutMs) return;
|
|
682
|
+
const pause = pending.resend ? this.bootstrapPause : void 0;
|
|
683
|
+
if (pause && !pause.announced && !pause.timer && !pause.probe) pause.probe = {
|
|
684
|
+
pending,
|
|
685
|
+
revision: pause.revision
|
|
686
|
+
};
|
|
687
|
+
pending.waitingForResume = Boolean(pause && (pause.announced || pause.probe?.pending !== pending));
|
|
665
688
|
if (pending.waitingForResume) return;
|
|
666
689
|
try {
|
|
667
690
|
sender.send(frame);
|
|
@@ -672,7 +695,7 @@ var GatewayPendingRequests = class {
|
|
|
672
695
|
if (retire("CLIENT_SEND_ERROR")) reject(error instanceof Error ? error : new Error(String(error)));
|
|
673
696
|
}
|
|
674
697
|
};
|
|
675
|
-
if (method === "agent.identity.get") pending.resend = send;
|
|
698
|
+
if (method === "agent.identity.get" || method === "sessions.subscribe" || method === "sessions.groups.list" || method === "question.list" || method === "sessions.list") pending.resend = send;
|
|
676
699
|
send();
|
|
677
700
|
} catch (error) {
|
|
678
701
|
if (retire("CLIENT_SEND_ERROR")) reject(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -690,12 +713,14 @@ var GatewayPendingRequests = class {
|
|
|
690
713
|
}
|
|
691
714
|
return;
|
|
692
715
|
}
|
|
693
|
-
if (!frame.ok && pending.resend && !pending.acceptedNotified && frame.error?.code === "UNAVAILABLE" && frame.error.retryable === true && isGatewaySuspendUnavailableError(frame.error)) {
|
|
694
|
-
this.
|
|
716
|
+
if (!frame.ok && pending.resend && !pending.acceptedNotified && frame.error?.code === "UNAVAILABLE" && frame.error.retryable === true && (isGatewaySuspendUnavailableError(frame.error) || isGatewayRestartUnavailableError(frame.error))) {
|
|
717
|
+
if (this.bootstrapPause?.probe?.pending === pending) this.bootstrapPause.probe = void 0;
|
|
718
|
+
this.pauseBootstrapRequests(isGatewayRestartUnavailableError(frame.error) ? Math.ceil(Math.max(GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS, frame.error.retryAfterMs ?? 0) * (1 + Math.random() * .2)) : frame.error.retryAfterMs);
|
|
695
719
|
pending.waitingForResume = true;
|
|
696
720
|
return;
|
|
697
721
|
}
|
|
698
722
|
this.pending.delete(frame.id);
|
|
723
|
+
if (frame.ok && this.bootstrapPause?.probe?.pending === pending && this.bootstrapPause.probe.revision === this.bootstrapPause.revision) this.resumeBootstrapRequests();
|
|
699
724
|
pending.cleanup?.();
|
|
700
725
|
if (frame.ok) {
|
|
701
726
|
this.finishTiming(frame.id, pending, true);
|
|
@@ -708,8 +733,8 @@ var GatewayPendingRequests = class {
|
|
|
708
733
|
pending.reject(error);
|
|
709
734
|
}
|
|
710
735
|
flush(error) {
|
|
711
|
-
clearTimeout(this.
|
|
712
|
-
this.
|
|
736
|
+
clearTimeout(this.bootstrapPause?.timer);
|
|
737
|
+
this.bootstrapPause = void 0;
|
|
713
738
|
const retired = this.pending;
|
|
714
739
|
this.pending = /* @__PURE__ */ new Map();
|
|
715
740
|
this.requestSequence = 0;
|
|
@@ -1004,6 +1029,10 @@ var GatewayProtocolClient = class {
|
|
|
1004
1029
|
error: requestError,
|
|
1005
1030
|
reconnectDelayMs: decision.reconnectDelayMs
|
|
1006
1031
|
};
|
|
1032
|
+
if (decision.keepOpen) {
|
|
1033
|
+
this.clearHandshakeTimer();
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1007
1036
|
if (decision.stop) this.stopped = true;
|
|
1008
1037
|
this.connectionAbort?.abort();
|
|
1009
1038
|
socket.close(decision.closeCode, decision.closeReason);
|
|
@@ -1046,8 +1075,8 @@ var GatewayProtocolClient = class {
|
|
|
1046
1075
|
this.sendConnect(socket, generation);
|
|
1047
1076
|
return;
|
|
1048
1077
|
}
|
|
1049
|
-
const seq =
|
|
1050
|
-
if (seq !==
|
|
1078
|
+
const seq = parsed.seq;
|
|
1079
|
+
if (seq !== void 0) {
|
|
1051
1080
|
if (this.lastSeq !== null && seq > this.lastSeq + 1) {
|
|
1052
1081
|
const expected = this.lastSeq + 1;
|
|
1053
1082
|
const state = asRecord(parsed.payload).state;
|
|
@@ -1072,6 +1101,7 @@ var GatewayProtocolClient = class {
|
|
|
1072
1101
|
dispatchEvent(socket, generation, event) {
|
|
1073
1102
|
const listeners = this.listeners.snapshot();
|
|
1074
1103
|
if (event.event === "gateway.suspension" && typeof event.payload === "object" && event.payload !== null && "phase" in event.payload) this.requests.setSuspensionPhase(event.payload.phase);
|
|
1104
|
+
else if (event.event === "shutdown" && typeof asRecord(event.payload).restartExpectedMs === "number") this.requests.setSuspensionPhase("draining");
|
|
1075
1105
|
this.invoke("event", () => this.opts.onEvent?.(event));
|
|
1076
1106
|
for (const [listener, subscription] of listeners) {
|
|
1077
1107
|
if (!this.isActive(socket, generation) || this.connectionAbort?.signal.aborted) return;
|
|
@@ -1195,6 +1225,8 @@ const NON_RECOVERABLE_AUTH_ERRORS = /* @__PURE__ */ new Set([
|
|
|
1195
1225
|
ConnectErrorDetailCodes.AUTH_VERIFIED_USER_REQUIRED,
|
|
1196
1226
|
ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH,
|
|
1197
1227
|
ConnectErrorDetailCodes.PAIRING_REQUIRED,
|
|
1228
|
+
ConnectErrorDetailCodes.PAIRING_REJECTED,
|
|
1229
|
+
ConnectErrorDetailCodes.PAIRING_EXPIRED,
|
|
1198
1230
|
ConnectErrorDetailCodes.CONTROL_UI_DEVICE_IDENTITY_REQUIRED,
|
|
1199
1231
|
ConnectErrorDetailCodes.DEVICE_IDENTITY_REQUIRED
|
|
1200
1232
|
]);
|
|
@@ -1290,10 +1322,59 @@ function compareStableStrings(left, right) {
|
|
|
1290
1322
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
1291
1323
|
}
|
|
1292
1324
|
//#endregion
|
|
1293
|
-
//#region packages/gateway-client/src/session-projection-message-
|
|
1294
|
-
function
|
|
1295
|
-
|
|
1325
|
+
//#region packages/gateway-client/src/session-projection-message-content.ts
|
|
1326
|
+
function readSessionMessageDisplayContent(message) {
|
|
1327
|
+
const record = asNullableRecord(message);
|
|
1328
|
+
const content = typeof message === "string" ? message : record?.content;
|
|
1329
|
+
const media = asNullableRecord(record?.["__openclaw"])?.media;
|
|
1330
|
+
let hasNonText = Array.isArray(media) && media.length > 0;
|
|
1331
|
+
const texts = [];
|
|
1332
|
+
for (const block of Array.isArray(content) ? content : [content]) {
|
|
1333
|
+
const entry = asNullableRecord(block);
|
|
1334
|
+
if (entry && entry.type !== "text" && entry.type !== "input_text" && entry.type !== "output_text") {
|
|
1335
|
+
hasNonText ||= entry.type !== "thinking" && entry.type !== "reasoning" && entry.type !== "redacted_thinking";
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
const text = normalizeNullableString(entry ? entry.text : block);
|
|
1339
|
+
if (text) texts.push(text);
|
|
1340
|
+
}
|
|
1341
|
+
const fallback = texts.length === 0 ? normalizeNullableString(record?.text) : null;
|
|
1342
|
+
return {
|
|
1343
|
+
text: fallback ?? texts.join("\n"),
|
|
1344
|
+
hasNonText,
|
|
1345
|
+
usesFallbackText: fallback !== null
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
/** Status notices remain visible but do not define the terminal reply they accompany. */
|
|
1349
|
+
function projectSessionTerminalReplyMessage(message) {
|
|
1350
|
+
const record = asNullableRecord(message);
|
|
1351
|
+
if (!record || !Array.isArray(record.content)) return message;
|
|
1352
|
+
const content = record.content.filter((block) => asNullableRecord(block)?.openclawStatusNotice !== true);
|
|
1353
|
+
if (content.length === record.content.length || content.length === 0) return message;
|
|
1354
|
+
return {
|
|
1355
|
+
...record,
|
|
1356
|
+
content,
|
|
1357
|
+
text: void 0
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
/** Check whether a projected message has text or another displayable block. */
|
|
1361
|
+
function hasDisplayableSessionMessage(message) {
|
|
1362
|
+
const { text, hasNonText } = readSessionMessageDisplayContent(message);
|
|
1363
|
+
return Boolean(text) || hasNonText;
|
|
1364
|
+
}
|
|
1365
|
+
function normalizeChatErrorComparisonText(text) {
|
|
1366
|
+
return text.trim().replace(/^⚠️\s*/u, "").replace(/^Error:\s*/iu, "").replace(/\s+/gu, " ").trim();
|
|
1367
|
+
}
|
|
1368
|
+
/** Diagnostic-only error projections can be retired when their run resumes. */
|
|
1369
|
+
function isSessionProjectionErrorMessage(message, errorMessage) {
|
|
1370
|
+
const role = asNullableRecord(message)?.role;
|
|
1371
|
+
if (typeof role === "string" && role.trim().toLowerCase() !== "assistant") return false;
|
|
1372
|
+
const { text, hasNonText } = readSessionMessageDisplayContent(message);
|
|
1373
|
+
const normalizedText = normalizeChatErrorComparisonText(text);
|
|
1374
|
+
return !hasNonText && (!normalizedText || normalizedText === "[assistant turn failed before producing content]" || normalizedText === GATEWAY_ASSISTANT_ERROR_FALLBACK_TEXT || normalizedText === normalizeChatErrorComparisonText(errorMessage ?? ""));
|
|
1296
1375
|
}
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region packages/gateway-client/src/session-projection-message-identity.ts
|
|
1297
1378
|
function readPositiveSafeInteger(value) {
|
|
1298
1379
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
|
|
1299
1380
|
}
|
|
@@ -1303,33 +1384,33 @@ function readSessionMessageSequence(message, envelope) {
|
|
|
1303
1384
|
}
|
|
1304
1385
|
/** Run ownership normalizes a user-turn suffix without changing its persisted send key. */
|
|
1305
1386
|
function normalizeSessionProjectionRunId(value) {
|
|
1306
|
-
const runId =
|
|
1387
|
+
const runId = normalizeNullableString(value);
|
|
1307
1388
|
return runId?.endsWith(":user") ? runId.slice(0, -5) || null : runId;
|
|
1308
1389
|
}
|
|
1309
1390
|
/** Persisted row facts win; assistant run ownership comes from its authoritative producer. */
|
|
1310
1391
|
function readSessionMessageIdentity(message, envelope) {
|
|
1311
1392
|
const record = asNullableRecord(message);
|
|
1312
|
-
const role =
|
|
1393
|
+
const role = normalizeNullableString(record?.role)?.toLowerCase();
|
|
1313
1394
|
if (!record || !role) return null;
|
|
1314
1395
|
const metadata = asNullableRecord(record["__openclaw"]);
|
|
1315
|
-
const importedFrom =
|
|
1316
|
-
const cliSessionId =
|
|
1317
|
-
const externalId =
|
|
1396
|
+
const importedFrom = normalizeNullableString(metadata?.importedFrom);
|
|
1397
|
+
const cliSessionId = normalizeNullableString(metadata?.cliSessionId);
|
|
1398
|
+
const externalId = normalizeNullableString(metadata?.externalId);
|
|
1318
1399
|
const position = asNullableRecord(metadata?.transcriptPosition);
|
|
1319
|
-
const positionSource =
|
|
1400
|
+
const positionSource = normalizeNullableString(position?.source);
|
|
1320
1401
|
const isImported = !(positionSource !== null && positionSource.length <= 128 && typeof position?.rawSeq === "number" && Number.isSafeInteger(position.rawSeq) && position.rawSeq >= 0) && Boolean(importedFrom || cliSessionId || externalId);
|
|
1321
|
-
const idempotencyKey =
|
|
1402
|
+
const idempotencyKey = normalizeNullableString(metadata?.idempotencyKey) ?? normalizeNullableString(record.idempotencyKey) ?? normalizeNullableString(envelope?.idempotencyKey) ?? normalizeNullableString(envelope?.clientRunId);
|
|
1322
1403
|
const persistedRunId = normalizeSessionProjectionRunId(idempotencyKey);
|
|
1323
1404
|
const envelopeRunId = normalizeSessionProjectionRunId(envelope?.runId);
|
|
1324
1405
|
const metadataRunId = normalizeSessionProjectionRunId(metadata?.runId);
|
|
1325
1406
|
const fallbackRunId = normalizeSessionProjectionRunId(asNullableRecord(record.openclawStreamFallback)?.runId);
|
|
1326
|
-
const mirroredMessage =
|
|
1327
|
-
const isCliAssistant = role === "assistant" &&
|
|
1328
|
-
const canonicalPersistedRunId = isCliAssistant && persistedRunId?.startsWith("cli-assistant:") ?
|
|
1407
|
+
const mirroredMessage = normalizeNullableString(metadata?.mirrorOrigin) !== null;
|
|
1408
|
+
const isCliAssistant = role === "assistant" && normalizeNullableString(record.api)?.toLowerCase() === "cli";
|
|
1409
|
+
const canonicalPersistedRunId = isCliAssistant && persistedRunId?.startsWith("cli-assistant:") ? normalizeNullableString(persistedRunId.slice(14)) : persistedRunId;
|
|
1329
1410
|
const runId = role === "assistant" ? metadataRunId ?? envelopeRunId ?? fallbackRunId ?? (isCliAssistant || !mirroredMessage ? canonicalPersistedRunId : null) : metadataRunId ?? canonicalPersistedRunId ?? envelopeRunId;
|
|
1330
1411
|
return {
|
|
1331
1412
|
role,
|
|
1332
|
-
id:
|
|
1413
|
+
id: normalizeNullableString(metadata?.id) ?? normalizeNullableString(envelope?.messageId),
|
|
1333
1414
|
sequence: readSessionMessageSequence(message, envelope),
|
|
1334
1415
|
idempotencyKey,
|
|
1335
1416
|
sendId: role === "user" ? persistedRunId ?? runId : null,
|
|
@@ -1345,11 +1426,11 @@ function readSessionMessageIdentity(message, envelope) {
|
|
|
1345
1426
|
/** A commentary item's display identity is separate from the transcript row that later owns it. */
|
|
1346
1427
|
function readAssistantStreamSegmentIdentity(message) {
|
|
1347
1428
|
const record = asNullableRecord(message);
|
|
1348
|
-
if (
|
|
1429
|
+
if (normalizeNullableString(record?.role)?.toLowerCase() !== "assistant") return;
|
|
1349
1430
|
const fallback = asNullableRecord(record?.openclawStreamFallback);
|
|
1350
|
-
const itemId =
|
|
1431
|
+
const itemId = normalizeNullableString(fallback?.itemId);
|
|
1351
1432
|
if (!itemId) return;
|
|
1352
|
-
const runId = readSessionMessageIdentity(message)?.runId ??
|
|
1433
|
+
const runId = readSessionMessageIdentity(message)?.runId ?? normalizeNullableString(record?.runId) ?? normalizeNullableString(fallback?.runId);
|
|
1353
1434
|
return {
|
|
1354
1435
|
itemId,
|
|
1355
1436
|
...runId ? { runId } : {}
|
|
@@ -1362,7 +1443,7 @@ function sameAssistantPersistenceReceipt(left, right) {
|
|
|
1362
1443
|
/** Local turns have no durable transcript metadata beyond their own optional send key. */
|
|
1363
1444
|
function isLocallyOptimisticSessionMessage(message) {
|
|
1364
1445
|
const record = asNullableRecord(message);
|
|
1365
|
-
const role =
|
|
1446
|
+
const role = normalizeNullableString(record?.role)?.toLowerCase();
|
|
1366
1447
|
if (role !== "user" && role !== "assistant") return false;
|
|
1367
1448
|
if (asNullableRecord(record?.openclawStreamFallback)) return false;
|
|
1368
1449
|
const metadata = asNullableRecord(record?.["__openclaw"]);
|
|
@@ -1395,58 +1476,6 @@ function createSessionProjectionEntry(message, options) {
|
|
|
1395
1476
|
};
|
|
1396
1477
|
}
|
|
1397
1478
|
//#endregion
|
|
1398
|
-
//#region packages/gateway-client/src/session-projection-message-content.ts
|
|
1399
|
-
function readSessionMessageDisplayContent(message) {
|
|
1400
|
-
const record = asNullableRecord(message);
|
|
1401
|
-
const content = typeof message === "string" ? message : record?.content;
|
|
1402
|
-
const media = asNullableRecord(record?.["__openclaw"])?.media;
|
|
1403
|
-
let hasNonText = Array.isArray(media) && media.length > 0;
|
|
1404
|
-
const texts = [];
|
|
1405
|
-
for (const block of Array.isArray(content) ? content : [content]) {
|
|
1406
|
-
const entry = asNullableRecord(block);
|
|
1407
|
-
if (entry && entry.type !== "text" && entry.type !== "input_text" && entry.type !== "output_text") {
|
|
1408
|
-
hasNonText ||= entry.type !== "thinking" && entry.type !== "reasoning" && entry.type !== "redacted_thinking";
|
|
1409
|
-
continue;
|
|
1410
|
-
}
|
|
1411
|
-
const text = readSessionProjectionString(entry ? entry.text : block);
|
|
1412
|
-
if (text) texts.push(text);
|
|
1413
|
-
}
|
|
1414
|
-
const fallback = texts.length === 0 ? readSessionProjectionString(record?.text) : null;
|
|
1415
|
-
return {
|
|
1416
|
-
text: fallback ?? texts.join("\n"),
|
|
1417
|
-
hasNonText,
|
|
1418
|
-
usesFallbackText: fallback !== null
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
/** Status notices remain visible but do not define the terminal reply they accompany. */
|
|
1422
|
-
function projectSessionTerminalReplyMessage(message) {
|
|
1423
|
-
const record = asNullableRecord(message);
|
|
1424
|
-
if (!record || !Array.isArray(record.content)) return message;
|
|
1425
|
-
const content = record.content.filter((block) => asNullableRecord(block)?.openclawStatusNotice !== true);
|
|
1426
|
-
if (content.length === record.content.length || content.length === 0) return message;
|
|
1427
|
-
return {
|
|
1428
|
-
...record,
|
|
1429
|
-
content,
|
|
1430
|
-
text: void 0
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
/** Check whether a projected message has text or another displayable block. */
|
|
1434
|
-
function hasDisplayableSessionMessage(message) {
|
|
1435
|
-
const { text, hasNonText } = readSessionMessageDisplayContent(message);
|
|
1436
|
-
return Boolean(text) || hasNonText;
|
|
1437
|
-
}
|
|
1438
|
-
function normalizeChatErrorComparisonText(text) {
|
|
1439
|
-
return text.trim().replace(/^⚠️\s*/u, "").replace(/^Error:\s*/iu, "").replace(/\s+/gu, " ").trim();
|
|
1440
|
-
}
|
|
1441
|
-
/** Diagnostic-only error projections can be retired when their run resumes. */
|
|
1442
|
-
function isSessionProjectionErrorMessage(message, errorMessage) {
|
|
1443
|
-
const role = asNullableRecord(message)?.role;
|
|
1444
|
-
if (typeof role === "string" && role.trim().toLowerCase() !== "assistant") return false;
|
|
1445
|
-
const { text, hasNonText } = readSessionMessageDisplayContent(message);
|
|
1446
|
-
const normalizedText = normalizeChatErrorComparisonText(text);
|
|
1447
|
-
return !hasNonText && (!normalizedText || normalizedText === "[assistant turn failed before producing content]" || normalizedText === GATEWAY_ASSISTANT_ERROR_FALLBACK_TEXT || normalizedText === normalizeChatErrorComparisonText(errorMessage ?? ""));
|
|
1448
|
-
}
|
|
1449
|
-
//#endregion
|
|
1450
1479
|
//#region packages/gateway-client/src/session-projection-final-identity.ts
|
|
1451
1480
|
/** Terminal identity rules used to reconcile live and durable assistant projections. */
|
|
1452
1481
|
/** A run's selected text reply can inherit its terminal tool stop reason. */
|
|
@@ -1586,7 +1615,7 @@ function retainSessionProjectionRuns(runs) {
|
|
|
1586
1615
|
//#region packages/gateway-client/src/session-projection-run-event.ts
|
|
1587
1616
|
/** Normalizes Gateway run envelopes once for every browser and terminal adapter. */
|
|
1588
1617
|
function reduceSessionProjectionRunEvent(projection, event, scope = {}) {
|
|
1589
|
-
const runId =
|
|
1618
|
+
const runId = normalizeNullableString(event.runId);
|
|
1590
1619
|
if (!runId || typeof event.state !== "string" || ![
|
|
1591
1620
|
"delta",
|
|
1592
1621
|
"final",
|
|
@@ -1594,9 +1623,9 @@ function reduceSessionProjectionRunEvent(projection, event, scope = {}) {
|
|
|
1594
1623
|
"aborted"
|
|
1595
1624
|
].includes(event.state)) return null;
|
|
1596
1625
|
const message = event.state === "delta" ? mergeChatStreamMessage(projection.runs[runId]?.message, event) : event.message;
|
|
1597
|
-
const messageStopReason = isRecord(message) ?
|
|
1598
|
-
const stopReason =
|
|
1599
|
-
const errorKind =
|
|
1626
|
+
const messageStopReason = isRecord(message) ? normalizeNullableString(message.stopReason) : null;
|
|
1627
|
+
const stopReason = normalizeNullableString(event.stopReason) ?? messageStopReason;
|
|
1628
|
+
const errorKind = normalizeNullableString(event.errorKind);
|
|
1600
1629
|
const base = {
|
|
1601
1630
|
runId,
|
|
1602
1631
|
seq: typeof event.seq === "number" ? event.seq : void 0,
|
|
@@ -1675,7 +1704,7 @@ function entryMatches(left, right, allowSnapshotPromotion = false) {
|
|
|
1675
1704
|
const durableSegment = durableEntry === left ? leftSegment : rightSegment;
|
|
1676
1705
|
const provisionalSegment = durableEntry === left ? rightSegment : leftSegment;
|
|
1677
1706
|
if (provisionalEntry.identity.sequence === null && durableSegment?.runId && durableSegment.runId === provisionalSegment?.runId && durableSegment.itemId === provisionalSegment.itemId) return true;
|
|
1678
|
-
if (provisionalEntry.live && !durableSegment && !isSessionProjectionToolContinuation(durableEntry.message) && (!isToolUsePersistedFinalRow(durableEntry.message) || readFinalContentIdentity(durableEntry.message) === readFinalContentIdentity(provisionalEntry.message)) && provisionalEntry.identity.sequence === null && (provisionalEntry.afterSequence === void 0 || provisionalEntry.afterSequence !== null && durableEntry.identity.sequence !== null && durableEntry.identity.sequence > provisionalEntry.afterSequence) && durableEntry.identity.runId && durableEntry.identity.runId === provisionalEntry.identity.runId && (
|
|
1707
|
+
if (provisionalEntry.live && !durableSegment && !isSessionProjectionToolContinuation(durableEntry.message) && (!isToolUsePersistedFinalRow(durableEntry.message) || readFinalContentIdentity(durableEntry.message) === readFinalContentIdentity(provisionalEntry.message)) && provisionalEntry.identity.sequence === null && (provisionalEntry.afterSequence === void 0 || provisionalEntry.afterSequence !== null && durableEntry.identity.sequence !== null && durableEntry.identity.sequence > provisionalEntry.afterSequence) && durableEntry.identity.runId && durableEntry.identity.runId === provisionalEntry.identity.runId && (normalizeNullableString(durableMetadata?.mirrorOrigin) === null || durableMetadata?.runTerminal === true)) return true;
|
|
1679
1708
|
}
|
|
1680
1709
|
const persisted = left.identity;
|
|
1681
1710
|
const observed = right.identity;
|
|
@@ -1819,7 +1848,7 @@ function reconcileSessionProjectionSnapshot(state, messages, scope = {}, options
|
|
|
1819
1848
|
};
|
|
1820
1849
|
}
|
|
1821
1850
|
function updateRun(state, incoming) {
|
|
1822
|
-
const incomingErrorMessage =
|
|
1851
|
+
const incomingErrorMessage = normalizeNullableString(incoming.errorMessage);
|
|
1823
1852
|
const incomingSeq = typeof incoming.seq === "number" && Number.isSafeInteger(incoming.seq) && incoming.seq >= 0 ? incoming.seq : void 0;
|
|
1824
1853
|
const normalizedIncoming = {
|
|
1825
1854
|
...incoming,
|
|
@@ -1836,7 +1865,7 @@ function updateRun(state, incoming) {
|
|
|
1836
1865
|
const canAcceptFinal = currentHasDisplayableMessage ? current.status === incoming.status || (current.acceptedFinalMessageIdentities?.length ?? 0) > 0 : canRecoverSessionProjectionFinal(current.message, incoming.message);
|
|
1837
1866
|
const acceptFinal = incomingIsFinal && canAcceptFinal && incomingFinalIdentity !== null && !hasSessionProjectionAcceptedFinal(current, incoming.message);
|
|
1838
1867
|
const recoverMessage = acceptFinal && !currentHasDisplayableMessage;
|
|
1839
|
-
const recoverError =
|
|
1868
|
+
const recoverError = normalizeNullableString(current.errorMessage) === null && incomingErrorMessage !== null;
|
|
1840
1869
|
const updateTerminalSequence = incoming.status !== "streaming" && (incomingSeq === void 0 ? current.seq !== void 0 : current.seq === void 0 || incomingSeq > current.seq);
|
|
1841
1870
|
if (!acceptFinal && !recoverError && !updateTerminalSequence) return state;
|
|
1842
1871
|
const firstFinalIdentity = readSessionProjectionFinalMessageIdentity(current.message);
|
|
@@ -2280,4 +2309,4 @@ function releaseGatewaySessionMessageSubscription(subscription) {
|
|
|
2280
2309
|
return sessionMessageSubscriptionOwners.get(subscription)?.coordinator.release(subscription) ?? Promise.resolve();
|
|
2281
2310
|
}
|
|
2282
2311
|
//#endregion
|
|
2283
|
-
export {
|
|
2312
|
+
export { buildGatewayConnectAuth as A, normalizeDeviceMetadataForAuth as B, isGatewayProtocolResponseError as C, gatewayCredentialScope as D, resolveModelCatalogConnect as E, asNullableRecord as F, asRecord as I, isRecord as L, selectGatewayConnectAuth as M, shouldRetryGatewayWithDeviceToken as N, gatewayOriginScope as O, mergeChatStreamMessage as P, buildDeviceAuthPayload as R, GatewayProtocolRequestTimeoutError as S, raceWithTimeout as T, isSessionProjectionErrorMessage as _, createSessionProjection as a, GatewayProtocolClient as b, reduceSessionProjection as c, readSessionProjectionFinalMessageIdentity as d, isLocallyOptimisticSessionMessage as f, readSessionMessageSequence as g, readSessionMessageIdentity as h, resetGatewaySessionMessageSubscriptionCoordinator as i, resolveGatewayConnectScopes as j, GatewayBrowserDeviceAuthLifecycle as k, reduceSessionProjectionRunEvent as l, readAssistantStreamSegmentIdentity as m, getGatewaySessionMessageSubscriptionCoordinator as n, projectLiveSessionMessage as o, normalizeSessionProjectionRunId as p, releaseGatewaySessionMessageSubscription as r, reconcileSessionProjectionSnapshot as s, GatewaySessionMessageSubscriptionCoordinator as t, hasSessionProjectionAcceptedFinal as u, projectSessionTerminalReplyMessage as v, createAbortError as w, GatewayProtocolRequestError as x, shouldPauseGatewayReconnect as y, buildDeviceAuthPayloadV3 as z };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
//#region packages/normalization-core/src/string-coerce.ts
|
|
2
|
+
/** Trims string input and returns null for non-strings or empty strings. */
|
|
3
|
+
function normalizeNullableString(value) {
|
|
4
|
+
if (typeof value !== "string") return null;
|
|
5
|
+
const trimmed = value.trim();
|
|
6
|
+
return trimmed ? trimmed : null;
|
|
7
|
+
}
|
|
8
|
+
/** Trims string input and returns undefined for non-strings or empty strings. */
|
|
9
|
+
function normalizeOptionalString(value) {
|
|
10
|
+
return normalizeNullableString(value) ?? void 0;
|
|
11
|
+
}
|
|
12
|
+
/** Lowercases a normalized optional string. */
|
|
13
|
+
function normalizeOptionalLowercaseString(value) {
|
|
14
|
+
return normalizeOptionalString(value)?.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
/** Lowercases a normalized string or returns an empty string when absent. */
|
|
17
|
+
function normalizeLowercaseStringOrEmpty(value) {
|
|
18
|
+
return normalizeOptionalLowercaseString(value) ?? "";
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region packages/normalization-core/src/number-coercion.ts
|
|
22
|
+
/** Parses only safe integer numbers or base-10 integer strings. */
|
|
23
|
+
function parseStrictInteger(value) {
|
|
24
|
+
if (typeof value === "number") return Number.isSafeInteger(value) ? value : void 0;
|
|
25
|
+
const normalized = normalizeOptionalString(value);
|
|
26
|
+
if (!normalized || !/^[+-]?\d+$/.test(normalized)) return;
|
|
27
|
+
const parsed = Number(normalized);
|
|
28
|
+
return Number.isSafeInteger(parsed) ? parsed : void 0;
|
|
29
|
+
}
|
|
30
|
+
function parseStrictPositiveInteger(value) {
|
|
31
|
+
const parsed = parseStrictInteger(value);
|
|
32
|
+
return parsed !== void 0 && parsed > 0 ? parsed : void 0;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region packages/gateway-client/src/timeouts.ts
|
|
36
|
+
function isTestRuntimeEnv(env) {
|
|
37
|
+
return env.VITEST === "true" || env.VITEST === "1" || env.VITEST_POOL_ID !== void 0 || env.VITEST_WORKER_ID !== void 0 || env.NODE_ENV === "test" || env !== process.env && isTestRuntimeEnv(process.env);
|
|
38
|
+
}
|
|
39
|
+
/** Maximum delay Node timers can represent without overflow warnings. */
|
|
40
|
+
const MAX_SAFE_TIMEOUT_DELAY_MS = 2147483647;
|
|
41
|
+
/** Default server-side window for gateway preauth handshakes. */
|
|
42
|
+
const DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
43
|
+
/** Starts the browser-safe deadline that covers Gateway connect preparation and hello. */
|
|
44
|
+
function startGatewayConnectTimeout(onTimeout) {
|
|
45
|
+
const timer = setTimeout(onTimeout, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS);
|
|
46
|
+
timer.unref?.();
|
|
47
|
+
return timer;
|
|
48
|
+
}
|
|
49
|
+
/** Clears either pending Gateway handshake phase without retaining its timer. */
|
|
50
|
+
function clearGatewayConnectTimeout(timer) {
|
|
51
|
+
if (timer !== null) clearTimeout(timer);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/** Default deadline for a single non-streaming Gateway request. */
|
|
55
|
+
const DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS = 3e4;
|
|
56
|
+
/** Minimum client watchdog delay for connect challenge setup. */
|
|
57
|
+
const MIN_CONNECT_CHALLENGE_TIMEOUT_MS = 250;
|
|
58
|
+
/** Default maximum client watchdog delay, aligned with the preauth server timeout. */
|
|
59
|
+
const MAX_CONNECT_CHALLENGE_TIMEOUT_MS = DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS;
|
|
60
|
+
/** Clamps arbitrary timer delays to Node's safe range and an optional floor. */
|
|
61
|
+
function resolveSafeTimeoutDelayMs(delayMs, opts) {
|
|
62
|
+
const rawMinMs = opts?.minMs ?? 1;
|
|
63
|
+
const minMs = Math.min(MAX_SAFE_TIMEOUT_DELAY_MS, Math.max(0, Number.isFinite(rawMinMs) ? Math.floor(rawMinMs) : 1));
|
|
64
|
+
return Math.min(MAX_SAFE_TIMEOUT_DELAY_MS, Math.max(minMs, Number.isFinite(delayMs) ? Math.floor(delayMs) : minMs));
|
|
65
|
+
}
|
|
66
|
+
/** Adds grace time while preserving safe timer bounds if inputs overflow or are invalid. */
|
|
67
|
+
function addSafeTimeoutDelayGraceMs(delayMs, graceMs, opts) {
|
|
68
|
+
if (!Number.isFinite(delayMs) || !Number.isFinite(graceMs)) return resolveSafeTimeoutDelayMs(MAX_SAFE_TIMEOUT_DELAY_MS, opts);
|
|
69
|
+
const withGrace = delayMs + graceMs;
|
|
70
|
+
return resolveSafeTimeoutDelayMs(Number.isFinite(withGrace) ? withGrace : MAX_SAFE_TIMEOUT_DELAY_MS, opts);
|
|
71
|
+
}
|
|
72
|
+
/** Resolves optional timeout values through a fallback and safe timer clamp. */
|
|
73
|
+
function resolveFiniteTimeoutDelayMs(delayMs, fallbackMs, opts) {
|
|
74
|
+
return resolveSafeTimeoutDelayMs(typeof delayMs === "number" && Number.isFinite(delayMs) ? delayMs : fallbackMs, opts);
|
|
75
|
+
}
|
|
76
|
+
/** Clamps connect challenge watchdog timeouts to the gateway-supported range. */
|
|
77
|
+
function clampConnectChallengeTimeoutMs(timeoutMs, maxTimeoutMs = MAX_CONNECT_CHALLENGE_TIMEOUT_MS) {
|
|
78
|
+
return Math.max(250, Math.min(Math.max(250, maxTimeoutMs), timeoutMs));
|
|
79
|
+
}
|
|
80
|
+
/** Reads the connect challenge watchdog override from the process environment. */
|
|
81
|
+
function getConnectChallengeTimeoutMsFromEnv(env = process.env) {
|
|
82
|
+
const raw = env.OPENCLAW_CONNECT_CHALLENGE_TIMEOUT_MS;
|
|
83
|
+
if (raw) {
|
|
84
|
+
const parsed = parseStrictPositiveInteger(raw);
|
|
85
|
+
if (parsed !== void 0) return resolveSafeTimeoutDelayMs(parsed);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function normalizePositiveTimeoutMs(timeoutMs) {
|
|
89
|
+
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? resolveSafeTimeoutDelayMs(timeoutMs) : void 0;
|
|
90
|
+
}
|
|
91
|
+
/** Resolves the client watchdog timeout using explicit, env, then preauth defaults. */
|
|
92
|
+
function resolveConnectChallengeTimeoutMs(timeoutMs, params) {
|
|
93
|
+
const configuredPreauthTimeoutMs = resolvePreauthHandshakeTimeoutMs({
|
|
94
|
+
env: params?.env,
|
|
95
|
+
configuredTimeoutMs: params?.configuredTimeoutMs
|
|
96
|
+
});
|
|
97
|
+
const maxTimeoutMs = Math.max(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, configuredPreauthTimeoutMs);
|
|
98
|
+
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) return clampConnectChallengeTimeoutMs(timeoutMs, maxTimeoutMs);
|
|
99
|
+
const envOverride = getConnectChallengeTimeoutMsFromEnv(params?.env);
|
|
100
|
+
if (envOverride !== void 0) return clampConnectChallengeTimeoutMs(envOverride, Math.max(maxTimeoutMs, envOverride));
|
|
101
|
+
return clampConnectChallengeTimeoutMs(configuredPreauthTimeoutMs, maxTimeoutMs);
|
|
102
|
+
}
|
|
103
|
+
/** Resolves the server preauth timeout from env, explicit config, or default. */
|
|
104
|
+
function resolvePreauthHandshakeTimeoutMs(params) {
|
|
105
|
+
const env = params?.env ?? process.env;
|
|
106
|
+
const configuredTimeout = env.OPENCLAW_HANDSHAKE_TIMEOUT_MS || (isTestRuntimeEnv(env) ? env.OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS : void 0);
|
|
107
|
+
if (configuredTimeout) {
|
|
108
|
+
const parsed = parseStrictPositiveInteger(configuredTimeout);
|
|
109
|
+
if (parsed !== void 0) return resolveSafeTimeoutDelayMs(parsed);
|
|
110
|
+
}
|
|
111
|
+
const configured = normalizePositiveTimeoutMs(params?.configuredTimeoutMs);
|
|
112
|
+
if (configured !== void 0) return configured;
|
|
113
|
+
return DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { normalizeOptionalString as _, MIN_CONNECT_CHALLENGE_TIMEOUT_MS as a, clearGatewayConnectTimeout as c, resolveFiniteTimeoutDelayMs as d, resolvePreauthHandshakeTimeoutMs as f, normalizeNullableString as g, normalizeLowercaseStringOrEmpty as h, MAX_SAFE_TIMEOUT_DELAY_MS as i, getConnectChallengeTimeoutMsFromEnv as l, startGatewayConnectTimeout as m, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS as n, addSafeTimeoutDelayGraceMs as o, resolveSafeTimeoutDelayMs as p, MAX_CONNECT_CHALLENGE_TIMEOUT_MS as r, clampConnectChallengeTimeoutMs as s, DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS as t, resolveConnectChallengeTimeoutMs as u };
|
package/dist/timeouts.mjs
CHANGED
|
@@ -1,88 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function parsePositiveTimeoutSetting(value) {
|
|
3
|
-
const trimmed = value.trim();
|
|
4
|
-
if (!/^\+?\d+$/u.test(trimmed)) return;
|
|
5
|
-
const parsed = Number(trimmed);
|
|
6
|
-
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
7
|
-
}
|
|
8
|
-
function isTestRuntimeEnv(env) {
|
|
9
|
-
return env.VITEST === "true" || env.VITEST === "1" || env.VITEST_POOL_ID !== void 0 || env.VITEST_WORKER_ID !== void 0 || env.NODE_ENV === "test" || env !== process.env && isTestRuntimeEnv(process.env);
|
|
10
|
-
}
|
|
11
|
-
/** Maximum delay Node timers can represent without overflow warnings. */
|
|
12
|
-
const MAX_SAFE_TIMEOUT_DELAY_MS = 2147483647;
|
|
13
|
-
/** Default server-side window for gateway preauth handshakes. */
|
|
14
|
-
const DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
15
|
-
/** Starts the browser-safe deadline that covers Gateway connect preparation and hello. */
|
|
16
|
-
function startGatewayConnectTimeout(onTimeout) {
|
|
17
|
-
const timer = setTimeout(onTimeout, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS);
|
|
18
|
-
timer.unref?.();
|
|
19
|
-
return timer;
|
|
20
|
-
}
|
|
21
|
-
/** Clears either pending Gateway handshake phase without retaining its timer. */
|
|
22
|
-
function clearGatewayConnectTimeout(timer) {
|
|
23
|
-
if (timer !== null) clearTimeout(timer);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
/** Default deadline for a single non-streaming Gateway request. */
|
|
27
|
-
const DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS = 3e4;
|
|
28
|
-
/** Minimum client watchdog delay for connect challenge setup. */
|
|
29
|
-
const MIN_CONNECT_CHALLENGE_TIMEOUT_MS = 250;
|
|
30
|
-
/** Default maximum client watchdog delay, aligned with the preauth server timeout. */
|
|
31
|
-
const MAX_CONNECT_CHALLENGE_TIMEOUT_MS = DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS;
|
|
32
|
-
/** Clamps arbitrary timer delays to Node's safe range and an optional floor. */
|
|
33
|
-
function resolveSafeTimeoutDelayMs(delayMs, opts) {
|
|
34
|
-
const rawMinMs = opts?.minMs ?? 1;
|
|
35
|
-
const minMs = Math.min(MAX_SAFE_TIMEOUT_DELAY_MS, Math.max(0, Number.isFinite(rawMinMs) ? Math.floor(rawMinMs) : 1));
|
|
36
|
-
return Math.min(MAX_SAFE_TIMEOUT_DELAY_MS, Math.max(minMs, Number.isFinite(delayMs) ? Math.floor(delayMs) : minMs));
|
|
37
|
-
}
|
|
38
|
-
/** Adds grace time while preserving safe timer bounds if inputs overflow or are invalid. */
|
|
39
|
-
function addSafeTimeoutDelayGraceMs(delayMs, graceMs, opts) {
|
|
40
|
-
if (!Number.isFinite(delayMs) || !Number.isFinite(graceMs)) return resolveSafeTimeoutDelayMs(MAX_SAFE_TIMEOUT_DELAY_MS, opts);
|
|
41
|
-
const withGrace = delayMs + graceMs;
|
|
42
|
-
return resolveSafeTimeoutDelayMs(Number.isFinite(withGrace) ? withGrace : MAX_SAFE_TIMEOUT_DELAY_MS, opts);
|
|
43
|
-
}
|
|
44
|
-
/** Resolves optional timeout values through a fallback and safe timer clamp. */
|
|
45
|
-
function resolveFiniteTimeoutDelayMs(delayMs, fallbackMs, opts) {
|
|
46
|
-
return resolveSafeTimeoutDelayMs(typeof delayMs === "number" && Number.isFinite(delayMs) ? delayMs : fallbackMs, opts);
|
|
47
|
-
}
|
|
48
|
-
/** Clamps connect challenge watchdog timeouts to the gateway-supported range. */
|
|
49
|
-
function clampConnectChallengeTimeoutMs(timeoutMs, maxTimeoutMs = MAX_CONNECT_CHALLENGE_TIMEOUT_MS) {
|
|
50
|
-
return Math.max(250, Math.min(Math.max(250, maxTimeoutMs), timeoutMs));
|
|
51
|
-
}
|
|
52
|
-
/** Reads the connect challenge watchdog override from the process environment. */
|
|
53
|
-
function getConnectChallengeTimeoutMsFromEnv(env = process.env) {
|
|
54
|
-
const raw = env.OPENCLAW_CONNECT_CHALLENGE_TIMEOUT_MS;
|
|
55
|
-
if (raw) {
|
|
56
|
-
const parsed = parsePositiveTimeoutSetting(raw);
|
|
57
|
-
if (parsed !== void 0) return resolveSafeTimeoutDelayMs(parsed);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function normalizePositiveTimeoutMs(timeoutMs) {
|
|
61
|
-
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? resolveSafeTimeoutDelayMs(timeoutMs) : void 0;
|
|
62
|
-
}
|
|
63
|
-
/** Resolves the client watchdog timeout using explicit, env, then preauth defaults. */
|
|
64
|
-
function resolveConnectChallengeTimeoutMs(timeoutMs, params) {
|
|
65
|
-
const configuredPreauthTimeoutMs = resolvePreauthHandshakeTimeoutMs({
|
|
66
|
-
env: params?.env,
|
|
67
|
-
configuredTimeoutMs: params?.configuredTimeoutMs
|
|
68
|
-
});
|
|
69
|
-
const maxTimeoutMs = Math.max(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, configuredPreauthTimeoutMs);
|
|
70
|
-
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) return clampConnectChallengeTimeoutMs(timeoutMs, maxTimeoutMs);
|
|
71
|
-
const envOverride = getConnectChallengeTimeoutMsFromEnv(params?.env);
|
|
72
|
-
if (envOverride !== void 0) return clampConnectChallengeTimeoutMs(envOverride, Math.max(maxTimeoutMs, envOverride));
|
|
73
|
-
return clampConnectChallengeTimeoutMs(configuredPreauthTimeoutMs, maxTimeoutMs);
|
|
74
|
-
}
|
|
75
|
-
/** Resolves the server preauth timeout from env, explicit config, or default. */
|
|
76
|
-
function resolvePreauthHandshakeTimeoutMs(params) {
|
|
77
|
-
const env = params?.env ?? process.env;
|
|
78
|
-
const configuredTimeout = env.OPENCLAW_HANDSHAKE_TIMEOUT_MS || (isTestRuntimeEnv(env) ? env.OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS : void 0);
|
|
79
|
-
if (configuredTimeout) {
|
|
80
|
-
const parsed = parsePositiveTimeoutSetting(configuredTimeout);
|
|
81
|
-
if (parsed !== void 0) return resolveSafeTimeoutDelayMs(parsed);
|
|
82
|
-
}
|
|
83
|
-
const configured = normalizePositiveTimeoutMs(params?.configuredTimeoutMs);
|
|
84
|
-
if (configured !== void 0) return configured;
|
|
85
|
-
return DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS;
|
|
86
|
-
}
|
|
87
|
-
//#endregion
|
|
1
|
+
import { a as MIN_CONNECT_CHALLENGE_TIMEOUT_MS, c as clearGatewayConnectTimeout, d as resolveFiniteTimeoutDelayMs, f as resolvePreauthHandshakeTimeoutMs, i as MAX_SAFE_TIMEOUT_DELAY_MS, l as getConnectChallengeTimeoutMsFromEnv, m as startGatewayConnectTimeout, n as DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, o as addSafeTimeoutDelayGraceMs, p as resolveSafeTimeoutDelayMs, r as MAX_CONNECT_CHALLENGE_TIMEOUT_MS, s as clampConnectChallengeTimeoutMs, t as DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, u as resolveConnectChallengeTimeoutMs } from "./timeouts-CQTCye5U.mjs";
|
|
88
2
|
export { 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, clearGatewayConnectTimeout, getConnectChallengeTimeoutMsFromEnv, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, startGatewayConnectTimeout };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/gateway-client",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.5-beta.1",
|
|
4
4
|
"description": "Reference WebSocket client for the OpenClaw Gateway protocol",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"client",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@openclaw/gateway-protocol": "2026.10.
|
|
65
|
+
"@openclaw/gateway-protocol": "2026.10.5-beta.1",
|
|
66
66
|
"ipaddr.js": "2.5.0",
|
|
67
|
-
"ws": "8.
|
|
67
|
+
"ws": "8.22.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@openclaw/normalization-core": "0.0.0-private"
|