@butlerw/vellum 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/README.md +411 -0
- package/__fixtures__/responses/code-generation.json +42 -0
- package/__fixtures__/responses/error-response.json +20 -0
- package/__fixtures__/responses/hello-world.json +32 -0
- package/dist/auth-6MCXESOH.js +26 -0
- package/dist/chunk-SECXJGWA.js +597 -0
- package/dist/index.js +34023 -0
- package/package.json +67 -0
- package/src/__tests__/commands.e2e.test.ts +728 -0
- package/src/__tests__/credentials.test.ts +713 -0
- package/src/__tests__/mode-e2e.test.ts +391 -0
- package/src/__tests__/tui-integration.test.tsx +1271 -0
- package/src/agents/__tests__/task-persistence.test.ts +235 -0
- package/src/agents/commands/delegate.ts +240 -0
- package/src/agents/commands/index.ts +10 -0
- package/src/agents/commands/resume.ts +335 -0
- package/src/agents/index.ts +29 -0
- package/src/agents/task-persistence.ts +272 -0
- package/src/agents/task-resumption.ts +242 -0
- package/src/app.tsx +4737 -0
- package/src/commands/__tests__/.gitkeep +1 -0
- package/src/commands/__tests__/agents.test.ts +606 -0
- package/src/commands/__tests__/auth.test.ts +626 -0
- package/src/commands/__tests__/autocomplete.test.ts +683 -0
- package/src/commands/__tests__/batch.test.ts +287 -0
- package/src/commands/__tests__/chain-pipe-parser.test.ts +654 -0
- package/src/commands/__tests__/completion.test.ts +238 -0
- package/src/commands/__tests__/core.test.ts +363 -0
- package/src/commands/__tests__/executor.test.ts +496 -0
- package/src/commands/__tests__/exit-codes.test.ts +220 -0
- package/src/commands/__tests__/init.test.ts +243 -0
- package/src/commands/__tests__/language.test.ts +353 -0
- package/src/commands/__tests__/mode-cli.test.ts +667 -0
- package/src/commands/__tests__/model.test.ts +277 -0
- package/src/commands/__tests__/parser.test.ts +493 -0
- package/src/commands/__tests__/performance.bench.ts +380 -0
- package/src/commands/__tests__/registry.test.ts +534 -0
- package/src/commands/__tests__/resume.test.ts +449 -0
- package/src/commands/__tests__/security.test.ts +845 -0
- package/src/commands/__tests__/stream-json.test.ts +372 -0
- package/src/commands/__tests__/user-commands.test.ts +597 -0
- package/src/commands/adapters.ts +267 -0
- package/src/commands/agent.ts +395 -0
- package/src/commands/agents/generate.ts +506 -0
- package/src/commands/agents/index.ts +272 -0
- package/src/commands/agents/show.ts +271 -0
- package/src/commands/agents/validate.ts +387 -0
- package/src/commands/auth.ts +883 -0
- package/src/commands/autocomplete.ts +480 -0
- package/src/commands/batch/command.ts +388 -0
- package/src/commands/batch/executor.ts +361 -0
- package/src/commands/batch/index.ts +12 -0
- package/src/commands/commit.ts +235 -0
- package/src/commands/completion/index.ts +371 -0
- package/src/commands/condense.ts +191 -0
- package/src/commands/config.ts +344 -0
- package/src/commands/context-provider.ts +173 -0
- package/src/commands/copy.ts +329 -0
- package/src/commands/core/clear.ts +38 -0
- package/src/commands/core/exit.ts +43 -0
- package/src/commands/core/help.ts +354 -0
- package/src/commands/core/index.ts +15 -0
- package/src/commands/cost.ts +179 -0
- package/src/commands/credentials.tsx +618 -0
- package/src/commands/custom-agents/__tests__/custom-agents.test.ts +709 -0
- package/src/commands/custom-agents/create.ts +377 -0
- package/src/commands/custom-agents/export.ts +135 -0
- package/src/commands/custom-agents/import.ts +199 -0
- package/src/commands/custom-agents/index.ts +372 -0
- package/src/commands/custom-agents/info.ts +318 -0
- package/src/commands/custom-agents/list.ts +267 -0
- package/src/commands/custom-agents/validate.ts +388 -0
- package/src/commands/diff-mode.ts +241 -0
- package/src/commands/env.ts +53 -0
- package/src/commands/executor.ts +579 -0
- package/src/commands/exit-codes.ts +202 -0
- package/src/commands/index.ts +701 -0
- package/src/commands/init/index.ts +15 -0
- package/src/commands/init/prompts.ts +366 -0
- package/src/commands/init/templates/commands-readme.md +80 -0
- package/src/commands/init/templates/example-command.md +79 -0
- package/src/commands/init/templates/example-skill.md +168 -0
- package/src/commands/init/templates/example-workflow.md +101 -0
- package/src/commands/init/templates/prompts-readme.md +52 -0
- package/src/commands/init/templates/rules-readme.md +63 -0
- package/src/commands/init/templates/skills-readme.md +83 -0
- package/src/commands/init/templates/workflows-readme.md +94 -0
- package/src/commands/init.ts +391 -0
- package/src/commands/install.ts +90 -0
- package/src/commands/language.ts +191 -0
- package/src/commands/loaders/.gitkeep +1 -0
- package/src/commands/lsp.ts +199 -0
- package/src/commands/markdown-commands.ts +253 -0
- package/src/commands/mcp.ts +588 -0
- package/src/commands/memory/export.ts +341 -0
- package/src/commands/memory/index.ts +148 -0
- package/src/commands/memory/list.ts +261 -0
- package/src/commands/memory/search.ts +346 -0
- package/src/commands/memory/utils.ts +15 -0
- package/src/commands/metrics.ts +75 -0
- package/src/commands/migrate/index.ts +16 -0
- package/src/commands/migrate/prompts.ts +477 -0
- package/src/commands/mode.ts +331 -0
- package/src/commands/model.ts +298 -0
- package/src/commands/onboard.ts +205 -0
- package/src/commands/open.ts +169 -0
- package/src/commands/output/stream-json.ts +373 -0
- package/src/commands/parser/chain-parser.ts +370 -0
- package/src/commands/parser/index.ts +29 -0
- package/src/commands/parser/pipe-parser.ts +480 -0
- package/src/commands/parser.ts +588 -0
- package/src/commands/persistence.ts +355 -0
- package/src/commands/progress.ts +18 -0
- package/src/commands/prompt/index.ts +17 -0
- package/src/commands/prompt/validate.ts +621 -0
- package/src/commands/prompt-priority.ts +401 -0
- package/src/commands/registry.ts +374 -0
- package/src/commands/sandbox/index.ts +131 -0
- package/src/commands/security/index.ts +21 -0
- package/src/commands/security/input-sanitizer.ts +168 -0
- package/src/commands/security/permission-checker.ts +456 -0
- package/src/commands/security/sensitive-data.ts +350 -0
- package/src/commands/session/delete.ts +38 -0
- package/src/commands/session/export.ts +39 -0
- package/src/commands/session/index.ts +26 -0
- package/src/commands/session/list.ts +26 -0
- package/src/commands/session/resume.ts +562 -0
- package/src/commands/session/search.ts +434 -0
- package/src/commands/session/show.ts +26 -0
- package/src/commands/settings.ts +368 -0
- package/src/commands/setup.ts +23 -0
- package/src/commands/shell/index.ts +16 -0
- package/src/commands/shell/setup.ts +422 -0
- package/src/commands/shell-init.ts +50 -0
- package/src/commands/shell-integration/index.ts +194 -0
- package/src/commands/skill.ts +1220 -0
- package/src/commands/spec.ts +558 -0
- package/src/commands/status.ts +246 -0
- package/src/commands/theme.ts +211 -0
- package/src/commands/think.ts +551 -0
- package/src/commands/trust.ts +211 -0
- package/src/commands/tutorial.ts +522 -0
- package/src/commands/types.ts +512 -0
- package/src/commands/update.ts +274 -0
- package/src/commands/usage.ts +213 -0
- package/src/commands/user-commands.ts +630 -0
- package/src/commands/utils.ts +142 -0
- package/src/commands/vim.ts +152 -0
- package/src/commands/workflow.ts +257 -0
- package/src/components/header.tsx +25 -0
- package/src/components/input.tsx +25 -0
- package/src/components/message-list.tsx +32 -0
- package/src/components/status-bar.tsx +23 -0
- package/src/index.tsx +614 -0
- package/src/onboarding/__tests__/tutorial.test.ts +740 -0
- package/src/onboarding/index.ts +69 -0
- package/src/onboarding/tips/index.ts +9 -0
- package/src/onboarding/tips/tip-engine.ts +459 -0
- package/src/onboarding/tutorial/index.ts +88 -0
- package/src/onboarding/tutorial/lessons/basics.ts +151 -0
- package/src/onboarding/tutorial/lessons/index.ts +151 -0
- package/src/onboarding/tutorial/lessons/modes.ts +230 -0
- package/src/onboarding/tutorial/lessons/tools.ts +172 -0
- package/src/onboarding/tutorial/progress-tracker.ts +350 -0
- package/src/onboarding/tutorial/storage.ts +249 -0
- package/src/onboarding/tutorial/tutorial-system.ts +462 -0
- package/src/onboarding/tutorial/types.ts +310 -0
- package/src/orchestrator-singleton.ts +129 -0
- package/src/shutdown.ts +33 -0
- package/src/test/e2e/assertions.ts +267 -0
- package/src/test/e2e/fixtures.ts +204 -0
- package/src/test/e2e/harness.ts +575 -0
- package/src/test/e2e/index.ts +57 -0
- package/src/test/e2e/types.ts +228 -0
- package/src/test/fixtures/__tests__/fake-response-loader.test.ts +314 -0
- package/src/test/fixtures/fake-response-loader.ts +314 -0
- package/src/test/fixtures/index.ts +20 -0
- package/src/tui/__tests__/mcp-panel.test.tsx +82 -0
- package/src/tui/__tests__/mcp-wiring.test.tsx +78 -0
- package/src/tui/__tests__/mode-components.test.tsx +395 -0
- package/src/tui/__tests__/permission-ask-flow.test.tsx +138 -0
- package/src/tui/__tests__/sidebar-panel-data.test.tsx +148 -0
- package/src/tui/__tests__/tools-panel-hotkeys.test.tsx +41 -0
- package/src/tui/adapters/agent-adapter.ts +1008 -0
- package/src/tui/adapters/index.ts +48 -0
- package/src/tui/adapters/message-adapter.ts +315 -0
- package/src/tui/adapters/persistence-bridge.ts +331 -0
- package/src/tui/adapters/session-adapter.ts +419 -0
- package/src/tui/buffered-stdout.ts +223 -0
- package/src/tui/components/AgentProgress.tsx +424 -0
- package/src/tui/components/Banner/AsciiArt.ts +160 -0
- package/src/tui/components/Banner/Banner.tsx +355 -0
- package/src/tui/components/Banner/ShimmerContext.tsx +131 -0
- package/src/tui/components/Banner/ShimmerText.tsx +193 -0
- package/src/tui/components/Banner/TypeWriterGradient.tsx +321 -0
- package/src/tui/components/Banner/index.ts +61 -0
- package/src/tui/components/Banner/useShimmer.ts +241 -0
- package/src/tui/components/ChatView.tsx +11 -0
- package/src/tui/components/Checkpoint/CheckpointDiffView.tsx +371 -0
- package/src/tui/components/Checkpoint/SnapshotCheckpointPanel.tsx +440 -0
- package/src/tui/components/Checkpoint/index.ts +19 -0
- package/src/tui/components/CostDisplay.tsx +226 -0
- package/src/tui/components/InitErrorBanner.tsx +122 -0
- package/src/tui/components/Input/Autocomplete.tsx +603 -0
- package/src/tui/components/Input/EnhancedCommandInput.tsx +471 -0
- package/src/tui/components/Input/HighlightedText.tsx +236 -0
- package/src/tui/components/Input/MentionAutocomplete.tsx +375 -0
- package/src/tui/components/Input/TextInput.tsx +1002 -0
- package/src/tui/components/Input/__tests__/Autocomplete.test.tsx +374 -0
- package/src/tui/components/Input/__tests__/TextInput.test.tsx +241 -0
- package/src/tui/components/Input/__tests__/highlight.test.ts +219 -0
- package/src/tui/components/Input/__tests__/slash-command-utils.test.ts +104 -0
- package/src/tui/components/Input/highlight.ts +362 -0
- package/src/tui/components/Input/index.ts +36 -0
- package/src/tui/components/Input/slash-command-utils.ts +135 -0
- package/src/tui/components/Layout.tsx +432 -0
- package/src/tui/components/McpPanel.tsx +137 -0
- package/src/tui/components/MemoryPanel.tsx +448 -0
- package/src/tui/components/Messages/CodeBlock.tsx +527 -0
- package/src/tui/components/Messages/DiffView.tsx +679 -0
- package/src/tui/components/Messages/ImageReference.tsx +89 -0
- package/src/tui/components/Messages/MarkdownBlock.tsx +228 -0
- package/src/tui/components/Messages/MarkdownRenderer.tsx +498 -0
- package/src/tui/components/Messages/MessageBubble.tsx +270 -0
- package/src/tui/components/Messages/MessageList.tsx +1719 -0
- package/src/tui/components/Messages/StreamingText.tsx +216 -0
- package/src/tui/components/Messages/ThinkingBlock.tsx +408 -0
- package/src/tui/components/Messages/ToolResultPreview.tsx +243 -0
- package/src/tui/components/Messages/__tests__/CodeBlock.test.tsx +296 -0
- package/src/tui/components/Messages/__tests__/DiffView.test.tsx +239 -0
- package/src/tui/components/Messages/__tests__/MarkdownRenderer.test.tsx +303 -0
- package/src/tui/components/Messages/__tests__/MessageBubble.test.tsx +268 -0
- package/src/tui/components/Messages/__tests__/MessageList.test.tsx +324 -0
- package/src/tui/components/Messages/__tests__/StreamingText.test.tsx +215 -0
- package/src/tui/components/Messages/index.ts +25 -0
- package/src/tui/components/ModeIndicator.tsx +177 -0
- package/src/tui/components/ModeSelector.tsx +216 -0
- package/src/tui/components/ModelSelector.tsx +339 -0
- package/src/tui/components/OnboardingWizard.tsx +670 -0
- package/src/tui/components/PhaseProgressIndicator.tsx +270 -0
- package/src/tui/components/RateLimitIndicator.tsx +82 -0
- package/src/tui/components/ScreenReaderLayout.tsx +295 -0
- package/src/tui/components/SettingsPanel.tsx +643 -0
- package/src/tui/components/Sidebar/SystemStatusPanel.tsx +284 -0
- package/src/tui/components/Sidebar/index.ts +9 -0
- package/src/tui/components/Status/ModelStatusBar.tsx +270 -0
- package/src/tui/components/Status/index.ts +12 -0
- package/src/tui/components/StatusBar/AgentModeIndicator.tsx +257 -0
- package/src/tui/components/StatusBar/ContextProgress.tsx +167 -0
- package/src/tui/components/StatusBar/FileChangesIndicator.tsx +62 -0
- package/src/tui/components/StatusBar/GitIndicator.tsx +89 -0
- package/src/tui/components/StatusBar/HeaderBar.tsx +126 -0
- package/src/tui/components/StatusBar/ModelIndicator.tsx +157 -0
- package/src/tui/components/StatusBar/PersistenceStatusIndicator.tsx +210 -0
- package/src/tui/components/StatusBar/ResilienceIndicator.tsx +106 -0
- package/src/tui/components/StatusBar/SandboxIndicator.tsx +167 -0
- package/src/tui/components/StatusBar/StatusBar.tsx +368 -0
- package/src/tui/components/StatusBar/ThinkingModeIndicator.tsx +170 -0
- package/src/tui/components/StatusBar/TokenBreakdown.tsx +246 -0
- package/src/tui/components/StatusBar/TokenCounter.tsx +135 -0
- package/src/tui/components/StatusBar/TrustModeIndicator.tsx +130 -0
- package/src/tui/components/StatusBar/WorkspaceIndicator.tsx +86 -0
- package/src/tui/components/StatusBar/__tests__/AgentModeIndicator.test.tsx +193 -0
- package/src/tui/components/StatusBar/__tests__/StatusBar.test.tsx +729 -0
- package/src/tui/components/StatusBar/index.ts +60 -0
- package/src/tui/components/TipBanner.tsx +115 -0
- package/src/tui/components/TodoItem.tsx +208 -0
- package/src/tui/components/TodoPanel.tsx +455 -0
- package/src/tui/components/Tools/ApprovalQueue.tsx +407 -0
- package/src/tui/components/Tools/OptionSelector.tsx +160 -0
- package/src/tui/components/Tools/PermissionDialog.tsx +286 -0
- package/src/tui/components/Tools/ToolParams.tsx +483 -0
- package/src/tui/components/Tools/ToolsPanel.tsx +178 -0
- package/src/tui/components/Tools/__tests__/PermissionDialog.test.tsx +510 -0
- package/src/tui/components/Tools/__tests__/ToolParams.test.tsx +432 -0
- package/src/tui/components/Tools/index.ts +21 -0
- package/src/tui/components/TrustPrompt.tsx +279 -0
- package/src/tui/components/UpdateBanner.tsx +166 -0
- package/src/tui/components/VimModeIndicator.tsx +112 -0
- package/src/tui/components/backtrack/BacktrackControls.tsx +402 -0
- package/src/tui/components/backtrack/index.ts +13 -0
- package/src/tui/components/common/AutoApprovalStatus.tsx +251 -0
- package/src/tui/components/common/CostWarning.tsx +294 -0
- package/src/tui/components/common/DynamicShortcutHints.tsx +209 -0
- package/src/tui/components/common/EnhancedLoadingIndicator.tsx +305 -0
- package/src/tui/components/common/ErrorBoundary.tsx +140 -0
- package/src/tui/components/common/GradientText.tsx +224 -0
- package/src/tui/components/common/HotkeyHelpModal.tsx +193 -0
- package/src/tui/components/common/HotkeyHints.tsx +70 -0
- package/src/tui/components/common/MaxSizedBox.tsx +354 -0
- package/src/tui/components/common/NewMessagesBadge.tsx +65 -0
- package/src/tui/components/common/ProtectedFileLegend.tsx +89 -0
- package/src/tui/components/common/ScrollIndicator.tsx +160 -0
- package/src/tui/components/common/Spinner.tsx +342 -0
- package/src/tui/components/common/StreamingIndicator.tsx +316 -0
- package/src/tui/components/common/VirtualizedList/VirtualizedList.tsx +428 -0
- package/src/tui/components/common/VirtualizedList/hooks/index.ts +19 -0
- package/src/tui/components/common/VirtualizedList/hooks/useBatchedScroll.ts +64 -0
- package/src/tui/components/common/VirtualizedList/hooks/useScrollAnchor.ts +290 -0
- package/src/tui/components/common/VirtualizedList/hooks/useVirtualization.ts +340 -0
- package/src/tui/components/common/VirtualizedList/index.ts +30 -0
- package/src/tui/components/common/VirtualizedList/types.ts +107 -0
- package/src/tui/components/common/__tests__/NewMessagesBadge.test.tsx +74 -0
- package/src/tui/components/common/__tests__/ScrollIndicator.test.tsx +193 -0
- package/src/tui/components/common/index.ts +110 -0
- package/src/tui/components/index.ts +79 -0
- package/src/tui/components/session/CheckpointPanel.tsx +323 -0
- package/src/tui/components/session/RollbackDialog.tsx +169 -0
- package/src/tui/components/session/SessionItem.tsx +136 -0
- package/src/tui/components/session/SessionListPanel.tsx +252 -0
- package/src/tui/components/session/SessionPicker.tsx +449 -0
- package/src/tui/components/session/SessionPreview.tsx +240 -0
- package/src/tui/components/session/__tests__/session.test.tsx +408 -0
- package/src/tui/components/session/index.ts +28 -0
- package/src/tui/components/session/types.ts +116 -0
- package/src/tui/components/theme/__tests__/tokens.test.ts +471 -0
- package/src/tui/components/theme/index.ts +227 -0
- package/src/tui/components/theme/tokens.ts +484 -0
- package/src/tui/config/defaults.ts +134 -0
- package/src/tui/config/index.ts +17 -0
- package/src/tui/context/AnimationContext.tsx +284 -0
- package/src/tui/context/AppContext.tsx +349 -0
- package/src/tui/context/BracketedPasteContext.tsx +372 -0
- package/src/tui/context/LspContext.tsx +192 -0
- package/src/tui/context/McpContext.tsx +325 -0
- package/src/tui/context/MessagesContext.tsx +870 -0
- package/src/tui/context/OverflowContext.tsx +213 -0
- package/src/tui/context/RateLimitContext.tsx +108 -0
- package/src/tui/context/ResilienceContext.tsx +275 -0
- package/src/tui/context/RootProvider.tsx +136 -0
- package/src/tui/context/ScrollContext.tsx +331 -0
- package/src/tui/context/ToolsContext.tsx +702 -0
- package/src/tui/context/__tests__/BracketedPasteContext.test.tsx +416 -0
- package/src/tui/context/index.ts +140 -0
- package/src/tui/enterprise-integration.ts +282 -0
- package/src/tui/hooks/__tests__/useBacktrack.test.tsx +138 -0
- package/src/tui/hooks/__tests__/useBracketedPaste.test.tsx +222 -0
- package/src/tui/hooks/__tests__/useCopyMode.test.tsx +336 -0
- package/src/tui/hooks/__tests__/useHotkeys.ctrl-input.test.tsx +96 -0
- package/src/tui/hooks/__tests__/useHotkeys.test.tsx +454 -0
- package/src/tui/hooks/__tests__/useInputHistory.test.tsx +660 -0
- package/src/tui/hooks/__tests__/useLineBuffer.test.ts +295 -0
- package/src/tui/hooks/__tests__/useModeController.test.ts +137 -0
- package/src/tui/hooks/__tests__/useModeShortcuts.test.tsx +142 -0
- package/src/tui/hooks/__tests__/useScrollController.test.ts +464 -0
- package/src/tui/hooks/__tests__/useVim.test.tsx +531 -0
- package/src/tui/hooks/index.ts +252 -0
- package/src/tui/hooks/useAgentLoop.ts +712 -0
- package/src/tui/hooks/useAlternateBuffer.ts +398 -0
- package/src/tui/hooks/useAnimatedScrollbar.ts +241 -0
- package/src/tui/hooks/useBacktrack.ts +443 -0
- package/src/tui/hooks/useBracketedPaste.ts +104 -0
- package/src/tui/hooks/useCollapsible.ts +240 -0
- package/src/tui/hooks/useCopyMode.ts +382 -0
- package/src/tui/hooks/useCostSummary.ts +75 -0
- package/src/tui/hooks/useDesktopNotification.ts +414 -0
- package/src/tui/hooks/useDiffMode.ts +44 -0
- package/src/tui/hooks/useFileChangeStats.ts +110 -0
- package/src/tui/hooks/useFileSuggestions.ts +284 -0
- package/src/tui/hooks/useFlickerDetector.ts +250 -0
- package/src/tui/hooks/useGitStatus.ts +200 -0
- package/src/tui/hooks/useHotkeys.ts +579 -0
- package/src/tui/hooks/useImagePaste.ts +114 -0
- package/src/tui/hooks/useInputHighlight.ts +145 -0
- package/src/tui/hooks/useInputHistory.ts +246 -0
- package/src/tui/hooks/useKeyboardScroll.ts +209 -0
- package/src/tui/hooks/useLineBuffer.ts +356 -0
- package/src/tui/hooks/useMentionAutocomplete.ts +235 -0
- package/src/tui/hooks/useModeController.ts +167 -0
- package/src/tui/hooks/useModeShortcuts.ts +196 -0
- package/src/tui/hooks/usePermissionHandler.ts +146 -0
- package/src/tui/hooks/usePersistence.ts +480 -0
- package/src/tui/hooks/usePersistenceShortcuts.ts +225 -0
- package/src/tui/hooks/usePlaceholderRotation.ts +143 -0
- package/src/tui/hooks/useProviderStatus.ts +270 -0
- package/src/tui/hooks/useRateLimitStatus.ts +90 -0
- package/src/tui/hooks/useScreenReader.ts +315 -0
- package/src/tui/hooks/useScrollController.ts +450 -0
- package/src/tui/hooks/useScrollEventBatcher.ts +185 -0
- package/src/tui/hooks/useSidebarPanelData.ts +115 -0
- package/src/tui/hooks/useSmoothScroll.ts +202 -0
- package/src/tui/hooks/useSnapshots.ts +300 -0
- package/src/tui/hooks/useStateAndRef.ts +50 -0
- package/src/tui/hooks/useTerminalSize.ts +206 -0
- package/src/tui/hooks/useToolApprovalController.ts +91 -0
- package/src/tui/hooks/useVim.ts +334 -0
- package/src/tui/hooks/useWorkspace.ts +56 -0
- package/src/tui/i18n/__tests__/init.test.ts +278 -0
- package/src/tui/i18n/__tests__/language-config.test.ts +199 -0
- package/src/tui/i18n/__tests__/locale-detection.test.ts +250 -0
- package/src/tui/i18n/__tests__/settings-integration.test.ts +262 -0
- package/src/tui/i18n/index.ts +72 -0
- package/src/tui/i18n/init.ts +131 -0
- package/src/tui/i18n/language-config.ts +106 -0
- package/src/tui/i18n/locale-detection.ts +173 -0
- package/src/tui/i18n/settings-integration.ts +557 -0
- package/src/tui/i18n/tui-namespace.ts +538 -0
- package/src/tui/i18n/types.ts +312 -0
- package/src/tui/index.ts +43 -0
- package/src/tui/lsp-integration.ts +409 -0
- package/src/tui/metrics-integration.ts +366 -0
- package/src/tui/plugins.ts +383 -0
- package/src/tui/resilience.ts +342 -0
- package/src/tui/sandbox-integration.ts +317 -0
- package/src/tui/services/clipboard.ts +348 -0
- package/src/tui/services/fuzzy-search.ts +441 -0
- package/src/tui/services/index.ts +72 -0
- package/src/tui/services/markdown-renderer.ts +565 -0
- package/src/tui/services/open-external.ts +247 -0
- package/src/tui/services/syntax-highlighter.ts +483 -0
- package/src/tui/slash-commands.ts +12 -0
- package/src/tui/theme/index.ts +15 -0
- package/src/tui/theme/provider.tsx +206 -0
- package/src/tui/tip-integration.ts +300 -0
- package/src/tui/types/__tests__/ink-extended.test.ts +121 -0
- package/src/tui/types/ink-extended.ts +87 -0
- package/src/tui/utils/__tests__/bracketedPaste.test.ts +231 -0
- package/src/tui/utils/__tests__/heightEstimator.test.ts +157 -0
- package/src/tui/utils/__tests__/text-width.test.ts +158 -0
- package/src/tui/utils/__tests__/textSanitizer.test.ts +266 -0
- package/src/tui/utils/__tests__/ui-sizing.test.ts +169 -0
- package/src/tui/utils/bracketedPaste.ts +107 -0
- package/src/tui/utils/cursor-manager.ts +131 -0
- package/src/tui/utils/detectTerminal.ts +596 -0
- package/src/tui/utils/findLastSafeSplitPoint.ts +92 -0
- package/src/tui/utils/heightEstimator.ts +198 -0
- package/src/tui/utils/index.ts +91 -0
- package/src/tui/utils/isNarrowWidth.ts +52 -0
- package/src/tui/utils/stdoutGuard.ts +90 -0
- package/src/tui/utils/synchronized-update.ts +70 -0
- package/src/tui/utils/text-width.ts +225 -0
- package/src/tui/utils/textSanitizer.ts +225 -0
- package/src/tui/utils/textUtils.ts +114 -0
- package/src/tui/utils/ui-sizing.ts +192 -0
- package/src/tui-blessed/app.ts +160 -0
- package/src/tui-blessed/index.ts +2 -0
- package/src/tui-blessed/neo-blessed.d.ts +6 -0
- package/src/tui-blessed/test.ts +21 -0
- package/src/tui-blessed/types.ts +14 -0
- package/src/utils/icons.ts +130 -0
- package/src/utils/index.ts +33 -0
- package/src/utils/resume-hint.ts +86 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Checker
|
|
3
|
+
*
|
|
4
|
+
* Security module for validating command permissions against defined policies.
|
|
5
|
+
* Provides file access, network access, and general resource permission checks.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/security/permission-checker
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// T052: Types
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Security policy for command execution
|
|
18
|
+
*
|
|
19
|
+
* Defines what resources a command is allowed to access.
|
|
20
|
+
* All fields are optional - if not specified, the resource type is unrestricted.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const policy: CommandSecurityPolicy = {
|
|
25
|
+
* allowedPaths: ['./src/**', './config/**'],
|
|
26
|
+
* deniedPaths: ['**\/.env', '**\/secrets/**'],
|
|
27
|
+
* allowedHosts: ['api.example.com', 'localhost'],
|
|
28
|
+
* deniedHosts: ['*.evil.com'],
|
|
29
|
+
* requiresAuth: true,
|
|
30
|
+
* maxExecutionTime: 30000,
|
|
31
|
+
* };
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export interface CommandSecurityPolicy {
|
|
35
|
+
/** Allowed file paths (glob patterns or absolute paths) */
|
|
36
|
+
readonly allowedPaths?: readonly string[];
|
|
37
|
+
/** Blocked file paths (glob patterns or absolute paths) - takes precedence over allowedPaths */
|
|
38
|
+
readonly deniedPaths?: readonly string[];
|
|
39
|
+
/** Allowed network hosts (domain names or IP addresses, supports wildcards) */
|
|
40
|
+
readonly allowedHosts?: readonly string[];
|
|
41
|
+
/** Blocked network hosts - takes precedence over allowedHosts */
|
|
42
|
+
readonly deniedHosts?: readonly string[];
|
|
43
|
+
/** Whether the command requires an authenticated session */
|
|
44
|
+
readonly requiresAuth?: boolean;
|
|
45
|
+
/** Maximum execution time in milliseconds */
|
|
46
|
+
readonly maxExecutionTime?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Result of a permission check
|
|
51
|
+
*
|
|
52
|
+
* Discriminated union that indicates whether access is allowed or denied.
|
|
53
|
+
* When denied, includes the reason and optional suggestion for resolution.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = checker.checkFileAccess('/etc/passwd', policy);
|
|
58
|
+
* if (!result.allowed) {
|
|
59
|
+
* console.error(`Denied: ${result.reason}`);
|
|
60
|
+
* if (result.suggestion) {
|
|
61
|
+
* console.log(`Suggestion: ${result.suggestion}`);
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export type PermissionResult =
|
|
67
|
+
| { readonly allowed: true }
|
|
68
|
+
| { readonly allowed: false; readonly reason: string; readonly suggestion?: string };
|
|
69
|
+
|
|
70
|
+
// =============================================================================
|
|
71
|
+
// T052: PermissionChecker Class
|
|
72
|
+
// =============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* PermissionChecker - Validates command permissions against security policies
|
|
76
|
+
*
|
|
77
|
+
* Provides methods to check if a command is allowed to access specific resources
|
|
78
|
+
* based on the defined security policy.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const checker = new PermissionChecker();
|
|
83
|
+
*
|
|
84
|
+
* const policy: CommandSecurityPolicy = {
|
|
85
|
+
* allowedPaths: ['./src/**'],
|
|
86
|
+
* deniedPaths: ['**\/.env'],
|
|
87
|
+
* };
|
|
88
|
+
*
|
|
89
|
+
* const result = checker.checkFileAccess('./src/app.ts', policy);
|
|
90
|
+
* if (result.allowed) {
|
|
91
|
+
* // Proceed with file access
|
|
92
|
+
* } else {
|
|
93
|
+
* console.error(result.reason);
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export class PermissionChecker {
|
|
98
|
+
/**
|
|
99
|
+
* The base directory for resolving relative paths
|
|
100
|
+
*/
|
|
101
|
+
private readonly baseDir: string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a new PermissionChecker
|
|
105
|
+
*
|
|
106
|
+
* @param baseDir - Base directory for resolving relative paths (defaults to cwd)
|
|
107
|
+
*/
|
|
108
|
+
constructor(baseDir?: string) {
|
|
109
|
+
this.baseDir = baseDir ?? process.cwd();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if file access is allowed by the policy
|
|
114
|
+
*
|
|
115
|
+
* Validates a file path against the allowed and denied path patterns
|
|
116
|
+
* in the security policy.
|
|
117
|
+
*
|
|
118
|
+
* @param filePath - The file path to check (absolute or relative)
|
|
119
|
+
* @param policy - The security policy to check against
|
|
120
|
+
* @returns PermissionResult indicating if access is allowed
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const result = checker.checkFileAccess('./config.json', {
|
|
125
|
+
* allowedPaths: ['./config/**'],
|
|
126
|
+
* deniedPaths: ['./config/secrets/**'],
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
checkFileAccess(filePath: string, policy: CommandSecurityPolicy): PermissionResult {
|
|
131
|
+
if (!filePath) {
|
|
132
|
+
return {
|
|
133
|
+
allowed: false,
|
|
134
|
+
reason: "File path is required",
|
|
135
|
+
suggestion: "Provide a valid file path",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Normalize the path for consistent matching
|
|
140
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
141
|
+
|
|
142
|
+
// Check denied paths first (they take precedence)
|
|
143
|
+
if (policy.deniedPaths && policy.deniedPaths.length > 0) {
|
|
144
|
+
for (const pattern of policy.deniedPaths) {
|
|
145
|
+
if (this.matchesPattern(normalizedPath, pattern)) {
|
|
146
|
+
return {
|
|
147
|
+
allowed: false,
|
|
148
|
+
reason: `Path '${filePath}' is blocked by security policy`,
|
|
149
|
+
suggestion: "Check the command's deniedPaths configuration",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// If allowedPaths is defined, path must match at least one pattern
|
|
156
|
+
if (policy.allowedPaths && policy.allowedPaths.length > 0) {
|
|
157
|
+
const isAllowed = policy.allowedPaths.some((pattern) =>
|
|
158
|
+
this.matchesPattern(normalizedPath, pattern)
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (!isAllowed) {
|
|
162
|
+
return {
|
|
163
|
+
allowed: false,
|
|
164
|
+
reason: `Path '${filePath}' is not in allowed paths`,
|
|
165
|
+
suggestion: `Allowed paths: ${policy.allowedPaths.join(", ")}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { allowed: true };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if network access is allowed by the policy
|
|
175
|
+
*
|
|
176
|
+
* Validates a host against the allowed and denied host patterns
|
|
177
|
+
* in the security policy.
|
|
178
|
+
*
|
|
179
|
+
* @param host - The host to check (domain name or IP address)
|
|
180
|
+
* @param policy - The security policy to check against
|
|
181
|
+
* @returns PermissionResult indicating if access is allowed
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const result = checker.checkNetworkAccess('api.example.com', {
|
|
186
|
+
* allowedHosts: ['*.example.com', 'localhost'],
|
|
187
|
+
* deniedHosts: ['internal.example.com'],
|
|
188
|
+
* });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
checkNetworkAccess(host: string, policy: CommandSecurityPolicy): PermissionResult {
|
|
192
|
+
if (!host) {
|
|
193
|
+
return {
|
|
194
|
+
allowed: false,
|
|
195
|
+
reason: "Host is required",
|
|
196
|
+
suggestion: "Provide a valid host name or IP address",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Normalize the host (lowercase, trim)
|
|
201
|
+
const normalizedHost = host.toLowerCase().trim();
|
|
202
|
+
|
|
203
|
+
// Check denied hosts first (they take precedence)
|
|
204
|
+
if (policy.deniedHosts && policy.deniedHosts.length > 0) {
|
|
205
|
+
for (const pattern of policy.deniedHosts) {
|
|
206
|
+
if (this.matchesHostPattern(normalizedHost, pattern)) {
|
|
207
|
+
return {
|
|
208
|
+
allowed: false,
|
|
209
|
+
reason: `Host '${host}' is blocked by security policy`,
|
|
210
|
+
suggestion: "Check the command's deniedHosts configuration",
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// If allowedHosts is defined, host must match at least one pattern
|
|
217
|
+
if (policy.allowedHosts && policy.allowedHosts.length > 0) {
|
|
218
|
+
const isAllowed = policy.allowedHosts.some((pattern) =>
|
|
219
|
+
this.matchesHostPattern(normalizedHost, pattern)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
if (!isAllowed) {
|
|
223
|
+
return {
|
|
224
|
+
allowed: false,
|
|
225
|
+
reason: `Host '${host}' is not in allowed hosts`,
|
|
226
|
+
suggestion: `Allowed hosts: ${policy.allowedHosts.join(", ")}`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return { allowed: true };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check if a general action on a resource is allowed
|
|
236
|
+
*
|
|
237
|
+
* Generic permission check for custom resource types not covered
|
|
238
|
+
* by file or network access.
|
|
239
|
+
*
|
|
240
|
+
* @param action - The action being performed (e.g., 'read', 'write', 'execute')
|
|
241
|
+
* @param resource - The resource identifier
|
|
242
|
+
* @param policy - The security policy to check against
|
|
243
|
+
* @returns PermissionResult indicating if the action is allowed
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const result = checker.checkPolicy('execute', 'shell:rm', {
|
|
248
|
+
* requiresAuth: true,
|
|
249
|
+
* });
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
checkPolicy(action: string, resource: string, policy: CommandSecurityPolicy): PermissionResult {
|
|
253
|
+
if (!action) {
|
|
254
|
+
return {
|
|
255
|
+
allowed: false,
|
|
256
|
+
reason: "Action is required",
|
|
257
|
+
suggestion: "Specify the action being performed",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!resource) {
|
|
262
|
+
return {
|
|
263
|
+
allowed: false,
|
|
264
|
+
reason: "Resource is required",
|
|
265
|
+
suggestion: "Specify the resource being accessed",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Handle file-like resources
|
|
270
|
+
if (resource.startsWith("file:")) {
|
|
271
|
+
const filePath = resource.slice(5); // Remove 'file:' prefix
|
|
272
|
+
return this.checkFileAccess(filePath, policy);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Handle network-like resources
|
|
276
|
+
if (resource.startsWith("http://") || resource.startsWith("https://")) {
|
|
277
|
+
try {
|
|
278
|
+
const url = new URL(resource);
|
|
279
|
+
return this.checkNetworkAccess(url.host, policy);
|
|
280
|
+
} catch {
|
|
281
|
+
return {
|
|
282
|
+
allowed: false,
|
|
283
|
+
reason: `Invalid URL: ${resource}`,
|
|
284
|
+
suggestion: "Provide a valid URL",
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Handle host-like resources
|
|
290
|
+
if (resource.startsWith("host:")) {
|
|
291
|
+
const host = resource.slice(5); // Remove 'host:' prefix
|
|
292
|
+
return this.checkNetworkAccess(host, policy);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// For other resources, allow by default (specific checks should be added as needed)
|
|
296
|
+
return { allowed: true };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Normalize a file path for consistent matching
|
|
301
|
+
*
|
|
302
|
+
* @param filePath - The path to normalize
|
|
303
|
+
* @returns Normalized path using forward slashes
|
|
304
|
+
*/
|
|
305
|
+
private normalizePath(filePath: string): string {
|
|
306
|
+
// Resolve relative paths against base directory
|
|
307
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
308
|
+
? filePath
|
|
309
|
+
: path.resolve(this.baseDir, filePath);
|
|
310
|
+
|
|
311
|
+
// Normalize path separators to forward slashes for glob matching
|
|
312
|
+
return absolutePath.replace(/\\/g, "/");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Check if a path matches a glob pattern
|
|
317
|
+
*
|
|
318
|
+
* @param filePath - The normalized file path
|
|
319
|
+
* @param pattern - The glob pattern to match against
|
|
320
|
+
* @returns true if the path matches the pattern
|
|
321
|
+
*/
|
|
322
|
+
private matchesPattern(filePath: string, pattern: string): boolean {
|
|
323
|
+
// Normalize the pattern path separators
|
|
324
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
325
|
+
|
|
326
|
+
// Handle patterns that start with ** (match from any point in the path)
|
|
327
|
+
if (normalizedPattern.startsWith("**/")) {
|
|
328
|
+
// For **/ patterns, we want to match from any point, not just the start
|
|
329
|
+
return this.globMatch(filePath, normalizedPattern, process.platform === "win32");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// If pattern is not absolute and doesn't start with **, resolve it against base directory
|
|
333
|
+
const resolvedPattern = path.isAbsolute(normalizedPattern)
|
|
334
|
+
? normalizedPattern
|
|
335
|
+
: path.resolve(this.baseDir, normalizedPattern).replace(/\\/g, "/");
|
|
336
|
+
|
|
337
|
+
// Use simple glob matching implementation
|
|
338
|
+
return this.globMatch(filePath, resolvedPattern, process.platform === "win32");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Simple glob pattern matching
|
|
343
|
+
*
|
|
344
|
+
* Supports:
|
|
345
|
+
* - ** for matching any number of directories
|
|
346
|
+
* - * for matching any characters within a path segment
|
|
347
|
+
* - ? for matching a single character
|
|
348
|
+
*
|
|
349
|
+
* @param filePath - The file path to test
|
|
350
|
+
* @param pattern - The glob pattern
|
|
351
|
+
* @param ignoreCase - Whether to ignore case (for Windows)
|
|
352
|
+
* @returns true if the path matches the pattern
|
|
353
|
+
*/
|
|
354
|
+
private globMatch(filePath: string, pattern: string, ignoreCase: boolean): boolean {
|
|
355
|
+
// Normalize case if needed
|
|
356
|
+
const normalizedPath = ignoreCase ? filePath.toLowerCase() : filePath;
|
|
357
|
+
const normalizedGlob = ignoreCase ? pattern.toLowerCase() : pattern;
|
|
358
|
+
|
|
359
|
+
// Handle ** at the beginning specially - it should match from any point
|
|
360
|
+
if (normalizedGlob.startsWith("**/")) {
|
|
361
|
+
const suffix = normalizedGlob.slice(3); // Remove **/
|
|
362
|
+
// Try matching from any position in the path
|
|
363
|
+
const segments = normalizedPath.split("/");
|
|
364
|
+
for (let i = 0; i < segments.length; i++) {
|
|
365
|
+
const subPath = segments.slice(i).join("/");
|
|
366
|
+
if (this.simpleGlobMatch(subPath, suffix, ignoreCase)) {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Handle /** at the end - it should match the path and anything under it
|
|
374
|
+
if (normalizedGlob.endsWith("/**")) {
|
|
375
|
+
const prefix = normalizedGlob.slice(0, -3); // Remove /**
|
|
376
|
+
// The path should start with the prefix (or equal it)
|
|
377
|
+
if (normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
// Also try the regular glob match
|
|
381
|
+
return this.simpleGlobMatch(normalizedPath, normalizedGlob, ignoreCase);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return this.simpleGlobMatch(normalizedPath, normalizedGlob, ignoreCase);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Simple glob to regex matching (non-recursive)
|
|
389
|
+
*
|
|
390
|
+
* @param filePath - The file path to test
|
|
391
|
+
* @param pattern - The glob pattern
|
|
392
|
+
* @param ignoreCase - Whether to ignore case
|
|
393
|
+
* @returns true if the path matches the pattern
|
|
394
|
+
*/
|
|
395
|
+
private simpleGlobMatch(filePath: string, pattern: string, ignoreCase: boolean): boolean {
|
|
396
|
+
const normalizedPath = ignoreCase ? filePath.toLowerCase() : filePath;
|
|
397
|
+
const normalizedGlob = ignoreCase ? pattern.toLowerCase() : pattern;
|
|
398
|
+
|
|
399
|
+
// Convert glob pattern to regex
|
|
400
|
+
const regexPattern = normalizedGlob
|
|
401
|
+
// Escape regex special chars except * and ?
|
|
402
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
403
|
+
// ** matches any path (including slashes)
|
|
404
|
+
.replace(/\*\*/g, "\0GLOBSTAR\0")
|
|
405
|
+
// * matches anything except slash
|
|
406
|
+
.replace(/\*/g, "[^/]*")
|
|
407
|
+
// ? matches single char except slash
|
|
408
|
+
.replace(/\?/g, "[^/]")
|
|
409
|
+
// Restore globstar
|
|
410
|
+
.replace(/\0GLOBSTAR\0/g, ".*");
|
|
411
|
+
|
|
412
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
413
|
+
return regex.test(normalizedPath);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Check if a host matches a wildcard pattern
|
|
418
|
+
*
|
|
419
|
+
* Supports:
|
|
420
|
+
* - Exact match: 'example.com'
|
|
421
|
+
* - Wildcard prefix: '*.example.com'
|
|
422
|
+
* - Full wildcard: '*'
|
|
423
|
+
*
|
|
424
|
+
* @param host - The normalized host
|
|
425
|
+
* @param pattern - The pattern to match against
|
|
426
|
+
* @returns true if the host matches the pattern
|
|
427
|
+
*/
|
|
428
|
+
private matchesHostPattern(host: string, pattern: string): boolean {
|
|
429
|
+
const normalizedPattern = pattern.toLowerCase().trim();
|
|
430
|
+
|
|
431
|
+
// Full wildcard matches everything
|
|
432
|
+
if (normalizedPattern === "*") {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Wildcard prefix pattern (*.example.com)
|
|
437
|
+
if (normalizedPattern.startsWith("*.")) {
|
|
438
|
+
const suffix = normalizedPattern.slice(2); // Remove '*.'
|
|
439
|
+
// Match the exact suffix or any subdomain
|
|
440
|
+
return host === suffix || host.endsWith(`.${suffix}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Exact match
|
|
444
|
+
return host === normalizedPattern;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Create a PermissionChecker with default configuration
|
|
450
|
+
*
|
|
451
|
+
* @param baseDir - Optional base directory for path resolution
|
|
452
|
+
* @returns Configured PermissionChecker instance
|
|
453
|
+
*/
|
|
454
|
+
export function createPermissionChecker(baseDir?: string): PermissionChecker {
|
|
455
|
+
return new PermissionChecker(baseDir);
|
|
456
|
+
}
|