byterover-cli 0.3.5 → 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.
- package/README.md +119 -63
- package/bin/dev.js +8 -1
- package/bin/run.js +7 -0
- package/dist/commands/cipher-agent/run.d.ts +30 -0
- package/dist/commands/cipher-agent/run.js +123 -61
- package/dist/commands/cipher-agent/set-prompt.d.ts +2 -0
- package/dist/commands/cipher-agent/set-prompt.js +13 -8
- package/dist/commands/cipher-agent/show-prompt.d.ts +2 -0
- package/dist/commands/cipher-agent/show-prompt.js +17 -12
- package/dist/commands/curate.d.ts +3 -60
- package/dist/commands/curate.js +45 -341
- package/dist/commands/foo.d.ts +4 -2
- package/dist/commands/foo.js +21 -16
- package/dist/commands/main.d.ts +9 -0
- package/dist/commands/main.js +34 -0
- package/dist/commands/query.d.ts +2 -48
- package/dist/commands/query.js +19 -287
- package/dist/commands/status.d.ts +2 -13
- package/dist/commands/status.js +12 -91
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +23 -19
- package/dist/config/environment.d.ts +1 -1
- package/dist/config/environment.js +2 -2
- package/dist/constants.d.ts +4 -5
- package/dist/constants.js +5 -5
- package/dist/core/domain/cipher/errors/storage-error.d.ts +89 -0
- package/dist/core/domain/cipher/errors/storage-error.js +130 -0
- package/dist/core/domain/cipher/queue/types.d.ts +71 -0
- package/dist/core/domain/cipher/queue/types.js +9 -0
- package/dist/core/domain/cipher/storage/message-storage-types.d.ts +218 -0
- package/dist/core/domain/cipher/storage/message-storage-types.js +18 -0
- package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
- package/dist/core/domain/cipher/tools/constants.js +1 -0
- package/dist/core/domain/entities/event.d.ts +1 -1
- package/dist/core/domain/entities/event.js +8 -1
- package/dist/core/domain/entities/global-config.d.ts +36 -0
- package/dist/core/domain/entities/global-config.js +66 -0
- package/dist/core/domain/knowledge/directory-manager.d.ts +10 -0
- package/dist/core/domain/knowledge/directory-manager.js +18 -0
- package/dist/core/domain/knowledge/markdown-writer.d.ts +9 -0
- package/dist/core/domain/knowledge/markdown-writer.js +51 -1
- package/dist/core/interfaces/cipher/i-agent-storage.d.ts +152 -0
- package/dist/core/interfaces/cipher/i-agent-storage.js +1 -0
- package/dist/core/interfaces/cipher/i-cipher-agent.d.ts +2 -0
- package/dist/core/interfaces/cipher/i-key-storage.d.ts +91 -0
- package/dist/core/interfaces/cipher/i-key-storage.js +1 -0
- package/dist/core/interfaces/i-global-config-store.d.ts +34 -0
- package/dist/core/interfaces/i-global-config-store.js +1 -0
- package/dist/core/interfaces/i-onboarding-preference-store.d.ts +20 -0
- package/dist/core/interfaces/i-onboarding-preference-store.js +1 -0
- package/dist/core/interfaces/i-terminal.d.ts +146 -0
- package/dist/core/interfaces/i-terminal.js +1 -0
- package/dist/core/interfaces/i-workspace-detector-service.d.ts +8 -0
- package/dist/core/interfaces/i-workspace-detector-service.js +1 -0
- package/dist/core/interfaces/usecase/i-clear-use-case.d.ts +6 -0
- package/dist/core/interfaces/usecase/i-clear-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-curate-use-case.d.ts +10 -0
- package/dist/core/interfaces/usecase/i-curate-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-init-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-init-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-login-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-login-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-logout-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-logout-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-pull-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-pull-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-push-use-case.d.ts +6 -0
- package/dist/core/interfaces/usecase/i-push-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-query-use-case.d.ts +9 -0
- package/dist/core/interfaces/usecase/i-query-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-space-list-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-space-list-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-space-switch-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-space-switch-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-status-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-status-use-case.js +1 -0
- package/dist/hooks/init/welcome.js +10 -26
- package/dist/infra/cipher/agent-service-factory.d.ts +13 -6
- package/dist/infra/cipher/agent-service-factory.js +40 -16
- package/dist/infra/cipher/cipher-agent.js +4 -4
- package/dist/infra/cipher/consumer/consumer-lock.d.ts +20 -0
- package/dist/infra/cipher/consumer/consumer-lock.js +40 -0
- package/dist/infra/cipher/consumer/consumer-service.d.ts +99 -0
- package/dist/infra/cipher/consumer/consumer-service.js +165 -0
- package/dist/infra/cipher/consumer/execution-consumer.d.ts +121 -0
- package/dist/infra/cipher/consumer/execution-consumer.js +523 -0
- package/dist/infra/cipher/consumer/index.d.ts +33 -0
- package/dist/infra/cipher/consumer/index.js +33 -0
- package/dist/infra/cipher/consumer/queue-polling-service.d.ts +120 -0
- package/dist/infra/cipher/consumer/queue-polling-service.js +248 -0
- package/dist/infra/cipher/http/internal-llm-http-service.d.ts +94 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +118 -0
- package/dist/infra/cipher/llm/context/compaction/compaction-service.d.ts +106 -0
- package/dist/infra/cipher/llm/context/compaction/compaction-service.js +132 -0
- package/dist/infra/cipher/llm/context/compaction/index.d.ts +9 -0
- package/dist/infra/cipher/llm/context/compaction/index.js +9 -0
- package/dist/infra/cipher/llm/context/context-manager.d.ts +46 -2
- package/dist/infra/cipher/llm/context/context-manager.js +68 -4
- package/dist/infra/cipher/llm/context/rw-lock.d.ts +72 -0
- package/dist/infra/cipher/llm/context/rw-lock.js +145 -0
- package/dist/infra/cipher/llm/generators/byterover-content-generator.d.ts +7 -7
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +8 -8
- package/dist/infra/cipher/llm/internal-llm-service.js +2 -0
- package/dist/infra/cipher/session/session-manager.d.ts +4 -4
- package/dist/infra/cipher/session/session-manager.js +5 -5
- package/dist/infra/cipher/storage/agent-storage.d.ts +246 -0
- package/dist/infra/cipher/storage/agent-storage.js +956 -0
- package/dist/infra/cipher/storage/dual-format-history-storage.d.ts +77 -0
- package/dist/infra/cipher/storage/dual-format-history-storage.js +149 -0
- package/dist/infra/cipher/storage/granular-history-storage.d.ts +65 -0
- package/dist/infra/cipher/storage/granular-history-storage.js +118 -0
- package/dist/infra/cipher/storage/message-storage-service.d.ts +108 -0
- package/dist/infra/cipher/storage/message-storage-service.js +529 -0
- package/dist/infra/cipher/storage/process-utils.d.ts +16 -0
- package/dist/infra/cipher/storage/process-utils.js +43 -0
- package/dist/infra/cipher/storage/sqlite-key-storage.d.ts +105 -0
- package/dist/infra/cipher/storage/sqlite-key-storage.js +404 -0
- package/dist/infra/cipher/system-prompt/simple-prompt-factory.d.ts +1 -0
- package/dist/infra/cipher/system-prompt/simple-prompt-factory.js +7 -0
- package/dist/infra/cipher/tools/default-policy-rules.js +1 -1
- package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +10 -0
- package/dist/infra/cipher/tools/implementations/curate-tool.js +371 -0
- package/dist/infra/cipher/tools/implementations/find-knowledge-topics-tool.js +11 -8
- package/dist/infra/cipher/tools/tool-manager.d.ts +8 -2
- package/dist/infra/cipher/tools/tool-manager.js +29 -2
- package/dist/infra/cipher/tools/tool-registry.js +7 -0
- package/dist/infra/http/authenticated-http-client.d.ts +21 -0
- package/dist/infra/http/authenticated-http-client.js +38 -0
- package/dist/infra/repl/commands/arg-parser.d.ts +97 -0
- package/dist/infra/repl/commands/arg-parser.js +129 -0
- package/dist/infra/repl/commands/clear-command.d.ts +5 -0
- package/dist/infra/repl/commands/clear-command.js +61 -0
- package/dist/infra/repl/commands/curate-command.d.ts +9 -0
- package/dist/infra/repl/commands/curate-command.js +88 -0
- package/dist/infra/repl/commands/gen-rules-command.d.ts +7 -0
- package/dist/infra/repl/commands/gen-rules-command.js +38 -0
- package/dist/infra/repl/commands/index.d.ts +8 -0
- package/dist/infra/repl/commands/index.js +36 -0
- package/dist/infra/repl/commands/init-command.d.ts +7 -0
- package/dist/infra/repl/commands/init-command.js +83 -0
- package/dist/infra/repl/commands/login-command.d.ts +7 -0
- package/dist/infra/repl/commands/login-command.js +50 -0
- package/dist/infra/repl/commands/logout-command.d.ts +5 -0
- package/dist/infra/repl/commands/logout-command.js +48 -0
- package/dist/infra/repl/commands/pull-command.d.ts +5 -0
- package/dist/infra/repl/commands/pull-command.js +61 -0
- package/dist/infra/repl/commands/push-command.d.ts +5 -0
- package/dist/infra/repl/commands/push-command.js +66 -0
- package/dist/infra/repl/commands/query-command.d.ts +5 -0
- package/dist/infra/repl/commands/query-command.js +66 -0
- package/dist/infra/repl/commands/space/index.d.ts +5 -0
- package/dist/infra/repl/commands/space/index.js +14 -0
- package/dist/infra/repl/commands/space/list-command.d.ts +5 -0
- package/dist/infra/repl/commands/space/list-command.js +70 -0
- package/dist/infra/repl/commands/space/switch-command.d.ts +5 -0
- package/dist/infra/repl/commands/space/switch-command.js +37 -0
- package/dist/infra/repl/commands/status-command.d.ts +5 -0
- package/dist/infra/repl/commands/status-command.js +39 -0
- package/dist/infra/repl/repl-startup.d.ts +18 -0
- package/dist/infra/repl/repl-startup.js +28 -0
- package/dist/infra/storage/file-global-config-store.d.ts +22 -0
- package/dist/infra/storage/file-global-config-store.js +65 -0
- package/dist/infra/storage/file-onboarding-preference-store.d.ts +10 -0
- package/dist/infra/storage/file-onboarding-preference-store.js +46 -0
- package/dist/infra/terminal/oclif-terminal.d.ts +19 -0
- package/dist/infra/terminal/oclif-terminal.js +60 -0
- package/dist/infra/terminal/repl-terminal.d.ts +31 -0
- package/dist/infra/terminal/repl-terminal.js +116 -0
- package/dist/infra/tracking/mixpanel-tracking-service.d.ts +11 -1
- package/dist/infra/tracking/mixpanel-tracking-service.js +18 -13
- package/dist/infra/usecase/clear-use-case.d.ts +20 -0
- package/dist/infra/usecase/clear-use-case.js +58 -0
- package/dist/infra/usecase/curate-use-case.d.ts +66 -0
- package/dist/infra/usecase/curate-use-case.js +288 -0
- package/dist/{commands/gen-rules.d.ts → infra/usecase/generate-rules-use-case.d.ts} +14 -20
- package/dist/{commands/gen-rules.js → infra/usecase/generate-rules-use-case.js} +59 -78
- package/dist/infra/usecase/init-use-case.d.ts +139 -0
- package/dist/{commands/init.js → infra/usecase/init-use-case.js} +197 -233
- package/dist/infra/usecase/login-use-case.d.ts +28 -0
- package/dist/infra/usecase/login-use-case.js +94 -0
- package/dist/infra/usecase/logout-use-case.d.ts +22 -0
- package/dist/infra/usecase/logout-use-case.js +51 -0
- package/dist/infra/usecase/pull-use-case.d.ts +35 -0
- package/dist/infra/usecase/pull-use-case.js +89 -0
- package/dist/infra/usecase/push-use-case.d.ts +37 -0
- package/dist/infra/usecase/push-use-case.js +124 -0
- package/dist/infra/usecase/query-use-case.d.ts +78 -0
- package/dist/infra/usecase/query-use-case.js +402 -0
- package/dist/infra/usecase/space-list-use-case.d.ts +27 -0
- package/dist/infra/usecase/space-list-use-case.js +64 -0
- package/dist/infra/usecase/space-switch-use-case.d.ts +36 -0
- package/dist/infra/usecase/space-switch-use-case.js +140 -0
- package/dist/infra/usecase/status-use-case.d.ts +27 -0
- package/dist/infra/usecase/status-use-case.js +97 -0
- package/dist/infra/workspace/workspace-detector-service.d.ts +3 -6
- package/dist/resources/prompts/curate-context-tree-curation.yml +23 -11
- package/dist/resources/prompts/query-context-tree-retrieval.yml +3 -4
- package/dist/resources/prompts/system-prompt.yml +1 -1
- package/dist/resources/prompts/tool-outputs.yml +4 -3
- package/dist/templates/sections/command-reference.md +12 -0
- package/dist/templates/sections/workflow.md +10 -1
- package/dist/tui/app.d.ts +9 -0
- package/dist/tui/app.js +26 -0
- package/dist/tui/components/enter-prompt.d.ts +13 -0
- package/dist/tui/components/enter-prompt.js +15 -0
- package/dist/tui/components/execution/execution-changes.d.ts +14 -0
- package/dist/tui/components/execution/execution-changes.js +15 -0
- package/dist/tui/components/execution/execution-content.d.ts +25 -0
- package/dist/tui/components/execution/execution-content.js +67 -0
- package/dist/tui/components/execution/execution-input.d.ts +12 -0
- package/dist/tui/components/execution/execution-input.js +16 -0
- package/dist/tui/components/execution/execution-progress.d.ts +21 -0
- package/dist/tui/components/execution/execution-progress.js +21 -0
- package/dist/tui/components/execution/execution-status.d.ts +13 -0
- package/dist/tui/components/execution/execution-status.js +19 -0
- package/dist/tui/components/execution/index.d.ts +11 -0
- package/dist/tui/components/execution/index.js +11 -0
- package/dist/tui/components/execution/log-item.d.ts +17 -0
- package/dist/tui/components/execution/log-item.js +25 -0
- package/dist/tui/components/footer.d.ts +5 -0
- package/dist/tui/components/footer.js +12 -0
- package/dist/tui/components/header.d.ts +18 -0
- package/dist/tui/components/header.js +18 -0
- package/dist/tui/components/index.d.ts +17 -0
- package/dist/tui/components/index.js +14 -0
- package/dist/tui/components/inline-prompts/index.d.ts +15 -0
- package/dist/tui/components/inline-prompts/index.js +10 -0
- package/dist/tui/components/inline-prompts/inline-confirm.d.ts +17 -0
- package/dist/tui/components/inline-prompts/inline-confirm.js +32 -0
- package/dist/tui/components/inline-prompts/inline-file-selector.d.ts +43 -0
- package/dist/tui/components/inline-prompts/inline-file-selector.js +185 -0
- package/dist/tui/components/inline-prompts/inline-input.d.ts +19 -0
- package/dist/tui/components/inline-prompts/inline-input.js +32 -0
- package/dist/tui/components/inline-prompts/inline-search.d.ts +20 -0
- package/dist/tui/components/inline-prompts/inline-search.js +50 -0
- package/dist/tui/components/inline-prompts/inline-select.d.ts +20 -0
- package/dist/tui/components/inline-prompts/inline-select.js +34 -0
- package/dist/tui/components/logo.d.ts +43 -0
- package/dist/tui/components/logo.js +103 -0
- package/dist/tui/components/message-item.d.ts +12 -0
- package/dist/tui/components/message-item.js +12 -0
- package/dist/tui/components/onboarding/copyable-prompt.d.ts +15 -0
- package/dist/tui/components/onboarding/copyable-prompt.js +65 -0
- package/dist/tui/components/onboarding/index.d.ts +7 -0
- package/dist/tui/components/onboarding/index.js +6 -0
- package/dist/tui/components/onboarding/onboarding-flow.d.ts +13 -0
- package/dist/tui/components/onboarding/onboarding-flow.js +304 -0
- package/dist/tui/components/onboarding/onboarding-step.d.ts +23 -0
- package/dist/tui/components/onboarding/onboarding-step.js +12 -0
- package/dist/tui/components/output-log.d.ts +14 -0
- package/dist/tui/components/output-log.js +13 -0
- package/dist/tui/components/scrollable-list.d.ts +30 -0
- package/dist/tui/components/scrollable-list.js +121 -0
- package/dist/tui/components/suggestions.d.ts +16 -0
- package/dist/tui/components/suggestions.js +162 -0
- package/dist/tui/components/tab-bar.d.ts +10 -0
- package/dist/tui/components/tab-bar.js +12 -0
- package/dist/tui/constants.d.ts +11 -0
- package/dist/tui/constants.js +13 -0
- package/dist/tui/contexts/auth-context.d.ts +30 -0
- package/dist/tui/contexts/auth-context.js +153 -0
- package/dist/tui/contexts/consumer.d.ts +31 -0
- package/dist/tui/contexts/consumer.js +56 -0
- package/dist/tui/contexts/index.d.ts +6 -0
- package/dist/tui/contexts/index.js +6 -0
- package/dist/tui/contexts/onboarding-context.d.ts +43 -0
- package/dist/tui/contexts/onboarding-context.js +181 -0
- package/dist/tui/contexts/services-context.d.ts +29 -0
- package/dist/tui/contexts/services-context.js +20 -0
- package/dist/tui/contexts/use-commands.d.ts +29 -0
- package/dist/tui/contexts/use-commands.js +54 -0
- package/dist/tui/contexts/use-mode.d.ts +43 -0
- package/dist/tui/contexts/use-mode.js +76 -0
- package/dist/tui/contexts/use-theme.d.ts +53 -0
- package/dist/tui/contexts/use-theme.js +60 -0
- package/dist/tui/hooks/index.d.ts +17 -0
- package/dist/tui/hooks/index.js +14 -0
- package/dist/tui/hooks/use-activity-logs.d.ts +26 -0
- package/dist/tui/hooks/use-activity-logs.js +90 -0
- package/dist/tui/hooks/use-consumer.d.ts +12 -0
- package/dist/tui/hooks/use-consumer.js +50 -0
- package/dist/tui/hooks/use-onboarding.d.ts +7 -0
- package/dist/tui/hooks/use-onboarding.js +6 -0
- package/dist/tui/hooks/use-queue-polling.d.ts +31 -0
- package/dist/tui/hooks/use-queue-polling.js +90 -0
- package/dist/tui/hooks/use-slash-command-processor.d.ts +16 -0
- package/dist/tui/hooks/use-slash-command-processor.js +132 -0
- package/dist/tui/hooks/use-slash-completion.d.ts +30 -0
- package/dist/tui/hooks/use-slash-completion.js +230 -0
- package/dist/tui/hooks/use-tab-navigation.d.ts +10 -0
- package/dist/tui/hooks/use-tab-navigation.js +35 -0
- package/dist/tui/hooks/use-visible-window.d.ts +22 -0
- package/dist/tui/hooks/use-visible-window.js +37 -0
- package/dist/tui/index.d.ts +1 -0
- package/dist/tui/index.js +1 -0
- package/dist/tui/providers/app-providers.d.ts +25 -0
- package/dist/tui/providers/app-providers.js +9 -0
- package/dist/tui/types/commands.d.ts +252 -0
- package/dist/tui/types/commands.js +16 -0
- package/dist/tui/types/dialogs.d.ts +37 -0
- package/dist/tui/types/dialogs.js +4 -0
- package/dist/tui/types/index.d.ts +11 -0
- package/dist/tui/types/index.js +7 -0
- package/dist/tui/types/messages.d.ts +55 -0
- package/dist/tui/types/messages.js +4 -0
- package/dist/tui/types/prompts.d.ts +100 -0
- package/dist/tui/types/prompts.js +4 -0
- package/dist/tui/types/ui.d.ts +14 -0
- package/dist/tui/types/ui.js +4 -0
- package/dist/tui/types.d.ts +1 -0
- package/dist/tui/types.js +1 -0
- package/dist/tui/views/command-view.d.ts +12 -0
- package/dist/tui/views/command-view.js +451 -0
- package/dist/tui/views/index.d.ts +6 -0
- package/dist/tui/views/index.js +6 -0
- package/dist/tui/views/login-view.d.ts +10 -0
- package/dist/tui/views/login-view.js +30 -0
- package/dist/tui/views/logs-view.d.ts +11 -0
- package/dist/tui/views/logs-view.js +73 -0
- package/dist/utils/file-validator.d.ts +16 -0
- package/dist/utils/file-validator.js +81 -0
- package/dist/utils/global-config-path.d.ts +15 -0
- package/dist/utils/global-config-path.js +38 -0
- package/oclif.manifest.json +29 -315
- package/package.json +11 -4
- package/dist/commands/clear.d.ts +0 -19
- package/dist/commands/clear.js +0 -78
- package/dist/commands/init.d.ts +0 -130
- package/dist/commands/login.d.ts +0 -22
- package/dist/commands/login.js +0 -108
- package/dist/commands/logout.d.ts +0 -16
- package/dist/commands/logout.js +0 -61
- package/dist/commands/pull.d.ts +0 -33
- package/dist/commands/pull.js +0 -115
- package/dist/commands/push.d.ts +0 -35
- package/dist/commands/push.js +0 -160
- package/dist/commands/space/list.d.ts +0 -25
- package/dist/commands/space/list.js +0 -114
- package/dist/commands/space/switch.d.ts +0 -36
- package/dist/commands/space/switch.js +0 -160
- package/dist/infra/cipher/grpc/internal-llm-grpc-service.d.ts +0 -149
- package/dist/infra/cipher/grpc/internal-llm-grpc-service.js +0 -364
- package/dist/infra/cipher/grpc/internal-llm-grpc.proto +0 -94
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScrollableList Component
|
|
3
|
+
*
|
|
4
|
+
* Generic scrollable list that works reliably with Ink's layout.
|
|
5
|
+
* Uses item-based slicing with dynamic height calculation.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
export interface ScrollableListProps<T> {
|
|
9
|
+
/** Auto-scroll to bottom when new items are added */
|
|
10
|
+
autoScrollToBottom?: boolean;
|
|
11
|
+
/** Available height in lines for the list */
|
|
12
|
+
availableHeight: number;
|
|
13
|
+
/** Estimate line count for an item (default: 1 line per item) */
|
|
14
|
+
estimateItemHeight?: (item: T, index: number) => number;
|
|
15
|
+
/** Whether keyboard input is active for this list */
|
|
16
|
+
isActive?: boolean;
|
|
17
|
+
/** Array of items to render */
|
|
18
|
+
items: T[];
|
|
19
|
+
/** Extract unique key for each item */
|
|
20
|
+
keyExtractor: (item: T, index: number) => string;
|
|
21
|
+
/** Called when scroll position changes */
|
|
22
|
+
onScroll?: (offset: number, total: number) => void;
|
|
23
|
+
/** Render function for each item */
|
|
24
|
+
renderItem: (item: T, index: number) => React.ReactNode;
|
|
25
|
+
/** Scroll step size (items per keypress) */
|
|
26
|
+
scrollStep?: number;
|
|
27
|
+
/** Show scroll position indicator */
|
|
28
|
+
showIndicator?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function ScrollableList<T>({ autoScrollToBottom, availableHeight, estimateItemHeight, isActive, items, keyExtractor, onScroll, renderItem, scrollStep, showIndicator, }: ScrollableListProps<T>): React.ReactElement;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ScrollableList Component
|
|
4
|
+
*
|
|
5
|
+
* Generic scrollable list that works reliably with Ink's layout.
|
|
6
|
+
* Uses item-based slicing with dynamic height calculation.
|
|
7
|
+
*/
|
|
8
|
+
import { Box, Text, useInput } from 'ink';
|
|
9
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
10
|
+
export function ScrollableList({ autoScrollToBottom = true, availableHeight, estimateItemHeight = () => 1, isActive = true, items, keyExtractor, onScroll, renderItem, scrollStep = 1, showIndicator = true, }) {
|
|
11
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
12
|
+
const totalItems = items.length;
|
|
13
|
+
// Calculate which items are visible based on available height
|
|
14
|
+
const { visibleEndIndex, visibleItems, visibleStartIndex } = useMemo(() => {
|
|
15
|
+
if (totalItems === 0) {
|
|
16
|
+
return { visibleEndIndex: 0, visibleItems: [], visibleStartIndex: 0 };
|
|
17
|
+
}
|
|
18
|
+
// Calculate heights of all items
|
|
19
|
+
const itemHeights = [];
|
|
20
|
+
for (let i = 0; i < totalItems; i++) {
|
|
21
|
+
const h = estimateItemHeight(items[i], i);
|
|
22
|
+
itemHeights.push(h);
|
|
23
|
+
}
|
|
24
|
+
// Reserve space for indicators (1 line each)
|
|
25
|
+
const indicatorSpace = showIndicator ? 2 : 0;
|
|
26
|
+
const contentHeight = Math.max(1, availableHeight - indicatorSpace);
|
|
27
|
+
// Find visible range starting from scrollOffset
|
|
28
|
+
const startIdx = Math.min(scrollOffset, totalItems - 1);
|
|
29
|
+
let endIdx = startIdx;
|
|
30
|
+
let accumulatedHeight = 0;
|
|
31
|
+
// Accumulate items until we fill the available height
|
|
32
|
+
for (let i = startIdx; i < totalItems; i++) {
|
|
33
|
+
if (accumulatedHeight + itemHeights[i] > contentHeight && i > startIdx) {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
accumulatedHeight += itemHeights[i];
|
|
37
|
+
endIdx = i + 1;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
visibleEndIndex: endIdx,
|
|
41
|
+
visibleItems: items.slice(startIdx, endIdx),
|
|
42
|
+
visibleStartIndex: startIdx,
|
|
43
|
+
};
|
|
44
|
+
}, [items, totalItems, scrollOffset, availableHeight, estimateItemHeight, showIndicator]);
|
|
45
|
+
// Calculate max scroll offset
|
|
46
|
+
const maxOffset = useMemo(() => {
|
|
47
|
+
if (totalItems === 0)
|
|
48
|
+
return 0;
|
|
49
|
+
// Find the minimum start index that shows the last item
|
|
50
|
+
let maxStart = totalItems - 1;
|
|
51
|
+
const indicatorSpace = showIndicator ? 2 : 0;
|
|
52
|
+
const contentHeight = Math.max(1, availableHeight - indicatorSpace);
|
|
53
|
+
let accumulatedHeight = 0;
|
|
54
|
+
for (let i = totalItems - 1; i >= 0; i--) {
|
|
55
|
+
const h = estimateItemHeight(items[i], i);
|
|
56
|
+
if (accumulatedHeight + h > contentHeight && i < totalItems - 1) {
|
|
57
|
+
maxStart = i + 1;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
accumulatedHeight += h;
|
|
61
|
+
maxStart = i;
|
|
62
|
+
}
|
|
63
|
+
return maxStart;
|
|
64
|
+
}, [items, totalItems, availableHeight, estimateItemHeight, showIndicator]);
|
|
65
|
+
// Clamp scroll offset when items or height changes
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
setScrollOffset((prev) => Math.min(prev, Math.max(0, maxOffset)));
|
|
68
|
+
}, [maxOffset]);
|
|
69
|
+
// Auto-scroll to bottom when new items are added
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (autoScrollToBottom && totalItems > 0) {
|
|
72
|
+
setScrollOffset(maxOffset);
|
|
73
|
+
}
|
|
74
|
+
}, [autoScrollToBottom, totalItems, maxOffset]);
|
|
75
|
+
// Notify parent of scroll changes
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
onScroll?.(scrollOffset, totalItems);
|
|
78
|
+
}, [scrollOffset, totalItems, onScroll]);
|
|
79
|
+
const scrollUp = useCallback(() => {
|
|
80
|
+
setScrollOffset((prev) => Math.max(0, prev - scrollStep));
|
|
81
|
+
}, [scrollStep]);
|
|
82
|
+
const scrollDown = useCallback(() => {
|
|
83
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + scrollStep));
|
|
84
|
+
}, [maxOffset, scrollStep]);
|
|
85
|
+
const scrollToTop = useCallback(() => {
|
|
86
|
+
setScrollOffset(0);
|
|
87
|
+
}, []);
|
|
88
|
+
const scrollToBottom = useCallback(() => {
|
|
89
|
+
setScrollOffset(maxOffset);
|
|
90
|
+
}, [maxOffset]);
|
|
91
|
+
useInput((input, key) => {
|
|
92
|
+
if (key.upArrow) {
|
|
93
|
+
scrollUp();
|
|
94
|
+
}
|
|
95
|
+
else if (key.downArrow) {
|
|
96
|
+
scrollDown();
|
|
97
|
+
}
|
|
98
|
+
else if (key.pageUp || (key.ctrl && input === 'u')) {
|
|
99
|
+
// Page up - scroll by ~half viewport worth of items
|
|
100
|
+
setScrollOffset((prev) => Math.max(0, prev - 3));
|
|
101
|
+
}
|
|
102
|
+
else if (key.pageDown || (key.ctrl && input === 'd')) {
|
|
103
|
+
// Page down - scroll by ~half viewport worth of items
|
|
104
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + 3));
|
|
105
|
+
}
|
|
106
|
+
else if (input === 'g' && !key.ctrl) {
|
|
107
|
+
scrollToTop();
|
|
108
|
+
}
|
|
109
|
+
else if (input === 'G') {
|
|
110
|
+
scrollToBottom();
|
|
111
|
+
}
|
|
112
|
+
}, { isActive });
|
|
113
|
+
const canScrollUp = scrollOffset > 0;
|
|
114
|
+
const canScrollDown = scrollOffset < maxOffset;
|
|
115
|
+
const itemsAbove = visibleStartIndex;
|
|
116
|
+
const itemsBelow = totalItems - visibleEndIndex;
|
|
117
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [showIndicator && canScrollUp && (_jsx(Box, { justifyContent: "center", children: _jsxs(Text, { dimColor: true, children: ["\u2191 ", itemsAbove, " more above"] }) })), visibleItems.map((item, idx) => {
|
|
118
|
+
const actualIndex = visibleStartIndex + idx;
|
|
119
|
+
return _jsx(React.Fragment, { children: renderItem(item, actualIndex) }, keyExtractor(item, actualIndex));
|
|
120
|
+
}), showIndicator && canScrollDown && (_jsx(Box, { justifyContent: "center", children: _jsxs(Text, { dimColor: true, children: ["\u2193 ", itemsBelow, " more below"] }) }))] }));
|
|
121
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suggestions Component
|
|
3
|
+
*
|
|
4
|
+
* Displays command suggestions with auto-completion.
|
|
5
|
+
* Uses useSlashCompletion hook internally to manage state.
|
|
6
|
+
* Shows args/flags details for the selected command.
|
|
7
|
+
* Shows max 7 items with sliding window that follows selection.
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
interface SuggestionsProps {
|
|
11
|
+
input: string;
|
|
12
|
+
onInsert?: (value: string) => void;
|
|
13
|
+
onSelect?: (value: string) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const Suggestions: React.FC<SuggestionsProps>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Suggestions Component
|
|
4
|
+
*
|
|
5
|
+
* Displays command suggestions with auto-completion.
|
|
6
|
+
* Uses useSlashCompletion hook internally to manage state.
|
|
7
|
+
* Shows args/flags details for the selected command.
|
|
8
|
+
* Shows max 7 items with sliding window that follows selection.
|
|
9
|
+
*/
|
|
10
|
+
import { Box, Text, useInput } from 'ink';
|
|
11
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
12
|
+
import { useMode } from '../contexts/use-mode.js';
|
|
13
|
+
import { useTheme } from '../contexts/use-theme.js';
|
|
14
|
+
import { useSlashCompletion } from '../hooks/index.js';
|
|
15
|
+
const MAX_VISIBLE_ITEMS = 5;
|
|
16
|
+
/**
|
|
17
|
+
* Format usage string from args, flags, and subcommands
|
|
18
|
+
*/
|
|
19
|
+
function formatUsage(label, args, flags, subCommands) {
|
|
20
|
+
let usage = label;
|
|
21
|
+
if (subCommands?.length) {
|
|
22
|
+
usage += ' <subcommand>';
|
|
23
|
+
}
|
|
24
|
+
if (args?.length) {
|
|
25
|
+
const argsStr = args.map((a) => (a.required ? `<${a.name}>` : `[${a.name}]`)).join(' ');
|
|
26
|
+
usage += ` ${argsStr}`;
|
|
27
|
+
}
|
|
28
|
+
if (flags?.length) {
|
|
29
|
+
const flagsStr = flags.map((f) => `[--${f.name}]`).join(' ');
|
|
30
|
+
usage += ` ${flagsStr}`;
|
|
31
|
+
}
|
|
32
|
+
return usage;
|
|
33
|
+
}
|
|
34
|
+
export const Suggestions = ({ input, onInsert, onSelect }) => {
|
|
35
|
+
const { theme: { colors }, } = useTheme();
|
|
36
|
+
const { mode, setMode } = useMode();
|
|
37
|
+
const { activeIndex, clearSuggestions, hasMatchedCommand, isCommandAttempt, nextSuggestion, prevSuggestion, selectSuggestion, suggestions, } = useSlashCompletion(input);
|
|
38
|
+
// Track if user dismissed suggestions with Escape
|
|
39
|
+
const isDismissedRef = useRef(false);
|
|
40
|
+
const prevInputRef = useRef(input);
|
|
41
|
+
// Reset dismissed state when input changes
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (input !== prevInputRef.current) {
|
|
44
|
+
isDismissedRef.current = false;
|
|
45
|
+
prevInputRef.current = input;
|
|
46
|
+
}
|
|
47
|
+
}, [input]);
|
|
48
|
+
// Manage mode based on suggestions visibility
|
|
49
|
+
// Don't show suggestions mode when user is typing arguments for a matched command
|
|
50
|
+
// Don't re-enable if user dismissed with Escape
|
|
51
|
+
// Only control mode when already in console/suggestions mode (don't override 'activity')
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (isDismissedRef.current) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Only manage mode transitions within console/suggestions context
|
|
57
|
+
if (mode !== 'console' && mode !== 'suggestions') {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (suggestions.length > 0 || (isCommandAttempt && !hasMatchedCommand)) {
|
|
61
|
+
setMode('suggestions');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
setMode('console');
|
|
65
|
+
}
|
|
66
|
+
}, [mode, suggestions.length, isCommandAttempt, hasMatchedCommand, setMode]);
|
|
67
|
+
// Calculate visible window based on selected index
|
|
68
|
+
const { visibleSuggestions, windowStart } = useMemo(() => {
|
|
69
|
+
if (suggestions.length <= MAX_VISIBLE_ITEMS) {
|
|
70
|
+
return { visibleSuggestions: suggestions, windowStart: 0 };
|
|
71
|
+
}
|
|
72
|
+
// Calculate window start to keep selected item visible
|
|
73
|
+
let start = 0;
|
|
74
|
+
if (activeIndex >= MAX_VISIBLE_ITEMS) {
|
|
75
|
+
start = activeIndex - MAX_VISIBLE_ITEMS + 1;
|
|
76
|
+
}
|
|
77
|
+
// Ensure we don't go past the end
|
|
78
|
+
const maxStart = suggestions.length - MAX_VISIBLE_ITEMS;
|
|
79
|
+
start = Math.min(start, maxStart);
|
|
80
|
+
return {
|
|
81
|
+
visibleSuggestions: suggestions.slice(start, start + MAX_VISIBLE_ITEMS),
|
|
82
|
+
windowStart: start,
|
|
83
|
+
};
|
|
84
|
+
}, [suggestions, activeIndex]);
|
|
85
|
+
useInput((_input, key) => {
|
|
86
|
+
if (key.upArrow) {
|
|
87
|
+
prevSuggestion();
|
|
88
|
+
}
|
|
89
|
+
if (key.downArrow) {
|
|
90
|
+
nextSuggestion();
|
|
91
|
+
}
|
|
92
|
+
if (key.return) {
|
|
93
|
+
const value = selectSuggestion();
|
|
94
|
+
if (value) {
|
|
95
|
+
// In file completion mode (value contains @)
|
|
96
|
+
const isFileCompletion = value.includes('@');
|
|
97
|
+
const isFolder = value.endsWith('/');
|
|
98
|
+
if (isFileCompletion && onInsert) {
|
|
99
|
+
onInsert(value);
|
|
100
|
+
// For folders, stay in suggestions mode to show contents
|
|
101
|
+
if (isFolder) {
|
|
102
|
+
clearSuggestions();
|
|
103
|
+
// Don't exit suggestions mode - new suggestions will appear
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (onSelect) {
|
|
108
|
+
onSelect(value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
clearSuggestions();
|
|
112
|
+
setMode('console');
|
|
113
|
+
}
|
|
114
|
+
if (key.tab) {
|
|
115
|
+
const value = selectSuggestion();
|
|
116
|
+
if (value && onInsert) {
|
|
117
|
+
onInsert(value);
|
|
118
|
+
// For folders, stay in suggestions mode
|
|
119
|
+
if (value.endsWith('/')) {
|
|
120
|
+
clearSuggestions();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
clearSuggestions();
|
|
125
|
+
setMode('console');
|
|
126
|
+
}
|
|
127
|
+
if (key.escape) {
|
|
128
|
+
isDismissedRef.current = true;
|
|
129
|
+
clearSuggestions();
|
|
130
|
+
setMode('console');
|
|
131
|
+
}
|
|
132
|
+
}, { isActive: mode === 'suggestions' });
|
|
133
|
+
// Don't render if dismissed
|
|
134
|
+
if (isDismissedRef.current) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
// Show "No commands found" when typing an unknown command
|
|
138
|
+
// Don't show when user is typing arguments for a known command
|
|
139
|
+
if (suggestions.length === 0) {
|
|
140
|
+
if (isCommandAttempt && !hasMatchedCommand && input.trim().length > 1) {
|
|
141
|
+
return (_jsx(Box, { borderColor: colors.border, borderStyle: "single", paddingX: 1, children: _jsx(Text, { color: colors.dimText, children: "No commands found" }) }));
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
// Calculate max label width for alignment
|
|
146
|
+
const maxLabelLength = Math.max(...suggestions.map((s) => s.label.length));
|
|
147
|
+
const labelWidth = Math.max(maxLabelLength, 12);
|
|
148
|
+
// Get the selected suggestion
|
|
149
|
+
const selectedSuggestion = activeIndex >= 0 ? suggestions[activeIndex] : null;
|
|
150
|
+
const hasDetails = selectedSuggestion &&
|
|
151
|
+
(selectedSuggestion.args?.length || selectedSuggestion.flags?.length || selectedSuggestion.subCommands?.length);
|
|
152
|
+
// Calculate if there are more items above/below
|
|
153
|
+
const hasMoreAbove = windowStart > 0;
|
|
154
|
+
const hasMoreBelow = windowStart + MAX_VISIBLE_ITEMS < suggestions.length;
|
|
155
|
+
return (_jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", paddingX: 1, children: [hasMoreAbove && (_jsxs(Text, { color: colors.dimText, dimColor: true, children: ["\u2191 ", windowStart, " more above"] })), visibleSuggestions.map((suggestion, index) => {
|
|
156
|
+
const actualIndex = windowStart + index;
|
|
157
|
+
const isActive = actualIndex === activeIndex;
|
|
158
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { backgroundColor: isActive ? colors.dimText : undefined, color: colors.text, children: [isActive ? '❯ ' : ' ', suggestion.label.padEnd(labelWidth)] }), _jsxs(Text, { color: colors.dimText, children: [" ", suggestion.description || ''] })] }, suggestion.value));
|
|
159
|
+
}), hasMoreBelow && (_jsxs(Text, { color: colors.dimText, dimColor: true, children: ["\u2193 ", suggestions.length - windowStart - MAX_VISIBLE_ITEMS, " more below"] })), hasDetails && (_jsxs(Box, { borderColor: colors.border, borderStyle: "single", borderTop: true, flexDirection: "column", marginTop: 0, children: [_jsxs(Text, { color: colors.text, children: ["Usage:", ' ', formatUsage(selectedSuggestion.label, selectedSuggestion.args, selectedSuggestion.flags, selectedSuggestion.subCommands)] }), selectedSuggestion.subCommands?.map((sub) => (_jsxs(Text, { color: colors.dimText, children: [' ', _jsx(Text, { color: colors.text, children: sub.name.padEnd(labelWidth + 2) }), ' ', sub.description] }, sub.name))), selectedSuggestion.args?.map((arg) => (_jsxs(Text, { color: colors.dimText, children: [' ', _jsx(Text, { color: colors.text, children: (arg.required ? `<${arg.name}>` : `[${arg.name}]`).padEnd(labelWidth + 2) }), ' ', arg.description] }, arg.name))), selectedSuggestion.flags?.map((flag) => (_jsxs(Text, { color: colors.dimText, children: [' ', _jsx(Text, { color: colors.text, children: (flag.type === 'file'
|
|
160
|
+
? `${flag.char || '@'}file`
|
|
161
|
+
: (flag.char ? `-${flag.char}, ` : '') + `--${flag.name}`).padEnd(labelWidth + 2) }), ' ', flag.description, flag.default !== undefined && _jsxs(Text, { children: [" (default: ", String(flag.default), ")"] })] }, flag.name)))] }))] }));
|
|
162
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Tab Bar Component
|
|
4
|
+
*/
|
|
5
|
+
import { Box, Text } from 'ink';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { TABS } from '../constants.js';
|
|
8
|
+
import { useTheme } from '../contexts/use-theme.js';
|
|
9
|
+
export const TabBar = ({ activeTab }) => {
|
|
10
|
+
const { theme: { colors }, } = useTheme();
|
|
11
|
+
return (_jsxs(Box, { alignItems: "flex-end", paddingX: 1, width: "100%", children: [_jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", height: "100%", width: 2 }), TABS.map((tab) => (_jsxs(React.Fragment, { children: [_jsx(Box, { borderBottomColor: activeTab === tab.id ? colors.primary : colors.border, borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", children: _jsx(Text, { children: tab.label }) }), _jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", height: "100%", width: 6 })] }, tab.id))), _jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", flexGrow: 1, height: "100%" })] }));
|
|
12
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Constants
|
|
3
|
+
*/
|
|
4
|
+
import type { Tab } from './types.js';
|
|
5
|
+
export declare const TABS: Tab[];
|
|
6
|
+
export declare const LAYOUT: {
|
|
7
|
+
readonly footerHeight: 2;
|
|
8
|
+
readonly headerHeight: 4;
|
|
9
|
+
readonly tabBarHeight: 2;
|
|
10
|
+
};
|
|
11
|
+
export declare const DEFAULT_TAB: "activity";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Constants
|
|
3
|
+
*/
|
|
4
|
+
export const TABS = [
|
|
5
|
+
{ id: 'activity', label: 'Activity' },
|
|
6
|
+
{ id: 'console', label: 'Console' },
|
|
7
|
+
];
|
|
8
|
+
export const LAYOUT = {
|
|
9
|
+
footerHeight: 2,
|
|
10
|
+
headerHeight: 4,
|
|
11
|
+
tabBarHeight: 2,
|
|
12
|
+
};
|
|
13
|
+
export const DEFAULT_TAB = 'activity';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Context
|
|
3
|
+
*
|
|
4
|
+
* Manages authentication state and login flow.
|
|
5
|
+
* Executes LoginUseCase directly for authentication.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { AuthToken } from '../../core/domain/entities/auth-token.js';
|
|
9
|
+
import type { BrvConfig } from '../../core/domain/entities/brv-config.js';
|
|
10
|
+
import type { AuthState } from '../types.js';
|
|
11
|
+
export interface AuthContextValue {
|
|
12
|
+
authToken: AuthToken | undefined;
|
|
13
|
+
brvConfig: BrvConfig | undefined;
|
|
14
|
+
isInitialConfigLoaded: boolean;
|
|
15
|
+
isLoggingIn: boolean;
|
|
16
|
+
loginOutput: string[];
|
|
17
|
+
authState: AuthState;
|
|
18
|
+
isAuthorized: boolean;
|
|
19
|
+
login: () => void;
|
|
20
|
+
reloadAuth: () => Promise<void>;
|
|
21
|
+
reloadBrvConfig: () => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
interface AuthProviderProps {
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
initialAuthToken?: AuthToken;
|
|
26
|
+
initialBrvConfig?: BrvConfig;
|
|
27
|
+
}
|
|
28
|
+
export declare function AuthProvider({ children, initialAuthToken, initialBrvConfig }: AuthProviderProps): React.ReactElement;
|
|
29
|
+
export declare function useAuth(): AuthContextValue;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Auth Context
|
|
4
|
+
*
|
|
5
|
+
* Manages authentication state and login flow.
|
|
6
|
+
* Executes LoginUseCase directly for authentication.
|
|
7
|
+
*/
|
|
8
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
9
|
+
import { getAuthConfig } from '../../config/auth.config.js';
|
|
10
|
+
import { getCurrentConfig } from '../../config/environment.js';
|
|
11
|
+
import { OAuthService } from '../../infra/auth/oauth-service.js';
|
|
12
|
+
import { OidcDiscoveryService } from '../../infra/auth/oidc-discovery-service.js';
|
|
13
|
+
import { SystemBrowserLauncher } from '../../infra/browser/system-browser-launcher.js';
|
|
14
|
+
import { CallbackHandler } from '../../infra/http/callback-handler.js';
|
|
15
|
+
import { FileGlobalConfigStore } from "../../infra/storage/file-global-config-store.js";
|
|
16
|
+
import { MixpanelTrackingService } from '../../infra/tracking/mixpanel-tracking-service.js';
|
|
17
|
+
import { LoginUseCase } from '../../infra/usecase/login-use-case.js';
|
|
18
|
+
import { HttpUserService } from '../../infra/user/http-user-service.js';
|
|
19
|
+
import { useServices } from './services-context.js';
|
|
20
|
+
const AuthContext = createContext(undefined);
|
|
21
|
+
/**
|
|
22
|
+
* Simple terminal that appends output to state
|
|
23
|
+
* Only supports log/error/warn - interactive prompts throw errors
|
|
24
|
+
*/
|
|
25
|
+
function createStateTerminal(appendOutput) {
|
|
26
|
+
return {
|
|
27
|
+
actionStart() { },
|
|
28
|
+
actionStop() { },
|
|
29
|
+
async confirm() {
|
|
30
|
+
return true;
|
|
31
|
+
},
|
|
32
|
+
error(message) {
|
|
33
|
+
appendOutput(`Error: ${message}`);
|
|
34
|
+
},
|
|
35
|
+
async fileSelector() {
|
|
36
|
+
throw new Error('fileSelector not supported in login terminal');
|
|
37
|
+
},
|
|
38
|
+
async input() {
|
|
39
|
+
throw new Error('input not supported in login terminal');
|
|
40
|
+
},
|
|
41
|
+
log(message) {
|
|
42
|
+
appendOutput(message ?? '');
|
|
43
|
+
},
|
|
44
|
+
async search() {
|
|
45
|
+
throw new Error('search not supported in login terminal');
|
|
46
|
+
},
|
|
47
|
+
async select() {
|
|
48
|
+
throw new Error('select not supported in login terminal');
|
|
49
|
+
},
|
|
50
|
+
warn(message) {
|
|
51
|
+
appendOutput(`Warning: ${message}`);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function AuthProvider({ children, initialAuthToken, initialBrvConfig }) {
|
|
56
|
+
const { projectConfigStore, tokenStore } = useServices();
|
|
57
|
+
// State
|
|
58
|
+
const [authToken, setAuthToken] = useState(initialAuthToken);
|
|
59
|
+
const [brvConfig, setBrvConfig] = useState(initialBrvConfig);
|
|
60
|
+
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
|
61
|
+
const [loginOutput, setLoginOutput] = useState([]);
|
|
62
|
+
const [isInitialConfigLoaded, setIsInitialConfigLoaded] = useState(false);
|
|
63
|
+
// Computed
|
|
64
|
+
const authState = authToken?.isValid() ? 'authorized' : 'unauthorized';
|
|
65
|
+
const isAuthorized = authState === 'authorized';
|
|
66
|
+
// Reload auth state (after login or logout)
|
|
67
|
+
const reloadAuth = useCallback(async () => {
|
|
68
|
+
// Clear display output
|
|
69
|
+
setLoginOutput([]);
|
|
70
|
+
try {
|
|
71
|
+
// Reload brv config state before checking auth token
|
|
72
|
+
await reloadBrvConfig();
|
|
73
|
+
}
|
|
74
|
+
catch { }
|
|
75
|
+
const newToken = await tokenStore.load();
|
|
76
|
+
if (newToken?.isValid()) {
|
|
77
|
+
setAuthToken(newToken);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Token is undefined or invalid (logged out or expired)
|
|
81
|
+
setAuthToken(undefined);
|
|
82
|
+
}
|
|
83
|
+
}, [tokenStore, projectConfigStore]);
|
|
84
|
+
// Reload brv config state
|
|
85
|
+
const reloadBrvConfig = useCallback(async () => {
|
|
86
|
+
const configExists = await projectConfigStore.exists();
|
|
87
|
+
if (configExists) {
|
|
88
|
+
const config = await projectConfigStore.read();
|
|
89
|
+
setBrvConfig(config);
|
|
90
|
+
}
|
|
91
|
+
}, [projectConfigStore]);
|
|
92
|
+
// Login action - executes LoginUseCase
|
|
93
|
+
const login = useCallback(() => {
|
|
94
|
+
setIsLoggingIn(true);
|
|
95
|
+
setLoginOutput([]);
|
|
96
|
+
const appendOutput = (line) => {
|
|
97
|
+
setLoginOutput((prev) => [...prev, line]);
|
|
98
|
+
};
|
|
99
|
+
const runLogin = async () => {
|
|
100
|
+
try {
|
|
101
|
+
const config = getCurrentConfig();
|
|
102
|
+
const globalConfigStore = new FileGlobalConfigStore();
|
|
103
|
+
const trackingService = new MixpanelTrackingService({ globalConfigStore, tokenStore });
|
|
104
|
+
const discoveryService = new OidcDiscoveryService();
|
|
105
|
+
const authConfig = await getAuthConfig(discoveryService);
|
|
106
|
+
const useCase = new LoginUseCase({
|
|
107
|
+
authService: new OAuthService(authConfig),
|
|
108
|
+
browserLauncher: new SystemBrowserLauncher(),
|
|
109
|
+
callbackHandler: new CallbackHandler(),
|
|
110
|
+
terminal: createStateTerminal(appendOutput),
|
|
111
|
+
tokenStore,
|
|
112
|
+
trackingService,
|
|
113
|
+
userService: new HttpUserService({ apiBaseUrl: config.apiBaseUrl }),
|
|
114
|
+
});
|
|
115
|
+
await useCase.run();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
appendOutput(`Error: ${error instanceof Error ? error.message : 'Login failed'}`);
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
setIsLoggingIn(false);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
runLogin();
|
|
125
|
+
}, [tokenStore]);
|
|
126
|
+
// Reload brv config state on mount and mark initial load complete
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
reloadBrvConfig().then(() => {
|
|
129
|
+
setIsInitialConfigLoaded(true);
|
|
130
|
+
});
|
|
131
|
+
}, [reloadBrvConfig]);
|
|
132
|
+
// Memoize context value
|
|
133
|
+
const value = useMemo(() => ({
|
|
134
|
+
authState,
|
|
135
|
+
authToken,
|
|
136
|
+
brvConfig,
|
|
137
|
+
isAuthorized,
|
|
138
|
+
isInitialConfigLoaded,
|
|
139
|
+
isLoggingIn,
|
|
140
|
+
login,
|
|
141
|
+
loginOutput,
|
|
142
|
+
reloadAuth,
|
|
143
|
+
reloadBrvConfig
|
|
144
|
+
}), [authToken, brvConfig, isInitialConfigLoaded, isLoggingIn, loginOutput, authState, isAuthorized, login, reloadAuth]);
|
|
145
|
+
return _jsx(AuthContext.Provider, { value: value, children: children });
|
|
146
|
+
}
|
|
147
|
+
export function useAuth() {
|
|
148
|
+
const context = useContext(AuthContext);
|
|
149
|
+
if (!context) {
|
|
150
|
+
throw new Error('useAuth must be used within AuthProvider');
|
|
151
|
+
}
|
|
152
|
+
return context;
|
|
153
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer Context
|
|
3
|
+
*
|
|
4
|
+
* Global context for managing consumer service and queue polling state.
|
|
5
|
+
* Consolidates useConsumer and useQueuePolling hooks into a single context.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const { consumerId, sessionExecutions, stats } = useConsumer()
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import type { ExecutionWithToolCalls, QueueStats } from '../../infra/cipher/consumer/queue-polling-service.js';
|
|
14
|
+
import type { ConsumerStatus } from '../types.js';
|
|
15
|
+
interface ConsumerContextValue {
|
|
16
|
+
consumerError: Error | null;
|
|
17
|
+
consumerId: null | string;
|
|
18
|
+
consumerStatus: ConsumerStatus;
|
|
19
|
+
isConnected: boolean;
|
|
20
|
+
pollingError: Error | null;
|
|
21
|
+
reconnectCount: number;
|
|
22
|
+
restart: () => Promise<void>;
|
|
23
|
+
sessionExecutions: ExecutionWithToolCalls[];
|
|
24
|
+
stats: null | QueueStats;
|
|
25
|
+
}
|
|
26
|
+
interface ConsumerProviderProps {
|
|
27
|
+
children: React.ReactNode;
|
|
28
|
+
}
|
|
29
|
+
export declare function ConsumerProvider({ children }: ConsumerProviderProps): React.ReactElement;
|
|
30
|
+
export declare function useConsumer(): ConsumerContextValue;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Consumer Context
|
|
4
|
+
*
|
|
5
|
+
* Global context for managing consumer service and queue polling state.
|
|
6
|
+
* Consolidates useConsumer and useQueuePolling hooks into a single context.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const { consumerId, sessionExecutions, stats } = useConsumer()
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
14
|
+
import { useConsumer as useConsumerHook } from '../hooks/use-consumer.js';
|
|
15
|
+
import { useQueuePolling } from '../hooks/use-queue-polling.js';
|
|
16
|
+
const ConsumerContext = createContext(undefined);
|
|
17
|
+
export function ConsumerProvider({ children }) {
|
|
18
|
+
// Start consumer service
|
|
19
|
+
const { consumerError, consumerId, consumerStatus, restart } = useConsumerHook();
|
|
20
|
+
// Poll queue for executions and stats
|
|
21
|
+
const { error: pollingError, isConnected, reconnectCount, sessionExecutions, stats, } = useQueuePolling({
|
|
22
|
+
consumerId: consumerId ?? undefined,
|
|
23
|
+
pollInterval: 1000,
|
|
24
|
+
});
|
|
25
|
+
const contextValue = useMemo(() => ({
|
|
26
|
+
// Consumer state
|
|
27
|
+
consumerError,
|
|
28
|
+
consumerId,
|
|
29
|
+
consumerStatus,
|
|
30
|
+
// Queue polling state
|
|
31
|
+
isConnected,
|
|
32
|
+
pollingError,
|
|
33
|
+
reconnectCount,
|
|
34
|
+
restart,
|
|
35
|
+
sessionExecutions,
|
|
36
|
+
stats,
|
|
37
|
+
}), [
|
|
38
|
+
consumerError,
|
|
39
|
+
consumerId,
|
|
40
|
+
consumerStatus,
|
|
41
|
+
restart,
|
|
42
|
+
isConnected,
|
|
43
|
+
pollingError,
|
|
44
|
+
reconnectCount,
|
|
45
|
+
sessionExecutions,
|
|
46
|
+
stats,
|
|
47
|
+
]);
|
|
48
|
+
return _jsx(ConsumerContext.Provider, { value: contextValue, children: children });
|
|
49
|
+
}
|
|
50
|
+
export function useConsumer() {
|
|
51
|
+
const context = useContext(ConsumerContext);
|
|
52
|
+
if (!context) {
|
|
53
|
+
throw new Error('useConsumer must be used within ConsumerProvider');
|
|
54
|
+
}
|
|
55
|
+
return context;
|
|
56
|
+
}
|