/** * Short-lived WS connection helper for CLI commands (peers, send, inbox, state). * * Opens a connection to one mesh, runs a callback, then closes cleanly. * The caller never deals with connect/close lifecycle. */ import { hostname } from "node:os"; import { createInterface } from "node:readline"; import { BrokerClient } from "~/services/broker/facade.js"; import { readConfig } from "~/services/config/facade.js"; import type { JoinedMesh } from "~/services/config/facade.js"; export interface ConnectOpts { /** Mesh slug to connect to. Auto-selects if only one mesh joined. */ meshSlug?: string | null; /** Display name for this session. Defaults to hostname-pid. */ displayName?: string; /** Connect to all meshes and run fn for each. */ all?: boolean; } async function pickMesh(meshes: JoinedMesh[]): Promise { console.log("\n Select mesh:"); meshes.forEach((m, i) => { console.log(` ${i + 1}) ${m.slug}`); }); console.log(""); const rl = createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(" Choice [1]: ", (answer) => { rl.close(); const idx = parseInt(answer || "1", 10) - 1; if (idx >= 0 && idx < meshes.length) { resolve(meshes[idx]!); } else { console.error(" Invalid choice, using first mesh."); resolve(meshes[0]!); } }); }); } export async function withMesh( opts: ConnectOpts, fn: (client: BrokerClient, mesh: JoinedMesh) => Promise, ): Promise { const config = readConfig(); if (config.meshes.length === 0) { console.error("No meshes joined. Run `claudemesh join ` first."); process.exit(1); } let mesh: JoinedMesh; if (opts.meshSlug) { const found = config.meshes.find((m) => m.slug === opts.meshSlug); if (!found) { console.error( `Mesh "${opts.meshSlug}" not found. Joined: ${config.meshes.map((m) => m.slug).join(", ")}`, ); process.exit(1); } mesh = found; } else if (config.meshes.length === 1) { mesh = config.meshes[0]!; } else { mesh = await pickMesh(config.meshes); } const displayName = opts.displayName ?? config.displayName ?? `${hostname()}-${process.pid}`; const client = new BrokerClient(mesh, { displayName }); try { await client.connect(); const result = await fn(client, mesh); return result; } finally { client.close(); } }