@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,609 @@
|
|
|
1
|
+
import { logger } from '../utils';
|
|
2
|
+
// ==========================================
|
|
3
|
+
// SafetyManager
|
|
4
|
+
// ==========================================
|
|
5
|
+
export class SafetyManager {
|
|
6
|
+
checks;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.checks = new Map();
|
|
9
|
+
this.initializeDefaultChecks();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Run pre-execution safety checks
|
|
13
|
+
*/
|
|
14
|
+
async runPreExecutionChecks(task, plan) {
|
|
15
|
+
logger.info(`Running pre-execution safety checks for task: ${task.id}`);
|
|
16
|
+
const preExecutionChecks = Array.from(this.checks.values()).filter(check => check.type === 'pre_execution');
|
|
17
|
+
const results = [];
|
|
18
|
+
const context = {
|
|
19
|
+
task,
|
|
20
|
+
plan,
|
|
21
|
+
environment: task.context.environment,
|
|
22
|
+
provider: task.context.provider,
|
|
23
|
+
components: task.context.components,
|
|
24
|
+
};
|
|
25
|
+
for (const check of preExecutionChecks) {
|
|
26
|
+
try {
|
|
27
|
+
const result = await check.check(context);
|
|
28
|
+
results.push(result);
|
|
29
|
+
logger.info(`Safety check ${check.id}: ${result.passed ? 'PASSED' : 'FAILED'} (${check.name})`);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger.error(`Error running safety check ${check.id}`, error);
|
|
33
|
+
results.push({
|
|
34
|
+
passed: false,
|
|
35
|
+
severity: 'high',
|
|
36
|
+
message: `Check failed with error: ${error.message}`,
|
|
37
|
+
can_proceed: false,
|
|
38
|
+
requires_approval: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Identify blockers (failed checks that prevent execution)
|
|
43
|
+
const blockers = results.filter(r => !r.passed && !r.can_proceed);
|
|
44
|
+
const passed = blockers.length === 0;
|
|
45
|
+
logger.info(`Pre-execution checks: ${results.length} total, ${results.filter(r => r.passed).length} passed, ${blockers.length} blockers`);
|
|
46
|
+
return { passed, results, blockers };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run during-execution safety checks
|
|
50
|
+
*/
|
|
51
|
+
async runDuringExecutionChecks(context) {
|
|
52
|
+
logger.info('Running during-execution safety checks');
|
|
53
|
+
const duringExecutionChecks = Array.from(this.checks.values()).filter(check => check.type === 'during_execution');
|
|
54
|
+
const results = [];
|
|
55
|
+
for (const check of duringExecutionChecks) {
|
|
56
|
+
try {
|
|
57
|
+
const result = await check.check(context);
|
|
58
|
+
results.push(result);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
logger.error(`Error running safety check ${check.id}`, error);
|
|
62
|
+
results.push({
|
|
63
|
+
passed: false,
|
|
64
|
+
severity: 'high',
|
|
65
|
+
message: `Check failed with error: ${error.message}`,
|
|
66
|
+
can_proceed: false,
|
|
67
|
+
requires_approval: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const passed = results.every(r => r.passed || r.can_proceed);
|
|
72
|
+
return { passed, results };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Run post-execution safety checks
|
|
76
|
+
*/
|
|
77
|
+
async runPostExecutionChecks(context) {
|
|
78
|
+
logger.info('Running post-execution safety checks');
|
|
79
|
+
const postExecutionChecks = Array.from(this.checks.values()).filter(check => check.type === 'post_execution');
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const check of postExecutionChecks) {
|
|
82
|
+
try {
|
|
83
|
+
const result = await check.check(context);
|
|
84
|
+
results.push(result);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
logger.error(`Error running safety check ${check.id}`, error);
|
|
88
|
+
results.push({
|
|
89
|
+
passed: false,
|
|
90
|
+
severity: 'high',
|
|
91
|
+
message: `Check failed with error: ${error.message}`,
|
|
92
|
+
can_proceed: true, // Post-execution failures don't block
|
|
93
|
+
requires_approval: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const passed = results.every(r => r.passed);
|
|
98
|
+
return { passed, results };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Register custom safety check
|
|
102
|
+
*/
|
|
103
|
+
registerCheck(check) {
|
|
104
|
+
this.checks.set(check.id, check);
|
|
105
|
+
logger.info(`Registered safety check: ${check.id}`);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Remove safety check
|
|
109
|
+
*/
|
|
110
|
+
removeCheck(checkId) {
|
|
111
|
+
this.checks.delete(checkId);
|
|
112
|
+
logger.info(`Removed safety check: ${checkId}`);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get all checks
|
|
116
|
+
*/
|
|
117
|
+
getAllChecks() {
|
|
118
|
+
return Array.from(this.checks.values());
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Initialize default safety checks
|
|
122
|
+
*/
|
|
123
|
+
initializeDefaultChecks() {
|
|
124
|
+
// ===== Pre-Execution Checks =====
|
|
125
|
+
// Check: Production environment safeguards
|
|
126
|
+
this.registerCheck({
|
|
127
|
+
id: 'pre_prod_safeguard',
|
|
128
|
+
type: 'pre_execution',
|
|
129
|
+
category: 'security',
|
|
130
|
+
name: 'Production Environment Safeguards',
|
|
131
|
+
description: 'Verify additional safeguards for production deployments',
|
|
132
|
+
severity: 'critical',
|
|
133
|
+
check: async (context) => {
|
|
134
|
+
const task = context.task;
|
|
135
|
+
const plan = context.plan;
|
|
136
|
+
if (task.context.environment === 'production') {
|
|
137
|
+
// Production requires approval
|
|
138
|
+
if (!plan.requires_approval || !plan.approved_by) {
|
|
139
|
+
return {
|
|
140
|
+
passed: false,
|
|
141
|
+
severity: 'critical',
|
|
142
|
+
message: 'Production deployment requires explicit approval',
|
|
143
|
+
can_proceed: false,
|
|
144
|
+
requires_approval: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Check for high-risk operations
|
|
148
|
+
if (plan.risk_level === 'high' || plan.risk_level === 'critical') {
|
|
149
|
+
return {
|
|
150
|
+
passed: true,
|
|
151
|
+
severity: 'high',
|
|
152
|
+
message: 'High-risk production deployment approved',
|
|
153
|
+
details: {
|
|
154
|
+
risk_level: plan.risk_level,
|
|
155
|
+
approved_by: plan.approved_by,
|
|
156
|
+
},
|
|
157
|
+
can_proceed: true,
|
|
158
|
+
requires_approval: false,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
passed: true,
|
|
164
|
+
severity: 'low',
|
|
165
|
+
message: 'Environment safeguards satisfied',
|
|
166
|
+
can_proceed: true,
|
|
167
|
+
requires_approval: false,
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
// Check: Cost limits
|
|
172
|
+
this.registerCheck({
|
|
173
|
+
id: 'pre_cost_limit',
|
|
174
|
+
type: 'pre_execution',
|
|
175
|
+
category: 'cost',
|
|
176
|
+
name: 'Cost Limit Check',
|
|
177
|
+
description: 'Verify estimated cost is within acceptable limits',
|
|
178
|
+
severity: 'high',
|
|
179
|
+
check: async (context) => {
|
|
180
|
+
const plan = context.plan;
|
|
181
|
+
const estimatedCost = plan.estimated_cost || 0;
|
|
182
|
+
const maxCost = 5000; // $5000 per month limit
|
|
183
|
+
if (estimatedCost > maxCost) {
|
|
184
|
+
return {
|
|
185
|
+
passed: false,
|
|
186
|
+
severity: 'high',
|
|
187
|
+
message: `Estimated cost $${estimatedCost} exceeds limit $${maxCost}`,
|
|
188
|
+
details: {
|
|
189
|
+
estimated: estimatedCost,
|
|
190
|
+
limit: maxCost,
|
|
191
|
+
},
|
|
192
|
+
can_proceed: false,
|
|
193
|
+
requires_approval: true,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
passed: true,
|
|
198
|
+
severity: 'low',
|
|
199
|
+
message: `Estimated cost $${estimatedCost} within limit`,
|
|
200
|
+
can_proceed: true,
|
|
201
|
+
requires_approval: false,
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
// Check: Security best practices
|
|
206
|
+
this.registerCheck({
|
|
207
|
+
id: 'pre_security_practices',
|
|
208
|
+
type: 'pre_execution',
|
|
209
|
+
category: 'security',
|
|
210
|
+
name: 'Security Best Practices',
|
|
211
|
+
description: 'Verify security best practices are applied',
|
|
212
|
+
severity: 'critical',
|
|
213
|
+
check: async (context) => {
|
|
214
|
+
const plan = context.plan;
|
|
215
|
+
// Check if security validation steps are included
|
|
216
|
+
const hasSecurityValidation = plan.steps.some(step => step.action === 'apply_best_practices');
|
|
217
|
+
if (!hasSecurityValidation) {
|
|
218
|
+
return {
|
|
219
|
+
passed: false,
|
|
220
|
+
severity: 'critical',
|
|
221
|
+
message: 'Plan missing security best practices validation',
|
|
222
|
+
can_proceed: false,
|
|
223
|
+
requires_approval: true,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
passed: true,
|
|
228
|
+
severity: 'low',
|
|
229
|
+
message: 'Security best practices validation included',
|
|
230
|
+
can_proceed: true,
|
|
231
|
+
requires_approval: false,
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
// Check: Backup strategy
|
|
236
|
+
this.registerCheck({
|
|
237
|
+
id: 'pre_backup_strategy',
|
|
238
|
+
type: 'pre_execution',
|
|
239
|
+
category: 'availability',
|
|
240
|
+
name: 'Backup Strategy',
|
|
241
|
+
description: 'Verify backup strategy is defined for stateful components',
|
|
242
|
+
severity: 'high',
|
|
243
|
+
check: async (context) => {
|
|
244
|
+
const task = context.task;
|
|
245
|
+
const hasStatefulComponents = task.context.components.some(c => ['rds', 's3', 'efs'].includes(c));
|
|
246
|
+
if (hasStatefulComponents) {
|
|
247
|
+
// Check if backup is configured
|
|
248
|
+
const hasBackup = task.context.requirements?.backup_enabled === true;
|
|
249
|
+
if (!hasBackup && task.context.environment === 'production') {
|
|
250
|
+
return {
|
|
251
|
+
passed: false,
|
|
252
|
+
severity: 'high',
|
|
253
|
+
message: 'Production stateful components require backup strategy',
|
|
254
|
+
can_proceed: false,
|
|
255
|
+
requires_approval: true,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (!hasBackup) {
|
|
259
|
+
return {
|
|
260
|
+
passed: true,
|
|
261
|
+
severity: 'medium',
|
|
262
|
+
message: 'Backup recommended for stateful components',
|
|
263
|
+
can_proceed: true,
|
|
264
|
+
requires_approval: false,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
passed: true,
|
|
270
|
+
severity: 'low',
|
|
271
|
+
message: 'Backup strategy validated',
|
|
272
|
+
can_proceed: true,
|
|
273
|
+
requires_approval: false,
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
// Check: Destructive operations
|
|
278
|
+
this.registerCheck({
|
|
279
|
+
id: 'pre_destructive_ops',
|
|
280
|
+
type: 'pre_execution',
|
|
281
|
+
category: 'availability',
|
|
282
|
+
name: 'Destructive Operations',
|
|
283
|
+
description: 'Verify destructive operations are intentional',
|
|
284
|
+
severity: 'critical',
|
|
285
|
+
check: async (context) => {
|
|
286
|
+
const plan = context.plan;
|
|
287
|
+
// Check for deployment steps that could be destructive
|
|
288
|
+
const hasDeployment = plan.steps.some(step => step.action === 'apply_deployment');
|
|
289
|
+
if (hasDeployment) {
|
|
290
|
+
// Ensure plan has rollback capability
|
|
291
|
+
const hasRollback = plan.steps.some(step => step.rollback_action);
|
|
292
|
+
if (!hasRollback) {
|
|
293
|
+
return {
|
|
294
|
+
passed: false,
|
|
295
|
+
severity: 'high',
|
|
296
|
+
message: 'Deployment steps should have rollback capability',
|
|
297
|
+
can_proceed: true, // Can proceed but warned
|
|
298
|
+
requires_approval: true,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
passed: true,
|
|
304
|
+
severity: 'low',
|
|
305
|
+
message: 'Destructive operations validated',
|
|
306
|
+
can_proceed: true,
|
|
307
|
+
requires_approval: false,
|
|
308
|
+
};
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
// ===== During-Execution Checks =====
|
|
312
|
+
// Check: Resource creation rate
|
|
313
|
+
this.registerCheck({
|
|
314
|
+
id: 'during_resource_rate',
|
|
315
|
+
type: 'during_execution',
|
|
316
|
+
category: 'cost',
|
|
317
|
+
name: 'Resource Creation Rate',
|
|
318
|
+
description: 'Monitor resource creation rate for anomalies',
|
|
319
|
+
severity: 'medium',
|
|
320
|
+
check: async (context) => {
|
|
321
|
+
// In production, this would monitor actual resource creation
|
|
322
|
+
const resourcesCreated = context.resources_created || 0;
|
|
323
|
+
const maxRate = 50; // Max 50 resources per execution
|
|
324
|
+
if (resourcesCreated > maxRate) {
|
|
325
|
+
return {
|
|
326
|
+
passed: false,
|
|
327
|
+
severity: 'high',
|
|
328
|
+
message: `Unusual resource creation rate: ${resourcesCreated} resources`,
|
|
329
|
+
details: { count: resourcesCreated, threshold: maxRate },
|
|
330
|
+
can_proceed: false,
|
|
331
|
+
requires_approval: true,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
passed: true,
|
|
336
|
+
severity: 'low',
|
|
337
|
+
message: 'Resource creation rate normal',
|
|
338
|
+
can_proceed: true,
|
|
339
|
+
requires_approval: false,
|
|
340
|
+
};
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
// Check: Execution timeout
|
|
344
|
+
this.registerCheck({
|
|
345
|
+
id: 'during_execution_timeout',
|
|
346
|
+
type: 'during_execution',
|
|
347
|
+
category: 'availability',
|
|
348
|
+
name: 'Execution Timeout',
|
|
349
|
+
description: 'Ensure execution does not exceed time limits',
|
|
350
|
+
severity: 'medium',
|
|
351
|
+
check: async (context) => {
|
|
352
|
+
const startTime = context.start_time;
|
|
353
|
+
const maxDuration = 3600000; // 1 hour
|
|
354
|
+
if (startTime) {
|
|
355
|
+
const elapsed = Date.now() - startTime.getTime();
|
|
356
|
+
if (elapsed > maxDuration) {
|
|
357
|
+
return {
|
|
358
|
+
passed: false,
|
|
359
|
+
severity: 'high',
|
|
360
|
+
message: 'Execution exceeding time limit',
|
|
361
|
+
details: { elapsed, limit: maxDuration },
|
|
362
|
+
can_proceed: false,
|
|
363
|
+
requires_approval: false,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
passed: true,
|
|
369
|
+
severity: 'low',
|
|
370
|
+
message: 'Execution within time limits',
|
|
371
|
+
can_proceed: true,
|
|
372
|
+
requires_approval: false,
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
// ===== Post-Execution Checks =====
|
|
377
|
+
// Check: Deployment verification
|
|
378
|
+
this.registerCheck({
|
|
379
|
+
id: 'post_deployment_verify',
|
|
380
|
+
type: 'post_execution',
|
|
381
|
+
category: 'availability',
|
|
382
|
+
name: 'Deployment Verification',
|
|
383
|
+
description: 'Verify deployed resources are healthy',
|
|
384
|
+
severity: 'high',
|
|
385
|
+
check: async (context) => {
|
|
386
|
+
const deploymentSuccess = context.deployment_success;
|
|
387
|
+
if (deploymentSuccess === false) {
|
|
388
|
+
return {
|
|
389
|
+
passed: false,
|
|
390
|
+
severity: 'critical',
|
|
391
|
+
message: 'Deployment verification failed',
|
|
392
|
+
can_proceed: true, // Already deployed
|
|
393
|
+
requires_approval: false,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
passed: true,
|
|
398
|
+
severity: 'low',
|
|
399
|
+
message: 'Deployment verified successfully',
|
|
400
|
+
can_proceed: true,
|
|
401
|
+
requires_approval: false,
|
|
402
|
+
};
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
// Check: Cost anomaly detection
|
|
406
|
+
this.registerCheck({
|
|
407
|
+
id: 'post_cost_anomaly',
|
|
408
|
+
type: 'post_execution',
|
|
409
|
+
category: 'cost',
|
|
410
|
+
name: 'Cost Anomaly Detection',
|
|
411
|
+
description: 'Check for unexpected cost increases',
|
|
412
|
+
severity: 'medium',
|
|
413
|
+
check: async (context) => {
|
|
414
|
+
const estimatedCost = context.estimated_cost || 0;
|
|
415
|
+
const actualCost = context.actual_cost || estimatedCost;
|
|
416
|
+
// Allow 20% variance
|
|
417
|
+
const variance = Math.abs(actualCost - estimatedCost) / estimatedCost;
|
|
418
|
+
if (variance > 0.2) {
|
|
419
|
+
return {
|
|
420
|
+
passed: false,
|
|
421
|
+
severity: 'high',
|
|
422
|
+
message: `Actual cost differs significantly from estimate`,
|
|
423
|
+
details: {
|
|
424
|
+
estimated: estimatedCost,
|
|
425
|
+
actual: actualCost,
|
|
426
|
+
variance: `${(variance * 100).toFixed(1)}%`,
|
|
427
|
+
},
|
|
428
|
+
can_proceed: true,
|
|
429
|
+
requires_approval: false,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
passed: true,
|
|
434
|
+
severity: 'low',
|
|
435
|
+
message: 'Cost within expected range',
|
|
436
|
+
can_proceed: true,
|
|
437
|
+
requires_approval: false,
|
|
438
|
+
};
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
// Check: Security posture
|
|
442
|
+
this.registerCheck({
|
|
443
|
+
id: 'post_security_posture',
|
|
444
|
+
type: 'post_execution',
|
|
445
|
+
category: 'security',
|
|
446
|
+
name: 'Security Posture Assessment',
|
|
447
|
+
description: 'Assess final security configuration',
|
|
448
|
+
severity: 'critical',
|
|
449
|
+
check: async (context) => {
|
|
450
|
+
const securityScore = context.security_score || 0;
|
|
451
|
+
const minScore = 80;
|
|
452
|
+
if (securityScore < minScore) {
|
|
453
|
+
return {
|
|
454
|
+
passed: false,
|
|
455
|
+
severity: 'critical',
|
|
456
|
+
message: `Security score ${securityScore} below threshold ${minScore}`,
|
|
457
|
+
details: { score: securityScore, threshold: minScore },
|
|
458
|
+
can_proceed: true,
|
|
459
|
+
requires_approval: false,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
passed: true,
|
|
464
|
+
severity: 'low',
|
|
465
|
+
message: `Security score ${securityScore} meets requirements`,
|
|
466
|
+
can_proceed: true,
|
|
467
|
+
requires_approval: false,
|
|
468
|
+
};
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
// Check: No production delete without backup
|
|
472
|
+
this.registerCheck({
|
|
473
|
+
id: 'no_production_delete_without_backup',
|
|
474
|
+
type: 'pre_execution',
|
|
475
|
+
category: 'availability',
|
|
476
|
+
name: 'No Production Delete Without Backup',
|
|
477
|
+
description: 'Blocks destroy/delete operations in production when backup is not enabled',
|
|
478
|
+
severity: 'critical',
|
|
479
|
+
check: async (context) => {
|
|
480
|
+
const task = context.task;
|
|
481
|
+
const plan = context.plan;
|
|
482
|
+
if (task.context.environment !== 'production') {
|
|
483
|
+
return {
|
|
484
|
+
passed: true,
|
|
485
|
+
severity: 'low',
|
|
486
|
+
message: 'Non-production environment, policy not applicable',
|
|
487
|
+
can_proceed: true,
|
|
488
|
+
requires_approval: false,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const hasDestructiveAction = plan.steps.some(step => (step.action === 'apply_deployment' && step.parameters?.destroy) ||
|
|
492
|
+
(step.action === 'apply_deployment' &&
|
|
493
|
+
step.description?.toLowerCase().includes('delete')) ||
|
|
494
|
+
step.description?.toLowerCase().includes('destroy'));
|
|
495
|
+
if (!hasDestructiveAction) {
|
|
496
|
+
return {
|
|
497
|
+
passed: true,
|
|
498
|
+
severity: 'low',
|
|
499
|
+
message: 'No destructive actions found in plan',
|
|
500
|
+
can_proceed: true,
|
|
501
|
+
requires_approval: false,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
const backupEnabled = task.context.requirements?.backup_enabled === true;
|
|
505
|
+
if (!backupEnabled) {
|
|
506
|
+
return {
|
|
507
|
+
passed: false,
|
|
508
|
+
severity: 'critical',
|
|
509
|
+
message: 'Production destroy/delete operations require backup_enabled in requirements. ' +
|
|
510
|
+
'Create a backup first or use a staging environment.',
|
|
511
|
+
can_proceed: false,
|
|
512
|
+
requires_approval: true,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
passed: true,
|
|
517
|
+
severity: 'low',
|
|
518
|
+
message: 'Backup enabled for production destructive operation',
|
|
519
|
+
can_proceed: true,
|
|
520
|
+
requires_approval: false,
|
|
521
|
+
};
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
// Check: Require dry-run before apply
|
|
525
|
+
this.registerCheck({
|
|
526
|
+
id: 'require_dry_run_first',
|
|
527
|
+
type: 'pre_execution',
|
|
528
|
+
category: 'compliance',
|
|
529
|
+
name: 'Require Dry Run First',
|
|
530
|
+
description: 'Blocks apply_deployment if no plan_deployment step precedes it',
|
|
531
|
+
severity: 'high',
|
|
532
|
+
check: async (context) => {
|
|
533
|
+
const plan = context.plan;
|
|
534
|
+
const hasApply = plan.steps.some(step => step.action === 'apply_deployment');
|
|
535
|
+
if (!hasApply) {
|
|
536
|
+
return {
|
|
537
|
+
passed: true,
|
|
538
|
+
severity: 'low',
|
|
539
|
+
message: 'No apply_deployment steps in plan',
|
|
540
|
+
can_proceed: true,
|
|
541
|
+
requires_approval: false,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const hasPlan = plan.steps.some(step => step.action === 'plan_deployment');
|
|
545
|
+
if (!hasPlan) {
|
|
546
|
+
return {
|
|
547
|
+
passed: false,
|
|
548
|
+
severity: 'high',
|
|
549
|
+
message: 'Plan contains apply_deployment without a preceding plan_deployment step. ' +
|
|
550
|
+
'Run with --dry-run first to preview changes.',
|
|
551
|
+
can_proceed: false,
|
|
552
|
+
requires_approval: true,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
passed: true,
|
|
557
|
+
severity: 'low',
|
|
558
|
+
message: 'Dry-run (plan_deployment) step precedes apply',
|
|
559
|
+
can_proceed: true,
|
|
560
|
+
requires_approval: false,
|
|
561
|
+
};
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
// Check: Token budget guardrail
|
|
565
|
+
this.registerCheck({
|
|
566
|
+
id: 'pre_token_budget',
|
|
567
|
+
type: 'pre_execution',
|
|
568
|
+
category: 'cost',
|
|
569
|
+
name: 'Token Budget Check',
|
|
570
|
+
description: 'Verify estimated token usage does not exceed budget',
|
|
571
|
+
severity: 'high',
|
|
572
|
+
check: async (context) => {
|
|
573
|
+
const plan = context.plan;
|
|
574
|
+
const maxTokensPerTask = parseInt(process.env.MAX_TOKENS_PER_TASK || '0', 10);
|
|
575
|
+
if (!maxTokensPerTask || maxTokensPerTask <= 0) {
|
|
576
|
+
return {
|
|
577
|
+
passed: true,
|
|
578
|
+
severity: 'low',
|
|
579
|
+
message: 'No token budget configured',
|
|
580
|
+
can_proceed: true,
|
|
581
|
+
requires_approval: false,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
const estimatedTokens = plan.estimated_tokens ?? plan.estimated_cost ?? 0;
|
|
585
|
+
if (estimatedTokens > maxTokensPerTask) {
|
|
586
|
+
return {
|
|
587
|
+
passed: false,
|
|
588
|
+
severity: 'high',
|
|
589
|
+
message: `Estimated token usage (${estimatedTokens}) exceeds budget (${maxTokensPerTask})`,
|
|
590
|
+
details: {
|
|
591
|
+
estimated: estimatedTokens,
|
|
592
|
+
budget: maxTokensPerTask,
|
|
593
|
+
},
|
|
594
|
+
can_proceed: false,
|
|
595
|
+
requires_approval: true,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
passed: true,
|
|
600
|
+
severity: 'low',
|
|
601
|
+
message: `Estimated tokens (${estimatedTokens}) within budget (${maxTokensPerTask})`,
|
|
602
|
+
can_proceed: true,
|
|
603
|
+
requires_approval: false,
|
|
604
|
+
};
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
logger.info(`Initialized ${this.checks.size} default safety checks`);
|
|
608
|
+
}
|
|
609
|
+
}
|