@interactive-inc/claude-funnel 0.27.0 → 0.28.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.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { n as FunnelConnectorAdapter, t as CallInput } from "./connector-adapter-VA6undzc.js";
2
2
  import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "./discord-connector-schema-Df_McRJI.js";
3
- import { S as NotifyFn, _ as connectorConnectionEventSchema, a as ConnectorConnectionStatus, b as FunnelLogger, c as ConnectorProcessedQuery, d as ConnectorRawEvent, f as ConnectorRawQuery, g as StoredRawEvent, h as StoredProcessedEvent, i as ConnectorConnectionRecord, l as ConnectorProcessedRecord, m as StoredConnectionEvent, n as ConnectorConnectionEvent, o as ConnectorDiagnosticLog, p as ConnectorRawRecord, r as ConnectorConnectionQuery, s as ConnectorProcessedEvent, t as CONNECTOR_CONNECTION_STATUSES, u as ConnectorQuery, v as connectorProcessedEventSchema, x as FunnelConnectorListener, y as connectorRawEventSchema } from "./connector-diagnostic-log-Clb2sCcz.js";
3
+ import { S as NotifyFn, _ as connectorConnectionEventSchema, a as ConnectorConnectionStatus, b as FunnelLogger, c as ConnectorProcessedQuery, d as ConnectorRawEvent, f as ConnectorRawQuery, g as StoredRawEvent, h as StoredProcessedEvent, i as ConnectorConnectionRecord, l as ConnectorProcessedRecord, m as StoredConnectionEvent, n as ConnectorConnectionEvent, o as ConnectorDiagnosticLog, p as ConnectorRawRecord, r as ConnectorConnectionQuery, s as ConnectorProcessedEvent, t as CONNECTOR_CONNECTION_STATUSES, u as ConnectorQuery, v as connectorProcessedEventSchema, x as FunnelConnectorListener, y as connectorRawEventSchema } from "./connector-diagnostic-log-OPpPi9V9.js";
4
4
  import { a as FunnelProcessRunner, c as RunResult, i as DetachOptions, n as ghConnectorSchema, o as ProcessSnapshot, r as AttachOptions, s as RunOptions, t as GhConnectorConfig } from "./gh-connector-schema-CQmEWzdV.js";
5
- import { a as FunnelFileSystem, c as ScheduleEntry, d as scheduleEntrySchema, i as FileStat, l as scheduleCatchupPolicySchema, n as ScheduleOnFired, o as ScheduleCatchupPolicy, s as ScheduleConnectorConfig, u as scheduleConnectorSchema } from "./schedule-listener-C2-KqHQc.js";
6
- import { a as SlackProcessed, c as SlackRawEvent, d as slackConnectorSchema, i as FunnelSlackEventProcessor, l as SlackSkipReason, n as SlackOnAppCreated, o as SlackProcessedEmit, r as SlackPreprocessEvent, s as SlackProcessedSkip, u as SlackConnectorConfig } from "./slack-listener-BMknoyVr.js";
5
+ import { a as FunnelFileSystem, c as ScheduleEntry, d as scheduleEntrySchema, i as FileStat, l as scheduleCatchupPolicySchema, n as ScheduleOnFired, o as ScheduleCatchupPolicy, s as ScheduleConnectorConfig, u as scheduleConnectorSchema } from "./schedule-listener-3M6WkH1Y.js";
6
+ import { a as SlackProcessed, c as SlackRawEvent, d as slackConnectorSchema, i as FunnelSlackEventProcessor, l as SlackSkipReason, n as SlackOnAppCreated, o as SlackProcessedEmit, r as SlackPreprocessEvent, s as SlackProcessedSkip, u as SlackConnectorConfig } from "./slack-listener-CLTiOEJw.js";
7
7
  import { z } from "zod";
8
8
  import * as _$hono_factory0 from "hono/factory";
9
9
  import { Hono } from "hono";
@@ -1094,6 +1094,9 @@ declare abstract class FunnelEventLog {
1094
1094
  abstract record(record: FunnelEventRecord): void;
1095
1095
  abstract loadSince(since: number): ReplayableEvent[];
1096
1096
  abstract findMaxOffset(): number;
1097
+ /** Drop every stored event and reclaim the file. The broadcaster's in-memory
1098
+ * offset counter is unaffected, so offsets keep increasing after a clear. */
1099
+ abstract clear(): void;
1097
1100
  abstract close(): void;
1098
1101
  }
1099
1102
  //#endregion
@@ -1773,7 +1776,9 @@ type Props$2 = {
1773
1776
  /** SQLite database file path. Created on first write. ":memory:" for tests. */path: string; /** Override for tests. Defaults to `Date.now`. */
1774
1777
  now?: () => number; /** Optional row cap. Pruned on every insert. */
1775
1778
  maxRows?: number; /** Optional age cap in ms. Pruned on every insert. */
1776
- maxAgeMs?: number;
1779
+ maxAgeMs?: number; /** Optional on-disk byte cap. Checked periodically; on overflow the oldest rows are dropped toward targetBytes and the file is VACUUMed. */
1780
+ maxBytes?: number; /** Shrink target when maxBytes is exceeded. Defaults to maxBytes/4. */
1781
+ targetBytes?: number;
1777
1782
  };
1778
1783
  /**
1779
1784
  * SQLite-backed `FunnelEventLog`. One indexed table holds every broadcaster
@@ -1820,6 +1825,7 @@ declare class SqliteFunnelEventLog extends FunnelEventLog {
1820
1825
  limit?: number;
1821
1826
  }): ReplayableEvent[];
1822
1827
  findMaxOffset(): number;
1828
+ clear(): void;
1823
1829
  close(): void;
1824
1830
  }
1825
1831
  //#endregion
@@ -1884,6 +1890,7 @@ declare class SqliteConnectorDiagnosticLog extends ConnectorDiagnosticLog {
1884
1890
  queryRaw(query: ConnectorRawQuery): StoredRawEvent[];
1885
1891
  queryProcessed(query: ConnectorProcessedQuery): StoredProcessedEvent[];
1886
1892
  queryConnection(query: ConnectorConnectionQuery): StoredConnectionEvent[];
1893
+ clear(): void;
1887
1894
  close(): void;
1888
1895
  }
1889
1896
  //#endregion
package/dist/index.js CHANGED
@@ -2263,6 +2263,8 @@ var FunnelEventLog = class {};
2263
2263
  //#region lib/logger/leuco-logger-sqlite-sink.ts
2264
2264
  /** Conservative whitelist for column names interpolated into SQL. */
2265
2265
  const COLUMN_NAME_RE = /^[a-z_][a-z0-9_]*$/;
2266
+ /** How many inserts between on-disk size checks (see insertsSinceByteCheck). */
2267
+ const BYTE_CHECK_INTERVAL = 500;
2266
2268
  const RESERVED_COLUMNS = new Set([
2267
2269
  "seq",
2268
2270
  "ts",
@@ -2316,6 +2318,8 @@ var LeucoLoggerSqliteSink = class {
2316
2318
  db;
2317
2319
  maxRows;
2318
2320
  maxAgeMs;
2321
+ maxBytes;
2322
+ targetBytes;
2319
2323
  now;
2320
2324
  indexes;
2321
2325
  extractIndexes;
@@ -2325,12 +2329,16 @@ var LeucoLoggerSqliteSink = class {
2325
2329
  countStmt;
2326
2330
  trimRowsStmt;
2327
2331
  trimAgeStmt;
2332
+ trimOldestStmt;
2333
+ insertsSinceByteCheck = 0;
2328
2334
  constructor(props) {
2329
2335
  this.db = new Database(props.path);
2330
2336
  this.db.run("PRAGMA journal_mode = WAL");
2331
2337
  this.migrate();
2332
2338
  this.maxRows = props.maxRows ?? null;
2333
2339
  this.maxAgeMs = props.maxAgeMs ?? null;
2340
+ this.maxBytes = props.maxBytes ?? null;
2341
+ this.targetBytes = props.targetBytes ?? (props.maxBytes !== void 0 ? Math.floor(props.maxBytes / 4) : null);
2334
2342
  this.now = props.now ?? (() => Date.now());
2335
2343
  this.indexes = props.indexes ?? [];
2336
2344
  if (this.indexes.length > 0) {
@@ -2353,6 +2361,7 @@ var LeucoLoggerSqliteSink = class {
2353
2361
  this.countStmt = this.db.prepare("SELECT COUNT(*) AS n FROM leuco_log");
2354
2362
  this.trimRowsStmt = this.db.prepare("DELETE FROM leuco_log WHERE seq <= (SELECT seq FROM leuco_log ORDER BY seq DESC LIMIT 1 OFFSET ?)");
2355
2363
  this.trimAgeStmt = this.db.prepare("DELETE FROM leuco_log WHERE ts < ?");
2364
+ this.trimOldestStmt = this.db.prepare("DELETE FROM leuco_log WHERE seq IN (SELECT seq FROM leuco_log ORDER BY seq ASC LIMIT ?)");
2356
2365
  }
2357
2366
  insert(input) {
2358
2367
  try {
@@ -2463,6 +2472,40 @@ var LeucoLoggerSqliteSink = class {
2463
2472
  if (row && row.n > this.maxRows) this.trimRowsStmt.run(this.maxRows);
2464
2473
  }
2465
2474
  if (this.maxAgeMs !== null) this.trimAgeStmt.run(this.now() - this.maxAgeMs);
2475
+ this.maybeTrimBytes();
2476
+ }
2477
+ /**
2478
+ * Throttled byte-size enforcement. Only every BYTE_CHECK_INTERVAL inserts do
2479
+ * we measure the file; on overflow we estimate how many of the oldest rows to
2480
+ * drop to land near targetBytes (by the byte/row ratio), delete them in one
2481
+ * statement, then VACUUM once to return the freed pages to the filesystem (a
2482
+ * plain DELETE only frees pages inside the file). One DELETE + one VACUUM per
2483
+ * overflow keeps the expensive rewrite rare — the file must refill the whole
2484
+ * maxBytes→targetBytes delta before the next overflow can trigger.
2485
+ */
2486
+ maybeTrimBytes() {
2487
+ if (this.maxBytes === null || this.targetBytes === null) return;
2488
+ this.insertsSinceByteCheck += 1;
2489
+ if (this.insertsSinceByteCheck < BYTE_CHECK_INTERVAL) return;
2490
+ this.insertsSinceByteCheck = 0;
2491
+ const bytes = this.byteSize();
2492
+ if (bytes <= this.maxBytes) return;
2493
+ const rows = this.countStmt.get()?.n ?? 0;
2494
+ if (rows === 0) return;
2495
+ const bytesToFree = bytes - this.targetBytes;
2496
+ const bytesPerRow = bytes / rows;
2497
+ const rowsToDrop = Math.min(rows, Math.ceil(bytesToFree / bytesPerRow));
2498
+ this.trimOldestStmt.run(rowsToDrop);
2499
+ this.db.run("VACUUM");
2500
+ }
2501
+ byteSize() {
2502
+ return (this.db.prepare("PRAGMA page_count").get()?.n ?? 0) * (this.db.prepare("PRAGMA page_size").get()?.n ?? 0);
2503
+ }
2504
+ /** Drop every row and reclaim the file space. Used by `<log>.clear()`. */
2505
+ clear() {
2506
+ this.db.run("DELETE FROM leuco_log");
2507
+ this.db.run("VACUUM");
2508
+ this.insertsSinceByteCheck = 0;
2466
2509
  }
2467
2510
  syncIndexColumns() {
2468
2511
  const existing = new Set(this.db.prepare("PRAGMA table_info(leuco_log)").all().map((r) => r.name));
@@ -2539,7 +2582,9 @@ var SqliteFunnelEventLog = class extends FunnelEventLog {
2539
2582
  }),
2540
2583
  now: this.now,
2541
2584
  ...props.maxRows !== void 0 ? { maxRows: props.maxRows } : {},
2542
- ...props.maxAgeMs !== void 0 ? { maxAgeMs: props.maxAgeMs } : {}
2585
+ ...props.maxAgeMs !== void 0 ? { maxAgeMs: props.maxAgeMs } : {},
2586
+ ...props.maxBytes !== void 0 ? { maxBytes: props.maxBytes } : {},
2587
+ ...props.targetBytes !== void 0 ? { targetBytes: props.targetBytes } : {}
2543
2588
  });
2544
2589
  }
2545
2590
  /**
@@ -2600,6 +2645,9 @@ var SqliteFunnelEventLog = class extends FunnelEventLog {
2600
2645
  findMaxOffset() {
2601
2646
  return this.sink.getMaxSeq();
2602
2647
  }
2648
+ clear() {
2649
+ this.sink.clear();
2650
+ }
2603
2651
  close() {
2604
2652
  this.sink.close();
2605
2653
  }
@@ -4319,6 +4367,11 @@ var SqliteConnectorDiagnosticLog = class extends ConnectorDiagnosticLog {
4319
4367
  detail: record.event.detail
4320
4368
  }));
4321
4369
  }
4370
+ clear() {
4371
+ this.raw.clear();
4372
+ this.processed.clear();
4373
+ this.connection.clear();
4374
+ }
4322
4375
  close() {
4323
4376
  this.raw.close();
4324
4377
  this.processed.close();
@@ -1,4 +1,4 @@
1
- import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-Clb2sCcz.js";
1
+ import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-OPpPi9V9.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region lib/connectors/schedule-connector-schema.d.ts
@@ -1,4 +1,4 @@
1
- import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-Clb2sCcz.js";
1
+ import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-OPpPi9V9.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.27.0",
3
+ "version": "0.28.0",
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",