hub-runner 0.2.1 → 0.2.3

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/main.mjs CHANGED
@@ -2174,17 +2174,17 @@ function createSupervisor({
2174
2174
  warn(`[registry] ! ${entry.name} crashed: ${entry.error}`);
2175
2175
  return changed(entry);
2176
2176
  }
2177
- const delay2 = Math.min(BACKOFF_BASE_MS * 2 ** (entry.crashes.length - 1), BACKOFF_MAX_MS);
2177
+ const delay3 = Math.min(BACKOFF_BASE_MS * 2 ** (entry.crashes.length - 1), BACKOFF_MAX_MS);
2178
2178
  entry.status = "down";
2179
- entry.nextRestartAt = new Date(now + delay2).toISOString();
2180
- warn(`[registry] ${entry.name} exited unexpectedly (${how}); restarting in ${delay2 / 1e3}s`);
2179
+ entry.nextRestartAt = new Date(now + delay3).toISOString();
2180
+ warn(`[registry] ${entry.name} exited unexpectedly (${how}); restarting in ${delay3 / 1e3}s`);
2181
2181
  entry.timer = setTimeout(async () => {
2182
2182
  await untilTreeGone(pgid, tree, KILL_GRACE_MS);
2183
2183
  if (entry.status !== "down" || entry.stopping) return;
2184
2184
  entry.restarts++;
2185
2185
  changed(entry);
2186
2186
  void spawnAgent(entry, { recovered: true });
2187
- }, delay2);
2187
+ }, delay3);
2188
2188
  entry.timer.unref();
2189
2189
  changed(entry);
2190
2190
  }
@@ -2575,7 +2575,7 @@ async function sendToBot(fetchFn, eve, d, context, loadImage) {
2575
2575
  }
2576
2576
 
2577
2577
  // ../../hosted-hub/runner/hub-runner.ts
2578
- import { setTimeout as delay } from "node:timers/promises";
2578
+ import { setTimeout as delay2 } from "node:timers/promises";
2579
2579
 
2580
2580
  // ../../hosted-hub/runner/status.ts
2581
2581
  import { renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
@@ -3130,6 +3130,8 @@ async function stageLocal(workspace, dir2, pnpm) {
3130
3130
  for (const entry of readdirSync3(staged)) if (!SKIP.has(entry)) rmSync2(join3(staged, entry), { recursive: true, force: true });
3131
3131
  }
3132
3132
  cpSync(join3(workspace, "agents"), join3(dir2, "agents"), { recursive: true, force: true, filter: (p) => !SKIP.has(p.split("/").at(-1)) });
3133
+ rmSync2(join3(dir2, "lib"), { recursive: true, force: true });
3134
+ if (existsSync3(join3(workspace, "lib"))) cpSync(join3(workspace, "lib"), join3(dir2, "lib"), { recursive: true, filter: (p) => !SKIP.has(p.split("/").at(-1)) });
3133
3135
  const config = join3(dir2, "hub.workspace.json");
3134
3136
  if (existsSync3(config) || (() => {
3135
3137
  try {
@@ -4545,6 +4547,38 @@ function modelCredential(o) {
4545
4547
  } };
4546
4548
  }
4547
4549
 
4550
+ // ../../hosted-hub/runner/held.ts
4551
+ import { setTimeout as delay } from "node:timers/promises";
4552
+ function heldReports(send, failed) {
4553
+ const told = /* @__PURE__ */ new Map();
4554
+ const now = (bot) => told.has(bot) ? told.get(bot) : null;
4555
+ let sending = Promise.resolve();
4556
+ const tell = (bot, reason) => sending = sending.catch(() => {
4557
+ }).then(async () => {
4558
+ if (now(bot) === reason) return;
4559
+ told.set(bot, void 0);
4560
+ await send({ bot, reason });
4561
+ if (reason) told.set(bot, reason);
4562
+ else told.delete(bot);
4563
+ });
4564
+ return (waiting, why) => {
4565
+ for (const bot of /* @__PURE__ */ new Set([...waiting, ...told.keys()])) {
4566
+ const reason = why(bot);
4567
+ if (now(bot) !== reason) tell(bot, reason).catch(failed);
4568
+ }
4569
+ };
4570
+ }
4571
+ async function stopWithin(stop, ms, signal) {
4572
+ const stopped = new AbortController();
4573
+ await Promise.race([
4574
+ stop.finally(() => stopped.abort()),
4575
+ delay(ms, void 0, { signal: AbortSignal.any([signal, stopped.signal]) }).then(() => {
4576
+ throw new Error(`it did not stop in ${ms / 1e3}s`);
4577
+ }, () => {
4578
+ })
4579
+ ]);
4580
+ }
4581
+
4548
4582
  // ../../hosted-hub/runner/hub-runner.ts
4549
4583
  var realBots = { stage: stageLocal, supervise: createSupervisor };
4550
4584
  var HeldElsewhere = class extends Error {
@@ -4822,7 +4856,7 @@ async function run3(config, bots = realBots) {
4822
4856
  if (restarted.get(name)?.requestedAt !== requestedAt) {
4823
4857
  if (agent) {
4824
4858
  try {
4825
- await supervisor.stop(agent);
4859
+ await stopWithin(supervisor.stop(agent), config.stopTimeoutMs ?? 6e4, shutdown.signal);
4826
4860
  shutdown.signal.throwIfAborted();
4827
4861
  supervisor.start(agent);
4828
4862
  if (!await supervisor.waitUp(agent, 12e4)) throw new Error("it did not come back up");
@@ -4867,7 +4901,7 @@ async function run3(config, bots = realBots) {
4867
4901
  }
4868
4902
  }
4869
4903
  let syncing = null, again = false, drained = null;
4870
- const unsettled = /* @__PURE__ */ new Set();
4904
+ const unsettled = /* @__PURE__ */ new Set(), held = heldReports((body) => hubJson("/api/local-runner/setup", { method: "POST", body: JSON.stringify(body), signal: AbortSignal.timeout(ACK_MS) }), (e) => failed("held report", e));
4871
4905
  async function sync() {
4872
4906
  const work = await hubJson("/api/local-runner/work");
4873
4907
  if (drained && !work.sessions.length && !work.children.length && !work.deliveries.some(([d]) => unsettled.has(d.id))) {
@@ -4895,6 +4929,7 @@ async function run3(config, bots = realBots) {
4895
4929
  for (const [id, abort] of following) if (![...sessions, ...work.children].some((s) => s.sessionId === id)) abort.abort();
4896
4930
  restart(work);
4897
4931
  compact2(work);
4932
+ held(work.deliveries.map(([d]) => d.bot), (bot) => drained ? "draining" : restarting.has(bot) ? "restarting" : !running.has(bot) ? "stopped" : null);
4898
4933
  await Promise.all([
4899
4934
  ...sessions.filter((s) => s.cancel.length).map((s) => cancel(s, work.human).catch((e) => failed("cancel", e))),
4900
4935
  ...work.deliveries.filter(([d]) => (!drained || unsettled.has(d.id)) && running.has(d.bot) && !restarting.has(d.bot) && !dead.has(d.bot) && !retired.has(d.sessionId)).map((q) => deliver(q, work).catch((e) => failed("delivery", e)))
@@ -4951,7 +4986,7 @@ async function run3(config, bots = realBots) {
4951
4986
  } finally {
4952
4987
  clearTimeout(watchdog);
4953
4988
  }
4954
- await delay(RETRY_MS, void 0, { signal: shutdown.signal }).catch(() => {
4989
+ await delay2(RETRY_MS, void 0, { signal: shutdown.signal }).catch(() => {
4955
4990
  });
4956
4991
  }
4957
4992
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hub-runner",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Runs a hub workspace's Mac bots on this Mac under launchd, with no app needed.",
6
6
  "type": "module",
@@ -10,7 +10,8 @@
10
10
  "files": [
11
11
  "bin",
12
12
  "lib",
13
- "main.mjs"
13
+ "main.mjs",
14
+ "check.mjs"
14
15
  ],
15
16
  "engines": {
16
17
  "node": ">=24"