@bivy/bivy 0.1.0-staging.3 → 0.1.0-staging.5

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/bin/bivy.mjs CHANGED
@@ -3265,15 +3265,47 @@ async function cmdStatus(args = []) {
3265
3265
  try { status = await localApi(config, "/api/status"); } catch {}
3266
3266
  }
3267
3267
  if (json) {
3268
- console.log(JSON.stringify({ reachable, url: url(config), workspace: status?.workspace || config.workspace, service: serviceStatusLine(), remoteConfigured: Boolean(status?.relay?.configured || fs.existsSync(relayConfigPath)), status }, null, 2));
3268
+ const relayCfgJson = loadRelayConfig();
3269
+ console.log(JSON.stringify({
3270
+ reachable,
3271
+ url: url(config),
3272
+ workspace: status?.workspace || config.workspace,
3273
+ service: serviceStatusLine(),
3274
+ remoteConfigured: Boolean(status?.relay?.configured || relayCfgJson),
3275
+ relay: {
3276
+ configured: Boolean(status?.relay?.configured || relayCfgJson),
3277
+ connected: status?.relay?.connected ?? null,
3278
+ app: status?.relay?.controlPlaneUrl || relayCfgJson?.controlPlaneUrl || null,
3279
+ relay: status?.relay?.relayUrl || relayCfgJson?.url || null,
3280
+ lastError: status?.relay?.lastError || null,
3281
+ },
3282
+ status,
3283
+ }, null, 2));
3269
3284
  return;
3270
3285
  }
3271
3286
  console.log(c.bold("\n Bivy node\n"));
3272
3287
  console.log(` url: ${url(config)} ${reachable ? c.green("● reachable") : c.dim("○ not reachable")}`);
3273
3288
  console.log(` workspace: ${status?.workspace || config.workspace}`);
3274
3289
  console.log(` ${serviceStatusLine()}`);
3275
- console.log(` remote: ${status?.relay?.configured || fs.existsSync(relayConfigPath) ? c.green("relay configured") : "local only"}`);
3276
3290
  const relay = loadRelayConfig();
3291
+ const relaySt = status?.relay;
3292
+ const relayConfigured = Boolean(relaySt?.configured || relay);
3293
+ if (!relayConfigured) {
3294
+ console.log(` remote: ${c.dim("local only")} ${c.dim("('bivy relay:setup' to enable remote access)")}`);
3295
+ } else {
3296
+ // The live link state is only knowable when the node is running; if it's
3297
+ // down we can say it's configured but not whether it's currently connected.
3298
+ let state;
3299
+ if (!reachable) state = c.yellow("configured (node not running)");
3300
+ else if (relaySt?.connected) state = c.green("● connected");
3301
+ else state = c.yellow("○ configured, not connected");
3302
+ const relErr = relaySt?.lastError ? c.dim(` (${relaySt.lastError})`) : "";
3303
+ console.log(` remote: ${state}${relErr}`);
3304
+ const cpUrl = relaySt?.controlPlaneUrl || relay?.controlPlaneUrl;
3305
+ const rlUrl = relaySt?.relayUrl || relay?.url;
3306
+ if (cpUrl) console.log(` app: ${cpUrl}`);
3307
+ if (rlUrl) console.log(` relay: ${rlUrl}`);
3308
+ }
3277
3309
  if (relay?.controlPlaneUrl && relay?.enrollmentToken) {
3278
3310
  try {
3279
3311
  const acct = await controlPlaneNodeApi(relay, "/node/account");
@@ -3331,7 +3363,15 @@ async function cmdDoctor(args = []) {
3331
3363
  console.log(` ${mark(agentAvailable, true)} agent ${runtimeInfo?.displayName || defaultAgent}${agentAvailable ? "" : c.dim(" not available — install it or run 'bivy setup'")}`);
3332
3364
  console.log(` ${mark(hasModelConfig(config), authOwner !== "bivy")} model ${hasModelConfig(config) ? "configured" : authOwner === "bivy" ? c.dim("not configured — run 'bivy login'") : c.dim("agent-native auth — use the agent's CLI login if needed")}`);
3333
3365
  const relayConfigured = Boolean(status?.relay?.configured || fs.existsSync(relayConfigPath));
3334
- console.log(` ${relayConfigured ? ok : c.dim("○")} remote ${relayConfigured ? (status?.relay?.connected ? c.green("relay connected") : "relay configured") : c.dim("local only — 'bivy relay:setup' to enable")}`);
3366
+ const relayConnected = Boolean(status?.relay?.connected);
3367
+ const relayApp = status?.relay?.controlPlaneUrl;
3368
+ const relayErr = status?.relay?.lastError;
3369
+ const relayLine = !relayConfigured
3370
+ ? c.dim("local only — 'bivy relay:setup' to enable")
3371
+ : relayConnected
3372
+ ? c.green("relay connected") + (relayApp ? c.dim(` ${relayApp}`) : "")
3373
+ : c.yellow("configured, not connected") + (relayErr ? c.dim(` (${relayErr})`) : "");
3374
+ console.log(` ${relayConfigured ? (relayConnected ? ok : warn) : c.dim("○")} remote ${relayLine}`);
3335
3375
  // Derived from BUILTIN_TERMINAL_AGENTS (the same list 'bivy agents'/'bivy run'
3336
3376
  // use) rather than a hand-maintained list, so it can't drift out of sync (#113).
3337
3377
  const agentCommands = [...BUILTIN_TERMINAL_AGENTS.values()].filter((a) => a.type === "command").map((a) => a.command);
@@ -51,6 +51,14 @@ export class RelayConnector {
51
51
  heartbeatTimer;
52
52
  stableTimer;
53
53
  lastPongAt = 0;
54
+ // True only between the relay's `ready` message (auth + entitlement passed)
55
+ // and the socket closing. This — not "a connector object exists" — is what
56
+ // "connected" means to the control plane, so it's what `bivy status` reports.
57
+ ready = false;
58
+ // Most recent relay-side failure (ticket mint, socket error, or an `error`
59
+ // frame), surfaced by `bivy status`/`doctor` so a node that never connects
60
+ // explains why instead of silently showing "configured".
61
+ lastErrorMessage;
54
62
  replay = new ReplayGuard();
55
63
  reassembler = new FrameReassembler();
56
64
  pairing;
@@ -71,12 +79,26 @@ export class RelayConnector {
71
79
  return;
72
80
  this.ws.send(JSON.stringify({ t: "pair", p: JSON.stringify({ k: "key.rotate", deliveries }) }));
73
81
  }
82
+ /**
83
+ * True only while the relay link is live AND the relay has sent `ready`
84
+ * (auth/entitlement checks passed) — i.e. the node is actually reachable from
85
+ * the control plane. A socket that opened but was rejected, or one still
86
+ * reconnecting, reads false.
87
+ */
88
+ get connected() {
89
+ return this.ready && this.ws?.readyState === WebSocket.OPEN;
90
+ }
91
+ /** Most recent relay-side failure, if any — for status/diagnostics. */
92
+ get lastError() {
93
+ return this.lastErrorMessage;
94
+ }
74
95
  start() {
75
96
  this.closed = false;
76
97
  void this.connect();
77
98
  }
78
99
  stop() {
79
100
  this.closed = true;
101
+ this.ready = false;
80
102
  this.stopHeartbeat();
81
103
  this.clearBackoffReset();
82
104
  this.ws?.close();
@@ -250,6 +272,7 @@ export class RelayConnector {
250
272
  ({ ticket, relayUrl } = await this.mintTicket());
251
273
  }
252
274
  catch (error) {
275
+ this.lastErrorMessage = `ticket mint failed: ${error.message}`;
253
276
  console.warn("[relay] could not mint relay ticket:", error.message);
254
277
  this.scheduleReconnect();
255
278
  return;
@@ -276,6 +299,8 @@ export class RelayConnector {
276
299
  return;
277
300
  }
278
301
  if (env.t === "ready") {
302
+ this.ready = true;
303
+ this.lastErrorMessage = undefined;
279
304
  this.startHeartbeat(ws);
280
305
  this.scheduleBackoffReset(ws);
281
306
  console.log("[relay] connected");
@@ -311,6 +336,7 @@ export class RelayConnector {
311
336
  }
312
337
  if (env.t === "error") {
313
338
  const message = env.error || "Relay error";
339
+ this.lastErrorMessage = message;
314
340
  console.warn("[relay] error:", message);
315
341
  if (isFatalRelayError(message)) {
316
342
  console.warn("[relay] disabling connector; fix relay setup/plan and restart the node dev server");
@@ -323,11 +349,13 @@ export class RelayConnector {
323
349
  this.lastPongAt = Date.now();
324
350
  });
325
351
  ws.on("close", () => {
352
+ this.ready = false;
326
353
  this.stopHeartbeat();
327
354
  this.clearBackoffReset();
328
355
  this.scheduleReconnect();
329
356
  });
330
357
  ws.on("error", (error) => {
358
+ this.lastErrorMessage = error.message;
331
359
  console.warn("[relay] socket error:", error.message);
332
360
  });
333
361
  }
package/dist/server.js CHANGED
@@ -7577,7 +7577,14 @@ app.get("/api/status", (_req, res) => {
7577
7577
  workspaceBoundary: true,
7578
7578
  strictApprovalOptIn: true,
7579
7579
  },
7580
- relay: { configured: Boolean(relayConfig), connected: Boolean(relay) },
7580
+ relay: {
7581
+ configured: Boolean(relayConfig),
7582
+ // Real link state (relay sent `ready`), not merely "a connector exists".
7583
+ connected: Boolean(relay?.connected),
7584
+ controlPlaneUrl: relayConfig?.controlPlaneUrl,
7585
+ relayUrl: relayConfig?.url,
7586
+ ...(relay?.lastError ? { lastError: relay.lastError } : {}),
7587
+ },
7581
7588
  sessions: {
7582
7589
  open: new Set(openSessions.values()).size,
7583
7590
  indexed: metadata.listSessions().length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.1.0-staging.3",
3
+ "version": "0.1.0-staging.5",
4
4
  "type": "module",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "description": "Run coding agents on machines you own. Source-available, self-hostable agent workspace.",