@interactive-inc/claude-funnel 0.67.0 → 0.68.1
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 +24 -3
- package/dist/bin.js +276 -270
- package/dist/{channels-BBA3GTAa.d.ts → channels-B0cifCfw.d.ts} +2 -1
- package/dist/claude.d.ts +2 -2
- package/dist/claude.js +5 -5
- package/dist/connector-descriptor-D4XtEsH8.d.ts +215 -0
- package/dist/connectors/discord.d.ts +3 -2
- package/dist/connectors/discord.js +1 -1
- package/dist/connectors/gh.d.ts +3 -2
- package/dist/connectors/gh.js +1 -1
- package/dist/connectors/schedule.d.ts +13 -11
- package/dist/connectors/schedule.js +3 -3
- package/dist/connectors/slack.d.ts +3 -2
- package/dist/connectors/slack.js +1 -1
- 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-CdjwPoty.js → discord-connector-CAcWifJn.js} +1 -1
- package/dist/docs.js +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/event-log.d.ts +2 -0
- package/dist/event-log.js +3 -0
- package/dist/{file-process-guard-DAYe9Ndt.d.ts → file-process-guard-D-QatrwL.d.ts} +1 -1
- package/dist/{flume-source-listener-CLtsdcT2.d.ts → flume-source-listener-Ba8_1Mg-.d.ts} +2 -1
- package/dist/{funnel-diagnostics-B-PCuv_E.js → funnel-diagnostics-7rV3HxY3.js} +100 -143
- package/dist/{funnel-diagnostics-CnKus3Jg.d.ts → funnel-diagnostics-CDrZznTB.d.ts} +10 -13
- package/dist/{funnel-docs-BFcqITFA.js → funnel-docs-CI4hMkhq.js} +8 -3
- package/dist/{funnel-doctor-CviUTINw.d.ts → funnel-doctor-CY4S5cnz.d.ts} +1 -1
- package/dist/gateway/daemon.js +63 -83
- package/dist/gateway.d.ts +2 -2
- package/dist/gateway.js +1 -1
- package/dist/{gh-connector-D-iyjl-d.js → gh-connector-DMwI6zJA.js} +1 -1
- package/dist/{index-B4RvEaW-.d.ts → index-fHrKn4jm.d.ts} +7 -2
- package/dist/index.d.ts +18 -14
- package/dist/index.js +124 -30
- package/dist/local-config.d.ts +1 -1
- package/dist/logger.d.ts +6 -270
- package/dist/logger.js +6 -141
- package/dist/memory-event-log-BY8XjFzi.js +147 -0
- package/dist/{memory-token-prompter-D3R_kgM_.d.ts → memory-token-prompter-B20tl_aU.d.ts} +1 -1
- package/dist/{schedule-connector-teeDMHUe.js → schedule-connector-BXUOYgwZ.js} +59 -95
- package/dist/schedule-connector-schema-CNV8PgWX.js +137 -0
- package/dist/schedule-connector-schema-Tq5kJul-.d.ts +127 -0
- package/dist/{slack-connector-BE0KAjCs.js → slack-connector-BTtUiXAU.js} +2 -2
- package/dist/{slot-fields-5ERhytIA.js → slot-fields-eCWizJqS.js} +1 -1
- package/dist/{sqlite-diagnostic-log-BDxfXGZy.js → sqlite-diagnostic-log-Qs4cf9I_.js} +53 -84
- package/dist/sqlite-event-log-BYgCFMxC.d.ts +284 -0
- package/dist/{funnel-log-sqlite-sink-CXbs3qQp.js → sqlite-event-log-eZuXKszJ.js} +52 -11
- package/package.json +16 -1
- package/dist/connector-descriptor-F75ZI8Fh.d.ts +0 -391
- package/dist/diagnostic-sql-reader-CoBk_EFF.js +0 -83
- package/dist/schedule-connector-schema-C2AChpwX.js +0 -31
- package/dist/schedule-connector-schema-DBko_Bf7.d.ts +0 -48
|
@@ -3,7 +3,7 @@ import { t as errorMessageOf } from "./error-message-of-BG4ezZ_s.js";
|
|
|
3
3
|
import { t as discordConnectorSchema } from "./discord-connector-schema-saAGuPmJ.js";
|
|
4
4
|
import { t as FunnelConnectorAdapter } from "./connector-adapter-BL2ehMej.js";
|
|
5
5
|
import { t as resolveConnectorToken } from "./resolve-connector-token-BwiJCu82.js";
|
|
6
|
-
import { i as resolveFlumeDeps, n as FunnelFlumeSourceListener, r as flumeLogHandler, t as slotFields } from "./slot-fields-
|
|
6
|
+
import { i as resolveFlumeDeps, n as FunnelFlumeSourceListener, r as flumeLogHandler, t as slotFields } from "./slot-fields-eCWizJqS.js";
|
|
7
7
|
import { FlumeDiscordGatewayIntents, FlumeDiscordSource } from "@interactive-inc/flume/discord";
|
|
8
8
|
//#region lib/engine/connectors/discord-adapter.ts
|
|
9
9
|
const DISCORD_API_BASE = "https://discord.com/api/v10";
|
package/dist/docs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as FunnelDocs } from "./funnel-docs-
|
|
1
|
+
import { t as FunnelDocs } from "./funnel-docs-CI4hMkhq.js";
|
|
2
2
|
export { FunnelDocs };
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as DoctorReport, r as FunnelDoctor, t as DoctorFixMode } from "./funnel-doctor-
|
|
1
|
+
import { n as DoctorReport, r as FunnelDoctor, t as DoctorFixMode } from "./funnel-doctor-CY4S5cnz.js";
|
|
2
2
|
export { DoctorFixMode, DoctorReport, FunnelDoctor };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as MemoryEventLogProps, c as EventLogValidator, d as EventLogEntry, i as MemoryEventLog, l as EventLogRelay, n as SqliteEventLogProps, o as EventLog, r as SqliteEventLogQuery, s as EventLogProps, t as SqliteEventLog, u as EventLogStore } from "./sqlite-event-log-BYgCFMxC.js";
|
|
2
|
+
export { EventLog, EventLogEntry, EventLogProps, EventLogRelay, EventLogStore, EventLogValidator, MemoryEventLog, MemoryEventLogProps, SqliteEventLog, SqliteEventLogProps, SqliteEventLogQuery };
|
|
@@ -2,7 +2,7 @@ import { t as FunnelLogger } from "./logger-mMZvblTx.js";
|
|
|
2
2
|
import { n as FunnelFileSystem } from "./file-system-BNtl997T.js";
|
|
3
3
|
import { t as ChannelConfig } from "./settings-schema-B-CKLuST.js";
|
|
4
4
|
import { n as FunnelIdGenerator } from "./settings-reader-CyTpbAre.js";
|
|
5
|
-
import {
|
|
5
|
+
import { b as FunnelProcessRunner } from "./connector-descriptor-D4XtEsH8.js";
|
|
6
6
|
//#region lib/engine/claude/channel-resolver.d.ts
|
|
7
7
|
type ChannelResolver = {
|
|
8
8
|
get(name: string): ChannelConfig | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as FunnelLogger } from "./logger-mMZvblTx.js";
|
|
2
|
-
import {
|
|
2
|
+
import { d as FunnelConnectorListener } from "./connector-descriptor-D4XtEsH8.js";
|
|
3
|
+
import { a as ConnectorConnectionStatus, o as ConnectorDiagnosticLog } from "./diagnostic-log-BsAg5qZK.js";
|
|
3
4
|
import { FlumeEventHandler, FlumeLogHandler, FlumeReconnectOptions, FlumeRunning, FlumeRuntimeDeps, FlumeSource, FlumeStatus } from "@interactive-inc/flume";
|
|
4
5
|
//#region lib/engine/connectors/connector-diagnostics-recorder.d.ts
|
|
5
6
|
type Props$1 = {
|
|
@@ -1,58 +1,8 @@
|
|
|
1
1
|
import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-HP-bo9Kv.js";
|
|
2
2
|
import { t as errorMessageOf } from "./error-message-of-BG4ezZ_s.js";
|
|
3
|
-
import { t as
|
|
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;
|
|
@@ -65,8 +15,20 @@ const connectorOf = (channel, connectorId) => {
|
|
|
65
15
|
return channel.connectors?.find((connector) => connector.id === connectorId)?.name;
|
|
66
16
|
};
|
|
67
17
|
const FLAPPING_ERROR_THRESHOLD = 3;
|
|
68
|
-
const
|
|
69
|
-
|
|
18
|
+
const CONNECTION_DIAGNOSIS_WINDOW = 100;
|
|
19
|
+
const connectionKeyOf = (event) => `${event.type}\u0000${event.connectorId ?? ""}`;
|
|
20
|
+
const unresolvedConnectionFailures = (timeline) => {
|
|
21
|
+
const lastConnectedIndex = /* @__PURE__ */ new Map();
|
|
22
|
+
timeline.forEach((event, index) => {
|
|
23
|
+
if (event.status === "connected") lastConnectedIndex.set(connectionKeyOf(event), index);
|
|
24
|
+
});
|
|
25
|
+
return timeline.filter((event, index) => {
|
|
26
|
+
if (event.status !== "auth-failed" && event.status !== "error") return false;
|
|
27
|
+
return (lastConnectedIndex.get(connectionKeyOf(event)) ?? -1) < index;
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const buildDiagnosis = (report, unresolvedErrors = report.connectionErrors) => {
|
|
31
|
+
const rootCause = (unresolvedErrors[unresolvedErrors.length - 1] ?? null)?.detail ?? null;
|
|
70
32
|
const channel = report.channel;
|
|
71
33
|
if (!report.gateway.running) return {
|
|
72
34
|
status: "error",
|
|
@@ -92,7 +54,7 @@ const buildDiagnosis = (report) => {
|
|
|
92
54
|
nextActions: [`fnl channels ${channel} connectors add <name> --type=slack ...`],
|
|
93
55
|
rootCause: null
|
|
94
56
|
};
|
|
95
|
-
const authFailed =
|
|
57
|
+
const authFailed = unresolvedErrors.filter((e) => e.status === "auth-failed");
|
|
96
58
|
if (authFailed.length > 0) {
|
|
97
59
|
const detail = authFailed[authFailed.length - 1]?.detail ?? null;
|
|
98
60
|
return {
|
|
@@ -174,14 +136,12 @@ var FunnelDiagnostics = class {
|
|
|
174
136
|
const target = channelName ? channels.find((ch) => ch.name === channelName) ?? null : channels[0] ?? null;
|
|
175
137
|
if (!target) return null;
|
|
176
138
|
const gatewayProbe = await this.fetchGatewayStatus();
|
|
177
|
-
|
|
178
|
-
return this.buildChannelDiagnosis(target, gatewayProbe, store, 5);
|
|
139
|
+
return this.buildChannelDiagnosis(target, gatewayProbe, 5);
|
|
179
140
|
}
|
|
180
141
|
async diagnoseAll() {
|
|
181
142
|
const channels = this.props.channels.list();
|
|
182
143
|
const gatewayProbe = await this.fetchGatewayStatus();
|
|
183
|
-
const
|
|
184
|
-
const reports = await Promise.all(channels.map((ch) => this.buildChannelDiagnosis(ch, gatewayProbe, store, 5)));
|
|
144
|
+
const reports = await Promise.all(channels.map((ch) => this.buildChannelDiagnosis(ch, gatewayProbe, 5)));
|
|
185
145
|
const errorChannels = reports.filter((r) => r.diagnosis.status === "error").map((r) => r.channel);
|
|
186
146
|
const warnChannels = reports.filter((r) => r.diagnosis.status === "warn").map((r) => r.channel);
|
|
187
147
|
const okChannels = reports.filter((r) => r.diagnosis.status === "ok").map((r) => r.channel);
|
|
@@ -201,20 +161,22 @@ var FunnelDiagnostics = class {
|
|
|
201
161
|
}
|
|
202
162
|
async recentEvents(channelName, options = {}) {
|
|
203
163
|
const limit = options.limit ?? 20;
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
164
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
165
|
+
if (scope === null) return [];
|
|
166
|
+
return this.queryLog((log) => log.queryProcessed({
|
|
167
|
+
...scope,
|
|
168
|
+
limit
|
|
169
|
+
})).map(diagnosticEventOfProcessed);
|
|
209
170
|
}
|
|
210
171
|
async droppedEvents(channelName, options = {}) {
|
|
211
172
|
const limit = options.limit ?? 20;
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
173
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
174
|
+
if (scope === null) return [];
|
|
175
|
+
return this.queryLog((log) => log.queryProcessed({
|
|
176
|
+
...scope,
|
|
177
|
+
outcomePrefix: "skip:",
|
|
178
|
+
limit
|
|
179
|
+
})).map(diagnosticEventOfProcessed);
|
|
218
180
|
}
|
|
219
181
|
/**
|
|
220
182
|
* Raw inbound rows the connector recorded before any processing. The most
|
|
@@ -224,20 +186,22 @@ var FunnelDiagnostics = class {
|
|
|
224
186
|
*/
|
|
225
187
|
async rawEvents(channelName, options = {}) {
|
|
226
188
|
const limit = options.limit ?? 20;
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
189
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
190
|
+
if (scope === null) return [];
|
|
191
|
+
return this.queryLog((log) => log.queryRaw({
|
|
192
|
+
...scope,
|
|
193
|
+
limit
|
|
194
|
+
})).map(diagnosticEventOfRaw);
|
|
232
195
|
}
|
|
233
196
|
async connectionErrors(channelName, options = {}) {
|
|
234
197
|
const limit = options.limit ?? 20;
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
198
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
199
|
+
if (scope === null) return [];
|
|
200
|
+
return this.queryLog((log) => log.queryConnection({
|
|
201
|
+
...scope,
|
|
202
|
+
statuses: ["auth-failed", "error"],
|
|
203
|
+
limit
|
|
204
|
+
})).map(diagnosticConnectionEventOf);
|
|
241
205
|
}
|
|
242
206
|
/**
|
|
243
207
|
* Full connection lifecycle for one channel/connector — started, connected,
|
|
@@ -248,11 +212,12 @@ var FunnelDiagnostics = class {
|
|
|
248
212
|
*/
|
|
249
213
|
async connectionTimeline(channelName, options = {}) {
|
|
250
214
|
const limit = options.limit ?? 20;
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
215
|
+
const scope = this.resolveScope(channelName, options.connector);
|
|
216
|
+
if (scope === null) return [];
|
|
217
|
+
return this.queryLog((log) => log.queryConnection({
|
|
218
|
+
...scope,
|
|
219
|
+
limit
|
|
220
|
+
})).map(diagnosticConnectionEventOf);
|
|
256
221
|
}
|
|
257
222
|
/**
|
|
258
223
|
* Tail of `~/.funnel/.../funnel.log`. Use when a flume internal log (e.g.
|
|
@@ -293,28 +258,30 @@ var FunnelDiagnostics = class {
|
|
|
293
258
|
async replay(channelName, seq) {
|
|
294
259
|
const channel = this.props.channels.list().find((ch) => ch.name === channelName);
|
|
295
260
|
if (!channel) return { state: "not-found" };
|
|
296
|
-
|
|
297
|
-
if (!store) return {
|
|
261
|
+
if (!this.props.diagnosticLog) return {
|
|
298
262
|
state: "error",
|
|
299
|
-
reason: "no diagnostic
|
|
263
|
+
reason: "no diagnostic log configured"
|
|
300
264
|
};
|
|
301
|
-
const
|
|
302
|
-
|
|
265
|
+
const rows = this.queryLogResult((log) => log.queryProcessed({
|
|
266
|
+
channelId: channel.id,
|
|
267
|
+
...seq !== void 0 ? { seq } : { outcomePrefix: "emitted" },
|
|
268
|
+
limit: 1
|
|
269
|
+
}));
|
|
303
270
|
if (rows instanceof Error) return {
|
|
304
271
|
state: "error",
|
|
305
272
|
reason: rows.message
|
|
306
273
|
};
|
|
307
274
|
const firstRow = rows[0];
|
|
308
275
|
if (!firstRow) return { state: "not-found" };
|
|
309
|
-
const replaySeq =
|
|
310
|
-
const eventId =
|
|
311
|
-
const connectorId =
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
276
|
+
const replaySeq = firstRow.seq;
|
|
277
|
+
const eventId = firstRow.eventId;
|
|
278
|
+
const connectorId = firstRow.connectorId;
|
|
279
|
+
const processedContent = firstRow.payload.length > 0 ? firstRow.payload : null;
|
|
280
|
+
const rawContent = processedContent === null ? this.queryLog((log) => log.queryRaw({
|
|
281
|
+
eventId,
|
|
282
|
+
limit: 1
|
|
283
|
+
}))[0]?.payload ?? null : null;
|
|
284
|
+
const content = processedContent ?? rawContent;
|
|
318
285
|
if (!content) return {
|
|
319
286
|
state: "error",
|
|
320
287
|
reason: "event has no payload to replay"
|
|
@@ -336,52 +303,35 @@ var FunnelDiagnostics = class {
|
|
|
336
303
|
preview: content.slice(0, 60)
|
|
337
304
|
};
|
|
338
305
|
}
|
|
339
|
-
resolveStore() {
|
|
340
|
-
const tmpDir = this.props.tmpDir;
|
|
341
|
-
const rawPath = join(tmpDir, "connector-raw.db");
|
|
342
|
-
const processedPath = join(tmpDir, "connector-processed.db");
|
|
343
|
-
const connectionPath = join(tmpDir, "connector-connection.db");
|
|
344
|
-
if (!existsSync(rawPath) || !existsSync(processedPath) || !existsSync(connectionPath)) return null;
|
|
345
|
-
return {
|
|
346
|
-
rawPath,
|
|
347
|
-
processedPath,
|
|
348
|
-
connectionPath
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
resolveChannelId(channelName) {
|
|
352
|
-
if (!channelName) return null;
|
|
353
|
-
return this.props.channels.list().find((ch) => ch.name === channelName)?.id ?? null;
|
|
354
|
-
}
|
|
355
306
|
/**
|
|
356
|
-
* Resolves
|
|
357
|
-
*
|
|
358
|
-
* resolved (channel not found, connector not found in that channel, no
|
|
359
|
-
* store on disk yet). Centralises the channel/connector → id mapping so
|
|
360
|
-
* each read method does not redo the lookup.
|
|
307
|
+
* Resolves human-facing channel/connector names into the stable ids stored
|
|
308
|
+
* by the diagnostic log. Connector names are only unique inside a channel.
|
|
361
309
|
*/
|
|
362
310
|
resolveScope(channelName, connectorName) {
|
|
363
|
-
|
|
364
|
-
if (!store) return null;
|
|
365
|
-
if (!channelName) return {
|
|
366
|
-
store,
|
|
367
|
-
whereClause: "",
|
|
368
|
-
params: []
|
|
369
|
-
};
|
|
311
|
+
if (!channelName) return {};
|
|
370
312
|
const channel = this.props.channels.list().find((ch) => ch.name === channelName) ?? null;
|
|
371
313
|
if (!channel) return null;
|
|
372
|
-
if (!connectorName) return {
|
|
373
|
-
store,
|
|
374
|
-
whereClause: "WHERE channel_id = ?",
|
|
375
|
-
params: [channel.id]
|
|
376
|
-
};
|
|
314
|
+
if (!connectorName) return { channelId: channel.id };
|
|
377
315
|
const connectorId = channel.connectors?.find((c) => c.name === connectorName)?.id ?? null;
|
|
378
316
|
if (!connectorId) return null;
|
|
379
317
|
return {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
params: [channel.id, connectorId]
|
|
318
|
+
channelId: channel.id,
|
|
319
|
+
connectorId
|
|
383
320
|
};
|
|
384
321
|
}
|
|
322
|
+
queryLog(query) {
|
|
323
|
+
const result = this.queryLogResult(query);
|
|
324
|
+
return result instanceof Error ? [] : result;
|
|
325
|
+
}
|
|
326
|
+
queryLogResult(query) {
|
|
327
|
+
const log = this.props.diagnosticLog;
|
|
328
|
+
if (!log) return /* @__PURE__ */ new Error("no diagnostic log configured");
|
|
329
|
+
try {
|
|
330
|
+
return query(log);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
return new Error(errorMessageOf(error));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
385
335
|
async fetchGatewayStatus() {
|
|
386
336
|
const gatewayStatus = this.props.gateway.getStatus();
|
|
387
337
|
if (!gatewayStatus.running) return {
|
|
@@ -421,7 +371,7 @@ var FunnelDiagnostics = class {
|
|
|
421
371
|
error: null
|
|
422
372
|
};
|
|
423
373
|
}
|
|
424
|
-
async buildChannelDiagnosis(target, gatewayProbe,
|
|
374
|
+
async buildChannelDiagnosis(target, gatewayProbe, eventLimit) {
|
|
425
375
|
const gatewayStatus = this.props.gateway.getStatus();
|
|
426
376
|
const targetName = target.name;
|
|
427
377
|
const gatewayBody = gatewayProbe.body;
|
|
@@ -452,17 +402,24 @@ var FunnelDiagnostics = class {
|
|
|
452
402
|
}));
|
|
453
403
|
baseReport.claudeClients = gatewayBody.clients.filter((cl) => cl.channelName === targetName).length;
|
|
454
404
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
405
|
+
let unresolvedErrors = [];
|
|
406
|
+
if (this.props.diagnosticLog) {
|
|
407
|
+
baseReport.recentEvents = this.queryLog((log) => log.queryProcessed({
|
|
408
|
+
channelId: target.id,
|
|
409
|
+
limit: eventLimit
|
|
410
|
+
})).map(diagnosticEventOfProcessed);
|
|
411
|
+
const connectionRows = this.queryLog((log) => log.queryConnection({
|
|
412
|
+
channelId: target.id,
|
|
413
|
+
limit: CONNECTION_DIAGNOSIS_WINDOW
|
|
414
|
+
}));
|
|
415
|
+
baseReport.connectionErrors = connectionRows.filter((row) => row.status === "auth-failed" || row.status === "error").slice(-3).map(diagnosticConnectionEventOf);
|
|
416
|
+
unresolvedErrors = unresolvedConnectionFailures(connectionRows).map(diagnosticConnectionEventOf);
|
|
460
417
|
}
|
|
461
418
|
return {
|
|
462
419
|
...baseReport,
|
|
463
|
-
diagnosis: buildDiagnosis(baseReport)
|
|
420
|
+
diagnosis: buildDiagnosis(baseReport, unresolvedErrors)
|
|
464
421
|
};
|
|
465
422
|
}
|
|
466
423
|
};
|
|
467
424
|
//#endregion
|
|
468
|
-
export {
|
|
425
|
+
export { FunnelDiagnostics as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
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";
|
|
2
3
|
//#region lib/engine/diagnostic-log/diagnostic-sql-reader.d.ts
|
|
3
4
|
type Props$1 = {
|
|
4
5
|
/** SQLite file holding the raw (pre-filter) table. */
|
|
@@ -56,6 +57,9 @@ type DiagnosticConnectionError = {
|
|
|
56
57
|
declare const previewOf: (payload: unknown) => string | null;
|
|
57
58
|
declare const toDiagnosticEvent: (row: Record<string, unknown>) => DiagnosticEvent;
|
|
58
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;
|
|
59
63
|
declare const queryRows: (reader: ConnectorDiagnosticSqlReader, sql: string, params: (string | number | null)[]) => Record<string, unknown>[] | Error;
|
|
60
64
|
//#endregion
|
|
61
65
|
//#region lib/services/diagnostics/funnel-diagnostics.d.ts
|
|
@@ -95,6 +99,7 @@ type Props = {
|
|
|
95
99
|
gatewayToken: DiagnosticsTokenReader;
|
|
96
100
|
channels: DiagnosticsChannelSource;
|
|
97
101
|
publisher: DiagnosticsPublisher;
|
|
102
|
+
diagnosticLog?: ConnectorDiagnosticLog;
|
|
98
103
|
tmpDir: string;
|
|
99
104
|
};
|
|
100
105
|
type DiagnosisStatus = "ok" | "warn" | "error";
|
|
@@ -159,11 +164,6 @@ type ReplayResult = {
|
|
|
159
164
|
} | {
|
|
160
165
|
state: "not-found";
|
|
161
166
|
};
|
|
162
|
-
type StorePaths = {
|
|
163
|
-
rawPath: string;
|
|
164
|
-
processedPath: string;
|
|
165
|
-
connectionPath: string;
|
|
166
|
-
};
|
|
167
167
|
/**
|
|
168
168
|
* Programmable diagnostics surface — used by both the CLI (fnl debug …) and
|
|
169
169
|
* the MCP tools (fnl_debug, fnl_recent_events, …). Pure read-side, no
|
|
@@ -224,18 +224,15 @@ declare class FunnelDiagnostics {
|
|
|
224
224
|
truncated: boolean;
|
|
225
225
|
}>;
|
|
226
226
|
replay(channelName: string, seq?: number): Promise<ReplayResult>;
|
|
227
|
-
resolveStore(): StorePaths | null;
|
|
228
|
-
private resolveChannelId;
|
|
229
227
|
/**
|
|
230
|
-
* Resolves
|
|
231
|
-
*
|
|
232
|
-
* resolved (channel not found, connector not found in that channel, no
|
|
233
|
-
* store on disk yet). Centralises the channel/connector → id mapping so
|
|
234
|
-
* 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.
|
|
235
230
|
*/
|
|
236
231
|
private resolveScope;
|
|
232
|
+
private queryLog;
|
|
233
|
+
private queryLogResult;
|
|
237
234
|
private fetchGatewayStatus;
|
|
238
235
|
private buildChannelDiagnosis;
|
|
239
236
|
}
|
|
240
237
|
//#endregion
|
|
241
|
-
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
|
|
|
@@ -708,6 +708,11 @@ related: fnl docs architecture, fnl docs debugging, fnl docs mcp`,
|
|
|
708
708
|
fnl channels daily connectors cron schedules add morning \\
|
|
709
709
|
--cron="0 9 * * *" --prompt="summarize yesterday's PRs"
|
|
710
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
|
+
|
|
711
716
|
— diagnose "events stopped arriving" —
|
|
712
717
|
|
|
713
718
|
fnl debug --all --json
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as FunnelDiagnostics, n as DiagnoseAllReport, t as ChannelDiagnosis } from "./funnel-diagnostics-
|
|
1
|
+
import { c as FunnelDiagnostics, n as DiagnoseAllReport, t as ChannelDiagnosis } from "./funnel-diagnostics-CDrZznTB.js";
|
|
2
2
|
import { n as RecoveryAction, t as FunnelRecovery } from "./funnel-recovery-BwcSNNSN.js";
|
|
3
3
|
//#region lib/services/doctor/funnel-doctor.d.ts
|
|
4
4
|
type Props = {
|