@vybestack/llxprt-code 0.5.0 → 0.6.0
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 +0 -244
- package/dist/package.json +15 -6
- package/dist/src/auth/gemini-oauth-provider.d.ts +18 -0
- package/dist/src/auth/gemini-oauth-provider.js +140 -78
- package/dist/src/auth/gemini-oauth-provider.js.map +1 -1
- package/dist/src/auth/local-oauth-callback.spec.js +29 -2
- package/dist/src/auth/local-oauth-callback.spec.js.map +1 -1
- package/dist/src/commands/extensions/disable.d.ts +1 -2
- package/dist/src/commands/extensions/disable.js +15 -3
- package/dist/src/commands/extensions/disable.js.map +1 -1
- package/dist/src/commands/extensions/enable.d.ts +1 -2
- package/dist/src/commands/extensions/enable.js +17 -6
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/install.d.ts +3 -1
- package/dist/src/commands/extensions/install.js +63 -12
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +166 -13
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +2 -2
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/list.d.ts +1 -1
- package/dist/src/commands/extensions/list.js +3 -2
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/new.test.js +2 -2
- package/dist/src/commands/extensions/new.test.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +1 -1
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.test.js +4 -1
- package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
- package/dist/src/commands/extensions/update.d.ts +2 -2
- package/dist/src/commands/extensions/update.js +68 -19
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/mcp/add.js +6 -1
- package/dist/src/commands/mcp/add.js.map +1 -1
- package/dist/src/commands/mcp/list.js +3 -2
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/config/__tests__/profileBootstrap.test.js +1351 -1
- package/dist/src/config/__tests__/profileBootstrap.test.js.map +1 -1
- package/dist/src/config/config.d.ts +4 -1
- package/dist/src/config/config.js +172 -69
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.loadMemory.test.js +6 -3
- package/dist/src/config/config.loadMemory.test.js.map +1 -1
- package/dist/src/config/extension.d.ts +48 -14
- package/dist/src/config/extension.js +263 -142
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/extensions/extensionEnablement.d.ts +47 -0
- package/dist/src/config/extensions/extensionEnablement.js +188 -0
- package/dist/src/config/extensions/extensionEnablement.js.map +1 -0
- package/dist/src/config/extensions/extensionEnablement.test.d.ts +6 -0
- package/dist/src/config/extensions/extensionEnablement.test.js +333 -0
- package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -0
- package/dist/src/config/extensions/github.d.ts +37 -0
- package/dist/src/config/extensions/github.js +348 -0
- package/dist/src/config/extensions/github.js.map +1 -0
- package/dist/src/config/extensions/github.test.d.ts +6 -0
- package/dist/src/config/extensions/github.test.js +340 -0
- package/dist/src/config/extensions/github.test.js.map +1 -0
- package/dist/src/config/extensions/update.d.ts +19 -0
- package/dist/src/config/extensions/update.js +118 -0
- package/dist/src/config/extensions/update.js.map +1 -0
- package/dist/src/config/extensions/update.test.d.ts +6 -0
- package/dist/src/config/extensions/update.test.js +340 -0
- package/dist/src/config/extensions/update.test.js.map +1 -0
- package/dist/src/config/extensions/variableSchema.d.ts +8 -0
- package/dist/src/config/extensions/variableSchema.js +4 -0
- package/dist/src/config/extensions/variableSchema.js.map +1 -1
- package/dist/src/config/keyBindings.d.ts +1 -0
- package/dist/src/config/keyBindings.js +3 -1
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/paths.d.ts +8 -0
- package/dist/src/config/paths.js +11 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/config/profileBootstrap.d.ts +13 -0
- package/dist/src/config/profileBootstrap.js +369 -15
- package/dist/src/config/profileBootstrap.js.map +1 -1
- package/dist/src/config/settings.d.ts +14 -4
- package/dist/src/config/settings.js +161 -99
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +510 -216
- package/dist/src/config/settingsSchema.js +468 -214
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.js +18 -31
- package/dist/src/config/settingsSchema.test.js.map +1 -1
- package/dist/src/config/trustedFolders.d.ts +6 -3
- package/dist/src/config/trustedFolders.js +35 -13
- package/dist/src/config/trustedFolders.js.map +1 -1
- package/dist/src/config/trustedFolders.test.js +83 -9
- package/dist/src/config/trustedFolders.test.js.map +1 -1
- package/dist/src/coreToolToggle.test.d.ts +6 -0
- package/dist/src/coreToolToggle.test.js +427 -0
- package/dist/src/coreToolToggle.test.js.map +1 -0
- package/dist/src/extensions/extensionAutoUpdater.d.ts +76 -0
- package/dist/src/extensions/extensionAutoUpdater.js +279 -0
- package/dist/src/extensions/extensionAutoUpdater.js.map +1 -0
- package/dist/src/extensions/extensionAutoUpdater.test.d.ts +6 -0
- package/dist/src/extensions/extensionAutoUpdater.test.js +146 -0
- package/dist/src/extensions/extensionAutoUpdater.test.js.map +1 -0
- package/dist/src/gemini.js +93 -63
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +121 -1
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/integration-tests/cli-args.integration.test.js +299 -0
- package/dist/src/integration-tests/cli-args.integration.test.js.map +1 -1
- package/dist/src/nonInteractiveCli.d.ts +2 -1
- package/dist/src/nonInteractiveCli.js +29 -16
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCliCommands.d.ts +17 -0
- package/dist/src/nonInteractiveCliCommands.js +81 -0
- package/dist/src/nonInteractiveCliCommands.js.map +1 -0
- package/dist/src/runtime/runtimeSettings.test.js +2 -0
- package/dist/src/runtime/runtimeSettings.test.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.d.ts +3 -3
- package/dist/src/services/BuiltinCommandLoader.js +7 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +37 -0
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/services/McpPromptLoader.js +39 -34
- package/dist/src/services/McpPromptLoader.js.map +1 -1
- package/dist/src/settings/ephemeralSettings.js +56 -0
- package/dist/src/settings/ephemeralSettings.js.map +1 -1
- package/dist/src/test-utils/createExtension.d.ts +15 -0
- package/dist/src/test-utils/createExtension.js +24 -0
- package/dist/src/test-utils/createExtension.js.map +1 -0
- package/dist/src/test-utils/mockCommandContext.js +4 -2
- package/dist/src/test-utils/mockCommandContext.js.map +1 -1
- package/dist/src/test-utils/render.d.ts +3 -1
- package/dist/src/test-utils/render.js +4 -1
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/App.d.ts +18 -3
- package/dist/src/ui/App.js +32 -873
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/AppContainer.d.ts +19 -0
- package/dist/src/ui/AppContainer.js +1187 -0
- package/dist/src/ui/AppContainer.js.map +1 -0
- package/dist/src/ui/IdeIntegrationNudge.d.ts +2 -2
- package/dist/src/ui/IdeIntegrationNudge.js +4 -7
- package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.js +214 -58
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.js +1 -2
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/diagnosticsCommand.js +2 -2
- package/dist/src/ui/commands/diagnosticsCommand.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +3 -2
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/docsCommand.js +1 -1
- package/dist/src/ui/commands/docsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.d.ts +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +92 -17
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/helpCommand.js +6 -5
- package/dist/src/ui/commands/helpCommand.js.map +1 -1
- package/dist/src/ui/commands/ideCommand.js +7 -7
- package/dist/src/ui/commands/ideCommand.js.map +1 -1
- package/dist/src/ui/commands/loggingCommand.js +1 -1
- package/dist/src/ui/commands/loggingCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +11 -3
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.js +21 -3
- package/dist/src/ui/commands/memoryCommand.js.map +1 -1
- package/dist/src/ui/commands/permissionsCommand.d.ts +7 -0
- package/dist/src/ui/commands/permissionsCommand.js +16 -0
- package/dist/src/ui/commands/permissionsCommand.js.map +1 -0
- package/dist/src/ui/commands/permissionsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/permissionsCommand.test.js +55 -0
- package/dist/src/ui/commands/permissionsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/policiesCommand.d.ts +10 -0
- package/dist/src/ui/commands/policiesCommand.js +112 -0
- package/dist/src/ui/commands/policiesCommand.js.map +1 -0
- package/dist/src/ui/commands/policiesCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/policiesCommand.test.js +224 -0
- package/dist/src/ui/commands/policiesCommand.test.js.map +1 -0
- package/dist/src/ui/commands/setCommand.js +105 -30
- package/dist/src/ui/commands/setCommand.js.map +1 -1
- package/dist/src/ui/commands/setCommand.test.js +1 -1
- package/dist/src/ui/commands/setCommand.test.js.map +1 -1
- package/dist/src/ui/commands/statsCommand.js +11 -1
- package/dist/src/ui/commands/statsCommand.js.map +1 -1
- package/dist/src/ui/commands/test/subagentCommand.test.js +6 -1
- package/dist/src/ui/commands/test/subagentCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +19 -23
- package/dist/src/ui/commands/types.js +1 -1
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/commands/uiprofileCommand.d.ts +7 -0
- package/dist/src/ui/commands/uiprofileCommand.js +23 -0
- package/dist/src/ui/commands/uiprofileCommand.js.map +1 -0
- package/dist/src/ui/components/AuthDialog.js +4 -0
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.test.js +32 -32
- package/dist/src/ui/components/AuthDialog.test.js.map +1 -1
- package/dist/src/ui/components/CacheStatsDisplay.d.ts +7 -0
- package/dist/src/ui/components/CacheStatsDisplay.js +35 -0
- package/dist/src/ui/components/CacheStatsDisplay.js.map +1 -0
- package/dist/src/ui/components/CacheStatsDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/CacheStatsDisplay.test.js +118 -0
- package/dist/src/ui/components/CacheStatsDisplay.test.js.map +1 -0
- package/dist/src/ui/components/CliSpinner.d.ts +10 -0
- package/dist/src/ui/components/CliSpinner.js +20 -0
- package/dist/src/ui/components/CliSpinner.js.map +1 -0
- package/dist/src/ui/components/Composer.d.ts +17 -0
- package/dist/src/ui/components/Composer.js +16 -0
- package/dist/src/ui/components/Composer.js.map +1 -0
- package/dist/src/ui/components/ConsentPrompt.d.ts +13 -0
- package/dist/src/ui/components/ConsentPrompt.js +18 -0
- package/dist/src/ui/components/ConsentPrompt.js.map +1 -0
- package/dist/src/ui/components/ConsentPrompt.test.d.ts +6 -0
- package/dist/src/ui/components/ConsentPrompt.test.js +67 -0
- package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -0
- package/dist/src/ui/components/DebugProfiler.d.ts +15 -0
- package/dist/src/ui/components/DebugProfiler.js +137 -13
- package/dist/src/ui/components/DebugProfiler.js.map +1 -1
- package/dist/src/ui/components/DebugProfiler.test.d.ts +6 -0
- package/dist/src/ui/components/DebugProfiler.test.js +131 -0
- package/dist/src/ui/components/DebugProfiler.test.js.map +1 -0
- package/dist/src/ui/components/DialogManager.d.ts +16 -0
- package/dist/src/ui/components/DialogManager.js +131 -0
- package/dist/src/ui/components/DialogManager.js.map +1 -0
- package/dist/src/ui/components/EditorSettingsDialog.js +18 -8
- package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.js +4 -0
- package/dist/src/ui/components/FolderTrustDialog.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +2 -2
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.d.ts +6 -2
- package/dist/src/ui/components/HistoryItemDisplay.js +10 -2
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.test.js +90 -10
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.d.ts +6 -0
- package/dist/src/ui/components/InputPrompt.js +52 -18
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoadProfileDialog.js +13 -9
- package/dist/src/ui/components/LoadProfileDialog.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.js +1 -1
- package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.test.js +6 -0
- package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/components/LoggingDialog.js +14 -10
- package/dist/src/ui/components/LoggingDialog.js.map +1 -1
- package/dist/src/ui/components/Notifications.d.ts +14 -0
- package/dist/src/ui/components/Notifications.js +34 -0
- package/dist/src/ui/components/Notifications.js.map +1 -0
- package/dist/src/ui/components/OAuthCodeDialog.js +2 -1
- package/dist/src/ui/components/OAuthCodeDialog.js.map +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.d.ts +14 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js +115 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.d.ts +6 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +98 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -0
- package/dist/src/ui/components/ProviderDialog.js +23 -18
- package/dist/src/ui/components/ProviderDialog.js.map +1 -1
- package/dist/src/ui/components/ProviderModelDialog.js +24 -18
- package/dist/src/ui/components/ProviderModelDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.d.ts +12 -2
- package/dist/src/ui/components/SettingsDialog.js +253 -23
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +90 -8
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js +3 -0
- package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.js +6 -4
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/ToolsDialog.js +6 -4
- package/dist/src/ui/components/ToolsDialog.js.map +1 -1
- package/dist/src/ui/components/WorkspaceMigrationDialog.js +5 -3
- package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +37 -6
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +9 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js +7 -3
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.js +7 -3
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/WarningMessage.d.ts +11 -0
- package/dist/src/ui/components/messages/WarningMessage.js +10 -0
- package/dist/src/ui/components/messages/WarningMessage.js.map +1 -0
- package/dist/src/ui/components/messages/WarningMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/WarningMessage.test.js +15 -0
- package/dist/src/ui/components/messages/WarningMessage.test.js.map +1 -0
- package/dist/src/ui/components/shared/BaseSelectionList.d.ts +38 -0
- package/dist/src/ui/components/shared/BaseSelectionList.js +76 -0
- package/dist/src/ui/components/shared/BaseSelectionList.js.map +1 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.d.ts +6 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.js +376 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.d.ts +4 -5
- package/dist/src/ui/components/shared/RadioButtonSelect.js +11 -117
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.test.d.ts +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js +116 -95
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.d.ts +6 -0
- package/dist/src/ui/components/shared/text-buffer.js +71 -4
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/components/views/ChatList.d.ts +12 -0
- package/dist/src/ui/components/views/ChatList.js +17 -0
- package/dist/src/ui/components/views/ChatList.js.map +1 -0
- package/dist/src/ui/components/views/ChatList.test.d.ts +6 -0
- package/dist/src/ui/components/views/ChatList.test.js +42 -0
- package/dist/src/ui/components/views/ChatList.test.js.map +1 -0
- package/dist/src/ui/constants.d.ts +1 -0
- package/dist/src/ui/constants.js +1 -0
- package/dist/src/ui/constants.js.map +1 -1
- package/dist/src/ui/contexts/FocusContext.d.ts +7 -0
- package/dist/src/ui/contexts/FocusContext.js +9 -0
- package/dist/src/ui/contexts/FocusContext.js.map +1 -0
- package/dist/src/ui/contexts/KeypressContext.d.ts +1 -1
- package/dist/src/ui/contexts/KeypressContext.js +63 -4
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +156 -0
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.d.ts +6 -0
- package/dist/src/ui/contexts/SessionContext.js +107 -5
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/contexts/UIActionsContext.d.ts +82 -0
- package/dist/src/ui/contexts/UIActionsContext.js +20 -0
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -0
- package/dist/src/ui/contexts/UIStateContext.d.ts +121 -0
- package/dist/src/ui/contexts/UIStateContext.js +20 -0
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -0
- package/dist/src/ui/contexts/VimModeContext.js +4 -4
- package/dist/src/ui/contexts/VimModeContext.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +29 -6
- package/dist/src/ui/hooks/slashCommandProcessor.js +150 -155
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useBracketedPaste.js +3 -4
- package/dist/src/ui/hooks/useBracketedPaste.js.map +1 -1
- package/dist/src/ui/hooks/useEditorSettings.js +1 -1
- package/dist/src/ui/hooks/useEditorSettings.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionAutoUpdate.d.ts +13 -0
- package/dist/src/ui/hooks/useExtensionAutoUpdate.js +40 -0
- package/dist/src/ui/hooks/useExtensionAutoUpdate.js.map +1 -0
- package/dist/src/ui/hooks/useExtensionUpdates.d.ts +21 -0
- package/dist/src/ui/hooks/useExtensionUpdates.js +168 -0
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -0
- package/dist/src/ui/hooks/useExtensionUpdates.test.d.ts +6 -0
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +243 -0
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -0
- package/dist/src/ui/hooks/useFlickerDetector.d.ts +22 -0
- package/dist/src/ui/hooks/useFlickerDetector.js +38 -0
- package/dist/src/ui/hooks/useFlickerDetector.js.map +1 -0
- package/dist/src/ui/hooks/useFlickerDetector.test.d.ts +6 -0
- package/dist/src/ui/hooks/useFlickerDetector.test.js +142 -0
- package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -0
- package/dist/src/ui/hooks/useGeminiStream.d.ts +1 -1
- package/dist/src/ui/hooks/useGeminiStream.integration.test.js +1 -1
- package/dist/src/ui/hooks/useGeminiStream.integration.test.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +41 -18
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useMemoryMonitor.d.ts +13 -0
- package/dist/src/ui/hooks/useMemoryMonitor.js +28 -0
- package/dist/src/ui/hooks/useMemoryMonitor.js.map +1 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.d.ts +34 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.js +90 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.js.map +1 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.d.ts +6 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +60 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -0
- package/dist/src/ui/hooks/useReactToolScheduler.d.ts +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +5 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSelectionList.d.ts +34 -0
- package/dist/src/ui/hooks/useSelectionList.js +292 -0
- package/dist/src/ui/hooks/useSelectionList.js.map +1 -0
- package/dist/src/ui/hooks/useSelectionList.test.d.ts +6 -0
- package/dist/src/ui/hooks/useSelectionList.test.js +726 -0
- package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -0
- package/dist/src/ui/hooks/useShowMemoryCommand.js +1 -1
- package/dist/src/ui/hooks/useShowMemoryCommand.js.map +1 -1
- package/dist/src/ui/hooks/useStateAndRef.d.ts +1 -1
- package/dist/src/ui/hooks/useStateAndRef.js +2 -2
- package/dist/src/ui/hooks/useStateAndRef.js.map +1 -1
- package/dist/src/ui/hooks/useStaticHistoryRefresh.d.ts +13 -0
- package/dist/src/ui/hooks/useStaticHistoryRefresh.js +35 -0
- package/dist/src/ui/hooks/useStaticHistoryRefresh.js.map +1 -0
- package/dist/src/ui/hooks/useStaticHistoryRefresh.test.d.ts +6 -0
- package/dist/src/ui/hooks/useStaticHistoryRefresh.test.js +42 -0
- package/dist/src/ui/hooks/useStaticHistoryRefresh.test.js.map +1 -0
- package/dist/src/ui/hooks/useThemeCommand.js +7 -7
- package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +17 -20
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/keyMatchers.test.js +6 -0
- package/dist/src/ui/keyMatchers.test.js.map +1 -1
- package/dist/src/ui/layouts/DefaultAppLayout.d.ts +23 -0
- package/dist/src/ui/layouts/DefaultAppLayout.js +76 -0
- package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -0
- package/dist/src/ui/noninteractive/nonInteractiveUi.d.ts +12 -0
- package/dist/src/ui/noninteractive/nonInteractiveUi.js +31 -0
- package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js +2 -2
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js.map +1 -1
- package/dist/src/ui/privacy/MultiProviderPrivacyNotice.d.ts +11 -0
- package/dist/src/ui/privacy/MultiProviderPrivacyNotice.js +92 -0
- package/dist/src/ui/privacy/MultiProviderPrivacyNotice.js.map +1 -0
- package/dist/src/ui/privacy/MultiProviderPrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/MultiProviderPrivacyNotice.test.js +106 -0
- package/dist/src/ui/privacy/MultiProviderPrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/PrivacyNotice.js +4 -9
- package/dist/src/ui/privacy/PrivacyNotice.js.map +1 -1
- package/dist/src/ui/state/extensions.d.ts +61 -0
- package/dist/src/ui/state/extensions.js +84 -0
- package/dist/src/ui/state/extensions.js.map +1 -0
- package/dist/src/ui/themes/theme.js +2 -2
- package/dist/src/ui/themes/theme.js.map +1 -1
- package/dist/src/ui/types.d.ts +58 -2
- package/dist/src/ui/types.js +6 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.js +1 -2
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.test.d.ts +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.test.js +94 -104
- package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -1
- package/dist/src/ui/utils/bracketedPaste.d.ts +9 -0
- package/dist/src/ui/utils/bracketedPaste.js +14 -0
- package/dist/src/ui/utils/bracketedPaste.js.map +1 -0
- package/dist/src/ui/utils/displayUtils.d.ts +1 -0
- package/dist/src/ui/utils/displayUtils.js +4 -1
- package/dist/src/ui/utils/displayUtils.js.map +1 -1
- package/dist/src/ui/utils/displayUtils.test.js +36 -17
- package/dist/src/ui/utils/displayUtils.test.js.map +1 -1
- package/dist/src/ui/utils/highlight.d.ts +2 -1
- package/dist/src/ui/utils/highlight.js +52 -8
- package/dist/src/ui/utils/highlight.js.map +1 -1
- package/dist/src/ui/utils/highlight.test.js +15 -14
- package/dist/src/ui/utils/highlight.test.js.map +1 -1
- package/dist/src/ui/utils/kittyProtocolDetector.d.ts +1 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js +11 -3
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
- package/dist/src/ui/utils/terminalSequences.d.ts +22 -0
- package/dist/src/ui/utils/terminalSequences.js +23 -0
- package/dist/src/ui/utils/terminalSequences.js.map +1 -0
- package/dist/src/ui/utils/textUtils.d.ts +10 -0
- package/dist/src/ui/utils/textUtils.js +82 -1
- package/dist/src/ui/utils/textUtils.js.map +1 -1
- package/dist/src/ui/utils/textUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/textUtils.test.js +132 -0
- package/dist/src/ui/utils/textUtils.test.js.map +1 -0
- package/dist/src/utils/bootstrap.d.ts +24 -0
- package/dist/src/utils/bootstrap.js +55 -0
- package/dist/src/utils/bootstrap.js.map +1 -0
- package/dist/src/utils/bootstrap.test.d.ts +6 -0
- package/dist/src/utils/bootstrap.test.js +103 -0
- package/dist/src/utils/bootstrap.test.js.map +1 -0
- package/dist/src/utils/cleanup.d.ts +6 -0
- package/dist/src/utils/cleanup.js +14 -0
- package/dist/src/utils/cleanup.js.map +1 -1
- package/dist/src/utils/commands.d.ts +17 -0
- package/dist/src/utils/commands.js +60 -0
- package/dist/src/utils/commands.js.map +1 -0
- package/dist/src/utils/dialogScopeUtils.d.ts +3 -0
- package/dist/src/utils/dialogScopeUtils.js +11 -2
- package/dist/src/utils/dialogScopeUtils.js.map +1 -1
- package/dist/src/utils/dynamicSettings.d.ts +20 -0
- package/dist/src/utils/dynamicSettings.js +110 -0
- package/dist/src/utils/dynamicSettings.js.map +1 -0
- package/dist/src/utils/dynamicSettings.test.d.ts +6 -0
- package/dist/src/utils/dynamicSettings.test.js +329 -0
- package/dist/src/utils/dynamicSettings.test.js.map +1 -0
- package/dist/src/utils/events.d.ts +3 -1
- package/dist/src/utils/events.js +2 -0
- package/dist/src/utils/events.js.map +1 -1
- package/dist/src/utils/handleAutoUpdate.js +4 -1
- package/dist/src/utils/handleAutoUpdate.js.map +1 -1
- package/dist/src/utils/installationInfo.d.ts +1 -0
- package/dist/src/utils/installationInfo.js +5 -3
- package/dist/src/utils/installationInfo.js.map +1 -1
- package/dist/src/utils/relaunch.d.ts +16 -0
- package/dist/src/utils/relaunch.js +34 -0
- package/dist/src/utils/relaunch.js.map +1 -0
- package/dist/src/utils/relaunch.test.d.ts +6 -0
- package/dist/src/utils/relaunch.test.js +96 -0
- package/dist/src/utils/relaunch.test.js.map +1 -0
- package/dist/src/utils/sessionCleanup.d.ts +22 -0
- package/dist/src/utils/sessionCleanup.integration.test.d.ts +6 -0
- package/dist/src/utils/sessionCleanup.integration.test.js +174 -0
- package/dist/src/utils/sessionCleanup.integration.test.js.map +1 -0
- package/dist/src/utils/sessionCleanup.js +213 -0
- package/dist/src/utils/sessionCleanup.js.map +1 -0
- package/dist/src/utils/sessionCleanup.test.d.ts +6 -0
- package/dist/src/utils/sessionCleanup.test.js +1144 -0
- package/dist/src/utils/sessionCleanup.test.js.map +1 -0
- package/dist/src/utils/sessionUtils.d.ts +37 -0
- package/dist/src/utils/sessionUtils.js +71 -0
- package/dist/src/utils/sessionUtils.js.map +1 -0
- package/dist/src/utils/settingsUtils.d.ts +6 -3
- package/dist/src/utils/settingsUtils.js +39 -11
- package/dist/src/utils/settingsUtils.js.map +1 -1
- package/dist/src/utils/settingsUtils.test.js +96 -96
- package/dist/src/utils/settingsUtils.test.js.map +1 -1
- package/dist/src/utils/singleSettingSaver.js +5 -0
- package/dist/src/utils/singleSettingSaver.js.map +1 -1
- package/dist/src/utils/windowTitle.d.ts +12 -0
- package/dist/src/utils/windowTitle.js +19 -0
- package/dist/src/utils/windowTitle.js.map +1 -0
- package/dist/src/utils/windowTitle.test.d.ts +6 -0
- package/dist/src/utils/windowTitle.test.js +49 -0
- package/dist/src/utils/windowTitle.test.js.map +1 -0
- package/dist/src/zed-integration/acp.js +1 -2
- package/dist/src/zed-integration/acp.js.map +1 -1
- package/dist/src/zed-integration/fileSystemService.d.ts +1 -0
- package/dist/src/zed-integration/fileSystemService.js +3 -0
- package/dist/src/zed-integration/fileSystemService.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +212 -212
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +15 -6
- package/dist/src/ui/App.quittingMessages.test.d.ts +0 -1
- package/dist/src/ui/App.quittingMessages.test.js +0 -14
- package/dist/src/ui/App.quittingMessages.test.js.map +0 -1
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* Copyright 2025 Vybestack LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
-
import { parseBootstrapArgs, prepareRuntimeForProfile, createBootstrapResult, } from '../profileBootstrap.js';
|
|
8
|
+
import { parseBootstrapArgs, prepareRuntimeForProfile, createBootstrapResult, parseInlineProfile, } from '../profileBootstrap.js';
|
|
8
9
|
vi.mock('../../runtime/runtimeSettings.js', () => ({
|
|
9
10
|
registerCliProviderInfrastructure: vi.fn(),
|
|
10
11
|
}));
|
|
11
12
|
const parseArgs = parseBootstrapArgs;
|
|
13
|
+
const parseArgsWithMeta = parseBootstrapArgs;
|
|
12
14
|
const prepareRuntime = prepareRuntimeForProfile;
|
|
13
15
|
const finalizeBootstrap = createBootstrapResult;
|
|
14
16
|
describe('profileBootstrap helpers', () => {
|
|
@@ -78,6 +80,7 @@ describe('profileBootstrap helpers', () => {
|
|
|
78
80
|
const parsed = {
|
|
79
81
|
bootstrapArgs: {
|
|
80
82
|
profileName: 'synthetic',
|
|
83
|
+
profileJson: null,
|
|
81
84
|
providerOverride: null,
|
|
82
85
|
modelOverride: null,
|
|
83
86
|
keyOverride: null,
|
|
@@ -112,4 +115,1351 @@ describe('profileBootstrap helpers', () => {
|
|
|
112
115
|
});
|
|
113
116
|
});
|
|
114
117
|
});
|
|
118
|
+
describe('--profile flag parsing @plan:PLAN-20251118-ISSUE533.P04', () => {
|
|
119
|
+
let originalArgv;
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
originalArgv = [...process.argv];
|
|
122
|
+
});
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
process.argv = originalArgv;
|
|
125
|
+
});
|
|
126
|
+
// Group 1: Basic Parsing (5 tests)
|
|
127
|
+
/**
|
|
128
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
129
|
+
* @requirement:REQ-PROF-001.1
|
|
130
|
+
* @scenario: Parse --profile with space-separated JSON string
|
|
131
|
+
* @given: Command line with --profile followed by JSON string
|
|
132
|
+
* @when: parseBootstrapArgs is called
|
|
133
|
+
* @then: profileJson is populated with the JSON string and profileName is null
|
|
134
|
+
*/
|
|
135
|
+
it('should parse --profile with space-separated JSON string', () => {
|
|
136
|
+
process.argv = [
|
|
137
|
+
'node',
|
|
138
|
+
'llxprt',
|
|
139
|
+
'--profile',
|
|
140
|
+
'{"provider":"openai","model":"gpt-4"}',
|
|
141
|
+
];
|
|
142
|
+
const result = parseBootstrapArgs();
|
|
143
|
+
expect(result.bootstrapArgs.profileJson).toBe('{"provider":"openai","model":"gpt-4"}');
|
|
144
|
+
expect(result.bootstrapArgs.profileName).toBeNull();
|
|
145
|
+
});
|
|
146
|
+
/**
|
|
147
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
148
|
+
* @requirement:REQ-PROF-001.1
|
|
149
|
+
* @scenario: Parse --profile with equals syntax
|
|
150
|
+
* @given: Command line with --profile=<JSON>
|
|
151
|
+
* @when: parseBootstrapArgs is called
|
|
152
|
+
* @then: profileJson is populated with the JSON string
|
|
153
|
+
*/
|
|
154
|
+
it('should parse --profile with equals syntax', () => {
|
|
155
|
+
process.argv = [
|
|
156
|
+
'node',
|
|
157
|
+
'llxprt',
|
|
158
|
+
'--profile={"provider":"anthropic","model":"claude-3"}',
|
|
159
|
+
];
|
|
160
|
+
const result = parseBootstrapArgs();
|
|
161
|
+
expect(result.bootstrapArgs.profileJson).toBe('{"provider":"anthropic","model":"claude-3"}');
|
|
162
|
+
expect(result.bootstrapArgs.profileName).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
/**
|
|
165
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
166
|
+
* @requirement:REQ-PROF-001.1
|
|
167
|
+
* @scenario: Accept empty JSON object
|
|
168
|
+
* @given: Command line with --profile {}
|
|
169
|
+
* @when: parseBootstrapArgs is called
|
|
170
|
+
* @then: profileJson is set to "{}"
|
|
171
|
+
*/
|
|
172
|
+
it('should accept empty JSON object', () => {
|
|
173
|
+
process.argv = ['node', 'llxprt', '--profile', '{}'];
|
|
174
|
+
const result = parseBootstrapArgs();
|
|
175
|
+
expect(result.bootstrapArgs.profileJson).toBe('{}');
|
|
176
|
+
});
|
|
177
|
+
/**
|
|
178
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
179
|
+
* @requirement:REQ-PROF-001.1
|
|
180
|
+
* @scenario: Preserve whitespace in JSON string
|
|
181
|
+
* @given: Command line with JSON containing various whitespace
|
|
182
|
+
* @when: parseBootstrapArgs is called
|
|
183
|
+
* @then: profileJson preserves all whitespace characters
|
|
184
|
+
*/
|
|
185
|
+
it('should preserve whitespace in JSON string', () => {
|
|
186
|
+
const jsonWithWhitespace = '{\n "provider": "openai",\n "model": "gpt-4"\n}';
|
|
187
|
+
process.argv = ['node', 'llxprt', '--profile', jsonWithWhitespace];
|
|
188
|
+
const result = parseBootstrapArgs();
|
|
189
|
+
expect(result.bootstrapArgs.profileJson).toBe(jsonWithWhitespace);
|
|
190
|
+
});
|
|
191
|
+
/**
|
|
192
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
193
|
+
* @requirement:REQ-PROF-001.1
|
|
194
|
+
* @scenario: Parse --profile alongside other flags
|
|
195
|
+
* @given: Command line with --profile and other flags like --provider
|
|
196
|
+
* @when: parseBootstrapArgs is called
|
|
197
|
+
* @then: Both profileJson and other args are correctly populated
|
|
198
|
+
*/
|
|
199
|
+
it('should parse --profile alongside other flags', () => {
|
|
200
|
+
process.argv = [
|
|
201
|
+
'node',
|
|
202
|
+
'llxprt',
|
|
203
|
+
'--profile',
|
|
204
|
+
'{"model":"gpt-4"}',
|
|
205
|
+
'--key',
|
|
206
|
+
'test-key',
|
|
207
|
+
'--set',
|
|
208
|
+
'debug=true',
|
|
209
|
+
];
|
|
210
|
+
const result = parseBootstrapArgs();
|
|
211
|
+
expect(result.bootstrapArgs.profileJson).toBe('{"model":"gpt-4"}');
|
|
212
|
+
expect(result.bootstrapArgs.keyOverride).toBe('test-key');
|
|
213
|
+
expect(result.bootstrapArgs.setOverrides).toEqual(['debug=true']);
|
|
214
|
+
});
|
|
215
|
+
// Group 2: Error Cases (4 tests)
|
|
216
|
+
/**
|
|
217
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
218
|
+
* @requirement:REQ-PROF-001.2
|
|
219
|
+
* @scenario: Throw error when --profile has no value
|
|
220
|
+
* @given: Command line with --profile at the end without a value
|
|
221
|
+
* @when: parseBootstrapArgs is called
|
|
222
|
+
* @then: Error is thrown indicating missing value
|
|
223
|
+
*/
|
|
224
|
+
it('should throw error when --profile has no value', () => {
|
|
225
|
+
process.argv = ['node', 'llxprt', '--profile'];
|
|
226
|
+
expect(() => parseBootstrapArgs()).toThrow();
|
|
227
|
+
});
|
|
228
|
+
/**
|
|
229
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
230
|
+
* @requirement:REQ-PROF-001.2
|
|
231
|
+
* @scenario: Throw error when --profile is followed by another flag
|
|
232
|
+
* @given: Command line with --profile followed by --provider
|
|
233
|
+
* @when: parseBootstrapArgs is called
|
|
234
|
+
* @then: Error is thrown indicating missing value
|
|
235
|
+
*/
|
|
236
|
+
it('should throw error when --profile is followed by another flag', () => {
|
|
237
|
+
process.argv = ['node', 'llxprt', '--profile', '--provider', 'openai'];
|
|
238
|
+
expect(() => parseBootstrapArgs()).toThrow();
|
|
239
|
+
});
|
|
240
|
+
/**
|
|
241
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
242
|
+
* @requirement:REQ-PROF-001.2
|
|
243
|
+
* @scenario: Accept empty string (validation fails later)
|
|
244
|
+
* @given: Command line with --profile ""
|
|
245
|
+
* @when: parseBootstrapArgs is called
|
|
246
|
+
* @then: profileJson is set to empty string (validation happens in Phase 05)
|
|
247
|
+
*/
|
|
248
|
+
it('should accept empty string for --profile', () => {
|
|
249
|
+
process.argv = ['node', 'llxprt', '--profile', ''];
|
|
250
|
+
const result = parseBootstrapArgs();
|
|
251
|
+
expect(result.bootstrapArgs.profileJson).toBe('');
|
|
252
|
+
});
|
|
253
|
+
/**
|
|
254
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
255
|
+
* @requirement:REQ-PROF-003.3
|
|
256
|
+
* @scenario: Throw error for JSON exceeding 10KB
|
|
257
|
+
* @given: Command line with JSON string larger than 10KB
|
|
258
|
+
* @when: parseBootstrapArgs is called
|
|
259
|
+
* @then: Error message indicates size limit exceeded
|
|
260
|
+
*/
|
|
261
|
+
it('should throw error for JSON exceeding 10KB', () => {
|
|
262
|
+
const largeJson = '{"data":"' + 'x'.repeat(10 * 1024) + '"}';
|
|
263
|
+
process.argv = ['node', 'llxprt', '--profile', largeJson];
|
|
264
|
+
expect(() => parseBootstrapArgs()).toThrow(/exceeds maximum size of 10KB/i);
|
|
265
|
+
});
|
|
266
|
+
// Group 3: Mutual Exclusivity (4 tests)
|
|
267
|
+
/**
|
|
268
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
269
|
+
* @requirement:REQ-INT-001.2
|
|
270
|
+
* @scenario: Throw error when both --profile and --profile-load are used
|
|
271
|
+
* @given: Command line with both --profile and --profile-load
|
|
272
|
+
* @when: parseBootstrapArgs is called
|
|
273
|
+
* @then: Error message indicates mutual exclusivity
|
|
274
|
+
*/
|
|
275
|
+
it('should throw error when both --profile and --profile-load are used', () => {
|
|
276
|
+
process.argv = [
|
|
277
|
+
'node',
|
|
278
|
+
'llxprt',
|
|
279
|
+
'--profile',
|
|
280
|
+
'{"provider":"openai"}',
|
|
281
|
+
'--profile-load',
|
|
282
|
+
'my-profile',
|
|
283
|
+
];
|
|
284
|
+
expect(() => parseBootstrapArgs()).toThrow(/cannot use both.*--profile.*--profile-load/i);
|
|
285
|
+
});
|
|
286
|
+
/**
|
|
287
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
288
|
+
* @requirement:REQ-INT-001.2
|
|
289
|
+
* @scenario: Throw error regardless of flag order
|
|
290
|
+
* @given: Command line with --profile-load before --profile
|
|
291
|
+
* @when: parseBootstrapArgs is called
|
|
292
|
+
* @then: Error message indicates mutual exclusivity
|
|
293
|
+
*/
|
|
294
|
+
it('should throw error regardless of flag order', () => {
|
|
295
|
+
process.argv = [
|
|
296
|
+
'node',
|
|
297
|
+
'llxprt',
|
|
298
|
+
'--profile-load',
|
|
299
|
+
'my-profile',
|
|
300
|
+
'--profile',
|
|
301
|
+
'{"provider":"openai"}',
|
|
302
|
+
];
|
|
303
|
+
expect(() => parseBootstrapArgs()).toThrow(/cannot use both.*--profile.*--profile-load/i);
|
|
304
|
+
});
|
|
305
|
+
/**
|
|
306
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
307
|
+
* @requirement:REQ-INT-001.2
|
|
308
|
+
* @scenario: Error message includes helpful guidance
|
|
309
|
+
* @given: Command line with both flags
|
|
310
|
+
* @when: parseBootstrapArgs is called
|
|
311
|
+
* @then: Error message suggests using one flag at a time
|
|
312
|
+
*/
|
|
313
|
+
it('should provide helpful error message for mutual exclusivity', () => {
|
|
314
|
+
process.argv = [
|
|
315
|
+
'node',
|
|
316
|
+
'llxprt',
|
|
317
|
+
'--profile',
|
|
318
|
+
'{"provider":"openai"}',
|
|
319
|
+
'--profile-load',
|
|
320
|
+
'my-profile',
|
|
321
|
+
];
|
|
322
|
+
expect(() => parseBootstrapArgs()).toThrow(/cannot use both.*--profile.*--profile-load.*use one at a time/i);
|
|
323
|
+
});
|
|
324
|
+
/**
|
|
325
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
326
|
+
* @requirement:REQ-INT-001.2
|
|
327
|
+
* @scenario: No error when only --profile is used
|
|
328
|
+
* @given: Command line with only --profile flag
|
|
329
|
+
* @when: parseBootstrapArgs is called
|
|
330
|
+
* @then: Parsing succeeds without error
|
|
331
|
+
*/
|
|
332
|
+
it('should not throw error when only --profile is used', () => {
|
|
333
|
+
process.argv = ['node', 'llxprt', '--profile', '{"provider":"openai"}'];
|
|
334
|
+
expect(() => parseBootstrapArgs()).not.toThrow();
|
|
335
|
+
});
|
|
336
|
+
// Group 4: Edge Cases (2 tests)
|
|
337
|
+
/**
|
|
338
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
339
|
+
* @requirement:REQ-PROF-001.3
|
|
340
|
+
* @scenario: Use last --profile value when multiple specified
|
|
341
|
+
* @given: Command line with --profile specified multiple times
|
|
342
|
+
* @when: parseBootstrapArgs is called
|
|
343
|
+
* @then: profileJson contains the last specified value
|
|
344
|
+
*/
|
|
345
|
+
it('should use last --profile value when multiple specified', () => {
|
|
346
|
+
process.argv = [
|
|
347
|
+
'node',
|
|
348
|
+
'llxprt',
|
|
349
|
+
'--profile',
|
|
350
|
+
'{"provider":"openai"}',
|
|
351
|
+
'--profile',
|
|
352
|
+
'{"provider":"anthropic"}',
|
|
353
|
+
];
|
|
354
|
+
const result = parseBootstrapArgs();
|
|
355
|
+
expect(result.bootstrapArgs.profileJson).toBe('{"provider":"anthropic"}');
|
|
356
|
+
});
|
|
357
|
+
/**
|
|
358
|
+
* @plan:PLAN-20251118-ISSUE533.P04
|
|
359
|
+
* @requirement:REQ-PROF-001.1
|
|
360
|
+
* @scenario: Preserve JSON with special characters
|
|
361
|
+
* @given: Command line with JSON containing quotes, backslashes, and unicode
|
|
362
|
+
* @when: parseBootstrapArgs is called
|
|
363
|
+
* @then: profileJson preserves all special characters exactly
|
|
364
|
+
*/
|
|
365
|
+
it('should preserve JSON with special characters', () => {
|
|
366
|
+
const jsonWithSpecialChars = '{"message":"Hello \\"World\\"","emoji":"🚀","path":"C:\\\\Users"}';
|
|
367
|
+
process.argv = ['node', 'llxprt', '--profile', jsonWithSpecialChars];
|
|
368
|
+
const result = parseBootstrapArgs();
|
|
369
|
+
expect(result.bootstrapArgs.profileJson).toBe(jsonWithSpecialChars);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
describe('parseInlineProfile() @plan:PLAN-20251118-ISSUE533.P07', () => {
|
|
373
|
+
/**
|
|
374
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
375
|
+
* @requirement:REQ-PROF-002.1
|
|
376
|
+
* @scenario: Parse minimal valid profile with only required fields
|
|
377
|
+
* @given: JSON string with provider, model, and key
|
|
378
|
+
* @when: parseInlineProfile() is called
|
|
379
|
+
* @then: Returns ProfileApplicationResult with provider and model names
|
|
380
|
+
*/
|
|
381
|
+
it('should parse minimal valid profile with required fields', () => {
|
|
382
|
+
const jsonString = JSON.stringify({
|
|
383
|
+
provider: 'anthropic',
|
|
384
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
385
|
+
key: 'sk-test-key-123',
|
|
386
|
+
});
|
|
387
|
+
const result = parseInlineProfile(jsonString);
|
|
388
|
+
expect(result.providerName).toBe('anthropic');
|
|
389
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
390
|
+
expect(result.warnings).toEqual([]);
|
|
391
|
+
});
|
|
392
|
+
/**
|
|
393
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
394
|
+
* @requirement:REQ-PROF-002.1
|
|
395
|
+
* @scenario: Parse profile with optional configuration fields
|
|
396
|
+
* @given: JSON string with provider, model, key, temperature, and maxTokens
|
|
397
|
+
* @when: parseInlineProfile() is called
|
|
398
|
+
* @then: Returns ProfileApplicationResult with provider and model names
|
|
399
|
+
*/
|
|
400
|
+
it('should parse profile with optional fields like temperature and maxTokens', () => {
|
|
401
|
+
const jsonString = JSON.stringify({
|
|
402
|
+
provider: 'openai',
|
|
403
|
+
model: 'gpt-4',
|
|
404
|
+
key: 'sk-test-key-456',
|
|
405
|
+
temperature: 0.7,
|
|
406
|
+
maxTokens: 2000,
|
|
407
|
+
});
|
|
408
|
+
const result = parseInlineProfile(jsonString);
|
|
409
|
+
expect(result.providerName).toBe('openai');
|
|
410
|
+
expect(result.modelName).toBe('gpt-4');
|
|
411
|
+
expect(result.warnings).toEqual([]);
|
|
412
|
+
});
|
|
413
|
+
/**
|
|
414
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
415
|
+
* @requirement:REQ-PROF-002.1
|
|
416
|
+
* @scenario: Parse profile with whitespace formatting
|
|
417
|
+
* @given: JSON string with extra whitespace and newlines
|
|
418
|
+
* @when: parseInlineProfile() is called
|
|
419
|
+
* @then: Returns ProfileApplicationResult ignoring whitespace
|
|
420
|
+
*/
|
|
421
|
+
it('should parse profile with whitespace formatting', () => {
|
|
422
|
+
const jsonString = `
|
|
423
|
+
{
|
|
424
|
+
"provider": "anthropic",
|
|
425
|
+
"model": "claude-3-5-sonnet-20241022",
|
|
426
|
+
"key": "sk-test-key-789"
|
|
427
|
+
}
|
|
428
|
+
`;
|
|
429
|
+
const result = parseInlineProfile(jsonString);
|
|
430
|
+
expect(result.providerName).toBe('anthropic');
|
|
431
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
432
|
+
expect(result.warnings).toEqual([]);
|
|
433
|
+
});
|
|
434
|
+
/**
|
|
435
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
436
|
+
* @requirement:REQ-PROF-002.1
|
|
437
|
+
* @scenario: Parse profile with nested objects
|
|
438
|
+
* @given: JSON string with nested tool_choice configuration
|
|
439
|
+
* @when: parseInlineProfile() is called
|
|
440
|
+
* @then: Returns ProfileApplicationResult with provider and model names
|
|
441
|
+
*/
|
|
442
|
+
it('should parse profile with nested objects like tool_choice', () => {
|
|
443
|
+
const jsonString = JSON.stringify({
|
|
444
|
+
provider: 'openai',
|
|
445
|
+
model: 'gpt-4',
|
|
446
|
+
key: 'sk-test-key-abc',
|
|
447
|
+
tool_choice: {
|
|
448
|
+
type: 'function',
|
|
449
|
+
function: { name: 'get_weather' },
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
const result = parseInlineProfile(jsonString);
|
|
453
|
+
expect(result.providerName).toBe('openai');
|
|
454
|
+
expect(result.modelName).toBe('gpt-4');
|
|
455
|
+
expect(result.warnings).toEqual([]);
|
|
456
|
+
});
|
|
457
|
+
/**
|
|
458
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
459
|
+
* @requirement:REQ-PROF-002.1
|
|
460
|
+
* @scenario: Parse profiles for all supported providers
|
|
461
|
+
* @given: JSON strings for openai, anthropic, google, and azure providers
|
|
462
|
+
* @when: parseInlineProfile() is called for each
|
|
463
|
+
* @then: Returns correct provider and model names for each
|
|
464
|
+
*/
|
|
465
|
+
it('should parse profiles for all supported providers', () => {
|
|
466
|
+
const providers = [
|
|
467
|
+
{ provider: 'openai', model: 'gpt-4', key: 'key1' },
|
|
468
|
+
{
|
|
469
|
+
provider: 'anthropic',
|
|
470
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
471
|
+
key: 'key2',
|
|
472
|
+
},
|
|
473
|
+
{ provider: 'google', model: 'gemini-pro', key: 'key3' },
|
|
474
|
+
{ provider: 'azure', model: 'gpt-4', key: 'key4' },
|
|
475
|
+
];
|
|
476
|
+
providers.forEach((profile) => {
|
|
477
|
+
const result = parseInlineProfile(JSON.stringify(profile));
|
|
478
|
+
expect(result.providerName).toBe(profile.provider);
|
|
479
|
+
expect(result.modelName).toBe(profile.model);
|
|
480
|
+
expect(result.warnings).toEqual([]);
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
/**
|
|
484
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
485
|
+
* @requirement:REQ-PROF-001.3
|
|
486
|
+
* @scenario: Handle invalid JSON syntax
|
|
487
|
+
* @given: Malformed JSON string with syntax errors
|
|
488
|
+
* @when: parseInlineProfile() is called
|
|
489
|
+
* @then: Returns error in ProfileApplicationResult
|
|
490
|
+
*/
|
|
491
|
+
it('should handle invalid JSON syntax', () => {
|
|
492
|
+
const invalidJson = '{provider: "anthropic", invalid syntax}';
|
|
493
|
+
const result = parseInlineProfile(invalidJson);
|
|
494
|
+
expect(result.providerName).toBe('');
|
|
495
|
+
expect(result.modelName).toBe('');
|
|
496
|
+
expect(result.error).toBeDefined();
|
|
497
|
+
expect(result.error).toContain('JSON');
|
|
498
|
+
});
|
|
499
|
+
/**
|
|
500
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
501
|
+
* @requirement:REQ-PROF-001.3
|
|
502
|
+
* @scenario: Handle empty string input
|
|
503
|
+
* @given: Empty string
|
|
504
|
+
* @when: parseInlineProfile() is called
|
|
505
|
+
* @then: Returns error in ProfileApplicationResult
|
|
506
|
+
*/
|
|
507
|
+
it('should handle empty string', () => {
|
|
508
|
+
const result = parseInlineProfile('');
|
|
509
|
+
expect(result.providerName).toBe('');
|
|
510
|
+
expect(result.modelName).toBe('');
|
|
511
|
+
expect(result.error).toBeDefined();
|
|
512
|
+
expect(result.error).toContain('JSON');
|
|
513
|
+
});
|
|
514
|
+
/**
|
|
515
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
516
|
+
* @requirement:REQ-PROF-001.3
|
|
517
|
+
* @scenario: Handle whitespace-only string input
|
|
518
|
+
* @given: String containing only whitespace
|
|
519
|
+
* @when: parseInlineProfile() is called
|
|
520
|
+
* @then: Returns error in ProfileApplicationResult
|
|
521
|
+
*/
|
|
522
|
+
it('should handle whitespace-only string', () => {
|
|
523
|
+
const result = parseInlineProfile(' \n\t ');
|
|
524
|
+
expect(result.providerName).toBe('');
|
|
525
|
+
expect(result.modelName).toBe('');
|
|
526
|
+
expect(result.error).toBeDefined();
|
|
527
|
+
expect(result.error).toContain('JSON');
|
|
528
|
+
});
|
|
529
|
+
/**
|
|
530
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
531
|
+
* @requirement:REQ-PROF-002.3
|
|
532
|
+
* @scenario: Validate missing provider field
|
|
533
|
+
* @given: JSON string without provider field
|
|
534
|
+
* @when: parseInlineProfile() is called
|
|
535
|
+
* @then: Returns validation error in ProfileApplicationResult
|
|
536
|
+
*/
|
|
537
|
+
it('should reject profile missing provider field', () => {
|
|
538
|
+
const jsonString = JSON.stringify({
|
|
539
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
540
|
+
key: 'sk-test-key-missing-provider',
|
|
541
|
+
});
|
|
542
|
+
const result = parseInlineProfile(jsonString);
|
|
543
|
+
expect(result.providerName).toBe('');
|
|
544
|
+
expect(result.modelName).toBe('');
|
|
545
|
+
expect(result.error).toBeDefined();
|
|
546
|
+
expect(result.error).toContain('provider');
|
|
547
|
+
});
|
|
548
|
+
/**
|
|
549
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
550
|
+
* @requirement:REQ-PROF-002.3
|
|
551
|
+
* @scenario: Validate missing model field
|
|
552
|
+
* @given: JSON string without model field
|
|
553
|
+
* @when: parseInlineProfile() is called
|
|
554
|
+
* @then: Returns validation error in ProfileApplicationResult
|
|
555
|
+
*/
|
|
556
|
+
it('should reject profile missing model field', () => {
|
|
557
|
+
const jsonString = JSON.stringify({
|
|
558
|
+
provider: 'anthropic',
|
|
559
|
+
key: 'sk-test-key-missing-model',
|
|
560
|
+
});
|
|
561
|
+
const result = parseInlineProfile(jsonString);
|
|
562
|
+
expect(result.providerName).toBe('');
|
|
563
|
+
expect(result.modelName).toBe('');
|
|
564
|
+
expect(result.error).toBeDefined();
|
|
565
|
+
expect(result.error).toContain('model');
|
|
566
|
+
});
|
|
567
|
+
/**
|
|
568
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
569
|
+
* @requirement:REQ-PROF-002.3
|
|
570
|
+
* @scenario: Accept custom/alias provider names
|
|
571
|
+
* @given: JSON string with custom provider name (e.g., alias like "Synthetic")
|
|
572
|
+
* @when: parseInlineProfile() is called
|
|
573
|
+
* @then: Parses successfully - provider validation happens later during application
|
|
574
|
+
*/
|
|
575
|
+
it('should accept custom provider names for aliases', () => {
|
|
576
|
+
const jsonString = JSON.stringify({
|
|
577
|
+
provider: 'Synthetic',
|
|
578
|
+
model: 'hf:zai-org/GLM-4.6',
|
|
579
|
+
key: 'sk-test-key-custom-provider',
|
|
580
|
+
});
|
|
581
|
+
const result = parseInlineProfile(jsonString);
|
|
582
|
+
expect(result.error).toBeUndefined();
|
|
583
|
+
expect(result.providerName).toBe('Synthetic');
|
|
584
|
+
expect(result.modelName).toBe('hf:zai-org/GLM-4.6');
|
|
585
|
+
expect(result.warnings).toEqual([]);
|
|
586
|
+
});
|
|
587
|
+
/**
|
|
588
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
589
|
+
* @requirement:REQ-PROF-002.3
|
|
590
|
+
* @scenario: Validate invalid field types
|
|
591
|
+
* @given: JSON string with temperature as string instead of number
|
|
592
|
+
* @when: parseInlineProfile() is called
|
|
593
|
+
* @then: Returns validation error in ProfileApplicationResult
|
|
594
|
+
*/
|
|
595
|
+
it('should reject profile with invalid field types', () => {
|
|
596
|
+
const jsonString = JSON.stringify({
|
|
597
|
+
provider: 'anthropic',
|
|
598
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
599
|
+
key: 'sk-test-key-type-error',
|
|
600
|
+
temperature: 'hot',
|
|
601
|
+
});
|
|
602
|
+
const result = parseInlineProfile(jsonString);
|
|
603
|
+
expect(result.providerName).toBe('');
|
|
604
|
+
expect(result.modelName).toBe('');
|
|
605
|
+
expect(result.error).toBeDefined();
|
|
606
|
+
expect(result.error).toContain('temperature');
|
|
607
|
+
});
|
|
608
|
+
/**
|
|
609
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
610
|
+
* @requirement:REQ-PROF-003.3
|
|
611
|
+
* @scenario: Reject nesting depth exceeding limit
|
|
612
|
+
* @given: JSON string with >5 levels of nesting
|
|
613
|
+
* @when: parseInlineProfile() is called
|
|
614
|
+
* @then: Returns security error in ProfileApplicationResult
|
|
615
|
+
*/
|
|
616
|
+
it('should reject nesting depth exceeding limit of 5 levels', () => {
|
|
617
|
+
const deeplyNested = {
|
|
618
|
+
provider: 'anthropic',
|
|
619
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
620
|
+
key: 'sk-test-key-deep',
|
|
621
|
+
level1: {
|
|
622
|
+
level2: {
|
|
623
|
+
level3: {
|
|
624
|
+
level4: {
|
|
625
|
+
level5: {
|
|
626
|
+
level6: 'too deep',
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
const result = parseInlineProfile(JSON.stringify(deeplyNested));
|
|
634
|
+
expect(result.providerName).toBe('');
|
|
635
|
+
expect(result.modelName).toBe('');
|
|
636
|
+
expect(result.error).toBeDefined();
|
|
637
|
+
expect(result.error).toContain('nesting');
|
|
638
|
+
});
|
|
639
|
+
/**
|
|
640
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
641
|
+
* @requirement:REQ-PROF-003.3
|
|
642
|
+
* @scenario: Accept nesting depth at limit
|
|
643
|
+
* @given: JSON string with exactly 5 levels of nesting
|
|
644
|
+
* @when: parseInlineProfile() is called
|
|
645
|
+
* @then: Returns successful ProfileApplicationResult
|
|
646
|
+
*/
|
|
647
|
+
it('should accept nesting depth at limit of 5 levels', () => {
|
|
648
|
+
const atLimitNested = {
|
|
649
|
+
provider: 'anthropic',
|
|
650
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
651
|
+
key: 'sk-test-key-limit',
|
|
652
|
+
level1: {
|
|
653
|
+
level2: {
|
|
654
|
+
level3: {
|
|
655
|
+
level4: {
|
|
656
|
+
level5: 'at limit',
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
};
|
|
662
|
+
const result = parseInlineProfile(JSON.stringify(atLimitNested));
|
|
663
|
+
expect(result.providerName).toBe('anthropic');
|
|
664
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
665
|
+
expect(result.warnings).toEqual([]);
|
|
666
|
+
});
|
|
667
|
+
/**
|
|
668
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
669
|
+
* @requirement:REQ-PROF-003.2
|
|
670
|
+
* @scenario: Reject profile with __proto__ field
|
|
671
|
+
* @given: JSON string containing __proto__ field
|
|
672
|
+
* @when: parseInlineProfile() is called
|
|
673
|
+
* @then: Returns security error in ProfileApplicationResult
|
|
674
|
+
*/
|
|
675
|
+
it('should reject profile with disallowed __proto__ field', () => {
|
|
676
|
+
// JSON.stringify does not serialize __proto__, so construct the string directly
|
|
677
|
+
const jsonString = '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test-key-proto","__proto__":{"isAdmin":true}}';
|
|
678
|
+
const result = parseInlineProfile(jsonString);
|
|
679
|
+
expect(result.providerName).toBe('');
|
|
680
|
+
expect(result.modelName).toBe('');
|
|
681
|
+
expect(result.error).toBeDefined();
|
|
682
|
+
expect(result.error).toContain('__proto__');
|
|
683
|
+
});
|
|
684
|
+
/**
|
|
685
|
+
* @plan:PLAN-20251118-ISSUE533.P07
|
|
686
|
+
* @requirement:REQ-PROF-003.2
|
|
687
|
+
* @scenario: Reject profile with constructor field
|
|
688
|
+
* @given: JSON string containing constructor field
|
|
689
|
+
* @when: parseInlineProfile() is called
|
|
690
|
+
* @then: Returns security error in ProfileApplicationResult
|
|
691
|
+
*/
|
|
692
|
+
it('should reject profile with disallowed constructor field', () => {
|
|
693
|
+
const jsonString = JSON.stringify({
|
|
694
|
+
provider: 'anthropic',
|
|
695
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
696
|
+
key: 'sk-test-key-constructor',
|
|
697
|
+
constructor: { prototype: {} },
|
|
698
|
+
});
|
|
699
|
+
const result = parseInlineProfile(jsonString);
|
|
700
|
+
expect(result.providerName).toBe('');
|
|
701
|
+
expect(result.modelName).toBe('');
|
|
702
|
+
expect(result.error).toBeDefined();
|
|
703
|
+
expect(result.error).toContain('constructor');
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
describe('applyBootstrapProfile() with --profile @plan:PLAN-20251118-ISSUE533.P09', () => {
|
|
707
|
+
let mockSettingsService;
|
|
708
|
+
let mockConfig;
|
|
709
|
+
let mockOAuthManager;
|
|
710
|
+
beforeEach(() => {
|
|
711
|
+
vi.clearAllMocks();
|
|
712
|
+
mockSettingsService = {
|
|
713
|
+
listProfiles: vi.fn(),
|
|
714
|
+
getProfile: vi.fn(),
|
|
715
|
+
getProfileByID: vi.fn(),
|
|
716
|
+
isLoaded: vi.fn().mockReturnValue(true),
|
|
717
|
+
};
|
|
718
|
+
mockConfig = {
|
|
719
|
+
profiles: [],
|
|
720
|
+
};
|
|
721
|
+
mockOAuthManager = {};
|
|
722
|
+
});
|
|
723
|
+
afterEach(() => {
|
|
724
|
+
vi.clearAllMocks();
|
|
725
|
+
});
|
|
726
|
+
// Group 1: Basic Profile Application (4 tests)
|
|
727
|
+
/**
|
|
728
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
729
|
+
* @requirement:REQ-INT-001.1
|
|
730
|
+
* @scenario: Apply a complete inline profile successfully
|
|
731
|
+
* @given: profileJson contains provider, model, and key
|
|
732
|
+
* @when: applyBootstrapProfile() is called
|
|
733
|
+
* @then: Returns bootstrap result with provider and model set correctly
|
|
734
|
+
*/
|
|
735
|
+
it('should apply inline profile successfully with provider, model, and key', () => {
|
|
736
|
+
const args = {
|
|
737
|
+
profileName: null,
|
|
738
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test123"}',
|
|
739
|
+
providerOverride: null,
|
|
740
|
+
modelOverride: null,
|
|
741
|
+
keyOverride: null,
|
|
742
|
+
keyfileOverride: null,
|
|
743
|
+
baseurlOverride: null,
|
|
744
|
+
setOverrides: null,
|
|
745
|
+
};
|
|
746
|
+
const parsed = parseArgsWithMeta(args, {
|
|
747
|
+
settingsService: mockSettingsService,
|
|
748
|
+
config: mockConfig,
|
|
749
|
+
oauthManager: mockOAuthManager,
|
|
750
|
+
});
|
|
751
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
752
|
+
expect(result.providerName).toBe('openai');
|
|
753
|
+
expect(result.modelName).toBe('gpt-4');
|
|
754
|
+
expect(result.warnings).toEqual([]);
|
|
755
|
+
});
|
|
756
|
+
/**
|
|
757
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
758
|
+
* @requirement:REQ-INT-001.1
|
|
759
|
+
* @scenario: Apply Anthropic inline profile
|
|
760
|
+
* @given: profileJson contains Anthropic provider and claude model
|
|
761
|
+
* @when: applyBootstrapProfile() is called
|
|
762
|
+
* @then: Returns bootstrap result with Anthropic provider and model
|
|
763
|
+
*/
|
|
764
|
+
it('should apply Anthropic inline profile', () => {
|
|
765
|
+
const args = {
|
|
766
|
+
profileName: null,
|
|
767
|
+
profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-ant-test"}',
|
|
768
|
+
providerOverride: null,
|
|
769
|
+
modelOverride: null,
|
|
770
|
+
keyOverride: null,
|
|
771
|
+
keyfileOverride: null,
|
|
772
|
+
baseurlOverride: null,
|
|
773
|
+
setOverrides: null,
|
|
774
|
+
};
|
|
775
|
+
const parsed = parseArgsWithMeta(args, {
|
|
776
|
+
settingsService: mockSettingsService,
|
|
777
|
+
config: mockConfig,
|
|
778
|
+
oauthManager: mockOAuthManager,
|
|
779
|
+
});
|
|
780
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
781
|
+
expect(result.providerName).toBe('anthropic');
|
|
782
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
783
|
+
expect(result.warnings).toEqual([]);
|
|
784
|
+
});
|
|
785
|
+
/**
|
|
786
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
787
|
+
* @requirement:REQ-INT-001.1
|
|
788
|
+
* @scenario: Apply inline profile with optional fields
|
|
789
|
+
* @given: profileJson contains provider, model, key, temperature, and maxTokens
|
|
790
|
+
* @when: applyBootstrapProfile() is called
|
|
791
|
+
* @then: Returns bootstrap result with all fields set correctly
|
|
792
|
+
*/
|
|
793
|
+
it('should apply inline profile with optional fields (temperature, maxTokens)', () => {
|
|
794
|
+
const args = {
|
|
795
|
+
profileName: null,
|
|
796
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test","temperature":0.7,"maxTokens":2000}',
|
|
797
|
+
providerOverride: null,
|
|
798
|
+
modelOverride: null,
|
|
799
|
+
keyOverride: null,
|
|
800
|
+
keyfileOverride: null,
|
|
801
|
+
baseurlOverride: null,
|
|
802
|
+
setOverrides: null,
|
|
803
|
+
};
|
|
804
|
+
const parsed = parseArgsWithMeta(args, {
|
|
805
|
+
settingsService: mockSettingsService,
|
|
806
|
+
config: mockConfig,
|
|
807
|
+
oauthManager: mockOAuthManager,
|
|
808
|
+
});
|
|
809
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
810
|
+
expect(result.providerName).toBe('openai');
|
|
811
|
+
expect(result.modelName).toBe('gpt-4');
|
|
812
|
+
expect(result.warnings).toEqual([]);
|
|
813
|
+
});
|
|
814
|
+
/**
|
|
815
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
816
|
+
* @requirement:REQ-INT-001.1
|
|
817
|
+
* @scenario: Return empty result when no profile specified
|
|
818
|
+
* @given: Both profileName and profileJson are null
|
|
819
|
+
* @when: applyBootstrapProfile() is called
|
|
820
|
+
* @then: Returns empty bootstrap result with no provider or model
|
|
821
|
+
*/
|
|
822
|
+
it('should return empty result when no profile specified', () => {
|
|
823
|
+
const args = {
|
|
824
|
+
profileName: null,
|
|
825
|
+
profileJson: null,
|
|
826
|
+
providerOverride: null,
|
|
827
|
+
modelOverride: null,
|
|
828
|
+
keyOverride: null,
|
|
829
|
+
keyfileOverride: null,
|
|
830
|
+
baseurlOverride: null,
|
|
831
|
+
setOverrides: null,
|
|
832
|
+
};
|
|
833
|
+
const parsed = parseArgsWithMeta(args, {
|
|
834
|
+
settingsService: mockSettingsService,
|
|
835
|
+
config: mockConfig,
|
|
836
|
+
oauthManager: mockOAuthManager,
|
|
837
|
+
});
|
|
838
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
839
|
+
expect(result.providerName).toBeNull();
|
|
840
|
+
expect(result.modelName).toBeNull();
|
|
841
|
+
expect(result.warnings).toEqual([]);
|
|
842
|
+
});
|
|
843
|
+
// Group 2: Override Precedence (4 tests)
|
|
844
|
+
/**
|
|
845
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
846
|
+
* @requirement:REQ-INT-001.1
|
|
847
|
+
* @scenario: Model override takes precedence over inline profile
|
|
848
|
+
* @given: profileJson contains model "gpt-4" and modelOverride is "gpt-3.5-turbo"
|
|
849
|
+
* @when: applyBootstrapProfile() is called
|
|
850
|
+
* @then: Returns bootstrap result with model "gpt-3.5-turbo" from override
|
|
851
|
+
*/
|
|
852
|
+
it('should apply model override over inline profile model', () => {
|
|
853
|
+
const args = {
|
|
854
|
+
profileName: null,
|
|
855
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
|
|
856
|
+
providerOverride: null,
|
|
857
|
+
modelOverride: 'gpt-3.5-turbo',
|
|
858
|
+
keyOverride: null,
|
|
859
|
+
keyfileOverride: null,
|
|
860
|
+
baseurlOverride: null,
|
|
861
|
+
setOverrides: null,
|
|
862
|
+
};
|
|
863
|
+
const parsed = parseArgsWithMeta(args, {
|
|
864
|
+
settingsService: mockSettingsService,
|
|
865
|
+
config: mockConfig,
|
|
866
|
+
oauthManager: mockOAuthManager,
|
|
867
|
+
});
|
|
868
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
869
|
+
expect(result.providerName).toBe('openai');
|
|
870
|
+
expect(result.modelName).toBe('gpt-3.5-turbo');
|
|
871
|
+
expect(result.warnings).toEqual([]);
|
|
872
|
+
});
|
|
873
|
+
/**
|
|
874
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
875
|
+
* @requirement:REQ-INT-001.1
|
|
876
|
+
* @scenario: Provider override takes precedence over inline profile
|
|
877
|
+
* @given: profileJson contains provider "openai" and providerOverride is "anthropic"
|
|
878
|
+
* @when: applyBootstrapProfile() is called
|
|
879
|
+
* @then: Returns bootstrap result with provider "anthropic" from override
|
|
880
|
+
*/
|
|
881
|
+
it('should apply provider override over inline profile provider', () => {
|
|
882
|
+
const args = {
|
|
883
|
+
profileName: null,
|
|
884
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
|
|
885
|
+
providerOverride: 'anthropic',
|
|
886
|
+
modelOverride: null,
|
|
887
|
+
keyOverride: null,
|
|
888
|
+
keyfileOverride: null,
|
|
889
|
+
baseurlOverride: null,
|
|
890
|
+
setOverrides: null,
|
|
891
|
+
};
|
|
892
|
+
const parsed = parseArgsWithMeta(args, {
|
|
893
|
+
settingsService: mockSettingsService,
|
|
894
|
+
config: mockConfig,
|
|
895
|
+
oauthManager: mockOAuthManager,
|
|
896
|
+
});
|
|
897
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
898
|
+
expect(result.providerName).toBe('anthropic');
|
|
899
|
+
expect(result.modelName).toBe('gpt-4');
|
|
900
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
901
|
+
});
|
|
902
|
+
/**
|
|
903
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
904
|
+
* @requirement:REQ-INT-001.1
|
|
905
|
+
* @scenario: Key override with warning generated
|
|
906
|
+
* @given: profileJson contains key and keyOverride is provided
|
|
907
|
+
* @when: applyBootstrapProfile() is called
|
|
908
|
+
* @then: Returns bootstrap result with warning about key override
|
|
909
|
+
*/
|
|
910
|
+
it('should generate warning when key override is applied', () => {
|
|
911
|
+
const args = {
|
|
912
|
+
profileName: null,
|
|
913
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
|
|
914
|
+
providerOverride: null,
|
|
915
|
+
modelOverride: null,
|
|
916
|
+
keyOverride: 'sk-override',
|
|
917
|
+
keyfileOverride: null,
|
|
918
|
+
baseurlOverride: null,
|
|
919
|
+
setOverrides: null,
|
|
920
|
+
};
|
|
921
|
+
const parsed = parseArgsWithMeta(args, {
|
|
922
|
+
settingsService: mockSettingsService,
|
|
923
|
+
config: mockConfig,
|
|
924
|
+
oauthManager: mockOAuthManager,
|
|
925
|
+
});
|
|
926
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
927
|
+
expect(result.providerName).toBe('openai');
|
|
928
|
+
expect(result.modelName).toBe('gpt-4');
|
|
929
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
930
|
+
});
|
|
931
|
+
/**
|
|
932
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
933
|
+
* @requirement:REQ-INT-001.1
|
|
934
|
+
* @scenario: Multiple overrides with warnings
|
|
935
|
+
* @given: profileJson contains provider, model, key and all have overrides
|
|
936
|
+
* @when: applyBootstrapProfile() is called
|
|
937
|
+
* @then: Returns bootstrap result with multiple warnings for overrides
|
|
938
|
+
*/
|
|
939
|
+
it('should generate multiple warnings when multiple overrides are applied', () => {
|
|
940
|
+
const args = {
|
|
941
|
+
profileName: null,
|
|
942
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-test"}',
|
|
943
|
+
providerOverride: 'anthropic',
|
|
944
|
+
modelOverride: 'claude-3-5-sonnet-20241022',
|
|
945
|
+
keyOverride: 'sk-override',
|
|
946
|
+
keyfileOverride: null,
|
|
947
|
+
baseurlOverride: null,
|
|
948
|
+
setOverrides: null,
|
|
949
|
+
};
|
|
950
|
+
const parsed = parseArgsWithMeta(args, {
|
|
951
|
+
settingsService: mockSettingsService,
|
|
952
|
+
config: mockConfig,
|
|
953
|
+
oauthManager: mockOAuthManager,
|
|
954
|
+
});
|
|
955
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
956
|
+
expect(result.providerName).toBe('anthropic');
|
|
957
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
958
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
959
|
+
});
|
|
960
|
+
// Group 3: Validation Error Handling (2 tests)
|
|
961
|
+
/**
|
|
962
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
963
|
+
* @requirement:REQ-INT-001.1
|
|
964
|
+
* @scenario: Throw error for invalid JSON in profileJson
|
|
965
|
+
* @given: profileJson contains invalid JSON syntax
|
|
966
|
+
* @when: applyBootstrapProfile() is called
|
|
967
|
+
* @then: Throws an error about invalid JSON
|
|
968
|
+
*/
|
|
969
|
+
it('should throw error for invalid JSON in profileJson', () => {
|
|
970
|
+
const args = {
|
|
971
|
+
profileName: null,
|
|
972
|
+
profileJson: '{invalid json}',
|
|
973
|
+
providerOverride: null,
|
|
974
|
+
modelOverride: null,
|
|
975
|
+
keyOverride: null,
|
|
976
|
+
keyfileOverride: null,
|
|
977
|
+
baseurlOverride: null,
|
|
978
|
+
setOverrides: null,
|
|
979
|
+
};
|
|
980
|
+
expect(() => {
|
|
981
|
+
parseArgsWithMeta(args, {
|
|
982
|
+
settingsService: mockSettingsService,
|
|
983
|
+
config: mockConfig,
|
|
984
|
+
oauthManager: mockOAuthManager,
|
|
985
|
+
});
|
|
986
|
+
}).toThrow();
|
|
987
|
+
});
|
|
988
|
+
/**
|
|
989
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
990
|
+
* @requirement:REQ-INT-001.1
|
|
991
|
+
* @scenario: Throw error for profile validation failure
|
|
992
|
+
* @given: profileJson is valid JSON but missing required field (provider)
|
|
993
|
+
* @when: applyBootstrapProfile() is called
|
|
994
|
+
* @then: Throws an error about missing required field
|
|
995
|
+
*/
|
|
996
|
+
it('should throw error for profile validation failure (missing required field)', () => {
|
|
997
|
+
const args = {
|
|
998
|
+
profileName: null,
|
|
999
|
+
profileJson: '{"model":"gpt-4","key":"sk-test"}',
|
|
1000
|
+
providerOverride: null,
|
|
1001
|
+
modelOverride: null,
|
|
1002
|
+
keyOverride: null,
|
|
1003
|
+
keyfileOverride: null,
|
|
1004
|
+
baseurlOverride: null,
|
|
1005
|
+
setOverrides: null,
|
|
1006
|
+
};
|
|
1007
|
+
expect(() => {
|
|
1008
|
+
parseArgsWithMeta(args, {
|
|
1009
|
+
settingsService: mockSettingsService,
|
|
1010
|
+
config: mockConfig,
|
|
1011
|
+
oauthManager: mockOAuthManager,
|
|
1012
|
+
});
|
|
1013
|
+
}).toThrow();
|
|
1014
|
+
});
|
|
1015
|
+
// Group 4: Backward Compatibility (2 tests)
|
|
1016
|
+
/**
|
|
1017
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1018
|
+
* @requirement:REQ-INT-001.1
|
|
1019
|
+
* @scenario: Maintain --profile-load behavior
|
|
1020
|
+
* @given: profileName is specified (not profileJson)
|
|
1021
|
+
* @when: applyBootstrapProfile() is called
|
|
1022
|
+
* @then: Returns bootstrap result from named profile as before
|
|
1023
|
+
*/
|
|
1024
|
+
it('should maintain --profile-load behavior when profileName is specified', () => {
|
|
1025
|
+
const mockProfile = {
|
|
1026
|
+
id: 'test-profile',
|
|
1027
|
+
name: 'test-profile',
|
|
1028
|
+
provider: 'openai',
|
|
1029
|
+
model: 'gpt-4',
|
|
1030
|
+
key: 'sk-test',
|
|
1031
|
+
};
|
|
1032
|
+
mockSettingsService.getProfile.mockReturnValue(mockProfile);
|
|
1033
|
+
const args = {
|
|
1034
|
+
profileName: 'test-profile',
|
|
1035
|
+
profileJson: null,
|
|
1036
|
+
providerOverride: null,
|
|
1037
|
+
modelOverride: null,
|
|
1038
|
+
keyOverride: null,
|
|
1039
|
+
keyfileOverride: null,
|
|
1040
|
+
baseurlOverride: null,
|
|
1041
|
+
setOverrides: null,
|
|
1042
|
+
};
|
|
1043
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1044
|
+
settingsService: mockSettingsService,
|
|
1045
|
+
config: mockConfig,
|
|
1046
|
+
oauthManager: mockOAuthManager,
|
|
1047
|
+
});
|
|
1048
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1049
|
+
expect(result.providerName).toBe('openai');
|
|
1050
|
+
expect(result.modelName).toBe('gpt-4');
|
|
1051
|
+
expect(mockSettingsService.getProfile).toHaveBeenCalledWith('test-profile');
|
|
1052
|
+
});
|
|
1053
|
+
/**
|
|
1054
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1055
|
+
* @requirement:REQ-INT-001.1
|
|
1056
|
+
* @scenario: Apply overrides without profile
|
|
1057
|
+
* @given: No profileName or profileJson, but overrides are provided
|
|
1058
|
+
* @when: applyBootstrapProfile() is called
|
|
1059
|
+
* @then: Returns bootstrap result with command-line overrides only
|
|
1060
|
+
*/
|
|
1061
|
+
it('should apply overrides without profile (command-line only overrides)', () => {
|
|
1062
|
+
const args = {
|
|
1063
|
+
profileName: null,
|
|
1064
|
+
profileJson: null,
|
|
1065
|
+
providerOverride: 'openai',
|
|
1066
|
+
modelOverride: 'gpt-4',
|
|
1067
|
+
keyOverride: null,
|
|
1068
|
+
keyfileOverride: null,
|
|
1069
|
+
baseurlOverride: null,
|
|
1070
|
+
setOverrides: null,
|
|
1071
|
+
};
|
|
1072
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1073
|
+
settingsService: mockSettingsService,
|
|
1074
|
+
config: mockConfig,
|
|
1075
|
+
oauthManager: mockOAuthManager,
|
|
1076
|
+
});
|
|
1077
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1078
|
+
expect(result.providerName).toBe('openai');
|
|
1079
|
+
expect(result.modelName).toBe('gpt-4');
|
|
1080
|
+
expect(result.warnings).toEqual([]);
|
|
1081
|
+
});
|
|
1082
|
+
});
|
|
1083
|
+
/**
|
|
1084
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1085
|
+
* Test suite for applyBootstrapProfile() with --profile flag integration (alternative mock setup)
|
|
1086
|
+
*/
|
|
1087
|
+
describe('applyBootstrapProfile() with --profile - alternative tests @plan:PLAN-20251118-ISSUE533.P09', () => {
|
|
1088
|
+
let mockSettings;
|
|
1089
|
+
let mockConfig;
|
|
1090
|
+
beforeEach(() => {
|
|
1091
|
+
vi.clearAllMocks();
|
|
1092
|
+
mockConfig = { profiles: [] };
|
|
1093
|
+
mockSettings = {
|
|
1094
|
+
listProfiles: vi.fn(),
|
|
1095
|
+
getProfile: vi.fn(),
|
|
1096
|
+
getProfileByID: vi.fn(),
|
|
1097
|
+
isLoaded: vi.fn().mockReturnValue(true),
|
|
1098
|
+
};
|
|
1099
|
+
});
|
|
1100
|
+
afterEach(() => {
|
|
1101
|
+
vi.clearAllMocks();
|
|
1102
|
+
});
|
|
1103
|
+
/**
|
|
1104
|
+
* Group 1: Basic Profile Application (4 tests)
|
|
1105
|
+
*/
|
|
1106
|
+
/**
|
|
1107
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1108
|
+
* @requirement:REQ-INT-001.1
|
|
1109
|
+
* @scenario: Apply inline profile without overrides
|
|
1110
|
+
* @given: profileJson with provider, model, key
|
|
1111
|
+
* @when: applyBootstrapProfile() called
|
|
1112
|
+
* @then: Returns BootstrapRuntimeState with profile values
|
|
1113
|
+
*/
|
|
1114
|
+
it('should apply inline profile without overrides', () => {
|
|
1115
|
+
const args = {
|
|
1116
|
+
profileName: null,
|
|
1117
|
+
profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
|
|
1118
|
+
providerOverride: null,
|
|
1119
|
+
modelOverride: null,
|
|
1120
|
+
keyOverride: null,
|
|
1121
|
+
keyfileOverride: null,
|
|
1122
|
+
baseurlOverride: null,
|
|
1123
|
+
setOverrides: null,
|
|
1124
|
+
};
|
|
1125
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1126
|
+
settingsService: mockSettings,
|
|
1127
|
+
config: mockConfig,
|
|
1128
|
+
oauthManager: {},
|
|
1129
|
+
});
|
|
1130
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1131
|
+
expect(result.providerName).toBe('anthropic');
|
|
1132
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
1133
|
+
});
|
|
1134
|
+
/**
|
|
1135
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1136
|
+
* @requirement:REQ-INT-001.2
|
|
1137
|
+
* @scenario: Apply inline profile with provider override
|
|
1138
|
+
* @given: profileJson with anthropic + --provider openai
|
|
1139
|
+
* @when: applyBootstrapProfile() called
|
|
1140
|
+
* @then: Override takes precedence
|
|
1141
|
+
*/
|
|
1142
|
+
it('should apply provider override over inline profile', () => {
|
|
1143
|
+
const args = {
|
|
1144
|
+
profileName: null,
|
|
1145
|
+
profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
|
|
1146
|
+
providerOverride: 'openai',
|
|
1147
|
+
modelOverride: null,
|
|
1148
|
+
keyOverride: null,
|
|
1149
|
+
keyfileOverride: null,
|
|
1150
|
+
baseurlOverride: null,
|
|
1151
|
+
setOverrides: null,
|
|
1152
|
+
};
|
|
1153
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1154
|
+
settingsService: mockSettings,
|
|
1155
|
+
config: mockConfig,
|
|
1156
|
+
oauthManager: {},
|
|
1157
|
+
});
|
|
1158
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1159
|
+
expect(result.providerName).toBe('openai');
|
|
1160
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022'); // Original model preserved
|
|
1161
|
+
});
|
|
1162
|
+
/**
|
|
1163
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1164
|
+
* @requirement:REQ-INT-001.3
|
|
1165
|
+
* @scenario: Apply inline profile with model override
|
|
1166
|
+
* @given: profileJson with sonnet + --model gpt-4
|
|
1167
|
+
* @when: applyBootstrapProfile() called
|
|
1168
|
+
* @then: Override takes precedence
|
|
1169
|
+
*/
|
|
1170
|
+
it('should apply model override over inline profile', () => {
|
|
1171
|
+
const args = {
|
|
1172
|
+
profileName: null,
|
|
1173
|
+
profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
|
|
1174
|
+
providerOverride: null,
|
|
1175
|
+
modelOverride: 'gpt-4',
|
|
1176
|
+
keyOverride: null,
|
|
1177
|
+
keyfileOverride: null,
|
|
1178
|
+
baseurlOverride: null,
|
|
1179
|
+
setOverrides: null,
|
|
1180
|
+
};
|
|
1181
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1182
|
+
settingsService: mockSettings,
|
|
1183
|
+
config: mockConfig,
|
|
1184
|
+
oauthManager: {},
|
|
1185
|
+
});
|
|
1186
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1187
|
+
expect(result.providerName).toBe('anthropic'); // Original provider preserved
|
|
1188
|
+
expect(result.modelName).toBe('gpt-4');
|
|
1189
|
+
});
|
|
1190
|
+
/**
|
|
1191
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1192
|
+
* @requirement:REQ-INT-001.4
|
|
1193
|
+
* @scenario: Apply inline profile with key override
|
|
1194
|
+
* @given: profileJson with sk-test + --key sk-override
|
|
1195
|
+
* @when: applyBootstrapProfile() called
|
|
1196
|
+
* @then: Override takes precedence
|
|
1197
|
+
*/
|
|
1198
|
+
it('should apply key override over inline profile', () => {
|
|
1199
|
+
const args = {
|
|
1200
|
+
profileName: null,
|
|
1201
|
+
profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
|
|
1202
|
+
providerOverride: null,
|
|
1203
|
+
modelOverride: null,
|
|
1204
|
+
keyOverride: 'sk-override',
|
|
1205
|
+
keyfileOverride: null,
|
|
1206
|
+
baseurlOverride: null,
|
|
1207
|
+
setOverrides: null,
|
|
1208
|
+
};
|
|
1209
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1210
|
+
settingsService: mockSettings,
|
|
1211
|
+
config: mockConfig,
|
|
1212
|
+
oauthManager: {},
|
|
1213
|
+
});
|
|
1214
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1215
|
+
expect(result.providerName).toBe('anthropic');
|
|
1216
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
1217
|
+
});
|
|
1218
|
+
/**
|
|
1219
|
+
* Group 2: Profile Source Priority (4 tests)
|
|
1220
|
+
*/
|
|
1221
|
+
/**
|
|
1222
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1223
|
+
* @requirement:REQ-INT-002.1
|
|
1224
|
+
* @scenario: Inline profile takes precedence over named profile
|
|
1225
|
+
* @given: Both --profile myprofile and --profile {...}
|
|
1226
|
+
* @when: applyBootstrapProfile() called
|
|
1227
|
+
* @then: Inline profile values used
|
|
1228
|
+
*/
|
|
1229
|
+
it('should prioritize inline profile over named profile', () => {
|
|
1230
|
+
const args = {
|
|
1231
|
+
profileName: 'myprofile',
|
|
1232
|
+
profileJson: '{"provider":"openai","model":"gpt-4","key":"sk-inline"}',
|
|
1233
|
+
providerOverride: null,
|
|
1234
|
+
modelOverride: null,
|
|
1235
|
+
keyOverride: null,
|
|
1236
|
+
keyfileOverride: null,
|
|
1237
|
+
baseurlOverride: null,
|
|
1238
|
+
setOverrides: null,
|
|
1239
|
+
};
|
|
1240
|
+
mockSettings.getProfile.mockReturnValue({
|
|
1241
|
+
provider: 'anthropic',
|
|
1242
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
1243
|
+
key: 'sk-named',
|
|
1244
|
+
});
|
|
1245
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1246
|
+
settingsService: mockSettings,
|
|
1247
|
+
config: mockConfig,
|
|
1248
|
+
oauthManager: {},
|
|
1249
|
+
});
|
|
1250
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1251
|
+
expect(result.providerName).toBe('openai');
|
|
1252
|
+
expect(result.modelName).toBe('gpt-4');
|
|
1253
|
+
});
|
|
1254
|
+
/**
|
|
1255
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1256
|
+
* @requirement:REQ-INT-002.2
|
|
1257
|
+
* @scenario: Named profile used when no inline profile
|
|
1258
|
+
* @given: --profile myprofile only
|
|
1259
|
+
* @when: applyBootstrapProfile() called
|
|
1260
|
+
* @then: Named profile values used
|
|
1261
|
+
*/
|
|
1262
|
+
it('should use named profile when no inline profile provided', () => {
|
|
1263
|
+
const args = {
|
|
1264
|
+
profileName: 'myprofile',
|
|
1265
|
+
profileJson: null,
|
|
1266
|
+
providerOverride: null,
|
|
1267
|
+
modelOverride: null,
|
|
1268
|
+
keyOverride: null,
|
|
1269
|
+
keyfileOverride: null,
|
|
1270
|
+
baseurlOverride: null,
|
|
1271
|
+
setOverrides: null,
|
|
1272
|
+
};
|
|
1273
|
+
mockSettings.getProfile.mockReturnValue({
|
|
1274
|
+
provider: 'anthropic',
|
|
1275
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
1276
|
+
key: 'sk-named',
|
|
1277
|
+
});
|
|
1278
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1279
|
+
settingsService: mockSettings,
|
|
1280
|
+
config: mockConfig,
|
|
1281
|
+
oauthManager: {},
|
|
1282
|
+
});
|
|
1283
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1284
|
+
expect(result.providerName).toBe('anthropic');
|
|
1285
|
+
expect(result.modelName).toBe('claude-3-5-sonnet-20241022');
|
|
1286
|
+
});
|
|
1287
|
+
/**
|
|
1288
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1289
|
+
* @requirement:REQ-INT-002.3
|
|
1290
|
+
* @scenario: Override flags work with inline profiles
|
|
1291
|
+
* @given: --profile {...} + --model override
|
|
1292
|
+
* @when: applyBootstrapProfile() called
|
|
1293
|
+
* @then: Override takes precedence over inline
|
|
1294
|
+
*/
|
|
1295
|
+
it('should apply overrides to inline profile values', () => {
|
|
1296
|
+
const args = {
|
|
1297
|
+
profileName: null,
|
|
1298
|
+
profileJson: '{"provider":"anthropic","model":"claude-3-5-sonnet-20241022","key":"sk-test"}',
|
|
1299
|
+
providerOverride: 'openai',
|
|
1300
|
+
modelOverride: 'gpt-4',
|
|
1301
|
+
keyOverride: 'sk-override',
|
|
1302
|
+
keyfileOverride: null,
|
|
1303
|
+
baseurlOverride: null,
|
|
1304
|
+
setOverrides: null,
|
|
1305
|
+
};
|
|
1306
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1307
|
+
settingsService: mockSettings,
|
|
1308
|
+
config: mockConfig,
|
|
1309
|
+
oauthManager: {},
|
|
1310
|
+
});
|
|
1311
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1312
|
+
expect(result.providerName).toBe('openai');
|
|
1313
|
+
expect(result.modelName).toBe('gpt-4');
|
|
1314
|
+
});
|
|
1315
|
+
/**
|
|
1316
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1317
|
+
* @requirement:REQ-INT-002.4
|
|
1318
|
+
* @scenario: Override flags work with named profiles
|
|
1319
|
+
* @given: --profile myprofile + --model override
|
|
1320
|
+
* @when: applyBootstrapProfile() called
|
|
1321
|
+
* @then: Override takes precedence over named
|
|
1322
|
+
*/
|
|
1323
|
+
it('should apply overrides to named profile values', () => {
|
|
1324
|
+
const args = {
|
|
1325
|
+
profileName: 'myprofile',
|
|
1326
|
+
profileJson: null,
|
|
1327
|
+
providerOverride: 'openai',
|
|
1328
|
+
modelOverride: 'gpt-4',
|
|
1329
|
+
keyOverride: null,
|
|
1330
|
+
keyfileOverride: null,
|
|
1331
|
+
baseurlOverride: null,
|
|
1332
|
+
setOverrides: null,
|
|
1333
|
+
};
|
|
1334
|
+
mockSettings.getProfile.mockReturnValue({
|
|
1335
|
+
provider: 'anthropic',
|
|
1336
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
1337
|
+
key: 'sk-named',
|
|
1338
|
+
});
|
|
1339
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1340
|
+
settingsService: mockSettings,
|
|
1341
|
+
config: mockConfig,
|
|
1342
|
+
oauthManager: {},
|
|
1343
|
+
});
|
|
1344
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1345
|
+
expect(result.providerName).toBe('openai');
|
|
1346
|
+
expect(result.modelName).toBe('gpt-4');
|
|
1347
|
+
});
|
|
1348
|
+
/**
|
|
1349
|
+
* Group 3: Error Handling (4 tests)
|
|
1350
|
+
*/
|
|
1351
|
+
/**
|
|
1352
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1353
|
+
* @requirement:REQ-INT-003.1
|
|
1354
|
+
* @scenario: Invalid JSON in inline profile
|
|
1355
|
+
* @given: --profile {bad json}
|
|
1356
|
+
* @when: applyBootstrapProfile() called
|
|
1357
|
+
* @then: Throws ProfileBootstrapError
|
|
1358
|
+
*/
|
|
1359
|
+
it('should throw error for invalid JSON in inline profile', () => {
|
|
1360
|
+
const args = {
|
|
1361
|
+
profileName: null,
|
|
1362
|
+
profileJson: '{bad json}',
|
|
1363
|
+
providerOverride: null,
|
|
1364
|
+
modelOverride: null,
|
|
1365
|
+
keyOverride: null,
|
|
1366
|
+
keyfileOverride: null,
|
|
1367
|
+
baseurlOverride: null,
|
|
1368
|
+
setOverrides: null,
|
|
1369
|
+
};
|
|
1370
|
+
expect(() => {
|
|
1371
|
+
parseArgsWithMeta(args, {
|
|
1372
|
+
settingsService: mockSettings,
|
|
1373
|
+
config: mockConfig,
|
|
1374
|
+
oauthManager: {},
|
|
1375
|
+
});
|
|
1376
|
+
}).toThrow();
|
|
1377
|
+
});
|
|
1378
|
+
/**
|
|
1379
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1380
|
+
* @requirement:REQ-INT-003.2
|
|
1381
|
+
* @scenario: Missing required fields in inline profile
|
|
1382
|
+
* @given: --profile {"provider":"anthropic"} (no model/key)
|
|
1383
|
+
* @when: applyBootstrapProfile() called
|
|
1384
|
+
* @then: Throws ProfileBootstrapError
|
|
1385
|
+
*/
|
|
1386
|
+
it('should throw error for missing required fields in inline profile', () => {
|
|
1387
|
+
const args = {
|
|
1388
|
+
profileName: null,
|
|
1389
|
+
profileJson: '{"provider":"anthropic"}',
|
|
1390
|
+
providerOverride: null,
|
|
1391
|
+
modelOverride: null,
|
|
1392
|
+
keyOverride: null,
|
|
1393
|
+
keyfileOverride: null,
|
|
1394
|
+
baseurlOverride: null,
|
|
1395
|
+
setOverrides: null,
|
|
1396
|
+
};
|
|
1397
|
+
expect(() => {
|
|
1398
|
+
parseArgsWithMeta(args, {
|
|
1399
|
+
settingsService: mockSettings,
|
|
1400
|
+
config: mockConfig,
|
|
1401
|
+
oauthManager: {},
|
|
1402
|
+
});
|
|
1403
|
+
}).toThrow();
|
|
1404
|
+
});
|
|
1405
|
+
/**
|
|
1406
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1407
|
+
* @requirement:REQ-INT-003.3
|
|
1408
|
+
* @scenario: Named profile not found
|
|
1409
|
+
* @given: --profile nonexistent
|
|
1410
|
+
* @when: applyBootstrapProfile() called
|
|
1411
|
+
* @then: Throws ProfileBootstrapError
|
|
1412
|
+
*/
|
|
1413
|
+
it('should throw error when named profile not found', () => {
|
|
1414
|
+
const args = {
|
|
1415
|
+
profileName: 'nonexistent',
|
|
1416
|
+
profileJson: null,
|
|
1417
|
+
providerOverride: null,
|
|
1418
|
+
modelOverride: null,
|
|
1419
|
+
keyOverride: null,
|
|
1420
|
+
keyfileOverride: null,
|
|
1421
|
+
baseurlOverride: null,
|
|
1422
|
+
setOverrides: null,
|
|
1423
|
+
};
|
|
1424
|
+
mockSettings.getProfile.mockReturnValue(null);
|
|
1425
|
+
const parsed = parseArgsWithMeta(args, {
|
|
1426
|
+
settingsService: mockSettings,
|
|
1427
|
+
config: mockConfig,
|
|
1428
|
+
oauthManager: {},
|
|
1429
|
+
});
|
|
1430
|
+
const result = createBootstrapResult(parsed.bootstrapArgs, parsed.runtimeMetadata);
|
|
1431
|
+
// When profile is not found, the result should have empty provider/model and a warning
|
|
1432
|
+
expect(result.providerName).toBe('');
|
|
1433
|
+
expect(result.modelName).toBe('');
|
|
1434
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
1435
|
+
expect(result.warnings[0]).toContain('not found');
|
|
1436
|
+
});
|
|
1437
|
+
/**
|
|
1438
|
+
* @plan:PLAN-20251118-ISSUE533.P09
|
|
1439
|
+
* @requirement:REQ-INT-003.4
|
|
1440
|
+
* @scenario: Empty inline profile object
|
|
1441
|
+
* @given: --profile {}
|
|
1442
|
+
* @when: applyBootstrapProfile() called
|
|
1443
|
+
* @then: Throws ProfileBootstrapError
|
|
1444
|
+
*/
|
|
1445
|
+
it('should throw error for empty inline profile object', () => {
|
|
1446
|
+
const args = {
|
|
1447
|
+
profileName: null,
|
|
1448
|
+
profileJson: '{}',
|
|
1449
|
+
providerOverride: null,
|
|
1450
|
+
modelOverride: null,
|
|
1451
|
+
keyOverride: null,
|
|
1452
|
+
keyfileOverride: null,
|
|
1453
|
+
baseurlOverride: null,
|
|
1454
|
+
setOverrides: null,
|
|
1455
|
+
};
|
|
1456
|
+
expect(() => {
|
|
1457
|
+
parseArgsWithMeta(args, {
|
|
1458
|
+
settingsService: mockSettings,
|
|
1459
|
+
config: mockConfig,
|
|
1460
|
+
oauthManager: {},
|
|
1461
|
+
});
|
|
1462
|
+
}).toThrow();
|
|
1463
|
+
});
|
|
1464
|
+
});
|
|
115
1465
|
//# sourceMappingURL=profileBootstrap.test.js.map
|