@interactive-inc/claude-funnel 0.53.0 → 0.55.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 +3 -3
- package/dist/bin.js +1276 -520
- package/dist/claude.d.ts +22 -5
- package/dist/claude.js +455 -168
- package/dist/{connector-adapter-CePYBTgW.d.ts → connector-adapter-1PxjN-Uk.d.ts} +1 -1
- package/dist/{connector-adapter-D5Utumgz.js → connector-adapter-qwXLjQId.js} +1 -1
- package/dist/{connector-listener-DU54DN-f.js → connector-listener-CpHBecCj.js} +1 -1
- package/dist/connectors/discord.d.ts +6 -6
- package/dist/connectors/discord.js +2 -2
- package/dist/connectors/gh.d.ts +6 -6
- package/dist/connectors/gh.js +2 -2
- package/dist/connectors/schedule.d.ts +12 -2
- package/dist/connectors/schedule.js +2 -2
- package/dist/connectors/slack.d.ts +3 -3
- package/dist/connectors/slack.js +2 -2
- package/dist/{connector-diagnostic-log-yTOojKUR.d.ts → diagnostic-log-Bxe7Bbvw.d.ts} +2 -2
- package/dist/diagnostic-sql-reader-CzYgZpq2.js +83 -0
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +2 -0
- package/dist/{discord-connector-schema-CBDyGdOI.js → discord-connector-schema-B_N6IXLz.js} +1 -1
- package/dist/{discord-connector-schema-R0Uu-3ns.d.ts → discord-connector-schema-CPgcZkXh.d.ts} +1 -1
- package/dist/{discord-listener-_jSE3HsQ.js → discord-listener-C0MoKdQO.js} +6 -6
- package/dist/docs.d.ts +2 -0
- package/dist/docs.js +2 -0
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.js +2 -0
- package/dist/{file-process-guard-DMeLB6Zd.d.ts → file-process-guard-DI1742H5.d.ts} +5 -4
- package/dist/funnel-diagnostics-BpKYrMSu.js +300 -0
- package/dist/funnel-diagnostics-qWy5tPSq.d.ts +176 -0
- package/dist/funnel-docs-dXPokzr5.d.ts +18 -0
- package/dist/funnel-docs-ng5K8w4j.js +653 -0
- package/dist/funnel-doctor-BF3Rdgk0.d.ts +34 -0
- package/dist/funnel-doctor-CApCezTq.js +82 -0
- package/dist/funnel-recovery-BUBsu7WX.d.ts +101 -0
- package/dist/funnel-recovery-D9CxD5Zs.js +134 -0
- package/dist/gateway/daemon.js +810 -211
- package/dist/{gateway-base-url-ssk_He5G.js → gateway-base-url-6foMXfFf.js} +5 -5
- package/dist/gateway.d.ts +2 -2
- package/dist/gateway.js +2 -2
- package/dist/{gh-connector-schema-eoTtHbY6.d.ts → gh-connector-schema-CU1ojfIF.d.ts} +1 -1
- package/dist/{gh-connector-schema-o3Q1-ojL.js → gh-connector-schema-DUcZgN2Q.js} +1 -1
- package/dist/{gh-listener-DH-fClQm.js → gh-listener-Dsx6AmhH.js} +5 -5
- package/dist/{index-DF5VmCPJ.d.ts → index-CrngHrne.d.ts} +104 -607
- package/dist/index.d.ts +16 -11
- package/dist/index.js +508 -972
- package/dist/{local-config-json-schema-D8i-BogY.js → local-config-json-schema-DE1zkMcb.js} +12 -8
- package/dist/{local-config-sync-Cq39mT6p.d.ts → local-config-sync-B8b04LrZ.d.ts} +21 -16
- package/dist/local-config.d.ts +2 -2
- package/dist/local-config.js +2 -2
- package/dist/{memory-connector-diagnostic-log-COUWCsT_.js → memory-diagnostic-log-BZ1VD80X.js} +26 -95
- package/dist/{memory-token-prompter-CKV7VBM5.d.ts → memory-token-prompter-Lo3YRDzq.d.ts} +4 -4
- package/dist/{memory-token-prompter-Q7Snwsv2.js → memory-token-prompter-vBXxY20-.js} +2 -2
- package/dist/{profiles-f0mNmEyP.d.ts → profiles-EHTeCOqB.d.ts} +3 -2
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +1 -1
- package/dist/recovery.d.ts +2 -0
- package/dist/recovery.js +2 -0
- package/dist/{resolve-connector-token-BHmZLRrV.js → resolve-connector-token-CczqG_Ig.js} +1 -1
- package/dist/{schedule-connector-schema-iCI61gzU.js → schedule-connector-schema-B_xO5z5B.js} +1 -1
- package/dist/{schedule-listener-CUyUFFR1.d.ts → schedule-listener-DKh0hnkK.d.ts} +5 -5
- package/dist/{schedule-listener-ePAjians.js → schedule-listener-DP9Jhc6U.js} +14 -4
- package/dist/settings-reader-CBrgz01o.d.ts +18 -0
- package/dist/{settings-reader-BSU6JyvM.d.ts → settings-schema-zhnMIa8I.d.ts} +1 -16
- package/dist/{slack-connector-schema-BCNWluHM.js → slack-connector-schema-C1zEf4TG.js} +1 -1
- package/dist/{slack-listener-Bv5xI9gC.d.ts → slack-listener-COQA8wAZ.d.ts} +4 -4
- package/dist/{slack-listener-ClQuHhEF.js → slack-listener-DUKPcpJH.js} +7 -7
- package/dist/{mcp-QeNCBhOD.js → yaml-render-OhUN-qkS.js} +52 -34
- package/package.json +21 -1
- /package/dist/{file-system-BeOKXjlV.d.ts → file-system-Wub9Nto4.d.ts} +0 -0
- /package/dist/{process-runner-DfniuWVU.d.ts → process-runner-D5I_jhYQ.d.ts} +0 -0
- /package/dist/{profiles-wMRnjSid.js → profiles-MnXvYfZF.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "../
|
|
2
|
-
import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-
|
|
3
|
-
import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "../discord-connector-schema-
|
|
1
|
+
import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "../diagnostic-log-Bxe7Bbvw.js";
|
|
2
|
+
import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-1PxjN-Uk.js";
|
|
3
|
+
import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "../discord-connector-schema-CPgcZkXh.js";
|
|
4
4
|
|
|
5
5
|
//#region lib/engine/http/http-client.d.ts
|
|
6
6
|
type HttpRequest = {
|
|
@@ -19,7 +19,7 @@ declare abstract class FunnelHttpClient {
|
|
|
19
19
|
abstract fetch(request: HttpRequest): Promise<HttpResponse>;
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
|
-
//#region lib/connectors/discord-adapter.d.ts
|
|
22
|
+
//#region lib/engine/connectors/discord-adapter.d.ts
|
|
23
23
|
type Deps$1 = {
|
|
24
24
|
config: DiscordConnectorConfig; /** Environment used to resolve a `botTokenEnv` reference. Defaults to process.env. */
|
|
25
25
|
env?: NodeJS.ProcessEnv;
|
|
@@ -32,7 +32,7 @@ declare class FunnelDiscordAdapter extends FunnelConnectorAdapter {
|
|
|
32
32
|
call(input: CallInput): Promise<unknown>;
|
|
33
33
|
}
|
|
34
34
|
//#endregion
|
|
35
|
-
//#region lib/connectors/discord-event-processor.d.ts
|
|
35
|
+
//#region lib/engine/connectors/discord-event-processor.d.ts
|
|
36
36
|
type DiscordInboundMessage = {
|
|
37
37
|
authorId: string;
|
|
38
38
|
authorIsBot: boolean;
|
|
@@ -59,7 +59,7 @@ declare class FunnelDiscordEventProcessor {
|
|
|
59
59
|
process(message: DiscordInboundMessage): DiscordProcessed;
|
|
60
60
|
}
|
|
61
61
|
//#endregion
|
|
62
|
-
//#region lib/connectors/discord-listener.d.ts
|
|
62
|
+
//#region lib/engine/connectors/discord-listener.d.ts
|
|
63
63
|
type Deps = {
|
|
64
64
|
config: DiscordConnectorConfig; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
|
|
65
65
|
channelId?: string; /** Environment used to resolve a `botTokenEnv` reference. Defaults to process.env. */
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as FunnelDiscordEventProcessor, r as FunnelDiscordAdapter, t as FunnelDiscordListener } from "../discord-listener-
|
|
2
|
-
import { t as discordConnectorSchema } from "../discord-connector-schema-
|
|
1
|
+
import { n as FunnelDiscordEventProcessor, r as FunnelDiscordAdapter, t as FunnelDiscordListener } from "../discord-listener-C0MoKdQO.js";
|
|
2
|
+
import { t as discordConnectorSchema } from "../discord-connector-schema-B_N6IXLz.js";
|
|
3
3
|
export { FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelDiscordListener, discordConnectorSchema };
|
package/dist/connectors/gh.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "../
|
|
2
|
-
import { r as FunnelProcessRunner } from "../process-runner-
|
|
3
|
-
import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-
|
|
4
|
-
import { n as ghConnectorSchema, t as GhConnectorConfig } from "../gh-connector-schema-
|
|
1
|
+
import { S as FunnelLogger, b as FunnelConnectorListener, o as ConnectorDiagnosticLog, x as NotifyFn } from "../diagnostic-log-Bxe7Bbvw.js";
|
|
2
|
+
import { r as FunnelProcessRunner } from "../process-runner-D5I_jhYQ.js";
|
|
3
|
+
import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-1PxjN-Uk.js";
|
|
4
|
+
import { n as ghConnectorSchema, t as GhConnectorConfig } from "../gh-connector-schema-CU1ojfIF.js";
|
|
5
5
|
|
|
6
|
-
//#region lib/connectors/gh-adapter.d.ts
|
|
6
|
+
//#region lib/engine/connectors/gh-adapter.d.ts
|
|
7
7
|
type Deps$1 = {
|
|
8
8
|
process?: FunnelProcessRunner;
|
|
9
9
|
};
|
|
@@ -13,7 +13,7 @@ declare class FunnelGhAdapter extends FunnelConnectorAdapter {
|
|
|
13
13
|
call(input: CallInput): Promise<unknown>;
|
|
14
14
|
}
|
|
15
15
|
//#endregion
|
|
16
|
-
//#region lib/connectors/gh-listener.d.ts
|
|
16
|
+
//#region lib/engine/connectors/gh-listener.d.ts
|
|
17
17
|
type Deps = {
|
|
18
18
|
config: GhConnectorConfig; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
|
|
19
19
|
channelId?: string;
|
package/dist/connectors/gh.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as ghConnectorSchema } from "../gh-connector-schema-
|
|
2
|
-
import { n as FunnelGhAdapter, t as FunnelGhListener } from "../gh-listener-
|
|
1
|
+
import { t as ghConnectorSchema } from "../gh-connector-schema-DUcZgN2Q.js";
|
|
2
|
+
import { n as FunnelGhAdapter, t as FunnelGhListener } from "../gh-listener-Dsx6AmhH.js";
|
|
3
3
|
export { FunnelGhAdapter, FunnelGhListener, ghConnectorSchema };
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import { a as ScheduleEntry, c as scheduleEntrySchema, i as ScheduleConnectorConfig, l as ScheduleStateStore, n as ScheduleOnFired, o as scheduleCatchupPolicySchema, r as ScheduleCatchupPolicy, s as scheduleConnectorSchema, t as FunnelScheduleListener } from "../schedule-listener-
|
|
1
|
+
import { a as ScheduleEntry, c as scheduleEntrySchema, i as ScheduleConnectorConfig, l as ScheduleStateStore, n as ScheduleOnFired, o as scheduleCatchupPolicySchema, r as ScheduleCatchupPolicy, s as scheduleConnectorSchema, t as FunnelScheduleListener } from "../schedule-listener-DKh0hnkK.js";
|
|
2
2
|
|
|
3
|
-
//#region lib/connectors/match-cron.d.ts
|
|
3
|
+
//#region lib/engine/connectors/match-cron.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns true when `date` (local time) satisfies a 5-field cron expression.
|
|
6
|
+
*
|
|
7
|
+
* Two deliberate deviations from Vixie cron, called out so schedules are not
|
|
8
|
+
* written expecting the other behavior:
|
|
9
|
+
* - Day-of-month and day-of-week are ANDed, not ORed. Vixie cron fires when
|
|
10
|
+
* EITHER matches once both are restricted; here every field must match.
|
|
11
|
+
* - Day-of-week is 0-6 (Sunday=0). `7` for Sunday is NOT accepted (it throws
|
|
12
|
+
* as out-of-range); use `0`.
|
|
13
|
+
*/
|
|
4
14
|
declare const matchCron: (expr: string, date: Date) => boolean;
|
|
5
15
|
//#endregion
|
|
6
16
|
export { FunnelScheduleListener, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleEntry, ScheduleOnFired, ScheduleStateStore, matchCron, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as ScheduleStateStore, r as matchCron, t as FunnelScheduleListener } from "../schedule-listener-
|
|
2
|
-
import { n as scheduleConnectorSchema, r as scheduleEntrySchema, t as scheduleCatchupPolicySchema } from "../schedule-connector-schema-
|
|
1
|
+
import { n as ScheduleStateStore, r as matchCron, t as FunnelScheduleListener } from "../schedule-listener-DP9Jhc6U.js";
|
|
2
|
+
import { n as scheduleConnectorSchema, r as scheduleEntrySchema, t as scheduleCatchupPolicySchema } from "../schedule-connector-schema-B_xO5z5B.js";
|
|
3
3
|
export { FunnelScheduleListener, ScheduleStateStore, matchCron, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-
|
|
2
|
-
import { a as slackConnectorSchema, c as SlackProcessedEmit, d as SlackSkipReason, i as SlackConnectorConfig, l as SlackProcessedSkip, n as SlackOnAppCreated, o as FunnelSlackEventProcessor, r as SlackPreprocessEvent, s as SlackProcessed, t as FunnelSlackListener, u as SlackRawEvent } from "../slack-listener-
|
|
1
|
+
import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-1PxjN-Uk.js";
|
|
2
|
+
import { a as slackConnectorSchema, c as SlackProcessedEmit, d as SlackSkipReason, i as SlackConnectorConfig, l as SlackProcessedSkip, n as SlackOnAppCreated, o as FunnelSlackEventProcessor, r as SlackPreprocessEvent, s as SlackProcessed, t as FunnelSlackListener, u as SlackRawEvent } from "../slack-listener-COQA8wAZ.js";
|
|
3
3
|
|
|
4
|
-
//#region lib/connectors/slack-adapter.d.ts
|
|
4
|
+
//#region lib/engine/connectors/slack-adapter.d.ts
|
|
5
5
|
type SlackWebClientLike = {
|
|
6
6
|
apiCall: (method: string, options?: Record<string, unknown>) => Promise<unknown>;
|
|
7
7
|
};
|
package/dist/connectors/slack.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as FunnelSlackEventProcessor, r as FunnelSlackAdapter, t as FunnelSlackListener } from "../slack-listener-
|
|
2
|
-
import { t as slackConnectorSchema } from "../slack-connector-schema-
|
|
1
|
+
import { n as FunnelSlackEventProcessor, r as FunnelSlackAdapter, t as FunnelSlackListener } from "../slack-listener-DUKPcpJH.js";
|
|
2
|
+
import { t as slackConnectorSchema } from "../slack-connector-schema-C1zEf4TG.js";
|
|
3
3
|
export { FunnelSlackAdapter, FunnelSlackEventProcessor, FunnelSlackListener, slackConnectorSchema };
|
|
@@ -13,7 +13,7 @@ declare abstract class FunnelLogger {
|
|
|
13
13
|
abstract readonly file: string | null;
|
|
14
14
|
}
|
|
15
15
|
//#endregion
|
|
16
|
-
//#region lib/connectors/connector-listener.d.ts
|
|
16
|
+
//#region lib/engine/connectors/connector-listener.d.ts
|
|
17
17
|
type NotifyFn = (content: string, meta?: Record<string, string>) => Promise<void>;
|
|
18
18
|
/**
|
|
19
19
|
* Long-lived event source for one connector.
|
|
@@ -32,7 +32,7 @@ declare abstract class FunnelConnectorListener {
|
|
|
32
32
|
isAlive(): boolean;
|
|
33
33
|
}
|
|
34
34
|
//#endregion
|
|
35
|
-
//#region lib/gateway/
|
|
35
|
+
//#region lib/gateway/diagnostic-log/diagnostic-log.d.ts
|
|
36
36
|
/**
|
|
37
37
|
* Points in the listener's connection lifecycle. The single source of truth
|
|
38
38
|
* for the value set: the `status` column schema, the `ConnectorConnectionStatus`
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
//#region lib/gateway/diagnostic-log/diagnostic-sql-reader.ts
|
|
3
|
+
/**
|
|
4
|
+
* Read-only SQL surface over the three diagnostic tables, for Claude to query
|
|
5
|
+
* the log with arbitrary `SELECT`s. It opens all files read-only and exposes
|
|
6
|
+
* three views — `raw`, `processed`, `connection` — that hide the storage
|
|
7
|
+
* details (the physical table is `leuco_log` and each row's columns live
|
|
8
|
+
* inside a JSON `event` blob): the views surface the columns as plain fields,
|
|
9
|
+
* with `payload` already pulled out of the nested JSON.
|
|
10
|
+
*
|
|
11
|
+
* The tables are separate files. `raw` and `processed` share an `event_id`,
|
|
12
|
+
* so a `JOIN` answers "the event arrived, but what verdict did it get?";
|
|
13
|
+
* `connection` answers the other half — "did the listener ever connect at
|
|
14
|
+
* all?". Writes are impossible: the connection is read-only and `query`
|
|
15
|
+
* rejects anything but a single `SELECT`.
|
|
16
|
+
*/
|
|
17
|
+
var ConnectorDiagnosticSqlReader = class {
|
|
18
|
+
db;
|
|
19
|
+
constructor(props) {
|
|
20
|
+
const db = new Database(props.rawPath, { readonly: true });
|
|
21
|
+
try {
|
|
22
|
+
db.run("PRAGMA busy_timeout = 500");
|
|
23
|
+
db.prepare("ATTACH DATABASE ? AS processeddb").run(props.processedPath);
|
|
24
|
+
db.prepare("ATTACH DATABASE ? AS connectiondb").run(props.connectionPath);
|
|
25
|
+
db.run(rawViewSql);
|
|
26
|
+
db.run(processedViewSql);
|
|
27
|
+
db.run(connectionViewSql);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
db.close();
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
this.db = db;
|
|
33
|
+
Object.freeze(this);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run one read-only `SELECT` and return the rows. Returns an `Error` (rather
|
|
37
|
+
* than throwing) for a non-SELECT statement or a SQL error, so the caller
|
|
38
|
+
* can surface the message without a stack trace.
|
|
39
|
+
*/
|
|
40
|
+
query(sql, params = []) {
|
|
41
|
+
const trimmed = sql.trim().replace(/;$/, "").trim();
|
|
42
|
+
if (!/^select\b/i.test(trimmed)) return /* @__PURE__ */ new Error("only a single SELECT statement is allowed");
|
|
43
|
+
if (trimmed.includes(";")) return /* @__PURE__ */ new Error("only a single statement is allowed (remove the ';')");
|
|
44
|
+
try {
|
|
45
|
+
return this.db.prepare(trimmed).all(...params);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
close() {
|
|
51
|
+
this.db.close();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const rawViewSql = `CREATE TEMP VIEW raw AS SELECT
|
|
55
|
+
seq,
|
|
56
|
+
ts,
|
|
57
|
+
json_extract(event, '$.event_id') AS event_id,
|
|
58
|
+
json_extract(event, '$.type') AS type,
|
|
59
|
+
json_extract(event, '$.connector_id') AS connector_id,
|
|
60
|
+
json_extract(event, '$.channel_id') AS channel_id,
|
|
61
|
+
json_extract(event, '$.payload') AS payload
|
|
62
|
+
FROM main.leuco_log`;
|
|
63
|
+
const processedViewSql = `CREATE TEMP VIEW processed AS SELECT
|
|
64
|
+
seq,
|
|
65
|
+
ts,
|
|
66
|
+
json_extract(event, '$.event_id') AS event_id,
|
|
67
|
+
json_extract(event, '$.type') AS type,
|
|
68
|
+
json_extract(event, '$.connector_id') AS connector_id,
|
|
69
|
+
json_extract(event, '$.channel_id') AS channel_id,
|
|
70
|
+
json_extract(event, '$.outcome') AS outcome,
|
|
71
|
+
json_extract(event, '$.payload') AS payload
|
|
72
|
+
FROM processeddb.leuco_log`;
|
|
73
|
+
const connectionViewSql = `CREATE TEMP VIEW connection AS SELECT
|
|
74
|
+
seq,
|
|
75
|
+
ts,
|
|
76
|
+
json_extract(event, '$.type') AS type,
|
|
77
|
+
json_extract(event, '$.connector_id') AS connector_id,
|
|
78
|
+
json_extract(event, '$.channel_id') AS channel_id,
|
|
79
|
+
json_extract(event, '$.status') AS status,
|
|
80
|
+
json_extract(event, '$.detail') AS detail
|
|
81
|
+
FROM connectiondb.leuco_log`;
|
|
82
|
+
//#endregion
|
|
83
|
+
export { ConnectorDiagnosticSqlReader as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as DiagnosticsGatewayProbe, c as FunnelDiagnostics, d as DiagnosticEvent, f as previewOf, h as toDiagnosticEvent, i as DiagnosticsChannelSource, l as ReplayResult, m as toDiagnosticConnectionError, n as DiagnoseAllReport, o as DiagnosticsPublisher, p as queryRows, r as DiagnosisStatus, s as DiagnosticsTokenReader, t as ChannelDiagnosis, u as DiagnosticConnectionError } from "./funnel-diagnostics-qWy5tPSq.js";
|
|
2
|
+
export { ChannelDiagnosis, DiagnoseAllReport, DiagnosisStatus, DiagnosticConnectionError, DiagnosticEvent, DiagnosticsChannelSource, DiagnosticsGatewayProbe, DiagnosticsPublisher, DiagnosticsTokenReader, FunnelDiagnostics, ReplayResult, previewOf, queryRows, toDiagnosticConnectionError, toDiagnosticEvent };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
//#region lib/connectors/discord-connector-schema.ts
|
|
2
|
+
//#region lib/engine/connectors/discord-connector-schema.ts
|
|
3
3
|
/**
|
|
4
4
|
* Like slack, a discord connector holds either a literal `botToken` or a
|
|
5
5
|
* `botTokenEnv` reference resolved from `process.env` at listener start. The
|
package/dist/{discord-connector-schema-R0Uu-3ns.d.ts → discord-connector-schema-CPgcZkXh.d.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
//#region lib/connectors/discord-connector-schema.d.ts
|
|
3
|
+
//#region lib/engine/connectors/discord-connector-schema.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Like slack, a discord connector holds either a literal `botToken` or a
|
|
6
6
|
* `botTokenEnv` reference resolved from `process.env` at listener start. The
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as FunnelConnectorAdapter } from "./connector-adapter-
|
|
2
|
-
import { t as resolveConnectorToken } from "./resolve-connector-token-
|
|
3
|
-
import { t as FunnelConnectorListener } from "./connector-listener-
|
|
1
|
+
import { t as FunnelConnectorAdapter } from "./connector-adapter-qwXLjQId.js";
|
|
2
|
+
import { t as resolveConnectorToken } from "./resolve-connector-token-CczqG_Ig.js";
|
|
3
|
+
import { t as FunnelConnectorListener } from "./connector-listener-CpHBecCj.js";
|
|
4
4
|
import { Client, GatewayIntentBits, Partials } from "discord.js";
|
|
5
5
|
//#region lib/engine/http/http-client.ts
|
|
6
6
|
var FunnelHttpClient = class {};
|
|
@@ -26,7 +26,7 @@ var NodeFunnelHttpClient = class extends FunnelHttpClient {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
//#endregion
|
|
29
|
-
//#region lib/connectors/discord-adapter.ts
|
|
29
|
+
//#region lib/engine/connectors/discord-adapter.ts
|
|
30
30
|
const DISCORD_API_BASE = "https://discord.com/api/v10";
|
|
31
31
|
const defaultHttp = new NodeFunnelHttpClient();
|
|
32
32
|
var FunnelDiscordAdapter = class extends FunnelConnectorAdapter {
|
|
@@ -63,7 +63,7 @@ var FunnelDiscordAdapter = class extends FunnelConnectorAdapter {
|
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
//#endregion
|
|
66
|
-
//#region lib/connectors/discord-event-processor.ts
|
|
66
|
+
//#region lib/engine/connectors/discord-event-processor.ts
|
|
67
67
|
var FunnelDiscordEventProcessor = class {
|
|
68
68
|
ownUserId;
|
|
69
69
|
constructor(props) {
|
|
@@ -86,7 +86,7 @@ var FunnelDiscordEventProcessor = class {
|
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
//#endregion
|
|
89
|
-
//#region lib/connectors/discord-listener.ts
|
|
89
|
+
//#region lib/engine/connectors/discord-listener.ts
|
|
90
90
|
var FunnelDiscordListener = class extends FunnelConnectorListener {
|
|
91
91
|
config;
|
|
92
92
|
channelId;
|
package/dist/docs.d.ts
ADDED
package/dist/docs.js
ADDED
package/dist/doctor.d.ts
ADDED
package/dist/doctor.js
ADDED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { t as ChannelConfig } from "./settings-schema-zhnMIa8I.js";
|
|
2
|
+
import { n as FunnelIdGenerator } from "./settings-reader-CBrgz01o.js";
|
|
3
|
+
import { S as FunnelLogger } from "./diagnostic-log-Bxe7Bbvw.js";
|
|
4
|
+
import { r as FunnelProcessRunner } from "./process-runner-D5I_jhYQ.js";
|
|
5
|
+
import { n as FunnelFileSystem } from "./file-system-Wub9Nto4.js";
|
|
5
6
|
|
|
6
7
|
//#region lib/engine/claude/channel-resolver.d.ts
|
|
7
8
|
type ChannelResolver = {
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-CzYgZpq2.js";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
//#region lib/services/diagnostics/diagnostic-event.ts
|
|
5
|
+
const stringOrNull = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
6
|
+
const numberOrNull = (value) => typeof value === "number" ? value : null;
|
|
7
|
+
const stringOr = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
8
|
+
const isStringKeyedObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9
|
+
const parsePayloadObject = (payload) => {
|
|
10
|
+
if (payload === null) return null;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(payload);
|
|
13
|
+
if (isStringKeyedObject(parsed)) return parsed;
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
const truncate = (text, max) => text.length <= max ? text : `${text.slice(0, max)}…`;
|
|
20
|
+
const previewOf = (payload) => {
|
|
21
|
+
if (typeof payload !== "string" || payload.length === 0) return null;
|
|
22
|
+
const parsed = parsePayloadObject(payload);
|
|
23
|
+
if (parsed !== null && "text" in parsed) return truncate(String(parsed.text), 60);
|
|
24
|
+
return truncate(payload, 60);
|
|
25
|
+
};
|
|
26
|
+
const toDiagnosticEvent = (row) => {
|
|
27
|
+
const payload = stringOrNull(row.payload);
|
|
28
|
+
return {
|
|
29
|
+
seq: numberOrNull(row.seq),
|
|
30
|
+
ts: numberOrNull(row.ts),
|
|
31
|
+
type: stringOr(row.type, "?"),
|
|
32
|
+
outcome: stringOr(row.outcome, "?"),
|
|
33
|
+
eventId: stringOrNull(row.event_id),
|
|
34
|
+
payload,
|
|
35
|
+
payloadParsed: parsePayloadObject(payload),
|
|
36
|
+
preview: previewOf(row.payload)
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const toDiagnosticConnectionError = (row) => ({
|
|
40
|
+
seq: numberOrNull(row.seq),
|
|
41
|
+
ts: numberOrNull(row.ts),
|
|
42
|
+
type: stringOr(row.type, "?"),
|
|
43
|
+
status: stringOr(row.status, "?"),
|
|
44
|
+
detail: stringOrNull(row.detail)
|
|
45
|
+
});
|
|
46
|
+
const queryRows = (reader, sql, params) => {
|
|
47
|
+
try {
|
|
48
|
+
return reader.query(sql, params);
|
|
49
|
+
} finally {
|
|
50
|
+
reader.close();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region lib/services/diagnostics/funnel-diagnostics.ts
|
|
55
|
+
const isGatewayStatusResponse = (value) => {
|
|
56
|
+
if (value === null || typeof value !== "object") return false;
|
|
57
|
+
if (!("clients" in value) || !Array.isArray(value.clients)) return false;
|
|
58
|
+
if (!("listeners" in value) || !Array.isArray(value.listeners)) return false;
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
61
|
+
const connectorOf = (channel, connectorId) => {
|
|
62
|
+
if (connectorId === null) return void 0;
|
|
63
|
+
return channel.connectors?.find((connector) => connector.id === connectorId)?.name;
|
|
64
|
+
};
|
|
65
|
+
const buildDiagnosis = (report) => {
|
|
66
|
+
const rootCause = (report.connectionErrors[report.connectionErrors.length - 1] ?? null)?.detail ?? null;
|
|
67
|
+
if (!report.gateway.running) return {
|
|
68
|
+
status: "error",
|
|
69
|
+
message: "gateway is not running",
|
|
70
|
+
nextActions: ["fnl gateway start"],
|
|
71
|
+
rootCause: null
|
|
72
|
+
};
|
|
73
|
+
const channel = report.channel;
|
|
74
|
+
if (!(report.listeners.length > 0)) return {
|
|
75
|
+
status: "warn",
|
|
76
|
+
message: "no connectors configured on this channel",
|
|
77
|
+
nextActions: [`fnl channels ${channel} connectors add <name> --type=slack ...`],
|
|
78
|
+
rootCause: null
|
|
79
|
+
};
|
|
80
|
+
const allDead = report.listeners.every((l) => !l.alive);
|
|
81
|
+
const someDead = report.listeners.some((l) => !l.alive);
|
|
82
|
+
if (allDead) return {
|
|
83
|
+
status: "error",
|
|
84
|
+
message: "all listeners are dead",
|
|
85
|
+
nextActions: ["fnl doctor --fix", "fnl doctor --fix --aggressive"],
|
|
86
|
+
rootCause
|
|
87
|
+
};
|
|
88
|
+
if (someDead) return {
|
|
89
|
+
status: "warn",
|
|
90
|
+
message: "some listeners are dead",
|
|
91
|
+
nextActions: ["fnl doctor --fix"],
|
|
92
|
+
rootCause
|
|
93
|
+
};
|
|
94
|
+
if (report.claudeClients === 0) return {
|
|
95
|
+
status: "warn",
|
|
96
|
+
message: "no Claude connected to this channel",
|
|
97
|
+
nextActions: [`fnl claude --channel ${channel}`],
|
|
98
|
+
rootCause: null
|
|
99
|
+
};
|
|
100
|
+
if (report.listeners.some((l) => l.errors > 0)) return {
|
|
101
|
+
status: "warn",
|
|
102
|
+
message: "listeners have errors",
|
|
103
|
+
nextActions: ["fnl gateway logs"],
|
|
104
|
+
rootCause
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
status: "ok",
|
|
108
|
+
message: "everything looks healthy",
|
|
109
|
+
nextActions: [],
|
|
110
|
+
rootCause: null
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Programmable diagnostics surface — used by both the CLI (fnl debug …) and
|
|
115
|
+
* the MCP tools (fnl_debug, fnl_recent_events, …). Pure read-side, no
|
|
116
|
+
* mutation; pair with FunnelRecovery for self-healing actions.
|
|
117
|
+
*/
|
|
118
|
+
var FunnelDiagnostics = class {
|
|
119
|
+
constructor(props) {
|
|
120
|
+
this.props = props;
|
|
121
|
+
Object.freeze(this);
|
|
122
|
+
}
|
|
123
|
+
async diagnose(channelName) {
|
|
124
|
+
const channels = this.props.channels.list();
|
|
125
|
+
const target = channelName ? channels.find((ch) => ch.name === channelName) ?? null : channels[0] ?? null;
|
|
126
|
+
if (!target) return null;
|
|
127
|
+
const gatewayBody = await this.fetchGatewayStatus();
|
|
128
|
+
const store = this.resolveStore();
|
|
129
|
+
return this.buildChannelDiagnosis(target, gatewayBody, store, 5);
|
|
130
|
+
}
|
|
131
|
+
async diagnoseAll() {
|
|
132
|
+
const channels = this.props.channels.list();
|
|
133
|
+
const gatewayBody = await this.fetchGatewayStatus();
|
|
134
|
+
const store = this.resolveStore();
|
|
135
|
+
const reports = await Promise.all(channels.map((ch) => this.buildChannelDiagnosis(ch, gatewayBody, store, 5)));
|
|
136
|
+
const errorChannels = reports.filter((r) => r.diagnosis.status === "error").map((r) => r.channel);
|
|
137
|
+
const warnChannels = reports.filter((r) => r.diagnosis.status === "warn").map((r) => r.channel);
|
|
138
|
+
const okChannels = reports.filter((r) => r.diagnosis.status === "ok").map((r) => r.channel);
|
|
139
|
+
const uniqueActions = [...new Set(reports.flatMap((r) => r.diagnosis.nextActions))];
|
|
140
|
+
return {
|
|
141
|
+
summary: {
|
|
142
|
+
total: reports.length,
|
|
143
|
+
ok: okChannels.length,
|
|
144
|
+
warn: warnChannels.length,
|
|
145
|
+
error: errorChannels.length,
|
|
146
|
+
criticalChannels: errorChannels,
|
|
147
|
+
warnChannels,
|
|
148
|
+
suggestedActions: uniqueActions
|
|
149
|
+
},
|
|
150
|
+
channels: reports
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
async recentEvents(channelName, limit = 20) {
|
|
154
|
+
const store = this.resolveStore();
|
|
155
|
+
if (!store) return [];
|
|
156
|
+
const channelId = this.resolveChannelId(channelName);
|
|
157
|
+
if (channelName && !channelId) return [];
|
|
158
|
+
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
159
|
+
const rows = channelId ? queryRows(reader, "SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT ?", [channelId, limit]) : queryRows(reader, "SELECT seq, ts, type, outcome, payload FROM processed ORDER BY seq DESC LIMIT ?", [limit]);
|
|
160
|
+
if (rows instanceof Error) return [];
|
|
161
|
+
return rows.reverse().map(toDiagnosticEvent);
|
|
162
|
+
}
|
|
163
|
+
async droppedEvents(channelName, limit = 20) {
|
|
164
|
+
const store = this.resolveStore();
|
|
165
|
+
if (!store) return [];
|
|
166
|
+
const channelId = this.resolveChannelId(channelName);
|
|
167
|
+
if (channelName && !channelId) return [];
|
|
168
|
+
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
169
|
+
const rows = channelId ? queryRows(reader, "SELECT seq, ts, type, outcome, payload, event_id FROM processed WHERE channel_id = ? AND outcome LIKE 'skip:%' ORDER BY seq DESC LIMIT ?", [channelId, limit]) : queryRows(reader, "SELECT seq, ts, type, outcome, payload, event_id FROM processed WHERE outcome LIKE 'skip:%' ORDER BY seq DESC LIMIT ?", [limit]);
|
|
170
|
+
if (rows instanceof Error) return [];
|
|
171
|
+
return rows.reverse().map(toDiagnosticEvent);
|
|
172
|
+
}
|
|
173
|
+
async connectionErrors(channelName, limit = 20) {
|
|
174
|
+
const store = this.resolveStore();
|
|
175
|
+
if (!store) return [];
|
|
176
|
+
const channelId = this.resolveChannelId(channelName);
|
|
177
|
+
if (channelName && !channelId) return [];
|
|
178
|
+
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
179
|
+
const rows = channelId ? queryRows(reader, "SELECT seq, ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT ?", [channelId, limit]) : queryRows(reader, "SELECT seq, ts, type, status, detail FROM connection WHERE status IN ('auth-failed','error') ORDER BY seq DESC LIMIT ?", [limit]);
|
|
180
|
+
if (rows instanceof Error) return [];
|
|
181
|
+
return rows.reverse().map(toDiagnosticConnectionError);
|
|
182
|
+
}
|
|
183
|
+
async replay(channelName, seq) {
|
|
184
|
+
const channel = this.props.channels.list().find((ch) => ch.name === channelName);
|
|
185
|
+
if (!channel) return { state: "not-found" };
|
|
186
|
+
const store = this.resolveStore();
|
|
187
|
+
if (!store) return {
|
|
188
|
+
state: "error",
|
|
189
|
+
reason: "no diagnostic store yet"
|
|
190
|
+
};
|
|
191
|
+
const reader = new ConnectorDiagnosticSqlReader(store);
|
|
192
|
+
const rows = seq !== void 0 ? queryRows(reader, "SELECT seq, event_id, type, payload, connector_id, channel_id FROM processed WHERE channel_id = ? AND seq = ? LIMIT 1", [channel.id, seq]) : queryRows(reader, "SELECT seq, event_id, type, payload, connector_id, channel_id FROM processed WHERE channel_id = ? AND outcome LIKE 'emitted%' ORDER BY seq DESC LIMIT 1", [channel.id]);
|
|
193
|
+
if (rows instanceof Error) return {
|
|
194
|
+
state: "error",
|
|
195
|
+
reason: rows.message
|
|
196
|
+
};
|
|
197
|
+
const firstRow = rows[0];
|
|
198
|
+
if (!firstRow) return { state: "not-found" };
|
|
199
|
+
const replaySeq = typeof firstRow.seq === "number" ? firstRow.seq : null;
|
|
200
|
+
const eventId = typeof firstRow.event_id === "string" ? firstRow.event_id : null;
|
|
201
|
+
const connectorId = typeof firstRow.connector_id === "string" ? firstRow.connector_id : null;
|
|
202
|
+
let content = typeof firstRow.payload === "string" ? firstRow.payload : null;
|
|
203
|
+
if ((!content || content.length === 0) && eventId) {
|
|
204
|
+
const rawRows = queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT payload FROM raw WHERE event_id = ? LIMIT 1", [eventId]);
|
|
205
|
+
const rawRow = rawRows instanceof Error ? null : rawRows[0];
|
|
206
|
+
if (rawRow) content = typeof rawRow.payload === "string" ? rawRow.payload : null;
|
|
207
|
+
}
|
|
208
|
+
if (!content) return {
|
|
209
|
+
state: "error",
|
|
210
|
+
reason: "event has no payload to replay"
|
|
211
|
+
};
|
|
212
|
+
const connectorName = connectorOf(channel, connectorId);
|
|
213
|
+
const result = await this.props.publisher.publish(channel.name, {
|
|
214
|
+
content,
|
|
215
|
+
connector: connectorName
|
|
216
|
+
});
|
|
217
|
+
if (result.state === "offline") return { state: "offline" };
|
|
218
|
+
if (result.state === "error") return {
|
|
219
|
+
state: "error",
|
|
220
|
+
reason: result.reason
|
|
221
|
+
};
|
|
222
|
+
return {
|
|
223
|
+
state: "ok",
|
|
224
|
+
seq: replaySeq,
|
|
225
|
+
offset: result.offset,
|
|
226
|
+
preview: content.slice(0, 60)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
resolveStore() {
|
|
230
|
+
const tmpDir = this.props.tmpDir;
|
|
231
|
+
const rawPath = join(tmpDir, "connector-raw.db");
|
|
232
|
+
const processedPath = join(tmpDir, "connector-processed.db");
|
|
233
|
+
const connectionPath = join(tmpDir, "connector-connection.db");
|
|
234
|
+
if (!existsSync(rawPath) || !existsSync(processedPath) || !existsSync(connectionPath)) return null;
|
|
235
|
+
return {
|
|
236
|
+
rawPath,
|
|
237
|
+
processedPath,
|
|
238
|
+
connectionPath
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
resolveChannelId(channelName) {
|
|
242
|
+
if (!channelName) return null;
|
|
243
|
+
return this.props.channels.list().find((ch) => ch.name === channelName)?.id ?? null;
|
|
244
|
+
}
|
|
245
|
+
async fetchGatewayStatus() {
|
|
246
|
+
const gatewayStatus = this.props.gateway.getStatus();
|
|
247
|
+
if (!gatewayStatus.running) return null;
|
|
248
|
+
const token = this.props.gatewayToken.read();
|
|
249
|
+
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
|
250
|
+
const res = await fetch(`http://127.0.0.1:${gatewayStatus.port}/status`, { headers }).catch(() => null);
|
|
251
|
+
if (!res || !res.ok) return null;
|
|
252
|
+
const body = await res.json();
|
|
253
|
+
return isGatewayStatusResponse(body) ? body : null;
|
|
254
|
+
}
|
|
255
|
+
async buildChannelDiagnosis(target, gatewayBody, store, eventLimit) {
|
|
256
|
+
const gatewayStatus = this.props.gateway.getStatus();
|
|
257
|
+
const targetName = target.name;
|
|
258
|
+
const baseReport = {
|
|
259
|
+
channel: targetName,
|
|
260
|
+
channelId: target.id,
|
|
261
|
+
gateway: {
|
|
262
|
+
running: gatewayStatus.running,
|
|
263
|
+
pid: gatewayStatus.pid,
|
|
264
|
+
port: gatewayStatus.running ? gatewayStatus.port : null,
|
|
265
|
+
uptimeMs: gatewayBody?.uptimeMs ?? null
|
|
266
|
+
},
|
|
267
|
+
listeners: [],
|
|
268
|
+
claudeClients: 0,
|
|
269
|
+
recentEvents: [],
|
|
270
|
+
connectionErrors: []
|
|
271
|
+
};
|
|
272
|
+
if (gatewayBody) {
|
|
273
|
+
baseReport.listeners = gatewayBody.listeners.filter((l) => l.channelName === targetName).map((l) => ({
|
|
274
|
+
name: l.name,
|
|
275
|
+
type: l.type,
|
|
276
|
+
alive: l.alive,
|
|
277
|
+
events: l.events,
|
|
278
|
+
errors: l.errors,
|
|
279
|
+
lastEventAt: l.lastEventAt
|
|
280
|
+
}));
|
|
281
|
+
baseReport.claudeClients = gatewayBody.clients.filter((cl) => cl.channelName === targetName).length;
|
|
282
|
+
}
|
|
283
|
+
if (store) {
|
|
284
|
+
const evRows = queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT seq, ts, type, outcome, payload FROM processed WHERE channel_id = ? ORDER BY seq DESC LIMIT ?", [target.id, eventLimit]);
|
|
285
|
+
if (!(evRows instanceof Error)) baseReport.recentEvents = evRows.reverse().map(toDiagnosticEvent);
|
|
286
|
+
const hasDeadListeners = baseReport.listeners.some((l) => !l.alive);
|
|
287
|
+
const hasListenerErrors = baseReport.listeners.some((l) => l.errors > 0);
|
|
288
|
+
if (hasDeadListeners || hasListenerErrors) {
|
|
289
|
+
const errRows = queryRows(new ConnectorDiagnosticSqlReader(store), "SELECT ts, type, status, detail FROM connection WHERE channel_id = ? AND status IN ('auth-failed','error') ORDER BY seq DESC LIMIT 3", [target.id]);
|
|
290
|
+
if (!(errRows instanceof Error)) baseReport.connectionErrors = errRows.reverse().map(toDiagnosticConnectionError);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
...baseReport,
|
|
295
|
+
diagnosis: buildDiagnosis(baseReport)
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
//#endregion
|
|
300
|
+
export { toDiagnosticEvent as a, toDiagnosticConnectionError as i, previewOf as n, queryRows as r, FunnelDiagnostics as t };
|