@qduc/term2 0.7.2 → 0.7.3
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/dist/agent.d.ts +1 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +17 -1
- package/dist/agent.js.map +1 -1
- package/dist/agent.test.js +36 -0
- package/dist/agent.test.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +57 -12
- package/dist/app.js.map +1 -1
- package/dist/cli.integration.test.js +60 -4
- package/dist/cli.integration.test.js.map +1 -1
- package/dist/cli.js +33 -2
- package/dist/cli.js.map +1 -1
- package/dist/components/ApprovalPrompt.ask-user.test.d.ts +2 -0
- package/dist/components/ApprovalPrompt.ask-user.test.d.ts.map +1 -0
- package/dist/components/ApprovalPrompt.ask-user.test.js +108 -0
- package/dist/components/ApprovalPrompt.ask-user.test.js.map +1 -0
- package/dist/components/ApprovalPrompt.d.ts +4 -4
- package/dist/components/ApprovalPrompt.d.ts.map +1 -1
- package/dist/components/ApprovalPrompt.js +76 -13
- package/dist/components/ApprovalPrompt.js.map +1 -1
- package/dist/components/BottomArea.d.ts +7 -2
- package/dist/components/BottomArea.d.ts.map +1 -1
- package/dist/components/BottomArea.js +11 -6
- package/dist/components/BottomArea.js.map +1 -1
- package/dist/components/BottomArea.test.js +4 -3
- package/dist/components/BottomArea.test.js.map +1 -1
- package/dist/components/Common/MenuContainer.d.ts +3 -2
- package/dist/components/Common/MenuContainer.d.ts.map +1 -1
- package/dist/components/Common/MenuContainer.js +14 -2
- package/dist/components/Common/MenuContainer.js.map +1 -1
- package/dist/components/Common/MenuContainer.test.d.ts +2 -0
- package/dist/components/Common/MenuContainer.test.d.ts.map +1 -0
- package/dist/components/Common/MenuContainer.test.js +66 -0
- package/dist/components/Common/MenuContainer.test.js.map +1 -0
- package/dist/components/Input/PopupManager.d.ts +14 -0
- package/dist/components/Input/PopupManager.d.ts.map +1 -1
- package/dist/components/Input/PopupManager.js +6 -4
- package/dist/components/Input/PopupManager.js.map +1 -1
- package/dist/components/Input/popup-props.d.ts +3 -1
- package/dist/components/Input/popup-props.d.ts.map +1 -1
- package/dist/components/Input/popup-props.js +12 -1
- package/dist/components/Input/popup-props.js.map +1 -1
- package/dist/components/Input/popup-props.test.js +9 -0
- package/dist/components/Input/popup-props.test.js.map +1 -1
- package/dist/components/InputBox.d.ts +7 -0
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +81 -30
- package/dist/components/InputBox.js.map +1 -1
- package/dist/components/InputBox.test.js +133 -10
- package/dist/components/InputBox.test.js.map +1 -1
- package/dist/components/LargeUncachedConfirmationPrompt.d.ts +2 -1
- package/dist/components/LargeUncachedConfirmationPrompt.d.ts.map +1 -1
- package/dist/components/LargeUncachedConfirmationPrompt.js +8 -3
- package/dist/components/LargeUncachedConfirmationPrompt.js.map +1 -1
- package/dist/components/LargeUncachedConfirmationPrompt.test.js +5 -4
- package/dist/components/LargeUncachedConfirmationPrompt.test.js.map +1 -1
- package/dist/components/MessageList.d.ts +2 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +6 -1
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/MessageList.test.js +26 -0
- package/dist/components/MessageList.test.js.map +1 -1
- package/dist/components/PathSelectionMenu.d.ts +1 -0
- package/dist/components/PathSelectionMenu.d.ts.map +1 -1
- package/dist/components/PathSelectionMenu.js +11 -9
- package/dist/components/PathSelectionMenu.js.map +1 -1
- package/dist/components/PathSelectionMenu.test.d.ts +2 -0
- package/dist/components/PathSelectionMenu.test.d.ts.map +1 -0
- package/dist/components/PathSelectionMenu.test.js +17 -0
- package/dist/components/PathSelectionMenu.test.js.map +1 -0
- package/dist/components/ProviderSelectionMenu.d.ts +15 -0
- package/dist/components/ProviderSelectionMenu.d.ts.map +1 -0
- package/dist/components/ProviderSelectionMenu.js +172 -0
- package/dist/components/ProviderSelectionMenu.js.map +1 -0
- package/dist/components/ProviderSelectionMenu.test.d.ts +2 -0
- package/dist/components/ProviderSelectionMenu.test.d.ts.map +1 -0
- package/dist/components/ProviderSelectionMenu.test.js +97 -0
- package/dist/components/ProviderSelectionMenu.test.js.map +1 -0
- package/dist/components/SettingsValueSelectionMenu.d.ts +2 -1
- package/dist/components/SettingsValueSelectionMenu.d.ts.map +1 -1
- package/dist/components/SettingsValueSelectionMenu.js +12 -6
- package/dist/components/SettingsValueSelectionMenu.js.map +1 -1
- package/dist/components/StatusBar.d.ts +1 -0
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +3 -2
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/StatusBar.test.js +11 -0
- package/dist/components/StatusBar.test.js.map +1 -1
- package/dist/context/InputContext.d.ts +1 -1
- package/dist/context/InputContext.d.ts.map +1 -1
- package/dist/context/InputContext.js.map +1 -1
- package/dist/hooks/use-app-commands.d.ts +2 -1
- package/dist/hooks/use-app-commands.d.ts.map +1 -1
- package/dist/hooks/use-app-commands.js +11 -2
- package/dist/hooks/use-app-commands.js.map +1 -1
- package/dist/hooks/use-app-commands.test.js +2 -0
- package/dist/hooks/use-app-commands.test.js.map +1 -1
- package/dist/hooks/use-conversation.clear.test.js +31 -0
- package/dist/hooks/use-conversation.clear.test.js.map +1 -1
- package/dist/hooks/use-conversation.d.ts +4 -1
- package/dist/hooks/use-conversation.d.ts.map +1 -1
- package/dist/hooks/use-conversation.js +18 -2
- package/dist/hooks/use-conversation.js.map +1 -1
- package/dist/hooks/use-escape-key.d.ts +12 -3
- package/dist/hooks/use-escape-key.d.ts.map +1 -1
- package/dist/hooks/use-escape-key.js +25 -11
- package/dist/hooks/use-escape-key.js.map +1 -1
- package/dist/hooks/use-escape-key.test.js +154 -14
- package/dist/hooks/use-escape-key.test.js.map +1 -1
- package/dist/hooks/use-mode-handlers.d.ts +13 -1
- package/dist/hooks/use-mode-handlers.d.ts.map +1 -1
- package/dist/hooks/use-mode-handlers.js +22 -1
- package/dist/hooks/use-mode-handlers.js.map +1 -1
- package/dist/hooks/use-mode-handlers.test.d.ts +2 -0
- package/dist/hooks/use-mode-handlers.test.d.ts.map +1 -0
- package/dist/hooks/use-mode-handlers.test.js +119 -0
- package/dist/hooks/use-mode-handlers.test.js.map +1 -0
- package/dist/hooks/use-path-completion.d.ts +10 -3
- package/dist/hooks/use-path-completion.d.ts.map +1 -1
- package/dist/hooks/use-path-completion.js +53 -9
- package/dist/hooks/use-path-completion.js.map +1 -1
- package/dist/hooks/use-path-completion.test.js +61 -49
- package/dist/hooks/use-path-completion.test.js.map +1 -1
- package/dist/hooks/use-provider-selection.d.ts +60 -0
- package/dist/hooks/use-provider-selection.d.ts.map +1 -0
- package/dist/hooks/use-provider-selection.js +767 -0
- package/dist/hooks/use-provider-selection.js.map +1 -0
- package/dist/hooks/use-provider-selection.test.d.ts +2 -0
- package/dist/hooks/use-provider-selection.test.d.ts.map +1 -0
- package/dist/hooks/use-provider-selection.test.js +793 -0
- package/dist/hooks/use-provider-selection.test.js.map +1 -0
- package/dist/hooks/use-runtime-settings.d.ts.map +1 -1
- package/dist/hooks/use-runtime-settings.js +2 -2
- package/dist/hooks/use-runtime-settings.js.map +1 -1
- package/dist/hooks/use-selection.d.ts +3 -1
- package/dist/hooks/use-selection.d.ts.map +1 -1
- package/dist/hooks/use-selection.js +115 -24
- package/dist/hooks/use-selection.js.map +1 -1
- package/dist/hooks/use-selection.test.js +96 -2
- package/dist/hooks/use-selection.test.js.map +1 -1
- package/dist/hooks/use-settings-value-completion.d.ts +3 -0
- package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
- package/dist/hooks/use-settings-value-completion.js +140 -18
- package/dist/hooks/use-settings-value-completion.js.map +1 -1
- package/dist/hooks/use-settings-value-completion.test.js +71 -1
- package/dist/hooks/use-settings-value-completion.test.js.map +1 -1
- package/dist/hooks/use-trigger-detection.d.ts +4 -2
- package/dist/hooks/use-trigger-detection.d.ts.map +1 -1
- package/dist/hooks/use-trigger-detection.js +42 -8
- package/dist/hooks/use-trigger-detection.js.map +1 -1
- package/dist/hooks/use-trigger-detection.test.js +2 -1
- package/dist/hooks/use-trigger-detection.test.js.map +1 -1
- package/dist/lib/openai-agent-client.chat.test.js +8 -0
- package/dist/lib/openai-agent-client.chat.test.js.map +1 -1
- package/dist/lib/openai-agent-client.d.ts +11 -6
- package/dist/lib/openai-agent-client.d.ts.map +1 -1
- package/dist/lib/openai-agent-client.js +199 -15
- package/dist/lib/openai-agent-client.js.map +1 -1
- package/dist/lib/openai-agent-client.public-methods.test.js +326 -23
- package/dist/lib/openai-agent-client.public-methods.test.js.map +1 -1
- package/dist/lib/openai-agent-client.test.js +62 -0
- package/dist/lib/openai-agent-client.test.js.map +1 -1
- package/dist/lib/tool-invoke.d.ts +27 -0
- package/dist/lib/tool-invoke.d.ts.map +1 -1
- package/dist/lib/tool-invoke.js +124 -95
- package/dist/lib/tool-invoke.js.map +1 -1
- package/dist/lib/tool-invoke.test.js +84 -22
- package/dist/lib/tool-invoke.test.js.map +1 -1
- package/dist/non-interactive.d.ts +2 -1
- package/dist/non-interactive.d.ts.map +1 -1
- package/dist/non-interactive.js +10 -2
- package/dist/non-interactive.js.map +1 -1
- package/dist/prompts/orchestrator.md +27 -41
- package/dist/prompts/plan-mode-info.md +2 -0
- package/dist/prompts/prompt-constructor.d.ts.map +1 -1
- package/dist/prompts/prompt-constructor.js +8 -0
- package/dist/prompts/prompt-constructor.js.map +1 -1
- package/dist/prompts/prompt-constructor.test.js +13 -0
- package/dist/prompts/prompt-constructor.test.js.map +1 -1
- package/dist/prompts/prompt-constructor.test.ts +16 -0
- package/dist/prompts/prompt-constructor.ts +10 -0
- package/dist/prompts/subagent-delegation.d.ts.map +1 -1
- package/dist/prompts/subagent-delegation.js +17 -1
- package/dist/prompts/subagent-delegation.js.map +1 -1
- package/dist/prompts/subagent-delegation.test.js +21 -0
- package/dist/prompts/subagent-delegation.test.js.map +1 -1
- package/dist/prompts/subagent-delegation.test.ts +26 -0
- package/dist/prompts/subagent-delegation.ts +17 -1
- package/dist/prompts/subagents/base-anthropic.md +3 -0
- package/dist/prompts/subagents/base-codex.md +3 -0
- package/dist/prompts/subagents/base-gpt-5-modern.md +3 -0
- package/dist/prompts/subagents/base-simple.md +3 -0
- package/dist/prompts/subagents/explorer.md +8 -10
- package/dist/prompts/subagents/researcher.md +2 -1
- package/dist/prompts/subagents/worker.md +12 -3
- package/dist/prompts/subagents/worktree-hygiene.md +6 -0
- package/dist/providers/ai-sdk-anthropic.provider.d.ts +4 -1
- package/dist/providers/ai-sdk-anthropic.provider.d.ts.map +1 -1
- package/dist/providers/ai-sdk-anthropic.provider.js +65 -22
- package/dist/providers/ai-sdk-anthropic.provider.js.map +1 -1
- package/dist/providers/ai-sdk-anthropic.provider.test.js +24 -1
- package/dist/providers/ai-sdk-anthropic.provider.test.js.map +1 -1
- package/dist/providers/anthropic-middleware.d.ts +25 -0
- package/dist/providers/anthropic-middleware.d.ts.map +1 -0
- package/dist/providers/anthropic-middleware.js +32 -0
- package/dist/providers/anthropic-middleware.js.map +1 -0
- package/dist/providers/anthropic-middleware.test.d.ts +2 -0
- package/dist/providers/anthropic-middleware.test.d.ts.map +1 -0
- package/dist/providers/anthropic-middleware.test.js +142 -0
- package/dist/providers/anthropic-middleware.test.js.map +1 -0
- package/dist/providers/codex-responses-model.d.ts.map +1 -1
- package/dist/providers/codex-responses-model.js +47 -30
- package/dist/providers/codex-responses-model.js.map +1 -1
- package/dist/providers/codex-responses-model.test.js +31 -0
- package/dist/providers/codex-responses-model.test.js.map +1 -1
- package/dist/providers/codex.provider.d.ts.map +1 -1
- package/dist/providers/codex.provider.js +16 -9
- package/dist/providers/codex.provider.js.map +1 -1
- package/dist/providers/codex.provider.test.js +2 -1
- package/dist/providers/codex.provider.test.js.map +1 -1
- package/dist/providers/custom-provider-adapter.test.js +6 -2
- package/dist/providers/custom-provider-adapter.test.js.map +1 -1
- package/dist/providers/fallback-responses-model.d.ts +3 -1
- package/dist/providers/fallback-responses-model.d.ts.map +1 -1
- package/dist/providers/fallback-responses-model.js +22 -3
- package/dist/providers/fallback-responses-model.js.map +1 -1
- package/dist/providers/fallback-responses-model.test.js +36 -8
- package/dist/providers/fallback-responses-model.test.js.map +1 -1
- package/dist/providers/fetch/composer.d.ts +1 -0
- package/dist/providers/fetch/composer.d.ts.map +1 -1
- package/dist/providers/fetch/composer.js +1 -0
- package/dist/providers/fetch/composer.js.map +1 -1
- package/dist/providers/fetch/composer.test.js +58 -17
- package/dist/providers/fetch/composer.test.js.map +1 -1
- package/dist/providers/fetch/logging-middleware.d.ts +3 -2
- package/dist/providers/fetch/logging-middleware.d.ts.map +1 -1
- package/dist/providers/fetch/logging-middleware.js +5 -2
- package/dist/providers/fetch/logging-middleware.js.map +1 -1
- package/dist/providers/openai-compatible-lazy.d.ts.map +1 -1
- package/dist/providers/openai-compatible-lazy.js +2 -1
- package/dist/providers/openai-compatible-lazy.js.map +1 -1
- package/dist/providers/openai-compatible-middleware.d.ts +5 -2
- package/dist/providers/openai-compatible-middleware.d.ts.map +1 -1
- package/dist/providers/openai-compatible-middleware.js +8 -11
- package/dist/providers/openai-compatible-middleware.js.map +1 -1
- package/dist/providers/openai-compatible.provider.d.ts +5 -1
- package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
- package/dist/providers/openai-compatible.provider.js +59 -37
- package/dist/providers/openai-compatible.provider.js.map +1 -1
- package/dist/providers/openai-compatible.provider.test.js +67 -3
- package/dist/providers/openai-compatible.provider.test.js.map +1 -1
- package/dist/providers/openai.provider.d.ts +8 -1
- package/dist/providers/openai.provider.d.ts.map +1 -1
- package/dist/providers/openai.provider.js +43 -10
- package/dist/providers/openai.provider.js.map +1 -1
- package/dist/providers/openai.provider.test.js +53 -16
- package/dist/providers/openai.provider.test.js.map +1 -1
- package/dist/providers/opencode-routing.d.ts +4 -0
- package/dist/providers/opencode-routing.d.ts.map +1 -0
- package/dist/providers/opencode-routing.js +14 -0
- package/dist/providers/opencode-routing.js.map +1 -0
- package/dist/providers/opencode-session.d.ts +19 -0
- package/dist/providers/opencode-session.d.ts.map +1 -0
- package/dist/providers/opencode-session.js +76 -0
- package/dist/providers/opencode-session.js.map +1 -0
- package/dist/providers/opencode.provider.d.ts +0 -8
- package/dist/providers/opencode.provider.d.ts.map +1 -1
- package/dist/providers/opencode.provider.js +0 -32
- package/dist/providers/opencode.provider.js.map +1 -1
- package/dist/providers/opencode.provider.test.d.ts +2 -0
- package/dist/providers/opencode.provider.test.d.ts.map +1 -0
- package/dist/providers/opencode.provider.test.js +186 -0
- package/dist/providers/opencode.provider.test.js.map +1 -0
- package/dist/providers/openrouter.provider.createRunner.test.js +5 -0
- package/dist/providers/openrouter.provider.createRunner.test.js.map +1 -1
- package/dist/providers/openrouter.provider.d.ts.map +1 -1
- package/dist/providers/openrouter.provider.js +8 -4
- package/dist/providers/openrouter.provider.js.map +1 -1
- package/dist/providers/registry.d.ts +3 -0
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/registry.test.js +1 -0
- package/dist/providers/registry.test.js.map +1 -1
- package/dist/scripts/eval-auto-approval/runner.js +4 -0
- package/dist/scripts/eval-auto-approval/runner.js.map +1 -1
- package/dist/services/conversation-integration.test.js +8 -4
- package/dist/services/conversation-integration.test.js.map +1 -1
- package/dist/services/conversation-persistence.test.js +7 -0
- package/dist/services/conversation-persistence.test.js.map +1 -1
- package/dist/services/conversation-replay.d.ts.map +1 -1
- package/dist/services/conversation-replay.js +95 -4
- package/dist/services/conversation-replay.js.map +1 -1
- package/dist/services/conversation-replay.test.js +312 -0
- package/dist/services/conversation-replay.test.js.map +1 -1
- package/dist/services/conversation-result-builder.test.js +9 -0
- package/dist/services/conversation-result-builder.test.js.map +1 -1
- package/dist/services/conversation-service.d.ts +3 -1
- package/dist/services/conversation-service.d.ts.map +1 -1
- package/dist/services/conversation-service.js +3 -0
- package/dist/services/conversation-service.js.map +1 -1
- package/dist/services/conversation-session-reasoning.test.js +5 -1
- package/dist/services/conversation-session-reasoning.test.js.map +1 -1
- package/dist/services/conversation-session.auto-approval.test.js +25 -1
- package/dist/services/conversation-session.auto-approval.test.js.map +1 -1
- package/dist/services/conversation-session.d.ts +7 -2
- package/dist/services/conversation-session.d.ts.map +1 -1
- package/dist/services/conversation-session.js +70 -45
- package/dist/services/conversation-session.js.map +1 -1
- package/dist/services/file-service.d.ts +20 -4
- package/dist/services/file-service.d.ts.map +1 -1
- package/dist/services/file-service.js +111 -52
- package/dist/services/file-service.js.map +1 -1
- package/dist/services/file-service.test.d.ts +2 -0
- package/dist/services/file-service.test.d.ts.map +1 -0
- package/dist/services/file-service.test.js +60 -0
- package/dist/services/file-service.test.js.map +1 -0
- package/dist/services/logging-service.d.ts +0 -3
- package/dist/services/logging-service.d.ts.map +1 -1
- package/dist/services/logging-service.js +12 -8
- package/dist/services/logging-service.js.map +1 -1
- package/dist/services/mode-notices.d.ts +4 -5
- package/dist/services/mode-notices.d.ts.map +1 -1
- package/dist/services/mode-notices.js +4 -5
- package/dist/services/mode-notices.js.map +1 -1
- package/dist/services/notification-service.d.ts +31 -11
- package/dist/services/notification-service.d.ts.map +1 -1
- package/dist/services/notification-service.js +88 -40
- package/dist/services/notification-service.js.map +1 -1
- package/dist/services/notification-service.test.js +135 -62
- package/dist/services/notification-service.test.js.map +1 -1
- package/dist/services/provider-traffic.d.ts +8 -9
- package/dist/services/provider-traffic.d.ts.map +1 -1
- package/dist/services/provider-traffic.js +24 -12
- package/dist/services/provider-traffic.js.map +1 -1
- package/dist/services/provider-traffic.test.js +51 -1
- package/dist/services/provider-traffic.test.js.map +1 -1
- package/dist/services/service-interfaces.d.ts +12 -16
- package/dist/services/service-interfaces.d.ts.map +1 -1
- package/dist/services/session-context-service.d.ts +8 -0
- package/dist/services/session-context-service.d.ts.map +1 -0
- package/dist/services/session-context-service.js +19 -0
- package/dist/services/session-context-service.js.map +1 -0
- package/dist/services/session-context-service.test.d.ts +2 -0
- package/dist/services/session-context-service.test.d.ts.map +1 -0
- package/dist/services/session-context-service.test.js +38 -0
- package/dist/services/session-context-service.test.js.map +1 -0
- package/dist/services/settings-env.d.ts.map +1 -1
- package/dist/services/settings-env.js +4 -1
- package/dist/services/settings-env.js.map +1 -1
- package/dist/services/settings-persistence.d.ts.map +1 -1
- package/dist/services/settings-persistence.js +0 -1
- package/dist/services/settings-persistence.js.map +1 -1
- package/dist/services/settings-persistence.test.js +2 -2
- package/dist/services/settings-persistence.test.js.map +1 -1
- package/dist/services/settings-schema.d.ts +9 -0
- package/dist/services/settings-schema.d.ts.map +1 -1
- package/dist/services/settings-schema.js +15 -4
- package/dist/services/settings-schema.js.map +1 -1
- package/dist/services/settings-service.d.ts +8 -0
- package/dist/services/settings-service.d.ts.map +1 -1
- package/dist/services/settings-service.js +58 -21
- package/dist/services/settings-service.js.map +1 -1
- package/dist/services/shell-auto-approval-evaluator.d.ts +3 -2
- package/dist/services/shell-auto-approval-evaluator.d.ts.map +1 -1
- package/dist/services/shell-auto-approval-evaluator.js +3 -3
- package/dist/services/shell-auto-approval-evaluator.js.map +1 -1
- package/dist/services/shell-auto-approval-evaluator.test.js +20 -0
- package/dist/services/shell-auto-approval-evaluator.test.js.map +1 -1
- package/dist/services/shell-auto-approval-resolver.d.ts +2 -1
- package/dist/services/shell-auto-approval-resolver.d.ts.map +1 -1
- package/dist/services/shell-auto-approval-resolver.js +2 -0
- package/dist/services/shell-auto-approval-resolver.js.map +1 -1
- package/dist/services/shell-auto-approval-resolver.test.js +10 -0
- package/dist/services/shell-auto-approval-resolver.test.js.map +1 -1
- package/dist/services/subagents/subagent-manager.d.ts +2 -1
- package/dist/services/subagents/subagent-manager.d.ts.map +1 -1
- package/dist/services/subagents/subagent-manager.js +182 -13
- package/dist/services/subagents/subagent-manager.js.map +1 -1
- package/dist/services/subagents/subagent-manager.test.js +548 -17
- package/dist/services/subagents/subagent-manager.test.js.map +1 -1
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +2 -2
- package/dist/tools/apply-patch.js.map +1 -1
- package/dist/tools/ask-user-constants.d.ts +7 -0
- package/dist/tools/ask-user-constants.d.ts.map +1 -0
- package/dist/tools/ask-user-constants.js +7 -0
- package/dist/tools/ask-user-constants.js.map +1 -0
- package/dist/tools/ask-user.d.ts +11 -0
- package/dist/tools/ask-user.d.ts.map +1 -0
- package/dist/tools/ask-user.js +56 -0
- package/dist/tools/ask-user.js.map +1 -0
- package/dist/tools/ask-user.test.d.ts +2 -0
- package/dist/tools/ask-user.test.d.ts.map +1 -0
- package/dist/tools/ask-user.test.js +129 -0
- package/dist/tools/ask-user.test.js.map +1 -0
- package/dist/tools/format-helpers.d.ts +13 -0
- package/dist/tools/format-helpers.d.ts.map +1 -1
- package/dist/tools/format-helpers.js +34 -0
- package/dist/tools/format-helpers.js.map +1 -1
- package/dist/tools/format-helpers.test.d.ts +2 -0
- package/dist/tools/format-helpers.test.d.ts.map +1 -0
- package/dist/tools/format-helpers.test.js +51 -0
- package/dist/tools/format-helpers.test.js.map +1 -0
- package/dist/tools/search-replace.d.ts.map +1 -1
- package/dist/tools/search-replace.js +2 -2
- package/dist/tools/search-replace.js.map +1 -1
- package/dist/tools/tool-names.d.ts +1 -0
- package/dist/tools/tool-names.d.ts.map +1 -1
- package/dist/tools/tool-names.js +1 -0
- package/dist/tools/tool-names.js.map +1 -1
- package/dist/utils/command-safety/_scratch-classify.test.d.ts +2 -0
- package/dist/utils/command-safety/_scratch-classify.test.d.ts.map +1 -0
- package/dist/utils/command-safety/_scratch-classify.test.js +22 -0
- package/dist/utils/command-safety/_scratch-classify.test.js.map +1 -0
- package/dist/utils/command-safety/constants.d.ts.map +1 -1
- package/dist/utils/command-safety/constants.js +21 -0
- package/dist/utils/command-safety/constants.js.map +1 -1
- package/dist/utils/command-safety/handlers/git-handler.d.ts.map +1 -1
- package/dist/utils/command-safety/handlers/git-handler.js +28 -2
- package/dist/utils/command-safety/handlers/git-handler.js.map +1 -1
- package/dist/utils/command-safety/path-analysis.d.ts.map +1 -1
- package/dist/utils/command-safety/path-analysis.js +6 -0
- package/dist/utils/command-safety/path-analysis.js.map +1 -1
- package/dist/utils/command-safety/scratch-classify.test.d.ts +2 -0
- package/dist/utils/command-safety/scratch-classify.test.d.ts.map +1 -0
- package/dist/utils/command-safety/scratch-classify.test.js +22 -0
- package/dist/utils/command-safety/scratch-classify.test.js.map +1 -0
- package/dist/utils/command-safety/scratch-tally.test.d.ts +2 -0
- package/dist/utils/command-safety/scratch-tally.test.d.ts.map +1 -0
- package/dist/utils/command-safety/scratch-tally.test.js +68 -0
- package/dist/utils/command-safety/scratch-tally.test.js.map +1 -0
- package/dist/utils/command-safety.evaluator-false-positives.test.d.ts +2 -0
- package/dist/utils/command-safety.evaluator-false-positives.test.d.ts.map +1 -0
- package/dist/utils/command-safety.evaluator-false-positives.test.js +72 -0
- package/dist/utils/command-safety.evaluator-false-positives.test.js.map +1 -0
- package/dist/utils/header-sanitizer.d.ts +2 -0
- package/dist/utils/header-sanitizer.d.ts.map +1 -0
- package/dist/utils/header-sanitizer.js +38 -0
- package/dist/utils/header-sanitizer.js.map +1 -0
- package/dist/utils/header-sanitizer.test.d.ts +2 -0
- package/dist/utils/header-sanitizer.test.d.ts.map +1 -0
- package/dist/utils/header-sanitizer.test.js +56 -0
- package/dist/utils/header-sanitizer.test.js.map +1 -0
- package/dist/utils/home-directory-start-guard.d.ts +10 -0
- package/dist/utils/home-directory-start-guard.d.ts.map +1 -0
- package/dist/utils/home-directory-start-guard.js +20 -0
- package/dist/utils/home-directory-start-guard.js.map +1 -0
- package/dist/utils/home-directory-start-guard.test.d.ts +2 -0
- package/dist/utils/home-directory-start-guard.test.d.ts.map +1 -0
- package/dist/utils/home-directory-start-guard.test.js +40 -0
- package/dist/utils/home-directory-start-guard.test.js.map +1 -0
- package/dist/utils/inject-warning-into-tool-output.d.ts +2 -0
- package/dist/utils/inject-warning-into-tool-output.d.ts.map +1 -0
- package/dist/utils/inject-warning-into-tool-output.js +66 -0
- package/dist/utils/inject-warning-into-tool-output.js.map +1 -0
- package/dist/utils/provider-traffic-extractor.d.ts +1 -0
- package/dist/utils/provider-traffic-extractor.d.ts.map +1 -1
- package/dist/utils/provider-traffic-extractor.js +1 -0
- package/dist/utils/provider-traffic-extractor.js.map +1 -1
- package/dist/utils/provider-traffic-extractor.test.js +2 -0
- package/dist/utils/provider-traffic-extractor.test.js.map +1 -1
- package/dist/utils/token-usage.d.ts +3 -1
- package/dist/utils/token-usage.d.ts.map +1 -1
- package/dist/utils/token-usage.js +7 -2
- package/dist/utils/token-usage.js.map +1 -1
- package/dist/utils/token-usage.test.js +15 -0
- package/dist/utils/token-usage.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
3
|
+
import test from 'ava';
|
|
4
|
+
import React, { act, useEffect } from 'react';
|
|
5
|
+
import { render } from 'ink-testing-library';
|
|
6
|
+
import { useProviderSelection } from './use-provider-selection.js';
|
|
7
|
+
import { InputProvider } from '../context/InputContext.js';
|
|
8
|
+
const TestComponent = ({ settingsService, onHookResult, }) => {
|
|
9
|
+
const hook = useProviderSelection(settingsService);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
onHookResult(hook);
|
|
12
|
+
});
|
|
13
|
+
return null;
|
|
14
|
+
};
|
|
15
|
+
const flush = async () => {
|
|
16
|
+
await act(async () => {
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
function createMockSettingsService(initialProviders = [], initialActive = 'openai') {
|
|
21
|
+
const settings = new Map([
|
|
22
|
+
['providers', initialProviders],
|
|
23
|
+
['agent.provider', initialActive],
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
get: (key) => settings.get(key),
|
|
27
|
+
set: (key, value) => settings.set(key, value),
|
|
28
|
+
setPersistent: (key, value) => settings.set(key, value),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
test.serial('useProviderSelection - lists registered and custom providers on open', async (t) => {
|
|
32
|
+
const customProviders = [{ name: 'custom-ollama', type: 'openai-compatible', baseUrl: 'http://localhost:11434/v1' }];
|
|
33
|
+
const settingsService = createMockSettingsService(customProviders, 'openai');
|
|
34
|
+
let hook;
|
|
35
|
+
let renderer;
|
|
36
|
+
await act(async () => {
|
|
37
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
38
|
+
settingsService,
|
|
39
|
+
onHookResult: (h) => {
|
|
40
|
+
hook = h;
|
|
41
|
+
},
|
|
42
|
+
})));
|
|
43
|
+
});
|
|
44
|
+
t.true(hook !== undefined);
|
|
45
|
+
await act(async () => {
|
|
46
|
+
hook.open();
|
|
47
|
+
});
|
|
48
|
+
await flush();
|
|
49
|
+
t.is(hook.phase, 'list');
|
|
50
|
+
const items = hook.items;
|
|
51
|
+
t.true(items.some((item) => item.id === 'openai' && !item.isCustom && item.isActive));
|
|
52
|
+
t.true(items.some((item) => item.id === 'custom-ollama' && item.isCustom && !item.isActive));
|
|
53
|
+
await act(async () => {
|
|
54
|
+
renderer.unmount();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
test.serial('useProviderSelection - selecting a custom provider opens edit_fields directly', async (t) => {
|
|
58
|
+
const customProviders = [{ name: 'custom-ollama', type: 'openai-compatible', baseUrl: 'http://localhost:11434/v1' }];
|
|
59
|
+
const settingsService = createMockSettingsService(customProviders, 'openai');
|
|
60
|
+
let hook;
|
|
61
|
+
let renderer;
|
|
62
|
+
await act(async () => {
|
|
63
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
64
|
+
settingsService,
|
|
65
|
+
onHookResult: (h) => {
|
|
66
|
+
hook = h;
|
|
67
|
+
},
|
|
68
|
+
})));
|
|
69
|
+
});
|
|
70
|
+
await act(async () => {
|
|
71
|
+
hook.open();
|
|
72
|
+
});
|
|
73
|
+
await flush();
|
|
74
|
+
// Test selecting custom-ollama
|
|
75
|
+
await act(async () => {
|
|
76
|
+
const active = hook.getActiveItems();
|
|
77
|
+
const targetIdx = active.findIndex((item) => item.kind === 'provider' && item.id === 'custom-ollama');
|
|
78
|
+
let moves = 0;
|
|
79
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
80
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
81
|
+
moves++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (let i = 0; i < moves; i++) {
|
|
85
|
+
hook.moveDown();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
await flush();
|
|
89
|
+
await act(async () => {
|
|
90
|
+
hook.selectItem();
|
|
91
|
+
});
|
|
92
|
+
await flush();
|
|
93
|
+
t.is(hook.phase, 'edit_fields');
|
|
94
|
+
t.is(hook.selectedIndex, 0);
|
|
95
|
+
t.is(hook.draft?.name, 'custom-ollama');
|
|
96
|
+
t.is(hook.draft?.baseUrl, 'http://localhost:11434/v1');
|
|
97
|
+
await act(async () => {
|
|
98
|
+
renderer.unmount();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
test.serial('useProviderSelection - selecting a built-in provider (except Codex) enters editing flow; Codex is a no-op', async (t) => {
|
|
102
|
+
const settingsService = createMockSettingsService([], 'openai');
|
|
103
|
+
let hook;
|
|
104
|
+
let renderer;
|
|
105
|
+
await act(async () => {
|
|
106
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
107
|
+
settingsService,
|
|
108
|
+
onHookResult: (h) => {
|
|
109
|
+
hook = h;
|
|
110
|
+
},
|
|
111
|
+
})));
|
|
112
|
+
});
|
|
113
|
+
await act(async () => {
|
|
114
|
+
hook.open();
|
|
115
|
+
});
|
|
116
|
+
await flush();
|
|
117
|
+
// Verify Codex is skipped by keyboard navigation
|
|
118
|
+
const active = hook.getActiveItems();
|
|
119
|
+
const codexIdx = active.findIndex((item) => item.kind === 'provider' && item.id === 'codex');
|
|
120
|
+
t.true(codexIdx !== -1);
|
|
121
|
+
// Let's verify that navigating moveDown starting from index 0 skips codexIdx
|
|
122
|
+
t.is(hook.selectedIndex, 0); // Start at OpenAI
|
|
123
|
+
// Move down a fixed number of times. We should never land on codexIdx!
|
|
124
|
+
const visitedIndices = new Set();
|
|
125
|
+
await act(async () => {
|
|
126
|
+
for (let i = 0; i < active.length; i++) {
|
|
127
|
+
visitedIndices.add(hook.selectedIndex);
|
|
128
|
+
hook.moveDown();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
await flush();
|
|
132
|
+
t.false(visitedIndices.has(codexIdx), 'Keyboard navigation should skip Codex');
|
|
133
|
+
// Navigate to OpenAI (first item, idx 0) and select it
|
|
134
|
+
await act(async () => {
|
|
135
|
+
for (let i = 0; i < active.length; i++) {
|
|
136
|
+
if (hook.selectedIndex === 0)
|
|
137
|
+
break;
|
|
138
|
+
hook.moveUp();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
await flush();
|
|
142
|
+
await act(async () => {
|
|
143
|
+
hook.selectItem();
|
|
144
|
+
});
|
|
145
|
+
await flush();
|
|
146
|
+
// OpenAI opens edit_fields
|
|
147
|
+
t.is(hook.phase, 'edit_fields');
|
|
148
|
+
t.is(hook.selectedIndex, 2); // API Key is selected
|
|
149
|
+
t.is(hook.draft?.name, 'OpenAI');
|
|
150
|
+
// Verify we can edit API Key and save it
|
|
151
|
+
await act(async () => {
|
|
152
|
+
hook.selectItem(); // select API Key at index 2
|
|
153
|
+
});
|
|
154
|
+
await flush();
|
|
155
|
+
t.is(hook.phase, 'wizard_key');
|
|
156
|
+
await act(async () => {
|
|
157
|
+
hook.handleTextInputSubmit('sk-mock-openai-key');
|
|
158
|
+
});
|
|
159
|
+
await flush();
|
|
160
|
+
t.is(hook.phase, 'edit_fields');
|
|
161
|
+
t.is(hook.selectedIndex, 2); // Selected row is API Key row
|
|
162
|
+
await act(async () => {
|
|
163
|
+
hook.moveDown(); // Move to "Save Changes" at index 3
|
|
164
|
+
});
|
|
165
|
+
await flush();
|
|
166
|
+
t.is(hook.selectedIndex, 3);
|
|
167
|
+
await act(async () => {
|
|
168
|
+
hook.selectItem(); // Save Changes
|
|
169
|
+
});
|
|
170
|
+
await flush();
|
|
171
|
+
t.is(hook.phase, 'list');
|
|
172
|
+
t.is(settingsService.get('agent.openai.apiKey'), 'sk-mock-openai-key');
|
|
173
|
+
await act(async () => {
|
|
174
|
+
renderer.unmount();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
test.serial('useProviderSelection - requestDelete on a custom provider opens confirm_delete', async (t) => {
|
|
178
|
+
const customProviders = [{ name: 'custom-ollama', type: 'openai-compatible', baseUrl: 'http://localhost:11434/v1' }];
|
|
179
|
+
const settingsService = createMockSettingsService(customProviders, 'openai');
|
|
180
|
+
let hook;
|
|
181
|
+
let renderer;
|
|
182
|
+
await act(async () => {
|
|
183
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
184
|
+
settingsService,
|
|
185
|
+
onHookResult: (h) => {
|
|
186
|
+
hook = h;
|
|
187
|
+
},
|
|
188
|
+
})));
|
|
189
|
+
});
|
|
190
|
+
await act(async () => {
|
|
191
|
+
hook.open();
|
|
192
|
+
});
|
|
193
|
+
await flush();
|
|
194
|
+
// Navigate to custom-ollama
|
|
195
|
+
await act(async () => {
|
|
196
|
+
const active = hook.getActiveItems();
|
|
197
|
+
const targetIdx = active.findIndex((item) => item.kind === 'provider' && item.id === 'custom-ollama');
|
|
198
|
+
let moves = 0;
|
|
199
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
200
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
201
|
+
moves++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (let i = 0; i < moves; i++) {
|
|
205
|
+
hook.moveDown();
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
await flush();
|
|
209
|
+
await act(async () => {
|
|
210
|
+
hook.requestDelete();
|
|
211
|
+
});
|
|
212
|
+
await flush();
|
|
213
|
+
t.is(hook.phase, 'confirm_delete');
|
|
214
|
+
// Default cursor should be on "No, keep it" for safety.
|
|
215
|
+
t.is(hook.selectedIndex, 1);
|
|
216
|
+
// Confirming "No, keep it" returns to the list.
|
|
217
|
+
await act(async () => {
|
|
218
|
+
hook.selectItem();
|
|
219
|
+
});
|
|
220
|
+
await flush();
|
|
221
|
+
t.is(hook.phase, 'list');
|
|
222
|
+
t.is(settingsService.get('providers').length, 1);
|
|
223
|
+
// Now request delete again, this time confirm "Yes".
|
|
224
|
+
await act(async () => {
|
|
225
|
+
const active = hook.getActiveItems();
|
|
226
|
+
const targetIdx = active.findIndex((item) => item.kind === 'provider' && item.id === 'custom-ollama');
|
|
227
|
+
let moves = 0;
|
|
228
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
229
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
230
|
+
moves++;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (let i = 0; i < moves; i++) {
|
|
234
|
+
hook.moveDown();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
await flush();
|
|
238
|
+
await act(async () => {
|
|
239
|
+
hook.requestDelete();
|
|
240
|
+
});
|
|
241
|
+
await flush();
|
|
242
|
+
t.is(hook.phase, 'confirm_delete');
|
|
243
|
+
await act(async () => {
|
|
244
|
+
hook.moveUp(); // move from default "No" to "Yes"
|
|
245
|
+
});
|
|
246
|
+
await flush();
|
|
247
|
+
t.is(hook.selectedIndex, 0);
|
|
248
|
+
await act(async () => {
|
|
249
|
+
hook.selectItem();
|
|
250
|
+
});
|
|
251
|
+
await flush();
|
|
252
|
+
t.is(hook.phase, 'list');
|
|
253
|
+
t.is(settingsService.get('providers').length, 0);
|
|
254
|
+
t.is(settingsService.get('agent.provider'), 'openai');
|
|
255
|
+
await act(async () => {
|
|
256
|
+
renderer.unmount();
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
test.serial('useProviderSelection - requestDelete on a built-in is a no-op', async (t) => {
|
|
260
|
+
const settingsService = createMockSettingsService([], 'openai');
|
|
261
|
+
let hook;
|
|
262
|
+
let renderer;
|
|
263
|
+
await act(async () => {
|
|
264
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
265
|
+
settingsService,
|
|
266
|
+
onHookResult: (h) => {
|
|
267
|
+
hook = h;
|
|
268
|
+
},
|
|
269
|
+
})));
|
|
270
|
+
});
|
|
271
|
+
await act(async () => {
|
|
272
|
+
hook.open();
|
|
273
|
+
});
|
|
274
|
+
await flush();
|
|
275
|
+
// openai is at index 0 in the built-ins.
|
|
276
|
+
await act(async () => {
|
|
277
|
+
hook.requestDelete();
|
|
278
|
+
});
|
|
279
|
+
await flush();
|
|
280
|
+
t.is(hook.phase, 'list');
|
|
281
|
+
await act(async () => {
|
|
282
|
+
renderer.unmount();
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
test.serial('useProviderSelection - add provider wizard flow and validation', async (t) => {
|
|
286
|
+
const settingsService = createMockSettingsService([], 'openai');
|
|
287
|
+
let hook;
|
|
288
|
+
let renderer;
|
|
289
|
+
await act(async () => {
|
|
290
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
291
|
+
settingsService,
|
|
292
|
+
onHookResult: (h) => {
|
|
293
|
+
hook = h;
|
|
294
|
+
},
|
|
295
|
+
})));
|
|
296
|
+
});
|
|
297
|
+
await act(async () => {
|
|
298
|
+
hook.open();
|
|
299
|
+
});
|
|
300
|
+
await flush();
|
|
301
|
+
// Select "Add Custom Provider" which is at the end of the list
|
|
302
|
+
await act(async () => {
|
|
303
|
+
const active = hook.getActiveItems();
|
|
304
|
+
const targetIdx = active.findIndex((item) => item.kind === 'add-provider');
|
|
305
|
+
let moves = 0;
|
|
306
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
307
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
308
|
+
moves++;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
for (let i = 0; i < moves; i++) {
|
|
312
|
+
hook.moveDown();
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
await flush();
|
|
316
|
+
await act(async () => {
|
|
317
|
+
hook.selectItem();
|
|
318
|
+
});
|
|
319
|
+
await flush();
|
|
320
|
+
t.is(hook.phase, 'wizard_name');
|
|
321
|
+
// Submit empty name (fails)
|
|
322
|
+
let success = false;
|
|
323
|
+
await act(async () => {
|
|
324
|
+
success = hook.handleTextInputSubmit('');
|
|
325
|
+
});
|
|
326
|
+
await flush();
|
|
327
|
+
t.false(success);
|
|
328
|
+
t.is(hook.errorMessage, 'Name cannot be empty.');
|
|
329
|
+
// Submit invalid name format (fails)
|
|
330
|
+
await act(async () => {
|
|
331
|
+
success = hook.handleTextInputSubmit('my provider');
|
|
332
|
+
});
|
|
333
|
+
await flush();
|
|
334
|
+
t.false(success);
|
|
335
|
+
t.is(hook.errorMessage, 'Name must start with a letter or number and contain only letters, numbers, hyphens, underscores, and dots.');
|
|
336
|
+
// Submit duplicate/built-in name (fails)
|
|
337
|
+
await act(async () => {
|
|
338
|
+
success = hook.handleTextInputSubmit('openai');
|
|
339
|
+
});
|
|
340
|
+
await flush();
|
|
341
|
+
t.false(success);
|
|
342
|
+
t.true(hook.errorMessage.includes('already exists'));
|
|
343
|
+
// Submit valid name (success)
|
|
344
|
+
await act(async () => {
|
|
345
|
+
success = hook.handleTextInputSubmit('my-custom-provider');
|
|
346
|
+
});
|
|
347
|
+
await flush();
|
|
348
|
+
t.true(success);
|
|
349
|
+
t.is(hook.phase, 'wizard_type');
|
|
350
|
+
t.is(hook.draft?.name, 'my-custom-provider');
|
|
351
|
+
// Select first type (openai-compatible)
|
|
352
|
+
await act(async () => {
|
|
353
|
+
hook.selectItem();
|
|
354
|
+
});
|
|
355
|
+
await flush();
|
|
356
|
+
t.is(hook.phase, 'wizard_url');
|
|
357
|
+
t.is(hook.draft?.type, 'openai-compatible');
|
|
358
|
+
// Submit invalid URL (fails)
|
|
359
|
+
await act(async () => {
|
|
360
|
+
success = hook.handleTextInputSubmit('not-a-url');
|
|
361
|
+
});
|
|
362
|
+
await flush();
|
|
363
|
+
t.false(success);
|
|
364
|
+
t.is(hook.errorMessage, 'Invalid URL format. Make sure it starts with http:// or https://');
|
|
365
|
+
// Submit valid URL (success)
|
|
366
|
+
await act(async () => {
|
|
367
|
+
success = hook.handleTextInputSubmit('http://localhost:8000/v1');
|
|
368
|
+
});
|
|
369
|
+
await flush();
|
|
370
|
+
t.true(success);
|
|
371
|
+
t.is(hook.phase, 'wizard_key');
|
|
372
|
+
t.is(hook.draft?.baseUrl, 'http://localhost:8000/v1');
|
|
373
|
+
// Submit empty API Key (success)
|
|
374
|
+
await act(async () => {
|
|
375
|
+
success = hook.handleTextInputSubmit('');
|
|
376
|
+
});
|
|
377
|
+
await flush();
|
|
378
|
+
t.is(hook.phase, 'edit_fields');
|
|
379
|
+
const editFields = hook.getActiveItems();
|
|
380
|
+
const baseUrlField = editFields.find((item) => item.kind === 'field' && item.label === 'Base URL');
|
|
381
|
+
t.truthy(baseUrlField);
|
|
382
|
+
t.is(baseUrlField?.detail, 'http://localhost:8000/v1');
|
|
383
|
+
t.false(baseUrlField?.detail?.includes('required') ?? false);
|
|
384
|
+
// Save changes
|
|
385
|
+
t.is(hook.selectedIndex, 4); // "Save Changes" should be pre-selected
|
|
386
|
+
await act(async () => {
|
|
387
|
+
hook.selectItem();
|
|
388
|
+
});
|
|
389
|
+
await flush();
|
|
390
|
+
t.is(hook.phase, 'list');
|
|
391
|
+
t.is(hook.errorMessage, null);
|
|
392
|
+
// Check persisted in settings
|
|
393
|
+
const persisted = settingsService.get('providers');
|
|
394
|
+
t.true(Array.isArray(persisted));
|
|
395
|
+
t.is(persisted.length, 1);
|
|
396
|
+
t.is(persisted[0].name, 'my-custom-provider');
|
|
397
|
+
t.is(persisted[0].type, 'openai-compatible');
|
|
398
|
+
t.is(persisted[0].baseUrl, 'http://localhost:8000/v1');
|
|
399
|
+
await act(async () => {
|
|
400
|
+
renderer.unmount();
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
test.serial('useProviderSelection - goBack returns wizard fields to the correct phase', async (t) => {
|
|
404
|
+
const customProviders = [
|
|
405
|
+
{ name: 'existing-provider', type: 'openai-compatible', baseUrl: 'http://localhost:11434/v1' },
|
|
406
|
+
];
|
|
407
|
+
const settingsService = createMockSettingsService(customProviders, 'existing-provider');
|
|
408
|
+
let hook;
|
|
409
|
+
let renderer;
|
|
410
|
+
await act(async () => {
|
|
411
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
412
|
+
settingsService,
|
|
413
|
+
onHookResult: (h) => {
|
|
414
|
+
hook = h;
|
|
415
|
+
},
|
|
416
|
+
})));
|
|
417
|
+
});
|
|
418
|
+
await act(async () => {
|
|
419
|
+
hook.open();
|
|
420
|
+
});
|
|
421
|
+
await flush();
|
|
422
|
+
await act(async () => {
|
|
423
|
+
const active = hook.getActiveItems();
|
|
424
|
+
const targetIdx = active.findIndex((item) => item.kind === 'provider' && item.id === 'existing-provider');
|
|
425
|
+
let moves = 0;
|
|
426
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
427
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
428
|
+
moves++;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
for (let i = 0; i < moves; i++) {
|
|
432
|
+
hook.moveDown();
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
await flush();
|
|
436
|
+
// Enter on a custom provider jumps straight to edit_fields.
|
|
437
|
+
await act(async () => {
|
|
438
|
+
hook.selectItem();
|
|
439
|
+
});
|
|
440
|
+
await flush();
|
|
441
|
+
t.is(hook.phase, 'edit_fields');
|
|
442
|
+
t.is(hook.selectedIndex, 0);
|
|
443
|
+
// Enter on the Name field opens the name editor.
|
|
444
|
+
await act(async () => {
|
|
445
|
+
hook.selectItem();
|
|
446
|
+
});
|
|
447
|
+
await flush();
|
|
448
|
+
t.is(hook.phase, 'wizard_name');
|
|
449
|
+
t.is(hook.selectedIndex, 0);
|
|
450
|
+
await act(async () => {
|
|
451
|
+
hook.goBack();
|
|
452
|
+
});
|
|
453
|
+
await flush();
|
|
454
|
+
t.is(hook.phase, 'edit_fields');
|
|
455
|
+
t.is(hook.selectedIndex, 0);
|
|
456
|
+
await act(async () => {
|
|
457
|
+
renderer.unmount();
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
test.serial('useProviderSelection - confirm discard appears when backing out of a modified wizard', async (t) => {
|
|
461
|
+
const settingsService = createMockSettingsService([], 'openai');
|
|
462
|
+
let hook;
|
|
463
|
+
let renderer;
|
|
464
|
+
await act(async () => {
|
|
465
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
466
|
+
settingsService,
|
|
467
|
+
onHookResult: (h) => {
|
|
468
|
+
hook = h;
|
|
469
|
+
},
|
|
470
|
+
})));
|
|
471
|
+
});
|
|
472
|
+
await act(async () => {
|
|
473
|
+
hook.open();
|
|
474
|
+
});
|
|
475
|
+
await flush();
|
|
476
|
+
await act(async () => {
|
|
477
|
+
const active = hook.getActiveItems();
|
|
478
|
+
const targetIdx = active.findIndex((item) => item.kind === 'add-provider');
|
|
479
|
+
let moves = 0;
|
|
480
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
481
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
482
|
+
moves++;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
for (let i = 0; i < moves; i++) {
|
|
486
|
+
hook.moveDown();
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
await flush();
|
|
490
|
+
await act(async () => {
|
|
491
|
+
hook.selectItem();
|
|
492
|
+
});
|
|
493
|
+
await flush();
|
|
494
|
+
t.is(hook.phase, 'wizard_name');
|
|
495
|
+
await act(async () => {
|
|
496
|
+
hook.handleTextInputSubmit('test-provider');
|
|
497
|
+
});
|
|
498
|
+
await flush();
|
|
499
|
+
t.is(hook.phase, 'wizard_type');
|
|
500
|
+
await act(async () => {
|
|
501
|
+
hook.goBack();
|
|
502
|
+
});
|
|
503
|
+
await flush();
|
|
504
|
+
t.is(hook.phase, 'confirm_discard');
|
|
505
|
+
t.is(hook.selectedIndex, 1);
|
|
506
|
+
await act(async () => {
|
|
507
|
+
hook.selectItem();
|
|
508
|
+
});
|
|
509
|
+
await flush();
|
|
510
|
+
t.is(hook.phase, 'wizard_type');
|
|
511
|
+
t.is(hook.selectedIndex, 0);
|
|
512
|
+
await act(async () => {
|
|
513
|
+
hook.goBack();
|
|
514
|
+
});
|
|
515
|
+
await flush();
|
|
516
|
+
t.is(hook.phase, 'confirm_discard');
|
|
517
|
+
await act(async () => {
|
|
518
|
+
hook.moveUp();
|
|
519
|
+
});
|
|
520
|
+
await flush();
|
|
521
|
+
await act(async () => {
|
|
522
|
+
hook.selectItem();
|
|
523
|
+
});
|
|
524
|
+
await flush();
|
|
525
|
+
t.is(hook.phase, 'wizard_name');
|
|
526
|
+
t.truthy(hook.draft);
|
|
527
|
+
await act(async () => {
|
|
528
|
+
renderer.unmount();
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
test.serial('useProviderSelection - back out of an unmodified wizard returns directly to the list', async (t) => {
|
|
532
|
+
const settingsService = createMockSettingsService([], 'openai');
|
|
533
|
+
let hook;
|
|
534
|
+
let renderer;
|
|
535
|
+
await act(async () => {
|
|
536
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
537
|
+
settingsService,
|
|
538
|
+
onHookResult: (h) => {
|
|
539
|
+
hook = h;
|
|
540
|
+
},
|
|
541
|
+
})));
|
|
542
|
+
});
|
|
543
|
+
await act(async () => {
|
|
544
|
+
hook.open();
|
|
545
|
+
});
|
|
546
|
+
await flush();
|
|
547
|
+
await act(async () => {
|
|
548
|
+
const active = hook.getActiveItems();
|
|
549
|
+
const targetIdx = active.findIndex((item) => item.kind === 'add-provider');
|
|
550
|
+
let moves = 0;
|
|
551
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
552
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
553
|
+
moves++;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
for (let i = 0; i < moves; i++) {
|
|
557
|
+
hook.moveDown();
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
await flush();
|
|
561
|
+
await act(async () => {
|
|
562
|
+
hook.selectItem();
|
|
563
|
+
});
|
|
564
|
+
await flush();
|
|
565
|
+
t.is(hook.phase, 'wizard_name');
|
|
566
|
+
await act(async () => {
|
|
567
|
+
hook.goBack();
|
|
568
|
+
});
|
|
569
|
+
await flush();
|
|
570
|
+
t.is(hook.phase, 'list');
|
|
571
|
+
await act(async () => {
|
|
572
|
+
renderer.unmount();
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
test.serial('useProviderSelection - saveDraft rejects duplicate names discovered after editing starts', async (t) => {
|
|
576
|
+
const settingsService = createMockSettingsService([], 'openai');
|
|
577
|
+
let hook;
|
|
578
|
+
let renderer;
|
|
579
|
+
await act(async () => {
|
|
580
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
581
|
+
settingsService,
|
|
582
|
+
onHookResult: (h) => {
|
|
583
|
+
hook = h;
|
|
584
|
+
},
|
|
585
|
+
})));
|
|
586
|
+
});
|
|
587
|
+
await act(async () => {
|
|
588
|
+
hook.open();
|
|
589
|
+
});
|
|
590
|
+
await flush();
|
|
591
|
+
await act(async () => {
|
|
592
|
+
const active = hook.getActiveItems();
|
|
593
|
+
const targetIdx = active.findIndex((item) => item.kind === 'add-provider');
|
|
594
|
+
let moves = 0;
|
|
595
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
596
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
597
|
+
moves++;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
for (let i = 0; i < moves; i++) {
|
|
601
|
+
hook.moveDown();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
await flush();
|
|
605
|
+
await act(async () => {
|
|
606
|
+
hook.selectItem();
|
|
607
|
+
});
|
|
608
|
+
await flush();
|
|
609
|
+
t.is(hook.phase, 'wizard_name');
|
|
610
|
+
let success = false;
|
|
611
|
+
await act(async () => {
|
|
612
|
+
success = hook.handleTextInputSubmit('late-conflict-provider');
|
|
613
|
+
});
|
|
614
|
+
await flush();
|
|
615
|
+
t.true(success);
|
|
616
|
+
await act(async () => {
|
|
617
|
+
hook.selectItem();
|
|
618
|
+
});
|
|
619
|
+
await flush();
|
|
620
|
+
t.is(hook.phase, 'wizard_url');
|
|
621
|
+
t.is(hook.draft?.type, 'openai-compatible');
|
|
622
|
+
await act(async () => {
|
|
623
|
+
hook.handleTextInputSubmit('http://localhost:8000/v1');
|
|
624
|
+
});
|
|
625
|
+
await flush();
|
|
626
|
+
await act(async () => {
|
|
627
|
+
hook.handleTextInputSubmit('');
|
|
628
|
+
});
|
|
629
|
+
await flush();
|
|
630
|
+
settingsService.set('providers', [
|
|
631
|
+
{ name: 'late-conflict-provider', type: 'openai-compatible', baseUrl: 'http://localhost:9000/v1' },
|
|
632
|
+
]);
|
|
633
|
+
await act(async () => {
|
|
634
|
+
hook.selectItem();
|
|
635
|
+
});
|
|
636
|
+
await flush();
|
|
637
|
+
t.is(hook.phase, 'edit_fields');
|
|
638
|
+
t.deepEqual(hook.fieldErrors, { name: "Provider with name 'late-conflict-provider' already exists." });
|
|
639
|
+
t.is(hook.errorMessage, null);
|
|
640
|
+
t.is(settingsService.get('providers').length, 1);
|
|
641
|
+
await act(async () => {
|
|
642
|
+
renderer.unmount();
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
test.serial('useProviderSelection - unchanged provider names are allowed when editing', async (t) => {
|
|
646
|
+
const customProviders = [
|
|
647
|
+
{ name: 'existing-provider', type: 'openai-compatible', baseUrl: 'http://localhost:11434/v1' },
|
|
648
|
+
];
|
|
649
|
+
const settingsService = createMockSettingsService(customProviders, 'existing-provider');
|
|
650
|
+
let hook;
|
|
651
|
+
let renderer;
|
|
652
|
+
await act(async () => {
|
|
653
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
654
|
+
settingsService,
|
|
655
|
+
onHookResult: (h) => {
|
|
656
|
+
hook = h;
|
|
657
|
+
},
|
|
658
|
+
})));
|
|
659
|
+
});
|
|
660
|
+
await act(async () => {
|
|
661
|
+
hook.open();
|
|
662
|
+
});
|
|
663
|
+
await flush();
|
|
664
|
+
await act(async () => {
|
|
665
|
+
const active = hook.getActiveItems();
|
|
666
|
+
const targetIdx = active.findIndex((item) => item.kind === 'provider' && item.id === 'existing-provider');
|
|
667
|
+
let moves = 0;
|
|
668
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
669
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
670
|
+
moves++;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
for (let i = 0; i < moves; i++) {
|
|
674
|
+
hook.moveDown();
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
await flush();
|
|
678
|
+
// Enter on the custom provider goes directly to edit_fields.
|
|
679
|
+
await act(async () => {
|
|
680
|
+
hook.selectItem();
|
|
681
|
+
});
|
|
682
|
+
await flush();
|
|
683
|
+
// Enter on the Name field opens the name editor.
|
|
684
|
+
await act(async () => {
|
|
685
|
+
hook.selectItem();
|
|
686
|
+
});
|
|
687
|
+
await flush();
|
|
688
|
+
t.is(hook.phase, 'wizard_name');
|
|
689
|
+
let success = false;
|
|
690
|
+
await act(async () => {
|
|
691
|
+
success = hook.handleTextInputSubmit('existing-provider');
|
|
692
|
+
});
|
|
693
|
+
await flush();
|
|
694
|
+
t.true(success);
|
|
695
|
+
t.is(hook.phase, 'edit_fields');
|
|
696
|
+
await act(async () => {
|
|
697
|
+
hook.moveDown();
|
|
698
|
+
hook.moveDown();
|
|
699
|
+
hook.moveDown();
|
|
700
|
+
hook.moveDown();
|
|
701
|
+
});
|
|
702
|
+
await flush();
|
|
703
|
+
t.is(hook.selectedIndex, 4);
|
|
704
|
+
await act(async () => {
|
|
705
|
+
hook.selectItem();
|
|
706
|
+
});
|
|
707
|
+
await flush();
|
|
708
|
+
t.is(hook.phase, 'list');
|
|
709
|
+
t.is(hook.errorMessage, null);
|
|
710
|
+
t.is(settingsService.get('providers').length, 1);
|
|
711
|
+
t.is(settingsService.get('providers')[0].name, 'existing-provider');
|
|
712
|
+
await act(async () => {
|
|
713
|
+
renderer.unmount();
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
test.serial('useProviderSelection - scrollOffset updates when navigating and resets on phase change', async (t) => {
|
|
717
|
+
// Create many mock custom providers to exceed the scroll threshold of 10
|
|
718
|
+
const customProviders = Array.from({ length: 15 }, (_, i) => ({
|
|
719
|
+
name: `custom-provider-${i}`,
|
|
720
|
+
type: 'openai-compatible',
|
|
721
|
+
baseUrl: `http://localhost:1143${i}/v1`,
|
|
722
|
+
}));
|
|
723
|
+
const settingsService = createMockSettingsService(customProviders, 'openai');
|
|
724
|
+
let hook;
|
|
725
|
+
let renderer;
|
|
726
|
+
await act(async () => {
|
|
727
|
+
renderer = render(React.createElement(InputProvider, {}, React.createElement(TestComponent, {
|
|
728
|
+
settingsService,
|
|
729
|
+
onHookResult: (h) => {
|
|
730
|
+
hook = h;
|
|
731
|
+
},
|
|
732
|
+
})));
|
|
733
|
+
});
|
|
734
|
+
await act(async () => {
|
|
735
|
+
hook.open();
|
|
736
|
+
});
|
|
737
|
+
await flush();
|
|
738
|
+
t.is(hook.phase, 'list');
|
|
739
|
+
t.is(hook.scrollOffset, 0);
|
|
740
|
+
// Move down 10 times to reach index 11 (the 11th active item)
|
|
741
|
+
await act(async () => {
|
|
742
|
+
for (let i = 0; i < 10; i++) {
|
|
743
|
+
hook.moveDown();
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
await flush();
|
|
747
|
+
t.is(hook.selectedIndex, 11);
|
|
748
|
+
t.is(hook.scrollOffset, 2, 'scrollOffset should scroll to 2 when reaching index 11');
|
|
749
|
+
// Move down 1 more time to index 12 (the 12th active item)
|
|
750
|
+
await act(async () => {
|
|
751
|
+
hook.moveDown();
|
|
752
|
+
});
|
|
753
|
+
await flush();
|
|
754
|
+
t.is(hook.selectedIndex, 12);
|
|
755
|
+
t.is(hook.scrollOffset, 3, 'scrollOffset should scroll to 3 when reaching index 12');
|
|
756
|
+
// Move up to index 0 (scrollOffset should sync back)
|
|
757
|
+
await act(async () => {
|
|
758
|
+
for (let i = 0; i < 11; i++) {
|
|
759
|
+
hook.moveUp();
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
await flush();
|
|
763
|
+
t.is(hook.selectedIndex, 0);
|
|
764
|
+
t.is(hook.scrollOffset, 0, 'scrollOffset should return to 0 when moving back to the top');
|
|
765
|
+
// Transition to wizard_name phase to verify scrollOffset resets
|
|
766
|
+
// Let's select "Add Custom Provider" which is at the end of the list
|
|
767
|
+
await act(async () => {
|
|
768
|
+
const active = hook.getActiveItems();
|
|
769
|
+
const targetIdx = active.findIndex((item) => item.kind === 'add-provider');
|
|
770
|
+
let moves = 0;
|
|
771
|
+
for (let i = 0; i < targetIdx; i++) {
|
|
772
|
+
if (!(active[i].kind === 'provider' && active[i].id === 'codex')) {
|
|
773
|
+
moves++;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
for (let i = 0; i < moves; i++) {
|
|
777
|
+
hook.moveDown();
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
await flush();
|
|
781
|
+
t.is(hook.selectedIndex, hook.items.length);
|
|
782
|
+
t.true(hook.scrollOffset > 0, 'scrollOffset should be non-zero near the end of the long list');
|
|
783
|
+
await act(async () => {
|
|
784
|
+
hook.selectItem();
|
|
785
|
+
});
|
|
786
|
+
await flush();
|
|
787
|
+
t.is(hook.phase, 'wizard_name');
|
|
788
|
+
t.is(hook.scrollOffset, 0, 'scrollOffset should reset to 0 when phase changes to wizard_name');
|
|
789
|
+
await act(async () => {
|
|
790
|
+
renderer.unmount();
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
//# sourceMappingURL=use-provider-selection.test.js.map
|