@interactive-inc/claude-funnel 0.16.1 → 0.17.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/README.md +22 -24
- package/dist/bin.js +293 -287
- package/dist/gateway/daemon.js +112 -112
- package/dist/index.d.ts +18 -39
- package/dist/index.js +76 -74
- package/funnel.schema.json +0 -15
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -150,6 +150,8 @@ declare const channelConfigSchema: z.ZodObject<{
|
|
|
150
150
|
fanout: "fanout";
|
|
151
151
|
exclusive: "exclusive";
|
|
152
152
|
}>>;
|
|
153
|
+
options: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
154
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
153
155
|
connectors: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
154
156
|
id: z.ZodString;
|
|
155
157
|
name: z.ZodString;
|
|
@@ -195,9 +197,7 @@ type ChannelConfig = z.infer<typeof channelConfigSchema>;
|
|
|
195
197
|
declare const profileConfigSchema: z.ZodObject<{
|
|
196
198
|
name: z.ZodString;
|
|
197
199
|
path: z.ZodString;
|
|
198
|
-
subAgent: z.ZodString;
|
|
199
200
|
channelId: z.ZodString;
|
|
200
|
-
brief: z.ZodOptional<z.ZodBoolean>;
|
|
201
201
|
}, z.core.$strip>;
|
|
202
202
|
type ProfileConfig = z.infer<typeof profileConfigSchema>;
|
|
203
203
|
declare const SETTINGS_VERSION = 1;
|
|
@@ -210,6 +210,8 @@ declare const settingsSchema: z.ZodObject<{
|
|
|
210
210
|
fanout: "fanout";
|
|
211
211
|
exclusive: "exclusive";
|
|
212
212
|
}>>;
|
|
213
|
+
options: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
214
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
213
215
|
connectors: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
214
216
|
id: z.ZodString;
|
|
215
217
|
name: z.ZodString;
|
|
@@ -254,9 +256,7 @@ declare const settingsSchema: z.ZodObject<{
|
|
|
254
256
|
profiles: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
255
257
|
name: z.ZodString;
|
|
256
258
|
path: z.ZodString;
|
|
257
|
-
subAgent: z.ZodString;
|
|
258
259
|
channelId: z.ZodString;
|
|
259
|
-
brief: z.ZodOptional<z.ZodBoolean>;
|
|
260
260
|
}, z.core.$strip>>>;
|
|
261
261
|
}, z.core.$strip>;
|
|
262
262
|
type Settings = z.infer<typeof settingsSchema>;
|
|
@@ -318,8 +318,12 @@ declare class FunnelChannels {
|
|
|
318
318
|
add(input: {
|
|
319
319
|
name: string;
|
|
320
320
|
delivery?: ChannelDeliveryMode;
|
|
321
|
+
options?: string[];
|
|
322
|
+
env?: Record<string, string>;
|
|
321
323
|
}): ChannelConfig;
|
|
322
324
|
setDelivery(name: string, delivery: ChannelDeliveryMode): void;
|
|
325
|
+
setOptions(name: string, options: string[]): void;
|
|
326
|
+
setEnv(name: string, env: Record<string, string>): void;
|
|
323
327
|
remove(name: string): void;
|
|
324
328
|
rename(oldName: string, newName: string): void;
|
|
325
329
|
listConnectors(channelName: string): ConnectorConfig[];
|
|
@@ -392,11 +396,8 @@ declare class FunnelMcp {
|
|
|
392
396
|
type LaunchOptions = {
|
|
393
397
|
channel: string;
|
|
394
398
|
cwd?: string;
|
|
395
|
-
subAgent?: string;
|
|
396
399
|
userArgs?: string[];
|
|
397
|
-
profileName?: string;
|
|
398
|
-
brief?: boolean; /** Extra env vars merged under process.env (process.env wins on collision). */
|
|
399
|
-
extraEnv?: Record<string, string>;
|
|
400
|
+
profileName?: string;
|
|
400
401
|
};
|
|
401
402
|
type Deps$12 = {
|
|
402
403
|
channels: FunnelChannels;
|
|
@@ -508,8 +509,6 @@ declare const channelSpecSchema: z.ZodObject<{
|
|
|
508
509
|
type ChannelSpec = z.infer<typeof channelSpecSchema>;
|
|
509
510
|
declare const localConfigSchema: z.ZodObject<{
|
|
510
511
|
$schema: z.ZodOptional<z.ZodString>;
|
|
511
|
-
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
512
|
-
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
513
512
|
channels: z.ZodArray<z.ZodObject<{
|
|
514
513
|
name: z.ZodString;
|
|
515
514
|
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -2410,10 +2409,8 @@ declare const createCliApp: (funnel: Funnel) => _$hono_hono_base0.HonoBase<Env,
|
|
|
2410
2409
|
};
|
|
2411
2410
|
} & {
|
|
2412
2411
|
query: {
|
|
2413
|
-
path: string
|
|
2414
|
-
|
|
2415
|
-
channel: string | string[];
|
|
2416
|
-
brief?: string | string[] | undefined;
|
|
2412
|
+
path: string;
|
|
2413
|
+
channel: string;
|
|
2417
2414
|
};
|
|
2418
2415
|
};
|
|
2419
2416
|
output: string;
|
|
@@ -2426,10 +2423,8 @@ declare const createCliApp: (funnel: Funnel) => _$hono_hono_base0.HonoBase<Env,
|
|
|
2426
2423
|
};
|
|
2427
2424
|
} & {
|
|
2428
2425
|
query: {
|
|
2429
|
-
path: string
|
|
2430
|
-
|
|
2431
|
-
channel: string | string[];
|
|
2432
|
-
brief?: string | string[] | undefined;
|
|
2426
|
+
path: string;
|
|
2427
|
+
channel: string;
|
|
2433
2428
|
};
|
|
2434
2429
|
};
|
|
2435
2430
|
output: `added profile "${string}"`;
|
|
@@ -2456,10 +2451,7 @@ declare const createCliApp: (funnel: Funnel) => _$hono_hono_base0.HonoBase<Env,
|
|
|
2456
2451
|
} & {
|
|
2457
2452
|
query: {
|
|
2458
2453
|
path?: string | undefined;
|
|
2459
|
-
"sub-agent"?: string | undefined;
|
|
2460
2454
|
channel?: string | undefined;
|
|
2461
|
-
brief?: string | string[] | undefined;
|
|
2462
|
-
"no-brief"?: string | string[] | undefined;
|
|
2463
2455
|
};
|
|
2464
2456
|
};
|
|
2465
2457
|
output: string;
|
|
@@ -2473,10 +2465,7 @@ declare const createCliApp: (funnel: Funnel) => _$hono_hono_base0.HonoBase<Env,
|
|
|
2473
2465
|
} & {
|
|
2474
2466
|
query: {
|
|
2475
2467
|
path?: string | undefined;
|
|
2476
|
-
"sub-agent"?: string | undefined;
|
|
2477
2468
|
channel?: string | undefined;
|
|
2478
|
-
brief?: string | string[] | undefined;
|
|
2479
|
-
"no-brief"?: string | string[] | undefined;
|
|
2480
2469
|
};
|
|
2481
2470
|
};
|
|
2482
2471
|
output: `updated profile "${string}"`;
|
|
@@ -3701,10 +3690,8 @@ declare const app: _$hono_hono_base0.HonoBase<Env, {
|
|
|
3701
3690
|
};
|
|
3702
3691
|
} & {
|
|
3703
3692
|
query: {
|
|
3704
|
-
path: string
|
|
3705
|
-
|
|
3706
|
-
channel: string | string[];
|
|
3707
|
-
brief?: string | string[] | undefined;
|
|
3693
|
+
path: string;
|
|
3694
|
+
channel: string;
|
|
3708
3695
|
};
|
|
3709
3696
|
};
|
|
3710
3697
|
output: string;
|
|
@@ -3717,10 +3704,8 @@ declare const app: _$hono_hono_base0.HonoBase<Env, {
|
|
|
3717
3704
|
};
|
|
3718
3705
|
} & {
|
|
3719
3706
|
query: {
|
|
3720
|
-
path: string
|
|
3721
|
-
|
|
3722
|
-
channel: string | string[];
|
|
3723
|
-
brief?: string | string[] | undefined;
|
|
3707
|
+
path: string;
|
|
3708
|
+
channel: string;
|
|
3724
3709
|
};
|
|
3725
3710
|
};
|
|
3726
3711
|
output: `added profile "${string}"`;
|
|
@@ -3747,10 +3732,7 @@ declare const app: _$hono_hono_base0.HonoBase<Env, {
|
|
|
3747
3732
|
} & {
|
|
3748
3733
|
query: {
|
|
3749
3734
|
path?: string | undefined;
|
|
3750
|
-
"sub-agent"?: string | undefined;
|
|
3751
3735
|
channel?: string | undefined;
|
|
3752
|
-
brief?: string | string[] | undefined;
|
|
3753
|
-
"no-brief"?: string | string[] | undefined;
|
|
3754
3736
|
};
|
|
3755
3737
|
};
|
|
3756
3738
|
output: string;
|
|
@@ -3764,10 +3746,7 @@ declare const app: _$hono_hono_base0.HonoBase<Env, {
|
|
|
3764
3746
|
} & {
|
|
3765
3747
|
query: {
|
|
3766
3748
|
path?: string | undefined;
|
|
3767
|
-
"sub-agent"?: string | undefined;
|
|
3768
3749
|
channel?: string | undefined;
|
|
3769
|
-
brief?: string | string[] | undefined;
|
|
3770
|
-
"no-brief"?: string | string[] | undefined;
|
|
3771
3750
|
};
|
|
3772
3751
|
};
|
|
3773
3752
|
output: `updated profile "${string}"`;
|
|
@@ -4212,4 +4191,4 @@ ${string}`;
|
|
|
4212
4191
|
//#region lib/tui/tui.d.ts
|
|
4213
4192
|
declare function launchTui(funnel: Funnel): Promise<void>;
|
|
4214
4193
|
//#endregion
|
|
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 };
|
|
4194
|
+
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, type GatewayEmitInput, type GatewayRouteDeps, type Env$1 as GatewayServerEnv, 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
|
@@ -50,15 +50,16 @@ const channelConfigSchema = z.object({
|
|
|
50
50
|
id: z.string(),
|
|
51
51
|
name: z.string(),
|
|
52
52
|
delivery: channelDeliveryModeSchema.default("fanout"),
|
|
53
|
+
/** Args prepended to the claude argv on every launch bound to this channel. */
|
|
54
|
+
options: z.array(z.string()).default([]),
|
|
55
|
+
/** Env vars layered under the launched claude process. process.env wins on collision. */
|
|
56
|
+
env: z.record(z.string(), z.string()).default({}),
|
|
53
57
|
connectors: z.array(connectorConfigSchema).default([])
|
|
54
58
|
});
|
|
55
59
|
const profileConfigSchema = z.object({
|
|
56
60
|
name: z.string(),
|
|
57
61
|
path: z.string(),
|
|
58
|
-
|
|
59
|
-
channelId: z.string(),
|
|
60
|
-
/** Forwards `--brief` to claude on launch (enables the SendUserMessage tool). */
|
|
61
|
-
brief: z.boolean().optional()
|
|
62
|
+
channelId: z.string()
|
|
62
63
|
});
|
|
63
64
|
const SETTINGS_VERSION = 1;
|
|
64
65
|
const settingsSchema = z.object({
|
|
@@ -305,6 +306,8 @@ var FunnelChannels = class {
|
|
|
305
306
|
id: this.idGenerator.generate(),
|
|
306
307
|
name: input.name,
|
|
307
308
|
delivery: input.delivery ?? "fanout",
|
|
309
|
+
options: input.options ?? [],
|
|
310
|
+
env: input.env ?? {},
|
|
308
311
|
connectors: []
|
|
309
312
|
};
|
|
310
313
|
settings.channels.push(channel);
|
|
@@ -317,6 +320,18 @@ var FunnelChannels = class {
|
|
|
317
320
|
channel.delivery = delivery;
|
|
318
321
|
this.store.write(settings);
|
|
319
322
|
}
|
|
323
|
+
setOptions(name, options) {
|
|
324
|
+
const settings = this.store.read();
|
|
325
|
+
const channel = this.requireChannel(settings, name);
|
|
326
|
+
channel.options = options;
|
|
327
|
+
this.store.write(settings);
|
|
328
|
+
}
|
|
329
|
+
setEnv(name, env) {
|
|
330
|
+
const settings = this.store.read();
|
|
331
|
+
const channel = this.requireChannel(settings, name);
|
|
332
|
+
channel.env = env;
|
|
333
|
+
this.store.write(settings);
|
|
334
|
+
}
|
|
320
335
|
remove(name) {
|
|
321
336
|
const settings = this.store.read();
|
|
322
337
|
const index = settings.channels.findIndex((c) => c.name === name);
|
|
@@ -567,12 +582,11 @@ var FunnelClaude = class {
|
|
|
567
582
|
this.writePidFile(options.profileName);
|
|
568
583
|
this.installCleanup(options.profileName);
|
|
569
584
|
}
|
|
570
|
-
const claudeArgs = this.buildArgs(options, cwd);
|
|
571
|
-
const env = this.buildEnv(channel.id,
|
|
585
|
+
const claudeArgs = this.buildArgs(channel.options, options.userArgs ?? [], cwd);
|
|
586
|
+
const env = this.buildEnv(channel.id, channel.env);
|
|
572
587
|
this.logger.info(`claude launch`, {
|
|
573
588
|
channel: options.channel,
|
|
574
589
|
channelId: channel.id,
|
|
575
|
-
subAgent: options.subAgent,
|
|
576
590
|
cwd
|
|
577
591
|
});
|
|
578
592
|
try {
|
|
@@ -628,17 +642,15 @@ var FunnelClaude = class {
|
|
|
628
642
|
if (!state) return false;
|
|
629
643
|
return !state.startsWith("Z");
|
|
630
644
|
}
|
|
631
|
-
buildArgs(
|
|
632
|
-
const result = [...
|
|
645
|
+
buildArgs(channelOptions, userArgs, cwd) {
|
|
646
|
+
const result = [...channelOptions, ...userArgs];
|
|
633
647
|
const mcpName = this.mcp.findInstalledName(cwd);
|
|
634
648
|
if (mcpName && !result.includes("--dangerously-load-development-channels") && !result.includes("--channels")) result.push("--dangerously-load-development-channels", `server:${mcpName}`);
|
|
635
|
-
if (!result.includes("--agent") && options.subAgent) result.push("--agent", options.subAgent);
|
|
636
|
-
if (options.brief && !result.includes("--brief")) result.push("--brief");
|
|
637
649
|
return result;
|
|
638
650
|
}
|
|
639
|
-
buildEnv(channelId,
|
|
651
|
+
buildEnv(channelId, channelEnv) {
|
|
640
652
|
const env = {};
|
|
641
|
-
|
|
653
|
+
for (const [key, value] of Object.entries(channelEnv)) env[key] = value;
|
|
642
654
|
for (const [key, value] of Object.entries(globalThis.process.env)) if (typeof value === "string") env[key] = value;
|
|
643
655
|
env.FUNNEL_CHANNEL_ID = channelId;
|
|
644
656
|
return env;
|
|
@@ -783,16 +795,14 @@ const connectorSpecSchema = z.discriminatedUnion("type", [
|
|
|
783
795
|
]);
|
|
784
796
|
const channelSpecSchema = z.object({
|
|
785
797
|
name: z.string(),
|
|
798
|
+
/** Args prepended to the claude argv on every launch bound to this channel. */
|
|
786
799
|
options: z.array(z.string()).optional(),
|
|
800
|
+
/** Env vars layered under the launched claude process. process.env wins on collision. */
|
|
787
801
|
env: z.record(z.string(), z.string()).optional(),
|
|
788
802
|
connectors: z.array(connectorSpecSchema).optional()
|
|
789
803
|
});
|
|
790
804
|
const localConfigSchema = z.object({
|
|
791
805
|
$schema: z.string().optional(),
|
|
792
|
-
/** Extra args forwarded to every channel's launch before the channel's own options. User-supplied CLI args still come last. */
|
|
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. */
|
|
795
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
796
806
|
/** Declared channels. First entry is the default; --channel <name> selects by name. */
|
|
797
807
|
channels: z.array(channelSpecSchema).min(1)
|
|
798
808
|
});
|
|
@@ -881,6 +891,17 @@ var FunnelLocalConfig = class {
|
|
|
881
891
|
var FunnelTokenPrompter = class {};
|
|
882
892
|
//#endregion
|
|
883
893
|
//#region lib/engine/local-config/local-config-sync.ts
|
|
894
|
+
const arraysEqual = (a, b) => {
|
|
895
|
+
if (a.length !== b.length) return false;
|
|
896
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
897
|
+
return true;
|
|
898
|
+
};
|
|
899
|
+
const recordsEqual = (a, b) => {
|
|
900
|
+
const keys = Object.keys(a);
|
|
901
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
902
|
+
for (const key of keys) if (a[key] !== b[key]) return false;
|
|
903
|
+
return true;
|
|
904
|
+
};
|
|
884
905
|
/**
|
|
885
906
|
* Reconciles a single funnel.json channel spec with `~/.funnel/settings.json`.
|
|
886
907
|
* The spec is the source of truth for the channel it declares:
|
|
@@ -910,7 +931,18 @@ var FunnelLocalConfigSync = class {
|
|
|
910
931
|
Object.freeze(this);
|
|
911
932
|
}
|
|
912
933
|
async ensure(channel, cwd) {
|
|
913
|
-
|
|
934
|
+
const existing = this.channels.get(channel.name);
|
|
935
|
+
if (!existing) this.channels.add({
|
|
936
|
+
name: channel.name,
|
|
937
|
+
options: channel.options ?? [],
|
|
938
|
+
env: channel.env ?? {}
|
|
939
|
+
});
|
|
940
|
+
else {
|
|
941
|
+
const nextOptions = channel.options ?? [];
|
|
942
|
+
const nextEnv = channel.env ?? {};
|
|
943
|
+
if (!arraysEqual(existing.options, nextOptions)) this.channels.setOptions(channel.name, nextOptions);
|
|
944
|
+
if (!recordsEqual(existing.env, nextEnv)) this.channels.setEnv(channel.name, nextEnv);
|
|
945
|
+
}
|
|
914
946
|
if (channel.connectors === void 0) return;
|
|
915
947
|
const dotenv = this.dotenv.read(cwd);
|
|
916
948
|
const touched = /* @__PURE__ */ new Set();
|
|
@@ -1315,7 +1347,6 @@ var FunnelProfiles = class {
|
|
|
1315
1347
|
profile.channelId = fields.channelId;
|
|
1316
1348
|
}
|
|
1317
1349
|
if (fields.path !== void 0) profile.path = fields.path;
|
|
1318
|
-
if (fields.subAgent !== void 0) profile.subAgent = fields.subAgent;
|
|
1319
1350
|
this.store.write(settings);
|
|
1320
1351
|
}
|
|
1321
1352
|
};
|
|
@@ -3361,6 +3392,7 @@ var Funnel = class Funnel {
|
|
|
3361
3392
|
process: this.process,
|
|
3362
3393
|
clock: this.clock,
|
|
3363
3394
|
logger: this.logger,
|
|
3395
|
+
dir: this.paths.dir,
|
|
3364
3396
|
killCompetingSlack: options.killCompetingSlack,
|
|
3365
3397
|
token: options.token ?? this.gatewayToken.ensure(),
|
|
3366
3398
|
extraRoutes: options.extraRoutes
|
|
@@ -4121,10 +4153,8 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4121
4153
|
const exitCode = await funnel.claude.launch({
|
|
4122
4154
|
channel: profile.channelId,
|
|
4123
4155
|
cwd: profile.path,
|
|
4124
|
-
subAgent: profile.subAgent,
|
|
4125
4156
|
userArgs,
|
|
4126
|
-
profileName: profile.name
|
|
4127
|
-
brief: profile.brief
|
|
4157
|
+
profileName: profile.name
|
|
4128
4158
|
});
|
|
4129
4159
|
process.exit(exitCode);
|
|
4130
4160
|
}
|
|
@@ -4137,15 +4167,7 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4137
4167
|
const exitCode = await funnel.claude.launch({
|
|
4138
4168
|
channel: picked.name,
|
|
4139
4169
|
cwd,
|
|
4140
|
-
userArgs
|
|
4141
|
-
...local.options ?? [],
|
|
4142
|
-
...picked.options ?? [],
|
|
4143
|
-
...userArgs
|
|
4144
|
-
],
|
|
4145
|
-
extraEnv: {
|
|
4146
|
-
...local.env ?? {},
|
|
4147
|
-
...picked.env ?? {}
|
|
4148
|
-
}
|
|
4170
|
+
userArgs
|
|
4149
4171
|
});
|
|
4150
4172
|
process.exit(exitCode);
|
|
4151
4173
|
}
|
|
@@ -4154,10 +4176,8 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4154
4176
|
const exitCode = await funnel.claude.launch({
|
|
4155
4177
|
channel: defaultProfile.channelId,
|
|
4156
4178
|
cwd: defaultProfile.path,
|
|
4157
|
-
subAgent: defaultProfile.subAgent,
|
|
4158
4179
|
userArgs,
|
|
4159
|
-
profileName: defaultProfile.name
|
|
4160
|
-
brief: defaultProfile.brief
|
|
4180
|
+
profileName: defaultProfile.name
|
|
4161
4181
|
});
|
|
4162
4182
|
process.exit(exitCode);
|
|
4163
4183
|
});
|
|
@@ -4387,18 +4407,18 @@ examples:
|
|
|
4387
4407
|
//#region lib/cli/routes/profiles.add.$profile.ts
|
|
4388
4408
|
const addHelp = `funnel profiles add — add a profile
|
|
4389
4409
|
|
|
4390
|
-
usage: funnel profiles add <name> --path <path> --
|
|
4410
|
+
usage: funnel profiles add <name> --path <path> --channel <channel-name>
|
|
4391
4411
|
|
|
4392
4412
|
options:
|
|
4393
|
-
--path
|
|
4394
|
-
--
|
|
4395
|
-
|
|
4396
|
-
|
|
4413
|
+
--path working directory passed to claude as cwd
|
|
4414
|
+
--channel channel name (resolved to channel id internally)
|
|
4415
|
+
|
|
4416
|
+
Per-launch flags like --agent or --brief now live on the channel itself
|
|
4417
|
+
(set with \`fnl channels <name> set options ...\`), so profiles are only
|
|
4418
|
+
\`{ name, path, channelId }\`.`;
|
|
4397
4419
|
const profilesAddHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({
|
|
4398
4420
|
path: z.string(),
|
|
4399
|
-
|
|
4400
|
-
channel: z.string(),
|
|
4401
|
-
brief: z.coerce.boolean().optional()
|
|
4421
|
+
channel: z.string()
|
|
4402
4422
|
}), addHelp), (c) => {
|
|
4403
4423
|
const param = c.req.valid("param");
|
|
4404
4424
|
const query = c.req.valid("query");
|
|
@@ -4408,9 +4428,7 @@ const profilesAddHandler = factory.createHandlers(zValidator$1("param", z.object
|
|
|
4408
4428
|
funnel.profiles.add({
|
|
4409
4429
|
name: param.profile,
|
|
4410
4430
|
path: query.path,
|
|
4411
|
-
|
|
4412
|
-
channelId: channel.id,
|
|
4413
|
-
...query.brief !== void 0 ? { brief: query.brief } : {}
|
|
4431
|
+
channelId: channel.id
|
|
4414
4432
|
});
|
|
4415
4433
|
return c.text(`added profile "${param.profile}"`);
|
|
4416
4434
|
});
|
|
@@ -4453,7 +4471,6 @@ const profilesLaunchHandler = factory.createHandlers(zValidator$1("param", z.obj
|
|
|
4453
4471
|
const exitCode = await funnel.claude.launch({
|
|
4454
4472
|
channel: profile.channelId,
|
|
4455
4473
|
cwd: profile.path,
|
|
4456
|
-
subAgent: profile.subAgent,
|
|
4457
4474
|
userArgs: queryToCliArgs(c.req.url, RESERVED_KEYS),
|
|
4458
4475
|
profileName: profile.name
|
|
4459
4476
|
});
|
|
@@ -4473,25 +4490,19 @@ const profilesRemoveHandler = factory.createHandlers(zValidator$1("param", z.obj
|
|
|
4473
4490
|
//#region lib/cli/routes/profiles.set.$profile.ts
|
|
4474
4491
|
const setHelp = `funnel profiles <name> set — update a profile
|
|
4475
4492
|
|
|
4476
|
-
usage: funnel profiles <name> set [--path <path>] [--
|
|
4493
|
+
usage: funnel profiles <name> set [--path <path>] [--channel <channel-name>]`;
|
|
4477
4494
|
const profilesSetHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({
|
|
4478
4495
|
path: z.string().optional(),
|
|
4479
|
-
|
|
4480
|
-
channel: z.string().optional(),
|
|
4481
|
-
brief: z.coerce.boolean().optional(),
|
|
4482
|
-
"no-brief": z.coerce.boolean().optional()
|
|
4496
|
+
channel: z.string().optional()
|
|
4483
4497
|
}), setHelp), (c) => {
|
|
4484
4498
|
const param = c.req.valid("param");
|
|
4485
4499
|
const query = c.req.valid("query");
|
|
4486
4500
|
const funnel = c.var.funnel;
|
|
4487
4501
|
const channel = query.channel !== void 0 ? funnel.channels.get(query.channel) : null;
|
|
4488
4502
|
if (query.channel !== void 0 && !channel) throw new HTTPException(400, { message: `channel "${query.channel}" not found` });
|
|
4489
|
-
const brief = query["no-brief"] ? false : query.brief;
|
|
4490
4503
|
funnel.profiles.update(param.profile, {
|
|
4491
4504
|
path: query.path,
|
|
4492
|
-
|
|
4493
|
-
channelId: channel?.id,
|
|
4494
|
-
...brief !== void 0 ? { brief } : {}
|
|
4505
|
+
channelId: channel?.id
|
|
4495
4506
|
});
|
|
4496
4507
|
return c.text(`updated profile "${param.profile}"`);
|
|
4497
4508
|
});
|
|
@@ -4501,23 +4512,27 @@ usage: funnel profiles [subcommand]
|
|
|
4501
4512
|
|
|
4502
4513
|
subcommands:
|
|
4503
4514
|
(none) list (first entry is the default)
|
|
4504
|
-
add <name> --path <path> --
|
|
4505
|
-
<name> set [--path ...] [--
|
|
4515
|
+
add <name> --path <path> --channel <channel>
|
|
4516
|
+
<name> set [--path ...] [--channel ...]
|
|
4506
4517
|
<name> as-default move profile to the front (becomes default)
|
|
4507
4518
|
rename <old> <new> rename
|
|
4508
4519
|
remove <name> remove
|
|
4509
4520
|
<name> run launch (sugar for fnl claude -p <name>)
|
|
4510
4521
|
<name> launch (alias for run)
|
|
4511
4522
|
|
|
4523
|
+
Per-launch flags like --agent or --brief now live on the channel itself
|
|
4524
|
+
(set with \`fnl channels <name> set options ...\`), so profiles are only
|
|
4525
|
+
\`{ name, path, channelId }\`.
|
|
4526
|
+
|
|
4512
4527
|
examples:
|
|
4513
|
-
funnel profiles add cto --path /repo/myapp --
|
|
4528
|
+
funnel profiles add cto --path /repo/myapp --channel prod-inbox
|
|
4514
4529
|
funnel profiles cto as-default
|
|
4515
4530
|
funnel profiles cto run`), (c) => {
|
|
4516
4531
|
const profiles = c.var.funnel.profiles.list();
|
|
4517
4532
|
if (profiles.length === 0) return c.text("no profiles");
|
|
4518
4533
|
const lines = profiles.map((profile, index) => {
|
|
4519
4534
|
const tag = index === 0 ? " (default)" : "";
|
|
4520
|
-
return `${profile.name}${tag} [path=${profile.path},
|
|
4535
|
+
return `${profile.name}${tag} [path=${profile.path}, channel=${profile.channelId}]`;
|
|
4521
4536
|
});
|
|
4522
4537
|
return c.text(lines.join("\n"));
|
|
4523
4538
|
});
|
|
@@ -4571,7 +4586,7 @@ const statusHandler = factory.createHandlers(zValidator$1("query", z.object({}),
|
|
|
4571
4586
|
const tag = index === 0 ? " (default)" : "";
|
|
4572
4587
|
const channel = funnel.channels.getById(profile.channelId);
|
|
4573
4588
|
const channelLabel = channel ? channel.name : `id:${profile.channelId}`;
|
|
4574
|
-
lines.push(` - ${profile.name}${tag} [path=${profile.path},
|
|
4589
|
+
lines.push(` - ${profile.name}${tag} [path=${profile.path}, channel=${channelLabel}]`);
|
|
4575
4590
|
}
|
|
4576
4591
|
lines.push("");
|
|
4577
4592
|
if (!gatewayStatus.running) lines.push("gateway: not running");
|
|
@@ -4949,7 +4964,7 @@ function ProfileLauncher(props) {
|
|
|
4949
4964
|
children: profile.name
|
|
4950
4965
|
}), /* @__PURE__ */ jsx("span", {
|
|
4951
4966
|
fg: funnel.faint,
|
|
4952
|
-
children: ` → channel ${profile.channelId} · path ${profile.path}
|
|
4967
|
+
children: ` → channel ${profile.channelId} · path ${profile.path}`
|
|
4953
4968
|
})]
|
|
4954
4969
|
}, profile.name);
|
|
4955
4970
|
})
|
|
@@ -6464,9 +6479,6 @@ function ProfilesView(props) {
|
|
|
6464
6479
|
} else if (field === "path") {
|
|
6465
6480
|
const next = raw.trim();
|
|
6466
6481
|
if (next) props.funnel.profiles.update(profile.name, { path: next });
|
|
6467
|
-
} else if (field === "sub-agent") {
|
|
6468
|
-
const next = raw.trim();
|
|
6469
|
-
if (next) props.funnel.profiles.update(profile.name, { subAgent: next });
|
|
6470
6482
|
}
|
|
6471
6483
|
} catch (error) {
|
|
6472
6484
|
props.funnel.logger.error(error instanceof Error ? error.message : String(error));
|
|
@@ -6490,7 +6502,6 @@ function ProfilesView(props) {
|
|
|
6490
6502
|
props.funnel.profiles.add({
|
|
6491
6503
|
name,
|
|
6492
6504
|
path: "",
|
|
6493
|
-
subAgent: "",
|
|
6494
6505
|
channelId
|
|
6495
6506
|
});
|
|
6496
6507
|
props.setFocusedKey(fieldKey(name, "name"));
|
|
@@ -6524,14 +6535,6 @@ function ProfilesView(props) {
|
|
|
6524
6535
|
onCommit: (raw) => commit(profile, "path", raw),
|
|
6525
6536
|
placeholder: "repository path"
|
|
6526
6537
|
}),
|
|
6527
|
-
/* @__PURE__ */ jsx(EditableField, {
|
|
6528
|
-
label: "sub-agent",
|
|
6529
|
-
initialValue: profile.subAgent,
|
|
6530
|
-
focused: props.focusedKey === fieldKey(profile.name, "sub-agent"),
|
|
6531
|
-
onFocus: () => props.setFocusedKey(fieldKey(profile.name, "sub-agent")),
|
|
6532
|
-
onCommit: (raw) => commit(profile, "sub-agent", raw),
|
|
6533
|
-
placeholder: "claude --agent value"
|
|
6534
|
-
}),
|
|
6535
6538
|
/* @__PURE__ */ jsx(EditableField, {
|
|
6536
6539
|
label: "channel",
|
|
6537
6540
|
initialValue: profile.channelId,
|
|
@@ -6657,7 +6660,6 @@ function App(props) {
|
|
|
6657
6660
|
await props.funnel.claude.launch({
|
|
6658
6661
|
channel: profile.channelId,
|
|
6659
6662
|
cwd: profile.path,
|
|
6660
|
-
subAgent: profile.subAgent,
|
|
6661
6663
|
profileName: profile.name
|
|
6662
6664
|
});
|
|
6663
6665
|
} catch (error) {
|
package/funnel.schema.json
CHANGED
|
@@ -5,21 +5,6 @@
|
|
|
5
5
|
"$schema": {
|
|
6
6
|
"type": "string"
|
|
7
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
8
|
"channels": {
|
|
24
9
|
"minItems": 1,
|
|
25
10
|
"type": "array",
|
package/package.json
CHANGED