Discovery: wide-area bridge DNS-SD
# Conflicts: # apps/ios/Sources/Bridge/BridgeDiscoveryModel.swift # src/cli/dns-cli.ts
This commit is contained in:
@@ -85,6 +85,9 @@ describe("gateway SIGTERM", () => {
|
||||
env: {
|
||||
...process.env,
|
||||
CLAWDIS_SKIP_PROVIDERS: "1",
|
||||
// Avoid port collisions with other test processes that may also start a bridge server.
|
||||
CLAWDIS_BRIDGE_HOST: "127.0.0.1",
|
||||
CLAWDIS_BRIDGE_PORT: "0",
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
|
||||
@@ -8,6 +8,8 @@ export const PresenceEntrySchema = Type.Object(
|
||||
ip: Type.Optional(NonEmptyString),
|
||||
version: Type.Optional(NonEmptyString),
|
||||
platform: Type.Optional(NonEmptyString),
|
||||
deviceFamily: Type.Optional(NonEmptyString),
|
||||
modelIdentifier: Type.Optional(NonEmptyString),
|
||||
mode: Type.Optional(NonEmptyString),
|
||||
lastInputSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
|
||||
reason: Type.Optional(NonEmptyString),
|
||||
@@ -63,6 +65,8 @@ export const ConnectParamsSchema = Type.Object(
|
||||
name: NonEmptyString,
|
||||
version: NonEmptyString,
|
||||
platform: NonEmptyString,
|
||||
deviceFamily: Type.Optional(NonEmptyString),
|
||||
modelIdentifier: Type.Optional(NonEmptyString),
|
||||
mode: NonEmptyString,
|
||||
instanceId: Type.Optional(NonEmptyString),
|
||||
},
|
||||
|
||||
@@ -2109,6 +2109,8 @@ describe("gateway server", () => {
|
||||
name: "fingerprint",
|
||||
version: "9.9.9",
|
||||
platform: "test",
|
||||
deviceFamily: "iPad",
|
||||
modelIdentifier: "iPad16,6",
|
||||
mode: "ui",
|
||||
instanceId: "abc",
|
||||
},
|
||||
@@ -2133,6 +2135,8 @@ describe("gateway server", () => {
|
||||
expect(clientEntry?.host).toBe("fingerprint");
|
||||
expect(clientEntry?.version).toBe("9.9.9");
|
||||
expect(clientEntry?.mode).toBe("ui");
|
||||
expect(clientEntry?.deviceFamily).toBe("iPad");
|
||||
expect(clientEntry?.modelIdentifier).toBe("iPad16,6");
|
||||
|
||||
ws.close();
|
||||
await server.close();
|
||||
|
||||
@@ -1300,12 +1300,16 @@ export async function startGatewayServer(
|
||||
const ip = node.remoteIp?.trim();
|
||||
const version = node.version?.trim() || "unknown";
|
||||
const platform = node.platform?.trim() || undefined;
|
||||
const deviceFamily = node.deviceFamily?.trim() || undefined;
|
||||
const modelIdentifier = node.modelIdentifier?.trim() || undefined;
|
||||
const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason iris-connected`;
|
||||
upsertPresence(node.nodeId, {
|
||||
host,
|
||||
ip,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
modelIdentifier,
|
||||
mode: "remote",
|
||||
reason: "iris-connected",
|
||||
lastInputSeconds: 0,
|
||||
@@ -1342,12 +1346,16 @@ export async function startGatewayServer(
|
||||
const ip = node.remoteIp?.trim();
|
||||
const version = node.version?.trim() || "unknown";
|
||||
const platform = node.platform?.trim() || undefined;
|
||||
const deviceFamily = node.deviceFamily?.trim() || undefined;
|
||||
const modelIdentifier = node.modelIdentifier?.trim() || undefined;
|
||||
const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason iris-disconnected`;
|
||||
upsertPresence(node.nodeId, {
|
||||
host,
|
||||
ip,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
modelIdentifier,
|
||||
mode: "remote",
|
||||
reason: "iris-disconnected",
|
||||
lastInputSeconds: 0,
|
||||
@@ -1743,6 +1751,8 @@ export async function startGatewayServer(
|
||||
ip: isLoopbackAddress(remoteAddr) ? undefined : remoteAddr,
|
||||
version: connectParams.client.version,
|
||||
platform: connectParams.client.platform,
|
||||
deviceFamily: connectParams.client.deviceFamily,
|
||||
modelIdentifier: connectParams.client.modelIdentifier,
|
||||
mode: connectParams.client.mode,
|
||||
instanceId: connectParams.client.instanceId,
|
||||
reason: "connect",
|
||||
@@ -2424,6 +2434,14 @@ export async function startGatewayServer(
|
||||
typeof params.version === "string" ? params.version : undefined;
|
||||
const platform =
|
||||
typeof params.platform === "string" ? params.platform : undefined;
|
||||
const deviceFamily =
|
||||
typeof params.deviceFamily === "string"
|
||||
? params.deviceFamily
|
||||
: undefined;
|
||||
const modelIdentifier =
|
||||
typeof params.modelIdentifier === "string"
|
||||
? params.modelIdentifier
|
||||
: undefined;
|
||||
const lastInputSeconds =
|
||||
typeof params.lastInputSeconds === "number" &&
|
||||
Number.isFinite(params.lastInputSeconds)
|
||||
@@ -2444,6 +2462,8 @@ export async function startGatewayServer(
|
||||
mode,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
modelIdentifier,
|
||||
lastInputSeconds,
|
||||
reason,
|
||||
tags,
|
||||
|
||||
@@ -225,6 +225,8 @@ describe("node bridge server", () => {
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
remoteIp?: string;
|
||||
} | null = null;
|
||||
|
||||
@@ -233,6 +235,8 @@ describe("node bridge server", () => {
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
remoteIp?: string;
|
||||
} | null = null;
|
||||
|
||||
@@ -262,6 +266,8 @@ describe("node bridge server", () => {
|
||||
displayName: "Iris",
|
||||
platform: "ios",
|
||||
version: "1.0",
|
||||
deviceFamily: "iPad",
|
||||
modelIdentifier: "iPad16,6",
|
||||
});
|
||||
|
||||
// Approve the pending request from the gateway side.
|
||||
@@ -296,6 +302,8 @@ describe("node bridge server", () => {
|
||||
displayName: "Different name",
|
||||
platform: "ios",
|
||||
version: "2.0",
|
||||
deviceFamily: "iPad",
|
||||
modelIdentifier: "iPad99,1",
|
||||
});
|
||||
const line3 = JSON.parse(await readLine2()) as { type: string };
|
||||
expect(line3.type).toBe("hello-ok");
|
||||
@@ -310,6 +318,8 @@ describe("node bridge server", () => {
|
||||
expect(lastAuthed?.displayName).toBe("Iris");
|
||||
expect(lastAuthed?.platform).toBe("ios");
|
||||
expect(lastAuthed?.version).toBe("1.0");
|
||||
expect(lastAuthed?.deviceFamily).toBe("iPad");
|
||||
expect(lastAuthed?.modelIdentifier).toBe("iPad16,6");
|
||||
expect(lastAuthed?.remoteIp?.includes("127.0.0.1")).toBe(true);
|
||||
|
||||
socket2.destroy();
|
||||
|
||||
@@ -17,6 +17,8 @@ type BridgeHelloFrame = {
|
||||
token?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
};
|
||||
|
||||
type BridgePairRequestFrame = {
|
||||
@@ -25,6 +27,8 @@ type BridgePairRequestFrame = {
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
remoteAddress?: string;
|
||||
};
|
||||
|
||||
@@ -108,6 +112,8 @@ export type NodeBridgeClientInfo = {
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
remoteIp?: string;
|
||||
};
|
||||
|
||||
@@ -263,6 +269,8 @@ export async function startNodeBridgeServer(
|
||||
displayName: verified.node.displayName ?? hello.displayName,
|
||||
platform: verified.node.platform ?? hello.platform,
|
||||
version: verified.node.version ?? hello.version,
|
||||
deviceFamily: verified.node.deviceFamily ?? hello.deviceFamily,
|
||||
modelIdentifier: verified.node.modelIdentifier ?? hello.modelIdentifier,
|
||||
remoteIp: remoteAddress,
|
||||
};
|
||||
connections.set(nodeId, { socket, nodeInfo, invokeWaiters });
|
||||
@@ -319,6 +327,8 @@ export async function startNodeBridgeServer(
|
||||
displayName: req.displayName,
|
||||
platform: req.platform,
|
||||
version: req.version,
|
||||
deviceFamily: req.deviceFamily,
|
||||
modelIdentifier: req.modelIdentifier,
|
||||
remoteIp: remoteAddress,
|
||||
},
|
||||
opts.pairingBaseDir,
|
||||
@@ -347,6 +357,8 @@ export async function startNodeBridgeServer(
|
||||
displayName: req.displayName,
|
||||
platform: req.platform,
|
||||
version: req.version,
|
||||
deviceFamily: req.deviceFamily,
|
||||
modelIdentifier: req.modelIdentifier,
|
||||
remoteIp: remoteAddress,
|
||||
};
|
||||
connections.set(nodeId, { socket, nodeInfo, invokeWaiters });
|
||||
|
||||
@@ -9,6 +9,8 @@ export type NodePairingPendingRequest = {
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
remoteIp?: string;
|
||||
isRepair?: boolean;
|
||||
ts: number;
|
||||
@@ -20,6 +22,8 @@ export type NodePairingPairedNode = {
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
version?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
remoteIp?: string;
|
||||
createdAtMs: number;
|
||||
approvedAtMs: number;
|
||||
@@ -172,6 +176,8 @@ export async function requestNodePairing(
|
||||
displayName: req.displayName,
|
||||
platform: req.platform,
|
||||
version: req.version,
|
||||
deviceFamily: req.deviceFamily,
|
||||
modelIdentifier: req.modelIdentifier,
|
||||
remoteIp: req.remoteIp,
|
||||
isRepair,
|
||||
ts: Date.now(),
|
||||
@@ -199,6 +205,8 @@ export async function approveNodePairing(
|
||||
displayName: pending.displayName,
|
||||
platform: pending.platform,
|
||||
version: pending.version,
|
||||
deviceFamily: pending.deviceFamily,
|
||||
modelIdentifier: pending.modelIdentifier,
|
||||
remoteIp: pending.remoteIp,
|
||||
createdAtMs: existing?.createdAtMs ?? now,
|
||||
approvedAtMs: now,
|
||||
|
||||
@@ -5,6 +5,8 @@ export type SystemPresence = {
|
||||
ip?: string;
|
||||
version?: string;
|
||||
platform?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
lastInputSeconds?: number;
|
||||
mode?: string;
|
||||
reason?: string;
|
||||
@@ -54,12 +56,20 @@ function initSelfPresence() {
|
||||
if (p === "win32") return `windows ${rel}`;
|
||||
return `${p} ${rel}`;
|
||||
})();
|
||||
const deviceFamily = (() => {
|
||||
const p = os.platform();
|
||||
if (p === "darwin") return "Mac";
|
||||
if (p === "win32") return "Windows";
|
||||
if (p === "linux") return "Linux";
|
||||
return p;
|
||||
})();
|
||||
const text = `Gateway: ${host}${ip ? ` (${ip})` : ""} · app ${version} · mode gateway · reason self`;
|
||||
const selfEntry: SystemPresence = {
|
||||
host,
|
||||
ip,
|
||||
version,
|
||||
platform,
|
||||
deviceFamily,
|
||||
mode: "gateway",
|
||||
reason: "self",
|
||||
text,
|
||||
@@ -123,6 +133,8 @@ type SystemPresencePayload = {
|
||||
ip?: string;
|
||||
version?: string;
|
||||
platform?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
lastInputSeconds?: number;
|
||||
mode?: string;
|
||||
reason?: string;
|
||||
@@ -147,6 +159,8 @@ export function updateSystemPresence(payload: SystemPresencePayload) {
|
||||
ip: payload.ip ?? parsed.ip ?? existing.ip,
|
||||
version: payload.version ?? parsed.version ?? existing.version,
|
||||
platform: payload.platform ?? existing.platform,
|
||||
deviceFamily: payload.deviceFamily ?? existing.deviceFamily,
|
||||
modelIdentifier: payload.modelIdentifier ?? existing.modelIdentifier,
|
||||
mode: payload.mode ?? parsed.mode ?? existing.mode,
|
||||
lastInputSeconds:
|
||||
payload.lastInputSeconds ??
|
||||
|
||||
Reference in New Issue
Block a user