@interactive-inc/claude-funnel 0.66.0 → 0.68.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 +31 -6
  2. package/dist/bin.js +292 -383
  3. package/dist/channel.d.ts +76 -52
  4. package/dist/channel.js +82 -47
  5. package/dist/{channels-DR90RmUP.d.ts → channels-B0cifCfw.d.ts} +6 -6
  6. package/dist/claude.d.ts +11 -8
  7. package/dist/claude.js +31 -15
  8. package/dist/connector-descriptor-D4XtEsH8.d.ts +215 -0
  9. package/dist/{connector-diagnostics-recorder-COtNEmUp.js → connector-diagnostics-recorder-BmOijU1p.js} +1 -0
  10. package/dist/connectors/discord.d.ts +11 -8
  11. package/dist/connectors/discord.js +2 -2
  12. package/dist/connectors/gh.d.ts +7 -6
  13. package/dist/connectors/gh.js +2 -2
  14. package/dist/connectors/schedule.d.ts +21 -17
  15. package/dist/connectors/schedule.js +3 -3
  16. package/dist/connectors/slack.d.ts +15 -10
  17. package/dist/connectors/slack.js +3 -3
  18. package/dist/diagnostic-event-CxMM5Bl2.js +78 -0
  19. package/dist/diagnostic-log-BsAg5qZK.d.ts +186 -0
  20. package/dist/diagnostics.d.ts +2 -2
  21. package/dist/diagnostics.js +3 -2
  22. package/dist/{discord-connector-DXARcO6w.js → discord-connector-CAcWifJn.js} +6 -6
  23. package/dist/{discord-connector-schema-D1dyxhXk.d.ts → discord-connector-schema-BaBZYHxK.d.ts} +0 -1
  24. package/dist/docs.d.ts +1 -1
  25. package/dist/docs.js +1 -1
  26. package/dist/doctor.d.ts +1 -1
  27. package/dist/doctor.js +1 -1
  28. package/dist/event-log.d.ts +2 -0
  29. package/dist/event-log.js +3 -0
  30. package/dist/{file-process-guard-CiBwXjzm.d.ts → file-process-guard-D-QatrwL.d.ts} +22 -18
  31. package/dist/{file-system-DHgbuTFb.d.ts → file-system-BNtl997T.d.ts} +2 -1
  32. package/dist/{flume-source-listener-FzR5lGpL.d.ts → flume-source-listener-Ba8_1Mg-.d.ts} +9 -6
  33. package/dist/{funnel-diagnostics-Cvk6Sk4x.js → funnel-diagnostics-CAG7hJP0.js} +85 -141
  34. package/dist/{funnel-diagnostics-kbd27d7I.d.ts → funnel-diagnostics-CDrZznTB.d.ts} +18 -18
  35. package/dist/{funnel-docs-C-ge0MuB.js → funnel-docs-CI4hMkhq.js} +12 -3
  36. package/dist/{funnel-doctor-CVTNUtDn.d.ts → funnel-doctor-CY4S5cnz.d.ts} +20 -9
  37. package/dist/{funnel-doctor-XrI2GBH8.js → funnel-doctor-Csw5qjcH.js} +1 -0
  38. package/dist/{funnel-error-0t1MK1R6.js → funnel-error-DoSBCogD.js} +13 -0
  39. package/dist/{funnel-recovery-D8MkiTpk.d.ts → funnel-recovery-BwcSNNSN.d.ts} +1 -2
  40. package/dist/{funnel-recovery-DKnEutUS.js → funnel-recovery-DYbSYGWC.js} +1 -0
  41. package/dist/gateway/daemon.js +60 -139
  42. package/dist/gateway.d.ts +2 -3
  43. package/dist/gateway.js +2 -2
  44. package/dist/{gh-connector-BUGCOEWS.js → gh-connector-DMwI6zJA.js} +5 -5
  45. package/dist/{gh-connector-schema-BCHEqpUg.d.ts → gh-connector-schema-BrNsCl1T.d.ts} +0 -1
  46. package/dist/{index-BU1TtuFy.d.ts → index-fHrKn4jm.d.ts} +210 -40
  47. package/dist/index.d.ts +125 -115
  48. package/dist/index.js +242 -57
  49. package/dist/{local-config-json-schema-DexV8vX3.js → local-config-json-schema-_-YVHX4c.js} +4 -4
  50. package/dist/local-config.d.ts +2 -3
  51. package/dist/local-config.js +1 -1
  52. package/dist/logger.d.ts +17 -265
  53. package/dist/logger.js +6 -141
  54. package/dist/memory-event-log-BY8XjFzi.js +147 -0
  55. package/dist/{memory-token-prompter-D0kIdhar.d.ts → memory-token-prompter-B20tl_aU.d.ts} +2 -3
  56. package/dist/{node-file-system-BOXIHW_Q.js → node-file-system-xJx7dymE.js} +11 -3
  57. package/dist/{profiles-M24JrcOh.d.ts → profiles-CePRnv3F.d.ts} +3 -4
  58. package/dist/{profiles-ZHLONml4.js → profiles-KpN7_gBw.js} +19 -5
  59. package/dist/profiles.d.ts +1 -1
  60. package/dist/profiles.js +1 -1
  61. package/dist/recovery.d.ts +1 -1
  62. package/dist/recovery.js +1 -1
  63. package/dist/{schedule-connector-9k3gOIgl.js → schedule-connector-BXUOYgwZ.js} +63 -99
  64. package/dist/schedule-connector-schema-CNV8PgWX.js +137 -0
  65. package/dist/schedule-connector-schema-Tq5kJul-.d.ts +127 -0
  66. package/dist/{settings-reader-BnKIbmsD.d.ts → settings-reader-CyTpbAre.d.ts} +1 -2
  67. package/dist/{settings-schema-IShSMm5G.d.ts → settings-schema-B-CKLuST.d.ts} +0 -1
  68. package/dist/{settings-store-ccv291GV.js → settings-store-ZFWBhOnp.js} +12 -3
  69. package/dist/{slack-connector-CxpWagbT.js → slack-connector-CC3V2VXQ.js} +10 -9
  70. package/dist/{slack-event-processor-xFDG3US0.js → slack-event-processor-Cs_YuI3w.js} +2 -2
  71. package/dist/{slack-event-processor-BDTr62cN.d.ts → slack-event-processor-DmAlq4cV.d.ts} +0 -1
  72. package/dist/{slot-fields-D-pvMgTK.js → slot-fields-eCWizJqS.js} +4 -4
  73. package/dist/{sqlite-diagnostic-log-CwhXE9Qh.js → sqlite-diagnostic-log-Qs4cf9I_.js} +92 -100
  74. package/dist/sqlite-event-log-BYgCFMxC.d.ts +284 -0
  75. package/dist/{funnel-log-sqlite-sink-DLYkY0pZ.js → sqlite-event-log-eZuXKszJ.js} +53 -12
  76. package/dist/{yaml-render-DVEtYuF1.js → yaml-render-DxxRQT-V.js} +3 -3
  77. package/package.json +25 -10
  78. package/dist/broadcaster-DVNcw00K.d.ts +0 -138
  79. package/dist/connector-descriptor-CmyHlEiT.d.ts +0 -380
  80. package/dist/diagnostic-sql-reader-oXZnWFf_.js +0 -83
  81. package/dist/file-system-Wvzc2ePY.js +0 -9
  82. package/dist/logger-B6iyNbxM.js +0 -9
  83. package/dist/schedule-connector-schema-D7ZEKvit.d.ts +0 -49
  84. package/dist/schedule-connector-schema-DKEPZnVv.js +0 -31
  85. package/dist/settings-reader-9FcX3qS1.js +0 -11
  86. /package/dist/{connector-adapter-Dvs8N7ew.js → connector-adapter-BL2ehMej.js} +0 -0
  87. /package/dist/{connector-listener-mPGZYa8e.js → connector-listener-BcQMzY6Q.js} +0 -0
  88. /package/dist/{discord-connector-schema-B4YpWpR3.js → discord-connector-schema-saAGuPmJ.js} +0 -0
  89. /package/dist/{error-message-of-ColuYmAk.js → error-message-of-BG4ezZ_s.js} +0 -0
  90. /package/dist/{funnel-docs-Cd66sKyV.d.ts → funnel-docs-BJWawfWI.d.ts} +0 -0
  91. /package/dist/{gh-connector-schema-CAqIhzGr.js → gh-connector-schema-DmZTrzUu.js} +0 -0
  92. /package/dist/{logger-Bc_Ooypb.d.ts → logger-mMZvblTx.d.ts} +0 -0
  93. /package/dist/{loopback-fetch-CVNuN3YZ.js → loopback-fetch-HP-bo9Kv.js} +0 -0
  94. /package/dist/{node-http-client-u00atiKx.js → node-http-client-SX3xFh4h.js} +0 -0
  95. /package/dist/{node-process-runner-DxTvycoK.js → node-process-runner-D4rzPl8Q.js} +0 -0
  96. /package/dist/{resolve-connector-token-DxDG9mhf.js → resolve-connector-token-BwiJCu82.js} +0 -0
  97. /package/dist/{slack-connector-schema-Dem8to4P.js → slack-connector-schema-EGVuuqSX.js} +0 -0
@@ -1,6 +1,6 @@
1
- import { t as errorMessageOf } from "./error-message-of-ColuYmAk.js";
2
- import { t as FunnelConnectorListener } from "./connector-listener-mPGZYa8e.js";
3
- import { t as FunnelConnectorDiagnosticsRecorder } from "./connector-diagnostics-recorder-COtNEmUp.js";
1
+ import { t as errorMessageOf } from "./error-message-of-BG4ezZ_s.js";
2
+ import { t as FunnelConnectorListener } from "./connector-listener-BcQMzY6Q.js";
3
+ import { t as FunnelConnectorDiagnosticsRecorder } from "./connector-diagnostics-recorder-BmOijU1p.js";
4
4
  import { Flume, createFlumeDefaultDeps } from "@interactive-inc/flume";
5
5
  //#region lib/engine/connectors/flume-deps.ts
6
6
  /**
@@ -46,7 +46,7 @@ const flumeLogHandler = (logger) => {
46
46
  };
47
47
  };
48
48
  const buildMeta = (log) => {
49
- const meta = { ...log.detail ?? {} };
49
+ const meta = { ...log.detail };
50
50
  if (log.error) {
51
51
  meta.error = log.error.message;
52
52
  if (log.error.stack) meta.stack = log.error.stack;
@@ -1,15 +1,15 @@
1
- import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-CVNuN3YZ.js";
2
- import { t as NodeFunnelFileSystem } from "./node-file-system-BOXIHW_Q.js";
3
- import { t as NodeFunnelProcessRunner } from "./node-process-runner-DxTvycoK.js";
4
- import { n as FUNNEL_DIR, s as resolveFunnelPort } from "./settings-store-ccv291GV.js";
5
- import { t as FunnelAuthFailedError } from "./funnel-error-0t1MK1R6.js";
6
- import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-oXZnWFf_.js";
7
- import { t as FunnelLogSqliteSink } from "./funnel-log-sqlite-sink-DLYkY0pZ.js";
1
+ import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-HP-bo9Kv.js";
2
+ import { t as NodeFunnelFileSystem } from "./node-file-system-xJx7dymE.js";
3
+ import { t as NodeFunnelProcessRunner } from "./node-process-runner-D4rzPl8Q.js";
4
+ import { n as FUNNEL_DIR, s as resolveFunnelPort } from "./settings-store-ZFWBhOnp.js";
5
+ import { t as FunnelAuthFailedError } from "./funnel-error-DoSBCogD.js";
6
+ import { n as diagnosticEventOfProcessed, t as diagnosticConnectionEventOf } from "./diagnostic-event-CxMM5Bl2.js";
7
+ import { t as SqliteEventLog } from "./sqlite-event-log-eZuXKszJ.js";
8
8
  import { dirname, join } from "node:path";
9
9
  import { chmodSync, existsSync, mkdirSync } from "node:fs";
10
10
  import { homedir, tmpdir } from "node:os";
11
11
  import { z } from "zod";
12
- import { timingSafeEqual } from "node:crypto";
12
+ import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
13
13
  import { createFactory } from "hono/factory";
14
14
  import { HTTPException } from "hono/http-exception";
15
15
  import { zValidator } from "@hono/zod-validator";
@@ -128,6 +128,7 @@ var ConnectorDiagnosticLog = class {};
128
128
  * rejecting a malformed payload.
129
129
  */
130
130
  var MemoryConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
131
+ now;
131
132
  raws = [];
132
133
  processeds = [];
133
134
  connections = [];
@@ -158,21 +159,31 @@ var MemoryConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
158
159
  });
159
160
  }
160
161
  queryRaw(query) {
161
- return takeRecent(this.raws.filter((event) => matches(event, query)), query.limit);
162
+ const matched = this.raws.filter((event) => {
163
+ if (!matches(event, query)) return false;
164
+ if (query.eventId !== void 0 && event.eventId !== query.eventId) return false;
165
+ return true;
166
+ });
167
+ return takeRecent(matched, query.limit);
162
168
  }
163
169
  queryProcessed(query) {
164
- return takeRecent(this.processeds.filter((event) => {
170
+ const matched = this.processeds.filter((event) => {
165
171
  if (!matches(event, query)) return false;
172
+ if (query.eventId !== void 0 && event.eventId !== query.eventId) return false;
166
173
  if (query.outcome !== void 0 && event.outcome !== query.outcome) return false;
174
+ if (query.outcomePrefix !== void 0 && !event.outcome.startsWith(query.outcomePrefix)) return false;
167
175
  return true;
168
- }), query.limit);
176
+ });
177
+ return takeRecent(matched, query.limit);
169
178
  }
170
179
  queryConnection(query) {
171
- return takeRecent(this.connections.filter((event) => {
180
+ const matched = this.connections.filter((event) => {
172
181
  if (!matches(event, query)) return false;
173
182
  if (query.status !== void 0 && event.status !== query.status) return false;
183
+ if (query.statuses !== void 0 && !query.statuses.includes(event.status)) return false;
174
184
  return true;
175
- }), query.limit);
185
+ });
186
+ return takeRecent(matched, query.limit);
176
187
  }
177
188
  clear() {
178
189
  this.raws.length = 0;
@@ -185,6 +196,7 @@ const matches = (event, query) => {
185
196
  if (query.type !== void 0 && event.type !== query.type) return false;
186
197
  if (query.connectorId !== void 0 && event.connectorId !== query.connectorId) return false;
187
198
  if (query.channelId !== void 0 && event.channelId !== query.channelId) return false;
199
+ if ("seq" in event && query.seq !== void 0 && event.seq !== query.seq) return false;
188
200
  return true;
189
201
  };
190
202
  const takeRecent = (events, limit) => {
@@ -279,7 +291,8 @@ var FunnelChannelPublisher = class {
279
291
  */
280
292
  const requireBearerToken = (deps) => {
281
293
  return async (c, next) => {
282
- if (!constantTimeEqual((c.req.header("authorization") ?? "").match(/^Bearer\s+(.+)$/i)?.[1] ?? "", deps.expected)) return c.text("unauthorized", 401);
294
+ const presented = (c.req.header("authorization") ?? "").match(/^Bearer\s+(.+)$/i)?.[1] ?? "";
295
+ if (!constantTimeEqual(presented, deps.expected)) return c.text("unauthorized", 401);
283
296
  return await next();
284
297
  };
285
298
  };
@@ -447,7 +460,6 @@ var FunnelBroadcaster = class {
447
460
  offset: this.latestOffset
448
461
  };
449
462
  const payload = JSON.stringify(event);
450
- meta?.connector;
451
463
  this.eventsBroadcast += 1;
452
464
  this.lastBroadcastAt = this.now();
453
465
  if (this.replayBufferSize > 0) {
@@ -574,7 +586,7 @@ var SqliteFunnelEventLog = class extends FunnelEventLog {
574
586
  this.now = props.now ?? (() => Date.now());
575
587
  this.logger = props.logger;
576
588
  this.onError = props.onError;
577
- this.sink = new FunnelLogSqliteSink({
589
+ this.sink = new SqliteEventLog({
578
590
  path: props.path,
579
591
  indexes: ["channel_id", "connector_id"],
580
592
  extractIndexes: (event) => ({
@@ -626,7 +638,10 @@ var SqliteFunnelEventLog = class extends FunnelEventLog {
626
638
  * so this returns the full slice and lets the caller filter.
627
639
  */
628
640
  loadSince(since) {
629
- const records = this.sink.query({ sinceSeq: since });
641
+ const records = this.sink.query({
642
+ sinceSeq: since,
643
+ limit: Number.MAX_SAFE_INTEGER
644
+ });
630
645
  const out = [];
631
646
  for (const record of records) out.push({
632
647
  content: record.event.content,
@@ -885,7 +900,7 @@ var FunnelListenerRegistry = class FunnelListenerRegistry {
885
900
  async stopAll() {
886
901
  this.stopHealthCheck();
887
902
  this.pendingRetry.clear();
888
- for (const [, entry] of [...this.running.entries()]) await this.stop(entry.channelName, entry.config.name);
903
+ for (const [, entry] of this.running.entries()) await this.stop(entry.channelName, entry.config.name);
889
904
  }
890
905
  ensureStats(key) {
891
906
  const existing = this.stats.get(key);
@@ -932,7 +947,7 @@ var FunnelListenerRegistry = class FunnelListenerRegistry {
932
947
  this.healthCheckInFlight = true;
933
948
  try {
934
949
  const dead = [];
935
- for (const [key, entry] of [...this.running.entries()]) {
950
+ for (const [key, entry] of this.running.entries()) {
936
951
  if (entry.listener.isAlive()) {
937
952
  this.failureCounts.delete(key);
938
953
  continue;
@@ -945,7 +960,7 @@ var FunnelListenerRegistry = class FunnelListenerRegistry {
945
960
  }
946
961
  await Promise.all(dead.map((target) => this.recoverDead(target.channelName, target.connectorName, target.type)));
947
962
  const retries = [];
948
- for (const [key, pending] of [...this.pendingRetry.entries()]) {
963
+ for (const [key, pending] of this.pendingRetry.entries()) {
949
964
  if (this.running.has(key)) {
950
965
  this.pendingRetry.delete(key);
951
966
  continue;
@@ -1181,19 +1196,6 @@ const channelsPublishHandler = factory.createHandlers(zParam(z.object({ channel:
1181
1196
  });
1182
1197
  //#endregion
1183
1198
  //#region lib/gateway/routes/debug.ts
1184
- const extractPreview = (payload) => {
1185
- if (typeof payload !== "string" || payload.length === 0) return null;
1186
- try {
1187
- const parsed = JSON.parse(payload);
1188
- if (parsed !== null && typeof parsed === "object" && "text" in parsed) {
1189
- const text = String(parsed.text);
1190
- return text.length > 80 ? `${text.slice(0, 80)}…` : text;
1191
- }
1192
- } catch {
1193
- return payload.length > 80 ? `${payload.slice(0, 80)}…` : payload;
1194
- }
1195
- return payload.length > 80 ? `${payload.slice(0, 80)}…` : payload;
1196
- };
1197
1199
  const buildChannelDiagnosis = (channel) => {
1198
1200
  const rootCause = (channel.connectionErrors[channel.connectionErrors.length - 1] ?? null)?.detail ?? null;
1199
1201
  if (channel.connectors.length === 0) return {
@@ -1242,11 +1244,6 @@ const debugHandler = factory.createHandlers(async (c) => {
1242
1244
  const gatewayListeners = deps.registry.list();
1243
1245
  const gatewayClients = deps.broadcaster.listChannels();
1244
1246
  const metrics = deps.broadcaster.getMetrics();
1245
- const tmpDir = funnelTmpDir();
1246
- const rawPath = join(tmpDir, "connector-raw.db");
1247
- const processedPath = join(tmpDir, "connector-processed.db");
1248
- const connectionPath = join(tmpDir, "connector-connection.db");
1249
- const hasStore = existsSync(rawPath) && existsSync(processedPath) && existsSync(connectionPath);
1250
1247
  const channels = targetChannels.map((ch) => {
1251
1248
  const listenerEntry = gatewayListeners.find((l) => l.channelName === ch.name) ?? null;
1252
1249
  const listener = listenerEntry ? {
@@ -1256,61 +1253,30 @@ const debugHandler = factory.createHandlers(async (c) => {
1256
1253
  lastEventAt: listenerEntry.lastEventAt
1257
1254
  } : null;
1258
1255
  const claudeClients = gatewayClients.filter((cl) => cl.channel === ch.id || cl.channel === ch.name).length;
1259
- const recentEvents = [];
1260
- const connectionErrors = [];
1261
- if (hasStore) {
1262
- const reader = new ConnectorDiagnosticSqlReader({
1263
- rawPath,
1264
- processedPath,
1265
- connectionPath
1266
- });
1267
- const rows = (() => {
1268
- try {
1269
- return reader.query("SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT 10", [ch.id]);
1270
- } finally {
1271
- reader.close();
1272
- }
1273
- })();
1274
- if (!(rows instanceof Error)) for (const row of [...rows].reverse()) {
1275
- const rawPayload = typeof row.payload === "string" ? row.payload : null;
1276
- let payloadParsed = null;
1277
- if (rawPayload) try {
1278
- const parsed = JSON.parse(rawPayload);
1279
- if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) payloadParsed = parsed;
1280
- } catch {
1281
- payloadParsed = null;
1282
- }
1283
- recentEvents.push({
1284
- seq: typeof row.seq === "number" ? row.seq : null,
1285
- ts: typeof row.ts === "number" ? row.ts : null,
1286
- type: typeof row.type === "string" ? row.type : "?",
1287
- outcome: typeof row.outcome === "string" ? row.outcome : "?",
1288
- payload: rawPayload,
1289
- payloadParsed,
1290
- preview: extractPreview(row.payload)
1291
- });
1256
+ const recentEvents = (() => {
1257
+ if (!deps.diagnosticLog) return [];
1258
+ try {
1259
+ return deps.diagnosticLog.queryProcessed({
1260
+ channelId: ch.id,
1261
+ limit: 10
1262
+ }).map(diagnosticEventOfProcessed);
1263
+ } catch {
1264
+ return [];
1292
1265
  }
1293
- if (listener && (!listener.alive || listener.errors > 0) || !listener) {
1294
- const errReader = new ConnectorDiagnosticSqlReader({
1295
- rawPath,
1296
- processedPath,
1297
- connectionPath
1298
- });
1299
- const errRows = (() => {
1300
- try {
1301
- return errReader.query("SELECT ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT 3", [ch.id]);
1302
- } finally {
1303
- errReader.close();
1304
- }
1305
- })();
1306
- if (!(errRows instanceof Error)) for (const row of [...errRows].reverse()) connectionErrors.push({
1307
- ts: typeof row.ts === "number" ? row.ts : null,
1308
- type: typeof row.type === "string" ? row.type : "?",
1309
- status: typeof row.status === "string" ? row.status : "?",
1310
- detail: typeof row.detail === "string" && row.detail.length > 0 ? row.detail : null
1311
- });
1266
+ })();
1267
+ const needsConnErrors = listener && (!listener.alive || listener.errors > 0) || !listener;
1268
+ const connectionErrors = (() => {
1269
+ if (!needsConnErrors || !deps.diagnosticLog) return [];
1270
+ try {
1271
+ return deps.diagnosticLog.queryConnection({
1272
+ channelId: ch.id,
1273
+ statuses: ["auth-failed", "error"],
1274
+ limit: 3
1275
+ }).map(diagnosticConnectionEventOf);
1276
+ } catch {
1277
+ return [];
1312
1278
  }
1313
- }
1279
+ })();
1314
1280
  const base = {
1315
1281
  id: ch.id,
1316
1282
  name: ch.name,
@@ -1410,15 +1376,26 @@ function buildGatewayRoutes() {
1410
1376
  }
1411
1377
  const gatewayRoutes = buildGatewayRoutes();
1412
1378
  //#endregion
1379
+ //#region lib/gateway/default-event-db-path.ts
1380
+ /**
1381
+ * Gives each durable gateway endpoint its own replay database. Port zero is
1382
+ * ephemeral, so each construction receives a fresh scope instead of pretending
1383
+ * that replay can survive a restart onto a different assigned port.
1384
+ */
1385
+ function defaultEventDbPath(props) {
1386
+ const endpoint = props.port === 0 ? randomUUID() : String(props.port);
1387
+ const scope = createHash("sha256").update(`${props.funnelDir}\0${endpoint}`).digest("hex").slice(0, 20);
1388
+ return join(props.tmpDir, "events", `${scope}.db`);
1389
+ }
1390
+ //#endregion
1413
1391
  //#region lib/gateway/gateway-server.ts
1414
1392
  const DEFAULT_HOST = "127.0.0.1";
1415
- const LOOPBACK_HOSTS = new Set([
1393
+ const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
1416
1394
  "127.0.0.1",
1417
1395
  "localhost",
1418
1396
  "::1",
1419
1397
  "::ffff:127.0.0.1"
1420
1398
  ]);
1421
- const defaultDbPath = () => join(funnelTmpDir(), "events.db");
1422
1399
  const defaultOnError = () => {};
1423
1400
  /**
1424
1401
  * In-process gateway: runs `Bun.serve` (HTTP + WebSocket /ws), boots connector
@@ -1449,6 +1426,7 @@ var FunnelGatewayServer = class {
1449
1426
  nowMs;
1450
1427
  extraRoutes;
1451
1428
  ownsEventLog;
1429
+ diagnosticLog;
1452
1430
  startedAt = null;
1453
1431
  server = null;
1454
1432
  disposed = false;
@@ -1456,16 +1434,21 @@ var FunnelGatewayServer = class {
1456
1434
  this.channels = deps.channels;
1457
1435
  this.configuredPort = deps.port ?? resolveFunnelPort();
1458
1436
  this.configuredHostname = deps.hostname ?? DEFAULT_HOST;
1459
- this.dbPath = deps.dbPath ?? defaultDbPath();
1437
+ this.dir = deps.dir ?? FUNNEL_DIR;
1438
+ this.dbPath = deps.dbPath ?? defaultEventDbPath({
1439
+ tmpDir: deps.tmpDir ?? funnelTmpDir(),
1440
+ funnelDir: this.dir,
1441
+ port: this.configuredPort
1442
+ });
1460
1443
  this.process = deps.process;
1461
1444
  this.logger = deps.logger;
1462
1445
  this.onError = deps.onError ?? defaultOnError;
1463
1446
  this.selfPid = deps.selfPid ?? globalThis.process.pid;
1464
- this.dir = deps.dir ?? FUNNEL_DIR;
1465
1447
  this.killCompetingSlack = deps.killCompetingSlack ?? true;
1466
1448
  this.token = deps.token ?? "";
1467
1449
  this.allowInsecureHost = deps.allowInsecureHost ?? false;
1468
1450
  this.extraRoutes = deps.extraRoutes ?? null;
1451
+ this.diagnosticLog = deps.diagnosticLog;
1469
1452
  const clock = deps.clock;
1470
1453
  this.nowMs = clock ? () => clock.millis() : () => Date.now();
1471
1454
  if (deps.eventLog) {
@@ -1658,7 +1641,8 @@ var FunnelGatewayServer = class {
1658
1641
  registry: this.registry,
1659
1642
  channels: this.channels,
1660
1643
  uptimeMs: () => this.startedAt ? this.nowMs() - this.startedAt : 0,
1661
- emit: (input) => this.emit(input)
1644
+ emit: (input) => this.emit(input),
1645
+ diagnosticLog: this.diagnosticLog
1662
1646
  });
1663
1647
  return next();
1664
1648
  });
@@ -1923,7 +1907,7 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
1923
1907
  ...ageCap,
1924
1908
  ...rawMax !== void 0 ? { maxRows: rawMax } : {}
1925
1909
  };
1926
- this.raw = new FunnelLogSqliteSink({
1910
+ this.raw = new SqliteEventLog({
1927
1911
  path: props.rawPath,
1928
1912
  indexes: [
1929
1913
  "event_id",
@@ -1937,7 +1921,7 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
1937
1921
  }),
1938
1922
  ...rawCap
1939
1923
  });
1940
- this.processed = new FunnelLogSqliteSink({
1924
+ this.processed = new SqliteEventLog({
1941
1925
  path: props.processedPath,
1942
1926
  indexes: [
1943
1927
  "event_id",
@@ -1953,7 +1937,7 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
1953
1937
  }),
1954
1938
  ...verdictCap
1955
1939
  });
1956
- this.connection = new FunnelLogSqliteSink({
1940
+ this.connection = new SqliteEventLog({
1957
1941
  path: props.connectionPath,
1958
1942
  indexes: [
1959
1943
  "connector_id",
@@ -2019,10 +2003,13 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
2019
2003
  });
2020
2004
  }
2021
2005
  queryRaw(query) {
2006
+ const where = buildWhere(query);
2007
+ if (query.eventId !== void 0) where.event_id = query.eventId;
2022
2008
  return this.raw.query({
2023
2009
  ...query.type !== void 0 ? { type: query.type } : {},
2010
+ ...query.seq !== void 0 ? { seq: query.seq } : {},
2024
2011
  ...query.limit !== void 0 ? { limit: query.limit } : {},
2025
- where: buildWhere(query),
2012
+ where,
2026
2013
  order: "desc"
2027
2014
  }).map((record) => ({
2028
2015
  seq: record.seq,
@@ -2036,11 +2023,14 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
2036
2023
  }
2037
2024
  queryProcessed(query) {
2038
2025
  const where = buildWhere(query);
2026
+ if (query.eventId !== void 0) where.event_id = query.eventId;
2039
2027
  if (query.outcome !== void 0) where.outcome = query.outcome;
2040
2028
  return this.processed.query({
2041
2029
  ...query.type !== void 0 ? { type: query.type } : {},
2030
+ ...query.seq !== void 0 ? { seq: query.seq } : {},
2042
2031
  ...query.limit !== void 0 ? { limit: query.limit } : {},
2043
2032
  where,
2033
+ ...query.outcomePrefix !== void 0 ? { wherePrefix: { outcome: query.outcomePrefix } } : {},
2044
2034
  order: "desc"
2045
2035
  }).map((record) => ({
2046
2036
  seq: record.seq,
@@ -2058,8 +2048,10 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
2058
2048
  if (query.status !== void 0) where.status = query.status;
2059
2049
  return this.connection.query({
2060
2050
  ...query.type !== void 0 ? { type: query.type } : {},
2051
+ ...query.seq !== void 0 ? { seq: query.seq } : {},
2061
2052
  ...query.limit !== void 0 ? { limit: query.limit } : {},
2062
2053
  where,
2054
+ ...query.statuses !== void 0 ? { whereIn: { status: query.statuses } } : {},
2063
2055
  order: "desc"
2064
2056
  }).map((record) => ({
2065
2057
  seq: record.seq,