diff --git a/CHANGELOG.md b/CHANGELOG.md index f6d793cee..6532b0bde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ## 2.0.0-beta1 — 2025-12-14 -First Clawdis release post rebrand. This is a semver-major because we dropped legacy providers/agents and moved defaults to new paths while adding a full macOS companion app, a WebSocket Gateway, and an iOS node (Iris). +First Clawdis release post rebrand. This is a semver-major because we dropped legacy providers/agents and moved defaults to new paths while adding a full macOS companion app, a WebSocket Gateway, and an iOS node. ### Breaking - Renamed to **Clawdis**: defaults now live under `~/.clawdis` (sessions in `~/.clawdis/sessions/`, IPC at `~/.clawdis/clawdis.sock`, logs in `/tmp/clawdis`). Launchd labels and config filenames follow the new name; legacy stores are copied forward on first run. @@ -19,7 +19,7 @@ First Clawdis release post rebrand. This is a semver-major because we dropped le ### Gateway, nodes, and automation - New typed Gateway WS protocol (JSON schema validated) with `clawdis gateway {health,status,send,agent,call}` helpers and structured presence/instance updates for all clients. - Optional LAN-facing bridge (`tcp://0.0.0.0:18790`) keeps the Gateway loopback-only while enabling direct Bonjour-discovered connections for paired nodes. -- Node pairing + management via `clawdis nodes {pending,approve,reject,invoke}` (used by the iOS node “Iris” and future remote nodes). +- Node pairing + management via `clawdis nodes {pending,approve,reject,invoke}` (used by the iOS node and future remote nodes). - Cron jobs are Gateway-owned (`clawdis cron …`) with run history stored as JSONL and support for “isolated summary” posting into the main session. ### macOS companion app @@ -29,10 +29,10 @@ First Clawdis release post rebrand. This is a semver-major because we dropped le - **Browser control**: manage clawd’s dedicated Chrome/Chromium with tab listing/open/focus/close, screenshots, DOM query/dump, and “AI snapshots” (aria/domSnapshot/ai) via `clawdis browser …` and UI controls. - **Remote gateway control**: Bonjour discovery for local masters plus SSH-tunnel fallback for remote control when multicast is unavailable. -### iOS node (Iris) +### iOS node - New iOS companion app that pairs to the Gateway bridge, reports presence as a node, and exposes a WKWebView “Canvas” for agent-driven UI. -- `clawdis nodes invoke` supports `canvas.eval` and `canvas.snapshot` to drive and verify the iOS Canvas (fails fast when Iris is backgrounded). -- Voice wake words are configurable in-app; Iris reconnects to the last bridge when credentials are still present in Keychain. +- `clawdis nodes invoke` supports `canvas.eval` and `canvas.snapshot` to drive and verify the iOS Canvas (fails fast when the iOS node is backgrounded). +- Voice wake words are configurable in-app; the iOS node reconnects to the last bridge when credentials are still present in Keychain. ### WhatsApp & agent experience - Group chats fully supported: mention-gated triggers (including media-only captions), sender attribution, session primer with subject/member roster, allowlist bypass when you’re @‑mentioned, and safer handling of view-once/ephemeral media. diff --git a/README.md b/README.md index 76f1dc597..9b12e558b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ WhatsApp / Telegram ├─ CLI (clawdis …) ├─ WebChat (loopback UI) ├─ macOS app (Clawdis.app) - └─ iOS node (Iris) via Bridge + pairing + └─ iOS node via Bridge + pairing ``` ## Why "CLAWDIS"? @@ -53,7 +53,7 @@ Because every space lobster needs a time-and-space machine. The Doctor has a TAR - 🎤 **Voice & transcription hooks** — Voice Wake (macOS/iOS) + optional transcription pipeline - 🔧 **Tool Streaming** — Real-time display (💻📄✍️📝) - 🖥️ **macOS Companion (Clawdis.app)** — Menu bar controls, Voice Wake, WebChat, onboarding, remote gateway control -- 📱 **iOS Node (Iris)** — Pairs as a node, exposes a Canvas surface, forwards voice wake transcripts +- 📱 **iOS node** — Pairs as a node, exposes a Canvas surface, forwards voice wake transcripts Only the Pi CLI is supported now; legacy Claude/Codex/Gemini paths have been removed. @@ -69,7 +69,7 @@ Only the Pi CLI is supported now; legacy Claude/Codex/Gemini paths have been rem - **TypeScript (ESM)**: CLI + Gateway live in `src/` and run on Node ≥ 22. - **macOS app (Swift)**: menu bar companion lives in `apps/macos/`. -- **iOS app (Swift)**: Iris node prototype lives in `apps/ios/`. +- **iOS app (Swift)**: iOS node prototype lives in `apps/ios/`. ## Quick Start @@ -118,9 +118,9 @@ If delivery fails (e.g. WhatsApp disconnected / Telegram token missing), Clawdis Build/run the mac app with `./scripts/restart-mac.sh` (packages, installs, and launches), or `swift build --package-path apps/macos && open dist/Clawdis.app`. -### iOS Node (Iris) (internal) +### iOS node (internal) -Iris is an internal/prototype iOS app that connects as a **remote node**: +The iOS node app is an internal/prototype app that connects as a **remote node**: - **Voice trigger:** forwards transcripts into the Gateway (agent runs + wakeups). - **Canvas screen:** a WKWebView + `` surface the agent can control (via `canvas.eval` / `canvas.snapshot` over `node.invoke`). @@ -164,7 +164,7 @@ Optional: enable/configure clawd’s dedicated browser control (defaults are alr - [Troubleshooting](./docs/troubleshooting.md) - [The Lore](./docs/lore.md) 🦞 - [Telegram (Bot API)](./docs/telegram.md) -- [iOS node runbook (Iris)](./docs/ios/connect.md) +- [iOS node runbook](./docs/ios/connect.md) - [macOS app spec](./docs/clawdis-mac.md) ## Clawd diff --git a/apps/android/README.md b/apps/android/README.md index 76be2f226..c015ff84c 100644 --- a/apps/android/README.md +++ b/apps/android/README.md @@ -1,6 +1,6 @@ ## Clawdis Node (Android) (internal) -Modern Android “node” app (Iris parity): connects to the **Gateway-owned bridge** (`_clawdis-bridge._tcp`) over TCP and exposes **Canvas + Chat + Camera**. +Modern Android node app: connects to the **Gateway-owned bridge** (`_clawdis-bridge._tcp`) over TCP and exposes **Canvas + Chat + Camera**. Notes: - The node keeps the connection alive via a **foreground service** (persistent notification with a Disconnect action). diff --git a/apps/ios/Tests/BridgeEndpointIDTests.swift b/apps/ios/Tests/BridgeEndpointIDTests.swift index e518f30a1..2eabd6f37 100644 --- a/apps/ios/Tests/BridgeEndpointIDTests.swift +++ b/apps/ios/Tests/BridgeEndpointIDTests.swift @@ -6,12 +6,12 @@ import Testing @Suite struct BridgeEndpointIDTests { @Test func stableIDForServiceDecodesAndNormalizesName() { let endpoint = NWEndpoint.service( - name: "Clawdis\\032Bridge \\032 Iris\n", + name: "Clawdis\\032Bridge \\032 Node\n", type: "_clawdis-bridge._tcp", domain: "local.", interface: nil) - #expect(BridgeEndpointID.stableID(endpoint) == "_clawdis-bridge._tcp|local.|Clawdis Bridge Iris") + #expect(BridgeEndpointID.stableID(endpoint) == "_clawdis-bridge._tcp|local.|Clawdis Bridge Node") } @Test func stableIDForNonServiceUsesEndpointDescription() { diff --git a/apps/macos/Tests/ClawdisIPCTests/NodeListTests.swift b/apps/macos/Tests/ClawdisIPCTests/NodeListTests.swift index 83493160b..d48536dc9 100644 --- a/apps/macos/Tests/ClawdisIPCTests/NodeListTests.swift +++ b/apps/macos/Tests/ClawdisIPCTests/NodeListTests.swift @@ -8,7 +8,7 @@ import Testing nodes: [ ControlRequestHandler.GatewayNodeListPayload.Node( nodeId: "n1", - displayName: "Iris", + displayName: "Node", platform: "iOS", version: "1.0", deviceFamily: "iPad", @@ -36,10 +36,10 @@ import Testing #expect(res.pairedNodeIds.sorted() == ["n1", "n2"]) #expect(res.connectedNodeIds == ["n1"]) - let iris = res.nodes.first { $0.nodeId == "n1" } - #expect(iris?.remoteAddress == "192.168.0.88") - #expect(iris?.deviceFamily == "iPad") - #expect(iris?.modelIdentifier == "iPad14,5") - #expect(iris?.capabilities?.sorted() == ["camera", "canvas"]) + let node = res.nodes.first { $0.nodeId == "n1" } + #expect(node?.remoteAddress == "192.168.0.88") + #expect(node?.deviceFamily == "iPad") + #expect(node?.modelIdentifier == "iPad14,5") + #expect(node?.capabilities?.sorted() == ["camera", "canvas"]) } } diff --git a/docs/bonjour.md b/docs/bonjour.md index 58f5e8395..ca64a61d1 100644 --- a/docs/bonjour.md +++ b/docs/bonjour.md @@ -10,7 +10,7 @@ Clawdis uses Bonjour (mDNS / DNS-SD) as a **LAN-only convenience** to discover a ## Wide-Area Bonjour (Unicast DNS-SD) over Tailscale -If you want Iris/iPad auto-discovery while the Gateway is on another network (e.g. Vienna ⇄ London), you can keep the `NWBrowser` UX but switch discovery from multicast mDNS (`local.`) to **unicast DNS-SD** (“Wide-Area Bonjour”) over Tailscale. +If you want iOS node auto-discovery while the Gateway is on another network (e.g. Vienna ⇄ London), you can keep the `NWBrowser` UX but switch discovery from multicast mDNS (`local.`) to **unicast DNS-SD** (“Wide-Area Bonjour”) over Tailscale. High level: @@ -59,7 +59,7 @@ In the Tailscale admin console: - Add a nameserver pointing at the gateway’s tailnet IP (UDP/TCP 53). - Add split DNS so the domain `clawdis.internal` uses that nameserver. -Once clients accept tailnet DNS, Iris can browse `_clawdis-bridge._tcp` in `clawdis.internal.` without multicast. +Once clients accept tailnet DNS, iOS nodes can browse `_clawdis-bridge._tcp` in `clawdis.internal.` without multicast. ### Bridge listener security (recommended) @@ -82,7 +82,7 @@ Only the **Node Gateway** (`clawd` / `clawdis gateway`) advertises Bonjour beaco ## Service types - `_clawdis-master._tcp` — “master gateway” discovery beacon (primarily for macOS remote-control UX). -- `_clawdis-bridge._tcp` — bridge transport beacon (used by Iris/iOS nodes). +- `_clawdis-bridge._tcp` — bridge transport beacon (used by iOS/Android nodes). ## TXT keys (non-secret hints) @@ -93,7 +93,7 @@ The Gateway advertises small non-secret hints to make UI flows convenient: - `sshPort=` (defaults to 22 when not overridden) - `gatewayPort=` (informational; the Gateway WS is typically loopback-only) - `bridgePort=` (only when bridge is enabled) -- `canvasPort=` (only when the optional canvas host is enabled; default `18793`) +- `canvasPort=` (only when the canvas host is running; enabled by default; default `18793`) - `tailnetDns=` (optional hint; may be absent) ## Debugging on macOS @@ -119,9 +119,9 @@ Look for `bonjour:` lines, especially: - `bonjour: ... name conflict resolved` / `hostname conflict resolved` - `bonjour: watchdog detected non-announced service; attempting re-advertise ...` (self-heal attempt after sleep/interface churn) -## Debugging on iOS (Iris) +## Debugging on iOS node -Iris discovers bridges via `NWBrowser` browsing `_clawdis-bridge._tcp`. +The iOS node app discovers bridges via `NWBrowser` browsing `_clawdis-bridge._tcp`. To capture what the browser is doing: diff --git a/docs/configuration.md b/docs/configuration.md index 809a12762..8a8962849 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -178,9 +178,9 @@ When enabled, the server: } ``` -### `bridge` (Iris/node bridge server) +### `bridge` (node bridge server) -The Gateway can expose a simple TCP bridge for nodes (iOS/Android “Iris”), typically on port `18790`. +The Gateway can expose a simple TCP bridge for nodes (iOS/Android), typically on port `18790`. Defaults: - enabled: `true` diff --git a/docs/discovery.md b/docs/discovery.md index 03311061a..6cf192fda 100644 --- a/docs/discovery.md +++ b/docs/discovery.md @@ -10,7 +10,7 @@ read_when: Clawdis has two distinct problems that look similar on the surface: 1) **Operator remote control**: the macOS menu bar app controlling a “master” gateway running elsewhere. -2) **Node pairing**: Iris/iOS (and future nodes) finding a gateway and pairing securely. +2) **Node pairing**: iOS/Android (and future nodes) finding a gateway and pairing securely. The design goal is to keep all network discovery/advertising in the **Node Gateway** (`clawd` / `clawdis gateway`) and keep clients (mac app, iOS) as consumers. @@ -55,7 +55,7 @@ Troubleshooting and beacon details: `docs/bonjour.md`. - `sshPort=22` (or whatever is advertised) - `gatewayPort=18789` (loopback WS port; informational) - `bridgePort=18790` (when bridge is enabled) - - `canvasPort=18793` (when the optional canvas host is enabled) + - `canvasPort=18793` (when the canvas host is running; enabled by default) - `tailnetDns=` (optional hint) Disable/override: diff --git a/docs/index.md b/docs/index.md index 3cbaf1330..5df8cc52e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -34,15 +34,15 @@ WhatsApp / Telegram ▼ ┌──────────────────────────┐ │ Gateway │ ws://127.0.0.1:18789 (loopback-only) - │ (single source) │ tcp://0.0.0.0:18790 (optional Bridge) - │ │ http://0.0.0.0:18793 (optional Canvas host) + │ (single source) │ tcp://0.0.0.0:18790 (Bridge) + │ │ http://0.0.0.0:18793 (Canvas host) └───────────┬───────────────┘ │ ├─ Pi agent (RPC) ├─ CLI (clawdis …) ├─ Chat UI (SwiftUI) ├─ macOS app (Clawdis.app) - └─ iOS node (Iris) via Bridge + pairing + └─ iOS node via Bridge + pairing ``` Most operations flow through the **Gateway** (`clawdis gateway`), a single long-running process that owns provider connections and the WebSocket control plane. @@ -52,7 +52,7 @@ Most operations flow through the **Gateway** (`clawdis gateway`), a single long- - **One Gateway per host**: it is the only process allowed to own the WhatsApp Web session. - **Loopback-first**: Gateway WS is `ws://127.0.0.1:18789` (not exposed on the LAN). - **Bridge for nodes**: optional LAN/tailnet-facing bridge on `tcp://0.0.0.0:18790` for paired nodes (Bonjour-discoverable). -- **Canvas host (optional)**: LAN/tailnet HTTP file server (default `18793`) for node WebViews; see `docs/configuration.md` (`canvasHost`). +- **Canvas host**: LAN/tailnet HTTP file server (default `18793`) for node WebViews; see `docs/configuration.md` (`canvasHost`). - **Remote use**: SSH tunnel or tailnet/VPN; see `docs/remote.md` and `docs/discovery.md`. ## Features (high level) @@ -65,7 +65,7 @@ Most operations flow through the **Gateway** (`clawdis gateway`), a single long- - 📎 **Media Support** — Send and receive images, audio, documents - 🎤 **Voice notes** — Optional transcription hook - 🖥️ **WebChat + macOS app** — Local UI + menu bar companion for ops and voice wake -- 📱 **iOS node (Iris)** — Pairs as a node and exposes a Canvas surface +- 📱 **iOS node** — Pairs as a node and exposes a Canvas surface Note: legacy Claude/Codex/Gemini/Opencode paths have been removed; Pi is the only coding-agent path. diff --git a/docs/remote.md b/docs/remote.md index 60e79cb2a..aafb89a2c 100644 --- a/docs/remote.md +++ b/docs/remote.md @@ -8,7 +8,7 @@ read_when: This repo supports “remote over SSH” by keeping a single Gateway (the master) running on a host (e.g., your Mac Studio) and connecting clients to it. - For **operators (you / the macOS app)**: SSH tunneling is the universal fallback. -- For **nodes (Iris/iOS and future devices)**: prefer the Gateway **Bridge** when on the same LAN/tailnet (see `docs/discovery.md`). +- For **nodes (iOS/Android and future devices)**: prefer the Gateway **Bridge** when on the same LAN/tailnet (see `docs/discovery.md`). ## The core idea diff --git a/docs/voicewake.md b/docs/voicewake.md index 1a3ee14ec..35a1e54f2 100644 --- a/docs/voicewake.md +++ b/docs/voicewake.md @@ -50,7 +50,7 @@ Who receives it: - Uses the global list to gate `VoiceWakeRuntime` triggers. - Editing “Trigger words” in Voice Wake settings calls `voicewake.set` and then relies on the broadcast to keep other clients in sync. -### iOS node (Iris) +### iOS node - Uses the global list for `VoiceWakeManager` trigger detection. - Editing Wake Words in Settings calls `voicewake.set` (over the bridge) and also keeps local wake-word detection responsive. @@ -59,4 +59,3 @@ Who receives it: - Exposes a Wake Words editor in Settings. - Calls `voicewake.set` over the bridge so edits sync everywhere. - diff --git a/src/config/config.ts b/src/config/config.ts index 5984ca648..2ac35e1e0 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -76,7 +76,7 @@ export type BridgeConfig = { enabled?: boolean; port?: number; /** - * Bind address policy for the Iris bridge server. + * Bind address policy for the node bridge server. * - auto: prefer tailnet IP when present, else LAN (0.0.0.0) * - lan: 0.0.0.0 (reachable on local network + any forwarded interfaces) * - tailnet: bind only to the Tailscale interface IP (100.64.0.0/10) diff --git a/src/gateway/server.test.ts b/src/gateway/server.test.ts index 9750081ef..628ff031a 100644 --- a/src/gateway/server.test.ts +++ b/src/gateway/server.test.ts @@ -405,7 +405,7 @@ describe("gateway server", () => { type: "req", id: "pair-req-1", method: "node.pair.request", - params: { nodeId: "n1", displayName: "Iris" }, + params: { nodeId: "n1", displayName: "Node" }, }), ); const res1 = await onceMessage<{ @@ -432,7 +432,7 @@ describe("gateway server", () => { type: "req", id: "pair-req-2", method: "node.pair.request", - params: { nodeId: "n1", displayName: "Iris" }, + params: { nodeId: "n1", displayName: "Node" }, }), ); const res2 = await onceMessage<{ @@ -827,7 +827,7 @@ describe("gateway server", () => { (p) => typeof p === "object" && p !== null && - (p as { instanceId?: unknown }).instanceId === "iris-1" && + (p as { instanceId?: unknown }).instanceId === "node-1" && (p as { reason?: unknown }).reason === reason, ); }, @@ -835,20 +835,20 @@ describe("gateway server", () => { ); }; - const presenceConnectedP = waitPresenceReason("iris-connected"); + const presenceConnectedP = waitPresenceReason("node-connected"); await bridgeCall?.onAuthenticated?.({ - nodeId: "iris-1", - displayName: "Iris", + nodeId: "node-1", + displayName: "Node", platform: "ios", version: "1.0", remoteIp: "10.0.0.10", }); await presenceConnectedP; - const presenceDisconnectedP = waitPresenceReason("iris-disconnected"); + const presenceDisconnectedP = waitPresenceReason("node-disconnected"); await bridgeCall?.onDisconnected?.({ - nodeId: "iris-1", - displayName: "Iris", + nodeId: "node-1", + displayName: "Node", platform: "ios", version: "1.0", remoteIp: "10.0.0.10", diff --git a/src/gateway/server.ts b/src/gateway/server.ts index 8943eeec9..58867cb54 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -1263,7 +1263,7 @@ export async function startGatewayServer(port = 18789): Promise { thinking: p.thinking, deliver: p.deliver, timeout: Math.ceil(timeoutMs / 1000).toString(), - surface: `Iris(${nodeId})`, + surface: `Node(${nodeId})`, abortSignal: abortController.signal, }, defaultRuntime, @@ -1367,7 +1367,7 @@ export async function startGatewayServer(port = 18789): Promise { sessionId, thinking: "low", deliver: false, - surface: "Iris", + surface: "Node", }, defaultRuntime, deps, @@ -1442,7 +1442,7 @@ export async function startGatewayServer(port = 18789): Promise { typeof link?.timeoutSeconds === "number" ? link.timeoutSeconds.toString() : undefined, - surface: "Iris", + surface: "Node", }, defaultRuntime, deps, @@ -1508,7 +1508,7 @@ export async function startGatewayServer(port = 18789): Promise { 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`; + const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason node-connected`; upsertPresence(node.nodeId, { host, ip, @@ -1517,7 +1517,7 @@ export async function startGatewayServer(port = 18789): Promise { deviceFamily, modelIdentifier, mode: "remote", - reason: "iris-connected", + reason: "node-connected", lastInputSeconds: 0, instanceId: node.nodeId, text, @@ -1554,7 +1554,7 @@ export async function startGatewayServer(port = 18789): Promise { 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`; + const text = `Node: ${host}${ip ? ` (${ip})` : ""} · app ${version} · last input 0s ago · mode remote · reason node-disconnected`; upsertPresence(node.nodeId, { host, ip, @@ -1563,7 +1563,7 @@ export async function startGatewayServer(port = 18789): Promise { deviceFamily, modelIdentifier, mode: "remote", - reason: "iris-disconnected", + reason: "node-disconnected", lastInputSeconds: 0, instanceId: node.nodeId, text, @@ -1589,7 +1589,7 @@ export async function startGatewayServer(port = 18789): Promise { if (started.port > 0) { bridge = started; defaultRuntime.log( - `bridge listening on tcp://${bridgeHost}:${bridge.port} (Iris)`, + `bridge listening on tcp://${bridgeHost}:${bridge.port} (node)`, ); } } catch (err) { diff --git a/src/infra/bonjour.ts b/src/infra/bonjour.ts index 7e41eeca4..822f4899c 100644 --- a/src/infra/bonjour.ts +++ b/src/infra/bonjour.ts @@ -137,7 +137,7 @@ export async function startGatewayBonjourAdvertiser( svc: master as unknown as BonjourService, }); - // Optional bridge beacon (same type used by Iris/iOS today). + // Optional bridge beacon (same type used by iOS/Android nodes today). if (typeof opts.bridgePort === "number" && opts.bridgePort > 0) { const bridge = responder.createService({ name: safeServiceName(instanceName), diff --git a/src/infra/bridge/server.test.ts b/src/infra/bridge/server.test.ts index 2bfb70451..8c560db62 100644 --- a/src/infra/bridge/server.test.ts +++ b/src/infra/bridge/server.test.ts @@ -263,7 +263,7 @@ describe("node bridge server", () => { sendLine(socket, { type: "pair-request", nodeId: "n4", - displayName: "Iris", + displayName: "Node", platform: "ios", version: "1.0", deviceFamily: "iPad", @@ -315,7 +315,7 @@ describe("node bridge server", () => { expect(lastAuthed?.nodeId).toBe("n4"); // Prefer paired metadata over hello payload (token verifies the stored node record). - expect(lastAuthed?.displayName).toBe("Iris"); + expect(lastAuthed?.displayName).toBe("Node"); expect(lastAuthed?.platform).toBe("ios"); expect(lastAuthed?.version).toBe("1.0"); expect(lastAuthed?.deviceFamily).toBe("iPad"); @@ -425,7 +425,7 @@ describe("node bridge server", () => { sendLine(socket, { type: "pair-request", nodeId: "n-caps", - displayName: "Iris", + displayName: "Node", platform: "ios", version: "1.0", deviceFamily: "iPad",