@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,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Commands
|
|
3
|
+
* Audit log viewer and export CLI commands
|
|
4
|
+
*/
|
|
5
|
+
import { ui } from '../../wizard/ui';
|
|
6
|
+
import { auditClient } from '../../clients/enterprise-client';
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
/**
|
|
9
|
+
* Get current team ID from config or environment
|
|
10
|
+
*/
|
|
11
|
+
function getCurrentTeamId() {
|
|
12
|
+
return process.env.NIMBUS_TEAM_ID || null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse relative time string (e.g., "7d", "24h") to ISO date
|
|
16
|
+
*/
|
|
17
|
+
function parseRelativeTime(timeStr) {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const match = timeStr.match(/^(\d+)([dhwm])$/);
|
|
20
|
+
if (!match) {
|
|
21
|
+
return timeStr; // Assume it's already a valid date string
|
|
22
|
+
}
|
|
23
|
+
const value = parseInt(match[1], 10);
|
|
24
|
+
const unit = match[2];
|
|
25
|
+
let ms;
|
|
26
|
+
switch (unit) {
|
|
27
|
+
case 'h':
|
|
28
|
+
ms = value * 60 * 60 * 1000;
|
|
29
|
+
break;
|
|
30
|
+
case 'd':
|
|
31
|
+
ms = value * 24 * 60 * 60 * 1000;
|
|
32
|
+
break;
|
|
33
|
+
case 'w':
|
|
34
|
+
ms = value * 7 * 24 * 60 * 60 * 1000;
|
|
35
|
+
break;
|
|
36
|
+
case 'm':
|
|
37
|
+
ms = value * 30 * 24 * 60 * 60 * 1000;
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
return timeStr;
|
|
41
|
+
}
|
|
42
|
+
return new Date(now - ms).toISOString();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse audit list options
|
|
46
|
+
*/
|
|
47
|
+
export function parseAuditListOptions(args) {
|
|
48
|
+
const options = {};
|
|
49
|
+
for (let i = 0; i < args.length; i++) {
|
|
50
|
+
const arg = args[i];
|
|
51
|
+
if (arg === '--since' && args[i + 1]) {
|
|
52
|
+
options.since = parseRelativeTime(args[++i]);
|
|
53
|
+
}
|
|
54
|
+
else if (arg === '--until' && args[i + 1]) {
|
|
55
|
+
options.until = parseRelativeTime(args[++i]);
|
|
56
|
+
}
|
|
57
|
+
else if (arg === '--action' && args[i + 1]) {
|
|
58
|
+
options.action = args[++i];
|
|
59
|
+
}
|
|
60
|
+
else if (arg === '--user' && args[i + 1]) {
|
|
61
|
+
options.userId = args[++i];
|
|
62
|
+
}
|
|
63
|
+
else if ((arg === '--limit' || arg === '-n') && args[i + 1]) {
|
|
64
|
+
options.limit = parseInt(args[++i], 10);
|
|
65
|
+
}
|
|
66
|
+
else if (arg === '--json') {
|
|
67
|
+
options.json = true;
|
|
68
|
+
}
|
|
69
|
+
else if (arg === '--non-interactive') {
|
|
70
|
+
options.nonInteractive = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return options;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Parse audit export options
|
|
77
|
+
*/
|
|
78
|
+
export function parseAuditExportOptions(args) {
|
|
79
|
+
const options = {};
|
|
80
|
+
for (let i = 0; i < args.length; i++) {
|
|
81
|
+
const arg = args[i];
|
|
82
|
+
if (arg === '--format' && args[i + 1]) {
|
|
83
|
+
options.format = args[++i];
|
|
84
|
+
}
|
|
85
|
+
else if ((arg === '--output' || arg === '-o') && args[i + 1]) {
|
|
86
|
+
options.output = args[++i];
|
|
87
|
+
}
|
|
88
|
+
else if (arg === '--since' && args[i + 1]) {
|
|
89
|
+
options.since = parseRelativeTime(args[++i]);
|
|
90
|
+
}
|
|
91
|
+
else if (arg === '--until' && args[i + 1]) {
|
|
92
|
+
options.until = parseRelativeTime(args[++i]);
|
|
93
|
+
}
|
|
94
|
+
else if (arg === '--non-interactive') {
|
|
95
|
+
options.nonInteractive = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return options;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Format timestamp for display
|
|
102
|
+
*/
|
|
103
|
+
function formatTimestamp(ts) {
|
|
104
|
+
const date = new Date(ts);
|
|
105
|
+
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Audit list command
|
|
109
|
+
*/
|
|
110
|
+
export async function auditListCommand(options) {
|
|
111
|
+
try {
|
|
112
|
+
const teamId = getCurrentTeamId();
|
|
113
|
+
if (!teamId) {
|
|
114
|
+
ui.error('No team selected. Run `nimbus team switch <team-id>` first.');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
ui.startSpinner({ message: 'Fetching audit logs...' });
|
|
118
|
+
const result = await auditClient.queryLogs({
|
|
119
|
+
teamId,
|
|
120
|
+
userId: options.userId,
|
|
121
|
+
action: options.action,
|
|
122
|
+
since: options.since,
|
|
123
|
+
until: options.until,
|
|
124
|
+
limit: options.limit || 50,
|
|
125
|
+
});
|
|
126
|
+
ui.stopSpinnerSuccess(`Found ${result.total} logs (showing ${result.logs.length})`);
|
|
127
|
+
if (options.json) {
|
|
128
|
+
console.log(JSON.stringify(result, null, 2));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (result.logs.length === 0) {
|
|
132
|
+
ui.info('No audit logs found');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
ui.newLine();
|
|
136
|
+
ui.table({
|
|
137
|
+
columns: [
|
|
138
|
+
{ key: 'timestamp', header: 'Time', width: 20 },
|
|
139
|
+
{ key: 'action', header: 'Action', width: 20 },
|
|
140
|
+
{ key: 'user', header: 'User', width: 15 },
|
|
141
|
+
{ key: 'status', header: 'Status', width: 10 },
|
|
142
|
+
{ key: 'resource', header: 'Resource' },
|
|
143
|
+
],
|
|
144
|
+
data: result.logs.map(log => ({
|
|
145
|
+
timestamp: formatTimestamp(log.timestamp),
|
|
146
|
+
action: log.action,
|
|
147
|
+
user: log.userId || '-',
|
|
148
|
+
status: log.status,
|
|
149
|
+
resource: log.resourceType ? `${log.resourceType}/${log.resourceId || ''}` : '-',
|
|
150
|
+
})),
|
|
151
|
+
});
|
|
152
|
+
if (result.total > result.logs.length) {
|
|
153
|
+
ui.newLine();
|
|
154
|
+
ui.dim(`Showing ${result.logs.length} of ${result.total} logs. Use --limit to show more.`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
ui.stopSpinnerFail('Failed to fetch audit logs');
|
|
159
|
+
ui.error(error.message);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Audit export command
|
|
164
|
+
*/
|
|
165
|
+
export async function auditExportCommand(options) {
|
|
166
|
+
try {
|
|
167
|
+
const teamId = getCurrentTeamId();
|
|
168
|
+
if (!teamId) {
|
|
169
|
+
ui.error('No team selected. Run `nimbus team switch <team-id>` first.');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const format = options.format || 'json';
|
|
173
|
+
const output = options.output || `audit-logs-${new Date().toISOString().split('T')[0]}.${format}`;
|
|
174
|
+
ui.startSpinner({ message: `Exporting audit logs to ${output}...` });
|
|
175
|
+
const content = await auditClient.exportLogs(format, {
|
|
176
|
+
teamId,
|
|
177
|
+
since: options.since,
|
|
178
|
+
until: options.until,
|
|
179
|
+
});
|
|
180
|
+
fs.writeFileSync(output, content);
|
|
181
|
+
ui.stopSpinnerSuccess(`Exported to ${output}`);
|
|
182
|
+
const stats = fs.statSync(output);
|
|
183
|
+
ui.info(`File size: ${(stats.size / 1024).toFixed(1)} KB`);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
ui.stopSpinnerFail('Failed to export audit logs');
|
|
187
|
+
ui.error(error.message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Parse audit scan options from CLI args
|
|
192
|
+
*/
|
|
193
|
+
export function parseAuditScanOptions(args) {
|
|
194
|
+
const options = {};
|
|
195
|
+
for (let i = 0; i < args.length; i++) {
|
|
196
|
+
const arg = args[i];
|
|
197
|
+
if (arg === '--framework' && args[i + 1]) {
|
|
198
|
+
options.framework = args[++i];
|
|
199
|
+
}
|
|
200
|
+
else if ((arg === '--output' || arg === '-o') && args[i + 1]) {
|
|
201
|
+
options.output = args[++i];
|
|
202
|
+
}
|
|
203
|
+
else if ((arg === '--dir' || arg === '-d') && args[i + 1]) {
|
|
204
|
+
options.dir = args[++i];
|
|
205
|
+
}
|
|
206
|
+
else if (arg === '--threshold' && args[i + 1]) {
|
|
207
|
+
options.threshold = parseInt(args[++i], 10);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return options;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Audit scan subcommand — runs the local security scanner
|
|
214
|
+
*/
|
|
215
|
+
export async function auditScanCommand(options) {
|
|
216
|
+
const { scanSecurity } = await import('../../audit/security-scanner');
|
|
217
|
+
const dir = options.dir ?? process.cwd();
|
|
218
|
+
ui.startSpinner({ message: `Scanning ${dir} for security issues...` });
|
|
219
|
+
let result;
|
|
220
|
+
try {
|
|
221
|
+
result = await scanSecurity({ dir });
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
ui.stopSpinnerFail('Scan failed');
|
|
225
|
+
ui.error(e.message);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
ui.stopSpinnerSuccess(`Scan complete: ${result.findings.length} finding(s) in ${result.scannedFiles} file(s)`);
|
|
229
|
+
// Filter by framework if provided (maps framework to relevant finding IDs)
|
|
230
|
+
let findings = result.findings;
|
|
231
|
+
if (options.framework) {
|
|
232
|
+
// Each framework maps loosely to severity thresholds
|
|
233
|
+
const frameworkSeverityMap = {
|
|
234
|
+
soc2: ['CRITICAL', 'HIGH', 'MEDIUM'],
|
|
235
|
+
hipaa: ['CRITICAL', 'HIGH', 'MEDIUM'],
|
|
236
|
+
pci: ['CRITICAL', 'HIGH'],
|
|
237
|
+
iso27001: ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'],
|
|
238
|
+
};
|
|
239
|
+
const allowedSeverities = frameworkSeverityMap[options.framework] ?? [];
|
|
240
|
+
findings = findings.filter((f) => allowedSeverities.includes(f.severity));
|
|
241
|
+
ui.dim(`Framework filter (${options.framework}): showing ${findings.length} relevant finding(s)`);
|
|
242
|
+
}
|
|
243
|
+
if (findings.length === 0) {
|
|
244
|
+
ui.info('No findings. Scan clean.');
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
ui.newLine();
|
|
248
|
+
for (const finding of findings) {
|
|
249
|
+
const severityColor = finding.severity === 'CRITICAL' || finding.severity === 'HIGH'
|
|
250
|
+
? 'red'
|
|
251
|
+
: finding.severity === 'MEDIUM'
|
|
252
|
+
? 'yellow'
|
|
253
|
+
: 'white';
|
|
254
|
+
ui.print(`[${ui.color(finding.severity, severityColor)}] ${finding.id}: ${finding.title}`);
|
|
255
|
+
if (finding.file)
|
|
256
|
+
ui.dim(` File: ${finding.file}${finding.line ? `:${finding.line}` : ''}`);
|
|
257
|
+
ui.dim(` ${finding.recommendation}`);
|
|
258
|
+
ui.newLine();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Write JSON report to file if requested
|
|
262
|
+
if (options.output) {
|
|
263
|
+
const report = {
|
|
264
|
+
timestamp: result.timestamp.toISOString(),
|
|
265
|
+
scannedFiles: result.scannedFiles,
|
|
266
|
+
scanDuration: result.scanDuration,
|
|
267
|
+
framework: options.framework ?? null,
|
|
268
|
+
findingsCount: findings.length,
|
|
269
|
+
findings,
|
|
270
|
+
};
|
|
271
|
+
fs.writeFileSync(options.output, JSON.stringify(report, null, 2), 'utf-8');
|
|
272
|
+
ui.print(`Report written to ${options.output}`);
|
|
273
|
+
}
|
|
274
|
+
// Threshold check — exit code 1 if too many findings
|
|
275
|
+
if (options.threshold !== undefined && findings.length > options.threshold) {
|
|
276
|
+
ui.error(`Findings (${findings.length}) exceeded threshold (${options.threshold}). Exiting with code 1.`);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Main audit command dispatcher
|
|
282
|
+
*/
|
|
283
|
+
export async function auditCommand(subcommand, args) {
|
|
284
|
+
switch (subcommand) {
|
|
285
|
+
case 'list':
|
|
286
|
+
case '':
|
|
287
|
+
await auditListCommand(parseAuditListOptions(args));
|
|
288
|
+
break;
|
|
289
|
+
case 'export':
|
|
290
|
+
await auditExportCommand(parseAuditExportOptions(args));
|
|
291
|
+
break;
|
|
292
|
+
case 'scan':
|
|
293
|
+
await auditScanCommand(parseAuditScanOptions(args));
|
|
294
|
+
break;
|
|
295
|
+
default:
|
|
296
|
+
ui.error(`Unknown audit command: ${subcommand}`);
|
|
297
|
+
ui.newLine();
|
|
298
|
+
ui.info('Available audit commands:');
|
|
299
|
+
ui.print(' nimbus audit - List audit logs');
|
|
300
|
+
ui.print(' nimbus audit list - List audit logs');
|
|
301
|
+
ui.print(' nimbus audit export - Export audit logs');
|
|
302
|
+
ui.print(' nimbus audit scan - Scan directory for security issues');
|
|
303
|
+
ui.newLine();
|
|
304
|
+
ui.info('Options (list):');
|
|
305
|
+
ui.print(' --since <time> Filter logs since (e.g., 7d, 24h, 2024-01-01)');
|
|
306
|
+
ui.print(' --until <time> Filter logs until');
|
|
307
|
+
ui.print(' --action <type> Filter by action type');
|
|
308
|
+
ui.print(' --user <id> Filter by user');
|
|
309
|
+
ui.print(' --limit <n> Number of logs to show');
|
|
310
|
+
ui.print(' --json Output as JSON');
|
|
311
|
+
ui.newLine();
|
|
312
|
+
ui.info('Options (scan):');
|
|
313
|
+
ui.print(' --framework <f> Compliance framework: soc2|hipaa|pci|iso27001');
|
|
314
|
+
ui.print(' --output <file> Write JSON report to file');
|
|
315
|
+
ui.print(' --dir <path> Directory to scan (default: cwd)');
|
|
316
|
+
ui.print(' --threshold <n> Exit code 1 if findings exceed this count');
|
|
317
|
+
ui.newLine();
|
|
318
|
+
ui.info('Export options:');
|
|
319
|
+
ui.print(' --format <type> Export format (csv|json)');
|
|
320
|
+
ui.print(' --output <file> Output file path');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud Credential Management Commands
|
|
3
|
+
*
|
|
4
|
+
* Validate and display cloud provider credentials.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* nimbus auth aws [--profile <name>]
|
|
8
|
+
* nimbus auth gcp [--project <name>]
|
|
9
|
+
* nimbus auth azure [--subscription <id>]
|
|
10
|
+
*/
|
|
11
|
+
import { logger } from '../utils';
|
|
12
|
+
import { ui } from '../wizard/ui';
|
|
13
|
+
/**
|
|
14
|
+
* Check if a CLI tool is installed
|
|
15
|
+
*/
|
|
16
|
+
async function isCliInstalled(cmd) {
|
|
17
|
+
const { execFile } = await import('child_process');
|
|
18
|
+
const { promisify } = await import('util');
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
try {
|
|
21
|
+
await execFileAsync(cmd, ['--version'], { timeout: 5000 });
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run a CLI command and capture output
|
|
30
|
+
*/
|
|
31
|
+
async function runCommand(cmd, args) {
|
|
32
|
+
const { execFile } = await import('child_process');
|
|
33
|
+
const { promisify } = await import('util');
|
|
34
|
+
const execFileAsync = promisify(execFile);
|
|
35
|
+
try {
|
|
36
|
+
const { stdout } = await execFileAsync(cmd, args, { timeout: 15000 });
|
|
37
|
+
return { stdout: stdout.trim(), success: true };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return { stdout: error.stdout?.trim() || error.message, success: false };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* nimbus auth aws — Validate AWS credentials
|
|
45
|
+
*/
|
|
46
|
+
export async function authAwsCommand(options = {}) {
|
|
47
|
+
logger.info('Validating AWS credentials', { options });
|
|
48
|
+
ui.header('AWS Credentials');
|
|
49
|
+
// Check if AWS CLI is installed
|
|
50
|
+
const installed = await isCliInstalled('aws');
|
|
51
|
+
if (!installed) {
|
|
52
|
+
ui.error('AWS CLI is not installed');
|
|
53
|
+
ui.newLine();
|
|
54
|
+
ui.print('Install the AWS CLI:');
|
|
55
|
+
ui.print(` ${ui.dim('brew install awscli')} (macOS)`);
|
|
56
|
+
ui.print(` ${ui.dim('pip install awscli')} (pip)`);
|
|
57
|
+
ui.print(` ${ui.dim('https://aws.amazon.com/cli/')} (official)`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
ui.startSpinner({ message: 'Validating AWS credentials...' });
|
|
61
|
+
// Get caller identity
|
|
62
|
+
const identityArgs = ['sts', 'get-caller-identity', '--output', 'json'];
|
|
63
|
+
if (options.profile) {
|
|
64
|
+
identityArgs.push('--profile', options.profile);
|
|
65
|
+
}
|
|
66
|
+
const identity = await runCommand('aws', identityArgs);
|
|
67
|
+
if (!identity.success) {
|
|
68
|
+
ui.stopSpinnerFail('AWS credentials validation failed');
|
|
69
|
+
ui.newLine();
|
|
70
|
+
ui.error('Unable to validate AWS credentials');
|
|
71
|
+
ui.print(ui.dim(identity.stdout));
|
|
72
|
+
ui.newLine();
|
|
73
|
+
ui.print('Configure credentials:');
|
|
74
|
+
ui.print(` ${ui.dim('aws configure')}`);
|
|
75
|
+
ui.print(` ${ui.dim('export AWS_ACCESS_KEY_ID=...')}`);
|
|
76
|
+
ui.print(` ${ui.dim('export AWS_SECRET_ACCESS_KEY=...')}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
ui.stopSpinnerSuccess('AWS credentials valid');
|
|
80
|
+
ui.newLine();
|
|
81
|
+
// Parse identity
|
|
82
|
+
try {
|
|
83
|
+
const data = JSON.parse(identity.stdout);
|
|
84
|
+
ui.print(` ${ui.bold('Account:')} ${data.Account}`);
|
|
85
|
+
ui.print(` ${ui.bold('User ARN:')} ${data.Arn}`);
|
|
86
|
+
ui.print(` ${ui.bold('User ID:')} ${data.UserId}`);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
ui.print(` ${identity.stdout}`);
|
|
90
|
+
}
|
|
91
|
+
// Get current region
|
|
92
|
+
const regionArgs = ['configure', 'get', 'region'];
|
|
93
|
+
if (options.profile) {
|
|
94
|
+
regionArgs.push('--profile', options.profile);
|
|
95
|
+
}
|
|
96
|
+
const region = await runCommand('aws', regionArgs);
|
|
97
|
+
if (region.success && region.stdout) {
|
|
98
|
+
ui.print(` ${ui.bold('Region:')} ${region.stdout}`);
|
|
99
|
+
}
|
|
100
|
+
if (options.profile) {
|
|
101
|
+
ui.print(` ${ui.bold('Profile:')} ${options.profile}`);
|
|
102
|
+
}
|
|
103
|
+
ui.newLine();
|
|
104
|
+
ui.success('AWS credentials are configured and valid');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* nimbus auth gcp — Validate GCP credentials
|
|
108
|
+
*/
|
|
109
|
+
export async function authGcpCommand(options = {}) {
|
|
110
|
+
logger.info('Validating GCP credentials', { options });
|
|
111
|
+
ui.header('GCP Credentials');
|
|
112
|
+
// Check if gcloud CLI is installed
|
|
113
|
+
const installed = await isCliInstalled('gcloud');
|
|
114
|
+
if (!installed) {
|
|
115
|
+
ui.error('Google Cloud SDK (gcloud) is not installed');
|
|
116
|
+
ui.newLine();
|
|
117
|
+
ui.print('Install the Google Cloud SDK:');
|
|
118
|
+
ui.print(` ${ui.dim('brew install google-cloud-sdk')} (macOS)`);
|
|
119
|
+
ui.print(` ${ui.dim('https://cloud.google.com/sdk/docs/install')} (official)`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
ui.startSpinner({ message: 'Validating GCP credentials...' });
|
|
123
|
+
// Get current account
|
|
124
|
+
const account = await runCommand('gcloud', [
|
|
125
|
+
'auth',
|
|
126
|
+
'list',
|
|
127
|
+
'--filter=status:ACTIVE',
|
|
128
|
+
'--format=value(account)',
|
|
129
|
+
]);
|
|
130
|
+
if (!account.success || !account.stdout) {
|
|
131
|
+
ui.stopSpinnerFail('GCP credentials validation failed');
|
|
132
|
+
ui.newLine();
|
|
133
|
+
ui.error('No active GCP credentials found');
|
|
134
|
+
ui.print('Configure credentials:');
|
|
135
|
+
ui.print(` ${ui.dim('gcloud auth login')}`);
|
|
136
|
+
ui.print(` ${ui.dim('gcloud auth application-default login')}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
ui.stopSpinnerSuccess('GCP credentials valid');
|
|
140
|
+
ui.newLine();
|
|
141
|
+
ui.print(` ${ui.bold('Account:')} ${account.stdout}`);
|
|
142
|
+
// Check Application Default Credentials
|
|
143
|
+
const adcCheck = await runCommand('gcloud', [
|
|
144
|
+
'auth',
|
|
145
|
+
'application-default',
|
|
146
|
+
'print-access-token',
|
|
147
|
+
]);
|
|
148
|
+
if (adcCheck.success) {
|
|
149
|
+
ui.print(` ${ui.bold('ADC:')} ${ui.color('configured', 'green')}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
ui.print(` ${ui.bold('ADC:')} ${ui.color('not configured', 'yellow')}`);
|
|
153
|
+
ui.print(ui.dim(' Set with: gcloud auth application-default login'));
|
|
154
|
+
}
|
|
155
|
+
// Get current project
|
|
156
|
+
const projectArgs = options.project
|
|
157
|
+
? ['config', 'get-value', 'project', '--project', options.project]
|
|
158
|
+
: ['config', 'get-value', 'project'];
|
|
159
|
+
const project = await runCommand('gcloud', projectArgs);
|
|
160
|
+
if (project.success && project.stdout && project.stdout !== '(unset)') {
|
|
161
|
+
ui.print(` ${ui.bold('Project:')} ${project.stdout}`);
|
|
162
|
+
}
|
|
163
|
+
else if (options.project) {
|
|
164
|
+
ui.print(` ${ui.bold('Project:')} ${options.project}`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
ui.print(` ${ui.bold('Project:')} ${ui.color('not set', 'yellow')}`);
|
|
168
|
+
ui.print(ui.dim(' Set with: gcloud config set project PROJECT_ID'));
|
|
169
|
+
}
|
|
170
|
+
// Get current region
|
|
171
|
+
const region = await runCommand('gcloud', ['config', 'get-value', 'compute/region']);
|
|
172
|
+
if (region.success && region.stdout && region.stdout !== '(unset)') {
|
|
173
|
+
ui.print(` ${ui.bold('Region:')} ${region.stdout}`);
|
|
174
|
+
}
|
|
175
|
+
ui.newLine();
|
|
176
|
+
ui.success('GCP credentials are configured and valid');
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* nimbus auth azure — Validate Azure credentials
|
|
180
|
+
*/
|
|
181
|
+
export async function authAzureCommand(options = {}) {
|
|
182
|
+
logger.info('Validating Azure credentials', { options });
|
|
183
|
+
ui.header('Azure Credentials');
|
|
184
|
+
// Check if az CLI is installed
|
|
185
|
+
const installed = await isCliInstalled('az');
|
|
186
|
+
if (!installed) {
|
|
187
|
+
ui.error('Azure CLI (az) is not installed');
|
|
188
|
+
ui.newLine();
|
|
189
|
+
ui.print('Install the Azure CLI:');
|
|
190
|
+
ui.print(` ${ui.dim('brew install azure-cli')} (macOS)`);
|
|
191
|
+
ui.print(` ${ui.dim('https://learn.microsoft.com/en-us/cli/azure/install-azure-cli')} (official)`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
ui.startSpinner({ message: 'Validating Azure credentials...' });
|
|
195
|
+
// Get current account
|
|
196
|
+
const account = await runCommand('az', ['account', 'show', '--output', 'json']);
|
|
197
|
+
if (!account.success) {
|
|
198
|
+
ui.stopSpinnerFail('Azure credentials validation failed');
|
|
199
|
+
ui.newLine();
|
|
200
|
+
ui.error('No active Azure credentials found');
|
|
201
|
+
ui.print('Configure credentials:');
|
|
202
|
+
ui.print(` ${ui.dim('az login')}`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
ui.stopSpinnerSuccess('Azure credentials valid');
|
|
206
|
+
ui.newLine();
|
|
207
|
+
try {
|
|
208
|
+
const data = JSON.parse(account.stdout);
|
|
209
|
+
ui.print(` ${ui.bold('Subscription:')} ${data.name} (${data.id})`);
|
|
210
|
+
ui.print(` ${ui.bold('Tenant:')} ${data.tenantId}`);
|
|
211
|
+
ui.print(` ${ui.bold('State:')} ${data.state}`);
|
|
212
|
+
if (data.user) {
|
|
213
|
+
ui.print(` ${ui.bold('User:')} ${data.user.name} (${data.user.type})`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
ui.print(` ${account.stdout}`);
|
|
218
|
+
}
|
|
219
|
+
ui.newLine();
|
|
220
|
+
ui.success('Azure credentials are configured and valid');
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* H1: AWS SSO Login — delegates to `aws sso login` so the CLI handles the browser flow.
|
|
224
|
+
* spawnSync with stdio: 'inherit' so device codes / browser prompts appear in terminal.
|
|
225
|
+
*/
|
|
226
|
+
export async function loginAwsCommand(options = {}) {
|
|
227
|
+
const installed = await isCliInstalled('aws');
|
|
228
|
+
if (!installed) {
|
|
229
|
+
ui.error('AWS CLI is not installed. Install from https://aws.amazon.com/cli/');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
ui.info('Launching AWS SSO login...');
|
|
233
|
+
ui.print(ui.dim('The browser (or device code) flow is handled by the AWS CLI.'));
|
|
234
|
+
ui.newLine();
|
|
235
|
+
const { spawnSync } = await import('child_process');
|
|
236
|
+
const args = ['sso', 'login'];
|
|
237
|
+
if (options.profile) {
|
|
238
|
+
args.push('--profile', options.profile);
|
|
239
|
+
}
|
|
240
|
+
const result = spawnSync('aws', args, { stdio: 'inherit' });
|
|
241
|
+
if (result.status === 0) {
|
|
242
|
+
ui.success('AWS SSO login completed successfully.');
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
ui.error('AWS SSO login failed or was cancelled.');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* H1: GCP Login — delegates to `gcloud auth login --no-launch-browser` (device code flow).
|
|
250
|
+
*/
|
|
251
|
+
export async function loginGcpCommand(options = {}) {
|
|
252
|
+
const installed = await isCliInstalled('gcloud');
|
|
253
|
+
if (!installed) {
|
|
254
|
+
ui.error('Google Cloud SDK not installed. See https://cloud.google.com/sdk/docs/install');
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
ui.info('Launching GCP device-code login...');
|
|
258
|
+
ui.print(ui.dim('Follow the URL and code shown below to complete authentication.'));
|
|
259
|
+
ui.newLine();
|
|
260
|
+
const { spawnSync } = await import('child_process');
|
|
261
|
+
const args = ['auth', 'login', '--no-launch-browser'];
|
|
262
|
+
if (options.project) {
|
|
263
|
+
args.push('--project', options.project);
|
|
264
|
+
}
|
|
265
|
+
const result = spawnSync('gcloud', args, { stdio: 'inherit' });
|
|
266
|
+
if (result.status === 0) {
|
|
267
|
+
ui.success('GCP login completed successfully.');
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
ui.error('GCP login failed or was cancelled.');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* H1: Azure Login — delegates to `az login --use-device-code`.
|
|
275
|
+
*/
|
|
276
|
+
export async function loginAzureCommand(options = {}) {
|
|
277
|
+
const installed = await isCliInstalled('az');
|
|
278
|
+
if (!installed) {
|
|
279
|
+
ui.error('Azure CLI not installed. See https://learn.microsoft.com/en-us/cli/azure/install-azure-cli');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
ui.info('Launching Azure device-code login...');
|
|
283
|
+
ui.print(ui.dim('Follow the URL and code shown below to complete authentication.'));
|
|
284
|
+
ui.newLine();
|
|
285
|
+
const { spawnSync } = await import('child_process');
|
|
286
|
+
const args = ['login', '--use-device-code'];
|
|
287
|
+
if (options.subscription) {
|
|
288
|
+
args.push('--subscription', options.subscription);
|
|
289
|
+
}
|
|
290
|
+
const result = spawnSync('az', args, { stdio: 'inherit' });
|
|
291
|
+
if (result.status === 0) {
|
|
292
|
+
ui.success('Azure login completed successfully.');
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
ui.error('Azure login failed or was cancelled.');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Cloud auth parent command router
|
|
300
|
+
*/
|
|
301
|
+
export async function authCloudCommand(provider, options = {}) {
|
|
302
|
+
switch (provider) {
|
|
303
|
+
case 'aws':
|
|
304
|
+
await authAwsCommand(options);
|
|
305
|
+
break;
|
|
306
|
+
case 'gcp':
|
|
307
|
+
case 'google':
|
|
308
|
+
await authGcpCommand(options);
|
|
309
|
+
break;
|
|
310
|
+
case 'azure':
|
|
311
|
+
await authAzureCommand(options);
|
|
312
|
+
break;
|
|
313
|
+
default:
|
|
314
|
+
ui.error(`Unknown cloud provider: ${provider}`);
|
|
315
|
+
ui.newLine();
|
|
316
|
+
ui.print('Supported providers:');
|
|
317
|
+
ui.print(' nimbus auth aws — Validate AWS credentials');
|
|
318
|
+
ui.print(' nimbus auth gcp — Validate GCP credentials');
|
|
319
|
+
ui.print(' nimbus auth azure — Validate Azure credentials');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Cloud login command router — delegates to CLI tools for SSO/OAuth flows (H1).
|
|
324
|
+
*/
|
|
325
|
+
export async function loginCloudCommand(provider, options = {}) {
|
|
326
|
+
switch (provider) {
|
|
327
|
+
case 'aws':
|
|
328
|
+
await loginAwsCommand(options);
|
|
329
|
+
break;
|
|
330
|
+
case 'gcp':
|
|
331
|
+
case 'google':
|
|
332
|
+
await loginGcpCommand(options);
|
|
333
|
+
break;
|
|
334
|
+
case 'azure':
|
|
335
|
+
await loginAzureCommand(options);
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
ui.error(`Unknown cloud provider: ${provider}`);
|
|
339
|
+
ui.newLine();
|
|
340
|
+
ui.print('Supported providers:');
|
|
341
|
+
ui.print(' nimbus auth login aws — AWS SSO login (browser/device code)');
|
|
342
|
+
ui.print(' nimbus auth login gcp — GCP device-code login');
|
|
343
|
+
ui.print(' nimbus auth login azure — Azure device-code login');
|
|
344
|
+
}
|
|
345
|
+
}
|