@interactive-inc/claude-funnel 0.17.0 → 0.19.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/dist/bin.js +240 -240
- package/dist/connectors/gh.d.ts +1 -1
- package/dist/connectors/gh.js +1 -1
- package/dist/gateway/daemon.js +4 -4
- package/dist/{gh-connector-schema-BZFAS-p-.d.ts → gh-connector-schema-BNyTaASt.d.ts} +3 -0
- package/dist/{gh-connector-schema-2ml29MBC.js → gh-connector-schema-CD5HIkrd.js} +4 -2
- package/dist/index.d.ts +22 -3
- package/dist/index.js +120 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as FunnelDiscordAdapter, n as FunnelDiscordListener, t as discordConnectorSchema } from "./discord-connector-schema-ygf5Df-2.js";
|
|
2
2
|
import { n as FunnelLogger, r as FunnelConnectorListener, t as NodeFunnelLogger } from "./node-logger-DQz_BGOD.js";
|
|
3
|
-
import { a as FunnelProcessRunner, i as NodeFunnelProcessRunner, n as FunnelGhListener, r as FunnelGhAdapter, t as ghConnectorSchema } from "./gh-connector-schema-
|
|
3
|
+
import { a as FunnelProcessRunner, i as NodeFunnelProcessRunner, n as FunnelGhListener, r as FunnelGhAdapter, t as ghConnectorSchema } from "./gh-connector-schema-CD5HIkrd.js";
|
|
4
4
|
import { a as ScheduleStateStore, i as FunnelScheduleListener, n as scheduleConnectorSchema, o as NodeFunnelFileSystem, r as scheduleEntrySchema, s as FunnelFileSystem, t as scheduleCatchupPolicySchema } from "./schedule-connector-schema-FxP7LPlx.js";
|
|
5
5
|
import { i as FunnelSlackAdapter, n as FunnelSlackListener, r as FunnelSlackEventProcessor, t as slackConnectorSchema } from "./slack-connector-schema-B4hsf3AY.js";
|
|
6
6
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -570,7 +570,7 @@ var FunnelClaude = class {
|
|
|
570
570
|
if (!channel) throw new Error(`channel "${options.channel}" not found`);
|
|
571
571
|
if (options.profileName && this.isRunning(options.profileName)) throw new Error(`profile "${options.profileName}" is already running`);
|
|
572
572
|
const cwd = options.cwd ?? globalThis.process.cwd();
|
|
573
|
-
if (!this.mcp.findInstalledName(cwd)) {
|
|
573
|
+
if ((options.installMcp ?? true) && !this.mcp.findInstalledName(cwd)) {
|
|
574
574
|
this.mcp.install(cwd);
|
|
575
575
|
this.logger.info(`added funnel MCP to .mcp.json`, { cwd });
|
|
576
576
|
}
|
|
@@ -592,7 +592,8 @@ var FunnelClaude = class {
|
|
|
592
592
|
try {
|
|
593
593
|
return await this.process.attach(["claude", ...claudeArgs], {
|
|
594
594
|
cwd,
|
|
595
|
-
env
|
|
595
|
+
env,
|
|
596
|
+
onSpawned: options.onSpawned
|
|
596
597
|
});
|
|
597
598
|
} finally {
|
|
598
599
|
if (options.profileName) this.removePidFile(options.profileName);
|
|
@@ -917,6 +918,9 @@ const recordsEqual = (a, b) => {
|
|
|
917
918
|
* absent field means "do not manage connectors from here" and leaves
|
|
918
919
|
* everything in `~/.funnel` alone. Other channels in funnel.json (not
|
|
919
920
|
* passed to this call) are untouched.
|
|
921
|
+
*
|
|
922
|
+
* Returns the per-connector change set so callers (e.g. the claude launcher)
|
|
923
|
+
* can drive listener hot-reload on the gateway after settings are written.
|
|
920
924
|
*/
|
|
921
925
|
var FunnelLocalConfigSync = class {
|
|
922
926
|
channels;
|
|
@@ -943,14 +947,25 @@ var FunnelLocalConfigSync = class {
|
|
|
943
947
|
if (!arraysEqual(existing.options, nextOptions)) this.channels.setOptions(channel.name, nextOptions);
|
|
944
948
|
if (!recordsEqual(existing.env, nextEnv)) this.channels.setEnv(channel.name, nextEnv);
|
|
945
949
|
}
|
|
946
|
-
if (channel.connectors === void 0) return
|
|
950
|
+
if (channel.connectors === void 0) return {
|
|
951
|
+
touched: [],
|
|
952
|
+
removed: []
|
|
953
|
+
};
|
|
947
954
|
const dotenv = this.dotenv.read(cwd);
|
|
948
|
-
const touched =
|
|
955
|
+
const touched = [];
|
|
956
|
+
const touchedIds = /* @__PURE__ */ new Set();
|
|
949
957
|
for (const spec of channel.connectors) {
|
|
950
|
-
const
|
|
951
|
-
touched.
|
|
958
|
+
const outcome = await this.ensureConnector(channel.name, spec, dotenv);
|
|
959
|
+
touched.push({
|
|
960
|
+
name: outcome.name,
|
|
961
|
+
changed: outcome.changed
|
|
962
|
+
});
|
|
963
|
+
touchedIds.add(outcome.id);
|
|
952
964
|
}
|
|
953
|
-
|
|
965
|
+
return {
|
|
966
|
+
touched,
|
|
967
|
+
removed: this.removeExtras(channel.name, touchedIds)
|
|
968
|
+
};
|
|
954
969
|
}
|
|
955
970
|
async ensureConnector(channelName, spec, dotenv) {
|
|
956
971
|
if (spec.type === "slack") return await this.ensureSlack(channelName, spec, dotenv);
|
|
@@ -975,11 +990,22 @@ var FunnelLocalConfigSync = class {
|
|
|
975
990
|
existing: byName?.appToken
|
|
976
991
|
});
|
|
977
992
|
if (byName) {
|
|
978
|
-
if (byName.botToken !== botToken || byName.appToken !== appToken)
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
993
|
+
if (byName.botToken !== botToken || byName.appToken !== appToken) {
|
|
994
|
+
this.channels.updateSlackConnector(channelName, spec.name, {
|
|
995
|
+
botToken,
|
|
996
|
+
appToken
|
|
997
|
+
});
|
|
998
|
+
return {
|
|
999
|
+
id: byName.id,
|
|
1000
|
+
name: spec.name,
|
|
1001
|
+
changed: true
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
return {
|
|
1005
|
+
id: byName.id,
|
|
1006
|
+
name: spec.name,
|
|
1007
|
+
changed: false
|
|
1008
|
+
};
|
|
983
1009
|
}
|
|
984
1010
|
const byToken = this.findSlackByToken(channelName, [botToken, appToken]);
|
|
985
1011
|
if (byToken) {
|
|
@@ -988,14 +1014,22 @@ var FunnelLocalConfigSync = class {
|
|
|
988
1014
|
botToken,
|
|
989
1015
|
appToken
|
|
990
1016
|
});
|
|
991
|
-
return
|
|
1017
|
+
return {
|
|
1018
|
+
id: byToken.id,
|
|
1019
|
+
name: spec.name,
|
|
1020
|
+
changed: true
|
|
1021
|
+
};
|
|
992
1022
|
}
|
|
993
|
-
return
|
|
994
|
-
|
|
1023
|
+
return {
|
|
1024
|
+
id: this.channels.addConnector(channelName, {
|
|
1025
|
+
type: "slack",
|
|
1026
|
+
name: spec.name,
|
|
1027
|
+
botToken,
|
|
1028
|
+
appToken
|
|
1029
|
+
}).id,
|
|
995
1030
|
name: spec.name,
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
}).id;
|
|
1031
|
+
changed: true
|
|
1032
|
+
};
|
|
999
1033
|
}
|
|
1000
1034
|
async ensureDiscord(channelName, spec, dotenv) {
|
|
1001
1035
|
const byName = this.findExistingDiscord(channelName, spec.name);
|
|
@@ -1007,42 +1041,84 @@ var FunnelLocalConfigSync = class {
|
|
|
1007
1041
|
existing: byName?.botToken
|
|
1008
1042
|
});
|
|
1009
1043
|
if (byName) {
|
|
1010
|
-
if (byName.botToken !== botToken)
|
|
1011
|
-
|
|
1044
|
+
if (byName.botToken !== botToken) {
|
|
1045
|
+
this.channels.updateDiscordConnector(channelName, spec.name, { botToken });
|
|
1046
|
+
return {
|
|
1047
|
+
id: byName.id,
|
|
1048
|
+
name: spec.name,
|
|
1049
|
+
changed: true
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
return {
|
|
1053
|
+
id: byName.id,
|
|
1054
|
+
name: spec.name,
|
|
1055
|
+
changed: false
|
|
1056
|
+
};
|
|
1012
1057
|
}
|
|
1013
1058
|
const byToken = this.findDiscordByToken(channelName, botToken);
|
|
1014
1059
|
if (byToken) {
|
|
1015
1060
|
this.channels.renameConnector(channelName, byToken.name, spec.name);
|
|
1016
1061
|
if (byToken.botToken !== botToken) this.channels.updateDiscordConnector(channelName, spec.name, { botToken });
|
|
1017
|
-
return
|
|
1062
|
+
return {
|
|
1063
|
+
id: byToken.id,
|
|
1064
|
+
name: spec.name,
|
|
1065
|
+
changed: true
|
|
1066
|
+
};
|
|
1018
1067
|
}
|
|
1019
|
-
return
|
|
1020
|
-
|
|
1068
|
+
return {
|
|
1069
|
+
id: this.channels.addConnector(channelName, {
|
|
1070
|
+
type: "discord",
|
|
1071
|
+
name: spec.name,
|
|
1072
|
+
botToken
|
|
1073
|
+
}).id,
|
|
1021
1074
|
name: spec.name,
|
|
1022
|
-
|
|
1023
|
-
}
|
|
1075
|
+
changed: true
|
|
1076
|
+
};
|
|
1024
1077
|
}
|
|
1025
1078
|
ensureGh(channelName, spec) {
|
|
1026
1079
|
const existing = this.channels.getConnector(channelName, spec.name);
|
|
1027
1080
|
if (existing && existing.type !== "gh") throw new Error(`connector "${spec.name}" exists in channel "${channelName}" with type "${existing.type}", funnel.json declares "gh"`);
|
|
1028
1081
|
if (existing && existing.type === "gh") {
|
|
1029
|
-
if (spec.pollInterval !== void 0 && existing.pollInterval !== spec.pollInterval)
|
|
1030
|
-
|
|
1082
|
+
if (spec.pollInterval !== void 0 && existing.pollInterval !== spec.pollInterval) {
|
|
1083
|
+
this.channels.updateGhConnector(channelName, spec.name, { pollInterval: spec.pollInterval });
|
|
1084
|
+
return {
|
|
1085
|
+
id: existing.id,
|
|
1086
|
+
name: spec.name,
|
|
1087
|
+
changed: true
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
return {
|
|
1091
|
+
id: existing.id,
|
|
1092
|
+
name: spec.name,
|
|
1093
|
+
changed: false
|
|
1094
|
+
};
|
|
1031
1095
|
}
|
|
1032
|
-
return
|
|
1033
|
-
|
|
1096
|
+
return {
|
|
1097
|
+
id: this.channels.addConnector(channelName, {
|
|
1098
|
+
type: "gh",
|
|
1099
|
+
name: spec.name,
|
|
1100
|
+
...spec.pollInterval !== void 0 ? { pollInterval: spec.pollInterval } : {}
|
|
1101
|
+
}).id,
|
|
1034
1102
|
name: spec.name,
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1103
|
+
changed: true
|
|
1104
|
+
};
|
|
1037
1105
|
}
|
|
1038
1106
|
ensureSchedule(channelName, spec) {
|
|
1039
1107
|
const existing = this.channels.getConnector(channelName, spec.name);
|
|
1040
1108
|
if (existing && existing.type !== "schedule") throw new Error(`connector "${spec.name}" exists in channel "${channelName}" with type "${existing.type}", funnel.json declares "schedule"`);
|
|
1041
|
-
if (existing && existing.type === "schedule") return
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1109
|
+
if (existing && existing.type === "schedule") return {
|
|
1110
|
+
id: existing.id,
|
|
1111
|
+
name: spec.name,
|
|
1112
|
+
changed: false
|
|
1113
|
+
};
|
|
1114
|
+
return {
|
|
1115
|
+
id: this.channels.addConnector(channelName, {
|
|
1116
|
+
type: "schedule",
|
|
1117
|
+
name: spec.name
|
|
1118
|
+
}).id,
|
|
1119
|
+
name: spec.name,
|
|
1120
|
+
changed: true
|
|
1121
|
+
};
|
|
1046
1122
|
}
|
|
1047
1123
|
findExistingSlack(channelName, connectorName) {
|
|
1048
1124
|
const existing = this.channels.getConnector(channelName, connectorName);
|
|
@@ -1076,9 +1152,10 @@ var FunnelLocalConfigSync = class {
|
|
|
1076
1152
|
}
|
|
1077
1153
|
removeExtras(channelName, touched) {
|
|
1078
1154
|
const channel = this.channels.get(channelName);
|
|
1079
|
-
if (!channel) return;
|
|
1155
|
+
if (!channel) return [];
|
|
1080
1156
|
const stale = channel.connectors.filter((c) => !touched.has(c.id));
|
|
1081
1157
|
for (const connector of stale) this.channels.removeConnector(channelName, connector.name);
|
|
1158
|
+
return stale.map((c) => c.name);
|
|
1082
1159
|
}
|
|
1083
1160
|
async resolveField(input) {
|
|
1084
1161
|
if (input.literal !== void 0 && input.envVar !== void 0) throw new Error(`${input.label} is set both as a literal and as env.${input.label.split(".").pop()}; pick one`);
|
|
@@ -1254,6 +1331,7 @@ var MemoryFunnelProcessRunner = class extends FunnelProcessRunner {
|
|
|
1254
1331
|
command,
|
|
1255
1332
|
options
|
|
1256
1333
|
});
|
|
1334
|
+
if (options.onSpawned) options.onSpawned(1);
|
|
1257
1335
|
return (await this.handler(command)).exitCode ?? 0;
|
|
1258
1336
|
}
|
|
1259
1337
|
detach(command, options = {}) {
|
|
@@ -4163,7 +4241,10 @@ const claudeHandler = factory.createHandlers(zValidator$1("query", z.object({
|
|
|
4163
4241
|
if (local) {
|
|
4164
4242
|
const picked = query.channel !== void 0 ? local.channels.find((c) => c.name === query.channel) : local.channels[0];
|
|
4165
4243
|
if (!picked) throw new HTTPException(404, { message: query.channel ? `channel "${query.channel}" is not declared in funnel.json` : `funnel.json declares no channels` });
|
|
4166
|
-
await funnel.localConfigSync.ensure(picked, cwd);
|
|
4244
|
+
const synced = await funnel.localConfigSync.ensure(picked, cwd);
|
|
4245
|
+
for (const outcome of synced.touched) if (outcome.changed) await funnel.listeners.restart(picked.name, outcome.name);
|
|
4246
|
+
else await funnel.listeners.start(picked.name, outcome.name);
|
|
4247
|
+
for (const name of synced.removed) await funnel.listeners.stop(picked.name, name);
|
|
4167
4248
|
const exitCode = await funnel.claude.launch({
|
|
4168
4249
|
channel: picked.name,
|
|
4169
4250
|
cwd,
|
package/package.json
CHANGED