mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 03:10:42 +00:00
fix(gateway): harden token fallback/reconnect behavior and docs (#42507)
* fix(gateway): harden token fallback and auth reconnect handling * docs(gateway): clarify auth retry and token-drift recovery * fix(gateway): tighten auth reconnect gating across clients * fix: harden gateway token retry (#42507) (thanks @joshavant)
This commit is contained in:
42
src/gateway/protocol/connect-error-details.test.ts
Normal file
42
src/gateway/protocol/connect-error-details.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
readConnectErrorDetailCode,
|
||||
readConnectErrorRecoveryAdvice,
|
||||
} from "./connect-error-details.js";
|
||||
|
||||
describe("readConnectErrorDetailCode", () => {
|
||||
it("reads structured detail codes", () => {
|
||||
expect(readConnectErrorDetailCode({ code: "AUTH_TOKEN_MISMATCH" })).toBe("AUTH_TOKEN_MISMATCH");
|
||||
});
|
||||
|
||||
it("returns null for invalid detail payloads", () => {
|
||||
expect(readConnectErrorDetailCode(null)).toBeNull();
|
||||
expect(readConnectErrorDetailCode("AUTH_TOKEN_MISMATCH")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("readConnectErrorRecoveryAdvice", () => {
|
||||
it("reads retry advice fields when present", () => {
|
||||
expect(
|
||||
readConnectErrorRecoveryAdvice({
|
||||
canRetryWithDeviceToken: true,
|
||||
recommendedNextStep: "retry_with_device_token",
|
||||
}),
|
||||
).toEqual({
|
||||
canRetryWithDeviceToken: true,
|
||||
recommendedNextStep: "retry_with_device_token",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns empty advice for invalid payloads", () => {
|
||||
expect(readConnectErrorRecoveryAdvice(null)).toEqual({});
|
||||
expect(readConnectErrorRecoveryAdvice("x")).toEqual({});
|
||||
expect(readConnectErrorRecoveryAdvice({ canRetryWithDeviceToken: "yes" })).toEqual({});
|
||||
expect(
|
||||
readConnectErrorRecoveryAdvice({
|
||||
canRetryWithDeviceToken: true,
|
||||
recommendedNextStep: "retry_with_magic",
|
||||
}),
|
||||
).toEqual({ canRetryWithDeviceToken: true, recommendedNextStep: undefined });
|
||||
});
|
||||
});
|
||||
@@ -28,6 +28,26 @@ export const ConnectErrorDetailCodes = {
|
||||
export type ConnectErrorDetailCode =
|
||||
(typeof ConnectErrorDetailCodes)[keyof typeof ConnectErrorDetailCodes];
|
||||
|
||||
export type ConnectRecoveryNextStep =
|
||||
| "retry_with_device_token"
|
||||
| "update_auth_configuration"
|
||||
| "update_auth_credentials"
|
||||
| "wait_then_retry"
|
||||
| "review_auth_configuration";
|
||||
|
||||
export type ConnectErrorRecoveryAdvice = {
|
||||
canRetryWithDeviceToken?: boolean;
|
||||
recommendedNextStep?: ConnectRecoveryNextStep;
|
||||
};
|
||||
|
||||
const CONNECT_RECOVERY_NEXT_STEP_VALUES: ReadonlySet<ConnectRecoveryNextStep> = new Set([
|
||||
"retry_with_device_token",
|
||||
"update_auth_configuration",
|
||||
"update_auth_credentials",
|
||||
"wait_then_retry",
|
||||
"review_auth_configuration",
|
||||
]);
|
||||
|
||||
export function resolveAuthConnectErrorDetailCode(
|
||||
reason: string | undefined,
|
||||
): ConnectErrorDetailCode {
|
||||
@@ -91,3 +111,26 @@ export function readConnectErrorDetailCode(details: unknown): string | null {
|
||||
const code = (details as { code?: unknown }).code;
|
||||
return typeof code === "string" && code.trim().length > 0 ? code : null;
|
||||
}
|
||||
|
||||
export function readConnectErrorRecoveryAdvice(details: unknown): ConnectErrorRecoveryAdvice {
|
||||
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
||||
return {};
|
||||
}
|
||||
const raw = details as {
|
||||
canRetryWithDeviceToken?: unknown;
|
||||
recommendedNextStep?: unknown;
|
||||
};
|
||||
const canRetryWithDeviceToken =
|
||||
typeof raw.canRetryWithDeviceToken === "boolean" ? raw.canRetryWithDeviceToken : undefined;
|
||||
const normalizedNextStep =
|
||||
typeof raw.recommendedNextStep === "string" ? raw.recommendedNextStep.trim() : "";
|
||||
const recommendedNextStep = CONNECT_RECOVERY_NEXT_STEP_VALUES.has(
|
||||
normalizedNextStep as ConnectRecoveryNextStep,
|
||||
)
|
||||
? (normalizedNextStep as ConnectRecoveryNextStep)
|
||||
: undefined;
|
||||
return {
|
||||
canRetryWithDeviceToken,
|
||||
recommendedNextStep,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user