@bivy/bivy 0.18.11-staging.10 → 0.18.11-staging.12

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.
@@ -121,8 +121,13 @@ export async function fetchPendingWork(cfg) {
121
121
  /** Consume the authoritative claim snapshot, not the earlier queue listing. */
122
122
  export async function claimWork(cfg, id) {
123
123
  const res = await cp(cfg, "POST", `/node/work/${encodeURIComponent(id)}/claim`, randomUUID());
124
- if (!res.ok)
124
+ if (!res.ok) {
125
+ // 409 is the normal "another node won" race; anything else would otherwise
126
+ // leave an item silently pending forever.
127
+ if (res.status !== 409)
128
+ console.warn(`[control-plane-tasks] claim ${id} failed: HTTP ${res.status}`);
125
129
  return undefined;
130
+ }
126
131
  const data = await res.json();
127
132
  return data.item ?? { id }; // compatibility with older control planes
128
133
  }
package/dist/server.js CHANGED
@@ -568,7 +568,8 @@ const runDelegation = new RunDelegationService({
568
568
  if (!sessionId)
569
569
  return undefined;
570
570
  const routing = raw.routing && typeof raw.routing === "object" ? raw.routing : {};
571
- const machine = typeof routing.nodeLabel === "string" && routing.nodeLabel ? routing.nodeLabel : identity.name;
571
+ // The queue routes by label `bivy/<machine>`.
572
+ const machine = typeof routing.nodeLabel === "string" && routing.nodeLabel ? routing.nodeLabel.replace(/^bivy\//, "") : identity.name;
572
573
  if (machine === identity.name)
573
574
  return delegationAnswer(sessionId);
574
575
  const target = (await accountMachines()).find((m) => m.name === machine);
@@ -80,7 +80,23 @@ export class SiblingClient {
80
80
  ws.on("message", (data) => this.onMessage(String(data), grant));
81
81
  ws.on("close", () => this.handleClose());
82
82
  ws.on("error", (err) => this.rejectPaired?.(err));
83
- await this.paired;
83
+ // A sibling that never answers pairing (offline behind a stale "online",
84
+ // or an incompatible build) must fail, not hang its caller forever.
85
+ const timeoutMs = this.opts.pairTimeoutMs ?? 20_000;
86
+ let timer;
87
+ try {
88
+ await Promise.race([
89
+ this.paired,
90
+ new Promise((_, reject) => { timer = setTimeout(() => reject(new Error("sibling did not answer pairing")), timeoutMs); }),
91
+ ]);
92
+ }
93
+ catch (err) {
94
+ this.close();
95
+ throw err;
96
+ }
97
+ finally {
98
+ clearTimeout(timer);
99
+ }
84
100
  }
85
101
  async onMessage(raw, grant) {
86
102
  let msg;
@@ -94,11 +110,23 @@ export class SiblingClient {
94
110
  // Begin account pairing to obtain the sibling's room key.
95
111
  // `ephemeral`: a sibling replica is a node↔node bridge, not a user device —
96
112
  // authorized for the room key but never listed as a signed-in device.
97
- this.sendControl({ t: "pair", p: { k: "pair.account", sessionToken: grant, devicePublicKeyB64: this.keypair.publicKeyB64, label: this.opts.label ?? "Bivy replica", ephemeral: true } });
113
+ // Pair payloads travel as a JSON string, as the node's relay client
114
+ // expects (it ignores any other shape) and as it replies.
115
+ this.sendControl({ t: "pair", p: JSON.stringify({ k: "pair.account", sessionToken: grant, devicePublicKeyB64: this.keypair.publicKeyB64, label: this.opts.label ?? "Bivy replica", ephemeral: true }) });
98
116
  return;
99
117
  }
100
118
  if (msg.t === "pair") {
101
- this.handlePair(msg.p);
119
+ let pair = msg.p;
120
+ if (typeof pair === "string") {
121
+ try {
122
+ pair = JSON.parse(pair);
123
+ }
124
+ catch {
125
+ return;
126
+ }
127
+ }
128
+ if (pair && typeof pair === "object")
129
+ this.handlePair(pair);
102
130
  return;
103
131
  }
104
132
  if (msg.t === "frame" && typeof msg.p === "string" && this.cipher) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.18.11-staging.10",
3
+ "version": "0.18.11-staging.12",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",