@interactive-inc/claude-funnel 0.53.0 → 0.56.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 +1229 -486
- 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 +838 -252
- 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 +509 -973
- 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-BbFVqDzz.js} +30 -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
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region lib/services/doctor/funnel-doctor.ts
|
|
2
|
+
/**
|
|
3
|
+
* One-shot diagnose-and-fix entry point. The CLI exposes this as `fnl doctor`,
|
|
4
|
+
* the MCP server exposes it as `fnl_doctor`. Both surfaces should prefer this
|
|
5
|
+
* over chaining FunnelDiagnostics and FunnelRecovery by hand — the orchestration
|
|
6
|
+
* logic (which fixes to attempt, in what order, when to escalate) lives here.
|
|
7
|
+
*/
|
|
8
|
+
var FunnelDoctor = class {
|
|
9
|
+
constructor(props) {
|
|
10
|
+
this.props = props;
|
|
11
|
+
Object.freeze(this);
|
|
12
|
+
}
|
|
13
|
+
async run(mode = "off") {
|
|
14
|
+
if (mode === "off") {
|
|
15
|
+
const after = await this.props.diagnostics.diagnoseAll();
|
|
16
|
+
return this.buildReport({
|
|
17
|
+
before: null,
|
|
18
|
+
after,
|
|
19
|
+
appliedActions: [],
|
|
20
|
+
fixFailed: false
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const before = await this.props.diagnostics.diagnoseAll();
|
|
24
|
+
const applied = [];
|
|
25
|
+
let fixFailed = false;
|
|
26
|
+
if (before.channels.some((ch) => ch.diagnosis.status === "error" && !ch.gateway.running)) {
|
|
27
|
+
const result = await this.props.recovery.ensureGatewayRunning();
|
|
28
|
+
applied.push(...result.actions);
|
|
29
|
+
if (!result.ok) fixFailed = true;
|
|
30
|
+
}
|
|
31
|
+
if (before.channels.some((ch) => ch.listeners.some((l) => !l.alive))) {
|
|
32
|
+
const result = await this.props.recovery.restartAllDeadListeners();
|
|
33
|
+
applied.push(...result.actions);
|
|
34
|
+
if (!result.ok && result.actions.length === 0) fixFailed = true;
|
|
35
|
+
}
|
|
36
|
+
if (mode === "aggressive") {
|
|
37
|
+
if (applied.length === 0 || before.channels.some((ch) => ch.diagnosis.status === "error")) {
|
|
38
|
+
const result = await this.props.recovery.restartGateway();
|
|
39
|
+
applied.push(...result.actions);
|
|
40
|
+
if (!result.ok) fixFailed = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const after = await this.props.diagnostics.diagnoseAll();
|
|
44
|
+
return this.buildReport({
|
|
45
|
+
before,
|
|
46
|
+
after,
|
|
47
|
+
appliedActions: applied,
|
|
48
|
+
fixFailed
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
buildReport(input) {
|
|
52
|
+
const remaining = input.after.channels.filter((ch) => ch.diagnosis.status !== "ok").map((ch) => ({
|
|
53
|
+
channel: ch.channel,
|
|
54
|
+
diagnosis: ch.diagnosis
|
|
55
|
+
}));
|
|
56
|
+
const status = input.fixFailed ? "error" : remaining.length === 0 ? "ok" : "warn";
|
|
57
|
+
return {
|
|
58
|
+
status,
|
|
59
|
+
message: buildMessage({
|
|
60
|
+
mode: input.before === null ? "off" : "fix",
|
|
61
|
+
status,
|
|
62
|
+
appliedCount: input.appliedActions.length,
|
|
63
|
+
remainingCount: remaining.length
|
|
64
|
+
}),
|
|
65
|
+
before: input.before,
|
|
66
|
+
after: input.after,
|
|
67
|
+
appliedActions: input.appliedActions,
|
|
68
|
+
remainingIssues: remaining
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const buildMessage = (input) => {
|
|
73
|
+
if (input.mode === "off") {
|
|
74
|
+
if (input.remainingCount === 0) return "all channels healthy";
|
|
75
|
+
return `${input.remainingCount} channel(s) need attention — run \`fnl doctor --fix\` to apply suggested fixes`;
|
|
76
|
+
}
|
|
77
|
+
if (input.status === "error") return "one or more fix steps failed — check fnl gateway logs";
|
|
78
|
+
if (input.status === "ok") return `applied ${input.appliedCount} fix(es), all channels healthy`;
|
|
79
|
+
return `applied ${input.appliedCount} fix(es), ${input.remainingCount} channel(s) still need attention`;
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { FunnelDoctor as t };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { t as ChannelConfig } from "./settings-schema-zhnMIa8I.js";
|
|
2
|
+
|
|
3
|
+
//#region lib/services/recovery/funnel-recovery.d.ts
|
|
4
|
+
/** Narrow gateway control — start / stop / restart and a probe. */
|
|
5
|
+
type RecoveryGatewayControl = {
|
|
6
|
+
isRunning(): boolean;
|
|
7
|
+
start(options?: {
|
|
8
|
+
caffeinate?: boolean;
|
|
9
|
+
}): Promise<boolean>;
|
|
10
|
+
restart(options?: {
|
|
11
|
+
onlyIfRunning?: boolean;
|
|
12
|
+
caffeinate?: boolean;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
ok: boolean;
|
|
15
|
+
wasRunning: boolean;
|
|
16
|
+
stopped: boolean;
|
|
17
|
+
started: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
/** Narrow listeners client — restart per (channel, connector). */
|
|
21
|
+
type RecoveryListenerControl = {
|
|
22
|
+
list(): Promise<{
|
|
23
|
+
state: "ok";
|
|
24
|
+
listeners: {
|
|
25
|
+
channelName: string;
|
|
26
|
+
name: string;
|
|
27
|
+
alive: boolean;
|
|
28
|
+
}[];
|
|
29
|
+
} | {
|
|
30
|
+
state: "offline";
|
|
31
|
+
} | {
|
|
32
|
+
state: "error";
|
|
33
|
+
reason: string;
|
|
34
|
+
}>;
|
|
35
|
+
restart(channelName: string, connectorName: string): Promise<{
|
|
36
|
+
state: "ok";
|
|
37
|
+
} | {
|
|
38
|
+
state: "offline";
|
|
39
|
+
} | {
|
|
40
|
+
state: "not-found";
|
|
41
|
+
} | {
|
|
42
|
+
state: "error";
|
|
43
|
+
reason: string;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
/** Narrow channel registry. */
|
|
47
|
+
type RecoveryChannelSource = {
|
|
48
|
+
list(): ChannelConfig[];
|
|
49
|
+
};
|
|
50
|
+
type Props = {
|
|
51
|
+
gateway: RecoveryGatewayControl;
|
|
52
|
+
listeners: RecoveryListenerControl;
|
|
53
|
+
channels: RecoveryChannelSource;
|
|
54
|
+
};
|
|
55
|
+
type RecoveryAction = {
|
|
56
|
+
kind: "gateway:started";
|
|
57
|
+
} | {
|
|
58
|
+
kind: "gateway:already-running";
|
|
59
|
+
} | {
|
|
60
|
+
kind: "gateway:restarted";
|
|
61
|
+
} | {
|
|
62
|
+
kind: "listener:restarted";
|
|
63
|
+
channel: string;
|
|
64
|
+
connector: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "listener:skipped";
|
|
67
|
+
channel: string;
|
|
68
|
+
connector: string;
|
|
69
|
+
reason: string;
|
|
70
|
+
};
|
|
71
|
+
type RecoveryResult = {
|
|
72
|
+
ok: boolean;
|
|
73
|
+
actions: RecoveryAction[];
|
|
74
|
+
message: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Programmable self-healing primitives. The CLI / MCP / SDK normally drive
|
|
78
|
+
* these through FunnelDoctor (which decides which fixes to attempt and in
|
|
79
|
+
* what order). Exposed as a building block for hosts that want fine-grained
|
|
80
|
+
* control (custom policies, scripted maintenance).
|
|
81
|
+
*
|
|
82
|
+
* Every method returns a RecoveryResult so the caller can chain without
|
|
83
|
+
* throwing.
|
|
84
|
+
*/
|
|
85
|
+
declare class FunnelRecovery {
|
|
86
|
+
private readonly props;
|
|
87
|
+
constructor(props: Props);
|
|
88
|
+
/**
|
|
89
|
+
* Make sure the gateway daemon is running. Returns immediately if already up.
|
|
90
|
+
*/
|
|
91
|
+
ensureGatewayRunning(): Promise<RecoveryResult>;
|
|
92
|
+
restartGateway(): Promise<RecoveryResult>;
|
|
93
|
+
restartListener(channelName: string, connectorName: string): Promise<RecoveryResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Restart every dead listener across every channel. The gateway must already
|
|
96
|
+
* be running — call ensureGatewayRunning() first if unsure.
|
|
97
|
+
*/
|
|
98
|
+
restartAllDeadListeners(): Promise<RecoveryResult>;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { RecoveryListenerControl as a, RecoveryGatewayControl as i, RecoveryAction as n, RecoveryResult as o, RecoveryChannelSource as r, FunnelRecovery as t };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//#region lib/services/recovery/funnel-recovery.ts
|
|
2
|
+
/**
|
|
3
|
+
* Programmable self-healing primitives. The CLI / MCP / SDK normally drive
|
|
4
|
+
* these through FunnelDoctor (which decides which fixes to attempt and in
|
|
5
|
+
* what order). Exposed as a building block for hosts that want fine-grained
|
|
6
|
+
* control (custom policies, scripted maintenance).
|
|
7
|
+
*
|
|
8
|
+
* Every method returns a RecoveryResult so the caller can chain without
|
|
9
|
+
* throwing.
|
|
10
|
+
*/
|
|
11
|
+
var FunnelRecovery = class {
|
|
12
|
+
constructor(props) {
|
|
13
|
+
this.props = props;
|
|
14
|
+
Object.freeze(this);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Make sure the gateway daemon is running. Returns immediately if already up.
|
|
18
|
+
*/
|
|
19
|
+
async ensureGatewayRunning() {
|
|
20
|
+
if (this.props.gateway.isRunning()) return {
|
|
21
|
+
ok: true,
|
|
22
|
+
actions: [{ kind: "gateway:already-running" }],
|
|
23
|
+
message: "gateway is already running"
|
|
24
|
+
};
|
|
25
|
+
if (!await this.props.gateway.start()) return {
|
|
26
|
+
ok: false,
|
|
27
|
+
actions: [],
|
|
28
|
+
message: "gateway failed to start — check `fnl gateway logs`"
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
actions: [{ kind: "gateway:started" }],
|
|
33
|
+
message: "gateway started"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async restartGateway() {
|
|
37
|
+
if (!(await this.props.gateway.restart()).ok) return {
|
|
38
|
+
ok: false,
|
|
39
|
+
actions: [],
|
|
40
|
+
message: "gateway restart failed — check `fnl gateway logs`"
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
actions: [{ kind: "gateway:restarted" }],
|
|
45
|
+
message: "gateway restarted"
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async restartListener(channelName, connectorName) {
|
|
49
|
+
const op = await this.props.listeners.restart(channelName, connectorName);
|
|
50
|
+
if (op.state === "offline") return {
|
|
51
|
+
ok: false,
|
|
52
|
+
actions: [],
|
|
53
|
+
message: "gateway is not running — call ensureGatewayRunning() first"
|
|
54
|
+
};
|
|
55
|
+
if (op.state === "not-found") return {
|
|
56
|
+
ok: false,
|
|
57
|
+
actions: [{
|
|
58
|
+
kind: "listener:skipped",
|
|
59
|
+
channel: channelName,
|
|
60
|
+
connector: connectorName,
|
|
61
|
+
reason: "not-found"
|
|
62
|
+
}],
|
|
63
|
+
message: `listener not found: ${channelName}/${connectorName}`
|
|
64
|
+
};
|
|
65
|
+
if (op.state === "error") return {
|
|
66
|
+
ok: false,
|
|
67
|
+
actions: [{
|
|
68
|
+
kind: "listener:skipped",
|
|
69
|
+
channel: channelName,
|
|
70
|
+
connector: connectorName,
|
|
71
|
+
reason: op.reason
|
|
72
|
+
}],
|
|
73
|
+
message: `listener restart failed: ${op.reason}`
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
actions: [{
|
|
78
|
+
kind: "listener:restarted",
|
|
79
|
+
channel: channelName,
|
|
80
|
+
connector: connectorName
|
|
81
|
+
}],
|
|
82
|
+
message: `restarted ${channelName}/${connectorName}`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Restart every dead listener across every channel. The gateway must already
|
|
87
|
+
* be running — call ensureGatewayRunning() first if unsure.
|
|
88
|
+
*/
|
|
89
|
+
async restartAllDeadListeners() {
|
|
90
|
+
const listed = await this.props.listeners.list();
|
|
91
|
+
if (listed.state === "offline") return {
|
|
92
|
+
ok: false,
|
|
93
|
+
actions: [],
|
|
94
|
+
message: "gateway is not running — call ensureGatewayRunning() first"
|
|
95
|
+
};
|
|
96
|
+
if (listed.state === "error") return {
|
|
97
|
+
ok: false,
|
|
98
|
+
actions: [],
|
|
99
|
+
message: `could not list listeners: ${listed.reason}`
|
|
100
|
+
};
|
|
101
|
+
const dead = listed.listeners.filter((l) => !l.alive);
|
|
102
|
+
if (dead.length === 0) return {
|
|
103
|
+
ok: true,
|
|
104
|
+
actions: [],
|
|
105
|
+
message: "no dead listeners found"
|
|
106
|
+
};
|
|
107
|
+
const actions = [];
|
|
108
|
+
for (const listener of dead) {
|
|
109
|
+
const op = await this.props.listeners.restart(listener.channelName, listener.name);
|
|
110
|
+
if (op.state === "ok") actions.push({
|
|
111
|
+
kind: "listener:restarted",
|
|
112
|
+
channel: listener.channelName,
|
|
113
|
+
connector: listener.name
|
|
114
|
+
});
|
|
115
|
+
else {
|
|
116
|
+
const reason = op.state === "error" ? op.reason : op.state;
|
|
117
|
+
actions.push({
|
|
118
|
+
kind: "listener:skipped",
|
|
119
|
+
channel: listener.channelName,
|
|
120
|
+
connector: listener.name,
|
|
121
|
+
reason
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const restartedCount = actions.filter((a) => a.kind === "listener:restarted").length;
|
|
126
|
+
return {
|
|
127
|
+
ok: restartedCount > 0,
|
|
128
|
+
actions,
|
|
129
|
+
message: `restarted ${restartedCount}/${dead.length} dead listener(s)`
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
//#endregion
|
|
134
|
+
export { FunnelRecovery as t };
|