@interactive-inc/claude-funnel 0.8.0 → 0.8.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 (127) hide show
  1. package/dist/bin.js +583 -656
  2. package/dist/cli/factory.d.ts +7 -0
  3. package/dist/cli/router/query-to-cli-args.d.ts +1 -0
  4. package/dist/cli/router/to-request.d.ts +5 -0
  5. package/dist/cli/router/validator.d.ts +5 -0
  6. package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +42 -0
  7. package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +46 -0
  8. package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +54 -0
  9. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +66 -0
  10. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +42 -0
  11. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +46 -0
  12. package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +90 -0
  13. package/dist/cli/routes/channels.$channel.connectors.d.ts +38 -0
  14. package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +42 -0
  15. package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +62 -0
  16. package/dist/cli/routes/channels.$channel.d.ts +38 -0
  17. package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +42 -0
  18. package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +28 -0
  19. package/dist/cli/routes/channels.add.$channel.d.ts +46 -0
  20. package/dist/cli/routes/channels.d.ts +16 -0
  21. package/dist/cli/routes/channels.remove.$channel.d.ts +38 -0
  22. package/dist/cli/routes/claude.d.ts +32 -0
  23. package/dist/cli/routes/gateway.d.ts +20 -0
  24. package/dist/cli/routes/gateway.listeners.d.ts +17 -0
  25. package/dist/cli/routes/gateway.logs.d.ts +24 -0
  26. package/dist/cli/routes/gateway.restart.d.ts +24 -0
  27. package/dist/cli/routes/gateway.run.d.ts +24 -0
  28. package/dist/cli/routes/gateway.start.d.ts +24 -0
  29. package/dist/cli/routes/gateway.status.d.ts +13 -0
  30. package/dist/cli/routes/gateway.stop.d.ts +16 -0
  31. package/dist/cli/routes/index.d.ts +1222 -0
  32. package/dist/cli/routes/profiles.$profile.as-default.d.ts +38 -0
  33. package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +42 -0
  34. package/dist/cli/routes/profiles.$profile.run.d.ts +46 -0
  35. package/dist/cli/routes/profiles.add.$profile.d.ts +54 -0
  36. package/dist/cli/routes/profiles.d.ts +16 -0
  37. package/dist/cli/routes/profiles.remove.$profile.d.ts +38 -0
  38. package/dist/cli/routes/profiles.set.$profile.d.ts +54 -0
  39. package/dist/cli/routes/status.d.ts +16 -0
  40. package/dist/cli/routes/update.d.ts +16 -0
  41. package/dist/connectors/connector-adapter.d.ts +8 -0
  42. package/dist/connectors/connector-config-schema.d.ts +43 -0
  43. package/dist/connectors/connector-factory.d.ts +32 -0
  44. package/dist/connectors/connector-listener.d.ts +17 -0
  45. package/dist/connectors/discord-adapter.d.ts +14 -0
  46. package/dist/connectors/discord-connector-schema.d.ts +10 -0
  47. package/dist/connectors/discord-event-processor.d.ts +26 -0
  48. package/dist/connectors/discord-listener.d.ts +17 -0
  49. package/dist/connectors/gh-adapter.d.ts +11 -0
  50. package/dist/connectors/gh-connector-schema.d.ts +10 -0
  51. package/dist/connectors/gh-listener.d.ts +26 -0
  52. package/dist/connectors/match-cron.d.ts +1 -0
  53. package/dist/connectors/schedule-connector-schema.d.ts +45 -0
  54. package/dist/connectors/schedule-listener.d.ts +30 -0
  55. package/dist/connectors/schedule-state-store.d.ts +19 -0
  56. package/dist/connectors/slack-adapter.d.ts +15 -0
  57. package/dist/connectors/slack-connector-schema.d.ts +11 -0
  58. package/dist/connectors/slack-event-processor.d.ts +27 -0
  59. package/dist/connectors/slack-listener.d.ts +17 -0
  60. package/dist/engine/channels/channels.d.ts +106 -0
  61. package/dist/engine/claude/claude.d.ts +49 -0
  62. package/dist/engine/claude/gateway-controller.d.ts +6 -0
  63. package/dist/engine/fs/file-system.d.ts +24 -0
  64. package/dist/engine/fs/memory-file-system.d.ts +31 -0
  65. package/dist/engine/fs/node-file-system.d.ts +15 -0
  66. package/dist/engine/http/http-client.d.ts +15 -0
  67. package/dist/engine/http/memory-http-client.d.ts +12 -0
  68. package/dist/engine/http/node-http-client.d.ts +5 -0
  69. package/dist/engine/id/id-generator.d.ts +7 -0
  70. package/dist/engine/id/memory-id-generator.d.ts +11 -0
  71. package/dist/engine/id/node-id-generator.d.ts +4 -0
  72. package/dist/engine/logger/logger.d.ts +11 -0
  73. package/dist/engine/logger/memory-logger.d.ts +14 -0
  74. package/dist/engine/logger/node-logger.d.ts +15 -0
  75. package/dist/engine/logger/noop-logger.d.ts +7 -0
  76. package/dist/engine/mcp/channel-server.d.ts +1 -0
  77. package/dist/engine/mcp/mcp.d.ts +22 -0
  78. package/dist/engine/process/memory-process-runner.d.ts +43 -0
  79. package/dist/engine/process/node-process-runner.d.ts +9 -0
  80. package/dist/engine/process/process-runner.d.ts +29 -0
  81. package/dist/engine/profiles/profile-channel-checker.d.ts +7 -0
  82. package/dist/engine/profiles/profiles.d.ts +31 -0
  83. package/dist/engine/settings/mock-settings-reader.d.ts +9 -0
  84. package/dist/engine/settings/settings-reader.d.ts +5 -0
  85. package/dist/engine/settings/settings-schema.d.ts +132 -0
  86. package/dist/engine/settings/settings-store.d.ts +18 -0
  87. package/dist/engine/time/clock.d.ts +9 -0
  88. package/dist/engine/time/memory-clock.d.ts +12 -0
  89. package/dist/engine/time/node-clock.d.ts +4 -0
  90. package/dist/funnel.d.ts +95 -0
  91. package/dist/gateway/auth-middleware.d.ts +14 -0
  92. package/dist/gateway/broadcaster.d.ts +122 -0
  93. package/dist/gateway/daemon.d.ts +2 -0
  94. package/dist/gateway/daemon.js +192 -220
  95. package/dist/gateway/factory.d.ts +7 -0
  96. package/dist/gateway/funnel-event-store.d.ts +81 -0
  97. package/dist/gateway/gateway-server.d.ts +94 -0
  98. package/dist/gateway/gateway-token.d.ts +33 -0
  99. package/dist/gateway/gateway.d.ts +58 -0
  100. package/dist/gateway/kill-competing-slack-gateways.d.ts +9 -0
  101. package/dist/gateway/listener-supervisor.d.ts +85 -0
  102. package/dist/gateway/listeners-client.d.ts +53 -0
  103. package/dist/gateway/resolve-daemon-script.d.ts +11 -0
  104. package/dist/gateway/routes/channels.connectors.call.d.ts +41 -0
  105. package/dist/gateway/routes/health.d.ts +17 -0
  106. package/dist/gateway/routes/index.d.ts +209 -0
  107. package/dist/gateway/routes/listeners.list.d.ts +14 -0
  108. package/dist/gateway/routes/listeners.restart.d.ts +34 -0
  109. package/dist/gateway/routes/listeners.start.d.ts +34 -0
  110. package/dist/gateway/routes/listeners.stop.d.ts +34 -0
  111. package/dist/gateway/routes/route-deps.d.ts +10 -0
  112. package/dist/gateway/routes/status.d.ts +30 -0
  113. package/dist/gateway/routes/validator.d.ts +19 -0
  114. package/dist/index.d.ts +36 -0
  115. package/dist/index.js +3575 -0
  116. package/dist/logger/leuco-human-file-writer.d.ts +33 -0
  117. package/dist/logger/leuco-human-logger.d.ts +46 -0
  118. package/dist/logger/leuco-human-record.d.ts +15 -0
  119. package/dist/logger/leuco-human-stdout-writer.d.ts +20 -0
  120. package/dist/logger/leuco-human-writer.d.ts +13 -0
  121. package/dist/logger/leuco-logger-memory-sink.d.ts +33 -0
  122. package/dist/logger/leuco-logger-record.d.ts +13 -0
  123. package/dist/logger/leuco-logger-sink.d.ts +34 -0
  124. package/dist/logger/leuco-logger-sqlite-sink.d.ts +102 -0
  125. package/dist/logger/leuco-logger.d.ts +56 -0
  126. package/lib/index.ts +2 -0
  127. package/package.json +14 -9
@@ -0,0 +1,7 @@
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>;
@@ -0,0 +1,81 @@
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 {};
@@ -0,0 +1,94 @@
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 {};
@@ -0,0 +1,33 @@
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 {};
@@ -0,0 +1,58 @@
1
+ import { FunnelFileSystem } from "../engine/fs/file-system";
2
+ import { FunnelProcessRunner } from "../engine/process/process-runner";
3
+ import { FunnelClock } from "../engine/time/clock";
4
+ type Deps = {
5
+ process?: FunnelProcessRunner;
6
+ fs?: FunnelFileSystem;
7
+ clock?: FunnelClock;
8
+ dir?: string;
9
+ tmpDir?: string;
10
+ port?: number;
11
+ sleep?: (ms: number) => Promise<void>;
12
+ };
13
+ /**
14
+ * Manages the gateway daemon as a separate process via PID file.
15
+ * Use `start()` to spawn `bun daemon.ts` in the background and `stop()` to
16
+ * terminate it. For an in-process gateway, use `Funnel.gatewayServer` instead.
17
+ */
18
+ export declare class FunnelGateway {
19
+ private readonly process;
20
+ private readonly fs;
21
+ private readonly clock;
22
+ private readonly pidFile;
23
+ private readonly logDir;
24
+ private readonly gatewayLog;
25
+ private readonly tmpDir;
26
+ private readonly port;
27
+ private readonly sleep;
28
+ constructor(deps?: Deps);
29
+ isRunning(): boolean;
30
+ getStatus(): {
31
+ running: boolean;
32
+ pid: number | null;
33
+ port: number;
34
+ };
35
+ start(options?: {
36
+ caffeinate?: boolean;
37
+ }): Promise<boolean>;
38
+ buildStartCommand(gatewayScript: string, options?: {
39
+ caffeinate?: boolean;
40
+ }): string;
41
+ stop(): Promise<boolean>;
42
+ restart(options?: {
43
+ onlyIfRunning?: boolean;
44
+ caffeinate?: boolean;
45
+ }): Promise<{
46
+ ok: boolean;
47
+ wasRunning: boolean;
48
+ stopped: boolean;
49
+ started: boolean;
50
+ }>;
51
+ getLogDir(): string;
52
+ getGatewayLog(): string;
53
+ getPort(): number;
54
+ private readPid;
55
+ private removePid;
56
+ private isProcessAlive;
57
+ }
58
+ export {};
@@ -0,0 +1,9 @@
1
+ import { FunnelLogger } from "../engine/logger/logger";
2
+ import { FunnelProcessRunner } from "../engine/process/process-runner";
3
+ type Props = {
4
+ selfPid: number;
5
+ process?: FunnelProcessRunner;
6
+ logger?: FunnelLogger;
7
+ };
8
+ export declare const killCompetingSlackGateways: (props: Props) => Promise<number[]>;
9
+ export {};
@@ -0,0 +1,85 @@
1
+ import type { ConnectorConfig } from "../connectors/connector-config-schema";
2
+ import type { FunnelConnectorListener } from "../connectors/connector-listener";
3
+ import type { ChannelConnectorView } from "../engine/channels/channels";
4
+ import { FunnelLogger } from "../engine/logger/logger";
5
+ type ConnectorRegistry = {
6
+ listAllConnectors(): ChannelConnectorView[];
7
+ createListener(channelName: string, connectorName: string): {
8
+ config: ConnectorConfig;
9
+ channelId: string;
10
+ listener: FunnelConnectorListener;
11
+ } | null;
12
+ };
13
+ type SupervisorNotify = (channelName: string, connectorName: string, content: string, meta?: Record<string, string>) => Promise<void>;
14
+ type Deps = {
15
+ channels: ConnectorRegistry;
16
+ notify: SupervisorNotify;
17
+ logger?: FunnelLogger;
18
+ healthCheckIntervalMs?: number;
19
+ maxBackoffMs?: number;
20
+ sleep?: (ms: number) => Promise<void>;
21
+ now?: () => number;
22
+ };
23
+ type ListenerEntryStatus = {
24
+ channelName: string;
25
+ channelId: string;
26
+ name: string;
27
+ type: ConnectorConfig["type"];
28
+ alive: boolean;
29
+ events: number;
30
+ errors: number;
31
+ failureCount: number;
32
+ lastEventAt: string | null;
33
+ };
34
+ /**
35
+ * Owns the running listener instances and their lifecycle.
36
+ *
37
+ * Lives in the gateway process and is the only place that calls
38
+ * `listener.start()` / `listener.stop()`. Each entry is keyed by
39
+ * `${channelName}/${connectorName}` so the same connector name can exist in
40
+ * multiple channels without colliding.
41
+ *
42
+ * Periodically polls each running listener's `isAlive()` and auto-restarts
43
+ * dead listeners with exponential backoff (1s, 2s, 4s, ... capped). Resets
44
+ * the backoff counter on successful restart.
45
+ */
46
+ export declare class FunnelListenerSupervisor {
47
+ private readonly channels;
48
+ private readonly notify;
49
+ private readonly logger;
50
+ private readonly running;
51
+ private readonly failureCounts;
52
+ private readonly stats;
53
+ private readonly healthCheckIntervalMs;
54
+ private readonly maxBackoffMs;
55
+ private readonly sleep;
56
+ private readonly now;
57
+ private healthCheckTimer;
58
+ private healthCheckInFlight;
59
+ constructor(deps: Deps);
60
+ static keyOf(channelName: string, connectorName: string): string;
61
+ isRunning(channelName: string, connectorName: string): boolean;
62
+ list(): ListenerEntryStatus[];
63
+ start(channelName: string, connectorName: string): Promise<{
64
+ ok: boolean;
65
+ reason?: string;
66
+ }>;
67
+ stop(channelName: string, connectorName: string): Promise<{
68
+ ok: boolean;
69
+ reason?: string;
70
+ }>;
71
+ restart(channelName: string, connectorName: string): Promise<{
72
+ ok: boolean;
73
+ reason?: string;
74
+ }>;
75
+ startAll(): Promise<void>;
76
+ stopAll(): Promise<void>;
77
+ private ensureStats;
78
+ private recordEvent;
79
+ private recordError;
80
+ private startHealthCheck;
81
+ private stopHealthCheck;
82
+ private runHealthCheck;
83
+ private recoverDead;
84
+ }
85
+ export {};
@@ -0,0 +1,53 @@
1
+ import { z } from "zod";
2
+ type Deps = {
3
+ port: number;
4
+ isDaemonRunning: () => boolean;
5
+ /** Returns the daemon's gateway token, or null if unavailable. Sent as `Authorization: Bearer`. */
6
+ getToken?: () => string | null;
7
+ };
8
+ declare const listenerEntrySchema: z.ZodObject<{
9
+ channelName: z.ZodString;
10
+ channelId: z.ZodString;
11
+ name: z.ZodString;
12
+ type: z.ZodString;
13
+ alive: z.ZodBoolean;
14
+ }, z.core.$strip>;
15
+ export type ListenerEntry = z.infer<typeof listenerEntrySchema>;
16
+ export type ListenerOpResult = {
17
+ state: "ok";
18
+ } | {
19
+ state: "offline";
20
+ } | {
21
+ state: "error";
22
+ reason: string;
23
+ };
24
+ export type ListListenersResult = {
25
+ state: "ok";
26
+ listeners: ListenerEntry[];
27
+ } | {
28
+ state: "offline";
29
+ } | {
30
+ state: "error";
31
+ reason: string;
32
+ };
33
+ /**
34
+ * HTTP client for listener operations on a running gateway daemon.
35
+ *
36
+ * Returns `{ state: "offline" }` when the daemon isn't running so callers
37
+ * (CLI hot-reload paths) can treat that as a no-op without parsing strings.
38
+ * Pair this with `FunnelGateway` (process control) for the full picture.
39
+ */
40
+ export declare class FunnelListenersClient {
41
+ private readonly port;
42
+ private readonly isDaemonRunning;
43
+ private readonly getToken;
44
+ constructor(deps: Deps);
45
+ list(): Promise<ListListenersResult>;
46
+ start(channelName: string, connectorName: string): Promise<ListenerOpResult>;
47
+ stop(channelName: string, connectorName: string): Promise<ListenerOpResult>;
48
+ restart(channelName: string, connectorName: string): Promise<ListenerOpResult>;
49
+ private path;
50
+ private authHeaders;
51
+ private call;
52
+ }
53
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Locate the daemon entry script. Works in both dev (running from source)
3
+ * and built mode (bundled into dist/bin.js with daemon at dist/gateway/daemon.js).
4
+ *
5
+ * The candidates cover:
6
+ * 1. dev: this helper lives at lib/gateway/, so daemon.ts is its sibling
7
+ * 2. built sibling: dist/gateway/daemon.js if the helper itself ends up at dist/gateway/
8
+ * 3. bundled: when this helper is inlined into dist/bin.js, import.meta.dir is dist/,
9
+ * and daemon.js lives at dist/gateway/daemon.js
10
+ */
11
+ export declare const resolveDaemonScript: () => string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * POST /channels/:channel/connectors/:connector/call
3
+ *
4
+ * Generic adapter call. Used by the funnel MCP server (running in the Claude
5
+ * Code process) to send replies/reactions/etc. without spawning a CLI
6
+ * subprocess. Mirrors the CLI's `funnel channels <c> connectors <conn> request
7
+ * --method=...` but with a structured JSON body and no shell.
8
+ */
9
+ export declare const channelsConnectorsCallHandler: [import("hono/types").H<import("hono").Env, string, {
10
+ in: {
11
+ param: {
12
+ channel: string;
13
+ connector: string;
14
+ };
15
+ };
16
+ out: {
17
+ param: {
18
+ channel: string;
19
+ connector: string;
20
+ };
21
+ };
22
+ }, import("hono").TypedResponse<{
23
+ ok: boolean;
24
+ reason: string;
25
+ }, 400, "json">>, import("hono/types").H<import("../factory").Env, string, {
26
+ in: {
27
+ param: {
28
+ channel: string;
29
+ connector: string;
30
+ };
31
+ };
32
+ out: {
33
+ param: {
34
+ channel: string;
35
+ connector: string;
36
+ };
37
+ };
38
+ }, Promise<Response & import("hono").TypedResponse<{
39
+ ok: true;
40
+ result: import("hono/utils/types").JSONValue;
41
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>>];
@@ -0,0 +1,17 @@
1
+ /** GET /health — liveness + listener registry snapshot. */
2
+ export declare const healthHandler: [import("hono/types").H<import("../factory").Env, string, {}, Response & import("hono").TypedResponse<{
3
+ ok: true;
4
+ pid: number;
5
+ clients: number;
6
+ listeners: {
7
+ channelName: string;
8
+ channelId: string;
9
+ name: string;
10
+ type: import("../..").ConnectorConfig["type"];
11
+ alive: boolean;
12
+ events: number;
13
+ errors: number;
14
+ failureCount: number;
15
+ lastEventAt: string | null;
16
+ }[];
17
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>];