feat: unify device auth + pairing

This commit is contained in:
Peter Steinberger
2026-01-19 02:31:18 +00:00
parent 47d1f23d55
commit 73e9e787b4
30 changed files with 2041 additions and 20 deletions

View File

@@ -56,6 +56,12 @@ import {
CronStatusParamsSchema,
type CronUpdateParams,
CronUpdateParamsSchema,
type DevicePairApproveParams,
DevicePairApproveParamsSchema,
type DevicePairListParams,
DevicePairListParamsSchema,
type DevicePairRejectParams,
DevicePairRejectParamsSchema,
type ExecApprovalsGetParams,
ExecApprovalsGetParamsSchema,
type ExecApprovalsNodeGetParams,
@@ -65,6 +71,10 @@ import {
type ExecApprovalsSetParams,
ExecApprovalsSetParamsSchema,
type ExecApprovalsSnapshot,
type ExecApprovalRequestParams,
ExecApprovalRequestParamsSchema,
type ExecApprovalResolveParams,
ExecApprovalResolveParamsSchema,
ErrorCodes,
type ErrorShape,
ErrorShapeSchema,
@@ -239,12 +249,27 @@ export const validateCronUpdateParams = ajv.compile<CronUpdateParams>(CronUpdate
export const validateCronRemoveParams = ajv.compile<CronRemoveParams>(CronRemoveParamsSchema);
export const validateCronRunParams = ajv.compile<CronRunParams>(CronRunParamsSchema);
export const validateCronRunsParams = ajv.compile<CronRunsParams>(CronRunsParamsSchema);
export const validateDevicePairListParams = ajv.compile<DevicePairListParams>(
DevicePairListParamsSchema,
);
export const validateDevicePairApproveParams = ajv.compile<DevicePairApproveParams>(
DevicePairApproveParamsSchema,
);
export const validateDevicePairRejectParams = ajv.compile<DevicePairRejectParams>(
DevicePairRejectParamsSchema,
);
export const validateExecApprovalsGetParams = ajv.compile<ExecApprovalsGetParams>(
ExecApprovalsGetParamsSchema,
);
export const validateExecApprovalsSetParams = ajv.compile<ExecApprovalsSetParams>(
ExecApprovalsSetParamsSchema,
);
export const validateExecApprovalRequestParams = ajv.compile<ExecApprovalRequestParams>(
ExecApprovalRequestParamsSchema,
);
export const validateExecApprovalResolveParams = ajv.compile<ExecApprovalResolveParams>(
ExecApprovalResolveParamsSchema,
);
export const validateExecApprovalsNodeGetParams = ajv.compile<ExecApprovalsNodeGetParams>(
ExecApprovalsNodeGetParamsSchema,
);
@@ -364,6 +389,9 @@ export type {
NodePairRequestParams,
NodePairListParams,
NodePairApproveParams,
DevicePairListParams,
DevicePairApproveParams,
DevicePairRejectParams,
ConfigGetParams,
ConfigSetParams,
ConfigApplyParams,

View File

@@ -5,6 +5,7 @@ export * from "./schema/config.js";
export * from "./schema/cron.js";
export * from "./schema/error-codes.js";
export * from "./schema/exec-approvals.js";
export * from "./schema/devices.js";
export * from "./schema/frames.js";
export * from "./schema/logs-chat.js";
export * from "./schema/nodes.js";

View File

@@ -0,0 +1,44 @@
import { Type } from "@sinclair/typebox";
import { NonEmptyString } from "./primitives.js";
export const DevicePairListParamsSchema = Type.Object({}, { additionalProperties: false });
export const DevicePairApproveParamsSchema = Type.Object(
{ requestId: NonEmptyString },
{ additionalProperties: false },
);
export const DevicePairRejectParamsSchema = Type.Object(
{ requestId: NonEmptyString },
{ additionalProperties: false },
);
export const DevicePairRequestedEventSchema = Type.Object(
{
requestId: NonEmptyString,
deviceId: NonEmptyString,
publicKey: NonEmptyString,
displayName: Type.Optional(NonEmptyString),
platform: Type.Optional(NonEmptyString),
clientId: Type.Optional(NonEmptyString),
clientMode: Type.Optional(NonEmptyString),
role: Type.Optional(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 }),
},
{ additionalProperties: false },
);
export const DevicePairResolvedEventSchema = Type.Object(
{
requestId: NonEmptyString,
deviceId: NonEmptyString,
decision: NonEmptyString,
ts: Type.Integer({ minimum: 0 }),
},
{ additionalProperties: false },
);

View File

@@ -2,6 +2,7 @@ import type { ErrorShape } from "./types.js";
export const ErrorCodes = {
NOT_LINKED: "NOT_LINKED",
NOT_PAIRED: "NOT_PAIRED",
AGENT_TIMEOUT: "AGENT_TIMEOUT",
INVALID_REQUEST: "INVALID_REQUEST",
UNAVAILABLE: "UNAVAILABLE",

View File

@@ -86,3 +86,26 @@ export const ExecApprovalsNodeSetParamsSchema = Type.Object(
},
{ additionalProperties: false },
);
export const ExecApprovalRequestParamsSchema = Type.Object(
{
command: NonEmptyString,
cwd: Type.Optional(Type.String()),
host: Type.Optional(Type.String()),
security: Type.Optional(Type.String()),
ask: Type.Optional(Type.String()),
agentId: Type.Optional(Type.String()),
resolvedPath: Type.Optional(Type.String()),
sessionKey: Type.Optional(Type.String()),
timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
},
{ additionalProperties: false },
);
export const ExecApprovalResolveParamsSchema = Type.Object(
{
id: NonEmptyString,
decision: NonEmptyString,
},
{ additionalProperties: false },
);

View File

@@ -35,6 +35,19 @@ export const ConnectParamsSchema = Type.Object(
{ additionalProperties: false },
),
caps: Type.Optional(Type.Array(NonEmptyString, { default: [] })),
role: Type.Optional(NonEmptyString),
scopes: Type.Optional(Type.Array(NonEmptyString)),
device: Type.Optional(
Type.Object(
{
id: NonEmptyString,
publicKey: NonEmptyString,
signature: NonEmptyString,
signedAt: Type.Integer({ minimum: 0 }),
},
{ additionalProperties: false },
),
),
auth: Type.Optional(
Type.Object(
{

View File

@@ -53,7 +53,16 @@ import {
ExecApprovalsNodeSetParamsSchema,
ExecApprovalsSetParamsSchema,
ExecApprovalsSnapshotSchema,
ExecApprovalRequestParamsSchema,
ExecApprovalResolveParamsSchema,
} from "./exec-approvals.js";
import {
DevicePairApproveParamsSchema,
DevicePairListParamsSchema,
DevicePairRejectParamsSchema,
DevicePairRequestedEventSchema,
DevicePairResolvedEventSchema,
} from "./devices.js";
import {
ConnectParamsSchema,
ErrorShapeSchema,
@@ -182,6 +191,13 @@ export const ProtocolSchemas: Record<string, TSchema> = {
ExecApprovalsNodeGetParams: ExecApprovalsNodeGetParamsSchema,
ExecApprovalsNodeSetParams: ExecApprovalsNodeSetParamsSchema,
ExecApprovalsSnapshot: ExecApprovalsSnapshotSchema,
ExecApprovalRequestParams: ExecApprovalRequestParamsSchema,
ExecApprovalResolveParams: ExecApprovalResolveParamsSchema,
DevicePairListParams: DevicePairListParamsSchema,
DevicePairApproveParams: DevicePairApproveParamsSchema,
DevicePairRejectParams: DevicePairRejectParamsSchema,
DevicePairRequestedEvent: DevicePairRequestedEventSchema,
DevicePairResolvedEvent: DevicePairResolvedEventSchema,
ChatHistoryParams: ChatHistoryParamsSchema,
ChatSendParams: ChatSendParamsSchema,
ChatAbortParams: ChatAbortParamsSchema,

View File

@@ -51,7 +51,14 @@ import type {
ExecApprovalsNodeSetParamsSchema,
ExecApprovalsSetParamsSchema,
ExecApprovalsSnapshotSchema,
ExecApprovalRequestParamsSchema,
ExecApprovalResolveParamsSchema,
} from "./exec-approvals.js";
import type {
DevicePairApproveParamsSchema,
DevicePairListParamsSchema,
DevicePairRejectParamsSchema,
} from "./devices.js";
import type {
ConnectParamsSchema,
ErrorShapeSchema,
@@ -175,6 +182,11 @@ export type ExecApprovalsSetParams = Static<typeof ExecApprovalsSetParamsSchema>
export type ExecApprovalsNodeGetParams = Static<typeof ExecApprovalsNodeGetParamsSchema>;
export type ExecApprovalsNodeSetParams = Static<typeof ExecApprovalsNodeSetParamsSchema>;
export type ExecApprovalsSnapshot = Static<typeof ExecApprovalsSnapshotSchema>;
export type ExecApprovalRequestParams = Static<typeof ExecApprovalRequestParamsSchema>;
export type ExecApprovalResolveParams = Static<typeof ExecApprovalResolveParamsSchema>;
export type DevicePairListParams = Static<typeof DevicePairListParamsSchema>;
export type DevicePairApproveParams = Static<typeof DevicePairApproveParamsSchema>;
export type DevicePairRejectParams = Static<typeof DevicePairRejectParamsSchema>;
export type ChatAbortParams = Static<typeof ChatAbortParamsSchema>;
export type ChatInjectParams = Static<typeof ChatInjectParamsSchema>;
export type ChatEvent = Static<typeof ChatEventSchema>;