diff --git a/src/gateway/server.ts b/src/gateway/server.ts index 83c28ac4c..5eda0faa3 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -2965,17 +2965,20 @@ export async function startGatewayServer(port = 18789): Promise { connected.map((n) => [n.nodeId, n]), ); - const nodes = list.paired.map((n) => { - const live = connectedById.get(n.nodeId); - return { - nodeId: n.nodeId, - displayName: live?.displayName ?? n.displayName, - platform: live?.platform ?? n.platform, - version: live?.version ?? n.version, - remoteIp: live?.remoteIp ?? n.remoteIp, - connected: Boolean(live), - }; - }); + const nodes = list.paired.map((n) => { + const live = connectedById.get(n.nodeId); + return { + nodeId: n.nodeId, + displayName: live?.displayName ?? n.displayName, + platform: live?.platform ?? n.platform, + version: live?.version ?? n.version, + deviceFamily: live?.deviceFamily ?? n.deviceFamily, + modelIdentifier: live?.modelIdentifier ?? n.modelIdentifier, + remoteIp: live?.remoteIp ?? n.remoteIp, + caps: live?.caps, + connected: Boolean(live), + }; + }); respond(true, { ts: Date.now(), nodes }, undefined); } catch (err) { diff --git a/src/infra/bridge/server.test.ts b/src/infra/bridge/server.test.ts index 194e24565..566e89d22 100644 --- a/src/infra/bridge/server.test.ts +++ b/src/infra/bridge/server.test.ts @@ -412,4 +412,54 @@ describe("node bridge server", () => { await server.close(); }); + + it("tracks connected node caps and hardware identifiers", async () => { + const server = await startNodeBridgeServer({ + host: "127.0.0.1", + port: 0, + pairingBaseDir: baseDir, + }); + + const socket = net.connect({ host: "127.0.0.1", port: server.port }); + const readLine = createLineReader(socket); + sendLine(socket, { + type: "pair-request", + nodeId: "n-caps", + displayName: "Iris", + platform: "ios", + version: "1.0", + deviceFamily: "iPad", + modelIdentifier: "iPad14,5", + caps: ["canvas", "camera"], + }); + + // Approve the pending request from the gateway side. + let reqId: string | undefined; + for (let i = 0; i < 40; i += 1) { + const list = await listNodePairing(baseDir); + const req = list.pending.find((p) => p.nodeId === "n-caps"); + if (req) { + reqId = req.requestId; + break; + } + await new Promise((r) => setTimeout(r, 25)); + } + expect(reqId).toBeTruthy(); + if (!reqId) throw new Error("expected a pending requestId"); + await approveNodePairing(reqId, baseDir); + + const pairOk = JSON.parse(await readLine()) as { type: string }; + expect(pairOk.type).toBe("pair-ok"); + const helloOk = JSON.parse(await readLine()) as { type: string }; + expect(helloOk.type).toBe("hello-ok"); + + const connected = server.listConnected(); + const node = connected.find((n) => n.nodeId === "n-caps"); + expect(node?.deviceFamily).toBe("iPad"); + expect(node?.modelIdentifier).toBe("iPad14,5"); + expect(node?.caps).toEqual(["canvas", "camera"]); + + socket.destroy(); + await server.close(); + }); }); diff --git a/src/infra/bridge/server.ts b/src/infra/bridge/server.ts index 5af3faf2d..c2b80fc98 100644 --- a/src/infra/bridge/server.ts +++ b/src/infra/bridge/server.ts @@ -19,6 +19,7 @@ type BridgeHelloFrame = { version?: string; deviceFamily?: string; modelIdentifier?: string; + caps?: string[]; }; type BridgePairRequestFrame = { @@ -29,6 +30,7 @@ type BridgePairRequestFrame = { version?: string; deviceFamily?: string; modelIdentifier?: string; + caps?: string[]; remoteAddress?: string; }; @@ -115,6 +117,7 @@ export type NodeBridgeClientInfo = { deviceFamily?: string; modelIdentifier?: string; remoteIp?: string; + caps?: string[]; }; export type NodeBridgeServerOpts = { @@ -271,6 +274,9 @@ export async function startNodeBridgeServer( version: verified.node.version ?? hello.version, deviceFamily: verified.node.deviceFamily ?? hello.deviceFamily, modelIdentifier: verified.node.modelIdentifier ?? hello.modelIdentifier, + caps: Array.isArray(hello.caps) + ? hello.caps.map((c) => String(c)).filter(Boolean) + : undefined, remoteIp: remoteAddress, }; connections.set(nodeId, { socket, nodeInfo, invokeWaiters }); @@ -359,6 +365,9 @@ export async function startNodeBridgeServer( version: req.version, deviceFamily: req.deviceFamily, modelIdentifier: req.modelIdentifier, + caps: Array.isArray(req.caps) + ? req.caps.map((c) => String(c)).filter(Boolean) + : undefined, remoteIp: remoteAddress, }; connections.set(nodeId, { socket, nodeInfo, invokeWaiters });