@open-bus/cli 0.43.4 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +56 -7
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -21558,6 +21558,10 @@ var init_frames = __esm({
21558
21558
  hub: external_exports.enum(["connected", "connecting"]),
21559
21559
  /** Why the hub link is down: a Node error code, `timeout` or `HTTP 502`. */
21560
21560
  hub_error: external_exports.string().optional(),
21561
+ /** The link has been open at least once since the daemon started. Older daemons omit it. */
21562
+ hub_ever: external_exports.boolean().optional(),
21563
+ /** Ms epoch of the last drop, while the link is down after being up. Older daemons omit it. */
21564
+ hub_since: external_exports.number().optional(),
21561
21565
  /** The namespace `cwd` resolves to, when the request named one. */
21562
21566
  namespace: external_exports.string().optional(),
21563
21567
  /** One roster per namespace: this machine's sessions', or only `cwd`'s. */
@@ -22272,6 +22276,22 @@ function ancestorChain(pid) {
22272
22276
  }
22273
22277
  return out;
22274
22278
  }
22279
+ function statStartTicks(stat) {
22280
+ const close = stat.lastIndexOf(")");
22281
+ if (close < 0) return null;
22282
+ const start = stat.slice(close + 1).trim().split(/\s+/)[19];
22283
+ return start && /^\d+$/.test(start) ? start : null;
22284
+ }
22285
+ function claudeProcStartOf(pid, deps = {}) {
22286
+ if ((deps.platform ?? process.platform) !== "linux") return procStartOf(pid);
22287
+ try {
22288
+ return statStartTicks(
22289
+ (deps.readStat ?? ((p) => readFileSync(`/proc/${p}/stat`, "utf8")))(pid)
22290
+ );
22291
+ } catch {
22292
+ return null;
22293
+ }
22294
+ }
22275
22295
  function sessionHello(session, pid, cwd) {
22276
22296
  return Hello2.parse({
22277
22297
  v: 1,
@@ -37438,7 +37458,7 @@ async function connect4() {
37438
37458
  const session = findSession(process.pid, {
37439
37459
  sessionsDir: SESSIONS,
37440
37460
  ancestors: ancestorChain,
37441
- procStart: procStartOf
37461
+ procStart: claudeProcStartOf
37442
37462
  });
37443
37463
  if (!session || socketDirProblem(HOME)) return null;
37444
37464
  return await connectDaemon({
@@ -37952,7 +37972,7 @@ function attestClaude(hello, deps) {
37952
37972
  if (typeof raw.procStart !== "string") {
37953
37973
  return { ok: false, reason: "Claude session has no procStart" };
37954
37974
  }
37955
- const start = (deps.procStart ?? procStartOf)(pid);
37975
+ const start = (deps.procStart ?? claudeProcStartOf)(pid);
37956
37976
  if (!start) return { ok: false, reason: "session process is not running" };
37957
37977
  if (raw.procStart !== start) {
37958
37978
  return { ok: false, reason: "session process start time moved" };
@@ -40167,7 +40187,7 @@ async function runDaemon(deps = {}) {
40167
40187
  const running = cl.isRunning ?? isRunning;
40168
40188
  const claudeSessions = {
40169
40189
  dir: cl.sessionsDir ?? claudeSessionsDir(),
40170
- procStart: cachedProcStart(cl.procStart ?? procStartOf, running, {
40190
+ procStart: cachedProcStart(cl.procStart ?? claudeProcStartOf, running, {
40171
40191
  ttlMs: cl.syncMs ?? 2e3
40172
40192
  }),
40173
40193
  isRunning: running
@@ -40404,6 +40424,8 @@ async function runDaemon(deps = {}) {
40404
40424
  onClosed: announceSessions,
40405
40425
  log
40406
40426
  });
40427
+ let hubEver = false;
40428
+ let hubDroppedAt;
40407
40429
  const hub = new HubConnection({
40408
40430
  url: `${cfg.hub.replace(/^http/, "ws").replace(/\/$/, "")}/hub`,
40409
40431
  token: cfg.machine_token,
@@ -40519,10 +40541,13 @@ async function runDaemon(deps = {}) {
40519
40541
  },
40520
40542
  // The hub forgets agents with the link; sessions went out with hello.
40521
40543
  onOpen: () => {
40544
+ hubEver = true;
40545
+ hubDroppedAt = void 0;
40522
40546
  for (const rec of agents.all()) if (rec.live) announceUp(rec);
40523
40547
  outbox.flush();
40524
40548
  },
40525
40549
  onClose: () => {
40550
+ if (hubEver) hubDroppedAt ??= Date.now();
40526
40551
  upIds.clear();
40527
40552
  receipts.release();
40528
40553
  resends.dropped();
@@ -40613,6 +40638,8 @@ async function runDaemon(deps = {}) {
40613
40638
  label: cfg.label,
40614
40639
  hub: hub.open ? "connected" : "connecting",
40615
40640
  ...hub.open || !hub.lastError ? {} : { hub_error: hub.lastError },
40641
+ hub_ever: hubEver,
40642
+ ...hub.open || !hubEver || hubDroppedAt === void 0 ? {} : { hub_since: hubDroppedAt },
40616
40643
  ...only === void 0 ? {} : { namespace: only },
40617
40644
  namespaces,
40618
40645
  // Read-only: status never marks a late delivery shown.
@@ -40926,12 +40953,18 @@ function cell(value, width, more = "\u2026") {
40926
40953
  return v + " ".repeat(Math.max(0, width - len(v)));
40927
40954
  }
40928
40955
  var tokenTail = (token, more = "\u2026") => `ob_machine_${more}${token.slice(-4)}`;
40956
+ var minutesSince = (at, now) => Math.floor((now - at) / 6e4);
40929
40957
  function ago(at, now) {
40930
- const mins = Math.floor((now - at) / 6e4);
40958
+ const mins = minutesSince(at, now);
40931
40959
  if (mins < 1) return "now";
40932
40960
  if (mins < 60) return `${mins} min ago`;
40933
40961
  return `${Math.floor(mins / 60)} h ago`;
40934
40962
  }
40963
+ function forHowLong(since, now) {
40964
+ const mins = minutesSince(since, now);
40965
+ if (mins < 1) return void 0;
40966
+ return mins < 60 ? `for ${mins} min` : `for ${Math.floor(mins / 60)} h`;
40967
+ }
40935
40968
  var listing = (names2, last) => names2.length <= 1 ? names2[0] ?? "" : `${names2.slice(0, -1).join(", ")} ${last} ${names2.at(-1)}`;
40936
40969
  function copy(style) {
40937
40970
  const p = (tone, s) => paint(style, tone, s);
@@ -41182,7 +41215,13 @@ function copy(style) {
41182
41215
  next("If it keeps running, run npx @open-bus/cli doctor to see why.")
41183
41216
  ],
41184
41217
  // 7. status
41185
- hubRow: (host) => `Hub ${clip(host, 9)}`,
41218
+ hubRow: (host, link) => {
41219
+ if (!link) return `Hub ${clip(host, 9)}`;
41220
+ const word = `${link.state === "connected" ? g("live") : g("queued")} ${link.state}`;
41221
+ const tone = link.state === "connected" ? "success" : "warning";
41222
+ const dur = link.for ? ` ${p("muted", link.for)}` : "";
41223
+ return `Hub ${clip(host, 9 + 2 + len(word) + (link.for ? 2 + len(link.for) : 0))} ${p(tone, word)}${dur}`;
41224
+ },
41186
41225
  machineRow: (label) => `Machine ${clip(label, 9)}`,
41187
41226
  statusDaemonDown: () => [
41188
41227
  fail("Daemon not running"),
@@ -41791,7 +41830,7 @@ var noService = {
41791
41830
  remove: async () => ({}),
41792
41831
  cli: () => ["open-bus"]
41793
41832
  };
41794
- var VERSION = true ? "0.43.4" : JSON.parse(
41833
+ var VERSION = true ? "0.44.1" : JSON.parse(
41795
41834
  readFileSync9(new URL("../package.json", import.meta.url), "utf8")
41796
41835
  ).version;
41797
41836
  var which = (cmd) => {
@@ -44130,6 +44169,14 @@ var hostOf2 = (hub) => {
44130
44169
  return hub;
44131
44170
  }
44132
44171
  };
44172
+ var linkOf = (st, now) => {
44173
+ if (st.hub === "connected") return { state: "connected" };
44174
+ if (!st.hub_ever) return { state: "connecting" };
44175
+ return {
44176
+ state: "reconnecting",
44177
+ for: st.hub_since === void 0 ? void 0 : forHowLong(st.hub_since, now)
44178
+ };
44179
+ };
44133
44180
  async function status(ctx) {
44134
44181
  const c = ctx.copy;
44135
44182
  say(ctx, c.header(ctx.version), "");
@@ -44139,8 +44186,8 @@ async function status(ctx) {
44139
44186
  return 1;
44140
44187
  }
44141
44188
  const host = hostOf2(m.hub);
44142
- say(ctx, c.hubRow(host), c.machineRow(m.label));
44143
44189
  if (m.revoked) {
44190
+ say(ctx, c.hubRow(host), c.machineRow(m.label));
44144
44191
  sayErr(ctx, c.revoked());
44145
44192
  return 1;
44146
44193
  }
@@ -44148,9 +44195,11 @@ async function status(ctx) {
44148
44195
  try {
44149
44196
  st = await askDaemon(ctx, { timeoutMs: 3e3 });
44150
44197
  } catch {
44198
+ say(ctx, c.hubRow(host), c.machineRow(m.label));
44151
44199
  sayErr(ctx, socketFolderLines(ctx) ?? c.statusDaemonDown());
44152
44200
  return 1;
44153
44201
  }
44202
+ say(ctx, c.hubRow(host, linkOf(st, ctx.now())), c.machineRow(m.label));
44154
44203
  say(ctx, c.daemonRunning(st.pid, ctx.service.kind, ctx.service.fallback));
44155
44204
  if (st.hub !== "connected") {
44156
44205
  sayErr(ctx, c.hubNotReachable(host, st.hub_error, "status"));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@open-bus/cli",
3
3
  "type": "module",
4
- "version": "0.43.4",
4
+ "version": "0.44.1",
5
5
  "description": "One bus for all your AI coding agents, across machines.",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -29,14 +29,14 @@
29
29
  "typescript": "^5.9.3",
30
30
  "vitest": "^4.1.12",
31
31
  "zod": "^4.1.0",
32
- "@ob/adapter-mcp-json": "0.0.0",
32
+ "@ob/adapter-opencode": "0.0.0",
33
33
  "@ob/adapter-claude-code": "0.0.0",
34
+ "@ob/adapter-mcp-json": "0.0.0",
34
35
  "@ob/adapter-pi": "0.0.0",
35
36
  "@ob/daemon": "0.0.0",
36
- "@ob/adapter-opencode": "0.0.0",
37
- "@ob/ui": "0.0.0",
37
+ "@ob/ipc": "0.0.0",
38
38
  "@ob/shared": "0.0.0",
39
- "@ob/ipc": "0.0.0"
39
+ "@ob/ui": "0.0.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "node scripts/bundle.mjs",