@telorun/runner-core 0.8.2 → 0.9.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.
Files changed (97) hide show
  1. package/README.md +33 -2
  2. package/dist/backend.d.ts +107 -23
  3. package/dist/backend.d.ts.map +1 -1
  4. package/dist/config.d.ts +27 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +12 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/contract.d.ts +199 -15
  9. package/dist/contract.d.ts.map +1 -1
  10. package/dist/contract.js +9 -0
  11. package/dist/contract.js.map +1 -1
  12. package/dist/debug/ports-resolved.d.ts +26 -0
  13. package/dist/debug/ports-resolved.d.ts.map +1 -0
  14. package/dist/debug/ports-resolved.js +42 -0
  15. package/dist/debug/ports-resolved.js.map +1 -0
  16. package/dist/debug/relay.d.ts.map +1 -1
  17. package/dist/debug/relay.js +34 -6
  18. package/dist/debug/relay.js.map +1 -1
  19. package/dist/debug/run-projection.d.ts +53 -0
  20. package/dist/debug/run-projection.d.ts.map +1 -0
  21. package/dist/debug/run-projection.js +115 -0
  22. package/dist/debug/run-projection.js.map +1 -0
  23. package/dist/index.d.ts +8 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +7 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/routes/apps.d.ts +1 -1
  28. package/dist/routes/apps.d.ts.map +1 -1
  29. package/dist/routes/apps.js +14 -4
  30. package/dist/routes/apps.js.map +1 -1
  31. package/dist/routes/io.d.ts.map +1 -1
  32. package/dist/routes/io.js +42 -9
  33. package/dist/routes/io.js.map +1 -1
  34. package/dist/routes/session-start.d.ts +12 -12
  35. package/dist/routes/session-start.d.ts.map +1 -1
  36. package/dist/routes/session-start.js +86 -13
  37. package/dist/routes/session-start.js.map +1 -1
  38. package/dist/routes/sessions.d.ts +11 -1
  39. package/dist/routes/sessions.d.ts.map +1 -1
  40. package/dist/routes/sessions.js +429 -4
  41. package/dist/routes/sessions.js.map +1 -1
  42. package/dist/server.d.ts.map +1 -1
  43. package/dist/server.js +21 -1
  44. package/dist/server.js.map +1 -1
  45. package/dist/session/byte-ring-buffer.d.ts +6 -1
  46. package/dist/session/byte-ring-buffer.d.ts.map +1 -1
  47. package/dist/session/byte-ring-buffer.js +2 -2
  48. package/dist/session/byte-ring-buffer.js.map +1 -1
  49. package/dist/session/registry.d.ts +144 -13
  50. package/dist/session/registry.d.ts.map +1 -1
  51. package/dist/session/registry.js +166 -25
  52. package/dist/session/registry.js.map +1 -1
  53. package/dist/session/watch-supervisor.d.ts +42 -0
  54. package/dist/session/watch-supervisor.d.ts.map +1 -0
  55. package/dist/session/watch-supervisor.js +99 -0
  56. package/dist/session/watch-supervisor.js.map +1 -0
  57. package/dist/session/workspace-app.d.ts +4 -0
  58. package/dist/session/workspace-app.d.ts.map +1 -0
  59. package/dist/session/workspace-app.js +26 -0
  60. package/dist/session/workspace-app.js.map +1 -0
  61. package/dist/session/workspace-client.d.ts +33 -0
  62. package/dist/session/workspace-client.d.ts.map +1 -0
  63. package/dist/session/workspace-client.js +62 -0
  64. package/dist/session/workspace-client.js.map +1 -0
  65. package/dist/session/workspace-marker.d.ts +25 -0
  66. package/dist/session/workspace-marker.d.ts.map +1 -0
  67. package/dist/session/workspace-marker.js +35 -0
  68. package/dist/session/workspace-marker.js.map +1 -0
  69. package/dist/sse/channel.d.ts.map +1 -1
  70. package/dist/sse/channel.js +4 -0
  71. package/dist/sse/channel.js.map +1 -1
  72. package/package.json +4 -3
  73. package/src/backend.ts +111 -22
  74. package/src/config.ts +59 -0
  75. package/src/contract.ts +197 -15
  76. package/src/debug/ports-resolved.test.ts +66 -0
  77. package/src/debug/ports-resolved.ts +43 -0
  78. package/src/debug/relay.ts +32 -4
  79. package/src/debug/run-projection.test.ts +155 -0
  80. package/src/debug/run-projection.ts +122 -0
  81. package/src/index.ts +20 -1
  82. package/src/routes/apps.ts +14 -5
  83. package/src/routes/io.ts +58 -12
  84. package/src/routes/session-start.ts +106 -27
  85. package/src/routes/sessions.ts +498 -7
  86. package/src/server.ts +22 -1
  87. package/src/session/byte-ring-buffer.ts +8 -2
  88. package/src/session/registry.ts +288 -28
  89. package/src/session/ring-buffer.test.ts +4 -3
  90. package/src/session/watch-lifetime.test.ts +203 -0
  91. package/src/session/watch-supervisor.ts +112 -0
  92. package/src/session/workspace-app.ts +27 -0
  93. package/src/session/workspace-client.ts +80 -0
  94. package/src/session/workspace-marker.test.ts +35 -0
  95. package/src/session/workspace-marker.ts +39 -0
  96. package/src/sse/channel.ts +5 -0
  97. package/workspace-app/telo.yaml +228 -0
@@ -1,15 +1,38 @@
1
1
  import { EventEmitter } from "node:events";
2
- import type { BackendSession } from "../backend.js";
3
- import { type RunEvent, type RunStatus } from "../contract.js";
2
+ import type { DebugFrame } from "@telorun/debug-wire";
3
+ import type { BackendSession, WorkloadLaunch } from "../backend.js";
4
+ import { type ByteStreamTag, type IoMode, type PortMapping, type RunEvent, type RunStatus, type RunTrigger, type SessionMode, type WorkspaceCheckpointFile } from "../contract.js";
4
5
  import { ByteRingBuffer, type BufferedBytes } from "./byte-ring-buffer.js";
5
6
  import { EventRingBuffer, type BufferedEvent } from "./ring-buffer.js";
7
+ /**
8
+ * One application's terminal and run bookkeeping. The byte channel is keyed
9
+ * `(session, app)` rather than labelled on a merged stream: each app container
10
+ * has its OWN terminal, and merging them would make `/io` unattachable to one.
11
+ */
12
+ export interface AppChannel {
13
+ readonly name: string;
14
+ readonly io: IoMode;
15
+ readonly byteBuffer: ByteRingBuffer;
16
+ readonly byteEmitter: EventEmitter;
17
+ /** Monotonic per app, starting at 1. Counted here from the kernel lifecycle
18
+ * events the debug stream already carries — the kernel is asked for nothing. */
19
+ generation: number;
20
+ /** Epoch ms the current generation started, for `durationMs` on completion.
21
+ * Null between generations. */
22
+ startedAt: number | null;
23
+ /** The port set this app currently declares. Re-read on every reload, so a
24
+ * `ports:` edit patches the Service and Ingress instead of silently binding
25
+ * a port nothing routes. */
26
+ ports: PortMapping[];
27
+ }
6
28
  export interface SessionEntry {
7
29
  readonly sessionId: string;
8
30
  readonly createdAt: Date;
31
+ readonly mode: SessionMode;
9
32
  readonly buffer: EventRingBuffer;
10
- readonly byteBuffer: ByteRingBuffer;
11
33
  readonly emitter: EventEmitter;
12
- readonly byteEmitter: EventEmitter;
34
+ /** One channel per application container, insertion-ordered. */
35
+ readonly apps: Map<string, AppChannel>;
13
36
  /** The live backend workload. Null until `start` resolves; the route writes
14
37
  * stdin / resize / stop through it. A backend's `writeStdin` is a no-op once
15
38
  * the workload has terminated, so callers need not null it on exit. */
@@ -18,16 +41,88 @@ export interface SessionEntry {
18
41
  exitedAt: Date | null;
19
42
  userStopped: boolean;
20
43
  evictionTimer: NodeJS.Timeout | null;
44
+ /** Catalog name of the co-resident agent, when one was requested. */
45
+ agent?: string;
46
+ /** Last whole-tree workspace snapshot, pulled on the checkpoint timer and
47
+ * again on suspend. A CACHE, never the only copy: the editor holds the
48
+ * authoritative workspace, so a runner restart losing this costs an upload,
49
+ * not user work. */
50
+ checkpoint: WorkspaceCheckpoint | null;
51
+ /** How the NEXT generation of an app should be attributed. Structural rather
52
+ * than the projection type itself, so the registry stays free of the debug
53
+ * stream it knows nothing about. */
54
+ attribution: RunAttribution | null;
55
+ /** The description this session was launched from, retained so `resume` can
56
+ * build a fresh pod under the same id without a second copy of it. */
57
+ launch: WorkloadLaunch | null;
58
+ /** Live SSE/WS subscriber count. Zero for longer than the idle window is what
59
+ * suspends a watch session. */
60
+ subscribers: number;
61
+ /** Epoch ms the subscriber count last fell to zero; null while someone is
62
+ * attached. */
63
+ idleSince: number | null;
64
+ /** Epoch ms of recent reloads, for the per-session reload rate limit. A watch
65
+ * session reloads on every save, so an unbounded one is a way to keep a pod
66
+ * permanently rebuilding. */
67
+ reloads: number[];
68
+ /** Epoch ms the last workspace checkpoint was pulled. */
69
+ checkpointedAt: number | null;
70
+ }
71
+ /**
72
+ * The run projection as the registry sees it — structural, so the registry stays
73
+ * free of the debug stream it knows nothing about. `expect` is how a route tells
74
+ * it about a generation it is ABOUT to cause: without that, a manual reload
75
+ * would be reported as a watch reload, and the runner would be guessing at the
76
+ * one fact it actually knows.
77
+ */
78
+ export interface RunAttribution {
79
+ expect(app: string, trigger: RunTrigger): void;
80
+ expectAll(trigger: RunTrigger): void;
81
+ frame(app: string, frame: DebugFrame): void;
82
+ /** Close the open generation because its workload ended — a container that
83
+ * goes away emits no `Kernel.Stopped`, so the backend reports the ending. */
84
+ endGeneration(app: string, outcome: {
85
+ code?: number;
86
+ reason?: string;
87
+ }): void;
88
+ }
89
+ /** A whole-tree snapshot plus the contents needed to re-seed a fresh pod. Whole
90
+ * tree rather than a delta log from the write path: a manifest workspace is
91
+ * small, and one shape is easier to reason about than a replay that has to be
92
+ * correct. */
93
+ export interface WorkspaceCheckpoint {
94
+ takenAt: Date;
95
+ files: WorkspaceCheckpointFile[];
21
96
  }
22
97
  export interface RegistryDeps {
23
98
  maxSessions: number;
24
99
  exitTtlMs: number;
100
+ /** Per APP, not per session — each application container has its own terminal
101
+ * and its own replay buffer. */
25
102
  replayBufferBytes: number;
103
+ /** How long a suspended session record is retained before eviction. Bounds
104
+ * accumulation, and is deliberately not the pod deadline: that bounds a POD,
105
+ * so on its own nothing would ever evict a suspended record. */
106
+ suspendedTtlMs?: number;
26
107
  }
27
108
  export declare class SessionLimitError extends Error {
28
109
  }
29
110
  export declare class SessionEvictedError extends Error {
30
111
  }
112
+ export declare class UnknownAppError extends Error {
113
+ }
114
+ export interface RegisterArgs {
115
+ sessionId: string;
116
+ mode?: SessionMode;
117
+ agent?: string;
118
+ /** The applications this session runs. Defaults to one app named `app` with a
119
+ * terminal, so a single-app session behaves exactly as before. */
120
+ apps?: Array<{
121
+ name: string;
122
+ io?: IoMode;
123
+ ports?: PortMapping[];
124
+ }>;
125
+ }
31
126
  export declare class SessionRegistry {
32
127
  private readonly deps;
33
128
  private readonly sessions;
@@ -40,18 +135,54 @@ export declare class SessionRegistry {
40
135
  * Creates a fresh registry entry. Callers are responsible for guarding against
41
136
  * duplicate insertion. Throws SessionLimitError if we're at capacity.
42
137
  */
43
- register(args: {
44
- sessionId: string;
45
- }): SessionEntry;
46
- pushBytes(sessionId: string, bytes: Buffer): BufferedBytes | undefined;
47
- subscribeBytes(sessionId: string, listener: (b: BufferedBytes) => void): () => void;
138
+ register(args: RegisterArgs): SessionEntry;
139
+ /** Add an application channel to an existing entry. Used at register time and
140
+ * when `PUT /v1/sessions/:id/apps` changes the running set. */
141
+ addApp(entry: SessionEntry, app: {
142
+ name: string;
143
+ io?: IoMode;
144
+ ports?: PortMapping[];
145
+ }): AppChannel;
146
+ /** The single app of a single-app session — what `/io` and `reload` fall back
147
+ * to when no `app` is given. Undefined when the session runs several, where
148
+ * there is no defensible default among many terminals. */
149
+ soleApp(entry: SessionEntry): AppChannel | undefined;
150
+ pushBytes(sessionId: string, appName: string, bytes: Buffer, stream?: ByteStreamTag): BufferedBytes | undefined;
151
+ subscribeBytes(sessionId: string, appName: string, listener: (b: BufferedBytes) => void): () => void;
48
152
  emit(sessionId: string, event: RunEvent): BufferedEvent | undefined;
153
+ /** Begin a generation for one app and emit its `run.started`. Returns the new
154
+ * generation number, or undefined when the app is unknown. */
155
+ startGeneration(sessionId: string, appName: string, trigger: "initial" | "watch" | "manual" | "resume"): number | undefined;
156
+ /** Close the current generation of one app. A completion leaves the SESSION
157
+ * status untouched — that is the whole point of the split. */
158
+ finishGeneration(sessionId: string, appName: string, outcome: {
159
+ phase: "completed";
160
+ code: number;
161
+ } | {
162
+ phase: "failed";
163
+ reason: string;
164
+ }): void;
49
165
  subscribe(sessionId: string, listener: (e: BufferedEvent) => void): () => void;
50
- /** Free a slot at capacity by removing the oldest already-terminated session
51
- * (by exit time). Retained exited sessions are history kept for re-attach, so
52
- * they yield to a new run rather than blocking it; live sessions are never
53
- * evicted. Returns false when every session is still live. */
166
+ /** Count a live client. Idleness no SSE/WS subscriber for the configured
167
+ * window is what suspends a watch session, and aggressive reaping is what
168
+ * makes per-visitor watch sessions affordable; they only work as a pair. */
169
+ addSubscriber(sessionId: string): () => void;
170
+ /**
171
+ * Free a slot at capacity by removing the oldest RECLAIMABLE session: a
172
+ * terminated one (history kept for re-attach, which yields to a new run) or a
173
+ * SUSPENDED one.
174
+ *
175
+ * A suspended session is reclaimable because losing it costs exactly what the
176
+ * design already says a runner restart costs — a checkpoint the editor
177
+ * re-seeds from its own copy. Excluding it would let a handful of visitors who
178
+ * each left after five minutes hold every slot for the whole suspended TTL,
179
+ * which with the shipped defaults is a day.
180
+ *
181
+ * Ordered by when the session stopped being live, so the least recently
182
+ * abandoned goes first. Returns false when every session is still live.
183
+ */
54
184
  private evictOldestTerminal;
185
+ private cancelEviction;
55
186
  private scheduleEviction;
56
187
  /**
57
188
  * Remove an entry immediately (used by shutdown sweeps and startup cleanup).
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/session/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC;IAEnC;;4EAEwE;IACxE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAaD,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAC/C,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAEjD,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;gBAE/B,IAAI,EAAE,YAAY;IAE/C,IAAI,IAAI,MAAM;IAId,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIhD,IAAI,IAAI,YAAY,EAAE;IAItB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY;IA6BnD,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAqBtE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAOnF,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,aAAa,GAAG,SAAS;IAenE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAO9E;;;mEAG+D;IAC/D,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,gBAAgB;IAUxB;;;OAGG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAOnC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/session/registry.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from "node:events";
2
- import { isTerminal } from "../contract.js";
2
+ import { DEFAULT_APP_NAME, isTerminal, } from "../contract.js";
3
3
  import { ByteRingBuffer } from "./byte-ring-buffer.js";
4
4
  import { EventRingBuffer } from "./ring-buffer.js";
5
5
  const EVENT_EMITTED = "event";
@@ -15,6 +15,8 @@ export class SessionLimitError extends Error {
15
15
  }
16
16
  export class SessionEvictedError extends Error {
17
17
  }
18
+ export class UnknownAppError extends Error {
19
+ }
18
20
  export class SessionRegistry {
19
21
  deps;
20
22
  sessions = new Map();
@@ -44,32 +46,64 @@ export class SessionRegistry {
44
46
  const entry = {
45
47
  sessionId: args.sessionId,
46
48
  createdAt: new Date(),
49
+ mode: args.mode ?? "run",
47
50
  buffer: new EventRingBuffer(this.deps.replayBufferBytes),
48
- byteBuffer: new ByteRingBuffer(this.deps.replayBufferBytes),
49
51
  emitter: new EventEmitter(),
50
- byteEmitter: new EventEmitter(),
52
+ apps: new Map(),
51
53
  session: null,
52
54
  status: { kind: "starting" },
53
55
  exitedAt: null,
54
56
  userStopped: false,
55
57
  evictionTimer: null,
58
+ agent: args.agent,
59
+ checkpoint: null,
60
+ attribution: null,
61
+ launch: null,
62
+ subscribers: 0,
63
+ idleSince: Date.now(),
64
+ reloads: [],
65
+ checkpointedAt: null,
56
66
  };
57
67
  // Many transient SSE / WS subscribers per session is normal — bump the
58
68
  // default 10-listener warning to a high cap so the alarm still fires
59
69
  // for a real listener leak. 256 is well above the realistic concurrent-
60
70
  // tab count and well below "obviously a bug".
61
71
  entry.emitter.setMaxListeners(256);
62
- entry.byteEmitter.setMaxListeners(256);
72
+ for (const app of args.apps ?? [{ name: DEFAULT_APP_NAME }]) {
73
+ this.addApp(entry, app);
74
+ }
63
75
  this.sessions.set(args.sessionId, entry);
64
76
  return entry;
65
77
  }
66
- pushBytes(sessionId, bytes) {
67
- const entry = this.sessions.get(sessionId);
68
- if (!entry)
78
+ /** Add an application channel to an existing entry. Used at register time and
79
+ * when `PUT /v1/sessions/:id/apps` changes the running set. */
80
+ addApp(entry, app) {
81
+ const channel = {
82
+ name: app.name,
83
+ io: app.io ?? "tty",
84
+ byteBuffer: new ByteRingBuffer(this.deps.replayBufferBytes),
85
+ byteEmitter: new EventEmitter(),
86
+ generation: 0,
87
+ startedAt: null,
88
+ ports: app.ports ?? [],
89
+ };
90
+ channel.byteEmitter.setMaxListeners(256);
91
+ entry.apps.set(app.name, channel);
92
+ return channel;
93
+ }
94
+ /** The single app of a single-app session — what `/io` and `reload` fall back
95
+ * to when no `app` is given. Undefined when the session runs several, where
96
+ * there is no defensible default among many terminals. */
97
+ soleApp(entry) {
98
+ return entry.apps.size === 1 ? entry.apps.values().next().value : undefined;
99
+ }
100
+ pushBytes(sessionId, appName, bytes, stream = "tty") {
101
+ const channel = this.sessions.get(sessionId)?.apps.get(appName);
102
+ if (!channel)
69
103
  return undefined;
70
104
  if (bytes.byteLength <= MAX_PUSH_CHUNK) {
71
- const buffered = entry.byteBuffer.push(bytes);
72
- entry.byteEmitter.emit(BYTES_EMITTED, buffered);
105
+ const buffered = channel.byteBuffer.push(bytes, stream);
106
+ channel.byteEmitter.emit(BYTES_EMITTED, buffered);
73
107
  return buffered;
74
108
  }
75
109
  // Split the chunk into MAX_PUSH_CHUNK-sized slices, each getting its
@@ -79,17 +113,21 @@ export class SessionRegistry {
79
113
  const slice = bytes.subarray(off, Math.min(off + MAX_PUSH_CHUNK, bytes.byteLength));
80
114
  // subarray shares memory with the parent buffer; copy so the ring's
81
115
  // entry doesn't pin the original allocation past eviction.
82
- last = entry.byteBuffer.push(Buffer.from(slice));
83
- entry.byteEmitter.emit(BYTES_EMITTED, last);
116
+ last = channel.byteBuffer.push(Buffer.from(slice), stream);
117
+ channel.byteEmitter.emit(BYTES_EMITTED, last);
84
118
  }
85
119
  return last;
86
120
  }
87
- subscribeBytes(sessionId, listener) {
121
+ subscribeBytes(sessionId, appName, listener) {
88
122
  const entry = this.sessions.get(sessionId);
89
123
  if (!entry)
90
124
  throw new SessionEvictedError(`session '${sessionId}' not in registry`);
91
- entry.byteEmitter.on(BYTES_EMITTED, listener);
92
- return () => entry.byteEmitter.off(BYTES_EMITTED, listener);
125
+ const channel = entry.apps.get(appName);
126
+ if (!channel) {
127
+ throw new UnknownAppError(`session '${sessionId}' runs no app named '${appName}'`);
128
+ }
129
+ channel.byteEmitter.on(BYTES_EMITTED, listener);
130
+ return () => channel.byteEmitter.off(BYTES_EMITTED, listener);
93
131
  }
94
132
  emit(sessionId, event) {
95
133
  const entry = this.sessions.get(sessionId);
@@ -100,12 +138,65 @@ export class SessionRegistry {
100
138
  entry.status = event.status;
101
139
  if (isTerminal(event.status)) {
102
140
  entry.exitedAt = new Date();
103
- this.scheduleEviction(entry);
141
+ this.scheduleEviction(entry, this.deps.exitTtlMs);
142
+ }
143
+ else if (event.status.kind === "suspended") {
144
+ // Not terminal — the record and its checkpoint outlive the pod — but it
145
+ // still needs its own ceiling, or suspended records accumulate forever.
146
+ this.scheduleEviction(entry, this.deps.suspendedTtlMs);
147
+ }
148
+ else {
149
+ this.cancelEviction(entry);
104
150
  }
105
151
  }
106
152
  entry.emitter.emit(EVENT_EMITTED, buffered);
107
153
  return buffered;
108
154
  }
155
+ /** Begin a generation for one app and emit its `run.started`. Returns the new
156
+ * generation number, or undefined when the app is unknown. */
157
+ startGeneration(sessionId, appName, trigger) {
158
+ const channel = this.sessions.get(sessionId)?.apps.get(appName);
159
+ if (!channel)
160
+ return undefined;
161
+ channel.generation += 1;
162
+ channel.startedAt = Date.now();
163
+ this.emit(sessionId, {
164
+ type: "run",
165
+ app: appName,
166
+ generation: channel.generation,
167
+ phase: "started",
168
+ trigger,
169
+ });
170
+ return channel.generation;
171
+ }
172
+ /** Close the current generation of one app. A completion leaves the SESSION
173
+ * status untouched — that is the whole point of the split. */
174
+ finishGeneration(sessionId, appName, outcome) {
175
+ const channel = this.sessions.get(sessionId)?.apps.get(appName);
176
+ if (!channel || channel.generation === 0)
177
+ return;
178
+ const durationMs = channel.startedAt === null ? undefined : Date.now() - channel.startedAt;
179
+ channel.startedAt = null;
180
+ if (outcome.phase === "completed") {
181
+ this.emit(sessionId, {
182
+ type: "run",
183
+ app: appName,
184
+ generation: channel.generation,
185
+ phase: "completed",
186
+ code: outcome.code,
187
+ durationMs,
188
+ });
189
+ }
190
+ else {
191
+ this.emit(sessionId, {
192
+ type: "run",
193
+ app: appName,
194
+ generation: channel.generation,
195
+ phase: "failed",
196
+ reason: outcome.reason,
197
+ });
198
+ }
199
+ }
109
200
  subscribe(sessionId, listener) {
110
201
  const entry = this.sessions.get(sessionId);
111
202
  if (!entry)
@@ -113,28 +204,68 @@ export class SessionRegistry {
113
204
  entry.emitter.on(EVENT_EMITTED, listener);
114
205
  return () => entry.emitter.off(EVENT_EMITTED, listener);
115
206
  }
116
- /** Free a slot at capacity by removing the oldest already-terminated session
117
- * (by exit time). Retained exited sessions are history kept for re-attach, so
118
- * they yield to a new run rather than blocking it; live sessions are never
119
- * evicted. Returns false when every session is still live. */
207
+ /** Count a live client. Idleness no SSE/WS subscriber for the configured
208
+ * window is what suspends a watch session, and aggressive reaping is what
209
+ * makes per-visitor watch sessions affordable; they only work as a pair. */
210
+ addSubscriber(sessionId) {
211
+ const entry = this.sessions.get(sessionId);
212
+ if (!entry)
213
+ return () => { };
214
+ entry.subscribers += 1;
215
+ entry.idleSince = null;
216
+ let released = false;
217
+ return () => {
218
+ if (released)
219
+ return;
220
+ released = true;
221
+ entry.subscribers = Math.max(0, entry.subscribers - 1);
222
+ if (entry.subscribers === 0)
223
+ entry.idleSince = Date.now();
224
+ };
225
+ }
226
+ /**
227
+ * Free a slot at capacity by removing the oldest RECLAIMABLE session: a
228
+ * terminated one (history kept for re-attach, which yields to a new run) or a
229
+ * SUSPENDED one.
230
+ *
231
+ * A suspended session is reclaimable because losing it costs exactly what the
232
+ * design already says a runner restart costs — a checkpoint the editor
233
+ * re-seeds from its own copy. Excluding it would let a handful of visitors who
234
+ * each left after five minutes hold every slot for the whole suspended TTL,
235
+ * which with the shipped defaults is a day.
236
+ *
237
+ * Ordered by when the session stopped being live, so the least recently
238
+ * abandoned goes first. Returns false when every session is still live.
239
+ */
120
240
  evictOldestTerminal() {
121
241
  let oldest;
242
+ let oldestAt = Number.POSITIVE_INFINITY;
122
243
  for (const entry of this.sessions.values()) {
123
- if (entry.exitedAt === null)
244
+ const since = reclaimableSince(entry);
245
+ if (since === null || since >= oldestAt)
124
246
  continue;
125
- if (!oldest || entry.exitedAt < oldest.exitedAt)
126
- oldest = entry;
247
+ oldest = entry;
248
+ oldestAt = since;
127
249
  }
128
250
  if (!oldest)
129
251
  return false;
130
252
  return this.remove(oldest.sessionId);
131
253
  }
132
- scheduleEviction(entry) {
133
- if (entry.evictionTimer)
254
+ cancelEviction(entry) {
255
+ if (!entry.evictionTimer)
134
256
  return;
257
+ clearTimeout(entry.evictionTimer);
258
+ entry.evictionTimer = null;
259
+ }
260
+ scheduleEviction(entry, ttlMs) {
261
+ if (ttlMs === undefined)
262
+ return;
263
+ // A resume re-arms from `running`, so an already-armed timer is replaced
264
+ // rather than kept — the surviving one would evict a live session.
265
+ this.cancelEviction(entry);
135
266
  entry.evictionTimer = setTimeout(() => {
136
267
  this.sessions.delete(entry.sessionId);
137
- }, this.deps.exitTtlMs);
268
+ }, ttlMs);
138
269
  // Allow process exit even if evictions are pending — they are pure state,
139
270
  // not work.
140
271
  entry.evictionTimer.unref?.();
@@ -153,4 +284,14 @@ export class SessionRegistry {
153
284
  return true;
154
285
  }
155
286
  }
287
+ /** When a session stopped being live, or null while it still is. A terminated
288
+ * session is reclaimable from its exit; a suspended one from when it went idle
289
+ * (it has no pod and its workspace is a checkpoint the editor can re-seed). */
290
+ function reclaimableSince(entry) {
291
+ if (entry.exitedAt !== null)
292
+ return entry.exitedAt.getTime();
293
+ if (entry.status.kind === "suspended")
294
+ return entry.idleSince ?? 0;
295
+ return null;
296
+ }
156
297
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/session/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/session/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { SessionRegistry } from "./registry.js";
2
+ export interface WatchSupervisorDeps {
3
+ registry: SessionRegistry;
4
+ /** No SSE/WS subscriber for this long → suspend. */
5
+ idleMs: number;
6
+ /** How often to pull a whole-tree workspace snapshot. */
7
+ checkpointMs: number;
8
+ log?: {
9
+ warn(obj: unknown, msg: string): void;
10
+ };
11
+ }
12
+ /**
13
+ * The two background jobs that make a watch session affordable, and they only
14
+ * work as a pair: a periodic workspace checkpoint, and an idle reap that deletes
15
+ * the pod while keeping the session record and that checkpoint.
16
+ *
17
+ * Aggressive reaping is what makes per-visitor watch sessions affordable, and
18
+ * the checkpoint is what makes aggressive reaping safe to do.
19
+ *
20
+ * The checkpoint is a CACHE, never the only copy. The runner is a single replica
21
+ * with an in-memory registry, so a redeploy or crash drops every suspended
22
+ * session — the editor holds the authoritative workspace, already diffs its own
23
+ * files against `GET /workspace`, and on a `404` at resume creates a new session
24
+ * and re-seeds from its own copy in one change set. What the checkpoint saves is
25
+ * that upload.
26
+ */
27
+ export declare class WatchSupervisor {
28
+ private readonly deps;
29
+ private timer;
30
+ /** Sessions with a checkpoint or suspend in flight, so a slow snapshot cannot
31
+ * have a second one started on top of it every tick. */
32
+ private readonly busy;
33
+ constructor(deps: WatchSupervisorDeps);
34
+ start(): void;
35
+ stop(): void;
36
+ /** Exposed for tests, which drive the clock rather than waiting on it. */
37
+ tick(): Promise<void>;
38
+ private run;
39
+ private checkpoint;
40
+ private suspend;
41
+ }
42
+ //# sourceMappingURL=watch-supervisor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-supervisor.d.ts","sourceRoot":"","sources":["../../src/session/watch-supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,eAAe,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,eAAe,CAAC;IAC1B,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACjD;AAOD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAe;IAMd,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,KAAK,CAA+B;IAC5C;6DACyD;IACzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAEb,IAAI,EAAE,mBAAmB;IAEtD,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAMZ,0EAA0E;IACpE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAuBb,GAAG;YAiBH,UAAU;YAQV,OAAO;CAWtB"}