@interactive-inc/claude-funnel 0.58.0 → 0.58.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 (43) hide show
  1. package/dist/bin.js +1 -1
  2. package/dist/claude.d.ts +5 -5
  3. package/dist/claude.js +1 -1
  4. package/dist/connectors/discord.d.ts +3 -3
  5. package/dist/connectors/gh.d.ts +4 -4
  6. package/dist/connectors/schedule.d.ts +1 -1
  7. package/dist/connectors/slack.d.ts +2 -2
  8. package/dist/diagnostics.d.ts +1 -1
  9. package/dist/docs.d.ts +1 -1
  10. package/dist/doctor.d.ts +1 -1
  11. package/dist/{file-process-guard-DI1742H5.d.ts → file-process-guard-B3IFCj_G.d.ts} +5 -5
  12. package/dist/{funnel-diagnostics-qWy5tPSq.d.ts → funnel-diagnostics-K-wON25Y.d.ts} +1 -1
  13. package/dist/{funnel-doctor-BF3Rdgk0.d.ts → funnel-doctor-vxO96TCA.d.ts} +2 -2
  14. package/dist/funnel-log-sqlite-sink-B_5_4ybn.js +301 -0
  15. package/dist/{funnel-recovery-BUBsu7WX.d.ts → funnel-recovery-COExL9MD.d.ts} +1 -1
  16. package/dist/gateway.d.ts +2 -2
  17. package/dist/gateway.js +1 -1
  18. package/dist/{index-tP67P1Sy.d.ts → index-B9iyugar.d.ts} +13 -13
  19. package/dist/index.d.ts +16 -16
  20. package/dist/index.js +1 -1
  21. package/dist/{local-config-sync-BY20ixEV.d.ts → local-config-sync--f739oCJ.d.ts} +8 -8
  22. package/dist/local-config.d.ts +2 -2
  23. package/dist/local-config.js +1 -1
  24. package/dist/logger.d.ts +384 -0
  25. package/dist/logger.js +281 -0
  26. package/dist/{memory-diagnostic-log-CvqobDDs.js → memory-diagnostic-log-5LzwJ_F7.js} +1 -298
  27. package/dist/{memory-token-prompter-DOgptiIb.d.ts → memory-token-prompter-BlFwK9k7.d.ts} +2 -2
  28. package/dist/{profiles-EHTeCOqB.d.ts → profiles-g2qGVOWv.d.ts} +3 -3
  29. package/dist/profiles.d.ts +1 -1
  30. package/dist/recovery.d.ts +1 -1
  31. package/dist/{schedule-listener-DKh0hnkK.d.ts → schedule-listener-DoMPjHZj.d.ts} +2 -2
  32. package/dist/{settings-reader-CBrgz01o.d.ts → settings-reader-DPwqOVUm.d.ts} +1 -1
  33. package/dist/{slack-listener-DFW9vck4.d.ts → slack-listener-Dj9NFbAJ.d.ts} +1 -1
  34. package/package.json +1 -1
  35. /package/dist/{connector-adapter-BkYC6qiK.d.ts → connector-adapter-DGacCppE.d.ts} +0 -0
  36. /package/dist/{diagnostic-log-Bxe7Bbvw.d.ts → diagnostic-log-Cb3v8P7p.d.ts} +0 -0
  37. /package/dist/{discord-connector-schema-CWHVNIcB.d.ts → discord-connector-schema-CQyfDkLD.d.ts} +0 -0
  38. /package/dist/{file-system-Wub9Nto4.d.ts → file-system-DxpnnUVb.d.ts} +0 -0
  39. /package/dist/{funnel-docs-dXPokzr5.d.ts → funnel-docs-DYBs1-H_.d.ts} +0 -0
  40. /package/dist/{gh-connector-schema-CU1ojfIF.d.ts → gh-connector-schema-CZzwzvqY.d.ts} +0 -0
  41. /package/dist/{memory-token-prompter-vBXxY20-.js → memory-token-prompter-C7vREzCL.js} +0 -0
  42. /package/dist/{process-runner-D5I_jhYQ.d.ts → process-runner-Cx5O_fTf.d.ts} +0 -0
  43. /package/dist/{settings-schema-zhnMIa8I.d.ts → settings-schema-1hh11jnN.d.ts} +0 -0
@@ -2,11 +2,11 @@ import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-DUcZgN2Q.js"
2
2
  import { t as NodeFunnelFileSystem } from "./node-file-system-BcrmWN9I.js";
3
3
  import { r as FUNNEL_DIR, s as resolveFunnelPort, t as gatewayLoopbackUrl } from "./gateway-base-url-6foMXfFf.js";
4
4
  import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-CzYgZpq2.js";
5
+ import { t as FunnelLogSqliteSink } from "./funnel-log-sqlite-sink-B_5_4ybn.js";
5
6
  import { dirname, join } from "node:path";
6
7
  import { chmodSync, existsSync, mkdirSync } from "node:fs";
7
8
  import { z } from "zod";
8
9
  import { homedir, tmpdir } from "node:os";
9
- import { Database } from "bun:sqlite";
10
10
  import { timingSafeEqual } from "node:crypto";
11
11
  import { createFactory } from "hono/factory";
12
12
  import { HTTPException } from "hono/http-exception";
@@ -360,303 +360,6 @@ const funnelEventSchema = z.object({
360
360
  */
361
361
  var FunnelEventLog = class {};
362
362
  //#endregion
363
- //#region lib/logger/funnel-log-sqlite-sink.ts
364
- /** Conservative whitelist for column names interpolated into SQL. */
365
- const COLUMN_NAME_RE = /^[a-z_][a-z0-9_]*$/;
366
- /** How many inserts between on-disk size checks (see insertsSinceByteCheck). */
367
- const BYTE_CHECK_INTERVAL = 500;
368
- const RESERVED_COLUMNS = new Set([
369
- "seq",
370
- "ts",
371
- "type",
372
- "event"
373
- ]);
374
- /**
375
- * Schema versions. Each entry is the list of DDL statements that take the
376
- * database from version i to version i + 1. Migrations run in a transaction
377
- * so a partial failure rolls back. Adding a new version is append-only —
378
- * never edit a published one. Caller-defined index columns are added
379
- * dynamically on construct (independent of versioned migrations) because
380
- * they are configuration, not schema evolution.
381
- */
382
- const MIGRATIONS = [[
383
- "CREATE TABLE IF NOT EXISTS logs (seq INTEGER PRIMARY KEY, ts INTEGER NOT NULL, type TEXT, event TEXT NOT NULL)",
384
- "CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts)",
385
- "CREATE INDEX IF NOT EXISTS idx_logs_type ON logs (type)"
386
- ], [
387
- "DROP TABLE IF EXISTS leuco_log",
388
- "CREATE TABLE IF NOT EXISTS logs (seq INTEGER PRIMARY KEY, ts INTEGER NOT NULL, type TEXT, event TEXT NOT NULL)",
389
- "CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts)",
390
- "CREATE INDEX IF NOT EXISTS idx_logs_type ON logs (type)"
391
- ]];
392
- /**
393
- * SQLite-backed sink built on `bun:sqlite`. Implements both primary and
394
- * relay roles so the same instance can own seq generation for one bus and
395
- * mirror records from another (e.g. cross-process replication, restore
396
- * from a backup stream).
397
- *
398
- * Concurrency model: seq is `INTEGER PRIMARY KEY`, so SQLite assigns it
399
- * atomically via `lastInsertRowid`. Two `FunnelLog` instances pointed
400
- * at the same database file therefore see one monotonically increasing
401
- * seq stream without any bus-level coordination — the database itself is
402
- * the synchronization point.
403
- *
404
- * Schema is version-managed via `PRAGMA user_version`. Migrations are
405
- * append-only and run in a transaction on every construct so a partial
406
- * upgrade rolls back cleanly. Caller-defined `indexes` are layered on top
407
- * via `ALTER TABLE ADD COLUMN` + `CREATE INDEX IF NOT EXISTS`, so adding
408
- * a new index to an existing database is a no-downtime operation.
409
- *
410
- * Type safety: the second generic parameter `I` is the literal tuple of
411
- * index column names. `extractIndexes` and `query({ where })` are
412
- * both type-checked against this tuple, so a typo at the call site is a
413
- * compile-time error rather than a silent miss at runtime.
414
- *
415
- * Retention is bounded by `maxRows` and/or `maxAgeMs`. Both run on every
416
- * insert as a single indexed DELETE that no-ops below the cap.
417
- *
418
- * Bulk inserts use `insertMany`, which wraps the batch in one transaction
419
- * for ~10–100x throughput at the cost of one fsync per batch instead of
420
- * one per row.
421
- */
422
- var FunnelLogSqliteSink = class {
423
- db;
424
- maxRows;
425
- maxAgeMs;
426
- maxBytes;
427
- targetBytes;
428
- now;
429
- indexes;
430
- extractIndexes;
431
- insertStmt;
432
- insertWithSeqStmt;
433
- maxSeqStmt;
434
- countStmt;
435
- trimRowsStmt;
436
- trimAgeStmt;
437
- trimOldestStmt;
438
- insertsSinceByteCheck = 0;
439
- constructor(props) {
440
- if (props.path !== ":memory:") {
441
- const dir = dirname(props.path);
442
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
443
- }
444
- this.db = new Database(props.path);
445
- this.db.run("PRAGMA journal_mode = WAL");
446
- this.migrate();
447
- this.maxRows = props.maxRows ?? null;
448
- this.maxAgeMs = props.maxAgeMs ?? null;
449
- this.maxBytes = props.maxBytes ?? null;
450
- this.targetBytes = props.targetBytes ?? (props.maxBytes !== void 0 ? Math.floor(props.maxBytes / 4) : null);
451
- this.now = props.now ?? (() => Date.now());
452
- this.indexes = props.indexes ?? [];
453
- if (this.indexes.length > 0) {
454
- validateIndexNames(this.indexes);
455
- this.extractIndexes = props.extractIndexes ?? null;
456
- this.syncIndexColumns();
457
- } else this.extractIndexes = null;
458
- const cols = [
459
- "ts",
460
- "type",
461
- "event",
462
- ...this.indexes
463
- ];
464
- const placeholders = cols.map(() => "?").join(", ");
465
- this.insertStmt = this.db.prepare(`INSERT INTO logs (${cols.join(", ")}) VALUES (${placeholders})`);
466
- const colsWithSeq = ["seq", ...cols];
467
- const placeholdersWithSeq = colsWithSeq.map(() => "?").join(", ");
468
- this.insertWithSeqStmt = this.db.prepare(`INSERT INTO logs (${colsWithSeq.join(", ")}) VALUES (${placeholdersWithSeq})`);
469
- this.maxSeqStmt = this.db.prepare("SELECT COALESCE(MAX(seq), 0) AS max FROM logs");
470
- this.countStmt = this.db.prepare("SELECT COUNT(*) AS n FROM logs");
471
- this.trimRowsStmt = this.db.prepare("DELETE FROM logs WHERE seq <= (SELECT seq FROM logs ORDER BY seq DESC LIMIT 1 OFFSET ?)");
472
- this.trimAgeStmt = this.db.prepare("DELETE FROM logs WHERE ts < ?");
473
- this.trimOldestStmt = this.db.prepare("DELETE FROM logs WHERE seq IN (SELECT seq FROM logs ORDER BY seq ASC LIMIT ?)");
474
- }
475
- insert(input) {
476
- try {
477
- const params = this.buildInsertParams(input.ts, input.event);
478
- const result = this.insertStmt.run(...params);
479
- const seq = Number(result.lastInsertRowid);
480
- this.trim();
481
- return {
482
- seq,
483
- ts: input.ts,
484
- event: input.event
485
- };
486
- } catch (e) {
487
- return e instanceof Error ? e : new Error(String(e));
488
- }
489
- }
490
- insertMany(inputs) {
491
- if (inputs.length === 0) return [];
492
- try {
493
- const records = [];
494
- this.db.transaction((batch) => {
495
- for (const input of batch) {
496
- const params = this.buildInsertParams(input.ts, input.event);
497
- const result = this.insertStmt.run(...params);
498
- records.push({
499
- seq: Number(result.lastInsertRowid),
500
- ts: input.ts,
501
- event: input.event
502
- });
503
- }
504
- })(inputs);
505
- this.trim();
506
- return records;
507
- } catch (e) {
508
- return e instanceof Error ? e : new Error(String(e));
509
- }
510
- }
511
- write(record) {
512
- try {
513
- const params = [record.seq, ...this.buildInsertParams(record.ts, record.event)];
514
- this.insertWithSeqStmt.run(...params);
515
- this.trim();
516
- } catch (e) {
517
- return e instanceof Error ? e : new Error(String(e));
518
- }
519
- }
520
- getMaxSeq() {
521
- const row = this.maxSeqStmt.get();
522
- return row ? row.max : 0;
523
- }
524
- query(props = {}) {
525
- const conditions = ["seq > ?"];
526
- const params = [props.sinceSeq ?? 0];
527
- if (typeof props.type === "string") {
528
- conditions.push("type = ?");
529
- params.push(props.type);
530
- }
531
- if (props.where) this.appendWhereConditions(props.where, conditions, params);
532
- const limit = props.limit ?? 1e3;
533
- params.push(limit);
534
- const dir = props.order === "desc" ? "DESC" : "ASC";
535
- const sql = `SELECT seq, ts, type, event FROM logs WHERE ${conditions.join(" AND ")} ORDER BY seq ${dir} LIMIT ?`;
536
- const rows = this.db.prepare(sql).all(...params);
537
- if (dir === "DESC") rows.reverse();
538
- return rows.map(toRecord);
539
- }
540
- /**
541
- * Current schema version. Useful for diagnostics and for tests that want
542
- * to verify migrations ran. Reads `PRAGMA user_version` once per call.
543
- */
544
- getSchemaVersion() {
545
- return this.db.prepare("PRAGMA user_version").get()?.user_version ?? 0;
546
- }
547
- close() {
548
- this.db.close();
549
- }
550
- buildInsertParams(ts, event) {
551
- const type = extractType(event);
552
- const json = JSON.stringify(event);
553
- if (this.indexes.length === 0) return [
554
- ts,
555
- type,
556
- json
557
- ];
558
- const values = this.extractIndexes ? this.extractIndexes(event) : null;
559
- return [
560
- ts,
561
- type,
562
- json,
563
- ...this.indexes.map((col) => values?.[col] ?? null)
564
- ];
565
- }
566
- appendWhereConditions(where, conditions, params) {
567
- const widened = where;
568
- for (const col of this.indexes) {
569
- const value = widened[col];
570
- if (value === void 0) continue;
571
- if (value === null) conditions.push(`${col} IS NULL`);
572
- else {
573
- conditions.push(`${col} = ?`);
574
- params.push(value);
575
- }
576
- }
577
- }
578
- trim() {
579
- if (this.maxRows !== null) {
580
- const row = this.countStmt.get();
581
- if (row && row.n > this.maxRows) this.trimRowsStmt.run(this.maxRows);
582
- }
583
- if (this.maxAgeMs !== null) this.trimAgeStmt.run(this.now() - this.maxAgeMs);
584
- this.maybeTrimBytes();
585
- }
586
- /**
587
- * Throttled byte-size enforcement. Only every BYTE_CHECK_INTERVAL inserts do
588
- * we measure the file; on overflow we estimate how many of the oldest rows to
589
- * drop to land near targetBytes (by the byte/row ratio), delete them in one
590
- * statement, then VACUUM once to return the freed pages to the filesystem (a
591
- * plain DELETE only frees pages inside the file). One DELETE + one VACUUM per
592
- * overflow keeps the expensive rewrite rare — the file must refill the whole
593
- * maxBytes→targetBytes delta before the next overflow can trigger.
594
- */
595
- maybeTrimBytes() {
596
- if (this.maxBytes === null || this.targetBytes === null) return;
597
- this.insertsSinceByteCheck += 1;
598
- if (this.insertsSinceByteCheck < BYTE_CHECK_INTERVAL) return;
599
- this.insertsSinceByteCheck = 0;
600
- const bytes = this.byteSize();
601
- if (bytes <= this.maxBytes) return;
602
- const rows = this.countStmt.get()?.n ?? 0;
603
- if (rows === 0) return;
604
- const bytesToFree = bytes - this.targetBytes;
605
- const bytesPerRow = bytes / rows;
606
- const rowsToDrop = Math.min(rows, Math.ceil(bytesToFree / bytesPerRow));
607
- this.trimOldestStmt.run(rowsToDrop);
608
- this.db.run("VACUUM");
609
- }
610
- byteSize() {
611
- return (this.db.prepare("PRAGMA page_count").get()?.n ?? 0) * (this.db.prepare("PRAGMA page_size").get()?.n ?? 0);
612
- }
613
- /** Drop every row and reclaim the file space. Used by `<log>.clear()`. */
614
- clear() {
615
- this.db.run("DELETE FROM logs");
616
- this.db.run("VACUUM");
617
- this.insertsSinceByteCheck = 0;
618
- }
619
- syncIndexColumns() {
620
- const existing = new Set(this.db.prepare("PRAGMA table_info(logs)").all().map((r) => r.name));
621
- for (const col of this.indexes) {
622
- if (!existing.has(col)) this.db.run(`ALTER TABLE logs ADD COLUMN ${col} TEXT`);
623
- this.db.run(`CREATE INDEX IF NOT EXISTS idx_logs_${col} ON logs (${col})`);
624
- }
625
- }
626
- migrate() {
627
- const current = this.db.prepare("PRAGMA user_version").get()?.user_version ?? 0;
628
- if (current >= MIGRATIONS.length) return;
629
- const pending = MIGRATIONS.slice(current);
630
- let version = current;
631
- for (const stmts of pending) {
632
- version += 1;
633
- this.db.transaction(() => {
634
- for (const stmt of stmts) this.db.run(stmt);
635
- this.db.run(`PRAGMA user_version = ${version}`);
636
- })();
637
- }
638
- }
639
- };
640
- function validateIndexNames(names) {
641
- for (const name of names) {
642
- if (!COLUMN_NAME_RE.test(name)) throw new Error(`invalid index column name: ${name}`);
643
- if (RESERVED_COLUMNS.has(name)) throw new Error(`reserved index column name: ${name}`);
644
- }
645
- }
646
- function extractType(event) {
647
- if (typeof event !== "object" || event === null) return null;
648
- if (!("type" in event)) return null;
649
- const t = event.type;
650
- return typeof t === "string" ? t : null;
651
- }
652
- function toRecord(row) {
653
- return {
654
- seq: row.seq,
655
- ts: row.ts,
656
- event: JSON.parse(row.event)
657
- };
658
- }
659
- //#endregion
660
363
  //#region lib/gateway/event-log/sqlite-event-log.ts
661
364
  const MAX_CONTENT_CHARS = 2e3;
662
365
  /**
@@ -1,5 +1,5 @@
1
- import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
2
- import { i as FunnelTokenPrompter } from "./local-config-sync-BY20ixEV.js";
1
+ import { n as FunnelFileSystem } from "./file-system-DxpnnUVb.js";
2
+ import { i as FunnelTokenPrompter } from "./local-config-sync--f739oCJ.js";
3
3
 
4
4
  //#region lib/services/local-config/local-config-json-schema.d.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
- import { r as ProfileConfig } from "./settings-schema-zhnMIa8I.js";
2
- import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-CBrgz01o.js";
3
- import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
1
+ import { r as ProfileConfig } from "./settings-schema-1hh11jnN.js";
2
+ import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-DPwqOVUm.js";
3
+ import { n as FunnelFileSystem } from "./file-system-DxpnnUVb.js";
4
4
 
5
5
  //#region lib/engine/profiles/profiles.d.ts
6
6
  type Deps = {
@@ -1,2 +1,2 @@
1
- import { t as FunnelProfiles } from "./profiles-EHTeCOqB.js";
1
+ import { t as FunnelProfiles } from "./profiles-g2qGVOWv.js";
2
2
  export { FunnelProfiles };
@@ -1,2 +1,2 @@
1
- import { a as RecoveryListenerControl, i as RecoveryGatewayControl, n as RecoveryAction, o as RecoveryResult, r as RecoveryChannelSource, t as FunnelRecovery } from "./funnel-recovery-BUBsu7WX.js";
1
+ import { a as RecoveryListenerControl, i as RecoveryGatewayControl, n as RecoveryAction, o as RecoveryResult, r as RecoveryChannelSource, t as FunnelRecovery } from "./funnel-recovery-COExL9MD.js";
2
2
  export { FunnelRecovery, RecoveryAction, RecoveryChannelSource, RecoveryGatewayControl, RecoveryListenerControl, RecoveryResult };
@@ -1,5 +1,5 @@
1
- import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "./diagnostic-log-Bxe7Bbvw.js";
2
- import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
1
+ import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "./diagnostic-log-Cb3v8P7p.js";
2
+ import { n as FunnelFileSystem } from "./file-system-DxpnnUVb.js";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region lib/engine/connectors/schedule-state-store.d.ts
@@ -1,4 +1,4 @@
1
- import { a as Settings } from "./settings-schema-zhnMIa8I.js";
1
+ import { a as Settings } from "./settings-schema-1hh11jnN.js";
2
2
 
3
3
  //#region lib/engine/id/id-generator.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "./diagnostic-log-Bxe7Bbvw.js";
1
+ import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "./diagnostic-log-Cb3v8P7p.js";
2
2
  import { z } from "zod";
3
3
  import { App } from "@slack/bolt";
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.58.0",
3
+ "version": "0.58.1",
4
4
  "description": "Hub CLI that routes external events (Slack / GitHub / Discord) to Claude Code agents through subscription channels over MCP.",
5
5
  "keywords": [
6
6
  "bun",