@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
|
@@ -419,11 +419,17 @@ export async function costCommand(args: string[]): Promise<void> {
|
|
|
419
419
|
ui.newLine();
|
|
420
420
|
ui.print('Commands:');
|
|
421
421
|
ui.print(` ${ui.bold('estimate')} Estimate infrastructure costs from Terraform`);
|
|
422
|
+
ui.print(` ${ui.bold('compare')} Compare cost between two workspaces or directories`);
|
|
423
|
+
ui.print(` ${ui.bold('report')} Export session cost summary`);
|
|
422
424
|
ui.print(` ${ui.bold('history')} View historical cost data`);
|
|
425
|
+
ui.print(` ${ui.bold('diff')} Diff cost between two paths`);
|
|
423
426
|
ui.newLine();
|
|
424
427
|
ui.print('Examples:');
|
|
425
428
|
ui.print(' nimbus cost estimate');
|
|
429
|
+
ui.print(' nimbus cost estimate --workspace staging --provider aws');
|
|
426
430
|
ui.print(' nimbus cost estimate -d ./terraform --detailed');
|
|
431
|
+
ui.print(' nimbus cost compare ./workspace1 ./workspace2');
|
|
432
|
+
ui.print(' nimbus cost report --output csv');
|
|
427
433
|
ui.print(' nimbus cost history --days 30 --group-by service');
|
|
428
434
|
return;
|
|
429
435
|
}
|
|
@@ -432,12 +438,55 @@ export async function costCommand(args: string[]): Promise<void> {
|
|
|
432
438
|
const subArgs = args.slice(1);
|
|
433
439
|
|
|
434
440
|
switch (subcommand) {
|
|
435
|
-
case 'estimate':
|
|
436
|
-
|
|
441
|
+
case 'estimate': {
|
|
442
|
+
const opts = parseCostEstimateOptions(subArgs);
|
|
443
|
+
// M3: handle --workspace and --provider flags for standalone estimate
|
|
444
|
+
for (let i = 0; i < subArgs.length; i++) {
|
|
445
|
+
if (subArgs[i] === '--workspace' && subArgs[i + 1]) {
|
|
446
|
+
opts.directory = opts.directory ?? subArgs[i + 1];
|
|
447
|
+
}
|
|
448
|
+
if (subArgs[i] === '--provider' && subArgs[i + 1]) {
|
|
449
|
+
// Provider hint is informational — store in compare for future use
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
await costEstimateCommand(opts);
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
case 'compare': {
|
|
456
|
+
// M3: nimbus cost compare <workspace1> <workspace2>
|
|
457
|
+
const path1 = subArgs[0];
|
|
458
|
+
const path2 = subArgs[1];
|
|
459
|
+
if (!path1 || !path2) {
|
|
460
|
+
ui.error('Usage: nimbus cost compare <workspace1> <workspace2>');
|
|
461
|
+
ui.info('Compares infracost estimates between two directories.');
|
|
462
|
+
process.exit(1);
|
|
463
|
+
}
|
|
464
|
+
const format = subArgs.includes('--json') ? 'json' : 'table';
|
|
465
|
+
await costCompareCommand(path1, path2, { format });
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case 'report': {
|
|
469
|
+
// M3: nimbus cost report [--output csv|json|text]
|
|
470
|
+
let outputFormat: 'csv' | 'json' | 'text' = 'text';
|
|
471
|
+
for (let i = 0; i < subArgs.length; i++) {
|
|
472
|
+
if (subArgs[i] === '--output' && subArgs[i + 1]) {
|
|
473
|
+
const fmt = subArgs[i + 1];
|
|
474
|
+
if (fmt === 'csv' || fmt === 'json' || fmt === 'text') {
|
|
475
|
+
outputFormat = fmt;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
await costReportCommand(outputFormat);
|
|
437
480
|
break;
|
|
481
|
+
}
|
|
438
482
|
case 'history':
|
|
439
483
|
await costHistoryCommand(parseCostHistoryOptions(subArgs));
|
|
440
484
|
break;
|
|
485
|
+
case 'diff': {
|
|
486
|
+
const format = subArgs.includes('--json') ? 'json' : 'table';
|
|
487
|
+
await costDiffCommand(subArgs[0], subArgs[1], { format });
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
441
490
|
default:
|
|
442
491
|
ui.error(`Unknown cost command: ${subcommand}`);
|
|
443
492
|
ui.info('Run "nimbus cost" for usage');
|
|
@@ -592,3 +641,170 @@ export async function costHistoryCommand(options: CostHistoryOptions): Promise<v
|
|
|
592
641
|
ui.newLine();
|
|
593
642
|
ui.print('Run with --provider demo to see sample data.');
|
|
594
643
|
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* H5: Cost diff — compare infracost estimates between two directories or branches.
|
|
647
|
+
*/
|
|
648
|
+
export async function costDiffCommand(
|
|
649
|
+
path1: string,
|
|
650
|
+
path2: string,
|
|
651
|
+
opts: { format?: 'table' | 'json' } = {}
|
|
652
|
+
): Promise<void> {
|
|
653
|
+
if (!path1 || !path2) {
|
|
654
|
+
ui.error('Usage: nimbus cost diff <path1> <path2> [--json]');
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Check if infracost is available
|
|
659
|
+
try {
|
|
660
|
+
execSync('infracost --version', { stdio: 'pipe' });
|
|
661
|
+
} catch {
|
|
662
|
+
ui.error('infracost is not installed.');
|
|
663
|
+
ui.info('Install it from: https://www.infracost.io/docs/');
|
|
664
|
+
ui.info(' brew install infracost (macOS)');
|
|
665
|
+
ui.info(' curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh (Linux)');
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
ui.startSpinner({ message: `Comparing costs: ${path1} vs ${path2}` });
|
|
670
|
+
|
|
671
|
+
try {
|
|
672
|
+
const rawOutput = execSync(
|
|
673
|
+
`infracost diff --path "${path1}" --compare-to "${path2}" --format json`,
|
|
674
|
+
{ encoding: 'utf-8', stdio: 'pipe', maxBuffer: 10 * 1024 * 1024 }
|
|
675
|
+
);
|
|
676
|
+
const data = JSON.parse(rawOutput) as CostEstimate;
|
|
677
|
+
ui.stopSpinnerSuccess('Cost diff complete');
|
|
678
|
+
|
|
679
|
+
if (opts.format === 'json') {
|
|
680
|
+
console.log(JSON.stringify(data, null, 2));
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Table output
|
|
685
|
+
ui.header('Cost Diff');
|
|
686
|
+
ui.print(` Comparing: ${path1} (current) → ${path2} (baseline)`);
|
|
687
|
+
ui.newLine();
|
|
688
|
+
|
|
689
|
+
const monthly = data.diffTotalMonthlyCost ?? 0;
|
|
690
|
+
ui.print(` ${ui.bold('Monthly delta:')} ${formatChange(monthly, data.currency)}`);
|
|
691
|
+
ui.print(` ${ui.bold('New total: ')} ${formatCurrency(data.totalMonthlyCost, data.currency)}/mo`);
|
|
692
|
+
ui.newLine();
|
|
693
|
+
|
|
694
|
+
// Per-project breakdown
|
|
695
|
+
for (const project of data.projects ?? []) {
|
|
696
|
+
if ((project.diffTotalMonthlyCost ?? 0) === 0) continue;
|
|
697
|
+
ui.print(` ${ui.bold(project.name)}: ${formatChange(project.diffTotalMonthlyCost, data.currency)}/mo`);
|
|
698
|
+
for (const resource of (project.resources ?? []).slice(0, 10)) {
|
|
699
|
+
ui.print(` ${resource.name.slice(0, 50).padEnd(50)} ${formatCurrency(resource.monthlyCost, data.currency)}/mo`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
} catch (error: unknown) {
|
|
703
|
+
ui.stopSpinnerFail('Cost diff failed');
|
|
704
|
+
ui.error(error instanceof Error ? error.message : String(error));
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* M3: Cost compare — compare infracost estimates between two workspaces.
|
|
711
|
+
* Runs infracost breakdown for each path and shows the delta.
|
|
712
|
+
*/
|
|
713
|
+
export async function costCompareCommand(
|
|
714
|
+
workspace1: string,
|
|
715
|
+
workspace2: string,
|
|
716
|
+
opts: { format?: 'table' | 'json' } = {}
|
|
717
|
+
): Promise<void> {
|
|
718
|
+
if (!workspace1 || !workspace2) {
|
|
719
|
+
ui.error('Usage: nimbus cost compare <workspace1> <workspace2>');
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Try infracost first
|
|
724
|
+
const hasInfracost = checkInfracost();
|
|
725
|
+
if (!hasInfracost) {
|
|
726
|
+
ui.info('infracost is not installed — showing placeholder comparison.');
|
|
727
|
+
ui.info('Install infracost for real cost estimates: https://infracost.io');
|
|
728
|
+
ui.newLine();
|
|
729
|
+
ui.print(` Workspace 1: ${workspace1}`);
|
|
730
|
+
ui.print(` Workspace 2: ${workspace2}`);
|
|
731
|
+
ui.print(' Cost delta: N/A (install infracost for actual data)');
|
|
732
|
+
ui.newLine();
|
|
733
|
+
ui.info(' brew install infracost (macOS)');
|
|
734
|
+
ui.info(' curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh (Linux)');
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
ui.startSpinner({ message: `Comparing costs: ${workspace1} vs ${workspace2}` });
|
|
739
|
+
|
|
740
|
+
try {
|
|
741
|
+
// Run infracost breakdown for each workspace
|
|
742
|
+
const est1 = runInfracostBreakdown(workspace1);
|
|
743
|
+
const est2 = runInfracostBreakdown(workspace2);
|
|
744
|
+
|
|
745
|
+
ui.stopSpinnerSuccess('Cost comparison complete');
|
|
746
|
+
|
|
747
|
+
if (!est1 && !est2) {
|
|
748
|
+
ui.warning('Could not obtain infracost estimates for either workspace.');
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const cost1 = est1?.totalMonthlyCost ?? 0;
|
|
753
|
+
const cost2 = est2?.totalMonthlyCost ?? 0;
|
|
754
|
+
const delta = cost1 - cost2;
|
|
755
|
+
const currency = est1?.currency ?? est2?.currency ?? 'USD';
|
|
756
|
+
|
|
757
|
+
if (opts.format === 'json') {
|
|
758
|
+
console.log(JSON.stringify({ workspace1, workspace2, cost1, cost2, delta, currency }, null, 2));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
ui.header('Cost Comparison');
|
|
763
|
+
ui.print(` ${ui.bold(workspace1)}: ${formatCurrency(cost1, currency)}/mo`);
|
|
764
|
+
ui.print(` ${ui.bold(workspace2)}: ${formatCurrency(cost2, currency)}/mo`);
|
|
765
|
+
ui.newLine();
|
|
766
|
+
ui.print(` ${ui.bold('Delta (1 vs 2):')} ${formatChange(delta, currency)}/mo`);
|
|
767
|
+
} catch (err: unknown) {
|
|
768
|
+
ui.stopSpinnerFail('Cost comparison failed');
|
|
769
|
+
ui.error(err instanceof Error ? err.message : String(err));
|
|
770
|
+
process.exit(1);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* M3: Cost report — export the session cost summary.
|
|
776
|
+
* Reads session tool-call cost data and exports it in the requested format.
|
|
777
|
+
*/
|
|
778
|
+
export async function costReportCommand(outputFormat: 'csv' | 'json' | 'text' = 'text'): Promise<void> {
|
|
779
|
+
ui.header('Nimbus Cost Report', `Session cost summary (format: ${outputFormat})`);
|
|
780
|
+
|
|
781
|
+
// Attempt to read cost data from ~/.nimbus/nimbus.db via the state DB
|
|
782
|
+
// For now, expose session-level token cost data as a report placeholder.
|
|
783
|
+
// In a full implementation this would query the cost_tracker table in SQLite.
|
|
784
|
+
const report = {
|
|
785
|
+
generatedAt: new Date().toISOString(),
|
|
786
|
+
note: 'Session cost data is tracked in ~/.nimbus/nimbus.db (cost_tracker table).',
|
|
787
|
+
hint: 'Run `nimbus cost estimate` to estimate infrastructure costs for the current directory.',
|
|
788
|
+
format: outputFormat,
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
if (outputFormat === 'json') {
|
|
792
|
+
console.log(JSON.stringify(report, null, 2));
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (outputFormat === 'csv') {
|
|
797
|
+
console.log('timestamp,description,cost_usd');
|
|
798
|
+
console.log(`${report.generatedAt},session_summary,0.00`);
|
|
799
|
+
ui.newLine();
|
|
800
|
+
ui.info(report.note);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// text
|
|
805
|
+
ui.newLine();
|
|
806
|
+
ui.print(` ${ui.dim('Generated:')} ${report.generatedAt}`);
|
|
807
|
+
ui.newLine();
|
|
808
|
+
ui.info(report.note);
|
|
809
|
+
ui.info(report.hint);
|
|
810
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy Command
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates a full deployment workflow without LLM involvement:
|
|
5
|
+
* 1. Run `terraform plan` and show summary
|
|
6
|
+
* 2. Ask user to confirm (or auto-approve with --auto-approve / -y)
|
|
7
|
+
* 3. Run `terraform apply` with the plan
|
|
8
|
+
* 4. Run `kubectl rollout status` to verify pods come up
|
|
9
|
+
* 5. Print success/failure summary
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* nimbus deploy [--auto-approve] [--workspace <ws>] [--namespace <ns>]
|
|
13
|
+
* [--dry-run] [--no-apply]
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import * as readline from 'node:readline';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Types
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export interface DeployOptions {
|
|
24
|
+
autoApprove?: boolean;
|
|
25
|
+
workspace?: string;
|
|
26
|
+
namespace?: string;
|
|
27
|
+
dryRun?: boolean;
|
|
28
|
+
noApply?: boolean;
|
|
29
|
+
cwd?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
function banner(step: string, total: string, label: string): void {
|
|
37
|
+
process.stdout.write(`\n[${step}/${total}] ${label}\n`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ok(msg: string): void {
|
|
41
|
+
process.stdout.write(`[OK] ${msg}\n`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function warn(msg: string): void {
|
|
45
|
+
process.stdout.write(`[!!] ${msg}\n`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function fail(msg: string): void {
|
|
49
|
+
process.stdout.write(`[XX] ${msg}\n`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function run(
|
|
53
|
+
cmd: string,
|
|
54
|
+
args: string[],
|
|
55
|
+
opts: { cwd?: string; env?: NodeJS.ProcessEnv } = {}
|
|
56
|
+
): { stdout: string; stderr: string; status: number } {
|
|
57
|
+
const result = spawnSync(cmd, args, {
|
|
58
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
59
|
+
env: opts.env ?? process.env,
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
stdout: (result.stdout as string) ?? '',
|
|
65
|
+
stderr: (result.stderr as string) ?? '',
|
|
66
|
+
status: result.status ?? 1,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function askConfirm(question: string): Promise<boolean> {
|
|
71
|
+
return new Promise(resolve => {
|
|
72
|
+
const rl = readline.createInterface({
|
|
73
|
+
input: process.stdin,
|
|
74
|
+
output: process.stdout,
|
|
75
|
+
terminal: false,
|
|
76
|
+
});
|
|
77
|
+
rl.question(`${question} [y/N] `, answer => {
|
|
78
|
+
rl.close();
|
|
79
|
+
resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Parse CLI args
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
export function parseDeployArgs(args: string[]): DeployOptions {
|
|
89
|
+
const opts: DeployOptions = {};
|
|
90
|
+
for (let i = 0; i < args.length; i++) {
|
|
91
|
+
const arg = args[i];
|
|
92
|
+
if (arg === '--auto-approve' || arg === '-y') {
|
|
93
|
+
opts.autoApprove = true;
|
|
94
|
+
} else if (arg === '--workspace' && args[i + 1]) {
|
|
95
|
+
opts.workspace = args[++i];
|
|
96
|
+
} else if ((arg === '--namespace' || arg === '-n') && args[i + 1]) {
|
|
97
|
+
opts.namespace = args[++i];
|
|
98
|
+
} else if (arg === '--dry-run') {
|
|
99
|
+
opts.dryRun = true;
|
|
100
|
+
} else if (arg === '--no-apply') {
|
|
101
|
+
opts.noApply = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return opts;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Main command
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
export async function deployCommand(args: string[]): Promise<void> {
|
|
112
|
+
const opts = parseDeployArgs(args);
|
|
113
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
114
|
+
|
|
115
|
+
const totalSteps = opts.noApply ? '2' : '4';
|
|
116
|
+
let step = 1;
|
|
117
|
+
|
|
118
|
+
// ------------------------------------------------------------------
|
|
119
|
+
// Step 1: Select Terraform workspace (if requested)
|
|
120
|
+
// ------------------------------------------------------------------
|
|
121
|
+
if (opts.workspace) {
|
|
122
|
+
banner(String(step++), totalSteps, `Selecting Terraform workspace: ${opts.workspace}`);
|
|
123
|
+
const wsResult = run('terraform', ['workspace', 'select', opts.workspace], { cwd });
|
|
124
|
+
if (wsResult.status !== 0) {
|
|
125
|
+
warn(`Workspace '${opts.workspace}' not found — attempting to create it.`);
|
|
126
|
+
const newWsResult = run('terraform', ['workspace', 'new', opts.workspace], { cwd });
|
|
127
|
+
if (newWsResult.status !== 0) {
|
|
128
|
+
fail(`Failed to select or create workspace '${opts.workspace}':\n${newWsResult.stderr}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
ok(`Workspace set to '${opts.workspace}'`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ------------------------------------------------------------------
|
|
136
|
+
// Step 2/1: Terraform plan
|
|
137
|
+
// ------------------------------------------------------------------
|
|
138
|
+
banner(String(step++), totalSteps, 'Planning infrastructure changes...');
|
|
139
|
+
|
|
140
|
+
if (opts.dryRun) {
|
|
141
|
+
process.stdout.write(' [dry-run] Would run: terraform plan -out=nimbus-deploy.tfplan\n');
|
|
142
|
+
} else {
|
|
143
|
+
const planResult = run('terraform', ['plan', '-out=nimbus-deploy.tfplan', '-no-color'], { cwd });
|
|
144
|
+
|
|
145
|
+
// Always print plan output so the user can review it
|
|
146
|
+
if (planResult.stdout) process.stdout.write(planResult.stdout + '\n');
|
|
147
|
+
if (planResult.stderr) process.stderr.write(planResult.stderr + '\n');
|
|
148
|
+
|
|
149
|
+
if (planResult.status !== 0) {
|
|
150
|
+
fail('terraform plan failed. Fix the errors above before deploying.');
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Quick summary: count resource changes
|
|
155
|
+
const creates = (planResult.stdout.match(/\+ resource/g) ?? []).length;
|
|
156
|
+
const updates = (planResult.stdout.match(/~ resource/g) ?? []).length;
|
|
157
|
+
const destroys = (planResult.stdout.match(/- resource/g) ?? []).length;
|
|
158
|
+
process.stdout.write(
|
|
159
|
+
`\n Plan summary: ${creates} to add, ${updates} to change, ${destroys} to destroy.\n`
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (opts.noApply) {
|
|
163
|
+
ok('--no-apply flag set. Stopping after plan.');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ------------------------------------------------------------------
|
|
168
|
+
// Step 3: Confirm before apply
|
|
169
|
+
// ------------------------------------------------------------------
|
|
170
|
+
if (!opts.autoApprove) {
|
|
171
|
+
const confirmed = await askConfirm('\nProceed with terraform apply?');
|
|
172
|
+
if (!confirmed) {
|
|
173
|
+
warn('Deployment cancelled by user.');
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
process.stdout.write(' --auto-approve set — skipping confirmation.\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ------------------------------------------------------------------
|
|
181
|
+
// Step 4: Terraform apply
|
|
182
|
+
// ------------------------------------------------------------------
|
|
183
|
+
banner(String(step++), totalSteps, 'Applying infrastructure changes...');
|
|
184
|
+
|
|
185
|
+
const applyResult = run('terraform', ['apply', '-auto-approve', '-no-color', 'nimbus-deploy.tfplan'], { cwd });
|
|
186
|
+
|
|
187
|
+
if (applyResult.stdout) process.stdout.write(applyResult.stdout + '\n');
|
|
188
|
+
if (applyResult.stderr) process.stderr.write(applyResult.stderr + '\n');
|
|
189
|
+
|
|
190
|
+
// Clean up plan file regardless of success/failure
|
|
191
|
+
try {
|
|
192
|
+
const { unlinkSync } = await import('node:fs');
|
|
193
|
+
unlinkSync(`${cwd}/nimbus-deploy.tfplan`);
|
|
194
|
+
} catch { /* non-critical */ }
|
|
195
|
+
|
|
196
|
+
if (applyResult.status !== 0) {
|
|
197
|
+
fail('terraform apply failed.');
|
|
198
|
+
process.stdout.write('\n Rollback hint: Run `nimbus tf rollback` to restore previous state.\n\n');
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
ok('Terraform apply succeeded.');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ------------------------------------------------------------------
|
|
206
|
+
// Step 5: kubectl rollout status
|
|
207
|
+
// ------------------------------------------------------------------
|
|
208
|
+
banner(String(step++), totalSteps, 'Verifying Kubernetes rollout status...');
|
|
209
|
+
|
|
210
|
+
const nsArgs = opts.namespace ? ['--namespace', opts.namespace] : [];
|
|
211
|
+
|
|
212
|
+
// Collect deployments in the namespace (or all namespaces)
|
|
213
|
+
const getResult = run('kubectl', ['get', 'deployments', '-o', 'name', ...nsArgs], { cwd });
|
|
214
|
+
|
|
215
|
+
if (getResult.status !== 0) {
|
|
216
|
+
// kubectl may not be available or no cluster is configured — warn but do not fail
|
|
217
|
+
warn('Could not query kubectl deployments. Skipping rollout status check.');
|
|
218
|
+
warn('Ensure kubectl is configured and pointing to the correct cluster.');
|
|
219
|
+
} else {
|
|
220
|
+
const deployments = getResult.stdout.trim().split('\n').filter(Boolean);
|
|
221
|
+
|
|
222
|
+
if (deployments.length === 0) {
|
|
223
|
+
warn('No deployments found to verify. Pods may still be starting up.');
|
|
224
|
+
} else {
|
|
225
|
+
let allHealthy = true;
|
|
226
|
+
|
|
227
|
+
for (const deployment of deployments) {
|
|
228
|
+
// deployment looks like "deployment.apps/my-app"
|
|
229
|
+
const rolloutResult = run(
|
|
230
|
+
'kubectl',
|
|
231
|
+
['rollout', 'status', deployment, '--timeout=120s', ...nsArgs],
|
|
232
|
+
{ cwd }
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (rolloutResult.stdout) process.stdout.write(rolloutResult.stdout + '\n');
|
|
236
|
+
|
|
237
|
+
if (rolloutResult.status !== 0) {
|
|
238
|
+
warn(`Rollout timeout or error for ${deployment}: ${rolloutResult.stderr.trim()}`);
|
|
239
|
+
warn('Pods may still be coming up. Check with: kubectl get pods');
|
|
240
|
+
allHealthy = false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (allHealthy) {
|
|
245
|
+
ok(`All ${deployments.length} deployment(s) are healthy.`);
|
|
246
|
+
} else {
|
|
247
|
+
// Non-fatal: pods may still be starting
|
|
248
|
+
warn('Some deployments did not complete rollout within the timeout window.');
|
|
249
|
+
warn('The deploy itself succeeded — pods may still be starting up.');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ------------------------------------------------------------------
|
|
255
|
+
// Summary
|
|
256
|
+
// ------------------------------------------------------------------
|
|
257
|
+
process.stdout.write('\n');
|
|
258
|
+
ok('Deployment workflow complete.');
|
|
259
|
+
process.stdout.write('\n');
|
|
260
|
+
}
|