@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.
- package/LICENSE +21 -0
- package/README.md +726 -0
- package/dist/actions/handlers.d.ts +77 -0
- package/dist/actions/handlers.js +26 -0
- package/dist/actions/handlers.js.map +1 -0
- package/dist/actions/registry.d.ts +39 -0
- package/dist/actions/registry.js +62 -0
- package/dist/actions/registry.js.map +1 -0
- package/dist/bin/supervisord.d.ts +2 -0
- package/dist/bin/supervisord.js +86 -0
- package/dist/bin/supervisord.js.map +1 -0
- package/dist/config.d.ts +84 -0
- package/dist/config.js +115 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/client.d.ts +20 -0
- package/dist/daemon/client.js +131 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/control_protocol.d.ts +176 -0
- package/dist/daemon/control_protocol.js +63 -0
- package/dist/daemon/control_protocol.js.map +1 -0
- package/dist/daemon/cron_log.d.ts +45 -0
- package/dist/daemon/cron_log.js +71 -0
- package/dist/daemon/cron_log.js.map +1 -0
- package/dist/daemon/cron_registry.d.ts +80 -0
- package/dist/daemon/cron_registry.js +198 -0
- package/dist/daemon/cron_registry.js.map +1 -0
- package/dist/daemon/id.d.ts +24 -0
- package/dist/daemon/id.js +36 -0
- package/dist/daemon/id.js.map +1 -0
- package/dist/daemon/install.d.ts +170 -0
- package/dist/daemon/install.js +603 -0
- package/dist/daemon/install.js.map +1 -0
- package/dist/daemon/registry.d.ts +63 -0
- package/dist/daemon/registry.js +153 -0
- package/dist/daemon/registry.js.map +1 -0
- package/dist/daemon/rpc_child.d.ts +194 -0
- package/dist/daemon/rpc_child.js +419 -0
- package/dist/daemon/rpc_child.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +93 -0
- package/dist/daemon/supervisor.js +666 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/extension_ui_bridge.d.ts +26 -0
- package/dist/extension_ui_bridge.js +341 -0
- package/dist/extension_ui_bridge.js.map +1 -0
- package/dist/index.d.ts +197 -0
- package/dist/index.js +5750 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/mesh_result.d.ts +10 -0
- package/dist/mcp/mesh_result.js +42 -0
- package/dist/mcp/mesh_result.js.map +1 -0
- package/dist/mcp/mesh_server.d.ts +16 -0
- package/dist/mcp/mesh_server.js +373 -0
- package/dist/mcp/mesh_server.js.map +1 -0
- package/dist/mesh/canonical.d.ts +30 -0
- package/dist/mesh/canonical.js +63 -0
- package/dist/mesh/canonical.js.map +1 -0
- package/dist/mesh/client.d.ts +18 -0
- package/dist/mesh/client.js +127 -0
- package/dist/mesh/client.js.map +1 -0
- package/dist/mesh/encoding.d.ts +38 -0
- package/dist/mesh/encoding.js +217 -0
- package/dist/mesh/encoding.js.map +1 -0
- package/dist/mesh/self_revoke.d.ts +73 -0
- package/dist/mesh/self_revoke.js +407 -0
- package/dist/mesh/self_revoke.js.map +1 -0
- package/dist/mesh/siblings.d.ts +49 -0
- package/dist/mesh/siblings.js +159 -0
- package/dist/mesh/siblings.js.map +1 -0
- package/dist/mesh/types.d.ts +34 -0
- package/dist/mesh/types.js +11 -0
- package/dist/mesh/types.js.map +1 -0
- package/dist/mesh/verify.d.ts +17 -0
- package/dist/mesh/verify.js +86 -0
- package/dist/mesh/verify.js.map +1 -0
- package/dist/pairing/crypto.d.ts +8 -0
- package/dist/pairing/crypto.js +22 -0
- package/dist/pairing/crypto.js.map +1 -0
- package/dist/pairing/qr.d.ts +59 -0
- package/dist/pairing/qr.js +127 -0
- package/dist/pairing/qr.js.map +1 -0
- package/dist/pairing/storage.d.ts +120 -0
- package/dist/pairing/storage.js +614 -0
- package/dist/pairing/storage.js.map +1 -0
- package/dist/panel_bridge.d.ts +15 -0
- package/dist/panel_bridge.js +409 -0
- package/dist/panel_bridge.js.map +1 -0
- package/dist/paths.d.ts +39 -0
- package/dist/paths.js +51 -0
- package/dist/paths.js.map +1 -0
- package/dist/protocol/codec.d.ts +7 -0
- package/dist/protocol/codec.js +48 -0
- package/dist/protocol/codec.js.map +1 -0
- package/dist/protocol/types.d.ts +404 -0
- package/dist/protocol/types.js +2 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/rooms.d.ts +42 -0
- package/dist/rooms.js +78 -0
- package/dist/rooms.js.map +1 -0
- package/dist/session/bridge.d.ts +37 -0
- package/dist/session/bridge.js +134 -0
- package/dist/session/bridge.js.map +1 -0
- package/dist/session/broker.d.ts +189 -0
- package/dist/session/broker.js +527 -0
- package/dist/session/broker.js.map +1 -0
- package/dist/session/broker_remote.d.ts +74 -0
- package/dist/session/broker_remote.js +581 -0
- package/dist/session/broker_remote.js.map +1 -0
- package/dist/session/cwd_lock.d.ts +33 -0
- package/dist/session/cwd_lock.js +127 -0
- package/dist/session/cwd_lock.js.map +1 -0
- package/dist/session/debug_log.d.ts +3 -0
- package/dist/session/debug_log.js +32 -0
- package/dist/session/debug_log.js.map +1 -0
- package/dist/session/envelope.d.ts +33 -0
- package/dist/session/envelope.js +117 -0
- package/dist/session/envelope.js.map +1 -0
- package/dist/session/global_config.d.ts +33 -0
- package/dist/session/global_config.js +73 -0
- package/dist/session/global_config.js.map +1 -0
- package/dist/session/ipc.d.ts +27 -0
- package/dist/session/ipc.js +22 -0
- package/dist/session/ipc.js.map +1 -0
- package/dist/session/leader_election.d.ts +32 -0
- package/dist/session/leader_election.js +108 -0
- package/dist/session/leader_election.js.map +1 -0
- package/dist/session/local_config.d.ts +67 -0
- package/dist/session/local_config.js +193 -0
- package/dist/session/local_config.js.map +1 -0
- package/dist/session/mesh_node.d.ts +116 -0
- package/dist/session/mesh_node.js +596 -0
- package/dist/session/mesh_node.js.map +1 -0
- package/dist/session/peer.d.ts +139 -0
- package/dist/session/peer.js +474 -0
- package/dist/session/peer.js.map +1 -0
- package/dist/session/peer_inventory.d.ts +13 -0
- package/dist/session/peer_inventory.js +48 -0
- package/dist/session/peer_inventory.js.map +1 -0
- package/dist/session/peer_limits.d.ts +9 -0
- package/dist/session/peer_limits.js +24 -0
- package/dist/session/peer_limits.js.map +1 -0
- package/dist/session/rpc_envelope.d.ts +108 -0
- package/dist/session/rpc_envelope.js +221 -0
- package/dist/session/rpc_envelope.js.map +1 -0
- package/dist/session/rpc_inbound.d.ts +55 -0
- package/dist/session/rpc_inbound.js +136 -0
- package/dist/session/rpc_inbound.js.map +1 -0
- package/dist/session/setup_wizard.d.ts +39 -0
- package/dist/session/setup_wizard.js +63 -0
- package/dist/session/setup_wizard.js.map +1 -0
- package/dist/session/tools.d.ts +24 -0
- package/dist/session/tools.js +272 -0
- package/dist/session/tools.js.map +1 -0
- package/dist/session/wizard.d.ts +13 -0
- package/dist/session/wizard.js +20 -0
- package/dist/session/wizard.js.map +1 -0
- package/dist/transport/peer_channel.d.ts +52 -0
- package/dist/transport/peer_channel.js +145 -0
- package/dist/transport/peer_channel.js.map +1 -0
- package/dist/transport/pi_forward_client.d.ts +29 -0
- package/dist/transport/pi_forward_client.js +68 -0
- package/dist/transport/pi_forward_client.js.map +1 -0
- package/dist/transport/relay_client.d.ts +96 -0
- package/dist/transport/relay_client.js +210 -0
- package/dist/transport/relay_client.js.map +1 -0
- package/dist/ui/footer.d.ts +32 -0
- package/dist/ui/footer.js +34 -0
- package/dist/ui/footer.js.map +1 -0
- package/docs/daemon.md +289 -0
- package/package.json +92 -0
- package/service-templates/launchd.plist.template +35 -0
- package/service-templates/systemd.service.template +19 -0
- package/service-templates/task-launcher.vbs.template +10 -0
- package/service-templates/task-scheduler.xml.template +38 -0
- 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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"}
|