@warmdrift/kgauto-compiler 2.0.0-alpha.16 → 2.0.0-alpha.18

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.
@@ -0,0 +1,73 @@
1
+ import { G as GlassboxEvent } from '../types-DWF6mPGg.mjs';
2
+ import '../ir-C3P4gDt0.mjs';
3
+ import '../dialect.mjs';
4
+
5
+ /**
6
+ * Public entry point for `@warmdrift/kgauto-compiler/glassbox-routes`.
7
+ *
8
+ * One factory call from a Vercel Edge consumer route handler gets you both
9
+ * the replay-query (`proxy`) and live-SSE (`stream`) endpoints that the
10
+ * Glass-Box Chrome panel reads. Wiring is ~6 lines per app:
11
+ *
12
+ * // app/api/glassbox/proxy/route.ts
13
+ * import { createGlassboxRoutes } from '@warmdrift/kgauto-compiler/glassbox-routes';
14
+ * const { proxy } = createGlassboxRoutes({
15
+ * installToken: process.env.GLASSBOX_INSTALL_TOKEN!,
16
+ * extensionId: process.env.GLASSBOX_EXTENSION_ID!,
17
+ * brainEndpoint: process.env.GLASSBOX_BRAIN_ENDPOINT!,
18
+ * brainJwt: process.env.GLASSBOX_BRAIN_JWT!,
19
+ * appId: 'playbacksam',
20
+ * });
21
+ * export { proxy as GET };
22
+ *
23
+ * Auth model (defense in depth):
24
+ * - Bearer install token → primary, constant-time compared
25
+ * - chrome-extension Origin → secondary CSRF gate
26
+ *
27
+ * Scrub: optional sanitization runs at the proxy boundary before events or
28
+ * rows leave the consumer's infrastructure. Per pii-scrubber-call-site-not-
29
+ * package: kgauto stays naive to PII policy; consumers pass scrub hooks
30
+ * that encode their own data-handling rules.
31
+ *
32
+ * Brain reads use the scoped JWT minted via migration 013. RLS enforces
33
+ * `app_id = jwt.claim.app_id`; the proxy filter is belt-and-suspenders for
34
+ * payload size + log legibility.
35
+ */
36
+
37
+ interface GlassboxRoutesConfig {
38
+ /** Bearer token validated on every request via constant-time compare. Required. */
39
+ installToken: string;
40
+ /** chrome-extension://<id> — exact match required on Origin header. Required. */
41
+ extensionId: string;
42
+ /** Brain endpoint base (e.g. https://kgauto-brain.supabase.co). Used by `proxy` for replay queries. */
43
+ brainEndpoint: string;
44
+ /** Scoped JWT for brain reads. Use the per-consumer JWT minted via migration 013 (claim: app_id). */
45
+ brainJwt: string;
46
+ /** App scope filter — must match the JWT's app_id claim. */
47
+ appId: string;
48
+ /**
49
+ * Optional sanitization hook. Called with each event/trace BEFORE it leaves the consumer.
50
+ * Default: identity (no scrub). PII policy lives at the call-site, not in the package.
51
+ */
52
+ scrub?: (event: GlassboxEvent | Record<string, unknown>) => GlassboxEvent | Record<string, unknown>;
53
+ /**
54
+ * Test-only seam: override the brain fetch implementation. Production
55
+ * code never sets this; tests use it to mock PostgREST responses.
56
+ */
57
+ fetch?: typeof fetch;
58
+ /**
59
+ * Test-only seam: override the live-stream subscriber. Production code
60
+ * resolves to the alpha.17 `subscribe()` export; tests inject a fake
61
+ * source ReadableStream<GlassboxEvent>.
62
+ */
63
+ subscribe?: (traceId: string) => ReadableStream<GlassboxEvent>;
64
+ }
65
+ interface GlassboxRoutes {
66
+ /** GET /api/glassbox/proxy?traceId=<id> OR ?limit=20 (recent traces) */
67
+ proxy: (req: Request) => Promise<Response>;
68
+ /** GET /api/glassbox/stream?traceId=<id> (SSE) */
69
+ stream: (req: Request) => Promise<Response>;
70
+ }
71
+ declare function createGlassboxRoutes(config: GlassboxRoutesConfig): GlassboxRoutes;
72
+
73
+ export { type GlassboxRoutes, type GlassboxRoutesConfig, createGlassboxRoutes };
@@ -0,0 +1,73 @@
1
+ import { G as GlassboxEvent } from '../types-xeklorHU.js';
2
+ import '../ir-CFHU3BUT.js';
3
+ import '../dialect.js';
4
+
5
+ /**
6
+ * Public entry point for `@warmdrift/kgauto-compiler/glassbox-routes`.
7
+ *
8
+ * One factory call from a Vercel Edge consumer route handler gets you both
9
+ * the replay-query (`proxy`) and live-SSE (`stream`) endpoints that the
10
+ * Glass-Box Chrome panel reads. Wiring is ~6 lines per app:
11
+ *
12
+ * // app/api/glassbox/proxy/route.ts
13
+ * import { createGlassboxRoutes } from '@warmdrift/kgauto-compiler/glassbox-routes';
14
+ * const { proxy } = createGlassboxRoutes({
15
+ * installToken: process.env.GLASSBOX_INSTALL_TOKEN!,
16
+ * extensionId: process.env.GLASSBOX_EXTENSION_ID!,
17
+ * brainEndpoint: process.env.GLASSBOX_BRAIN_ENDPOINT!,
18
+ * brainJwt: process.env.GLASSBOX_BRAIN_JWT!,
19
+ * appId: 'playbacksam',
20
+ * });
21
+ * export { proxy as GET };
22
+ *
23
+ * Auth model (defense in depth):
24
+ * - Bearer install token → primary, constant-time compared
25
+ * - chrome-extension Origin → secondary CSRF gate
26
+ *
27
+ * Scrub: optional sanitization runs at the proxy boundary before events or
28
+ * rows leave the consumer's infrastructure. Per pii-scrubber-call-site-not-
29
+ * package: kgauto stays naive to PII policy; consumers pass scrub hooks
30
+ * that encode their own data-handling rules.
31
+ *
32
+ * Brain reads use the scoped JWT minted via migration 013. RLS enforces
33
+ * `app_id = jwt.claim.app_id`; the proxy filter is belt-and-suspenders for
34
+ * payload size + log legibility.
35
+ */
36
+
37
+ interface GlassboxRoutesConfig {
38
+ /** Bearer token validated on every request via constant-time compare. Required. */
39
+ installToken: string;
40
+ /** chrome-extension://<id> — exact match required on Origin header. Required. */
41
+ extensionId: string;
42
+ /** Brain endpoint base (e.g. https://kgauto-brain.supabase.co). Used by `proxy` for replay queries. */
43
+ brainEndpoint: string;
44
+ /** Scoped JWT for brain reads. Use the per-consumer JWT minted via migration 013 (claim: app_id). */
45
+ brainJwt: string;
46
+ /** App scope filter — must match the JWT's app_id claim. */
47
+ appId: string;
48
+ /**
49
+ * Optional sanitization hook. Called with each event/trace BEFORE it leaves the consumer.
50
+ * Default: identity (no scrub). PII policy lives at the call-site, not in the package.
51
+ */
52
+ scrub?: (event: GlassboxEvent | Record<string, unknown>) => GlassboxEvent | Record<string, unknown>;
53
+ /**
54
+ * Test-only seam: override the brain fetch implementation. Production
55
+ * code never sets this; tests use it to mock PostgREST responses.
56
+ */
57
+ fetch?: typeof fetch;
58
+ /**
59
+ * Test-only seam: override the live-stream subscriber. Production code
60
+ * resolves to the alpha.17 `subscribe()` export; tests inject a fake
61
+ * source ReadableStream<GlassboxEvent>.
62
+ */
63
+ subscribe?: (traceId: string) => ReadableStream<GlassboxEvent>;
64
+ }
65
+ interface GlassboxRoutes {
66
+ /** GET /api/glassbox/proxy?traceId=<id> OR ?limit=20 (recent traces) */
67
+ proxy: (req: Request) => Promise<Response>;
68
+ /** GET /api/glassbox/stream?traceId=<id> (SSE) */
69
+ stream: (req: Request) => Promise<Response>;
70
+ }
71
+ declare function createGlassboxRoutes(config: GlassboxRoutesConfig): GlassboxRoutes;
72
+
73
+ export { type GlassboxRoutes, type GlassboxRoutesConfig, createGlassboxRoutes };
@@ -0,0 +1,560 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/glassbox-routes/index.ts
21
+ var glassbox_routes_exports = {};
22
+ __export(glassbox_routes_exports, {
23
+ createGlassboxRoutes: () => createGlassboxRoutes
24
+ });
25
+ module.exports = __toCommonJS(glassbox_routes_exports);
26
+
27
+ // src/glassbox-routes/auth.ts
28
+ var import_node_buffer = require("buffer");
29
+ var import_node_crypto = require("crypto");
30
+ var JSON_HEADERS = { "Content-Type": "application/json" };
31
+ function jsonError(status, code) {
32
+ return new Response(JSON.stringify({ error: code }), {
33
+ status,
34
+ headers: JSON_HEADERS
35
+ });
36
+ }
37
+ function tokensEqual(a, b) {
38
+ if (a.length !== b.length) return false;
39
+ const ab = import_node_buffer.Buffer.from(a, "utf8");
40
+ const bb = import_node_buffer.Buffer.from(b, "utf8");
41
+ if (ab.length !== bb.length) return false;
42
+ return (0, import_node_crypto.timingSafeEqual)(ab, bb);
43
+ }
44
+ function checkAuth(req, config) {
45
+ const authHeader = req.headers.get("Authorization") ?? "";
46
+ const match = /^Bearer\s+(.+)$/i.exec(authHeader);
47
+ const provided = match?.[1]?.trim() ?? "";
48
+ if (!provided || !tokensEqual(provided, config.installToken)) {
49
+ return jsonError(401, "unauthorized");
50
+ }
51
+ const origin = req.headers.get("Origin") ?? "";
52
+ const expected = `chrome-extension://${config.extensionId}`;
53
+ if (origin !== expected) {
54
+ return jsonError(403, "forbidden_origin");
55
+ }
56
+ return null;
57
+ }
58
+
59
+ // src/glassbox-routes/proxy.ts
60
+ var JSON_HEADERS2 = {
61
+ "Content-Type": "application/json",
62
+ "Cache-Control": "no-store"
63
+ };
64
+ var DEFAULT_LIMIT = 20;
65
+ var MAX_LIMIT = 100;
66
+ function jsonResponse(status, body) {
67
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
68
+ }
69
+ function jsonError2(status, code) {
70
+ return jsonResponse(status, { error: code });
71
+ }
72
+ function applyScrub(row, scrub) {
73
+ if (!scrub || row == null || typeof row !== "object") return row;
74
+ try {
75
+ return scrub(row);
76
+ } catch {
77
+ return row;
78
+ }
79
+ }
80
+ function parseLimit(raw) {
81
+ if (!raw) return DEFAULT_LIMIT;
82
+ const n = Number.parseInt(raw, 10);
83
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
84
+ return Math.min(n, MAX_LIMIT);
85
+ }
86
+ function createProxyHandler(config) {
87
+ const {
88
+ installToken,
89
+ extensionId,
90
+ brainEndpoint,
91
+ brainJwt,
92
+ appId,
93
+ scrub,
94
+ fetch: fetchImpl
95
+ } = config;
96
+ const doFetch = fetchImpl ?? ((...args) => globalThis.fetch(...args));
97
+ const base = brainEndpoint.replace(/\/+$/, "");
98
+ return async function proxy(req) {
99
+ const authFail = checkAuth(req, { installToken, extensionId });
100
+ if (authFail) return authFail;
101
+ const url = new URL(req.url);
102
+ const traceId = url.searchParams.get("traceId");
103
+ const limit = parseLimit(url.searchParams.get("limit"));
104
+ const qs = new URLSearchParams();
105
+ qs.set("app_id", `eq.${appId}`);
106
+ if (traceId) {
107
+ qs.set("trace_id", `eq.${traceId}`);
108
+ } else {
109
+ qs.set("order", "created_at.desc");
110
+ qs.set("limit", String(limit));
111
+ }
112
+ const brainUrl = `${base}/rest/v1/compile_outcomes?${qs.toString()}`;
113
+ let brainRes;
114
+ try {
115
+ brainRes = await doFetch(brainUrl, {
116
+ method: "GET",
117
+ headers: {
118
+ Authorization: `Bearer ${brainJwt}`,
119
+ apikey: brainJwt,
120
+ Accept: "application/json"
121
+ }
122
+ });
123
+ } catch {
124
+ return jsonError2(502, "brain_unavailable");
125
+ }
126
+ if (brainRes.status === 401 || brainRes.status === 403) {
127
+ return jsonError2(500, "brain_auth_misconfig");
128
+ }
129
+ if (brainRes.status >= 500) {
130
+ return jsonError2(502, "brain_unavailable");
131
+ }
132
+ if (!brainRes.ok) {
133
+ return jsonError2(400, "bad_request");
134
+ }
135
+ let rows;
136
+ try {
137
+ rows = await brainRes.json();
138
+ } catch {
139
+ return jsonError2(502, "brain_unavailable");
140
+ }
141
+ if (!Array.isArray(rows)) {
142
+ return jsonError2(502, "brain_unavailable");
143
+ }
144
+ const scrubbed = rows.map((row) => applyScrub(row, scrub));
145
+ return jsonResponse(200, scrubbed);
146
+ };
147
+ }
148
+
149
+ // src/glassbox-routes/stream.ts
150
+ var SSE_HEADERS = {
151
+ "Content-Type": "text/event-stream",
152
+ "Cache-Control": "no-cache, no-transform",
153
+ Connection: "keep-alive",
154
+ "X-Accel-Buffering": "no"
155
+ };
156
+ var JSON_HEADERS3 = { "Content-Type": "application/json" };
157
+ function jsonError3(status, code) {
158
+ return new Response(JSON.stringify({ error: code }), {
159
+ status,
160
+ headers: JSON_HEADERS3
161
+ });
162
+ }
163
+ function applyScrub2(event, scrub) {
164
+ if (!scrub) return event;
165
+ try {
166
+ const out = scrub(event);
167
+ if (out && typeof out === "object" && typeof out.kind === "string" && typeof out.at === "number") {
168
+ return out;
169
+ }
170
+ return event;
171
+ } catch {
172
+ return event;
173
+ }
174
+ }
175
+ function sseFrame(eventName, data) {
176
+ const safeName = eventName.replace(/[\r\n]/g, "");
177
+ return `event: ${safeName}
178
+ data: ${JSON.stringify(data)}
179
+
180
+ `;
181
+ }
182
+ function createStreamHandler(config, subscribe2) {
183
+ const { installToken, extensionId, scrub } = config;
184
+ return async function stream(req) {
185
+ const authFail = checkAuth(req, { installToken, extensionId });
186
+ if (authFail) return authFail;
187
+ const url = new URL(req.url);
188
+ const traceId = url.searchParams.get("traceId");
189
+ if (!traceId) {
190
+ return jsonError3(400, "missing_trace_id");
191
+ }
192
+ const source = subscribe2(traceId);
193
+ const encoder = new TextEncoder();
194
+ let sourceReader;
195
+ let cancelled = false;
196
+ const body = new ReadableStream({
197
+ async start(controller) {
198
+ controller.enqueue(encoder.encode(sseFrame("ready", {})));
199
+ sourceReader = source.getReader();
200
+ const signal = req.signal;
201
+ if (signal) {
202
+ if (signal.aborted) {
203
+ cancelled = true;
204
+ await sourceReader.cancel();
205
+ try {
206
+ controller.close();
207
+ } catch {
208
+ }
209
+ return;
210
+ }
211
+ signal.addEventListener(
212
+ "abort",
213
+ () => {
214
+ cancelled = true;
215
+ sourceReader?.cancel().catch(() => {
216
+ });
217
+ try {
218
+ controller.close();
219
+ } catch {
220
+ }
221
+ },
222
+ { once: true }
223
+ );
224
+ }
225
+ try {
226
+ while (!cancelled) {
227
+ const { value, done } = await sourceReader.read();
228
+ if (done) break;
229
+ const scrubbed = applyScrub2(value, scrub);
230
+ controller.enqueue(
231
+ encoder.encode(sseFrame(scrubbed.kind, scrubbed))
232
+ );
233
+ }
234
+ } catch {
235
+ } finally {
236
+ try {
237
+ controller.close();
238
+ } catch {
239
+ }
240
+ try {
241
+ sourceReader?.releaseLock();
242
+ } catch {
243
+ }
244
+ }
245
+ },
246
+ cancel() {
247
+ cancelled = true;
248
+ sourceReader?.cancel().catch(() => {
249
+ });
250
+ }
251
+ });
252
+ return new Response(body, { status: 200, headers: SSE_HEADERS });
253
+ };
254
+ }
255
+
256
+ // src/glassbox/types.ts
257
+ var GLASSBOX_STREAM_TTL_MS = 6e4;
258
+
259
+ // src/glassbox/pubsub-memory.ts
260
+ var MemoryPubSub = class {
261
+ subscribers = /* @__PURE__ */ new Map();
262
+ async publish(traceId, event) {
263
+ const subs = this.subscribers.get(traceId);
264
+ if (!subs || subs.size === 0) return;
265
+ for (const sub of subs) {
266
+ if (sub.closed) continue;
267
+ try {
268
+ sub.controller.enqueue(event);
269
+ } catch {
270
+ sub.closed = true;
271
+ continue;
272
+ }
273
+ this.refreshTtl(traceId, sub);
274
+ }
275
+ }
276
+ subscribe(traceId) {
277
+ const self = this;
278
+ let sub;
279
+ return new ReadableStream({
280
+ start(controller) {
281
+ sub = {
282
+ controller,
283
+ ttlTimer: setTimeout(() => {
284
+ self.closeSubscriber(traceId, sub);
285
+ }, GLASSBOX_STREAM_TTL_MS),
286
+ closed: false
287
+ };
288
+ let set = self.subscribers.get(traceId);
289
+ if (!set) {
290
+ set = /* @__PURE__ */ new Set();
291
+ self.subscribers.set(traceId, set);
292
+ }
293
+ set.add(sub);
294
+ },
295
+ cancel() {
296
+ if (sub) self.removeSubscriber(traceId, sub);
297
+ }
298
+ });
299
+ }
300
+ /**
301
+ * Refresh the rolling TTL for a subscriber after an event lands. Replaces
302
+ * the existing timer with a fresh 60s one.
303
+ */
304
+ refreshTtl(traceId, sub) {
305
+ clearTimeout(sub.ttlTimer);
306
+ sub.ttlTimer = setTimeout(() => {
307
+ this.closeSubscriber(traceId, sub);
308
+ }, GLASSBOX_STREAM_TTL_MS);
309
+ }
310
+ /**
311
+ * Close the subscriber's stream cleanly and remove from the fan-out set.
312
+ * Idempotent — safe to call multiple times.
313
+ */
314
+ closeSubscriber(traceId, sub) {
315
+ if (sub.closed) return;
316
+ sub.closed = true;
317
+ clearTimeout(sub.ttlTimer);
318
+ try {
319
+ sub.controller.close();
320
+ } catch {
321
+ }
322
+ this.removeSubscriber(traceId, sub);
323
+ }
324
+ removeSubscriber(traceId, sub) {
325
+ clearTimeout(sub.ttlTimer);
326
+ const set = this.subscribers.get(traceId);
327
+ if (!set) return;
328
+ set.delete(sub);
329
+ if (set.size === 0) this.subscribers.delete(traceId);
330
+ }
331
+ /**
332
+ * Test-only reset. Tears down all subscribers, clears all state. Calling
333
+ * outside of tests is harmless but cancels every active stream.
334
+ */
335
+ _reset() {
336
+ for (const [, set] of this.subscribers) {
337
+ for (const sub of set) {
338
+ this.closeSubscriber("", sub);
339
+ }
340
+ }
341
+ this.subscribers.clear();
342
+ }
343
+ };
344
+
345
+ // src/glassbox/pubsub-upstash.ts
346
+ var UpstashPubSub = class {
347
+ url;
348
+ token;
349
+ fetchImpl;
350
+ blockMs;
351
+ maxLen;
352
+ constructor(cfg) {
353
+ this.url = cfg.url.replace(/\/$/, "");
354
+ this.token = cfg.token;
355
+ this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
356
+ this.blockMs = cfg.blockMs ?? 100;
357
+ this.maxLen = cfg.maxLen ?? 100;
358
+ }
359
+ async publish(traceId, event) {
360
+ const key = streamKey(traceId);
361
+ const payload = JSON.stringify(event);
362
+ await this.cmd([
363
+ "XADD",
364
+ key,
365
+ "MAXLEN",
366
+ "~",
367
+ String(this.maxLen),
368
+ "*",
369
+ "event",
370
+ payload
371
+ ]);
372
+ await this.cmd(["EXPIRE", key, String(Math.ceil(GLASSBOX_STREAM_TTL_MS / 1e3))]);
373
+ }
374
+ subscribe(traceId) {
375
+ const key = streamKey(traceId);
376
+ const self = this;
377
+ let cursor = "$";
378
+ let cancelled = false;
379
+ let ttlDeadline = Date.now() + GLASSBOX_STREAM_TTL_MS;
380
+ return new ReadableStream({
381
+ async start(controller) {
382
+ try {
383
+ while (!cancelled && Date.now() < ttlDeadline) {
384
+ const resp = await self.cmd([
385
+ "XREAD",
386
+ "BLOCK",
387
+ String(self.blockMs),
388
+ "STREAMS",
389
+ key,
390
+ cursor
391
+ ]);
392
+ if (cancelled) break;
393
+ const parsed = parseXReadResult(resp.result);
394
+ if (parsed.entries.length === 0) {
395
+ continue;
396
+ }
397
+ for (const entry of parsed.entries) {
398
+ const evt = decodeEvent(entry.fields);
399
+ if (evt) {
400
+ try {
401
+ controller.enqueue(evt);
402
+ } catch {
403
+ cancelled = true;
404
+ break;
405
+ }
406
+ }
407
+ cursor = entry.id;
408
+ }
409
+ ttlDeadline = Date.now() + GLASSBOX_STREAM_TTL_MS;
410
+ }
411
+ } catch (err) {
412
+ if (!cancelled) {
413
+ try {
414
+ controller.error(err);
415
+ } catch {
416
+ }
417
+ return;
418
+ }
419
+ }
420
+ try {
421
+ controller.close();
422
+ } catch {
423
+ }
424
+ },
425
+ cancel() {
426
+ cancelled = true;
427
+ }
428
+ });
429
+ }
430
+ async cmd(args) {
431
+ const res = await this.fetchImpl(this.url, {
432
+ method: "POST",
433
+ headers: {
434
+ Authorization: `Bearer ${this.token}`,
435
+ "Content-Type": "application/json"
436
+ },
437
+ body: JSON.stringify(args)
438
+ });
439
+ if (!res.ok) {
440
+ throw new Error(`Upstash ${args[0]} failed: HTTP ${res.status}`);
441
+ }
442
+ const json = await res.json();
443
+ if (json.error) {
444
+ throw new Error(`Upstash ${args[0]} failed: ${json.error}`);
445
+ }
446
+ return json;
447
+ }
448
+ };
449
+ function streamKey(traceId) {
450
+ return `glassbox:trace:${traceId}`;
451
+ }
452
+ function decodeEvent(fields) {
453
+ const raw = fields["event"];
454
+ if (!raw) return void 0;
455
+ try {
456
+ const parsed = JSON.parse(raw);
457
+ if (typeof parsed.kind === "string" && typeof parsed.at === "number") {
458
+ return parsed;
459
+ }
460
+ return void 0;
461
+ } catch {
462
+ return void 0;
463
+ }
464
+ }
465
+ function parseXReadResult(raw) {
466
+ if (!Array.isArray(raw)) return { entries: [] };
467
+ const entries = [];
468
+ for (const stream of raw) {
469
+ if (!Array.isArray(stream) || stream.length < 2) continue;
470
+ const streamEntries = stream[1];
471
+ if (!Array.isArray(streamEntries)) continue;
472
+ for (const entry of streamEntries) {
473
+ if (!Array.isArray(entry) || entry.length < 2) continue;
474
+ const id = String(entry[0]);
475
+ const flat = entry[1];
476
+ if (!Array.isArray(flat)) continue;
477
+ const fields = {};
478
+ for (let i = 0; i < flat.length; i += 2) {
479
+ const k = flat[i];
480
+ const v = flat[i + 1];
481
+ if (typeof k === "string") fields[k] = String(v ?? "");
482
+ }
483
+ entries.push({ id, fields });
484
+ }
485
+ }
486
+ return { entries };
487
+ }
488
+
489
+ // src/glassbox/emit.ts
490
+ var activePubSub;
491
+ function getPubSub() {
492
+ if (activePubSub) return activePubSub;
493
+ const url = readEnv("UPSTASH_REDIS_URL");
494
+ const token = readEnv("UPSTASH_REDIS_TOKEN");
495
+ if (url && token) {
496
+ activePubSub = new UpstashPubSub({ url, token });
497
+ } else {
498
+ activePubSub = new MemoryPubSub();
499
+ }
500
+ return activePubSub;
501
+ }
502
+ function readEnv(key) {
503
+ try {
504
+ if (typeof process !== "undefined" && process.env) {
505
+ const v = process.env[key];
506
+ return v && v.trim() !== "" ? v : void 0;
507
+ }
508
+ } catch {
509
+ }
510
+ return void 0;
511
+ }
512
+
513
+ // src/glassbox/subscribe.ts
514
+ function subscribe(traceId) {
515
+ if (!traceId) {
516
+ return new ReadableStream({
517
+ start(controller) {
518
+ controller.close();
519
+ }
520
+ });
521
+ }
522
+ return getPubSub().subscribe(traceId);
523
+ }
524
+
525
+ // src/glassbox-routes/index.ts
526
+ function requireString(name, value) {
527
+ if (typeof value !== "string" || value.length === 0) {
528
+ throw new Error(`createGlassboxRoutes: ${name} is required`);
529
+ }
530
+ return value;
531
+ }
532
+ function createGlassboxRoutes(config) {
533
+ const installToken = requireString("installToken", config.installToken);
534
+ const extensionId = requireString("extensionId", config.extensionId);
535
+ const brainEndpoint = requireString("brainEndpoint", config.brainEndpoint);
536
+ const brainJwt = requireString("brainJwt", config.brainJwt);
537
+ const appId = requireString("appId", config.appId);
538
+ const proxy = createProxyHandler({
539
+ installToken,
540
+ extensionId,
541
+ brainEndpoint,
542
+ brainJwt,
543
+ appId,
544
+ scrub: config.scrub,
545
+ fetch: config.fetch
546
+ });
547
+ const stream = createStreamHandler(
548
+ {
549
+ installToken,
550
+ extensionId,
551
+ scrub: config.scrub
552
+ },
553
+ config.subscribe ?? subscribe
554
+ );
555
+ return { proxy, stream };
556
+ }
557
+ // Annotate the CommonJS export names for ESM import in node:
558
+ 0 && (module.exports = {
559
+ createGlassboxRoutes
560
+ });