@interactive-inc/claude-funnel 0.7.1 → 0.8.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.
- package/README.md +155 -133
- package/dist/bin.js +1344 -0
- package/dist/cli/factory.d.ts +7 -0
- package/dist/cli/router/query-to-cli-args.d.ts +1 -0
- package/dist/cli/router/to-request.d.ts +5 -0
- package/dist/cli/router/validator.d.ts +5 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +46 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +54 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +66 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +46 -0
- package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +90 -0
- package/dist/cli/routes/channels.$channel.connectors.d.ts +38 -0
- package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +62 -0
- package/dist/cli/routes/channels.$channel.d.ts +38 -0
- package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +42 -0
- package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +28 -0
- package/dist/cli/routes/channels.add.$channel.d.ts +46 -0
- package/dist/cli/routes/channels.d.ts +16 -0
- package/dist/cli/routes/channels.remove.$channel.d.ts +38 -0
- package/dist/cli/routes/claude.d.ts +32 -0
- package/dist/cli/routes/gateway.d.ts +20 -0
- package/dist/cli/routes/gateway.listeners.d.ts +17 -0
- package/dist/cli/routes/gateway.logs.d.ts +24 -0
- package/dist/cli/routes/gateway.restart.d.ts +24 -0
- package/dist/cli/routes/gateway.run.d.ts +24 -0
- package/dist/cli/routes/gateway.start.d.ts +24 -0
- package/dist/cli/routes/gateway.status.d.ts +13 -0
- package/dist/cli/routes/gateway.stop.d.ts +16 -0
- package/dist/cli/routes/index.d.ts +1222 -0
- package/dist/cli/routes/profiles.$profile.as-default.d.ts +38 -0
- package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +42 -0
- package/dist/cli/routes/profiles.$profile.run.d.ts +46 -0
- package/dist/cli/routes/profiles.add.$profile.d.ts +54 -0
- package/dist/cli/routes/profiles.d.ts +16 -0
- package/dist/cli/routes/profiles.remove.$profile.d.ts +38 -0
- package/dist/cli/routes/profiles.set.$profile.d.ts +54 -0
- package/dist/cli/routes/status.d.ts +16 -0
- package/dist/cli/routes/update.d.ts +16 -0
- package/dist/connectors/connector-adapter.d.ts +8 -0
- package/dist/connectors/connector-config-schema.d.ts +43 -0
- package/dist/connectors/connector-factory.d.ts +32 -0
- package/dist/connectors/connector-listener.d.ts +17 -0
- package/dist/connectors/discord-adapter.d.ts +14 -0
- package/dist/connectors/discord-connector-schema.d.ts +10 -0
- package/dist/connectors/discord-event-processor.d.ts +26 -0
- package/dist/connectors/discord-listener.d.ts +17 -0
- package/dist/connectors/gh-adapter.d.ts +11 -0
- package/dist/connectors/gh-connector-schema.d.ts +10 -0
- package/dist/connectors/gh-listener.d.ts +26 -0
- package/dist/connectors/match-cron.d.ts +1 -0
- package/dist/connectors/schedule-connector-schema.d.ts +45 -0
- package/dist/connectors/schedule-listener.d.ts +30 -0
- package/dist/connectors/schedule-state-store.d.ts +19 -0
- package/dist/connectors/slack-adapter.d.ts +15 -0
- package/dist/connectors/slack-connector-schema.d.ts +11 -0
- package/dist/connectors/slack-event-processor.d.ts +27 -0
- package/dist/connectors/slack-listener.d.ts +17 -0
- package/dist/engine/channels/channels.d.ts +106 -0
- package/dist/engine/claude/claude.d.ts +49 -0
- package/dist/engine/claude/gateway-controller.d.ts +6 -0
- package/dist/engine/fs/file-system.d.ts +24 -0
- package/dist/engine/fs/memory-file-system.d.ts +31 -0
- package/dist/engine/fs/node-file-system.d.ts +15 -0
- package/dist/engine/http/http-client.d.ts +15 -0
- package/dist/engine/http/memory-http-client.d.ts +12 -0
- package/dist/engine/http/node-http-client.d.ts +5 -0
- package/dist/engine/id/id-generator.d.ts +7 -0
- package/dist/engine/id/memory-id-generator.d.ts +11 -0
- package/dist/engine/id/node-id-generator.d.ts +4 -0
- package/dist/engine/logger/logger.d.ts +11 -0
- package/dist/engine/logger/memory-logger.d.ts +14 -0
- package/dist/engine/logger/node-logger.d.ts +15 -0
- package/dist/engine/logger/noop-logger.d.ts +7 -0
- package/dist/engine/mcp/channel-server.d.ts +1 -0
- package/dist/engine/mcp/mcp.d.ts +22 -0
- package/dist/engine/process/memory-process-runner.d.ts +43 -0
- package/dist/engine/process/node-process-runner.d.ts +9 -0
- package/dist/engine/process/process-runner.d.ts +29 -0
- package/dist/engine/profiles/profile-channel-checker.d.ts +7 -0
- package/dist/engine/profiles/profiles.d.ts +31 -0
- package/dist/engine/settings/mock-settings-reader.d.ts +9 -0
- package/dist/engine/settings/settings-reader.d.ts +5 -0
- package/dist/engine/settings/settings-schema.d.ts +132 -0
- package/dist/engine/settings/settings-store.d.ts +18 -0
- package/dist/engine/time/clock.d.ts +9 -0
- package/dist/engine/time/memory-clock.d.ts +12 -0
- package/dist/engine/time/node-clock.d.ts +4 -0
- package/dist/funnel.d.ts +95 -0
- package/dist/gateway/auth-middleware.d.ts +14 -0
- package/dist/gateway/broadcaster.d.ts +122 -0
- package/dist/gateway/daemon.d.ts +2 -0
- package/dist/gateway/daemon.js +485 -0
- package/dist/gateway/factory.d.ts +7 -0
- package/dist/gateway/funnel-event-store.d.ts +81 -0
- package/dist/gateway/gateway-server.d.ts +94 -0
- package/dist/gateway/gateway-token.d.ts +33 -0
- package/dist/gateway/gateway.d.ts +58 -0
- package/dist/gateway/kill-competing-slack-gateways.d.ts +9 -0
- package/dist/gateway/listener-supervisor.d.ts +85 -0
- package/dist/gateway/listeners-client.d.ts +53 -0
- package/dist/gateway/resolve-daemon-script.d.ts +11 -0
- package/dist/gateway/routes/channels.connectors.call.d.ts +41 -0
- package/dist/gateway/routes/health.d.ts +17 -0
- package/dist/gateway/routes/index.d.ts +209 -0
- package/dist/gateway/routes/listeners.list.d.ts +14 -0
- package/dist/gateway/routes/listeners.restart.d.ts +34 -0
- package/dist/gateway/routes/listeners.start.d.ts +34 -0
- package/dist/gateway/routes/listeners.stop.d.ts +34 -0
- package/dist/gateway/routes/route-deps.d.ts +10 -0
- package/dist/gateway/routes/status.d.ts +30 -0
- package/dist/gateway/routes/validator.d.ts +19 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +3575 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/logger/leuco-human-file-writer.d.ts +33 -0
- package/dist/logger/leuco-human-logger.d.ts +46 -0
- package/dist/logger/leuco-human-record.d.ts +15 -0
- package/dist/logger/leuco-human-stdout-writer.d.ts +20 -0
- package/dist/logger/leuco-human-writer.d.ts +13 -0
- package/dist/logger/leuco-logger-memory-sink.d.ts +33 -0
- package/dist/logger/leuco-logger-record.d.ts +13 -0
- package/dist/logger/leuco-logger-sink.d.ts +34 -0
- package/dist/logger/leuco-logger-sqlite-sink.d.ts +102 -0
- package/dist/logger/leuco-logger.d.ts +56 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/lib/bin.ts +78 -0
- package/lib/{modules → cli}/router/to-request.ts +13 -20
- package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +27 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +40 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +41 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +22 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +23 -0
- package/lib/cli/routes/channels.$channel.connectors.$connector.ts +26 -0
- package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +92 -0
- package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +22 -0
- package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +63 -0
- package/lib/cli/routes/channels.$channel.connectors.ts +26 -0
- package/lib/cli/routes/channels.$channel.rename.$newName.ts +22 -0
- package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +34 -0
- package/lib/cli/routes/channels.$channel.ts +34 -0
- package/lib/cli/routes/channels.add.$channel.ts +33 -0
- package/lib/cli/routes/channels.remove.$channel.ts +20 -0
- package/lib/cli/routes/channels.ts +39 -0
- package/lib/cli/routes/claude.ts +69 -0
- package/lib/cli/routes/gateway.listeners.ts +41 -0
- package/lib/cli/routes/gateway.logs.ts +123 -0
- package/lib/{routes/gateway/restart.ts → cli/routes/gateway.restart.ts} +20 -5
- package/lib/cli/routes/gateway.run.ts +41 -0
- package/lib/cli/routes/gateway.start.ts +50 -0
- package/lib/cli/routes/gateway.status.ts +19 -0
- package/lib/cli/routes/gateway.stop.ts +32 -0
- package/lib/cli/routes/gateway.ts +55 -0
- package/lib/cli/routes/index.ts +202 -0
- package/lib/cli/routes/profiles.$profile.as-default.ts +22 -0
- package/lib/cli/routes/profiles.$profile.rename.$newName.ts +22 -0
- package/lib/cli/routes/profiles.$profile.run.ts +36 -0
- package/lib/cli/routes/profiles.add.$profile.ts +46 -0
- package/lib/cli/routes/profiles.remove.$profile.ts +20 -0
- package/lib/cli/routes/profiles.set.$profile.ts +46 -0
- package/lib/cli/routes/profiles.ts +40 -0
- package/lib/cli/routes/status.ts +93 -0
- package/lib/cli/routes/update.ts +27 -0
- package/lib/connectors/connector-config-schema.ts +16 -0
- package/lib/connectors/connector-factory.ts +94 -0
- package/lib/connectors/connector-listener.ts +20 -0
- package/lib/{modules/connectors/funnel-discord-adapter.ts → connectors/discord-adapter.ts} +6 -11
- package/lib/{modules/connectors → connectors}/discord-connector-schema.ts +4 -1
- package/lib/connectors/discord-listener.ts +111 -0
- package/lib/{modules/connectors/funnel-gh-adapter.ts → connectors/gh-adapter.ts} +3 -6
- package/lib/{modules/connectors → connectors}/gh-connector-schema.ts +4 -1
- package/lib/{modules/connectors/funnel-gh-listener.ts → connectors/gh-listener.ts} +45 -19
- package/lib/{modules/connectors → connectors}/match-cron.ts +10 -4
- package/lib/connectors/schedule-connector-schema.ts +33 -0
- package/lib/connectors/schedule-listener.ts +207 -0
- package/lib/connectors/schedule-state-store.ts +54 -0
- package/lib/connectors/slack-adapter.ts +36 -0
- package/lib/{modules/connectors → connectors}/slack-connector-schema.ts +4 -1
- package/lib/{modules/connectors/funnel-slack-event-processor.ts → connectors/slack-event-processor.ts} +15 -9
- package/lib/{modules/connectors/funnel-slack-listener.ts → connectors/slack-listener.ts} +33 -14
- package/lib/engine/channels/channels.ts +520 -0
- package/lib/{modules/claude/funnel-claude.ts → engine/claude/claude.ts} +28 -55
- package/lib/engine/claude/gateway-controller.ts +4 -0
- package/lib/{modules/fs/funnel-file-system.ts → engine/fs/file-system.ts} +4 -0
- package/lib/{modules/fs/memory-funnel-file-system.ts → engine/fs/memory-file-system.ts} +20 -3
- package/lib/{modules/fs/node-funnel-file-system.ts → engine/fs/node-file-system.ts} +14 -2
- package/lib/{modules/http/memory-funnel-http-client.ts → engine/http/memory-http-client.ts} +1 -5
- package/lib/{modules/http/node-funnel-http-client.ts → engine/http/node-http-client.ts} +1 -5
- package/lib/{modules/id/memory-funnel-id-generator.ts → engine/id/memory-id-generator.ts} +1 -1
- package/lib/{modules/id/node-funnel-id-generator.ts → engine/id/node-id-generator.ts} +1 -1
- package/lib/{modules/logger/memory-funnel-logger.ts → engine/logger/memory-logger.ts} +1 -1
- package/lib/{modules/logger/node-funnel-logger.ts → engine/logger/node-logger.ts} +1 -1
- package/lib/{modules/logger/noop-funnel-logger.ts → engine/logger/noop-logger.ts} +1 -1
- package/lib/engine/mcp/channel-server.ts +204 -0
- package/lib/{modules/mcp/funnel-mcp.ts → engine/mcp/mcp.ts} +24 -10
- package/lib/{modules/process/memory-funnel-process-runner.ts → engine/process/memory-process-runner.ts} +1 -1
- package/lib/{modules/process/node-funnel-process-runner.ts → engine/process/node-process-runner.ts} +12 -21
- package/lib/engine/profiles/profile-channel-checker.ts +7 -0
- package/lib/{modules/profiles/funnel-profiles.ts → engine/profiles/profiles.ts} +41 -43
- package/lib/{modules/settings/mock-funnel-settings-reader.ts → engine/settings/mock-settings-reader.ts} +4 -3
- package/lib/{modules/settings/funnel-settings-reader.ts → engine/settings/settings-reader.ts} +1 -1
- package/lib/engine/settings/settings-schema.ts +46 -0
- package/lib/engine/settings/settings-store.ts +110 -0
- package/lib/{modules/time/memory-funnel-clock.ts → engine/time/memory-clock.ts} +1 -1
- package/lib/{modules/time/node-funnel-clock.ts → engine/time/node-clock.ts} +1 -1
- package/lib/funnel.ts +83 -78
- package/lib/gateway/auth-middleware.ts +44 -0
- package/lib/gateway/broadcaster.ts +319 -0
- package/lib/gateway/daemon.ts +47 -0
- package/lib/gateway/factory.ts +10 -0
- package/lib/gateway/funnel-event-store.ts +155 -0
- package/lib/gateway/gateway-server.ts +414 -0
- package/lib/gateway/gateway-token.ts +79 -0
- package/lib/{modules/gateway/funnel-gateway.ts → gateway/gateway.ts} +27 -13
- package/lib/{modules/gateway → gateway}/kill-competing-slack-gateways.ts +4 -4
- package/lib/gateway/listener-supervisor.ts +339 -0
- package/lib/gateway/listeners-client.ts +128 -0
- package/lib/gateway/resolve-daemon-script.ts +26 -0
- package/lib/gateway/routes/channels.connectors.call.ts +39 -0
- package/lib/gateway/routes/health.ts +13 -0
- package/lib/gateway/routes/index.ts +24 -0
- package/lib/gateway/routes/listeners.list.ts +6 -0
- package/lib/gateway/routes/listeners.restart.ts +15 -0
- package/lib/gateway/routes/listeners.start.ts +15 -0
- package/lib/gateway/routes/listeners.stop.ts +15 -0
- package/lib/gateway/routes/route-deps.ts +11 -0
- package/lib/gateway/routes/status.ts +15 -0
- package/lib/gateway/routes/validator.ts +17 -0
- package/lib/index.ts +52 -92
- package/lib/logger/leuco-human-file-writer.ts +65 -0
- package/lib/logger/leuco-human-logger.ts +98 -0
- package/lib/logger/leuco-human-record.ts +16 -0
- package/lib/logger/leuco-human-stdout-writer.ts +26 -0
- package/lib/logger/leuco-human-writer.ts +14 -0
- package/lib/logger/leuco-logger-memory-sink.ts +67 -0
- package/lib/logger/leuco-logger-record.ts +13 -0
- package/lib/logger/leuco-logger-sink.ts +33 -0
- package/lib/logger/leuco-logger-sqlite-sink.ts +355 -0
- package/lib/logger/leuco-logger.ts +135 -0
- package/lib/tui/app.tsx +357 -0
- package/lib/tui/components/add-row.tsx +18 -0
- package/lib/tui/components/brand.tsx +27 -0
- package/lib/tui/components/card.tsx +44 -0
- package/lib/tui/components/detail-bar.tsx +46 -0
- package/lib/tui/components/editable-field.tsx +33 -0
- package/lib/tui/components/empty-state.tsx +11 -0
- package/lib/tui/components/gateway-status.tsx +66 -0
- package/lib/tui/components/keymap.tsx +29 -0
- package/lib/tui/components/menu-item.tsx +73 -0
- package/lib/tui/components/menu.tsx +26 -0
- package/lib/tui/components/panel-header.tsx +22 -0
- package/lib/tui/components/readonly-field.tsx +18 -0
- package/lib/tui/components/section-header.tsx +25 -0
- package/lib/tui/components/selection-accent.tsx +32 -0
- package/lib/tui/components/session-item.tsx +33 -0
- package/lib/tui/components/session-list.tsx +33 -0
- package/lib/tui/components/ui/hascii/accordion-item.tsx +88 -0
- package/lib/tui/components/ui/hascii/accordion.tsx +96 -0
- package/lib/tui/components/ui/hascii/alert-dialog.tsx +43 -0
- package/lib/tui/components/ui/hascii/badge.tsx +51 -0
- package/lib/tui/components/ui/hascii/breadcrumb.tsx +58 -0
- package/lib/tui/components/ui/hascii/button.tsx +194 -0
- package/lib/tui/components/ui/hascii/card-content.tsx +14 -0
- package/lib/tui/components/ui/hascii/card-description.tsx +13 -0
- package/lib/tui/components/ui/hascii/card-footer.tsx +14 -0
- package/lib/tui/components/ui/hascii/card-header.tsx +14 -0
- package/lib/tui/components/ui/hascii/card-title.tsx +13 -0
- package/lib/tui/components/ui/hascii/card.tsx +27 -0
- package/lib/tui/components/ui/hascii/checkbox.tsx +65 -0
- package/lib/tui/components/ui/hascii/command.tsx +159 -0
- package/lib/tui/components/ui/hascii/dialog-content.tsx +14 -0
- package/lib/tui/components/ui/hascii/dialog-description.tsx +13 -0
- package/lib/tui/components/ui/hascii/dialog-footer.tsx +14 -0
- package/lib/tui/components/ui/hascii/dialog-header.tsx +14 -0
- package/lib/tui/components/ui/hascii/dialog-title.tsx +13 -0
- package/lib/tui/components/ui/hascii/dialog.tsx +27 -0
- package/lib/tui/components/ui/hascii/file-tree.tsx +142 -0
- package/lib/tui/components/ui/hascii/focus-group.tsx +62 -0
- package/lib/tui/components/ui/hascii/form-item.tsx +43 -0
- package/lib/tui/components/ui/hascii/input-otp.tsx +86 -0
- package/lib/tui/components/ui/hascii/input.tsx +130 -0
- package/lib/tui/components/ui/hascii/pagination.tsx +105 -0
- package/lib/tui/components/ui/hascii/progress.tsx +28 -0
- package/lib/tui/components/ui/hascii/select.tsx +131 -0
- package/lib/tui/components/ui/hascii/separator.tsx +35 -0
- package/lib/tui/components/ui/hascii/sidebar-content.tsx +23 -0
- package/lib/tui/components/ui/hascii/sidebar-header.tsx +14 -0
- package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +67 -0
- package/lib/tui/components/ui/hascii/sidebar.tsx +24 -0
- package/lib/tui/components/ui/hascii/skeleton.tsx +60 -0
- package/lib/tui/components/ui/hascii/slider.tsx +91 -0
- package/lib/tui/components/ui/hascii/snackbar.tsx +75 -0
- package/lib/tui/components/ui/hascii/sparkline.tsx +53 -0
- package/lib/tui/components/ui/hascii/spinner.tsx +47 -0
- package/lib/tui/components/ui/hascii/stepper.tsx +54 -0
- package/lib/tui/components/ui/hascii/switch.tsx +66 -0
- package/lib/tui/components/ui/hascii/table.tsx +95 -0
- package/lib/tui/components/ui/hascii/tabs.tsx +59 -0
- package/lib/tui/components/ui/hascii/toggle-group-item.tsx +45 -0
- package/lib/tui/components/ui/hascii/toggle-group.tsx +99 -0
- package/lib/tui/components/ui/hascii/tree.tsx +104 -0
- package/lib/tui/components/view-shell.tsx +44 -0
- package/lib/tui/filter-input.tsx +33 -0
- package/lib/tui/hooks/hascii/use-pressable.ts +54 -0
- package/lib/tui/parse-comma-list.ts +14 -0
- package/lib/tui/profile-launcher.tsx +61 -0
- package/lib/tui/scrollbar-options.ts +19 -0
- package/lib/tui/sidebar.tsx +50 -0
- package/lib/tui/theme.ts +40 -0
- package/lib/tui/tui.tsx +20 -0
- package/lib/tui/types.ts +38 -0
- package/lib/tui/unique-name.ts +18 -0
- package/lib/tui/use-event-stream.ts +133 -0
- package/lib/tui/use-snapshot.ts +99 -0
- package/lib/tui/utils/hascii/form-item-context.tsx +23 -0
- package/lib/tui/utils/hascii/input-focus-context.tsx +31 -0
- package/lib/tui/utils/hascii/theme-context.tsx +26 -0
- package/lib/tui/utils/hascii/theme.ts +176 -0
- package/lib/tui/views/channels-view.tsx +108 -0
- package/lib/tui/views/connectors-view.tsx +164 -0
- package/lib/tui/views/events-view.tsx +160 -0
- package/lib/tui/views/listeners-view.tsx +80 -0
- package/lib/tui/views/profiles-view.tsx +152 -0
- package/package.json +55 -44
- package/lib/api.ts +0 -54
- package/lib/modules/channels/channel-connector-ref-updater.ts +0 -4
- package/lib/modules/channels/funnel-channels.ts +0 -160
- package/lib/modules/connectors/connector-config-schema.ts +0 -16
- package/lib/modules/connectors/connector-existence-checker.ts +0 -3
- package/lib/modules/connectors/funnel-callable-connector-store.ts +0 -9
- package/lib/modules/connectors/funnel-connector-listener.ts +0 -5
- package/lib/modules/connectors/funnel-connector-stores.ts +0 -52
- package/lib/modules/connectors/funnel-connector-type-store.ts +0 -24
- package/lib/modules/connectors/funnel-connectors.ts +0 -151
- package/lib/modules/connectors/funnel-discord-listener.ts +0 -71
- package/lib/modules/connectors/funnel-discord-store.ts +0 -88
- package/lib/modules/connectors/funnel-gh-store.ts +0 -101
- package/lib/modules/connectors/funnel-json-connector-store.ts +0 -100
- package/lib/modules/connectors/funnel-schedule-listener.ts +0 -130
- package/lib/modules/connectors/funnel-schedule-store.ts +0 -195
- package/lib/modules/connectors/funnel-slack-adapter.ts +0 -31
- package/lib/modules/connectors/funnel-slack-store.ts +0 -90
- package/lib/modules/connectors/migrate-legacy-connectors.ts +0 -81
- package/lib/modules/connectors/schedule-connector-schema.ts +0 -18
- package/lib/modules/connectors/schedule-last-fired-store.ts +0 -48
- package/lib/modules/gateway/daemon.ts +0 -74
- package/lib/modules/gateway/funnel-broadcaster.ts +0 -37
- package/lib/modules/gateway/funnel-event-logger.ts +0 -59
- package/lib/modules/gateway/funnel-gateway-server.ts +0 -241
- package/lib/modules/mcp/channel-server.ts +0 -76
- package/lib/modules/profiles/profile-channel-checker.ts +0 -3
- package/lib/modules/profiles/profile-channel-ref-updater.ts +0 -3
- package/lib/modules/repos/funnel-repositories.ts +0 -112
- package/lib/modules/schedule/funnel-schedule.ts +0 -39
- package/lib/modules/settings/funnel-settings-store.ts +0 -56
- package/lib/modules/settings/settings-schema.ts +0 -33
- package/lib/modules/tui/app.tsx +0 -44
- package/lib/modules/tui/tui.tsx +0 -13
- package/lib/routes/channels/add.help.ts +0 -3
- package/lib/routes/channels/add.ts +0 -21
- package/lib/routes/channels/connectors-attach.help.ts +0 -3
- package/lib/routes/channels/connectors-attach.ts +0 -17
- package/lib/routes/channels/connectors-detach.help.ts +0 -3
- package/lib/routes/channels/connectors-detach.ts +0 -17
- package/lib/routes/channels/group.help.ts +0 -16
- package/lib/routes/channels/group.ts +0 -22
- package/lib/routes/channels/remove.help.ts +0 -3
- package/lib/routes/channels/remove.ts +0 -17
- package/lib/routes/channels/rename.help.ts +0 -5
- package/lib/routes/channels/rename.ts +0 -17
- package/lib/routes/channels/routes.ts +0 -19
- package/lib/routes/channels/show.help.ts +0 -1
- package/lib/routes/channels/show.ts +0 -26
- package/lib/routes/claude/claude.help.ts +0 -16
- package/lib/routes/claude/claude.ts +0 -76
- package/lib/routes/claude/routes.ts +0 -4
- package/lib/routes/connectors/add.help.ts +0 -28
- package/lib/routes/connectors/add.ts +0 -64
- package/lib/routes/connectors/group.help.ts +0 -14
- package/lib/routes/connectors/group.ts +0 -18
- package/lib/routes/connectors/remove.help.ts +0 -3
- package/lib/routes/connectors/remove.ts +0 -17
- package/lib/routes/connectors/rename.help.ts +0 -5
- package/lib/routes/connectors/rename.ts +0 -17
- package/lib/routes/connectors/routes.ts +0 -23
- package/lib/routes/connectors/schedules-add.help.ts +0 -11
- package/lib/routes/connectors/schedules-add.ts +0 -33
- package/lib/routes/connectors/schedules-group.help.ts +0 -1
- package/lib/routes/connectors/schedules-group.ts +0 -38
- package/lib/routes/connectors/schedules-remove.help.ts +0 -3
- package/lib/routes/connectors/schedules-remove.ts +0 -17
- package/lib/routes/connectors/set.help.ts +0 -8
- package/lib/routes/connectors/set.ts +0 -72
- package/lib/routes/connectors/show.help.ts +0 -1
- package/lib/routes/connectors/show.ts +0 -41
- package/lib/routes/gateway/group.help.ts +0 -15
- package/lib/routes/gateway/group.ts +0 -28
- package/lib/routes/gateway/logs.help.ts +0 -13
- package/lib/routes/gateway/logs.ts +0 -102
- package/lib/routes/gateway/restart.help.ts +0 -10
- package/lib/routes/gateway/routes.ts +0 -18
- package/lib/routes/gateway/run.help.ts +0 -12
- package/lib/routes/gateway/run.ts +0 -35
- package/lib/routes/gateway/start.help.ts +0 -15
- package/lib/routes/gateway/start.ts +0 -32
- package/lib/routes/gateway/status.help.ts +0 -9
- package/lib/routes/gateway/status.ts +0 -28
- package/lib/routes/gateway/stop.help.ts +0 -8
- package/lib/routes/gateway/stop.ts +0 -21
- package/lib/routes/profiles/add.help.ts +0 -3
- package/lib/routes/profiles/add.ts +0 -33
- package/lib/routes/profiles/group.help.ts +0 -16
- package/lib/routes/profiles/group.ts +0 -25
- package/lib/routes/profiles/launch.help.ts +0 -4
- package/lib/routes/profiles/launch.ts +0 -36
- package/lib/routes/profiles/remove.help.ts +0 -3
- package/lib/routes/profiles/remove.ts +0 -17
- package/lib/routes/profiles/rename.help.ts +0 -5
- package/lib/routes/profiles/rename.ts +0 -17
- package/lib/routes/profiles/routes.ts +0 -18
- package/lib/routes/profiles/set.help.ts +0 -5
- package/lib/routes/profiles/set.ts +0 -32
- package/lib/routes/repos/add.help.ts +0 -6
- package/lib/routes/repos/add.ts +0 -20
- package/lib/routes/repos/group.help.ts +0 -11
- package/lib/routes/repos/group.ts +0 -18
- package/lib/routes/repos/remove.help.ts +0 -3
- package/lib/routes/repos/remove.ts +0 -17
- package/lib/routes/repos/rename.help.ts +0 -5
- package/lib/routes/repos/rename.ts +0 -17
- package/lib/routes/repos/routes.ts +0 -17
- package/lib/routes/repos/set.help.ts +0 -5
- package/lib/routes/repos/set.ts +0 -21
- package/lib/routes/repos/show.help.ts +0 -1
- package/lib/routes/repos/show.ts +0 -19
- package/lib/routes/request/discord-help.ts +0 -9
- package/lib/routes/request/discord.help.ts +0 -19
- package/lib/routes/request/discord.ts +0 -65
- package/lib/routes/request/group.help.ts +0 -15
- package/lib/routes/request/group.ts +0 -9
- package/lib/routes/request/routes.ts +0 -14
- package/lib/routes/request/slack-help.ts +0 -9
- package/lib/routes/request/slack.help.ts +0 -19
- package/lib/routes/request/slack.ts +0 -61
- package/lib/routes/status/routes.ts +0 -4
- package/lib/routes/status/status.help.ts +0 -6
- package/lib/routes/status/status.ts +0 -77
- package/lib/routes/update/routes.ts +0 -4
- package/lib/routes/update/update.help.ts +0 -5
- package/lib/routes/update/update.ts +0 -21
- package/lib/routes.ts +0 -40
- /package/lib/{factory.ts → cli/factory.ts} +0 -0
- /package/lib/{modules → cli}/router/query-to-cli-args.ts +0 -0
- /package/lib/{modules → cli}/router/validator.ts +0 -0
- /package/lib/{modules/connectors/funnel-connector-adapter.ts → connectors/connector-adapter.ts} +0 -0
- /package/lib/{modules/connectors/funnel-discord-event-processor.ts → connectors/discord-event-processor.ts} +0 -0
- /package/lib/{modules/http/funnel-http-client.ts → engine/http/http-client.ts} +0 -0
- /package/lib/{modules/id/funnel-id-generator.ts → engine/id/id-generator.ts} +0 -0
- /package/lib/{modules/logger/funnel-logger.ts → engine/logger/logger.ts} +0 -0
- /package/lib/{modules/process/funnel-process-runner.ts → engine/process/process-runner.ts} +0 -0
- /package/lib/{modules/time/funnel-clock.ts → engine/time/clock.ts} +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
import { connectorConfigSchema } from "@/connectors/connector-config-schema"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Routing mode when multiple WS clients are subscribed to the same channel.
|
|
6
|
+
*
|
|
7
|
+
* - `fanout` (default): every connected client receives every event. Right when each
|
|
8
|
+
* subscriber has its own job (e.g., TUI mirrors, distinct Claude profiles each running
|
|
9
|
+
* their own pipeline against the same source).
|
|
10
|
+
* - `exclusive`: each event is delivered to exactly one connected client, picked
|
|
11
|
+
* round-robin per channel. Right when subscribers are interchangeable workers and you
|
|
12
|
+
* want each event handled once. Tap=all clients (TUI dashboard) always receive,
|
|
13
|
+
* regardless of mode, so they can passively observe.
|
|
14
|
+
*/
|
|
15
|
+
export const channelDeliveryModeSchema = z.enum(["fanout", "exclusive"])
|
|
16
|
+
|
|
17
|
+
export type ChannelDeliveryMode = z.infer<typeof channelDeliveryModeSchema>
|
|
18
|
+
|
|
19
|
+
export const channelConfigSchema = z.object({
|
|
20
|
+
id: z.string(),
|
|
21
|
+
name: z.string(),
|
|
22
|
+
delivery: channelDeliveryModeSchema.default("fanout"),
|
|
23
|
+
connectors: z.array(connectorConfigSchema).default([]),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export type ChannelConfig = z.infer<typeof channelConfigSchema>
|
|
27
|
+
|
|
28
|
+
export const profileConfigSchema = z.object({
|
|
29
|
+
name: z.string(),
|
|
30
|
+
path: z.string(),
|
|
31
|
+
subAgent: z.string(),
|
|
32
|
+
channelId: z.string(),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export type ProfileConfig = z.infer<typeof profileConfigSchema>
|
|
36
|
+
|
|
37
|
+
export const SETTINGS_VERSION = 1
|
|
38
|
+
|
|
39
|
+
export const settingsSchema = z.object({
|
|
40
|
+
/** Schema version. New files always write the current version; older files without one are read as v1. */
|
|
41
|
+
version: z.literal(SETTINGS_VERSION).default(SETTINGS_VERSION),
|
|
42
|
+
channels: z.array(channelConfigSchema).default([]),
|
|
43
|
+
profiles: z.array(profileConfigSchema).default([]),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
export type Settings = z.infer<typeof settingsSchema>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { homedir } from "node:os"
|
|
2
|
+
import { dirname, join } from "node:path"
|
|
3
|
+
import { FunnelFileSystem } from "@/engine/fs/file-system"
|
|
4
|
+
import { NodeFunnelFileSystem } from "@/engine/fs/node-file-system"
|
|
5
|
+
import { FunnelSettingsReader } from "@/engine/settings/settings-reader"
|
|
6
|
+
import { SETTINGS_VERSION, settingsSchema } from "@/engine/settings/settings-schema"
|
|
7
|
+
import type { Settings } from "@/engine/settings/settings-schema"
|
|
8
|
+
|
|
9
|
+
export const FUNNEL_DIR = join(homedir(), ".funnel")
|
|
10
|
+
export const SETTINGS_PATH = join(FUNNEL_DIR, "settings.json")
|
|
11
|
+
|
|
12
|
+
type Deps = {
|
|
13
|
+
path?: string
|
|
14
|
+
fs?: FunnelFileSystem
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const defaultFs = new NodeFunnelFileSystem()
|
|
18
|
+
|
|
19
|
+
export class FunnelSettingsStore extends FunnelSettingsReader {
|
|
20
|
+
private readonly path: string
|
|
21
|
+
private readonly fs: FunnelFileSystem
|
|
22
|
+
|
|
23
|
+
constructor(deps: Deps = {}) {
|
|
24
|
+
super()
|
|
25
|
+
this.path = deps.path ?? SETTINGS_PATH
|
|
26
|
+
this.fs = deps.fs ?? defaultFs
|
|
27
|
+
Object.freeze(this)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
read(): Settings {
|
|
31
|
+
if (!this.fs.existsSync(this.path)) {
|
|
32
|
+
return {
|
|
33
|
+
version: SETTINGS_VERSION,
|
|
34
|
+
channels: [],
|
|
35
|
+
profiles: [],
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const content = this.fs.readFileSync(this.path)
|
|
40
|
+
const parsed: unknown = JSON.parse(content)
|
|
41
|
+
|
|
42
|
+
if (this.looksLikeLegacy(parsed)) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`legacy settings.json detected at ${this.path}. The schema changed (channel.connectors are now nested objects with ids; profile fields renamed). Migration is intentionally not provided. Back up and remove the old file:\n mv ${this.path} ${this.path}.bak`,
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
parsed &&
|
|
50
|
+
typeof parsed === "object" &&
|
|
51
|
+
"version" in parsed &&
|
|
52
|
+
parsed.version !== SETTINGS_VERSION
|
|
53
|
+
) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`unsupported settings.json version (${this.path}): expected ${SETTINGS_VERSION}, got ${String(parsed.version)}`,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = settingsSchema.safeParse(parsed)
|
|
60
|
+
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`invalid settings.json (${this.path}): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return result.data
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private looksLikeLegacy(parsed: unknown): boolean {
|
|
71
|
+
if (!parsed || typeof parsed !== "object") return false
|
|
72
|
+
|
|
73
|
+
const obj = parsed as Record<string, unknown>
|
|
74
|
+
|
|
75
|
+
if (Array.isArray(obj.channels)) {
|
|
76
|
+
for (const channel of obj.channels) {
|
|
77
|
+
if (!channel || typeof channel !== "object") continue
|
|
78
|
+
const ch = channel as Record<string, unknown>
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(ch.connectors) && ch.connectors.some((x) => typeof x === "string")) {
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!("id" in ch) && "name" in ch) return true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (Array.isArray(obj.connectors)) return true
|
|
89
|
+
if (Array.isArray(obj.repositories)) return true
|
|
90
|
+
|
|
91
|
+
if (Array.isArray(obj.profiles)) {
|
|
92
|
+
for (const profile of obj.profiles) {
|
|
93
|
+
if (!profile || typeof profile !== "object") continue
|
|
94
|
+
const p = profile as Record<string, unknown>
|
|
95
|
+
|
|
96
|
+
if ("repository" in p || "envFiles" in p || ("channel" in p && !("channelId" in p))) {
|
|
97
|
+
return true
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
write(settings: Settings): void {
|
|
106
|
+
this.fs.mkdirSync(dirname(this.path), { recursive: true })
|
|
107
|
+
const versioned: Settings = { ...settings, version: SETTINGS_VERSION }
|
|
108
|
+
this.fs.writeFileSync(this.path, `${JSON.stringify(versioned, null, 2)}\n`)
|
|
109
|
+
}
|
|
110
|
+
}
|
package/lib/funnel.ts
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import { join } from "node:path"
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "@/
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import type {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { FUNNEL_DIR, FunnelSettingsStore } from "@/modules/settings/funnel-settings-store"
|
|
21
|
-
import type { FunnelClock } from "@/modules/time/funnel-clock"
|
|
2
|
+
import { FunnelConnectorFactory } from "@/connectors/connector-factory"
|
|
3
|
+
import { FunnelChannels } from "@/engine/channels/channels"
|
|
4
|
+
import { FunnelClaude } from "@/engine/claude/claude"
|
|
5
|
+
import type { FunnelFileSystem } from "@/engine/fs/file-system"
|
|
6
|
+
import type { FunnelIdGenerator } from "@/engine/id/id-generator"
|
|
7
|
+
import { FunnelLogger } from "@/engine/logger/logger"
|
|
8
|
+
import { NodeFunnelLogger } from "@/engine/logger/node-logger"
|
|
9
|
+
import { FunnelMcp } from "@/engine/mcp/mcp"
|
|
10
|
+
import { FunnelProcessRunner } from "@/engine/process/process-runner"
|
|
11
|
+
import { NodeFunnelProcessRunner } from "@/engine/process/node-process-runner"
|
|
12
|
+
import { FunnelProfiles } from "@/engine/profiles/profiles"
|
|
13
|
+
import { FunnelSettingsReader } from "@/engine/settings/settings-reader"
|
|
14
|
+
import { FUNNEL_DIR, FunnelSettingsStore } from "@/engine/settings/settings-store"
|
|
15
|
+
import type { FunnelClock } from "@/engine/time/clock"
|
|
16
|
+
import { FunnelGateway } from "@/gateway/gateway"
|
|
17
|
+
import { FunnelGatewayServer } from "@/gateway/gateway-server"
|
|
18
|
+
import { FunnelGatewayToken } from "@/gateway/gateway-token"
|
|
19
|
+
import { FunnelListenersClient } from "@/gateway/listeners-client"
|
|
22
20
|
|
|
23
21
|
type Props = {
|
|
24
|
-
/** Settings persistence (channels
|
|
22
|
+
/** Settings persistence (channels with nested connectors / profiles). Defaults to a FunnelSettingsStore rooted at `dir`. */
|
|
25
23
|
store?: FunnelSettingsReader
|
|
26
24
|
/** Filesystem boundary. Replace with MemoryFunnelFileSystem to sandbox all disk I/O. */
|
|
27
25
|
fs?: FunnelFileSystem
|
|
@@ -31,14 +29,12 @@ type Props = {
|
|
|
31
29
|
logger?: FunnelLogger
|
|
32
30
|
/** Clock used by schedule listener, gh poll watermarks, and gateway timeouts. */
|
|
33
31
|
clock?: FunnelClock
|
|
34
|
-
/** ID generator for
|
|
32
|
+
/** ID generator for channel and connector ids. Use MemoryFunnelIdGenerator for deterministic tests. */
|
|
35
33
|
idGenerator?: FunnelIdGenerator
|
|
36
|
-
/** Funnel home directory (settings.json +
|
|
34
|
+
/** Funnel home directory (settings.json + per-channel/per-connector dirs). Defaults to ~/.funnel. */
|
|
37
35
|
dir?: string
|
|
38
36
|
/** Temp / runtime directory (gateway logs and PID adjacent files). Defaults to /tmp/funnel. */
|
|
39
37
|
tmpDir?: string
|
|
40
|
-
/** Pre-built connector stores. Useful when sharing stores between multiple Funnel instances. */
|
|
41
|
-
connectorStores?: ConnectorStoresBundle
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
/**
|
|
@@ -49,11 +45,14 @@ type Props = {
|
|
|
49
45
|
* injectable via `Props` — passing memory implementations gives a fully sandboxed
|
|
50
46
|
* Funnel that touches no real disk, processes, or wall-clock time.
|
|
51
47
|
*
|
|
48
|
+
* Connectors live nested inside their owning channel (channels[].connectors[]),
|
|
49
|
+
* so connector CRUD is reached via `funnel.channels.addConnector(...)` etc.
|
|
50
|
+
*
|
|
52
51
|
* @example
|
|
53
52
|
* ```ts
|
|
54
53
|
* const funnel = new Funnel({})
|
|
55
|
-
* funnel.
|
|
56
|
-
* funnel.channels.
|
|
54
|
+
* const channel = funnel.channels.add({ name: "inbox" })
|
|
55
|
+
* funnel.channels.addConnector("inbox", { type: "slack", name: "ops", botToken, appToken })
|
|
57
56
|
* await funnel.gatewayServer({ port: 9742 }).start()
|
|
58
57
|
* ```
|
|
59
58
|
*/
|
|
@@ -73,51 +72,51 @@ export class Funnel {
|
|
|
73
72
|
)
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/**
|
|
77
|
-
get
|
|
78
|
-
return (
|
|
79
|
-
this.props.connectorStores ??
|
|
80
|
-
createConnectorStores({
|
|
81
|
-
fs: this.props.fs,
|
|
82
|
-
process: this.props.process,
|
|
83
|
-
logger: this.props.logger,
|
|
84
|
-
clock: this.props.clock,
|
|
85
|
-
idGenerator: this.props.idGenerator,
|
|
86
|
-
dir: this.props.dir,
|
|
87
|
-
})
|
|
88
|
-
)
|
|
75
|
+
/** Process runner boundary. Defaults to NodeFunnelProcessRunner. */
|
|
76
|
+
get process(): FunnelProcessRunner {
|
|
77
|
+
return this.props.process ?? new NodeFunnelProcessRunner()
|
|
89
78
|
}
|
|
90
79
|
|
|
91
|
-
/**
|
|
92
|
-
get
|
|
93
|
-
return this.
|
|
80
|
+
/** Logger boundary. Defaults to NodeFunnelLogger. */
|
|
81
|
+
get logger(): FunnelLogger {
|
|
82
|
+
return this.props.logger ?? new NodeFunnelLogger()
|
|
94
83
|
}
|
|
95
84
|
|
|
96
|
-
/**
|
|
97
|
-
get
|
|
98
|
-
return
|
|
85
|
+
/** Pure factory that constructs per-type listeners and adapters from connector configs. */
|
|
86
|
+
get factory(): FunnelConnectorFactory {
|
|
87
|
+
return new FunnelConnectorFactory({
|
|
88
|
+
fs: this.props.fs,
|
|
89
|
+
process: this.props.process,
|
|
90
|
+
logger: this.props.logger,
|
|
91
|
+
dir: this.props.dir,
|
|
92
|
+
})
|
|
99
93
|
}
|
|
100
94
|
|
|
101
|
-
/**
|
|
102
|
-
get
|
|
103
|
-
return new
|
|
95
|
+
/** Channel CRUD + nested connector CRUD + schedule entries + listener/adapter dispatch. */
|
|
96
|
+
get channels(): FunnelChannels {
|
|
97
|
+
return new FunnelChannels({
|
|
98
|
+
store: this.store,
|
|
99
|
+
factory: this.factory,
|
|
100
|
+
profileChecker: this.profiles,
|
|
101
|
+
clock: this.props.clock,
|
|
102
|
+
idGenerator: this.props.idGenerator,
|
|
103
|
+
})
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
/** Launch profiles (named presets for `fnl claude
|
|
106
|
+
/** Launch profiles (named presets for `fnl claude`: path + sub-agent + channel id). */
|
|
107
107
|
get profiles(): FunnelProfiles {
|
|
108
108
|
return new FunnelProfiles({ store: this.store })
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
/**
|
|
112
|
-
get
|
|
113
|
-
return new
|
|
111
|
+
/** funnel MCP installer (writes/removes `.mcp.json` entries in target repos). */
|
|
112
|
+
get mcp(): FunnelMcp {
|
|
113
|
+
return new FunnelMcp({ fs: this.props.fs })
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/** Launch Claude Code with a channel injected via env, MCP installed, gateway ensured. */
|
|
117
117
|
get claude(): FunnelClaude {
|
|
118
118
|
return new FunnelClaude({
|
|
119
119
|
channels: this.channels,
|
|
120
|
-
repositories: this.repositories,
|
|
121
120
|
mcp: this.mcp,
|
|
122
121
|
gateway: this.gateway,
|
|
123
122
|
fs: this.props.fs,
|
|
@@ -138,45 +137,51 @@ export class Funnel {
|
|
|
138
137
|
})
|
|
139
138
|
}
|
|
140
139
|
|
|
140
|
+
/** Read / generate the daemon's gateway token (mode 0600 file under `dir`). */
|
|
141
|
+
get gatewayToken(): FunnelGatewayToken {
|
|
142
|
+
return new FunnelGatewayToken({ fs: this.props.fs, dir: this.props.dir })
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* HTTP client for listener operations on the running gateway daemon.
|
|
147
|
+
* Returns `{ state: "offline" }` when the daemon is offline so hot-reload
|
|
148
|
+
* paths stay write-only without parsing strings.
|
|
149
|
+
*/
|
|
150
|
+
get listeners(): FunnelListenersClient {
|
|
151
|
+
const gateway = this.gateway
|
|
152
|
+
const token = this.gatewayToken
|
|
153
|
+
|
|
154
|
+
return new FunnelListenersClient({
|
|
155
|
+
port: gateway.getPort(),
|
|
156
|
+
isDaemonRunning: () => gateway.isRunning(),
|
|
157
|
+
getToken: () => token.read(),
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
141
161
|
/**
|
|
142
162
|
* In-process gateway server. Unlike `gateway.start()` (which spawns a daemon),
|
|
143
163
|
* this returns a class that runs `Bun.serve` + listeners inside the current process —
|
|
144
164
|
* useful for tests, embedding, or custom hosts.
|
|
145
165
|
*/
|
|
146
166
|
gatewayServer(
|
|
147
|
-
options: {
|
|
167
|
+
options: {
|
|
168
|
+
port?: number
|
|
169
|
+
logDir?: string
|
|
170
|
+
killCompetingSlack?: boolean
|
|
171
|
+
/** Override the auth token. Defaults to the persisted gateway.token. Pass "" to disable auth (tests). */
|
|
172
|
+
token?: string
|
|
173
|
+
} = {},
|
|
148
174
|
): FunnelGatewayServer {
|
|
149
175
|
return new FunnelGatewayServer({
|
|
150
|
-
|
|
176
|
+
channels: this.channels,
|
|
151
177
|
settings: this.store,
|
|
152
178
|
port: options.port,
|
|
153
179
|
logDir: options.logDir,
|
|
154
|
-
fs: this.props.fs,
|
|
155
180
|
process: this.props.process,
|
|
156
181
|
clock: this.props.clock,
|
|
157
182
|
logger: this.props.logger,
|
|
158
183
|
killCompetingSlack: options.killCompetingSlack,
|
|
184
|
+
token: options.token ?? this.gatewayToken.ensure(),
|
|
159
185
|
})
|
|
160
186
|
}
|
|
161
|
-
|
|
162
|
-
/** funnel MCP installer (writes/removes `.mcp.json` entries in target repos). */
|
|
163
|
-
get mcp(): FunnelMcp {
|
|
164
|
-
return new FunnelMcp({ fs: this.props.fs })
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private wirePair(): { channels: FunnelChannels; connectors: FunnelConnectors } {
|
|
168
|
-
const stores = this.stores
|
|
169
|
-
const profiles = this.profiles
|
|
170
|
-
const channels: FunnelChannels = new FunnelChannels({
|
|
171
|
-
store: this.store,
|
|
172
|
-
connectorChecker: { has: (name) => connectors.has(name) },
|
|
173
|
-
profileChecker: profiles,
|
|
174
|
-
profileRefUpdater: profiles,
|
|
175
|
-
})
|
|
176
|
-
const connectors: FunnelConnectors = new FunnelConnectors({
|
|
177
|
-
...stores,
|
|
178
|
-
refUpdater: channels,
|
|
179
|
-
})
|
|
180
|
-
return { channels, connectors }
|
|
181
|
-
}
|
|
182
187
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto"
|
|
2
|
+
import type { MiddlewareHandler } from "hono"
|
|
3
|
+
import type { Env } from "@/gateway/factory"
|
|
4
|
+
|
|
5
|
+
type Deps = {
|
|
6
|
+
expected: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Verifies `Authorization: Bearer <token>` against the daemon's gateway token.
|
|
11
|
+
* Mounted on the routes that mutate listener state or expose detailed status.
|
|
12
|
+
* `/health` is intentionally left unauthenticated so the daemon manager can
|
|
13
|
+
* probe liveness without needing the token.
|
|
14
|
+
*/
|
|
15
|
+
export const requireBearerToken = (deps: Deps): MiddlewareHandler<Env> => {
|
|
16
|
+
return async (c, next) => {
|
|
17
|
+
const header = c.req.header("authorization") ?? ""
|
|
18
|
+
const match = header.match(/^Bearer\s+(.+)$/i)
|
|
19
|
+
const presented = match?.[1] ?? ""
|
|
20
|
+
|
|
21
|
+
if (!constantTimeEqual(presented, deps.expected)) {
|
|
22
|
+
return c.text("unauthorized", 401)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return await next()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const constantTimeEqual = (a: string, b: string): boolean => {
|
|
30
|
+
const bufA = Buffer.from(a, "utf-8")
|
|
31
|
+
const bufB = Buffer.from(b, "utf-8")
|
|
32
|
+
const maxLen = Math.max(bufA.length, bufB.length, 1)
|
|
33
|
+
const padA = Buffer.alloc(maxLen)
|
|
34
|
+
const padB = Buffer.alloc(maxLen)
|
|
35
|
+
|
|
36
|
+
bufA.copy(padA)
|
|
37
|
+
bufB.copy(padB)
|
|
38
|
+
|
|
39
|
+
// timingSafeEqual on equal-length padded buffers, then AND with length match
|
|
40
|
+
// so a length-only probe still requires the full comparison time.
|
|
41
|
+
const equal = timingSafeEqual(padA, padB)
|
|
42
|
+
|
|
43
|
+
return equal && bufA.length === bufB.length
|
|
44
|
+
}
|