@qduc/term2 0.1.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/LICENSE +21 -0
- package/dist/agent.d.ts +19 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +143 -0
- package/dist/agent.js.map +1 -0
- package/dist/app.d.ts +22 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +403 -0
- package/dist/app.js.map +1 -0
- package/dist/app.model-command-feedback.test.d.ts +2 -0
- package/dist/app.model-command-feedback.test.d.ts.map +1 -0
- package/dist/app.model-command-feedback.test.js +19 -0
- package/dist/app.model-command-feedback.test.js.map +1 -0
- package/dist/app.parseInput.test.d.ts +2 -0
- package/dist/app.parseInput.test.d.ts.map +1 -0
- package/dist/app.parseInput.test.js +97 -0
- package/dist/app.parseInput.test.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +241 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/ApprovalPrompt.d.ts +10 -0
- package/dist/components/ApprovalPrompt.d.ts.map +1 -0
- package/dist/components/ApprovalPrompt.js +163 -0
- package/dist/components/ApprovalPrompt.js.map +1 -0
- package/dist/components/Banner.d.ts +9 -0
- package/dist/components/Banner.d.ts.map +1 -0
- package/dist/components/Banner.js +86 -0
- package/dist/components/Banner.js.map +1 -0
- package/dist/components/BottomArea.d.ts +33 -0
- package/dist/components/BottomArea.d.ts.map +1 -0
- package/dist/components/BottomArea.js +31 -0
- package/dist/components/BottomArea.js.map +1 -0
- package/dist/components/BottomArea.test.d.ts +2 -0
- package/dist/components/BottomArea.test.d.ts.map +1 -0
- package/dist/components/BottomArea.test.js +73 -0
- package/dist/components/BottomArea.test.js.map +1 -0
- package/dist/components/ChatMessage.d.ts +7 -0
- package/dist/components/ChatMessage.d.ts.map +1 -0
- package/dist/components/ChatMessage.js +10 -0
- package/dist/components/ChatMessage.js.map +1 -0
- package/dist/components/CommandMessage.d.ts +15 -0
- package/dist/components/CommandMessage.d.ts.map +1 -0
- package/dist/components/CommandMessage.js +188 -0
- package/dist/components/CommandMessage.js.map +1 -0
- package/dist/components/CommandMessage.test.d.ts +2 -0
- package/dist/components/CommandMessage.test.d.ts.map +1 -0
- package/dist/components/CommandMessage.test.js +35 -0
- package/dist/components/CommandMessage.test.js.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +27 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +77 -0
- package/dist/components/ErrorBoundary.js.map +1 -0
- package/dist/components/ErrorBoundary.test.d.ts +2 -0
- package/dist/components/ErrorBoundary.test.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.test.js +32 -0
- package/dist/components/ErrorBoundary.test.js.map +1 -0
- package/dist/components/Input/PopupManager.d.ts +42 -0
- package/dist/components/Input/PopupManager.d.ts.map +1 -0
- package/dist/components/Input/PopupManager.js +13 -0
- package/dist/components/Input/PopupManager.js.map +1 -0
- package/dist/components/InputBox.d.ts +18 -0
- package/dist/components/InputBox.d.ts.map +1 -0
- package/dist/components/InputBox.js +384 -0
- package/dist/components/InputBox.js.map +1 -0
- package/dist/components/InputBox.menu-logic.test.d.ts +2 -0
- package/dist/components/InputBox.menu-logic.test.d.ts.map +1 -0
- package/dist/components/InputBox.menu-logic.test.js +151 -0
- package/dist/components/InputBox.menu-logic.test.js.map +1 -0
- package/dist/components/InputBox.test.d.ts +2 -0
- package/dist/components/InputBox.test.d.ts.map +1 -0
- package/dist/components/InputBox.test.js +91 -0
- package/dist/components/InputBox.test.js.map +1 -0
- package/dist/components/LiveResponse.d.ts +13 -0
- package/dist/components/LiveResponse.d.ts.map +1 -0
- package/dist/components/LiveResponse.js +16 -0
- package/dist/components/LiveResponse.js.map +1 -0
- package/dist/components/MarkdownRenderer.d.ts +8 -0
- package/dist/components/MarkdownRenderer.d.ts.map +1 -0
- package/dist/components/MarkdownRenderer.js +225 -0
- package/dist/components/MarkdownRenderer.js.map +1 -0
- package/dist/components/MentorMode.test.d.ts +2 -0
- package/dist/components/MentorMode.test.d.ts.map +1 -0
- package/dist/components/MentorMode.test.js.map +1 -0
- package/dist/components/MessageList.d.ts +7 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +29 -0
- package/dist/components/MessageList.js.map +1 -0
- package/dist/components/MessageList.test.d.ts +2 -0
- package/dist/components/MessageList.test.d.ts.map +1 -0
- package/dist/components/MessageList.test.js +15 -0
- package/dist/components/MessageList.test.js.map +1 -0
- package/dist/components/ModelSelectionMenu.d.ts +18 -0
- package/dist/components/ModelSelectionMenu.d.ts.map +1 -0
- package/dist/components/ModelSelectionMenu.js +91 -0
- package/dist/components/ModelSelectionMenu.js.map +1 -0
- package/dist/components/ModelSelectionMenu.test.d.ts +2 -0
- package/dist/components/ModelSelectionMenu.test.d.ts.map +1 -0
- package/dist/components/ModelSelectionMenu.test.js +83 -0
- package/dist/components/ModelSelectionMenu.test.js.map +1 -0
- package/dist/components/PathSelectionMenu.d.ts +12 -0
- package/dist/components/PathSelectionMenu.d.ts.map +1 -0
- package/dist/components/PathSelectionMenu.js +42 -0
- package/dist/components/PathSelectionMenu.js.map +1 -0
- package/dist/components/SettingsSelectionMenu.d.ts +9 -0
- package/dist/components/SettingsSelectionMenu.d.ts.map +1 -0
- package/dist/components/SettingsSelectionMenu.js +21 -0
- package/dist/components/SettingsSelectionMenu.js.map +1 -0
- package/dist/components/SlashCommandMenu.d.ts +15 -0
- package/dist/components/SlashCommandMenu.d.ts.map +1 -0
- package/dist/components/SlashCommandMenu.js +20 -0
- package/dist/components/SlashCommandMenu.js.map +1 -0
- package/dist/components/StatusBar.d.ts +11 -0
- package/dist/components/StatusBar.d.ts.map +1 -0
- package/dist/components/StatusBar.js +59 -0
- package/dist/components/StatusBar.js.map +1 -0
- package/dist/components/TextInput.d.ts +42 -0
- package/dist/components/TextInput.d.ts.map +1 -0
- package/dist/components/TextInput.js +397 -0
- package/dist/components/TextInput.js.map +1 -0
- package/dist/components/TextInput.test.d.ts +2 -0
- package/dist/components/TextInput.test.d.ts.map +1 -0
- package/dist/components/TextInput.test.js +75 -0
- package/dist/components/TextInput.test.js.map +1 -0
- package/dist/context/InputContext.d.ts +31 -0
- package/dist/context/InputContext.d.ts.map +1 -0
- package/dist/context/InputContext.js +36 -0
- package/dist/context/InputContext.js.map +1 -0
- package/dist/context/InputContext.stability.test.d.ts +2 -0
- package/dist/context/InputContext.stability.test.d.ts.map +1 -0
- package/dist/context/InputContext.stability.test.js +28 -0
- package/dist/context/InputContext.stability.test.js.map +1 -0
- package/dist/context/InputContext.test.d.ts +2 -0
- package/dist/context/InputContext.test.d.ts.map +1 -0
- package/dist/context/InputContext.test.js +168 -0
- package/dist/context/InputContext.test.js.map +1 -0
- package/dist/debug-schema.d.ts +2 -0
- package/dist/debug-schema.d.ts.map +1 -0
- package/dist/debug-schema.js +22 -0
- package/dist/debug-schema.js.map +1 -0
- package/dist/hooks/use-conversation.d.ts +78 -0
- package/dist/hooks/use-conversation.d.ts.map +1 -0
- package/dist/hooks/use-conversation.js +1017 -0
- package/dist/hooks/use-conversation.js.map +1 -0
- package/dist/hooks/use-input-history.d.ts +16 -0
- package/dist/hooks/use-input-history.d.ts.map +1 -0
- package/dist/hooks/use-input-history.js +71 -0
- package/dist/hooks/use-input-history.js.map +1 -0
- package/dist/hooks/use-model-selection.d.ts +27 -0
- package/dist/hooks/use-model-selection.d.ts.map +1 -0
- package/dist/hooks/use-model-selection.js +187 -0
- package/dist/hooks/use-model-selection.js.map +1 -0
- package/dist/hooks/use-model-selection.test.d.ts +2 -0
- package/dist/hooks/use-model-selection.test.d.ts.map +1 -0
- package/dist/hooks/use-model-selection.test.js +28 -0
- package/dist/hooks/use-model-selection.test.js.map +1 -0
- package/dist/hooks/use-path-completion.d.ts +22 -0
- package/dist/hooks/use-path-completion.d.ts.map +1 -0
- package/dist/hooks/use-path-completion.js +153 -0
- package/dist/hooks/use-path-completion.js.map +1 -0
- package/dist/hooks/use-path-completion.test.d.ts +2 -0
- package/dist/hooks/use-path-completion.test.d.ts.map +1 -0
- package/dist/hooks/use-path-completion.test.js +29 -0
- package/dist/hooks/use-path-completion.test.js.map +1 -0
- package/dist/hooks/use-setting.d.ts +7 -0
- package/dist/hooks/use-setting.d.ts.map +1 -0
- package/dist/hooks/use-setting.js +35 -0
- package/dist/hooks/use-setting.js.map +1 -0
- package/dist/hooks/use-settings-completion.d.ts +23 -0
- package/dist/hooks/use-settings-completion.d.ts.map +1 -0
- package/dist/hooks/use-settings-completion.js +164 -0
- package/dist/hooks/use-settings-completion.js.map +1 -0
- package/dist/hooks/use-settings-completion.test.d.ts +2 -0
- package/dist/hooks/use-settings-completion.test.d.ts.map +1 -0
- package/dist/hooks/use-settings-completion.test.js +334 -0
- package/dist/hooks/use-settings-completion.test.js.map +1 -0
- package/dist/hooks/use-slash-commands.d.ts +21 -0
- package/dist/hooks/use-slash-commands.d.ts.map +1 -0
- package/dist/hooks/use-slash-commands.js +87 -0
- package/dist/hooks/use-slash-commands.js.map +1 -0
- package/dist/hooks/use-slash-commands.test.d.ts +2 -0
- package/dist/hooks/use-slash-commands.test.d.ts.map +1 -0
- package/dist/hooks/use-slash-commands.test.js +246 -0
- package/dist/hooks/use-slash-commands.test.js.map +1 -0
- package/dist/lib/editor-impl.d.ts +23 -0
- package/dist/lib/editor-impl.d.ts.map +1 -0
- package/dist/lib/editor-impl.js +235 -0
- package/dist/lib/editor-impl.js.map +1 -0
- package/dist/lib/openai-agent-client.chat.test.d.ts +2 -0
- package/dist/lib/openai-agent-client.chat.test.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.chat.test.js +68 -0
- package/dist/lib/openai-agent-client.chat.test.js.map +1 -0
- package/dist/lib/openai-agent-client.d.ts +48 -0
- package/dist/lib/openai-agent-client.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.js +653 -0
- package/dist/lib/openai-agent-client.js.map +1 -0
- package/dist/lib/openai-agent-client.test.d.ts +2 -0
- package/dist/lib/openai-agent-client.test.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.test.js +181 -0
- package/dist/lib/openai-agent-client.test.js.map +1 -0
- package/dist/lib/shell.d.ts +7 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +56 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/tool-invoke.d.ts +4 -0
- package/dist/lib/tool-invoke.d.ts.map +1 -0
- package/dist/lib/tool-invoke.js +26 -0
- package/dist/lib/tool-invoke.js.map +1 -0
- package/dist/lib/tool-invoke.test.d.ts +2 -0
- package/dist/lib/tool-invoke.test.d.ts.map +1 -0
- package/dist/lib/tool-invoke.test.js +19 -0
- package/dist/lib/tool-invoke.test.js.map +1 -0
- package/dist/no-singleton-imports.test.d.ts +2 -0
- package/dist/no-singleton-imports.test.d.ts.map +1 -0
- package/dist/no-singleton-imports.test.js +30 -0
- package/dist/no-singleton-imports.test.js.map +1 -0
- package/dist/prompts/anthropic.md +79 -0
- package/dist/prompts/codex.md +97 -0
- package/dist/prompts/default.md +77 -0
- package/dist/prompts/default.md.bak +77 -0
- package/dist/prompts/gpt-5.md +318 -0
- package/dist/prompts/lite.md +29 -0
- package/dist/prompts/simple-mentor.md +207 -0
- package/dist/prompts/simple.md +189 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compatible/api.d.ts +17 -0
- package/dist/providers/openai-compatible/api.d.ts.map +1 -0
- package/dist/providers/openai-compatible/api.js +58 -0
- package/dist/providers/openai-compatible/api.js.map +1 -0
- package/dist/providers/openai-compatible/model.d.ts +17 -0
- package/dist/providers/openai-compatible/model.d.ts.map +1 -0
- package/dist/providers/openai-compatible/model.js +435 -0
- package/dist/providers/openai-compatible/model.js.map +1 -0
- package/dist/providers/openai-compatible/provider.d.ts +22 -0
- package/dist/providers/openai-compatible/provider.d.ts.map +1 -0
- package/dist/providers/openai-compatible/provider.js +43 -0
- package/dist/providers/openai-compatible/provider.js.map +1 -0
- package/dist/providers/openai-compatible/utils.d.ts +3 -0
- package/dist/providers/openai-compatible/utils.d.ts.map +1 -0
- package/dist/providers/openai-compatible/utils.js +11 -0
- package/dist/providers/openai-compatible/utils.js.map +1 -0
- package/dist/providers/openai-compatible.provider.d.ts +8 -0
- package/dist/providers/openai-compatible.provider.d.ts.map +1 -0
- package/dist/providers/openai-compatible.provider.js +71 -0
- package/dist/providers/openai-compatible.provider.js.map +1 -0
- package/dist/providers/openai.provider.d.ts +2 -0
- package/dist/providers/openai.provider.d.ts.map +1 -0
- package/dist/providers/openai.provider.js +36 -0
- package/dist/providers/openai.provider.js.map +1 -0
- package/dist/providers/openrouter/api.d.ts +39 -0
- package/dist/providers/openrouter/api.d.ts.map +1 -0
- package/dist/providers/openrouter/api.js +172 -0
- package/dist/providers/openrouter/api.js.map +1 -0
- package/dist/providers/openrouter/converters.d.ts +8 -0
- package/dist/providers/openrouter/converters.d.ts.map +1 -0
- package/dist/providers/openrouter/converters.js +382 -0
- package/dist/providers/openrouter/converters.js.map +1 -0
- package/dist/providers/openrouter/converters.test.d.ts +2 -0
- package/dist/providers/openrouter/converters.test.d.ts.map +1 -0
- package/dist/providers/openrouter/converters.test.js +158 -0
- package/dist/providers/openrouter/converters.test.js.map +1 -0
- package/dist/providers/openrouter/index.d.ts +4 -0
- package/dist/providers/openrouter/index.d.ts.map +1 -0
- package/dist/providers/openrouter/index.js +4 -0
- package/dist/providers/openrouter/index.js.map +1 -0
- package/dist/providers/openrouter/model.d.ts +14 -0
- package/dist/providers/openrouter/model.d.ts.map +1 -0
- package/dist/providers/openrouter/model.js +485 -0
- package/dist/providers/openrouter/model.js.map +1 -0
- package/dist/providers/openrouter/provider.d.ts +15 -0
- package/dist/providers/openrouter/provider.d.ts.map +1 -0
- package/dist/providers/openrouter/provider.js +21 -0
- package/dist/providers/openrouter/provider.js.map +1 -0
- package/dist/providers/openrouter/utils.d.ts +10 -0
- package/dist/providers/openrouter/utils.d.ts.map +1 -0
- package/dist/providers/openrouter/utils.js +27 -0
- package/dist/providers/openrouter/utils.js.map +1 -0
- package/dist/providers/openrouter.api.retry.test.d.ts +2 -0
- package/dist/providers/openrouter.api.retry.test.d.ts.map +1 -0
- package/dist/providers/openrouter.api.retry.test.js +148 -0
- package/dist/providers/openrouter.api.retry.test.js.map +1 -0
- package/dist/providers/openrouter.d.ts +2 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.history.test.d.ts +2 -0
- package/dist/providers/openrouter.history.test.d.ts.map +1 -0
- package/dist/providers/openrouter.history.test.js +533 -0
- package/dist/providers/openrouter.history.test.js.map +1 -0
- package/dist/providers/openrouter.js +4 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/openrouter.provider.createRunner.test.d.ts +2 -0
- package/dist/providers/openrouter.provider.createRunner.test.d.ts.map +1 -0
- package/dist/providers/openrouter.provider.createRunner.test.js +23 -0
- package/dist/providers/openrouter.provider.createRunner.test.js.map +1 -0
- package/dist/providers/openrouter.provider.d.ts +2 -0
- package/dist/providers/openrouter.provider.d.ts.map +1 -0
- package/dist/providers/openrouter.provider.js +56 -0
- package/dist/providers/openrouter.provider.js.map +1 -0
- package/dist/providers/openrouter.test.d.ts +2 -0
- package/dist/providers/openrouter.test.d.ts.map +1 -0
- package/dist/providers/openrouter.test.js +1382 -0
- package/dist/providers/openrouter.test.js.map +1 -0
- package/dist/providers/registry.d.ts +65 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +44 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/registry.test.d.ts +2 -0
- package/dist/providers/registry.test.d.ts.map +1 -0
- package/dist/providers/registry.test.js +76 -0
- package/dist/providers/registry.test.js.map +1 -0
- package/dist/providers/web-search/index.d.ts +8 -0
- package/dist/providers/web-search/index.d.ts.map +1 -0
- package/dist/providers/web-search/index.js +9 -0
- package/dist/providers/web-search/index.js.map +1 -0
- package/dist/providers/web-search/registry.d.ts +35 -0
- package/dist/providers/web-search/registry.d.ts.map +1 -0
- package/dist/providers/web-search/registry.js +56 -0
- package/dist/providers/web-search/registry.js.map +1 -0
- package/dist/providers/web-search/registry.test.d.ts +2 -0
- package/dist/providers/web-search/registry.test.d.ts.map +1 -0
- package/dist/providers/web-search/registry.test.js +105 -0
- package/dist/providers/web-search/registry.test.js.map +1 -0
- package/dist/providers/web-search/tavily.provider.d.ts +15 -0
- package/dist/providers/web-search/tavily.provider.d.ts.map +1 -0
- package/dist/providers/web-search/tavily.provider.js +69 -0
- package/dist/providers/web-search/tavily.provider.js.map +1 -0
- package/dist/providers/web-search/tavily.provider.test.d.ts +2 -0
- package/dist/providers/web-search/tavily.provider.test.d.ts.map +1 -0
- package/dist/providers/web-search/tavily.provider.test.js +67 -0
- package/dist/providers/web-search/tavily.provider.test.js.map +1 -0
- package/dist/providers/web-search/types.d.ts +55 -0
- package/dist/providers/web-search/types.d.ts.map +1 -0
- package/dist/providers/web-search/types.js +6 -0
- package/dist/providers/web-search/types.js.map +1 -0
- package/dist/safety-checker.js +57 -0
- package/dist/services/conversation-events.d.ts +76 -0
- package/dist/services/conversation-events.d.ts.map +1 -0
- package/dist/services/conversation-events.js +2 -0
- package/dist/services/conversation-events.js.map +1 -0
- package/dist/services/conversation-service.d.ts +31 -0
- package/dist/services/conversation-service.d.ts.map +1 -0
- package/dist/services/conversation-service.js +46 -0
- package/dist/services/conversation-service.js.map +1 -0
- package/dist/services/conversation-service.test.js +190 -0
- package/dist/services/conversation-session.d.ts +99 -0
- package/dist/services/conversation-session.d.ts.map +1 -0
- package/dist/services/conversation-session.js +978 -0
- package/dist/services/conversation-session.js.map +1 -0
- package/dist/services/conversation-store.d.ts +24 -0
- package/dist/services/conversation-store.d.ts.map +1 -0
- package/dist/services/conversation-store.js +216 -0
- package/dist/services/conversation-store.js.map +1 -0
- package/dist/services/conversation-store.test.d.ts +2 -0
- package/dist/services/conversation-store.test.d.ts.map +1 -0
- package/dist/services/conversation-store.test.js +167 -0
- package/dist/services/conversation-store.test.js.map +1 -0
- package/dist/services/execution-context.d.ts +10 -0
- package/dist/services/execution-context.d.ts.map +1 -0
- package/dist/services/execution-context.js +22 -0
- package/dist/services/execution-context.js.map +1 -0
- package/dist/services/execution-context.test.d.ts +2 -0
- package/dist/services/execution-context.test.d.ts.map +1 -0
- package/dist/services/execution-context.test.js +49 -0
- package/dist/services/execution-context.test.js.map +1 -0
- package/dist/services/file-service.d.ts +12 -0
- package/dist/services/file-service.d.ts.map +1 -0
- package/dist/services/file-service.js +90 -0
- package/dist/services/file-service.js.map +1 -0
- package/dist/services/history-service.d.ts +39 -0
- package/dist/services/history-service.d.ts.map +1 -0
- package/dist/services/history-service.js +152 -0
- package/dist/services/history-service.js.map +1 -0
- package/dist/services/logging-service.d.ts +75 -0
- package/dist/services/logging-service.d.ts.map +1 -0
- package/dist/services/logging-service.js +343 -0
- package/dist/services/logging-service.js.map +1 -0
- package/dist/services/model-service.d.ts +15 -0
- package/dist/services/model-service.d.ts.map +1 -0
- package/dist/services/model-service.js +46 -0
- package/dist/services/model-service.js.map +1 -0
- package/dist/services/model-service.test.d.ts +2 -0
- package/dist/services/model-service.test.d.ts.map +1 -0
- package/dist/services/model-service.test.js +128 -0
- package/dist/services/model-service.test.js.map +1 -0
- package/dist/services/service-interfaces.d.ts +33 -0
- package/dist/services/service-interfaces.d.ts.map +1 -0
- package/dist/services/service-interfaces.js +2 -0
- package/dist/services/service-interfaces.js.map +1 -0
- package/dist/services/settings-service.d.ts +316 -0
- package/dist/services/settings-service.d.ts.map +1 -0
- package/dist/services/settings-service.js +1128 -0
- package/dist/services/settings-service.js.map +1 -0
- package/dist/services/settings-service.mock.d.ts +20 -0
- package/dist/services/settings-service.mock.d.ts.map +1 -0
- package/dist/services/settings-service.mock.js +55 -0
- package/dist/services/settings-service.mock.js.map +1 -0
- package/dist/services/singleton-deprecation.test.d.ts +2 -0
- package/dist/services/singleton-deprecation.test.d.ts.map +1 -0
- package/dist/services/singleton-deprecation.test.js +59 -0
- package/dist/services/singleton-deprecation.test.js.map +1 -0
- package/dist/services/ssh-service.d.ts +32 -0
- package/dist/services/ssh-service.d.ts.map +1 -0
- package/dist/services/ssh-service.js +119 -0
- package/dist/services/ssh-service.js.map +1 -0
- package/dist/services/ssh-service.test.d.ts +2 -0
- package/dist/services/ssh-service.test.d.ts.map +1 -0
- package/dist/services/ssh-service.test.js +269 -0
- package/dist/services/ssh-service.test.js.map +1 -0
- package/dist/test-search-tool.d.ts +2 -0
- package/dist/test-search-tool.d.ts.map +1 -0
- package/dist/test-search-tool.js +36 -0
- package/dist/test-search-tool.js.map +1 -0
- package/dist/tools/apply-patch.d.ts +28 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +399 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/apply-patch.test.d.ts +2 -0
- package/dist/tools/apply-patch.test.d.ts.map +1 -0
- package/dist/tools/apply-patch.test.js +155 -0
- package/dist/tools/apply-patch.test.js.map +1 -0
- package/dist/tools/ask-mentor.d.ts +11 -0
- package/dist/tools/ask-mentor.d.ts.map +1 -0
- package/dist/tools/ask-mentor.js +52 -0
- package/dist/tools/ask-mentor.js.map +1 -0
- package/dist/tools/ask-mentor.test.d.ts +2 -0
- package/dist/tools/ask-mentor.test.d.ts.map +1 -0
- package/dist/tools/ask-mentor.test.js +47 -0
- package/dist/tools/ask-mentor.test.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +55 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/find-files.d.ts +15 -0
- package/dist/tools/find-files.d.ts.map +1 -0
- package/dist/tools/find-files.js +179 -0
- package/dist/tools/find-files.js.map +1 -0
- package/dist/tools/find-files.test.d.ts +2 -0
- package/dist/tools/find-files.test.d.ts.map +1 -0
- package/dist/tools/find-files.test.js +131 -0
- package/dist/tools/find-files.test.js.map +1 -0
- package/dist/tools/format-helpers.d.ts +34 -0
- package/dist/tools/format-helpers.d.ts.map +1 -0
- package/dist/tools/format-helpers.js +131 -0
- package/dist/tools/format-helpers.js.map +1 -0
- package/dist/tools/grep.d.ts +16 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +211 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/read-file.d.ts +15 -0
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +114 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/read-file.test.d.ts +2 -0
- package/dist/tools/read-file.test.d.ts.map +1 -0
- package/dist/tools/read-file.test.js +122 -0
- package/dist/tools/read-file.test.js.map +1 -0
- package/dist/tools/search-replace.d.ts +19 -0
- package/dist/tools/search-replace.d.ts.map +1 -0
- package/dist/tools/search-replace.js +411 -0
- package/dist/tools/search-replace.js.map +1 -0
- package/dist/tools/search-replace.test.d.ts +2 -0
- package/dist/tools/search-replace.test.d.ts.map +1 -0
- package/dist/tools/search-replace.test.js +302 -0
- package/dist/tools/search-replace.test.js.map +1 -0
- package/dist/tools/search.d.ts +15 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +143 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/shell.d.ts +19 -0
- package/dist/tools/shell.d.ts.map +1 -0
- package/dist/tools/shell.js +278 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/tools/tool-execution-context.d.ts +7 -0
- package/dist/tools/tool-execution-context.d.ts.map +1 -0
- package/dist/tools/tool-execution-context.js +7 -0
- package/dist/tools/tool-execution-context.js.map +1 -0
- package/dist/tools/types.d.ts +30 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/utils.d.ts +12 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +19 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/web-search.d.ts +29 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +106 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/web-search.test.d.ts +2 -0
- package/dist/tools/web-search.test.d.ts.map +1 -0
- package/dist/tools/web-search.test.js +176 -0
- package/dist/tools/web-search.test.js.map +1 -0
- package/dist/utils/command-logger.d.ts +11 -0
- package/dist/utils/command-logger.d.ts.map +1 -0
- package/dist/utils/command-logger.js +34 -0
- package/dist/utils/command-logger.js.map +1 -0
- package/dist/utils/command-safety/constants.d.ts +21 -0
- package/dist/utils/command-safety/constants.d.ts.map +1 -0
- package/dist/utils/command-safety/constants.js +245 -0
- package/dist/utils/command-safety/constants.js.map +1 -0
- package/dist/utils/command-safety/find-helpers.d.ts +15 -0
- package/dist/utils/command-safety/find-helpers.d.ts.map +1 -0
- package/dist/utils/command-safety/find-helpers.js +218 -0
- package/dist/utils/command-safety/find-helpers.js.map +1 -0
- package/dist/utils/command-safety/handlers/find-handler.d.ts +6 -0
- package/dist/utils/command-safety/handlers/find-handler.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/find-handler.js +113 -0
- package/dist/utils/command-safety/handlers/find-handler.js.map +1 -0
- package/dist/utils/command-safety/handlers/git-handler.d.ts +6 -0
- package/dist/utils/command-safety/handlers/git-handler.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/git-handler.js +68 -0
- package/dist/utils/command-safety/handlers/git-handler.js.map +1 -0
- package/dist/utils/command-safety/handlers/index.d.ts +13 -0
- package/dist/utils/command-safety/handlers/index.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/index.js +20 -0
- package/dist/utils/command-safety/handlers/index.js.map +1 -0
- package/dist/utils/command-safety/handlers/sed-handler.d.ts +6 -0
- package/dist/utils/command-safety/handlers/sed-handler.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/sed-handler.js +94 -0
- package/dist/utils/command-safety/handlers/sed-handler.js.map +1 -0
- package/dist/utils/command-safety/handlers/types.d.ts +36 -0
- package/dist/utils/command-safety/handlers/types.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/types.js +2 -0
- package/dist/utils/command-safety/handlers/types.js.map +1 -0
- package/dist/utils/command-safety/index.d.ts +14 -0
- package/dist/utils/command-safety/index.d.ts.map +1 -0
- package/dist/utils/command-safety/index.js +183 -0
- package/dist/utils/command-safety/index.js.map +1 -0
- package/dist/utils/command-safety/path-analysis.d.ts +4 -0
- package/dist/utils/command-safety/path-analysis.d.ts.map +1 -0
- package/dist/utils/command-safety/path-analysis.js +153 -0
- package/dist/utils/command-safety/path-analysis.js.map +1 -0
- package/dist/utils/command-safety/utils.d.ts +2 -0
- package/dist/utils/command-safety/utils.d.ts.map +1 -0
- package/dist/utils/command-safety/utils.js +22 -0
- package/dist/utils/command-safety/utils.js.map +1 -0
- package/dist/utils/command-safety.d.ts +21 -0
- package/dist/utils/command-safety.d.ts.map +1 -0
- package/dist/utils/command-safety.find.test.d.ts +2 -0
- package/dist/utils/command-safety.find.test.d.ts.map +1 -0
- package/dist/utils/command-safety.find.test.js +342 -0
- package/dist/utils/command-safety.find.test.js.map +1 -0
- package/dist/utils/command-safety.js +702 -0
- package/dist/utils/command-safety.js.map +1 -0
- package/dist/utils/command-safety.path.test.d.ts +2 -0
- package/dist/utils/command-safety.path.test.d.ts.map +1 -0
- package/dist/utils/command-safety.path.test.js +360 -0
- package/dist/utils/command-safety.path.test.js.map +1 -0
- package/dist/utils/diff.d.ts +2 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +44 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/diff.test.d.ts +2 -0
- package/dist/utils/diff.test.d.ts.map +1 -0
- package/dist/utils/diff.test.js +85 -0
- package/dist/utils/diff.test.js.map +1 -0
- package/dist/utils/error-helpers.d.ts +6 -0
- package/dist/utils/error-helpers.d.ts.map +1 -0
- package/dist/utils/error-helpers.js +46 -0
- package/dist/utils/error-helpers.js.map +1 -0
- package/dist/utils/error-helpers.test.d.ts +2 -0
- package/dist/utils/error-helpers.test.d.ts.map +1 -0
- package/dist/utils/error-helpers.test.js +152 -0
- package/dist/utils/error-helpers.test.js.map +1 -0
- package/dist/utils/execute-shell.d.ts +15 -0
- package/dist/utils/execute-shell.d.ts.map +1 -0
- package/dist/utils/execute-shell.js +34 -0
- package/dist/utils/execute-shell.js.map +1 -0
- package/dist/utils/execute-shell.test.d.ts +2 -0
- package/dist/utils/execute-shell.test.d.ts.map +1 -0
- package/dist/utils/execute-shell.test.js +20 -0
- package/dist/utils/execute-shell.test.js.map +1 -0
- package/dist/utils/extract-command-messages.d.ts +5 -0
- package/dist/utils/extract-command-messages.d.ts.map +1 -0
- package/dist/utils/extract-command-messages.js +140 -0
- package/dist/utils/extract-command-messages.js.map +1 -0
- package/dist/utils/extract-command-messages.repro.test.d.ts +2 -0
- package/dist/utils/extract-command-messages.repro.test.d.ts.map +1 -0
- package/dist/utils/extract-command-messages.repro.test.js +31 -0
- package/dist/utils/extract-command-messages.repro.test.js.map +1 -0
- package/dist/utils/extract-command-messages.test.js +57 -0
- package/dist/utils/message-buffer.d.ts +2 -0
- package/dist/utils/message-buffer.d.ts.map +1 -0
- package/dist/utils/message-buffer.js +15 -0
- package/dist/utils/message-buffer.js.map +1 -0
- package/dist/utils/message-buffer.test.d.ts +2 -0
- package/dist/utils/message-buffer.test.d.ts.map +1 -0
- package/dist/utils/message-buffer.test.js +17 -0
- package/dist/utils/message-buffer.test.js.map +1 -0
- package/dist/utils/output-trim.d.ts +31 -0
- package/dist/utils/output-trim.d.ts.map +1 -0
- package/dist/utils/output-trim.js +71 -0
- package/dist/utils/output-trim.js.map +1 -0
- package/dist/utils/provider-credentials.d.ts +10 -0
- package/dist/utils/provider-credentials.d.ts.map +1 -0
- package/dist/utils/provider-credentials.js +22 -0
- package/dist/utils/provider-credentials.js.map +1 -0
- package/dist/utils/settings-command.d.ts +13 -0
- package/dist/utils/settings-command.d.ts.map +1 -0
- package/dist/utils/settings-command.js +173 -0
- package/dist/utils/settings-command.js.map +1 -0
- package/dist/utils/ssh-config-parser.d.ts +21 -0
- package/dist/utils/ssh-config-parser.d.ts.map +1 -0
- package/dist/utils/ssh-config-parser.js +89 -0
- package/dist/utils/ssh-config-parser.js.map +1 -0
- package/dist/utils/ssh-config-parser.test.d.ts +2 -0
- package/dist/utils/ssh-config-parser.test.d.ts.map +1 -0
- package/dist/utils/ssh-config-parser.test.js +153 -0
- package/dist/utils/ssh-config-parser.test.js.map +1 -0
- package/dist/utils/streaming-updater.d.ts +7 -0
- package/dist/utils/streaming-updater.d.ts.map +1 -0
- package/dist/utils/streaming-updater.js +41 -0
- package/dist/utils/streaming-updater.js.map +1 -0
- package/dist/utils/throttle.d.ts +7 -0
- package/dist/utils/throttle.d.ts.map +1 -0
- package/dist/utils/throttle.js +49 -0
- package/dist/utils/throttle.js.map +1 -0
- package/package.json +108 -0
- package/readme.md +428 -0
|
@@ -0,0 +1,1128 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import envPaths from 'env-paths';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import deepEqual from 'fast-deep-equal';
|
|
6
|
+
import { LoggingService } from './logging-service.js';
|
|
7
|
+
// Import providers to ensure they're registered before schema construction
|
|
8
|
+
import '../providers/index.js';
|
|
9
|
+
import { getAllProviders, getProvider, upsertProvider, } from '../providers/index.js';
|
|
10
|
+
import { getAllWebSearchProviders } from '../providers/web-search/index.js';
|
|
11
|
+
import { createOpenAICompatibleProviderDefinition } from '../providers/openai-compatible.provider.js';
|
|
12
|
+
const paths = envPaths('term2');
|
|
13
|
+
// Define schemas for validation
|
|
14
|
+
const AgentSettingsSchema = z.object({
|
|
15
|
+
model: z.string().min(1).default('gpt-5.1'),
|
|
16
|
+
// 'default' signals we should *not* explicitly pass a reasoningEffort
|
|
17
|
+
// to the API, allowing it to decide what to use.
|
|
18
|
+
reasoningEffort: z
|
|
19
|
+
.enum(['default', 'none', 'minimal', 'low', 'medium', 'high'])
|
|
20
|
+
.default('default'),
|
|
21
|
+
// Temperature controls randomness. We keep it optional so providers/models
|
|
22
|
+
// can use their own defaults when unset.
|
|
23
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
24
|
+
maxTurns: z.number().int().positive().default(100),
|
|
25
|
+
retryAttempts: z.number().int().nonnegative().default(2),
|
|
26
|
+
// NOTE: We do NOT validate provider existence here because the provider
|
|
27
|
+
// registry can be extended at runtime from settings.json (custom providers).
|
|
28
|
+
// We validate/fallback after SettingsService loads and registers runtime providers.
|
|
29
|
+
provider: z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1)
|
|
32
|
+
.default('openai')
|
|
33
|
+
.describe('Provider to use for the agent'),
|
|
34
|
+
openrouter: z
|
|
35
|
+
.object({
|
|
36
|
+
apiKey: z.string().optional(),
|
|
37
|
+
baseUrl: z.string().url().optional(),
|
|
38
|
+
referrer: z.string().optional(),
|
|
39
|
+
title: z.string().optional(),
|
|
40
|
+
})
|
|
41
|
+
.optional(),
|
|
42
|
+
mentorModel: z.string().optional().describe('Model to use as a mentor'),
|
|
43
|
+
mentorReasoningEffort: z
|
|
44
|
+
.enum(['default', 'none', 'minimal', 'low', 'medium', 'high'])
|
|
45
|
+
.default('default')
|
|
46
|
+
.describe('Reasoning effort for the mentor model'),
|
|
47
|
+
});
|
|
48
|
+
const ShellSettingsSchema = z.object({
|
|
49
|
+
timeout: z.number().int().positive().default(120000),
|
|
50
|
+
maxOutputLines: z.number().int().positive().default(1000),
|
|
51
|
+
maxOutputChars: z.number().int().positive().default(10000),
|
|
52
|
+
});
|
|
53
|
+
const UISettingsSchema = z.object({
|
|
54
|
+
historySize: z.number().int().positive().default(1000),
|
|
55
|
+
});
|
|
56
|
+
const LoggingSettingsSchema = z.object({
|
|
57
|
+
logLevel: z
|
|
58
|
+
.enum(['error', 'warn', 'info', 'security', 'debug'])
|
|
59
|
+
.default('info'),
|
|
60
|
+
disableLogging: z.boolean().optional().default(false),
|
|
61
|
+
debugLogging: z.boolean().optional().default(false),
|
|
62
|
+
suppressConsoleOutput: z.boolean().optional().default(true),
|
|
63
|
+
});
|
|
64
|
+
const EnvironmentSettingsSchema = z.object({
|
|
65
|
+
nodeEnv: z.string().optional(),
|
|
66
|
+
});
|
|
67
|
+
const AppSettingsSchema = z.object({
|
|
68
|
+
shellPath: z.string().optional(),
|
|
69
|
+
// Independent mode flags that can be enabled together and persist across sessions
|
|
70
|
+
// mentorMode: uses simplified mentor prompt and enables ask_mentor tool (if mentorModel configured)
|
|
71
|
+
// editMode: auto-approves apply_patch operations within cwd for faster file editing
|
|
72
|
+
// liteMode: minimal context for general terminal assistance (no codebase tools/prompts)
|
|
73
|
+
mentorMode: z.boolean().optional().default(false),
|
|
74
|
+
editMode: z.boolean().optional().default(false),
|
|
75
|
+
liteMode: z.boolean().optional().default(false),
|
|
76
|
+
});
|
|
77
|
+
const ToolsSettingsSchema = z.object({
|
|
78
|
+
logFileOperations: z.boolean().optional().default(true),
|
|
79
|
+
});
|
|
80
|
+
const DebugSettingsSchema = z.object({
|
|
81
|
+
debugBashTool: z.boolean().optional().default(false),
|
|
82
|
+
});
|
|
83
|
+
const SSHSettingsSchema = z.object({
|
|
84
|
+
enabled: z.boolean().default(false),
|
|
85
|
+
host: z.string().optional(),
|
|
86
|
+
port: z.number().int().positive().default(22),
|
|
87
|
+
username: z.string().optional(),
|
|
88
|
+
remoteDir: z.string().optional(),
|
|
89
|
+
});
|
|
90
|
+
const WebSearchSettingsSchema = z.object({
|
|
91
|
+
provider: z.string().optional(),
|
|
92
|
+
tavily: z.object({
|
|
93
|
+
apiKey: z.string().optional(),
|
|
94
|
+
}).optional(),
|
|
95
|
+
});
|
|
96
|
+
const CustomProviderSchema = z.object({
|
|
97
|
+
name: z.string().min(1),
|
|
98
|
+
baseUrl: z.string().url(),
|
|
99
|
+
apiKey: z.string().optional(),
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* Settings that are sensitive and should NEVER be saved to disk.
|
|
103
|
+
* These are only loaded from environment variables.
|
|
104
|
+
*/
|
|
105
|
+
function getSensitiveSettingKeys() {
|
|
106
|
+
const keys = new Set(['app.shellPath']);
|
|
107
|
+
// Add provider-specific sensitive keys
|
|
108
|
+
for (const provider of getAllProviders()) {
|
|
109
|
+
if (provider.sensitiveSettingKeys) {
|
|
110
|
+
for (const key of provider.sensitiveSettingKeys) {
|
|
111
|
+
keys.add(key);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Add web search provider-specific sensitive keys
|
|
116
|
+
for (const provider of getAllWebSearchProviders()) {
|
|
117
|
+
if (provider.sensitiveSettingKeys) {
|
|
118
|
+
for (const key of provider.sensitiveSettingKeys) {
|
|
119
|
+
keys.add(key);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return keys;
|
|
124
|
+
}
|
|
125
|
+
const SENSITIVE_SETTING_KEYS = getSensitiveSettingKeys();
|
|
126
|
+
const SettingsSchema = z.object({
|
|
127
|
+
providers: z.array(CustomProviderSchema).optional().default([]),
|
|
128
|
+
agent: AgentSettingsSchema.optional(),
|
|
129
|
+
shell: ShellSettingsSchema.optional(),
|
|
130
|
+
ui: UISettingsSchema.optional(),
|
|
131
|
+
logging: LoggingSettingsSchema.optional(),
|
|
132
|
+
environment: EnvironmentSettingsSchema.optional(),
|
|
133
|
+
app: AppSettingsSchema.optional(),
|
|
134
|
+
tools: ToolsSettingsSchema.optional(),
|
|
135
|
+
debug: DebugSettingsSchema.optional(),
|
|
136
|
+
ssh: SSHSettingsSchema.optional(),
|
|
137
|
+
webSearch: WebSearchSettingsSchema.optional(),
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* Centralized list of all setting keys for consistency across the app.
|
|
141
|
+
* Used by settings command UI and other components to avoid duplication.
|
|
142
|
+
*/
|
|
143
|
+
export const SETTING_KEYS = {
|
|
144
|
+
AGENT_MODEL: 'agent.model',
|
|
145
|
+
AGENT_REASONING_EFFORT: 'agent.reasoningEffort',
|
|
146
|
+
AGENT_TEMPERATURE: 'agent.temperature',
|
|
147
|
+
AGENT_PROVIDER: 'agent.provider',
|
|
148
|
+
AGENT_MAX_TURNS: 'agent.maxTurns',
|
|
149
|
+
AGENT_RETRY_ATTEMPTS: 'agent.retryAttempts',
|
|
150
|
+
AGENT_OPENROUTER_API_KEY: 'agent.openrouter.apiKey', // Sensitive - env only
|
|
151
|
+
AGENT_OPENROUTER_BASE_URL: 'agent.openrouter.baseUrl', // Sensitive - env only
|
|
152
|
+
AGENT_OPENROUTER_REFERRER: 'agent.openrouter.referrer', // Sensitive - env only
|
|
153
|
+
AGENT_OPENROUTER_TITLE: 'agent.openrouter.title', // Sensitive - env only
|
|
154
|
+
AGENT_MENTOR_MODEL: 'agent.mentorModel',
|
|
155
|
+
AGENT_MENTOR_REASONING_EFFORT: 'agent.mentorReasoningEffort',
|
|
156
|
+
SHELL_TIMEOUT: 'shell.timeout',
|
|
157
|
+
SHELL_MAX_OUTPUT_LINES: 'shell.maxOutputLines',
|
|
158
|
+
SHELL_MAX_OUTPUT_CHARS: 'shell.maxOutputChars',
|
|
159
|
+
UI_HISTORY_SIZE: 'ui.historySize',
|
|
160
|
+
LOGGING_LOG_LEVEL: 'logging.logLevel',
|
|
161
|
+
LOGGING_DISABLE: 'logging.disableLogging',
|
|
162
|
+
LOGGING_DEBUG: 'logging.debugLogging',
|
|
163
|
+
LOGGING_SUPPRESS_CONSOLE: 'logging.suppressConsoleOutput',
|
|
164
|
+
ENV_NODE_ENV: 'environment.nodeEnv',
|
|
165
|
+
APP_SHELL_PATH: 'app.shellPath', // Sensitive - env only
|
|
166
|
+
APP_MENTOR_MODE: 'app.mentorMode',
|
|
167
|
+
APP_EDIT_MODE: 'app.editMode',
|
|
168
|
+
APP_LITE_MODE: 'app.liteMode',
|
|
169
|
+
TOOLS_LOG_FILE_OPS: 'tools.logFileOperations',
|
|
170
|
+
DEBUG_BASH_TOOL: 'debug.debugBashTool',
|
|
171
|
+
SSH_ENABLED: 'ssh.enabled',
|
|
172
|
+
SSH_HOST: 'ssh.host',
|
|
173
|
+
SSH_PORT: 'ssh.port',
|
|
174
|
+
SSH_USERNAME: 'ssh.username',
|
|
175
|
+
SSH_REMOTE_DIR: 'ssh.remoteDir',
|
|
176
|
+
WEB_SEARCH_PROVIDER: 'webSearch.provider',
|
|
177
|
+
WEB_SEARCH_TAVILY_API_KEY: 'webSearch.tavily.apiKey', // Sensitive - env only
|
|
178
|
+
};
|
|
179
|
+
// Define which settings are modifiable at runtime
|
|
180
|
+
const RUNTIME_MODIFIABLE_SETTINGS = new Set([
|
|
181
|
+
SETTING_KEYS.AGENT_MODEL,
|
|
182
|
+
SETTING_KEYS.AGENT_REASONING_EFFORT,
|
|
183
|
+
SETTING_KEYS.AGENT_TEMPERATURE,
|
|
184
|
+
SETTING_KEYS.AGENT_PROVIDER,
|
|
185
|
+
SETTING_KEYS.AGENT_MENTOR_MODEL,
|
|
186
|
+
SETTING_KEYS.AGENT_MENTOR_REASONING_EFFORT,
|
|
187
|
+
SETTING_KEYS.SHELL_TIMEOUT,
|
|
188
|
+
SETTING_KEYS.SHELL_MAX_OUTPUT_LINES,
|
|
189
|
+
SETTING_KEYS.SHELL_MAX_OUTPUT_CHARS,
|
|
190
|
+
SETTING_KEYS.LOGGING_LOG_LEVEL,
|
|
191
|
+
SETTING_KEYS.LOGGING_SUPPRESS_CONSOLE,
|
|
192
|
+
SETTING_KEYS.APP_MENTOR_MODE,
|
|
193
|
+
SETTING_KEYS.APP_EDIT_MODE,
|
|
194
|
+
SETTING_KEYS.APP_LITE_MODE,
|
|
195
|
+
]);
|
|
196
|
+
// Note: Sensitive settings are NOT in RUNTIME_MODIFIABLE_SETTINGS because they
|
|
197
|
+
// cannot be modified at all - they can only be set via environment variables at startup.
|
|
198
|
+
// app.mentorMode and app.editMode are runtime-modifiable AND persisted to disk so they
|
|
199
|
+
// survive across sessions (user's mode preference is preserved).
|
|
200
|
+
// Some settings with default values are optional to persist
|
|
201
|
+
const OPTIONAL_DEFAULT_KEYS = new Set([]);
|
|
202
|
+
// Default settings
|
|
203
|
+
const DEFAULT_SETTINGS = {
|
|
204
|
+
providers: [],
|
|
205
|
+
agent: {
|
|
206
|
+
model: 'gpt-5.1',
|
|
207
|
+
reasoningEffort: 'default',
|
|
208
|
+
maxTurns: 100,
|
|
209
|
+
retryAttempts: 2,
|
|
210
|
+
provider: 'openai',
|
|
211
|
+
openrouter: {
|
|
212
|
+
// defaults empty; can be provided via env or config
|
|
213
|
+
// defaults empty; can be provided via env or config
|
|
214
|
+
},
|
|
215
|
+
mentorModel: undefined,
|
|
216
|
+
mentorReasoningEffort: 'default',
|
|
217
|
+
},
|
|
218
|
+
shell: {
|
|
219
|
+
timeout: 120000,
|
|
220
|
+
maxOutputLines: 1000,
|
|
221
|
+
maxOutputChars: 10000,
|
|
222
|
+
},
|
|
223
|
+
ui: {
|
|
224
|
+
historySize: 1000,
|
|
225
|
+
},
|
|
226
|
+
logging: {
|
|
227
|
+
logLevel: 'info',
|
|
228
|
+
disableLogging: false,
|
|
229
|
+
debugLogging: false,
|
|
230
|
+
suppressConsoleOutput: true,
|
|
231
|
+
},
|
|
232
|
+
environment: {
|
|
233
|
+
nodeEnv: undefined,
|
|
234
|
+
},
|
|
235
|
+
app: {
|
|
236
|
+
shellPath: undefined,
|
|
237
|
+
mentorMode: false,
|
|
238
|
+
editMode: false,
|
|
239
|
+
liteMode: false,
|
|
240
|
+
},
|
|
241
|
+
tools: {
|
|
242
|
+
logFileOperations: true,
|
|
243
|
+
},
|
|
244
|
+
debug: {
|
|
245
|
+
debugBashTool: false,
|
|
246
|
+
},
|
|
247
|
+
ssh: {
|
|
248
|
+
enabled: false,
|
|
249
|
+
port: 22,
|
|
250
|
+
},
|
|
251
|
+
webSearch: {
|
|
252
|
+
provider: 'tavily',
|
|
253
|
+
tavily: {},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Service for managing application settings.
|
|
258
|
+
* Follows singleton pattern and supports:
|
|
259
|
+
* - XDG-compliant storage
|
|
260
|
+
* - Hierarchical precedence (CLI > Env > Config > Defaults)
|
|
261
|
+
* - Zod validation with graceful degradation
|
|
262
|
+
* - Runtime-modifiable vs startup-only settings
|
|
263
|
+
* - Setting source tracking
|
|
264
|
+
*/
|
|
265
|
+
export class SettingsService {
|
|
266
|
+
settings;
|
|
267
|
+
sources;
|
|
268
|
+
settingsDir;
|
|
269
|
+
disableLogging;
|
|
270
|
+
disableFilePersistence;
|
|
271
|
+
listeners = new Set();
|
|
272
|
+
loggingService;
|
|
273
|
+
// Detect if running in test environment
|
|
274
|
+
//
|
|
275
|
+
// We intentionally use a broad set of signals because different test runners
|
|
276
|
+
// set different environment variables. This prevents unit/integration tests
|
|
277
|
+
// from writing to disk by default (which can cause flaky tests and polluted
|
|
278
|
+
// developer machines/CI workspaces).
|
|
279
|
+
isTestEnvironment() {
|
|
280
|
+
return (process.env.NODE_ENV === 'test' ||
|
|
281
|
+
process.env.VITEST !== undefined ||
|
|
282
|
+
process.env.AVA_PATH !== undefined ||
|
|
283
|
+
process.env.JEST_WORKER_ID !== undefined ||
|
|
284
|
+
process.env.TERM2_TEST_MODE === 'true');
|
|
285
|
+
}
|
|
286
|
+
constructor(options) {
|
|
287
|
+
const { settingsDir = path.join(paths.log), disableLogging = false, disableFilePersistence, cli = {}, env = {}, loggingService, } = options ?? {};
|
|
288
|
+
const resolvedDisableLogging = disableLogging || parseBooleanEnv(process.env.DISABLE_LOGGING);
|
|
289
|
+
this.settingsDir = settingsDir;
|
|
290
|
+
this.disableLogging = resolvedDisableLogging;
|
|
291
|
+
this.sources = new Map();
|
|
292
|
+
// Use injected LoggingService or create a new one if not provided
|
|
293
|
+
this.loggingService =
|
|
294
|
+
loggingService ||
|
|
295
|
+
new LoggingService({
|
|
296
|
+
disableLogging: this.disableLogging,
|
|
297
|
+
});
|
|
298
|
+
// Disk persistence can be explicitly disabled (e.g., for tests), and is
|
|
299
|
+
// also automatically disabled when running under a known test runner.
|
|
300
|
+
this.disableFilePersistence =
|
|
301
|
+
disableFilePersistence ?? this.isTestEnvironment();
|
|
302
|
+
// Ensure settings directory exists
|
|
303
|
+
if (!fs.existsSync(this.settingsDir)) {
|
|
304
|
+
try {
|
|
305
|
+
fs.mkdirSync(this.settingsDir, { recursive: true });
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
if (!this.disableLogging) {
|
|
309
|
+
this.loggingService.error('Failed to create settings directory', {
|
|
310
|
+
error: error instanceof Error
|
|
311
|
+
? error.message
|
|
312
|
+
: String(error),
|
|
313
|
+
path: this.settingsDir,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Load settings with precedence: CLI > Env > Config > Default
|
|
319
|
+
const settingsFilePath = path.join(this.settingsDir, 'settings.json');
|
|
320
|
+
const configFileExisted = fs.existsSync(settingsFilePath);
|
|
321
|
+
const { validated: fileConfig, raw: rawFileConfig } = this.loadFromFile();
|
|
322
|
+
this.settings = this.merge(DEFAULT_SETTINGS, fileConfig, env, cli);
|
|
323
|
+
this.trackSources(DEFAULT_SETTINGS, fileConfig, env, cli);
|
|
324
|
+
// Register any runtime-defined providers from settings.json so they appear
|
|
325
|
+
// in the model selection menu and can be selected as agent.provider.
|
|
326
|
+
this.registerRuntimeProviders();
|
|
327
|
+
// Validate selected provider and fall back if invalid (without rejecting the
|
|
328
|
+
// entire settings file).
|
|
329
|
+
this.validateSelectedProvider();
|
|
330
|
+
// Apply logging level from settings to the logging service so it respects settings
|
|
331
|
+
try {
|
|
332
|
+
this.loggingService.setLogLevel(this.settings.logging.logLevel);
|
|
333
|
+
this.loggingService.setSuppressConsoleOutput(this.settings.logging.suppressConsoleOutput);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
if (!this.disableLogging) {
|
|
337
|
+
this.loggingService.warn('Failed to apply logging level from settings', {
|
|
338
|
+
error: error instanceof Error
|
|
339
|
+
? error.message
|
|
340
|
+
: String(error),
|
|
341
|
+
loggingLevel: this.settings.logging.logLevel,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (!this.disableLogging) {
|
|
346
|
+
this.loggingService.info('SettingsService initialized', {
|
|
347
|
+
cliOverrides: Object.keys(this.flattenSettings(cli)).length > 0,
|
|
348
|
+
envOverrides: Object.keys(this.flattenSettings(env)).length > 0,
|
|
349
|
+
configOverrides: Object.keys(this.flattenSettings(fileConfig)).length > 0,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
// Check if file config is missing any keys that exist in defaults
|
|
353
|
+
// Use raw file config (pre-Zod) to detect missing keys since Zod adds defaults
|
|
354
|
+
const shouldUpdateFile = configFileExisted &&
|
|
355
|
+
this.hasMissingKeys(rawFileConfig, DEFAULT_SETTINGS);
|
|
356
|
+
// If there was no config file on disk, persist the current merged settings so
|
|
357
|
+
// users get a settings.json created at startup (rather than waiting for a
|
|
358
|
+
// manual change). saveToFile is safe and handles errors/logging internally.
|
|
359
|
+
// Also update the file if new settings have been added since the file was created.
|
|
360
|
+
if (!configFileExisted) {
|
|
361
|
+
if (!this.disableFilePersistence) {
|
|
362
|
+
this.saveToFile();
|
|
363
|
+
if (!this.disableLogging) {
|
|
364
|
+
this.loggingService.info('Created settings file at startup', {
|
|
365
|
+
settingsFile: settingsFilePath,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else if (shouldUpdateFile) {
|
|
371
|
+
if (!this.disableFilePersistence) {
|
|
372
|
+
this.saveToFile();
|
|
373
|
+
if (!this.disableLogging) {
|
|
374
|
+
this.loggingService.info('Updated settings file with new default values', {
|
|
375
|
+
settingsFile: settingsFilePath,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
registerRuntimeProviders() {
|
|
382
|
+
const configured = this.settings?.providers;
|
|
383
|
+
if (!Array.isArray(configured) || configured.length === 0)
|
|
384
|
+
return;
|
|
385
|
+
for (const p of configured) {
|
|
386
|
+
const providerId = p?.name;
|
|
387
|
+
const baseUrl = p?.baseUrl;
|
|
388
|
+
if (!providerId || !baseUrl)
|
|
389
|
+
continue;
|
|
390
|
+
const existing = getProvider(providerId);
|
|
391
|
+
if (existing && !existing.isRuntimeDefined) {
|
|
392
|
+
if (!this.disableLogging) {
|
|
393
|
+
this.loggingService.warn('Skipping custom provider because it conflicts with a built-in provider id', { providerId });
|
|
394
|
+
}
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
try {
|
|
398
|
+
upsertProvider(createOpenAICompatibleProviderDefinition({
|
|
399
|
+
name: String(providerId),
|
|
400
|
+
baseUrl: String(baseUrl),
|
|
401
|
+
apiKey: p?.apiKey
|
|
402
|
+
? String(p.apiKey)
|
|
403
|
+
: undefined,
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
if (!this.disableLogging) {
|
|
408
|
+
this.loggingService.warn('Failed to register custom provider', {
|
|
409
|
+
providerId,
|
|
410
|
+
error: error instanceof Error
|
|
411
|
+
? error.message
|
|
412
|
+
: String(error),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
validateSelectedProvider() {
|
|
419
|
+
const current = this.settings?.agent?.provider || 'openai';
|
|
420
|
+
if (getProvider(current))
|
|
421
|
+
return;
|
|
422
|
+
if (!this.disableLogging) {
|
|
423
|
+
this.loggingService.warn('Configured agent.provider is not registered; falling back to openai', { provider: current });
|
|
424
|
+
}
|
|
425
|
+
this.settings.agent.provider = 'openai';
|
|
426
|
+
this.sources.set('agent.provider', 'default');
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Get a setting value by dot-notation key (e.g., 'agent.model')
|
|
430
|
+
*/
|
|
431
|
+
get(key) {
|
|
432
|
+
const keys = key.split('.');
|
|
433
|
+
let value = this.settings;
|
|
434
|
+
for (const k of keys) {
|
|
435
|
+
if (value && typeof value === 'object') {
|
|
436
|
+
value = value[k];
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return value;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Get the source of a setting
|
|
446
|
+
*/
|
|
447
|
+
getSource(key) {
|
|
448
|
+
return this.sources.get(key) || 'default';
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Check if a setting is runtime-modifiable
|
|
452
|
+
*/
|
|
453
|
+
isRuntimeModifiable(key) {
|
|
454
|
+
return RUNTIME_MODIFIABLE_SETTINGS.has(key);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Check if a setting is sensitive and should not be saved to disk
|
|
458
|
+
*/
|
|
459
|
+
isSensitive(key) {
|
|
460
|
+
return SENSITIVE_SETTING_KEYS.has(key);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Set a setting value (runtime modification)
|
|
464
|
+
* Only runtime-modifiable settings can be changed.
|
|
465
|
+
* Sensitive settings cannot be modified (must come from environment).
|
|
466
|
+
*/
|
|
467
|
+
set(key, value) {
|
|
468
|
+
if (this.isSensitive(key)) {
|
|
469
|
+
throw new Error(`Cannot modify '${key}' - it is a sensitive setting that can only be configured via environment variables.`);
|
|
470
|
+
}
|
|
471
|
+
if (!this.isRuntimeModifiable(key)) {
|
|
472
|
+
throw new Error(`Cannot modify '${key}' at runtime. Requires restart.`);
|
|
473
|
+
}
|
|
474
|
+
const keys = key.split('.');
|
|
475
|
+
let obj = this.settings;
|
|
476
|
+
// Navigate to parent
|
|
477
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
478
|
+
if (!obj[keys[i]]) {
|
|
479
|
+
obj[keys[i]] = {};
|
|
480
|
+
}
|
|
481
|
+
obj = obj[keys[i]];
|
|
482
|
+
}
|
|
483
|
+
// Set value
|
|
484
|
+
const lastKey = keys[keys.length - 1];
|
|
485
|
+
obj[lastKey] = value;
|
|
486
|
+
// Track source as 'cli' for runtime-set values
|
|
487
|
+
this.sources.set(key, 'cli');
|
|
488
|
+
// If we're changing the logging level, update the logging service runtime
|
|
489
|
+
if (key === 'logging.logLevel') {
|
|
490
|
+
try {
|
|
491
|
+
this.loggingService.setLogLevel(value);
|
|
492
|
+
}
|
|
493
|
+
catch (err) {
|
|
494
|
+
if (!this.disableLogging) {
|
|
495
|
+
this.loggingService.warn('Failed to update logging level at runtime', {
|
|
496
|
+
error: err instanceof Error
|
|
497
|
+
? err.message
|
|
498
|
+
: String(err),
|
|
499
|
+
loggingLevel: value,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (key === 'logging.suppressConsoleOutput') {
|
|
505
|
+
try {
|
|
506
|
+
this.loggingService.setSuppressConsoleOutput(Boolean(value));
|
|
507
|
+
}
|
|
508
|
+
catch (err) {
|
|
509
|
+
if (!this.disableLogging) {
|
|
510
|
+
this.loggingService.warn('Failed to update console output suppression at runtime', {
|
|
511
|
+
error: err instanceof Error
|
|
512
|
+
? err.message
|
|
513
|
+
: String(err),
|
|
514
|
+
suppressConsoleOutput: value,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Persist to file
|
|
520
|
+
if (!this.disableFilePersistence) {
|
|
521
|
+
this.saveToFile();
|
|
522
|
+
}
|
|
523
|
+
this.notifyChange(key);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Reset a setting to its default value.
|
|
527
|
+
* Sensitive settings cannot be reset as they should only come from env.
|
|
528
|
+
*/
|
|
529
|
+
reset(key) {
|
|
530
|
+
if (key && this.isSensitive(key)) {
|
|
531
|
+
throw new Error(`Cannot reset '${key}' - it is a sensitive setting that can only be configured via environment variables.`);
|
|
532
|
+
}
|
|
533
|
+
if (key) {
|
|
534
|
+
// Reset specific setting
|
|
535
|
+
const keys = key.split('.');
|
|
536
|
+
let obj = this.settings;
|
|
537
|
+
// Navigate to parent
|
|
538
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
539
|
+
if (!obj[keys[i]]) {
|
|
540
|
+
obj[keys[i]] = {};
|
|
541
|
+
}
|
|
542
|
+
obj = obj[keys[i]];
|
|
543
|
+
}
|
|
544
|
+
// Reset to default
|
|
545
|
+
const lastKey = keys[keys.length - 1];
|
|
546
|
+
const defaultKeys = key.split('.');
|
|
547
|
+
let defaultValue = DEFAULT_SETTINGS;
|
|
548
|
+
for (const k of defaultKeys) {
|
|
549
|
+
defaultValue = defaultValue[k];
|
|
550
|
+
}
|
|
551
|
+
obj[lastKey] = defaultValue;
|
|
552
|
+
this.sources.set(key, 'default');
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
// Reset all settings
|
|
556
|
+
this.settings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
|
557
|
+
this.sources.clear();
|
|
558
|
+
}
|
|
559
|
+
if (!this.disableFilePersistence) {
|
|
560
|
+
this.saveToFile();
|
|
561
|
+
}
|
|
562
|
+
this.notifyChange(key);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Subscribe to changes; returns an unsubscribe function.
|
|
566
|
+
*/
|
|
567
|
+
onChange(listener) {
|
|
568
|
+
this.listeners.add(listener);
|
|
569
|
+
return () => {
|
|
570
|
+
this.listeners.delete(listener);
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
notifyChange(changedKey) {
|
|
574
|
+
for (const listener of this.listeners) {
|
|
575
|
+
try {
|
|
576
|
+
listener(changedKey);
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
if (!this.disableLogging) {
|
|
580
|
+
this.loggingService.warn('Settings change listener threw', {
|
|
581
|
+
error: error instanceof Error
|
|
582
|
+
? error.message
|
|
583
|
+
: String(error),
|
|
584
|
+
changedKey,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Get all settings with their sources
|
|
592
|
+
*/
|
|
593
|
+
getAll() {
|
|
594
|
+
return {
|
|
595
|
+
agent: {
|
|
596
|
+
model: {
|
|
597
|
+
value: this.settings.agent.model,
|
|
598
|
+
source: this.getSource('agent.model'),
|
|
599
|
+
},
|
|
600
|
+
reasoningEffort: {
|
|
601
|
+
value: this.settings.agent.reasoningEffort,
|
|
602
|
+
source: this.getSource('agent.reasoningEffort'),
|
|
603
|
+
},
|
|
604
|
+
temperature: {
|
|
605
|
+
value: this.settings.agent.temperature,
|
|
606
|
+
source: this.getSource('agent.temperature'),
|
|
607
|
+
},
|
|
608
|
+
maxTurns: {
|
|
609
|
+
value: this.settings.agent.maxTurns,
|
|
610
|
+
source: this.getSource('agent.maxTurns'),
|
|
611
|
+
},
|
|
612
|
+
retryAttempts: {
|
|
613
|
+
value: this.settings.agent.retryAttempts,
|
|
614
|
+
source: this.getSource('agent.retryAttempts'),
|
|
615
|
+
},
|
|
616
|
+
provider: {
|
|
617
|
+
value: this.settings.agent.provider,
|
|
618
|
+
source: this.getSource('agent.provider'),
|
|
619
|
+
},
|
|
620
|
+
openrouter: {
|
|
621
|
+
value: this.settings.agent.openrouter,
|
|
622
|
+
source: this.getSource('agent.openrouter'),
|
|
623
|
+
},
|
|
624
|
+
mentorModel: {
|
|
625
|
+
value: this.settings.agent.mentorModel,
|
|
626
|
+
source: this.getSource('agent.mentorModel'),
|
|
627
|
+
},
|
|
628
|
+
mentorReasoningEffort: {
|
|
629
|
+
value: this.settings.agent.mentorReasoningEffort,
|
|
630
|
+
source: this.getSource('agent.mentorReasoningEffort'),
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
shell: {
|
|
634
|
+
timeout: {
|
|
635
|
+
value: this.settings.shell.timeout,
|
|
636
|
+
source: this.getSource('shell.timeout'),
|
|
637
|
+
},
|
|
638
|
+
maxOutputLines: {
|
|
639
|
+
value: this.settings.shell.maxOutputLines,
|
|
640
|
+
source: this.getSource('shell.maxOutputLines'),
|
|
641
|
+
},
|
|
642
|
+
maxOutputChars: {
|
|
643
|
+
value: this.settings.shell.maxOutputChars,
|
|
644
|
+
source: this.getSource('shell.maxOutputChars'),
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
ui: {
|
|
648
|
+
historySize: {
|
|
649
|
+
value: this.settings.ui.historySize,
|
|
650
|
+
source: this.getSource('ui.historySize'),
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
logging: {
|
|
654
|
+
logLevel: {
|
|
655
|
+
value: this.settings.logging.logLevel,
|
|
656
|
+
source: this.getSource('logging.logLevel'),
|
|
657
|
+
},
|
|
658
|
+
disableLogging: {
|
|
659
|
+
value: this.settings.logging.disableLogging,
|
|
660
|
+
source: this.getSource('logging.disableLogging'),
|
|
661
|
+
},
|
|
662
|
+
debugLogging: {
|
|
663
|
+
value: this.settings.logging.debugLogging,
|
|
664
|
+
source: this.getSource('logging.debugLogging'),
|
|
665
|
+
},
|
|
666
|
+
suppressConsoleOutput: {
|
|
667
|
+
value: this.settings.logging.suppressConsoleOutput,
|
|
668
|
+
source: this.getSource('logging.suppressConsoleOutput'),
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
environment: {
|
|
672
|
+
nodeEnv: {
|
|
673
|
+
value: this.settings.environment.nodeEnv,
|
|
674
|
+
source: this.getSource('environment.nodeEnv'),
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
app: {
|
|
678
|
+
shellPath: {
|
|
679
|
+
value: this.settings.app.shellPath,
|
|
680
|
+
source: this.getSource('app.shellPath'),
|
|
681
|
+
},
|
|
682
|
+
mentorMode: {
|
|
683
|
+
value: this.settings.app.mentorMode,
|
|
684
|
+
source: this.getSource('app.mentorMode'),
|
|
685
|
+
},
|
|
686
|
+
editMode: {
|
|
687
|
+
value: this.settings.app.editMode,
|
|
688
|
+
source: this.getSource('app.editMode'),
|
|
689
|
+
},
|
|
690
|
+
liteMode: {
|
|
691
|
+
value: this.settings.app.liteMode,
|
|
692
|
+
source: this.getSource('app.liteMode'),
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
tools: {
|
|
696
|
+
logFileOperations: {
|
|
697
|
+
value: this.settings.tools.logFileOperations,
|
|
698
|
+
source: this.getSource('tools.logFileOperations'),
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
debug: {
|
|
702
|
+
debugBashTool: {
|
|
703
|
+
value: this.settings.debug.debugBashTool,
|
|
704
|
+
source: this.getSource('debug.debugBashTool'),
|
|
705
|
+
},
|
|
706
|
+
},
|
|
707
|
+
ssh: {
|
|
708
|
+
enabled: {
|
|
709
|
+
value: this.settings.ssh.enabled,
|
|
710
|
+
source: this.getSource('ssh.enabled'),
|
|
711
|
+
},
|
|
712
|
+
host: {
|
|
713
|
+
value: this.settings.ssh.host,
|
|
714
|
+
source: this.getSource('ssh.host'),
|
|
715
|
+
},
|
|
716
|
+
port: {
|
|
717
|
+
value: this.settings.ssh.port,
|
|
718
|
+
source: this.getSource('ssh.port'),
|
|
719
|
+
},
|
|
720
|
+
username: {
|
|
721
|
+
value: this.settings.ssh.username,
|
|
722
|
+
source: this.getSource('ssh.username'),
|
|
723
|
+
},
|
|
724
|
+
remoteDir: {
|
|
725
|
+
value: this.settings.ssh.remoteDir,
|
|
726
|
+
source: this.getSource('ssh.remoteDir'),
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
webSearch: {
|
|
730
|
+
provider: {
|
|
731
|
+
value: this.settings.webSearch?.provider,
|
|
732
|
+
source: this.getSource('webSearch.provider'),
|
|
733
|
+
},
|
|
734
|
+
tavily: {
|
|
735
|
+
value: this.settings.webSearch?.tavily,
|
|
736
|
+
source: this.getSource('webSearch.tavily'),
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Load settings from file
|
|
743
|
+
* Returns both raw (pre-Zod) and validated data
|
|
744
|
+
*/
|
|
745
|
+
loadFromFile() {
|
|
746
|
+
try {
|
|
747
|
+
const settingsFile = path.join(this.settingsDir, 'settings.json');
|
|
748
|
+
if (!fs.existsSync(settingsFile)) {
|
|
749
|
+
return { validated: {}, raw: {} };
|
|
750
|
+
}
|
|
751
|
+
const content = fs.readFileSync(settingsFile, 'utf-8');
|
|
752
|
+
const parsed = JSON.parse(content);
|
|
753
|
+
// Validate and parse with Zod
|
|
754
|
+
const validated = SettingsSchema.safeParse(parsed);
|
|
755
|
+
if (!validated.success) {
|
|
756
|
+
if (!this.disableLogging) {
|
|
757
|
+
this.loggingService.warn('Settings file contains invalid values', {
|
|
758
|
+
errors: validated.error.issues.map(issue => ({
|
|
759
|
+
path: issue.path.join('.'),
|
|
760
|
+
message: issue.message,
|
|
761
|
+
})),
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
// Return empty object to trigger defaults
|
|
765
|
+
return { validated: {}, raw: parsed };
|
|
766
|
+
}
|
|
767
|
+
return { validated: validated.data, raw: parsed };
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
if (!this.disableLogging) {
|
|
771
|
+
this.loggingService.error('Failed to load settings file', {
|
|
772
|
+
error: error instanceof Error ? error.message : String(error),
|
|
773
|
+
settingsFile: path.join(this.settingsDir, 'settings.json'),
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
return { validated: {}, raw: {} };
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Save settings to file, excluding sensitive values
|
|
781
|
+
*/
|
|
782
|
+
saveToFile() {
|
|
783
|
+
if (this.disableFilePersistence) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
try {
|
|
787
|
+
const settingsFile = path.join(this.settingsDir, 'settings.json');
|
|
788
|
+
// Ensure directory exists
|
|
789
|
+
if (!fs.existsSync(this.settingsDir)) {
|
|
790
|
+
fs.mkdirSync(this.settingsDir, { recursive: true });
|
|
791
|
+
}
|
|
792
|
+
// Filter out sensitive settings before saving to disk
|
|
793
|
+
const settingsToSave = this.stripSensitiveSettings(this.settings);
|
|
794
|
+
const newContent = JSON.stringify(settingsToSave, null, 2);
|
|
795
|
+
// Only write if file doesn't exist or content has changed
|
|
796
|
+
// Compare parsed objects rather than string content to avoid false positives
|
|
797
|
+
// from formatting differences
|
|
798
|
+
if (fs.existsSync(settingsFile)) {
|
|
799
|
+
try {
|
|
800
|
+
const existingContent = fs.readFileSync(settingsFile, 'utf-8');
|
|
801
|
+
const existingParsed = JSON.parse(existingContent);
|
|
802
|
+
// Deep equality check that ignores formatting and key order
|
|
803
|
+
// Uses fast-deep-equal library for robust comparison
|
|
804
|
+
if (deepEqual(existingParsed, settingsToSave)) {
|
|
805
|
+
return; // No changes, don't write
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
catch (parseError) {
|
|
809
|
+
// If we can't parse the existing file, write the new content anyway
|
|
810
|
+
// This handles corrupted files gracefully
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
fs.writeFileSync(settingsFile, newContent, 'utf-8');
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
if (!this.disableLogging) {
|
|
817
|
+
this.loggingService.error('Failed to save settings file', {
|
|
818
|
+
error: error instanceof Error ? error.message : String(error),
|
|
819
|
+
settingsFile: path.join(this.settingsDir, 'settings.json'),
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Remove sensitive settings that should never be persisted to disk
|
|
826
|
+
*/
|
|
827
|
+
stripSensitiveSettings(settings) {
|
|
828
|
+
const cleaned = JSON.parse(JSON.stringify(settings));
|
|
829
|
+
// Remove sensitive openrouter fields (keep non-secret config)
|
|
830
|
+
if (cleaned.agent?.openrouter) {
|
|
831
|
+
delete cleaned.agent.openrouter.apiKey;
|
|
832
|
+
delete cleaned.agent.openrouter.baseUrl;
|
|
833
|
+
delete cleaned.agent.openrouter.referrer;
|
|
834
|
+
delete cleaned.agent.openrouter.title;
|
|
835
|
+
// Only keep model if it's set (it's not sensitive)
|
|
836
|
+
if (Object.keys(cleaned.agent.openrouter).length === 0) {
|
|
837
|
+
delete cleaned.agent.openrouter;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
// Remove sensitive app settings
|
|
841
|
+
if (cleaned.app) {
|
|
842
|
+
delete cleaned.app.shellPath;
|
|
843
|
+
// mentorMode and editMode are persisted so they survive across sessions
|
|
844
|
+
}
|
|
845
|
+
return cleaned;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Check if target object is missing any keys that exist in source
|
|
849
|
+
*/
|
|
850
|
+
hasMissingKeys(target, source, prefix = '') {
|
|
851
|
+
for (const key in source) {
|
|
852
|
+
if (!source.hasOwnProperty(key))
|
|
853
|
+
continue;
|
|
854
|
+
const pathKey = prefix ? `${prefix}.${key}` : key;
|
|
855
|
+
const sourceValue = source[key];
|
|
856
|
+
if (!(key in target)) {
|
|
857
|
+
// Skip optional default keys when deciding whether to rewrite file
|
|
858
|
+
if (OPTIONAL_DEFAULT_KEYS.has(pathKey)) {
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
// If the default value is undefined, treat it as optional for persistence
|
|
862
|
+
if (typeof sourceValue === 'undefined') {
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
const targetValue = target[key];
|
|
868
|
+
// Recursively check nested objects
|
|
869
|
+
if (sourceValue &&
|
|
870
|
+
typeof sourceValue === 'object' &&
|
|
871
|
+
!Array.isArray(sourceValue) &&
|
|
872
|
+
targetValue &&
|
|
873
|
+
typeof targetValue === 'object' &&
|
|
874
|
+
!Array.isArray(targetValue)) {
|
|
875
|
+
if (this.hasMissingKeys(targetValue, sourceValue, pathKey)) {
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Flatten nested object to dot notation
|
|
884
|
+
*/
|
|
885
|
+
flattenSettings(obj, prefix = '') {
|
|
886
|
+
const result = {};
|
|
887
|
+
for (const key in obj) {
|
|
888
|
+
if (!obj.hasOwnProperty(key))
|
|
889
|
+
continue;
|
|
890
|
+
const value = obj[key];
|
|
891
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
892
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
893
|
+
Object.assign(result, this.flattenSettings(value, newKey));
|
|
894
|
+
}
|
|
895
|
+
else {
|
|
896
|
+
result[newKey] = value;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return result;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Merge multiple settings sources with proper precedence
|
|
903
|
+
*/
|
|
904
|
+
merge(defaults, fileConfig, env, cli) {
|
|
905
|
+
// Deep merge starting with defaults
|
|
906
|
+
const result = JSON.parse(JSON.stringify(defaults));
|
|
907
|
+
// Merge file config
|
|
908
|
+
this.deepMerge(result, fileConfig);
|
|
909
|
+
// Merge env
|
|
910
|
+
this.deepMerge(result, env);
|
|
911
|
+
// Merge cli (highest priority)
|
|
912
|
+
this.deepMerge(result, cli);
|
|
913
|
+
// Ensure all required fields are present
|
|
914
|
+
const merged = {
|
|
915
|
+
providers: result.providers ||
|
|
916
|
+
JSON.parse(JSON.stringify(defaults.providers)),
|
|
917
|
+
agent: result.agent || JSON.parse(JSON.stringify(defaults.agent)),
|
|
918
|
+
shell: result.shell || JSON.parse(JSON.stringify(defaults.shell)),
|
|
919
|
+
ui: result.ui || JSON.parse(JSON.stringify(defaults.ui)),
|
|
920
|
+
logging: result.logging || JSON.parse(JSON.stringify(defaults.logging)),
|
|
921
|
+
environment: result.environment ||
|
|
922
|
+
JSON.parse(JSON.stringify(defaults.environment)),
|
|
923
|
+
app: result.app || JSON.parse(JSON.stringify(defaults.app)),
|
|
924
|
+
tools: result.tools || JSON.parse(JSON.stringify(defaults.tools)),
|
|
925
|
+
debug: result.debug || JSON.parse(JSON.stringify(defaults.debug)),
|
|
926
|
+
ssh: result.ssh || JSON.parse(JSON.stringify(defaults.ssh)),
|
|
927
|
+
webSearch: result.webSearch ||
|
|
928
|
+
JSON.parse(JSON.stringify(defaults.webSearch)),
|
|
929
|
+
};
|
|
930
|
+
// Validate final result
|
|
931
|
+
const validated = SettingsSchema.safeParse(merged);
|
|
932
|
+
if (validated.success) {
|
|
933
|
+
// Ensure we return a complete SettingsData object
|
|
934
|
+
return {
|
|
935
|
+
providers: merged.providers,
|
|
936
|
+
agent: merged.agent,
|
|
937
|
+
shell: merged.shell,
|
|
938
|
+
ui: merged.ui,
|
|
939
|
+
logging: merged.logging,
|
|
940
|
+
environment: merged.environment,
|
|
941
|
+
app: merged.app,
|
|
942
|
+
tools: merged.tools,
|
|
943
|
+
debug: merged.debug,
|
|
944
|
+
ssh: merged.ssh,
|
|
945
|
+
webSearch: merged.webSearch,
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
// If validation fails, return defaults
|
|
949
|
+
if (!this.disableLogging) {
|
|
950
|
+
this.loggingService.warn('Final merged settings failed validation, using defaults', {
|
|
951
|
+
errors: validated.error.issues.map(issue => ({
|
|
952
|
+
path: issue.path.join('.'),
|
|
953
|
+
message: issue.message,
|
|
954
|
+
})),
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
return defaults;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Deep merge source into target
|
|
961
|
+
*/
|
|
962
|
+
deepMerge(target, source) {
|
|
963
|
+
for (const key in source) {
|
|
964
|
+
if (!source.hasOwnProperty(key))
|
|
965
|
+
continue;
|
|
966
|
+
const sourceValue = source[key];
|
|
967
|
+
if (sourceValue &&
|
|
968
|
+
typeof sourceValue === 'object' &&
|
|
969
|
+
!Array.isArray(sourceValue)) {
|
|
970
|
+
if (!target[key] || typeof target[key] !== 'object') {
|
|
971
|
+
target[key] = {};
|
|
972
|
+
}
|
|
973
|
+
this.deepMerge(target[key], sourceValue);
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
target[key] = sourceValue;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Track the source of each setting
|
|
982
|
+
*/
|
|
983
|
+
trackSources(defaults, fileConfig, env, cli) {
|
|
984
|
+
const flatDefaults = this.flattenSettings(defaults);
|
|
985
|
+
const flatFileConfig = this.flattenSettings(fileConfig);
|
|
986
|
+
const flatEnv = this.flattenSettings(env);
|
|
987
|
+
const flatCli = this.flattenSettings(cli);
|
|
988
|
+
// For each possible setting key, determine its source
|
|
989
|
+
for (const key in flatDefaults) {
|
|
990
|
+
if (flatCli.hasOwnProperty(key)) {
|
|
991
|
+
this.sources.set(key, 'cli');
|
|
992
|
+
}
|
|
993
|
+
else if (flatEnv.hasOwnProperty(key)) {
|
|
994
|
+
this.sources.set(key, 'env');
|
|
995
|
+
}
|
|
996
|
+
else if (flatFileConfig.hasOwnProperty(key)) {
|
|
997
|
+
this.sources.set(key, 'config');
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
this.sources.set(key, 'default');
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Build environment-derived overrides from process.env
|
|
1007
|
+
* Exported for use in CLI initialization
|
|
1008
|
+
*/
|
|
1009
|
+
export function buildEnvOverrides() {
|
|
1010
|
+
const env = (typeof process !== 'undefined' ? process.env : {});
|
|
1011
|
+
const openrouter = {};
|
|
1012
|
+
if (env.OPENROUTER_API_KEY)
|
|
1013
|
+
openrouter.apiKey = env.OPENROUTER_API_KEY;
|
|
1014
|
+
if (env.OPENROUTER_MODEL)
|
|
1015
|
+
openrouter.model = env.OPENROUTER_MODEL;
|
|
1016
|
+
if (env.OPENROUTER_BASE_URL)
|
|
1017
|
+
openrouter.baseUrl = env.OPENROUTER_BASE_URL;
|
|
1018
|
+
if (env.OPENROUTER_REFERRER)
|
|
1019
|
+
openrouter.referrer = env.OPENROUTER_REFERRER;
|
|
1020
|
+
if (env.OPENROUTER_TITLE)
|
|
1021
|
+
openrouter.title = env.OPENROUTER_TITLE;
|
|
1022
|
+
const logging = {};
|
|
1023
|
+
if (env.LOG_LEVEL)
|
|
1024
|
+
logging.logLevel = env.LOG_LEVEL;
|
|
1025
|
+
if (env.DISABLE_LOGGING !== undefined)
|
|
1026
|
+
logging.disableLogging = String(env.DISABLE_LOGGING) === 'true';
|
|
1027
|
+
if (env.DEBUG_LOGGING !== undefined)
|
|
1028
|
+
logging.debugLogging = true;
|
|
1029
|
+
const environment = {
|
|
1030
|
+
nodeEnv: env.NODE_ENV,
|
|
1031
|
+
};
|
|
1032
|
+
const app = {
|
|
1033
|
+
shellPath: env.SHELL || env.COMSPEC,
|
|
1034
|
+
};
|
|
1035
|
+
const tools = {};
|
|
1036
|
+
if (env.LOG_FILE_OPERATIONS !== undefined)
|
|
1037
|
+
tools.logFileOperations = String(env.LOG_FILE_OPERATIONS) !== 'false';
|
|
1038
|
+
const debug = {};
|
|
1039
|
+
if (env.DEBUG_BASH_TOOL !== undefined)
|
|
1040
|
+
debug.debugBashTool = true;
|
|
1041
|
+
const webSearch = {};
|
|
1042
|
+
if (env.TAVILY_API_KEY) {
|
|
1043
|
+
webSearch.tavily = { apiKey: env.TAVILY_API_KEY };
|
|
1044
|
+
}
|
|
1045
|
+
if (env.WEB_SEARCH_PROVIDER) {
|
|
1046
|
+
webSearch.provider = env.WEB_SEARCH_PROVIDER;
|
|
1047
|
+
}
|
|
1048
|
+
const agent = { openrouter };
|
|
1049
|
+
return {
|
|
1050
|
+
agent,
|
|
1051
|
+
logging,
|
|
1052
|
+
environment,
|
|
1053
|
+
app,
|
|
1054
|
+
tools,
|
|
1055
|
+
debug,
|
|
1056
|
+
webSearch,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
const parseBooleanEnv = (value) => {
|
|
1060
|
+
if (typeof value !== 'string') {
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
const normalized = value.trim().toLowerCase();
|
|
1064
|
+
return normalized === '1' || normalized === 'true' || normalized === 'yes';
|
|
1065
|
+
};
|
|
1066
|
+
const isTestEnvironment = () => {
|
|
1067
|
+
return (process.env.NODE_ENV === 'test' ||
|
|
1068
|
+
process.env.VITEST !== undefined ||
|
|
1069
|
+
process.env.AVA_PATH !== undefined ||
|
|
1070
|
+
process.env.JEST_WORKER_ID !== undefined ||
|
|
1071
|
+
process.env.TERM2_TEST_MODE === 'true');
|
|
1072
|
+
};
|
|
1073
|
+
/**
|
|
1074
|
+
* @deprecated DO NOT USE - Singleton pattern is deprecated
|
|
1075
|
+
*
|
|
1076
|
+
* This singleton is deprecated and should not be used in application code.
|
|
1077
|
+
* Instead, pass the SettingsService instance via dependency injection:
|
|
1078
|
+
*
|
|
1079
|
+
* - In App component: Accept as a prop from cli.tsx
|
|
1080
|
+
* - In services/tools: Accept via constructor deps parameter
|
|
1081
|
+
* - In hooks: Use a context provider or accept as parameter
|
|
1082
|
+
*
|
|
1083
|
+
* This export now throws an error when accessed to catch deprecated usage.
|
|
1084
|
+
* It's only allowed in test files for backwards compatibility.
|
|
1085
|
+
*/
|
|
1086
|
+
const _settingsServiceInstance = new SettingsService({
|
|
1087
|
+
env: buildEnvOverrides(),
|
|
1088
|
+
loggingService: new LoggingService({
|
|
1089
|
+
disableLogging: parseBooleanEnv(process.env.DISABLE_LOGGING) ||
|
|
1090
|
+
isTestEnvironment(),
|
|
1091
|
+
debugLogging: parseBooleanEnv(process.env.DEBUG_LOGGING),
|
|
1092
|
+
}),
|
|
1093
|
+
});
|
|
1094
|
+
export const settingsService = new Proxy(_settingsServiceInstance, {
|
|
1095
|
+
get(target, prop) {
|
|
1096
|
+
// Allow access in test environment for backwards compatibility
|
|
1097
|
+
if (isTestEnvironment()) {
|
|
1098
|
+
const value = target[prop];
|
|
1099
|
+
// Bind methods to the original target to preserve 'this' context
|
|
1100
|
+
if (typeof value === 'function') {
|
|
1101
|
+
return value.bind(target);
|
|
1102
|
+
}
|
|
1103
|
+
return value;
|
|
1104
|
+
}
|
|
1105
|
+
// Get the caller's stack trace to show where the deprecated usage is
|
|
1106
|
+
const stack = new Error().stack || '';
|
|
1107
|
+
const callerLine = stack.split('\n')[2] || 'unknown location';
|
|
1108
|
+
throw new Error(`DEPRECATED: Direct use of settingsService singleton is not allowed.\n` +
|
|
1109
|
+
`Called from: ${callerLine.trim()}\n\n` +
|
|
1110
|
+
`Instead, pass SettingsService via dependency injection:\n` +
|
|
1111
|
+
` - In App component: Accept as prop from cli.tsx\n` +
|
|
1112
|
+
` - In services/tools: Accept via 'deps' constructor parameter\n` +
|
|
1113
|
+
` - In hooks: Accept as parameter or use a context provider\n\n` +
|
|
1114
|
+
`See source/app.tsx for an example of proper dependency injection.`);
|
|
1115
|
+
},
|
|
1116
|
+
});
|
|
1117
|
+
/**
|
|
1118
|
+
* Publicly exported list of sensitive settings for UI/CLI components to use.
|
|
1119
|
+
* These settings should only be configured via environment variables.
|
|
1120
|
+
*/
|
|
1121
|
+
export const SENSITIVE_SETTINGS = {
|
|
1122
|
+
AGENT_OPENROUTER_API_KEY: 'agent.openrouter.apiKey',
|
|
1123
|
+
AGENT_OPENROUTER_BASE_URL: 'agent.openrouter.baseUrl',
|
|
1124
|
+
AGENT_OPENROUTER_REFERRER: 'agent.openrouter.referrer',
|
|
1125
|
+
AGENT_OPENROUTER_TITLE: 'agent.openrouter.title',
|
|
1126
|
+
APP_SHELL_PATH: 'app.shellPath',
|
|
1127
|
+
};
|
|
1128
|
+
//# sourceMappingURL=settings-service.js.map
|