import type { DpopFailureReason } from "@t3tools/contracts"; import type { RelayProtectedError } from "@t3tools/contracts/relay"; export const DPOP_CLOCK_HINT = "Hint: Check that automatic date and time is enabled on both devices, then try again."; /** Older servers omit the DPoP category, but newer servers can also omit it for * a credential failure that happens after proof verification. */ export const DPOP_UNKNOWN_HINT = "Hint: Try again. If it still fails, clock skew may be the cause; check that automatic date and time is enabled on both devices."; export const DPOP_RETRY_HINT = "Hint: Try again. If the problem continues, copy the trace ID."; function dpopFailureHint(reason: DpopFailureReason | undefined): string { if (reason === "time_window") return DPOP_CLOCK_HINT; if (reason === undefined) return DPOP_UNKNOWN_HINT; return DPOP_RETRY_HINT; } export function dpopFailureMessage(message: string, reason: DpopFailureReason | undefined): string { return `${message} ${dpopFailureHint(reason)}`; } export function relayProtectedErrorMessage(error: RelayProtectedError): string { switch (error._tag) { case "RelayAuthInvalidError": switch (error.reason) { case "missing_bearer": case "invalid_bearer": return "Relay rejected the cloud session token."; case "invalid_dpop": return dpopFailureMessage("Relay rejected the DPoP proof.", error.dpopFailureReason); case "not_authorized": return "Relay rejected the authenticated request."; } case "RelayEnvironmentLinkProofExpiredError": return "Relay rejected an expired environment link proof."; case "RelayEnvironmentLinkProofInvalidError": return `Relay rejected the environment link proof (${error.reason}).`; case "RelayEnvironmentConnectNotAuthorizedError": // "Not authorized" covers non-auth causes too; surface the reason so a // missing link does not read as a credential problem. if (error.reason === "environment_link_not_found") { return "Relay has no active link for this environment. The environment server may not have re-established its link yet."; } return error.reason ? `Relay rejected the environment connection request (${error.reason}).` : "Relay rejected the environment connection request."; case "RelayEnvironmentEndpointUnavailableError": return `Relay could not reach the environment endpoint (${error.reason}).`; case "RelayEnvironmentEndpointTimedOutError": return "Relay timed out while contacting the environment endpoint."; case "RelayEnvironmentLinkFailedError": return `Relay could not link the environment (${error.reason}).`; case "RelayEnvironmentLinkUnavailableError": return `Relay cannot provision the managed endpoint (${error.reason}).`; case "RelayEnvironmentLinkLimitExceededError": return `Relay refused the link: this account already has its maximum of ${error.maxTunnels} managed tunnels. Unlink an environment to free one up.`; case "RelayAgentActivityPublishProofExpiredError": return "Relay rejected an expired agent activity publish proof."; case "RelayAgentActivityPublishProofInvalidError": return `Relay rejected the agent activity publish proof (${error.reason}).`; case "RelayInternalError": return `Relay encountered an internal error (${error.reason}).`; } }