@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,616 @@
|
|
|
1
|
+
import { logger } from '../utils';
|
|
2
|
+
import { LLMRouter } from '../llm/router';
|
|
3
|
+
// ==========================================
|
|
4
|
+
// Constants
|
|
5
|
+
// ==========================================
|
|
6
|
+
/** System prompt instructing the LLM to generate execution steps as JSON. */
|
|
7
|
+
const PLANNING_PROMPT = 'You are an infrastructure planning agent. Given the task context, generate an ordered array of execution steps as JSON. ' +
|
|
8
|
+
'Each step has fields: id (string like step_1), name (string), description (string), type (one of: generate, validate, deploy, configure, verify), ' +
|
|
9
|
+
'order (number), estimatedDuration (number in seconds). Return ONLY the JSON array, no markdown.';
|
|
10
|
+
/** System prompt instructing the LLM to assess risks as JSON. */
|
|
11
|
+
const RISK_ASSESSMENT_PROMPT = 'You are an infrastructure risk assessor. Given the task and execution steps, identify risks. ' +
|
|
12
|
+
'Return a JSON array of risks with fields: id (string), severity (low|medium|high|critical), ' +
|
|
13
|
+
'category (security|cost|availability|performance|compliance), description (string), mitigation (string), ' +
|
|
14
|
+
'probability (0-1), impact (0-1). Return ONLY the JSON array.';
|
|
15
|
+
/** Valid step types for plan steps. */
|
|
16
|
+
const VALID_STEP_TYPES = new Set(['generate', 'validate', 'deploy', 'configure', 'verify']);
|
|
17
|
+
/** Valid severity levels for risks. */
|
|
18
|
+
const VALID_SEVERITIES = new Set(['low', 'medium', 'high', 'critical']);
|
|
19
|
+
/** Valid risk categories. */
|
|
20
|
+
const VALID_CATEGORIES = new Set(['security', 'cost', 'availability', 'performance', 'compliance']);
|
|
21
|
+
// ==========================================
|
|
22
|
+
// Planner
|
|
23
|
+
// ==========================================
|
|
24
|
+
/** Detect the primary domain of a task description. */
|
|
25
|
+
function detectDomain(task) {
|
|
26
|
+
const desc = (task.type + ' ' + JSON.stringify(task.context)).toLowerCase();
|
|
27
|
+
if (desc.includes('helm') || desc.includes('chart') || desc.includes('release'))
|
|
28
|
+
return 'helm';
|
|
29
|
+
if (desc.includes('kubectl') || desc.includes('kubernetes') || desc.includes('pod') || desc.includes('deployment') || desc.includes('k8s'))
|
|
30
|
+
return 'kubernetes';
|
|
31
|
+
if (desc.includes('terraform') || desc.includes('.tf') || desc.includes('infrastructure') || desc.includes('provider'))
|
|
32
|
+
return 'terraform';
|
|
33
|
+
// Default to terraform for infrastructure tasks
|
|
34
|
+
if (task.type === 'deploy' || task.type === 'generate')
|
|
35
|
+
return 'terraform';
|
|
36
|
+
return 'generic';
|
|
37
|
+
}
|
|
38
|
+
export class Planner {
|
|
39
|
+
router;
|
|
40
|
+
constructor() {
|
|
41
|
+
this.router = new LLMRouter();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate an execution plan for a task
|
|
45
|
+
*/
|
|
46
|
+
async generatePlan(task) {
|
|
47
|
+
logger.info(`Generating plan for task: ${task.id}`);
|
|
48
|
+
const steps = await this.generateSteps(task);
|
|
49
|
+
const dependencies = this.analyzeDependencies(steps);
|
|
50
|
+
const risks = await this.assessRisks(task, steps);
|
|
51
|
+
const riskLevel = this.calculateOverallRiskLevel(risks);
|
|
52
|
+
const plan = {
|
|
53
|
+
id: this.generatePlanId(),
|
|
54
|
+
task_id: task.id,
|
|
55
|
+
status: 'draft',
|
|
56
|
+
created_at: new Date(),
|
|
57
|
+
updated_at: new Date(),
|
|
58
|
+
steps,
|
|
59
|
+
dependencies,
|
|
60
|
+
risks,
|
|
61
|
+
risk_level: riskLevel,
|
|
62
|
+
requires_approval: riskLevel === 'high' || riskLevel === 'critical',
|
|
63
|
+
};
|
|
64
|
+
// Estimate duration and cost
|
|
65
|
+
plan.estimated_duration = this.estimateDuration(steps);
|
|
66
|
+
plan.estimated_cost = await this.estimateCost(task, steps);
|
|
67
|
+
logger.info(`Generated plan ${plan.id} with ${steps.length} steps, risk level: ${riskLevel}`);
|
|
68
|
+
return plan;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Generate execution steps for a task.
|
|
72
|
+
* Attempts LLM-based generation first, falls back to heuristic logic.
|
|
73
|
+
*/
|
|
74
|
+
async generateSteps(task) {
|
|
75
|
+
try {
|
|
76
|
+
const llmSteps = await this.generateStepsWithLLM(task);
|
|
77
|
+
if (llmSteps.length > 0) {
|
|
78
|
+
logger.info(`Using LLM-generated steps (${llmSteps.length} steps)`);
|
|
79
|
+
return llmSteps;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger.debug(`LLM step generation failed, falling back to heuristics: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
return this.generateStepsHeuristic(task);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generate steps using the embedded LLM router.
|
|
89
|
+
*/
|
|
90
|
+
async generateStepsWithLLM(task) {
|
|
91
|
+
const response = await this.router.route({
|
|
92
|
+
messages: [
|
|
93
|
+
{ role: 'system', content: PLANNING_PROMPT },
|
|
94
|
+
{ role: 'user', content: JSON.stringify(task.context) },
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
const content = response?.content;
|
|
98
|
+
if (!content) {
|
|
99
|
+
throw new Error('LLM response missing content');
|
|
100
|
+
}
|
|
101
|
+
const parsed = JSON.parse(content);
|
|
102
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
103
|
+
throw new Error('LLM response is not a non-empty array');
|
|
104
|
+
}
|
|
105
|
+
const steps = [];
|
|
106
|
+
for (const item of parsed) {
|
|
107
|
+
if (typeof item !== 'object' ||
|
|
108
|
+
item === null ||
|
|
109
|
+
typeof item.id !== 'string' ||
|
|
110
|
+
typeof item.description !== 'string' ||
|
|
111
|
+
!VALID_STEP_TYPES.has(item.type)) {
|
|
112
|
+
throw new Error('LLM response contains invalid step structure');
|
|
113
|
+
}
|
|
114
|
+
steps.push({
|
|
115
|
+
id: item.id,
|
|
116
|
+
order: typeof item.order === 'number' ? item.order : steps.length + 1,
|
|
117
|
+
type: item.type,
|
|
118
|
+
description: item.description,
|
|
119
|
+
action: item.type,
|
|
120
|
+
parameters: {},
|
|
121
|
+
status: 'pending',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return steps;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Generate execution steps using heuristic logic (fallback).
|
|
128
|
+
*/
|
|
129
|
+
generateStepsHeuristic(task) {
|
|
130
|
+
const steps = [];
|
|
131
|
+
let order = 1;
|
|
132
|
+
// Domain-specific step generation
|
|
133
|
+
const domain = detectDomain(task);
|
|
134
|
+
if (domain === 'terraform') {
|
|
135
|
+
return this.generateTerraformSteps(task);
|
|
136
|
+
}
|
|
137
|
+
else if (domain === 'kubernetes') {
|
|
138
|
+
return this.generateKubernetesSteps(task);
|
|
139
|
+
}
|
|
140
|
+
else if (domain === 'helm') {
|
|
141
|
+
return this.generateHelmSteps(task);
|
|
142
|
+
}
|
|
143
|
+
// Step 1: Validate requirements
|
|
144
|
+
steps.push({
|
|
145
|
+
id: `step_${order++}`,
|
|
146
|
+
order: steps.length + 1,
|
|
147
|
+
type: 'validate',
|
|
148
|
+
description: 'Validate infrastructure requirements and constraints',
|
|
149
|
+
action: 'validate_requirements',
|
|
150
|
+
parameters: {
|
|
151
|
+
provider: task.context.provider,
|
|
152
|
+
components: task.context.components,
|
|
153
|
+
requirements: task.context.requirements,
|
|
154
|
+
},
|
|
155
|
+
status: 'pending',
|
|
156
|
+
});
|
|
157
|
+
// Step 2-N: Generate infrastructure components
|
|
158
|
+
for (const component of task.context.components) {
|
|
159
|
+
steps.push({
|
|
160
|
+
id: `step_${order++}`,
|
|
161
|
+
order: steps.length + 1,
|
|
162
|
+
type: 'generate',
|
|
163
|
+
description: `Generate ${component.toUpperCase()} configuration`,
|
|
164
|
+
component,
|
|
165
|
+
action: 'generate_component',
|
|
166
|
+
parameters: {
|
|
167
|
+
component,
|
|
168
|
+
provider: task.context.provider,
|
|
169
|
+
environment: task.context.environment,
|
|
170
|
+
requirements: task.context.requirements,
|
|
171
|
+
},
|
|
172
|
+
status: 'pending',
|
|
173
|
+
depends_on: ['step_1'], // Depends on validation
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Step: Validate generated code
|
|
177
|
+
steps.push({
|
|
178
|
+
id: `step_${order++}`,
|
|
179
|
+
order: steps.length + 1,
|
|
180
|
+
type: 'validate',
|
|
181
|
+
description: 'Validate generated infrastructure code',
|
|
182
|
+
action: 'validate_generated_code',
|
|
183
|
+
parameters: {
|
|
184
|
+
components: task.context.components,
|
|
185
|
+
},
|
|
186
|
+
status: 'pending',
|
|
187
|
+
depends_on: steps.slice(1, -1).map(s => s.id), // Depends on all generation steps
|
|
188
|
+
});
|
|
189
|
+
// Step: Apply best practices
|
|
190
|
+
steps.push({
|
|
191
|
+
id: `step_${order++}`,
|
|
192
|
+
order: steps.length + 1,
|
|
193
|
+
type: 'validate',
|
|
194
|
+
description: 'Apply security and best practices',
|
|
195
|
+
action: 'apply_best_practices',
|
|
196
|
+
parameters: {
|
|
197
|
+
components: task.context.components,
|
|
198
|
+
autofix: true,
|
|
199
|
+
},
|
|
200
|
+
status: 'pending',
|
|
201
|
+
depends_on: [steps[steps.length - 1].id],
|
|
202
|
+
});
|
|
203
|
+
// If deployment is requested
|
|
204
|
+
if (task.type === 'deploy') {
|
|
205
|
+
// Step: Plan deployment
|
|
206
|
+
steps.push({
|
|
207
|
+
id: `step_${order++}`,
|
|
208
|
+
order: steps.length + 1,
|
|
209
|
+
type: 'deploy',
|
|
210
|
+
description: 'Plan infrastructure deployment (terraform plan)',
|
|
211
|
+
action: 'plan_deployment',
|
|
212
|
+
parameters: {
|
|
213
|
+
provider: task.context.provider,
|
|
214
|
+
environment: task.context.environment,
|
|
215
|
+
},
|
|
216
|
+
status: 'pending',
|
|
217
|
+
depends_on: [steps[steps.length - 1].id],
|
|
218
|
+
});
|
|
219
|
+
// Step: Apply deployment
|
|
220
|
+
steps.push({
|
|
221
|
+
id: `step_${order++}`,
|
|
222
|
+
order: steps.length + 1,
|
|
223
|
+
type: 'deploy',
|
|
224
|
+
description: 'Apply infrastructure deployment (terraform apply)',
|
|
225
|
+
action: 'apply_deployment',
|
|
226
|
+
parameters: {
|
|
227
|
+
provider: task.context.provider,
|
|
228
|
+
environment: task.context.environment,
|
|
229
|
+
auto_approve: false,
|
|
230
|
+
},
|
|
231
|
+
status: 'pending',
|
|
232
|
+
depends_on: [steps[steps.length - 1].id],
|
|
233
|
+
rollback_action: `terraform_destroy:${task.context.environment ?? '.'}`,
|
|
234
|
+
rollback_parameters: {
|
|
235
|
+
provider: task.context.provider,
|
|
236
|
+
environment: task.context.environment,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
// Step: Verify deployment
|
|
240
|
+
steps.push({
|
|
241
|
+
id: `step_${order++}`,
|
|
242
|
+
order: steps.length + 1,
|
|
243
|
+
type: 'verify',
|
|
244
|
+
description: 'Verify deployed infrastructure',
|
|
245
|
+
action: 'verify_deployment',
|
|
246
|
+
parameters: {
|
|
247
|
+
components: task.context.components,
|
|
248
|
+
environment: task.context.environment,
|
|
249
|
+
},
|
|
250
|
+
status: 'pending',
|
|
251
|
+
depends_on: [steps[steps.length - 1].id],
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// Final step: Generate documentation
|
|
255
|
+
steps.push({
|
|
256
|
+
id: `step_${order++}`,
|
|
257
|
+
order: steps.length + 1,
|
|
258
|
+
type: 'generate',
|
|
259
|
+
description: 'Generate infrastructure documentation',
|
|
260
|
+
action: 'generate_documentation',
|
|
261
|
+
parameters: {
|
|
262
|
+
components: task.context.components,
|
|
263
|
+
include_diagrams: true,
|
|
264
|
+
},
|
|
265
|
+
status: 'pending',
|
|
266
|
+
depends_on: [steps[steps.length - 1].id],
|
|
267
|
+
});
|
|
268
|
+
return steps;
|
|
269
|
+
}
|
|
270
|
+
generateTerraformSteps(task) {
|
|
271
|
+
const steps = [];
|
|
272
|
+
let order = 1;
|
|
273
|
+
const env = task.context.environment ?? '.';
|
|
274
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform init — initialize providers and modules', action: 'terraform_init', parameters: { workdir: env }, status: 'pending' });
|
|
275
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform validate — check configuration syntax', action: 'terraform_validate', parameters: { workdir: env }, status: 'pending', depends_on: ['step_1'] });
|
|
276
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform plan — preview changes', action: 'terraform_plan', parameters: { workdir: env }, status: 'pending', depends_on: ['step_2'] });
|
|
277
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'Review plan output with human', action: 'review_plan', parameters: { workdir: env }, status: 'pending', depends_on: ['step_3'] });
|
|
278
|
+
if (task.type === 'deploy') {
|
|
279
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'terraform apply — apply approved changes', action: 'terraform_apply', parameters: { workdir: env }, status: 'pending', depends_on: ['step_4'], rollback_action: `terraform_destroy:${env}`, rollback_parameters: { workdir: env } });
|
|
280
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform output — capture outputs', action: 'terraform_output', parameters: { workdir: env }, status: 'pending', depends_on: ['step_5'] });
|
|
281
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify deployed infrastructure resources', action: 'verify_infra', parameters: { workdir: env }, status: 'pending', depends_on: ['step_6'] });
|
|
282
|
+
}
|
|
283
|
+
return steps;
|
|
284
|
+
}
|
|
285
|
+
generateKubernetesSteps(task) {
|
|
286
|
+
const steps = [];
|
|
287
|
+
let order = 1;
|
|
288
|
+
const env = task.context.environment ?? '.';
|
|
289
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'Validate Kubernetes manifests (dry-run client)', action: 'kubectl_dry_run', parameters: { workdir: env }, status: 'pending' });
|
|
290
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'kubectl diff — show what would change', action: 'kubectl_diff', parameters: { workdir: env }, status: 'pending', depends_on: ['step_1'] });
|
|
291
|
+
if (task.type === 'deploy') {
|
|
292
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'kubectl apply — deploy manifests', action: 'kubectl_apply', parameters: { workdir: env }, status: 'pending', depends_on: ['step_2'], rollback_action: 'kubectl_rollout_undo:deployment/app:default' });
|
|
293
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'kubectl rollout status — wait for rollout', action: 'kubectl_rollout_status', parameters: { workdir: env }, status: 'pending', depends_on: ['step_3'] });
|
|
294
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify all pods are running', action: 'verify_pods', parameters: { workdir: env }, status: 'pending', depends_on: ['step_4'] });
|
|
295
|
+
}
|
|
296
|
+
return steps;
|
|
297
|
+
}
|
|
298
|
+
generateHelmSteps(task) {
|
|
299
|
+
const steps = [];
|
|
300
|
+
let order = 1;
|
|
301
|
+
const release = task.context.components[0] ?? 'app';
|
|
302
|
+
const ns = task.context.environment ?? 'default';
|
|
303
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm lint — validate chart templates', action: 'helm_lint', parameters: { release, namespace: ns }, status: 'pending' });
|
|
304
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm template — render and preview manifests', action: 'helm_template', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_1'] });
|
|
305
|
+
if (task.type === 'deploy') {
|
|
306
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm diff upgrade — show what would change', action: 'helm_diff', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_2'] });
|
|
307
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'helm upgrade --install --atomic — deploy release', action: 'helm_upgrade', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_3'], rollback_action: `helm_rollback:${release}:0:${ns}` });
|
|
308
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'helm test — run chart tests', action: 'helm_test', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_4'] });
|
|
309
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify release is deployed and healthy', action: 'verify_release', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_5'] });
|
|
310
|
+
}
|
|
311
|
+
return steps;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Analyze dependencies between steps
|
|
315
|
+
*/
|
|
316
|
+
analyzeDependencies(steps) {
|
|
317
|
+
return steps
|
|
318
|
+
.filter(step => step.depends_on && step.depends_on.length > 0)
|
|
319
|
+
.map(step => ({
|
|
320
|
+
step_id: step.id,
|
|
321
|
+
depends_on: step.depends_on,
|
|
322
|
+
type: (step.depends_on.length === 1 ? 'sequential' : 'parallel'),
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Assess risks for the plan.
|
|
327
|
+
* Attempts LLM-based assessment first, falls back to heuristic logic.
|
|
328
|
+
*/
|
|
329
|
+
async assessRisks(task, steps) {
|
|
330
|
+
try {
|
|
331
|
+
const llmRisks = await this.assessRisksWithLLM(task, steps);
|
|
332
|
+
if (llmRisks.length > 0) {
|
|
333
|
+
logger.info(`Using LLM-assessed risks (${llmRisks.length} risks)`);
|
|
334
|
+
return llmRisks;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
logger.debug(`LLM risk assessment failed, falling back to heuristics: ${error.message}`);
|
|
339
|
+
}
|
|
340
|
+
return this.assessRisksHeuristic(task, steps);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Assess risks using the embedded LLM router.
|
|
344
|
+
*/
|
|
345
|
+
async assessRisksWithLLM(task, steps) {
|
|
346
|
+
const response = await this.router.route({
|
|
347
|
+
messages: [
|
|
348
|
+
{ role: 'system', content: RISK_ASSESSMENT_PROMPT },
|
|
349
|
+
{
|
|
350
|
+
role: 'user',
|
|
351
|
+
content: JSON.stringify({
|
|
352
|
+
task: task.context,
|
|
353
|
+
steps: steps.map(s => ({ id: s.id, type: s.type, description: s.description })),
|
|
354
|
+
}),
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
});
|
|
358
|
+
const content = response?.content;
|
|
359
|
+
if (!content) {
|
|
360
|
+
throw new Error('LLM response missing content');
|
|
361
|
+
}
|
|
362
|
+
const parsed = JSON.parse(content);
|
|
363
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
364
|
+
throw new Error('LLM response is not a non-empty array');
|
|
365
|
+
}
|
|
366
|
+
const risks = [];
|
|
367
|
+
for (const item of parsed) {
|
|
368
|
+
if (typeof item !== 'object' ||
|
|
369
|
+
item === null ||
|
|
370
|
+
typeof item.id !== 'string' ||
|
|
371
|
+
typeof item.description !== 'string' ||
|
|
372
|
+
!VALID_SEVERITIES.has(item.severity) ||
|
|
373
|
+
!VALID_CATEGORIES.has(item.category) ||
|
|
374
|
+
typeof item.probability !== 'number' ||
|
|
375
|
+
typeof item.impact !== 'number') {
|
|
376
|
+
throw new Error('LLM response contains invalid risk structure');
|
|
377
|
+
}
|
|
378
|
+
risks.push({
|
|
379
|
+
id: item.id,
|
|
380
|
+
severity: item.severity,
|
|
381
|
+
category: item.category,
|
|
382
|
+
description: item.description,
|
|
383
|
+
mitigation: typeof item.mitigation === 'string' ? item.mitigation : undefined,
|
|
384
|
+
probability: item.probability,
|
|
385
|
+
impact: item.impact,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return risks;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Assess risks using heuristic logic (fallback).
|
|
392
|
+
*/
|
|
393
|
+
assessRisksHeuristic(task, steps) {
|
|
394
|
+
const risks = [];
|
|
395
|
+
// Security risks
|
|
396
|
+
if (task.context.environment === 'production') {
|
|
397
|
+
risks.push({
|
|
398
|
+
id: 'risk_prod_deploy',
|
|
399
|
+
severity: 'high',
|
|
400
|
+
category: 'availability',
|
|
401
|
+
description: 'Deploying to production environment',
|
|
402
|
+
mitigation: 'Requires approval, automated testing, and gradual rollout',
|
|
403
|
+
probability: 0.3,
|
|
404
|
+
impact: 0.8,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
// Cost risks
|
|
408
|
+
const hasExpensiveComponents = task.context.components.some(c => ['eks', 'rds'].includes(c));
|
|
409
|
+
if (hasExpensiveComponents) {
|
|
410
|
+
risks.push({
|
|
411
|
+
id: 'risk_high_cost',
|
|
412
|
+
severity: 'medium',
|
|
413
|
+
category: 'cost',
|
|
414
|
+
description: 'Infrastructure includes high-cost components',
|
|
415
|
+
mitigation: 'Review instance types and enable autoscaling',
|
|
416
|
+
probability: 0.6,
|
|
417
|
+
impact: 0.5,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
// Compliance risks
|
|
421
|
+
if (task.context.components.includes('s3')) {
|
|
422
|
+
risks.push({
|
|
423
|
+
id: 'risk_data_security',
|
|
424
|
+
severity: 'high',
|
|
425
|
+
category: 'security',
|
|
426
|
+
description: 'Storage component requires encryption and access controls',
|
|
427
|
+
mitigation: 'Enable encryption at rest and in transit, implement least privilege access',
|
|
428
|
+
probability: 0.4,
|
|
429
|
+
impact: 0.9,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
// Deployment risks
|
|
433
|
+
const hasDeploymentSteps = steps.some(s => s.type === 'deploy');
|
|
434
|
+
if (hasDeploymentSteps && !task.context.requirements?.backup_enabled) {
|
|
435
|
+
risks.push({
|
|
436
|
+
id: 'risk_no_backup',
|
|
437
|
+
severity: 'high',
|
|
438
|
+
category: 'availability',
|
|
439
|
+
description: 'No backup strategy defined',
|
|
440
|
+
mitigation: 'Enable automated backups and test restoration procedures',
|
|
441
|
+
probability: 0.5,
|
|
442
|
+
impact: 0.7,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return risks;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Calculate overall risk level
|
|
449
|
+
*/
|
|
450
|
+
calculateOverallRiskLevel(risks) {
|
|
451
|
+
if (risks.length === 0) {
|
|
452
|
+
return 'low';
|
|
453
|
+
}
|
|
454
|
+
const hasCritical = risks.some(r => r.severity === 'critical');
|
|
455
|
+
if (hasCritical) {
|
|
456
|
+
return 'critical';
|
|
457
|
+
}
|
|
458
|
+
const highRisks = risks.filter(r => r.severity === 'high');
|
|
459
|
+
if (highRisks.length >= 2) {
|
|
460
|
+
return 'high';
|
|
461
|
+
}
|
|
462
|
+
if (highRisks.length === 1) {
|
|
463
|
+
return 'high';
|
|
464
|
+
}
|
|
465
|
+
const mediumRisks = risks.filter(r => r.severity === 'medium');
|
|
466
|
+
if (mediumRisks.length >= 3) {
|
|
467
|
+
return 'high';
|
|
468
|
+
}
|
|
469
|
+
if (mediumRisks.length >= 1) {
|
|
470
|
+
return 'medium';
|
|
471
|
+
}
|
|
472
|
+
return 'low';
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Estimate duration in seconds
|
|
476
|
+
*/
|
|
477
|
+
estimateDuration(steps) {
|
|
478
|
+
const durations = {
|
|
479
|
+
validate: 30,
|
|
480
|
+
generate: 60,
|
|
481
|
+
deploy: 600, // 10 minutes for deployment
|
|
482
|
+
verify: 120,
|
|
483
|
+
};
|
|
484
|
+
return steps.reduce((total, step) => {
|
|
485
|
+
return total + (durations[step.type] || 60);
|
|
486
|
+
}, 0);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Estimate cost in USD
|
|
490
|
+
*/
|
|
491
|
+
async estimateCost(task, _steps) {
|
|
492
|
+
let monthlyCost = 0;
|
|
493
|
+
const componentCosts = {
|
|
494
|
+
vpc: 0, // VPC itself is free, NAT gateway costs ~$32/month
|
|
495
|
+
eks: 73, // $0.10/hour * 730 hours
|
|
496
|
+
rds: 50, // t3.micro ~$15/month + storage
|
|
497
|
+
s3: 5, // Minimal storage estimate
|
|
498
|
+
};
|
|
499
|
+
for (const component of task.context.components) {
|
|
500
|
+
monthlyCost += componentCosts[component] || 0;
|
|
501
|
+
}
|
|
502
|
+
// Add NAT gateway cost if VPC is included
|
|
503
|
+
if (task.context.components.includes('vpc')) {
|
|
504
|
+
monthlyCost += 32;
|
|
505
|
+
}
|
|
506
|
+
return Math.round(monthlyCost);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Optimize plan for parallel execution
|
|
510
|
+
*/
|
|
511
|
+
optimizePlan(plan) {
|
|
512
|
+
// Identify steps that can run in parallel
|
|
513
|
+
const optimized = { ...plan };
|
|
514
|
+
// Group independent generation steps
|
|
515
|
+
const generationSteps = plan.steps.filter(s => s.type === 'generate' && s.component);
|
|
516
|
+
// Mark independent steps as parallelizable
|
|
517
|
+
for (let i = 0; i < generationSteps.length; i++) {
|
|
518
|
+
const step = generationSteps[i];
|
|
519
|
+
// If steps don't have interdependencies, they can run in parallel
|
|
520
|
+
if (!this.hasInterdependency(step, generationSteps, i)) {
|
|
521
|
+
step.parameters.parallel_group = 'generation';
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return optimized;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Check if step has interdependency with others
|
|
528
|
+
*/
|
|
529
|
+
hasInterdependency(step, steps, _index) {
|
|
530
|
+
// Check if this step's output is needed by another step in the group
|
|
531
|
+
// Simplified: assume VPC must be created before EKS/RDS
|
|
532
|
+
if (step.component === 'vpc') {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
if (step.component === 'eks' || step.component === 'rds') {
|
|
536
|
+
return steps.some(s => s.component === 'vpc');
|
|
537
|
+
}
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Validate plan is executable
|
|
542
|
+
*/
|
|
543
|
+
validatePlan(plan) {
|
|
544
|
+
const errors = [];
|
|
545
|
+
// Check for circular dependencies
|
|
546
|
+
if (this.hasCircularDependencies(plan)) {
|
|
547
|
+
errors.push('Plan contains circular dependencies');
|
|
548
|
+
}
|
|
549
|
+
// Check all dependencies exist
|
|
550
|
+
const stepIds = new Set(plan.steps.map(s => s.id));
|
|
551
|
+
for (const step of plan.steps) {
|
|
552
|
+
if (step.depends_on) {
|
|
553
|
+
for (const depId of step.depends_on) {
|
|
554
|
+
if (!stepIds.has(depId)) {
|
|
555
|
+
errors.push(`Step ${step.id} depends on non-existent step ${depId}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// Check step order matches dependencies
|
|
561
|
+
for (const step of plan.steps) {
|
|
562
|
+
if (step.depends_on) {
|
|
563
|
+
for (const depId of step.depends_on) {
|
|
564
|
+
const depStep = plan.steps.find(s => s.id === depId);
|
|
565
|
+
if (depStep && depStep.order >= step.order) {
|
|
566
|
+
errors.push(`Step ${step.id} has invalid order relative to dependency ${depId}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
valid: errors.length === 0,
|
|
573
|
+
errors,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Check for circular dependencies
|
|
578
|
+
*/
|
|
579
|
+
hasCircularDependencies(plan) {
|
|
580
|
+
const visited = new Set();
|
|
581
|
+
const recursionStack = new Set();
|
|
582
|
+
const hasCycle = (stepId) => {
|
|
583
|
+
visited.add(stepId);
|
|
584
|
+
recursionStack.add(stepId);
|
|
585
|
+
const step = plan.steps.find(s => s.id === stepId);
|
|
586
|
+
if (step?.depends_on) {
|
|
587
|
+
for (const depId of step.depends_on) {
|
|
588
|
+
if (!visited.has(depId)) {
|
|
589
|
+
if (hasCycle(depId)) {
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
else if (recursionStack.has(depId)) {
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
recursionStack.delete(stepId);
|
|
599
|
+
return false;
|
|
600
|
+
};
|
|
601
|
+
for (const step of plan.steps) {
|
|
602
|
+
if (!visited.has(step.id)) {
|
|
603
|
+
if (hasCycle(step.id)) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Generate plan ID
|
|
612
|
+
*/
|
|
613
|
+
generatePlanId() {
|
|
614
|
+
return `plan_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
615
|
+
}
|
|
616
|
+
}
|