@workerdeck/server 1.2.0 → 1.4.0

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/build/index.d.mts CHANGED
@@ -39,6 +39,7 @@ type SessionRegistryOptions = {
39
39
  declare class SessionRegistry {
40
40
  #private;
41
41
  constructor(options?: SessionRegistryOptions);
42
+ observe(listener: (runner: Runner) => void): () => void;
42
43
  create(config: SessionRunnerConfig): Runner;
43
44
  prepare(config: SessionRunnerConfig): Runner;
44
45
  adopt(runner: Runner): Runner;
@@ -216,20 +217,12 @@ type QueueServerOptions = {
216
217
  webhookRetryDelayMs?: number;
217
218
  onEvent?: (event: JobEvent) => void;
218
219
  };
219
- /** A point-in-time answer to "is it safe to stop yet?", as reported while draining. */
220
220
  type DrainReport = {
221
- /** Sessions mid-turn. These resolve on their own, so the drain waits for them. */
222
221
  working: string[];
223
- /**
224
- * Sessions blocked on a human — a pending approval. The drain names these but never waits for them: nothing about
225
- * shutting down will answer the prompt, so waiting is a hang with better manners.
226
- */
227
222
  awaitingHuman: string[];
228
- /** True when the deadline passed with work still running. */
229
223
  timedOut: boolean;
230
224
  };
231
225
  type DrainOptions = {
232
- /** Overall budget. The drain gives up and reports rather than blocking shutdown forever. */
233
226
  timeoutMs?: number;
234
227
  pollMs?: number;
235
228
  onProgress?: (report: DrainReport) => void;
@@ -243,10 +236,6 @@ type WorkerServer = {
243
236
  listen: (port: number, host?: string) => Promise<{
244
237
  port: number;
245
238
  }>;
246
- /**
247
- * Let running turns finish before `close()`. A courtesy, never a correctness requirement: records are written
248
- * continuously, so a hard stop already loses nothing. Refuses new sessions for as long as it runs.
249
- */
250
239
  drain: (options?: DrainOptions) => Promise<DrainReport>;
251
240
  close: () => Promise<void>;
252
241
  };
package/build/index.mjs CHANGED
@@ -732,12 +732,6 @@ async function handleHostFiles(ctx, req, res, pathname) {
732
732
  }
733
733
  //#endregion
734
734
  //#region src/routes/create-vet.ts
735
- /**
736
- * The one create-validation ladder, run by both create doors — `POST /sessions` and the
737
- * `session` block of `POST /jobs`. The scope design claims the two are indistinguishable, so
738
- * the order and the refusals have to come from a single place rather than two copies that can
739
- * drift. Mutates `req`: strips inert fields and pins the resolved profile name.
740
- */
741
735
  function vetCreateRequest(ctx, req, auth) {
742
736
  const { availability, factory } = ctx;
743
737
  const refusedScope = factory.applyScope(req, auth);
@@ -2662,9 +2656,15 @@ function serveWindow(held, now) {
2662
2656
  var SessionRegistry = class {
2663
2657
  #sessions = /* @__PURE__ */ new Map();
2664
2658
  #options;
2659
+ #observers = /* @__PURE__ */ new Set();
2665
2660
  constructor(options = {}) {
2666
2661
  this.#options = options;
2667
2662
  }
2663
+ observe(listener) {
2664
+ this.#observers.add(listener);
2665
+ for (const runner of this.#sessions.values()) listener(runner);
2666
+ return () => void this.#observers.delete(listener);
2667
+ }
2668
2668
  create(config) {
2669
2669
  return this.adopt(new SessionRunner(config));
2670
2670
  }
@@ -2679,7 +2679,10 @@ var SessionRegistry = class {
2679
2679
  register(runner) {
2680
2680
  const existing = this.#sessions.get(runner.id);
2681
2681
  this.#sessions.set(runner.id, runner);
2682
- if (existing !== runner) this.#options.onRegister?.(runner);
2682
+ if (existing !== runner) {
2683
+ this.#options.onRegister?.(runner);
2684
+ for (const listener of this.#observers) listener(runner);
2685
+ }
2683
2686
  return runner;
2684
2687
  }
2685
2688
  get(id) {
@@ -2898,15 +2901,7 @@ function createSessionFactory(deps) {
2898
2901
  }
2899
2902
  //#endregion
2900
2903
  //#region src/server.ts
2901
- /** How long a client gets to acknowledge the shutdown close frame before its socket is torn down. */
2902
2904
  const SOCKET_CLOSE_GRACE_MS = 250;
2903
- /**
2904
- * Split live sessions into "will finish by itself" and "needs a person".
2905
- *
2906
- * `sessionState` is the vocabulary the dashboard, the session list and `workerdeck guard` already sort by, and it
2907
- * draws exactly the line a drain needs: `working` covers starting/running and running subagents, while `attention`
2908
- * covers a pending approval. Re-spelling that set here is how the two definitions would drift apart.
2909
- */
2910
2905
  function surveyDrain(registry) {
2911
2906
  const working = [];
2912
2907
  const awaitingHuman = [];