@interactive-inc/claude-funnel 0.3.0 → 0.4.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 +66 -16
- package/lib/funnel.ts +46 -2
- package/lib/index.ts +4 -0
- package/lib/modules/channels/channel-connector-ref-updater.ts +4 -0
- package/lib/modules/channels/funnel-channels.ts +49 -7
- package/lib/modules/connectors/connector-config-schema.ts +16 -0
- package/lib/modules/connectors/connector-existence-checker.ts +3 -0
- package/lib/modules/connectors/discord-connector-schema.ts +9 -0
- package/lib/modules/connectors/funnel-callable-connector-store.ts +9 -0
- package/lib/modules/connectors/funnel-connector-stores.ts +24 -0
- package/lib/modules/connectors/funnel-connector-type-store.ts +24 -0
- package/lib/modules/connectors/funnel-connectors.ts +98 -77
- package/lib/modules/connectors/funnel-discord-adapter.ts +1 -1
- package/lib/modules/connectors/funnel-discord-listener.ts +1 -1
- package/lib/modules/connectors/funnel-discord-store.ts +84 -0
- package/lib/modules/connectors/funnel-gh-listener.ts +1 -1
- package/lib/modules/connectors/funnel-gh-store.ts +84 -0
- package/lib/modules/connectors/funnel-json-connector-store.ts +100 -0
- package/lib/modules/connectors/funnel-schedule-listener.ts +124 -0
- package/lib/modules/connectors/funnel-schedule-store.ts +178 -0
- package/lib/modules/connectors/funnel-slack-adapter.ts +1 -1
- package/lib/modules/connectors/funnel-slack-listener.ts +1 -1
- package/lib/modules/connectors/funnel-slack-store.ts +86 -0
- package/lib/modules/connectors/gh-connector-schema.ts +9 -0
- package/lib/modules/connectors/match-cron.ts +72 -0
- package/lib/modules/connectors/migrate-legacy-connectors.ts +77 -0
- package/lib/modules/connectors/schedule-connector-schema.ts +18 -0
- package/lib/modules/connectors/schedule-last-fired-store.ts +48 -0
- package/lib/modules/connectors/slack-connector-schema.ts +10 -0
- package/lib/modules/gateway/daemon.ts +30 -13
- package/lib/modules/profiles/funnel-profiles.ts +18 -0
- package/lib/modules/profiles/profile-channel-checker.ts +3 -0
- package/lib/modules/profiles/profile-channel-ref-updater.ts +3 -0
- package/lib/modules/schedule/funnel-schedule.ts +34 -0
- package/lib/modules/settings/funnel-settings-store.ts +0 -1
- package/lib/modules/settings/mock-funnel-settings-reader.ts +0 -1
- package/lib/modules/settings/settings-schema.ts +0 -34
- package/lib/routes/connectors/add.help.ts +10 -4
- package/lib/routes/connectors/add.ts +10 -1
- package/lib/routes/connectors/routes.ts +6 -0
- package/lib/routes/connectors/schedules-add.help.ts +11 -0
- package/lib/routes/connectors/schedules-add.ts +33 -0
- package/lib/routes/connectors/schedules-group.help.ts +1 -0
- package/lib/routes/connectors/schedules-group.ts +38 -0
- package/lib/routes/connectors/schedules-remove.help.ts +3 -0
- package/lib/routes/connectors/schedules-remove.ts +17 -0
- package/lib/routes/connectors/set.ts +47 -5
- package/lib/routes/connectors/show.ts +9 -0
- package/lib/routes/request/discord.ts +1 -1
- package/lib/routes/request/slack.ts +1 -1
- package/package.json +1 -1
- package/lib/modules/connectors/resolve-listener.ts +0 -13
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
import { HTTPException } from "hono/http-exception"
|
|
1
2
|
import { z } from "zod"
|
|
2
3
|
import { factory } from "@/factory"
|
|
3
4
|
import { zValidator } from "@/modules/router/validator"
|
|
4
5
|
import { help } from "@/routes/connectors/set.help"
|
|
5
6
|
|
|
7
|
+
const SLACK_FIELDS = ["bot-token", "app-token"] as const
|
|
8
|
+
const GH_FIELDS = ["poll-interval"] as const
|
|
9
|
+
const DISCORD_FIELDS = ["bot-token"] as const
|
|
10
|
+
|
|
11
|
+
const rejectExtraneous = (
|
|
12
|
+
query: Record<string, string | undefined>,
|
|
13
|
+
allowed: ReadonlyArray<string>,
|
|
14
|
+
type: string,
|
|
15
|
+
): void => {
|
|
16
|
+
for (const key of ["bot-token", "app-token", "poll-interval"]) {
|
|
17
|
+
if (query[key] === undefined) continue
|
|
18
|
+
if (allowed.includes(key)) continue
|
|
19
|
+
|
|
20
|
+
throw new HTTPException(400, {
|
|
21
|
+
message: `connector type "${type}" does not accept --${key}`,
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
export const connectorsSetHandler = factory.createHandlers(
|
|
7
27
|
zValidator("param", z.object({ name: z.string() })),
|
|
8
28
|
zValidator(
|
|
@@ -19,11 +39,33 @@ export const connectorsSetHandler = factory.createHandlers(
|
|
|
19
39
|
const query = c.req.valid("query")
|
|
20
40
|
const funnel = c.var.funnel
|
|
21
41
|
|
|
22
|
-
funnel.connectors.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
42
|
+
const current = funnel.connectors.get(param.name)
|
|
43
|
+
|
|
44
|
+
if (!current) {
|
|
45
|
+
throw new HTTPException(404, { message: `connector "${param.name}" not found` })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (current.type === "slack") {
|
|
49
|
+
rejectExtraneous(query, SLACK_FIELDS, "slack")
|
|
50
|
+
funnel.connectors.updateSlack(param.name, {
|
|
51
|
+
botToken: query["bot-token"],
|
|
52
|
+
appToken: query["app-token"],
|
|
53
|
+
})
|
|
54
|
+
} else if (current.type === "gh") {
|
|
55
|
+
rejectExtraneous(query, GH_FIELDS, "gh")
|
|
56
|
+
funnel.connectors.updateGh(param.name, {
|
|
57
|
+
pollInterval: query["poll-interval"] ? Number(query["poll-interval"]) : undefined,
|
|
58
|
+
})
|
|
59
|
+
} else if (current.type === "discord") {
|
|
60
|
+
rejectExtraneous(query, DISCORD_FIELDS, "discord")
|
|
61
|
+
funnel.connectors.updateDiscord(param.name, {
|
|
62
|
+
botToken: query["bot-token"],
|
|
63
|
+
})
|
|
64
|
+
} else {
|
|
65
|
+
throw new HTTPException(400, {
|
|
66
|
+
message: `schedule connectors have no top-level fields — use schedules add/remove`,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
27
69
|
|
|
28
70
|
return c.text(`updated connector "${param.name}"`)
|
|
29
71
|
},
|
|
@@ -25,6 +25,15 @@ export const connectorsShowHandler = factory.createHandlers(
|
|
|
25
25
|
lines.push(`pollInterval: ${connector.pollInterval ?? 60}s`)
|
|
26
26
|
} else if (connector.type === "discord") {
|
|
27
27
|
lines.push(`botToken: ${connector.botToken.slice(0, 8)}...`)
|
|
28
|
+
} else if (connector.type === "schedule") {
|
|
29
|
+
lines.push(`entries: ${connector.entries.length}`)
|
|
30
|
+
|
|
31
|
+
for (const entry of connector.entries) {
|
|
32
|
+
const status = entry.enabled ? "" : " (disabled)"
|
|
33
|
+
lines.push(` - ${entry.id}${status}`)
|
|
34
|
+
lines.push(` cron: ${entry.cron}`)
|
|
35
|
+
lines.push(` prompt: ${entry.prompt}`)
|
|
36
|
+
}
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
return c.text(lines.join("\n"))
|
|
@@ -54,7 +54,7 @@ export const requestDiscordHandler = factory.createHandlers(
|
|
|
54
54
|
})
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const result = await funnel.connectors.
|
|
57
|
+
const result = await funnel.connectors.callDiscord(query.connector, {
|
|
58
58
|
method: method.toUpperCase(),
|
|
59
59
|
path: query.path,
|
|
60
60
|
body: parseBody(query.body),
|
|
@@ -50,7 +50,7 @@ export const requestSlackHandler = factory.createHandlers(
|
|
|
50
50
|
})
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const result = await funnel.connectors.
|
|
53
|
+
const result = await funnel.connectors.callSlack(query.connector, {
|
|
54
54
|
method: "POST",
|
|
55
55
|
path: query.path,
|
|
56
56
|
body: parseBody(query.body),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interactive-inc/claude-funnel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Hub CLI that routes external events (Slack / GitHub / Discord) to Claude Code agents through subscription channels over MCP.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Interactive Inc.",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { FunnelConnectorListener } from "@/modules/connectors/funnel-connector-listener"
|
|
2
|
-
import { FunnelDiscordListener } from "@/modules/connectors/funnel-discord-listener"
|
|
3
|
-
import { FunnelGhListener } from "@/modules/connectors/funnel-gh-listener"
|
|
4
|
-
import { FunnelSlackListener } from "@/modules/connectors/funnel-slack-listener"
|
|
5
|
-
import type { ConnectorConfig } from "@/modules/settings/settings-schema"
|
|
6
|
-
|
|
7
|
-
export const resolveListener = (config: ConnectorConfig): FunnelConnectorListener => {
|
|
8
|
-
if (config.type === "slack") return new FunnelSlackListener({ config })
|
|
9
|
-
if (config.type === "gh") return new FunnelGhListener({ config })
|
|
10
|
-
if (config.type === "discord") return new FunnelDiscordListener({ config })
|
|
11
|
-
|
|
12
|
-
throw new Error(`unsupported connector type: ${(config as { type: string }).type}`)
|
|
13
|
-
}
|