@interactive-inc/claude-funnel 0.60.0 → 0.63.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 +2 -2
- package/dist/bin.js +428 -761
- package/dist/{channels-2g_BU1N0.d.ts → channels-B8RQPrVq.d.ts} +17 -16
- package/dist/claude.d.ts +5 -7
- package/dist/claude.js +143 -36
- package/dist/{connector-descriptor-6SXJoszo.d.ts → connector-descriptor-ClEEbuW3.d.ts} +50 -11
- package/dist/connector-diagnostics-recorder-COtNEmUp.js +42 -0
- package/dist/connectors/discord.d.ts +31 -37
- package/dist/connectors/discord.js +3 -3
- package/dist/connectors/gh.d.ts +37 -33
- package/dist/connectors/gh.js +3 -3
- package/dist/connectors/schedule.d.ts +9 -57
- package/dist/connectors/schedule.js +3 -3
- package/dist/connectors/slack.d.ts +71 -131
- package/dist/connectors/slack.js +4 -3
- package/dist/diagnostics.d.ts +1 -1
- package/dist/diagnostics.js +1 -1
- package/dist/discord-connector-DIFkYBbi.js +250 -0
- package/dist/discord-connector-schema-D-bOVAKt.d.ts +22 -0
- package/dist/docs.js +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +1 -1
- package/dist/{file-process-guard-DOlCr4GF.d.ts → file-process-guard-DGHxALfI.d.ts} +8 -6
- package/dist/{file-system-o51IsM0W.d.ts → file-system-VhwwXZbm.d.ts} +8 -0
- package/dist/flume-source-listener-Dim5szHG.d.ts +133 -0
- package/dist/{funnel-diagnostics-CSiJmPlZ.js → funnel-diagnostics-Cvk6Sk4x.js} +193 -43
- package/dist/{funnel-diagnostics-DpXOsCty.d.ts → funnel-diagnostics-b9ar0Ing.d.ts} +67 -5
- package/dist/{funnel-docs-BxXZ9Ksx.js → funnel-docs-C-ge0MuB.js} +42 -6
- package/dist/{funnel-doctor-CZf_0Luq.d.ts → funnel-doctor-CnRQi4kM.d.ts} +2 -2
- package/dist/{funnel-doctor-DiJCjHsg.js → funnel-doctor-XrI2GBH8.js} +1 -1
- package/dist/funnel-error-0t1MK1R6.js +75 -0
- package/dist/{funnel-recovery-DnLrdWO9.d.ts → funnel-recovery-CMhY8Jfk.d.ts} +1 -1
- package/dist/gateway/daemon.js +167 -527
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.js +3 -3
- package/dist/gh-connector-BUGCOEWS.js +187 -0
- package/dist/{gh-connector-schema-Rzwc1c1N.js → gh-connector-schema-CAqIhzGr.js} +7 -0
- package/dist/gh-connector-schema-DWQaB6gX.d.ts +16 -0
- package/dist/{index-CgY8NdMz.d.ts → index-DxRikYmu.d.ts} +37 -19
- package/dist/index.d.ts +182 -22
- package/dist/index.js +365 -174
- package/dist/{local-config-json-schema-JyLqOQNX.js → local-config-json-schema-DexV8vX3.js} +24 -4
- package/dist/local-config.d.ts +39 -2
- package/dist/local-config.js +53 -2
- package/dist/logger.js +1 -1
- package/dist/loopback-fetch-CVNuN3YZ.js +40 -0
- package/dist/{local-config-sync-Dh1Croqe.d.ts → memory-token-prompter-DP_YV9xX.d.ts} +30 -3
- package/dist/node-file-system-BOXIHW_Q.js +174 -0
- package/dist/{profiles-DSzTeKQw.js → profiles-ZHLONml4.js} +49 -49
- package/dist/{profiles-Cy5wXQ0L.d.ts → profiles-cVZQkM69.d.ts} +3 -3
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +1 -1
- package/dist/recovery.d.ts +1 -1
- package/dist/recovery.js +1 -1
- package/dist/resolve-connector-token-DxDG9mhf.js +22 -0
- package/dist/{schedule-connector-L4uzg5M8.js → schedule-connector-9k3gOIgl.js} +54 -55
- package/dist/schedule-connector-schema-Z0RXLgPI.d.ts +49 -0
- package/dist/settings-reader-BNxjsxCB.d.ts +27 -0
- package/dist/{settings-store-CUKSeTXC.js → settings-store-C2QdOH-t.js} +23 -4
- package/dist/slack-connector-BU86fIge.js +359 -0
- package/dist/slack-event-processor-BhCf5Wiy.d.ts +95 -0
- package/dist/slack-event-processor-xFDG3US0.js +176 -0
- package/dist/slot-fields-D-pvMgTK.js +249 -0
- package/dist/{memory-diagnostic-log-CI60kNfB.js → sqlite-diagnostic-log-DOTPW-tG.js} +373 -249
- package/dist/{yaml-render-qW34NlYz.js → yaml-render--J1_3BSA.js} +28 -21
- package/package.json +2 -4
- package/dist/discord-connector-BL36yvbL.js +0 -250
- package/dist/gateway-base-url-Dy4Ykuoh.js +0 -14
- package/dist/gh-connector-DpiixfQZ.js +0 -226
- package/dist/http-client-oICicjuO.d.ts +0 -18
- package/dist/memory-token-prompter-B4sjyaAq.d.ts +0 -57
- package/dist/memory-token-prompter-CZde7e6y.js +0 -61
- package/dist/node-file-system-Blr8pAir.js +0 -48
- package/dist/settings-reader-BIFB_j2f.d.ts +0 -18
- package/dist/slack-connector-DQIFPdBF.js +0 -484
- package/dist/slot-fields-CMoRpwuy.js +0 -45
- /package/dist/{connector-adapter-DU9Rvyec.js → connector-adapter-Dvs8N7ew.js} +0 -0
- /package/dist/{connector-listener-DR3aKOuK.js → connector-listener-mPGZYa8e.js} +0 -0
- /package/dist/{diagnostic-sql-reader-C9zR-Csp.js → diagnostic-sql-reader-oXZnWFf_.js} +0 -0
- /package/dist/{discord-connector-schema-B_N6IXLz.js → discord-connector-schema-B4YpWpR3.js} +0 -0
- /package/dist/{error-message-of-Byi4y0Uf.js → error-message-of-ColuYmAk.js} +0 -0
- /package/dist/{funnel-log-sqlite-sink-kqJbx2H7.js → funnel-log-sqlite-sink-DLYkY0pZ.js} +0 -0
- /package/dist/{funnel-recovery-BFdPjL6Z.js → funnel-recovery-DKnEutUS.js} +0 -0
- /package/dist/{node-http-client-lowp60Oa.js → node-http-client-u00atiKx.js} +0 -0
- /package/dist/{schedule-connector-schema-CfyuMCMh.js → schedule-connector-schema-DKEPZnVv.js} +0 -0
- /package/dist/{settings-reader-CtQ-Ix8_.js → settings-reader-9FcX3qS1.js} +0 -0
- /package/dist/{settings-schema-D1xcOqRu.d.ts → settings-schema-BL_c2Udm.d.ts} +0 -0
- /package/dist/{slack-connector-schema-C1zEf4TG.js → slack-connector-schema-Dem8to4P.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-CVNuN3YZ.js";
|
|
2
2
|
import { t as FunnelFileSystem } from "./file-system-Wvzc2ePY.js";
|
|
3
|
-
import { t as NodeFunnelFileSystem } from "./node-file-system-
|
|
3
|
+
import { t as NodeFunnelFileSystem } from "./node-file-system-BOXIHW_Q.js";
|
|
4
4
|
import { t as FunnelLogger } from "./logger-B6iyNbxM.js";
|
|
5
5
|
import { n as FunnelProcessRunner, t as NodeFunnelProcessRunner } from "./node-process-runner-DxTvycoK.js";
|
|
6
|
-
import { n as
|
|
7
|
-
import {
|
|
8
|
-
import { a as
|
|
9
|
-
import { a as
|
|
10
|
-
import { t as
|
|
11
|
-
import { t as
|
|
12
|
-
import { t as
|
|
13
|
-
import {
|
|
14
|
-
import { t as
|
|
15
|
-
import { t as
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { t as
|
|
19
|
-
import { t as
|
|
20
|
-
import {
|
|
6
|
+
import { n as FunnelHttpClient, t as NodeFunnelHttpClient } from "./node-http-client-u00atiKx.js";
|
|
7
|
+
import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-9FcX3qS1.js";
|
|
8
|
+
import { a as resolveFunnelDir, c as channelConfigSchema, d as settingsSchema, f as baseConnectorConfigSchema, i as SETTINGS_PATH, l as channelDeliveryModeSchema, n as FUNNEL_DIR, o as resolveFunnelPort, p as NodeFunnelIdGenerator, r as FunnelSettingsStore, s as SETTINGS_VERSION, t as DEFAULT_GATEWAY_PORT, u as profileConfigSchema } from "./settings-store-C2QdOH-t.js";
|
|
9
|
+
import { a as FunnelConnectorTypeMismatchError, c as FunnelTokenCollisionError, i as FunnelConnectorNotFoundError, n as FunnelChannelAlreadyExistsError, o as FunnelError, r as FunnelChannelNotFoundError, s as FunnelGatewayBindError, t as FunnelAuthFailedError } from "./funnel-error-0t1MK1R6.js";
|
|
10
|
+
import { a as FunnelMcp, o as FunnelFileProcessGuard, s as FunnelClaude, t as renderYaml } from "./yaml-render--J1_3BSA.js";
|
|
11
|
+
import { a as toDiagnosticEvent, i as toDiagnosticConnectionError, n as previewOf, r as queryRows, t as FunnelDiagnostics } from "./funnel-diagnostics-Cvk6Sk4x.js";
|
|
12
|
+
import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-oXZnWFf_.js";
|
|
13
|
+
import { t as FunnelDoctor } from "./funnel-doctor-XrI2GBH8.js";
|
|
14
|
+
import { t as FunnelDocs } from "./funnel-docs-C-ge0MuB.js";
|
|
15
|
+
import { a as FunnelTokenPrompter, i as FunnelLocalConfigSync, n as MemoryFunnelTokenPrompter, o as FunnelLocalConfig, r as NodeFunnelTokenPrompter, t as funnelJsonSchema } from "./local-config-json-schema-DexV8vX3.js";
|
|
16
|
+
import { t as discordConnectorSchema } from "./discord-connector-schema-B4YpWpR3.js";
|
|
17
|
+
import { t as ghConnectorSchema } from "./gh-connector-schema-CAqIhzGr.js";
|
|
18
|
+
import { t as slackConnectorSchema } from "./slack-connector-schema-Dem8to4P.js";
|
|
19
|
+
import { t as FunnelProfiles } from "./profiles-ZHLONml4.js";
|
|
20
|
+
import { t as FunnelRecovery } from "./funnel-recovery-DKnEutUS.js";
|
|
21
|
+
import { C as funnelTmpDir, S as connectorRawEventSchema, _ as MemoryConnectorDiagnosticLog, a as DEFAULT_GATEWAY_TOKEN_PATH, b as connectorConnectionEventSchema, c as FunnelListenerRegistry, d as funnelEventSchema, f as FunnelBroadcaster, g as publishResponseSchema, h as publishRequestSchema, i as channelWsUrl, l as SqliteFunnelEventLog, m as FunnelChannelPublisher, n as MemoryFunnelEventLog, o as FunnelGatewayToken, p as requireBearerToken, r as channelWsProtocols, s as FunnelGatewayServer, t as SqliteConnectorDiagnosticLog, u as FunnelEventLog, v as CONNECTOR_CONNECTION_STATUSES, x as connectorProcessedEventSchema, y as ConnectorDiagnosticLog } from "./sqlite-diagnostic-log-DOTPW-tG.js";
|
|
22
|
+
import { t as FunnelConnectorAdapter } from "./connector-adapter-Dvs8N7ew.js";
|
|
23
|
+
import { t as FunnelConnectorListener } from "./connector-listener-mPGZYa8e.js";
|
|
24
|
+
import { t as FunnelSlackEventProcessor } from "./slack-event-processor-xFDG3US0.js";
|
|
25
|
+
import { n as scheduleConnectorSchema, r as scheduleEntrySchema, t as scheduleCatchupPolicySchema } from "./schedule-connector-schema-DKEPZnVv.js";
|
|
21
26
|
import { dirname, join, resolve } from "node:path";
|
|
22
27
|
import { hc } from "hono/client";
|
|
23
28
|
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
@@ -27,9 +32,32 @@ import { createFactory } from "hono/factory";
|
|
|
27
32
|
import { HTTPException } from "hono/http-exception";
|
|
28
33
|
import { zValidator } from "@hono/zod-validator";
|
|
29
34
|
import { Hono } from "hono";
|
|
35
|
+
//#region lib/engine/time/clock.ts
|
|
36
|
+
/**
|
|
37
|
+
* Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
|
|
38
|
+
* is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
|
|
39
|
+
*/
|
|
40
|
+
var FunnelClock = class {
|
|
41
|
+
millis() {
|
|
42
|
+
return this.now().getTime();
|
|
43
|
+
}
|
|
44
|
+
iso() {
|
|
45
|
+
return this.now().toISOString();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region lib/engine/time/node-clock.ts
|
|
50
|
+
var NodeFunnelClock = class extends FunnelClock {
|
|
51
|
+
now() {
|
|
52
|
+
return /* @__PURE__ */ new Date();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
30
56
|
//#region lib/engine/connectors/connector-registry.ts
|
|
31
57
|
const defaultFs$1 = new NodeFunnelFileSystem();
|
|
32
58
|
const defaultProcess$1 = new NodeFunnelProcessRunner();
|
|
59
|
+
const defaultHttp = new NodeFunnelHttpClient();
|
|
60
|
+
const defaultClock$2 = new NodeFunnelClock();
|
|
33
61
|
/**
|
|
34
62
|
* Dispatches connector work to injected descriptors by `type`. Replaces the old
|
|
35
63
|
* hard-coded factory: core never imports a concrete connector, so listener and
|
|
@@ -43,15 +71,21 @@ var FunnelConnectorRegistry = class {
|
|
|
43
71
|
descriptors;
|
|
44
72
|
fs;
|
|
45
73
|
process;
|
|
74
|
+
http;
|
|
75
|
+
clock;
|
|
46
76
|
logger;
|
|
47
77
|
diagnosticLog;
|
|
78
|
+
signal;
|
|
48
79
|
dir;
|
|
49
80
|
constructor(deps) {
|
|
50
81
|
this.descriptors = new Map(deps.descriptors.map((descriptor) => [descriptor.type, descriptor]));
|
|
51
82
|
this.fs = deps.fs ?? defaultFs$1;
|
|
52
83
|
this.process = deps.process ?? defaultProcess$1;
|
|
84
|
+
this.http = deps.http ?? defaultHttp;
|
|
85
|
+
this.clock = deps.clock ?? defaultClock$2;
|
|
53
86
|
this.logger = deps.logger;
|
|
54
87
|
this.diagnosticLog = deps.diagnosticLog;
|
|
88
|
+
this.signal = deps.signal;
|
|
55
89
|
this.dir = deps.dir ?? FUNNEL_DIR;
|
|
56
90
|
Object.freeze(this);
|
|
57
91
|
}
|
|
@@ -104,8 +138,11 @@ var FunnelConnectorRegistry = class {
|
|
|
104
138
|
channelId,
|
|
105
139
|
fs: this.fs,
|
|
106
140
|
process: this.process,
|
|
141
|
+
http: this.http,
|
|
142
|
+
clock: this.clock,
|
|
107
143
|
logger: this.logger,
|
|
108
144
|
diagnosticLog: this.diagnosticLog,
|
|
145
|
+
signal: this.signal,
|
|
109
146
|
connectorDir: (channel, connector) => this.connectorDir(channel, connector)
|
|
110
147
|
};
|
|
111
148
|
}
|
|
@@ -113,32 +150,12 @@ var FunnelConnectorRegistry = class {
|
|
|
113
150
|
return {
|
|
114
151
|
fs: this.fs,
|
|
115
152
|
process: this.process,
|
|
153
|
+
http: this.http,
|
|
116
154
|
logger: this.logger
|
|
117
155
|
};
|
|
118
156
|
}
|
|
119
157
|
};
|
|
120
158
|
//#endregion
|
|
121
|
-
//#region lib/engine/time/clock.ts
|
|
122
|
-
/**
|
|
123
|
-
* Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
|
|
124
|
-
* is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
|
|
125
|
-
*/
|
|
126
|
-
var FunnelClock = class {
|
|
127
|
-
millis() {
|
|
128
|
-
return this.now().getTime();
|
|
129
|
-
}
|
|
130
|
-
iso() {
|
|
131
|
-
return this.now().toISOString();
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
//#endregion
|
|
135
|
-
//#region lib/engine/time/node-clock.ts
|
|
136
|
-
var NodeFunnelClock = class extends FunnelClock {
|
|
137
|
-
now() {
|
|
138
|
-
return /* @__PURE__ */ new Date();
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
//#endregion
|
|
142
159
|
//#region lib/engine/channels/channels.ts
|
|
143
160
|
const defaultClock$1 = new NodeFunnelClock();
|
|
144
161
|
const defaultIdGenerator = new NodeFunnelIdGenerator();
|
|
@@ -178,40 +195,40 @@ var FunnelChannels = class {
|
|
|
178
195
|
return this.list().find((c) => c.id === id) ?? null;
|
|
179
196
|
}
|
|
180
197
|
add(input) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
return this.store.update((settings) => {
|
|
199
|
+
if (settings.channels.some((c) => c.name === input.name)) throw new FunnelChannelAlreadyExistsError(input.name);
|
|
200
|
+
const channel = {
|
|
201
|
+
id: this.idGenerator.generate(),
|
|
202
|
+
name: input.name,
|
|
203
|
+
delivery: input.delivery ?? "fanout",
|
|
204
|
+
connectors: []
|
|
205
|
+
};
|
|
206
|
+
settings.channels.push(channel);
|
|
207
|
+
return channel;
|
|
208
|
+
});
|
|
192
209
|
}
|
|
193
210
|
setDelivery(name, delivery) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
211
|
+
this.store.update((settings) => {
|
|
212
|
+
const channel = this.requireChannel(settings, name);
|
|
213
|
+
channel.delivery = delivery;
|
|
214
|
+
});
|
|
198
215
|
}
|
|
199
216
|
remove(name) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
217
|
+
this.store.update((settings) => {
|
|
218
|
+
const index = settings.channels.findIndex((c) => c.name === name);
|
|
219
|
+
if (index < 0) throw new FunnelChannelNotFoundError(name);
|
|
220
|
+
const channel = settings.channels[index];
|
|
221
|
+
if (channel && this.profileChecker?.hasChannelRef(channel.id)) throw new Error(`channel "${name}" is referenced by a profile`);
|
|
222
|
+
settings.channels.splice(index, 1);
|
|
223
|
+
});
|
|
207
224
|
}
|
|
208
225
|
rename(oldName, newName) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
226
|
+
this.store.update((settings) => {
|
|
227
|
+
const channel = settings.channels.find((c) => c.name === oldName);
|
|
228
|
+
if (!channel) throw new FunnelChannelNotFoundError(oldName);
|
|
229
|
+
if (settings.channels.some((c) => c.name === newName)) throw new FunnelChannelAlreadyExistsError(newName);
|
|
230
|
+
channel.name = newName;
|
|
231
|
+
});
|
|
215
232
|
}
|
|
216
233
|
listConnectors(channelName) {
|
|
217
234
|
return this.requireChannel(this.store.read(), channelName).connectors;
|
|
@@ -231,35 +248,35 @@ var FunnelChannels = class {
|
|
|
231
248
|
return out;
|
|
232
249
|
}
|
|
233
250
|
addConnector(channelName, input) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
251
|
+
return this.store.update((settings) => {
|
|
252
|
+
const channel = this.requireChannel(settings, channelName);
|
|
253
|
+
if (channel.connectors.some((c) => c.name === input.name)) throw new Error(`connector "${input.name}" already exists in channel "${channelName}"`);
|
|
254
|
+
const candidate = this.registry.buildConfig(input, {
|
|
255
|
+
id: this.idGenerator.generate(),
|
|
256
|
+
now: this.clock.iso()
|
|
257
|
+
});
|
|
258
|
+
this.assertNoTokenCollision(settings, candidate);
|
|
259
|
+
channel.connectors.push(candidate);
|
|
260
|
+
return candidate;
|
|
240
261
|
});
|
|
241
|
-
this.assertNoTokenCollision(settings, candidate);
|
|
242
|
-
channel.connectors.push(candidate);
|
|
243
|
-
this.store.write(settings);
|
|
244
|
-
return candidate;
|
|
245
262
|
}
|
|
246
263
|
removeConnector(channelName, connectorName) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
264
|
+
this.store.update((settings) => {
|
|
265
|
+
const channel = this.requireChannel(settings, channelName);
|
|
266
|
+
const index = channel.connectors.findIndex((c) => c.name === connectorName);
|
|
267
|
+
if (index < 0) throw new FunnelConnectorNotFoundError(channelName, connectorName);
|
|
268
|
+
channel.connectors.splice(index, 1);
|
|
269
|
+
});
|
|
253
270
|
}
|
|
254
271
|
renameConnector(channelName, oldName, newName) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
272
|
+
this.store.update((settings) => {
|
|
273
|
+
const channel = this.requireChannel(settings, channelName);
|
|
274
|
+
const connector = channel.connectors.find((c) => c.name === oldName);
|
|
275
|
+
if (!connector) throw new Error(`connector "${oldName}" not found in channel "${channelName}"`);
|
|
276
|
+
if (channel.connectors.some((c) => c.name === newName)) throw new Error(`connector "${newName}" already exists in channel "${channelName}"`);
|
|
277
|
+
connector.name = newName;
|
|
278
|
+
connector.updatedAt = this.clock.iso();
|
|
279
|
+
});
|
|
263
280
|
}
|
|
264
281
|
/**
|
|
265
282
|
* Update a connector's mutable fields generically. The connector's descriptor
|
|
@@ -267,14 +284,14 @@ var FunnelChannels = class {
|
|
|
267
284
|
* so a slot can move between a literal and an env reference cleanly).
|
|
268
285
|
*/
|
|
269
286
|
updateConnector(channelName, connectorName, fields) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
this.store.update((settings) => {
|
|
288
|
+
const channel = this.requireChannel(settings, channelName);
|
|
289
|
+
const connector = channel.connectors.find((c) => c.name === connectorName);
|
|
290
|
+
if (!connector) throw new FunnelConnectorNotFoundError(channelName, connectorName);
|
|
291
|
+
const updated = this.registry.applyUpdate(connector, fields, { now: this.clock.iso() });
|
|
292
|
+
this.assertNoTokenCollision(settings, updated);
|
|
293
|
+
this.replaceConnector(channel, connector.name, updated);
|
|
294
|
+
});
|
|
278
295
|
}
|
|
279
296
|
/** Back-compat wrapper for `updateConnector` on a slack connector. */
|
|
280
297
|
updateSlackConnector(channelName, connectorName, fields) {
|
|
@@ -294,23 +311,21 @@ var FunnelChannels = class {
|
|
|
294
311
|
* result; the config is persisted only when the operation actually mutated it.
|
|
295
312
|
*/
|
|
296
313
|
connectorOp(channelName, connectorName, operation, args) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
314
|
+
return this.store.update((settings) => {
|
|
315
|
+
const channel = this.requireChannel(settings, channelName);
|
|
316
|
+
const connector = channel.connectors.find((c) => c.name === connectorName);
|
|
317
|
+
if (!connector) throw new FunnelConnectorNotFoundError(channelName, connectorName);
|
|
318
|
+
const outcome = this.registry.runOperation(connector, operation, args, {
|
|
319
|
+
generateId: () => this.idGenerator.generate(),
|
|
320
|
+
now: this.clock.iso()
|
|
321
|
+
});
|
|
322
|
+
if (outcome.config !== connector) this.replaceConnector(channel, connector.name, outcome.config);
|
|
323
|
+
return outcome.result;
|
|
304
324
|
});
|
|
305
|
-
if (outcome.config !== connector) {
|
|
306
|
-
this.replaceConnector(channel, connector.name, outcome.config);
|
|
307
|
-
this.store.write(settings);
|
|
308
|
-
}
|
|
309
|
-
return outcome.result;
|
|
310
325
|
}
|
|
311
326
|
async call(channelName, connectorName, input) {
|
|
312
327
|
const connector = this.getConnector(channelName, connectorName);
|
|
313
|
-
if (!connector) throw new
|
|
328
|
+
if (!connector) throw new FunnelConnectorNotFoundError(channelName, connectorName);
|
|
314
329
|
const adapter = this.registry.createAdapter(connector);
|
|
315
330
|
if (!adapter) throw new Error(`connector type "${connector.type}" does not support outbound calls`);
|
|
316
331
|
return await adapter.call(input);
|
|
@@ -338,7 +353,7 @@ var FunnelChannels = class {
|
|
|
338
353
|
}
|
|
339
354
|
requireChannel(settings, name) {
|
|
340
355
|
const channel = settings.channels.find((c) => c.name === name);
|
|
341
|
-
if (!channel) throw new
|
|
356
|
+
if (!channel) throw new FunnelChannelNotFoundError(name);
|
|
342
357
|
return channel;
|
|
343
358
|
}
|
|
344
359
|
replaceConnector(channel, connectorName, next) {
|
|
@@ -418,6 +433,9 @@ var MemoryFunnelFileSystem = class extends FunnelFileSystem {
|
|
|
418
433
|
mode: this.modes.get(path) ?? null
|
|
419
434
|
};
|
|
420
435
|
}
|
|
436
|
+
withFileLock(_lockPath, fn) {
|
|
437
|
+
return fn();
|
|
438
|
+
}
|
|
421
439
|
setMtime(path, mtimeMs) {
|
|
422
440
|
this.mtimes.set(path, mtimeMs);
|
|
423
441
|
}
|
|
@@ -602,6 +620,9 @@ var MockFunnelSettingsReader = class extends FunnelSettingsReader {
|
|
|
602
620
|
write(settings) {
|
|
603
621
|
this.state = settings;
|
|
604
622
|
}
|
|
623
|
+
update(mutator) {
|
|
624
|
+
return mutator(this.state);
|
|
625
|
+
}
|
|
605
626
|
};
|
|
606
627
|
//#endregion
|
|
607
628
|
//#region lib/engine/time/memory-clock.ts
|
|
@@ -622,6 +643,31 @@ var MemoryFunnelClock = class extends FunnelClock {
|
|
|
622
643
|
}
|
|
623
644
|
};
|
|
624
645
|
//#endregion
|
|
646
|
+
//#region lib/engine/http/memory-http-client.ts
|
|
647
|
+
var MemoryFunnelHttpClient = class extends FunnelHttpClient {
|
|
648
|
+
calls = [];
|
|
649
|
+
handler = () => ({
|
|
650
|
+
status: 200,
|
|
651
|
+
body: ""
|
|
652
|
+
});
|
|
653
|
+
on(handler) {
|
|
654
|
+
this.handler = handler;
|
|
655
|
+
return this;
|
|
656
|
+
}
|
|
657
|
+
async fetch(request) {
|
|
658
|
+
this.calls.push(request);
|
|
659
|
+
const response = await this.handler(request);
|
|
660
|
+
const status = response.status ?? 200;
|
|
661
|
+
const body = response.body ?? "";
|
|
662
|
+
return {
|
|
663
|
+
status,
|
|
664
|
+
ok: status >= 200 && status < 300,
|
|
665
|
+
text: async () => body,
|
|
666
|
+
json: async () => JSON.parse(body)
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
//#endregion
|
|
625
671
|
//#region lib/gateway/resolve-daemon-script.ts
|
|
626
672
|
/**
|
|
627
673
|
* Locate the daemon entry script. Works in both dev (running from source)
|
|
@@ -838,7 +884,7 @@ var FunnelListenersClient = class {
|
|
|
838
884
|
async list() {
|
|
839
885
|
if (!this.isDaemonRunning()) return { state: "offline" };
|
|
840
886
|
try {
|
|
841
|
-
const res = await
|
|
887
|
+
const res = await loopbackFetch(`${gatewayLoopbackUrl(this.port)}/listeners`, { headers: this.authHeaders() });
|
|
842
888
|
if (!res.ok) return {
|
|
843
889
|
state: "error",
|
|
844
890
|
reason: `HTTP ${res.status}`
|
|
@@ -880,12 +926,18 @@ var FunnelListenersClient = class {
|
|
|
880
926
|
}
|
|
881
927
|
async call(method, path) {
|
|
882
928
|
try {
|
|
883
|
-
const res = await
|
|
929
|
+
const res = await loopbackFetch(`${gatewayLoopbackUrl(this.port)}${path}`, {
|
|
884
930
|
method,
|
|
885
931
|
headers: this.authHeaders()
|
|
886
|
-
});
|
|
932
|
+
}, 3e4);
|
|
887
933
|
if (!res.ok) {
|
|
888
|
-
|
|
934
|
+
let body = null;
|
|
935
|
+
try {
|
|
936
|
+
body = await res.json();
|
|
937
|
+
} catch {
|
|
938
|
+
body = null;
|
|
939
|
+
}
|
|
940
|
+
const parsed = opErrorBodySchema.safeParse(body);
|
|
889
941
|
return {
|
|
890
942
|
state: "error",
|
|
891
943
|
reason: (parsed.success ? parsed.data.reason : void 0) ?? `HTTP ${res.status}`
|
|
@@ -944,6 +996,7 @@ var Funnel = class Funnel {
|
|
|
944
996
|
process;
|
|
945
997
|
logger;
|
|
946
998
|
clock;
|
|
999
|
+
http;
|
|
947
1000
|
onError;
|
|
948
1001
|
constructor(props = {}) {
|
|
949
1002
|
const dir = props.dir ?? resolveFunnelDir();
|
|
@@ -952,6 +1005,7 @@ var Funnel = class Funnel {
|
|
|
952
1005
|
const process = props.process ?? new NodeFunnelProcessRunner();
|
|
953
1006
|
const clock = props.clock ?? new NodeFunnelClock();
|
|
954
1007
|
const idGenerator = props.idGenerator ?? new NodeFunnelIdGenerator();
|
|
1008
|
+
const http = props.http ?? new NodeFunnelHttpClient();
|
|
955
1009
|
this.paths = {
|
|
956
1010
|
dir,
|
|
957
1011
|
tmpDir,
|
|
@@ -961,6 +1015,7 @@ var Funnel = class Funnel {
|
|
|
961
1015
|
this.process = process;
|
|
962
1016
|
this.logger = props.logger;
|
|
963
1017
|
this.clock = clock;
|
|
1018
|
+
this.http = http;
|
|
964
1019
|
this.onError = props.onError ?? noopOnError;
|
|
965
1020
|
const store = props.store ?? new FunnelSettingsStore({
|
|
966
1021
|
path: this.paths.settings,
|
|
@@ -971,8 +1026,11 @@ var Funnel = class Funnel {
|
|
|
971
1026
|
descriptors: props.connectors ?? [],
|
|
972
1027
|
fs,
|
|
973
1028
|
process,
|
|
1029
|
+
http,
|
|
1030
|
+
clock,
|
|
974
1031
|
logger: this.logger,
|
|
975
1032
|
diagnosticLog: props.diagnosticLog,
|
|
1033
|
+
signal: props.signal,
|
|
976
1034
|
dir
|
|
977
1035
|
});
|
|
978
1036
|
this.profiles = new FunnelProfiles({
|
|
@@ -1020,13 +1078,14 @@ var Funnel = class Funnel {
|
|
|
1020
1078
|
mcp,
|
|
1021
1079
|
gateway: this.gateway,
|
|
1022
1080
|
sessions: this.profiles,
|
|
1023
|
-
guard: new
|
|
1081
|
+
guard: new FunnelFileProcessGuard({
|
|
1024
1082
|
fs,
|
|
1025
1083
|
process,
|
|
1026
1084
|
dir
|
|
1027
1085
|
}),
|
|
1028
1086
|
process,
|
|
1029
|
-
logger: this.logger
|
|
1087
|
+
logger: this.logger,
|
|
1088
|
+
dir
|
|
1030
1089
|
});
|
|
1031
1090
|
this.diagnostics = new FunnelDiagnostics({
|
|
1032
1091
|
gateway: this.gateway,
|
|
@@ -1048,9 +1107,20 @@ var Funnel = class Funnel {
|
|
|
1048
1107
|
Object.freeze(this);
|
|
1049
1108
|
}
|
|
1050
1109
|
/**
|
|
1051
|
-
* Sandboxed Funnel wired with in-memory implementations for every IO
|
|
1052
|
-
* Touches no real disk, processes, wall-clock time,
|
|
1053
|
-
*
|
|
1110
|
+
* Sandboxed Funnel wired with in-memory implementations for every IO
|
|
1111
|
+
* boundary. Touches no real disk, processes, wall-clock time, UUIDs, HTTP,
|
|
1112
|
+
* TTY prompts, or diagnostic SQLite — safe for tests and ad-hoc
|
|
1113
|
+
* experiments. Override individual fields by passing them in `props`.
|
|
1114
|
+
*
|
|
1115
|
+
* NOT covered by `inMemory()`:
|
|
1116
|
+
* - `gatewayServer()` still calls `Bun.serve` and binds a real port; use
|
|
1117
|
+
* `port: 0` to let the OS pick one. The WebSocket subscription path
|
|
1118
|
+
* also crosses the real socket.
|
|
1119
|
+
* - Flume sources opened by listeners (Slack Socket Mode, Discord
|
|
1120
|
+
* Gateway, GitHub poll) still open real WebSockets / HTTP. Pass
|
|
1121
|
+
* `flumeDeps` to the descriptor's options if a test needs them stubbed.
|
|
1122
|
+
* - `funnel.gateway` (daemon process) — `start()` still spawns a child
|
|
1123
|
+
* process; only the in-process `gatewayServer()` is sandbox-friendly.
|
|
1054
1124
|
*/
|
|
1055
1125
|
static inMemory(props = {}) {
|
|
1056
1126
|
return new Funnel({
|
|
@@ -1061,6 +1131,9 @@ var Funnel = class Funnel {
|
|
|
1061
1131
|
logger: props.logger ?? new MemoryFunnelLogger(),
|
|
1062
1132
|
clock: props.clock ?? new MemoryFunnelClock(),
|
|
1063
1133
|
idGenerator: props.idGenerator ?? new MemoryFunnelIdGenerator(),
|
|
1134
|
+
http: props.http ?? new MemoryFunnelHttpClient(),
|
|
1135
|
+
tokenPrompter: props.tokenPrompter ?? new MemoryFunnelTokenPrompter(),
|
|
1136
|
+
diagnosticLog: props.diagnosticLog ?? new MemoryConnectorDiagnosticLog(),
|
|
1064
1137
|
dir: props.dir ?? SANDBOX_DIR,
|
|
1065
1138
|
tmpDir: props.tmpDir ?? SANDBOX_TMP_DIR
|
|
1066
1139
|
});
|
|
@@ -1093,7 +1166,7 @@ var Funnel = class Funnel {
|
|
|
1093
1166
|
* independently of FunnelClaude (e.g. checking if a named profile is running).
|
|
1094
1167
|
*/
|
|
1095
1168
|
createProcessGuard() {
|
|
1096
|
-
return new
|
|
1169
|
+
return new FunnelFileProcessGuard({
|
|
1097
1170
|
fs: this.fs,
|
|
1098
1171
|
process: this.process,
|
|
1099
1172
|
dir: this.paths.dir
|
|
@@ -1181,31 +1254,6 @@ var NoopFunnelLogger = class extends FunnelLogger {
|
|
|
1181
1254
|
error() {}
|
|
1182
1255
|
};
|
|
1183
1256
|
//#endregion
|
|
1184
|
-
//#region lib/engine/http/memory-http-client.ts
|
|
1185
|
-
var MemoryFunnelHttpClient = class extends FunnelHttpClient {
|
|
1186
|
-
calls = [];
|
|
1187
|
-
handler = () => ({
|
|
1188
|
-
status: 200,
|
|
1189
|
-
body: ""
|
|
1190
|
-
});
|
|
1191
|
-
on(handler) {
|
|
1192
|
-
this.handler = handler;
|
|
1193
|
-
return this;
|
|
1194
|
-
}
|
|
1195
|
-
async fetch(request) {
|
|
1196
|
-
this.calls.push(request);
|
|
1197
|
-
const response = await this.handler(request);
|
|
1198
|
-
const status = response.status ?? 200;
|
|
1199
|
-
const body = response.body ?? "";
|
|
1200
|
-
return {
|
|
1201
|
-
status,
|
|
1202
|
-
ok: status >= 200 && status < 300,
|
|
1203
|
-
text: async () => body,
|
|
1204
|
-
json: async () => JSON.parse(body)
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
//#endregion
|
|
1209
1257
|
//#region lib/gateway/service-routes.ts
|
|
1210
1258
|
/**
|
|
1211
1259
|
* Mountable Hono app that exposes the service layer (`FunnelDiagnostics` +
|
|
@@ -1234,24 +1282,65 @@ const buildServiceRoutes = (deps) => {
|
|
|
1234
1282
|
});
|
|
1235
1283
|
app.get("/diagnostics/events", async (c) => {
|
|
1236
1284
|
const channel = c.req.query("channel") ?? null;
|
|
1285
|
+
const connector = c.req.query("connector");
|
|
1237
1286
|
const limit = Number(c.req.query("limit") ?? "20");
|
|
1238
|
-
const events = await deps.diagnostics.recentEvents(channel,
|
|
1287
|
+
const events = await deps.diagnostics.recentEvents(channel, {
|
|
1288
|
+
connector,
|
|
1289
|
+
limit
|
|
1290
|
+
});
|
|
1239
1291
|
return c.json(events);
|
|
1240
1292
|
});
|
|
1241
1293
|
app.get("/diagnostics/dropped", async (c) => {
|
|
1242
1294
|
const channel = c.req.query("channel") ?? null;
|
|
1295
|
+
const connector = c.req.query("connector");
|
|
1243
1296
|
const limit = Number(c.req.query("limit") ?? "20");
|
|
1244
|
-
const events = await deps.diagnostics.droppedEvents(channel,
|
|
1297
|
+
const events = await deps.diagnostics.droppedEvents(channel, {
|
|
1298
|
+
connector,
|
|
1299
|
+
limit
|
|
1300
|
+
});
|
|
1245
1301
|
return c.json(events);
|
|
1246
1302
|
});
|
|
1247
1303
|
app.get("/diagnostics/errors", async (c) => {
|
|
1248
1304
|
const channel = c.req.query("channel") ?? null;
|
|
1305
|
+
const connector = c.req.query("connector");
|
|
1249
1306
|
const limit = Number(c.req.query("limit") ?? "20");
|
|
1250
|
-
const errors = await deps.diagnostics.connectionErrors(channel,
|
|
1307
|
+
const errors = await deps.diagnostics.connectionErrors(channel, {
|
|
1308
|
+
connector,
|
|
1309
|
+
limit
|
|
1310
|
+
});
|
|
1251
1311
|
return c.json(errors);
|
|
1252
1312
|
});
|
|
1313
|
+
app.get("/diagnostics/raw", async (c) => {
|
|
1314
|
+
const channel = c.req.query("channel") ?? null;
|
|
1315
|
+
const connector = c.req.query("connector");
|
|
1316
|
+
const limit = Number(c.req.query("limit") ?? "20");
|
|
1317
|
+
const events = await deps.diagnostics.rawEvents(channel, {
|
|
1318
|
+
connector,
|
|
1319
|
+
limit
|
|
1320
|
+
});
|
|
1321
|
+
return c.json(events);
|
|
1322
|
+
});
|
|
1323
|
+
app.get("/diagnostics/connection", async (c) => {
|
|
1324
|
+
const channel = c.req.query("channel") ?? null;
|
|
1325
|
+
const connector = c.req.query("connector");
|
|
1326
|
+
const limit = Number(c.req.query("limit") ?? "20");
|
|
1327
|
+
const rows = await deps.diagnostics.connectionTimeline(channel, {
|
|
1328
|
+
connector,
|
|
1329
|
+
limit
|
|
1330
|
+
});
|
|
1331
|
+
return c.json(rows);
|
|
1332
|
+
});
|
|
1333
|
+
app.get("/diagnostics/logs", async (c) => {
|
|
1334
|
+
const grep = c.req.query("grep") ?? void 0;
|
|
1335
|
+
const limit = Number(c.req.query("limit") ?? "200");
|
|
1336
|
+
const result = await deps.diagnostics.recentLogs({
|
|
1337
|
+
grep,
|
|
1338
|
+
limit
|
|
1339
|
+
});
|
|
1340
|
+
return c.json(result);
|
|
1341
|
+
});
|
|
1253
1342
|
app.post("/diagnostics/replay", async (c) => {
|
|
1254
|
-
const body = await c
|
|
1343
|
+
const body = await readJsonBody(c);
|
|
1255
1344
|
const channel = typeof body.channel === "string" ? body.channel : null;
|
|
1256
1345
|
const seq = typeof body.seq === "number" ? body.seq : void 0;
|
|
1257
1346
|
if (!channel) return c.json({ error: "channel is required" }, 400);
|
|
@@ -1259,13 +1348,22 @@ const buildServiceRoutes = (deps) => {
|
|
|
1259
1348
|
return c.json(result);
|
|
1260
1349
|
});
|
|
1261
1350
|
app.post("/doctor", async (c) => {
|
|
1262
|
-
const body = await c
|
|
1351
|
+
const body = await readJsonBody(c);
|
|
1263
1352
|
const mode = body.mode === "safe" || body.mode === "aggressive" || body.mode === "off" ? body.mode : "off";
|
|
1264
1353
|
const report = await deps.doctor.run(mode);
|
|
1265
1354
|
return c.json(report);
|
|
1266
1355
|
});
|
|
1267
1356
|
return app;
|
|
1268
1357
|
};
|
|
1358
|
+
const isStringKeyedObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1359
|
+
const readJsonBody = async (c) => {
|
|
1360
|
+
try {
|
|
1361
|
+
const body = await c.req.json();
|
|
1362
|
+
return isStringKeyedObject(body) ? body : {};
|
|
1363
|
+
} catch {
|
|
1364
|
+
return {};
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1269
1367
|
//#endregion
|
|
1270
1368
|
//#region lib/cli/factory.ts
|
|
1271
1369
|
const factory = createFactory();
|
|
@@ -1627,7 +1725,7 @@ const channelsConnectorsSetHandler = factory.createHandlers(zValidator$1("param"
|
|
|
1627
1725
|
"bot-token": z.string().optional(),
|
|
1628
1726
|
"app-token": z.string().optional(),
|
|
1629
1727
|
"poll-interval": z.coerce.number().int().positive().optional()
|
|
1630
|
-
}).
|
|
1728
|
+
}).loose()), async (c) => {
|
|
1631
1729
|
const param = c.req.valid("param");
|
|
1632
1730
|
const query = c.req.valid("query");
|
|
1633
1731
|
const funnel = c.env.funnel;
|
|
@@ -1719,7 +1817,7 @@ options:
|
|
|
1719
1817
|
output / valid YAML (or raw text when the adapter returns text)`), zValidator$1("query", z.object({
|
|
1720
1818
|
method: z.string(),
|
|
1721
1819
|
path: z.string().optional()
|
|
1722
|
-
}).
|
|
1820
|
+
}).loose()), async (c) => {
|
|
1723
1821
|
const param = c.req.valid("param");
|
|
1724
1822
|
const query = c.req.valid("query");
|
|
1725
1823
|
const funnel = c.env.funnel;
|
|
@@ -1840,7 +1938,7 @@ const channelsPublishHelpHandler = factory.createHandlers((c) => c.text(help$7))
|
|
|
1840
1938
|
const querySchema = z.object({
|
|
1841
1939
|
content: z.string().min(1, { message: "--content is required" }),
|
|
1842
1940
|
connector: z.string().min(1).optional()
|
|
1843
|
-
}).
|
|
1941
|
+
}).loose();
|
|
1844
1942
|
const channelsPublishHandler = factory.createHandlers(zValidator$1("param", z.object({ channel: z.string() })), zValidator$1("query", querySchema), async (c) => {
|
|
1845
1943
|
const param = c.req.valid("param");
|
|
1846
1944
|
const query = c.req.valid("query");
|
|
@@ -2143,7 +2241,7 @@ const RESERVED_KEYS$1 = ["profile", "channel"];
|
|
|
2143
2241
|
const claudeHandler = factory.createHandlers(helpGuard(claudeHelp), zValidator$1("query", z.object({
|
|
2144
2242
|
profile: z.string().optional(),
|
|
2145
2243
|
channel: z.string().optional()
|
|
2146
|
-
}).
|
|
2244
|
+
}).loose()), async (c) => {
|
|
2147
2245
|
const query = c.req.valid("query");
|
|
2148
2246
|
const { funnel, claude, profiles, localConfig, localConfigSync } = c.env;
|
|
2149
2247
|
const userArgs = queryToCliArgs(c.req.url, RESERVED_KEYS$1);
|
|
@@ -2204,35 +2302,41 @@ const claudeHandler = factory.createHandlers(helpGuard(claudeHelp), zValidator$1
|
|
|
2204
2302
|
});
|
|
2205
2303
|
//#endregion
|
|
2206
2304
|
//#region lib/cli/routes/debug.ts
|
|
2207
|
-
const debugHelp = `funnel debug / per-channel inspection (events, drops, connection
|
|
2305
|
+
const debugHelp = `funnel debug / per-channel inspection (events, drops, connection lifecycle, logs, replay)
|
|
2208
2306
|
|
|
2209
|
-
usage / funnel debug [subcommand] [--channel <name>] [--all] [--limit <N>]
|
|
2307
|
+
usage / funnel debug [subcommand] [--channel <name>] [--connector <name>] [--all] [--limit <N>]
|
|
2210
2308
|
|
|
2211
2309
|
subcommands:
|
|
2212
2310
|
(none) / full diagnosis for one channel (or --all for every channel)
|
|
2213
2311
|
events / last N processed events with outcome
|
|
2214
2312
|
dropped / events filtered out (skip:*) with payload
|
|
2215
|
-
errors / listener auth-failed and error
|
|
2313
|
+
errors / listener auth-failed and error rows
|
|
2314
|
+
connection / full connection lifecycle (started / connected / disconnected / stopped + errors)
|
|
2315
|
+
raw / raw inbound events before any processing (pre-processor drops)
|
|
2316
|
+
logs / tail of the gateway daemon log (FunnelLogger + flume internal logs)
|
|
2216
2317
|
replay / re-send a past event into a channel
|
|
2217
2318
|
|
|
2218
2319
|
options:
|
|
2219
2320
|
--channel <name> / channel to inspect (auto-selected when only one exists)
|
|
2321
|
+
--connector <name> / restrict events/dropped/errors/connection/raw to one connector
|
|
2220
2322
|
--all / diagnose every channel
|
|
2221
|
-
--limit <N> / number of rows (default 5 for diagnosis, 20 for subcommands)
|
|
2323
|
+
--limit <N> / number of rows (default 5 for diagnosis, 20 for subcommands, 200 for logs)
|
|
2222
2324
|
|
|
2223
2325
|
For the common case, prefer fnl doctor — it runs a full diagnosis and can apply
|
|
2224
2326
|
safe fixes in one shot. fnl debug is the lower-level view.
|
|
2225
2327
|
|
|
2226
2328
|
output / valid YAML
|
|
2227
2329
|
|
|
2228
|
-
programmable / funnel.diagnostics.diagnose() / .diagnoseAll() / .recentEvents() / .droppedEvents() / .connectionErrors() / .replay()
|
|
2330
|
+
programmable / funnel.diagnostics.diagnose() / .diagnoseAll() / .recentEvents() / .droppedEvents() / .rawEvents() / .connectionErrors() / .connectionTimeline() / .recentLogs() / .replay()
|
|
2229
2331
|
|
|
2230
2332
|
examples:
|
|
2231
2333
|
funnel debug
|
|
2232
2334
|
funnel debug --all
|
|
2233
2335
|
funnel debug --channel ops
|
|
2234
2336
|
funnel debug events --channel ops --limit 50
|
|
2235
|
-
funnel debug dropped --channel ops
|
|
2337
|
+
funnel debug dropped --channel ops
|
|
2338
|
+
funnel debug connection --channel ops --connector slack-prod
|
|
2339
|
+
funnel debug logs --grep slack`;
|
|
2236
2340
|
const debugEventsHelp = `funnel debug events / last N processed events
|
|
2237
2341
|
|
|
2238
2342
|
usage / funnel debug events [--channel <name>] [--limit <N>]
|
|
@@ -2256,8 +2360,30 @@ const debugReplayHelp = `funnel debug replay / re-publish a past event into a ch
|
|
|
2256
2360
|
usage / funnel debug replay --channel <name> [--seq <N>]
|
|
2257
2361
|
|
|
2258
2362
|
programmable / funnel.diagnostics.replay(channel, seq?)`;
|
|
2363
|
+
const debugRawHelp = `funnel debug raw / raw inbound events before any processing
|
|
2364
|
+
|
|
2365
|
+
usage / funnel debug raw [--channel <name>] [--connector <name>] [--limit <N>]
|
|
2366
|
+
|
|
2367
|
+
shows every event the listener received, including those dropped pre-processor
|
|
2368
|
+
(envelope shape changes, pre-READY, malformed payloads).
|
|
2369
|
+
|
|
2370
|
+
programmable / funnel.diagnostics.rawEvents(channel, { connector, limit })`;
|
|
2371
|
+
const debugConnectionHelp = `funnel debug connection / full lifecycle (started / connected / disconnected / stopped + auth-failed / error)
|
|
2372
|
+
|
|
2373
|
+
usage / funnel debug connection [--channel <name>] [--connector <name>] [--limit <N>]
|
|
2374
|
+
|
|
2375
|
+
programmable / funnel.diagnostics.connectionTimeline(channel, { connector, limit })`;
|
|
2376
|
+
const debugLogsHelp = `funnel debug logs / tail of the gateway daemon log
|
|
2377
|
+
|
|
2378
|
+
usage / funnel debug logs [--grep <substr>] [--limit <N>]
|
|
2379
|
+
|
|
2380
|
+
shows funnel.log entries — structured FunnelLogger output and forwarded flume
|
|
2381
|
+
logs. Use --grep to filter (case-insensitive substring).
|
|
2382
|
+
|
|
2383
|
+
programmable / funnel.diagnostics.recentLogs({ grep, limit })`;
|
|
2259
2384
|
const channelLimitQuery = z.object({
|
|
2260
2385
|
channel: z.string().optional(),
|
|
2386
|
+
connector: z.string().optional(),
|
|
2261
2387
|
limit: z.string().optional()
|
|
2262
2388
|
});
|
|
2263
2389
|
const resolveTargetChannel = (c, channelArg) => {
|
|
@@ -2332,7 +2458,10 @@ const debugEventsHandler = factory.createHandlers(helpGuard(debugEventsHelp), zV
|
|
|
2332
2458
|
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
2333
2459
|
const resolved = resolveTargetChannel(c, query.channel);
|
|
2334
2460
|
if (resolved.kind === "error") return c.text(renderYaml(resolved.payload));
|
|
2335
|
-
const events = await funnel.diagnostics.recentEvents(resolved.name,
|
|
2461
|
+
const events = await funnel.diagnostics.recentEvents(resolved.name, {
|
|
2462
|
+
connector: query.connector,
|
|
2463
|
+
limit
|
|
2464
|
+
});
|
|
2336
2465
|
return c.text(renderYaml({ events }));
|
|
2337
2466
|
});
|
|
2338
2467
|
const debugDroppedHandler = factory.createHandlers(helpGuard(debugDroppedHelp), zValidator$1("query", channelLimitQuery), async (c) => {
|
|
@@ -2341,7 +2470,10 @@ const debugDroppedHandler = factory.createHandlers(helpGuard(debugDroppedHelp),
|
|
|
2341
2470
|
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
2342
2471
|
const resolved = resolveTargetChannel(c, query.channel);
|
|
2343
2472
|
if (resolved.kind === "error") return c.text(renderYaml(resolved.payload));
|
|
2344
|
-
const events = await funnel.diagnostics.droppedEvents(resolved.name,
|
|
2473
|
+
const events = await funnel.diagnostics.droppedEvents(resolved.name, {
|
|
2474
|
+
connector: query.connector,
|
|
2475
|
+
limit
|
|
2476
|
+
});
|
|
2345
2477
|
return c.text(renderYaml({ dropped: events }));
|
|
2346
2478
|
});
|
|
2347
2479
|
const debugErrorsHandler = factory.createHandlers(helpGuard(debugErrorsHelp), zValidator$1("query", channelLimitQuery), async (c) => {
|
|
@@ -2350,7 +2482,10 @@ const debugErrorsHandler = factory.createHandlers(helpGuard(debugErrorsHelp), zV
|
|
|
2350
2482
|
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
2351
2483
|
const resolved = resolveTargetChannel(c, query.channel);
|
|
2352
2484
|
if (resolved.kind === "error") return c.text(renderYaml(resolved.payload));
|
|
2353
|
-
const errors = await funnel.diagnostics.connectionErrors(resolved.name,
|
|
2485
|
+
const errors = await funnel.diagnostics.connectionErrors(resolved.name, {
|
|
2486
|
+
connector: query.connector,
|
|
2487
|
+
limit
|
|
2488
|
+
});
|
|
2354
2489
|
return c.text(renderYaml({ errors }));
|
|
2355
2490
|
});
|
|
2356
2491
|
const debugReplayHandler = factory.createHandlers(helpGuard(debugReplayHelp), zValidator$1("query", z.object({
|
|
@@ -2366,6 +2501,43 @@ const debugReplayHandler = factory.createHandlers(helpGuard(debugReplayHelp), zV
|
|
|
2366
2501
|
const result = await funnel.diagnostics.replay(resolved.name, seq);
|
|
2367
2502
|
return c.text(renderYaml(result));
|
|
2368
2503
|
});
|
|
2504
|
+
const debugRawHandler = factory.createHandlers(helpGuard(debugRawHelp), zValidator$1("query", channelLimitQuery), async (c) => {
|
|
2505
|
+
const query = c.req.valid("query");
|
|
2506
|
+
const funnel = c.env.funnel;
|
|
2507
|
+
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
2508
|
+
const resolved = resolveTargetChannel(c, query.channel);
|
|
2509
|
+
if (resolved.kind === "error") return c.text(renderYaml(resolved.payload));
|
|
2510
|
+
const events = await funnel.diagnostics.rawEvents(resolved.name, {
|
|
2511
|
+
connector: query.connector,
|
|
2512
|
+
limit
|
|
2513
|
+
});
|
|
2514
|
+
return c.text(renderYaml({ raw: events }));
|
|
2515
|
+
});
|
|
2516
|
+
const debugConnectionHandler = factory.createHandlers(helpGuard(debugConnectionHelp), zValidator$1("query", channelLimitQuery), async (c) => {
|
|
2517
|
+
const query = c.req.valid("query");
|
|
2518
|
+
const funnel = c.env.funnel;
|
|
2519
|
+
const limit = query.limit ? Math.max(1, Number(query.limit)) : 20;
|
|
2520
|
+
const resolved = resolveTargetChannel(c, query.channel);
|
|
2521
|
+
if (resolved.kind === "error") return c.text(renderYaml(resolved.payload));
|
|
2522
|
+
const timeline = await funnel.diagnostics.connectionTimeline(resolved.name, {
|
|
2523
|
+
connector: query.connector,
|
|
2524
|
+
limit
|
|
2525
|
+
});
|
|
2526
|
+
return c.text(renderYaml({ connection: timeline }));
|
|
2527
|
+
});
|
|
2528
|
+
const debugLogsHandler = factory.createHandlers(helpGuard(debugLogsHelp), zValidator$1("query", z.object({
|
|
2529
|
+
grep: z.string().optional(),
|
|
2530
|
+
limit: z.string().optional()
|
|
2531
|
+
})), async (c) => {
|
|
2532
|
+
const query = c.req.valid("query");
|
|
2533
|
+
const funnel = c.env.funnel;
|
|
2534
|
+
const limit = query.limit ? Math.max(1, Number(query.limit)) : 200;
|
|
2535
|
+
const result = await funnel.diagnostics.recentLogs({
|
|
2536
|
+
grep: query.grep,
|
|
2537
|
+
limit
|
|
2538
|
+
});
|
|
2539
|
+
return c.text(renderYaml(result));
|
|
2540
|
+
});
|
|
2369
2541
|
const docsIndexHandler = factory.createHandlers(helpGuard(`funnel docs / embedded documentation
|
|
2370
2542
|
|
|
2371
2543
|
usage / funnel docs [topic]
|
|
@@ -2452,14 +2624,21 @@ examples:
|
|
|
2452
2624
|
funnel gateway logs
|
|
2453
2625
|
funnel gateway sql --preset recent`;
|
|
2454
2626
|
const renderGatewayStatus = async (c) => {
|
|
2455
|
-
const
|
|
2627
|
+
const funnel = c.env.funnel;
|
|
2628
|
+
const status = funnel.gateway.getStatus();
|
|
2456
2629
|
if (!status.running) return c.text(renderYaml({ running: false }), 503);
|
|
2457
|
-
const
|
|
2458
|
-
|
|
2630
|
+
const token = funnel.gatewayToken.read();
|
|
2631
|
+
let res = null;
|
|
2632
|
+
try {
|
|
2633
|
+
res = await loopbackFetch(`${gatewayLoopbackUrl(status.port)}/status`, { headers: token ? { authorization: `Bearer ${token}` } : {} });
|
|
2634
|
+
} catch {
|
|
2635
|
+
res = null;
|
|
2636
|
+
}
|
|
2637
|
+
if (!res || !res.ok) return c.text(renderYaml({
|
|
2459
2638
|
running: true,
|
|
2460
2639
|
pid: status.pid,
|
|
2461
2640
|
port: status.port,
|
|
2462
|
-
error: "
|
|
2641
|
+
error: res ? `status check failed (${res.status})` : "status check failed"
|
|
2463
2642
|
}));
|
|
2464
2643
|
const data = await res.json();
|
|
2465
2644
|
return c.text(renderYaml({
|
|
@@ -2769,7 +2948,13 @@ const HEALTH_POLL_INTERVAL_MS = 100;
|
|
|
2769
2948
|
const waitForHealth = async (port) => {
|
|
2770
2949
|
const deadline = Date.now() + HEALTH_TIMEOUT_MS;
|
|
2771
2950
|
while (Date.now() < deadline) {
|
|
2772
|
-
|
|
2951
|
+
let ok = false;
|
|
2952
|
+
try {
|
|
2953
|
+
ok = (await loopbackFetch(`${gatewayLoopbackUrl(port)}/health`)).ok;
|
|
2954
|
+
} catch {
|
|
2955
|
+
ok = false;
|
|
2956
|
+
}
|
|
2957
|
+
if (ok) return true;
|
|
2773
2958
|
await new Promise((resolve) => setTimeout(resolve, HEALTH_POLL_INTERVAL_MS));
|
|
2774
2959
|
}
|
|
2775
2960
|
return false;
|
|
@@ -2937,7 +3122,7 @@ const launchHelp = `funnel profiles <name> run — launch a profile (sugar for f
|
|
|
2937
3122
|
usage: funnel profiles <name> run [additional claude args...]
|
|
2938
3123
|
funnel profiles <name> (alias)`;
|
|
2939
3124
|
const RESERVED_KEYS = [];
|
|
2940
|
-
const profilesLaunchHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), helpGuard(launchHelp), zValidator$1("query", z.object({}).
|
|
3125
|
+
const profilesLaunchHandler = factory.createHandlers(zValidator$1("param", z.object({ profile: z.string() })), helpGuard(launchHelp), zValidator$1("query", z.object({}).loose()), async (c) => {
|
|
2941
3126
|
const param = c.req.valid("param");
|
|
2942
3127
|
c.env.funnel;
|
|
2943
3128
|
const { profiles, claude } = c.env;
|
|
@@ -3113,7 +3298,13 @@ const buildStatusReport = async (funnel, profiles) => {
|
|
|
3113
3298
|
const gatewayStatus = funnel.gateway.getStatus();
|
|
3114
3299
|
let gatewayData = null;
|
|
3115
3300
|
if (gatewayStatus.running) {
|
|
3116
|
-
const
|
|
3301
|
+
const token = funnel.gatewayToken.read();
|
|
3302
|
+
let res = null;
|
|
3303
|
+
try {
|
|
3304
|
+
res = await loopbackFetch(`${gatewayLoopbackUrl(gatewayStatus.port)}/status`, { headers: token ? { authorization: `Bearer ${token}` } : {} });
|
|
3305
|
+
} catch {
|
|
3306
|
+
res = null;
|
|
3307
|
+
}
|
|
3117
3308
|
if (res && res.ok) {
|
|
3118
3309
|
const body = await res.json();
|
|
3119
3310
|
if (isGatewayStatus(body)) gatewayData = body;
|
|
@@ -3215,6 +3406,6 @@ const updateHandler = factory.createHandlers(helpGuard(updateHelp), async (c) =>
|
|
|
3215
3406
|
const routes = factory.createApp().onError((error, c) => {
|
|
3216
3407
|
if (error instanceof HTTPException) return c.text(error.message, error.status);
|
|
3217
3408
|
return c.text(`error: ${error instanceof Error ? error.message : String(error)}`, 400);
|
|
3218
|
-
}).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("/docs", ...docsIndexHandler).get("/docs/:topic", ...docsTopicHandler).get("/doctor", ...doctorHandler).get("/schema", ...schemaHandler).get("/status", ...statusHandler).get("/update", ...updateHandler);
|
|
3409
|
+
}).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("/debug/raw", ...debugRawHandler).get("/debug/connection", ...debugConnectionHandler).get("/debug/logs", ...debugLogsHandler).get("/docs", ...docsIndexHandler).get("/docs/:topic", ...docsTopicHandler).get("/doctor", ...doctorHandler).get("/schema", ...schemaHandler).get("/status", ...statusHandler).get("/update", ...updateHandler);
|
|
3219
3410
|
//#endregion
|
|
3220
|
-
export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, DEFAULT_GATEWAY_PORT, DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, Funnel, FunnelBroadcaster, FunnelChannelPublisher, FunnelChannels, FunnelClock, FunnelConnectorAdapter, FunnelConnectorListener, FunnelConnectorRegistry, FunnelDiagnostics, FunnelDocs, FunnelDoctor, FunnelEventLog, FunnelFileSystem, FunnelGateway, FunnelGatewayServer, FunnelGatewayToken, FunnelHttpClient, FunnelIdGenerator,
|
|
3411
|
+
export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, ConnectorDiagnosticSqlReader, DEFAULT_GATEWAY_PORT, DEFAULT_GATEWAY_TOKEN_PATH, FUNNEL_DIR, Funnel, FunnelAuthFailedError, FunnelBroadcaster, FunnelChannelAlreadyExistsError, FunnelChannelNotFoundError, FunnelChannelPublisher, FunnelChannels, FunnelClock, FunnelConnectorAdapter, FunnelConnectorListener, FunnelConnectorNotFoundError, FunnelConnectorRegistry, FunnelConnectorTypeMismatchError, FunnelDiagnostics, FunnelDocs, FunnelDoctor, FunnelError, FunnelEventLog, FunnelFileSystem, FunnelGateway, FunnelGatewayBindError, FunnelGatewayServer, FunnelGatewayToken, FunnelHttpClient, FunnelIdGenerator, FunnelListenerRegistry, FunnelListenersClient, FunnelLogger, FunnelProcessRunner, FunnelRecovery, FunnelSettingsReader, FunnelSettingsStore, FunnelSlackEventProcessor, FunnelTokenCollisionError, FunnelTokenPrompter, MemoryConnectorDiagnosticLog, MemoryFunnelClock, MemoryFunnelEventLog, MemoryFunnelFileSystem, MemoryFunnelHttpClient, MemoryFunnelIdGenerator, MemoryFunnelLogger, MemoryFunnelProcessRunner, MemoryFunnelTokenPrompter, MockFunnelSettingsReader, NodeFunnelClock, NodeFunnelFileSystem, NodeFunnelHttpClient, NodeFunnelIdGenerator, NodeFunnelLogger, NodeFunnelProcessRunner, NodeFunnelTokenPrompter, NoopFunnelLogger, SETTINGS_PATH, SETTINGS_VERSION, SqliteConnectorDiagnosticLog, SqliteFunnelEventLog, baseConnectorConfigSchema, buildServiceRoutes, channelConfigSchema, channelDeliveryModeSchema, channelWsProtocols, channelWsUrl, routes as cliRoutes, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, createSettings, discordConnectorSchema, factory, funnelEventSchema, gatewayLoopbackUrl, ghConnectorSchema, previewOf, profileConfigSchema, publishRequestSchema, publishResponseSchema, queryRows, queryToCliArgs, resolveFunnelDir, resolveFunnelPort, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema, settingsSchema, slackConnectorSchema, toDiagnosticConnectionError, toDiagnosticEvent, toRequest };
|