@build-astron-co/nimbus 0.2.0 → 0.4.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/bin/nimbus +26 -10
- package/bin/nimbus.cmd +41 -0
- package/bin/nimbus.mjs +70 -0
- package/completions/nimbus.bash +38 -0
- package/completions/nimbus.fish +48 -0
- package/completions/nimbus.zsh +81 -0
- package/dist/src/agent/compaction-agent.js +215 -0
- package/dist/src/agent/context-manager.js +385 -0
- package/dist/src/agent/context.js +322 -0
- package/dist/src/agent/deploy-preview.js +395 -0
- package/dist/src/agent/expand-files.js +95 -0
- package/dist/src/agent/index.js +18 -0
- package/dist/src/agent/loop.js +1535 -0
- package/dist/src/agent/modes.js +347 -0
- package/dist/src/agent/permissions.js +396 -0
- package/dist/src/agent/subagents/base.js +67 -0
- package/dist/src/agent/subagents/cost.js +45 -0
- package/dist/src/agent/subagents/explore.js +36 -0
- package/dist/src/agent/subagents/general.js +41 -0
- package/dist/src/agent/subagents/index.js +88 -0
- package/dist/src/agent/subagents/infra.js +52 -0
- package/dist/src/agent/subagents/security.js +60 -0
- package/dist/src/agent/system-prompt.js +860 -0
- package/dist/src/app.js +152 -0
- package/dist/src/audit/activity-log.js +209 -0
- package/dist/src/audit/compliance-checker.js +419 -0
- package/dist/src/audit/cost-tracker.js +231 -0
- package/dist/src/audit/index.js +10 -0
- package/dist/src/audit/security-scanner.js +490 -0
- package/dist/src/auth/guard.js +64 -0
- package/dist/src/auth/index.js +19 -0
- package/dist/src/auth/keychain.js +79 -0
- package/dist/src/auth/oauth.js +389 -0
- package/dist/src/auth/providers.js +415 -0
- package/dist/src/auth/sso.js +87 -0
- package/dist/src/auth/store.js +424 -0
- package/dist/src/auth/types.js +5 -0
- package/dist/src/cli/index.js +8 -0
- package/dist/src/cli/init.js +1048 -0
- package/dist/src/cli/openapi-spec.js +346 -0
- package/dist/src/cli/run.js +505 -0
- package/dist/src/cli/serve-auth.js +56 -0
- package/dist/src/cli/serve.js +432 -0
- package/dist/src/cli/web.js +50 -0
- package/dist/src/cli.js +1574 -0
- package/dist/src/clients/core-engine-client.js +156 -0
- package/dist/src/clients/enterprise-client.js +246 -0
- package/dist/src/clients/generator-client.js +219 -0
- package/dist/src/clients/git-client.js +367 -0
- package/dist/src/clients/github-client.js +229 -0
- package/dist/src/clients/helm-client.js +299 -0
- package/dist/src/clients/index.js +18 -0
- package/dist/src/clients/k8s-client.js +270 -0
- package/dist/src/clients/llm-client.js +119 -0
- package/dist/src/clients/rest-client.js +104 -0
- package/dist/src/clients/service-discovery.js +35 -0
- package/dist/src/clients/terraform-client.js +302 -0
- package/dist/src/clients/tools-client.js +1227 -0
- package/dist/src/clients/ws-client.js +93 -0
- package/dist/src/commands/alias.js +91 -0
- package/dist/src/commands/analyze/index.js +313 -0
- package/dist/src/commands/apply/helm.js +375 -0
- package/dist/src/commands/apply/index.js +176 -0
- package/dist/src/commands/apply/k8s.js +350 -0
- package/dist/src/commands/apply/terraform.js +465 -0
- package/dist/src/commands/ask.js +137 -0
- package/dist/src/commands/audit/index.js +322 -0
- package/dist/src/commands/auth-cloud.js +345 -0
- package/dist/src/commands/auth-list.js +112 -0
- package/dist/src/commands/auth-profile.js +104 -0
- package/dist/src/commands/auth-refresh.js +161 -0
- package/dist/src/commands/auth-status.js +122 -0
- package/dist/src/commands/aws/ec2.js +402 -0
- package/dist/src/commands/aws/iam.js +304 -0
- package/dist/src/commands/aws/index.js +108 -0
- package/dist/src/commands/aws/lambda.js +317 -0
- package/dist/src/commands/aws/rds.js +345 -0
- package/dist/src/commands/aws/s3.js +346 -0
- package/dist/src/commands/aws/vpc.js +302 -0
- package/dist/src/commands/aws-discover.js +413 -0
- package/dist/src/commands/aws-terraform.js +618 -0
- package/dist/src/commands/azure/aks.js +305 -0
- package/dist/src/commands/azure/functions.js +200 -0
- package/dist/src/commands/azure/index.js +93 -0
- package/dist/src/commands/azure/storage.js +378 -0
- package/dist/src/commands/azure/vm.js +291 -0
- package/dist/src/commands/billing/index.js +224 -0
- package/dist/src/commands/chat.js +259 -0
- package/dist/src/commands/completions.js +255 -0
- package/dist/src/commands/config.js +291 -0
- package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
- package/dist/src/commands/cost/estimator.js +73 -0
- package/dist/src/commands/cost/index.js +625 -0
- package/dist/src/commands/cost/parsers/terraform.js +234 -0
- package/dist/src/commands/cost/parsers/types.js +4 -0
- package/dist/src/commands/cost/pricing/aws.js +501 -0
- package/dist/src/commands/cost/pricing/azure.js +462 -0
- package/dist/src/commands/cost/pricing/gcp.js +359 -0
- package/dist/src/commands/cost/pricing/index.js +24 -0
- package/dist/src/commands/demo.js +196 -0
- package/dist/src/commands/deploy.js +215 -0
- package/dist/src/commands/doctor.js +1291 -0
- package/dist/src/commands/drift/index.js +674 -0
- package/dist/src/commands/explain.js +235 -0
- package/dist/src/commands/export.js +120 -0
- package/dist/src/commands/feedback.js +319 -0
- package/dist/src/commands/fix.js +263 -0
- package/dist/src/commands/fs/index.js +338 -0
- package/dist/src/commands/gcp/compute.js +266 -0
- package/dist/src/commands/gcp/functions.js +221 -0
- package/dist/src/commands/gcp/gke.js +357 -0
- package/dist/src/commands/gcp/iam.js +295 -0
- package/dist/src/commands/gcp/index.js +105 -0
- package/dist/src/commands/gcp/storage.js +232 -0
- package/dist/src/commands/generate-helm.js +1026 -0
- package/dist/src/commands/generate-k8s.js +1263 -0
- package/dist/src/commands/generate-terraform.js +1058 -0
- package/dist/src/commands/gh/index.js +663 -0
- package/dist/src/commands/git/index.js +1208 -0
- package/dist/src/commands/helm/index.js +985 -0
- package/dist/src/commands/help.js +639 -0
- package/dist/src/commands/history.js +120 -0
- package/dist/src/commands/import.js +782 -0
- package/dist/src/commands/incident.js +144 -0
- package/dist/src/commands/index.js +109 -0
- package/dist/src/commands/init.js +955 -0
- package/dist/src/commands/k8s/index.js +979 -0
- package/dist/src/commands/login.js +588 -0
- package/dist/src/commands/logout.js +61 -0
- package/dist/src/commands/logs.js +160 -0
- package/dist/src/commands/onboarding.js +382 -0
- package/dist/src/commands/pipeline.js +153 -0
- package/dist/src/commands/plan/display.js +216 -0
- package/dist/src/commands/plan/index.js +525 -0
- package/dist/src/commands/plugin.js +325 -0
- package/dist/src/commands/preview.js +356 -0
- package/dist/src/commands/profile.js +297 -0
- package/dist/src/commands/questionnaire.js +1021 -0
- package/dist/src/commands/resume.js +35 -0
- package/dist/src/commands/rollback.js +259 -0
- package/dist/src/commands/rollout.js +74 -0
- package/dist/src/commands/runbook.js +307 -0
- package/dist/src/commands/schedule.js +202 -0
- package/dist/src/commands/status.js +213 -0
- package/dist/src/commands/team/index.js +309 -0
- package/dist/src/commands/team-context.js +200 -0
- package/dist/src/commands/template.js +204 -0
- package/dist/src/commands/tf/index.js +989 -0
- package/dist/src/commands/upgrade.js +515 -0
- package/dist/src/commands/usage/index.js +118 -0
- package/dist/src/commands/version.js +145 -0
- package/dist/src/commands/watch.js +127 -0
- package/dist/src/compat/index.js +2 -0
- package/dist/src/compat/runtime.js +10 -0
- package/dist/src/compat/sqlite.js +144 -0
- package/dist/src/config/index.js +6 -0
- package/dist/src/config/manager.js +469 -0
- package/dist/src/config/mode-store.js +57 -0
- package/dist/src/config/profiles.js +66 -0
- package/dist/src/config/safety-policy.js +251 -0
- package/dist/src/config/schema.js +107 -0
- package/dist/src/config/types.js +311 -0
- package/dist/src/config/workspace-state.js +38 -0
- package/dist/src/context/context-db.js +138 -0
- package/dist/src/demo/index.js +295 -0
- package/dist/src/demo/scenarios/full-journey.js +226 -0
- package/dist/src/demo/scenarios/getting-started.js +124 -0
- package/dist/src/demo/scenarios/helm-release.js +334 -0
- package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
- package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
- package/dist/src/demo/types.js +6 -0
- package/dist/src/engine/cost-estimator.js +334 -0
- package/dist/src/engine/diagram-generator.js +192 -0
- package/dist/src/engine/drift-detector.js +688 -0
- package/dist/src/engine/executor.js +832 -0
- package/dist/src/engine/index.js +39 -0
- package/dist/src/engine/orchestrator.js +436 -0
- package/dist/src/engine/planner.js +616 -0
- package/dist/src/engine/safety.js +609 -0
- package/dist/src/engine/verifier.js +664 -0
- package/dist/src/enterprise/audit.js +241 -0
- package/dist/src/enterprise/auth.js +189 -0
- package/dist/src/enterprise/billing.js +512 -0
- package/dist/src/enterprise/index.js +16 -0
- package/dist/src/enterprise/teams.js +315 -0
- package/dist/src/generator/best-practices.js +1375 -0
- package/dist/src/generator/helm.js +495 -0
- package/dist/src/generator/index.js +11 -0
- package/dist/src/generator/intent-parser.js +420 -0
- package/dist/src/generator/kubernetes.js +773 -0
- package/dist/src/generator/terraform.js +1472 -0
- package/dist/src/history/index.js +6 -0
- package/dist/src/history/manager.js +199 -0
- package/dist/src/history/types.js +6 -0
- package/dist/src/hooks/config.js +318 -0
- package/dist/src/hooks/engine.js +317 -0
- package/dist/src/hooks/index.js +2 -0
- package/dist/src/llm/auth-bridge.js +157 -0
- package/dist/src/llm/circuit-breaker.js +116 -0
- package/dist/src/llm/config-loader.js +172 -0
- package/dist/src/llm/cost-calculator.js +137 -0
- package/dist/src/llm/index.js +7 -0
- package/dist/src/llm/model-aliases.js +99 -0
- package/dist/src/llm/provider-registry.js +57 -0
- package/dist/src/llm/providers/anthropic.js +430 -0
- package/dist/src/llm/providers/bedrock.js +409 -0
- package/dist/src/llm/providers/google.js +344 -0
- package/dist/src/llm/providers/ollama.js +661 -0
- package/dist/src/llm/providers/openai-compatible.js +289 -0
- package/dist/src/llm/providers/openai.js +284 -0
- package/dist/src/llm/providers/openrouter.js +293 -0
- package/dist/src/llm/router.js +844 -0
- package/dist/src/llm/types.js +69 -0
- package/dist/src/lsp/client.js +239 -0
- package/dist/src/lsp/languages.js +95 -0
- package/dist/src/lsp/manager.js +243 -0
- package/dist/src/mcp/client.js +289 -0
- package/dist/src/mcp/index.js +5 -0
- package/dist/src/mcp/manager.js +113 -0
- package/dist/src/nimbus.js +212 -0
- package/dist/src/plugins/index.js +13 -0
- package/dist/src/plugins/loader.js +280 -0
- package/dist/src/plugins/manager.js +282 -0
- package/dist/src/plugins/types.js +23 -0
- package/dist/src/scanners/cicd-scanner.js +230 -0
- package/dist/src/scanners/cloud-scanner.js +415 -0
- package/dist/src/scanners/framework-scanner.js +430 -0
- package/dist/src/scanners/iac-scanner.js +350 -0
- package/dist/src/scanners/index.js +454 -0
- package/dist/src/scanners/language-scanner.js +258 -0
- package/dist/src/scanners/package-manager-scanner.js +252 -0
- package/dist/src/scanners/types.js +6 -0
- package/dist/src/sessions/manager.js +395 -0
- package/dist/src/sessions/types.js +4 -0
- package/dist/src/sharing/sync.js +238 -0
- package/dist/src/sharing/viewer.js +131 -0
- package/dist/src/snapshots/index.js +1 -0
- package/dist/src/snapshots/manager.js +432 -0
- package/dist/src/state/artifacts.js +94 -0
- package/dist/src/state/audit.js +73 -0
- package/dist/src/state/billing.js +126 -0
- package/dist/src/state/checkpoints.js +81 -0
- package/dist/src/state/config.js +58 -0
- package/dist/src/state/conversations.js +7 -0
- package/dist/src/state/credentials.js +96 -0
- package/dist/src/state/db.js +53 -0
- package/dist/src/state/index.js +23 -0
- package/dist/src/state/messages.js +76 -0
- package/dist/src/state/projects.js +92 -0
- package/dist/src/state/schema.js +233 -0
- package/dist/src/state/sessions.js +79 -0
- package/dist/src/state/teams.js +131 -0
- package/dist/src/telemetry.js +91 -0
- package/dist/src/tools/aws-ops.js +747 -0
- package/dist/src/tools/azure-ops.js +491 -0
- package/dist/src/tools/file-ops.js +451 -0
- package/dist/src/tools/gcp-ops.js +559 -0
- package/dist/src/tools/git-ops.js +557 -0
- package/dist/src/tools/github-ops.js +460 -0
- package/dist/src/tools/helm-ops.js +634 -0
- package/dist/src/tools/index.js +16 -0
- package/dist/src/tools/k8s-ops.js +579 -0
- package/dist/src/tools/schemas/converter.js +129 -0
- package/dist/src/tools/schemas/devops.js +3319 -0
- package/dist/src/tools/schemas/index.js +19 -0
- package/dist/src/tools/schemas/standard.js +966 -0
- package/dist/src/tools/schemas/types.js +409 -0
- package/dist/src/tools/spawn-exec.js +109 -0
- package/dist/src/tools/terraform-ops.js +627 -0
- package/dist/src/types/config.js +1 -0
- package/dist/src/types/drift.js +4 -0
- package/dist/src/types/enterprise.js +5 -0
- package/dist/src/types/index.js +14 -0
- package/dist/src/types/plan.js +1 -0
- package/dist/src/types/request.js +1 -0
- package/dist/src/types/response.js +1 -0
- package/dist/src/types/service.js +1 -0
- package/dist/src/ui/App.js +1672 -0
- package/dist/src/ui/DeployPreview.js +60 -0
- package/dist/src/ui/FileDiffModal.js +108 -0
- package/dist/src/ui/Header.js +46 -0
- package/dist/src/ui/HelpModal.js +9 -0
- package/dist/src/ui/InputBox.js +408 -0
- package/dist/src/ui/MessageList.js +795 -0
- package/dist/src/ui/PermissionPrompt.js +72 -0
- package/dist/src/ui/StatusBar.js +109 -0
- package/dist/src/ui/TerminalPane.js +31 -0
- package/dist/src/ui/ToolCallDisplay.js +303 -0
- package/dist/src/ui/TreePane.js +83 -0
- package/dist/src/ui/chat-ui.js +721 -0
- package/dist/src/ui/index.js +11 -0
- package/dist/src/ui/ink/index.js +1325 -0
- package/dist/src/ui/streaming.js +137 -0
- package/dist/src/ui/theme.js +78 -0
- package/dist/src/ui/types.js +7 -0
- package/dist/src/utils/analytics.js +61 -0
- package/dist/src/utils/cost-warning.js +25 -0
- package/dist/src/utils/env.js +42 -0
- package/dist/src/utils/errors.js +54 -0
- package/dist/src/utils/event-bus.js +22 -0
- package/dist/src/utils/index.js +16 -0
- package/dist/src/utils/logger.js +150 -0
- package/dist/src/utils/rate-limiter.js +90 -0
- package/dist/src/utils/service-auth.js +36 -0
- package/dist/src/utils/validation.js +39 -0
- package/dist/src/version.js +3 -0
- package/dist/src/watcher/index.js +192 -0
- package/dist/src/wizard/approval.js +275 -0
- package/dist/src/wizard/index.js +13 -0
- package/dist/src/wizard/prompts.js +273 -0
- package/dist/src/wizard/types.js +4 -0
- package/dist/src/wizard/ui.js +453 -0
- package/dist/src/wizard/wizard.js +227 -0
- package/package.json +31 -23
- package/src/__tests__/alias.test.ts +133 -0
- package/src/__tests__/app.test.ts +1 -1
- package/src/__tests__/audit.test.ts +1 -1
- package/src/__tests__/circuit-breaker.test.ts +1 -1
- package/src/__tests__/cli-run.test.ts +237 -1
- package/src/__tests__/compat-sqlite.test.ts +68 -0
- package/src/__tests__/context-manager.test.ts +131 -1
- package/src/__tests__/context.test.ts +1 -1
- package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
- package/src/__tests__/doctor.test.ts +48 -0
- package/src/__tests__/enterprise.test.ts +1 -1
- package/src/__tests__/export.test.ts +236 -0
- package/src/__tests__/gap-11-18-20.test.ts +958 -0
- package/src/__tests__/generator.test.ts +1 -1
- package/src/__tests__/helm-streaming.test.ts +127 -0
- package/src/__tests__/hooks.test.ts +1 -1
- package/src/__tests__/incident.test.ts +179 -0
- package/src/__tests__/init.test.ts +55 -4
- package/src/__tests__/intent-parser.test.ts +1 -1
- package/src/__tests__/llm-router.test.ts +1 -1
- package/src/__tests__/logs.test.ts +107 -0
- package/src/__tests__/loop-errors.test.ts +244 -0
- package/src/__tests__/lsp.test.ts +1 -1
- package/src/__tests__/modes.test.ts +1 -1
- package/src/__tests__/perf-optimizations.test.ts +847 -0
- package/src/__tests__/permissions.test.ts +1 -1
- package/src/__tests__/pipeline.test.ts +50 -0
- package/src/__tests__/polish-phase3.test.ts +340 -0
- package/src/__tests__/profile.test.ts +237 -0
- package/src/__tests__/rollback.test.ts +83 -0
- package/src/__tests__/runbook.test.ts +219 -0
- package/src/__tests__/schedule.test.ts +206 -0
- package/src/__tests__/serve.test.ts +1 -1
- package/src/__tests__/sessions.test.ts +96 -1
- package/src/__tests__/sharing.test.ts +53 -1
- package/src/__tests__/snapshots.test.ts +1 -1
- package/src/__tests__/standalone-migration.test.ts +199 -0
- package/src/__tests__/state-db.test.ts +1 -1
- package/src/__tests__/status.test.ts +158 -0
- package/src/__tests__/stream-with-tools.test.ts +71 -25
- package/src/__tests__/subagents.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +82 -3
- package/src/__tests__/terminal-gap-v2.test.ts +395 -0
- package/src/__tests__/terminal-parity.test.ts +393 -0
- package/src/__tests__/tf-apply.test.ts +187 -0
- package/src/__tests__/tool-converter.test.ts +1 -1
- package/src/__tests__/tool-schemas.test.ts +209 -4
- package/src/__tests__/tools.test.ts +4 -3
- package/src/__tests__/version-json.test.ts +184 -0
- package/src/__tests__/version.test.ts +1 -1
- package/src/__tests__/watch.test.ts +129 -0
- package/src/agent/compaction-agent.ts +40 -1
- package/src/agent/context-manager.ts +67 -3
- package/src/agent/deploy-preview.ts +62 -1
- package/src/agent/expand-files.ts +108 -0
- package/src/agent/loop.ts +1312 -31
- package/src/agent/permissions.ts +51 -4
- package/src/agent/system-prompt.ts +573 -19
- package/src/app.ts +58 -0
- package/src/audit/security-scanner.ts +45 -0
- package/src/auth/keychain.ts +82 -0
- package/src/auth/oauth.ts +15 -5
- package/src/cli/init.ts +378 -5
- package/src/cli/run.ts +407 -16
- package/src/cli/serve.ts +78 -1
- package/src/cli/web.ts +10 -6
- package/src/cli.ts +312 -1
- package/src/clients/service-discovery.ts +30 -25
- package/src/commands/alias.ts +100 -0
- package/src/commands/audit/index.ts +121 -2
- package/src/commands/auth-cloud.ts +113 -0
- package/src/commands/auth-refresh.ts +187 -0
- package/src/commands/aws-discover.ts +144 -251
- package/src/commands/aws-terraform.ts +68 -118
- package/src/commands/chat.ts +9 -3
- package/src/commands/completions.ts +268 -0
- package/src/commands/config.ts +26 -0
- package/src/commands/cost/index.ts +218 -2
- package/src/commands/deploy.ts +260 -0
- package/src/commands/doctor.ts +744 -152
- package/src/commands/drift/index.ts +371 -23
- package/src/commands/export.ts +146 -0
- package/src/commands/generate-k8s.ts +9 -61
- package/src/commands/generate-terraform.ts +191 -449
- package/src/commands/help.ts +212 -36
- package/src/commands/history.ts +8 -1
- package/src/commands/incident.ts +166 -0
- package/src/commands/init.ts +5 -0
- package/src/commands/login.ts +86 -1
- package/src/commands/logs.ts +167 -0
- package/src/commands/onboarding.ts +211 -34
- package/src/commands/pipeline.ts +186 -0
- package/src/commands/plugin.ts +398 -0
- package/src/commands/profile.ts +342 -0
- package/src/commands/questionnaire.ts +0 -98
- package/src/commands/resume.ts +26 -34
- package/src/commands/rollback.ts +315 -0
- package/src/commands/rollout.ts +88 -0
- package/src/commands/runbook.ts +346 -0
- package/src/commands/schedule.ts +236 -0
- package/src/commands/status.ts +252 -0
- package/src/commands/team-context.ts +220 -0
- package/src/commands/template.ts +58 -57
- package/src/commands/tf/index.ts +70 -11
- package/src/commands/upgrade.ts +57 -0
- package/src/commands/version.ts +54 -50
- package/src/commands/watch.ts +153 -0
- package/src/compat/runtime.ts +1 -1
- package/src/compat/sqlite.ts +75 -5
- package/src/config/mode-store.ts +62 -0
- package/src/config/profiles.ts +84 -0
- package/src/config/types.ts +83 -1
- package/src/config/workspace-state.ts +53 -0
- package/src/engine/cost-estimator.ts +52 -10
- package/src/engine/executor.ts +33 -2
- package/src/engine/planner.ts +68 -1
- package/src/generator/terraform.ts +8 -0
- package/src/history/manager.ts +2 -74
- package/src/hooks/engine.ts +5 -4
- package/src/llm/cost-calculator.ts +2 -2
- package/src/llm/providers/anthropic.ts +50 -21
- package/src/llm/router.ts +76 -7
- package/src/lsp/languages.ts +3 -0
- package/src/lsp/manager.ts +21 -5
- package/src/nimbus.ts +37 -18
- package/src/sessions/manager.ts +108 -1
- package/src/sharing/sync.ts +4 -0
- package/src/sharing/viewer.ts +66 -0
- package/src/tools/file-ops.ts +22 -0
- package/src/tools/schemas/devops.ts +3007 -117
- package/src/tools/schemas/standard.ts +5 -1
- package/src/tools/schemas/types.ts +31 -1
- package/src/tools/spawn-exec.ts +148 -0
- package/src/ui/App.tsx +1183 -66
- package/src/ui/DeployPreview.tsx +62 -57
- package/src/ui/FileDiffModal.tsx +162 -0
- package/src/ui/Header.tsx +87 -24
- package/src/ui/HelpModal.tsx +57 -0
- package/src/ui/InputBox.tsx +163 -10
- package/src/ui/MessageList.tsx +487 -40
- package/src/ui/PermissionPrompt.tsx +17 -5
- package/src/ui/StatusBar.tsx +122 -3
- package/src/ui/TerminalPane.tsx +84 -0
- package/src/ui/ToolCallDisplay.tsx +252 -18
- package/src/ui/TreePane.tsx +132 -0
- package/src/ui/chat-ui.ts +41 -44
- package/src/ui/ink/index.ts +771 -38
- package/src/ui/streaming.ts +1 -1
- package/src/ui/theme.ts +104 -0
- package/src/ui/types.ts +18 -0
- package/src/version.ts +1 -1
- package/src/watcher/index.ts +66 -15
- package/src/wizard/types.ts +1 -0
- package/src/wizard/ui.ts +1 -1
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { logger } from './logger';
|
|
2
|
+
const SERVICE_TOKEN_HEADER = 'x-internal-service-token';
|
|
3
|
+
export function validateServiceToken(req) {
|
|
4
|
+
const expectedToken = process.env.INTERNAL_SERVICE_TOKEN;
|
|
5
|
+
if (!expectedToken) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
const providedToken = req.headers.get(SERVICE_TOKEN_HEADER);
|
|
9
|
+
return providedToken === expectedToken;
|
|
10
|
+
}
|
|
11
|
+
export function serviceAuthMiddleware(req) {
|
|
12
|
+
const expectedToken = process.env.INTERNAL_SERVICE_TOKEN;
|
|
13
|
+
if (!expectedToken) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const url = new URL(req.url);
|
|
17
|
+
const path = url.pathname;
|
|
18
|
+
if (path === '/health' || path.startsWith('/swagger') || path === '/api/openapi.json') {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (!path.startsWith('/api/')) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (!validateServiceToken(req)) {
|
|
25
|
+
logger.warn(`Unauthorized service request to ${path}`);
|
|
26
|
+
return Response.json({ success: false, error: 'Unauthorized: invalid or missing service token' }, { status: 401 });
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
export function getServiceAuthHeaders() {
|
|
31
|
+
const token = process.env.INTERNAL_SERVICE_TOKEN;
|
|
32
|
+
if (!token) {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
return { [SERVICE_TOKEN_HEADER]: token };
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ValidationError } from './errors';
|
|
2
|
+
export function validateRequired(value, fieldName, service) {
|
|
3
|
+
if (value === undefined || value === null || value === '') {
|
|
4
|
+
throw new ValidationError(`Field '${fieldName}' is required`, service, { field: fieldName });
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function validateString(value, fieldName, service) {
|
|
8
|
+
if (typeof value !== 'string') {
|
|
9
|
+
throw new ValidationError(`Field '${fieldName}' must be a string`, service, {
|
|
10
|
+
field: fieldName,
|
|
11
|
+
received: typeof value,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
export function validateNumber(value, fieldName, service) {
|
|
17
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
18
|
+
throw new ValidationError(`Field '${fieldName}' must be a number`, service, {
|
|
19
|
+
field: fieldName,
|
|
20
|
+
received: typeof value,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
export function validateBoolean(value, fieldName, service) {
|
|
26
|
+
if (typeof value !== 'boolean') {
|
|
27
|
+
throw new ValidationError(`Field '${fieldName}' must be a boolean`, service, {
|
|
28
|
+
field: fieldName,
|
|
29
|
+
received: typeof value,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
export function validateEnum(value, fieldName, allowedValues, service) {
|
|
35
|
+
if (typeof value !== 'string' || !allowedValues.includes(value)) {
|
|
36
|
+
throw new ValidationError(`Field '${fieldName}' must be one of: ${allowedValues.join(', ')}`, service, { field: fieldName, received: value, allowed: allowedValues });
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem Watcher
|
|
3
|
+
*
|
|
4
|
+
* Watches the project directory for file changes and emits events
|
|
5
|
+
* that the agent loop can respond to (e.g., re-reading modified files).
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { EventEmitter } from 'node:events';
|
|
10
|
+
export class FileWatcher extends EventEmitter {
|
|
11
|
+
rootDir;
|
|
12
|
+
watcher = null;
|
|
13
|
+
/** Circular ring buffer for file-change events (avoids O(n) shift on cap). */
|
|
14
|
+
changesBuffer;
|
|
15
|
+
changesHead = 0; // index of the next write slot
|
|
16
|
+
changesSize = 0; // number of valid entries currently stored
|
|
17
|
+
maxChanges = 100;
|
|
18
|
+
ignorePatterns = [
|
|
19
|
+
'node_modules',
|
|
20
|
+
'.git',
|
|
21
|
+
'dist',
|
|
22
|
+
'coverage',
|
|
23
|
+
'.nimbus',
|
|
24
|
+
'__pycache__',
|
|
25
|
+
'.terraform',
|
|
26
|
+
];
|
|
27
|
+
constructor(rootDir) {
|
|
28
|
+
super();
|
|
29
|
+
this.rootDir = rootDir;
|
|
30
|
+
this.changesBuffer = new Array(this.maxChanges);
|
|
31
|
+
}
|
|
32
|
+
/** Push a change event into the ring buffer (O(1)). */
|
|
33
|
+
pushChange(event) {
|
|
34
|
+
this.changesBuffer[this.changesHead] = event;
|
|
35
|
+
this.changesHead = (this.changesHead + 1) % this.maxChanges;
|
|
36
|
+
if (this.changesSize < this.maxChanges) {
|
|
37
|
+
this.changesSize++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Return all stored change events in chronological order. */
|
|
41
|
+
getOrderedChanges() {
|
|
42
|
+
if (this.changesSize === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const start = this.changesSize < this.maxChanges
|
|
45
|
+
? 0
|
|
46
|
+
: this.changesHead; // oldest slot when buffer is full
|
|
47
|
+
const result = new Array(this.changesSize);
|
|
48
|
+
for (let i = 0; i < this.changesSize; i++) {
|
|
49
|
+
result[i] = this.changesBuffer[(start + i) % this.maxChanges];
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start watching the project directory.
|
|
55
|
+
*/
|
|
56
|
+
start() {
|
|
57
|
+
if (this.watcher) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
this.watcher = fs.watch(this.rootDir, { recursive: true }, (eventType, filename) => {
|
|
62
|
+
if (!filename) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Skip ignored paths
|
|
66
|
+
const parts = filename.split(path.sep);
|
|
67
|
+
if (parts.some(p => this.ignorePatterns.includes(p))) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Skip hidden files
|
|
71
|
+
if (parts.some(p => p.startsWith('.') && p !== '.env')) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const event = {
|
|
75
|
+
type: eventType,
|
|
76
|
+
path: path.join(this.rootDir, filename),
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
};
|
|
79
|
+
this.pushChange(event);
|
|
80
|
+
this.emit('change', event);
|
|
81
|
+
});
|
|
82
|
+
// recursive:true is only supported on macOS and Windows.
|
|
83
|
+
// On Linux, fs.watch silently ignores the flag and only watches the root dir.
|
|
84
|
+
if (process.platform === 'linux') {
|
|
85
|
+
// Watch key subdirectories individually as a workaround
|
|
86
|
+
const subdirs = ['src', 'lib', 'app', 'packages', 'services', 'test', 'tests', 'scripts'];
|
|
87
|
+
for (const sub of subdirs) {
|
|
88
|
+
const subPath = path.join(this.rootDir, sub);
|
|
89
|
+
try {
|
|
90
|
+
if (fs.existsSync(subPath) && fs.statSync(subPath).isDirectory()) {
|
|
91
|
+
fs.watch(subPath, { recursive: false }, (eventType, filename) => {
|
|
92
|
+
if (!filename) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const fullPath = path.join(subPath, filename);
|
|
96
|
+
const relParts = path.relative(this.rootDir, fullPath).split(path.sep);
|
|
97
|
+
if (relParts.some(p => this.ignorePatterns.includes(p))) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (relParts.some(p => p.startsWith('.') && p !== '.env')) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const event = {
|
|
104
|
+
type: eventType,
|
|
105
|
+
path: fullPath,
|
|
106
|
+
timestamp: Date.now(),
|
|
107
|
+
};
|
|
108
|
+
this.pushChange(event);
|
|
109
|
+
this.emit('change', event);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* non-critical */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Watching is non-critical -- some systems don't support recursive watch
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Stop watching.
|
|
125
|
+
*/
|
|
126
|
+
stop() {
|
|
127
|
+
if (this.watcher) {
|
|
128
|
+
this.watcher.close();
|
|
129
|
+
this.watcher = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get recent changes since a timestamp.
|
|
134
|
+
*/
|
|
135
|
+
getChangesSince(timestamp) {
|
|
136
|
+
return this.getOrderedChanges().filter(c => c.timestamp > timestamp);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get a summary of recent changes for the agent context.
|
|
140
|
+
* When `devopsOnly` is true, filters to DevOps-relevant files only:
|
|
141
|
+
* .tf, .yaml, .yml, Dockerfile, docker-compose.*, Jenkinsfile, .github/workflows/*.
|
|
142
|
+
*/
|
|
143
|
+
getSummary(since, devopsOnly = false) {
|
|
144
|
+
const relevant = since ? this.getChangesSince(since) : this.getOrderedChanges();
|
|
145
|
+
if (relevant.length === 0) {
|
|
146
|
+
return '';
|
|
147
|
+
}
|
|
148
|
+
const filtered = devopsOnly
|
|
149
|
+
? relevant.filter(c => {
|
|
150
|
+
const f = c.path.toLowerCase();
|
|
151
|
+
const base = path.basename(f);
|
|
152
|
+
return (f.endsWith('.tf') ||
|
|
153
|
+
f.endsWith('.tfvars') ||
|
|
154
|
+
f.endsWith('.yaml') ||
|
|
155
|
+
f.endsWith('.yml') ||
|
|
156
|
+
base === 'dockerfile' ||
|
|
157
|
+
base.startsWith('dockerfile.') ||
|
|
158
|
+
base.startsWith('docker-compose') ||
|
|
159
|
+
base === 'jenkinsfile' ||
|
|
160
|
+
f.includes('.github/workflows'));
|
|
161
|
+
})
|
|
162
|
+
: relevant;
|
|
163
|
+
if (filtered.length === 0) {
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
const uniquePaths = [...new Set(filtered.map(c => c.path))];
|
|
167
|
+
const lines = uniquePaths.slice(-20).map(p => {
|
|
168
|
+
const rel = path.relative(this.rootDir, p);
|
|
169
|
+
// Categorize for DevOps context
|
|
170
|
+
let category = '';
|
|
171
|
+
const lower = rel.toLowerCase();
|
|
172
|
+
if (lower.endsWith('.tf') || lower.endsWith('.tfvars'))
|
|
173
|
+
category = ' [terraform]';
|
|
174
|
+
else if (lower.endsWith('.yaml') || lower.endsWith('.yml'))
|
|
175
|
+
category = ' [yaml/k8s]';
|
|
176
|
+
else if (lower.includes('dockerfile'))
|
|
177
|
+
category = ' [docker]';
|
|
178
|
+
return ` - ${rel}${category}`;
|
|
179
|
+
});
|
|
180
|
+
return `Files changed externally:\n${lines.join('\n')}`;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear the change history.
|
|
184
|
+
*/
|
|
185
|
+
clearChanges() {
|
|
186
|
+
this.changesHead = 0;
|
|
187
|
+
this.changesSize = 0;
|
|
188
|
+
}
|
|
189
|
+
get isWatching() {
|
|
190
|
+
return this.watcher !== null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval Workflow Component
|
|
3
|
+
*
|
|
4
|
+
* Displays approval prompts for risky operations
|
|
5
|
+
*/
|
|
6
|
+
import { ui } from './ui';
|
|
7
|
+
import { confirm, input } from './prompts';
|
|
8
|
+
/**
|
|
9
|
+
* Display an approval prompt and return the result
|
|
10
|
+
*/
|
|
11
|
+
export async function promptForApproval(config) {
|
|
12
|
+
ui.newLine();
|
|
13
|
+
// Draw approval box
|
|
14
|
+
drawApprovalBox(config);
|
|
15
|
+
// Display risks
|
|
16
|
+
displayRisks(config.risks);
|
|
17
|
+
// Display affected resources if any
|
|
18
|
+
if (config.affectedResources && config.affectedResources.length > 0) {
|
|
19
|
+
displayAffectedResources(config.affectedResources);
|
|
20
|
+
}
|
|
21
|
+
// Display estimated cost if available
|
|
22
|
+
if (config.estimatedCost !== undefined) {
|
|
23
|
+
displayEstimatedCost(config.estimatedCost);
|
|
24
|
+
}
|
|
25
|
+
ui.newLine();
|
|
26
|
+
// Require confirmation for critical operations
|
|
27
|
+
const hasCritical = config.risks.some(r => r.severity === 'critical');
|
|
28
|
+
const hasDestructive = config.operation.toLowerCase().includes('destroy') ||
|
|
29
|
+
config.operation.toLowerCase().includes('delete');
|
|
30
|
+
if (hasCritical || hasDestructive || config.requireConfirmation) {
|
|
31
|
+
const confirmWord = config.confirmationWord || 'yes';
|
|
32
|
+
const userInput = await input({
|
|
33
|
+
message: `Type "${confirmWord}" to confirm:`,
|
|
34
|
+
defaultValue: '',
|
|
35
|
+
});
|
|
36
|
+
if (userInput.toLowerCase() !== confirmWord.toLowerCase()) {
|
|
37
|
+
ui.newLine();
|
|
38
|
+
ui.warning('Operation cancelled - confirmation word did not match');
|
|
39
|
+
return {
|
|
40
|
+
approved: false,
|
|
41
|
+
reason: 'Confirmation word mismatch',
|
|
42
|
+
timestamp: new Date(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Simple confirmation
|
|
48
|
+
const proceed = await confirm({
|
|
49
|
+
message: 'Do you want to proceed with this operation?',
|
|
50
|
+
defaultValue: false,
|
|
51
|
+
});
|
|
52
|
+
if (!proceed) {
|
|
53
|
+
ui.newLine();
|
|
54
|
+
ui.info('Operation cancelled by user');
|
|
55
|
+
return {
|
|
56
|
+
approved: false,
|
|
57
|
+
reason: 'User declined',
|
|
58
|
+
timestamp: new Date(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
ui.newLine();
|
|
63
|
+
ui.success('Operation approved');
|
|
64
|
+
return {
|
|
65
|
+
approved: true,
|
|
66
|
+
approvedBy: process.env.USER || 'unknown',
|
|
67
|
+
timestamp: new Date(),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Draw the approval box header
|
|
72
|
+
*/
|
|
73
|
+
function drawApprovalBox(config) {
|
|
74
|
+
const width = 60;
|
|
75
|
+
const borderColor = getSeverityColor(getHighestSeverity(config.risks));
|
|
76
|
+
ui.print(ui.color(`╔${'═'.repeat(width - 2)}╗`, borderColor));
|
|
77
|
+
// Title
|
|
78
|
+
const titleLine = ` APPROVAL REQUIRED `;
|
|
79
|
+
const padding = Math.floor((width - 2 - titleLine.length) / 2);
|
|
80
|
+
ui.print(ui.color('║', borderColor) +
|
|
81
|
+
' '.repeat(padding) +
|
|
82
|
+
ui.bold(ui.color(titleLine, 'yellow')) +
|
|
83
|
+
' '.repeat(width - 2 - padding - titleLine.length) +
|
|
84
|
+
ui.color('║', borderColor));
|
|
85
|
+
ui.print(ui.color(`╠${'═'.repeat(width - 2)}╣`, borderColor));
|
|
86
|
+
// Operation
|
|
87
|
+
const opLine = ` Operation: ${config.operation}`;
|
|
88
|
+
ui.print(ui.color('║', borderColor) + opLine.padEnd(width - 2) + ui.color('║', borderColor));
|
|
89
|
+
// Environment if available
|
|
90
|
+
if (config.environment) {
|
|
91
|
+
const envLine = ` Environment: ${config.environment}`;
|
|
92
|
+
ui.print(ui.color('║', borderColor) +
|
|
93
|
+
ui.color(envLine.padEnd(width - 2), 'yellow') +
|
|
94
|
+
ui.color('║', borderColor));
|
|
95
|
+
}
|
|
96
|
+
ui.print(ui.color(`╚${'═'.repeat(width - 2)}╝`, borderColor));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Display the list of risks
|
|
100
|
+
*/
|
|
101
|
+
function displayRisks(risks) {
|
|
102
|
+
if (risks.length === 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
ui.newLine();
|
|
106
|
+
ui.print(ui.bold(' Identified Risks:'));
|
|
107
|
+
ui.newLine();
|
|
108
|
+
// Sort by severity
|
|
109
|
+
const sortedRisks = [...risks].sort((a, b) => {
|
|
110
|
+
const order = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
111
|
+
return order[a.severity] - order[b.severity];
|
|
112
|
+
});
|
|
113
|
+
for (const risk of sortedRisks) {
|
|
114
|
+
const icon = getSeverityIcon(risk.severity);
|
|
115
|
+
const color = getSeverityColor(risk.severity);
|
|
116
|
+
const label = `[${risk.severity.toUpperCase()}]`.padEnd(10);
|
|
117
|
+
ui.print(` ${icon} ${ui.color(label, color)} ${risk.message}`);
|
|
118
|
+
// Show details if available
|
|
119
|
+
if (risk.details) {
|
|
120
|
+
for (const [key, value] of Object.entries(risk.details)) {
|
|
121
|
+
ui.print(ui.dim(` ${key}: ${value}`));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Display affected resources
|
|
128
|
+
*/
|
|
129
|
+
function displayAffectedResources(resources) {
|
|
130
|
+
ui.newLine();
|
|
131
|
+
ui.print(ui.bold(' Affected Resources:'));
|
|
132
|
+
ui.newLine();
|
|
133
|
+
const maxDisplay = 10;
|
|
134
|
+
const displayResources = resources.slice(0, maxDisplay);
|
|
135
|
+
for (const resource of displayResources) {
|
|
136
|
+
ui.print(` ${ui.color('•', 'cyan')} ${resource}`);
|
|
137
|
+
}
|
|
138
|
+
if (resources.length > maxDisplay) {
|
|
139
|
+
ui.print(ui.dim(` ... and ${resources.length - maxDisplay} more`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Display estimated cost
|
|
144
|
+
*/
|
|
145
|
+
function displayEstimatedCost(cost) {
|
|
146
|
+
ui.newLine();
|
|
147
|
+
ui.print(ui.bold(' Estimated Cost:'));
|
|
148
|
+
ui.newLine();
|
|
149
|
+
const costStr = `$${cost.toFixed(2)}/month`;
|
|
150
|
+
const color = cost > 1000 ? 'red' : cost > 500 ? 'yellow' : 'green';
|
|
151
|
+
ui.print(` ${ui.color(costStr, color)}`);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get the highest severity from a list of risks
|
|
155
|
+
*/
|
|
156
|
+
function getHighestSeverity(risks) {
|
|
157
|
+
const order = ['critical', 'high', 'medium', 'low'];
|
|
158
|
+
for (const severity of order) {
|
|
159
|
+
if (risks.some(r => r.severity === severity)) {
|
|
160
|
+
return severity;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return 'low';
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get icon for severity level
|
|
167
|
+
*/
|
|
168
|
+
function getSeverityIcon(severity) {
|
|
169
|
+
switch (severity) {
|
|
170
|
+
case 'critical':
|
|
171
|
+
return '🔴';
|
|
172
|
+
case 'high':
|
|
173
|
+
return '🟠';
|
|
174
|
+
case 'medium':
|
|
175
|
+
return '🟡';
|
|
176
|
+
case 'low':
|
|
177
|
+
return '🔵';
|
|
178
|
+
default:
|
|
179
|
+
return '⚪';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get color for severity level
|
|
184
|
+
*/
|
|
185
|
+
function getSeverityColor(severity) {
|
|
186
|
+
switch (severity) {
|
|
187
|
+
case 'critical':
|
|
188
|
+
return 'red';
|
|
189
|
+
case 'high':
|
|
190
|
+
return 'yellow';
|
|
191
|
+
case 'medium':
|
|
192
|
+
return 'cyan';
|
|
193
|
+
case 'low':
|
|
194
|
+
return 'blue';
|
|
195
|
+
default:
|
|
196
|
+
return 'white';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Quick approval check without full prompt
|
|
201
|
+
* Returns true if the operation should proceed based on policy
|
|
202
|
+
*/
|
|
203
|
+
export function shouldAutoApprove(operation, risks) {
|
|
204
|
+
// Never auto-approve if there are critical risks
|
|
205
|
+
if (risks.some(r => r.severity === 'critical')) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
// Never auto-approve destructive operations
|
|
209
|
+
if (['destroy', 'delete', 'terminate'].some(op => operation.toLowerCase().includes(op))) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
// Auto-approve if all risks are low severity
|
|
213
|
+
if (risks.every(r => r.severity === 'low')) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Confirm a destructive operation by requiring the user to type the resource name.
|
|
220
|
+
* Returns true only if the typed name matches exactly.
|
|
221
|
+
*/
|
|
222
|
+
export async function confirmWithResourceName(resourceName, resourceType) {
|
|
223
|
+
ui.newLine();
|
|
224
|
+
// Display warning box
|
|
225
|
+
const width = 60;
|
|
226
|
+
ui.print(ui.color(`╔${'═'.repeat(width - 2)}╗`, 'red'));
|
|
227
|
+
const titleLine = ' DESTRUCTIVE OPERATION ';
|
|
228
|
+
const padding = Math.floor((width - 2 - titleLine.length) / 2);
|
|
229
|
+
ui.print(ui.color('║', 'red') +
|
|
230
|
+
' '.repeat(padding) +
|
|
231
|
+
ui.bold(ui.color(titleLine, 'yellow')) +
|
|
232
|
+
' '.repeat(width - 2 - padding - titleLine.length) +
|
|
233
|
+
ui.color('║', 'red'));
|
|
234
|
+
ui.print(ui.color(`╠${'═'.repeat(width - 2)}╣`, 'red'));
|
|
235
|
+
const typeLine = ` Resource type: ${resourceType}`;
|
|
236
|
+
ui.print(ui.color('║', 'red') + typeLine.padEnd(width - 2) + ui.color('║', 'red'));
|
|
237
|
+
const nameLine = ` Resource name: ${resourceName}`;
|
|
238
|
+
ui.print(ui.color('║', 'red') + ui.color(nameLine.padEnd(width - 2), 'yellow') + ui.color('║', 'red'));
|
|
239
|
+
const warnLine = ' This action CANNOT be undone.';
|
|
240
|
+
ui.print(ui.color('║', 'red') + ui.color(warnLine.padEnd(width - 2), 'red') + ui.color('║', 'red'));
|
|
241
|
+
ui.print(ui.color(`╚${'═'.repeat(width - 2)}╝`, 'red'));
|
|
242
|
+
ui.newLine();
|
|
243
|
+
const userInput = await input({
|
|
244
|
+
message: `Type '${resourceName}' to confirm deletion:`,
|
|
245
|
+
defaultValue: '',
|
|
246
|
+
});
|
|
247
|
+
if (userInput !== resourceName) {
|
|
248
|
+
ui.newLine();
|
|
249
|
+
ui.warning('Operation cancelled - resource name did not match');
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
ui.newLine();
|
|
253
|
+
ui.success('Deletion confirmed');
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Display a safety summary without approval prompt
|
|
258
|
+
*/
|
|
259
|
+
export function displaySafetySummary(config) {
|
|
260
|
+
if (config.risks.length === 0) {
|
|
261
|
+
ui.success(`Safety check passed for: ${config.operation}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
ui.newLine();
|
|
265
|
+
ui.print(ui.bold(' Safety Check Summary:'));
|
|
266
|
+
ui.newLine();
|
|
267
|
+
displayRisks(config.risks);
|
|
268
|
+
ui.newLine();
|
|
269
|
+
if (config.passed) {
|
|
270
|
+
ui.success('All safety checks passed');
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
ui.error('Safety checks failed - operation blocked');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Wizard Framework
|
|
3
|
+
*
|
|
4
|
+
* Exports all wizard-related functionality
|
|
5
|
+
*/
|
|
6
|
+
// Types
|
|
7
|
+
export * from './types';
|
|
8
|
+
// UI utilities
|
|
9
|
+
export { WizardUI, ui } from './ui';
|
|
10
|
+
// Prompt utilities
|
|
11
|
+
export { select, multiSelect, confirm, input, pathInput, pressEnter, actionSelect, } from './prompts';
|
|
12
|
+
// Wizard framework
|
|
13
|
+
export { Wizard, createWizard, stepBuilders } from './wizard';
|