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
This commit is contained in:
Peter Steinberger
2026-08-12 18:32:00 -07:00
committed by GitHub
parent 0417abdfaa
commit cef071582e
65 changed files with 2252 additions and 120 deletions
@@ -333,4 +333,35 @@ describe("native Gateway protocol levels", () => {
"SessionApprovalEvent must decode terminal transitions.",
);
});
it("emits the scope upgrade result as a discriminated Swift union", async () => {
const swiftGeneratedPath =
"apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift";
const swiftGenerated = await readRepoFile(swiftGeneratedPath);
assertPattern(
swiftGenerated,
swiftGeneratedPath,
/public enum ScopeUpgradeResult: Codable, Sendable \{/,
"missing the generated ScopeUpgradeResult union.",
);
assertPattern(
swiftGenerated,
swiftGeneratedPath,
/case approved\(ScopeUpgradeApproved\)/,
"ScopeUpgradeResult must decode approved outcomes.",
);
assertPattern(
swiftGenerated,
swiftGeneratedPath,
/case rejected\(ScopeUpgradeRejected\)/,
"ScopeUpgradeResult must decode rejected outcomes.",
);
assertPattern(
swiftGenerated,
swiftGeneratedPath,
/case expired\(ScopeUpgradeExpired\)/,
"ScopeUpgradeResult must decode expired outcomes.",
);
});
});
@@ -44,6 +44,44 @@ export const DeviceTokenRevokeParamsSchema = closedObject({
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,
@@ -129,3 +167,7 @@ export type DevicePairSetupCodeResult = Static<typeof DevicePairSetupCodeResultS
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>;
@@ -44,6 +44,13 @@ export const PluginLifecycleProtocolSchemas = {
DevicePairRenameParams: devices.DevicePairRenameParamsSchema,
DeviceTokenRotateParams: devices.DeviceTokenRotateParamsSchema,
DeviceTokenRevokeParams: devices.DeviceTokenRevokeParamsSchema,
ScopeUpgradeRequest: devices.ScopeUpgradeRequestSchema,
ScopeUpgradeWait: devices.ScopeUpgradeWaitSchema,
ScopeUpgradeRegistration: devices.ScopeUpgradeRegistrationSchema,
ScopeUpgradeApproved: devices.ScopeUpgradeApprovedSchema,
ScopeUpgradeRejected: devices.ScopeUpgradeRejectedSchema,
ScopeUpgradeExpired: devices.ScopeUpgradeExpiredSchema,
ScopeUpgradeResult: devices.ScopeUpgradeResultSchema,
DevicePairRequestedEvent: devices.DevicePairRequestedEventSchema,
DevicePairResolvedEvent: devices.DevicePairResolvedEventSchema,
ChatHistoryParams: logsChat.ChatHistoryParamsSchema,
@@ -392,6 +392,8 @@ export const validateDevicePairSetupCodeParams = compile(S.DevicePairSetupCodePa
export const validateDevicePairRenameParams = compile(S.DevicePairRenameParamsSchema);
export const validateDeviceTokenRotateParams = compile(S.DeviceTokenRotateParamsSchema);
export const validateDeviceTokenRevokeParams = compile(S.DeviceTokenRevokeParamsSchema);
export const validateScopeUpgradeRequest = compile(S.ScopeUpgradeRequestSchema);
export const validateScopeUpgradeWait = compile(S.ScopeUpgradeWaitSchema);
export const validateApprovalPresentation = compile(S.ApprovalPresentationSchema);
export const validateApprovalGetParams = compile(S.ApprovalGetParamsSchema);
export const validateApprovalHistoryParams = compile(S.ApprovalHistoryParamsSchema);