@interactive-inc/claude-funnel 0.32.0 → 0.34.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
@@ -1492,6 +1492,15 @@ declare const funnelJsonSchema: () => Record<string, unknown>;
1492
1492
  * spawned with the env set resolves the override.
1493
1493
  */
1494
1494
  declare function resolveFunnelDir(): string;
1495
+ declare const DEFAULT_GATEWAY_PORT = 9742;
1496
+ /**
1497
+ * Resolves the gateway port. Defaults to 9742 — the port a programmatically
1498
+ * hosted gateway (`new Funnel().gatewayServer()`) uses. The `funnel` CLI entry
1499
+ * sets `FUNNEL_PORT` to a distinct default so a CLI launch never collides with
1500
+ * an embedding app's gateway on 9742. Read at call time so a daemon spawned
1501
+ * with the env set resolves the override.
1502
+ */
1503
+ declare function resolveFunnelPort(): number;
1495
1504
  declare const FUNNEL_DIR: string;
1496
1505
  declare const SETTINGS_PATH: string;
1497
1506
  type Deps = {
@@ -1507,11 +1516,11 @@ declare class FunnelSettingsStore extends FunnelSettingsReader {
1507
1516
  read(): Settings;
1508
1517
  private looksLikeLegacy;
1509
1518
  /**
1510
- * Non-destructive migration for profiles written before `id` existed. The id
1511
- * is a later addition to an otherwise-compatible schema, so rather than
1512
- * rejecting the file we mint a uuid for each profile that lacks one; the next
1513
- * `write` persists it. Mutates `parsed` in place (it is freshly JSON-parsed
1514
- * and discarded after the schema parse, so no shared state is touched).
1519
+ * Non-destructive migration for profiles written before `id` existed. Mints a
1520
+ * uuid for each profile lacking one and returns whether anything was minted, so
1521
+ * `read` can persist it immediately a profile id must be STABLE across reads,
1522
+ * otherwise `setSessionId` (a second read) sees a different id and can't match
1523
+ * the one the launch used. Mutates `parsed` in place (freshly JSON-parsed).
1515
1524
  */
1516
1525
  private backfillProfileIds;
1517
1526
  write(settings: Settings): void;
@@ -4580,4 +4589,4 @@ ${string}`;
4580
4589
  };
4581
4590
  }, "/", "/update">;
4582
4591
  //#endregion
4583
- export { AliveStub, AttachOptions, BroadcastEvent, BroadcastSubscriber, CONNECTOR_CONNECTION_STATUSES, ChannelConfig, ChannelConnectorView, ChannelDeliveryMode, ChannelServerOptions, ChannelSpec, ConnectorConfig, ConnectorConnectionEvent, ConnectorConnectionQuery, ConnectorConnectionRecord, ConnectorConnectionStatus, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, ConnectorProcessedEvent, ConnectorProcessedQuery, ConnectorProcessedRecord, ConnectorQuery, ConnectorRawEvent, ConnectorRawQuery, ConnectorRawRecord, ConnectorSpec, ConnectorSyncOutcome, ConnectorType, DEFAULT_GATEWAY_TOKEN_PATH, DetachOptions, DiscordConnectorConfig, Env, FUNNEL_DIR, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, FileStat, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClaude, FunnelClock, FunnelConnectorFactory, FunnelConnectorListener, FunnelEvent, FunnelEventLog, FunnelEventRecord, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, type GatewayEmitInput, type GatewayRouteDeps, type Env$1 as GatewayServerEnv, GhConnectorConfig, LOCAL_CONFIG_FILENAME, LaunchOptions, ListListenersResult, ListenerEntry, ListenerOpResult, LocalConfig, LocalConfigSyncResult, LogEntry, MemoryConnectorDiagnosticLog, MemoryFunnelClock, MemoryFunnelEventLog, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MemoryProcessCall, MemoryProcessHandler, MemoryProcessResponse, MemoryProcessSyncHandler, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, NotifyFn, OnFunnelError, ProcessListStub, ProcessSnapshot, ProfileConfig, ProfileSpec, PublishRequest, PublishResponse, PublishResult, ReplayableEvent, RunOptions, RunResult, SETTINGS_PATH, SETTINGS_VERSION, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleEntry, ScheduleListenerOptions, Settings, SlackConnectorConfig, SlackListenerOptions, SlackProcessed, SlackProcessedEmit, SlackProcessedSkip, SlackRawEvent, SlackSkipReason, SqliteConnectorDiagnosticLog, SqliteFunnelEventLog, StoredConnectionEvent, StoredProcessedEvent, StoredRawEvent, channelConfigSchema, channelDeliveryModeSchema, channelSpecSchema, app as cliApp, connectorConfigSchema, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, localConfigSchema, profileConfigSchema, profileSpecSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, resolveFunnelDir, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
4592
+ export { AliveStub, AttachOptions, BroadcastEvent, BroadcastSubscriber, CONNECTOR_CONNECTION_STATUSES, ChannelConfig, ChannelConnectorView, ChannelDeliveryMode, ChannelServerOptions, ChannelSpec, ConnectorConfig, ConnectorConnectionEvent, ConnectorConnectionQuery, ConnectorConnectionRecord, ConnectorConnectionStatus, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, ConnectorProcessedEvent, ConnectorProcessedQuery, ConnectorProcessedRecord, ConnectorQuery, ConnectorRawEvent, ConnectorRawQuery, ConnectorRawRecord, ConnectorSpec, ConnectorSyncOutcome, ConnectorType, DEFAULT_GATEWAY_PORT, DEFAULT_GATEWAY_TOKEN_PATH, DetachOptions, DiscordConnectorConfig, Env, FUNNEL_DIR, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, FileStat, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClaude, FunnelClock, FunnelConnectorFactory, FunnelConnectorListener, FunnelEvent, FunnelEventLog, FunnelEventRecord, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, type GatewayEmitInput, type GatewayRouteDeps, type Env$1 as GatewayServerEnv, GhConnectorConfig, LOCAL_CONFIG_FILENAME, LaunchOptions, ListListenersResult, ListenerEntry, ListenerOpResult, LocalConfig, LocalConfigSyncResult, LogEntry, MemoryConnectorDiagnosticLog, MemoryFunnelClock, MemoryFunnelEventLog, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MemoryProcessCall, MemoryProcessHandler, MemoryProcessResponse, MemoryProcessSyncHandler, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, NotifyFn, OnFunnelError, ProcessListStub, ProcessSnapshot, ProfileConfig, ProfileSpec, PublishRequest, PublishResponse, PublishResult, ReplayableEvent, RunOptions, RunResult, SETTINGS_PATH, SETTINGS_VERSION, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleEntry, ScheduleListenerOptions, Settings, SlackConnectorConfig, SlackListenerOptions, SlackProcessed, SlackProcessedEmit, SlackProcessedSkip, SlackRawEvent, SlackSkipReason, SqliteConnectorDiagnosticLog, SqliteFunnelEventLog, StoredConnectionEvent, StoredProcessedEvent, StoredRawEvent, channelConfigSchema, channelDeliveryModeSchema, channelSpecSchema, app as cliApp, connectorConfigSchema, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, localConfigSchema, profileConfigSchema, profileSpecSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, resolveFunnelDir, resolveFunnelPort, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
package/dist/index.js CHANGED
@@ -112,6 +112,17 @@ function resolveFunnelDir() {
112
112
  if (override && override.length > 0) return override;
113
113
  return join(homedir(), ".funnel");
114
114
  }
115
+ const DEFAULT_GATEWAY_PORT = 9742;
116
+ /**
117
+ * Resolves the gateway port. Defaults to 9742 — the port a programmatically
118
+ * hosted gateway (`new Funnel().gatewayServer()`) uses. The `funnel` CLI entry
119
+ * sets `FUNNEL_PORT` to a distinct default so a CLI launch never collides with
120
+ * an embedding app's gateway on 9742. Read at call time so a daemon spawned
121
+ * with the env set resolves the override.
122
+ */
123
+ function resolveFunnelPort() {
124
+ return Number(process.env.FUNNEL_PORT) || 9742;
125
+ }
115
126
  const FUNNEL_DIR = join(homedir(), ".funnel");
116
127
  const SETTINGS_PATH = join(FUNNEL_DIR, "settings.json");
117
128
  const defaultFs$5 = new NodeFunnelFileSystem();
@@ -137,9 +148,10 @@ var FunnelSettingsStore = class extends FunnelSettingsReader {
137
148
  const parsed = JSON.parse(content);
138
149
  if (this.looksLikeLegacy(parsed)) throw new Error(`legacy settings.json detected at ${this.path}. The schema changed (channel.connectors are now nested objects with ids; profile fields renamed). Migration is intentionally not provided. Back up and remove the old file:\n mv ${this.path} ${this.path}.bak`);
139
150
  if (parsed && typeof parsed === "object" && "version" in parsed && parsed.version !== 1) throw new Error(`unsupported settings.json version (${this.path}): expected 1, got ${String(parsed.version)}`);
140
- this.backfillProfileIds(parsed);
151
+ const minted = this.backfillProfileIds(parsed);
141
152
  const result = settingsSchema.safeParse(parsed);
142
153
  if (!result.success) throw new Error(`invalid settings.json (${this.path}): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`);
154
+ if (minted) this.write(result.data);
143
155
  return result.data;
144
156
  }
145
157
  looksLikeLegacy(parsed) {
@@ -161,21 +173,26 @@ var FunnelSettingsStore = class extends FunnelSettingsReader {
161
173
  return false;
162
174
  }
163
175
  /**
164
- * Non-destructive migration for profiles written before `id` existed. The id
165
- * is a later addition to an otherwise-compatible schema, so rather than
166
- * rejecting the file we mint a uuid for each profile that lacks one; the next
167
- * `write` persists it. Mutates `parsed` in place (it is freshly JSON-parsed
168
- * and discarded after the schema parse, so no shared state is touched).
176
+ * Non-destructive migration for profiles written before `id` existed. Mints a
177
+ * uuid for each profile lacking one and returns whether anything was minted, so
178
+ * `read` can persist it immediately a profile id must be STABLE across reads,
179
+ * otherwise `setSessionId` (a second read) sees a different id and can't match
180
+ * the one the launch used. Mutates `parsed` in place (freshly JSON-parsed).
169
181
  */
170
182
  backfillProfileIds(parsed) {
171
- if (!parsed || typeof parsed !== "object") return;
183
+ if (!parsed || typeof parsed !== "object") return false;
172
184
  const obj = parsed;
173
- if (!Array.isArray(obj.profiles)) return;
185
+ if (!Array.isArray(obj.profiles)) return false;
186
+ let minted = false;
174
187
  for (const profile of obj.profiles) {
175
188
  if (!profile || typeof profile !== "object") continue;
176
189
  const p = profile;
177
- if (typeof p.id !== "string") p.id = this.idGenerator.generate();
190
+ if (typeof p.id !== "string") {
191
+ p.id = this.idGenerator.generate();
192
+ minted = true;
193
+ }
178
194
  }
195
+ return minted;
179
196
  }
180
197
  write(settings) {
181
198
  this.fs.mkdirSync(dirname(this.path), { recursive: true });
@@ -784,6 +801,7 @@ var FunnelClaude = class {
784
801
  for (const [key, value] of Object.entries(recipeEnv)) env[key] = value;
785
802
  for (const [key, value] of Object.entries(globalThis.process.env)) if (typeof value === "string") env[key] = value;
786
803
  env.FUNNEL_CHANNEL_ID = channelId;
804
+ env.FUNNEL_PORT = String(resolveFunnelPort());
787
805
  return env;
788
806
  }
789
807
  };
@@ -1802,7 +1820,6 @@ const resolveDaemonScript = () => {
1802
1820
  };
1803
1821
  //#endregion
1804
1822
  //#region lib/gateway/gateway.ts
1805
- const DEFAULT_PORT$1 = 9742;
1806
1823
  const STARTUP_TIMEOUT_MS = 5e3;
1807
1824
  const SIGTERM_TIMEOUT_MS = 2e3;
1808
1825
  const POLL_INTERVAL_MS = 100;
@@ -1836,7 +1853,7 @@ var FunnelGateway = class {
1836
1853
  this.tmpDir = deps.tmpDir ?? funnelTmpDir();
1837
1854
  this.pidFile = join(this.dir, "gateway.pid");
1838
1855
  this.gatewayLog = join(this.tmpDir, "gateway.log");
1839
- this.port = deps.port ?? DEFAULT_PORT$1;
1856
+ this.port = deps.port ?? resolveFunnelPort();
1840
1857
  this.sleep = deps.sleep ?? defaultSleep$1;
1841
1858
  Object.freeze(this);
1842
1859
  }
@@ -3044,7 +3061,6 @@ const statusHandler$1 = factory$1.createHandlers((c) => {
3044
3061
  const gatewayRoutes = factory$1.createApp().get("/health", ...healthHandler).get("/status", ...statusHandler$1).get("/listeners", ...listenersListHandler).post("/listeners/:channel/:connector/start", ...listenersStartHandler).delete("/listeners/:channel/:connector", ...listenersStopHandler).post("/listeners/:channel/:connector/restart", ...listenersRestartHandler).post("/channels/:channel/connectors/:connector/call", ...channelsConnectorsCallHandler).post("/channels/:channel/publish", ...channelsPublishHandler$1);
3045
3062
  //#endregion
3046
3063
  //#region lib/gateway/gateway-server.ts
3047
- const DEFAULT_PORT = 9742;
3048
3064
  const DEFAULT_HOST = "127.0.0.1";
3049
3065
  const LOOPBACK_HOSTS = new Set([
3050
3066
  "127.0.0.1",
@@ -3087,7 +3103,7 @@ var FunnelGatewayServer = class {
3087
3103
  constructor(deps) {
3088
3104
  this.channels = deps.channels;
3089
3105
  this.settings = deps.settings;
3090
- this.port = deps.port ?? DEFAULT_PORT;
3106
+ this.port = deps.port ?? resolveFunnelPort();
3091
3107
  this.hostname = deps.hostname ?? DEFAULT_HOST;
3092
3108
  this.dbPath = deps.dbPath ?? defaultDbPath();
3093
3109
  this.process = deps.process;
@@ -3857,10 +3873,9 @@ const usageHintForType = (type) => {
3857
3873
  //#endregion
3858
3874
  //#region lib/engine/mcp/channel-server.ts
3859
3875
  const DEFAULT_FUNNEL_DIR = join(homedir(), ".funnel");
3860
- const DEFAULT_GATEWAY_BASE_URL = "http://localhost:9742";
3861
3876
  const startChannelServer = async (options = {}) => {
3862
3877
  const dir = options.dir ?? DEFAULT_FUNNEL_DIR;
3863
- const gatewayBaseUrl = options.gatewayUrl ?? process.env.FUNNEL_GATEWAY_URL ?? DEFAULT_GATEWAY_BASE_URL;
3878
+ const gatewayBaseUrl = options.gatewayUrl ?? process.env.FUNNEL_GATEWAY_URL ?? `http://localhost:${resolveFunnelPort()}`;
3864
3879
  const gatewayWsUrl = `${gatewayBaseUrl.replace(/^http/, "ws")}/ws`;
3865
3880
  const channelId = options.channelId ?? process.env.FUNNEL_CHANNEL_ID;
3866
3881
  const channel = channelId ? readChannelConnectors(dir, channelId) : null;
@@ -5387,7 +5402,7 @@ Spawned as a detached background process so it keeps running after the terminal
5387
5402
  On macOS wraps the process with caffeinate -is by default to prevent idle and system sleep.
5388
5403
  Use --no-caffeine to disable caffeinate.
5389
5404
 
5390
- port: 9742 (override via FUNNEL_PORT)
5405
+ port: 9743 (CLI default; override via FUNNEL_PORT)
5391
5406
  pid: ~/.funnel/gateway.pid
5392
5407
  log: ${join(funnelTmpDir(), "gateway.log")}
5393
5408
 
@@ -5747,4 +5762,4 @@ const createCliApp = (funnel) => {
5747
5762
  /** CLI Hono app wired to a default `new Funnel()`. For embedding with a custom Funnel use `createCliApp`. */
5748
5763
  const app = createCliApp(new Funnel());
5749
5764
  //#endregion
5750
- export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClaude, FunnelClock, FunnelConnectorFactory, FunnelConnectorListener, FunnelEventLog, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, MemoryConnectorDiagnosticLog, MemoryFunnelClock, MemoryFunnelEventLog, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, SETTINGS_PATH, SETTINGS_VERSION, SqliteConnectorDiagnosticLog, SqliteFunnelEventLog, channelConfigSchema, channelDeliveryModeSchema, channelSpecSchema, app as cliApp, connectorConfigSchema, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, localConfigSchema, profileConfigSchema, profileSpecSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, resolveFunnelDir, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
5765
+ export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, DEFAULT_GATEWAY_PORT, DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClaude, FunnelClock, FunnelConnectorFactory, FunnelConnectorListener, FunnelEventLog, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, MemoryConnectorDiagnosticLog, MemoryFunnelClock, MemoryFunnelEventLog, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, SETTINGS_PATH, SETTINGS_VERSION, SqliteConnectorDiagnosticLog, SqliteFunnelEventLog, channelConfigSchema, channelDeliveryModeSchema, channelSpecSchema, app as cliApp, connectorConfigSchema, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, localConfigSchema, profileConfigSchema, profileSpecSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, resolveFunnelDir, resolveFunnelPort, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.32.0",
3
+ "version": "0.34.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",