@interactive-inc/claude-funnel 0.3.0 → 0.4.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.
Files changed (52) hide show
  1. package/README.md +71 -21
  2. package/lib/funnel.ts +46 -2
  3. package/lib/index.ts +4 -0
  4. package/lib/modules/channels/channel-connector-ref-updater.ts +4 -0
  5. package/lib/modules/channels/funnel-channels.ts +49 -7
  6. package/lib/modules/connectors/connector-config-schema.ts +16 -0
  7. package/lib/modules/connectors/connector-existence-checker.ts +3 -0
  8. package/lib/modules/connectors/discord-connector-schema.ts +9 -0
  9. package/lib/modules/connectors/funnel-callable-connector-store.ts +9 -0
  10. package/lib/modules/connectors/funnel-connector-stores.ts +24 -0
  11. package/lib/modules/connectors/funnel-connector-type-store.ts +24 -0
  12. package/lib/modules/connectors/funnel-connectors.ts +98 -77
  13. package/lib/modules/connectors/funnel-discord-adapter.ts +1 -1
  14. package/lib/modules/connectors/funnel-discord-listener.ts +1 -1
  15. package/lib/modules/connectors/funnel-discord-store.ts +84 -0
  16. package/lib/modules/connectors/funnel-gh-listener.ts +1 -1
  17. package/lib/modules/connectors/funnel-gh-store.ts +84 -0
  18. package/lib/modules/connectors/funnel-json-connector-store.ts +100 -0
  19. package/lib/modules/connectors/funnel-schedule-listener.ts +124 -0
  20. package/lib/modules/connectors/funnel-schedule-store.ts +178 -0
  21. package/lib/modules/connectors/funnel-slack-adapter.ts +1 -1
  22. package/lib/modules/connectors/funnel-slack-listener.ts +1 -1
  23. package/lib/modules/connectors/funnel-slack-store.ts +86 -0
  24. package/lib/modules/connectors/gh-connector-schema.ts +9 -0
  25. package/lib/modules/connectors/match-cron.ts +72 -0
  26. package/lib/modules/connectors/migrate-legacy-connectors.ts +77 -0
  27. package/lib/modules/connectors/schedule-connector-schema.ts +18 -0
  28. package/lib/modules/connectors/schedule-last-fired-store.ts +48 -0
  29. package/lib/modules/connectors/slack-connector-schema.ts +10 -0
  30. package/lib/modules/gateway/daemon.ts +30 -13
  31. package/lib/modules/profiles/funnel-profiles.ts +18 -0
  32. package/lib/modules/profiles/profile-channel-checker.ts +3 -0
  33. package/lib/modules/profiles/profile-channel-ref-updater.ts +3 -0
  34. package/lib/modules/schedule/funnel-schedule.ts +34 -0
  35. package/lib/modules/settings/funnel-settings-store.ts +0 -1
  36. package/lib/modules/settings/mock-funnel-settings-reader.ts +0 -1
  37. package/lib/modules/settings/settings-schema.ts +0 -34
  38. package/lib/routes/connectors/add.help.ts +10 -4
  39. package/lib/routes/connectors/add.ts +10 -1
  40. package/lib/routes/connectors/routes.ts +6 -0
  41. package/lib/routes/connectors/schedules-add.help.ts +11 -0
  42. package/lib/routes/connectors/schedules-add.ts +33 -0
  43. package/lib/routes/connectors/schedules-group.help.ts +1 -0
  44. package/lib/routes/connectors/schedules-group.ts +38 -0
  45. package/lib/routes/connectors/schedules-remove.help.ts +3 -0
  46. package/lib/routes/connectors/schedules-remove.ts +17 -0
  47. package/lib/routes/connectors/set.ts +47 -5
  48. package/lib/routes/connectors/show.ts +9 -0
  49. package/lib/routes/request/discord.ts +1 -1
  50. package/lib/routes/request/slack.ts +1 -1
  51. package/package.json +4 -4
  52. package/lib/modules/connectors/resolve-listener.ts +0 -13
@@ -0,0 +1,38 @@
1
+ import { HTTPException } from "hono/http-exception"
2
+ import { z } from "zod"
3
+ import { factory } from "@/factory"
4
+ import { zValidator } from "@/modules/router/validator"
5
+ import { help } from "@/routes/connectors/schedules-group.help"
6
+
7
+ export const connectorsSchedulesGroupHandler = factory.createHandlers(
8
+ zValidator("param", z.object({ name: z.string() })),
9
+ zValidator("query", z.object({}), help),
10
+ (c) => {
11
+ const param = c.req.valid("param")
12
+ const funnel = c.var.funnel
13
+ const connector = funnel.connectors.get(param.name)
14
+
15
+ if (!connector) {
16
+ throw new HTTPException(404, { message: `connector "${param.name}" not found` })
17
+ }
18
+
19
+ if (connector.type !== "schedule") {
20
+ throw new HTTPException(400, {
21
+ message: `connector "${param.name}" is type "${connector.type}", not "schedule"`,
22
+ })
23
+ }
24
+
25
+ const entries = funnel.schedule.listEntries(param.name)
26
+
27
+ if (entries.length === 0) return c.text("no schedule entries")
28
+
29
+ const lines: string[] = []
30
+
31
+ for (const entry of entries) {
32
+ const status = entry.enabled ? "" : " (disabled)"
33
+ lines.push(`${entry.id}${status} ${entry.cron} ${entry.prompt}`)
34
+ }
35
+
36
+ return c.text(lines.join("\n"))
37
+ },
38
+ )
@@ -0,0 +1,3 @@
1
+ export const help = `funnel connectors <name> schedules remove — remove a schedule entry
2
+
3
+ usage: funnel connectors <name> schedules remove <id>`
@@ -0,0 +1,17 @@
1
+ import { z } from "zod"
2
+ import { factory } from "@/factory"
3
+ import { zValidator } from "@/modules/router/validator"
4
+ import { help } from "@/routes/connectors/schedules-remove.help"
5
+
6
+ export const connectorsSchedulesRemoveHandler = factory.createHandlers(
7
+ zValidator("param", z.object({ name: z.string(), id: z.string() })),
8
+ zValidator("query", z.object({}), help),
9
+ (c) => {
10
+ const param = c.req.valid("param")
11
+ const funnel = c.var.funnel
12
+
13
+ funnel.schedule.removeEntry(param.name, param.id)
14
+
15
+ return c.text(`removed schedule entry "${param.id}" from connector "${param.name}"`)
16
+ },
17
+ )
@@ -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.update(param.name, {
23
- botToken: query["bot-token"],
24
- appToken: query["app-token"],
25
- pollInterval: query["poll-interval"] ? Number(query["poll-interval"]) : undefined,
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.call(query.connector, {
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.call(query.connector, {
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,16 +1,16 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
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.",
7
- "homepage": "https://github.com/interactive-inc/claude-funnel#readme",
7
+ "homepage": "https://github.com/interactive-inc/open-claude-funnel#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/interactive-inc/claude-funnel.git"
10
+ "url": "git+https://github.com/interactive-inc/open-claude-funnel.git"
11
11
  },
12
12
  "bugs": {
13
- "url": "https://github.com/interactive-inc/claude-funnel/issues"
13
+ "url": "https://github.com/interactive-inc/open-claude-funnel/issues"
14
14
  },
15
15
  "keywords": [
16
16
  "claude",
@@ -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
- }