mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
cef071582e
* 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
174 lines
7.4 KiB
TypeScript
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>;
|