@interactive-inc/claude-funnel 0.15.1 → 0.16.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.
- package/README.md +47 -24
- package/dist/bin.js +572 -556
- package/dist/gateway/daemon.js +228 -215
- package/dist/index.d.ts +47 -12
- package/dist/index.js +54 -38
- package/funnel.schema.json +172 -0
- package/package.json +2 -2
- package/schemas/funnel.schema.json +0 -144
package/dist/index.d.ts
CHANGED
|
@@ -476,9 +476,8 @@ declare const connectorSpecSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
476
476
|
name: z.ZodString;
|
|
477
477
|
}, z.core.$strip>], "type">;
|
|
478
478
|
type ConnectorSpec = z.infer<typeof connectorSpecSchema>;
|
|
479
|
-
declare const
|
|
480
|
-
|
|
481
|
-
channel: z.ZodString;
|
|
479
|
+
declare const channelSpecSchema: z.ZodObject<{
|
|
480
|
+
name: z.ZodString;
|
|
482
481
|
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
483
482
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
484
483
|
connectors: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -506,6 +505,41 @@ declare const localConfigSchema: z.ZodObject<{
|
|
|
506
505
|
name: z.ZodString;
|
|
507
506
|
}, z.core.$strip>], "type">>>;
|
|
508
507
|
}, z.core.$strip>;
|
|
508
|
+
type ChannelSpec = z.infer<typeof channelSpecSchema>;
|
|
509
|
+
declare const localConfigSchema: z.ZodObject<{
|
|
510
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
511
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
512
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
513
|
+
channels: z.ZodArray<z.ZodObject<{
|
|
514
|
+
name: z.ZodString;
|
|
515
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
516
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
517
|
+
connectors: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
518
|
+
type: z.ZodLiteral<"slack">;
|
|
519
|
+
name: z.ZodString;
|
|
520
|
+
botToken: z.ZodOptional<z.ZodString>;
|
|
521
|
+
appToken: z.ZodOptional<z.ZodString>;
|
|
522
|
+
env: z.ZodOptional<z.ZodObject<{
|
|
523
|
+
botToken: z.ZodOptional<z.ZodString>;
|
|
524
|
+
appToken: z.ZodOptional<z.ZodString>;
|
|
525
|
+
}, z.core.$strip>>;
|
|
526
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
527
|
+
type: z.ZodLiteral<"discord">;
|
|
528
|
+
name: z.ZodString;
|
|
529
|
+
botToken: z.ZodOptional<z.ZodString>;
|
|
530
|
+
env: z.ZodOptional<z.ZodObject<{
|
|
531
|
+
botToken: z.ZodOptional<z.ZodString>;
|
|
532
|
+
}, z.core.$strip>>;
|
|
533
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
534
|
+
type: z.ZodLiteral<"gh">;
|
|
535
|
+
name: z.ZodString;
|
|
536
|
+
pollInterval: z.ZodOptional<z.ZodNumber>;
|
|
537
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
538
|
+
type: z.ZodLiteral<"schedule">;
|
|
539
|
+
name: z.ZodString;
|
|
540
|
+
}, z.core.$strip>], "type">>>;
|
|
541
|
+
}, z.core.$strip>>;
|
|
542
|
+
}, z.core.$strip>;
|
|
509
543
|
type LocalConfig = z.infer<typeof localConfigSchema>;
|
|
510
544
|
declare const LOCAL_CONFIG_FILENAME = "funnel.json";
|
|
511
545
|
declare const LOCAL_ENV_FILENAME = ".env.local";
|
|
@@ -545,8 +579,8 @@ type Deps$9 = {
|
|
|
545
579
|
env?: NodeJS.ProcessEnv;
|
|
546
580
|
};
|
|
547
581
|
/**
|
|
548
|
-
* Reconciles a
|
|
549
|
-
* is the source of truth for the channel it declares:
|
|
582
|
+
* Reconciles a single funnel.json channel spec with `~/.funnel/settings.json`.
|
|
583
|
+
* The spec is the source of truth for the channel it declares:
|
|
550
584
|
*
|
|
551
585
|
* - missing channel → created
|
|
552
586
|
* - declared connector matched by name → tokens reconciled
|
|
@@ -555,9 +589,10 @@ type Deps$9 = {
|
|
|
555
589
|
* - declared connector with no match → added
|
|
556
590
|
* - any connector left in the channel that the spec did not touch → removed
|
|
557
591
|
*
|
|
558
|
-
* Removal only fires when
|
|
559
|
-
* field means "do not manage connectors from here" and leaves
|
|
560
|
-
* `~/.funnel` alone.
|
|
592
|
+
* Removal only fires when the channel spec has a `connectors` field. An
|
|
593
|
+
* absent field means "do not manage connectors from here" and leaves
|
|
594
|
+
* everything in `~/.funnel` alone. Other channels in funnel.json (not
|
|
595
|
+
* passed to this call) are untouched.
|
|
561
596
|
*/
|
|
562
597
|
declare class FunnelLocalConfigSync {
|
|
563
598
|
private readonly channels;
|
|
@@ -565,7 +600,7 @@ declare class FunnelLocalConfigSync {
|
|
|
565
600
|
private readonly prompter;
|
|
566
601
|
private readonly env;
|
|
567
602
|
constructor(deps: Deps$9);
|
|
568
|
-
ensure(
|
|
603
|
+
ensure(channel: ChannelSpec, cwd: string): Promise<void>;
|
|
569
604
|
private ensureConnector;
|
|
570
605
|
private ensureSlack;
|
|
571
606
|
private ensureDiscord;
|
|
@@ -1613,7 +1648,7 @@ declare const createCliApp: (funnel: Funnel) => _$hono_hono_base0.HonoBase<Env,
|
|
|
1613
1648
|
channel?: string | undefined;
|
|
1614
1649
|
};
|
|
1615
1650
|
};
|
|
1616
|
-
output: "funnel claude — launch Claude Code\n\nusage:\n funnel claude launch
|
|
1651
|
+
output: "funnel claude — launch Claude Code\n\nusage:\n funnel claude launch the first channel from funnel.json, or the default profile\n funnel claude --channel <name> with funnel.json: select that channel; without: raw launch\n funnel claude -p <name> launch a named profile\n funnel claude --profile <name> (long form)\n funnel claude [...] any other argument is forwarded to the claude CLI\n\nresolution order:\n 1. --help print this help\n 2. --profile <name> named profile (ignores funnel.json)\n 3. ./funnel.json in the current directory + --channel selects (or first wins)\n 4. --channel <name> with no funnel.json → raw launch using an existing settings.json channel\n 5. the default profile (first entry in fnl profiles)\n\nfunnel-specific options (everything else passes through to claude verbatim):\n -p, --profile profile name to launch\n --channel channel name (selects from funnel.json, or raw-launches if no funnel.json)\n -h, --help show this help\n\nPositional args, unknown short flags (e.g. -c, -r), and claude's own flags\n(--agent, --resume, --model, --print, --output-format ...) are all forwarded.\nOn launch the FUNNEL_CHANNEL_ID env var is set and MCP connects to the gateway.";
|
|
1617
1652
|
outputFormat: "text";
|
|
1618
1653
|
status: _$hono_utils_http_status0.ContentfulStatusCode;
|
|
1619
1654
|
};
|
|
@@ -2904,7 +2939,7 @@ declare const app: _$hono_hono_base0.HonoBase<Env, {
|
|
|
2904
2939
|
channel?: string | undefined;
|
|
2905
2940
|
};
|
|
2906
2941
|
};
|
|
2907
|
-
output: "funnel claude — launch Claude Code\n\nusage:\n funnel claude launch
|
|
2942
|
+
output: "funnel claude — launch Claude Code\n\nusage:\n funnel claude launch the first channel from funnel.json, or the default profile\n funnel claude --channel <name> with funnel.json: select that channel; without: raw launch\n funnel claude -p <name> launch a named profile\n funnel claude --profile <name> (long form)\n funnel claude [...] any other argument is forwarded to the claude CLI\n\nresolution order:\n 1. --help print this help\n 2. --profile <name> named profile (ignores funnel.json)\n 3. ./funnel.json in the current directory + --channel selects (or first wins)\n 4. --channel <name> with no funnel.json → raw launch using an existing settings.json channel\n 5. the default profile (first entry in fnl profiles)\n\nfunnel-specific options (everything else passes through to claude verbatim):\n -p, --profile profile name to launch\n --channel channel name (selects from funnel.json, or raw-launches if no funnel.json)\n -h, --help show this help\n\nPositional args, unknown short flags (e.g. -c, -r), and claude's own flags\n(--agent, --resume, --model, --print, --output-format ...) are all forwarded.\nOn launch the FUNNEL_CHANNEL_ID env var is set and MCP connects to the gateway.";
|
|
2908
2943
|
outputFormat: "text";
|
|
2909
2944
|
status: _$hono_utils_http_status0.ContentfulStatusCode;
|
|
2910
2945
|
};
|
|
@@ -4177,4 +4212,4 @@ ${string}`;
|
|
|
4177
4212
|
//#region lib/tui/tui.d.ts
|
|
4178
4213
|
declare function launchTui(funnel: Funnel): Promise<void>;
|
|
4179
4214
|
//#endregion
|
|
4180
|
-
export { AttachOptions, BroadcastEvent, BroadcastSubscriber, ChannelConfig, ChannelConnectorView, ChannelDeliveryMode, ChannelServerOptions, ConnectorConfig, ConnectorSpec, 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, FunnelDotenvReader, FunnelEvent, FunnelEventStore, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, GhConnectorConfig, LOCAL_CONFIG_FILENAME, LOCAL_ENV_FILENAME, LaunchOptions, ListListenersResult, ListenerEntry, ListenerOpResult, LocalConfig, LogEntry, MemoryFunnelClock, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MemoryProcessCall, MemoryProcessHandler, MemoryProcessResponse, MemoryProcessSyncHandler, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, NotifyFn, ProfileConfig, PublishRequest, PublishResponse, PublishResult, ReplayableEvent, RunOptions, RunResult, SETTINGS_PATH, SETTINGS_VERSION, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleEntry, ScheduleListenerOptions, Settings, SlackConnectorConfig, SlackListenerOptions, SlackProcessed, SlackProcessedEmit, SlackProcessedSkip, SlackRawEvent, channelConfigSchema, channelDeliveryModeSchema, app as cliApp, connectorConfigSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, launchTui, localConfigSchema, profileConfigSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
|
|
4215
|
+
export { AttachOptions, BroadcastEvent, BroadcastSubscriber, ChannelConfig, ChannelConnectorView, ChannelDeliveryMode, ChannelServerOptions, ChannelSpec, ConnectorConfig, ConnectorSpec, 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, FunnelDotenvReader, FunnelEvent, FunnelEventStore, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, GhConnectorConfig, LOCAL_CONFIG_FILENAME, LOCAL_ENV_FILENAME, LaunchOptions, ListListenersResult, ListenerEntry, ListenerOpResult, LocalConfig, LogEntry, MemoryFunnelClock, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MemoryProcessCall, MemoryProcessHandler, MemoryProcessResponse, MemoryProcessSyncHandler, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, NotifyFn, ProfileConfig, PublishRequest, PublishResponse, PublishResult, ReplayableEvent, RunOptions, RunResult, SETTINGS_PATH, SETTINGS_VERSION, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleEntry, ScheduleListenerOptions, Settings, SlackConnectorConfig, SlackListenerOptions, SlackProcessed, SlackProcessedEmit, SlackProcessedSkip, SlackRawEvent, channelConfigSchema, channelDeliveryModeSchema, channelSpecSchema, app as cliApp, connectorConfigSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, launchTui, localConfigSchema, profileConfigSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
|
package/dist/index.js
CHANGED
|
@@ -737,20 +737,16 @@ var MemoryFunnelIdGenerator = class extends FunnelIdGenerator {
|
|
|
737
737
|
/**
|
|
738
738
|
* Per-repo launch config (`funnel.json`).
|
|
739
739
|
*
|
|
740
|
-
* `fnl claude` reads this when no --profile
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
* `~/.funnel/settings.json` on launch
|
|
740
|
+
* `fnl claude` reads this when no --profile is given and picks one of the
|
|
741
|
+
* declared channels (`--channel <name>` selects by name; otherwise the first
|
|
742
|
+
* entry wins). The chosen channel is materialized into
|
|
743
|
+
* `~/.funnel/settings.json` on launch — token fields in connectors resolve
|
|
744
|
+
* via literal / `env.<field>` / TTY prompt.
|
|
744
745
|
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
* resolved from process.env first, then ./.env.local
|
|
750
|
-
* 3. Field omitted everywhere → prompted for once on a TTY and persisted to
|
|
751
|
-
* `~/.funnel/settings.json`; non-TTY launches fail fast.
|
|
752
|
-
*
|
|
753
|
-
* `funnel.json` itself is never written to. Only `channel` is required.
|
|
746
|
+
* Top-level `options` and `env` are defaults shared by every channel: each
|
|
747
|
+
* channel's own `options` is appended after the shared ones (CLI semantics
|
|
748
|
+
* keep the later flag winning), and `env` is a shallow merge with the
|
|
749
|
+
* channel's keys overriding the shared ones.
|
|
754
750
|
*/
|
|
755
751
|
const slackEnvSchema = z.object({
|
|
756
752
|
botToken: z.string().optional(),
|
|
@@ -785,13 +781,20 @@ const connectorSpecSchema = z.discriminatedUnion("type", [
|
|
|
785
781
|
ghConnectorSpecSchema,
|
|
786
782
|
scheduleConnectorSpecSchema
|
|
787
783
|
]);
|
|
784
|
+
const channelSpecSchema = z.object({
|
|
785
|
+
name: z.string(),
|
|
786
|
+
options: z.array(z.string()).optional(),
|
|
787
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
788
|
+
connectors: z.array(connectorSpecSchema).optional()
|
|
789
|
+
});
|
|
788
790
|
const localConfigSchema = z.object({
|
|
789
791
|
$schema: z.string().optional(),
|
|
790
|
-
channel
|
|
791
|
-
/** Extra args forwarded to the claude CLI. Prepended before user-supplied CLI args so user args still win on collision (e.g. --model, --agent, --brief, --resume, positional session ids). */
|
|
792
|
+
/** Extra args forwarded to every channel's launch before the channel's own options. User-supplied CLI args still come last. */
|
|
792
793
|
options: z.array(z.string()).optional(),
|
|
794
|
+
/** Environment variables shared by every channel. Each channel's env merges on top; process.env wins overall. */
|
|
793
795
|
env: z.record(z.string(), z.string()).optional(),
|
|
794
|
-
|
|
796
|
+
/** Declared channels. First entry is the default; --channel <name> selects by name. */
|
|
797
|
+
channels: z.array(channelSpecSchema).min(1)
|
|
795
798
|
});
|
|
796
799
|
const LOCAL_CONFIG_FILENAME = "funnel.json";
|
|
797
800
|
const LOCAL_ENV_FILENAME = ".env.local";
|
|
@@ -879,8 +882,8 @@ var FunnelTokenPrompter = class {};
|
|
|
879
882
|
//#endregion
|
|
880
883
|
//#region lib/engine/local-config/local-config-sync.ts
|
|
881
884
|
/**
|
|
882
|
-
* Reconciles a
|
|
883
|
-
* is the source of truth for the channel it declares:
|
|
885
|
+
* Reconciles a single funnel.json channel spec with `~/.funnel/settings.json`.
|
|
886
|
+
* The spec is the source of truth for the channel it declares:
|
|
884
887
|
*
|
|
885
888
|
* - missing channel → created
|
|
886
889
|
* - declared connector matched by name → tokens reconciled
|
|
@@ -889,9 +892,10 @@ var FunnelTokenPrompter = class {};
|
|
|
889
892
|
* - declared connector with no match → added
|
|
890
893
|
* - any connector left in the channel that the spec did not touch → removed
|
|
891
894
|
*
|
|
892
|
-
* Removal only fires when
|
|
893
|
-
* field means "do not manage connectors from here" and leaves
|
|
894
|
-
* `~/.funnel` alone.
|
|
895
|
+
* Removal only fires when the channel spec has a `connectors` field. An
|
|
896
|
+
* absent field means "do not manage connectors from here" and leaves
|
|
897
|
+
* everything in `~/.funnel` alone. Other channels in funnel.json (not
|
|
898
|
+
* passed to this call) are untouched.
|
|
895
899
|
*/
|
|
896
900
|
var FunnelLocalConfigSync = class {
|
|
897
901
|
channels;
|
|
@@ -905,16 +909,16 @@ var FunnelLocalConfigSync = class {
|
|
|
905
909
|
this.env = deps.env ?? process.env;
|
|
906
910
|
Object.freeze(this);
|
|
907
911
|
}
|
|
908
|
-
async ensure(
|
|
909
|
-
if (!this.channels.get(
|
|
910
|
-
if (
|
|
912
|
+
async ensure(channel, cwd) {
|
|
913
|
+
if (!this.channels.get(channel.name)) this.channels.add({ name: channel.name });
|
|
914
|
+
if (channel.connectors === void 0) return;
|
|
911
915
|
const dotenv = this.dotenv.read(cwd);
|
|
912
916
|
const touched = /* @__PURE__ */ new Set();
|
|
913
|
-
for (const spec of
|
|
914
|
-
const id = await this.ensureConnector(
|
|
917
|
+
for (const spec of channel.connectors) {
|
|
918
|
+
const id = await this.ensureConnector(channel.name, spec, dotenv);
|
|
915
919
|
touched.add(id);
|
|
916
920
|
}
|
|
917
|
-
this.removeExtras(
|
|
921
|
+
this.removeExtras(channel.name, touched);
|
|
918
922
|
}
|
|
919
923
|
async ensureConnector(channelName, spec, dotenv) {
|
|
920
924
|
if (spec.type === "slack") return await this.ensureSlack(channelName, spec, dotenv);
|
|
@@ -4075,19 +4079,22 @@ examples:
|
|
|
4075
4079
|
const claudeHelp = `funnel claude — launch Claude Code
|
|
4076
4080
|
|
|
4077
4081
|
usage:
|
|
4078
|
-
funnel claude launch
|
|
4082
|
+
funnel claude launch the first channel from funnel.json, or the default profile
|
|
4083
|
+
funnel claude --channel <name> with funnel.json: select that channel; without: raw launch
|
|
4079
4084
|
funnel claude -p <name> launch a named profile
|
|
4080
4085
|
funnel claude --profile <name> (long form)
|
|
4081
|
-
funnel claude --channel <name> raw launch (no profile, cwd = current dir)
|
|
4082
4086
|
funnel claude [...] any other argument is forwarded to the claude CLI
|
|
4083
4087
|
|
|
4084
|
-
resolution order
|
|
4085
|
-
1.
|
|
4086
|
-
2.
|
|
4088
|
+
resolution order:
|
|
4089
|
+
1. --help print this help
|
|
4090
|
+
2. --profile <name> named profile (ignores funnel.json)
|
|
4091
|
+
3. ./funnel.json in the current directory + --channel selects (or first wins)
|
|
4092
|
+
4. --channel <name> with no funnel.json → raw launch using an existing settings.json channel
|
|
4093
|
+
5. the default profile (first entry in fnl profiles)
|
|
4087
4094
|
|
|
4088
4095
|
funnel-specific options (everything else passes through to claude verbatim):
|
|
4089
4096
|
-p, --profile profile name to launch
|
|
4090
|
-
--channel channel name (
|
|
4097
|
+
--channel channel name (selects from funnel.json, or raw-launches if no funnel.json)
|
|
4091
4098
|
-h, --help show this help
|
|
4092
4099
|
|
|
4093
4100
|
Positional args, unknown short flags (e.g. -c, -r), and claude's own flags
|
|
@@ -4124,12 +4131,21 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4124
4131
|
const cwd = process.cwd();
|
|
4125
4132
|
const local = funnel.localConfig.read(cwd);
|
|
4126
4133
|
if (local) {
|
|
4127
|
-
|
|
4134
|
+
const picked = query.channel !== void 0 ? local.channels.find((c) => c.name === query.channel) : local.channels[0];
|
|
4135
|
+
if (!picked) throw new HTTPException(404, { message: query.channel ? `channel "${query.channel}" is not declared in funnel.json` : `funnel.json declares no channels` });
|
|
4136
|
+
await funnel.localConfigSync.ensure(picked, cwd);
|
|
4128
4137
|
const exitCode = await funnel.claude.launch({
|
|
4129
|
-
channel:
|
|
4138
|
+
channel: picked.name,
|
|
4130
4139
|
cwd,
|
|
4131
|
-
userArgs: [
|
|
4132
|
-
|
|
4140
|
+
userArgs: [
|
|
4141
|
+
...local.options ?? [],
|
|
4142
|
+
...picked.options ?? [],
|
|
4143
|
+
...userArgs
|
|
4144
|
+
],
|
|
4145
|
+
extraEnv: {
|
|
4146
|
+
...local.env ?? {},
|
|
4147
|
+
...picked.env ?? {}
|
|
4148
|
+
}
|
|
4133
4149
|
});
|
|
4134
4150
|
process.exit(exitCode);
|
|
4135
4151
|
}
|
|
@@ -6831,4 +6847,4 @@ async function launchTui(funnel) {
|
|
|
6831
6847
|
});
|
|
6832
6848
|
}
|
|
6833
6849
|
//#endregion
|
|
6834
|
-
export { DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClaude, FunnelClock, FunnelConnectorFactory, FunnelConnectorListener, FunnelDotenvReader, FunnelEventStore, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, LOCAL_ENV_FILENAME, MemoryFunnelClock, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, SETTINGS_PATH, SETTINGS_VERSION, channelConfigSchema, channelDeliveryModeSchema, app as cliApp, connectorConfigSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, launchTui, localConfigSchema, profileConfigSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
|
|
6850
|
+
export { DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClaude, FunnelClock, FunnelConnectorFactory, FunnelConnectorListener, FunnelDotenvReader, FunnelEventStore, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelIdGenerator, FunnelListenerSupervisor, FunnelListenersClient, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLogger, FunnelMcp, FunnelProcessRunner, FunnelProfiles, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, LOCAL_ENV_FILENAME, MemoryFunnelClock, MemoryFunnelFileSystem, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, SETTINGS_PATH, SETTINGS_VERSION, channelConfigSchema, channelDeliveryModeSchema, channelSpecSchema, app as cliApp, connectorConfigSchema, connectorSpecSchema, createCliApp, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, launchTui, localConfigSchema, profileConfigSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"$schema": {
|
|
6
|
+
"type": "string"
|
|
7
|
+
},
|
|
8
|
+
"options": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"env": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"propertyNames": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
"additionalProperties": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"channels": {
|
|
24
|
+
"minItems": 1,
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"name": {
|
|
30
|
+
"type": "string"
|
|
31
|
+
},
|
|
32
|
+
"options": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": {
|
|
35
|
+
"type": "string"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"env": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"propertyNames": {
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
"additionalProperties": {
|
|
44
|
+
"type": "string"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"connectors": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"items": {
|
|
50
|
+
"oneOf": [
|
|
51
|
+
{
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"type": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"const": "slack"
|
|
57
|
+
},
|
|
58
|
+
"name": {
|
|
59
|
+
"type": "string"
|
|
60
|
+
},
|
|
61
|
+
"botToken": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
},
|
|
64
|
+
"appToken": {
|
|
65
|
+
"type": "string"
|
|
66
|
+
},
|
|
67
|
+
"env": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"properties": {
|
|
70
|
+
"botToken": {
|
|
71
|
+
"type": "string"
|
|
72
|
+
},
|
|
73
|
+
"appToken": {
|
|
74
|
+
"type": "string"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"additionalProperties": false
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": [
|
|
81
|
+
"type",
|
|
82
|
+
"name"
|
|
83
|
+
],
|
|
84
|
+
"additionalProperties": false
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "object",
|
|
88
|
+
"properties": {
|
|
89
|
+
"type": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"const": "discord"
|
|
92
|
+
},
|
|
93
|
+
"name": {
|
|
94
|
+
"type": "string"
|
|
95
|
+
},
|
|
96
|
+
"botToken": {
|
|
97
|
+
"type": "string"
|
|
98
|
+
},
|
|
99
|
+
"env": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"properties": {
|
|
102
|
+
"botToken": {
|
|
103
|
+
"type": "string"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"additionalProperties": false
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"required": [
|
|
110
|
+
"type",
|
|
111
|
+
"name"
|
|
112
|
+
],
|
|
113
|
+
"additionalProperties": false
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"type": "object",
|
|
117
|
+
"properties": {
|
|
118
|
+
"type": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"const": "gh"
|
|
121
|
+
},
|
|
122
|
+
"name": {
|
|
123
|
+
"type": "string"
|
|
124
|
+
},
|
|
125
|
+
"pollInterval": {
|
|
126
|
+
"type": "integer",
|
|
127
|
+
"exclusiveMinimum": 0,
|
|
128
|
+
"maximum": 9007199254740991
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"required": [
|
|
132
|
+
"type",
|
|
133
|
+
"name"
|
|
134
|
+
],
|
|
135
|
+
"additionalProperties": false
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"type": "object",
|
|
139
|
+
"properties": {
|
|
140
|
+
"type": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"const": "schedule"
|
|
143
|
+
},
|
|
144
|
+
"name": {
|
|
145
|
+
"type": "string"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"required": [
|
|
149
|
+
"type",
|
|
150
|
+
"name"
|
|
151
|
+
],
|
|
152
|
+
"additionalProperties": false
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": [
|
|
159
|
+
"name"
|
|
160
|
+
],
|
|
161
|
+
"additionalProperties": false
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"required": [
|
|
166
|
+
"channels"
|
|
167
|
+
],
|
|
168
|
+
"additionalProperties": false,
|
|
169
|
+
"title": "Funnel per-repo launch config",
|
|
170
|
+
"description": "Used by `fnl claude` when no --profile / --channel is given. Declares the channel to subscribe to, optional sub-agent and brief flag, environment variables to layer under process.env, and optional connectors to materialize into ~/.funnel/settings.json on launch."
|
|
171
|
+
}
|
|
172
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interactive-inc/claude-funnel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Hub CLI that routes external events (Slack / GitHub / Discord) to Claude Code agents through subscription channels over MCP.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"dist/**/*",
|
|
31
|
-
"
|
|
31
|
+
"funnel.schema.json",
|
|
32
32
|
"README.md",
|
|
33
33
|
"LICENSE"
|
|
34
34
|
],
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"type": "object",
|
|
4
|
-
"properties": {
|
|
5
|
-
"$schema": {
|
|
6
|
-
"type": "string"
|
|
7
|
-
},
|
|
8
|
-
"channel": {
|
|
9
|
-
"type": "string"
|
|
10
|
-
},
|
|
11
|
-
"options": {
|
|
12
|
-
"type": "array",
|
|
13
|
-
"items": {
|
|
14
|
-
"type": "string"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"env": {
|
|
18
|
-
"type": "object",
|
|
19
|
-
"propertyNames": {
|
|
20
|
-
"type": "string"
|
|
21
|
-
},
|
|
22
|
-
"additionalProperties": {
|
|
23
|
-
"type": "string"
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"connectors": {
|
|
27
|
-
"type": "array",
|
|
28
|
-
"items": {
|
|
29
|
-
"oneOf": [
|
|
30
|
-
{
|
|
31
|
-
"type": "object",
|
|
32
|
-
"properties": {
|
|
33
|
-
"type": {
|
|
34
|
-
"type": "string",
|
|
35
|
-
"const": "slack"
|
|
36
|
-
},
|
|
37
|
-
"name": {
|
|
38
|
-
"type": "string"
|
|
39
|
-
},
|
|
40
|
-
"botToken": {
|
|
41
|
-
"type": "string"
|
|
42
|
-
},
|
|
43
|
-
"appToken": {
|
|
44
|
-
"type": "string"
|
|
45
|
-
},
|
|
46
|
-
"env": {
|
|
47
|
-
"type": "object",
|
|
48
|
-
"properties": {
|
|
49
|
-
"botToken": {
|
|
50
|
-
"type": "string"
|
|
51
|
-
},
|
|
52
|
-
"appToken": {
|
|
53
|
-
"type": "string"
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"additionalProperties": false
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
"required": [
|
|
60
|
-
"type",
|
|
61
|
-
"name"
|
|
62
|
-
],
|
|
63
|
-
"additionalProperties": false
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"type": "object",
|
|
67
|
-
"properties": {
|
|
68
|
-
"type": {
|
|
69
|
-
"type": "string",
|
|
70
|
-
"const": "discord"
|
|
71
|
-
},
|
|
72
|
-
"name": {
|
|
73
|
-
"type": "string"
|
|
74
|
-
},
|
|
75
|
-
"botToken": {
|
|
76
|
-
"type": "string"
|
|
77
|
-
},
|
|
78
|
-
"env": {
|
|
79
|
-
"type": "object",
|
|
80
|
-
"properties": {
|
|
81
|
-
"botToken": {
|
|
82
|
-
"type": "string"
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
"additionalProperties": false
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
"required": [
|
|
89
|
-
"type",
|
|
90
|
-
"name"
|
|
91
|
-
],
|
|
92
|
-
"additionalProperties": false
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"type": "object",
|
|
96
|
-
"properties": {
|
|
97
|
-
"type": {
|
|
98
|
-
"type": "string",
|
|
99
|
-
"const": "gh"
|
|
100
|
-
},
|
|
101
|
-
"name": {
|
|
102
|
-
"type": "string"
|
|
103
|
-
},
|
|
104
|
-
"pollInterval": {
|
|
105
|
-
"type": "integer",
|
|
106
|
-
"exclusiveMinimum": 0,
|
|
107
|
-
"maximum": 9007199254740991
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
"required": [
|
|
111
|
-
"type",
|
|
112
|
-
"name"
|
|
113
|
-
],
|
|
114
|
-
"additionalProperties": false
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"type": "object",
|
|
118
|
-
"properties": {
|
|
119
|
-
"type": {
|
|
120
|
-
"type": "string",
|
|
121
|
-
"const": "schedule"
|
|
122
|
-
},
|
|
123
|
-
"name": {
|
|
124
|
-
"type": "string"
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
"required": [
|
|
128
|
-
"type",
|
|
129
|
-
"name"
|
|
130
|
-
],
|
|
131
|
-
"additionalProperties": false
|
|
132
|
-
}
|
|
133
|
-
]
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
"required": [
|
|
138
|
-
"channel"
|
|
139
|
-
],
|
|
140
|
-
"additionalProperties": false,
|
|
141
|
-
"title": "Funnel per-repo launch config",
|
|
142
|
-
"description": "Used by `fnl claude` when no --profile / --channel is given. Declares the channel to subscribe to, optional sub-agent and brief flag, environment variables to layer under process.env, and optional connectors to materialize into ~/.funnel/settings.json on launch."
|
|
143
|
-
}
|
|
144
|
-
|