@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,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terraform VPC Demo Scenario
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates creating a VPC with Terraform
|
|
5
|
+
*/
|
|
6
|
+
export const terraformVpcScenario = {
|
|
7
|
+
id: 'terraform-vpc',
|
|
8
|
+
name: 'Create AWS VPC with Terraform',
|
|
9
|
+
description: 'Deploy a complete VPC infrastructure using Nimbus and Terraform',
|
|
10
|
+
category: 'terraform',
|
|
11
|
+
duration: 10,
|
|
12
|
+
prerequisites: [
|
|
13
|
+
'AWS CLI configured with credentials',
|
|
14
|
+
'Terraform installed (v1.0+)',
|
|
15
|
+
'Nimbus CLI installed',
|
|
16
|
+
],
|
|
17
|
+
tags: ['terraform', 'aws', 'vpc', 'networking'],
|
|
18
|
+
steps: [
|
|
19
|
+
{
|
|
20
|
+
id: 'init-project',
|
|
21
|
+
title: 'Initialize Nimbus Project',
|
|
22
|
+
description: 'Scan the current directory and set up Nimbus configuration',
|
|
23
|
+
command: 'nimbus init',
|
|
24
|
+
showOutput: true,
|
|
25
|
+
waitForInput: true,
|
|
26
|
+
mockResponse: `
|
|
27
|
+
Nimbus Initialization
|
|
28
|
+
|
|
29
|
+
Scanning project...
|
|
30
|
+
|
|
31
|
+
Project Summary:
|
|
32
|
+
Type: infrastructure
|
|
33
|
+
Languages: HCL
|
|
34
|
+
IaC Tools: Terraform
|
|
35
|
+
Cloud: AWS
|
|
36
|
+
|
|
37
|
+
Created .nimbus/project.yaml
|
|
38
|
+
Created .nimbus/config.yaml
|
|
39
|
+
|
|
40
|
+
Project initialized successfully!
|
|
41
|
+
`.trim(),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'start-questionnaire',
|
|
45
|
+
title: 'Start Terraform Questionnaire',
|
|
46
|
+
description: 'Use the interactive wizard to configure VPC settings',
|
|
47
|
+
command: 'nimbus questionnaire terraform',
|
|
48
|
+
showOutput: true,
|
|
49
|
+
waitForInput: true,
|
|
50
|
+
mockResponse: `
|
|
51
|
+
Terraform Configuration Wizard
|
|
52
|
+
|
|
53
|
+
Starting local questionnaire...
|
|
54
|
+
|
|
55
|
+
Step 1/3: Provider Configuration
|
|
56
|
+
Provider: aws
|
|
57
|
+
Region: us-east-1
|
|
58
|
+
|
|
59
|
+
Step 2/3: Component Selection
|
|
60
|
+
Components: vpc
|
|
61
|
+
|
|
62
|
+
Step 3/3: VPC Configuration
|
|
63
|
+
CIDR Block: 10.0.0.0/16
|
|
64
|
+
Availability Zones: 3
|
|
65
|
+
|
|
66
|
+
Questionnaire completed!
|
|
67
|
+
Generating code...
|
|
68
|
+
|
|
69
|
+
Generated files:
|
|
70
|
+
● main.tf
|
|
71
|
+
● variables.tf
|
|
72
|
+
● outputs.tf
|
|
73
|
+
|
|
74
|
+
Output directory: ./terraform
|
|
75
|
+
`.trim(),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'preview-plan',
|
|
79
|
+
title: 'Preview Terraform Plan',
|
|
80
|
+
description: 'See what changes will be made before applying',
|
|
81
|
+
command: 'nimbus preview terraform ./terraform',
|
|
82
|
+
showOutput: true,
|
|
83
|
+
waitForInput: true,
|
|
84
|
+
mockResponse: `
|
|
85
|
+
Preview Terraform Changes
|
|
86
|
+
|
|
87
|
+
Directory: ./terraform
|
|
88
|
+
|
|
89
|
+
Creating execution plan...
|
|
90
|
+
Plan created
|
|
91
|
+
|
|
92
|
+
Plan Summary:
|
|
93
|
+
|
|
94
|
+
+ 6 to add
|
|
95
|
+
~ 0 to change
|
|
96
|
+
- 0 to destroy
|
|
97
|
+
|
|
98
|
+
Safety Check Summary:
|
|
99
|
+
|
|
100
|
+
🟡 [MEDIUM] This operation will modify infrastructure
|
|
101
|
+
|
|
102
|
+
All safety checks passed
|
|
103
|
+
`.trim(),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'apply-terraform',
|
|
107
|
+
title: 'Apply Terraform Configuration',
|
|
108
|
+
description: 'Deploy the VPC infrastructure with safety approval',
|
|
109
|
+
command: 'nimbus apply terraform ./terraform',
|
|
110
|
+
showOutput: true,
|
|
111
|
+
waitForInput: true,
|
|
112
|
+
mockResponse: `
|
|
113
|
+
Terraform Apply
|
|
114
|
+
|
|
115
|
+
Directory: ./terraform
|
|
116
|
+
|
|
117
|
+
Creating execution plan...
|
|
118
|
+
Plan created
|
|
119
|
+
|
|
120
|
+
Plan Summary:
|
|
121
|
+
+ 6 to add
|
|
122
|
+
|
|
123
|
+
╔══════════════════════════════════════════════════════════╗
|
|
124
|
+
║ APPROVAL REQUIRED ║
|
|
125
|
+
╠══════════════════════════════════════════════════════════╣
|
|
126
|
+
║ Operation: terraform apply ║
|
|
127
|
+
╚══════════════════════════════════════════════════════════╝
|
|
128
|
+
|
|
129
|
+
Identified Risks:
|
|
130
|
+
|
|
131
|
+
🟡 [MEDIUM] This operation will modify infrastructure
|
|
132
|
+
|
|
133
|
+
Do you want to proceed with this operation? Yes
|
|
134
|
+
|
|
135
|
+
Operation approved
|
|
136
|
+
|
|
137
|
+
Applying changes...
|
|
138
|
+
Apply complete!
|
|
139
|
+
|
|
140
|
+
Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
|
|
141
|
+
|
|
142
|
+
Outputs:
|
|
143
|
+
vpc_id = "vpc-0abc123def456789"
|
|
144
|
+
private_subnets = ["subnet-001", "subnet-002", "subnet-003"]
|
|
145
|
+
public_subnets = ["subnet-101", "subnet-102", "subnet-103"]
|
|
146
|
+
`.trim(),
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'verify-vpc',
|
|
150
|
+
title: 'Verify VPC Creation',
|
|
151
|
+
description: 'List VPCs to confirm our new VPC was created',
|
|
152
|
+
command: 'nimbus aws vpc list',
|
|
153
|
+
showOutput: true,
|
|
154
|
+
waitForInput: false,
|
|
155
|
+
mockResponse: `
|
|
156
|
+
VPCs
|
|
157
|
+
|
|
158
|
+
Found 2 VPC(s)
|
|
159
|
+
|
|
160
|
+
VPC ID Name CIDR State Default
|
|
161
|
+
─────────────────────────────────────────────────────────────────────
|
|
162
|
+
vpc-default default 172.31.0.0/16 available Yes
|
|
163
|
+
vpc-0abc123def456789 my-project 10.0.0.0/16 available No
|
|
164
|
+
`.trim(),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Estimator
|
|
3
|
+
*
|
|
4
|
+
* Standalone module for estimating monthly and annual infrastructure costs
|
|
5
|
+
* based on component type, environment, region, and usage patterns.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from the cost estimation logic in verifier.ts and planner.ts,
|
|
8
|
+
* with expanded capabilities for detailed breakdowns and recommendations.
|
|
9
|
+
*/
|
|
10
|
+
import { logger } from '../utils';
|
|
11
|
+
// ==========================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ==========================================
|
|
14
|
+
/**
|
|
15
|
+
* Base monthly component costs in USD.
|
|
16
|
+
*
|
|
17
|
+
* These are intentionally conservative estimates for the most common
|
|
18
|
+
* instance sizes and usage patterns. Extracted directly from the
|
|
19
|
+
* original verifier.ts cost table and expanded for additional components.
|
|
20
|
+
*
|
|
21
|
+
* Sources:
|
|
22
|
+
* vpc: NAT Gateway ($0.045/h * 730h) ~= $32
|
|
23
|
+
* eks: Control plane only ($0.10/h * 730h) ~= $73
|
|
24
|
+
* rds: db.t3.micro ($0.017/h) + 20GB storage (~$2.30) ~= $15; rounded to $50 with Multi-AZ
|
|
25
|
+
* s3: Minimal storage estimate (< 100GB) ~= $5
|
|
26
|
+
*/
|
|
27
|
+
const BASE_COMPONENT_COSTS = {
|
|
28
|
+
// AWS Compute
|
|
29
|
+
vpc: 32,
|
|
30
|
+
eks: 73,
|
|
31
|
+
ecs: 30,
|
|
32
|
+
lambda: 2,
|
|
33
|
+
// EC2 instance types (monthly on-demand us-east-1)
|
|
34
|
+
't3.micro': 8,
|
|
35
|
+
't3.small': 16,
|
|
36
|
+
't3.medium': 32,
|
|
37
|
+
't3.large': 65,
|
|
38
|
+
'm5.large': 70,
|
|
39
|
+
'm5.xlarge': 140,
|
|
40
|
+
'm5.2xlarge': 280,
|
|
41
|
+
'c5.large': 62,
|
|
42
|
+
'c5.xlarge': 124,
|
|
43
|
+
'r5.large': 91,
|
|
44
|
+
'r5.xlarge': 182,
|
|
45
|
+
// RDS instance types (monthly, single-AZ)
|
|
46
|
+
'db.t3.micro': 14,
|
|
47
|
+
'db.t3.small': 28,
|
|
48
|
+
'db.t3.medium': 56,
|
|
49
|
+
'db.r5.large': 175,
|
|
50
|
+
'db.r5.xlarge': 350,
|
|
51
|
+
// AWS Data
|
|
52
|
+
rds: 50,
|
|
53
|
+
s3: 5,
|
|
54
|
+
elasticache: 25,
|
|
55
|
+
sqs: 1,
|
|
56
|
+
sns: 1,
|
|
57
|
+
cloudfront: 10,
|
|
58
|
+
// AWS Network/LB
|
|
59
|
+
'aws_nat_gateway': 32,
|
|
60
|
+
'aws_lb': 25,
|
|
61
|
+
'aws_alb': 25,
|
|
62
|
+
'aws_instance': 30,
|
|
63
|
+
'aws_db_instance': 50,
|
|
64
|
+
'aws_s3_bucket': 5,
|
|
65
|
+
'aws_eks_cluster': 73,
|
|
66
|
+
'aws_elasticache_cluster': 25,
|
|
67
|
+
'aws_rds_cluster': 50,
|
|
68
|
+
'aws_lambda_function': 2,
|
|
69
|
+
'aws_cloudfront_distribution': 10,
|
|
70
|
+
// GCP
|
|
71
|
+
'google_compute_instance': 30,
|
|
72
|
+
'google_container_cluster': 73,
|
|
73
|
+
'google_sql_database_instance': 50,
|
|
74
|
+
'google_storage_bucket': 5,
|
|
75
|
+
// Azure
|
|
76
|
+
'azurerm_virtual_machine': 30,
|
|
77
|
+
'azurerm_kubernetes_cluster': 73,
|
|
78
|
+
'azurerm_sql_database': 50,
|
|
79
|
+
'azurerm_storage_account': 5,
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Regional cost multipliers relative to us-east-1 (base = 1.0).
|
|
83
|
+
* Approximate — derived from AWS published pricing differentials.
|
|
84
|
+
*/
|
|
85
|
+
const REGIONAL_MULTIPLIERS = {
|
|
86
|
+
'us-east-1': 1.0,
|
|
87
|
+
'us-east-2': 1.0,
|
|
88
|
+
'us-west-1': 1.08,
|
|
89
|
+
'us-west-2': 1.0,
|
|
90
|
+
'eu-west-1': 1.06,
|
|
91
|
+
'eu-west-2': 1.1,
|
|
92
|
+
'eu-central-1': 1.08,
|
|
93
|
+
'ap-southeast-1': 1.14,
|
|
94
|
+
'ap-northeast-1': 1.16,
|
|
95
|
+
'ap-south-1': 1.05,
|
|
96
|
+
'sa-east-1': 1.2,
|
|
97
|
+
'ca-central-1': 1.06,
|
|
98
|
+
// GCP regions (approximate relative costs)
|
|
99
|
+
'us-central1': 1.0,
|
|
100
|
+
'us-east1': 1.0,
|
|
101
|
+
'europe-west1': 1.08,
|
|
102
|
+
'asia-east1': 1.12,
|
|
103
|
+
// Azure regions
|
|
104
|
+
eastus: 1.0,
|
|
105
|
+
westus: 1.05,
|
|
106
|
+
westeurope: 1.1,
|
|
107
|
+
southeastasia: 1.12,
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Environment multipliers.
|
|
111
|
+
* Production typically uses larger, HA-ready instances; dev uses minimal sizes.
|
|
112
|
+
*/
|
|
113
|
+
const ENVIRONMENT_MULTIPLIERS = {
|
|
114
|
+
development: 0.5,
|
|
115
|
+
staging: 0.75,
|
|
116
|
+
production: 1.0,
|
|
117
|
+
prod: 1.0,
|
|
118
|
+
dev: 0.5,
|
|
119
|
+
staging_: 0.75,
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Human-readable notes per component explaining the cost assumption.
|
|
123
|
+
*/
|
|
124
|
+
const COMPONENT_NOTES = {
|
|
125
|
+
vpc: 'NAT Gateway ($0.045/h) — one AZ; add $32/mo per additional AZ',
|
|
126
|
+
eks: 'EKS control plane only ($0.10/h); node group EC2 costs are additive',
|
|
127
|
+
rds: 'db.t3.micro Multi-AZ estimated; scales significantly with instance class',
|
|
128
|
+
s3: 'Minimal estimate (<100GB, <1M requests); review lifecycle policies for long-term savings',
|
|
129
|
+
ecs: 'Fargate minimal workload; scales linearly with vCPU and memory allocation',
|
|
130
|
+
lambda: 'Under 1M invocations/month; free tier may apply',
|
|
131
|
+
cloudfront: 'Under 1TB egress/month; varies heavily with traffic patterns',
|
|
132
|
+
elasticache: 'cache.t3.micro; consider Reserved Nodes for >30% savings in production',
|
|
133
|
+
sqs: 'Under 1M requests/month; near-zero cost at small scale',
|
|
134
|
+
sns: 'Under 1M notifications/month; near-zero cost at small scale',
|
|
135
|
+
};
|
|
136
|
+
// ==========================================
|
|
137
|
+
// CostEstimator
|
|
138
|
+
// ==========================================
|
|
139
|
+
export class CostEstimator {
|
|
140
|
+
/**
|
|
141
|
+
* Estimate the monthly and annual cost for a given set of components.
|
|
142
|
+
*/
|
|
143
|
+
estimate(input) {
|
|
144
|
+
const environment = input.environment || 'production';
|
|
145
|
+
const provider = input.provider || 'aws';
|
|
146
|
+
const region = input.region || this.defaultRegion(provider);
|
|
147
|
+
const budgetLimit = input.budgetLimit;
|
|
148
|
+
logger.info(`Estimating cost for ${input.components.length} components ` +
|
|
149
|
+
`(env=${environment}, provider=${provider}, region=${region})`);
|
|
150
|
+
const envMultiplier = this.resolveEnvironmentMultiplier(environment);
|
|
151
|
+
const regionMultiplier = this.resolveRegionalMultiplier(region);
|
|
152
|
+
const breakdown = [];
|
|
153
|
+
for (const component of input.components) {
|
|
154
|
+
const baseCost = this.resolveBaseCost(component, input.customCosts);
|
|
155
|
+
const adjustedCost = Math.round(baseCost * envMultiplier * regionMultiplier * 100) / 100;
|
|
156
|
+
breakdown.push({
|
|
157
|
+
component,
|
|
158
|
+
baseMonthlyCost: baseCost,
|
|
159
|
+
adjustedMonthlyCost: adjustedCost,
|
|
160
|
+
environmentMultiplier: envMultiplier,
|
|
161
|
+
regionalMultiplier: regionMultiplier,
|
|
162
|
+
notes: COMPONENT_NOTES[component] ||
|
|
163
|
+
`Estimate for ${component}; verify against provider pricing`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const totalMonthlyCost = Math.round(breakdown.reduce((sum, b) => sum + b.adjustedMonthlyCost, 0) * 100) / 100;
|
|
167
|
+
const totalAnnualCost = Math.round(totalMonthlyCost * 12 * 100) / 100;
|
|
168
|
+
const withinBudget = budgetLimit !== undefined ? totalMonthlyCost <= budgetLimit : true;
|
|
169
|
+
const recommendations = this.generateRecommendations(input, breakdown, totalMonthlyCost);
|
|
170
|
+
logger.info(`Cost estimate: $${totalMonthlyCost}/mo ($${totalAnnualCost}/yr); ` +
|
|
171
|
+
`${withinBudget ? 'within' : 'exceeds'} budget`);
|
|
172
|
+
return {
|
|
173
|
+
totalMonthlyCost,
|
|
174
|
+
totalAnnualCost,
|
|
175
|
+
breakdown,
|
|
176
|
+
withinBudget,
|
|
177
|
+
budgetLimit,
|
|
178
|
+
recommendations,
|
|
179
|
+
environment,
|
|
180
|
+
provider,
|
|
181
|
+
generatedAt: new Date(),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Estimate cost from a flat context object (compatible with verifier/planner usage).
|
|
186
|
+
*
|
|
187
|
+
* This is a convenience wrapper that accepts the same `context` shape used
|
|
188
|
+
* by the Verifier's `runCostChecks` method, allowing the CostEstimator to
|
|
189
|
+
* serve as a drop-in replacement.
|
|
190
|
+
*/
|
|
191
|
+
estimateFromContext(context) {
|
|
192
|
+
const components = context.components || [];
|
|
193
|
+
const result = this.estimate({
|
|
194
|
+
components,
|
|
195
|
+
environment: context.environment || 'production',
|
|
196
|
+
provider: context.provider || 'aws',
|
|
197
|
+
region: context.region,
|
|
198
|
+
budgetLimit: context.budget_limit,
|
|
199
|
+
});
|
|
200
|
+
return result.totalMonthlyCost;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Format a CostEstimate as a Markdown report.
|
|
204
|
+
*/
|
|
205
|
+
formatAsMarkdown(estimate) {
|
|
206
|
+
const lines = [
|
|
207
|
+
`# Infrastructure Cost Estimate`,
|
|
208
|
+
``,
|
|
209
|
+
`**Environment:** ${estimate.environment}`,
|
|
210
|
+
`**Provider:** ${estimate.provider}`,
|
|
211
|
+
`**Generated:** ${estimate.generatedAt.toISOString()}`,
|
|
212
|
+
``,
|
|
213
|
+
`## Summary`,
|
|
214
|
+
``,
|
|
215
|
+
`| Metric | Value |`,
|
|
216
|
+
`|--------|-------|`,
|
|
217
|
+
`| Monthly Cost | $${estimate.totalMonthlyCost.toFixed(2)} |`,
|
|
218
|
+
`| Annual Cost | $${estimate.totalAnnualCost.toFixed(2)} |`,
|
|
219
|
+
];
|
|
220
|
+
if (estimate.budgetLimit !== undefined) {
|
|
221
|
+
lines.push(`| Budget Limit | $${estimate.budgetLimit.toFixed(2)} |`, `| Status | ${estimate.withinBudget ? 'Within Budget' : 'Over Budget'} |`);
|
|
222
|
+
}
|
|
223
|
+
lines.push(``, `## Component Breakdown`, ``, `| Component | Base ($/mo) | Adjusted ($/mo) | Notes |`, `|-----------|------------|----------------|-------|`);
|
|
224
|
+
for (const b of estimate.breakdown) {
|
|
225
|
+
lines.push(`| ${b.component} | $${b.baseMonthlyCost.toFixed(2)} | $${b.adjustedMonthlyCost.toFixed(2)} | ${b.notes} |`);
|
|
226
|
+
}
|
|
227
|
+
if (estimate.recommendations.length > 0) {
|
|
228
|
+
lines.push(``, `## Recommendations`, ``);
|
|
229
|
+
for (const rec of estimate.recommendations) {
|
|
230
|
+
lines.push(`- ${rec}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return lines.join('\n');
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Generate cost optimisation recommendations based on the estimate inputs and results.
|
|
237
|
+
*/
|
|
238
|
+
generateRecommendations(input, breakdown, totalMonthlyCost) {
|
|
239
|
+
const recommendations = [];
|
|
240
|
+
const env = (input.environment || 'production').toLowerCase();
|
|
241
|
+
const components = input.components.map(c => c.toLowerCase());
|
|
242
|
+
// Production: Reserved instances
|
|
243
|
+
if (env === 'production' || env === 'prod') {
|
|
244
|
+
const hasExpensive = components.some(c => ['eks', 'rds', 'elasticache'].includes(c));
|
|
245
|
+
if (hasExpensive) {
|
|
246
|
+
recommendations.push('Consider Reserved Instances or Savings Plans for EKS nodes, RDS, and ElastiCache — ' +
|
|
247
|
+
'typically 30-40% savings over on-demand pricing with 1-year commitments.');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Non-production: Single NAT gateway
|
|
251
|
+
if (env !== 'production' && env !== 'prod' && components.includes('vpc')) {
|
|
252
|
+
recommendations.push('Use a single NAT Gateway in non-production environments to save ~$32/mo per additional AZ.');
|
|
253
|
+
}
|
|
254
|
+
// S3 lifecycle policies
|
|
255
|
+
if (components.includes('s3')) {
|
|
256
|
+
recommendations.push('Configure S3 Lifecycle policies to transition infrequently accessed objects to ' +
|
|
257
|
+
'S3 Intelligent-Tiering or Glacier; can reduce storage costs by 40-60%.');
|
|
258
|
+
}
|
|
259
|
+
// Development: spot instances
|
|
260
|
+
if (env === 'development' || env === 'dev') {
|
|
261
|
+
if (components.some(c => ['eks', 'ecs'].includes(c))) {
|
|
262
|
+
recommendations.push('Use Spot Instances for development EKS node groups and ECS Fargate Spot — ' +
|
|
263
|
+
'up to 90% savings with appropriate interruption handling.');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// High overall cost warning
|
|
267
|
+
if (totalMonthlyCost > 1000) {
|
|
268
|
+
recommendations.push(`Total monthly cost $${totalMonthlyCost.toFixed(2)} is significant. ` +
|
|
269
|
+
'Review instance types, enable autoscaling, and run AWS Cost Explorer or GCP Cost Management ' +
|
|
270
|
+
'to identify unexpected spend.');
|
|
271
|
+
}
|
|
272
|
+
// Lambda: evaluate if replacing always-on compute
|
|
273
|
+
if (components.includes('lambda') &&
|
|
274
|
+
!components.includes('ecs') &&
|
|
275
|
+
!components.includes('eks')) {
|
|
276
|
+
recommendations.push('Lambda-only architecture is cost-efficient at low request volumes. ' +
|
|
277
|
+
'Monitor concurrency limits and cold-start latency as traffic grows.');
|
|
278
|
+
}
|
|
279
|
+
// EKS without VPC
|
|
280
|
+
if (components.includes('eks') && !components.includes('vpc')) {
|
|
281
|
+
recommendations.push('EKS clusters require a VPC. If using an existing VPC, ensure its NAT Gateway costs are ' +
|
|
282
|
+
'accounted for separately (typically +$32/mo per AZ).');
|
|
283
|
+
}
|
|
284
|
+
return recommendations;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Resolve base cost for a component, respecting custom overrides.
|
|
288
|
+
*/
|
|
289
|
+
resolveBaseCost(component, customCosts) {
|
|
290
|
+
if (customCosts && component in customCosts) {
|
|
291
|
+
return customCosts[component];
|
|
292
|
+
}
|
|
293
|
+
return BASE_COMPONENT_COSTS[component.toLowerCase()] ?? 0;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Resolve environment multiplier from an environment string.
|
|
297
|
+
*/
|
|
298
|
+
resolveEnvironmentMultiplier(environment) {
|
|
299
|
+
const key = environment.toLowerCase();
|
|
300
|
+
return ENVIRONMENT_MULTIPLIERS[key] ?? 1.0;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Resolve regional multiplier from a region string.
|
|
304
|
+
*/
|
|
305
|
+
resolveRegionalMultiplier(region) {
|
|
306
|
+
return REGIONAL_MULTIPLIERS[region] ?? 1.0;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Return the default region for a given provider.
|
|
310
|
+
*/
|
|
311
|
+
defaultRegion(provider) {
|
|
312
|
+
switch (provider.toLowerCase()) {
|
|
313
|
+
case 'gcp':
|
|
314
|
+
return 'us-central1';
|
|
315
|
+
case 'azure':
|
|
316
|
+
return 'eastus';
|
|
317
|
+
case 'aws':
|
|
318
|
+
default:
|
|
319
|
+
return 'us-east-1';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Get the base cost table (useful for display or testing).
|
|
324
|
+
*/
|
|
325
|
+
getBaseCostTable() {
|
|
326
|
+
return { ...BASE_COMPONENT_COSTS };
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Get the regional multiplier table (useful for display or testing).
|
|
330
|
+
*/
|
|
331
|
+
getRegionalMultiplierTable() {
|
|
332
|
+
return { ...REGIONAL_MULTIPLIERS };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ASCII Diagram Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates architecture diagrams using box-drawing characters.
|
|
5
|
+
* No external dependencies — pure string manipulation.
|
|
6
|
+
*
|
|
7
|
+
* Embedded version: identical to services/core-engine-service/src/components/diagram-generator.ts
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generates real ASCII architecture diagrams from component/connection data.
|
|
11
|
+
*/
|
|
12
|
+
export class DiagramGenerator {
|
|
13
|
+
boxWidth;
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.boxWidth = options.boxWidth || 24;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate an ASCII architecture diagram.
|
|
19
|
+
*/
|
|
20
|
+
generate(components, connections, title) {
|
|
21
|
+
if (components.length === 0) {
|
|
22
|
+
return '(no components)';
|
|
23
|
+
}
|
|
24
|
+
const lines = [];
|
|
25
|
+
// Title
|
|
26
|
+
if (title) {
|
|
27
|
+
lines.push(title);
|
|
28
|
+
lines.push('='.repeat(title.length));
|
|
29
|
+
lines.push('');
|
|
30
|
+
}
|
|
31
|
+
// Build adjacency for topological ordering
|
|
32
|
+
const adj = new Map();
|
|
33
|
+
const inDegree = new Map();
|
|
34
|
+
for (const c of components) {
|
|
35
|
+
adj.set(c.id, []);
|
|
36
|
+
inDegree.set(c.id, 0);
|
|
37
|
+
}
|
|
38
|
+
for (const conn of connections) {
|
|
39
|
+
adj.get(conn.from)?.push(conn.to);
|
|
40
|
+
inDegree.set(conn.to, (inDegree.get(conn.to) || 0) + 1);
|
|
41
|
+
}
|
|
42
|
+
// Topological sort (Kahn's algorithm) to determine row placement
|
|
43
|
+
const rows = [];
|
|
44
|
+
let queue = components.filter(c => (inDegree.get(c.id) || 0) === 0);
|
|
45
|
+
const placed = new Set();
|
|
46
|
+
while (queue.length > 0) {
|
|
47
|
+
rows.push([...queue]);
|
|
48
|
+
const nextQueue = [];
|
|
49
|
+
for (const node of queue) {
|
|
50
|
+
placed.add(node.id);
|
|
51
|
+
for (const neighbor of adj.get(node.id) || []) {
|
|
52
|
+
inDegree.set(neighbor, (inDegree.get(neighbor) || 0) - 1);
|
|
53
|
+
if (inDegree.get(neighbor) === 0 && !placed.has(neighbor)) {
|
|
54
|
+
const comp = components.find(c => c.id === neighbor);
|
|
55
|
+
if (comp) {
|
|
56
|
+
nextQueue.push(comp);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
queue = nextQueue;
|
|
62
|
+
}
|
|
63
|
+
// Add any unplaced components (cycles) to the last row
|
|
64
|
+
for (const c of components) {
|
|
65
|
+
if (!placed.has(c.id)) {
|
|
66
|
+
if (rows.length === 0) {
|
|
67
|
+
rows.push([]);
|
|
68
|
+
}
|
|
69
|
+
rows[rows.length - 1].push(c);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Track center positions for drawing connections
|
|
73
|
+
const colWidth = this.boxWidth + 4;
|
|
74
|
+
// Render rows of boxes
|
|
75
|
+
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
76
|
+
const row = rows[rowIdx];
|
|
77
|
+
const rowLines = this.renderBoxRow(row, colWidth);
|
|
78
|
+
for (const line of rowLines) {
|
|
79
|
+
lines.push(line);
|
|
80
|
+
}
|
|
81
|
+
// Draw vertical connection arrows between rows
|
|
82
|
+
if (rowIdx < rows.length - 1) {
|
|
83
|
+
const nextRow = rows[rowIdx + 1];
|
|
84
|
+
const arrowLines = this.renderArrows(row, nextRow, connections, colWidth);
|
|
85
|
+
for (const line of arrowLines) {
|
|
86
|
+
lines.push(line);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Legend for connections
|
|
91
|
+
if (connections.length > 0) {
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push('Connections:');
|
|
94
|
+
for (const conn of connections) {
|
|
95
|
+
const fromComp = components.find(c => c.id === conn.from);
|
|
96
|
+
const toComp = components.find(c => c.id === conn.to);
|
|
97
|
+
if (fromComp && toComp) {
|
|
98
|
+
const label = conn.label ? ` (${conn.label})` : '';
|
|
99
|
+
lines.push(` ${fromComp.label} --> ${toComp.label}${label}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return lines.join('\n');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Render a row of component boxes.
|
|
107
|
+
*/
|
|
108
|
+
renderBoxRow(components, colWidth) {
|
|
109
|
+
const w = this.boxWidth;
|
|
110
|
+
const top = `+${'-'.repeat(w)}+`;
|
|
111
|
+
const bot = `+${'-'.repeat(w)}+`;
|
|
112
|
+
const topLine = [];
|
|
113
|
+
const labelLine = [];
|
|
114
|
+
const typeLine = [];
|
|
115
|
+
const botLine = [];
|
|
116
|
+
for (const comp of components) {
|
|
117
|
+
const padding = colWidth - w - 2;
|
|
118
|
+
const pad = ' '.repeat(Math.max(0, padding / 2));
|
|
119
|
+
topLine.push(pad + top + pad);
|
|
120
|
+
const truncLabel = comp.label.length > w - 2 ? `${comp.label.substring(0, w - 5)}...` : comp.label;
|
|
121
|
+
const labelPadded = truncLabel.padStart(Math.floor((w + truncLabel.length) / 2)).padEnd(w);
|
|
122
|
+
labelLine.push(`${pad}|${labelPadded}|${pad}`);
|
|
123
|
+
if (comp.type) {
|
|
124
|
+
const truncType = comp.type.length > w - 2 ? `${comp.type.substring(0, w - 5)}...` : comp.type;
|
|
125
|
+
const typePadded = truncType.padStart(Math.floor((w + truncType.length) / 2)).padEnd(w);
|
|
126
|
+
typeLine.push(`${pad}|${typePadded}|${pad}`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
typeLine.push(`${pad}|${' '.repeat(w)}|${pad}`);
|
|
130
|
+
}
|
|
131
|
+
botLine.push(pad + bot + pad);
|
|
132
|
+
}
|
|
133
|
+
return [topLine.join(''), labelLine.join(''), typeLine.join(''), botLine.join('')];
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Render vertical arrows between two rows of components.
|
|
137
|
+
*/
|
|
138
|
+
renderArrows(fromRow, toRow, connections, colWidth) {
|
|
139
|
+
const totalCols = Math.max(fromRow.length, toRow.length);
|
|
140
|
+
const lineWidth = totalCols * colWidth;
|
|
141
|
+
// Find which connections go between these rows
|
|
142
|
+
const fromIds = new Set(fromRow.map(c => c.id));
|
|
143
|
+
const toIds = new Set(toRow.map(c => c.id));
|
|
144
|
+
const activeConns = connections.filter(c => fromIds.has(c.from) && toIds.has(c.to));
|
|
145
|
+
if (activeConns.length === 0) {
|
|
146
|
+
return [''];
|
|
147
|
+
}
|
|
148
|
+
// Draw simple vertical arrows at the center of each from-component
|
|
149
|
+
const arrowRow1 = new Array(lineWidth).fill(' ');
|
|
150
|
+
const arrowRow2 = new Array(lineWidth).fill(' ');
|
|
151
|
+
const arrowRow3 = new Array(lineWidth).fill(' ');
|
|
152
|
+
for (const conn of activeConns) {
|
|
153
|
+
const fromIdx = fromRow.findIndex(c => c.id === conn.from);
|
|
154
|
+
if (fromIdx >= 0) {
|
|
155
|
+
const center = Math.floor(fromIdx * colWidth + colWidth / 2);
|
|
156
|
+
if (center < lineWidth) {
|
|
157
|
+
arrowRow1[center] = '|';
|
|
158
|
+
arrowRow2[center] = '|';
|
|
159
|
+
arrowRow3[center] = 'V';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return [
|
|
164
|
+
arrowRow1.join('').trimEnd(),
|
|
165
|
+
arrowRow2.join('').trimEnd(),
|
|
166
|
+
arrowRow3.join('').trimEnd(),
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Generate a diagram for infrastructure components.
|
|
171
|
+
* Convenience method that maps common infra component names to diagram elements.
|
|
172
|
+
*/
|
|
173
|
+
generateInfrastructureDiagram(componentNames, provider = 'aws') {
|
|
174
|
+
const components = componentNames.map(name => ({
|
|
175
|
+
id: name,
|
|
176
|
+
label: name.toUpperCase(),
|
|
177
|
+
type: provider.toUpperCase(),
|
|
178
|
+
}));
|
|
179
|
+
// Infer common connections
|
|
180
|
+
const connections = [];
|
|
181
|
+
const hasVpc = componentNames.includes('vpc');
|
|
182
|
+
for (const name of componentNames) {
|
|
183
|
+
if (hasVpc && name !== 'vpc' && ['eks', 'rds', 'ecs', 'lambda'].includes(name)) {
|
|
184
|
+
connections.push({ from: 'vpc', to: name, label: 'network' });
|
|
185
|
+
}
|
|
186
|
+
if (name === 'eks' && componentNames.includes('rds')) {
|
|
187
|
+
connections.push({ from: 'eks', to: 'rds', label: 'database' });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return this.generate(components, connections, `${provider.toUpperCase()} Infrastructure Architecture`);
|
|
191
|
+
}
|
|
192
|
+
}
|