@warmdrift/kgauto-compiler 2.0.0-alpha.3 → 2.0.0-alpha.31

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,658 @@
1
+ import {
2
+ ARCHETYPE_FLOOR_DEFAULT,
3
+ getDefaultFallbackChain
4
+ } from "../chunk-WXCFWUCN.mjs";
5
+ import {
6
+ tryGetProfile
7
+ } from "../chunk-JQGRWJZO.mjs";
8
+ import {
9
+ subscribe,
10
+ subscribeApp
11
+ } from "../chunk-RO22VFIF.mjs";
12
+ import "../chunk-NBO4R5PC.mjs";
13
+
14
+ // src/glassbox-routes/auth.ts
15
+ var JSON_HEADERS = { "Content-Type": "application/json" };
16
+ function jsonError(status, code) {
17
+ return new Response(JSON.stringify({ error: code }), {
18
+ status,
19
+ headers: JSON_HEADERS
20
+ });
21
+ }
22
+ function tokensEqual(a, b) {
23
+ if (a.length !== b.length) return false;
24
+ let mismatch = 0;
25
+ for (let i = 0; i < a.length; i++) {
26
+ mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
27
+ }
28
+ return mismatch === 0;
29
+ }
30
+ function checkAuth(req, config) {
31
+ const authHeader = req.headers.get("Authorization") ?? "";
32
+ const match = /^Bearer\s+(.+)$/i.exec(authHeader);
33
+ const provided = match?.[1]?.trim() ?? "";
34
+ if (!provided || !tokensEqual(provided, config.installToken)) {
35
+ return jsonError(401, "unauthorized");
36
+ }
37
+ const origin = req.headers.get("Origin") ?? "";
38
+ const xExtId = req.headers.get("X-Glassbox-Extension-Id") ?? "";
39
+ const expectedOrigin = `chrome-extension://${config.extensionId}`;
40
+ const originOk = origin === expectedOrigin;
41
+ const xExtOk = xExtId.length > 0 && tokensEqual(xExtId, config.extensionId);
42
+ if (!originOk && !xExtOk) {
43
+ return jsonError(403, "forbidden_origin");
44
+ }
45
+ return null;
46
+ }
47
+
48
+ // src/glassbox-routes/counterfactuals.ts
49
+ var COUNTERFACTUAL_MIN_SAVINGS_RATIO = 0.1;
50
+ var COUNTERFACTUAL_MAX_RESULTS = 2;
51
+ function computeCounterfactuals(args) {
52
+ const {
53
+ servedModel,
54
+ servedCostUsd,
55
+ archetype,
56
+ tokensIn,
57
+ tokensOut,
58
+ cacheReadInputTokens = 0,
59
+ toolOrchestration
60
+ } = args;
61
+ if (tokensIn <= 0) return [];
62
+ if (servedCostUsd <= 0) return [];
63
+ let chain;
64
+ try {
65
+ chain = getDefaultFallbackChain({
66
+ archetype,
67
+ posture: "open",
68
+ maxDepth: 10,
69
+ toolOrchestration
70
+ });
71
+ } catch {
72
+ return [];
73
+ }
74
+ const candidates = [];
75
+ const minSavings = servedCostUsd * COUNTERFACTUAL_MIN_SAVINGS_RATIO;
76
+ for (const modelId of chain) {
77
+ if (modelId === servedModel) continue;
78
+ const profile = tryGetProfile(modelId);
79
+ if (!profile) continue;
80
+ const perf = profile.archetypePerf?.[archetype] ?? 5;
81
+ if (perf < ARCHETYPE_FLOOR_DEFAULT) continue;
82
+ const estimated = estimateCostUsd({
83
+ profile,
84
+ tokensIn,
85
+ tokensOut,
86
+ cacheReadInputTokens
87
+ });
88
+ if (estimated === void 0) continue;
89
+ const savings = servedCostUsd - estimated;
90
+ if (savings < minSavings) continue;
91
+ const savingsPercent = Math.round(savings / servedCostUsd * 100);
92
+ const reason = buildReason({
93
+ modelId,
94
+ archetype,
95
+ perf,
96
+ profile
97
+ });
98
+ candidates.push({
99
+ modelId,
100
+ estimatedCostUsd: round6(estimated),
101
+ savingsUsd: round6(savings),
102
+ savingsPercent,
103
+ reason
104
+ });
105
+ }
106
+ candidates.sort((a, b) => a.estimatedCostUsd - b.estimatedCostUsd);
107
+ return candidates.slice(0, COUNTERFACTUAL_MAX_RESULTS);
108
+ }
109
+ function estimateCostUsd(args) {
110
+ const { profile, tokensIn, tokensOut, cacheReadInputTokens } = args;
111
+ const cacheableIn = Math.min(cacheReadInputTokens, tokensIn);
112
+ const nonCachedIn = Math.max(tokensIn - cacheableIn, 0);
113
+ const discount = profile.lowering.cache.discount ?? 1;
114
+ const inUsd = nonCachedIn / 1e6 * profile.costInputPer1m + cacheableIn / 1e6 * profile.costInputPer1m * discount;
115
+ const outUsd = tokensOut / 1e6 * profile.costOutputPer1m;
116
+ const total = inUsd + outUsd;
117
+ if (!Number.isFinite(total)) return void 0;
118
+ if (total < 0) return void 0;
119
+ return total;
120
+ }
121
+ function round6(n) {
122
+ return Math.round(n * 1e6) / 1e6;
123
+ }
124
+ function buildReason(args) {
125
+ const { modelId, archetype, perf, profile } = args;
126
+ const hook = profile.strengths?.[0];
127
+ const suffix = hook ? `, ${hook.replace(/_/g, " ")}` : "";
128
+ return `${modelId} on ${archetype}: archetypePerf=${perf}${suffix}`;
129
+ }
130
+
131
+ // src/glassbox-routes/projected-cost.ts
132
+ var INSUFFICIENT_VOLUME_THRESHOLD = 5;
133
+ var WINDOW_DAYS = 7;
134
+ async function computeProjectedDailyCost(args) {
135
+ const {
136
+ appId,
137
+ archetype,
138
+ servedCostUsd,
139
+ brainEndpoint,
140
+ brainJwt,
141
+ brainAnonKey,
142
+ fetch: fetchImpl
143
+ } = args;
144
+ if (!appId || !archetype) return void 0;
145
+ if (!Number.isFinite(servedCostUsd) || servedCostUsd <= 0) return void 0;
146
+ const doFetch = fetchImpl ?? ((...a) => globalThis.fetch(...a));
147
+ const base = brainEndpoint.replace(/\/+$/, "");
148
+ const cutoffIso = new Date(
149
+ Date.now() - WINDOW_DAYS * 24 * 60 * 60 * 1e3
150
+ ).toISOString();
151
+ const qs = new URLSearchParams();
152
+ qs.set("app_id", `eq.${appId}`);
153
+ qs.set("intent_archetype", `eq.${archetype}`);
154
+ qs.set("created_at", `gte.${cutoffIso}`);
155
+ qs.set("select", "handle");
156
+ qs.set("limit", "0");
157
+ const url = `${base}/rest/v1/compile_outcomes?${qs.toString()}`;
158
+ let res;
159
+ try {
160
+ res = await doFetch(url, {
161
+ method: "GET",
162
+ headers: {
163
+ Authorization: `Bearer ${brainJwt}`,
164
+ apikey: brainAnonKey,
165
+ Accept: "application/json",
166
+ // Triggers PostgREST exact count in Content-Range header.
167
+ Prefer: "count=exact"
168
+ }
169
+ });
170
+ } catch {
171
+ return void 0;
172
+ }
173
+ if (!res.ok) return void 0;
174
+ const contentRange = res.headers.get("content-range");
175
+ const count = parseContentRangeCount(contentRange);
176
+ if (count === void 0) return void 0;
177
+ const avgPerDay = count / WINDOW_DAYS;
178
+ if (avgPerDay < INSUFFICIENT_VOLUME_THRESHOLD) return void 0;
179
+ const projected = avgPerDay * servedCostUsd;
180
+ return Math.round(projected * 1e6) / 1e6;
181
+ }
182
+ function parseContentRangeCount(header) {
183
+ if (!header) return void 0;
184
+ const slash = header.lastIndexOf("/");
185
+ if (slash < 0) return void 0;
186
+ const tail = header.slice(slash + 1).trim();
187
+ if (tail === "*" || tail === "") return void 0;
188
+ const n = Number.parseInt(tail, 10);
189
+ if (!Number.isFinite(n) || n < 0) return void 0;
190
+ return n;
191
+ }
192
+
193
+ // src/glassbox-routes/proxy.ts
194
+ var JSON_HEADERS2 = {
195
+ "Content-Type": "application/json",
196
+ "Cache-Control": "no-store"
197
+ };
198
+ var DEFAULT_LIMIT = 20;
199
+ var MAX_LIMIT = 100;
200
+ function jsonResponse(status, body) {
201
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
202
+ }
203
+ function jsonError2(status, code) {
204
+ return jsonResponse(status, { error: code });
205
+ }
206
+ function applyScrub(row, scrub) {
207
+ if (!scrub || row == null || typeof row !== "object") return row;
208
+ try {
209
+ return scrub(row);
210
+ } catch {
211
+ return row;
212
+ }
213
+ }
214
+ function parseLimit(raw) {
215
+ if (!raw) return DEFAULT_LIMIT;
216
+ const n = Number.parseInt(raw, 10);
217
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
218
+ return Math.min(n, MAX_LIMIT);
219
+ }
220
+ function rowToSummary(row) {
221
+ return {
222
+ traceId: typeof row.handle === "string" ? row.handle : "",
223
+ appId: typeof row.app_id === "string" ? row.app_id : "",
224
+ archetype: typeof row.intent_archetype === "string" ? row.intent_archetype : "",
225
+ target: typeof row.model === "string" ? row.model : "",
226
+ createdAt: typeof row.created_at === "string" ? row.created_at : "",
227
+ tokensIn: typeof row.tokens_in === "number" ? row.tokens_in : 0,
228
+ tokensOut: typeof row.tokens_out === "number" ? row.tokens_out : 0,
229
+ estimatedCostUsd: typeof row.cost_usd_actual === "number" ? row.cost_usd_actual : 0
230
+ };
231
+ }
232
+ var INPUT_RATIO_YELLOW = 0.65;
233
+ var INPUT_RATIO_RED = 0.85;
234
+ var CACHE_HEALTH_MIN_TOKENS = 1e3;
235
+ var CACHE_RATIO_GREEN = 0.5;
236
+ var CACHE_RATIO_YELLOW = 0.1;
237
+ var FALLBACK_REASONS = /* @__PURE__ */ new Set([
238
+ "rate_limit",
239
+ "provider_auth_failed",
240
+ "provider_error",
241
+ "cliff",
242
+ "cost_cap",
243
+ "contract_violation"
244
+ ]);
245
+ function asString(v) {
246
+ return typeof v === "string" && v.length > 0 ? v : void 0;
247
+ }
248
+ function asNumber(v) {
249
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
250
+ }
251
+ function asNumberOrZero(v) {
252
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
253
+ }
254
+ function asStringArray(v) {
255
+ if (!Array.isArray(v)) return [];
256
+ const out = [];
257
+ for (const e of v) {
258
+ if (typeof e === "string") out.push(e);
259
+ }
260
+ return out;
261
+ }
262
+ function asFallbackReason(v) {
263
+ if (typeof v !== "string") return void 0;
264
+ const candidate = v;
265
+ if (candidate && FALLBACK_REASONS.has(candidate)) return candidate;
266
+ return "provider_error";
267
+ }
268
+ function rowToAdvisory(raw) {
269
+ if (!raw || typeof raw !== "object") return void 0;
270
+ const r = raw;
271
+ const level = r.level;
272
+ const code = r.code;
273
+ const message = r.message;
274
+ if (level !== "info" && level !== "warn" && level !== "critical" || typeof code !== "string" || typeof message !== "string") {
275
+ return void 0;
276
+ }
277
+ const out = { level, code, message };
278
+ const suggestion = asString(r.suggestion);
279
+ if (suggestion) out.suggestion = suggestion;
280
+ const docsUrl = asString(r.docs_url ?? r.docsUrl);
281
+ if (docsUrl) out.docsUrl = docsUrl;
282
+ const adapter = toAdapter(r.suggested_adaptation ?? r.suggestedAdaptation);
283
+ if (adapter) out.suggestedAdaptation = adapter;
284
+ return out;
285
+ }
286
+ var SECTION_KINDS = /* @__PURE__ */ new Set([
287
+ "role_intro",
288
+ "tool_call_contract",
289
+ "narration_contract",
290
+ "user_turn",
291
+ "reference",
292
+ "arbitrary"
293
+ ]);
294
+ function summarizeSectionRewrite(kind, rule) {
295
+ if (kind === "tool_call_contract" && rule === "sequential-tool-cliff-below-floor") {
296
+ return "Sequential tool pattern applied (model cliff cleared at compile time).";
297
+ }
298
+ if (kind === "narration_contract" && rule === "narration-drift-anthropic") {
299
+ return "Narration tightened for Anthropic dialect (terse-log shape preserved).";
300
+ }
301
+ if (kind === "narration_contract" && rule === "narration-thinking-leak-deepseek") {
302
+ return "Thinking-block suppression applied (DeepSeek V4 internal reasoning kept off-wire).";
303
+ }
304
+ return `Translator applied rule "${rule}" to ${kind} section.`;
305
+ }
306
+ function rowToSectionRewrite(raw) {
307
+ if (!raw || typeof raw !== "object") return void 0;
308
+ const r = raw;
309
+ const sectionId = r.sectionId ?? r.section_id;
310
+ if (typeof sectionId !== "string" || sectionId.length === 0) return void 0;
311
+ const kind = r.kind;
312
+ if (typeof kind !== "string" || !SECTION_KINDS.has(kind)) {
313
+ return void 0;
314
+ }
315
+ const rule = r.rule;
316
+ if (typeof rule !== "string" || rule.length === 0) return void 0;
317
+ return {
318
+ sectionId,
319
+ kind,
320
+ rule,
321
+ summary: summarizeSectionRewrite(kind, rule)
322
+ };
323
+ }
324
+ function toAdapter(raw) {
325
+ if (!raw || typeof raw !== "object") return void 0;
326
+ const a = raw;
327
+ if (a.parameter === "toolOrchestration" && a.value === "sequential" && typeof a.consequence === "string") {
328
+ return {
329
+ parameter: "toolOrchestration",
330
+ value: "sequential",
331
+ consequence: a.consequence
332
+ };
333
+ }
334
+ return void 0;
335
+ }
336
+ function computeHealth(args) {
337
+ const {
338
+ tokensIn,
339
+ tokensOut,
340
+ historyCacheableTokens,
341
+ inputCacheHitRatio,
342
+ fellOverFrom,
343
+ target
344
+ } = args;
345
+ const total = tokensIn + tokensOut;
346
+ const ratio = total > 0 ? tokensIn / total : 0;
347
+ let inputRatioStatus;
348
+ if (ratio > INPUT_RATIO_RED) inputRatioStatus = "red";
349
+ else if (ratio > INPUT_RATIO_YELLOW) inputRatioStatus = "yellow";
350
+ else inputRatioStatus = "green";
351
+ let cacheStatus;
352
+ if (historyCacheableTokens <= CACHE_HEALTH_MIN_TOKENS) {
353
+ cacheStatus = "na";
354
+ } else if (inputCacheHitRatio >= CACHE_RATIO_GREEN) {
355
+ cacheStatus = "green";
356
+ } else if (inputCacheHitRatio >= CACHE_RATIO_YELLOW) {
357
+ cacheStatus = "yellow";
358
+ } else {
359
+ cacheStatus = "red";
360
+ }
361
+ const fallbackStatus = fellOverFrom !== void 0 && fellOverFrom !== target ? "red" : "green";
362
+ return { inputRatioStatus, cacheStatus, fallbackStatus };
363
+ }
364
+ function rowToDetail(row) {
365
+ const summary = rowToSummary(row);
366
+ const tokensIn = summary.tokensIn;
367
+ const tokensOut = summary.tokensOut;
368
+ const cacheReadInputTokens = asNumberOrZero(row.cache_read_input_tokens);
369
+ const cacheCreationInputTokens = asNumberOrZero(
370
+ row.cache_creation_input_tokens
371
+ );
372
+ const historyCacheableTokens = asNumberOrZero(row.history_cacheable_tokens);
373
+ const inputCacheHitRatio = tokensIn > 0 ? cacheReadInputTokens / tokensIn : 0;
374
+ const fellOverFrom = asString(row.fell_over_from);
375
+ const fallbackReasonRaw = row.fallback_reason;
376
+ const fallbackReason = fellOverFrom ? asFallbackReason(fallbackReasonRaw) : void 0;
377
+ const requestedModel = asString(row.requested_model) ?? fellOverFrom;
378
+ const advisoriesRaw = Array.isArray(row.advisories) ? row.advisories : [];
379
+ const advisories = [];
380
+ for (const a of advisoriesRaw) {
381
+ const rec = rowToAdvisory(a);
382
+ if (rec) advisories.push(rec);
383
+ }
384
+ const health = computeHealth({
385
+ tokensIn,
386
+ tokensOut,
387
+ cacheReadInputTokens,
388
+ historyCacheableTokens,
389
+ inputCacheHitRatio,
390
+ fellOverFrom,
391
+ target: summary.target
392
+ });
393
+ const sectionRewritesRaw = Array.isArray(row.section_rewrites_applied) ? row.section_rewrites_applied : [];
394
+ const sectionRewritesApplied = [];
395
+ for (const e of sectionRewritesRaw) {
396
+ const rw = rowToSectionRewrite(e);
397
+ if (rw) sectionRewritesApplied.push(rw);
398
+ }
399
+ const detail = {
400
+ ...summary,
401
+ mutationsApplied: asStringArray(row.mutations_applied),
402
+ advisories,
403
+ rawRequest: asString(row.prompt_preview),
404
+ rawResponse: asString(row.response_preview),
405
+ requestedModel,
406
+ finishReason: asString(row.finish_reason),
407
+ ttftMs: asNumber(row.ttft_ms),
408
+ totalMs: asNumber(row.total_ms) ?? asNumber(row.latency_ms),
409
+ toolsCount: asNumber(row.tools_count),
410
+ historyDepth: asNumber(row.history_depth),
411
+ systemPromptChars: asNumber(row.system_prompt_chars),
412
+ cacheReadInputTokens,
413
+ cacheCreationInputTokens,
414
+ historyCacheableTokens,
415
+ inputCacheHitRatio,
416
+ fellOverFrom,
417
+ fallbackReason,
418
+ sectionRewritesApplied,
419
+ health
420
+ };
421
+ return detail;
422
+ }
423
+ function createProxyHandler(config) {
424
+ const {
425
+ installToken,
426
+ extensionId,
427
+ brainEndpoint,
428
+ brainJwt,
429
+ brainAnonKey,
430
+ appId,
431
+ scrub,
432
+ fetch: fetchImpl
433
+ } = config;
434
+ const doFetch = fetchImpl ?? ((...args) => globalThis.fetch(...args));
435
+ const base = brainEndpoint.replace(/\/+$/, "");
436
+ return async function proxy(req) {
437
+ const authFail = checkAuth(req, { installToken, extensionId });
438
+ if (authFail) return authFail;
439
+ const url = new URL(req.url);
440
+ const traceId = url.searchParams.get("traceId");
441
+ const limit = parseLimit(url.searchParams.get("limit"));
442
+ const qs = new URLSearchParams();
443
+ qs.set("app_id", `eq.${appId}`);
444
+ if (traceId) {
445
+ qs.set("handle", `eq.${traceId}`);
446
+ } else {
447
+ qs.set("order", "created_at.desc");
448
+ qs.set("limit", String(limit));
449
+ }
450
+ const brainUrl = `${base}/rest/v1/compile_outcomes?${qs.toString()}`;
451
+ let brainRes;
452
+ try {
453
+ brainRes = await doFetch(brainUrl, {
454
+ method: "GET",
455
+ headers: {
456
+ // Authorization carries the scoped JWT — drives RLS via app_id claim.
457
+ Authorization: `Bearer ${brainJwt}`,
458
+ // apikey MUST be one of the project's known keys (anon or
459
+ // service_role). Supabase rejects any other JWT here, even when
460
+ // HS256-signed with the same secret. Pre-alpha.24 this was set to
461
+ // brainJwt and silently 401'd against real Supabase. See L-117.
462
+ apikey: brainAnonKey,
463
+ Accept: "application/json"
464
+ }
465
+ });
466
+ } catch {
467
+ return jsonError2(502, "brain_unavailable");
468
+ }
469
+ if (brainRes.status === 401 || brainRes.status === 403) {
470
+ return jsonError2(500, "brain_auth_misconfig");
471
+ }
472
+ if (brainRes.status >= 500) {
473
+ return jsonError2(502, "brain_unavailable");
474
+ }
475
+ if (!brainRes.ok) {
476
+ return jsonError2(400, "bad_request");
477
+ }
478
+ let rows;
479
+ try {
480
+ rows = await brainRes.json();
481
+ } catch {
482
+ return jsonError2(502, "brain_unavailable");
483
+ }
484
+ if (!Array.isArray(rows)) {
485
+ return jsonError2(502, "brain_unavailable");
486
+ }
487
+ const scrubbed = rows.map(
488
+ (row) => applyScrub(row, scrub)
489
+ );
490
+ if (traceId) {
491
+ const first = scrubbed[0];
492
+ if (!first) return jsonError2(404, "not_found");
493
+ const detail = rowToDetail(first);
494
+ const counterfactuals = computeCounterfactuals({
495
+ servedModel: detail.target,
496
+ servedCostUsd: detail.estimatedCostUsd,
497
+ archetype: detail.archetype,
498
+ tokensIn: detail.tokensIn,
499
+ tokensOut: detail.tokensOut,
500
+ cacheReadInputTokens: detail.cacheReadInputTokens
501
+ });
502
+ detail.counterfactuals = counterfactuals;
503
+ if (detail.estimatedCostUsd > 0) {
504
+ const projected = await computeProjectedDailyCost({
505
+ appId: detail.appId,
506
+ archetype: detail.archetype,
507
+ servedCostUsd: detail.estimatedCostUsd,
508
+ brainEndpoint: base,
509
+ brainJwt,
510
+ brainAnonKey,
511
+ fetch: doFetch
512
+ });
513
+ if (projected !== void 0) {
514
+ detail.projectedDailyCostUsd = projected;
515
+ }
516
+ }
517
+ return jsonResponse(200, detail);
518
+ }
519
+ return jsonResponse(200, { traces: scrubbed.map(rowToSummary) });
520
+ };
521
+ }
522
+
523
+ // src/glassbox-routes/stream.ts
524
+ var SSE_HEADERS = {
525
+ "Content-Type": "text/event-stream",
526
+ "Cache-Control": "no-cache, no-transform",
527
+ Connection: "keep-alive",
528
+ "X-Accel-Buffering": "no"
529
+ };
530
+ function applyScrub2(event, scrub) {
531
+ if (!scrub) return event;
532
+ try {
533
+ const out = scrub(event);
534
+ if (out && typeof out === "object" && typeof out.kind === "string" && typeof out.at === "number") {
535
+ return out;
536
+ }
537
+ return event;
538
+ } catch {
539
+ return event;
540
+ }
541
+ }
542
+ function sseFrame(eventName, data) {
543
+ const safeName = eventName.replace(/[\r\n]/g, "");
544
+ return `event: ${safeName}
545
+ data: ${JSON.stringify(data)}
546
+
547
+ `;
548
+ }
549
+ function createStreamHandler(config, subscribe2, subscribeApp2) {
550
+ const { installToken, extensionId, appId, scrub } = config;
551
+ return async function stream(req) {
552
+ const authFail = checkAuth(req, { installToken, extensionId });
553
+ if (authFail) return authFail;
554
+ const url = new URL(req.url);
555
+ const traceId = url.searchParams.get("traceId");
556
+ const source = traceId ? subscribe2(traceId) : subscribeApp2({ appId });
557
+ const encoder = new TextEncoder();
558
+ let sourceReader;
559
+ let cancelled = false;
560
+ const body = new ReadableStream({
561
+ async start(controller) {
562
+ controller.enqueue(encoder.encode(sseFrame("ready", {})));
563
+ sourceReader = source.getReader();
564
+ const signal = req.signal;
565
+ if (signal) {
566
+ if (signal.aborted) {
567
+ cancelled = true;
568
+ await sourceReader.cancel();
569
+ try {
570
+ controller.close();
571
+ } catch {
572
+ }
573
+ return;
574
+ }
575
+ signal.addEventListener(
576
+ "abort",
577
+ () => {
578
+ cancelled = true;
579
+ sourceReader?.cancel().catch(() => {
580
+ });
581
+ try {
582
+ controller.close();
583
+ } catch {
584
+ }
585
+ },
586
+ { once: true }
587
+ );
588
+ }
589
+ try {
590
+ while (!cancelled) {
591
+ const { value, done } = await sourceReader.read();
592
+ if (done) break;
593
+ const scrubbed = applyScrub2(value, scrub);
594
+ controller.enqueue(
595
+ encoder.encode(sseFrame(scrubbed.kind, scrubbed))
596
+ );
597
+ }
598
+ } catch {
599
+ } finally {
600
+ try {
601
+ controller.close();
602
+ } catch {
603
+ }
604
+ try {
605
+ sourceReader?.releaseLock();
606
+ } catch {
607
+ }
608
+ }
609
+ },
610
+ cancel() {
611
+ cancelled = true;
612
+ sourceReader?.cancel().catch(() => {
613
+ });
614
+ }
615
+ });
616
+ return new Response(body, { status: 200, headers: SSE_HEADERS });
617
+ };
618
+ }
619
+
620
+ // src/glassbox-routes/index.ts
621
+ function requireString(name, value) {
622
+ if (typeof value !== "string" || value.length === 0) {
623
+ throw new Error(`createGlassboxRoutes: ${name} is required`);
624
+ }
625
+ return value;
626
+ }
627
+ function createGlassboxRoutes(config) {
628
+ const installToken = requireString("installToken", config.installToken);
629
+ const extensionId = requireString("extensionId", config.extensionId);
630
+ const brainEndpoint = requireString("brainEndpoint", config.brainEndpoint);
631
+ const brainJwt = requireString("brainJwt", config.brainJwt);
632
+ const brainAnonKey = requireString("brainAnonKey", config.brainAnonKey);
633
+ const appId = requireString("appId", config.appId);
634
+ const proxy = createProxyHandler({
635
+ installToken,
636
+ extensionId,
637
+ brainEndpoint,
638
+ brainJwt,
639
+ brainAnonKey,
640
+ appId,
641
+ scrub: config.scrub,
642
+ fetch: config.fetch
643
+ });
644
+ const stream = createStreamHandler(
645
+ {
646
+ installToken,
647
+ extensionId,
648
+ appId,
649
+ scrub: config.scrub
650
+ },
651
+ config.subscribe ?? subscribe,
652
+ config.subscribeApp ?? subscribeApp
653
+ );
654
+ return { proxy, stream };
655
+ }
656
+ export {
657
+ createGlassboxRoutes
658
+ };