@mono-agent/session-web 0.4.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 (120) hide show
  1. package/README.md +92 -0
  2. package/dist/aggregator.d.ts +123 -0
  3. package/dist/aggregator.d.ts.map +1 -0
  4. package/dist/aggregator.js +1155 -0
  5. package/dist/aggregator.js.map +1 -0
  6. package/dist/discovery.d.ts +48 -0
  7. package/dist/discovery.d.ts.map +1 -0
  8. package/dist/discovery.js +177 -0
  9. package/dist/discovery.js.map +1 -0
  10. package/dist/history.d.ts +47 -0
  11. package/dist/history.d.ts.map +1 -0
  12. package/dist/history.js +149 -0
  13. package/dist/history.js.map +1 -0
  14. package/dist/index.d.ts +17 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +13 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/live-client.d.ts +50 -0
  19. package/dist/live-client.d.ts.map +1 -0
  20. package/dist/live-client.js +159 -0
  21. package/dist/live-client.js.map +1 -0
  22. package/dist/server.d.ts +30 -0
  23. package/dist/server.d.ts.map +1 -0
  24. package/dist/server.js +359 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/session-model.d.ts +54 -0
  27. package/dist/session-model.d.ts.map +1 -0
  28. package/dist/session-model.js +10 -0
  29. package/dist/session-model.js.map +1 -0
  30. package/package.json +39 -0
  31. package/webapp/dist/apple-touch-icon.png +0 -0
  32. package/webapp/dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  33. package/webapp/dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  34. package/webapp/dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  35. package/webapp/dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  36. package/webapp/dist/assets/ibm-plex-mono-cyrillic-600-normal-CTOM6hUh.woff2 +0 -0
  37. package/webapp/dist/assets/ibm-plex-mono-cyrillic-600-normal-fLZuRloM.woff +0 -0
  38. package/webapp/dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  39. package/webapp/dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  40. package/webapp/dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  41. package/webapp/dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  42. package/webapp/dist/assets/ibm-plex-mono-cyrillic-ext-600-normal-9HEixskS.woff +0 -0
  43. package/webapp/dist/assets/ibm-plex-mono-cyrillic-ext-600-normal-V-xxqcpd.woff2 +0 -0
  44. package/webapp/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  45. package/webapp/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  46. package/webapp/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  47. package/webapp/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  48. package/webapp/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  49. package/webapp/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  50. package/webapp/dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  51. package/webapp/dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  52. package/webapp/dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  53. package/webapp/dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  54. package/webapp/dist/assets/ibm-plex-mono-latin-ext-600-normal-D38SheWl.woff2 +0 -0
  55. package/webapp/dist/assets/ibm-plex-mono-latin-ext-600-normal-DmB0ttJJ.woff +0 -0
  56. package/webapp/dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  57. package/webapp/dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  58. package/webapp/dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  59. package/webapp/dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  60. package/webapp/dist/assets/ibm-plex-mono-vietnamese-600-normal-D2EvbN8M.woff2 +0 -0
  61. package/webapp/dist/assets/ibm-plex-mono-vietnamese-600-normal-iLQfcSjf.woff +0 -0
  62. package/webapp/dist/assets/index-ByXpmZqx.js +138 -0
  63. package/webapp/dist/assets/index-D-V-lq0B.css +1 -0
  64. package/webapp/dist/assets/newsreader-latin-400-italic-CEihAR-f.woff2 +0 -0
  65. package/webapp/dist/assets/newsreader-latin-400-italic-CNZoH1hn.woff +0 -0
  66. package/webapp/dist/assets/newsreader-latin-400-normal-BFBkh4jY.woff2 +0 -0
  67. package/webapp/dist/assets/newsreader-latin-400-normal-gRTjlS2D.woff +0 -0
  68. package/webapp/dist/assets/newsreader-latin-500-italic-Bsgye1qA.woff2 +0 -0
  69. package/webapp/dist/assets/newsreader-latin-500-italic-m-xI0GeS.woff +0 -0
  70. package/webapp/dist/assets/newsreader-latin-500-normal-B66TYsaK.woff2 +0 -0
  71. package/webapp/dist/assets/newsreader-latin-500-normal-DFwuUcdu.woff +0 -0
  72. package/webapp/dist/assets/newsreader-latin-ext-400-italic-BMTE_bNQ.woff2 +0 -0
  73. package/webapp/dist/assets/newsreader-latin-ext-400-italic-qdgKLcPG.woff +0 -0
  74. package/webapp/dist/assets/newsreader-latin-ext-400-normal-DYA1XoQK.woff +0 -0
  75. package/webapp/dist/assets/newsreader-latin-ext-400-normal-svq1FPys.woff2 +0 -0
  76. package/webapp/dist/assets/newsreader-latin-ext-500-italic-B83LgwQp.woff2 +0 -0
  77. package/webapp/dist/assets/newsreader-latin-ext-500-italic-Bz3NsAdg.woff +0 -0
  78. package/webapp/dist/assets/newsreader-latin-ext-500-normal-BNHmvKvI.woff2 +0 -0
  79. package/webapp/dist/assets/newsreader-latin-ext-500-normal-CZruMFou.woff +0 -0
  80. package/webapp/dist/assets/newsreader-vietnamese-400-italic-QbB8kb5s.woff +0 -0
  81. package/webapp/dist/assets/newsreader-vietnamese-400-italic-bZegYFuM.woff2 +0 -0
  82. package/webapp/dist/assets/newsreader-vietnamese-400-normal-BekUZro8.woff +0 -0
  83. package/webapp/dist/assets/newsreader-vietnamese-400-normal-DdKr49mV.woff2 +0 -0
  84. package/webapp/dist/assets/newsreader-vietnamese-500-italic-BsTGSz8L.woff +0 -0
  85. package/webapp/dist/assets/newsreader-vietnamese-500-italic-D26NjlyL.woff2 +0 -0
  86. package/webapp/dist/assets/newsreader-vietnamese-500-normal-BEAbKU8A.woff +0 -0
  87. package/webapp/dist/assets/newsreader-vietnamese-500-normal-CL6a8tp2.woff2 +0 -0
  88. package/webapp/dist/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
  89. package/webapp/dist/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
  90. package/webapp/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  91. package/webapp/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  92. package/webapp/dist/assets/space-grotesk-latin-600-normal-BflQw4A9.woff +0 -0
  93. package/webapp/dist/assets/space-grotesk-latin-600-normal-DjKNqYRj.woff2 +0 -0
  94. package/webapp/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
  95. package/webapp/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
  96. package/webapp/dist/assets/space-grotesk-latin-ext-400-normal-CfP_5XZW.woff2 +0 -0
  97. package/webapp/dist/assets/space-grotesk-latin-ext-400-normal-DRPE3kg4.woff +0 -0
  98. package/webapp/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
  99. package/webapp/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
  100. package/webapp/dist/assets/space-grotesk-latin-ext-600-normal-DxxdqCpr.woff2 +0 -0
  101. package/webapp/dist/assets/space-grotesk-latin-ext-600-normal-VcznFIpX.woff +0 -0
  102. package/webapp/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
  103. package/webapp/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
  104. package/webapp/dist/assets/space-grotesk-vietnamese-400-normal-B7xT_GF5.woff2 +0 -0
  105. package/webapp/dist/assets/space-grotesk-vietnamese-400-normal-BIWiOVfw.woff +0 -0
  106. package/webapp/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
  107. package/webapp/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
  108. package/webapp/dist/assets/space-grotesk-vietnamese-600-normal-D6zpsUhD.woff +0 -0
  109. package/webapp/dist/assets/space-grotesk-vietnamese-600-normal-DUi7WF5p.woff2 +0 -0
  110. package/webapp/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
  111. package/webapp/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
  112. package/webapp/dist/favicon.ico +0 -0
  113. package/webapp/dist/icon-192.png +0 -0
  114. package/webapp/dist/icon-512.png +0 -0
  115. package/webapp/dist/icon.svg +31 -0
  116. package/webapp/dist/index.html +25 -0
  117. package/webapp/dist/manifest.webmanifest +1 -0
  118. package/webapp/dist/registerSW.js +1 -0
  119. package/webapp/dist/sw.js +1 -0
  120. package/webapp/dist/workbox-9c191d2f.js +1 -0
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A read-only client for a running agent's operator-adapter live SSE endpoint
3
+ * (`GET <baseUrl>/v1/events`). Modeled on the TUI's `RemoteAgentResponder` reader
4
+ * loop (`packages/tui/src/remote/client.ts`): `fetch` with a streaming body,
5
+ * consumed via `res.body.getReader()` + `TextDecoder`, split into SSE frames on
6
+ * the blank-line separator. `data:` payloads are parsed as {@link RunEventFrame};
7
+ * `: ping` heartbeat comments are ignored. A dropped connection triggers bounded
8
+ * exponential-backoff reconnection until `close()`.
9
+ *
10
+ * HTTP only — this is how the `operator-surface` package reaches a `communication`
11
+ * adapter without importing it.
12
+ */
13
+ import { type RunEventFrame } from "@mono-agent/agent-contracts";
14
+ export type LiveStreamStatus = "connected" | "disconnected";
15
+ export interface LiveStreamLogger {
16
+ warn?(message: string, metadata?: unknown): void;
17
+ error?(message: string, metadata?: unknown): void;
18
+ }
19
+ export interface ConnectLiveStreamOptions {
20
+ /** The agent's `live` SSE base URL, e.g. `http://127.0.0.1:52789/live`. `/v1/events` hangs off it. */
21
+ readonly baseUrl: string;
22
+ readonly apiKey?: string;
23
+ /** Called for each parsed frame, in arrival order. */
24
+ readonly onFrame: (frame: RunEventFrame) => void;
25
+ /** Called on every connect ("connected") and drop ("disconnected"). */
26
+ readonly onStatus: (status: LiveStreamStatus) => void;
27
+ readonly logger?: LiveStreamLogger;
28
+ readonly fetchImpl?: typeof fetch;
29
+ /** Reconnect backoff floor (ms). Default 500. */
30
+ readonly baseDelayMs?: number;
31
+ /** Reconnect backoff ceiling (ms). Default 10_000. */
32
+ readonly maxDelayMs?: number;
33
+ /**
34
+ * Max consecutive failed reconnect attempts before giving up. Default: unbounded
35
+ * — a live viewer should reconnect for as long as the instance is discovered;
36
+ * the aggregator closes this connection when the instance vanishes. Tests set a
37
+ * small value to bound the loop.
38
+ */
39
+ readonly maxRetries?: number;
40
+ }
41
+ export interface LiveStreamConnection {
42
+ /** Idempotent: abort the in-flight request and stop reconnecting. */
43
+ close(): void;
44
+ }
45
+ /**
46
+ * Open a live SSE stream and keep it open (reconnecting on drop) until `close()`.
47
+ * Returns synchronously; the connection is driven by a detached async loop.
48
+ */
49
+ export declare function connectLiveStream(options: ConnectLiveStreamOptions): LiveStreamConnection;
50
+ //# sourceMappingURL=live-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-client.d.ts","sourceRoot":"","sources":["../src/live-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAqC,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEpG,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACjD,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,wBAAwB;IACvC,sGAAsG;IACtG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,KAAK,IAAI,IAAI,CAAC;CACf;AAKD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,oBAAoB,CAkEzF"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * A read-only client for a running agent's operator-adapter live SSE endpoint
3
+ * (`GET <baseUrl>/v1/events`). Modeled on the TUI's `RemoteAgentResponder` reader
4
+ * loop (`packages/tui/src/remote/client.ts`): `fetch` with a streaming body,
5
+ * consumed via `res.body.getReader()` + `TextDecoder`, split into SSE frames on
6
+ * the blank-line separator. `data:` payloads are parsed as {@link RunEventFrame};
7
+ * `: ping` heartbeat comments are ignored. A dropped connection triggers bounded
8
+ * exponential-backoff reconnection until `close()`.
9
+ *
10
+ * HTTP only — this is how the `operator-surface` package reaches a `communication`
11
+ * adapter without importing it.
12
+ */
13
+ import { DEFAULT_RUN_EVENT_MAX_FRAME_BYTES } from "@mono-agent/agent-contracts";
14
+ const SSE_FRAME_SEPARATOR = "\n\n";
15
+ const SSE_ENCODER = new TextEncoder();
16
+ /**
17
+ * Open a live SSE stream and keep it open (reconnecting on drop) until `close()`.
18
+ * Returns synchronously; the connection is driven by a detached async loop.
19
+ */
20
+ export function connectLiveStream(options) {
21
+ const baseUrl = options.baseUrl.replace(/\/+$/u, "");
22
+ const url = `${baseUrl}/v1/events`;
23
+ const fetchImpl = options.fetchImpl ?? fetch;
24
+ const baseDelayMs = options.baseDelayMs ?? 500;
25
+ const maxDelayMs = options.maxDelayMs ?? 10_000;
26
+ const maxRetries = options.maxRetries;
27
+ const headers = options.apiKey === undefined ? {} : { authorization: `Bearer ${options.apiKey}` };
28
+ let closed = false;
29
+ let controller;
30
+ let wakeDelay;
31
+ const stop = () => {
32
+ if (closed) {
33
+ return;
34
+ }
35
+ closed = true;
36
+ controller?.abort();
37
+ wakeDelay?.();
38
+ };
39
+ const loop = async () => {
40
+ let attempt = 0;
41
+ while (!closed) {
42
+ controller = new AbortController();
43
+ let connected = false;
44
+ try {
45
+ const response = await fetchImpl(url, { headers, signal: controller.signal });
46
+ if (!response.ok || response.body === null) {
47
+ throw new Error(`live endpoint returned ${response.status}${response.body === null ? " (empty body)" : ""}.`);
48
+ }
49
+ connected = true;
50
+ attempt = 0;
51
+ options.onStatus("connected");
52
+ await readFrames(response.body, options.onFrame, options.logger);
53
+ // A clean end means the server closed the stream — fall through to reconnect.
54
+ }
55
+ catch (error) {
56
+ if (!closed) {
57
+ options.logger?.warn?.("Live stream connection dropped.", {
58
+ baseUrl,
59
+ error: error instanceof Error ? error.message : String(error),
60
+ });
61
+ }
62
+ }
63
+ if (connected) {
64
+ options.onStatus("disconnected");
65
+ }
66
+ if (closed) {
67
+ break;
68
+ }
69
+ attempt += 1;
70
+ if (maxRetries !== undefined && attempt > maxRetries) {
71
+ options.logger?.error?.("Live stream giving up after exhausting reconnect attempts.", { baseUrl, attempt });
72
+ break;
73
+ }
74
+ await sleep(backoffDelay(attempt, baseDelayMs, maxDelayMs), (wake) => {
75
+ wakeDelay = wake;
76
+ });
77
+ wakeDelay = undefined;
78
+ }
79
+ };
80
+ void loop();
81
+ return { close: stop };
82
+ }
83
+ /**
84
+ * Drain a streaming SSE body, invoking `onFrame` for each `data:` frame. Splits on
85
+ * the SSE blank-line separator; within a frame, concatenates `data:` lines (per
86
+ * the SSE spec) and ignores comment lines (`:` — the heartbeat). A `data:` payload
87
+ * that fails to parse as JSON is logged and skipped rather than tearing the stream.
88
+ */
89
+ async function readFrames(body, onFrame, logger) {
90
+ const reader = body.getReader();
91
+ const decoder = new TextDecoder();
92
+ let buffered = "";
93
+ try {
94
+ for (;;) {
95
+ const { done, value } = await reader.read();
96
+ buffered += done ? decoder.decode() : decoder.decode(value, { stream: true });
97
+ let separator = buffered.indexOf(SSE_FRAME_SEPARATOR);
98
+ while (separator !== -1) {
99
+ const block = buffered.slice(0, separator);
100
+ buffered = buffered.slice(separator + SSE_FRAME_SEPARATOR.length);
101
+ emitBlock(block, onFrame, logger);
102
+ separator = buffered.indexOf(SSE_FRAME_SEPARATOR);
103
+ }
104
+ if (!done && SSE_ENCODER.encode(buffered).length > DEFAULT_RUN_EVENT_MAX_FRAME_BYTES) {
105
+ logger?.warn?.("Discarding oversized live SSE buffer.", { maxFrameBytes: DEFAULT_RUN_EVENT_MAX_FRAME_BYTES });
106
+ buffered = "";
107
+ }
108
+ if (done) {
109
+ break;
110
+ }
111
+ }
112
+ }
113
+ finally {
114
+ await reader.cancel().catch(() => undefined);
115
+ }
116
+ }
117
+ function emitBlock(block, onFrame, logger) {
118
+ const dataParts = [];
119
+ for (const rawLine of block.split("\n")) {
120
+ const line = rawLine.replace(/\r$/u, "");
121
+ if (line.startsWith("data:")) {
122
+ dataParts.push(line.slice("data:".length).replace(/^ /u, ""));
123
+ }
124
+ // Comment lines (": ping") and field lines we don't consume are ignored.
125
+ }
126
+ if (dataParts.length === 0) {
127
+ return;
128
+ }
129
+ const payload = dataParts.join("\n");
130
+ if (SSE_ENCODER.encode(payload).length > DEFAULT_RUN_EVENT_MAX_FRAME_BYTES) {
131
+ logger?.warn?.("Discarding oversized live SSE frame.", { maxFrameBytes: DEFAULT_RUN_EVENT_MAX_FRAME_BYTES });
132
+ return;
133
+ }
134
+ try {
135
+ onFrame(JSON.parse(payload));
136
+ }
137
+ catch (error) {
138
+ logger?.warn?.("Discarding unparseable live SSE frame.", {
139
+ error: error instanceof Error ? error.message : String(error),
140
+ });
141
+ }
142
+ }
143
+ /** Exponential backoff with full jitter, capped at `maxDelayMs`. */
144
+ function backoffDelay(attempt, baseDelayMs, maxDelayMs) {
145
+ const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));
146
+ return Math.round(Math.random() * exponential);
147
+ }
148
+ /** A cancelable delay: resolves after `ms`, or immediately once `register`ed waker fires. */
149
+ function sleep(ms, register) {
150
+ return new Promise((resolvePromise) => {
151
+ const timer = setTimeout(() => resolvePromise(), ms);
152
+ timer.unref?.();
153
+ register(() => {
154
+ clearTimeout(timer);
155
+ resolvePromise();
156
+ });
157
+ });
158
+ }
159
+ //# sourceMappingURL=live-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-client.js","sourceRoot":"","sources":["../src/live-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export interface StartSessionWebServerOptions {
2
+ readonly registryDirs: readonly string[];
3
+ readonly host?: string;
4
+ readonly port?: number;
5
+ readonly allowNonLoopback?: boolean;
6
+ readonly staleAfterMs?: number;
7
+ readonly maxRunsPerInstance?: number;
8
+ readonly env?: Record<string, string | undefined>;
9
+ readonly staticDir?: string;
10
+ /**
11
+ * Bearer token required for /api/* when present. Mandatory for non-loopback
12
+ * binds so the read-only operator API is not published unauthenticated.
13
+ */
14
+ readonly authToken?: string;
15
+ readonly logger?: {
16
+ info?(message: string, metadata?: unknown): void;
17
+ warn?(message: string, metadata?: unknown): void;
18
+ error?(message: string, metadata?: unknown): void;
19
+ };
20
+ /** Include memory-maintenance runs in history, API responses, and live frames. Default false. */
21
+ readonly includeMemory?: boolean;
22
+ /** Injectable clock for deterministic stale-running projection. Defaults to Date.now. */
23
+ readonly clock?: () => number;
24
+ }
25
+ export interface SessionWebServerHandle {
26
+ readonly url: string;
27
+ stop(): Promise<void>;
28
+ }
29
+ export declare function startSessionWebServer(options: StartSessionWebServerOptions): Promise<SessionWebServerHandle>;
30
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QACjD,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QACjD,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;KACnD,CAAC;IACF,iGAAiG;IACjG,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,yFAAyF;IACzF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAYD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAoIjC"}
package/dist/server.js ADDED
@@ -0,0 +1,359 @@
1
+ /**
2
+ * The single HTTP backend the browser PWA talks to. Serves the built SPA
3
+ * (`express.static`), a read-only JSON API, and a browser SSE stream — all backed
4
+ * by one {@link SessionAggregator}. Binds loopback-only unless `allowNonLoopback`,
5
+ * via the shared `assertSafeBind`/`listen`/`close` host-safety helpers.
6
+ */
7
+ import { createServer } from "node:http";
8
+ import { dirname, join, resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import { assertSafeBind, bearerTokensEqual, close, hostForUrl, isLoopbackHost, listen, normalizeOptionalString, readAuthorizationBearer, } from "@mono-agent/agent-contracts";
11
+ import express, {} from "express";
12
+ import { SessionAggregator } from "./aggregator.js";
13
+ const DEFAULT_HOST = "127.0.0.1";
14
+ const DEFAULT_PORT = 4599;
15
+ const DEFAULT_MAX_RUNS_PER_INSTANCE = 200;
16
+ /** Browser SSE heartbeat interval — a comment frame that keeps the connection warm through proxies. */
17
+ const HEARTBEAT_INTERVAL_MS = 15_000;
18
+ const MAX_BROWSER_SSE_QUEUE_FRAMES = 10_000;
19
+ const MAX_BROWSER_SSE_FRAME_BYTES = 1_000_000;
20
+ const BROWSER_FRAME_ENCODER = new TextEncoder();
21
+ class SessionWebServerError extends Error {
22
+ code;
23
+ constructor(code, message) {
24
+ super(message);
25
+ this.name = "SessionWebServerError";
26
+ this.code = code;
27
+ }
28
+ }
29
+ export async function startSessionWebServer(options) {
30
+ const host = options.host ?? DEFAULT_HOST;
31
+ const port = options.port ?? DEFAULT_PORT;
32
+ const maxRunsPerInstance = options.maxRunsPerInstance ?? DEFAULT_MAX_RUNS_PER_INSTANCE;
33
+ const staticDir = options.staticDir ?? defaultStaticDir();
34
+ const logger = options.logger;
35
+ const authToken = normalizeOptionalString(options.authToken);
36
+ assertSafeBind(host, options.allowNonLoopback === true, (boundHost) => new SessionWebServerError("unsafe_host", `Session web server refuses to bind a non-loopback host (${boundHost}) unless allowNonLoopback is true.`));
37
+ if (!isLoopbackHost(host) && authToken === undefined) {
38
+ throw new SessionWebServerError("missing_auth_token", "Session web server requires an auth token when binding a non-loopback host.");
39
+ }
40
+ const aggregator = new SessionAggregator({
41
+ registryDirs: options.registryDirs,
42
+ maxRunsPerInstance,
43
+ ...(options.staleAfterMs === undefined ? {} : { staleAfterMs: options.staleAfterMs }),
44
+ ...(options.env === undefined ? {} : { env: options.env }),
45
+ ...(logger === undefined ? {} : { logger: logger }),
46
+ ...(options.includeMemory === undefined ? {} : { includeMemory: options.includeMemory }),
47
+ ...(options.clock === undefined ? {} : { clock: options.clock }),
48
+ });
49
+ await aggregator.start();
50
+ const app = express();
51
+ const server = createServer(app);
52
+ const activeStreams = new Set();
53
+ if (authToken !== undefined) {
54
+ app.use("/api", requireApiAuth(authToken));
55
+ }
56
+ app.get("/api/instances", (_req, res) => {
57
+ res.json({ instances: aggregator.getInstances() });
58
+ });
59
+ app.get("/api/sessions", (req, res, next) => {
60
+ const instance = firstString(req.query.instance) ?? "all";
61
+ const offset = parseOffset(firstString(req.query.offset));
62
+ const limit = parseLimit(firstString(req.query.limit));
63
+ if (offset > 0 || limit !== undefined) {
64
+ const pageLimit = limit ?? maxRunsPerInstance;
65
+ aggregator
66
+ .getSessionSummariesPage(instance, { offset, limit: pageLimit })
67
+ .then((page) => {
68
+ res.json(page);
69
+ })
70
+ .catch((error) => next(error));
71
+ return;
72
+ }
73
+ const sessions = aggregator.getSessionSummaries(instance);
74
+ res.json({ sessions, total: sessions.length, offset: 0, limit: sessions.length, hasMore: false });
75
+ });
76
+ app.get("/api/sessions/:sourceId/:runId", (req, res, next) => {
77
+ const sourceId = req.params.sourceId;
78
+ const runId = req.params.runId;
79
+ aggregator
80
+ .getSession(sourceId, runId)
81
+ .then((session) => {
82
+ if (session === undefined) {
83
+ res.status(404).json({ error: { message: "Session not found.", code: "not_found" } });
84
+ return;
85
+ }
86
+ res.json({ session });
87
+ })
88
+ .catch((error) => next(error));
89
+ });
90
+ app.get("/api/stream", (_req, res) => {
91
+ handleStream(aggregator, res, activeStreams, logger);
92
+ });
93
+ // Any other /api/* path is a genuine 404 (never falls through to the SPA).
94
+ app.use("/api", (_req, res) => {
95
+ res.status(404).json({ error: { message: "Not found.", code: "not_found" } });
96
+ });
97
+ // Static SPA assets, then a catch-all that returns index.html for client-side
98
+ // routes (unknown non-/api GETs). Any file that exists is served by static; the
99
+ // fallback only fires for paths static didn't resolve.
100
+ app.use(express.static(staticDir));
101
+ app.use((req, res, next) => {
102
+ if (req.method !== "GET") {
103
+ next();
104
+ return;
105
+ }
106
+ res.sendFile(join(staticDir, "index.html"), (error) => {
107
+ if (error !== undefined && error !== null) {
108
+ next();
109
+ }
110
+ });
111
+ });
112
+ app.use((error, _req, res, next) => {
113
+ if (res.headersSent) {
114
+ next(error);
115
+ return;
116
+ }
117
+ logger?.error?.("Session web request failed.", { error: error instanceof Error ? error.message : String(error) });
118
+ res.status(500).json({ error: { message: "Internal server error.", code: "internal_error" } });
119
+ });
120
+ try {
121
+ const address = await listen(server, port, host, {
122
+ listenFailed: (reason) => new SessionWebServerError("start_failed", `Session web server failed to listen: ${reason}`),
123
+ noAddress: () => new SessionWebServerError("start_failed", "Session web server did not receive a TCP address."),
124
+ });
125
+ const url = `http://${hostForUrl(host)}:${address.port}/`;
126
+ return {
127
+ url,
128
+ async stop() {
129
+ for (const closeStream of [...activeStreams]) {
130
+ closeStream();
131
+ }
132
+ activeStreams.clear();
133
+ await close(server);
134
+ await aggregator.stop();
135
+ },
136
+ };
137
+ }
138
+ catch (error) {
139
+ // Don't leak the aggregator's watchers/live connections if we never bound.
140
+ await aggregator.stop();
141
+ throw error;
142
+ }
143
+ }
144
+ function handleStream(aggregator, res, activeStreams, logger) {
145
+ res.status(200);
146
+ res.setHeader("Content-Type", "text/event-stream");
147
+ res.setHeader("Cache-Control", "no-cache, no-transform");
148
+ res.setHeader("Connection", "keep-alive");
149
+ res.setHeader("X-Accel-Buffering", "no");
150
+ res.socket?.setNoDelay(true);
151
+ res.flushHeaders();
152
+ const queue = [];
153
+ const pendingUpserts = new Map();
154
+ const pendingRemovals = new Map();
155
+ let pendingInstances;
156
+ let draining = false;
157
+ let closed = false;
158
+ let unsubscribe;
159
+ let heartbeat;
160
+ const closeStream = () => {
161
+ if (closed) {
162
+ return;
163
+ }
164
+ closed = true;
165
+ if (heartbeat !== undefined) {
166
+ clearInterval(heartbeat);
167
+ heartbeat = undefined;
168
+ }
169
+ unsubscribe?.();
170
+ unsubscribe = undefined;
171
+ queue.length = 0;
172
+ pendingUpserts.clear();
173
+ pendingRemovals.clear();
174
+ pendingInstances = undefined;
175
+ activeStreams.delete(closeStream);
176
+ if (!res.writableEnded) {
177
+ res.end();
178
+ }
179
+ };
180
+ activeStreams.add(closeStream);
181
+ const flush = () => {
182
+ while (!closed && !draining && queue.length > 0) {
183
+ const entry = queue.shift();
184
+ if (entry === undefined) {
185
+ break;
186
+ }
187
+ if (entry.frame.t === "session_upsert") {
188
+ pendingUpserts.delete(sessionQueueKey(entry.frame.session));
189
+ }
190
+ else if (entry.frame.t === "session_removed") {
191
+ pendingRemovals.delete(sessionQueueKey({ id: entry.frame.runId, sourceId: entry.frame.sourceId }));
192
+ }
193
+ else if (entry === pendingInstances) {
194
+ pendingInstances = undefined;
195
+ }
196
+ if (res.writableEnded) {
197
+ closed = true;
198
+ return;
199
+ }
200
+ const payload = serializeBrowserFrame(entry.frame, logger);
201
+ if (payload === undefined) {
202
+ continue;
203
+ }
204
+ const ok = res.write(payload);
205
+ if (!ok) {
206
+ draining = true;
207
+ res.once("drain", () => {
208
+ draining = false;
209
+ flush();
210
+ });
211
+ return;
212
+ }
213
+ }
214
+ };
215
+ const enqueue = (frame) => {
216
+ if (closed || res.writableEnded) {
217
+ return;
218
+ }
219
+ if (frame.t === "session_upsert") {
220
+ const queueKey = sessionQueueKey(frame.session);
221
+ const existing = pendingUpserts.get(queueKey);
222
+ if (existing !== undefined) {
223
+ // Coalesce by REMOVING the stale queued upsert and re-appending the latest
224
+ // at the tail — not overwriting in place. If a session_removed for the same
225
+ // id was enqueued between the two upserts, the removed frame keeps its slot
226
+ // and the newer upsert lands AFTER it, so last-write-wins (run present) is
227
+ // preserved on drain rather than the browser ending on a stale removal.
228
+ const idx = queue.indexOf(existing);
229
+ if (idx >= 0) {
230
+ queue.splice(idx, 1);
231
+ }
232
+ }
233
+ const entry = { frame };
234
+ pendingUpserts.set(queueKey, entry);
235
+ queue.push(entry);
236
+ }
237
+ else if (frame.t === "session_removed") {
238
+ const queueKey = sessionQueueKey({ id: frame.runId, sourceId: frame.sourceId });
239
+ const staleUpsert = pendingUpserts.get(queueKey);
240
+ if (staleUpsert !== undefined) {
241
+ const idx = queue.indexOf(staleUpsert);
242
+ if (idx >= 0) {
243
+ queue.splice(idx, 1);
244
+ }
245
+ pendingUpserts.delete(queueKey);
246
+ }
247
+ const existing = pendingRemovals.get(queueKey);
248
+ if (existing !== undefined) {
249
+ const idx = queue.indexOf(existing);
250
+ if (idx >= 0) {
251
+ queue.splice(idx, 1);
252
+ }
253
+ }
254
+ const entry = { frame };
255
+ pendingRemovals.set(queueKey, entry);
256
+ queue.push(entry);
257
+ }
258
+ else if (frame.t === "instances") {
259
+ if (pendingInstances !== undefined) {
260
+ const idx = queue.indexOf(pendingInstances);
261
+ if (idx >= 0) {
262
+ queue.splice(idx, 1);
263
+ }
264
+ }
265
+ pendingInstances = { frame };
266
+ queue.push(pendingInstances);
267
+ }
268
+ else {
269
+ queue.push({ frame });
270
+ }
271
+ if (queue.length > MAX_BROWSER_SSE_QUEUE_FRAMES) {
272
+ closeStream();
273
+ return;
274
+ }
275
+ flush();
276
+ };
277
+ // Initial snapshot: the instance list, then one upsert per current session, so a
278
+ // fresh browser reconstructs full state before any live frame arrives. It flows
279
+ // through the same backpressure-aware queue; the aggregator's per-instance
280
+ // eviction cap bounds how large the snapshot can grow.
281
+ enqueue({ t: "instances", instances: aggregator.getInstances() });
282
+ for (const session of aggregator.getSessionSummaries("all")) {
283
+ enqueue({ t: "session_upsert", session });
284
+ }
285
+ unsubscribe = aggregator.subscribe(enqueue);
286
+ heartbeat = setInterval(() => {
287
+ // Skip the keep-alive while draining so we don't pile onto a full buffer.
288
+ if (!closed && !draining && !res.writableEnded) {
289
+ res.write(": ping\n\n");
290
+ }
291
+ }, HEARTBEAT_INTERVAL_MS);
292
+ heartbeat.unref?.();
293
+ res.on("close", () => {
294
+ closeStream();
295
+ });
296
+ }
297
+ function sessionQueueKey(session) {
298
+ return `${session.sourceId ?? session.instance ?? ""}:${session.id}`;
299
+ }
300
+ /**
301
+ * Resolve the built SPA directory relative to this module. The dist layout is
302
+ * `dist/server.js` alongside a sibling `webapp/dist`, so `../webapp/dist` from the
303
+ * module dir. Overridable by `options.staticDir`.
304
+ */
305
+ function defaultStaticDir() {
306
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
307
+ return resolve(moduleDir, "../webapp/dist");
308
+ }
309
+ function firstString(value) {
310
+ if (Array.isArray(value)) {
311
+ return firstString(value[0]);
312
+ }
313
+ return normalizeOptionalString(typeof value === "string" ? value : undefined);
314
+ }
315
+ function parseLimit(value) {
316
+ if (value === undefined) {
317
+ return undefined;
318
+ }
319
+ const parsed = Number.parseInt(value, 10);
320
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : undefined;
321
+ }
322
+ function parseOffset(value) {
323
+ if (value === undefined) {
324
+ return 0;
325
+ }
326
+ const parsed = Number.parseInt(value, 10);
327
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : 0;
328
+ }
329
+ function requireApiAuth(authToken) {
330
+ return (req, res, next) => {
331
+ const supplied = readAuthorizationBearer(req.headers.authorization) ?? firstString(req.query.token);
332
+ if (supplied !== undefined && bearerTokensEqual(supplied, authToken)) {
333
+ next();
334
+ return;
335
+ }
336
+ res.status(401).json({ error: { message: "Unauthorized.", code: "unauthorized" } });
337
+ };
338
+ }
339
+ function serializeBrowserFrame(frame, logger) {
340
+ try {
341
+ const json = JSON.stringify(frame);
342
+ if (BROWSER_FRAME_ENCODER.encode(json).length > MAX_BROWSER_SSE_FRAME_BYTES) {
343
+ logger?.warn?.("Dropped oversized browser SSE frame.", {
344
+ maxFrameBytes: MAX_BROWSER_SSE_FRAME_BYTES,
345
+ frameType: frame.t,
346
+ });
347
+ return undefined;
348
+ }
349
+ return `data: ${json}\n\n`;
350
+ }
351
+ catch (error) {
352
+ logger?.warn?.("Dropped unserializable browser SSE frame.", {
353
+ reason: error instanceof Error ? error.message : String(error),
354
+ frameType: frame.t,
355
+ });
356
+ return undefined;
357
+ }
358
+ }
359
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}