byollm 0.1.0-alpha.76 → 0.1.0-alpha.78

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  > [!WARNING]
2
- > **Alpha (`0.1.0-alpha.76`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.78`) — under active development. Don't use this yet.**
3
3
  >
4
4
  > Install it deliberately: `npx byollm@alpha`, or `npm install byollm@alpha`.
5
5
  >
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-MSRBBLRQ.js";
4
+ } from "./chunk-ZROCDZDA.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.exitCode = await main(process.argv.slice(2));
@@ -59,7 +59,8 @@ var BOLD = "\x1B[1m";
59
59
  var RESET = "\x1B[0m";
60
60
  function emphasisAllowed(context2) {
61
61
  if (context2.env["NO_COLOR"] !== void 0) return false;
62
- if (context2.env["FORCE_COLOR"] !== void 0) return true;
62
+ const forced = context2.env["FORCE_COLOR"];
63
+ if (forced !== void 0) return forced !== "0" && forced !== "";
63
64
  return context2.tty;
64
65
  }
65
66
  function emphasise(value, context2) {
@@ -3597,6 +3598,7 @@ function joinSections(sections) {
3597
3598
  }
3598
3599
 
3599
3600
  // src/runner.ts
3601
+ var AUTH_RECHECK_MS = 6e4;
3600
3602
  var DEFAULT_HEARTBEAT_MS = 1e4;
3601
3603
  function sameKey(a, b) {
3602
3604
  return a.identity === b.identity && a.encryption === b.encryption && a.encryptionSig === b.encryptionSig;
@@ -3665,6 +3667,10 @@ var Runner = class {
3665
3667
  * service was blocked.
3666
3668
  */
3667
3669
  #blockedReport = /* @__PURE__ */ new Map();
3670
+ /** Signed-out services' reports, kept for the same reason blocked ones are. */
3671
+ #withdrawnReport = /* @__PURE__ */ new Map();
3672
+ /** Earliest moment each withdrawn service may be asked again — T2-S1. */
3673
+ #authRecheckAt = /* @__PURE__ */ new Map();
3668
3674
  /** What was last handed to the writer, so an unchanged pass writes nothing. */
3669
3675
  #lastWritten;
3670
3676
  /**
@@ -3779,7 +3785,29 @@ var Runner = class {
3779
3785
  const capabilities = [];
3780
3786
  const probed = /* @__PURE__ */ new Map();
3781
3787
  for (const route of this.#options.loaded.routes) {
3782
- if (this.#unauthenticated.has(route.service)) continue;
3788
+ if (this.#unauthenticated.has(route.service)) {
3789
+ const recheckAt = this.#authRecheckAt.get(route.service) ?? 0;
3790
+ const backend = this.#backendFor(route);
3791
+ const lifted = this.#now() >= recheckAt && backend.canary !== void 0 && (await backend.canary(route.model)).healthy;
3792
+ if (!lifted) {
3793
+ if (this.#now() >= recheckAt) {
3794
+ this.#authRecheckAt.set(
3795
+ route.service,
3796
+ this.#now() + AUTH_RECHECK_MS
3797
+ );
3798
+ }
3799
+ const kept = this.#withdrawnReport.get(route.service);
3800
+ if (kept !== void 0) this.serviceStates.set(route.service, kept);
3801
+ continue;
3802
+ }
3803
+ this.#unauthenticated.delete(route.service);
3804
+ this.#withdrawnReport.delete(route.service);
3805
+ this.#authRecheckAt.delete(route.service);
3806
+ this.#options.onEvent?.({
3807
+ type: "service-signed-in",
3808
+ service: route.service
3809
+ });
3810
+ }
3783
3811
  const blockedUntil = this.#blocked.get(route.service);
3784
3812
  if (this.#blocked.has(route.service)) {
3785
3813
  if (blockedUntil !== void 0 && this.#now() < blockedUntil) {
@@ -4235,12 +4263,14 @@ var Runner = class {
4235
4263
  });
4236
4264
  if (!result.ok && result.code === "unauthorized") {
4237
4265
  this.#unauthenticated.add(route.service);
4238
- this.#noteServiceState(route.service, {
4266
+ const withdrawn = this.#noteServiceState(route.service, {
4267
+ ...this.serviceStates.get(route.service),
4239
4268
  state: {
4240
4269
  kind: "signed-out",
4241
4270
  detail: result.message
4242
4271
  }
4243
4272
  });
4273
+ this.#withdrawnReport.set(route.service, withdrawn);
4244
4274
  this.#options.onEvent?.({
4245
4275
  type: "service-not-signed-in",
4246
4276
  service: route.service,
@@ -4391,9 +4421,19 @@ var Runner = class {
4391
4421
  #writeServiceStates() {
4392
4422
  const now = JSON.stringify([...this.serviceStates]);
4393
4423
  if (now === this.#lastWritten) return;
4394
- this.#lastWritten = now;
4395
4424
  const noted = this.#options.onServiceStates?.(this.serviceStates);
4396
- if (noted !== void 0) void noted.catch(() => void 0);
4425
+ if (noted === void 0) {
4426
+ this.#lastWritten = now;
4427
+ return;
4428
+ }
4429
+ void noted.then(
4430
+ () => {
4431
+ this.#lastWritten = now;
4432
+ },
4433
+ () => {
4434
+ this.#lastWritten = void 0;
4435
+ }
4436
+ );
4397
4437
  }
4398
4438
  /**
4399
4439
  * Note a refusal that means the relationship is over.
@@ -4548,6 +4588,20 @@ var Runner = class {
4548
4588
  }
4549
4589
  const fetched = await this.#fetchWhenSealed(job);
4550
4590
  if (!fetched) return;
4591
+ if ("gone" in fetched) {
4592
+ this.#options.onEvent?.({
4593
+ type: "error",
4594
+ message: `${job.id} is not ours to run: ${fetched.gone}`
4595
+ });
4596
+ await this.#safely(
4597
+ () => this.#options.client.release({
4598
+ runnerId: this.#options.runnerId,
4599
+ leases: [{ jobId: job.id, leaseId: job.lease.id }],
4600
+ reason: "refused"
4601
+ })
4602
+ );
4603
+ return;
4604
+ }
4551
4605
  const payload = await this.#openPayload(job, fetched.envelope);
4552
4606
  const resolved = job.grant?.service;
4553
4607
  const route = this.#routeFor(job.kind, resolved);
@@ -4643,6 +4697,28 @@ var Runner = class {
4643
4697
  * the job up quietly — the upstream's `awaiting-payload` timer will requeue
4644
4698
  * it, and the stub was never lost.
4645
4699
  */
4700
+ /**
4701
+ * Wait for the payload, or say the job is over for this device — B038.
4702
+ *
4703
+ * The stuck-daemon loop, reported live on a real user's Windows machine: a
4704
+ * claimed job came back "unknown job", the daemon abandoned it **without
4705
+ * releasing the lease**, the hub re-offered it, and the same daemon
4706
+ * re-claimed the same id every twenty seconds forever. The log spammed
4707
+ * claim, unknown job, reclaim; the device looked wedged while fresh jobs on
4708
+ * a live site still ran fine.
4709
+ *
4710
+ * Both exits leaked. A terminal error threw straight out of `#handle` into
4711
+ * the background `catch`, and the give-up-at-deadline path returned `null`
4712
+ * into `if (!fetched) return`. Neither released anything, and the comment on
4713
+ * that `catch` explains why nobody noticed: letting the lease lapse *is*
4714
+ * the recovery the protocol is built around — for a transient failure. For
4715
+ * a permanent one it is an invitation to be offered the same job again.
4716
+ *
4717
+ * So this reports *why* it stopped instead of throwing, and the caller
4718
+ * releases. `refused` is the shape the relay already has for exactly this:
4719
+ * a permanent decline, which means the job is never offered to this device
4720
+ * again. Nothing new on the wire.
4721
+ */
4646
4722
  async #fetchWhenSealed(job) {
4647
4723
  const deadline = Math.min(job.lease.expiresAt, this.#now() + 3e4);
4648
4724
  let delay = 50;
@@ -4655,13 +4731,18 @@ var Runner = class {
4655
4731
  });
4656
4732
  } catch (error) {
4657
4733
  const notReady = error instanceof ClientError && error.kind === "not-ready";
4658
- if (!notReady) throw error;
4734
+ if (!notReady) {
4735
+ if (error instanceof ClientError && error.kind === "rejected") {
4736
+ return { gone: error.message };
4737
+ }
4738
+ throw error;
4739
+ }
4659
4740
  if (this.#now() + delay >= deadline) {
4660
4741
  this.#options.onEvent?.({
4661
4742
  type: "error",
4662
4743
  message: `gave up waiting for the payload of ${job.id}`
4663
4744
  });
4664
- return null;
4745
+ return { gone: `the payload never arrived for ${job.id}` };
4665
4746
  }
4666
4747
  await new Promise((resolve) => setTimeout(resolve, delay));
4667
4748
  delay = Math.min(delay * 2, 1e3);
@@ -6317,6 +6398,12 @@ function report(origin, event, io) {
6317
6398
  `${at} ${host} ${event.service} is not signed in \u2014 it has stopped taking work.
6318
6399
  ${event.detail}
6319
6400
  Sign in with that tool, then restart: \`byollm run\` re-checks on start.
6401
+ `
6402
+ );
6403
+ break;
6404
+ case "service-signed-in":
6405
+ io.err(
6406
+ `${at} ${host} ${event.service} is signed in again \u2014 it is taking work.
6320
6407
  `
6321
6408
  );
6322
6409
  break;
@@ -7029,7 +7116,7 @@ async function exists(path) {
7029
7116
  }
7030
7117
 
7031
7118
  // src/index.ts
7032
- var DAEMON_VERSION = "0.1.0-alpha.76";
7119
+ var DAEMON_VERSION = "0.1.0-alpha.78";
7033
7120
  function daemonVersion() {
7034
7121
  return {
7035
7122
  daemon: DAEMON_VERSION,
@@ -7081,4 +7168,4 @@ export {
7081
7168
  runCli,
7082
7169
  main
7083
7170
  };
7084
- //# sourceMappingURL=chunk-MSRBBLRQ.js.map
7171
+ //# sourceMappingURL=chunk-ZROCDZDA.js.map