@interactive-inc/claude-funnel 0.37.0 → 0.38.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 +7 -9
- package/dist/bin.js +299 -289
- package/dist/gateway/daemon.js +175 -175
- package/dist/index.d.ts +1838 -3400
- package/dist/index.js +371 -377
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { a as FunnelProcessRunner, i as NodeFunnelProcessRunner, n as FunnelGhLi
|
|
|
4
4
|
import { a as ScheduleStateStore, i as FunnelScheduleListener, n as scheduleConnectorSchema, o as NodeFunnelFileSystem, r as scheduleEntrySchema, s as FunnelFileSystem, t as scheduleCatchupPolicySchema } from "./schedule-connector-schema-CM-sRkac.js";
|
|
5
5
|
import { i as FunnelSlackAdapter, n as FunnelSlackListener, r as FunnelSlackEventProcessor, t as slackConnectorSchema } from "./slack-connector-schema-DDbSGPZn.js";
|
|
6
6
|
import { dirname, join, resolve } from "node:path";
|
|
7
|
+
import { hc } from "hono/client";
|
|
7
8
|
import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
import { homedir, tmpdir } from "node:os";
|
|
@@ -3076,7 +3077,7 @@ const connectionViewSql = `CREATE TEMP VIEW connection AS SELECT
|
|
|
3076
3077
|
FROM connectiondb.leuco_log`;
|
|
3077
3078
|
//#endregion
|
|
3078
3079
|
//#region lib/gateway/routes/debug.ts
|
|
3079
|
-
const extractPreview
|
|
3080
|
+
const extractPreview = (payload) => {
|
|
3080
3081
|
if (typeof payload !== "string" || payload.length === 0) return null;
|
|
3081
3082
|
try {
|
|
3082
3083
|
const parsed = JSON.parse(payload);
|
|
@@ -3182,7 +3183,7 @@ const debugHandler$1 = factory$1.createHandlers(async (c) => {
|
|
|
3182
3183
|
outcome: typeof row.outcome === "string" ? row.outcome : "?",
|
|
3183
3184
|
payload: rawPayload,
|
|
3184
3185
|
payloadParsed,
|
|
3185
|
-
preview: extractPreview
|
|
3186
|
+
preview: extractPreview(row.payload)
|
|
3186
3187
|
});
|
|
3187
3188
|
}
|
|
3188
3189
|
if (listener && (!listener.alive || listener.errors > 0) || !listener) {
|
|
@@ -3294,13 +3295,10 @@ const statusHandler$1 = factory$1.createHandlers((c) => {
|
|
|
3294
3295
|
});
|
|
3295
3296
|
//#endregion
|
|
3296
3297
|
//#region lib/gateway/routes/index.ts
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
* shape as CLI's `c.var.funnel`.
|
|
3302
|
-
*/
|
|
3303
|
-
const gatewayRoutes = factory$1.createApp().get("/health", ...healthHandler).get("/status", ...statusHandler$1).get("/debug", ...debugHandler$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);
|
|
3298
|
+
function buildGatewayRoutes() {
|
|
3299
|
+
return factory$1.createApp().get("/health", ...healthHandler).get("/status", ...statusHandler$1).get("/debug", ...debugHandler$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);
|
|
3300
|
+
}
|
|
3301
|
+
const gatewayRoutes = buildGatewayRoutes();
|
|
3304
3302
|
//#endregion
|
|
3305
3303
|
//#region lib/gateway/gateway-server.ts
|
|
3306
3304
|
const DEFAULT_HOST = "127.0.0.1";
|
|
@@ -4100,6 +4098,10 @@ var Funnel = class Funnel {
|
|
|
4100
4098
|
tmpDir: this.paths.tmpDir
|
|
4101
4099
|
}, channelName ?? null);
|
|
4102
4100
|
}
|
|
4101
|
+
gatewayClient() {
|
|
4102
|
+
const { port } = this.gateway.getStatus();
|
|
4103
|
+
return hc(`http://127.0.0.1:${port}`);
|
|
4104
|
+
}
|
|
4103
4105
|
};
|
|
4104
4106
|
//#endregion
|
|
4105
4107
|
//#region lib/engine/mcp/channel-subscriber.ts
|
|
@@ -5072,6 +5074,17 @@ const queryToCliArgs = (url, reservedKeys = []) => {
|
|
|
5072
5074
|
return args;
|
|
5073
5075
|
};
|
|
5074
5076
|
//#endregion
|
|
5077
|
+
//#region lib/cli/routes/channels.add.ts
|
|
5078
|
+
const help$17 = `funnel channels add — add a channel
|
|
5079
|
+
|
|
5080
|
+
usage: funnel channels add <name> [--delivery fanout|exclusive]
|
|
5081
|
+
|
|
5082
|
+
options:
|
|
5083
|
+
--delivery routing mode (default fanout):
|
|
5084
|
+
fanout every connected client receives every event
|
|
5085
|
+
exclusive each event delivered to exactly one client (round-robin)`;
|
|
5086
|
+
const channelsAddHelpHandler = factory.createHandlers((c) => c.text(help$17));
|
|
5087
|
+
//#endregion
|
|
5075
5088
|
//#region lib/cli/router/validator.ts
|
|
5076
5089
|
const zValidator$1 = (target, schema, helpText) => zValidator(target, schema, (result, c) => {
|
|
5077
5090
|
if (helpText && c.req.query("help")) return c.text(helpText);
|
|
@@ -5083,18 +5096,10 @@ const zValidator$1 = (target, schema, helpText) => zValidator(target, schema, (r
|
|
|
5083
5096
|
});
|
|
5084
5097
|
//#endregion
|
|
5085
5098
|
//#region lib/cli/routes/channels.add.$channel.ts
|
|
5086
|
-
const
|
|
5087
|
-
|
|
5088
|
-
usage: funnel channels add <name> [--delivery fanout|exclusive]
|
|
5089
|
-
|
|
5090
|
-
options:
|
|
5091
|
-
--delivery routing mode (default fanout):
|
|
5092
|
-
fanout every connected client receives every event
|
|
5093
|
-
exclusive each event delivered to exactly one client (round-robin)`;
|
|
5094
|
-
const channelsAddHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", z.object({ delivery: channelDeliveryModeSchema.optional() }), addHelp$3), (c) => {
|
|
5099
|
+
const channelsAddHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", z.object({ delivery: channelDeliveryModeSchema.optional() })), (c) => {
|
|
5095
5100
|
const param = c.req.valid("param");
|
|
5096
5101
|
const query = c.req.valid("query");
|
|
5097
|
-
const created = c.
|
|
5102
|
+
const created = c.env.funnel.channels.add({
|
|
5098
5103
|
name: param.channel,
|
|
5099
5104
|
delivery: query.delivery
|
|
5100
5105
|
});
|
|
@@ -5104,14 +5109,14 @@ const channelsConnectorsGroupHandler = factory.createHandlers(zValidator$1("para
|
|
|
5104
5109
|
|
|
5105
5110
|
usage: funnel channels <channel> connectors`), (c) => {
|
|
5106
5111
|
const param = c.req.valid("param");
|
|
5107
|
-
const channel = c.
|
|
5112
|
+
const channel = c.env.funnel.channels.get(param.channel);
|
|
5108
5113
|
if (!channel) throw new HTTPException(404, { message: `channel "${param.channel}" not found` });
|
|
5109
5114
|
if (channel.connectors.length === 0) return c.text(`no connectors in channel "${channel.name}"`);
|
|
5110
5115
|
return c.text(channel.connectors.map((c) => `${c.name} (${c.type}, id: ${c.id})`).join("\n"));
|
|
5111
5116
|
});
|
|
5112
5117
|
//#endregion
|
|
5113
|
-
//#region lib/cli/routes/channels.$channel.connectors.add
|
|
5114
|
-
const
|
|
5118
|
+
//#region lib/cli/routes/channels.$channel.connectors.add.ts
|
|
5119
|
+
const help$16 = `funnel channels <channel> connectors add <connector> — add a connector to a channel
|
|
5115
5120
|
|
|
5116
5121
|
usage:
|
|
5117
5122
|
funnel channels <channel> connectors add <connector> --type=slack --bot-token=xoxb-... --app-token=xapp-...
|
|
@@ -5120,6 +5125,9 @@ usage:
|
|
|
5120
5125
|
funnel channels <channel> connectors add <connector> --type=schedule
|
|
5121
5126
|
|
|
5122
5127
|
Token uniqueness is enforced across all channels.`;
|
|
5128
|
+
const channelsConnectorsAddHelpHandler = factory.createHandlers((c) => c.text(help$16));
|
|
5129
|
+
//#endregion
|
|
5130
|
+
//#region lib/cli/routes/channels.$channel.connectors.add.$connector.ts
|
|
5123
5131
|
const slackBody = z.object({
|
|
5124
5132
|
type: z.literal("slack"),
|
|
5125
5133
|
"bot-token": z.string().startsWith("xoxb-"),
|
|
@@ -5143,10 +5151,10 @@ const addBody = z.discriminatedUnion("type", [
|
|
|
5143
5151
|
const channelsConnectorsAddHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5144
5152
|
channel: z.string(),
|
|
5145
5153
|
connector: z.string()
|
|
5146
|
-
})), zValidator$1("query", addBody
|
|
5154
|
+
})), zValidator$1("query", addBody), async (c) => {
|
|
5147
5155
|
const param = c.req.valid("param");
|
|
5148
5156
|
const query = c.req.valid("query");
|
|
5149
|
-
const funnel = c.
|
|
5157
|
+
const funnel = c.env.funnel;
|
|
5150
5158
|
if (query.type === "slack") {
|
|
5151
5159
|
const created = funnel.channels.addConnector(param.channel, {
|
|
5152
5160
|
type: "slack",
|
|
@@ -5184,28 +5192,34 @@ const channelsConnectorsAddHandler = factory.createHandlers(zValidator$1("param"
|
|
|
5184
5192
|
return c.text(`added schedule connector "${created.name}" to channel "${param.channel}"`);
|
|
5185
5193
|
});
|
|
5186
5194
|
//#endregion
|
|
5187
|
-
//#region lib/cli/routes/channels.$channel.connectors.remove
|
|
5188
|
-
const
|
|
5195
|
+
//#region lib/cli/routes/channels.$channel.connectors.remove.ts
|
|
5196
|
+
const help$15 = `funnel channels <channel> connectors remove <connector> — remove a connector
|
|
5189
5197
|
|
|
5190
5198
|
usage: funnel channels <channel> connectors remove <connector>`;
|
|
5199
|
+
const channelsConnectorsRemoveHelpHandler = factory.createHandlers((c) => c.text(help$15));
|
|
5200
|
+
//#endregion
|
|
5201
|
+
//#region lib/cli/routes/channels.$channel.connectors.remove.$connector.ts
|
|
5191
5202
|
const channelsConnectorsRemoveHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5192
5203
|
channel: z.string(),
|
|
5193
5204
|
connector: z.string()
|
|
5194
|
-
})), zValidator$1("query", z.object({})
|
|
5205
|
+
})), zValidator$1("query", z.object({})), async (c) => {
|
|
5195
5206
|
const param = c.req.valid("param");
|
|
5196
|
-
const funnel = c.
|
|
5207
|
+
const funnel = c.env.funnel;
|
|
5197
5208
|
await funnel.listeners.stop(param.channel, param.connector);
|
|
5198
5209
|
funnel.channels.removeConnector(param.channel, param.connector);
|
|
5199
5210
|
return c.text(`removed connector "${param.connector}" from channel "${param.channel}"`);
|
|
5200
5211
|
});
|
|
5201
5212
|
//#endregion
|
|
5202
|
-
//#region lib/cli/routes/channels.$channel.connectors.set
|
|
5203
|
-
const
|
|
5213
|
+
//#region lib/cli/routes/channels.$channel.connectors.set.ts
|
|
5214
|
+
const help$14 = `funnel channels <channel> connectors set <connector> — update connector fields
|
|
5204
5215
|
|
|
5205
5216
|
usage:
|
|
5206
5217
|
funnel channels <ch> connectors set <conn> [--bot-token=...] [--app-token=...] # slack
|
|
5207
5218
|
funnel channels <ch> connectors set <conn> [--bot-token=...] # discord
|
|
5208
5219
|
funnel channels <ch> connectors set <conn> [--poll-interval=N] # gh`;
|
|
5220
|
+
const channelsConnectorsSetHelpHandler = factory.createHandlers((c) => c.text(help$14));
|
|
5221
|
+
//#endregion
|
|
5222
|
+
//#region lib/cli/routes/channels.$channel.connectors.set.$connector.ts
|
|
5209
5223
|
const channelsConnectorsSetHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5210
5224
|
channel: z.string(),
|
|
5211
5225
|
connector: z.string()
|
|
@@ -5213,10 +5227,10 @@ const channelsConnectorsSetHandler = factory.createHandlers(zValidator$1("param"
|
|
|
5213
5227
|
"bot-token": z.string().optional(),
|
|
5214
5228
|
"app-token": z.string().optional(),
|
|
5215
5229
|
"poll-interval": z.coerce.number().int().positive().optional()
|
|
5216
|
-
}).passthrough()
|
|
5230
|
+
}).passthrough()), async (c) => {
|
|
5217
5231
|
const param = c.req.valid("param");
|
|
5218
5232
|
const query = c.req.valid("query");
|
|
5219
|
-
const funnel = c.
|
|
5233
|
+
const funnel = c.env.funnel;
|
|
5220
5234
|
const existing = funnel.channels.getConnector(param.channel, param.connector);
|
|
5221
5235
|
if (!existing) throw new HTTPException(404, { message: `connector "${param.connector}" not found in channel "${param.channel}"` });
|
|
5222
5236
|
if (existing.type === "slack") funnel.channels.updateSlackConnector(param.channel, param.connector, {
|
|
@@ -5236,27 +5250,36 @@ const channelsConnectorsShowHandler = factory.createHandlers(zValidator$1("param
|
|
|
5236
5250
|
|
|
5237
5251
|
usage: funnel channels <channel> connectors show <connector>`), (c) => {
|
|
5238
5252
|
const param = c.req.valid("param");
|
|
5239
|
-
const connector = c.
|
|
5253
|
+
const connector = c.env.funnel.channels.getConnector(param.channel, param.connector);
|
|
5240
5254
|
if (!connector) throw new HTTPException(404, { message: `connector "${param.connector}" not found in channel "${param.channel}"` });
|
|
5241
5255
|
return c.text(JSON.stringify(connector, null, 2));
|
|
5242
5256
|
});
|
|
5243
5257
|
//#endregion
|
|
5244
|
-
//#region lib/cli/routes/channels.$channel.connectors
|
|
5245
|
-
const
|
|
5258
|
+
//#region lib/cli/routes/channels.$channel.connectors.rename.ts
|
|
5259
|
+
const help$13 = `funnel channels <channel> connectors rename <connector> <new-name>
|
|
5246
5260
|
|
|
5247
5261
|
usage: funnel channels <channel> connectors rename <connector> <new-name>`;
|
|
5262
|
+
const channelsConnectorsRenameHelpHandler = factory.createHandlers((c) => c.text(help$13));
|
|
5263
|
+
//#endregion
|
|
5264
|
+
//#region lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts
|
|
5248
5265
|
const channelsConnectorsRenameHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5249
5266
|
channel: z.string(),
|
|
5250
5267
|
connector: z.string(),
|
|
5251
5268
|
newName: z.string()
|
|
5252
|
-
})), zValidator$1("query", z.object({})
|
|
5269
|
+
})), zValidator$1("query", z.object({})), async (c) => {
|
|
5253
5270
|
const param = c.req.valid("param");
|
|
5254
|
-
const funnel = c.
|
|
5271
|
+
const funnel = c.env.funnel;
|
|
5255
5272
|
await funnel.listeners.stop(param.channel, param.connector);
|
|
5256
5273
|
funnel.channels.renameConnector(param.channel, param.connector, param.newName);
|
|
5257
5274
|
await funnel.listeners.start(param.channel, param.newName);
|
|
5258
5275
|
return c.text(`renamed connector "${param.connector}" to "${param.newName}"`);
|
|
5259
5276
|
});
|
|
5277
|
+
//#endregion
|
|
5278
|
+
//#region lib/cli/routes/channels.$channel.connectors.$connector.rename.ts
|
|
5279
|
+
const help$12 = `funnel channels <channel> connectors rename <connector> <new-name>
|
|
5280
|
+
|
|
5281
|
+
usage: funnel channels <channel> connectors rename <connector> <new-name>`;
|
|
5282
|
+
const channelsConnectorRenameHelpHandler = factory.createHandlers((c) => c.text(help$12));
|
|
5260
5283
|
const channelsConnectorsRequestHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5261
5284
|
channel: z.string(),
|
|
5262
5285
|
connector: z.string()
|
|
@@ -5265,7 +5288,7 @@ const channelsConnectorsRequestHandler = factory.createHandlers(zValidator$1("pa
|
|
|
5265
5288
|
usage: funnel channels <channel> connectors <connector> request --method=<api.method> [--key=value ...]`), async (c) => {
|
|
5266
5289
|
const param = c.req.valid("param");
|
|
5267
5290
|
const query = c.req.valid("query");
|
|
5268
|
-
const funnel = c.
|
|
5291
|
+
const funnel = c.env.funnel;
|
|
5269
5292
|
const passthrough = {};
|
|
5270
5293
|
for (const [k, v] of new URL(c.req.url).searchParams) {
|
|
5271
5294
|
if (k === "method") continue;
|
|
@@ -5285,15 +5308,18 @@ const channelsConnectorsSchedulesGroupHandler = factory.createHandlers(zValidato
|
|
|
5285
5308
|
|
|
5286
5309
|
usage: funnel channels <ch> connectors <conn> schedules`), (c) => {
|
|
5287
5310
|
const param = c.req.valid("param");
|
|
5288
|
-
const entries = c.
|
|
5311
|
+
const entries = c.env.funnel.channels.listScheduleEntries(param.channel, param.connector);
|
|
5289
5312
|
if (entries.length === 0) return c.text("no schedule entries");
|
|
5290
5313
|
return c.text(entries.map((e) => `${e.id}\t${e.cron}\t${e.enabled ? "on" : "off"}\t${e.prompt}`).join("\n"));
|
|
5291
5314
|
});
|
|
5292
5315
|
//#endregion
|
|
5293
|
-
//#region lib/cli/routes/channels.$channel.connectors.$connector.schedules.add
|
|
5294
|
-
const
|
|
5316
|
+
//#region lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.ts
|
|
5317
|
+
const help$11 = `funnel channels <ch> connectors <conn> schedules add <id> — add a schedule entry
|
|
5295
5318
|
|
|
5296
5319
|
usage: funnel channels <ch> connectors <conn> schedules add <id> --cron="*/5 * * * *" --prompt="..." [--enabled=true] [--catchup-policy=latest|all|skip]`;
|
|
5320
|
+
const channelsConnectorSchedulesAddHelpHandler = factory.createHandlers((c) => c.text(help$11));
|
|
5321
|
+
//#endregion
|
|
5322
|
+
//#region lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts
|
|
5297
5323
|
const channelsConnectorsSchedulesAddHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5298
5324
|
channel: z.string(),
|
|
5299
5325
|
connector: z.string(),
|
|
@@ -5303,10 +5329,10 @@ const channelsConnectorsSchedulesAddHandler = factory.createHandlers(zValidator$
|
|
|
5303
5329
|
prompt: z.string(),
|
|
5304
5330
|
enabled: z.coerce.boolean().optional(),
|
|
5305
5331
|
"catchup-policy": scheduleCatchupPolicySchema.optional()
|
|
5306
|
-
})
|
|
5332
|
+
})), async (c) => {
|
|
5307
5333
|
const param = c.req.valid("param");
|
|
5308
5334
|
const query = c.req.valid("query");
|
|
5309
|
-
const funnel = c.
|
|
5335
|
+
const funnel = c.env.funnel;
|
|
5310
5336
|
const entry = funnel.channels.addScheduleEntry(param.channel, param.connector, {
|
|
5311
5337
|
id: param.id,
|
|
5312
5338
|
cron: query.cron,
|
|
@@ -5318,24 +5344,27 @@ const channelsConnectorsSchedulesAddHandler = factory.createHandlers(zValidator$
|
|
|
5318
5344
|
return c.text(`added schedule entry "${entry.id}"`);
|
|
5319
5345
|
});
|
|
5320
5346
|
//#endregion
|
|
5321
|
-
//#region lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove
|
|
5322
|
-
const
|
|
5347
|
+
//#region lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.ts
|
|
5348
|
+
const help$10 = `funnel channels <ch> connectors <conn> schedules remove <id>
|
|
5323
5349
|
|
|
5324
5350
|
usage: funnel channels <ch> connectors <conn> schedules remove <id>`;
|
|
5351
|
+
const channelsConnectorSchedulesRemoveHelpHandler = factory.createHandlers((c) => c.text(help$10));
|
|
5352
|
+
//#endregion
|
|
5353
|
+
//#region lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts
|
|
5325
5354
|
const channelsConnectorsSchedulesRemoveHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5326
5355
|
channel: z.string(),
|
|
5327
5356
|
connector: z.string(),
|
|
5328
5357
|
id: z.string()
|
|
5329
|
-
})), zValidator$1("query", z.object({})
|
|
5358
|
+
})), zValidator$1("query", z.object({})), async (c) => {
|
|
5330
5359
|
const param = c.req.valid("param");
|
|
5331
|
-
const funnel = c.
|
|
5360
|
+
const funnel = c.env.funnel;
|
|
5332
5361
|
funnel.channels.removeScheduleEntry(param.channel, param.connector, param.id);
|
|
5333
5362
|
await funnel.listeners.restart(param.channel, param.connector);
|
|
5334
5363
|
return c.text(`removed schedule entry "${param.id}"`);
|
|
5335
5364
|
});
|
|
5336
5365
|
//#endregion
|
|
5337
|
-
//#region lib/cli/routes/channels
|
|
5338
|
-
const
|
|
5366
|
+
//#region lib/cli/routes/channels.publish.ts
|
|
5367
|
+
const help$9 = `funnel channels <channel> publish — push arbitrary content into a channel
|
|
5339
5368
|
|
|
5340
5369
|
usage: funnel channels <channel> publish --content="<text>" [--connector=<name>] [--meta-<key>=<value> ...]
|
|
5341
5370
|
|
|
@@ -5343,14 +5372,17 @@ options:
|
|
|
5343
5372
|
--content Required. The event body delivered to subscribers.
|
|
5344
5373
|
--connector Optional. Stamp the event with a connector name (resolved to id when found).
|
|
5345
5374
|
--meta-<key> Optional. Repeatable. Added to meta. Example: --meta-source=cron`;
|
|
5375
|
+
const channelsPublishHelpHandler = factory.createHandlers((c) => c.text(help$9));
|
|
5376
|
+
//#endregion
|
|
5377
|
+
//#region lib/cli/routes/channels.$channel.publish.ts
|
|
5346
5378
|
const querySchema = z.object({
|
|
5347
5379
|
content: z.string().min(1, { message: "--content is required" }),
|
|
5348
5380
|
connector: z.string().min(1).optional()
|
|
5349
5381
|
}).passthrough();
|
|
5350
|
-
const channelsPublishHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", querySchema
|
|
5382
|
+
const channelsPublishHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", querySchema), async (c) => {
|
|
5351
5383
|
const param = c.req.valid("param");
|
|
5352
5384
|
const query = c.req.valid("query");
|
|
5353
|
-
const funnel = c.
|
|
5385
|
+
const funnel = c.env.funnel;
|
|
5354
5386
|
const meta = {};
|
|
5355
5387
|
for (const [k, v] of new URL(c.req.url).searchParams) if (k.startsWith("meta-")) meta[k.slice(5)] = v;
|
|
5356
5388
|
const result = await funnel.publisher.publish(param.channel, {
|
|
@@ -5363,28 +5395,42 @@ const channelsPublishHandler = factory.createHandlers(zValidator$1("param", z.ob
|
|
|
5363
5395
|
return c.text(`published (offset=${result.offset})`);
|
|
5364
5396
|
});
|
|
5365
5397
|
//#endregion
|
|
5366
|
-
//#region lib/cli/routes/channels.remove
|
|
5367
|
-
const
|
|
5398
|
+
//#region lib/cli/routes/channels.remove.ts
|
|
5399
|
+
const help$8 = `funnel channels remove — remove a channel
|
|
5368
5400
|
|
|
5369
5401
|
usage: funnel channels remove <name>`;
|
|
5370
|
-
const
|
|
5402
|
+
const channelsRemoveHelpHandler = factory.createHandlers((c) => c.text(help$8));
|
|
5403
|
+
//#endregion
|
|
5404
|
+
//#region lib/cli/routes/channels.remove.$channel.ts
|
|
5405
|
+
const channelsRemoveHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", z.object({})), (c) => {
|
|
5371
5406
|
const param = c.req.valid("param");
|
|
5372
|
-
c.
|
|
5407
|
+
c.env.funnel.channels.remove(param.channel);
|
|
5373
5408
|
return c.text(`removed channel "${param.channel}"`);
|
|
5374
5409
|
});
|
|
5375
5410
|
//#endregion
|
|
5376
|
-
//#region lib/cli/routes/channels
|
|
5377
|
-
const
|
|
5411
|
+
//#region lib/cli/routes/channels.rename.ts
|
|
5412
|
+
const help$7 = `funnel channels rename — rename a channel
|
|
5413
|
+
|
|
5414
|
+
usage:
|
|
5415
|
+
funnel channels rename <old> <new>
|
|
5416
|
+
funnel channels <old> rename <new>`;
|
|
5417
|
+
const channelsRenameHelpHandler = factory.createHandlers((c) => c.text(help$7));
|
|
5418
|
+
//#endregion
|
|
5419
|
+
//#region lib/cli/routes/channels.$channel.rename.ts
|
|
5420
|
+
const help$6 = `funnel channels rename — rename a channel
|
|
5378
5421
|
|
|
5379
5422
|
usage:
|
|
5380
5423
|
funnel channels rename <old> <new>
|
|
5381
5424
|
funnel channels <old> rename <new>`;
|
|
5425
|
+
const channelsChannelRenameHelpHandler = factory.createHandlers((c) => c.text(help$6));
|
|
5426
|
+
//#endregion
|
|
5427
|
+
//#region lib/cli/routes/channels.$channel.rename.$newName.ts
|
|
5382
5428
|
const channelsRenameHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
5383
5429
|
channel: z.string(),
|
|
5384
5430
|
newName: z.string()
|
|
5385
|
-
})), zValidator$1("query", z.object({})
|
|
5431
|
+
})), zValidator$1("query", z.object({})), (c) => {
|
|
5386
5432
|
const param = c.req.valid("param");
|
|
5387
|
-
c.
|
|
5433
|
+
c.env.funnel.channels.rename(param.channel, param.newName);
|
|
5388
5434
|
return c.text(`renamed channel "${param.channel}" to "${param.newName}"`);
|
|
5389
5435
|
});
|
|
5390
5436
|
const channelsSetDeliveryHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
@@ -5401,12 +5447,12 @@ modes:
|
|
|
5401
5447
|
tap=all clients (TUI dashboard, debugging) always receive regardless of mode.
|
|
5402
5448
|
`), (c) => {
|
|
5403
5449
|
const param = c.req.valid("param");
|
|
5404
|
-
c.
|
|
5450
|
+
c.env.funnel.channels.setDelivery(param.channel, param.mode);
|
|
5405
5451
|
return c.text(`channel "${param.channel}" delivery set to ${param.mode}`);
|
|
5406
5452
|
});
|
|
5407
5453
|
const channelsShowHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", z.object({}), `funnel channels <name> — show channel details`), (c) => {
|
|
5408
5454
|
const param = c.req.valid("param");
|
|
5409
|
-
const channel = c.
|
|
5455
|
+
const channel = c.env.funnel.channels.get(param.channel);
|
|
5410
5456
|
if (!channel) throw new HTTPException(404, { message: `channel "${param.channel}" not found` });
|
|
5411
5457
|
const connectorLines = channel.connectors.length ? channel.connectors.map((c) => ` - ${c.name} (${c.type}, id: ${c.id})`) : [" (none)"];
|
|
5412
5458
|
const lines = [
|
|
@@ -5444,7 +5490,7 @@ examples:
|
|
|
5444
5490
|
funnel channels prod-inbox connectors add prod-slack --type=slack --bot-token=xoxb-... --app-token=xapp-...
|
|
5445
5491
|
funnel channels prod-inbox`), (c) => {
|
|
5446
5492
|
const query = c.req.valid("query");
|
|
5447
|
-
const channels = c.
|
|
5493
|
+
const channels = c.env.funnel.channels.list();
|
|
5448
5494
|
if (query.json === "true" || query.json === "") return c.json(channels.map((ch) => ({
|
|
5449
5495
|
id: ch.id,
|
|
5450
5496
|
name: ch.name,
|
|
@@ -5464,6 +5510,106 @@ examples:
|
|
|
5464
5510
|
return c.text(lines.join("\n"));
|
|
5465
5511
|
});
|
|
5466
5512
|
//#endregion
|
|
5513
|
+
//#region lib/cli/routes/channels.validate.ts
|
|
5514
|
+
const help$5 = `funnel channels <channel> validate — check connector configuration
|
|
5515
|
+
|
|
5516
|
+
usage: funnel channels <channel> validate [--json]
|
|
5517
|
+
|
|
5518
|
+
options:
|
|
5519
|
+
--json output as JSON
|
|
5520
|
+
|
|
5521
|
+
Checks that each connector has the required tokens and fields set.
|
|
5522
|
+
Does not make any network calls — static config check only.
|
|
5523
|
+
|
|
5524
|
+
examples:
|
|
5525
|
+
funnel channels open-karte validate
|
|
5526
|
+
funnel channels open-karte validate --json`;
|
|
5527
|
+
const channelsValidateHelpHandler = factory.createHandlers((c) => c.text(help$5));
|
|
5528
|
+
//#endregion
|
|
5529
|
+
//#region lib/cli/routes/channels.$channel.validate.ts
|
|
5530
|
+
const validateConnector = (connector) => {
|
|
5531
|
+
const issues = [];
|
|
5532
|
+
if (connector.type === "slack") {
|
|
5533
|
+
if (!(connector.botToken || connector.botTokenEnv)) issues.push({
|
|
5534
|
+
connector: connector.name,
|
|
5535
|
+
field: "botToken",
|
|
5536
|
+
message: "missing botToken (xoxb-...) or botTokenEnv"
|
|
5537
|
+
});
|
|
5538
|
+
if (!(connector.appToken || connector.appTokenEnv)) issues.push({
|
|
5539
|
+
connector: connector.name,
|
|
5540
|
+
field: "appToken",
|
|
5541
|
+
message: "missing appToken (xapp-...) or appTokenEnv"
|
|
5542
|
+
});
|
|
5543
|
+
if (connector.botToken && typeof connector.botToken === "string" && !connector.botToken.startsWith("xoxb-")) issues.push({
|
|
5544
|
+
connector: connector.name,
|
|
5545
|
+
field: "botToken",
|
|
5546
|
+
message: `botToken must start with xoxb- (got: ${connector.botToken.slice(0, 8)}...)`
|
|
5547
|
+
});
|
|
5548
|
+
if (connector.appToken && typeof connector.appToken === "string" && !connector.appToken.startsWith("xapp-")) issues.push({
|
|
5549
|
+
connector: connector.name,
|
|
5550
|
+
field: "appToken",
|
|
5551
|
+
message: `appToken must start with xapp- (got: ${connector.appToken.slice(0, 8)}...)`
|
|
5552
|
+
});
|
|
5553
|
+
}
|
|
5554
|
+
if (connector.type === "gh") {
|
|
5555
|
+
if (!(connector.token || connector.tokenEnv)) issues.push({
|
|
5556
|
+
connector: connector.name,
|
|
5557
|
+
field: "token",
|
|
5558
|
+
message: "missing token or tokenEnv for GitHub connector"
|
|
5559
|
+
});
|
|
5560
|
+
if (!connector.repo) issues.push({
|
|
5561
|
+
connector: connector.name,
|
|
5562
|
+
field: "repo",
|
|
5563
|
+
message: "missing repo (expected owner/repo format)"
|
|
5564
|
+
});
|
|
5565
|
+
}
|
|
5566
|
+
if (connector.type === "discord") {
|
|
5567
|
+
if (!(connector.botToken || connector.botTokenEnv)) issues.push({
|
|
5568
|
+
connector: connector.name,
|
|
5569
|
+
field: "botToken",
|
|
5570
|
+
message: "missing botToken or botTokenEnv for Discord connector"
|
|
5571
|
+
});
|
|
5572
|
+
}
|
|
5573
|
+
return issues;
|
|
5574
|
+
};
|
|
5575
|
+
const channelsValidateHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", z.object({ json: z.enum([
|
|
5576
|
+
"true",
|
|
5577
|
+
"false",
|
|
5578
|
+
""
|
|
5579
|
+
]).optional() })), (c) => {
|
|
5580
|
+
const param = c.req.valid("param");
|
|
5581
|
+
const query = c.req.valid("query");
|
|
5582
|
+
const funnel = c.env.funnel;
|
|
5583
|
+
const isJson = query.json === "true" || query.json === "";
|
|
5584
|
+
const channel = funnel.channels.get(param.channel);
|
|
5585
|
+
if (!channel) throw new HTTPException(404, { message: `channel "${param.channel}" not found` });
|
|
5586
|
+
if (channel.connectors.length === 0) {
|
|
5587
|
+
if (isJson) return c.json({
|
|
5588
|
+
channel: channel.name,
|
|
5589
|
+
valid: false,
|
|
5590
|
+
issues: [{
|
|
5591
|
+
connector: "(none)",
|
|
5592
|
+
field: "connectors",
|
|
5593
|
+
message: "no connectors configured"
|
|
5594
|
+
}]
|
|
5595
|
+
});
|
|
5596
|
+
return c.text(`⚠ ${channel.name}: no connectors configured`);
|
|
5597
|
+
}
|
|
5598
|
+
const allIssues = [];
|
|
5599
|
+
for (const connector of channel.connectors) {
|
|
5600
|
+
const issues = validateConnector(connector);
|
|
5601
|
+
allIssues.push(...issues);
|
|
5602
|
+
}
|
|
5603
|
+
if (isJson) return c.json({
|
|
5604
|
+
channel: channel.name,
|
|
5605
|
+
valid: allIssues.length === 0,
|
|
5606
|
+
issues: allIssues
|
|
5607
|
+
});
|
|
5608
|
+
if (allIssues.length === 0) return c.text(`✓ ${channel.name}: all connectors valid`);
|
|
5609
|
+
const lines = allIssues.map((issue) => `✗ ${channel.name}/${issue.connector}: ${issue.message}`);
|
|
5610
|
+
return c.text(lines.join("\n"));
|
|
5611
|
+
});
|
|
5612
|
+
//#endregion
|
|
5467
5613
|
//#region lib/cli/routes/claude.ts
|
|
5468
5614
|
const claudeHelp = `funnel claude — launch Claude Code
|
|
5469
5615
|
|
|
@@ -5495,7 +5641,7 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
5495
5641
|
channel: z.string().optional()
|
|
5496
5642
|
}).passthrough(), claudeHelp), async (c) => {
|
|
5497
5643
|
const query = c.req.valid("query");
|
|
5498
|
-
const funnel = c.
|
|
5644
|
+
const funnel = c.env.funnel;
|
|
5499
5645
|
const userArgs = queryToCliArgs(c.req.url, RESERVED_KEYS$1);
|
|
5500
5646
|
if (query.channel && !query.profile) {
|
|
5501
5647
|
const exitCode = await funnel.claude.launch({
|
|
@@ -5548,6 +5694,71 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
5548
5694
|
process.exit(exitCode);
|
|
5549
5695
|
});
|
|
5550
5696
|
//#endregion
|
|
5697
|
+
//#region lib/cli/routes/debug-row.ts
|
|
5698
|
+
const stringOrNull = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
5699
|
+
const numberOrNull = (value) => typeof value === "number" ? value : null;
|
|
5700
|
+
const stringOr = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
5701
|
+
/**
|
|
5702
|
+
* Parse a payload string as a JSON object. Returns null for non-strings,
|
|
5703
|
+
* malformed JSON, or any non-object JSON (arrays, primitives) — the callers
|
|
5704
|
+
* only ever want the object form.
|
|
5705
|
+
*/
|
|
5706
|
+
const parsePayloadObject = (payload) => {
|
|
5707
|
+
if (payload === null) return null;
|
|
5708
|
+
try {
|
|
5709
|
+
const parsed = JSON.parse(payload);
|
|
5710
|
+
if (isStringKeyedObject(parsed)) return parsed;
|
|
5711
|
+
} catch {
|
|
5712
|
+
return null;
|
|
5713
|
+
}
|
|
5714
|
+
return null;
|
|
5715
|
+
};
|
|
5716
|
+
const isStringKeyedObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5717
|
+
const truncate = (text, max) => text.length <= max ? text : `${text.slice(0, max)}…`;
|
|
5718
|
+
/**
|
|
5719
|
+
* A short human preview of a payload: the `text` field when the payload is a
|
|
5720
|
+
* JSON object that has one, otherwise the raw payload, both truncated.
|
|
5721
|
+
*/
|
|
5722
|
+
const previewOf = (payload) => {
|
|
5723
|
+
if (typeof payload !== "string" || payload.length === 0) return null;
|
|
5724
|
+
const parsed = parsePayloadObject(payload);
|
|
5725
|
+
if (parsed !== null && "text" in parsed) return truncate(String(parsed.text), 60);
|
|
5726
|
+
return truncate(payload, 60);
|
|
5727
|
+
};
|
|
5728
|
+
/** Narrow one processed-table row into a `DebugEvent`. */
|
|
5729
|
+
const toDebugEvent = (row) => {
|
|
5730
|
+
const payload = stringOrNull(row.payload);
|
|
5731
|
+
return {
|
|
5732
|
+
seq: numberOrNull(row.seq),
|
|
5733
|
+
ts: numberOrNull(row.ts),
|
|
5734
|
+
type: stringOr(row.type, "?"),
|
|
5735
|
+
outcome: stringOr(row.outcome, "?"),
|
|
5736
|
+
eventId: stringOrNull(row.event_id),
|
|
5737
|
+
payload,
|
|
5738
|
+
payloadParsed: parsePayloadObject(payload),
|
|
5739
|
+
preview: previewOf(row.payload)
|
|
5740
|
+
};
|
|
5741
|
+
};
|
|
5742
|
+
/** Narrow one connection-table row into a `DebugConnectionError`. */
|
|
5743
|
+
const toDebugConnectionError = (row) => ({
|
|
5744
|
+
seq: numberOrNull(row.seq),
|
|
5745
|
+
ts: numberOrNull(row.ts),
|
|
5746
|
+
type: stringOr(row.type, "?"),
|
|
5747
|
+
status: stringOr(row.status, "?"),
|
|
5748
|
+
detail: stringOrNull(row.detail)
|
|
5749
|
+
});
|
|
5750
|
+
/**
|
|
5751
|
+
* Open a reader, run one query, and always close — the shared shape behind
|
|
5752
|
+
* every diagnostic lookup. Returns the rows or the reader's `Error`.
|
|
5753
|
+
*/
|
|
5754
|
+
const queryRows = (reader, sql, params) => {
|
|
5755
|
+
try {
|
|
5756
|
+
return reader.query(sql, params);
|
|
5757
|
+
} finally {
|
|
5758
|
+
reader.close();
|
|
5759
|
+
}
|
|
5760
|
+
};
|
|
5761
|
+
//#endregion
|
|
5551
5762
|
//#region lib/cli/routes/debug.ts
|
|
5552
5763
|
const debugHelp = `funnel debug — diagnose why Claude is not receiving events
|
|
5553
5764
|
|
|
@@ -5639,17 +5850,6 @@ const formatTs = (epochMs) => {
|
|
|
5639
5850
|
if (typeof epochMs !== "number") return "?";
|
|
5640
5851
|
return new Date(epochMs).toISOString().slice(11, 19);
|
|
5641
5852
|
};
|
|
5642
|
-
const truncate = (text, max) => text.length <= max ? text : `${text.slice(0, max)}…`;
|
|
5643
|
-
const extractPreview = (payload) => {
|
|
5644
|
-
if (typeof payload !== "string" || payload.length === 0) return null;
|
|
5645
|
-
try {
|
|
5646
|
-
const parsed = JSON.parse(payload);
|
|
5647
|
-
if (parsed !== null && typeof parsed === "object" && "text" in parsed) return truncate(String(parsed.text), 60);
|
|
5648
|
-
} catch {
|
|
5649
|
-
return truncate(payload, 60);
|
|
5650
|
-
}
|
|
5651
|
-
return truncate(payload, 60);
|
|
5652
|
-
};
|
|
5653
5853
|
const buildDiagnosis = (report) => {
|
|
5654
5854
|
const rootCause = (report.connectionErrors[report.connectionErrors.length - 1] ?? null)?.detail ?? null;
|
|
5655
5855
|
if (!report.gateway.running) return {
|
|
@@ -5762,6 +5962,16 @@ const resolveStoreOrNull = () => {
|
|
|
5762
5962
|
connectionPath
|
|
5763
5963
|
};
|
|
5764
5964
|
};
|
|
5965
|
+
/**
|
|
5966
|
+
* Resolve the connector name for a connector id on a channel, used to attribute
|
|
5967
|
+
* a replayed event back to its source connector. Returns undefined when the id
|
|
5968
|
+
* is null or no longer present (connectors can be removed after an event was
|
|
5969
|
+
* logged).
|
|
5970
|
+
*/
|
|
5971
|
+
const connectorOf = (channel, connectorId) => {
|
|
5972
|
+
if (connectorId === null) return void 0;
|
|
5973
|
+
return channel.connectors?.find((connector) => connector.id === connectorId)?.name;
|
|
5974
|
+
};
|
|
5765
5975
|
const resolveChannelId = (channels, channelName) => {
|
|
5766
5976
|
if (channelName) {
|
|
5767
5977
|
const match = channels.find((ch) => ch.name === channelName);
|
|
@@ -5799,7 +6009,7 @@ const debugEventsHandler = factory.createHandlers(zValidator$1("query", z.object
|
|
|
5799
6009
|
]).optional()
|
|
5800
6010
|
}), debugEventsHelp), async (c) => {
|
|
5801
6011
|
const query = c.req.valid("query");
|
|
5802
|
-
const channels = c.
|
|
6012
|
+
const channels = c.env.funnel.channels.list();
|
|
5803
6013
|
const isJson = query.json === "true" || query.json === "";
|
|
5804
6014
|
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
5805
6015
|
const store = resolveStoreOrNull();
|
|
@@ -5828,34 +6038,9 @@ const debugEventsHandler = factory.createHandlers(zValidator$1("query", z.object
|
|
|
5828
6038
|
}
|
|
5829
6039
|
const channel = resolved.channel;
|
|
5830
6040
|
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
5831
|
-
const rows = (
|
|
5832
|
-
try {
|
|
5833
|
-
if (channel) return reader.query("SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT ?", [channel.id, limit]);
|
|
5834
|
-
return reader.query("SELECT seq, ts, type, outcome, payload FROM processed ORDER BY seq DESC LIMIT ?", [limit]);
|
|
5835
|
-
} finally {
|
|
5836
|
-
reader.close();
|
|
5837
|
-
}
|
|
5838
|
-
})();
|
|
6041
|
+
const rows = channel ? queryRows(reader, "SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT ?", [channel.id, limit]) : queryRows(reader, "SELECT seq, ts, type, outcome, payload FROM processed ORDER BY seq DESC LIMIT ?", [limit]);
|
|
5839
6042
|
if (rows instanceof Error) return c.text(`error: ${rows.message}`);
|
|
5840
|
-
const events =
|
|
5841
|
-
const rawPayload = typeof row.payload === "string" ? row.payload : null;
|
|
5842
|
-
let payloadParsed = null;
|
|
5843
|
-
if (rawPayload) try {
|
|
5844
|
-
const parsed = JSON.parse(rawPayload);
|
|
5845
|
-
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) payloadParsed = parsed;
|
|
5846
|
-
} catch {
|
|
5847
|
-
payloadParsed = null;
|
|
5848
|
-
}
|
|
5849
|
-
return {
|
|
5850
|
-
seq: typeof row.seq === "number" ? row.seq : null,
|
|
5851
|
-
ts: typeof row.ts === "number" ? row.ts : null,
|
|
5852
|
-
type: typeof row.type === "string" ? row.type : "?",
|
|
5853
|
-
outcome: typeof row.outcome === "string" ? row.outcome : "?",
|
|
5854
|
-
payload: rawPayload,
|
|
5855
|
-
payloadParsed,
|
|
5856
|
-
preview: extractPreview(row.payload)
|
|
5857
|
-
};
|
|
5858
|
-
});
|
|
6043
|
+
const events = rows.reverse().map(toDebugEvent);
|
|
5859
6044
|
if (isJson) return c.json(events);
|
|
5860
6045
|
if (events.length === 0) return c.text("no events recorded");
|
|
5861
6046
|
const lines = events.map((ev) => {
|
|
@@ -5877,7 +6062,7 @@ const debugDroppedHandler = factory.createHandlers(zValidator$1("query", z.objec
|
|
|
5877
6062
|
]).optional()
|
|
5878
6063
|
}), debugDroppedHelp), async (c) => {
|
|
5879
6064
|
const query = c.req.valid("query");
|
|
5880
|
-
const channels = c.
|
|
6065
|
+
const channels = c.env.funnel.channels.list();
|
|
5881
6066
|
const isJson = query.json === "true" || query.json === "";
|
|
5882
6067
|
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
5883
6068
|
const store = resolveStoreOrNull();
|
|
@@ -5906,39 +6091,13 @@ const debugDroppedHandler = factory.createHandlers(zValidator$1("query", z.objec
|
|
|
5906
6091
|
}
|
|
5907
6092
|
const channel = resolvedDropped.channel;
|
|
5908
6093
|
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
5909
|
-
const rows = (
|
|
5910
|
-
try {
|
|
5911
|
-
if (channel) return reader.query("SELECT p.seq, p.ts, p.type, p.outcome, p.payload, p.event_id FROM processed p WHERE p.channel_id = ? AND p.outcome LIKE 'skip:%' ORDER BY p.seq DESC LIMIT ?", [channel.id, limit]);
|
|
5912
|
-
return reader.query("SELECT seq, ts, type, outcome, payload, event_id FROM processed WHERE outcome LIKE 'skip:%' ORDER BY seq DESC LIMIT ?", [limit]);
|
|
5913
|
-
} finally {
|
|
5914
|
-
reader.close();
|
|
5915
|
-
}
|
|
5916
|
-
})();
|
|
6094
|
+
const rows = channel ? queryRows(reader, "SELECT p.seq, p.ts, p.type, p.outcome, p.payload, p.event_id FROM processed p WHERE p.channel_id = ? AND p.outcome LIKE 'skip:%' ORDER BY p.seq DESC LIMIT ?", [channel.id, limit]) : queryRows(reader, "SELECT seq, ts, type, outcome, payload, event_id FROM processed WHERE outcome LIKE 'skip:%' ORDER BY seq DESC LIMIT ?", [limit]);
|
|
5917
6095
|
if (rows instanceof Error) return c.text(`error: ${rows.message}`);
|
|
5918
|
-
const events =
|
|
5919
|
-
const rawPayload = typeof row.payload === "string" ? row.payload : null;
|
|
5920
|
-
let payloadParsed = null;
|
|
5921
|
-
if (rawPayload) try {
|
|
5922
|
-
const parsed = JSON.parse(rawPayload);
|
|
5923
|
-
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) payloadParsed = parsed;
|
|
5924
|
-
} catch {
|
|
5925
|
-
payloadParsed = null;
|
|
5926
|
-
}
|
|
5927
|
-
return {
|
|
5928
|
-
seq: typeof row.seq === "number" ? row.seq : null,
|
|
5929
|
-
ts: typeof row.ts === "number" ? row.ts : null,
|
|
5930
|
-
type: typeof row.type === "string" ? row.type : "?",
|
|
5931
|
-
outcome: typeof row.outcome === "string" ? row.outcome : "?",
|
|
5932
|
-
event_id: typeof row.event_id === "string" ? row.event_id : null,
|
|
5933
|
-
payload: rawPayload,
|
|
5934
|
-
payloadParsed,
|
|
5935
|
-
preview: extractPreview(row.payload)
|
|
5936
|
-
};
|
|
5937
|
-
});
|
|
6096
|
+
const events = rows.reverse().map(toDebugEvent);
|
|
5938
6097
|
if (isJson) return c.json(events);
|
|
5939
6098
|
if (events.length === 0) return c.text("no dropped events recorded");
|
|
5940
6099
|
const lines = events.map((ev) => {
|
|
5941
|
-
return `${formatTs(ev.ts)} ${ev.type.padEnd(8)} ${ev.outcome.padEnd(20)}${ev.seq !== null ? ` seq=${ev.seq}` : ""}${ev.
|
|
6100
|
+
return `${formatTs(ev.ts)} ${ev.type.padEnd(8)} ${ev.outcome.padEnd(20)}${ev.seq !== null ? ` seq=${ev.seq}` : ""}${ev.eventId ? ` event_id=${ev.eventId.slice(0, 8)}` : ""}${ev.preview ? ` "${ev.preview}"` : ""}`;
|
|
5942
6101
|
});
|
|
5943
6102
|
return c.text(lines.join("\n"));
|
|
5944
6103
|
});
|
|
@@ -5952,7 +6111,7 @@ const debugErrorsHandler = factory.createHandlers(zValidator$1("query", z.object
|
|
|
5952
6111
|
]).optional()
|
|
5953
6112
|
}), debugErrorsHelp), async (c) => {
|
|
5954
6113
|
const query = c.req.valid("query");
|
|
5955
|
-
const channels = c.
|
|
6114
|
+
const channels = c.env.funnel.channels.list();
|
|
5956
6115
|
const isJson = query.json === "true" || query.json === "";
|
|
5957
6116
|
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
5958
6117
|
const store = resolveStoreOrNull();
|
|
@@ -5981,22 +6140,9 @@ const debugErrorsHandler = factory.createHandlers(zValidator$1("query", z.object
|
|
|
5981
6140
|
}
|
|
5982
6141
|
const channel = resolvedErrors.channel;
|
|
5983
6142
|
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
5984
|
-
const rows = (()
|
|
5985
|
-
try {
|
|
5986
|
-
if (channel) return reader.query("SELECT seq, ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT ?", [channel.id, limit]);
|
|
5987
|
-
return reader.query("SELECT seq, ts, type, status, detail FROM connection WHERE status IN ('auth-failed','error') ORDER BY seq DESC LIMIT ?", [limit]);
|
|
5988
|
-
} finally {
|
|
5989
|
-
reader.close();
|
|
5990
|
-
}
|
|
5991
|
-
})();
|
|
6143
|
+
const rows = channel ? queryRows(reader, "SELECT seq, ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT ?", [channel.id, limit]) : queryRows(reader, "SELECT seq, ts, type, status, detail FROM connection WHERE status IN ('auth-failed','error') ORDER BY seq DESC LIMIT ?", [limit]);
|
|
5992
6144
|
if (rows instanceof Error) return c.text(`error: ${rows.message}`);
|
|
5993
|
-
const errors =
|
|
5994
|
-
seq: typeof row.seq === "number" ? row.seq : null,
|
|
5995
|
-
ts: typeof row.ts === "number" ? row.ts : null,
|
|
5996
|
-
type: typeof row.type === "string" ? row.type : "?",
|
|
5997
|
-
status: typeof row.status === "string" ? row.status : "?",
|
|
5998
|
-
detail: typeof row.detail === "string" && row.detail.length > 0 ? row.detail : null
|
|
5999
|
-
}));
|
|
6145
|
+
const errors = rows.reverse().map(toDebugConnectionError);
|
|
6000
6146
|
if (isJson) return c.json(errors);
|
|
6001
6147
|
if (errors.length === 0) return c.text("no connection errors recorded");
|
|
6002
6148
|
const lines = errors.map((ev) => {
|
|
@@ -6032,50 +6178,13 @@ const buildChannelReport = async (targetChannel, gatewayStatus, gatewayBodyOrNul
|
|
|
6032
6178
|
baseReport.claudeClients = gatewayBodyOrNull.clients.filter((cl) => !cl.tapAll && cl.channelName === targetChannelName).length;
|
|
6033
6179
|
}
|
|
6034
6180
|
if (store) {
|
|
6035
|
-
const
|
|
6036
|
-
|
|
6037
|
-
try {
|
|
6038
|
-
return reader.query("SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT ?", [targetChannel.id, limit]);
|
|
6039
|
-
} finally {
|
|
6040
|
-
reader.close();
|
|
6041
|
-
}
|
|
6042
|
-
})();
|
|
6043
|
-
if (!(evRows instanceof Error)) baseReport.recentEvents = evRows.reverse().map((row) => {
|
|
6044
|
-
const rawPayload = typeof row.payload === "string" ? row.payload : null;
|
|
6045
|
-
let payloadParsed = null;
|
|
6046
|
-
if (rawPayload) try {
|
|
6047
|
-
const parsed = JSON.parse(rawPayload);
|
|
6048
|
-
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) payloadParsed = parsed;
|
|
6049
|
-
} catch {
|
|
6050
|
-
payloadParsed = null;
|
|
6051
|
-
}
|
|
6052
|
-
return {
|
|
6053
|
-
seq: typeof row.seq === "number" ? row.seq : null,
|
|
6054
|
-
ts: typeof row.ts === "number" ? row.ts : null,
|
|
6055
|
-
type: typeof row.type === "string" ? row.type : "?",
|
|
6056
|
-
outcome: typeof row.outcome === "string" ? row.outcome : "?",
|
|
6057
|
-
payload: rawPayload,
|
|
6058
|
-
payloadParsed,
|
|
6059
|
-
preview: extractPreview(row.payload)
|
|
6060
|
-
};
|
|
6061
|
-
});
|
|
6181
|
+
const evRows = queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT ?", [targetChannel.id, limit]);
|
|
6182
|
+
if (!(evRows instanceof Error)) baseReport.recentEvents = evRows.reverse().map(toDebugEvent);
|
|
6062
6183
|
const hasDeadListeners = baseReport.listeners.some((l) => !l.alive);
|
|
6063
6184
|
const hasListenerErrors = baseReport.listeners.some((l) => l.errors > 0);
|
|
6064
6185
|
if (hasDeadListeners || hasListenerErrors) {
|
|
6065
|
-
const
|
|
6066
|
-
|
|
6067
|
-
try {
|
|
6068
|
-
return errReader.query("SELECT ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT 3", [targetChannel.id]);
|
|
6069
|
-
} finally {
|
|
6070
|
-
errReader.close();
|
|
6071
|
-
}
|
|
6072
|
-
})();
|
|
6073
|
-
if (!(errRows instanceof Error)) baseReport.connectionErrors = errRows.reverse().map((row) => ({
|
|
6074
|
-
ts: typeof row.ts === "number" ? row.ts : null,
|
|
6075
|
-
type: typeof row.type === "string" ? row.type : "?",
|
|
6076
|
-
status: typeof row.status === "string" ? row.status : "?",
|
|
6077
|
-
detail: typeof row.detail === "string" && row.detail.length > 0 ? row.detail : null
|
|
6078
|
-
}));
|
|
6186
|
+
const errRows = queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT 3", [targetChannel.id]);
|
|
6187
|
+
if (!(errRows instanceof Error)) baseReport.connectionErrors = errRows.reverse().map(toDebugConnectionError);
|
|
6079
6188
|
}
|
|
6080
6189
|
}
|
|
6081
6190
|
return {
|
|
@@ -6098,7 +6207,7 @@ const debugHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
6098
6207
|
limit: z.string().optional()
|
|
6099
6208
|
}), debugHelp), async (c) => {
|
|
6100
6209
|
const query = c.req.valid("query");
|
|
6101
|
-
const funnel = c.
|
|
6210
|
+
const funnel = c.env.funnel;
|
|
6102
6211
|
const channels = funnel.channels.list();
|
|
6103
6212
|
const gatewayStatus = funnel.gateway.getStatus();
|
|
6104
6213
|
const isJson = query.json === "true" || query.json === "";
|
|
@@ -6193,7 +6302,7 @@ examples:
|
|
|
6193
6302
|
fnl debug replay --channel open-karte --seq 412
|
|
6194
6303
|
fnl debug replay --channel open-karte --json`), async (c) => {
|
|
6195
6304
|
const query = c.req.valid("query");
|
|
6196
|
-
const funnel = c.
|
|
6305
|
+
const funnel = c.env.funnel;
|
|
6197
6306
|
const channels = funnel.channels.list();
|
|
6198
6307
|
const isJson = query.json === "true" || query.json === "";
|
|
6199
6308
|
const resolved = resolveChannelId(channels, query.channel);
|
|
@@ -6221,15 +6330,7 @@ examples:
|
|
|
6221
6330
|
if (isJson) return c.json({ error: "no diagnostic store yet (start the gateway first)" });
|
|
6222
6331
|
return c.text("no diagnostic store yet (start the gateway first)");
|
|
6223
6332
|
}
|
|
6224
|
-
const
|
|
6225
|
-
const rows = (() => {
|
|
6226
|
-
try {
|
|
6227
|
-
if (query.seq) return reader.query("SELECT seq, event_id, type, payload, connector_id, channel_id FROM processed WHERE channel_id = ? AND seq = ? LIMIT 1", [targetChannel.id, Number(query.seq)]);
|
|
6228
|
-
return reader.query("SELECT seq, event_id, type, payload, connector_id, channel_id FROM processed WHERE channel_id = ? AND outcome LIKE 'emitted%' ORDER BY seq DESC LIMIT 1", [targetChannel.id]);
|
|
6229
|
-
} finally {
|
|
6230
|
-
reader.close();
|
|
6231
|
-
}
|
|
6232
|
-
})();
|
|
6333
|
+
const rows = query.seq ? queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT seq, event_id, type, payload, connector_id, channel_id FROM processed WHERE channel_id = ? AND seq = ? LIMIT 1", [targetChannel.id, Number(query.seq)]) : queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT seq, event_id, type, payload, connector_id, channel_id FROM processed WHERE channel_id = ? AND outcome LIKE 'emitted%' ORDER BY seq DESC LIMIT 1", [targetChannel.id]);
|
|
6233
6334
|
if (rows instanceof Error) {
|
|
6234
6335
|
if (isJson) return c.json({ error: rows.message });
|
|
6235
6336
|
return c.text(`error: ${rows.message}`);
|
|
@@ -6244,24 +6345,15 @@ examples:
|
|
|
6244
6345
|
const connectorId = typeof firstRow.connector_id === "string" ? firstRow.connector_id : null;
|
|
6245
6346
|
let content = typeof firstRow.payload === "string" ? firstRow.payload : null;
|
|
6246
6347
|
if ((!content || content.length === 0) && eventId) {
|
|
6247
|
-
const
|
|
6248
|
-
const
|
|
6249
|
-
|
|
6250
|
-
return rawReader.query("SELECT payload FROM raw WHERE event_id = ? LIMIT 1", [eventId]);
|
|
6251
|
-
} finally {
|
|
6252
|
-
rawReader.close();
|
|
6253
|
-
}
|
|
6254
|
-
})();
|
|
6255
|
-
if (!(rawRows instanceof Error) && rawRows[0]) {
|
|
6256
|
-
const rawRow = rawRows[0];
|
|
6257
|
-
content = typeof rawRow.payload === "string" ? rawRow.payload : null;
|
|
6258
|
-
}
|
|
6348
|
+
const rawRows = queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT payload FROM raw WHERE event_id = ? LIMIT 1", [eventId]);
|
|
6349
|
+
const rawRow = rawRows instanceof Error ? null : rawRows[0];
|
|
6350
|
+
if (rawRow) content = typeof rawRow.payload === "string" ? rawRow.payload : null;
|
|
6259
6351
|
}
|
|
6260
6352
|
if (!content) {
|
|
6261
6353
|
if (isJson) return c.json({ error: "event has no payload to replay" });
|
|
6262
6354
|
return c.text("event has no payload to replay");
|
|
6263
6355
|
}
|
|
6264
|
-
const connectorName =
|
|
6356
|
+
const connectorName = connectorOf(targetChannel, connectorId);
|
|
6265
6357
|
const result = await funnel.publisher.publish(targetChannel.name, {
|
|
6266
6358
|
content,
|
|
6267
6359
|
connector: connectorName
|
|
@@ -6277,7 +6369,7 @@ examples:
|
|
|
6277
6369
|
if (isJson) return c.json({ error: result.reason });
|
|
6278
6370
|
return c.text(`error: ${result.reason}`);
|
|
6279
6371
|
}
|
|
6280
|
-
const preview =
|
|
6372
|
+
const preview = previewOf(content);
|
|
6281
6373
|
if (isJson) return c.json({
|
|
6282
6374
|
replayed: true,
|
|
6283
6375
|
seq,
|
|
@@ -6287,103 +6379,6 @@ examples:
|
|
|
6287
6379
|
return c.text(`replayed seq=${seq ?? "?"} → offset=${result.offset}${preview ? ` "${preview}"` : ""}`);
|
|
6288
6380
|
});
|
|
6289
6381
|
//#endregion
|
|
6290
|
-
//#region lib/cli/routes/channels.$channel.validate.ts
|
|
6291
|
-
const validateHelp = `funnel channels <channel> validate — check connector configuration
|
|
6292
|
-
|
|
6293
|
-
usage: funnel channels <channel> validate [--json]
|
|
6294
|
-
|
|
6295
|
-
options:
|
|
6296
|
-
--json output as JSON
|
|
6297
|
-
|
|
6298
|
-
Checks that each connector has the required tokens and fields set.
|
|
6299
|
-
Does not make any network calls — static config check only.
|
|
6300
|
-
|
|
6301
|
-
examples:
|
|
6302
|
-
funnel channels open-karte validate
|
|
6303
|
-
funnel channels open-karte validate --json`;
|
|
6304
|
-
const validateConnector = (connector) => {
|
|
6305
|
-
const issues = [];
|
|
6306
|
-
if (connector.type === "slack") {
|
|
6307
|
-
if (!(connector.botToken || connector.botTokenEnv)) issues.push({
|
|
6308
|
-
connector: connector.name,
|
|
6309
|
-
field: "botToken",
|
|
6310
|
-
message: "missing botToken (xoxb-...) or botTokenEnv"
|
|
6311
|
-
});
|
|
6312
|
-
if (!(connector.appToken || connector.appTokenEnv)) issues.push({
|
|
6313
|
-
connector: connector.name,
|
|
6314
|
-
field: "appToken",
|
|
6315
|
-
message: "missing appToken (xapp-...) or appTokenEnv"
|
|
6316
|
-
});
|
|
6317
|
-
if (connector.botToken && typeof connector.botToken === "string" && !connector.botToken.startsWith("xoxb-")) issues.push({
|
|
6318
|
-
connector: connector.name,
|
|
6319
|
-
field: "botToken",
|
|
6320
|
-
message: `botToken must start with xoxb- (got: ${connector.botToken.slice(0, 8)}...)`
|
|
6321
|
-
});
|
|
6322
|
-
if (connector.appToken && typeof connector.appToken === "string" && !connector.appToken.startsWith("xapp-")) issues.push({
|
|
6323
|
-
connector: connector.name,
|
|
6324
|
-
field: "appToken",
|
|
6325
|
-
message: `appToken must start with xapp- (got: ${connector.appToken.slice(0, 8)}...)`
|
|
6326
|
-
});
|
|
6327
|
-
}
|
|
6328
|
-
if (connector.type === "gh") {
|
|
6329
|
-
if (!(connector.token || connector.tokenEnv)) issues.push({
|
|
6330
|
-
connector: connector.name,
|
|
6331
|
-
field: "token",
|
|
6332
|
-
message: "missing token or tokenEnv for GitHub connector"
|
|
6333
|
-
});
|
|
6334
|
-
if (!connector.repo) issues.push({
|
|
6335
|
-
connector: connector.name,
|
|
6336
|
-
field: "repo",
|
|
6337
|
-
message: "missing repo (expected owner/repo format)"
|
|
6338
|
-
});
|
|
6339
|
-
}
|
|
6340
|
-
if (connector.type === "discord") {
|
|
6341
|
-
if (!(connector.botToken || connector.botTokenEnv)) issues.push({
|
|
6342
|
-
connector: connector.name,
|
|
6343
|
-
field: "botToken",
|
|
6344
|
-
message: "missing botToken or botTokenEnv for Discord connector"
|
|
6345
|
-
});
|
|
6346
|
-
}
|
|
6347
|
-
return issues;
|
|
6348
|
-
};
|
|
6349
|
-
const channelsValidateHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", z.object({ json: z.enum([
|
|
6350
|
-
"true",
|
|
6351
|
-
"false",
|
|
6352
|
-
""
|
|
6353
|
-
]).optional() }), validateHelp), (c) => {
|
|
6354
|
-
const param = c.req.valid("param");
|
|
6355
|
-
const query = c.req.valid("query");
|
|
6356
|
-
const funnel = c.var.funnel;
|
|
6357
|
-
const isJson = query.json === "true" || query.json === "";
|
|
6358
|
-
const channel = funnel.channels.get(param.channel);
|
|
6359
|
-
if (!channel) throw new HTTPException(404, { message: `channel "${param.channel}" not found` });
|
|
6360
|
-
if (channel.connectors.length === 0) {
|
|
6361
|
-
if (isJson) return c.json({
|
|
6362
|
-
channel: channel.name,
|
|
6363
|
-
valid: false,
|
|
6364
|
-
issues: [{
|
|
6365
|
-
connector: "(none)",
|
|
6366
|
-
field: "connectors",
|
|
6367
|
-
message: "no connectors configured"
|
|
6368
|
-
}]
|
|
6369
|
-
});
|
|
6370
|
-
return c.text(`⚠ ${channel.name}: no connectors configured`);
|
|
6371
|
-
}
|
|
6372
|
-
const allIssues = [];
|
|
6373
|
-
for (const connector of channel.connectors) {
|
|
6374
|
-
const issues = validateConnector(connector);
|
|
6375
|
-
allIssues.push(...issues);
|
|
6376
|
-
}
|
|
6377
|
-
if (isJson) return c.json({
|
|
6378
|
-
channel: channel.name,
|
|
6379
|
-
valid: allIssues.length === 0,
|
|
6380
|
-
issues: allIssues
|
|
6381
|
-
});
|
|
6382
|
-
if (allIssues.length === 0) return c.text(`✓ ${channel.name}: all connectors valid`);
|
|
6383
|
-
const lines = allIssues.map((issue) => `✗ ${channel.name}/${issue.connector}: ${issue.message}`);
|
|
6384
|
-
return c.text(lines.join("\n"));
|
|
6385
|
-
});
|
|
6386
|
-
//#endregion
|
|
6387
6382
|
//#region lib/cli/routes/gateway.ts
|
|
6388
6383
|
const groupHelp$1 = `funnel gateway — manage the funnel daemon
|
|
6389
6384
|
|
|
@@ -6411,7 +6406,7 @@ examples:
|
|
|
6411
6406
|
|
|
6412
6407
|
see also: fnl debug --channel <name> (higher-level diagnosis with next-action hints)`;
|
|
6413
6408
|
const renderGatewayStatus = async (c) => {
|
|
6414
|
-
const status = c.
|
|
6409
|
+
const status = c.env.funnel.gateway.getStatus();
|
|
6415
6410
|
if (!status.running) throw new HTTPException(503, { message: "funnel gateway: not running" });
|
|
6416
6411
|
const res = await fetch(`http://127.0.0.1:${status.port}/status`).catch(() => null);
|
|
6417
6412
|
if (!res) return c.text(`funnel gateway: running (pid ${status.pid}) — health check failed`);
|
|
@@ -6453,7 +6448,7 @@ Reads /listeners from the running gateway daemon and prints the live registry.
|
|
|
6453
6448
|
|
|
6454
6449
|
examples:
|
|
6455
6450
|
funnel gateway listeners`), async (c) => {
|
|
6456
|
-
const result = await c.
|
|
6451
|
+
const result = await c.env.funnel.listeners.list();
|
|
6457
6452
|
if (result.state === "offline") throw new HTTPException(503, { message: "funnel gateway: not running" });
|
|
6458
6453
|
if (result.state === "error") throw new HTTPException(503, { message: `funnel gateway: ${result.reason}` });
|
|
6459
6454
|
if (result.listeners.length === 0) return c.text("funnel gateway: no running listeners");
|
|
@@ -6619,7 +6614,7 @@ const gatewaySqlHandler = factory.createHandlers(zValidator$1("query", z.object(
|
|
|
6619
6614
|
limit: z.string().optional()
|
|
6620
6615
|
}), sqlHelp), async (c) => {
|
|
6621
6616
|
const query = c.req.valid("query");
|
|
6622
|
-
const funnel = c.
|
|
6617
|
+
const funnel = c.env.funnel;
|
|
6623
6618
|
let sql = null;
|
|
6624
6619
|
let params = [];
|
|
6625
6620
|
let resolvedChannelId = null;
|
|
@@ -6669,7 +6664,7 @@ examples:
|
|
|
6669
6664
|
funnel gateway restart
|
|
6670
6665
|
funnel gateway restart --no-caffeine`), async (c) => {
|
|
6671
6666
|
const query = c.req.valid("query");
|
|
6672
|
-
const result = await c.
|
|
6667
|
+
const result = await c.env.funnel.gateway.restart({ caffeinate: query["no-caffeine"] !== "true" });
|
|
6673
6668
|
const lines = [];
|
|
6674
6669
|
if (result.wasRunning) lines.push(result.stopped ? "funnel gateway: stopped" : "funnel gateway: failed to stop");
|
|
6675
6670
|
if (result.stopped) lines.push(result.started ? "funnel gateway: started" : "funnel gateway: failed to start");
|
|
@@ -6690,7 +6685,7 @@ examples:
|
|
|
6690
6685
|
funnel gateway run
|
|
6691
6686
|
funnel gateway run --no-caffeine`), async (c) => {
|
|
6692
6687
|
const query = c.req.valid("query");
|
|
6693
|
-
const funnel = c.
|
|
6688
|
+
const funnel = c.env.funnel;
|
|
6694
6689
|
const gatewayScript = resolveDaemonScript();
|
|
6695
6690
|
const command = query["no-caffeine"] !== "true" && process.platform === "darwin" ? [
|
|
6696
6691
|
"caffeinate",
|
|
@@ -6720,7 +6715,7 @@ examples:
|
|
|
6720
6715
|
funnel gateway start --no-caffeine`;
|
|
6721
6716
|
const gatewayStartHandler = factory.createHandlers(zValidator$1("query", z.object({ "no-caffeine": z.string().optional() }), startHelp), async (c) => {
|
|
6722
6717
|
const query = c.req.valid("query");
|
|
6723
|
-
const funnel = c.
|
|
6718
|
+
const funnel = c.env.funnel;
|
|
6724
6719
|
if (funnel.gateway.isRunning()) {
|
|
6725
6720
|
const status = funnel.gateway.getStatus();
|
|
6726
6721
|
return c.text(`funnel gateway: already running (pid ${status.pid})`);
|
|
@@ -6747,7 +6742,7 @@ examples:
|
|
|
6747
6742
|
funnel gateway status --json`), async (c) => {
|
|
6748
6743
|
const query = c.req.valid("query");
|
|
6749
6744
|
if (!(query.json === "true" || query.json === "")) return renderGatewayStatus(c);
|
|
6750
|
-
const status = c.
|
|
6745
|
+
const status = c.env.funnel.gateway.getStatus();
|
|
6751
6746
|
if (!status.running) throw new HTTPException(503, { message: "funnel gateway: not running" });
|
|
6752
6747
|
const res = await fetch(`http://127.0.0.1:${status.port}/status`).catch(() => null);
|
|
6753
6748
|
if (!res) return c.json({
|
|
@@ -6771,12 +6766,29 @@ Terminates the process whose PID is stored in ~/.funnel/gateway.pid.
|
|
|
6771
6766
|
|
|
6772
6767
|
examples:
|
|
6773
6768
|
funnel gateway stop`), async (c) => {
|
|
6774
|
-
const funnel = c.
|
|
6769
|
+
const funnel = c.env.funnel;
|
|
6775
6770
|
if (!funnel.gateway.isRunning()) return c.text("funnel gateway: no running process");
|
|
6776
6771
|
if (!await funnel.gateway.stop()) throw new HTTPException(500, { message: "funnel gateway: failed to stop" });
|
|
6777
6772
|
return c.text("funnel gateway: stopped");
|
|
6778
6773
|
});
|
|
6779
6774
|
//#endregion
|
|
6775
|
+
//#region lib/cli/routes/profiles.add.ts
|
|
6776
|
+
const help$4 = `funnel profiles add — add a profile
|
|
6777
|
+
|
|
6778
|
+
usage: funnel profiles add <name> --path <path> --channel <channel-name> [recipe]
|
|
6779
|
+
|
|
6780
|
+
options:
|
|
6781
|
+
--path working directory passed to claude as cwd
|
|
6782
|
+
--channel channel name (resolved to channel id internally)
|
|
6783
|
+
--agent sub-agent name, prepended to the launch argv as --agent <name>
|
|
6784
|
+
--options extra launch argv as one whitespace-split string (e.g. "--brief")
|
|
6785
|
+
--env env vars layered under the process, as "KEY=VAL,KEY2=VAL2"
|
|
6786
|
+
--no-resume start a fresh claude session every launch (default resumes)
|
|
6787
|
+
|
|
6788
|
+
The launch recipe (--agent / --options / --env / --resume) lives on the
|
|
6789
|
+
profile; the channel only declares transport (connectors / delivery).`;
|
|
6790
|
+
const profilesAddHelpHandler = factory.createHandlers((c) => c.text(help$4));
|
|
6791
|
+
//#endregion
|
|
6780
6792
|
//#region lib/cli/routes/parse-profile-recipe.ts
|
|
6781
6793
|
/**
|
|
6782
6794
|
* Turns the single-string CLI flags (`--agent`, `--options "<argv>"`,
|
|
@@ -6813,20 +6825,6 @@ const parseProfileRecipe = (query) => {
|
|
|
6813
6825
|
};
|
|
6814
6826
|
//#endregion
|
|
6815
6827
|
//#region lib/cli/routes/profiles.add.$profile.ts
|
|
6816
|
-
const addHelp = `funnel profiles add — add a profile
|
|
6817
|
-
|
|
6818
|
-
usage: funnel profiles add <name> --path <path> --channel <channel-name> [recipe]
|
|
6819
|
-
|
|
6820
|
-
options:
|
|
6821
|
-
--path working directory passed to claude as cwd
|
|
6822
|
-
--channel channel name (resolved to channel id internally)
|
|
6823
|
-
--agent sub-agent name, prepended to the launch argv as --agent <name>
|
|
6824
|
-
--options extra launch argv as one whitespace-split string (e.g. "--brief")
|
|
6825
|
-
--env env vars layered under the process, as "KEY=VAL,KEY2=VAL2"
|
|
6826
|
-
--no-resume start a fresh claude session every launch (default resumes)
|
|
6827
|
-
|
|
6828
|
-
The launch recipe (--agent / --options / --env / --resume) lives on the
|
|
6829
|
-
profile; the channel only declares transport (connectors / delivery).`;
|
|
6830
6828
|
const profilesAddHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({
|
|
6831
6829
|
path: z.string(),
|
|
6832
6830
|
channel: z.string(),
|
|
@@ -6835,10 +6833,10 @@ const profilesAddHandler = factory.createHandlers(zValidator$1("param", z.object
|
|
|
6835
6833
|
env: z.string().optional(),
|
|
6836
6834
|
resume: z.string().optional(),
|
|
6837
6835
|
"no-resume": z.string().optional()
|
|
6838
|
-
})
|
|
6836
|
+
})), (c) => {
|
|
6839
6837
|
const param = c.req.valid("param");
|
|
6840
6838
|
const query = c.req.valid("query");
|
|
6841
|
-
const funnel = c.
|
|
6839
|
+
const funnel = c.env.funnel;
|
|
6842
6840
|
const channel = funnel.channels.get(query.channel);
|
|
6843
6841
|
if (!channel) throw new HTTPException(400, { message: `channel "${query.channel}" not found` });
|
|
6844
6842
|
const recipe = parseProfileRecipe(query);
|
|
@@ -6858,22 +6856,33 @@ usage: funnel profiles <name> as-default
|
|
|
6858
6856
|
|
|
6859
6857
|
the first profile in the list is treated as the default for fnl claude.`), (c) => {
|
|
6860
6858
|
const param = c.req.valid("param");
|
|
6861
|
-
c.
|
|
6859
|
+
c.env.funnel.profiles.asDefault(param.profile);
|
|
6862
6860
|
return c.text(`profile "${param.profile}" is now the default`);
|
|
6863
6861
|
});
|
|
6864
6862
|
//#endregion
|
|
6865
|
-
//#region lib/cli/routes/profiles
|
|
6866
|
-
const
|
|
6863
|
+
//#region lib/cli/routes/profiles.rename.ts
|
|
6864
|
+
const help$3 = `funnel profiles rename — rename a profile
|
|
6865
|
+
|
|
6866
|
+
usage:
|
|
6867
|
+
funnel profiles rename <old> <new>
|
|
6868
|
+
funnel profiles <old> rename <new>`;
|
|
6869
|
+
const profilesRenameHelpHandler = factory.createHandlers((c) => c.text(help$3));
|
|
6870
|
+
//#endregion
|
|
6871
|
+
//#region lib/cli/routes/profiles.$profile.rename.ts
|
|
6872
|
+
const help$2 = `funnel profiles rename — rename a profile
|
|
6867
6873
|
|
|
6868
6874
|
usage:
|
|
6869
6875
|
funnel profiles rename <old> <new>
|
|
6870
6876
|
funnel profiles <old> rename <new>`;
|
|
6877
|
+
const profilesProfileRenameHelpHandler = factory.createHandlers((c) => c.text(help$2));
|
|
6878
|
+
//#endregion
|
|
6879
|
+
//#region lib/cli/routes/profiles.$profile.rename.$newName.ts
|
|
6871
6880
|
const profilesRenameHandler = factory.createHandlers(zValidator$1("param", z.object({
|
|
6872
6881
|
profile: z.string(),
|
|
6873
6882
|
newName: z.string()
|
|
6874
|
-
})), zValidator$1("query", z.object({})
|
|
6883
|
+
})), zValidator$1("query", z.object({})), (c) => {
|
|
6875
6884
|
const param = c.req.valid("param");
|
|
6876
|
-
c.
|
|
6885
|
+
c.env.funnel.profiles.rename(param.profile, param.newName);
|
|
6877
6886
|
return c.text(`renamed profile "${param.profile}" to "${param.newName}"`);
|
|
6878
6887
|
});
|
|
6879
6888
|
//#endregion
|
|
@@ -6885,7 +6894,7 @@ usage: funnel profiles <name> run [additional claude args...]
|
|
|
6885
6894
|
const RESERVED_KEYS = [];
|
|
6886
6895
|
const profilesLaunchHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({}).passthrough(), launchHelp), async (c) => {
|
|
6887
6896
|
const param = c.req.valid("param");
|
|
6888
|
-
const funnel = c.
|
|
6897
|
+
const funnel = c.env.funnel;
|
|
6889
6898
|
const profile = funnel.profiles.get(param.profile);
|
|
6890
6899
|
if (!profile) throw new HTTPException(404, { message: `profile "${param.profile}" not found` });
|
|
6891
6900
|
const exitCode = await funnel.claude.launch({
|
|
@@ -6900,18 +6909,21 @@ const profilesLaunchHandler = factory.createHandlers(zValidator$1("param", z.obj
|
|
|
6900
6909
|
process.exit(exitCode);
|
|
6901
6910
|
});
|
|
6902
6911
|
//#endregion
|
|
6903
|
-
//#region lib/cli/routes/profiles.remove
|
|
6904
|
-
const
|
|
6912
|
+
//#region lib/cli/routes/profiles.remove.ts
|
|
6913
|
+
const help$1 = `funnel profiles remove — remove a profile
|
|
6905
6914
|
|
|
6906
6915
|
usage: funnel profiles remove <name>`;
|
|
6907
|
-
const
|
|
6916
|
+
const profilesRemoveHelpHandler = factory.createHandlers((c) => c.text(help$1));
|
|
6917
|
+
//#endregion
|
|
6918
|
+
//#region lib/cli/routes/profiles.remove.$profile.ts
|
|
6919
|
+
const profilesRemoveHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({})), (c) => {
|
|
6908
6920
|
const param = c.req.valid("param");
|
|
6909
|
-
c.
|
|
6921
|
+
c.env.funnel.profiles.remove(param.profile);
|
|
6910
6922
|
return c.text(`removed profile "${param.profile}"`);
|
|
6911
6923
|
});
|
|
6912
6924
|
//#endregion
|
|
6913
|
-
//#region lib/cli/routes/profiles.set
|
|
6914
|
-
const
|
|
6925
|
+
//#region lib/cli/routes/profiles.set.ts
|
|
6926
|
+
const help = `funnel profiles <name> set — update a profile
|
|
6915
6927
|
|
|
6916
6928
|
usage: funnel profiles <name> set [--path <path>] [--channel <channel-name>] [recipe]
|
|
6917
6929
|
|
|
@@ -6925,6 +6937,9 @@ options:
|
|
|
6925
6937
|
|
|
6926
6938
|
Only the flags you pass are changed; --agent and --options together replace
|
|
6927
6939
|
the profile's whole options list.`;
|
|
6940
|
+
const profilesSetHelpHandler = factory.createHandlers((c) => c.text(help));
|
|
6941
|
+
//#endregion
|
|
6942
|
+
//#region lib/cli/routes/profiles.set.$profile.ts
|
|
6928
6943
|
const profilesSetHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), zValidator$1("query", z.object({
|
|
6929
6944
|
path: z.string().optional(),
|
|
6930
6945
|
channel: z.string().optional(),
|
|
@@ -6933,10 +6948,10 @@ const profilesSetHandler = factory.createHandlers(zValidator$1("param", z.object
|
|
|
6933
6948
|
env: z.string().optional(),
|
|
6934
6949
|
resume: z.string().optional(),
|
|
6935
6950
|
"no-resume": z.string().optional()
|
|
6936
|
-
})
|
|
6951
|
+
})), (c) => {
|
|
6937
6952
|
const param = c.req.valid("param");
|
|
6938
6953
|
const query = c.req.valid("query");
|
|
6939
|
-
const funnel = c.
|
|
6954
|
+
const funnel = c.env.funnel;
|
|
6940
6955
|
const channel = query.channel !== void 0 ? funnel.channels.get(query.channel) : null;
|
|
6941
6956
|
if (query.channel !== void 0 && !channel) throw new HTTPException(400, { message: `channel "${query.channel}" not found` });
|
|
6942
6957
|
const recipe = parseProfileRecipe(query);
|
|
@@ -6971,7 +6986,7 @@ examples:
|
|
|
6971
6986
|
funnel profiles add cto --path /repo/myapp --channel prod-inbox --agent pm --options "--brief"
|
|
6972
6987
|
funnel profiles cto as-default
|
|
6973
6988
|
funnel profiles cto run`), (c) => {
|
|
6974
|
-
const profiles = c.
|
|
6989
|
+
const profiles = c.env.funnel.profiles.list();
|
|
6975
6990
|
if (profiles.length === 0) return c.text("no profiles");
|
|
6976
6991
|
const lines = profiles.map((profile, index) => {
|
|
6977
6992
|
const tag = index === 0 ? " (default)" : "";
|
|
@@ -7092,7 +7107,7 @@ const statusHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
7092
7107
|
interval: z.string().optional()
|
|
7093
7108
|
}), statusHelp), async (c) => {
|
|
7094
7109
|
const query = c.req.valid("query");
|
|
7095
|
-
const funnel = c.
|
|
7110
|
+
const funnel = c.env.funnel;
|
|
7096
7111
|
const isWatch = query.watch === "true" || query.watch === "";
|
|
7097
7112
|
const intervalSec = Math.min(60, Math.max(1, query.interval ? Number(query.interval) : 3));
|
|
7098
7113
|
if (!isWatch) {
|
|
@@ -7137,30 +7152,9 @@ const updateHandler = factory.createHandlers(zValidator$1("query", z.object({}),
|
|
|
7137
7152
|
});
|
|
7138
7153
|
//#endregion
|
|
7139
7154
|
//#region lib/cli/routes/index.ts
|
|
7140
|
-
const
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
* uses against a custom Funnel (e.g. one with sandboxed boundaries).
|
|
7145
|
-
*
|
|
7146
|
-
* All CLI verbs (`add` / `remove` / `set` / `rename` / `as-default` / `request`) map to POST in
|
|
7147
|
-
* to-request.ts and stay in the URL as a literal segment. Read paths (list / show / launch) keep GET.
|
|
7148
|
-
* Help shortcuts at parameterless URLs return the help text directly so `funnel <verb>` (no args) is
|
|
7149
|
-
* informative instead of 404.
|
|
7150
|
-
*/
|
|
7151
|
-
const createCliApp = (funnel) => {
|
|
7152
|
-
const base = factory.createApp();
|
|
7153
|
-
base.use((c, next) => {
|
|
7154
|
-
c.set("funnel", funnel);
|
|
7155
|
-
return next();
|
|
7156
|
-
});
|
|
7157
|
-
base.onError((error, c) => {
|
|
7158
|
-
if (error instanceof HTTPException) return c.text(`error: ${error.message}`, error.status);
|
|
7159
|
-
return c.text(`error: ${error instanceof Error ? error.message : String(error)}`, 400);
|
|
7160
|
-
});
|
|
7161
|
-
return base.get("/claude", ...claudeHandler).get("/channels", ...channelsGroupHandler).post("/channels/add", ...helpRoute(addHelp$3)).post("/channels/add/:channel", ...channelsAddHandler).post("/channels/remove", ...helpRoute(removeHelp$1)).post("/channels/remove/:channel", ...channelsRemoveHandler).post("/channels/rename/:channel/:newName", ...channelsRenameHandler).post("/channels/:channel/rename/:newName", ...channelsRenameHandler).post("/channels/rename", ...helpRoute(renameHelp$1)).post("/channels/:channel/rename", ...helpRoute(renameHelp$1)).post("/channels/:channel/set/delivery/:mode", ...channelsSetDeliveryHandler).post("/channels/publish", ...helpRoute(publishHelp)).post("/channels/:channel/publish", ...channelsPublishHandler).get("/channels/:channel/validate", ...channelsValidateHandler).get("/channels/validate", ...helpRoute(validateHelp)).get("/channels/:channel", ...channelsShowHandler).get("/channels/:channel/connectors", ...channelsConnectorsGroupHandler).post("/channels/:channel/connectors/add", ...helpRoute(addHelp$2)).post("/channels/:channel/connectors/add/:connector", ...channelsConnectorsAddHandler).post("/channels/:channel/connectors/remove", ...helpRoute(removeHelp$3)).post("/channels/:channel/connectors/remove/:connector", ...channelsConnectorsRemoveHandler).post("/channels/:channel/connectors/set", ...helpRoute(setHelp$1)).post("/channels/:channel/connectors/set/:connector", ...channelsConnectorsSetHandler).post("/channels/:channel/connectors/rename/:connector/:newName", ...channelsConnectorsRenameHandler).post("/channels/:channel/connectors/:connector/rename/:newName", ...channelsConnectorsRenameHandler).post("/channels/:channel/connectors/rename", ...helpRoute(renameHelp$2)).post("/channels/:channel/connectors/:connector/rename", ...helpRoute(renameHelp$2)).post("/channels/:channel/connectors/:connector/request", ...channelsConnectorsRequestHandler).get("/channels/:channel/connectors/:connector", ...channelsConnectorsShowHandler).get("/channels/:channel/connectors/:connector/schedules", ...channelsConnectorsSchedulesGroupHandler).post("/channels/:channel/connectors/:connector/schedules/add", ...helpRoute(addHelp$1)).post("/channels/:channel/connectors/:connector/schedules/add/:id", ...channelsConnectorsSchedulesAddHandler).post("/channels/:channel/connectors/:connector/schedules/remove", ...helpRoute(removeHelp$2)).post("/channels/:channel/connectors/:connector/schedules/remove/:id", ...channelsConnectorsSchedulesRemoveHandler).get("/profiles", ...profilesGroupHandler).post("/profiles/add", ...helpRoute(addHelp)).post("/profiles/add/:profile", ...profilesAddHandler).post("/profiles/set", ...helpRoute(setHelp)).post("/profiles/set/:profile", ...profilesSetHandler).post("/profiles/remove", ...helpRoute(removeHelp)).post("/profiles/remove/:profile", ...profilesRemoveHandler).post("/profiles/rename/:profile/:newName", ...profilesRenameHandler).post("/profiles/:profile/rename/:newName", ...profilesRenameHandler).post("/profiles/rename", ...helpRoute(renameHelp)).post("/profiles/:profile/rename", ...helpRoute(renameHelp)).post("/profiles/:profile/as-default", ...profilesAsDefaultHandler).get("/profiles/:profile/run", ...profilesLaunchHandler).get("/profiles/:profile", ...profilesLaunchHandler).get("/gateway", ...gatewayGroupHandler).get("/gateway/status", ...gatewayStatusHandler).get("/gateway/start", ...gatewayStartHandler).get("/gateway/stop", ...gatewayStopHandler).get("/gateway/restart", ...gatewayRestartHandler).get("/gateway/run", ...gatewayRunHandler).get("/gateway/logs", ...gatewayLogsHandler).get("/gateway/sql", ...gatewaySqlHandler).get("/gateway/listeners", ...gatewayListenersHandler).get("/debug", ...debugHandler).get("/debug/events", ...debugEventsHandler).get("/debug/dropped", ...debugDroppedHandler).get("/debug/errors", ...debugErrorsHandler).get("/debug/replay", ...debugReplayHandler).get("/schema", ...schemaHandler).get("/status", ...statusHandler).get("/update", ...updateHandler);
|
|
7162
|
-
};
|
|
7163
|
-
/** CLI Hono app wired to a default `new Funnel()`. For embedding with a custom Funnel use `createCliApp`. */
|
|
7164
|
-
const app = createCliApp(new Funnel());
|
|
7155
|
+
const routes = factory.createApp().onError((error, c) => {
|
|
7156
|
+
if (error instanceof HTTPException) return c.text(`error: ${error.message}`, error.status);
|
|
7157
|
+
return c.text(`error: ${error instanceof Error ? error.message : String(error)}`, 400);
|
|
7158
|
+
}).get("/claude", ...claudeHandler).get("/channels", ...channelsGroupHandler).post("/channels/add", ...channelsAddHelpHandler).post("/channels/add/:channel", ...channelsAddHandler).post("/channels/remove", ...channelsRemoveHelpHandler).post("/channels/remove/:channel", ...channelsRemoveHandler).post("/channels/rename/:channel/:newName", ...channelsRenameHandler).post("/channels/:channel/rename/:newName", ...channelsRenameHandler).post("/channels/rename", ...channelsRenameHelpHandler).post("/channels/:channel/rename", ...channelsChannelRenameHelpHandler).post("/channels/:channel/set/delivery/:mode", ...channelsSetDeliveryHandler).post("/channels/publish", ...channelsPublishHelpHandler).post("/channels/:channel/publish", ...channelsPublishHandler).get("/channels/:channel/validate", ...channelsValidateHandler).get("/channels/validate", ...channelsValidateHelpHandler).get("/channels/:channel", ...channelsShowHandler).get("/channels/:channel/connectors", ...channelsConnectorsGroupHandler).post("/channels/:channel/connectors/add", ...channelsConnectorsAddHelpHandler).post("/channels/:channel/connectors/add/:connector", ...channelsConnectorsAddHandler).post("/channels/:channel/connectors/remove", ...channelsConnectorsRemoveHelpHandler).post("/channels/:channel/connectors/remove/:connector", ...channelsConnectorsRemoveHandler).post("/channels/:channel/connectors/set", ...channelsConnectorsSetHelpHandler).post("/channels/:channel/connectors/set/:connector", ...channelsConnectorsSetHandler).post("/channels/:channel/connectors/rename/:connector/:newName", ...channelsConnectorsRenameHandler).post("/channels/:channel/connectors/:connector/rename/:newName", ...channelsConnectorsRenameHandler).post("/channels/:channel/connectors/rename", ...channelsConnectorsRenameHelpHandler).post("/channels/:channel/connectors/:connector/rename", ...channelsConnectorRenameHelpHandler).post("/channels/:channel/connectors/:connector/request", ...channelsConnectorsRequestHandler).get("/channels/:channel/connectors/:connector", ...channelsConnectorsShowHandler).get("/channels/:channel/connectors/:connector/schedules", ...channelsConnectorsSchedulesGroupHandler).post("/channels/:channel/connectors/:connector/schedules/add", ...channelsConnectorSchedulesAddHelpHandler).post("/channels/:channel/connectors/:connector/schedules/add/:id", ...channelsConnectorsSchedulesAddHandler).post("/channels/:channel/connectors/:connector/schedules/remove", ...channelsConnectorSchedulesRemoveHelpHandler).post("/channels/:channel/connectors/:connector/schedules/remove/:id", ...channelsConnectorsSchedulesRemoveHandler).get("/profiles", ...profilesGroupHandler).post("/profiles/add", ...profilesAddHelpHandler).post("/profiles/add/:profile", ...profilesAddHandler).post("/profiles/set", ...profilesSetHelpHandler).post("/profiles/set/:profile", ...profilesSetHandler).post("/profiles/remove", ...profilesRemoveHelpHandler).post("/profiles/remove/:profile", ...profilesRemoveHandler).post("/profiles/rename/:profile/:newName", ...profilesRenameHandler).post("/profiles/:profile/rename/:newName", ...profilesRenameHandler).post("/profiles/rename", ...profilesRenameHelpHandler).post("/profiles/:profile/rename", ...profilesProfileRenameHelpHandler).post("/profiles/:profile/as-default", ...profilesAsDefaultHandler).get("/profiles/:profile/run", ...profilesLaunchHandler).get("/profiles/:profile", ...profilesLaunchHandler).get("/gateway", ...gatewayGroupHandler).get("/gateway/status", ...gatewayStatusHandler).get("/gateway/start", ...gatewayStartHandler).get("/gateway/stop", ...gatewayStopHandler).get("/gateway/restart", ...gatewayRestartHandler).get("/gateway/run", ...gatewayRunHandler).get("/gateway/logs", ...gatewayLogsHandler).get("/gateway/sql", ...gatewaySqlHandler).get("/gateway/listeners", ...gatewayListenersHandler).get("/debug", ...debugHandler).get("/debug/events", ...debugEventsHandler).get("/debug/dropped", ...debugDroppedHandler).get("/debug/errors", ...debugErrorsHandler).get("/debug/replay", ...debugReplayHandler).get("/schema", ...schemaHandler).get("/status", ...statusHandler).get("/update", ...updateHandler);
|
|
7165
7159
|
//#endregion
|
|
7166
|
-
export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, DEFAULT_GATEWAY_PORT, DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, FUNNEL_MCP_ARGS, 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,
|
|
7160
|
+
export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, DEFAULT_GATEWAY_PORT, DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, FUNNEL_MCP_ARGS, 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, routes as cliRoutes, connectorConfigSchema, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, connectorSpecSchema, createSettings, discordConnectorSchema, factory, funnelEventSchema, funnelJsonSchema, ghConnectorSchema, localConfigSchema, profileConfigSchema, profileSpecSchema, publishRequestSchema, publishResponseSchema, queryToCliArgs, resolveFunnelDir, resolveFunnelPort, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, startChannelServer, toRequest };
|