@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,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-account profile management.
|
|
3
|
+
*
|
|
4
|
+
* Profiles are stored at ~/.nimbus/profiles/<name>.json and merged into
|
|
5
|
+
* the app config at startup when --profile <name> is passed.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
export interface ConfigProfile {
|
|
12
|
+
name: string;
|
|
13
|
+
/** Anthropic API key for this profile */
|
|
14
|
+
anthropicApiKey?: string;
|
|
15
|
+
/** AWS profile name (maps to AWS_PROFILE env var) */
|
|
16
|
+
awsProfile?: string;
|
|
17
|
+
/** AWS region override */
|
|
18
|
+
awsRegion?: string;
|
|
19
|
+
/** GCP project ID */
|
|
20
|
+
gcpProject?: string;
|
|
21
|
+
/** Azure subscription ID */
|
|
22
|
+
azureSubscription?: string;
|
|
23
|
+
/** Default agent mode for this profile */
|
|
24
|
+
defaultMode?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getProfilesDir(): string {
|
|
28
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
|
|
29
|
+
return join(home, '.nimbus', 'profiles');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function loadProfile(name: string): ConfigProfile | null {
|
|
33
|
+
const profilePath = join(getProfilesDir(), `${name}.json`);
|
|
34
|
+
if (!existsSync(profilePath)) return null;
|
|
35
|
+
try {
|
|
36
|
+
const raw = readFileSync(profilePath, 'utf-8');
|
|
37
|
+
return { name, ...JSON.parse(raw) } as ConfigProfile;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function saveProfile(profile: ConfigProfile): void {
|
|
44
|
+
const profilesDir = getProfilesDir();
|
|
45
|
+
mkdirSync(profilesDir, { recursive: true });
|
|
46
|
+
const { name, ...rest } = profile;
|
|
47
|
+
writeFileSync(join(profilesDir, `${name}.json`), JSON.stringify(rest, null, 2), 'utf-8');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function listProfiles(): string[] {
|
|
51
|
+
const profilesDir = getProfilesDir();
|
|
52
|
+
if (!existsSync(profilesDir)) return [];
|
|
53
|
+
try {
|
|
54
|
+
return readdirSync(profilesDir)
|
|
55
|
+
.filter(f => f.endsWith('.json'))
|
|
56
|
+
.map(f => f.slice(0, -5));
|
|
57
|
+
} catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Apply a profile to the current process environment.
|
|
64
|
+
* This mutates process.env so all subsequent operations pick up the profile.
|
|
65
|
+
*/
|
|
66
|
+
export function applyProfile(profile: ConfigProfile): void {
|
|
67
|
+
if (profile.anthropicApiKey) {
|
|
68
|
+
process.env.ANTHROPIC_API_KEY = profile.anthropicApiKey;
|
|
69
|
+
}
|
|
70
|
+
if (profile.awsProfile) {
|
|
71
|
+
process.env.AWS_PROFILE = profile.awsProfile;
|
|
72
|
+
}
|
|
73
|
+
if (profile.awsRegion) {
|
|
74
|
+
process.env.AWS_DEFAULT_REGION = profile.awsRegion;
|
|
75
|
+
process.env.AWS_REGION = profile.awsRegion;
|
|
76
|
+
}
|
|
77
|
+
if (profile.gcpProject) {
|
|
78
|
+
process.env.GCLOUD_PROJECT = profile.gcpProject;
|
|
79
|
+
process.env.GOOGLE_CLOUD_PROJECT = profile.gcpProject;
|
|
80
|
+
}
|
|
81
|
+
if (profile.azureSubscription) {
|
|
82
|
+
process.env.AZURE_SUBSCRIPTION_ID = profile.azureSubscription;
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/config/types.ts
CHANGED
|
@@ -261,7 +261,11 @@ export type ConfigKey =
|
|
|
261
261
|
| 'llm.cost_optimization.cheap_model'
|
|
262
262
|
| 'llm.cost_optimization.expensive_model'
|
|
263
263
|
| 'llm.cost_optimization.use_cheap_model_for'
|
|
264
|
-
| 'llm.cost_optimization.use_expensive_model_for'
|
|
264
|
+
| 'llm.cost_optimization.use_expensive_model_for'
|
|
265
|
+
| 'primaryClouds'
|
|
266
|
+
| 'model'
|
|
267
|
+
| 'defaultCostBudget'
|
|
268
|
+
| 'agentTurnTimeoutSeconds';
|
|
265
269
|
|
|
266
270
|
/**
|
|
267
271
|
* Config key metadata for help/validation
|
|
@@ -524,4 +528,82 @@ export const CONFIG_KEYS: ConfigKeyInfo[] = [
|
|
|
524
528
|
description: 'Ordered list of fallback LLM providers to try on failure (JSON array)',
|
|
525
529
|
type: 'string',
|
|
526
530
|
},
|
|
531
|
+
// M4: Primary cloud providers
|
|
532
|
+
{
|
|
533
|
+
key: 'primaryClouds',
|
|
534
|
+
description: 'Primary cloud providers (comma-separated: aws,gcp,azure)',
|
|
535
|
+
type: 'string',
|
|
536
|
+
},
|
|
537
|
+
// M4: Model override (validated against known model IDs)
|
|
538
|
+
{
|
|
539
|
+
key: 'model',
|
|
540
|
+
description: 'Default model ID (e.g. claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001)',
|
|
541
|
+
type: 'string',
|
|
542
|
+
},
|
|
543
|
+
// G16: Default cost budget per session
|
|
544
|
+
{
|
|
545
|
+
key: 'defaultCostBudget',
|
|
546
|
+
description: 'Default cost budget in USD per session (e.g. 1.00). 0 means unlimited.',
|
|
547
|
+
type: 'number',
|
|
548
|
+
defaultValue: 0,
|
|
549
|
+
},
|
|
550
|
+
// G21: Configurable agent turn silence timeout
|
|
551
|
+
{
|
|
552
|
+
key: 'agentTurnTimeoutSeconds',
|
|
553
|
+
description: 'Seconds to wait for LLM response before aborting (default: 60)',
|
|
554
|
+
type: 'number',
|
|
555
|
+
defaultValue: 60,
|
|
556
|
+
},
|
|
527
557
|
];
|
|
558
|
+
|
|
559
|
+
// ---------------------------------------------------------------------------
|
|
560
|
+
// M3: Per-project configuration (nimbus.yaml / .nimbus/config.json)
|
|
561
|
+
// ---------------------------------------------------------------------------
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Per-project Nimbus configuration for team defaults.
|
|
565
|
+
* Stored in <project-root>/nimbus.yaml OR <project-root>/.nimbus/config.json.
|
|
566
|
+
* JSON-only format to avoid adding a YAML parser dependency.
|
|
567
|
+
*/
|
|
568
|
+
export interface ProjectConfig {
|
|
569
|
+
/** Default Terraform workspace for this project */
|
|
570
|
+
defaultWorkspace?: string;
|
|
571
|
+
/** Default kubectl context for this project */
|
|
572
|
+
defaultContext?: string;
|
|
573
|
+
/** Default Kubernetes namespace */
|
|
574
|
+
defaultNamespace?: string;
|
|
575
|
+
/** Tool call patterns to auto-approve (e.g. ["kubectl get *", "terraform plan"]) */
|
|
576
|
+
autoApprove?: string[];
|
|
577
|
+
/** Environment names that should be treated as protected (require confirmation for destructive ops) */
|
|
578
|
+
protectedEnvironments?: string[];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Load per-project configuration from:
|
|
583
|
+
* 1. <cwd>/nimbus.yaml (JSON-parsed, no YAML dep)
|
|
584
|
+
* 2. <cwd>/.nimbus/config.json
|
|
585
|
+
*
|
|
586
|
+
* Returns null if neither file exists or both fail to parse.
|
|
587
|
+
*/
|
|
588
|
+
export function loadProjectConfig(cwd: string): ProjectConfig | null {
|
|
589
|
+
const { existsSync, readFileSync } = require('node:fs') as typeof import('node:fs');
|
|
590
|
+
const { join } = require('node:path') as typeof import('node:path');
|
|
591
|
+
|
|
592
|
+
const candidates = [
|
|
593
|
+
join(cwd, 'nimbus.yaml'),
|
|
594
|
+
join(cwd, '.nimbus', 'config.json'),
|
|
595
|
+
];
|
|
596
|
+
|
|
597
|
+
for (const file of candidates) {
|
|
598
|
+
try {
|
|
599
|
+
if (!existsSync(file)) continue;
|
|
600
|
+
const raw = readFileSync(file, 'utf-8');
|
|
601
|
+
const parsed = JSON.parse(raw) as ProjectConfig;
|
|
602
|
+
return parsed;
|
|
603
|
+
} catch {
|
|
604
|
+
// Try next candidate
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace state persistence — saves terraform workspace + kubectl context
|
|
3
|
+
* per working directory to ~/.nimbus/workspace-state.json
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
|
|
9
|
+
export interface WorkspaceState {
|
|
10
|
+
terraformWorkspace?: string;
|
|
11
|
+
kubectlContext?: string;
|
|
12
|
+
awsProfile?: string;
|
|
13
|
+
awsRegion?: string;
|
|
14
|
+
gcpProject?: string;
|
|
15
|
+
lastSeen?: string; // ISO timestamp
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type StateFile = Record<string, WorkspaceState>; // keyed by cwd
|
|
19
|
+
|
|
20
|
+
const STATE_PATH = join(homedir(), '.nimbus', 'workspace-state.json');
|
|
21
|
+
|
|
22
|
+
function loadStateFile(): StateFile {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(readFileSync(STATE_PATH, 'utf-8')) as StateFile;
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function saveStateFile(state: StateFile): void {
|
|
31
|
+
try {
|
|
32
|
+
mkdirSync(join(homedir(), '.nimbus'), { recursive: true });
|
|
33
|
+
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), 'utf-8');
|
|
34
|
+
} catch { /* non-critical */ }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function loadWorkspaceState(cwd: string): WorkspaceState {
|
|
38
|
+
const all = loadStateFile();
|
|
39
|
+
return all[cwd] ?? {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function saveWorkspaceState(cwd: string, state: WorkspaceState): void {
|
|
43
|
+
const all = loadStateFile();
|
|
44
|
+
all[cwd] = { ...all[cwd], ...state, lastSeen: new Date().toISOString() };
|
|
45
|
+
saveStateFile(all);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function mergeWorkspaceState(cwd: string, infra: Partial<WorkspaceState>): WorkspaceState {
|
|
49
|
+
const existing = loadWorkspaceState(cwd);
|
|
50
|
+
const merged = { ...existing, ...infra };
|
|
51
|
+
saveWorkspaceState(cwd, merged);
|
|
52
|
+
return merged;
|
|
53
|
+
}
|
|
@@ -96,16 +96,58 @@ export interface CostEstimate {
|
|
|
96
96
|
* s3: Minimal storage estimate (< 100GB) ~= $5
|
|
97
97
|
*/
|
|
98
98
|
const BASE_COMPONENT_COSTS: Record<string, number> = {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
// AWS Compute
|
|
100
|
+
vpc: 32,
|
|
101
|
+
eks: 73,
|
|
102
|
+
ecs: 30,
|
|
103
|
+
lambda: 2,
|
|
104
|
+
// EC2 instance types (monthly on-demand us-east-1)
|
|
105
|
+
't3.micro': 8,
|
|
106
|
+
't3.small': 16,
|
|
107
|
+
't3.medium': 32,
|
|
108
|
+
't3.large': 65,
|
|
109
|
+
'm5.large': 70,
|
|
110
|
+
'm5.xlarge': 140,
|
|
111
|
+
'm5.2xlarge': 280,
|
|
112
|
+
'c5.large': 62,
|
|
113
|
+
'c5.xlarge': 124,
|
|
114
|
+
'r5.large': 91,
|
|
115
|
+
'r5.xlarge': 182,
|
|
116
|
+
// RDS instance types (monthly, single-AZ)
|
|
117
|
+
'db.t3.micro': 14,
|
|
118
|
+
'db.t3.small': 28,
|
|
119
|
+
'db.t3.medium': 56,
|
|
120
|
+
'db.r5.large': 175,
|
|
121
|
+
'db.r5.xlarge': 350,
|
|
122
|
+
// AWS Data
|
|
123
|
+
rds: 50,
|
|
124
|
+
s3: 5,
|
|
125
|
+
elasticache: 25,
|
|
126
|
+
sqs: 1,
|
|
127
|
+
sns: 1,
|
|
128
|
+
cloudfront: 10,
|
|
129
|
+
// AWS Network/LB
|
|
130
|
+
'aws_nat_gateway': 32,
|
|
131
|
+
'aws_lb': 25,
|
|
132
|
+
'aws_alb': 25,
|
|
133
|
+
'aws_instance': 30,
|
|
134
|
+
'aws_db_instance': 50,
|
|
135
|
+
'aws_s3_bucket': 5,
|
|
136
|
+
'aws_eks_cluster': 73,
|
|
137
|
+
'aws_elasticache_cluster': 25,
|
|
138
|
+
'aws_rds_cluster': 50,
|
|
139
|
+
'aws_lambda_function': 2,
|
|
140
|
+
'aws_cloudfront_distribution': 10,
|
|
141
|
+
// GCP
|
|
142
|
+
'google_compute_instance': 30,
|
|
143
|
+
'google_container_cluster': 73,
|
|
144
|
+
'google_sql_database_instance': 50,
|
|
145
|
+
'google_storage_bucket': 5,
|
|
146
|
+
// Azure
|
|
147
|
+
'azurerm_virtual_machine': 30,
|
|
148
|
+
'azurerm_kubernetes_cluster': 73,
|
|
149
|
+
'azurerm_sql_database': 50,
|
|
150
|
+
'azurerm_storage_account': 5,
|
|
109
151
|
};
|
|
110
152
|
|
|
111
153
|
/**
|
package/src/engine/executor.ts
CHANGED
|
@@ -917,8 +917,39 @@ export class Executor {
|
|
|
917
917
|
// Execute rollback action
|
|
918
918
|
this.log(executionId, 'info', `Executing rollback: ${step.rollback_action}`);
|
|
919
919
|
|
|
920
|
-
//
|
|
921
|
-
|
|
920
|
+
// Parse and execute the rollback action
|
|
921
|
+
const action = step.rollback_action;
|
|
922
|
+
const [prefix, ...rest] = action.split(':');
|
|
923
|
+
|
|
924
|
+
let rollbackCmd: string;
|
|
925
|
+
if (prefix === 'terraform_destroy') {
|
|
926
|
+
const workdir = rest.join(':');
|
|
927
|
+
rollbackCmd = `terraform -chdir=${workdir} destroy -auto-approve -no-color`;
|
|
928
|
+
} else if (prefix === 'kubectl_rollout_undo') {
|
|
929
|
+
const [resource, namespace] = rest;
|
|
930
|
+
rollbackCmd = `kubectl rollout undo ${resource}${namespace ? ` -n ${namespace}` : ''}`;
|
|
931
|
+
} else if (prefix === 'helm_rollback') {
|
|
932
|
+
const [release, revision, namespace] = rest;
|
|
933
|
+
rollbackCmd = `helm rollback ${release} ${revision ?? '0'}${namespace ? ` -n ${namespace}` : ''}`;
|
|
934
|
+
} else if (prefix === 'bash') {
|
|
935
|
+
rollbackCmd = rest.join(':');
|
|
936
|
+
} else {
|
|
937
|
+
this.log(executionId, 'warn', `Unknown rollback prefix "${prefix}" — skipping`);
|
|
938
|
+
rollbackCmd = '';
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (rollbackCmd) {
|
|
942
|
+
this.log(executionId, 'info', `Running: ${rollbackCmd}`);
|
|
943
|
+
await new Promise<void>((resolve, reject) => {
|
|
944
|
+
const { exec } = require('node:child_process') as typeof import('node:child_process');
|
|
945
|
+
exec(rollbackCmd, { timeout: 600_000 }, (error, stdout, stderr) => {
|
|
946
|
+
if (stdout) this.log(executionId, 'info', stdout);
|
|
947
|
+
if (stderr) this.log(executionId, 'info', stderr);
|
|
948
|
+
if (error) reject(error);
|
|
949
|
+
else resolve();
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
}
|
|
922
953
|
|
|
923
954
|
const completedAt = new Date();
|
|
924
955
|
|
package/src/engine/planner.ts
CHANGED
|
@@ -115,6 +115,17 @@ const VALID_CATEGORIES = new Set(['security', 'cost', 'availability', 'performan
|
|
|
115
115
|
// Planner
|
|
116
116
|
// ==========================================
|
|
117
117
|
|
|
118
|
+
/** Detect the primary domain of a task description. */
|
|
119
|
+
function detectDomain(task: AgentTask): 'terraform' | 'kubernetes' | 'helm' | 'generic' {
|
|
120
|
+
const desc = (task.type + ' ' + JSON.stringify(task.context)).toLowerCase();
|
|
121
|
+
if (desc.includes('helm') || desc.includes('chart') || desc.includes('release')) return 'helm';
|
|
122
|
+
if (desc.includes('kubectl') || desc.includes('kubernetes') || desc.includes('pod') || desc.includes('deployment') || desc.includes('k8s')) return 'kubernetes';
|
|
123
|
+
if (desc.includes('terraform') || desc.includes('.tf') || desc.includes('infrastructure') || desc.includes('provider')) return 'terraform';
|
|
124
|
+
// Default to terraform for infrastructure tasks
|
|
125
|
+
if (task.type === 'deploy' || task.type === 'generate') return 'terraform';
|
|
126
|
+
return 'generic';
|
|
127
|
+
}
|
|
128
|
+
|
|
118
129
|
export class Planner {
|
|
119
130
|
private router: LLMRouter;
|
|
120
131
|
|
|
@@ -230,6 +241,16 @@ export class Planner {
|
|
|
230
241
|
const steps: PlanStep[] = [];
|
|
231
242
|
let order = 1;
|
|
232
243
|
|
|
244
|
+
// Domain-specific step generation
|
|
245
|
+
const domain = detectDomain(task);
|
|
246
|
+
if (domain === 'terraform') {
|
|
247
|
+
return this.generateTerraformSteps(task);
|
|
248
|
+
} else if (domain === 'kubernetes') {
|
|
249
|
+
return this.generateKubernetesSteps(task);
|
|
250
|
+
} else if (domain === 'helm') {
|
|
251
|
+
return this.generateHelmSteps(task);
|
|
252
|
+
}
|
|
253
|
+
|
|
233
254
|
// Step 1: Validate requirements
|
|
234
255
|
steps.push({
|
|
235
256
|
id: `step_${order++}`,
|
|
@@ -325,7 +346,7 @@ export class Planner {
|
|
|
325
346
|
},
|
|
326
347
|
status: 'pending',
|
|
327
348
|
depends_on: [steps[steps.length - 1].id],
|
|
328
|
-
rollback_action: '
|
|
349
|
+
rollback_action: `terraform_destroy:${task.context.environment ?? '.'}` as string,
|
|
329
350
|
rollback_parameters: {
|
|
330
351
|
provider: task.context.provider,
|
|
331
352
|
environment: task.context.environment,
|
|
@@ -366,6 +387,52 @@ export class Planner {
|
|
|
366
387
|
return steps;
|
|
367
388
|
}
|
|
368
389
|
|
|
390
|
+
private generateTerraformSteps(task: AgentTask): PlanStep[] {
|
|
391
|
+
const steps: PlanStep[] = [];
|
|
392
|
+
let order = 1;
|
|
393
|
+
const env = task.context.environment ?? '.';
|
|
394
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform init — initialize providers and modules', action: 'terraform_init', parameters: { workdir: env }, status: 'pending' });
|
|
395
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform validate — check configuration syntax', action: 'terraform_validate', parameters: { workdir: env }, status: 'pending', depends_on: ['step_1'] });
|
|
396
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform plan — preview changes', action: 'terraform_plan', parameters: { workdir: env }, status: 'pending', depends_on: ['step_2'] });
|
|
397
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'Review plan output with human', action: 'review_plan', parameters: { workdir: env }, status: 'pending', depends_on: ['step_3'] });
|
|
398
|
+
if (task.type === 'deploy') {
|
|
399
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'terraform apply — apply approved changes', action: 'terraform_apply', parameters: { workdir: env }, status: 'pending', depends_on: ['step_4'], rollback_action: `terraform_destroy:${env}`, rollback_parameters: { workdir: env } });
|
|
400
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform output — capture outputs', action: 'terraform_output', parameters: { workdir: env }, status: 'pending', depends_on: ['step_5'] });
|
|
401
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify deployed infrastructure resources', action: 'verify_infra', parameters: { workdir: env }, status: 'pending', depends_on: ['step_6'] });
|
|
402
|
+
}
|
|
403
|
+
return steps;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private generateKubernetesSteps(task: AgentTask): PlanStep[] {
|
|
407
|
+
const steps: PlanStep[] = [];
|
|
408
|
+
let order = 1;
|
|
409
|
+
const env = task.context.environment ?? '.';
|
|
410
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'Validate Kubernetes manifests (dry-run client)', action: 'kubectl_dry_run', parameters: { workdir: env }, status: 'pending' });
|
|
411
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'kubectl diff — show what would change', action: 'kubectl_diff', parameters: { workdir: env }, status: 'pending', depends_on: ['step_1'] });
|
|
412
|
+
if (task.type === 'deploy') {
|
|
413
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'kubectl apply — deploy manifests', action: 'kubectl_apply', parameters: { workdir: env }, status: 'pending', depends_on: ['step_2'], rollback_action: 'kubectl_rollout_undo:deployment/app:default' });
|
|
414
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'kubectl rollout status — wait for rollout', action: 'kubectl_rollout_status', parameters: { workdir: env }, status: 'pending', depends_on: ['step_3'] });
|
|
415
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify all pods are running', action: 'verify_pods', parameters: { workdir: env }, status: 'pending', depends_on: ['step_4'] });
|
|
416
|
+
}
|
|
417
|
+
return steps;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private generateHelmSteps(task: AgentTask): PlanStep[] {
|
|
421
|
+
const steps: PlanStep[] = [];
|
|
422
|
+
let order = 1;
|
|
423
|
+
const release = task.context.components[0] ?? 'app';
|
|
424
|
+
const ns = task.context.environment ?? 'default';
|
|
425
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm lint — validate chart templates', action: 'helm_lint', parameters: { release, namespace: ns }, status: 'pending' });
|
|
426
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm template — render and preview manifests', action: 'helm_template', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_1'] });
|
|
427
|
+
if (task.type === 'deploy') {
|
|
428
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm diff upgrade — show what would change', action: 'helm_diff', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_2'] });
|
|
429
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'helm upgrade --install --atomic — deploy release', action: 'helm_upgrade', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_3'], rollback_action: `helm_rollback:${release}:0:${ns}` });
|
|
430
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'helm test — run chart tests', action: 'helm_test', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_4'] });
|
|
431
|
+
steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify release is deployed and healthy', action: 'verify_release', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_5'] });
|
|
432
|
+
}
|
|
433
|
+
return steps;
|
|
434
|
+
}
|
|
435
|
+
|
|
369
436
|
/**
|
|
370
437
|
* Analyze dependencies between steps
|
|
371
438
|
*/
|
|
@@ -1865,3 +1865,11 @@ output "alias_arn" {
|
|
|
1865
1865
|
}
|
|
1866
1866
|
}
|
|
1867
1867
|
}
|
|
1868
|
+
|
|
1869
|
+
/**
|
|
1870
|
+
* Convenience function — instantiate the generator and run it.
|
|
1871
|
+
* Used by generate-terraform.ts and aws-terraform.ts.
|
|
1872
|
+
*/
|
|
1873
|
+
export async function generateTerraformProject(config: TerraformProjectConfig): Promise<GeneratedProject> {
|
|
1874
|
+
return new TerraformProjectGenerator().generate(config);
|
|
1875
|
+
}
|
package/src/history/manager.ts
CHANGED
|
@@ -40,11 +40,9 @@ function createEmptyHistoryFile(): HistoryFile {
|
|
|
40
40
|
export class HistoryManager {
|
|
41
41
|
private historyPath: string;
|
|
42
42
|
private historyFile: HistoryFile | null = null;
|
|
43
|
-
private stateServiceUrl: string;
|
|
44
43
|
|
|
45
44
|
constructor(historyPath?: string) {
|
|
46
45
|
this.historyPath = historyPath || path.join(os.homedir(), '.nimbus', 'history.json');
|
|
47
|
-
this.stateServiceUrl = process.env.STATE_SERVICE_URL || 'http://localhost:3004';
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
/**
|
|
@@ -118,66 +116,6 @@ export class HistoryManager {
|
|
|
118
116
|
fs.writeFileSync(this.historyPath, content, { mode: 0o600 });
|
|
119
117
|
}
|
|
120
118
|
|
|
121
|
-
/**
|
|
122
|
-
* Sync a history entry to the remote State Service.
|
|
123
|
-
* Fire-and-forget: never throws on network or parse errors.
|
|
124
|
-
*/
|
|
125
|
-
private async syncToStateService(entry: HistoryEntry): Promise<void> {
|
|
126
|
-
try {
|
|
127
|
-
await fetch(`${this.stateServiceUrl}/api/state/history`, {
|
|
128
|
-
method: 'POST',
|
|
129
|
-
headers: { 'Content-Type': 'application/json' },
|
|
130
|
-
body: JSON.stringify(entry),
|
|
131
|
-
});
|
|
132
|
-
} catch {
|
|
133
|
-
// Fire-and-forget: swallow all errors silently
|
|
134
|
-
console.warn('[nimbus] Failed to sync history entry to State Service');
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Load history entries from the remote State Service.
|
|
140
|
-
* Returns the entries array on success, or null on any failure
|
|
141
|
-
* so the caller can fall back to local storage.
|
|
142
|
-
*/
|
|
143
|
-
private async loadFromStateService(options: HistoryQueryOptions): Promise<HistoryEntry[] | null> {
|
|
144
|
-
try {
|
|
145
|
-
const params = new URLSearchParams();
|
|
146
|
-
if (options.limit !== undefined) {
|
|
147
|
-
params.set('limit', String(options.limit));
|
|
148
|
-
}
|
|
149
|
-
if (options.since) {
|
|
150
|
-
params.set('since', options.since);
|
|
151
|
-
}
|
|
152
|
-
if (options.until) {
|
|
153
|
-
params.set('until', options.until);
|
|
154
|
-
}
|
|
155
|
-
if (options.command) {
|
|
156
|
-
params.set('command', options.command);
|
|
157
|
-
}
|
|
158
|
-
if (options.status) {
|
|
159
|
-
params.set('status', options.status);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const queryString = params.toString();
|
|
163
|
-
const url = `${this.stateServiceUrl}/api/state/history${queryString ? `?${queryString}` : ''}`;
|
|
164
|
-
|
|
165
|
-
const response = await fetch(url);
|
|
166
|
-
if (!response.ok) {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const body = (await response.json()) as { success?: boolean; data?: HistoryEntry[] };
|
|
171
|
-
if (body.success && Array.isArray(body.data)) {
|
|
172
|
-
return body.data;
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
175
|
-
} catch {
|
|
176
|
-
// Network error, parse error, etc. — fall back to local
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
119
|
/**
|
|
182
120
|
* Add a new entry to history
|
|
183
121
|
*/
|
|
@@ -202,9 +140,6 @@ export class HistoryManager {
|
|
|
202
140
|
|
|
203
141
|
this.save(historyFile);
|
|
204
142
|
|
|
205
|
-
// Fire-and-forget: sync to State Service without blocking
|
|
206
|
-
this.syncToStateService(entry).catch(() => {});
|
|
207
|
-
|
|
208
143
|
return entry;
|
|
209
144
|
}
|
|
210
145
|
|
|
@@ -245,17 +180,10 @@ export class HistoryManager {
|
|
|
245
180
|
|
|
246
181
|
/**
|
|
247
182
|
* Get history entries with optional filtering.
|
|
248
|
-
*
|
|
249
|
-
* falls back to local file storage on any failure.
|
|
183
|
+
* Reads from local file storage.
|
|
250
184
|
*/
|
|
251
185
|
async getEntries(options: HistoryQueryOptions = {}): Promise<HistoryEntry[]> {
|
|
252
|
-
//
|
|
253
|
-
const remoteEntries = await this.loadFromStateService(options);
|
|
254
|
-
if (remoteEntries !== null) {
|
|
255
|
-
return remoteEntries;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Fall back to local file
|
|
186
|
+
// Read from local file
|
|
259
187
|
const historyFile = this.load();
|
|
260
188
|
let entries = [...historyFile.entries];
|
|
261
189
|
|
package/src/hooks/engine.ts
CHANGED
|
@@ -242,13 +242,14 @@ export class HookEngine {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// Write context JSON to stdin
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
if (child.stdin) {
|
|
246
|
+
child.stdin.on('error', () => { /* EPIPE or other write errors — ignore */ });
|
|
247
|
+
try {
|
|
247
248
|
child.stdin.write(JSON.stringify(context));
|
|
248
249
|
child.stdin.end();
|
|
250
|
+
} catch {
|
|
251
|
+
// stdin may already be closed -- ignore
|
|
249
252
|
}
|
|
250
|
-
} catch {
|
|
251
|
-
// stdin may already be closed -- ignore
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
// Collect stdout and stderr
|
|
@@ -138,13 +138,13 @@ export function calculateCost(
|
|
|
138
138
|
|
|
139
139
|
const providerPricing = PRICING[provider];
|
|
140
140
|
if (!providerPricing) {
|
|
141
|
-
logger.
|
|
141
|
+
logger.debug(`No pricing data for provider "${provider}", returning zero cost`);
|
|
142
142
|
return { costUSD: 0, breakdown: { input: 0, output: 0 } };
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
const entry = providerPricing[model];
|
|
146
146
|
if (!entry) {
|
|
147
|
-
logger.
|
|
147
|
+
logger.debug(
|
|
148
148
|
`No pricing data for model "${model}" on provider "${provider}", returning zero cost`
|
|
149
149
|
);
|
|
150
150
|
return { costUSD: 0, breakdown: { input: 0, output: 0 } };
|