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.";export 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}).`; }}
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.";
export 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}).`;
}
}