diff --git a/src/bridges/claude-code/channel.ts b/src/bridges/claude-code/channel.ts index c2f5fda..be6c34b 100644 --- a/src/bridges/claude-code/channel.ts +++ b/src/bridges/claude-code/channel.ts @@ -21,8 +21,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { - MeshStore, - CommsTool, + createBridgeMesh, buildAction, ensureRegistered, extractStreamingBehavior, @@ -30,9 +29,7 @@ import { isActionableEvent, MCP_TOOL_PARAMS, } from "../../core/index.js"; -import { TlsTransport } from "../../core/tls-transport.js"; import { - loadOrCreateIdentity, releaseIdentityLock, type IdentitySlot, } from "../../core/identity-store.js"; @@ -155,17 +152,12 @@ function drainPending(filePath: string): string[] { } export async function run(): Promise { - // Persistent identity for this slot: a stable fingerprint means the agent - // ID survives restarts, so peers can keep targeting us + // Persistent identity for this slot: a stable device-id means the agent ID survives restarts, so peers can keep targeting us const identitySlot: IdentitySlot = { harness: "claude-code", cwd: process.cwd(), }; - const identity = loadOrCreateIdentity(identitySlot); - const store = new MeshStore(); - store.peerId = identity.fingerprint; - store.setTransport(new TlsTransport(store.events, identity)); - const tool = new CommsTool(store, store.discovery); + const { store, tool } = createBridgeMesh(identitySlot); let agentId: string | undefined; const claudeCodePid = findClaudeCodePid(); diff --git a/src/bridges/codex/tool.ts b/src/bridges/codex/tool.ts index da4c400..3b0d290 100644 --- a/src/bridges/codex/tool.ts +++ b/src/bridges/codex/tool.ts @@ -12,18 +12,13 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { - MeshStore, - CommsTool, + createBridgeMesh, buildAction, ensureRegistered, drainAndFormat, MCP_TOOL_PARAMS, } from "../../core/index.js"; -import { TlsTransport } from "../../core/tls-transport.js"; -import { - loadOrCreateIdentity, - type IdentitySlot, -} from "../../core/identity-store.js"; +import type { IdentitySlot } from "../../core/identity-store.js"; import { tryStartWebServer } from "../user/web/server.js"; import { nanoid } from "../../core/nanoid.js"; @@ -32,15 +27,9 @@ function isRecord(value: unknown): value is Record { } export async function run(): Promise { - // Persistent identity for this slot: a stable fingerprint means the agent - // ID survives restarts, so peers can keep targeting us. The stdio server - // has no graceful shutdown hook; a stale lock self-heals via the pid probe. + // Persistent identity for this slot: a stable device-id means the agent ID survives restarts, so peers can keep targeting us. The stdio server has no graceful shutdown hook; a stale lock self-heals via the pid probe. const identitySlot: IdentitySlot = { harness: "codex", cwd: process.cwd() }; - const identity = loadOrCreateIdentity(identitySlot); - const store = new MeshStore(); - store.peerId = identity.fingerprint; - store.setTransport(new TlsTransport(store.events, identity)); - const tool = new CommsTool(store, store.discovery); + const { store, tool } = createBridgeMesh(identitySlot); let agentId: string | undefined; const mcp = new McpServer( diff --git a/src/bridges/opencode/plugin.ts b/src/bridges/opencode/plugin.ts index 25c471f..4432689 100644 --- a/src/bridges/opencode/plugin.ts +++ b/src/bridges/opencode/plugin.ts @@ -9,26 +9,17 @@ */ import { - MeshStore, + createBridgeMesh, ensureRegistered, formatDeliveryEvent, } from "../../core/index.js"; -import { TlsTransport } from "../../core/tls-transport.js"; -import { - loadOrCreateIdentity, - type IdentitySlot, -} from "../../core/identity-store.js"; +import type { IdentitySlot } from "../../core/identity-store.js"; import { tryStartWebServer } from "../user/web/server.js"; import { nanoid } from "../../core/nanoid.js"; -// Persistent identity for this slot: a stable fingerprint means the agent -// ID survives restarts, so peers can keep targeting us. A plugin unload has -// no hook here; a stale lock self-heals via the pid probe. +// Persistent identity for this slot: a stable device-id means the agent ID survives restarts, so peers can keep targeting us. A plugin unload has no hook here; a stale lock self-heals via the pid probe. const identitySlot: IdentitySlot = { harness: "opencode", cwd: process.cwd() }; -const identity = loadOrCreateIdentity(identitySlot); -const store = new MeshStore(); -store.peerId = identity.fingerprint; -store.setTransport(new TlsTransport(store.events, identity)); +const { store } = createBridgeMesh(identitySlot); // Minimal interface for the OpenCode SDK client we actually use interface OpenCodeClient { diff --git a/src/bridges/pi/index.ts b/src/bridges/pi/index.ts index d8c0d87..f9b1eef 100644 --- a/src/bridges/pi/index.ts +++ b/src/bridges/pi/index.ts @@ -19,8 +19,7 @@ import { Type } from "typebox"; import { StringEnum } from "@mariozechner/pi-ai"; import { - MeshStore, - CommsTool, + createBridgeMesh, buildAction, ensureProjectRoom, ensureRegistered, @@ -28,9 +27,7 @@ import { formatDeliveryEvent, isActionableEvent, } from "../../core/index.js"; -import { TlsTransport } from "../../core/tls-transport.js"; import { - loadOrCreateIdentity, releaseIdentityLock, type IdentitySlot, } from "../../core/identity-store.js"; @@ -45,14 +42,9 @@ function getWebPort(handle: WebServerHandle): number | undefined { } export default function (pi: ExtensionAPI) { - // Persistent identity for this slot: a stable fingerprint means the agent - // ID survives restarts, so peers can keep targeting us + // Persistent identity for this slot: a stable device-id means the agent ID survives restarts, so peers can keep targeting us const identitySlot: IdentitySlot = { harness: "pi", cwd: process.cwd() }; - const identity = loadOrCreateIdentity(identitySlot); - const store = new MeshStore(); - store.peerId = identity.fingerprint; - store.setTransport(new TlsTransport(store.events, identity)); - const tool = new CommsTool(store, store.discovery); + const { store, tool } = createBridgeMesh(identitySlot); let agentId: string | undefined; let webHandle: WebServerHandle | undefined; diff --git a/src/bridges/user/controller.ts b/src/bridges/user/controller.ts index d43e89d..5564b5b 100644 --- a/src/bridges/user/controller.ts +++ b/src/bridges/user/controller.ts @@ -9,12 +9,11 @@ import { EventEmitter } from "node:events"; import { MeshStore, CommsTool, + createBridgeMesh, ensureRegistered, formatDeliveryEvent, } from "../../core/index.js"; -import { TlsTransport } from "../../core/tls-transport.js"; import { - loadOrCreateIdentity, releaseIdentityLock, type IdentitySlot, } from "../../core/identity-store.js"; @@ -48,15 +47,12 @@ export class ChatController extends EventEmitter { coordinatorPort?: number, ) { super(); - // Persistent identity for the web user's slot so the chat identity - // survives relaunches of the standalone web CLI + // Persistent identity for the web user's slot so the chat identity survives relaunches of the standalone web CLI const identitySlot: IdentitySlot = { harness: "user", cwd: process.cwd() }; this.ownedIdentitySlot = identitySlot; - const identity = loadOrCreateIdentity(identitySlot); - this.store = new MeshStore(coordinatorPort); - this.store.peerId = identity.fingerprint; - this.store.setTransport(new TlsTransport(this.store.events, identity)); - this.tool = new CommsTool(this.store, this.store.discovery); + const { store, tool } = createBridgeMesh(identitySlot, coordinatorPort); + this.store = store; + this.tool = tool; // Push delivery events to UIs this.store.onDelivery = (_agentId: string, event: DeliveryEvent) => { diff --git a/src/test/approval.integration.test.ts b/src/test/approval.integration.test.ts index e2f1243..02ab221 100644 --- a/src/test/approval.integration.test.ts +++ b/src/test/approval.integration.test.ts @@ -28,14 +28,9 @@ function findFreePort(): Promise { }); } -/** Unique port counter to avoid reusing ports across sequential tests. */ -let portOffset = 0; - -/** Find a free port with a unique offset to avoid conflicts. */ -async function uniquePort(): Promise { - portOffset += 10; - const base = await findFreePort(); - return base + portOffset; +/** Find a free port for a single test's use. An alias for findFreePort(): asking the OS for a fresh ephemeral port each call already guarantees distinctness from any other currently-bound port, so no arithmetic offset is layered on top -- a prior +offset scheme could push an already-high OS-assigned port past 65535 and fail with ERR_SOCKET_BAD_PORT. */ +function uniquePort(): Promise { + return findFreePort(); } function sleep(ms: number): Promise { @@ -45,7 +40,7 @@ function sleep(ms: number): Promise { describe("connection approval", () => { void test("coordinator receives connection_request from connecting peer", async () => { const portA = await uniquePort(); - const portB = portA + 100; + const portB = await uniquePort(); // Set up coordinator (store A) const storeA = new MeshStore(portA); @@ -121,7 +116,7 @@ describe("connection approval", () => { void test("accept establishes the peer connection", async () => { const portA = await uniquePort(); - const portB = portA + 100; + const portB = await uniquePort(); const storeA = new MeshStore(portA); wireTestTransport(storeA); @@ -200,7 +195,7 @@ describe("connection approval", () => { void test("reject closes with reason", async () => { const portA = await uniquePort(); - const portB = portA + 100; + const portB = await uniquePort(); const storeA = new MeshStore(portA); wireTestTransport(storeA); @@ -273,7 +268,7 @@ describe("connection approval", () => { void test("mesh_pending lists pending connections", async () => { const portA = await uniquePort(); - const portB = portA + 100; + const portB = await uniquePort(); const storeA = new MeshStore(portA); wireTestTransport(storeA); @@ -351,7 +346,7 @@ describe("connection approval", () => { void test("tool handles mesh_connect/mesh_accept/mesh_reject/mesh_pending actions", async () => { const portA = await uniquePort(); - const portB = portA + 100; + const portB = await uniquePort(); const storeA = new MeshStore(portA); wireTestTransport(storeA); @@ -503,7 +498,7 @@ describe("connection approval", () => { void test("tool mesh_reject returns error message", async () => { const portA = await uniquePort(); - const portB = portA + 100; + const portB = await uniquePort(); const storeA = new MeshStore(portA); wireTestTransport(storeA);