@interactive-inc/claude-funnel 0.15.2 → 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 +44 -33
- package/dist/bin.js +585 -563
- package/dist/gateway/daemon.js +228 -215
- package/dist/index.d.ts +62 -48
- package/dist/index.js +117 -99
- package/funnel.schema.json +132 -119
- package/package.json +1 -1
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;
|
|
@@ -737,20 +749,16 @@ var MemoryFunnelIdGenerator = class extends FunnelIdGenerator {
|
|
|
737
749
|
/**
|
|
738
750
|
* Per-repo launch config (`funnel.json`).
|
|
739
751
|
*
|
|
740
|
-
* `fnl claude` reads this when no --profile
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
* `~/.funnel/settings.json` on launch
|
|
752
|
+
* `fnl claude` reads this when no --profile is given and picks one of the
|
|
753
|
+
* declared channels (`--channel <name>` selects by name; otherwise the first
|
|
754
|
+
* entry wins). The chosen channel is materialized into
|
|
755
|
+
* `~/.funnel/settings.json` on launch — token fields in connectors resolve
|
|
756
|
+
* via literal / `env.<field>` / TTY prompt.
|
|
744
757
|
*
|
|
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.
|
|
758
|
+
* Top-level `options` and `env` are defaults shared by every channel: each
|
|
759
|
+
* channel's own `options` is appended after the shared ones (CLI semantics
|
|
760
|
+
* keep the later flag winning), and `env` is a shallow merge with the
|
|
761
|
+
* channel's keys overriding the shared ones.
|
|
754
762
|
*/
|
|
755
763
|
const slackEnvSchema = z.object({
|
|
756
764
|
botToken: z.string().optional(),
|
|
@@ -785,14 +793,19 @@ const connectorSpecSchema = z.discriminatedUnion("type", [
|
|
|
785
793
|
ghConnectorSpecSchema,
|
|
786
794
|
scheduleConnectorSpecSchema
|
|
787
795
|
]);
|
|
788
|
-
const
|
|
789
|
-
|
|
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). */
|
|
796
|
+
const channelSpecSchema = z.object({
|
|
797
|
+
name: z.string(),
|
|
798
|
+
/** Args prepended to the claude argv on every launch bound to this channel. */
|
|
792
799
|
options: z.array(z.string()).optional(),
|
|
800
|
+
/** Env vars layered under the launched claude process. process.env wins on collision. */
|
|
793
801
|
env: z.record(z.string(), z.string()).optional(),
|
|
794
802
|
connectors: z.array(connectorSpecSchema).optional()
|
|
795
803
|
});
|
|
804
|
+
const localConfigSchema = z.object({
|
|
805
|
+
$schema: z.string().optional(),
|
|
806
|
+
/** Declared channels. First entry is the default; --channel <name> selects by name. */
|
|
807
|
+
channels: z.array(channelSpecSchema).min(1)
|
|
808
|
+
});
|
|
796
809
|
const LOCAL_CONFIG_FILENAME = "funnel.json";
|
|
797
810
|
const LOCAL_ENV_FILENAME = ".env.local";
|
|
798
811
|
//#endregion
|
|
@@ -878,9 +891,20 @@ var FunnelLocalConfig = class {
|
|
|
878
891
|
var FunnelTokenPrompter = class {};
|
|
879
892
|
//#endregion
|
|
880
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
|
+
};
|
|
881
905
|
/**
|
|
882
|
-
* Reconciles a
|
|
883
|
-
* is the source of truth for the channel it declares:
|
|
906
|
+
* Reconciles a single funnel.json channel spec with `~/.funnel/settings.json`.
|
|
907
|
+
* The spec is the source of truth for the channel it declares:
|
|
884
908
|
*
|
|
885
909
|
* - missing channel → created
|
|
886
910
|
* - declared connector matched by name → tokens reconciled
|
|
@@ -889,9 +913,10 @@ var FunnelTokenPrompter = class {};
|
|
|
889
913
|
* - declared connector with no match → added
|
|
890
914
|
* - any connector left in the channel that the spec did not touch → removed
|
|
891
915
|
*
|
|
892
|
-
* Removal only fires when
|
|
893
|
-
* field means "do not manage connectors from here" and leaves
|
|
894
|
-
* `~/.funnel` alone.
|
|
916
|
+
* Removal only fires when the channel spec has a `connectors` field. An
|
|
917
|
+
* absent field means "do not manage connectors from here" and leaves
|
|
918
|
+
* everything in `~/.funnel` alone. Other channels in funnel.json (not
|
|
919
|
+
* passed to this call) are untouched.
|
|
895
920
|
*/
|
|
896
921
|
var FunnelLocalConfigSync = class {
|
|
897
922
|
channels;
|
|
@@ -905,16 +930,27 @@ var FunnelLocalConfigSync = class {
|
|
|
905
930
|
this.env = deps.env ?? process.env;
|
|
906
931
|
Object.freeze(this);
|
|
907
932
|
}
|
|
908
|
-
async ensure(
|
|
909
|
-
|
|
910
|
-
if (
|
|
933
|
+
async ensure(channel, cwd) {
|
|
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
|
+
}
|
|
946
|
+
if (channel.connectors === void 0) return;
|
|
911
947
|
const dotenv = this.dotenv.read(cwd);
|
|
912
948
|
const touched = /* @__PURE__ */ new Set();
|
|
913
|
-
for (const spec of
|
|
914
|
-
const id = await this.ensureConnector(
|
|
949
|
+
for (const spec of channel.connectors) {
|
|
950
|
+
const id = await this.ensureConnector(channel.name, spec, dotenv);
|
|
915
951
|
touched.add(id);
|
|
916
952
|
}
|
|
917
|
-
this.removeExtras(
|
|
953
|
+
this.removeExtras(channel.name, touched);
|
|
918
954
|
}
|
|
919
955
|
async ensureConnector(channelName, spec, dotenv) {
|
|
920
956
|
if (spec.type === "slack") return await this.ensureSlack(channelName, spec, dotenv);
|
|
@@ -1311,7 +1347,6 @@ var FunnelProfiles = class {
|
|
|
1311
1347
|
profile.channelId = fields.channelId;
|
|
1312
1348
|
}
|
|
1313
1349
|
if (fields.path !== void 0) profile.path = fields.path;
|
|
1314
|
-
if (fields.subAgent !== void 0) profile.subAgent = fields.subAgent;
|
|
1315
1350
|
this.store.write(settings);
|
|
1316
1351
|
}
|
|
1317
1352
|
};
|
|
@@ -3357,6 +3392,7 @@ var Funnel = class Funnel {
|
|
|
3357
3392
|
process: this.process,
|
|
3358
3393
|
clock: this.clock,
|
|
3359
3394
|
logger: this.logger,
|
|
3395
|
+
dir: this.paths.dir,
|
|
3360
3396
|
killCompetingSlack: options.killCompetingSlack,
|
|
3361
3397
|
token: options.token ?? this.gatewayToken.ensure(),
|
|
3362
3398
|
extraRoutes: options.extraRoutes
|
|
@@ -4075,19 +4111,22 @@ examples:
|
|
|
4075
4111
|
const claudeHelp = `funnel claude — launch Claude Code
|
|
4076
4112
|
|
|
4077
4113
|
usage:
|
|
4078
|
-
funnel claude launch
|
|
4114
|
+
funnel claude launch the first channel from funnel.json, or the default profile
|
|
4115
|
+
funnel claude --channel <name> with funnel.json: select that channel; without: raw launch
|
|
4079
4116
|
funnel claude -p <name> launch a named profile
|
|
4080
4117
|
funnel claude --profile <name> (long form)
|
|
4081
|
-
funnel claude --channel <name> raw launch (no profile, cwd = current dir)
|
|
4082
4118
|
funnel claude [...] any other argument is forwarded to the claude CLI
|
|
4083
4119
|
|
|
4084
|
-
resolution order
|
|
4085
|
-
1.
|
|
4086
|
-
2.
|
|
4120
|
+
resolution order:
|
|
4121
|
+
1. --help print this help
|
|
4122
|
+
2. --profile <name> named profile (ignores funnel.json)
|
|
4123
|
+
3. ./funnel.json in the current directory + --channel selects (or first wins)
|
|
4124
|
+
4. --channel <name> with no funnel.json → raw launch using an existing settings.json channel
|
|
4125
|
+
5. the default profile (first entry in fnl profiles)
|
|
4087
4126
|
|
|
4088
4127
|
funnel-specific options (everything else passes through to claude verbatim):
|
|
4089
4128
|
-p, --profile profile name to launch
|
|
4090
|
-
--channel channel name (
|
|
4129
|
+
--channel channel name (selects from funnel.json, or raw-launches if no funnel.json)
|
|
4091
4130
|
-h, --help show this help
|
|
4092
4131
|
|
|
4093
4132
|
Positional args, unknown short flags (e.g. -c, -r), and claude's own flags
|
|
@@ -4114,22 +4153,21 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4114
4153
|
const exitCode = await funnel.claude.launch({
|
|
4115
4154
|
channel: profile.channelId,
|
|
4116
4155
|
cwd: profile.path,
|
|
4117
|
-
subAgent: profile.subAgent,
|
|
4118
4156
|
userArgs,
|
|
4119
|
-
profileName: profile.name
|
|
4120
|
-
brief: profile.brief
|
|
4157
|
+
profileName: profile.name
|
|
4121
4158
|
});
|
|
4122
4159
|
process.exit(exitCode);
|
|
4123
4160
|
}
|
|
4124
4161
|
const cwd = process.cwd();
|
|
4125
4162
|
const local = funnel.localConfig.read(cwd);
|
|
4126
4163
|
if (local) {
|
|
4127
|
-
|
|
4164
|
+
const picked = query.channel !== void 0 ? local.channels.find((c) => c.name === query.channel) : local.channels[0];
|
|
4165
|
+
if (!picked) throw new HTTPException(404, { message: query.channel ? `channel "${query.channel}" is not declared in funnel.json` : `funnel.json declares no channels` });
|
|
4166
|
+
await funnel.localConfigSync.ensure(picked, cwd);
|
|
4128
4167
|
const exitCode = await funnel.claude.launch({
|
|
4129
|
-
channel:
|
|
4168
|
+
channel: picked.name,
|
|
4130
4169
|
cwd,
|
|
4131
|
-
userArgs
|
|
4132
|
-
extraEnv: local.env
|
|
4170
|
+
userArgs
|
|
4133
4171
|
});
|
|
4134
4172
|
process.exit(exitCode);
|
|
4135
4173
|
}
|
|
@@ -4138,10 +4176,8 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4138
4176
|
const exitCode = await funnel.claude.launch({
|
|
4139
4177
|
channel: defaultProfile.channelId,
|
|
4140
4178
|
cwd: defaultProfile.path,
|
|
4141
|
-
subAgent: defaultProfile.subAgent,
|
|
4142
4179
|
userArgs,
|
|
4143
|
-
profileName: defaultProfile.name
|
|
4144
|
-
brief: defaultProfile.brief
|
|
4180
|
+
profileName: defaultProfile.name
|
|
4145
4181
|
});
|
|
4146
4182
|
process.exit(exitCode);
|
|
4147
4183
|
});
|
|
@@ -4371,18 +4407,18 @@ examples:
|
|
|
4371
4407
|
//#region lib/cli/routes/profiles.add.$profile.ts
|
|
4372
4408
|
const addHelp = `funnel profiles add — add a profile
|
|
4373
4409
|
|
|
4374
|
-
usage: funnel profiles add <name> --path <path> --
|
|
4410
|
+
usage: funnel profiles add <name> --path <path> --channel <channel-name>
|
|
4375
4411
|
|
|
4376
4412
|
options:
|
|
4377
|
-
--path
|
|
4378
|
-
--
|
|
4379
|
-
|
|
4380
|
-
|
|
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 }\`.`;
|
|
4381
4419
|
const profilesAddHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({
|
|
4382
4420
|
path: z.string(),
|
|
4383
|
-
|
|
4384
|
-
channel: z.string(),
|
|
4385
|
-
brief: z.coerce.boolean().optional()
|
|
4421
|
+
channel: z.string()
|
|
4386
4422
|
}), addHelp), (c) => {
|
|
4387
4423
|
const param = c.req.valid("param");
|
|
4388
4424
|
const query = c.req.valid("query");
|
|
@@ -4392,9 +4428,7 @@ const profilesAddHandler = factory.createHandlers(zValidator$1("param", z.object
|
|
|
4392
4428
|
funnel.profiles.add({
|
|
4393
4429
|
name: param.profile,
|
|
4394
4430
|
path: query.path,
|
|
4395
|
-
|
|
4396
|
-
channelId: channel.id,
|
|
4397
|
-
...query.brief !== void 0 ? { brief: query.brief } : {}
|
|
4431
|
+
channelId: channel.id
|
|
4398
4432
|
});
|
|
4399
4433
|
return c.text(`added profile "${param.profile}"`);
|
|
4400
4434
|
});
|
|
@@ -4437,7 +4471,6 @@ const profilesLaunchHandler = factory.createHandlers(zValidator$1("param", z.obj
|
|
|
4437
4471
|
const exitCode = await funnel.claude.launch({
|
|
4438
4472
|
channel: profile.channelId,
|
|
4439
4473
|
cwd: profile.path,
|
|
4440
|
-
subAgent: profile.subAgent,
|
|
4441
4474
|
userArgs: queryToCliArgs(c.req.url, RESERVED_KEYS),
|
|
4442
4475
|
profileName: profile.name
|
|
4443
4476
|
});
|
|
@@ -4457,25 +4490,19 @@ const profilesRemoveHandler = factory.createHandlers(zValidator$1("param", z.obj
|
|
|
4457
4490
|
//#region lib/cli/routes/profiles.set.$profile.ts
|
|
4458
4491
|
const setHelp = `funnel profiles <name> set — update a profile
|
|
4459
4492
|
|
|
4460
|
-
usage: funnel profiles <name> set [--path <path>] [--
|
|
4493
|
+
usage: funnel profiles <name> set [--path <path>] [--channel <channel-name>]`;
|
|
4461
4494
|
const profilesSetHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({
|
|
4462
4495
|
path: z.string().optional(),
|
|
4463
|
-
|
|
4464
|
-
channel: z.string().optional(),
|
|
4465
|
-
brief: z.coerce.boolean().optional(),
|
|
4466
|
-
"no-brief": z.coerce.boolean().optional()
|
|
4496
|
+
channel: z.string().optional()
|
|
4467
4497
|
}), setHelp), (c) => {
|
|
4468
4498
|
const param = c.req.valid("param");
|
|
4469
4499
|
const query = c.req.valid("query");
|
|
4470
4500
|
const funnel = c.var.funnel;
|
|
4471
4501
|
const channel = query.channel !== void 0 ? funnel.channels.get(query.channel) : null;
|
|
4472
4502
|
if (query.channel !== void 0 && !channel) throw new HTTPException(400, { message: `channel "${query.channel}" not found` });
|
|
4473
|
-
const brief = query["no-brief"] ? false : query.brief;
|
|
4474
4503
|
funnel.profiles.update(param.profile, {
|
|
4475
4504
|
path: query.path,
|
|
4476
|
-
|
|
4477
|
-
channelId: channel?.id,
|
|
4478
|
-
...brief !== void 0 ? { brief } : {}
|
|
4505
|
+
channelId: channel?.id
|
|
4479
4506
|
});
|
|
4480
4507
|
return c.text(`updated profile "${param.profile}"`);
|
|
4481
4508
|
});
|
|
@@ -4485,23 +4512,27 @@ usage: funnel profiles [subcommand]
|
|
|
4485
4512
|
|
|
4486
4513
|
subcommands:
|
|
4487
4514
|
(none) list (first entry is the default)
|
|
4488
|
-
add <name> --path <path> --
|
|
4489
|
-
<name> set [--path ...] [--
|
|
4515
|
+
add <name> --path <path> --channel <channel>
|
|
4516
|
+
<name> set [--path ...] [--channel ...]
|
|
4490
4517
|
<name> as-default move profile to the front (becomes default)
|
|
4491
4518
|
rename <old> <new> rename
|
|
4492
4519
|
remove <name> remove
|
|
4493
4520
|
<name> run launch (sugar for fnl claude -p <name>)
|
|
4494
4521
|
<name> launch (alias for run)
|
|
4495
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
|
+
|
|
4496
4527
|
examples:
|
|
4497
|
-
funnel profiles add cto --path /repo/myapp --
|
|
4528
|
+
funnel profiles add cto --path /repo/myapp --channel prod-inbox
|
|
4498
4529
|
funnel profiles cto as-default
|
|
4499
4530
|
funnel profiles cto run`), (c) => {
|
|
4500
4531
|
const profiles = c.var.funnel.profiles.list();
|
|
4501
4532
|
if (profiles.length === 0) return c.text("no profiles");
|
|
4502
4533
|
const lines = profiles.map((profile, index) => {
|
|
4503
4534
|
const tag = index === 0 ? " (default)" : "";
|
|
4504
|
-
return `${profile.name}${tag} [path=${profile.path},
|
|
4535
|
+
return `${profile.name}${tag} [path=${profile.path}, channel=${profile.channelId}]`;
|
|
4505
4536
|
});
|
|
4506
4537
|
return c.text(lines.join("\n"));
|
|
4507
4538
|
});
|
|
@@ -4555,7 +4586,7 @@ const statusHandler = factory.createHandlers(zValidator$1("query", z.object({}),
|
|
|
4555
4586
|
const tag = index === 0 ? " (default)" : "";
|
|
4556
4587
|
const channel = funnel.channels.getById(profile.channelId);
|
|
4557
4588
|
const channelLabel = channel ? channel.name : `id:${profile.channelId}`;
|
|
4558
|
-
lines.push(` - ${profile.name}${tag} [path=${profile.path},
|
|
4589
|
+
lines.push(` - ${profile.name}${tag} [path=${profile.path}, channel=${channelLabel}]`);
|
|
4559
4590
|
}
|
|
4560
4591
|
lines.push("");
|
|
4561
4592
|
if (!gatewayStatus.running) lines.push("gateway: not running");
|
|
@@ -4933,7 +4964,7 @@ function ProfileLauncher(props) {
|
|
|
4933
4964
|
children: profile.name
|
|
4934
4965
|
}), /* @__PURE__ */ jsx("span", {
|
|
4935
4966
|
fg: funnel.faint,
|
|
4936
|
-
children: ` → channel ${profile.channelId} · path ${profile.path}
|
|
4967
|
+
children: ` → channel ${profile.channelId} · path ${profile.path}`
|
|
4937
4968
|
})]
|
|
4938
4969
|
}, profile.name);
|
|
4939
4970
|
})
|
|
@@ -6448,9 +6479,6 @@ function ProfilesView(props) {
|
|
|
6448
6479
|
} else if (field === "path") {
|
|
6449
6480
|
const next = raw.trim();
|
|
6450
6481
|
if (next) props.funnel.profiles.update(profile.name, { path: next });
|
|
6451
|
-
} else if (field === "sub-agent") {
|
|
6452
|
-
const next = raw.trim();
|
|
6453
|
-
if (next) props.funnel.profiles.update(profile.name, { subAgent: next });
|
|
6454
6482
|
}
|
|
6455
6483
|
} catch (error) {
|
|
6456
6484
|
props.funnel.logger.error(error instanceof Error ? error.message : String(error));
|
|
@@ -6474,7 +6502,6 @@ function ProfilesView(props) {
|
|
|
6474
6502
|
props.funnel.profiles.add({
|
|
6475
6503
|
name,
|
|
6476
6504
|
path: "",
|
|
6477
|
-
subAgent: "",
|
|
6478
6505
|
channelId
|
|
6479
6506
|
});
|
|
6480
6507
|
props.setFocusedKey(fieldKey(name, "name"));
|
|
@@ -6508,14 +6535,6 @@ function ProfilesView(props) {
|
|
|
6508
6535
|
onCommit: (raw) => commit(profile, "path", raw),
|
|
6509
6536
|
placeholder: "repository path"
|
|
6510
6537
|
}),
|
|
6511
|
-
/* @__PURE__ */ jsx(EditableField, {
|
|
6512
|
-
label: "sub-agent",
|
|
6513
|
-
initialValue: profile.subAgent,
|
|
6514
|
-
focused: props.focusedKey === fieldKey(profile.name, "sub-agent"),
|
|
6515
|
-
onFocus: () => props.setFocusedKey(fieldKey(profile.name, "sub-agent")),
|
|
6516
|
-
onCommit: (raw) => commit(profile, "sub-agent", raw),
|
|
6517
|
-
placeholder: "claude --agent value"
|
|
6518
|
-
}),
|
|
6519
6538
|
/* @__PURE__ */ jsx(EditableField, {
|
|
6520
6539
|
label: "channel",
|
|
6521
6540
|
initialValue: profile.channelId,
|
|
@@ -6641,7 +6660,6 @@ function App(props) {
|
|
|
6641
6660
|
await props.funnel.claude.launch({
|
|
6642
6661
|
channel: profile.channelId,
|
|
6643
6662
|
cwd: profile.path,
|
|
6644
|
-
subAgent: profile.subAgent,
|
|
6645
6663
|
profileName: profile.name
|
|
6646
6664
|
});
|
|
6647
6665
|
} catch (error) {
|
|
@@ -6831,4 +6849,4 @@ async function launchTui(funnel) {
|
|
|
6831
6849
|
});
|
|
6832
6850
|
}
|
|
6833
6851
|
//#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 };
|
|
6852
|
+
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 };
|