@geohar/un-bien 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +726 -0
  3. package/dist/actions/handlers.d.ts +77 -0
  4. package/dist/actions/handlers.js +26 -0
  5. package/dist/actions/handlers.js.map +1 -0
  6. package/dist/actions/registry.d.ts +39 -0
  7. package/dist/actions/registry.js +62 -0
  8. package/dist/actions/registry.js.map +1 -0
  9. package/dist/bin/supervisord.d.ts +2 -0
  10. package/dist/bin/supervisord.js +86 -0
  11. package/dist/bin/supervisord.js.map +1 -0
  12. package/dist/config.d.ts +84 -0
  13. package/dist/config.js +115 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/daemon/client.d.ts +20 -0
  16. package/dist/daemon/client.js +131 -0
  17. package/dist/daemon/client.js.map +1 -0
  18. package/dist/daemon/control_protocol.d.ts +176 -0
  19. package/dist/daemon/control_protocol.js +63 -0
  20. package/dist/daemon/control_protocol.js.map +1 -0
  21. package/dist/daemon/cron_log.d.ts +45 -0
  22. package/dist/daemon/cron_log.js +71 -0
  23. package/dist/daemon/cron_log.js.map +1 -0
  24. package/dist/daemon/cron_registry.d.ts +80 -0
  25. package/dist/daemon/cron_registry.js +198 -0
  26. package/dist/daemon/cron_registry.js.map +1 -0
  27. package/dist/daemon/id.d.ts +24 -0
  28. package/dist/daemon/id.js +36 -0
  29. package/dist/daemon/id.js.map +1 -0
  30. package/dist/daemon/install.d.ts +170 -0
  31. package/dist/daemon/install.js +603 -0
  32. package/dist/daemon/install.js.map +1 -0
  33. package/dist/daemon/registry.d.ts +63 -0
  34. package/dist/daemon/registry.js +153 -0
  35. package/dist/daemon/registry.js.map +1 -0
  36. package/dist/daemon/rpc_child.d.ts +194 -0
  37. package/dist/daemon/rpc_child.js +419 -0
  38. package/dist/daemon/rpc_child.js.map +1 -0
  39. package/dist/daemon/supervisor.d.ts +93 -0
  40. package/dist/daemon/supervisor.js +666 -0
  41. package/dist/daemon/supervisor.js.map +1 -0
  42. package/dist/extension_ui_bridge.d.ts +26 -0
  43. package/dist/extension_ui_bridge.js +341 -0
  44. package/dist/extension_ui_bridge.js.map +1 -0
  45. package/dist/index.d.ts +197 -0
  46. package/dist/index.js +5750 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/mcp/mesh_result.d.ts +10 -0
  49. package/dist/mcp/mesh_result.js +42 -0
  50. package/dist/mcp/mesh_result.js.map +1 -0
  51. package/dist/mcp/mesh_server.d.ts +16 -0
  52. package/dist/mcp/mesh_server.js +373 -0
  53. package/dist/mcp/mesh_server.js.map +1 -0
  54. package/dist/mesh/canonical.d.ts +30 -0
  55. package/dist/mesh/canonical.js +63 -0
  56. package/dist/mesh/canonical.js.map +1 -0
  57. package/dist/mesh/client.d.ts +18 -0
  58. package/dist/mesh/client.js +127 -0
  59. package/dist/mesh/client.js.map +1 -0
  60. package/dist/mesh/encoding.d.ts +38 -0
  61. package/dist/mesh/encoding.js +217 -0
  62. package/dist/mesh/encoding.js.map +1 -0
  63. package/dist/mesh/self_revoke.d.ts +73 -0
  64. package/dist/mesh/self_revoke.js +407 -0
  65. package/dist/mesh/self_revoke.js.map +1 -0
  66. package/dist/mesh/siblings.d.ts +49 -0
  67. package/dist/mesh/siblings.js +159 -0
  68. package/dist/mesh/siblings.js.map +1 -0
  69. package/dist/mesh/types.d.ts +34 -0
  70. package/dist/mesh/types.js +11 -0
  71. package/dist/mesh/types.js.map +1 -0
  72. package/dist/mesh/verify.d.ts +17 -0
  73. package/dist/mesh/verify.js +86 -0
  74. package/dist/mesh/verify.js.map +1 -0
  75. package/dist/pairing/crypto.d.ts +8 -0
  76. package/dist/pairing/crypto.js +22 -0
  77. package/dist/pairing/crypto.js.map +1 -0
  78. package/dist/pairing/qr.d.ts +59 -0
  79. package/dist/pairing/qr.js +127 -0
  80. package/dist/pairing/qr.js.map +1 -0
  81. package/dist/pairing/storage.d.ts +120 -0
  82. package/dist/pairing/storage.js +614 -0
  83. package/dist/pairing/storage.js.map +1 -0
  84. package/dist/panel_bridge.d.ts +15 -0
  85. package/dist/panel_bridge.js +409 -0
  86. package/dist/panel_bridge.js.map +1 -0
  87. package/dist/paths.d.ts +39 -0
  88. package/dist/paths.js +51 -0
  89. package/dist/paths.js.map +1 -0
  90. package/dist/protocol/codec.d.ts +7 -0
  91. package/dist/protocol/codec.js +48 -0
  92. package/dist/protocol/codec.js.map +1 -0
  93. package/dist/protocol/types.d.ts +404 -0
  94. package/dist/protocol/types.js +2 -0
  95. package/dist/protocol/types.js.map +1 -0
  96. package/dist/rooms.d.ts +42 -0
  97. package/dist/rooms.js +78 -0
  98. package/dist/rooms.js.map +1 -0
  99. package/dist/session/bridge.d.ts +37 -0
  100. package/dist/session/bridge.js +134 -0
  101. package/dist/session/bridge.js.map +1 -0
  102. package/dist/session/broker.d.ts +189 -0
  103. package/dist/session/broker.js +527 -0
  104. package/dist/session/broker.js.map +1 -0
  105. package/dist/session/broker_remote.d.ts +74 -0
  106. package/dist/session/broker_remote.js +581 -0
  107. package/dist/session/broker_remote.js.map +1 -0
  108. package/dist/session/cwd_lock.d.ts +33 -0
  109. package/dist/session/cwd_lock.js +127 -0
  110. package/dist/session/cwd_lock.js.map +1 -0
  111. package/dist/session/debug_log.d.ts +3 -0
  112. package/dist/session/debug_log.js +32 -0
  113. package/dist/session/debug_log.js.map +1 -0
  114. package/dist/session/envelope.d.ts +33 -0
  115. package/dist/session/envelope.js +117 -0
  116. package/dist/session/envelope.js.map +1 -0
  117. package/dist/session/global_config.d.ts +33 -0
  118. package/dist/session/global_config.js +73 -0
  119. package/dist/session/global_config.js.map +1 -0
  120. package/dist/session/ipc.d.ts +27 -0
  121. package/dist/session/ipc.js +22 -0
  122. package/dist/session/ipc.js.map +1 -0
  123. package/dist/session/leader_election.d.ts +32 -0
  124. package/dist/session/leader_election.js +108 -0
  125. package/dist/session/leader_election.js.map +1 -0
  126. package/dist/session/local_config.d.ts +67 -0
  127. package/dist/session/local_config.js +193 -0
  128. package/dist/session/local_config.js.map +1 -0
  129. package/dist/session/mesh_node.d.ts +116 -0
  130. package/dist/session/mesh_node.js +596 -0
  131. package/dist/session/mesh_node.js.map +1 -0
  132. package/dist/session/peer.d.ts +139 -0
  133. package/dist/session/peer.js +474 -0
  134. package/dist/session/peer.js.map +1 -0
  135. package/dist/session/peer_inventory.d.ts +13 -0
  136. package/dist/session/peer_inventory.js +48 -0
  137. package/dist/session/peer_inventory.js.map +1 -0
  138. package/dist/session/peer_limits.d.ts +9 -0
  139. package/dist/session/peer_limits.js +24 -0
  140. package/dist/session/peer_limits.js.map +1 -0
  141. package/dist/session/rpc_envelope.d.ts +108 -0
  142. package/dist/session/rpc_envelope.js +221 -0
  143. package/dist/session/rpc_envelope.js.map +1 -0
  144. package/dist/session/rpc_inbound.d.ts +55 -0
  145. package/dist/session/rpc_inbound.js +136 -0
  146. package/dist/session/rpc_inbound.js.map +1 -0
  147. package/dist/session/setup_wizard.d.ts +39 -0
  148. package/dist/session/setup_wizard.js +63 -0
  149. package/dist/session/setup_wizard.js.map +1 -0
  150. package/dist/session/tools.d.ts +24 -0
  151. package/dist/session/tools.js +272 -0
  152. package/dist/session/tools.js.map +1 -0
  153. package/dist/session/wizard.d.ts +13 -0
  154. package/dist/session/wizard.js +20 -0
  155. package/dist/session/wizard.js.map +1 -0
  156. package/dist/transport/peer_channel.d.ts +52 -0
  157. package/dist/transport/peer_channel.js +145 -0
  158. package/dist/transport/peer_channel.js.map +1 -0
  159. package/dist/transport/pi_forward_client.d.ts +29 -0
  160. package/dist/transport/pi_forward_client.js +68 -0
  161. package/dist/transport/pi_forward_client.js.map +1 -0
  162. package/dist/transport/relay_client.d.ts +96 -0
  163. package/dist/transport/relay_client.js +210 -0
  164. package/dist/transport/relay_client.js.map +1 -0
  165. package/dist/ui/footer.d.ts +32 -0
  166. package/dist/ui/footer.js +34 -0
  167. package/dist/ui/footer.js.map +1 -0
  168. package/docs/daemon.md +289 -0
  169. package/package.json +92 -0
  170. package/service-templates/launchd.plist.template +35 -0
  171. package/service-templates/systemd.service.template +19 -0
  172. package/service-templates/task-launcher.vbs.template +10 -0
  173. package/service-templates/task-scheduler.xml.template +38 -0
  174. package/skills/agent-network/SKILL.md +310 -0
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP server that bridges Claude Code to the un-bien agent mesh.
4
+ *
5
+ * Spawned by Claude Code as an MCP server subprocess (stdio).
6
+ * Joins the mesh through the shared `MeshNode` abstraction — the SAME
7
+ * composition the Pi extension uses — so Claude is a first-class mesh
8
+ * participant: it can lead the local UDS broker when no Pi/daemon is up,
9
+ * and (as leader) bring up its own cross-PC relay bridge with its own
10
+ * Pi-key. As a follower it rides the existing leader's bridge.
11
+ *
12
+ * Launched by `unbien claude` (registers this in Claude's local MCP
13
+ * scope). Args: [--cwd <path>] [--name <agentName>] [--no-bridge]
14
+ * Env: UNBIEN_MCP_CWD, UNBIEN_MCP_NAME
15
+ */
16
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import { z } from "zod";
19
+ import { MeshNode } from "../session/mesh_node.js";
20
+ import { loadLocalConfig, defaultAgentName, localConfigExists, } from "../session/local_config.js";
21
+ import { formatMeshAckResult } from "./mesh_result.js";
22
+ import { sessionSockPath, sessionAuditPath, LOCAL_SESSION_NAME, } from "../session/global_config.js";
23
+ import { resolveRelayUrl } from "../config.js";
24
+ import { acquireCwdLock } from "../session/cwd_lock.js";
25
+ import { realpathSync } from "node:fs";
26
+ // ── Args / config ─────────────────────────────────────────────────────────────
27
+ const _argv = process.argv.slice(2);
28
+ // This agent's folder = the dir the `claude` session was launched in, which
29
+ // Claude sets as this subprocess's `process.cwd()`. We deliberately do NOT use
30
+ // CLAUDE_PROJECT_DIR: that's the git repo root, which would collapse every
31
+ // monorepo subproject (app/, relay/, …) into one identity + one lock. The
32
+ // `unbien claude` launcher therefore registers us WITHOUT a baked `--cwd`,
33
+ // so one shared local-scope entry self-identifies per session. `--cwd` and
34
+ // UNBIEN_MCP_CWD remain as explicit overrides (tests / manual launches).
35
+ let _cwd = process.env["UNBIEN_MCP_CWD"] ?? process.cwd();
36
+ let _nameOverride = process.env["UNBIEN_MCP_NAME"];
37
+ let _bridgeEnabled = true;
38
+ for (let i = 0; i < _argv.length; i++) {
39
+ if (_argv[i] === "--cwd" && _argv[i + 1]) {
40
+ _cwd = _argv[++i];
41
+ }
42
+ else if (_argv[i] === "--name" && _argv[i + 1]) {
43
+ _nameOverride = _argv[++i];
44
+ }
45
+ else if (_argv[i] === "--no-bridge") {
46
+ _bridgeEnabled = false;
47
+ }
48
+ }
49
+ const _cfg = loadLocalConfig(_cwd);
50
+ const AGENT_NAME = _nameOverride ?? _cfg.agent_name ?? defaultAgentName(_cwd);
51
+ // Platform-aware (plan/40): POSIX → broker.sock file; Windows → named pipe.
52
+ const BROKER_SOCK = sessionSockPath(LOCAL_SESSION_NAME);
53
+ const AUDIT_PATH = sessionAuditPath(LOCAL_SESSION_NAME);
54
+ const inbox = [];
55
+ // ── Mesh node ─────────────────────────────────────────────────────────────────
56
+ const { url: relayUrl } = resolveRelayUrl();
57
+ // Diagnostics go to STDERR — stdout is the JSON-RPC channel, so writing there
58
+ // would corrupt the MCP protocol. Claude Code captures an MCP server's stderr
59
+ // into its mcp-logs, which is where these land for debugging.
60
+ function logErr(msg) {
61
+ process.stderr.write(`[un-bien-mesh ${isoNow()}] ${msg}\n`);
62
+ }
63
+ // Survive stray async failures. This process runs background work (relay WS
64
+ // churn, UDS failover, the MCP SDK) whose errors previously had NO global
65
+ // handler — a single unhandled rejection or exception silently killed this
66
+ // stdio subprocess, and Claude surfaced the MCP as "disconnected" with no
67
+ // trace. None of that background work is fatal to serving tools over stdio, so
68
+ // log loudly and keep running instead of dying. (We intentionally do NOT
69
+ // process.exit here: the tools stay available even if the mesh is degraded.)
70
+ process.on("unhandledRejection", (reason) => {
71
+ logErr(`unhandledRejection: ${reason instanceof Error ? (reason.stack ?? reason.message) : String(reason)}`);
72
+ });
73
+ process.on("uncaughtException", (err) => {
74
+ logErr(`uncaughtException: ${err.stack ?? err.message}`);
75
+ });
76
+ // Canonicalize so the (cwd, name) the broker keys us by matches roomIdForCwd
77
+ // and the Pi extension's own register (symlinked cwds map to one identity).
78
+ let _canonCwd = _cwd;
79
+ try {
80
+ _canonCwd = realpathSync(_cwd);
81
+ }
82
+ catch {
83
+ /* cwd missing — use raw path */
84
+ }
85
+ const mesh = new MeshNode({
86
+ sockPath: BROKER_SOCK,
87
+ name: AGENT_NAME,
88
+ cwd: _canonCwd, // plan/38: register with (cwd, name) → address `<cwd>@<name>`
89
+ auditPath: AUDIT_PATH,
90
+ // Own Pi-key cross-PC bridge — active only when this node leads (no Pi /
91
+ // daemon already hosting the broker for this cwd). As a follower the
92
+ // bridge stays dormant and cross-PC rides the existing leader.
93
+ ...(_bridgeEnabled && relayUrl !== null
94
+ ? { bridge: { relayUrl, cwd: _cwd } }
95
+ : {}),
96
+ // Silent: stdout is the MCP JSON-RPC channel and stderr noise isn't wanted.
97
+ // Real failures still surface via the global handlers / fail-loud below.
98
+ log: () => {
99
+ /* no-op */
100
+ },
101
+ });
102
+ let meshReady = false;
103
+ // When the mesh isn't joined, this explains why (folder busy / connecting), so
104
+ // the tools return something actionable instead of a generic "not connected".
105
+ let degradedReason = "connecting to the mesh…";
106
+ // ── MCP server setup ──────────────────────────────────────────────────────────
107
+ const mcp = new McpServer({ name: "un-bien-mesh", version: "0.4.3" }, {
108
+ capabilities: { experimental: { "claude/channel": {} } },
109
+ instructions: [
110
+ `You are connected to the un-bien agent mesh as "${AGENT_NAME}".`,
111
+ "At the start of each turn call get_messages to check for incoming messages from other agents.",
112
+ "Use list_peers to discover available agents.",
113
+ "Use agent_send to send messages — pass the exact address returned by list_peers (form `<cwd>@<name>`, `<pc>:` prefix cross-PC) VERBATIM; never build one by hand.",
114
+ 'Use "broadcast" as the target to send to all peers in your folder (cwd) at once.',
115
+ "Follow the agent-network protocol (in your system prompt) for the full details (ACK statuses, replies via re, `<cwd>@<name>` addresses, cross-PC `<pc>:` prefix).",
116
+ ].join("\n"),
117
+ });
118
+ function notReady() {
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: `Mesh not available yet — ${degradedReason}`,
124
+ },
125
+ ],
126
+ isError: true,
127
+ };
128
+ }
129
+ mcp.registerTool("list_peers", {
130
+ description: "List all agents currently in the mesh (local + remote PCs).",
131
+ inputSchema: {},
132
+ }, async () => {
133
+ if (!meshReady)
134
+ return notReady();
135
+ try {
136
+ const peers = await mesh.listPeers();
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: peers.length > 0 ? peers.join("\n") : "(no peers)",
142
+ },
143
+ ],
144
+ };
145
+ }
146
+ catch (e) {
147
+ return {
148
+ content: [
149
+ { type: "text", text: `list_peers failed: ${String(e)}` },
150
+ ],
151
+ isError: true,
152
+ };
153
+ }
154
+ });
155
+ mcp.registerTool("agent_send", {
156
+ description: 'Send a message to another agent. Use "broadcast" to send to all peers.',
157
+ inputSchema: {
158
+ to: z
159
+ .string()
160
+ .describe('Peer address from list_peers (form "<cwd>@<name>", or "<pc>:<cwd>@<name>" cross-PC) echoed verbatim, or "broadcast"'),
161
+ body: z.unknown().describe("Message body — any JSON value"),
162
+ re: z
163
+ .string()
164
+ .optional()
165
+ .describe("Optional: id of the message you are replying to"),
166
+ },
167
+ }, async ({ to, body, re }) => {
168
+ if (!meshReady)
169
+ return notReady();
170
+ if (to === mesh.address() || to === mesh.name()) {
171
+ return {
172
+ content: [{ type: "text", text: "Cannot send to yourself" }],
173
+ isError: true,
174
+ };
175
+ }
176
+ try {
177
+ if (to === "broadcast") {
178
+ await mesh.send(to, body, re ?? null);
179
+ return { content: [{ type: "text", text: "Broadcast sent" }] };
180
+ }
181
+ const ack = await mesh.sendWithAck(to, body, re ?? null);
182
+ return formatMeshAckResult(to, ack);
183
+ }
184
+ catch (e) {
185
+ return {
186
+ content: [{ type: "text", text: `send failed: ${String(e)}` }],
187
+ isError: true,
188
+ };
189
+ }
190
+ });
191
+ mcp.registerTool("get_messages", {
192
+ description: "Return and clear all pending incoming messages from other agents. Call at the start of each turn.",
193
+ inputSchema: {},
194
+ }, async () => {
195
+ const msgs = inbox.splice(0);
196
+ if (msgs.length === 0)
197
+ return { content: [{ type: "text", text: "(no messages)" }] };
198
+ const lines = msgs.map((m) => `[${m.at}] from=${m.from}${m.re ? ` re=${m.re}` : ""}\nid=${m.id}\n${JSON.stringify(m.body, null, 2)}`);
199
+ return { content: [{ type: "text", text: lines.join("\n\n") }] };
200
+ });
201
+ // ── Main ──────────────────────────────────────────────────────────────────────
202
+ function isoNow() {
203
+ return new Date().toISOString();
204
+ }
205
+ // Background lock+join state. The cwd lock enforces the per-folder singleton
206
+ // (at most one un-bien agent — Pi OR Claude — per folder; a second peer with
207
+ // the same cwd-derived name would be a ghost).
208
+ //
209
+ // We retry only briefly — just enough to ride out a restart RACE (the previous
210
+ // MCP for this folder is still tearing down when Claude respawns us). After a
211
+ // short grace we FAIL LOUD (exit non-zero) rather than degrade forever: a
212
+ // silent connected-but-idle MCP hides the real problem (you launched claude in
213
+ // the wrong folder, or this folder already has a live agent — a duplicate
214
+ // session). Failing makes Claude show the MCP as errored so you actually see it.
215
+ const JOIN_RETRY_MS = 2_000;
216
+ const MAX_JOIN_ATTEMPTS = 4; // ~6–8s grace for a restart race, then fail loud
217
+ let _lock = null;
218
+ let _lockRetryTimer = null;
219
+ let _lockAttempt = 0;
220
+ let _joined = false;
221
+ let _shuttingDown = false;
222
+ async function main() {
223
+ // Subscribe BEFORE connecting so we don't miss early envelopes. The
224
+ // SessionPeer swallows broker ACKs / system events itself, so handlers
225
+ // only see real peer messages (and replies, which carry `re`).
226
+ mesh.onMessage((env) => {
227
+ // plan/34 (passive presence): broker control/system envelopes
228
+ // (`peer_joined` / `peer_left` / `list_peers_reply`) are presence signals,
229
+ // NOT agent messages. Never push them to the inbox or the claude/channel —
230
+ // otherwise a peer joining/leaving would wake this agent's turn. Discovery
231
+ // is pull-based via `list_peers`. (Real broker ACKs and `re` replies are
232
+ // already swallowed upstream by SessionPeer.)
233
+ if (env.from === "broker" || env.from.endsWith(":broker"))
234
+ return;
235
+ const msg = {
236
+ from: env.from,
237
+ body: env.body,
238
+ id: env.id,
239
+ re: env.re,
240
+ at: isoNow(),
241
+ };
242
+ inbox.push(msg);
243
+ // Push via claude/channel so Claude wakes immediately (when the session
244
+ // was launched with --dangerously-load-development-channels server:un-bien-mesh).
245
+ void mcp.server
246
+ .notification({
247
+ method: "notifications/claude/channel",
248
+ params: {
249
+ content: `📨 Message from ${msg.from}:\n${JSON.stringify(msg.body, null, 2)}`,
250
+ },
251
+ })
252
+ .catch(() => {
253
+ /* channels not enabled — get_messages polling covers it */
254
+ });
255
+ });
256
+ // Connect the stdio transport FIRST and unconditionally, so Claude Code
257
+ // always sees this server as connected and the tools stay reachable — even
258
+ // before (or entirely without) a mesh join. The mesh join is layered on
259
+ // best-effort below; a busy folder no longer kills the MCP.
260
+ const transport = new StdioServerTransport();
261
+ transport.onclose = shutdown;
262
+ process.stdin.on("end", shutdown);
263
+ process.stdin.on("close", shutdown);
264
+ await mcp.connect(transport);
265
+ // Only join the mesh if this folder is an actual un-bien agent — i.e. it
266
+ // has a local config (written by the `unbien claude` wizard) or an
267
+ // explicit name override. `-s local` MCP registrations are inherited by
268
+ // EVERY claude session in the git repo, so without this gate a plain claude
269
+ // opened in any subfolder would auto-grab that folder's lock and join as a
270
+ // stray agent — colliding with the real agent. No config ⇒ stay connected
271
+ // but idle (tools report why); don't lock, don't join, don't retry.
272
+ if (localConfigExists(_cwd) || _nameOverride !== undefined) {
273
+ // Kick off the lock+join in the background. Never awaited — the MCP is
274
+ // already serving; mesh availability arrives (and recovers) asynchronously.
275
+ void tryJoinMesh();
276
+ }
277
+ else {
278
+ degradedReason =
279
+ `this folder is not a un-bien agent (no config). Run "unbien claude" ` +
280
+ `here to make it one. Mesh tools are idle.`;
281
+ }
282
+ }
283
+ /** Acquire the per-cwd lock, then join the mesh. Retries briefly to absorb a
284
+ * restart race; if the lock or join still fails after MAX_JOIN_ATTEMPTS, FAIL
285
+ * LOUD (exit) so the failure is visible instead of silently degrading. */
286
+ async function tryJoinMesh() {
287
+ if (_joined || _shuttingDown)
288
+ return;
289
+ const res = await acquireCwdLock(_cwd);
290
+ if (!res.ok) {
291
+ _lockAttempt++;
292
+ if (_lockAttempt >= MAX_JOIN_ATTEMPTS) {
293
+ _failLoud(`folder already served by another un-bien agent (lock: ${res.lockPath})`);
294
+ }
295
+ degradedReason = `folder busy (lock ${res.lockPath}); attempt ${_lockAttempt}/${MAX_JOIN_ATTEMPTS}`;
296
+ _scheduleJoinRetry(JOIN_RETRY_MS);
297
+ return;
298
+ }
299
+ _lock = res;
300
+ try {
301
+ await mesh.connect();
302
+ meshReady = true;
303
+ _joined = true;
304
+ _lockAttempt = 0;
305
+ }
306
+ catch (e) {
307
+ // Got the lock but the broker join failed (e.g. socket churn). Release the
308
+ // lock so another contender isn't starved, then retry / fail loud.
309
+ _lockAttempt++;
310
+ try {
311
+ _lock.release();
312
+ }
313
+ catch {
314
+ /* best-effort */
315
+ }
316
+ _lock = null;
317
+ if (_lockAttempt >= MAX_JOIN_ATTEMPTS) {
318
+ _failLoud(`mesh join failed: ${String(e)}`);
319
+ }
320
+ degradedReason = `mesh join failed (attempt ${_lockAttempt}/${MAX_JOIN_ATTEMPTS}): ${String(e)}`;
321
+ _scheduleJoinRetry(JOIN_RETRY_MS);
322
+ }
323
+ }
324
+ function _scheduleJoinRetry(delayMs) {
325
+ if (_lockRetryTimer || _joined || _shuttingDown)
326
+ return;
327
+ const t = setTimeout(() => {
328
+ _lockRetryTimer = null;
329
+ void tryJoinMesh();
330
+ }, delayMs);
331
+ // Don't let the retry timer alone keep the process alive past stdin close.
332
+ t.unref?.();
333
+ _lockRetryTimer = t;
334
+ }
335
+ /** Exit non-zero with a loud, specific reason so Claude surfaces the MCP as
336
+ * errored (visible) instead of a silent connected-but-idle peer. */
337
+ function _failLoud(reason) {
338
+ logErr(`FATAL: ${reason}. Exiting so the failure is visible. cwd=${_cwd}. ` +
339
+ `Most likely you launched claude in the WRONG FOLDER, or this folder ` +
340
+ `already has a running un-bien agent (a duplicate session). ` +
341
+ `Launch one agent per folder via "unbien claude" from the project dir.`);
342
+ process.exit(1);
343
+ }
344
+ // Exit cleanly when Claude Code disconnects. The MeshNode keeps a UDS socket
345
+ // (and, when leader, a relay WS) open, so without this the process would linger
346
+ // after Claude exits — orphaning the mesh peer (it keeps showing "online" with
347
+ // nothing attached). We leave the mesh, then exit. Triggered by the MCP
348
+ // transport closing or stdin hitting EOF (whichever the host does first).
349
+ function shutdown() {
350
+ if (_shuttingDown)
351
+ return;
352
+ _shuttingDown = true;
353
+ if (_lockRetryTimer) {
354
+ clearTimeout(_lockRetryTimer);
355
+ _lockRetryTimer = null;
356
+ }
357
+ try {
358
+ _lock?.release();
359
+ }
360
+ catch {
361
+ /* OS frees the UDS lock on exit anyway */
362
+ }
363
+ void Promise.resolve(mesh.close())
364
+ .catch(() => {
365
+ /* best-effort */
366
+ })
367
+ .finally(() => process.exit(0));
368
+ }
369
+ main().catch((err) => {
370
+ process.stderr.write(`[un-bien-mesh] fatal: ${String(err)}\n`);
371
+ process.exit(1);
372
+ });
373
+ //# sourceMappingURL=mesh_server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_server.js","sourceRoot":"","sources":["../../src/mcp/mesh_server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * JCS-like canonical JSON encoder for the mesh membership protocol.
3
+ *
4
+ * **Spec**: plan/24-mesh-membership.md — "chaves ordenadas alfabeticamente +
5
+ * separadores `,` e `:` sem espaços. Mesma regra em Dart, Rust, TypeScript."
6
+ *
7
+ * **Bit-compatibility contract** (must hold across the 3 implementations):
8
+ * - Object keys sorted lexicographically by UTF-16 code unit order
9
+ * (matches Dart's String compareTo, Rust's BTreeMap<String, _>, and
10
+ * JS's Array.prototype.sort() default).
11
+ * - No whitespace between any tokens.
12
+ * - Arrays preserve insertion order (caller's responsibility to sort if
13
+ * determinism across producers matters — verifier doesn't enforce).
14
+ * - Numbers serialized via JSON.stringify default (no trailing zeros).
15
+ * Mesh schema only carries integers (`version`, `issued_at`), so the
16
+ * fragile float-formatting space is avoided by design.
17
+ * - Strings UTF-8 encoded, JSON-escape rules per RFC 8259 (control chars
18
+ * and `\\`, `"` escaped; raw UTF-8 for printable code points). Matches
19
+ * `dart:convert.jsonEncode` and `serde_json::to_string` defaults.
20
+ * - `null` preserved; `undefined`/functions/symbols dropped (mirrors
21
+ * JSON.stringify).
22
+ *
23
+ * Verification path: receiver canonicalizes nothing — it verifies the
24
+ * raw blob bytes as-received against the signature. This module is used
25
+ * only on the *sender* side, and on the *test* side to construct fixtures
26
+ * for `verify.test.ts`.
27
+ */
28
+ export declare function canonicalize(value: unknown): string;
29
+ /** Canonical JSON string as UTF-8 bytes. The exact bytes that get signed. */
30
+ export declare function canonicalBytes(value: unknown): Uint8Array;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * JCS-like canonical JSON encoder for the mesh membership protocol.
3
+ *
4
+ * **Spec**: plan/24-mesh-membership.md — "chaves ordenadas alfabeticamente +
5
+ * separadores `,` e `:` sem espaços. Mesma regra em Dart, Rust, TypeScript."
6
+ *
7
+ * **Bit-compatibility contract** (must hold across the 3 implementations):
8
+ * - Object keys sorted lexicographically by UTF-16 code unit order
9
+ * (matches Dart's String compareTo, Rust's BTreeMap<String, _>, and
10
+ * JS's Array.prototype.sort() default).
11
+ * - No whitespace between any tokens.
12
+ * - Arrays preserve insertion order (caller's responsibility to sort if
13
+ * determinism across producers matters — verifier doesn't enforce).
14
+ * - Numbers serialized via JSON.stringify default (no trailing zeros).
15
+ * Mesh schema only carries integers (`version`, `issued_at`), so the
16
+ * fragile float-formatting space is avoided by design.
17
+ * - Strings UTF-8 encoded, JSON-escape rules per RFC 8259 (control chars
18
+ * and `\\`, `"` escaped; raw UTF-8 for printable code points). Matches
19
+ * `dart:convert.jsonEncode` and `serde_json::to_string` defaults.
20
+ * - `null` preserved; `undefined`/functions/symbols dropped (mirrors
21
+ * JSON.stringify).
22
+ *
23
+ * Verification path: receiver canonicalizes nothing — it verifies the
24
+ * raw blob bytes as-received against the signature. This module is used
25
+ * only on the *sender* side, and on the *test* side to construct fixtures
26
+ * for `verify.test.ts`.
27
+ */
28
+ export function canonicalize(value) {
29
+ if (value === null)
30
+ return "null";
31
+ if (typeof value === "boolean")
32
+ return value ? "true" : "false";
33
+ if (typeof value === "number") {
34
+ if (!Number.isFinite(value)) {
35
+ throw new Error("canonical: non-finite number cannot be JSON-encoded");
36
+ }
37
+ return JSON.stringify(value);
38
+ }
39
+ if (typeof value === "string")
40
+ return JSON.stringify(value);
41
+ if (Array.isArray(value)) {
42
+ // Inside arrays, undefined → null (mirrors JSON.stringify).
43
+ const items = value.map((v) => v === undefined ? "null" : canonicalize(v));
44
+ return "[" + items.join(",") + "]";
45
+ }
46
+ if (typeof value === "object") {
47
+ const obj = value;
48
+ const keys = Object.keys(obj)
49
+ .filter((k) => obj[k] !== undefined)
50
+ .sort();
51
+ const pairs = keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k]));
52
+ return "{" + pairs.join(",") + "}";
53
+ }
54
+ // undefined / functions / symbols at the root → JSON.stringify returns
55
+ // undefined; we surface as "null" for callers that wrap us.
56
+ return "null";
57
+ }
58
+ const TEXT_ENCODER = new TextEncoder();
59
+ /** Canonical JSON string as UTF-8 bytes. The exact bytes that get signed. */
60
+ export function canonicalBytes(value) {
61
+ return TEXT_ENCODER.encode(canonicalize(value));
62
+ }
63
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../src/mesh/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,4DAA4D;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5B,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAC3C,CAAC;QACF,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;aACnC,IAAI,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CACtD,CAAC;QACF,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACrC,CAAC;IACD,uEAAuE;IACvE,4DAA4D;IAC5D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC;AAEvC,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,YAAY,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { MeshEnvelope } from "./types.js";
2
+ export declare class MeshFetchUnavailableError extends Error {
3
+ readonly name = "MeshFetchUnavailableError";
4
+ }
5
+ export declare class MeshFetchInvalidResponseError extends Error {
6
+ readonly name = "MeshFetchInvalidResponseError";
7
+ }
8
+ export interface MeshClientOptions {
9
+ /** Finite deadline covering response headers and body parsing. */
10
+ readonly requestTimeoutMs?: number;
11
+ }
12
+ /** Finite-deadline HTTP client for Relay mesh membership envelopes. */
13
+ export declare class MeshClient {
14
+ private readonly baseUrl;
15
+ private readonly requestTimeoutMs;
16
+ constructor(relayUrl: string, options?: MeshClientOptions);
17
+ get(hash: string, since?: number): Promise<MeshEnvelope | null>;
18
+ }
@@ -0,0 +1,127 @@
1
+ export class MeshFetchUnavailableError extends Error {
2
+ name = "MeshFetchUnavailableError";
3
+ }
4
+ export class MeshFetchInvalidResponseError extends Error {
5
+ name = "MeshFetchInvalidResponseError";
6
+ }
7
+ const DEFAULT_REQUEST_TIMEOUT_MS = 5_000;
8
+ const ED25519_SIGNATURE_BYTES = 64;
9
+ function invalidResponse() {
10
+ return new MeshFetchInvalidResponseError("mesh response is invalid");
11
+ }
12
+ function unavailable() {
13
+ return new MeshFetchUnavailableError("mesh request is unavailable");
14
+ }
15
+ function decodeStrictBase64(raw) {
16
+ if (raw.length === 0)
17
+ throw invalidResponse();
18
+ const hasStandardOnlyCharacters = /[+/]/.test(raw);
19
+ const hasUrlSafeOnlyCharacters = /[-_]/.test(raw);
20
+ if (hasStandardOnlyCharacters && hasUrlSafeOnlyCharacters) {
21
+ throw invalidResponse();
22
+ }
23
+ const firstPaddingIndex = raw.indexOf("=");
24
+ const body = firstPaddingIndex === -1 ? raw : raw.slice(0, firstPaddingIndex);
25
+ const padding = firstPaddingIndex === -1 ? "" : raw.slice(firstPaddingIndex);
26
+ const bodyPattern = hasUrlSafeOnlyCharacters
27
+ ? /^[A-Za-z0-9_-]+$/
28
+ : /^[A-Za-z0-9+/]+$/;
29
+ if (!bodyPattern.test(body) ||
30
+ (padding !== "" && !/^={1,2}$/.test(padding))) {
31
+ throw invalidResponse();
32
+ }
33
+ const requiredPaddingLength = (4 - (body.length % 4)) % 4;
34
+ if (requiredPaddingLength === 3 ||
35
+ (padding.length > 0 && padding.length !== requiredPaddingLength)) {
36
+ throw invalidResponse();
37
+ }
38
+ const normalizedBody = body.replaceAll("-", "+").replaceAll("_", "/");
39
+ const bytes = new Uint8Array(Buffer.from(normalizedBody + "=".repeat(requiredPaddingLength), "base64"));
40
+ const canonicalPadded = Buffer.from(bytes).toString("base64");
41
+ const canonicalUnpadded = canonicalPadded.replace(/=+$/, "");
42
+ const normalizedInput = normalizedBody + padding;
43
+ if (normalizedInput !== canonicalPadded &&
44
+ normalizedInput !== canonicalUnpadded) {
45
+ throw invalidResponse();
46
+ }
47
+ return bytes;
48
+ }
49
+ /** Finite-deadline HTTP client for Relay mesh membership envelopes. */
50
+ export class MeshClient {
51
+ baseUrl;
52
+ requestTimeoutMs;
53
+ constructor(relayUrl, options = {}) {
54
+ const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
55
+ if (!Number.isFinite(requestTimeoutMs) || requestTimeoutMs <= 0) {
56
+ throw new RangeError("requestTimeoutMs must be finite and positive");
57
+ }
58
+ this.baseUrl = relayUrl.replace(/\/+$/, "");
59
+ this.requestTimeoutMs = requestTimeoutMs;
60
+ }
61
+ async get(hash, since) {
62
+ const query = since === undefined ? "" : `?since=${encodeURIComponent(since)}`;
63
+ const url = `${this.baseUrl}/mesh/${encodeURIComponent(hash)}${query}`;
64
+ const controller = new AbortController();
65
+ let rejectOnAbort = null;
66
+ const aborted = new Promise((_resolve, reject) => {
67
+ rejectOnAbort = () => reject(unavailable());
68
+ controller.signal.addEventListener("abort", rejectOnAbort, {
69
+ once: true,
70
+ });
71
+ });
72
+ const timeout = setTimeout(() => controller.abort(), this.requestTimeoutMs);
73
+ try {
74
+ let response;
75
+ try {
76
+ response = await Promise.race([
77
+ fetch(url, { method: "GET", signal: controller.signal }),
78
+ aborted,
79
+ ]);
80
+ }
81
+ catch (error) {
82
+ if (error instanceof MeshFetchUnavailableError)
83
+ throw error;
84
+ throw unavailable();
85
+ }
86
+ if (response.status === 304 || response.status === 404)
87
+ return null;
88
+ if (response.status === 408 ||
89
+ response.status === 425 ||
90
+ response.status === 429 ||
91
+ (response.status >= 500 && response.status <= 599)) {
92
+ throw unavailable();
93
+ }
94
+ if (response.status !== 200)
95
+ throw invalidResponse();
96
+ let payload;
97
+ try {
98
+ payload = await Promise.race([response.json(), aborted]);
99
+ }
100
+ catch (error) {
101
+ if (error instanceof SyntaxError)
102
+ throw invalidResponse();
103
+ throw unavailable();
104
+ }
105
+ if (!payload ||
106
+ typeof payload !== "object" ||
107
+ typeof payload.blob !== "string" ||
108
+ typeof payload.sig !== "string") {
109
+ throw invalidResponse();
110
+ }
111
+ const body = payload;
112
+ const blob = decodeStrictBase64(body.blob);
113
+ const sig = decodeStrictBase64(body.sig);
114
+ if (blob.length === 0 || sig.length !== ED25519_SIGNATURE_BYTES) {
115
+ throw invalidResponse();
116
+ }
117
+ return { blob, sig };
118
+ }
119
+ finally {
120
+ clearTimeout(timeout);
121
+ if (rejectOnAbort) {
122
+ controller.signal.removeEventListener("abort", rejectOnAbort);
123
+ }
124
+ }
125
+ }
126
+ }
127
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mesh/client.ts"],"names":[],"mappings":"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"}