mirror of https://github.com/openclaw/openclaw.git
126 lines
4.0 KiB
TypeScript
126 lines
4.0 KiB
TypeScript
import { hasEffectivePairedDeviceRole, type PairedDevice } from "../infra/device-pairing.js";
|
|
import type { NodeListNode } from "../shared/node-list-types.js";
|
|
import type { NodeSession } from "./node-registry.js";
|
|
|
|
export type KnownNodeCatalog = {
|
|
pairedById: Map<string, NodeListNode>;
|
|
connectedById: Map<string, NodeSession>;
|
|
};
|
|
|
|
function uniqueSortedStrings(...items: Array<readonly string[] | undefined>): string[] {
|
|
const values = new Set<string>();
|
|
for (const item of items) {
|
|
if (!item) {
|
|
continue;
|
|
}
|
|
for (const value of item) {
|
|
const trimmed = value.trim();
|
|
if (trimmed) {
|
|
values.add(trimmed);
|
|
}
|
|
}
|
|
}
|
|
return [...values].toSorted((left, right) => left.localeCompare(right));
|
|
}
|
|
|
|
function buildPairedNodeRecord(entry: PairedDevice): NodeListNode {
|
|
return {
|
|
nodeId: entry.deviceId,
|
|
displayName: entry.displayName,
|
|
platform: entry.platform,
|
|
version: undefined,
|
|
coreVersion: undefined,
|
|
uiVersion: undefined,
|
|
clientId: entry.clientId,
|
|
clientMode: entry.clientMode,
|
|
deviceFamily: undefined,
|
|
modelIdentifier: undefined,
|
|
remoteIp: entry.remoteIp,
|
|
caps: [],
|
|
commands: [],
|
|
permissions: undefined,
|
|
approvedAtMs: entry.approvedAtMs,
|
|
paired: true,
|
|
connected: false,
|
|
};
|
|
}
|
|
|
|
function buildKnownNodeEntry(params: {
|
|
nodeId: string;
|
|
paired?: NodeListNode;
|
|
live?: NodeSession;
|
|
}): NodeListNode {
|
|
const { nodeId, paired, live } = params;
|
|
return {
|
|
nodeId,
|
|
displayName: live?.displayName ?? paired?.displayName,
|
|
platform: live?.platform ?? paired?.platform,
|
|
version: live?.version ?? paired?.version,
|
|
coreVersion: live?.coreVersion ?? paired?.coreVersion,
|
|
uiVersion: live?.uiVersion ?? paired?.uiVersion,
|
|
clientId: live?.clientId ?? paired?.clientId,
|
|
clientMode: live?.clientMode ?? paired?.clientMode,
|
|
deviceFamily: live?.deviceFamily ?? paired?.deviceFamily,
|
|
modelIdentifier: live?.modelIdentifier ?? paired?.modelIdentifier,
|
|
remoteIp: live?.remoteIp ?? paired?.remoteIp,
|
|
caps: uniqueSortedStrings(live?.caps, paired?.caps),
|
|
commands: uniqueSortedStrings(live?.commands, paired?.commands),
|
|
pathEnv: live?.pathEnv,
|
|
permissions: live?.permissions ?? paired?.permissions,
|
|
connectedAtMs: live?.connectedAtMs,
|
|
approvedAtMs: paired?.approvedAtMs,
|
|
paired: Boolean(paired),
|
|
connected: Boolean(live),
|
|
};
|
|
}
|
|
|
|
function compareKnownNodes(left: NodeListNode, right: NodeListNode): number {
|
|
if (left.connected !== right.connected) {
|
|
return left.connected ? -1 : 1;
|
|
}
|
|
const leftName = (left.displayName ?? left.nodeId).toLowerCase();
|
|
const rightName = (right.displayName ?? right.nodeId).toLowerCase();
|
|
if (leftName < rightName) {
|
|
return -1;
|
|
}
|
|
if (leftName > rightName) {
|
|
return 1;
|
|
}
|
|
return left.nodeId.localeCompare(right.nodeId);
|
|
}
|
|
|
|
export function createKnownNodeCatalog(params: {
|
|
pairedDevices: readonly PairedDevice[];
|
|
connectedNodes: readonly NodeSession[];
|
|
}): KnownNodeCatalog {
|
|
const pairedById = new Map(
|
|
params.pairedDevices
|
|
.filter((entry) => hasEffectivePairedDeviceRole(entry, "node"))
|
|
.map((entry) => [entry.deviceId, buildPairedNodeRecord(entry)]),
|
|
);
|
|
const connectedById = new Map(params.connectedNodes.map((entry) => [entry.nodeId, entry]));
|
|
return { pairedById, connectedById };
|
|
}
|
|
|
|
export function listKnownNodes(catalog: KnownNodeCatalog): NodeListNode[] {
|
|
const nodeIds = new Set<string>([...catalog.pairedById.keys(), ...catalog.connectedById.keys()]);
|
|
return [...nodeIds]
|
|
.map((nodeId) =>
|
|
buildKnownNodeEntry({
|
|
nodeId,
|
|
paired: catalog.pairedById.get(nodeId),
|
|
live: catalog.connectedById.get(nodeId),
|
|
}),
|
|
)
|
|
.toSorted(compareKnownNodes);
|
|
}
|
|
|
|
export function getKnownNode(catalog: KnownNodeCatalog, nodeId: string): NodeListNode | null {
|
|
const paired = catalog.pairedById.get(nodeId);
|
|
const live = catalog.connectedById.get(nodeId);
|
|
if (!paired && !live) {
|
|
return null;
|
|
}
|
|
return buildKnownNodeEntry({ nodeId, paired, live });
|
|
}
|