@interactive-inc/claude-funnel 0.8.1 → 0.10.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 +179 -80
- package/dist/bin.js +726 -658
- package/dist/connector-adapter-CXB-q_XC.d.ts +11 -0
- package/dist/connector-adapter-D5Utumgz.js +4 -0
- package/dist/connectors/discord.d.ts +76 -0
- package/dist/connectors/discord.js +2 -0
- package/dist/connectors/gh.d.ts +38 -0
- package/dist/connectors/gh.js +2 -0
- package/dist/connectors/schedule.d.ts +53 -0
- package/dist/connectors/schedule.js +2 -0
- package/dist/connectors/slack.d.ts +34 -0
- package/dist/connectors/slack.js +2 -0
- package/dist/discord-connector-schema-Dww2I4zH.d.ts +14 -0
- package/dist/discord-connector-schema-ygf5Df-2.js +173 -0
- package/dist/file-system-Co60LrmR.d.ts +74 -0
- package/dist/gateway/daemon.js +233 -183
- package/dist/gh-connector-schema-2ml29MBC.js +218 -0
- package/dist/gh-connector-schema-BZFAS-p-.d.ts +45 -0
- package/dist/index.d.ts +3881 -36
- package/dist/index.js +6217 -3483
- package/dist/logger-CTlXs7z4.d.ts +33 -0
- package/dist/node-logger-DQz_BGOD.js +61 -0
- package/dist/schedule-connector-schema-CkuIQ0JQ.js +325 -0
- package/dist/slack-connector-schema-Cd22WiHB.js +153 -0
- package/dist/slack-event-processor-CS-bAit9.d.ts +43 -0
- package/package.json +34 -28
- package/dist/cli/factory.d.ts +0 -7
- package/dist/cli/router/query-to-cli-args.d.ts +0 -1
- package/dist/cli/router/to-request.d.ts +0 -5
- package/dist/cli/router/validator.d.ts +0 -5
- package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +0 -42
- package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +0 -46
- package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +0 -54
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +0 -66
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +0 -42
- package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +0 -46
- package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +0 -90
- package/dist/cli/routes/channels.$channel.connectors.d.ts +0 -38
- package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +0 -42
- package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +0 -62
- package/dist/cli/routes/channels.$channel.d.ts +0 -38
- package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +0 -42
- package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +0 -28
- package/dist/cli/routes/channels.add.$channel.d.ts +0 -46
- package/dist/cli/routes/channels.d.ts +0 -16
- package/dist/cli/routes/channels.remove.$channel.d.ts +0 -38
- package/dist/cli/routes/claude.d.ts +0 -32
- package/dist/cli/routes/gateway.d.ts +0 -20
- package/dist/cli/routes/gateway.listeners.d.ts +0 -17
- package/dist/cli/routes/gateway.logs.d.ts +0 -24
- package/dist/cli/routes/gateway.restart.d.ts +0 -24
- package/dist/cli/routes/gateway.run.d.ts +0 -24
- package/dist/cli/routes/gateway.start.d.ts +0 -24
- package/dist/cli/routes/gateway.status.d.ts +0 -13
- package/dist/cli/routes/gateway.stop.d.ts +0 -16
- package/dist/cli/routes/index.d.ts +0 -1222
- package/dist/cli/routes/profiles.$profile.as-default.d.ts +0 -38
- package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +0 -42
- package/dist/cli/routes/profiles.$profile.run.d.ts +0 -46
- package/dist/cli/routes/profiles.add.$profile.d.ts +0 -54
- package/dist/cli/routes/profiles.d.ts +0 -16
- package/dist/cli/routes/profiles.remove.$profile.d.ts +0 -38
- package/dist/cli/routes/profiles.set.$profile.d.ts +0 -54
- package/dist/cli/routes/status.d.ts +0 -16
- package/dist/cli/routes/update.d.ts +0 -16
- package/dist/connectors/connector-adapter.d.ts +0 -8
- package/dist/connectors/connector-config-schema.d.ts +0 -43
- package/dist/connectors/connector-factory.d.ts +0 -32
- package/dist/connectors/connector-listener.d.ts +0 -17
- package/dist/connectors/discord-adapter.d.ts +0 -14
- package/dist/connectors/discord-connector-schema.d.ts +0 -10
- package/dist/connectors/discord-event-processor.d.ts +0 -26
- package/dist/connectors/discord-listener.d.ts +0 -17
- package/dist/connectors/gh-adapter.d.ts +0 -11
- package/dist/connectors/gh-connector-schema.d.ts +0 -10
- package/dist/connectors/gh-listener.d.ts +0 -26
- package/dist/connectors/match-cron.d.ts +0 -1
- package/dist/connectors/schedule-connector-schema.d.ts +0 -45
- package/dist/connectors/schedule-listener.d.ts +0 -30
- package/dist/connectors/schedule-state-store.d.ts +0 -19
- package/dist/connectors/slack-adapter.d.ts +0 -15
- package/dist/connectors/slack-connector-schema.d.ts +0 -11
- package/dist/connectors/slack-event-processor.d.ts +0 -27
- package/dist/connectors/slack-listener.d.ts +0 -17
- package/dist/engine/channels/channels.d.ts +0 -106
- package/dist/engine/claude/claude.d.ts +0 -49
- package/dist/engine/claude/gateway-controller.d.ts +0 -6
- package/dist/engine/fs/file-system.d.ts +0 -24
- package/dist/engine/fs/memory-file-system.d.ts +0 -31
- package/dist/engine/fs/node-file-system.d.ts +0 -15
- package/dist/engine/http/http-client.d.ts +0 -15
- package/dist/engine/http/memory-http-client.d.ts +0 -12
- package/dist/engine/http/node-http-client.d.ts +0 -5
- package/dist/engine/id/id-generator.d.ts +0 -7
- package/dist/engine/id/memory-id-generator.d.ts +0 -11
- package/dist/engine/id/node-id-generator.d.ts +0 -4
- package/dist/engine/logger/logger.d.ts +0 -11
- package/dist/engine/logger/memory-logger.d.ts +0 -14
- package/dist/engine/logger/node-logger.d.ts +0 -15
- package/dist/engine/logger/noop-logger.d.ts +0 -7
- package/dist/engine/mcp/channel-server.d.ts +0 -1
- package/dist/engine/mcp/mcp.d.ts +0 -22
- package/dist/engine/process/memory-process-runner.d.ts +0 -43
- package/dist/engine/process/node-process-runner.d.ts +0 -9
- package/dist/engine/process/process-runner.d.ts +0 -29
- package/dist/engine/profiles/profile-channel-checker.d.ts +0 -7
- package/dist/engine/profiles/profiles.d.ts +0 -31
- package/dist/engine/settings/mock-settings-reader.d.ts +0 -9
- package/dist/engine/settings/settings-reader.d.ts +0 -5
- package/dist/engine/settings/settings-schema.d.ts +0 -132
- package/dist/engine/settings/settings-store.d.ts +0 -18
- package/dist/engine/time/clock.d.ts +0 -9
- package/dist/engine/time/memory-clock.d.ts +0 -12
- package/dist/engine/time/node-clock.d.ts +0 -4
- package/dist/funnel.d.ts +0 -95
- package/dist/gateway/auth-middleware.d.ts +0 -14
- package/dist/gateway/broadcaster.d.ts +0 -122
- package/dist/gateway/daemon.d.ts +0 -2
- package/dist/gateway/factory.d.ts +0 -7
- package/dist/gateway/funnel-event-store.d.ts +0 -81
- package/dist/gateway/gateway-server.d.ts +0 -94
- package/dist/gateway/gateway-token.d.ts +0 -33
- package/dist/gateway/gateway.d.ts +0 -58
- package/dist/gateway/kill-competing-slack-gateways.d.ts +0 -9
- package/dist/gateway/listener-supervisor.d.ts +0 -85
- package/dist/gateway/listeners-client.d.ts +0 -53
- package/dist/gateway/resolve-daemon-script.d.ts +0 -11
- package/dist/gateway/routes/channels.connectors.call.d.ts +0 -41
- package/dist/gateway/routes/health.d.ts +0 -17
- package/dist/gateway/routes/index.d.ts +0 -209
- package/dist/gateway/routes/listeners.list.d.ts +0 -14
- package/dist/gateway/routes/listeners.restart.d.ts +0 -34
- package/dist/gateway/routes/listeners.start.d.ts +0 -34
- package/dist/gateway/routes/listeners.stop.d.ts +0 -34
- package/dist/gateway/routes/route-deps.d.ts +0 -10
- package/dist/gateway/routes/status.d.ts +0 -30
- package/dist/gateway/routes/validator.d.ts +0 -19
- package/dist/logger/leuco-human-file-writer.d.ts +0 -33
- package/dist/logger/leuco-human-logger.d.ts +0 -46
- package/dist/logger/leuco-human-record.d.ts +0 -15
- package/dist/logger/leuco-human-stdout-writer.d.ts +0 -20
- package/dist/logger/leuco-human-writer.d.ts +0 -13
- package/dist/logger/leuco-logger-memory-sink.d.ts +0 -33
- package/dist/logger/leuco-logger-record.d.ts +0 -13
- package/dist/logger/leuco-logger-sink.d.ts +0 -34
- package/dist/logger/leuco-logger-sqlite-sink.d.ts +0 -102
- package/dist/logger/leuco-logger.d.ts +0 -56
- package/lib/bin.ts +0 -78
- package/lib/cli/factory.ts +0 -10
- package/lib/cli/router/query-to-cli-args.ts +0 -20
- package/lib/cli/router/to-request.ts +0 -112
- package/lib/cli/router/validator.ts +0 -27
- package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +0 -27
- package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +0 -40
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +0 -41
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +0 -22
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +0 -23
- package/lib/cli/routes/channels.$channel.connectors.$connector.ts +0 -26
- package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +0 -92
- package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +0 -22
- package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +0 -63
- package/lib/cli/routes/channels.$channel.connectors.ts +0 -26
- package/lib/cli/routes/channels.$channel.rename.$newName.ts +0 -22
- package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +0 -34
- package/lib/cli/routes/channels.$channel.ts +0 -34
- package/lib/cli/routes/channels.add.$channel.ts +0 -33
- package/lib/cli/routes/channels.remove.$channel.ts +0 -20
- package/lib/cli/routes/channels.ts +0 -39
- package/lib/cli/routes/claude.ts +0 -69
- package/lib/cli/routes/gateway.listeners.ts +0 -41
- package/lib/cli/routes/gateway.logs.ts +0 -123
- package/lib/cli/routes/gateway.restart.ts +0 -50
- package/lib/cli/routes/gateway.run.ts +0 -41
- package/lib/cli/routes/gateway.start.ts +0 -50
- package/lib/cli/routes/gateway.status.ts +0 -19
- package/lib/cli/routes/gateway.stop.ts +0 -32
- package/lib/cli/routes/gateway.ts +0 -55
- package/lib/cli/routes/index.ts +0 -202
- package/lib/cli/routes/profiles.$profile.as-default.ts +0 -22
- package/lib/cli/routes/profiles.$profile.rename.$newName.ts +0 -22
- package/lib/cli/routes/profiles.$profile.run.ts +0 -36
- package/lib/cli/routes/profiles.add.$profile.ts +0 -46
- package/lib/cli/routes/profiles.remove.$profile.ts +0 -20
- package/lib/cli/routes/profiles.set.$profile.ts +0 -46
- package/lib/cli/routes/profiles.ts +0 -40
- package/lib/cli/routes/status.ts +0 -93
- package/lib/cli/routes/update.ts +0 -27
- package/lib/connectors/connector-adapter.ts +0 -9
- package/lib/connectors/connector-config-schema.ts +0 -16
- package/lib/connectors/connector-factory.ts +0 -94
- package/lib/connectors/connector-listener.ts +0 -20
- package/lib/connectors/discord-adapter.ts +0 -51
- package/lib/connectors/discord-connector-schema.ts +0 -12
- package/lib/connectors/discord-event-processor.ts +0 -48
- package/lib/connectors/discord-listener.ts +0 -111
- package/lib/connectors/gh-adapter.ts +0 -48
- package/lib/connectors/gh-connector-schema.ts +0 -12
- package/lib/connectors/gh-listener.ts +0 -137
- package/lib/connectors/match-cron.ts +0 -78
- package/lib/connectors/schedule-connector-schema.ts +0 -33
- package/lib/connectors/schedule-listener.ts +0 -207
- package/lib/connectors/schedule-state-store.ts +0 -54
- package/lib/connectors/slack-adapter.ts +0 -36
- package/lib/connectors/slack-connector-schema.ts +0 -13
- package/lib/connectors/slack-event-processor.ts +0 -97
- package/lib/connectors/slack-listener.ts +0 -97
- package/lib/engine/channels/channels.ts +0 -520
- package/lib/engine/claude/claude.ts +0 -199
- package/lib/engine/claude/gateway-controller.ts +0 -4
- package/lib/engine/fs/file-system.ts +0 -23
- package/lib/engine/fs/memory-file-system.ts +0 -102
- package/lib/engine/fs/node-file-system.ts +0 -68
- package/lib/engine/http/http-client.ts +0 -17
- package/lib/engine/http/memory-http-client.ts +0 -36
- package/lib/engine/http/node-http-client.ts +0 -23
- package/lib/engine/id/id-generator.ts +0 -7
- package/lib/engine/id/memory-id-generator.ts +0 -20
- package/lib/engine/id/node-id-generator.ts +0 -7
- package/lib/engine/logger/logger.ts +0 -11
- package/lib/engine/logger/memory-logger.ts +0 -28
- package/lib/engine/logger/node-logger.ts +0 -49
- package/lib/engine/logger/noop-logger.ts +0 -9
- package/lib/engine/mcp/channel-server.ts +0 -204
- package/lib/engine/mcp/mcp.ts +0 -126
- package/lib/engine/process/memory-process-runner.ts +0 -88
- package/lib/engine/process/node-process-runner.ts +0 -91
- package/lib/engine/process/process-runner.ts +0 -33
- package/lib/engine/profiles/profile-channel-checker.ts +0 -7
- package/lib/engine/profiles/profiles.ts +0 -126
- package/lib/engine/settings/mock-settings-reader.ts +0 -27
- package/lib/engine/settings/settings-reader.ts +0 -6
- package/lib/engine/settings/settings-schema.ts +0 -46
- package/lib/engine/settings/settings-store.ts +0 -110
- package/lib/engine/time/clock.ts +0 -15
- package/lib/engine/time/memory-clock.ts +0 -26
- package/lib/engine/time/node-clock.ts +0 -7
- package/lib/funnel.ts +0 -187
- package/lib/gateway/auth-middleware.ts +0 -44
- package/lib/gateway/broadcaster.ts +0 -319
- package/lib/gateway/daemon.ts +0 -47
- package/lib/gateway/factory.ts +0 -10
- package/lib/gateway/funnel-event-store.ts +0 -155
- package/lib/gateway/gateway-server.ts +0 -414
- package/lib/gateway/gateway-token.ts +0 -79
- package/lib/gateway/gateway.ts +0 -209
- package/lib/gateway/kill-competing-slack-gateways.ts +0 -56
- package/lib/gateway/listener-supervisor.ts +0 -339
- package/lib/gateway/listeners-client.ts +0 -128
- package/lib/gateway/resolve-daemon-script.ts +0 -26
- package/lib/gateway/routes/channels.connectors.call.ts +0 -39
- package/lib/gateway/routes/health.ts +0 -13
- package/lib/gateway/routes/index.ts +0 -24
- package/lib/gateway/routes/listeners.list.ts +0 -6
- package/lib/gateway/routes/listeners.restart.ts +0 -15
- package/lib/gateway/routes/listeners.start.ts +0 -15
- package/lib/gateway/routes/listeners.stop.ts +0 -15
- package/lib/gateway/routes/route-deps.ts +0 -11
- package/lib/gateway/routes/status.ts +0 -15
- package/lib/gateway/routes/validator.ts +0 -17
- package/lib/index.ts +0 -52
- package/lib/logger/leuco-human-file-writer.ts +0 -65
- package/lib/logger/leuco-human-logger.ts +0 -98
- package/lib/logger/leuco-human-record.ts +0 -16
- package/lib/logger/leuco-human-stdout-writer.ts +0 -26
- package/lib/logger/leuco-human-writer.ts +0 -14
- package/lib/logger/leuco-logger-memory-sink.ts +0 -67
- package/lib/logger/leuco-logger-record.ts +0 -13
- package/lib/logger/leuco-logger-sink.ts +0 -33
- package/lib/logger/leuco-logger-sqlite-sink.ts +0 -355
- package/lib/logger/leuco-logger.ts +0 -135
- package/lib/tui/app.tsx +0 -357
- package/lib/tui/components/add-row.tsx +0 -18
- package/lib/tui/components/brand.tsx +0 -27
- package/lib/tui/components/card.tsx +0 -44
- package/lib/tui/components/detail-bar.tsx +0 -46
- package/lib/tui/components/editable-field.tsx +0 -33
- package/lib/tui/components/empty-state.tsx +0 -11
- package/lib/tui/components/gateway-status.tsx +0 -66
- package/lib/tui/components/keymap.tsx +0 -29
- package/lib/tui/components/menu-item.tsx +0 -73
- package/lib/tui/components/menu.tsx +0 -26
- package/lib/tui/components/panel-header.tsx +0 -22
- package/lib/tui/components/readonly-field.tsx +0 -18
- package/lib/tui/components/section-header.tsx +0 -25
- package/lib/tui/components/selection-accent.tsx +0 -32
- package/lib/tui/components/session-item.tsx +0 -33
- package/lib/tui/components/session-list.tsx +0 -33
- package/lib/tui/components/ui/hascii/accordion-item.tsx +0 -88
- package/lib/tui/components/ui/hascii/accordion.tsx +0 -96
- package/lib/tui/components/ui/hascii/alert-dialog.tsx +0 -43
- package/lib/tui/components/ui/hascii/badge.tsx +0 -51
- package/lib/tui/components/ui/hascii/breadcrumb.tsx +0 -58
- package/lib/tui/components/ui/hascii/button.tsx +0 -194
- package/lib/tui/components/ui/hascii/card-content.tsx +0 -14
- package/lib/tui/components/ui/hascii/card-description.tsx +0 -13
- package/lib/tui/components/ui/hascii/card-footer.tsx +0 -14
- package/lib/tui/components/ui/hascii/card-header.tsx +0 -14
- package/lib/tui/components/ui/hascii/card-title.tsx +0 -13
- package/lib/tui/components/ui/hascii/card.tsx +0 -27
- package/lib/tui/components/ui/hascii/checkbox.tsx +0 -65
- package/lib/tui/components/ui/hascii/command.tsx +0 -159
- package/lib/tui/components/ui/hascii/dialog-content.tsx +0 -14
- package/lib/tui/components/ui/hascii/dialog-description.tsx +0 -13
- package/lib/tui/components/ui/hascii/dialog-footer.tsx +0 -14
- package/lib/tui/components/ui/hascii/dialog-header.tsx +0 -14
- package/lib/tui/components/ui/hascii/dialog-title.tsx +0 -13
- package/lib/tui/components/ui/hascii/dialog.tsx +0 -27
- package/lib/tui/components/ui/hascii/file-tree.tsx +0 -142
- package/lib/tui/components/ui/hascii/focus-group.tsx +0 -62
- package/lib/tui/components/ui/hascii/form-item.tsx +0 -43
- package/lib/tui/components/ui/hascii/input-otp.tsx +0 -86
- package/lib/tui/components/ui/hascii/input.tsx +0 -130
- package/lib/tui/components/ui/hascii/pagination.tsx +0 -105
- package/lib/tui/components/ui/hascii/progress.tsx +0 -28
- package/lib/tui/components/ui/hascii/select.tsx +0 -131
- package/lib/tui/components/ui/hascii/separator.tsx +0 -35
- package/lib/tui/components/ui/hascii/sidebar-content.tsx +0 -23
- package/lib/tui/components/ui/hascii/sidebar-header.tsx +0 -14
- package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +0 -67
- package/lib/tui/components/ui/hascii/sidebar.tsx +0 -24
- package/lib/tui/components/ui/hascii/skeleton.tsx +0 -60
- package/lib/tui/components/ui/hascii/slider.tsx +0 -91
- package/lib/tui/components/ui/hascii/snackbar.tsx +0 -75
- package/lib/tui/components/ui/hascii/sparkline.tsx +0 -53
- package/lib/tui/components/ui/hascii/spinner.tsx +0 -47
- package/lib/tui/components/ui/hascii/stepper.tsx +0 -54
- package/lib/tui/components/ui/hascii/switch.tsx +0 -66
- package/lib/tui/components/ui/hascii/table.tsx +0 -95
- package/lib/tui/components/ui/hascii/tabs.tsx +0 -59
- package/lib/tui/components/ui/hascii/toggle-group-item.tsx +0 -45
- package/lib/tui/components/ui/hascii/toggle-group.tsx +0 -99
- package/lib/tui/components/ui/hascii/tree.tsx +0 -104
- package/lib/tui/components/view-shell.tsx +0 -44
- package/lib/tui/filter-input.tsx +0 -33
- package/lib/tui/hooks/hascii/use-pressable.ts +0 -54
- package/lib/tui/parse-comma-list.ts +0 -14
- package/lib/tui/profile-launcher.tsx +0 -61
- package/lib/tui/scrollbar-options.ts +0 -19
- package/lib/tui/sidebar.tsx +0 -50
- package/lib/tui/theme.ts +0 -40
- package/lib/tui/tui.tsx +0 -20
- package/lib/tui/types.ts +0 -38
- package/lib/tui/unique-name.ts +0 -18
- package/lib/tui/use-event-stream.ts +0 -133
- package/lib/tui/use-snapshot.ts +0 -99
- package/lib/tui/utils/hascii/form-item-context.tsx +0 -23
- package/lib/tui/utils/hascii/input-focus-context.tsx +0 -31
- package/lib/tui/utils/hascii/theme-context.tsx +0 -26
- package/lib/tui/utils/hascii/theme.ts +0 -176
- package/lib/tui/views/channels-view.tsx +0 -108
- package/lib/tui/views/connectors-view.tsx +0 -164
- package/lib/tui/views/events-view.tsx +0 -160
- package/lib/tui/views/listeners-view.tsx +0 -80
- package/lib/tui/views/profiles-view.tsx +0 -152
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { AddRow } from "@/tui/components/add-row"
|
|
3
|
-
import { Card } from "@/tui/components/card"
|
|
4
|
-
import { EditableField } from "@/tui/components/editable-field"
|
|
5
|
-
import { EmptyState } from "@/tui/components/empty-state"
|
|
6
|
-
import { PanelHeader } from "@/tui/components/panel-header"
|
|
7
|
-
import { ReadonlyField } from "@/tui/components/readonly-field"
|
|
8
|
-
import { ViewShell } from "@/tui/components/view-shell"
|
|
9
|
-
import type { Snapshot } from "@/tui/types"
|
|
10
|
-
import { uniqueName } from "@/tui/unique-name"
|
|
11
|
-
import type { Funnel } from "@/funnel"
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
snapshot: Snapshot
|
|
15
|
-
funnel: Funnel
|
|
16
|
-
refresh: () => void
|
|
17
|
-
focusedKey: string | null
|
|
18
|
-
setFocusedKey: (key: string | null) => void
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type Channel = Snapshot["channels"][number]
|
|
22
|
-
|
|
23
|
-
const fieldKey = (name: string, field: string): string => `channels::${name}::${field}`
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Channel inspector — one Card per channel. Connectors live nested inside the
|
|
27
|
-
* channel and are managed in the connectors view; here only the channel's
|
|
28
|
-
* name and id (read-only) are shown along with a count of nested connectors.
|
|
29
|
-
*/
|
|
30
|
-
export function ChannelsView(props: Props) {
|
|
31
|
-
const channels = props.snapshot.channels
|
|
32
|
-
|
|
33
|
-
const commit = (channel: Channel, field: string, raw: string): void => {
|
|
34
|
-
try {
|
|
35
|
-
if (field === "name") {
|
|
36
|
-
const next = raw.trim()
|
|
37
|
-
|
|
38
|
-
if (next && next !== channel.name) props.funnel.channels.rename(channel.name, next)
|
|
39
|
-
}
|
|
40
|
-
} catch (error) {
|
|
41
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
props.setFocusedKey(null)
|
|
45
|
-
props.refresh()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const removeChannel = (name: string): void => {
|
|
49
|
-
try {
|
|
50
|
-
props.funnel.channels.remove(name)
|
|
51
|
-
} catch (error) {
|
|
52
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
props.setFocusedKey(null)
|
|
56
|
-
props.refresh()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const addChannel = (): void => {
|
|
60
|
-
const name = uniqueName(
|
|
61
|
-
channels.map((c) => c.name),
|
|
62
|
-
"channel",
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const created = props.funnel.channels.add({ name })
|
|
67
|
-
props.setFocusedKey(fieldKey(created.name, "name"))
|
|
68
|
-
} catch (error) {
|
|
69
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
props.refresh()
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<ViewShell>
|
|
77
|
-
<PanelHeader label="channels" count={channels.length} />
|
|
78
|
-
|
|
79
|
-
{channels.length === 0 ? (
|
|
80
|
-
<EmptyState message="(none — use the button below to add one)" />
|
|
81
|
-
) : (
|
|
82
|
-
channels.map((channel) => (
|
|
83
|
-
<Card key={channel.id} title={channel.name} onDelete={() => removeChannel(channel.name)}>
|
|
84
|
-
<EditableField
|
|
85
|
-
label="name"
|
|
86
|
-
initialValue={channel.name}
|
|
87
|
-
focused={props.focusedKey === fieldKey(channel.name, "name")}
|
|
88
|
-
onFocus={() => props.setFocusedKey(fieldKey(channel.name, "name"))}
|
|
89
|
-
onCommit={(raw) => commit(channel, "name", raw)}
|
|
90
|
-
/>
|
|
91
|
-
<ReadonlyField label="id" value={channel.id} />
|
|
92
|
-
<ReadonlyField label="delivery" value={channel.delivery} />
|
|
93
|
-
<ReadonlyField
|
|
94
|
-
label="connectors"
|
|
95
|
-
value={
|
|
96
|
-
channel.connectors.length > 0
|
|
97
|
-
? channel.connectors.map((c) => `${c.name}:${c.type}`).join(", ")
|
|
98
|
-
: "(none)"
|
|
99
|
-
}
|
|
100
|
-
/>
|
|
101
|
-
</Card>
|
|
102
|
-
))
|
|
103
|
-
)}
|
|
104
|
-
|
|
105
|
-
<AddRow label="add channel" onClick={addChannel} />
|
|
106
|
-
</ViewShell>
|
|
107
|
-
)
|
|
108
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { AddRow } from "@/tui/components/add-row"
|
|
2
|
-
import { Card } from "@/tui/components/card"
|
|
3
|
-
import { EmptyState } from "@/tui/components/empty-state"
|
|
4
|
-
import { PanelHeader } from "@/tui/components/panel-header"
|
|
5
|
-
import { ReadonlyField } from "@/tui/components/readonly-field"
|
|
6
|
-
import { HasciiButton } from "@/tui/components/ui/hascii/button"
|
|
7
|
-
import { ViewShell } from "@/tui/components/view-shell"
|
|
8
|
-
import { funnel } from "@/tui/theme"
|
|
9
|
-
import type { Snapshot } from "@/tui/types"
|
|
10
|
-
import { uniqueName } from "@/tui/unique-name"
|
|
11
|
-
import type { Funnel } from "@/funnel"
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
snapshot: Snapshot
|
|
15
|
-
funnel: Funnel
|
|
16
|
-
refresh: () => void
|
|
17
|
-
focusedKey: string | null
|
|
18
|
-
setFocusedKey: (key: string | null) => void
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type Connector = Snapshot["connectors"][number]
|
|
22
|
-
type ConnectorType = Connector["type"]
|
|
23
|
-
|
|
24
|
-
const formatTimestamp = (iso: string | undefined): string => {
|
|
25
|
-
if (!iso) return "—"
|
|
26
|
-
|
|
27
|
-
const d = new Date(iso)
|
|
28
|
-
|
|
29
|
-
if (Number.isNaN(d.getTime())) return "—"
|
|
30
|
-
|
|
31
|
-
const yyyy = d.getFullYear()
|
|
32
|
-
const mm = String(d.getMonth() + 1).padStart(2, "0")
|
|
33
|
-
const dd = String(d.getDate()).padStart(2, "0")
|
|
34
|
-
const hh = String(d.getHours()).padStart(2, "0")
|
|
35
|
-
const mn = String(d.getMinutes()).padStart(2, "0")
|
|
36
|
-
|
|
37
|
-
return `${yyyy}-${mm}-${dd} ${hh}:${mn}`
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Channel-scoped connector inspector. Reads `funnel.channels.listAllConnectors()`
|
|
42
|
-
* (already flattened with channelName / channelId tags) and lets the user delete
|
|
43
|
-
* each connector or quickly add a new one to the first available channel via the
|
|
44
|
-
* AddRow buttons. Editing values is intentionally read-only — token / pollInterval
|
|
45
|
-
* mutation belongs to `fnl channels <ch> connectors set <conn> ...` because the
|
|
46
|
-
* same connector name can exist in multiple channels and inline edits would have
|
|
47
|
-
* to disambiguate.
|
|
48
|
-
*/
|
|
49
|
-
export function ConnectorsView(props: Props) {
|
|
50
|
-
const connectors = props.snapshot.connectors
|
|
51
|
-
const channels = props.snapshot.channels
|
|
52
|
-
const targetChannel = channels[0] ?? null
|
|
53
|
-
|
|
54
|
-
const logError = (error: unknown): void => {
|
|
55
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const removeConnector = (connector: Connector): void => {
|
|
59
|
-
props.funnel.listeners.stop(connector.channelName, connector.name).catch(logError)
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
props.funnel.channels.removeConnector(connector.channelName, connector.name)
|
|
63
|
-
} catch (error) {
|
|
64
|
-
logError(error)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
props.refresh()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const addConnector = (type: ConnectorType): void => {
|
|
71
|
-
if (!targetChannel) {
|
|
72
|
-
logError(new Error("add a channel first before creating a connector"))
|
|
73
|
-
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const existingNames = connectors
|
|
78
|
-
.filter((c) => c.channelId === targetChannel.id)
|
|
79
|
-
.map((c) => c.name)
|
|
80
|
-
const name = uniqueName(existingNames, type)
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
if (type === "slack") {
|
|
84
|
-
props.funnel.channels.addConnector(targetChannel.name, {
|
|
85
|
-
type: "slack",
|
|
86
|
-
name,
|
|
87
|
-
botToken: "xoxb-PLACEHOLDER",
|
|
88
|
-
appToken: "xapp-PLACEHOLDER",
|
|
89
|
-
})
|
|
90
|
-
} else if (type === "gh") {
|
|
91
|
-
props.funnel.channels.addConnector(targetChannel.name, { type: "gh", name })
|
|
92
|
-
} else if (type === "discord") {
|
|
93
|
-
props.funnel.channels.addConnector(targetChannel.name, {
|
|
94
|
-
type: "discord",
|
|
95
|
-
name,
|
|
96
|
-
botToken: "PLACEHOLDER-PLACEHOLDER",
|
|
97
|
-
})
|
|
98
|
-
} else {
|
|
99
|
-
props.funnel.channels.addConnector(targetChannel.name, { type: "schedule", name })
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
props.funnel.listeners.start(targetChannel.name, name).catch(logError)
|
|
103
|
-
} catch (error) {
|
|
104
|
-
logError(error)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
props.refresh()
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<ViewShell>
|
|
112
|
-
<PanelHeader label="connectors" count={connectors.length} />
|
|
113
|
-
|
|
114
|
-
{connectors.length === 0 ? (
|
|
115
|
-
<EmptyState message="(none — add via the buttons below or `fnl channels <ch> connectors add ...`)" />
|
|
116
|
-
) : (
|
|
117
|
-
connectors.map((connector) => (
|
|
118
|
-
<Card key={`${connector.channelId}::${connector.id}`} title={connector.name}>
|
|
119
|
-
<ReadonlyField label="channel" value={connector.channelName} />
|
|
120
|
-
<ReadonlyField label="type" value={connector.type} />
|
|
121
|
-
<ReadonlyField label="id" value={connector.id} />
|
|
122
|
-
{connector.type === "slack" ? (
|
|
123
|
-
<>
|
|
124
|
-
<ReadonlyField label="bot-token" value={connector.botToken} />
|
|
125
|
-
<ReadonlyField label="app-token" value={connector.appToken} />
|
|
126
|
-
</>
|
|
127
|
-
) : null}
|
|
128
|
-
{connector.type === "gh" ? (
|
|
129
|
-
<ReadonlyField label="poll" value={String(connector.pollInterval ?? 60)} />
|
|
130
|
-
) : null}
|
|
131
|
-
{connector.type === "discord" ? (
|
|
132
|
-
<ReadonlyField label="bot-token" value={connector.botToken} />
|
|
133
|
-
) : null}
|
|
134
|
-
{connector.type === "schedule" ? (
|
|
135
|
-
<ReadonlyField label="entries" value={String(connector.entries.length)} />
|
|
136
|
-
) : null}
|
|
137
|
-
<text fg={funnel.faint}>{`created ${formatTimestamp(connector.createdAt)}`}</text>
|
|
138
|
-
<box style={{ flexDirection: "row", justifyContent: "space-between" }}>
|
|
139
|
-
<text fg={funnel.faint}>{`updated ${formatTimestamp(connector.updatedAt)}`}</text>
|
|
140
|
-
<HasciiButton
|
|
141
|
-
variant="destructive"
|
|
142
|
-
size="sm"
|
|
143
|
-
onPress={() => removeConnector(connector)}
|
|
144
|
-
>
|
|
145
|
-
delete
|
|
146
|
-
</HasciiButton>
|
|
147
|
-
</box>
|
|
148
|
-
</Card>
|
|
149
|
-
))
|
|
150
|
-
)}
|
|
151
|
-
|
|
152
|
-
{targetChannel ? (
|
|
153
|
-
<text fg={funnel.faint}>{`add target channel: ${targetChannel.name}`}</text>
|
|
154
|
-
) : (
|
|
155
|
-
<text fg={funnel.warn}>add a channel first to enable the buttons below</text>
|
|
156
|
-
)}
|
|
157
|
-
|
|
158
|
-
<AddRow label="add slack" onClick={() => addConnector("slack")} />
|
|
159
|
-
<AddRow label="add gh" onClick={() => addConnector("gh")} />
|
|
160
|
-
<AddRow label="add discord" onClick={() => addConnector("discord")} />
|
|
161
|
-
<AddRow label="add schedule" onClick={() => addConnector("schedule")} />
|
|
162
|
-
</ViewShell>
|
|
163
|
-
)
|
|
164
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { DetailBar } from "@/tui/components/detail-bar"
|
|
3
|
-
import { HasciiSeparator } from "@/tui/components/ui/hascii/separator"
|
|
4
|
-
import { EmptyState } from "@/tui/components/empty-state"
|
|
5
|
-
import { Keymap } from "@/tui/components/keymap"
|
|
6
|
-
import { PanelHeader } from "@/tui/components/panel-header"
|
|
7
|
-
import { ViewShell } from "@/tui/components/view-shell"
|
|
8
|
-
import { funnel } from "@/tui/theme"
|
|
9
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
10
|
-
import type { StreamEvent, StreamStatus } from "@/tui/types"
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
events: StreamEvent[]
|
|
14
|
-
filter: string
|
|
15
|
-
selectedIndex: number
|
|
16
|
-
streamStatus: StreamStatus
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const streamLabel = (status: StreamStatus): string => {
|
|
20
|
-
if (status === "open") return "live"
|
|
21
|
-
if (status === "connecting") return "connecting…"
|
|
22
|
-
if (status === "closed") return "reconnecting…"
|
|
23
|
-
|
|
24
|
-
return "offline"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const formatTime = (ms: number): string => {
|
|
28
|
-
const date = new Date(ms)
|
|
29
|
-
const hh = String(date.getHours()).padStart(2, "0")
|
|
30
|
-
const mm = String(date.getMinutes()).padStart(2, "0")
|
|
31
|
-
const ss = String(date.getSeconds()).padStart(2, "0")
|
|
32
|
-
|
|
33
|
-
return `${hh}:${mm}:${ss}`
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const truncate = (value: string, max: number): string => {
|
|
37
|
-
const flat = value.replace(/\s+/g, " ").trim()
|
|
38
|
-
|
|
39
|
-
if (flat.length <= max) return flat
|
|
40
|
-
|
|
41
|
-
return `${flat.slice(0, max - 1)}…`
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const matches = (event: StreamEvent, filter: string): boolean => {
|
|
45
|
-
if (!filter) return true
|
|
46
|
-
|
|
47
|
-
const needle = filter.toLowerCase()
|
|
48
|
-
const haystack = [
|
|
49
|
-
event.content,
|
|
50
|
-
event.meta.connector ?? "",
|
|
51
|
-
event.meta.event_type ?? "",
|
|
52
|
-
event.meta.channel ?? "",
|
|
53
|
-
]
|
|
54
|
-
.join(" ")
|
|
55
|
-
.toLowerCase()
|
|
56
|
-
|
|
57
|
-
return haystack.includes(needle)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const tryParseJson = (value: string): unknown => {
|
|
61
|
-
try {
|
|
62
|
-
return JSON.parse(value)
|
|
63
|
-
} catch {
|
|
64
|
-
return value
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const formatJson = (value: unknown): string => {
|
|
69
|
-
try {
|
|
70
|
-
return JSON.stringify(value, null, 2)
|
|
71
|
-
} catch {
|
|
72
|
-
return String(value)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Live event stream + detail of the selected event.
|
|
78
|
-
*
|
|
79
|
-
* The events list lives inside `ViewShell` (padded canvas) while the
|
|
80
|
-
* detail strip is a sibling `DetailBar` so its background spans the
|
|
81
|
-
* full main column edge-to-edge and reads as a distinct elevated
|
|
82
|
-
* stratum below the list.
|
|
83
|
-
*/
|
|
84
|
-
export function EventsView(props: Props) {
|
|
85
|
-
const theme = useHasciiTheme()
|
|
86
|
-
const visible = props.events.filter((event) => matches(event, props.filter))
|
|
87
|
-
const selected = visible[props.selectedIndex] ?? null
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<box style={{ flexDirection: "column", flexGrow: 1 }}>
|
|
91
|
-
<ViewShell>
|
|
92
|
-
<PanelHeader
|
|
93
|
-
label="events"
|
|
94
|
-
count={visible.length}
|
|
95
|
-
hint={[
|
|
96
|
-
streamLabel(props.streamStatus),
|
|
97
|
-
`${props.events.length} total`,
|
|
98
|
-
props.filter ? `/${props.filter}/` : null,
|
|
99
|
-
]
|
|
100
|
-
.filter((part): part is string => part !== null)
|
|
101
|
-
.join(" · ")}
|
|
102
|
-
/>
|
|
103
|
-
|
|
104
|
-
{visible.length === 0 ? (
|
|
105
|
-
<EmptyState message="(no events yet — waiting for the first one)" />
|
|
106
|
-
) : (
|
|
107
|
-
visible.map((event, index) => {
|
|
108
|
-
const isSelected = index === props.selectedIndex
|
|
109
|
-
const connector = event.meta.connector ?? "system"
|
|
110
|
-
const eventType = event.meta.event_type ?? "?"
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<text key={event.id} bg={isSelected ? theme.color.muted : undefined}>
|
|
114
|
-
<span fg={theme.color.mutedForeground}>{formatTime(event.receivedAt)}</span>
|
|
115
|
-
<span fg={funnel.faint}> </span>
|
|
116
|
-
<span fg={theme.color.mutedForeground}>{eventType.padEnd(8)}</span>
|
|
117
|
-
<span fg={funnel.faint}>{" · "}</span>
|
|
118
|
-
<span fg={isSelected ? theme.color.foreground : theme.color.foreground}>
|
|
119
|
-
{connector.padEnd(14)}
|
|
120
|
-
</span>
|
|
121
|
-
<span fg={funnel.faint}> </span>
|
|
122
|
-
<span fg={isSelected ? theme.color.foreground : theme.color.mutedForeground}>
|
|
123
|
-
{truncate(event.content, 80)}
|
|
124
|
-
</span>
|
|
125
|
-
</text>
|
|
126
|
-
)
|
|
127
|
-
})
|
|
128
|
-
)}
|
|
129
|
-
|
|
130
|
-
<Keymap
|
|
131
|
-
hints={[
|
|
132
|
-
{ key: "j/k", label: "select" },
|
|
133
|
-
{ key: "/", label: "filter" },
|
|
134
|
-
]}
|
|
135
|
-
/>
|
|
136
|
-
</ViewShell>
|
|
137
|
-
|
|
138
|
-
<DetailBar>
|
|
139
|
-
<PanelHeader label="detail" />
|
|
140
|
-
|
|
141
|
-
{!selected ? (
|
|
142
|
-
<EmptyState message="(select an event with j/k to inspect)" />
|
|
143
|
-
) : (
|
|
144
|
-
<>
|
|
145
|
-
<text>
|
|
146
|
-
<span fg={theme.color.mutedForeground}>meta: </span>
|
|
147
|
-
<span fg={theme.color.foreground}>
|
|
148
|
-
{Object.entries(selected.meta)
|
|
149
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
150
|
-
.join(" ")}
|
|
151
|
-
</span>
|
|
152
|
-
</text>
|
|
153
|
-
<HasciiSeparator />
|
|
154
|
-
<text fg={theme.color.foreground}>{formatJson(tryParseJson(selected.content))}</text>
|
|
155
|
-
</>
|
|
156
|
-
)}
|
|
157
|
-
</DetailBar>
|
|
158
|
-
</box>
|
|
159
|
-
)
|
|
160
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { Card } from "@/tui/components/card"
|
|
3
|
-
import { EmptyState } from "@/tui/components/empty-state"
|
|
4
|
-
import { Keymap } from "@/tui/components/keymap"
|
|
5
|
-
import { PanelHeader } from "@/tui/components/panel-header"
|
|
6
|
-
import { ViewShell } from "@/tui/components/view-shell"
|
|
7
|
-
import { funnel } from "@/tui/theme"
|
|
8
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
9
|
-
import type { Snapshot, StreamEvent } from "@/tui/types"
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
snapshot: Snapshot
|
|
13
|
-
events: StreamEvent[]
|
|
14
|
-
selectedIndex: number
|
|
15
|
-
busy: boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const eventCountBy = (events: StreamEvent[], connectorName: string): number => {
|
|
19
|
-
let count = 0
|
|
20
|
-
|
|
21
|
-
for (const event of events) {
|
|
22
|
-
if (event.meta.connector === connectorName) count += 1
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return count
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Listener registry — one Card per listener. The Card title shows the
|
|
30
|
-
* listener's name; inside, a single status line carries the alive
|
|
31
|
-
* dot, the connector type, and the event count. Cursor selection is
|
|
32
|
-
* shown via the Card's `selected` accent. Listeners are runtime
|
|
33
|
-
* entities derived from connectors, so there is no add path here —
|
|
34
|
-
* register / remove a connector instead.
|
|
35
|
-
*/
|
|
36
|
-
export function ListenersView(props: Props) {
|
|
37
|
-
const theme = useHasciiTheme()
|
|
38
|
-
const listeners = props.snapshot.listeners
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<ViewShell>
|
|
42
|
-
<PanelHeader
|
|
43
|
-
label="listeners"
|
|
44
|
-
count={listeners.length}
|
|
45
|
-
hint={props.busy ? "working…" : undefined}
|
|
46
|
-
/>
|
|
47
|
-
|
|
48
|
-
{!props.snapshot.daemonReachable ? (
|
|
49
|
-
<EmptyState message="(gateway daemon offline — press G to start it)" />
|
|
50
|
-
) : listeners.length === 0 ? (
|
|
51
|
-
<EmptyState message="(no listeners — register a connector first)" />
|
|
52
|
-
) : (
|
|
53
|
-
listeners.map((entry, index) => {
|
|
54
|
-
const aliveColor = entry.alive ? funnel.alive : funnel.dead
|
|
55
|
-
const count = eventCountBy(props.events, entry.name)
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<Card key={entry.name} title={entry.name} selected={index === props.selectedIndex}>
|
|
59
|
-
<text>
|
|
60
|
-
<span fg={aliveColor}>{entry.alive ? "●" : "○"}</span>
|
|
61
|
-
<span fg={funnel.faint}> </span>
|
|
62
|
-
<span fg={theme.color.mutedForeground}>{entry.type}</span>
|
|
63
|
-
{count > 0 ? <span fg={theme.color.mutedForeground}>{` ${count}↓`}</span> : null}
|
|
64
|
-
</text>
|
|
65
|
-
</Card>
|
|
66
|
-
)
|
|
67
|
-
})
|
|
68
|
-
)}
|
|
69
|
-
|
|
70
|
-
<Keymap
|
|
71
|
-
hints={[
|
|
72
|
-
{ key: "j/k", label: "select" },
|
|
73
|
-
{ key: "s", label: "start" },
|
|
74
|
-
{ key: "x", label: "stop" },
|
|
75
|
-
{ key: "R", label: "restart" },
|
|
76
|
-
]}
|
|
77
|
-
/>
|
|
78
|
-
</ViewShell>
|
|
79
|
-
)
|
|
80
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { AddRow } from "@/tui/components/add-row"
|
|
3
|
-
import { Card } from "@/tui/components/card"
|
|
4
|
-
import { EditableField } from "@/tui/components/editable-field"
|
|
5
|
-
import { EmptyState } from "@/tui/components/empty-state"
|
|
6
|
-
import { Keymap } from "@/tui/components/keymap"
|
|
7
|
-
import { PanelHeader } from "@/tui/components/panel-header"
|
|
8
|
-
import { ViewShell } from "@/tui/components/view-shell"
|
|
9
|
-
import type { Snapshot } from "@/tui/types"
|
|
10
|
-
import { uniqueName } from "@/tui/unique-name"
|
|
11
|
-
import type { Funnel } from "@/funnel"
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
snapshot: Snapshot
|
|
15
|
-
selectedIndex: number
|
|
16
|
-
funnel: Funnel
|
|
17
|
-
refresh: () => void
|
|
18
|
-
focusedKey: string | null
|
|
19
|
-
setFocusedKey: (key: string | null) => void
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type Profile = Snapshot["profiles"][number]
|
|
23
|
-
|
|
24
|
-
const fieldKey = (name: string, field: string): string => `profiles::${name}::${field}`
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Profile list — one Card per profile. Selection (j/k cursor) shows the
|
|
28
|
-
* `▏` primary rule via the Card's `selected` prop; pressing `c`
|
|
29
|
-
* launches Claude Code with the selected profile.
|
|
30
|
-
*
|
|
31
|
-
* `+ add profile` at the foot creates a new profile pointed at the
|
|
32
|
-
* first existing channel (or an empty string if there are none, which
|
|
33
|
-
* the user must then edit before launching).
|
|
34
|
-
*/
|
|
35
|
-
export function ProfilesView(props: Props) {
|
|
36
|
-
const profiles = props.snapshot.profiles
|
|
37
|
-
const channels = props.snapshot.channels
|
|
38
|
-
|
|
39
|
-
const commit = (profile: Profile, field: string, raw: string): void => {
|
|
40
|
-
try {
|
|
41
|
-
if (field === "name") {
|
|
42
|
-
const next = raw.trim()
|
|
43
|
-
|
|
44
|
-
if (next && next !== profile.name) props.funnel.profiles.rename(profile.name, next)
|
|
45
|
-
} else if (field === "channel") {
|
|
46
|
-
const next = raw.trim()
|
|
47
|
-
|
|
48
|
-
if (next) props.funnel.profiles.update(profile.name, { channelId: next })
|
|
49
|
-
} else if (field === "path") {
|
|
50
|
-
const next = raw.trim()
|
|
51
|
-
|
|
52
|
-
if (next) props.funnel.profiles.update(profile.name, { path: next })
|
|
53
|
-
} else if (field === "sub-agent") {
|
|
54
|
-
const next = raw.trim()
|
|
55
|
-
|
|
56
|
-
if (next) props.funnel.profiles.update(profile.name, { subAgent: next })
|
|
57
|
-
}
|
|
58
|
-
} catch (error) {
|
|
59
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
props.setFocusedKey(null)
|
|
63
|
-
props.refresh()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const removeProfile = (name: string): void => {
|
|
67
|
-
try {
|
|
68
|
-
props.funnel.profiles.remove(name)
|
|
69
|
-
} catch (error) {
|
|
70
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
props.setFocusedKey(null)
|
|
74
|
-
props.refresh()
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const addProfile = (): void => {
|
|
78
|
-
const name = uniqueName(
|
|
79
|
-
profiles.map((p) => p.name),
|
|
80
|
-
"profile",
|
|
81
|
-
)
|
|
82
|
-
const channelId = channels[0]?.name ?? ""
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
props.funnel.profiles.add({ name, path: "", subAgent: "", channelId })
|
|
86
|
-
props.setFocusedKey(fieldKey(name, "name"))
|
|
87
|
-
} catch (error) {
|
|
88
|
-
props.funnel.logger.error(error instanceof Error ? error.message : String(error))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
props.refresh()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<ViewShell>
|
|
96
|
-
<PanelHeader label="profiles" count={profiles.length} />
|
|
97
|
-
|
|
98
|
-
{profiles.length === 0 ? (
|
|
99
|
-
<EmptyState message="(none — use the button below to add one)" />
|
|
100
|
-
) : (
|
|
101
|
-
profiles.map((profile, index) => (
|
|
102
|
-
<Card
|
|
103
|
-
key={profile.name}
|
|
104
|
-
title={profile.name}
|
|
105
|
-
selected={index === props.selectedIndex}
|
|
106
|
-
onDelete={() => removeProfile(profile.name)}
|
|
107
|
-
>
|
|
108
|
-
<EditableField
|
|
109
|
-
label="name"
|
|
110
|
-
initialValue={profile.name}
|
|
111
|
-
focused={props.focusedKey === fieldKey(profile.name, "name")}
|
|
112
|
-
onFocus={() => props.setFocusedKey(fieldKey(profile.name, "name"))}
|
|
113
|
-
onCommit={(raw) => commit(profile, "name", raw)}
|
|
114
|
-
/>
|
|
115
|
-
<EditableField
|
|
116
|
-
label="path"
|
|
117
|
-
initialValue={profile.path}
|
|
118
|
-
focused={props.focusedKey === fieldKey(profile.name, "path")}
|
|
119
|
-
onFocus={() => props.setFocusedKey(fieldKey(profile.name, "path"))}
|
|
120
|
-
onCommit={(raw) => commit(profile, "path", raw)}
|
|
121
|
-
placeholder="repository path"
|
|
122
|
-
/>
|
|
123
|
-
<EditableField
|
|
124
|
-
label="sub-agent"
|
|
125
|
-
initialValue={profile.subAgent}
|
|
126
|
-
focused={props.focusedKey === fieldKey(profile.name, "sub-agent")}
|
|
127
|
-
onFocus={() => props.setFocusedKey(fieldKey(profile.name, "sub-agent"))}
|
|
128
|
-
onCommit={(raw) => commit(profile, "sub-agent", raw)}
|
|
129
|
-
placeholder="claude --agent value"
|
|
130
|
-
/>
|
|
131
|
-
<EditableField
|
|
132
|
-
label="channel"
|
|
133
|
-
initialValue={profile.channelId}
|
|
134
|
-
focused={props.focusedKey === fieldKey(profile.name, "channel")}
|
|
135
|
-
onFocus={() => props.setFocusedKey(fieldKey(profile.name, "channel"))}
|
|
136
|
-
onCommit={(raw) => commit(profile, "channel", raw)}
|
|
137
|
-
/>
|
|
138
|
-
</Card>
|
|
139
|
-
))
|
|
140
|
-
)}
|
|
141
|
-
|
|
142
|
-
<AddRow label="add profile" onClick={addProfile} />
|
|
143
|
-
|
|
144
|
-
<Keymap
|
|
145
|
-
hints={[
|
|
146
|
-
{ key: "j/k", label: "select" },
|
|
147
|
-
{ key: "c", label: "launch" },
|
|
148
|
-
]}
|
|
149
|
-
/>
|
|
150
|
-
</ViewShell>
|
|
151
|
-
)
|
|
152
|
-
}
|