@interactive-inc/claude-funnel 0.66.0 → 0.68.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 +31 -6
- package/dist/bin.js +292 -383
- package/dist/channel.d.ts +76 -52
- package/dist/channel.js +82 -47
- package/dist/{channels-DR90RmUP.d.ts → channels-B0cifCfw.d.ts} +6 -6
- package/dist/claude.d.ts +11 -8
- package/dist/claude.js +31 -15
- package/dist/connector-descriptor-D4XtEsH8.d.ts +215 -0
- package/dist/{connector-diagnostics-recorder-COtNEmUp.js → connector-diagnostics-recorder-BmOijU1p.js} +1 -0
- package/dist/connectors/discord.d.ts +11 -8
- package/dist/connectors/discord.js +2 -2
- package/dist/connectors/gh.d.ts +7 -6
- package/dist/connectors/gh.js +2 -2
- package/dist/connectors/schedule.d.ts +21 -17
- package/dist/connectors/schedule.js +3 -3
- package/dist/connectors/slack.d.ts +15 -10
- package/dist/connectors/slack.js +3 -3
- package/dist/diagnostic-event-CxMM5Bl2.js +78 -0
- package/dist/diagnostic-log-BsAg5qZK.d.ts +186 -0
- package/dist/diagnostics.d.ts +2 -2
- package/dist/diagnostics.js +3 -2
- package/dist/{discord-connector-DXARcO6w.js → discord-connector-CAcWifJn.js} +6 -6
- package/dist/{discord-connector-schema-D1dyxhXk.d.ts → discord-connector-schema-BaBZYHxK.d.ts} +0 -1
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +1 -1
- package/dist/event-log.d.ts +2 -0
- package/dist/event-log.js +3 -0
- package/dist/{file-process-guard-CiBwXjzm.d.ts → file-process-guard-D-QatrwL.d.ts} +22 -18
- package/dist/{file-system-DHgbuTFb.d.ts → file-system-BNtl997T.d.ts} +2 -1
- package/dist/{flume-source-listener-FzR5lGpL.d.ts → flume-source-listener-Ba8_1Mg-.d.ts} +9 -6
- package/dist/{funnel-diagnostics-Cvk6Sk4x.js → funnel-diagnostics-CAG7hJP0.js} +85 -141
- package/dist/{funnel-diagnostics-kbd27d7I.d.ts → funnel-diagnostics-CDrZznTB.d.ts} +18 -18
- package/dist/{funnel-docs-C-ge0MuB.js → funnel-docs-CI4hMkhq.js} +12 -3
- package/dist/{funnel-doctor-CVTNUtDn.d.ts → funnel-doctor-CY4S5cnz.d.ts} +20 -9
- package/dist/{funnel-doctor-XrI2GBH8.js → funnel-doctor-Csw5qjcH.js} +1 -0
- package/dist/{funnel-error-0t1MK1R6.js → funnel-error-DoSBCogD.js} +13 -0
- package/dist/{funnel-recovery-D8MkiTpk.d.ts → funnel-recovery-BwcSNNSN.d.ts} +1 -2
- package/dist/{funnel-recovery-DKnEutUS.js → funnel-recovery-DYbSYGWC.js} +1 -0
- package/dist/gateway/daemon.js +60 -139
- package/dist/gateway.d.ts +2 -3
- package/dist/gateway.js +2 -2
- package/dist/{gh-connector-BUGCOEWS.js → gh-connector-DMwI6zJA.js} +5 -5
- package/dist/{gh-connector-schema-BCHEqpUg.d.ts → gh-connector-schema-BrNsCl1T.d.ts} +0 -1
- package/dist/{index-BU1TtuFy.d.ts → index-fHrKn4jm.d.ts} +210 -40
- package/dist/index.d.ts +125 -115
- package/dist/index.js +242 -57
- package/dist/{local-config-json-schema-DexV8vX3.js → local-config-json-schema-_-YVHX4c.js} +4 -4
- package/dist/local-config.d.ts +2 -3
- package/dist/local-config.js +1 -1
- package/dist/logger.d.ts +17 -265
- package/dist/logger.js +6 -141
- package/dist/memory-event-log-BY8XjFzi.js +147 -0
- package/dist/{memory-token-prompter-D0kIdhar.d.ts → memory-token-prompter-B20tl_aU.d.ts} +2 -3
- package/dist/{node-file-system-BOXIHW_Q.js → node-file-system-xJx7dymE.js} +11 -3
- package/dist/{profiles-M24JrcOh.d.ts → profiles-CePRnv3F.d.ts} +3 -4
- package/dist/{profiles-ZHLONml4.js → profiles-KpN7_gBw.js} +19 -5
- 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/{schedule-connector-9k3gOIgl.js → schedule-connector-BXUOYgwZ.js} +63 -99
- package/dist/schedule-connector-schema-CNV8PgWX.js +137 -0
- package/dist/schedule-connector-schema-Tq5kJul-.d.ts +127 -0
- package/dist/{settings-reader-BnKIbmsD.d.ts → settings-reader-CyTpbAre.d.ts} +1 -2
- package/dist/{settings-schema-IShSMm5G.d.ts → settings-schema-B-CKLuST.d.ts} +0 -1
- package/dist/{settings-store-ccv291GV.js → settings-store-ZFWBhOnp.js} +12 -3
- package/dist/{slack-connector-CxpWagbT.js → slack-connector-CC3V2VXQ.js} +10 -9
- package/dist/{slack-event-processor-xFDG3US0.js → slack-event-processor-Cs_YuI3w.js} +2 -2
- package/dist/{slack-event-processor-BDTr62cN.d.ts → slack-event-processor-DmAlq4cV.d.ts} +0 -1
- package/dist/{slot-fields-D-pvMgTK.js → slot-fields-eCWizJqS.js} +4 -4
- package/dist/{sqlite-diagnostic-log-CwhXE9Qh.js → sqlite-diagnostic-log-Qs4cf9I_.js} +92 -100
- package/dist/sqlite-event-log-BYgCFMxC.d.ts +284 -0
- package/dist/{funnel-log-sqlite-sink-DLYkY0pZ.js → sqlite-event-log-eZuXKszJ.js} +53 -12
- package/dist/{yaml-render-DVEtYuF1.js → yaml-render-DxxRQT-V.js} +3 -3
- package/package.json +25 -10
- package/dist/broadcaster-DVNcw00K.d.ts +0 -138
- package/dist/connector-descriptor-CmyHlEiT.d.ts +0 -380
- package/dist/diagnostic-sql-reader-oXZnWFf_.js +0 -83
- package/dist/file-system-Wvzc2ePY.js +0 -9
- package/dist/logger-B6iyNbxM.js +0 -9
- package/dist/schedule-connector-schema-D7ZEKvit.d.ts +0 -49
- package/dist/schedule-connector-schema-DKEPZnVv.js +0 -31
- package/dist/settings-reader-9FcX3qS1.js +0 -11
- /package/dist/{connector-adapter-Dvs8N7ew.js → connector-adapter-BL2ehMej.js} +0 -0
- /package/dist/{connector-listener-mPGZYa8e.js → connector-listener-BcQMzY6Q.js} +0 -0
- /package/dist/{discord-connector-schema-B4YpWpR3.js → discord-connector-schema-saAGuPmJ.js} +0 -0
- /package/dist/{error-message-of-ColuYmAk.js → error-message-of-BG4ezZ_s.js} +0 -0
- /package/dist/{funnel-docs-Cd66sKyV.d.ts → funnel-docs-BJWawfWI.d.ts} +0 -0
- /package/dist/{gh-connector-schema-CAqIhzGr.js → gh-connector-schema-DmZTrzUu.js} +0 -0
- /package/dist/{logger-Bc_Ooypb.d.ts → logger-mMZvblTx.d.ts} +0 -0
- /package/dist/{loopback-fetch-CVNuN3YZ.js → loopback-fetch-HP-bo9Kv.js} +0 -0
- /package/dist/{node-http-client-u00atiKx.js → node-http-client-SX3xFh4h.js} +0 -0
- /package/dist/{node-process-runner-DxTvycoK.js → node-process-runner-D4rzPl8Q.js} +0 -0
- /package/dist/{resolve-connector-token-DxDG9mhf.js → resolve-connector-token-BwiJCu82.js} +0 -0
- /package/dist/{slack-connector-schema-Dem8to4P.js → slack-connector-schema-EGVuuqSX.js} +0 -0
|
@@ -1,58 +1,8 @@
|
|
|
1
|
-
import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-
|
|
2
|
-
import { t as errorMessageOf } from "./error-message-of-
|
|
3
|
-
import { t as
|
|
1
|
+
import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-HP-bo9Kv.js";
|
|
2
|
+
import { t as errorMessageOf } from "./error-message-of-BG4ezZ_s.js";
|
|
3
|
+
import { n as diagnosticEventOfProcessed, r as diagnosticEventOfRaw, t as diagnosticConnectionEventOf } from "./diagnostic-event-CxMM5Bl2.js";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
//#region lib/services/diagnostics/diagnostic-event.ts
|
|
7
|
-
const stringOrNull = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
8
|
-
const numberOrNull = (value) => typeof value === "number" ? value : null;
|
|
9
|
-
const stringOr = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
10
|
-
const isStringKeyedObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11
|
-
const parsePayloadObject = (payload) => {
|
|
12
|
-
if (payload === null) return null;
|
|
13
|
-
try {
|
|
14
|
-
const parsed = JSON.parse(payload);
|
|
15
|
-
if (isStringKeyedObject(parsed)) return parsed;
|
|
16
|
-
} catch {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
return null;
|
|
20
|
-
};
|
|
21
|
-
const truncate = (text, max) => text.length <= max ? text : `${text.slice(0, max)}…`;
|
|
22
|
-
const previewOf = (payload) => {
|
|
23
|
-
if (typeof payload !== "string" || payload.length === 0) return null;
|
|
24
|
-
const parsed = parsePayloadObject(payload);
|
|
25
|
-
if (parsed !== null && "text" in parsed) return truncate(String(parsed.text), 60);
|
|
26
|
-
return truncate(payload, 60);
|
|
27
|
-
};
|
|
28
|
-
const toDiagnosticEvent = (row) => {
|
|
29
|
-
const payload = stringOrNull(row.payload);
|
|
30
|
-
return {
|
|
31
|
-
seq: numberOrNull(row.seq),
|
|
32
|
-
ts: numberOrNull(row.ts),
|
|
33
|
-
type: stringOr(row.type, "?"),
|
|
34
|
-
outcome: stringOr(row.outcome, "?"),
|
|
35
|
-
eventId: stringOrNull(row.event_id),
|
|
36
|
-
payload,
|
|
37
|
-
payloadParsed: parsePayloadObject(payload),
|
|
38
|
-
preview: previewOf(row.payload)
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
const toDiagnosticConnectionError = (row) => ({
|
|
42
|
-
seq: numberOrNull(row.seq),
|
|
43
|
-
ts: numberOrNull(row.ts),
|
|
44
|
-
type: stringOr(row.type, "?"),
|
|
45
|
-
status: stringOr(row.status, "?"),
|
|
46
|
-
detail: stringOrNull(row.detail)
|
|
47
|
-
});
|
|
48
|
-
const queryRows = (reader, sql, params) => {
|
|
49
|
-
try {
|
|
50
|
-
return reader.query(sql, params);
|
|
51
|
-
} finally {
|
|
52
|
-
reader.close();
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
//#endregion
|
|
56
6
|
//#region lib/services/diagnostics/funnel-diagnostics.ts
|
|
57
7
|
const isGatewayStatusResponse = (value) => {
|
|
58
8
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -164,6 +114,7 @@ const diagnoseSlackEventSubscriptionGap = (report) => {
|
|
|
164
114
|
* mutation; pair with FunnelRecovery for self-healing actions.
|
|
165
115
|
*/
|
|
166
116
|
var FunnelDiagnostics = class {
|
|
117
|
+
props;
|
|
167
118
|
constructor(props) {
|
|
168
119
|
this.props = props;
|
|
169
120
|
Object.freeze(this);
|
|
@@ -173,14 +124,12 @@ var FunnelDiagnostics = class {
|
|
|
173
124
|
const target = channelName ? channels.find((ch) => ch.name === channelName) ?? null : channels[0] ?? null;
|
|
174
125
|
if (!target) return null;
|
|
175
126
|
const gatewayProbe = await this.fetchGatewayStatus();
|
|
176
|
-
|
|
177
|
-
return this.buildChannelDiagnosis(target, gatewayProbe, store, 5);
|
|
127
|
+
return this.buildChannelDiagnosis(target, gatewayProbe, 5);
|
|
178
128
|
}
|
|
179
129
|
async diagnoseAll() {
|
|
180
130
|
const channels = this.props.channels.list();
|
|
181
131
|
const gatewayProbe = await this.fetchGatewayStatus();
|
|
182
|
-
const
|
|
183
|
-
const reports = await Promise.all(channels.map((ch) => this.buildChannelDiagnosis(ch, gatewayProbe, store, 5)));
|
|
132
|
+
const reports = await Promise.all(channels.map((ch) => this.buildChannelDiagnosis(ch, gatewayProbe, 5)));
|
|
184
133
|
const errorChannels = reports.filter((r) => r.diagnosis.status === "error").map((r) => r.channel);
|
|
185
134
|
const warnChannels = reports.filter((r) => r.diagnosis.status === "warn").map((r) => r.channel);
|
|
186
135
|
const okChannels = reports.filter((r) => r.diagnosis.status === "ok").map((r) => r.channel);
|
|
@@ -200,20 +149,22 @@ var FunnelDiagnostics = class {
|
|
|
200
149
|
}
|
|
201
150
|
async recentEvents(channelName, options = {}) {
|
|
202
151
|
const limit = options.limit ?? 20;
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
152
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
153
|
+
if (scope === null) return [];
|
|
154
|
+
return this.queryLog((log) => log.queryProcessed({
|
|
155
|
+
...scope,
|
|
156
|
+
limit
|
|
157
|
+
})).map(diagnosticEventOfProcessed);
|
|
208
158
|
}
|
|
209
159
|
async droppedEvents(channelName, options = {}) {
|
|
210
160
|
const limit = options.limit ?? 20;
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
161
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
162
|
+
if (scope === null) return [];
|
|
163
|
+
return this.queryLog((log) => log.queryProcessed({
|
|
164
|
+
...scope,
|
|
165
|
+
outcomePrefix: "skip:",
|
|
166
|
+
limit
|
|
167
|
+
})).map(diagnosticEventOfProcessed);
|
|
217
168
|
}
|
|
218
169
|
/**
|
|
219
170
|
* Raw inbound rows the connector recorded before any processing. The most
|
|
@@ -223,20 +174,22 @@ var FunnelDiagnostics = class {
|
|
|
223
174
|
*/
|
|
224
175
|
async rawEvents(channelName, options = {}) {
|
|
225
176
|
const limit = options.limit ?? 20;
|
|
226
|
-
const
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
177
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
178
|
+
if (scope === null) return [];
|
|
179
|
+
return this.queryLog((log) => log.queryRaw({
|
|
180
|
+
...scope,
|
|
181
|
+
limit
|
|
182
|
+
})).map(diagnosticEventOfRaw);
|
|
231
183
|
}
|
|
232
184
|
async connectionErrors(channelName, options = {}) {
|
|
233
185
|
const limit = options.limit ?? 20;
|
|
234
|
-
const
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
186
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
187
|
+
if (scope === null) return [];
|
|
188
|
+
return this.queryLog((log) => log.queryConnection({
|
|
189
|
+
...scope,
|
|
190
|
+
statuses: ["auth-failed", "error"],
|
|
191
|
+
limit
|
|
192
|
+
})).map(diagnosticConnectionEventOf);
|
|
240
193
|
}
|
|
241
194
|
/**
|
|
242
195
|
* Full connection lifecycle for one channel/connector — started, connected,
|
|
@@ -247,11 +200,12 @@ var FunnelDiagnostics = class {
|
|
|
247
200
|
*/
|
|
248
201
|
async connectionTimeline(channelName, options = {}) {
|
|
249
202
|
const limit = options.limit ?? 20;
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
203
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
204
|
+
if (scope === null) return [];
|
|
205
|
+
return this.queryLog((log) => log.queryConnection({
|
|
206
|
+
...scope,
|
|
207
|
+
limit
|
|
208
|
+
})).map(diagnosticConnectionEventOf);
|
|
255
209
|
}
|
|
256
210
|
/**
|
|
257
211
|
* Tail of `~/.funnel/.../funnel.log`. Use when a flume internal log (e.g.
|
|
@@ -292,28 +246,30 @@ var FunnelDiagnostics = class {
|
|
|
292
246
|
async replay(channelName, seq) {
|
|
293
247
|
const channel = this.props.channels.list().find((ch) => ch.name === channelName);
|
|
294
248
|
if (!channel) return { state: "not-found" };
|
|
295
|
-
|
|
296
|
-
if (!store) return {
|
|
249
|
+
if (!this.props.diagnosticLog) return {
|
|
297
250
|
state: "error",
|
|
298
|
-
reason: "no diagnostic
|
|
251
|
+
reason: "no diagnostic log configured"
|
|
299
252
|
};
|
|
300
|
-
const
|
|
301
|
-
|
|
253
|
+
const rows = this.queryLogResult((log) => log.queryProcessed({
|
|
254
|
+
channelId: channel.id,
|
|
255
|
+
...seq !== void 0 ? { seq } : { outcomePrefix: "emitted" },
|
|
256
|
+
limit: 1
|
|
257
|
+
}));
|
|
302
258
|
if (rows instanceof Error) return {
|
|
303
259
|
state: "error",
|
|
304
260
|
reason: rows.message
|
|
305
261
|
};
|
|
306
262
|
const firstRow = rows[0];
|
|
307
263
|
if (!firstRow) return { state: "not-found" };
|
|
308
|
-
const replaySeq =
|
|
309
|
-
const eventId =
|
|
310
|
-
const connectorId =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
264
|
+
const replaySeq = firstRow.seq;
|
|
265
|
+
const eventId = firstRow.eventId;
|
|
266
|
+
const connectorId = firstRow.connectorId;
|
|
267
|
+
const processedContent = firstRow.payload.length > 0 ? firstRow.payload : null;
|
|
268
|
+
const rawContent = processedContent === null ? this.queryLog((log) => log.queryRaw({
|
|
269
|
+
eventId,
|
|
270
|
+
limit: 1
|
|
271
|
+
}))[0]?.payload ?? null : null;
|
|
272
|
+
const content = processedContent ?? rawContent;
|
|
317
273
|
if (!content) return {
|
|
318
274
|
state: "error",
|
|
319
275
|
reason: "event has no payload to replay"
|
|
@@ -335,52 +291,35 @@ var FunnelDiagnostics = class {
|
|
|
335
291
|
preview: content.slice(0, 60)
|
|
336
292
|
};
|
|
337
293
|
}
|
|
338
|
-
resolveStore() {
|
|
339
|
-
const tmpDir = this.props.tmpDir;
|
|
340
|
-
const rawPath = join(tmpDir, "connector-raw.db");
|
|
341
|
-
const processedPath = join(tmpDir, "connector-processed.db");
|
|
342
|
-
const connectionPath = join(tmpDir, "connector-connection.db");
|
|
343
|
-
if (!existsSync(rawPath) || !existsSync(processedPath) || !existsSync(connectionPath)) return null;
|
|
344
|
-
return {
|
|
345
|
-
rawPath,
|
|
346
|
-
processedPath,
|
|
347
|
-
connectionPath
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
resolveChannelId(channelName) {
|
|
351
|
-
if (!channelName) return null;
|
|
352
|
-
return this.props.channels.list().find((ch) => ch.name === channelName)?.id ?? null;
|
|
353
|
-
}
|
|
354
294
|
/**
|
|
355
|
-
* Resolves
|
|
356
|
-
*
|
|
357
|
-
* resolved (channel not found, connector not found in that channel, no
|
|
358
|
-
* store on disk yet). Centralises the channel/connector → id mapping so
|
|
359
|
-
* each read method does not redo the lookup.
|
|
295
|
+
* Resolves human-facing channel/connector names into the stable ids stored
|
|
296
|
+
* by the diagnostic log. Connector names are only unique inside a channel.
|
|
360
297
|
*/
|
|
361
298
|
resolveScope(channelName, connectorName) {
|
|
362
|
-
|
|
363
|
-
if (!store) return null;
|
|
364
|
-
if (!channelName) return {
|
|
365
|
-
store,
|
|
366
|
-
whereClause: "",
|
|
367
|
-
params: []
|
|
368
|
-
};
|
|
299
|
+
if (!channelName) return {};
|
|
369
300
|
const channel = this.props.channels.list().find((ch) => ch.name === channelName) ?? null;
|
|
370
301
|
if (!channel) return null;
|
|
371
|
-
if (!connectorName) return {
|
|
372
|
-
store,
|
|
373
|
-
whereClause: "WHERE channel_id = ?",
|
|
374
|
-
params: [channel.id]
|
|
375
|
-
};
|
|
302
|
+
if (!connectorName) return { channelId: channel.id };
|
|
376
303
|
const connectorId = channel.connectors?.find((c) => c.name === connectorName)?.id ?? null;
|
|
377
304
|
if (!connectorId) return null;
|
|
378
305
|
return {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
params: [channel.id, connectorId]
|
|
306
|
+
channelId: channel.id,
|
|
307
|
+
connectorId
|
|
382
308
|
};
|
|
383
309
|
}
|
|
310
|
+
queryLog(query) {
|
|
311
|
+
const result = this.queryLogResult(query);
|
|
312
|
+
return result instanceof Error ? [] : result;
|
|
313
|
+
}
|
|
314
|
+
queryLogResult(query) {
|
|
315
|
+
const log = this.props.diagnosticLog;
|
|
316
|
+
if (!log) return /* @__PURE__ */ new Error("no diagnostic log configured");
|
|
317
|
+
try {
|
|
318
|
+
return query(log);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return new Error(errorMessageOf(error));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
384
323
|
async fetchGatewayStatus() {
|
|
385
324
|
const gatewayStatus = this.props.gateway.getStatus();
|
|
386
325
|
if (!gatewayStatus.running) return {
|
|
@@ -420,7 +359,7 @@ var FunnelDiagnostics = class {
|
|
|
420
359
|
error: null
|
|
421
360
|
};
|
|
422
361
|
}
|
|
423
|
-
async buildChannelDiagnosis(target, gatewayProbe,
|
|
362
|
+
async buildChannelDiagnosis(target, gatewayProbe, eventLimit) {
|
|
424
363
|
const gatewayStatus = this.props.gateway.getStatus();
|
|
425
364
|
const targetName = target.name;
|
|
426
365
|
const gatewayBody = gatewayProbe.body;
|
|
@@ -451,11 +390,16 @@ var FunnelDiagnostics = class {
|
|
|
451
390
|
}));
|
|
452
391
|
baseReport.claudeClients = gatewayBody.clients.filter((cl) => cl.channelName === targetName).length;
|
|
453
392
|
}
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
393
|
+
if (this.props.diagnosticLog) {
|
|
394
|
+
baseReport.recentEvents = this.queryLog((log) => log.queryProcessed({
|
|
395
|
+
channelId: target.id,
|
|
396
|
+
limit: eventLimit
|
|
397
|
+
})).map(diagnosticEventOfProcessed);
|
|
398
|
+
baseReport.connectionErrors = this.queryLog((log) => log.queryConnection({
|
|
399
|
+
channelId: target.id,
|
|
400
|
+
statuses: ["auth-failed", "error"],
|
|
401
|
+
limit: 3
|
|
402
|
+
})).map(diagnosticConnectionEventOf);
|
|
459
403
|
}
|
|
460
404
|
return {
|
|
461
405
|
...baseReport,
|
|
@@ -464,4 +408,4 @@ var FunnelDiagnostics = class {
|
|
|
464
408
|
}
|
|
465
409
|
};
|
|
466
410
|
//#endregion
|
|
467
|
-
export {
|
|
411
|
+
export { FunnelDiagnostics as t };
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { t as ChannelConfig } from "./settings-schema-
|
|
2
|
-
|
|
1
|
+
import { t as ChannelConfig } from "./settings-schema-B-CKLuST.js";
|
|
2
|
+
import { g as StoredRawEvent, h as StoredProcessedEvent, m as StoredConnectionEvent, o as ConnectorDiagnosticLog } from "./diagnostic-log-BsAg5qZK.js";
|
|
3
3
|
//#region lib/engine/diagnostic-log/diagnostic-sql-reader.d.ts
|
|
4
4
|
type Props$1 = {
|
|
5
|
-
/** SQLite file holding the raw (pre-filter) table. */
|
|
6
|
-
|
|
5
|
+
/** SQLite file holding the raw (pre-filter) table. */
|
|
6
|
+
rawPath: string;
|
|
7
|
+
/** SQLite file holding the processed (verdict) table. */
|
|
8
|
+
processedPath: string;
|
|
9
|
+
/** SQLite file holding the connection (lifecycle) table. */
|
|
7
10
|
connectionPath: string;
|
|
8
11
|
};
|
|
9
12
|
type Row = Record<string, unknown>;
|
|
@@ -54,6 +57,9 @@ type DiagnosticConnectionError = {
|
|
|
54
57
|
declare const previewOf: (payload: unknown) => string | null;
|
|
55
58
|
declare const toDiagnosticEvent: (row: Record<string, unknown>) => DiagnosticEvent;
|
|
56
59
|
declare const toDiagnosticConnectionError: (row: Record<string, unknown>) => DiagnosticConnectionError;
|
|
60
|
+
declare const diagnosticEventOfProcessed: (event: StoredProcessedEvent) => DiagnosticEvent;
|
|
61
|
+
declare const diagnosticEventOfRaw: (event: StoredRawEvent) => DiagnosticEvent;
|
|
62
|
+
declare const diagnosticConnectionEventOf: (event: StoredConnectionEvent) => DiagnosticConnectionError;
|
|
57
63
|
declare const queryRows: (reader: ConnectorDiagnosticSqlReader, sql: string, params: (string | number | null)[]) => Record<string, unknown>[] | Error;
|
|
58
64
|
//#endregion
|
|
59
65
|
//#region lib/services/diagnostics/funnel-diagnostics.d.ts
|
|
@@ -93,6 +99,7 @@ type Props = {
|
|
|
93
99
|
gatewayToken: DiagnosticsTokenReader;
|
|
94
100
|
channels: DiagnosticsChannelSource;
|
|
95
101
|
publisher: DiagnosticsPublisher;
|
|
102
|
+
diagnosticLog?: ConnectorDiagnosticLog;
|
|
96
103
|
tmpDir: string;
|
|
97
104
|
};
|
|
98
105
|
type DiagnosisStatus = "ok" | "warn" | "error";
|
|
@@ -111,7 +118,8 @@ type ChannelDiagnosis = {
|
|
|
111
118
|
* but the probe failed (auth refused, fetch error, non-OK response).
|
|
112
119
|
*/
|
|
113
120
|
statusError: string | null;
|
|
114
|
-
};
|
|
121
|
+
};
|
|
122
|
+
/** Connectors declared in settings for this channel. */
|
|
115
123
|
configuredConnectors: number;
|
|
116
124
|
listeners: Array<{
|
|
117
125
|
name: string;
|
|
@@ -156,11 +164,6 @@ type ReplayResult = {
|
|
|
156
164
|
} | {
|
|
157
165
|
state: "not-found";
|
|
158
166
|
};
|
|
159
|
-
type StorePaths = {
|
|
160
|
-
rawPath: string;
|
|
161
|
-
processedPath: string;
|
|
162
|
-
connectionPath: string;
|
|
163
|
-
};
|
|
164
167
|
/**
|
|
165
168
|
* Programmable diagnostics surface — used by both the CLI (fnl debug …) and
|
|
166
169
|
* the MCP tools (fnl_debug, fnl_recent_events, …). Pure read-side, no
|
|
@@ -221,18 +224,15 @@ declare class FunnelDiagnostics {
|
|
|
221
224
|
truncated: boolean;
|
|
222
225
|
}>;
|
|
223
226
|
replay(channelName: string, seq?: number): Promise<ReplayResult>;
|
|
224
|
-
resolveStore(): StorePaths | null;
|
|
225
|
-
private resolveChannelId;
|
|
226
227
|
/**
|
|
227
|
-
* Resolves
|
|
228
|
-
*
|
|
229
|
-
* resolved (channel not found, connector not found in that channel, no
|
|
230
|
-
* store on disk yet). Centralises the channel/connector → id mapping so
|
|
231
|
-
* each read method does not redo the lookup.
|
|
228
|
+
* Resolves human-facing channel/connector names into the stable ids stored
|
|
229
|
+
* by the diagnostic log. Connector names are only unique inside a channel.
|
|
232
230
|
*/
|
|
233
231
|
private resolveScope;
|
|
232
|
+
private queryLog;
|
|
233
|
+
private queryLogResult;
|
|
234
234
|
private fetchGatewayStatus;
|
|
235
235
|
private buildChannelDiagnosis;
|
|
236
236
|
}
|
|
237
237
|
//#endregion
|
|
238
|
-
export { DiagnosticsGatewayProbe as a, FunnelDiagnostics as c, DiagnosticEvent as d,
|
|
238
|
+
export { toDiagnosticConnectionError as _, DiagnosticsGatewayProbe as a, FunnelDiagnostics as c, DiagnosticEvent as d, diagnosticConnectionEventOf as f, queryRows as g, previewOf as h, DiagnosticsChannelSource as i, ReplayResult as l, diagnosticEventOfRaw as m, DiagnoseAllReport as n, DiagnosticsPublisher as o, diagnosticEventOfProcessed as p, DiagnosisStatus as r, DiagnosticsTokenReader as s, ChannelDiagnosis as t, DiagnosticConnectionError as u, toDiagnosticEvent as v, ConnectorDiagnosticSqlReader as y };
|
|
@@ -124,7 +124,7 @@ types:
|
|
|
124
124
|
slack Socket Mode listener + REST adapter
|
|
125
125
|
gh GitHub polling listener + REST adapter
|
|
126
126
|
discord Discord listener + REST adapter
|
|
127
|
-
schedule cron-
|
|
127
|
+
schedule cron + one-shot tick listener (no adapter — schedule does not send out)
|
|
128
128
|
|
|
129
129
|
per-type parts:
|
|
130
130
|
|
|
@@ -145,8 +145,8 @@ operations (all nested under a channel):
|
|
|
145
145
|
fnl channels <ch> connectors <name> request … proxy an HTTP call to the
|
|
146
146
|
adapter (e.g. send a Slack
|
|
147
147
|
message via Claude path)
|
|
148
|
-
fnl channels <ch> connectors <name> schedules … manage cron
|
|
149
|
-
schedule
|
|
148
|
+
fnl channels <ch> connectors <name> schedules … manage cron / one-shot
|
|
149
|
+
schedule entries
|
|
150
150
|
|
|
151
151
|
tokens:
|
|
152
152
|
|
|
@@ -378,6 +378,10 @@ what funnel never writes to funnel.json:
|
|
|
378
378
|
|
|
379
379
|
~/.funnel/projects/<id>/settings.json per-repo, set via CLI or TTY prompt
|
|
380
380
|
|
|
381
|
+
When a profile declares resume: true, its generated Claude session id lives
|
|
382
|
+
in ~/.funnel/projects/<id>/claude/local-sessions.json. The id is runtime
|
|
383
|
+
state and is never written to the committed funnel.json.
|
|
384
|
+
|
|
381
385
|
how it is read:
|
|
382
386
|
|
|
383
387
|
All CLI commands check for funnel.json in cwd. If found, FUNNEL_DIR is
|
|
@@ -704,6 +708,11 @@ related: fnl docs architecture, fnl docs debugging, fnl docs mcp`,
|
|
|
704
708
|
fnl channels daily connectors cron schedules add morning \\
|
|
705
709
|
--cron="0 9 * * *" --prompt="summarize yesterday's PRs"
|
|
706
710
|
|
|
711
|
+
— schedule a one-shot prompt —
|
|
712
|
+
|
|
713
|
+
fnl channels daily connectors cron schedules add release \\
|
|
714
|
+
--run-at="2026-08-01T09:00:00+09:00" --prompt="run the release check"
|
|
715
|
+
|
|
707
716
|
— diagnose "events stopped arriving" —
|
|
708
717
|
|
|
709
718
|
fnl debug --all --json
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
-
import { c as FunnelDiagnostics, n as DiagnoseAllReport, t as ChannelDiagnosis } from "./funnel-diagnostics-
|
|
2
|
-
import { n as RecoveryAction, t as FunnelRecovery } from "./funnel-recovery-
|
|
3
|
-
|
|
1
|
+
import { c as FunnelDiagnostics, n as DiagnoseAllReport, t as ChannelDiagnosis } from "./funnel-diagnostics-CDrZznTB.js";
|
|
2
|
+
import { n as RecoveryAction, t as FunnelRecovery } from "./funnel-recovery-BwcSNNSN.js";
|
|
4
3
|
//#region lib/services/doctor/funnel-doctor.d.ts
|
|
5
4
|
type Props = {
|
|
6
5
|
diagnostics: FunnelDiagnostics;
|
|
7
6
|
recovery: FunnelRecovery;
|
|
8
7
|
};
|
|
9
|
-
type DoctorFixMode =
|
|
8
|
+
type DoctorFixMode =
|
|
9
|
+
/** No mutations. Run diagnoseAll and report what would be fixed. */
|
|
10
|
+
"off" |
|
|
11
|
+
/** Apply only idempotent, low-risk fixes — start the gateway, restart dead listeners. */
|
|
12
|
+
"safe" |
|
|
13
|
+
/** Add high-impact fixes — full gateway restart when partial fixes are insufficient. */
|
|
14
|
+
"aggressive";
|
|
10
15
|
type DoctorReport = {
|
|
11
|
-
/** "ok" if every channel is healthy after the run; "warn" if anything is still off; "error" if a fix step itself failed. */
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
/** "ok" if every channel is healthy after the run; "warn" if anything is still off; "error" if a fix step itself failed. */
|
|
17
|
+
status: "ok" | "warn" | "error";
|
|
18
|
+
/** Human-readable summary suitable for stdout. */
|
|
19
|
+
message: string;
|
|
20
|
+
/** Aggregated diagnosis after any fix pass. */
|
|
21
|
+
after: DiagnoseAllReport;
|
|
22
|
+
/** Diagnosis before fixing, only included when fix mode is not "off". */
|
|
23
|
+
before: DiagnoseAllReport | null;
|
|
24
|
+
/** Each recovery action that ran during the fix pass. */
|
|
25
|
+
appliedActions: RecoveryAction[];
|
|
26
|
+
/** Channels still unhealthy after the fix pass (or all unhealthy channels when fix is off). */
|
|
16
27
|
remainingIssues: Array<{
|
|
17
28
|
channel: string;
|
|
18
29
|
diagnosis: ChannelDiagnosis["diagnosis"];
|
|
@@ -17,6 +17,7 @@ var FunnelError = class extends Error {
|
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
var FunnelChannelNotFoundError = class extends FunnelError {
|
|
20
|
+
channel;
|
|
20
21
|
code = "channel-not-found";
|
|
21
22
|
constructor(channel, options) {
|
|
22
23
|
super(`channel not found: ${channel}`, options);
|
|
@@ -24,6 +25,7 @@ var FunnelChannelNotFoundError = class extends FunnelError {
|
|
|
24
25
|
}
|
|
25
26
|
};
|
|
26
27
|
var FunnelChannelAlreadyExistsError = class extends FunnelError {
|
|
28
|
+
channel;
|
|
27
29
|
code = "channel-already-exists";
|
|
28
30
|
constructor(channel, options) {
|
|
29
31
|
super(`channel already exists: ${channel}`, options);
|
|
@@ -31,6 +33,8 @@ var FunnelChannelAlreadyExistsError = class extends FunnelError {
|
|
|
31
33
|
}
|
|
32
34
|
};
|
|
33
35
|
var FunnelConnectorNotFoundError = class extends FunnelError {
|
|
36
|
+
channel;
|
|
37
|
+
connector;
|
|
34
38
|
code = "connector-not-found";
|
|
35
39
|
constructor(channel, connector, options) {
|
|
36
40
|
super(`connector not found in ${channel}: ${connector}`, options);
|
|
@@ -39,6 +43,9 @@ var FunnelConnectorNotFoundError = class extends FunnelError {
|
|
|
39
43
|
}
|
|
40
44
|
};
|
|
41
45
|
var FunnelConnectorTypeMismatchError = class extends FunnelError {
|
|
46
|
+
connector;
|
|
47
|
+
expected;
|
|
48
|
+
actual;
|
|
42
49
|
code = "connector-type-mismatch";
|
|
43
50
|
constructor(connector, expected, actual, options) {
|
|
44
51
|
super(`connector ${connector} type mismatch: expected ${expected}, got ${actual}`, options);
|
|
@@ -48,6 +55,8 @@ var FunnelConnectorTypeMismatchError = class extends FunnelError {
|
|
|
48
55
|
}
|
|
49
56
|
};
|
|
50
57
|
var FunnelAuthFailedError = class extends FunnelError {
|
|
58
|
+
connector;
|
|
59
|
+
detail;
|
|
51
60
|
code = "auth-failed";
|
|
52
61
|
constructor(connector, detail, options) {
|
|
53
62
|
super(`${connector}: auth failed — ${detail}`, options);
|
|
@@ -56,6 +65,9 @@ var FunnelAuthFailedError = class extends FunnelError {
|
|
|
56
65
|
}
|
|
57
66
|
};
|
|
58
67
|
var FunnelGatewayBindError = class extends FunnelError {
|
|
68
|
+
host;
|
|
69
|
+
port;
|
|
70
|
+
detail;
|
|
59
71
|
code = "gateway-bind";
|
|
60
72
|
constructor(host, port, detail, options) {
|
|
61
73
|
super(`gateway failed to bind ${host}:${port} — ${detail}`, options);
|
|
@@ -65,6 +77,7 @@ var FunnelGatewayBindError = class extends FunnelError {
|
|
|
65
77
|
}
|
|
66
78
|
};
|
|
67
79
|
var FunnelTokenCollisionError = class extends FunnelError {
|
|
80
|
+
connector;
|
|
68
81
|
code = "token-collision";
|
|
69
82
|
constructor(connector, options) {
|
|
70
83
|
super(`${connector}: both literal token and tokenEnv reference are set — pick one`, options);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { t as ChannelConfig } from "./settings-schema-
|
|
2
|
-
|
|
1
|
+
import { t as ChannelConfig } from "./settings-schema-B-CKLuST.js";
|
|
3
2
|
//#region lib/services/recovery/funnel-recovery.d.ts
|
|
4
3
|
/** Narrow gateway control — start / stop / restart and a probe. */
|
|
5
4
|
type RecoveryGatewayControl = {
|