remote-pi 0.1.3 → 0.2.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/README.md +160 -40
- package/dist/bin/supervisord.d.ts +2 -0
- package/dist/bin/supervisord.js +44 -0
- package/dist/bin/supervisord.js.map +1 -0
- package/dist/config.d.ts +44 -13
- package/dist/config.js +61 -22
- package/dist/config.js.map +1 -1
- package/dist/daemon/client.d.ts +20 -0
- package/dist/daemon/client.js +128 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/control_protocol.d.ts +100 -0
- package/dist/daemon/control_protocol.js +63 -0
- package/dist/daemon/control_protocol.js.map +1 -0
- package/dist/daemon/id.d.ts +18 -0
- package/dist/daemon/id.js +30 -0
- package/dist/daemon/id.js.map +1 -0
- package/dist/daemon/install.d.ts +132 -0
- package/dist/daemon/install.js +312 -0
- package/dist/daemon/install.js.map +1 -0
- package/dist/daemon/registry.d.ts +47 -0
- package/dist/daemon/registry.js +123 -0
- package/dist/daemon/registry.js.map +1 -0
- package/dist/daemon/rpc_child.d.ts +76 -0
- package/dist/daemon/rpc_child.js +130 -0
- package/dist/daemon/rpc_child.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +38 -0
- package/dist/daemon/supervisor.js +301 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/index.d.ts +62 -8
- package/dist/index.js +1226 -301
- package/dist/index.js.map +1 -1
- package/dist/mesh/canonical.d.ts +30 -0
- package/dist/mesh/canonical.js +61 -0
- package/dist/mesh/canonical.js.map +1 -0
- package/dist/mesh/client.d.ts +31 -0
- package/dist/mesh/client.js +56 -0
- package/dist/mesh/client.js.map +1 -0
- package/dist/mesh/encoding.d.ts +36 -0
- package/dist/mesh/encoding.js +53 -0
- package/dist/mesh/encoding.js.map +1 -0
- package/dist/mesh/self_revoke.d.ts +111 -0
- package/dist/mesh/self_revoke.js +182 -0
- package/dist/mesh/self_revoke.js.map +1 -0
- package/dist/mesh/siblings.d.ts +62 -0
- package/dist/mesh/siblings.js +95 -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 +77 -0
- package/dist/mesh/verify.js.map +1 -0
- package/dist/pairing/qr.d.ts +16 -5
- package/dist/pairing/qr.js +27 -8
- package/dist/pairing/qr.js.map +1 -1
- package/dist/pairing/storage.d.ts +41 -0
- package/dist/pairing/storage.js +158 -21
- package/dist/pairing/storage.js.map +1 -1
- package/dist/protocol/types.d.ts +23 -0
- package/dist/session/broker.d.ts +74 -0
- package/dist/session/broker.js +142 -4
- package/dist/session/broker.js.map +1 -1
- package/dist/session/broker_remote.d.ts +110 -0
- package/dist/session/broker_remote.js +397 -0
- package/dist/session/broker_remote.js.map +1 -0
- package/dist/session/cwd_lock.d.ts +28 -0
- package/dist/session/cwd_lock.js +89 -0
- package/dist/session/cwd_lock.js.map +1 -0
- package/dist/session/global_config.d.ts +9 -0
- package/dist/session/global_config.js +9 -0
- package/dist/session/global_config.js.map +1 -1
- package/dist/session/leader_election.d.ts +16 -0
- package/dist/session/leader_election.js +22 -0
- package/dist/session/leader_election.js.map +1 -1
- package/dist/session/local_config.d.ts +12 -5
- package/dist/session/local_config.js +24 -3
- package/dist/session/local_config.js.map +1 -1
- package/dist/session/peer.d.ts +28 -1
- package/dist/session/peer.js +69 -2
- package/dist/session/peer.js.map +1 -1
- 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/setup_wizard.d.ts +32 -8
- package/dist/session/setup_wizard.js +45 -33
- package/dist/session/setup_wizard.js.map +1 -1
- package/dist/session/tools.d.ts +15 -7
- package/dist/session/tools.js +145 -31
- package/dist/session/tools.js.map +1 -1
- package/dist/transport/pi_forward_client.d.ts +29 -0
- package/dist/transport/pi_forward_client.js +62 -0
- package/dist/transport/pi_forward_client.js.map +1 -0
- package/dist/ui/footer.js +8 -6
- package/dist/ui/footer.js.map +1 -1
- package/docs/daemon.md +289 -0
- package/package.json +8 -2
- package/service-templates/launchd.plist.template +35 -0
- package/service-templates/systemd.service.template +19 -0
- package/skills/agent-network/SKILL.md +273 -294
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { envelope, uuidv7 } from "./envelope.js";
|
|
2
|
+
/**
|
|
3
|
+
* Plan/25 Wave B/C — cross-PC broker.
|
|
4
|
+
*
|
|
5
|
+
* Maintains a cache of `<pc_label> → { peers, pc_pubkey, ts }` populated
|
|
6
|
+
* by `peers_update` envelopes pushed from sibling Pis and refreshed lazily
|
|
7
|
+
* via `peers_request` on cache miss.
|
|
8
|
+
*
|
|
9
|
+
* Owns two halves of the protocol:
|
|
10
|
+
*
|
|
11
|
+
* - **Outbound** (`tryRouteOutbound`): broker hands off envelopes with a
|
|
12
|
+
* known `<pc>:` prefix. We rewrite `env.from` with our own pc_label,
|
|
13
|
+
* pack onto the relay via `pi_forward_client.sendEnvelopeToPi`.
|
|
14
|
+
*
|
|
15
|
+
* - **Inbound** (`handleIncoming`): `pi_forward_client` emits envelopes
|
|
16
|
+
* received from a verified `from_pc`. We:
|
|
17
|
+
* 1. Anti-spoof the `envelope.from` prefix against the sibling cache
|
|
18
|
+
* keyed by `from_pc` (defends against a Pi lying about its own
|
|
19
|
+
* `pc_label`).
|
|
20
|
+
* 2. Intercept control envelopes (`peers_update`, `peers_request`,
|
|
21
|
+
* `transport_error`) before any local UDS delivery.
|
|
22
|
+
* 3. Strip the `<pc>:` prefix from `env.to` and call
|
|
23
|
+
* `broker.injectFromRemote`. Build a one-way ACK envelope back via
|
|
24
|
+
* the relay so the cross-PC sender's `sendWithAck` resolves.
|
|
25
|
+
*
|
|
26
|
+
* The replies-bypass-busy rule from Wave 0 is honoured at injection time
|
|
27
|
+
* by the broker itself; `broker_remote` does not duplicate the check.
|
|
28
|
+
*
|
|
29
|
+
* Siblings (`Map<pc_label, pc_pubkey>`) are seeded externally by the
|
|
30
|
+
* extension at bootstrap (typically from `mesh_versions` of every paired
|
|
31
|
+
* Owner). Membership is the only thing we trust to ground anti-spoof —
|
|
32
|
+
* the cache of peers is just for routing UX.
|
|
33
|
+
*/
|
|
34
|
+
const CACHE_TTL_MS = 5 * 60_000;
|
|
35
|
+
const PEERS_REQUEST_TIMEOUT_MS = 2_000;
|
|
36
|
+
const BROKER_NAME = "broker";
|
|
37
|
+
export class BrokerRemote {
|
|
38
|
+
broker;
|
|
39
|
+
pi;
|
|
40
|
+
selfPcLabel;
|
|
41
|
+
selfPcPubkey;
|
|
42
|
+
cacheTtlMs;
|
|
43
|
+
log;
|
|
44
|
+
/** Siblings: pc_label → pc_pubkey. Authoritative for anti-spoof. */
|
|
45
|
+
siblingByLabel = new Map();
|
|
46
|
+
/** Reverse index built from siblings: pc_pubkey → pc_label. */
|
|
47
|
+
siblingByPubkey = new Map();
|
|
48
|
+
/** Cache of peers per remote pc_label. */
|
|
49
|
+
remotePeers = new Map();
|
|
50
|
+
/** In-flight `peers_request` calls, keyed by pc_label. */
|
|
51
|
+
pendingFills = new Map();
|
|
52
|
+
/** Local peers (UDS) at the moment of last `onLocalPeersChanged` call. */
|
|
53
|
+
lastLocalPeers = [];
|
|
54
|
+
onIncoming;
|
|
55
|
+
detached = false;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
this.broker = opts.broker;
|
|
58
|
+
this.pi = opts.pi;
|
|
59
|
+
this.selfPcLabel = opts.selfPcLabel;
|
|
60
|
+
this.selfPcPubkey = opts.selfPcPubkey;
|
|
61
|
+
this.cacheTtlMs = opts.cacheTtlMs ?? CACHE_TTL_MS;
|
|
62
|
+
this.log = opts.log ?? ((msg) => console.error(msg));
|
|
63
|
+
for (const s of opts.siblings ?? [])
|
|
64
|
+
this._addSibling(s);
|
|
65
|
+
this.onIncoming = (env, fromPc) => this.handleIncoming(env, fromPc);
|
|
66
|
+
this.pi.on("envelope", this.onIncoming);
|
|
67
|
+
this.broker.setRemoteRouter(this);
|
|
68
|
+
// Seed `lastLocalPeers` from the live broker so the first
|
|
69
|
+
// `onLocalPeersChanged`-triggered push (or the bootstrap pushes below)
|
|
70
|
+
// already carry real state. Without seeding, the same single-peer
|
|
71
|
+
// mesh bug applies to OUTGOING `peers_update`s too: we'd announce
|
|
72
|
+
// ourselves to siblings as having zero peers.
|
|
73
|
+
this.lastLocalPeers = this.broker.peerNames();
|
|
74
|
+
// Plan/25 Wave B bootstrap: kick a `peers_request` at every known
|
|
75
|
+
// sibling so the cache is warm before anyone calls `list_peers` or
|
|
76
|
+
// `agent_send` cross-PC. Also push our current peer list proactively
|
|
77
|
+
// so siblings don't have to wait for their own request roundtrip.
|
|
78
|
+
// Best-effort; siblings offline at boot will reply when they come
|
|
79
|
+
// online and push their own `peers_update`.
|
|
80
|
+
this._bootstrapWithSiblings();
|
|
81
|
+
}
|
|
82
|
+
/** Bootstrap: announce ourselves AND ask every sibling for their peers.
|
|
83
|
+
* Single helper so `_addSibling` can reuse half of it when a new
|
|
84
|
+
* sibling appears via `setSiblings`. */
|
|
85
|
+
_bootstrapWithSiblings() {
|
|
86
|
+
for (const [, pcPubkey] of this.siblingByLabel) {
|
|
87
|
+
this._sendControlEnvelope(pcPubkey, { type: "peers_request" });
|
|
88
|
+
this._sendControlEnvelope(pcPubkey, {
|
|
89
|
+
type: "peers_update",
|
|
90
|
+
peers: this.lastLocalPeers,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
detach() {
|
|
95
|
+
if (this.detached)
|
|
96
|
+
return;
|
|
97
|
+
this.detached = true;
|
|
98
|
+
this.pi.off("envelope", this.onIncoming);
|
|
99
|
+
this.broker.setRemoteRouter(null);
|
|
100
|
+
}
|
|
101
|
+
// ── Sibling management ────────────────────────────────────────────────────
|
|
102
|
+
/** Replace or extend the sibling set. Idempotent on identical input.
|
|
103
|
+
* Removes any sibling missing from `next`. Plan/25 Wave B bootstrap:
|
|
104
|
+
* fires `peers_request` at any sibling that wasn't in the previous
|
|
105
|
+
* set so the cache warms up without waiting for their next push. */
|
|
106
|
+
setSiblings(next) {
|
|
107
|
+
const prevPubkeys = new Set(this.siblingByPubkey.keys());
|
|
108
|
+
this.siblingByLabel.clear();
|
|
109
|
+
this.siblingByPubkey.clear();
|
|
110
|
+
for (const s of next)
|
|
111
|
+
this._addSibling(s);
|
|
112
|
+
// Drop cache entries for siblings that disappeared.
|
|
113
|
+
for (const label of [...this.remotePeers.keys()]) {
|
|
114
|
+
if (!this.siblingByLabel.has(label))
|
|
115
|
+
this.remotePeers.delete(label);
|
|
116
|
+
}
|
|
117
|
+
// For newly-added pubkeys: do the same announce+request pair the
|
|
118
|
+
// constructor does. Re-pinging siblings we already knew about would
|
|
119
|
+
// be wasteful (and triggers redundant audit lines on their side).
|
|
120
|
+
for (const [, pcPubkey] of this.siblingByLabel) {
|
|
121
|
+
if (prevPubkeys.has(pcPubkey))
|
|
122
|
+
continue;
|
|
123
|
+
this._sendControlEnvelope(pcPubkey, { type: "peers_request" });
|
|
124
|
+
this._sendControlEnvelope(pcPubkey, {
|
|
125
|
+
type: "peers_update",
|
|
126
|
+
peers: this.lastLocalPeers,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
_addSibling(s) {
|
|
131
|
+
if (!s.pcLabel || !s.pcPubkey)
|
|
132
|
+
return;
|
|
133
|
+
if (s.pcLabel === this.selfPcLabel)
|
|
134
|
+
return; // never list self as sibling
|
|
135
|
+
if (s.pcPubkey === this.selfPcPubkey)
|
|
136
|
+
return;
|
|
137
|
+
this.siblingByLabel.set(s.pcLabel, s.pcPubkey);
|
|
138
|
+
this.siblingByPubkey.set(s.pcPubkey, s.pcLabel);
|
|
139
|
+
}
|
|
140
|
+
// ── Public cache API ──────────────────────────────────────────────────────
|
|
141
|
+
/** Returns the cached peer list for a remote pc_label, or [] when
|
|
142
|
+
* unknown / expired. */
|
|
143
|
+
getRemotePeers(pcLabel) {
|
|
144
|
+
const entry = this.remotePeers.get(pcLabel);
|
|
145
|
+
if (!entry)
|
|
146
|
+
return [];
|
|
147
|
+
if (Date.now() - entry.ts > this.cacheTtlMs)
|
|
148
|
+
return [];
|
|
149
|
+
return [...entry.peers];
|
|
150
|
+
}
|
|
151
|
+
/** Returns the full cross-PC inventory: pc_label → peers (TTL-respected). */
|
|
152
|
+
getAllRemote() {
|
|
153
|
+
const out = {};
|
|
154
|
+
for (const [label] of this.remotePeers) {
|
|
155
|
+
const peers = this.getRemotePeers(label);
|
|
156
|
+
if (peers.length > 0)
|
|
157
|
+
out[label] = peers;
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
/** Returns aggregated remote peer names (`<pc>:<peer>`) for the broker's
|
|
162
|
+
* `list_peers` reply. Skips siblings with no cache entry. */
|
|
163
|
+
listRemotePeers() {
|
|
164
|
+
const out = [];
|
|
165
|
+
for (const [label] of this.remotePeers) {
|
|
166
|
+
for (const peer of this.getRemotePeers(label)) {
|
|
167
|
+
out.push(`${label}:${peer}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
// ── Push proativo ─────────────────────────────────────────────────────────
|
|
173
|
+
/**
|
|
174
|
+
* Called whenever the local UDS broker's peer set changes
|
|
175
|
+
* (peer_joined/peer_left). We push a `peers_update` envelope to every
|
|
176
|
+
* sibling so their caches stay fresh without polling.
|
|
177
|
+
*/
|
|
178
|
+
onLocalPeersChanged(peers) {
|
|
179
|
+
this.lastLocalPeers = [...peers];
|
|
180
|
+
if (this.siblingByLabel.size === 0)
|
|
181
|
+
return;
|
|
182
|
+
const body = { type: "peers_update", peers: this.lastLocalPeers };
|
|
183
|
+
for (const [, pcPubkey] of this.siblingByLabel) {
|
|
184
|
+
this._sendControlEnvelope(pcPubkey, body);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// ── RemoteRouter ──────────────────────────────────────────────────────────
|
|
188
|
+
/**
|
|
189
|
+
* Broker hook (plan/25 Wave C). Inspect `env.to` for a `<pc>:` prefix:
|
|
190
|
+
*
|
|
191
|
+
* - no prefix or prefix == selfPcLabel → return false (broker delivers
|
|
192
|
+
* locally; if same-self prefix is present we DON'T strip it here —
|
|
193
|
+
* the local resolver will treat it as a literal name, which works
|
|
194
|
+
* because local names don't carry colons in practice)
|
|
195
|
+
* - prefix === known sibling label → rewrite `env.from`, pack onto the
|
|
196
|
+
* relay, return true. May trigger a lazy `peers_request` when the
|
|
197
|
+
* cache is empty (returns false on hard cache miss so the broker
|
|
198
|
+
* surfaces a transport_error path; we always optimistically send,
|
|
199
|
+
* and ACK timeout in the sender ends up reporting the failure).
|
|
200
|
+
* - prefix is not a known sibling label → return false (backward-compat
|
|
201
|
+
* for hypothetical local names containing `:`)
|
|
202
|
+
*/
|
|
203
|
+
tryRouteOutbound(env) {
|
|
204
|
+
if (this.detached)
|
|
205
|
+
return false;
|
|
206
|
+
if (typeof env.to !== "string")
|
|
207
|
+
return false;
|
|
208
|
+
const parsed = parseAddress(env.to);
|
|
209
|
+
if (!parsed)
|
|
210
|
+
return false;
|
|
211
|
+
const { pcLabel } = parsed;
|
|
212
|
+
if (pcLabel === this.selfPcLabel)
|
|
213
|
+
return false; // same-PC: local handles
|
|
214
|
+
const siblingPk = this.siblingByLabel.get(pcLabel);
|
|
215
|
+
if (!siblingPk)
|
|
216
|
+
return false; // unknown prefix → fall through
|
|
217
|
+
// We have a destination PC. Rewrite `from` with our own pc_label.
|
|
218
|
+
const rewritten = {
|
|
219
|
+
...env,
|
|
220
|
+
from: `${this.selfPcLabel}:${env.from}`,
|
|
221
|
+
};
|
|
222
|
+
// Optimistic send. If the recipient's cache doesn't list our target
|
|
223
|
+
// yet, the recipient's wrapper still injects (the broker just decides
|
|
224
|
+
// received/busy/denied on actual local UDS state). A simultaneous
|
|
225
|
+
// `peers_request` warms the cache for next time.
|
|
226
|
+
this.pi.sendEnvelopeToPi(siblingPk, rewritten);
|
|
227
|
+
if (this.remotePeers.get(pcLabel) === undefined) {
|
|
228
|
+
this._sendControlEnvelope(siblingPk, { type: "peers_request" });
|
|
229
|
+
void this._awaitPeersFill(pcLabel, PEERS_REQUEST_TIMEOUT_MS);
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
// ── Inbound ───────────────────────────────────────────────────────────────
|
|
234
|
+
/**
|
|
235
|
+
* Entry point for envelopes the relay forwards to us. Receives the
|
|
236
|
+
* envelope verbatim plus the verified `from_pc` (Pi-pubkey of the
|
|
237
|
+
* sender, authoritative — relay-checked).
|
|
238
|
+
*/
|
|
239
|
+
handleIncoming(env, fromPc) {
|
|
240
|
+
// ── transport_error from relay ─────────────────────────────────────────
|
|
241
|
+
// The relay synthesises these with `from_pc = "_relay"` and
|
|
242
|
+
// `envelope.from = "_relay"`. Inject locally as a system envelope
|
|
243
|
+
// addressed to the original sender (env.to is the original sender's
|
|
244
|
+
// prefixed address; strip the prefix and deliver via UDS).
|
|
245
|
+
if (fromPc === "_relay") {
|
|
246
|
+
this._propagateTransportError(env);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// ── anti-spoof ─────────────────────────────────────────────────────────
|
|
250
|
+
const claimedLabel = this.siblingByPubkey.get(fromPc);
|
|
251
|
+
if (!claimedLabel) {
|
|
252
|
+
this.log(`[broker_remote] drop: from_pc ${fromPc.slice(0, 12)}… not in sibling cache`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (typeof env.from === "string") {
|
|
256
|
+
const fromPrefix = env.from.split(":", 1)[0];
|
|
257
|
+
if (fromPrefix !== claimedLabel) {
|
|
258
|
+
this.log(`[broker_remote] drop: envelope.from "${env.from}" prefix ` +
|
|
259
|
+
`mismatches sibling label "${claimedLabel}"`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const body = env.body;
|
|
264
|
+
const bodyType = body && typeof body === "object" ? body.type : undefined;
|
|
265
|
+
// ── control: peers_update ──────────────────────────────────────────────
|
|
266
|
+
if (bodyType === "peers_update") {
|
|
267
|
+
const peers = Array.isArray(body.peers)
|
|
268
|
+
? body.peers.filter((p) => typeof p === "string")
|
|
269
|
+
: [];
|
|
270
|
+
this._setRemoteCache(claimedLabel, fromPc, peers);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// ── control: peers_request ─────────────────────────────────────────────
|
|
274
|
+
if (bodyType === "peers_request") {
|
|
275
|
+
// Always query the broker directly for the current peer list. We
|
|
276
|
+
// can't rely on `lastLocalPeers` because that cache is fed by the
|
|
277
|
+
// `peer_joined`/`peer_left` broadcast in index.ts — and the broker
|
|
278
|
+
// never delivers a `peer_joined` to the peer that just joined (see
|
|
279
|
+
// `_broadcastSystem(..., excludeName=assigned)`). In a single-peer
|
|
280
|
+
// mesh, no event ever fires → `lastLocalPeers` stays `[]` →
|
|
281
|
+
// siblings see us as "no peers" → cache populates empty → cross-PC
|
|
282
|
+
// `list_peers` misses us. Querying broker.peerNames() resolves
|
|
283
|
+
// sync (Map keys), so this is essentially free.
|
|
284
|
+
const currentLocal = this.broker.peerNames();
|
|
285
|
+
this._sendControlEnvelope(fromPc, {
|
|
286
|
+
type: "peers_update",
|
|
287
|
+
peers: currentLocal,
|
|
288
|
+
});
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
// ── control: ack ───────────────────────────────────────────────────────
|
|
292
|
+
// ACK envelopes from a remote wrapper are addressed to our local
|
|
293
|
+
// sender. Strip prefix from `to` and inject so the sender's
|
|
294
|
+
// `sendWithAck` pending resolves. ACK envelopes carry `re` (always)
|
|
295
|
+
// and bypass busy via the `isReply` rule in `injectFromRemote`.
|
|
296
|
+
// (No special-casing needed — generic injection below covers them.)
|
|
297
|
+
// ── regular envelope: strip `to` prefix and inject ─────────────────────
|
|
298
|
+
if (typeof env.to !== "string") {
|
|
299
|
+
this.log("[broker_remote] drop: cross-PC envelope must be unicast string");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const toParsed = parseAddress(env.to);
|
|
303
|
+
let injectedEnv = env;
|
|
304
|
+
if (toParsed && toParsed.pcLabel === this.selfPcLabel) {
|
|
305
|
+
injectedEnv = { ...env, to: toParsed.peerName };
|
|
306
|
+
}
|
|
307
|
+
else if (toParsed) {
|
|
308
|
+
// `to` carries a third-party prefix — not for us. Drop.
|
|
309
|
+
this.log(`[broker_remote] drop: envelope.to "${env.to}" not addressed to ` +
|
|
310
|
+
`selfPcLabel "${this.selfPcLabel}"`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const status = this.broker.injectFromRemote(injectedEnv);
|
|
314
|
+
// Only generate an ACK for non-ACK envelopes — otherwise we'd loop
|
|
315
|
+
// ACKing the ACK. Detect by body shape.
|
|
316
|
+
if (bodyType === "ack")
|
|
317
|
+
return;
|
|
318
|
+
// Forward an ACK envelope back to fromPc. The cross-PC sender's
|
|
319
|
+
// `sendWithAck` correlates by `re = env.id`.
|
|
320
|
+
const ackBody = { type: "ack", status, target: injectedEnv.to };
|
|
321
|
+
const ackEnv = {
|
|
322
|
+
from: `${this.selfPcLabel}:${BROKER_NAME}`,
|
|
323
|
+
to: env.from,
|
|
324
|
+
id: uuidv7(),
|
|
325
|
+
re: env.id,
|
|
326
|
+
body: ackBody,
|
|
327
|
+
};
|
|
328
|
+
this.pi.sendEnvelopeToPi(fromPc, ackEnv);
|
|
329
|
+
}
|
|
330
|
+
// ── Internals ─────────────────────────────────────────────────────────────
|
|
331
|
+
_setRemoteCache(pcLabel, pcPubkey, peers) {
|
|
332
|
+
this.remotePeers.set(pcLabel, { peers, pcPubkey, ts: Date.now() });
|
|
333
|
+
// Resolve any pending `peers_request` waiters for this label.
|
|
334
|
+
const pending = this.pendingFills.get(pcLabel);
|
|
335
|
+
if (pending) {
|
|
336
|
+
for (const slot of pending) {
|
|
337
|
+
clearTimeout(slot.timer);
|
|
338
|
+
slot.resolve();
|
|
339
|
+
}
|
|
340
|
+
this.pendingFills.delete(pcLabel);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
_awaitPeersFill(pcLabel, timeoutMs) {
|
|
344
|
+
return new Promise((resolve) => {
|
|
345
|
+
const slot = {
|
|
346
|
+
resolve,
|
|
347
|
+
timer: setTimeout(() => {
|
|
348
|
+
const set = this.pendingFills.get(pcLabel);
|
|
349
|
+
set?.delete(slot);
|
|
350
|
+
resolve();
|
|
351
|
+
}, timeoutMs),
|
|
352
|
+
};
|
|
353
|
+
const set = this.pendingFills.get(pcLabel) ?? new Set();
|
|
354
|
+
set.add(slot);
|
|
355
|
+
this.pendingFills.set(pcLabel, set);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
_propagateTransportError(env) {
|
|
359
|
+
// Strip prefix from to (if any) and deliver to the local sender by
|
|
360
|
+
// injecting the envelope into the broker. Per plan/25 spec the
|
|
361
|
+
// wrapper's `sendWithAck` will see this as a body with
|
|
362
|
+
// `type:"transport_error"` correlated by `re`. The ackPending matcher
|
|
363
|
+
// only resolves for body.type === "ack", so transport_error envelopes
|
|
364
|
+
// fall through to handlers — which is what we want (sender's pending
|
|
365
|
+
// map times out, then handler dispatches inbox notification).
|
|
366
|
+
if (typeof env.to !== "string")
|
|
367
|
+
return;
|
|
368
|
+
const parsed = parseAddress(env.to);
|
|
369
|
+
const injected = parsed && parsed.pcLabel === this.selfPcLabel
|
|
370
|
+
? { ...env, to: parsed.peerName }
|
|
371
|
+
: env;
|
|
372
|
+
this.broker.injectFromRemote(injected);
|
|
373
|
+
}
|
|
374
|
+
_sendControlEnvelope(toPc, body) {
|
|
375
|
+
const env = envelope(`${this.selfPcLabel}:_broker_remote`, `${this._labelForPubkey(toPc) ?? "?"}:_broker_remote`, body, null);
|
|
376
|
+
this.pi.sendEnvelopeToPi(toPc, env);
|
|
377
|
+
}
|
|
378
|
+
_labelForPubkey(pcPubkey) {
|
|
379
|
+
return this.siblingByPubkey.get(pcPubkey);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
383
|
+
/**
|
|
384
|
+
* Parse a `<pc>:<peer>` address. Returns null when the input doesn't
|
|
385
|
+
* carry a `:`. Note: callers are responsible for deciding whether the
|
|
386
|
+
* parsed `pcLabel` is meaningful (i.e., matches selfPcLabel or a known
|
|
387
|
+
* sibling); a non-null return here does NOT imply the address is remote.
|
|
388
|
+
* The broker's prefix routing uses this — local names containing literal
|
|
389
|
+
* `:` continue working as long as no sibling carries the same prefix.
|
|
390
|
+
*/
|
|
391
|
+
export function parseAddress(to) {
|
|
392
|
+
const idx = to.indexOf(":");
|
|
393
|
+
if (idx <= 0 || idx === to.length - 1)
|
|
394
|
+
return null;
|
|
395
|
+
return { pcLabel: to.slice(0, idx), peerName: to.slice(idx + 1) };
|
|
396
|
+
}
|
|
397
|
+
//# sourceMappingURL=broker_remote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broker_remote.js","sourceRoot":"","sources":["../../src/session/broker_remote.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface AcquiredLock {
|
|
2
|
+
ok: true;
|
|
3
|
+
/** Manual release. Optional — process exit cleans up too. */
|
|
4
|
+
release(): void;
|
|
5
|
+
}
|
|
6
|
+
export interface RefusedLock {
|
|
7
|
+
ok: false;
|
|
8
|
+
/** Where the live lock socket lives, in case the caller wants to log it. */
|
|
9
|
+
lockPath: string;
|
|
10
|
+
}
|
|
11
|
+
export type CwdLockResult = AcquiredLock | RefusedLock;
|
|
12
|
+
/** Path of the lock socket for a given cwd. Pure helper (no IO). */
|
|
13
|
+
export declare function lockPathForCwd(cwd: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Attempts to acquire the cwd lock. Resolves with either:
|
|
16
|
+
* - `{ ok: true, release }` when we own it (server bound + retained).
|
|
17
|
+
* - `{ ok: false, lockPath }` when a live Pi already holds the lock.
|
|
18
|
+
*
|
|
19
|
+
* Self-healing path: if the prior holder crashed, the leftover socket file
|
|
20
|
+
* fails `tryConnect` (ECONNREFUSED). We unlink it and retry the bind — the
|
|
21
|
+
* second attempt then succeeds.
|
|
22
|
+
*
|
|
23
|
+
* The server holds no actual listener logic; its only job is to keep the
|
|
24
|
+
* UDS endpoint pinned by the OS. No incoming connection ever does anything
|
|
25
|
+
* useful — the next-Pi-attempt's `tryConnect` succeeding is the entire
|
|
26
|
+
* signal we care about.
|
|
27
|
+
*/
|
|
28
|
+
export declare function acquireCwdLock(cwd: string): Promise<CwdLockResult>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { roomIdForCwd } from "../rooms.js";
|
|
5
|
+
import { removeStaleSock, tryBind, tryConnect } from "./leader_election.js";
|
|
6
|
+
/**
|
|
7
|
+
* Per-cwd singleton lock for `/remote-pi`. At most one Pi process per
|
|
8
|
+
* working directory may hold the lock; the second attempt is refused.
|
|
9
|
+
*
|
|
10
|
+
* Why a UDS bind instead of a PID lock file:
|
|
11
|
+
* - The OS releases the socket handle the instant the process dies, even
|
|
12
|
+
* on `kill -9` or hard crash. No stale-lock cleanup needed.
|
|
13
|
+
* - The next Pi to try in the same cwd detects the dead socket via
|
|
14
|
+
* `ECONNREFUSED` on `tryConnect`, unlinks the leftover file, and
|
|
15
|
+
* binds successfully. Fully self-healing.
|
|
16
|
+
* - Kernel-enforced — there is no race window between "check if held"
|
|
17
|
+
* and "claim", which an explicit PID file would have.
|
|
18
|
+
*
|
|
19
|
+
* Lock files live in `~/.pi/remote/locks/<roomId>.sock` (where `roomId` is
|
|
20
|
+
* `sha256(realpath(cwd))[:12]`), NOT inside the cwd itself, to dodge:
|
|
21
|
+
* - The 104/108-char path-length limit on UDS sockets on macOS/Linux.
|
|
22
|
+
* - Symlinked cwds (realpath canonicalization happens in `roomIdForCwd`).
|
|
23
|
+
* - Read-only cwds (the home directory is always writable).
|
|
24
|
+
*
|
|
25
|
+
* Caller workflow:
|
|
26
|
+
* const lock = await acquireCwdLock(cwd);
|
|
27
|
+
* if (!lock.ok) { ui.notify("Já tem um agente rodando nessa pasta."); return; }
|
|
28
|
+
* // …run /remote-pi normally; lock auto-releases on process exit
|
|
29
|
+
*/
|
|
30
|
+
const LOCKS_DIR = join(homedir(), ".pi", "remote", "locks");
|
|
31
|
+
/** Path of the lock socket for a given cwd. Pure helper (no IO). */
|
|
32
|
+
export function lockPathForCwd(cwd) {
|
|
33
|
+
return join(LOCKS_DIR, `${roomIdForCwd(cwd)}.sock`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Attempts to acquire the cwd lock. Resolves with either:
|
|
37
|
+
* - `{ ok: true, release }` when we own it (server bound + retained).
|
|
38
|
+
* - `{ ok: false, lockPath }` when a live Pi already holds the lock.
|
|
39
|
+
*
|
|
40
|
+
* Self-healing path: if the prior holder crashed, the leftover socket file
|
|
41
|
+
* fails `tryConnect` (ECONNREFUSED). We unlink it and retry the bind — the
|
|
42
|
+
* second attempt then succeeds.
|
|
43
|
+
*
|
|
44
|
+
* The server holds no actual listener logic; its only job is to keep the
|
|
45
|
+
* UDS endpoint pinned by the OS. No incoming connection ever does anything
|
|
46
|
+
* useful — the next-Pi-attempt's `tryConnect` succeeding is the entire
|
|
47
|
+
* signal we care about.
|
|
48
|
+
*/
|
|
49
|
+
export async function acquireCwdLock(cwd) {
|
|
50
|
+
const lockPath = lockPathForCwd(cwd);
|
|
51
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
52
|
+
// First attempt: bind directly.
|
|
53
|
+
let server = await tryBind(lockPath);
|
|
54
|
+
if (server)
|
|
55
|
+
return _acquired(server, lockPath);
|
|
56
|
+
// Bind failed — check whether the existing socket has a live listener.
|
|
57
|
+
const probe = await tryConnect(lockPath);
|
|
58
|
+
if (probe) {
|
|
59
|
+
probe.destroy();
|
|
60
|
+
return { ok: false, lockPath };
|
|
61
|
+
}
|
|
62
|
+
// Socket is stale (no listener answered). Clean up + retry bind once.
|
|
63
|
+
removeStaleSock(lockPath);
|
|
64
|
+
server = await tryBind(lockPath);
|
|
65
|
+
if (server)
|
|
66
|
+
return _acquired(server, lockPath);
|
|
67
|
+
// Race: someone else bound between our unlink and retry. Treat as held.
|
|
68
|
+
return { ok: false, lockPath };
|
|
69
|
+
}
|
|
70
|
+
function _acquired(server, _lockPath) {
|
|
71
|
+
let released = false;
|
|
72
|
+
// Don't keep the event loop alive just to hold the socket — the Pi
|
|
73
|
+
// process has its own reasons to stay up (relay WS, broker, etc.).
|
|
74
|
+
// When those are gone, the OS will tear the socket down with us.
|
|
75
|
+
server.unref();
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
release: () => {
|
|
79
|
+
if (released)
|
|
80
|
+
return;
|
|
81
|
+
released = true;
|
|
82
|
+
try {
|
|
83
|
+
server.close();
|
|
84
|
+
}
|
|
85
|
+
catch { /* ignored */ }
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=cwd_lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cwd_lock.js","sourceRoot":"","sources":["../../src/session/cwd_lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAgB5D,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,IAAI,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElD,gCAAgC;IAChC,IAAI,MAAM,GAAkB,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,MAAM;QAAE,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE/C,uEAAuE;IACvE,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACjC,CAAC;IAED,sEAAsE;IACtE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC1B,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,MAAM;QAAE,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE/C,wEAAwE;IACxE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,SAAiB;IAClD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,mEAAmE;IACnE,mEAAmE;IACnE,iEAAiE;IACjE,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,OAAO;QACL,EAAE,EAAE,IAAI;QACR,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC;gBAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;QACjD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed UDS session name. The local mesh is single per machine — every Pi
|
|
3
|
+
* process on the host shares this broker. Previous versions exposed
|
|
4
|
+
* multi-session support (named sessions, leave/rename/sessions commands);
|
|
5
|
+
* those were removed in the 2026-05-23 simplification because in practice
|
|
6
|
+
* every install converged on one session anyway and multi-session UX added
|
|
7
|
+
* friction without value.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LOCAL_SESSION_NAME = "local";
|
|
1
10
|
/** Ensures the new subdirs exist inside the existing ~/.pi/remote/. */
|
|
2
11
|
export declare function ensureGlobalDirs(): void;
|
|
3
12
|
/** Path to the UDS socket for a named session. */
|
|
@@ -4,6 +4,15 @@ import { join } from "node:path";
|
|
|
4
4
|
const HOME_PI_REMOTE = join(homedir(), ".pi", "remote");
|
|
5
5
|
const SESSIONS_DIR = join(HOME_PI_REMOTE, "sessions");
|
|
6
6
|
const SKILLS_DIR = join(HOME_PI_REMOTE, "skills");
|
|
7
|
+
/**
|
|
8
|
+
* Fixed UDS session name. The local mesh is single per machine — every Pi
|
|
9
|
+
* process on the host shares this broker. Previous versions exposed
|
|
10
|
+
* multi-session support (named sessions, leave/rename/sessions commands);
|
|
11
|
+
* those were removed in the 2026-05-23 simplification because in practice
|
|
12
|
+
* every install converged on one session anyway and multi-session UX added
|
|
13
|
+
* friction without value.
|
|
14
|
+
*/
|
|
15
|
+
export const LOCAL_SESSION_NAME = "local";
|
|
7
16
|
/** Ensures the new subdirs exist inside the existing ~/.pi/remote/. */
|
|
8
17
|
export function ensureGlobalDirs() {
|
|
9
18
|
mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global_config.js","sourceRoot":"","sources":["../../src/session/global_config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AACxD,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;AACtD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;AAElD,uEAAuE;AACvE,MAAM,UAAU,gBAAgB;IAC9B,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,YAAY;IAC1B,gBAAgB,EAAE,CAAC;IACnB,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YAChD,IAAI,CAAC;gBACH,OAAO,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAC3D,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,KAAK,CAAC;YAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,UAAU,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3C,CAAC"}
|
|
1
|
+
{"version":3,"file":"global_config.js","sourceRoot":"","sources":["../../src/session/global_config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AACxD,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;AACtD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C,uEAAuE;AACvE,MAAM,UAAU,gBAAgB;IAC9B,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,YAAY;IAC1B,gBAAgB,EAAE,CAAC;IACnB,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YAChD,IAAI,CAAC;gBACH,OAAO,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAC3D,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,KAAK,CAAC;YAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,UAAU,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -14,3 +14,19 @@ export type ElectionResult = {
|
|
|
14
14
|
* Returns the role + the live handle (server for leader, socket for follower).
|
|
15
15
|
*/
|
|
16
16
|
export declare function joinOrLead(sockPath: string): Promise<ElectionResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Probes a UDS path: if a live listener responds, returns the connected
|
|
19
|
+
* socket; otherwise returns null (timeout or ECONNREFUSED). Exported so
|
|
20
|
+
* the cwd-lock primitive can reuse it without duplicating the OS dance.
|
|
21
|
+
*/
|
|
22
|
+
export declare function tryConnect(sockPath: string): Promise<Socket | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Attempts to bind a UDS server on `sockPath`. Returns the live server on
|
|
25
|
+
* success, null when the path is already held (EADDRINUSE) or any other
|
|
26
|
+
* bind error. Caller is responsible for `unlink`ing a stale sock file
|
|
27
|
+
* before retrying.
|
|
28
|
+
*/
|
|
29
|
+
export declare function tryBind(sockPath: string): Promise<Server | null>;
|
|
30
|
+
/** Unlinks the sock file if it exists and is actually a socket. No-op
|
|
31
|
+
* otherwise. Used to clear stale sockets left behind by a crashed peer. */
|
|
32
|
+
export declare function removeStaleSock(sockPath: string): void;
|
|
@@ -27,6 +27,28 @@ export async function joinOrLead(sockPath) {
|
|
|
27
27
|
}
|
|
28
28
|
throw new Error(`leader election failed after ${MAX_ATTEMPTS} attempts: ${sockPath}`);
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Probes a UDS path: if a live listener responds, returns the connected
|
|
32
|
+
* socket; otherwise returns null (timeout or ECONNREFUSED). Exported so
|
|
33
|
+
* the cwd-lock primitive can reuse it without duplicating the OS dance.
|
|
34
|
+
*/
|
|
35
|
+
export function tryConnect(sockPath) {
|
|
36
|
+
return _tryConnect(sockPath);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Attempts to bind a UDS server on `sockPath`. Returns the live server on
|
|
40
|
+
* success, null when the path is already held (EADDRINUSE) or any other
|
|
41
|
+
* bind error. Caller is responsible for `unlink`ing a stale sock file
|
|
42
|
+
* before retrying.
|
|
43
|
+
*/
|
|
44
|
+
export function tryBind(sockPath) {
|
|
45
|
+
return _tryBind(sockPath);
|
|
46
|
+
}
|
|
47
|
+
/** Unlinks the sock file if it exists and is actually a socket. No-op
|
|
48
|
+
* otherwise. Used to clear stale sockets left behind by a crashed peer. */
|
|
49
|
+
export function removeStaleSock(sockPath) {
|
|
50
|
+
_removeStaleSock(sockPath);
|
|
51
|
+
}
|
|
30
52
|
function _tryConnect(sockPath) {
|
|
31
53
|
return new Promise((resolve) => {
|
|
32
54
|
const sock = createConnection({ path: sockPath });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leader_election.js","sourceRoot":"","sources":["../../src/session/leader_election.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"leader_election.js","sourceRoot":"","sources":["../../src/session/leader_election.ts"],"names":[],"mappings":"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"}
|