Files
openclaw/packages/gateway-protocol/src/schema/devices.ts
T
Peter Steinberger cef071582e feat: let limited browsers request admin access (#121459)
* feat(gateway): add live device scope upgrades

* feat(ui): add limited-access upgrade flow

* fix(protocol): refresh Swift scope upgrade models

* perf(ui): lazy-load device scope upgrades

* fix(ci): complete scope upgrade generated surfaces

* perf(ui): lazy-load GitHub link hovercards

* fix(ui): keep admin repair guidance focusable

* fix(ui): gate and refresh scope upgrade banner

* refactor(ui): keep gateway client within line budget

* fix(ci): align rebased scope upgrade checks

* fix(ui): resolve scope upgrade in browser tests

* fix(gateway): honor refreshed scope upgrade deadline

* fix(gateway): honor refreshed scope upgrade deadline

* fix(gateway): coalesce scope upgrade waiters

* fix(ui): gate scope upgrade actions

* chore(plugin-sdk): refresh rebased API baseline

* fix(scope-upgrade): return canonical request ids

* fix(ui): preserve gateway event type binding

* fix(protocol): generate scope upgrade result models

* fix(ui): preserve scope upgrade recovery guidance

* chore(plugin-sdk): refresh rebased API baseline

* test(ui): avoid scope upgrade navigation race

* docs(control-ui): clarify scope upgrade approver

* test(gateway): align appended method counts

* chore(plugin-sdk): refresh rebased API baseline

* refactor(ui): keep place picker within line budget

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): refresh rebased API baseline

* fix(gateway): preserve scope-upgrade browser origin
2026-08-12 18:32:00 -07:00

174 lines
7.4 KiB
TypeScript

// Gateway Protocol schema module defines protocol validation shapes.
import type { Static } from "typebox";
import { Type } from "typebox";
import { closedObject } from "./closed-object.js";
import { NonEmptyString } from "./primitives.js";
/**
* Device pairing and token-management protocol schemas.
*
* These payloads cross the gateway approval boundary, so request ids and device
* ids stay explicit and feature handlers own the authorization checks.
*/
/** Lists pending and approved device pairing records. */
export const DevicePairListParamsSchema = closedObject({});
/** Approves a pending pairing request by request id. */
export const DevicePairApproveParamsSchema = closedObject({ requestId: NonEmptyString });
/** Rejects a pending pairing request by request id. */
export const DevicePairRejectParamsSchema = closedObject({ requestId: NonEmptyString });
/** Removes an approved or remembered device by device id. */
export const DevicePairRemoveParamsSchema = closedObject({ deviceId: NonEmptyString });
/** Operator-assigned label for a paired device (max 64 chars after protocol bound). */
const DevicePairLabelString = Type.String({ minLength: 1, maxLength: 64 });
/** Renames a paired device while preserving its stable device id. */
export const DevicePairRenameParamsSchema = closedObject({
deviceId: NonEmptyString,
label: DevicePairLabelString,
});
/** Rotates or issues a device token for a specific role/scope grant. */
export const DeviceTokenRotateParamsSchema = closedObject({
deviceId: NonEmptyString,
role: NonEmptyString,
scopes: Type.Optional(Type.Array(NonEmptyString)),
});
/** Revokes one role-bound device token grant. */
export const DeviceTokenRevokeParamsSchema = closedObject({
deviceId: NonEmptyString,
role: NonEmptyString,
});
/** Requests an approval-bound operator scope upgrade for the calling device. */
export const ScopeUpgradeRequestSchema = closedObject({
scopes: Type.Array(NonEmptyString, { minItems: 1, maxItems: 8, uniqueItems: true }),
});
/** Identifies the pending scope upgrade observed by the calling device. */
export const ScopeUpgradeWaitSchema = closedObject({ requestId: NonEmptyString });
/** Registers a pending scope upgrade without exposing device credentials. */
export const ScopeUpgradeRegistrationSchema = closedObject({ requestId: NonEmptyString });
/** Returns an approved scope upgrade with the freshly rotated credential. */
export const ScopeUpgradeApprovedSchema = closedObject({
status: Type.Literal("approved"),
requestId: NonEmptyString,
deviceToken: NonEmptyString,
scopes: Type.Array(NonEmptyString, { minItems: 1, maxItems: 8, uniqueItems: true }),
});
/** Reports that an administrator rejected the pending scope upgrade. */
export const ScopeUpgradeRejectedSchema = closedObject({
status: Type.Literal("rejected"),
requestId: NonEmptyString,
});
/** Reports that the pending scope upgrade expired before approval. */
export const ScopeUpgradeExpiredSchema = closedObject({
status: Type.Literal("expired"),
requestId: NonEmptyString,
});
/** Returns the terminal scope-upgrade state to the identity-bound waiter. */
export const ScopeUpgradeResultSchema = Type.Union([
ScopeUpgradeApprovedSchema,
ScopeUpgradeRejectedSchema,
ScopeUpgradeExpiredSchema,
]);
/** Event emitted when a client opens or refreshes a pairing request. */
export const DevicePairRequestedEventSchema = closedObject({
requestId: NonEmptyString,
deviceId: NonEmptyString,
publicKey: NonEmptyString,
displayName: Type.Optional(NonEmptyString),
platform: Type.Optional(NonEmptyString),
deviceFamily: Type.Optional(NonEmptyString),
clientId: Type.Optional(NonEmptyString),
clientMode: Type.Optional(NonEmptyString),
browserOrigin: Type.Optional(NonEmptyString),
role: Type.Optional(NonEmptyString),
roles: Type.Optional(Type.Array(NonEmptyString)),
scopes: Type.Optional(Type.Array(NonEmptyString)),
remoteIp: Type.Optional(NonEmptyString),
silent: Type.Optional(Type.Boolean()),
isRepair: Type.Optional(Type.Boolean()),
ts: Type.Integer({ minimum: 0 }),
});
/** Event emitted after a pairing request is approved, rejected, or otherwise resolved. */
export const DevicePairResolvedEventSchema = closedObject({
requestId: NonEmptyString,
deviceId: NonEmptyString,
decision: NonEmptyString,
ts: Type.Integer({ minimum: 0 }),
});
const SetupCodeQrDataUrlSchema = Type.String({
maxLength: 16_384,
pattern: "^data:image/png;base64,",
});
/**
* Generates a device-pairing setup code (and optional QR) so a mobile/companion
* client can scan it and connect to this gateway. The embedded setup code mints
* a short-lived bootstrap token that defaults to full native-mobile operator
* access, so this method requires operator.admin
* (enforced by the core method descriptor's method-scope policy, not the handler)
* and is not advertised. `bootstrapProfile: "limited"` omits operator.admin;
* `bootstrapProfile: "node"` narrows the handoff to a node role with no operator
* scopes for companion devices such as watchOS.
*/
export const DevicePairSetupCodeParamsSchema = closedObject({
publicUrl: Type.Optional(NonEmptyString),
preferRemoteUrl: Type.Optional(Type.Boolean()),
includeQr: Type.Optional(Type.Boolean()),
bootstrapProfile: Type.Optional(Type.String({ enum: ["limited", "node"] })),
joinUrl: Type.Optional(Type.Literal(true)),
});
/**
* Setup code plus non-secret connection metadata. `auth` is a label only
* ("token" | "password"); the gateway credential itself is never returned.
* `accessDowngraded` reports the plaintext-LAN safety fallback from full to
* limited access so the presenting client can explain how to upgrade.
*/
export const DevicePairSetupCodeResultSchema = closedObject({
setupCode: NonEmptyString,
joinUrl: Type.Optional(NonEmptyString),
qrDataUrl: Type.Optional(SetupCodeQrDataUrlSchema),
gatewayUrl: NonEmptyString,
gatewayUrls: Type.Optional(
Type.Array(NonEmptyString, { minItems: 2, maxItems: 8, uniqueItems: true }),
),
auth: Type.Union([Type.Literal("token"), Type.Literal("password")]),
urlSource: NonEmptyString,
access: Type.Optional(
Type.Union([Type.Literal("full"), Type.Literal("limited"), Type.Literal("node")]),
),
accessDowngraded: Type.Optional(Type.Boolean()),
expiresAtMs: Type.Optional(Type.Integer({ minimum: 0 })),
});
// Wire types derive directly from local schema consts so public d.ts graphs never
// pull in the ProtocolSchemas registry.
export type DevicePairListParams = Static<typeof DevicePairListParamsSchema>;
export type DevicePairApproveParams = Static<typeof DevicePairApproveParamsSchema>;
export type DevicePairRejectParams = Static<typeof DevicePairRejectParamsSchema>;
export type DevicePairRemoveParams = Static<typeof DevicePairRemoveParamsSchema>;
export type DevicePairSetupCodeParams = Static<typeof DevicePairSetupCodeParamsSchema>;
export type DevicePairSetupCodeResult = Static<typeof DevicePairSetupCodeResultSchema>;
export type DevicePairRenameParams = Static<typeof DevicePairRenameParamsSchema>;
export type DeviceTokenRotateParams = Static<typeof DeviceTokenRotateParamsSchema>;
export type DeviceTokenRevokeParams = Static<typeof DeviceTokenRevokeParamsSchema>;
export type ScopeUpgradeRequest = Static<typeof ScopeUpgradeRequestSchema>;
export type ScopeUpgradeWait = Static<typeof ScopeUpgradeWaitSchema>;
export type ScopeUpgradeRegistration = Static<typeof ScopeUpgradeRegistrationSchema>;
export type ScopeUpgradeResult = Static<typeof ScopeUpgradeResultSchema>;