@interactive-inc/claude-funnel 0.8.1 → 0.10.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 (175) hide show
  1. package/README.md +179 -80
  2. package/dist/bin.js +724 -656
  3. package/dist/connector-adapter-CXB-q_XC.d.ts +11 -0
  4. package/dist/connector-adapter-D5Utumgz.js +4 -0
  5. package/dist/connectors/discord.d.ts +76 -0
  6. package/dist/connectors/discord.js +2 -0
  7. package/dist/connectors/gh.d.ts +38 -0
  8. package/dist/connectors/gh.js +2 -0
  9. package/dist/connectors/schedule.d.ts +53 -0
  10. package/dist/connectors/schedule.js +2 -0
  11. package/dist/connectors/slack.d.ts +62 -0
  12. package/dist/connectors/slack.js +2 -0
  13. package/dist/discord-connector-schema-Dww2I4zH.d.ts +14 -0
  14. package/dist/discord-connector-schema-ygf5Df-2.js +173 -0
  15. package/dist/file-system-Co60LrmR.d.ts +74 -0
  16. package/dist/gateway/daemon.js +233 -183
  17. package/dist/gh-connector-schema-2ml29MBC.js +218 -0
  18. package/dist/gh-connector-schema-BZFAS-p-.d.ts +45 -0
  19. package/dist/index.d.ts +3888 -36
  20. package/dist/index.js +6206 -3485
  21. package/dist/logger-CTlXs7z4.d.ts +33 -0
  22. package/dist/node-logger-DQz_BGOD.js +61 -0
  23. package/dist/schedule-connector-schema-CkuIQ0JQ.js +325 -0
  24. package/dist/slack-connector-schema-Cd22WiHB.js +153 -0
  25. package/dist/slack-connector-schema-D7zAHN8k.d.ts +15 -0
  26. package/lib/bin.ts +1 -76
  27. package/lib/cli/index.ts +85 -0
  28. package/lib/cli/router/to-request.ts +1 -0
  29. package/lib/cli/routes/channels.$channel.publish.ts +52 -0
  30. package/lib/cli/routes/claude.ts +1 -0
  31. package/lib/cli/routes/index.ts +35 -18
  32. package/lib/cli/routes/profiles.add.$profile.ts +5 -2
  33. package/lib/cli/routes/profiles.set.$profile.ts +10 -11
  34. package/lib/connectors/discord.ts +4 -0
  35. package/lib/connectors/gh.ts +3 -0
  36. package/lib/connectors/schedule.ts +4 -0
  37. package/lib/connectors/slack.ts +4 -0
  38. package/lib/engine/claude/claude.ts +6 -0
  39. package/lib/engine/mcp/channel-server.ts +34 -115
  40. package/lib/engine/mcp/channel-subscriber.ts +82 -0
  41. package/lib/engine/mcp/read-channel-connectors.ts +34 -0
  42. package/lib/engine/mcp/read-gateway-token.ts +16 -0
  43. package/lib/engine/mcp/usage-hint-for-type.ts +15 -0
  44. package/lib/engine/settings/settings-schema.ts +2 -0
  45. package/lib/funnel.ts +162 -55
  46. package/lib/gateway/broadcaster.ts +1 -1
  47. package/lib/gateway/channel-publisher.ts +67 -0
  48. package/lib/gateway/gateway-server.ts +28 -16
  49. package/lib/gateway/publish-schema.ts +27 -0
  50. package/lib/gateway/routes/channels.publish.ts +44 -0
  51. package/lib/gateway/routes/index.ts +2 -0
  52. package/lib/gateway/routes/route-deps.ts +8 -0
  53. package/lib/index.ts +15 -0
  54. package/package.json +34 -23
  55. package/dist/cli/factory.d.ts +0 -7
  56. package/dist/cli/router/query-to-cli-args.d.ts +0 -1
  57. package/dist/cli/router/to-request.d.ts +0 -5
  58. package/dist/cli/router/validator.d.ts +0 -5
  59. package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +0 -42
  60. package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +0 -46
  61. package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +0 -54
  62. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +0 -66
  63. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +0 -42
  64. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +0 -46
  65. package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +0 -90
  66. package/dist/cli/routes/channels.$channel.connectors.d.ts +0 -38
  67. package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +0 -42
  68. package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +0 -62
  69. package/dist/cli/routes/channels.$channel.d.ts +0 -38
  70. package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +0 -42
  71. package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +0 -28
  72. package/dist/cli/routes/channels.add.$channel.d.ts +0 -46
  73. package/dist/cli/routes/channels.d.ts +0 -16
  74. package/dist/cli/routes/channels.remove.$channel.d.ts +0 -38
  75. package/dist/cli/routes/claude.d.ts +0 -32
  76. package/dist/cli/routes/gateway.d.ts +0 -20
  77. package/dist/cli/routes/gateway.listeners.d.ts +0 -17
  78. package/dist/cli/routes/gateway.logs.d.ts +0 -24
  79. package/dist/cli/routes/gateway.restart.d.ts +0 -24
  80. package/dist/cli/routes/gateway.run.d.ts +0 -24
  81. package/dist/cli/routes/gateway.start.d.ts +0 -24
  82. package/dist/cli/routes/gateway.status.d.ts +0 -13
  83. package/dist/cli/routes/gateway.stop.d.ts +0 -16
  84. package/dist/cli/routes/index.d.ts +0 -1222
  85. package/dist/cli/routes/profiles.$profile.as-default.d.ts +0 -38
  86. package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +0 -42
  87. package/dist/cli/routes/profiles.$profile.run.d.ts +0 -46
  88. package/dist/cli/routes/profiles.add.$profile.d.ts +0 -54
  89. package/dist/cli/routes/profiles.d.ts +0 -16
  90. package/dist/cli/routes/profiles.remove.$profile.d.ts +0 -38
  91. package/dist/cli/routes/profiles.set.$profile.d.ts +0 -54
  92. package/dist/cli/routes/status.d.ts +0 -16
  93. package/dist/cli/routes/update.d.ts +0 -16
  94. package/dist/connectors/connector-adapter.d.ts +0 -8
  95. package/dist/connectors/connector-config-schema.d.ts +0 -43
  96. package/dist/connectors/connector-factory.d.ts +0 -32
  97. package/dist/connectors/connector-listener.d.ts +0 -17
  98. package/dist/connectors/discord-adapter.d.ts +0 -14
  99. package/dist/connectors/discord-connector-schema.d.ts +0 -10
  100. package/dist/connectors/discord-event-processor.d.ts +0 -26
  101. package/dist/connectors/discord-listener.d.ts +0 -17
  102. package/dist/connectors/gh-adapter.d.ts +0 -11
  103. package/dist/connectors/gh-connector-schema.d.ts +0 -10
  104. package/dist/connectors/gh-listener.d.ts +0 -26
  105. package/dist/connectors/match-cron.d.ts +0 -1
  106. package/dist/connectors/schedule-connector-schema.d.ts +0 -45
  107. package/dist/connectors/schedule-listener.d.ts +0 -30
  108. package/dist/connectors/schedule-state-store.d.ts +0 -19
  109. package/dist/connectors/slack-adapter.d.ts +0 -15
  110. package/dist/connectors/slack-connector-schema.d.ts +0 -11
  111. package/dist/connectors/slack-event-processor.d.ts +0 -27
  112. package/dist/connectors/slack-listener.d.ts +0 -17
  113. package/dist/engine/channels/channels.d.ts +0 -106
  114. package/dist/engine/claude/claude.d.ts +0 -49
  115. package/dist/engine/claude/gateway-controller.d.ts +0 -6
  116. package/dist/engine/fs/file-system.d.ts +0 -24
  117. package/dist/engine/fs/memory-file-system.d.ts +0 -31
  118. package/dist/engine/fs/node-file-system.d.ts +0 -15
  119. package/dist/engine/http/http-client.d.ts +0 -15
  120. package/dist/engine/http/memory-http-client.d.ts +0 -12
  121. package/dist/engine/http/node-http-client.d.ts +0 -5
  122. package/dist/engine/id/id-generator.d.ts +0 -7
  123. package/dist/engine/id/memory-id-generator.d.ts +0 -11
  124. package/dist/engine/id/node-id-generator.d.ts +0 -4
  125. package/dist/engine/logger/logger.d.ts +0 -11
  126. package/dist/engine/logger/memory-logger.d.ts +0 -14
  127. package/dist/engine/logger/node-logger.d.ts +0 -15
  128. package/dist/engine/logger/noop-logger.d.ts +0 -7
  129. package/dist/engine/mcp/channel-server.d.ts +0 -1
  130. package/dist/engine/mcp/mcp.d.ts +0 -22
  131. package/dist/engine/process/memory-process-runner.d.ts +0 -43
  132. package/dist/engine/process/node-process-runner.d.ts +0 -9
  133. package/dist/engine/process/process-runner.d.ts +0 -29
  134. package/dist/engine/profiles/profile-channel-checker.d.ts +0 -7
  135. package/dist/engine/profiles/profiles.d.ts +0 -31
  136. package/dist/engine/settings/mock-settings-reader.d.ts +0 -9
  137. package/dist/engine/settings/settings-reader.d.ts +0 -5
  138. package/dist/engine/settings/settings-schema.d.ts +0 -132
  139. package/dist/engine/settings/settings-store.d.ts +0 -18
  140. package/dist/engine/time/clock.d.ts +0 -9
  141. package/dist/engine/time/memory-clock.d.ts +0 -12
  142. package/dist/engine/time/node-clock.d.ts +0 -4
  143. package/dist/funnel.d.ts +0 -95
  144. package/dist/gateway/auth-middleware.d.ts +0 -14
  145. package/dist/gateway/broadcaster.d.ts +0 -122
  146. package/dist/gateway/daemon.d.ts +0 -2
  147. package/dist/gateway/factory.d.ts +0 -7
  148. package/dist/gateway/funnel-event-store.d.ts +0 -81
  149. package/dist/gateway/gateway-server.d.ts +0 -94
  150. package/dist/gateway/gateway-token.d.ts +0 -33
  151. package/dist/gateway/gateway.d.ts +0 -58
  152. package/dist/gateway/kill-competing-slack-gateways.d.ts +0 -9
  153. package/dist/gateway/listener-supervisor.d.ts +0 -85
  154. package/dist/gateway/listeners-client.d.ts +0 -53
  155. package/dist/gateway/resolve-daemon-script.d.ts +0 -11
  156. package/dist/gateway/routes/channels.connectors.call.d.ts +0 -41
  157. package/dist/gateway/routes/health.d.ts +0 -17
  158. package/dist/gateway/routes/index.d.ts +0 -209
  159. package/dist/gateway/routes/listeners.list.d.ts +0 -14
  160. package/dist/gateway/routes/listeners.restart.d.ts +0 -34
  161. package/dist/gateway/routes/listeners.start.d.ts +0 -34
  162. package/dist/gateway/routes/listeners.stop.d.ts +0 -34
  163. package/dist/gateway/routes/route-deps.d.ts +0 -10
  164. package/dist/gateway/routes/status.d.ts +0 -30
  165. package/dist/gateway/routes/validator.d.ts +0 -19
  166. package/dist/logger/leuco-human-file-writer.d.ts +0 -33
  167. package/dist/logger/leuco-human-logger.d.ts +0 -46
  168. package/dist/logger/leuco-human-record.d.ts +0 -15
  169. package/dist/logger/leuco-human-stdout-writer.d.ts +0 -20
  170. package/dist/logger/leuco-human-writer.d.ts +0 -13
  171. package/dist/logger/leuco-logger-memory-sink.d.ts +0 -33
  172. package/dist/logger/leuco-logger-record.d.ts +0 -13
  173. package/dist/logger/leuco-logger-sink.d.ts +0 -34
  174. package/dist/logger/leuco-logger-sqlite-sink.d.ts +0 -102
  175. package/dist/logger/leuco-logger.d.ts +0 -56
@@ -1,132 +0,0 @@
1
- import { z } from "zod";
2
- /**
3
- * Routing mode when multiple WS clients are subscribed to the same channel.
4
- *
5
- * - `fanout` (default): every connected client receives every event. Right when each
6
- * subscriber has its own job (e.g., TUI mirrors, distinct Claude profiles each running
7
- * their own pipeline against the same source).
8
- * - `exclusive`: each event is delivered to exactly one connected client, picked
9
- * round-robin per channel. Right when subscribers are interchangeable workers and you
10
- * want each event handled once. Tap=all clients (TUI dashboard) always receive,
11
- * regardless of mode, so they can passively observe.
12
- */
13
- export declare const channelDeliveryModeSchema: z.ZodEnum<{
14
- fanout: "fanout";
15
- exclusive: "exclusive";
16
- }>;
17
- export type ChannelDeliveryMode = z.infer<typeof channelDeliveryModeSchema>;
18
- export declare const channelConfigSchema: z.ZodObject<{
19
- id: z.ZodString;
20
- name: z.ZodString;
21
- delivery: z.ZodDefault<z.ZodEnum<{
22
- fanout: "fanout";
23
- exclusive: "exclusive";
24
- }>>;
25
- connectors: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
26
- id: z.ZodString;
27
- name: z.ZodString;
28
- type: z.ZodLiteral<"slack">;
29
- botToken: z.ZodString;
30
- appToken: z.ZodString;
31
- createdAt: z.ZodOptional<z.ZodString>;
32
- updatedAt: z.ZodOptional<z.ZodString>;
33
- }, z.core.$strip>, z.ZodObject<{
34
- id: z.ZodString;
35
- name: z.ZodString;
36
- type: z.ZodLiteral<"gh">;
37
- pollInterval: z.ZodOptional<z.ZodNumber>;
38
- createdAt: z.ZodOptional<z.ZodString>;
39
- updatedAt: z.ZodOptional<z.ZodString>;
40
- }, z.core.$strip>, z.ZodObject<{
41
- id: z.ZodString;
42
- name: z.ZodString;
43
- type: z.ZodLiteral<"discord">;
44
- botToken: z.ZodString;
45
- createdAt: z.ZodOptional<z.ZodString>;
46
- updatedAt: z.ZodOptional<z.ZodString>;
47
- }, z.core.$strip>, z.ZodObject<{
48
- id: z.ZodString;
49
- name: z.ZodString;
50
- type: z.ZodLiteral<"schedule">;
51
- entries: z.ZodDefault<z.ZodArray<z.ZodObject<{
52
- id: z.ZodString;
53
- cron: z.ZodString;
54
- prompt: z.ZodString;
55
- enabled: z.ZodDefault<z.ZodBoolean>;
56
- catchupPolicy: z.ZodDefault<z.ZodEnum<{
57
- latest: "latest";
58
- all: "all";
59
- skip: "skip";
60
- }>>;
61
- }, z.core.$strip>>>;
62
- createdAt: z.ZodOptional<z.ZodString>;
63
- updatedAt: z.ZodOptional<z.ZodString>;
64
- }, z.core.$strip>], "type">>>;
65
- }, z.core.$strip>;
66
- export type ChannelConfig = z.infer<typeof channelConfigSchema>;
67
- export declare const profileConfigSchema: z.ZodObject<{
68
- name: z.ZodString;
69
- path: z.ZodString;
70
- subAgent: z.ZodString;
71
- channelId: z.ZodString;
72
- }, z.core.$strip>;
73
- export type ProfileConfig = z.infer<typeof profileConfigSchema>;
74
- export declare const SETTINGS_VERSION = 1;
75
- export declare const settingsSchema: z.ZodObject<{
76
- version: z.ZodDefault<z.ZodLiteral<1>>;
77
- channels: z.ZodDefault<z.ZodArray<z.ZodObject<{
78
- id: z.ZodString;
79
- name: z.ZodString;
80
- delivery: z.ZodDefault<z.ZodEnum<{
81
- fanout: "fanout";
82
- exclusive: "exclusive";
83
- }>>;
84
- connectors: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
85
- id: z.ZodString;
86
- name: z.ZodString;
87
- type: z.ZodLiteral<"slack">;
88
- botToken: z.ZodString;
89
- appToken: z.ZodString;
90
- createdAt: z.ZodOptional<z.ZodString>;
91
- updatedAt: z.ZodOptional<z.ZodString>;
92
- }, z.core.$strip>, z.ZodObject<{
93
- id: z.ZodString;
94
- name: z.ZodString;
95
- type: z.ZodLiteral<"gh">;
96
- pollInterval: z.ZodOptional<z.ZodNumber>;
97
- createdAt: z.ZodOptional<z.ZodString>;
98
- updatedAt: z.ZodOptional<z.ZodString>;
99
- }, z.core.$strip>, z.ZodObject<{
100
- id: z.ZodString;
101
- name: z.ZodString;
102
- type: z.ZodLiteral<"discord">;
103
- botToken: z.ZodString;
104
- createdAt: z.ZodOptional<z.ZodString>;
105
- updatedAt: z.ZodOptional<z.ZodString>;
106
- }, z.core.$strip>, z.ZodObject<{
107
- id: z.ZodString;
108
- name: z.ZodString;
109
- type: z.ZodLiteral<"schedule">;
110
- entries: z.ZodDefault<z.ZodArray<z.ZodObject<{
111
- id: z.ZodString;
112
- cron: z.ZodString;
113
- prompt: z.ZodString;
114
- enabled: z.ZodDefault<z.ZodBoolean>;
115
- catchupPolicy: z.ZodDefault<z.ZodEnum<{
116
- latest: "latest";
117
- all: "all";
118
- skip: "skip";
119
- }>>;
120
- }, z.core.$strip>>>;
121
- createdAt: z.ZodOptional<z.ZodString>;
122
- updatedAt: z.ZodOptional<z.ZodString>;
123
- }, z.core.$strip>], "type">>>;
124
- }, z.core.$strip>>>;
125
- profiles: z.ZodDefault<z.ZodArray<z.ZodObject<{
126
- name: z.ZodString;
127
- path: z.ZodString;
128
- subAgent: z.ZodString;
129
- channelId: z.ZodString;
130
- }, z.core.$strip>>>;
131
- }, z.core.$strip>;
132
- export type Settings = z.infer<typeof settingsSchema>;
@@ -1,18 +0,0 @@
1
- import { FunnelFileSystem } from "../fs/file-system";
2
- import { FunnelSettingsReader } from "./settings-reader";
3
- import type { Settings } from "./settings-schema";
4
- export declare const FUNNEL_DIR: string;
5
- export declare const SETTINGS_PATH: string;
6
- type Deps = {
7
- path?: string;
8
- fs?: FunnelFileSystem;
9
- };
10
- export declare class FunnelSettingsStore extends FunnelSettingsReader {
11
- private readonly path;
12
- private readonly fs;
13
- constructor(deps?: Deps);
14
- read(): Settings;
15
- private looksLikeLegacy;
16
- write(settings: Settings): void;
17
- }
18
- export {};
@@ -1,9 +0,0 @@
1
- /**
2
- * Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
3
- * is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
4
- */
5
- export declare abstract class FunnelClock {
6
- abstract now(): Date;
7
- millis(): number;
8
- iso(): string;
9
- }
@@ -1,12 +0,0 @@
1
- import { FunnelClock } from "./clock";
2
- type Props = {
3
- start?: Date;
4
- };
5
- export declare class MemoryFunnelClock extends FunnelClock {
6
- private current;
7
- constructor(props?: Props);
8
- now(): Date;
9
- set(date: Date): void;
10
- advance(ms: number): void;
11
- }
12
- export {};
@@ -1,4 +0,0 @@
1
- import { FunnelClock } from "./clock";
2
- export declare class NodeFunnelClock extends FunnelClock {
3
- now(): Date;
4
- }
package/dist/funnel.d.ts DELETED
@@ -1,95 +0,0 @@
1
- import { FunnelConnectorFactory } from "./connectors/connector-factory";
2
- import { FunnelChannels } from "./engine/channels/channels";
3
- import { FunnelClaude } from "./engine/claude/claude";
4
- import type { FunnelFileSystem } from "./engine/fs/file-system";
5
- import type { FunnelIdGenerator } from "./engine/id/id-generator";
6
- import { FunnelLogger } from "./engine/logger/logger";
7
- import { FunnelMcp } from "./engine/mcp/mcp";
8
- import { FunnelProcessRunner } from "./engine/process/process-runner";
9
- import { FunnelProfiles } from "./engine/profiles/profiles";
10
- import { FunnelSettingsReader } from "./engine/settings/settings-reader";
11
- import type { FunnelClock } from "./engine/time/clock";
12
- import { FunnelGateway } from "./gateway/gateway";
13
- import { FunnelGatewayServer } from "./gateway/gateway-server";
14
- import { FunnelGatewayToken } from "./gateway/gateway-token";
15
- import { FunnelListenersClient } from "./gateway/listeners-client";
16
- type Props = {
17
- /** Settings persistence (channels with nested connectors / profiles). Defaults to a FunnelSettingsStore rooted at `dir`. */
18
- store?: FunnelSettingsReader;
19
- /** Filesystem boundary. Replace with MemoryFunnelFileSystem to sandbox all disk I/O. */
20
- fs?: FunnelFileSystem;
21
- /** Process runner used by gateway / claude / gh listener. Replace with MemoryFunnelProcessRunner for tests. */
22
- process?: FunnelProcessRunner;
23
- /** Logger flowed into every facet. Replace with MemoryFunnelLogger or NoopFunnelLogger to silence/inspect. */
24
- logger?: FunnelLogger;
25
- /** Clock used by schedule listener, gh poll watermarks, and gateway timeouts. */
26
- clock?: FunnelClock;
27
- /** ID generator for channel and connector ids. Use MemoryFunnelIdGenerator for deterministic tests. */
28
- idGenerator?: FunnelIdGenerator;
29
- /** Funnel home directory (settings.json + per-channel/per-connector dirs). Defaults to ~/.funnel. */
30
- dir?: string;
31
- /** Temp / runtime directory (gateway logs and PID adjacent files). Defaults to /tmp/funnel. */
32
- tmpDir?: string;
33
- };
34
- /**
35
- * Facade exposing every funnel facet as a getter.
36
- *
37
- * The same `Funnel` is used by the CLI, the TUI, and as a programmable library.
38
- * All side-effecting boundaries (filesystem, process, logger, clock, id, paths) are
39
- * injectable via `Props` — passing memory implementations gives a fully sandboxed
40
- * Funnel that touches no real disk, processes, or wall-clock time.
41
- *
42
- * Connectors live nested inside their owning channel (channels[].connectors[]),
43
- * so connector CRUD is reached via `funnel.channels.addConnector(...)` etc.
44
- *
45
- * @example
46
- * ```ts
47
- * const funnel = new Funnel({})
48
- * const channel = funnel.channels.add({ name: "inbox" })
49
- * funnel.channels.addConnector("inbox", { type: "slack", name: "ops", botToken, appToken })
50
- * await funnel.gatewayServer({ port: 9742 }).start()
51
- * ```
52
- */
53
- export declare class Funnel {
54
- private readonly props;
55
- constructor(props?: Props);
56
- /** Settings reader. If not injected, a FunnelSettingsStore rooted at `dir` is created. */
57
- get store(): FunnelSettingsReader;
58
- /** Process runner boundary. Defaults to NodeFunnelProcessRunner. */
59
- get process(): FunnelProcessRunner;
60
- /** Logger boundary. Defaults to NodeFunnelLogger. */
61
- get logger(): FunnelLogger;
62
- /** Pure factory that constructs per-type listeners and adapters from connector configs. */
63
- get factory(): FunnelConnectorFactory;
64
- /** Channel CRUD + nested connector CRUD + schedule entries + listener/adapter dispatch. */
65
- get channels(): FunnelChannels;
66
- /** Launch profiles (named presets for `fnl claude`: path + sub-agent + channel id). */
67
- get profiles(): FunnelProfiles;
68
- /** funnel MCP installer (writes/removes `.mcp.json` entries in target repos). */
69
- get mcp(): FunnelMcp;
70
- /** Launch Claude Code with a channel injected via env, MCP installed, gateway ensured. */
71
- get claude(): FunnelClaude;
72
- /** Gateway daemon controller (PID-file, start/stop the separate `bun daemon.ts` process). */
73
- get gateway(): FunnelGateway;
74
- /** Read / generate the daemon's gateway token (mode 0600 file under `dir`). */
75
- get gatewayToken(): FunnelGatewayToken;
76
- /**
77
- * HTTP client for listener operations on the running gateway daemon.
78
- * Returns `{ state: "offline" }` when the daemon is offline so hot-reload
79
- * paths stay write-only without parsing strings.
80
- */
81
- get listeners(): FunnelListenersClient;
82
- /**
83
- * In-process gateway server. Unlike `gateway.start()` (which spawns a daemon),
84
- * this returns a class that runs `Bun.serve` + listeners inside the current process —
85
- * useful for tests, embedding, or custom hosts.
86
- */
87
- gatewayServer(options?: {
88
- port?: number;
89
- logDir?: string;
90
- killCompetingSlack?: boolean;
91
- /** Override the auth token. Defaults to the persisted gateway.token. Pass "" to disable auth (tests). */
92
- token?: string;
93
- }): FunnelGatewayServer;
94
- }
95
- export {};
@@ -1,14 +0,0 @@
1
- import type { MiddlewareHandler } from "hono";
2
- import type { Env } from "./factory";
3
- type Deps = {
4
- expected: string;
5
- };
6
- /**
7
- * Verifies `Authorization: Bearer <token>` against the daemon's gateway token.
8
- * Mounted on the routes that mutate listener state or expose detailed status.
9
- * `/health` is intentionally left unauthenticated so the daemon manager can
10
- * probe liveness without needing the token.
11
- */
12
- export declare const requireBearerToken: (deps: Deps) => MiddlewareHandler<Env>;
13
- export declare const constantTimeEqual: (a: string, b: string) => boolean;
14
- export {};
@@ -1,122 +0,0 @@
1
- import type { ServerWebSocket } from "bun";
2
- import { FunnelLogger } from "../engine/logger/logger";
3
- type ClientData = {
4
- /** Stable channel id (uuid) that the WS client subscribed to. */
5
- channel: string;
6
- /** Human-facing channel name resolved at upgrade time, kept for log readability. */
7
- channelName?: string | null;
8
- /** Connector names belonging to that channel; used by tap-all replay filtering. */
9
- connectors: string[];
10
- tapAll?: boolean;
11
- /** Routing mode resolved from channel config at upgrade time. Defaults to fanout. */
12
- delivery?: "fanout" | "exclusive";
13
- };
14
- export type BroadcastEvent = {
15
- content: string;
16
- meta?: Record<string, string>;
17
- };
18
- export type ReplayableEvent = BroadcastEvent & {
19
- offset: number;
20
- };
21
- export type BroadcastSubscriber = (event: ReplayableEvent) => void;
22
- /**
23
- * Optional persistent replay source. Wired in by the gateway-server with
24
- * `FunnelEventStore` (SQLite-backed) so reconnects across daemon restarts
25
- * can recover events older than the in-memory buffer via an indexed
26
- * `seq > since` range scan.
27
- */
28
- type ReplaySource = {
29
- loadSince(since: number): ReplayableEvent[];
30
- };
31
- type Deps = {
32
- logger?: FunnelLogger;
33
- maxBufferedBytes?: number;
34
- now?: () => number;
35
- /** Number of recent events kept in the in-memory replay buffer. */
36
- replayBufferSize?: number;
37
- /** Hard byte cap on replay buffer payloads. Older events are evicted FIFO until under this cap. */
38
- replayBufferMaxBytes?: number;
39
- /** Persistent replay source consulted when the in-memory buffer cannot satisfy `since`. */
40
- persistentReplay?: ReplaySource;
41
- };
42
- type BroadcasterMetrics = {
43
- clients: number;
44
- subscribers: number;
45
- eventsBroadcast: number;
46
- droppedSlowClients: number;
47
- lastBroadcastAt: string | null;
48
- /** Latest emitted offset. Clients can `?since=<offset>` to ask for events strictly after this point. */
49
- latestOffset: number;
50
- /** Oldest offset still held in the replay buffer. Older values cannot be replayed and trigger a full resync. */
51
- oldestReplayableOffset: number | null;
52
- };
53
- /**
54
- * In-process pub/sub for connector events.
55
- *
56
- * Two outbound paths:
57
- * - WS clients connected via the gateway's `/ws` endpoint, scoped per channel
58
- * - In-process subscribers registered via `subscribe()` (programmable API)
59
- *
60
- * Backpressure: if a WS client's `bufferedAmount` exceeds `maxBufferedBytes`
61
- * (default 1 MiB), the client is closed with code 1009 and dropped from the
62
- * registry to keep one slow consumer from blocking the daemon.
63
- *
64
- * Replay: every emitted event gets a strictly increasing `offset`. The latest
65
- * `replayBufferSize` events are kept in memory; reconnecting WS clients can
66
- * pass `?since=<offset>` and the broadcaster resends matching events before
67
- * resuming the live stream. The in-memory ring covers short reconnects;
68
- * older history is served from the SQLite event store wired in as
69
- * `persistentReplay`.
70
- */
71
- export declare class FunnelBroadcaster {
72
- private readonly clients;
73
- private readonly subscribers;
74
- private readonly logger;
75
- private readonly maxBufferedBytes;
76
- private readonly now;
77
- private readonly replayBufferSize;
78
- private readonly replayBufferMaxBytes;
79
- private readonly replayBuffer;
80
- private readonly persistentReplay;
81
- private readonly exclusiveCursor;
82
- private replayBufferBytes;
83
- private eventsBroadcast;
84
- private droppedSlowClients;
85
- private lastBroadcastAt;
86
- private latestOffset;
87
- constructor(deps?: Deps);
88
- getMetrics(): BroadcasterMetrics;
89
- /**
90
- * Returns events with offset > since, filtered by the connector subscription
91
- * rules of `data`. Used at WS upgrade time when the client passes `?since=<offset>`.
92
- *
93
- * Two-tier lookup:
94
- * 1. The in-memory ring buffer (covers short reconnects, last `replayBufferSize` events).
95
- * 2. If `since` predates the oldest in-memory entry and a persistent replay source
96
- * is wired in (SQLite), the gap is filled from disk. This covers reconnects across
97
- * daemon restarts where the in-memory buffer was lost.
98
- *
99
- * Result is sorted ascending by offset and de-duplicated against the in-memory buffer.
100
- */
101
- replaySince(since: number, data: ClientData): ReplayableEvent[];
102
- private matchesClient;
103
- /**
104
- * Returns the list of WS clients that should receive `event`. Tap=all clients always
105
- * receive (passive observation). For each per-channel group:
106
- * - fanout → every matching client receives
107
- * - exclusive → exactly one client receives, picked round-robin per channel
108
- */
109
- private pickRecipients;
110
- addClient(ws: ServerWebSocket<unknown>, data: ClientData): void;
111
- removeClient(ws: ServerWebSocket<unknown>): void;
112
- getClientCount(): number;
113
- listChannels(): {
114
- channel: string;
115
- connectors: string[];
116
- }[];
117
- subscribe(handler: BroadcastSubscriber): () => void;
118
- broadcast(content: string, meta?: Record<string, string>): ReplayableEvent;
119
- /** Forward-seed the offset counter (used at startup from the persisted event store). */
120
- seedLatestOffset(offset: number): void;
121
- }
122
- export {};
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env bun
2
- export {};
@@ -1,7 +0,0 @@
1
- import type { GatewayRouteDeps } from "./routes/route-deps";
2
- export type Env = {
3
- Variables: {
4
- deps: GatewayRouteDeps;
5
- };
6
- };
7
- export declare const factory: import("hono/factory").Factory<Env, string>;
@@ -1,81 +0,0 @@
1
- import { z } from "zod";
2
- import type { ReplayableEvent } from "./broadcaster";
3
- /**
4
- * Replayable event payload persisted by the gateway. Domain events the
5
- * broadcaster emits to WS clients land here so reconnects across daemon
6
- * restarts can be served from disk. System events (gateway start, channel
7
- * connected, etc.) are routed to `FunnelLogger` instead — they never go
8
- * through this store, which keeps the seq space clean for replay.
9
- */
10
- export declare const funnelEventSchema: z.ZodObject<{
11
- type: z.ZodString;
12
- content: z.ZodString;
13
- channel_id: z.ZodNullable<z.ZodString>;
14
- connector_id: z.ZodNullable<z.ZodString>;
15
- meta: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>;
16
- }, z.core.$strip>;
17
- export type FunnelEvent = z.infer<typeof funnelEventSchema>;
18
- type Props = {
19
- /** SQLite database file path. Created on first write. ":memory:" for tests. */
20
- path: string;
21
- /** Override for tests. Defaults to `Date.now`. */
22
- now?: () => number;
23
- /** Optional row cap. Pruned on every insert. */
24
- maxRows?: number;
25
- /** Optional age cap in ms. Pruned on every insert. */
26
- maxAgeMs?: number;
27
- };
28
- /**
29
- * SQLite-backed event store. One indexed table holds every broadcaster
30
- * event with `channel_id` and `connector_id` as dedicated columns, so
31
- * per-channel and per-connector replay is an indexed range scan.
32
- *
33
- * Concurrency: `seq` is `INTEGER PRIMARY KEY`, so SQLite assigns it
34
- * atomically. The broadcaster owns its own offset counter at runtime
35
- * (seeded from `findMaxOffset()` at startup); each broadcaster event
36
- * flows in here via `record()` with that pre-assigned offset, which the
37
- * sink stores via `write()` — PK uniqueness catches double-emit bugs.
38
- *
39
- * System events (gateway lifecycle, channel connect/disconnect, etc.) do
40
- * NOT go through this store. They are diagnostic only and live in
41
- * `FunnelLogger`'s file so the seq space here stays exclusive to
42
- * broadcaster traffic. This is what makes the broadcaster's seq seeding
43
- * (`getMaxSeq()` at startup) correct without per-event coordination.
44
- */
45
- export declare class FunnelEventStore {
46
- private readonly sink;
47
- private readonly now;
48
- constructor(props: Props);
49
- /**
50
- * Persist a broadcaster-driven event with its assigned offset. Caller
51
- * (the gateway-server) supplies the offset from `broadcaster.broadcast()`
52
- * so this store and the broadcaster's in-memory ring stay aligned.
53
- */
54
- record(props: {
55
- content: string;
56
- channelId: string | null;
57
- connectorId: string | null;
58
- meta: Record<string, string> | null;
59
- offset: number;
60
- }): void;
61
- /**
62
- * Returns events with offset > since. Filtering by channel/connector is
63
- * the broadcaster's responsibility (it knows the client's subscription),
64
- * so this returns the full slice and lets the caller filter.
65
- */
66
- loadSince(since: number): ReplayableEvent[];
67
- /**
68
- * Returns events for one channel (and optionally one connector). Used
69
- * by the gateway logs CLI for scoped queries. Channel/connector filters
70
- * are indexed columns, so this is an indexed range scan.
71
- */
72
- loadForChannel(props: {
73
- channelId: string;
74
- connectorId?: string;
75
- sinceSeq?: number;
76
- limit?: number;
77
- }): ReplayableEvent[];
78
- findMaxOffset(): number;
79
- close(): void;
80
- }
81
- export {};
@@ -1,94 +0,0 @@
1
- import type { Server } from "bun";
2
- import type { FunnelChannels } from "../engine/channels/channels";
3
- import { FunnelBroadcaster } from "./broadcaster";
4
- import { FunnelEventStore } from "./funnel-event-store";
5
- import { FunnelListenerSupervisor } from "./listener-supervisor";
6
- import { FunnelLogger } from "../engine/logger/logger";
7
- import type { FunnelProcessRunner } from "../engine/process/process-runner";
8
- import type { FunnelSettingsReader } from "../engine/settings/settings-reader";
9
- import type { FunnelClock } from "../engine/time/clock";
10
- type Deps = {
11
- channels: FunnelChannels;
12
- settings: FunnelSettingsReader;
13
- port?: number;
14
- /** Directory holding the SQLite event store. The DB file lives at `<logDir>/events.db`. */
15
- logDir?: string;
16
- process?: FunnelProcessRunner;
17
- clock?: FunnelClock;
18
- logger?: FunnelLogger;
19
- selfPid?: number;
20
- killCompetingSlack?: boolean;
21
- /** Bearer token required for `/listeners*`, `/status`, and `/ws`. Empty string disables auth (tests only). */
22
- token?: string;
23
- };
24
- type WsData = {
25
- /** Stable channel id (uuid) the client subscribed to. "" for tap-all clients. */
26
- channel: string;
27
- /** Resolved channel name (for log readability). null for tap-all or unknown. */
28
- channelName: string | null;
29
- /** Connector names belonging to that channel; used by tap-all replay filtering. */
30
- connectors: string[];
31
- tapAll?: boolean;
32
- /** Routing mode for this channel; resolved at upgrade time from settings. */
33
- delivery: "fanout" | "exclusive";
34
- /** Replay any events with offset strictly greater than this on open, then resume the live stream. */
35
- since?: number;
36
- };
37
- /**
38
- * In-process gateway: runs `Bun.serve` (HTTP + WebSocket /ws), boots connector
39
- * listeners through `FunnelListenerSupervisor`, fans events out via
40
- * `FunnelBroadcaster`, and persists them via `FunnelEventStore` (SQLite).
41
- * System events (gateway lifecycle, connect/disconnect) flow to `FunnelLogger`
42
- * instead — keeping the SQLite seq space exclusive to broadcaster traffic so
43
- * the broadcaster's offset counter and `getMaxSeq()` stay aligned without
44
- * per-event coordination. Exposes `/listeners` HTTP for runtime
45
- * start/stop/restart of individual connectors.
46
- */
47
- export declare class FunnelGatewayServer {
48
- private readonly channels;
49
- private readonly settings;
50
- private readonly port;
51
- private readonly logDir;
52
- private readonly process?;
53
- private readonly logger;
54
- private readonly selfPid;
55
- private readonly killCompetingSlack;
56
- private readonly token;
57
- private readonly broadcaster;
58
- private readonly eventStore;
59
- private readonly supervisor;
60
- private readonly nowMs;
61
- private startedAt;
62
- private server;
63
- constructor(deps: Deps);
64
- start(): Promise<Server<WsData>>;
65
- stop(): Promise<void>;
66
- getStatus(): {
67
- clients: number;
68
- channels: {
69
- channel: string;
70
- connectors: string[];
71
- }[];
72
- };
73
- getBroadcaster(): FunnelBroadcaster;
74
- getSupervisor(): FunnelListenerSupervisor;
75
- getEventStore(): FunnelEventStore;
76
- private handleFetch;
77
- private handleWsOpen;
78
- private handleWsClose;
79
- private logServerStarted;
80
- private buildApp;
81
- /**
82
- * Reads the bearer token from the WebSocket upgrade request. Accepts:
83
- * - `Sec-WebSocket-Protocol: funnel.token.<value>` (preferred — header, never logged in URLs)
84
- * - `Authorization: Bearer <value>` (also header-based)
85
- * Returns true on a constant-time match against the daemon token.
86
- */
87
- private tokenMatchesUpgrade;
88
- private resolveChannel;
89
- private bootListeners;
90
- private notify;
91
- private lookupChannelId;
92
- private lookupConnectorId;
93
- }
94
- export {};
@@ -1,33 +0,0 @@
1
- import { FunnelFileSystem } from "../engine/fs/file-system";
2
- type Deps = {
3
- fs?: FunnelFileSystem;
4
- dir?: string;
5
- generate?: () => string;
6
- };
7
- /**
8
- * Reads / generates the gateway daemon token used to authenticate
9
- * `/listeners*`, `/status`, and `/ws` connections.
10
- *
11
- * Token file: `<dir>/gateway.token` (default `~/.funnel/gateway.token`),
12
- * written with mode 0600. Clients on the same machine as the daemon read
13
- * the file directly; the token never leaves the user's home directory.
14
- */
15
- export declare class FunnelGatewayToken {
16
- private readonly fs;
17
- private readonly path;
18
- private readonly generate;
19
- constructor(deps?: Deps);
20
- read(): string | null;
21
- /**
22
- * Returns the existing token or, if missing, generates one and writes it with mode 0600.
23
- *
24
- * NOTE: not atomic — two concurrent `ensure()` calls (e.g., `fnl gateway start` racing
25
- * itself before the PID lock is acquired) could each generate independent tokens. The
26
- * gateway PID file makes this practically a non-issue; if you need stronger guarantees,
27
- * take a file lock around this call externally.
28
- */
29
- ensure(): string;
30
- getPath(): string;
31
- }
32
- export declare const DEFAULT_GATEWAY_TOKEN_PATH: string;
33
- export {};