chore: clean artifacts and fix device roles

This commit is contained in:
Peter Steinberger
2026-01-19 09:23:40 +00:00
parent 35e7c62e78
commit 9292ec9880
4 changed files with 38 additions and 2 deletions

3
.gitignore vendored
View File

@@ -35,6 +35,8 @@ vendor/
apps/ios/Clawdbot.xcodeproj/ apps/ios/Clawdbot.xcodeproj/
apps/ios/Clawdbot.xcodeproj/** apps/ios/Clawdbot.xcodeproj/**
apps/macos/.build/** apps/macos/.build/**
**/*.bun-build
apps/ios/*.xcfilelist
# Vendor build artifacts # Vendor build artifacts
vendor/a2ui/renderers/lit/dist/ vendor/a2ui/renderers/lit/dist/
@@ -48,6 +50,7 @@ apps/ios/fastlane/screenshots/
apps/ios/fastlane/test_output/ apps/ios/fastlane/test_output/
apps/ios/fastlane/logs/ apps/ios/fastlane/logs/
apps/ios/fastlane/.env apps/ios/fastlane/.env
apps/ios/fastlane/report.xml
# fastlane build artifacts (local) # fastlane build artifacts (local)
apps/ios/*.ipa apps/ios/*.ipa

View File

@@ -24,6 +24,7 @@ export const DevicePairRequestedEventSchema = Type.Object(
clientId: Type.Optional(NonEmptyString), clientId: Type.Optional(NonEmptyString),
clientMode: Type.Optional(NonEmptyString), clientMode: Type.Optional(NonEmptyString),
role: Type.Optional(NonEmptyString), role: Type.Optional(NonEmptyString),
roles: Type.Optional(Type.Array(NonEmptyString)),
scopes: Type.Optional(Type.Array(NonEmptyString)), scopes: Type.Optional(Type.Array(NonEmptyString)),
remoteIp: Type.Optional(NonEmptyString), remoteIp: Type.Optional(NonEmptyString),
silent: Type.Optional(Type.Boolean()), silent: Type.Optional(Type.Boolean()),

View File

@@ -30,6 +30,12 @@ import {
} from "./nodes.helpers.js"; } from "./nodes.helpers.js";
import type { GatewayRequestHandlers } from "./types.js"; import type { GatewayRequestHandlers } from "./types.js";
function isNodeEntry(entry: { role?: string; roles?: string[] }) {
if (entry.role === "node") return true;
if (Array.isArray(entry.roles) && entry.roles.includes("node")) return true;
return false;
}
export const nodeHandlers: GatewayRequestHandlers = { export const nodeHandlers: GatewayRequestHandlers = {
"node.pair.request": async ({ params, respond, context }) => { "node.pair.request": async ({ params, respond, context }) => {
if (!validateNodePairRequestParams(params)) { if (!validateNodePairRequestParams(params)) {
@@ -207,7 +213,7 @@ export const nodeHandlers: GatewayRequestHandlers = {
const list = await listDevicePairing(); const list = await listDevicePairing();
const pairedById = new Map( const pairedById = new Map(
list.paired list.paired
.filter((entry) => entry.role === "node") .filter((entry) => isNodeEntry(entry))
.map((entry) => [ .map((entry) => [
entry.deviceId, entry.deviceId,
{ {
@@ -284,7 +290,7 @@ export const nodeHandlers: GatewayRequestHandlers = {
} }
await respondUnavailableOnThrow(respond, async () => { await respondUnavailableOnThrow(respond, async () => {
const list = await listDevicePairing(); const list = await listDevicePairing();
const paired = list.paired.find((n) => n.deviceId === id && n.role === "node"); const paired = list.paired.find((n) => n.deviceId === id && isNodeEntry(n));
const connected = context.nodeRegistry.listConnected(); const connected = context.nodeRegistry.listConnected();
const live = connected.find((n) => n.nodeId === id); const live = connected.find((n) => n.nodeId === id);

View File

@@ -12,6 +12,7 @@ export type DevicePairingPendingRequest = {
clientId?: string; clientId?: string;
clientMode?: string; clientMode?: string;
role?: string; role?: string;
roles?: string[];
scopes?: string[]; scopes?: string[];
remoteIp?: string; remoteIp?: string;
silent?: boolean; silent?: boolean;
@@ -27,6 +28,7 @@ export type PairedDevice = {
clientId?: string; clientId?: string;
clientMode?: string; clientMode?: string;
role?: string; role?: string;
roles?: string[];
scopes?: string[]; scopes?: string[];
remoteIp?: string; remoteIp?: string;
createdAtMs: number; createdAtMs: number;
@@ -134,6 +136,24 @@ function normalizeDeviceId(deviceId: string) {
return deviceId.trim(); return deviceId.trim();
} }
function mergeRoles(...items: Array<string | string[] | undefined>): string[] | undefined {
const roles = new Set<string>();
for (const item of items) {
if (!item) continue;
if (Array.isArray(item)) {
for (const role of item) {
const trimmed = role.trim();
if (trimmed) roles.add(trimmed);
}
} else {
const trimmed = item.trim();
if (trimmed) roles.add(trimmed);
}
}
if (roles.size === 0) return undefined;
return [...roles];
}
export async function listDevicePairing(baseDir?: string): Promise<DevicePairingList> { export async function listDevicePairing(baseDir?: string): Promise<DevicePairingList> {
const state = await loadState(baseDir); const state = await loadState(baseDir);
const pending = Object.values(state.pendingById).sort((a, b) => b.ts - a.ts); const pending = Object.values(state.pendingById).sort((a, b) => b.ts - a.ts);
@@ -179,6 +199,7 @@ export async function requestDevicePairing(
clientId: req.clientId, clientId: req.clientId,
clientMode: req.clientMode, clientMode: req.clientMode,
role: req.role, role: req.role,
roles: req.role ? [req.role] : undefined,
scopes: req.scopes, scopes: req.scopes,
remoteIp: req.remoteIp, remoteIp: req.remoteIp,
silent: req.silent, silent: req.silent,
@@ -201,6 +222,7 @@ export async function approveDevicePairing(
if (!pending) return null; if (!pending) return null;
const now = Date.now(); const now = Date.now();
const existing = state.pairedByDeviceId[pending.deviceId]; const existing = state.pairedByDeviceId[pending.deviceId];
const roles = mergeRoles(existing?.roles, existing?.role, pending.roles, pending.role);
const device: PairedDevice = { const device: PairedDevice = {
deviceId: pending.deviceId, deviceId: pending.deviceId,
publicKey: pending.publicKey, publicKey: pending.publicKey,
@@ -209,6 +231,7 @@ export async function approveDevicePairing(
clientId: pending.clientId, clientId: pending.clientId,
clientMode: pending.clientMode, clientMode: pending.clientMode,
role: pending.role, role: pending.role,
roles,
scopes: pending.scopes, scopes: pending.scopes,
remoteIp: pending.remoteIp, remoteIp: pending.remoteIp,
createdAtMs: existing?.createdAtMs ?? now, createdAtMs: existing?.createdAtMs ?? now,
@@ -244,12 +267,15 @@ export async function updatePairedDeviceMetadata(
const state = await loadState(baseDir); const state = await loadState(baseDir);
const existing = state.pairedByDeviceId[normalizeDeviceId(deviceId)]; const existing = state.pairedByDeviceId[normalizeDeviceId(deviceId)];
if (!existing) return; if (!existing) return;
const roles = mergeRoles(existing.roles, existing.role, patch.role);
state.pairedByDeviceId[deviceId] = { state.pairedByDeviceId[deviceId] = {
...existing, ...existing,
...patch, ...patch,
deviceId: existing.deviceId, deviceId: existing.deviceId,
createdAtMs: existing.createdAtMs, createdAtMs: existing.createdAtMs,
approvedAtMs: existing.approvedAtMs, approvedAtMs: existing.approvedAtMs,
role: patch.role ?? existing.role,
roles,
}; };
await persistState(state, baseDir); await persistState(state, baseDir);
}); });