mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
perf(ui): reduce startup connect error overhead (#123910)
This commit is contained in:
committed by
GitHub
parent
b8a8df7008
commit
0982ee5729
@@ -11,7 +11,7 @@ import {
|
||||
formatConnectPairingRequiredMessage,
|
||||
normalizePairingConnectRequestId,
|
||||
readConnectErrorDetailCode,
|
||||
readControlUiBuildMismatchDetails,
|
||||
readControlUiBuildMismatchId,
|
||||
readConnectErrorRecoveryAdvice,
|
||||
readConnectPairingRequiredMessage,
|
||||
readPairingConnectErrorDetails,
|
||||
@@ -44,19 +44,15 @@ describe("readConnectErrorDetailCode", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("readControlUiBuildMismatchDetails", () => {
|
||||
describe("readControlUiBuildMismatchId", () => {
|
||||
it("returns a bounded reload target", () => {
|
||||
expect(
|
||||
readControlUiBuildMismatchDetails({
|
||||
readControlUiBuildMismatchId({
|
||||
code: ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH,
|
||||
gatewayBuildId: "gateway-build",
|
||||
reloadRequired: true,
|
||||
}),
|
||||
).toEqual({
|
||||
code: ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH,
|
||||
gatewayBuildId: "gateway-build",
|
||||
reloadRequired: true,
|
||||
});
|
||||
).toBe("gateway-build");
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -73,7 +69,7 @@ describe("readControlUiBuildMismatchDetails", () => {
|
||||
reloadRequired: false,
|
||||
},
|
||||
])("rejects malformed details", (details) => {
|
||||
expect(readControlUiBuildMismatchDetails(details)).toBeNull();
|
||||
expect(readControlUiBuildMismatchId(details)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -55,12 +55,6 @@ export const ConnectErrorDetailCodes = {
|
||||
type ConnectErrorDetailCode =
|
||||
(typeof ConnectErrorDetailCodes)[keyof typeof ConnectErrorDetailCodes];
|
||||
|
||||
export type ControlUiBuildMismatchDetails = {
|
||||
code: typeof ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH;
|
||||
gatewayBuildId: string;
|
||||
reloadRequired: true;
|
||||
};
|
||||
|
||||
/** Pairing-specific reasons clients can display and use for reconnect policy. */
|
||||
const ConnectPairingRequiredReasons = {
|
||||
NOT_PAIRED: "not-paired",
|
||||
@@ -237,15 +231,8 @@ export function readConnectErrorDetailCode(details: unknown): string | null {
|
||||
}
|
||||
|
||||
/** Read the exact target artifact from an untrusted reload-required rejection. */
|
||||
export function readControlUiBuildMismatchDetails(
|
||||
details: unknown,
|
||||
): ControlUiBuildMismatchDetails | null {
|
||||
if (
|
||||
readConnectErrorDetailCode(details) !== ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH ||
|
||||
!details ||
|
||||
typeof details !== "object" ||
|
||||
Array.isArray(details)
|
||||
) {
|
||||
export function readControlUiBuildMismatchId(details: unknown): string | null {
|
||||
if (readConnectErrorDetailCode(details) !== ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH) {
|
||||
return null;
|
||||
}
|
||||
const raw = details as { gatewayBuildId?: unknown; reloadRequired?: unknown };
|
||||
@@ -253,11 +240,7 @@ export function readControlUiBuildMismatchDetails(
|
||||
if (!gatewayBuildId || gatewayBuildId.length > 96 || raw.reloadRequired !== true) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
code: ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH,
|
||||
gatewayBuildId,
|
||||
reloadRequired: true,
|
||||
};
|
||||
return gatewayBuildId;
|
||||
}
|
||||
|
||||
/** Extracts normalized retry advice from untrusted connect-error details. */
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import {
|
||||
ConnectErrorDetailCodes,
|
||||
GatewayProtocolRequestError,
|
||||
MIN_CLIENT_PROTOCOL_VERSION,
|
||||
PROTOCOL_VERSION,
|
||||
readConnectErrorDetailCode,
|
||||
shouldPauseGatewayReconnect,
|
||||
} from "@openclaw/gateway-client/browser";
|
||||
|
||||
export function enrichProtocolMismatchDetails(
|
||||
@@ -30,22 +28,3 @@ export function resolveGatewayErrorDetailCode(
|
||||
): string | null {
|
||||
return readConnectErrorDetailCode(error?.details);
|
||||
}
|
||||
|
||||
export function isLegacyGatewayBuildIdSchemaError(
|
||||
error: GatewayProtocolRequestError,
|
||||
clientBuildId: string | undefined,
|
||||
): boolean {
|
||||
return Boolean(
|
||||
clientBuildId && /invalid connect params.*unexpected property.*buildid/iu.test(error.message),
|
||||
);
|
||||
}
|
||||
|
||||
/** Token mismatch stays with its bounded retry owner; static failures pause. */
|
||||
export function isNonRecoverableConnectError(error: { details?: unknown } | undefined): boolean {
|
||||
return error
|
||||
? shouldPauseGatewayReconnect({
|
||||
details: error.details,
|
||||
protocolMismatchIsTerminal: true,
|
||||
})
|
||||
: false;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
PROTOCOL_VERSION,
|
||||
resolveGatewayStartupRetryAfterMs,
|
||||
resolveSafeTimeoutDelayMs,
|
||||
shouldPauseGatewayReconnect,
|
||||
} from "@openclaw/gateway-client/browser";
|
||||
export type { EventFrame as GatewayEventFrame } from "@openclaw/gateway-client/browser";
|
||||
import type {
|
||||
@@ -54,8 +55,6 @@ import { generateUUID } from "../lib/uuid.ts";
|
||||
import { createBrowserGatewaySocket } from "./gateway-browser-socket.ts";
|
||||
import {
|
||||
enrichProtocolMismatchDetails,
|
||||
isLegacyGatewayBuildIdSchemaError,
|
||||
isNonRecoverableConnectError,
|
||||
resolveGatewayErrorDetailCode,
|
||||
} from "./gateway-connect-errors.ts";
|
||||
|
||||
@@ -288,7 +287,6 @@ export class GatewayBrowserClient {
|
||||
// Older shipped Gateways used a closed client schema. Downgrade once per
|
||||
// browser client; a document reload creates a fresh exact-identity attempt.
|
||||
private clientBuildIdCompatibilityDisabled = false;
|
||||
private clientBuildIdRetryBudgetUsed = false;
|
||||
// Close/stop advances this generation before another socket can make stale hello work look active.
|
||||
private recovery = { value: "", resolved: false, generation: 0 };
|
||||
private scopeUpgradeBinding: ScopeUpgradeBinding | null = null;
|
||||
@@ -380,7 +378,6 @@ export class GatewayBrowserClient {
|
||||
this.pendingDeviceTokenRetry = false;
|
||||
this.deviceTokenRetryBudgetUsed = false;
|
||||
this.clientBuildIdCompatibilityDisabled = false;
|
||||
this.clientBuildIdRetryBudgetUsed = false;
|
||||
}
|
||||
|
||||
get connected() {
|
||||
@@ -587,11 +584,11 @@ export class GatewayBrowserClient {
|
||||
const connectErrorCode =
|
||||
err instanceof GatewayRequestError ? resolveGatewayErrorDetailCode(err) : null;
|
||||
if (
|
||||
!this.clientBuildIdRetryBudgetUsed &&
|
||||
isLegacyGatewayBuildIdSchemaError(err, plan.params.client.buildId)
|
||||
!this.clientBuildIdCompatibilityDisabled &&
|
||||
plan.params.client.buildId &&
|
||||
/invalid connect params.*unexpected property.*buildid/iu.test(err.message)
|
||||
) {
|
||||
this.clientBuildIdCompatibilityDisabled = true;
|
||||
this.clientBuildIdRetryBudgetUsed = true;
|
||||
this.client.resetReconnectBackoff(250);
|
||||
return { closeCode: CONNECT_FAILED_CLOSE_CODE, closeReason: "connect retry" };
|
||||
}
|
||||
@@ -725,7 +722,10 @@ export class GatewayBrowserClient {
|
||||
const retry =
|
||||
connectErrorCode === ConnectErrorDetailCodes.AUTH_TOKEN_MISMATCH
|
||||
? this.pendingDeviceTokenRetry
|
||||
: !isNonRecoverableConnectError(connectError);
|
||||
: !shouldPauseGatewayReconnect({
|
||||
details: connectError?.details,
|
||||
protocolMismatchIsTerminal: true,
|
||||
});
|
||||
return { retry, notify: true, pendingError: error };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { readControlUiBuildMismatchDetails } from "@openclaw/gateway-client/browser";
|
||||
import { readControlUiBuildMismatchId } from "@openclaw/gateway-client/browser";
|
||||
import type { ControlUiBootstrapProfileHint } from "../../../src/gateway/control-ui-contract.js";
|
||||
// Control UI module owns the application gateway store: the reactive
|
||||
// snapshot around GatewayBrowserClient consumed by the app shell.
|
||||
@@ -431,9 +431,9 @@ export function createApplicationGateway(
|
||||
return;
|
||||
}
|
||||
stopCanvasSurfaceLease();
|
||||
const buildMismatch = readControlUiBuildMismatchDetails(error?.details);
|
||||
if (buildMismatch) {
|
||||
void scheduleStaleChunkReload({ buildId: buildMismatch.gatewayBuildId });
|
||||
const mismatchedBuildId = readControlUiBuildMismatchId(error?.details);
|
||||
if (mismatchedBuildId) {
|
||||
void scheduleStaleChunkReload({ buildId: mismatchedBuildId });
|
||||
}
|
||||
setSnapshot({
|
||||
...snapshot,
|
||||
|
||||
Reference in New Issue
Block a user