@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,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-Interactive CLI Mode
|
|
3
|
+
*
|
|
4
|
+
* Runs the Nimbus agent with a prompt from the command line.
|
|
5
|
+
* Outputs results to stdout and exits.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* nimbus run "deploy the staging environment"
|
|
9
|
+
* nimbus run "fix the failing tests" --auto-approve
|
|
10
|
+
* echo "analyze this repo" | nimbus run --stdin
|
|
11
|
+
* nimbus run "estimate costs" --format json --model anthropic/claude-haiku-4-5
|
|
12
|
+
* nimbus run --schema # print the JSON output schema and exit
|
|
13
|
+
*/
|
|
14
|
+
import { runAgentLoop } from '../agent/loop';
|
|
15
|
+
import { createPermissionState, checkPermission } from '../agent/permissions';
|
|
16
|
+
import { defaultToolRegistry } from '../tools/schemas/types';
|
|
17
|
+
import { standardTools } from '../tools/schemas/standard';
|
|
18
|
+
import { devopsTools } from '../tools/schemas/devops';
|
|
19
|
+
import { expandFileReferences } from '../agent/expand-files';
|
|
20
|
+
/**
|
|
21
|
+
* Parse `nimbus run` CLI arguments.
|
|
22
|
+
*/
|
|
23
|
+
export function parseRunArgs(args) {
|
|
24
|
+
let prompt = '';
|
|
25
|
+
let format = 'text';
|
|
26
|
+
let autoApprove = false;
|
|
27
|
+
let stdin = false;
|
|
28
|
+
let stdinJson = false;
|
|
29
|
+
let model;
|
|
30
|
+
let mode = 'build';
|
|
31
|
+
let maxTurns = 50;
|
|
32
|
+
let timeout;
|
|
33
|
+
let rawToolOutput = false;
|
|
34
|
+
let schema = false;
|
|
35
|
+
let dryRun = false;
|
|
36
|
+
let exitOnError = true; // C5: default true for CI/CD POSIX convention
|
|
37
|
+
let context;
|
|
38
|
+
let workspace;
|
|
39
|
+
let namespace;
|
|
40
|
+
let notify;
|
|
41
|
+
let notifySlack;
|
|
42
|
+
let budget;
|
|
43
|
+
const positional = [];
|
|
44
|
+
for (let i = 0; i < args.length; i++) {
|
|
45
|
+
const arg = args[i];
|
|
46
|
+
switch (arg) {
|
|
47
|
+
case '--format':
|
|
48
|
+
format = (args[++i] ?? 'text');
|
|
49
|
+
break;
|
|
50
|
+
case '--json':
|
|
51
|
+
format = 'json';
|
|
52
|
+
break;
|
|
53
|
+
case '--auto-approve':
|
|
54
|
+
case '-y':
|
|
55
|
+
case '--non-interactive':
|
|
56
|
+
autoApprove = true;
|
|
57
|
+
break;
|
|
58
|
+
case '--stdin':
|
|
59
|
+
stdin = true;
|
|
60
|
+
break;
|
|
61
|
+
case '--stdin-json':
|
|
62
|
+
stdinJson = true;
|
|
63
|
+
stdin = true; // also read stdin
|
|
64
|
+
break;
|
|
65
|
+
case '--model':
|
|
66
|
+
model = args[++i];
|
|
67
|
+
break;
|
|
68
|
+
case '--mode':
|
|
69
|
+
mode = (args[++i] ?? 'build');
|
|
70
|
+
break;
|
|
71
|
+
case '--max-turns':
|
|
72
|
+
maxTurns = parseInt(args[++i] ?? '50', 10);
|
|
73
|
+
break;
|
|
74
|
+
case '--timeout':
|
|
75
|
+
// G13: timeout in seconds, converted to ms
|
|
76
|
+
timeout = parseInt(args[++i] ?? '0', 10) * 1000;
|
|
77
|
+
break;
|
|
78
|
+
case '--raw-tool-output':
|
|
79
|
+
// G15: print last tool output as JSON
|
|
80
|
+
rawToolOutput = true;
|
|
81
|
+
break;
|
|
82
|
+
case '--schema':
|
|
83
|
+
// G23: print the JSON output schema and exit
|
|
84
|
+
schema = true;
|
|
85
|
+
break;
|
|
86
|
+
case '--json-schema':
|
|
87
|
+
// GAP-16: print the stable JSON output schema and exit
|
|
88
|
+
schema = true;
|
|
89
|
+
break;
|
|
90
|
+
case '--dry-run':
|
|
91
|
+
// M1: dry-run forces plan mode — no mutations allowed
|
|
92
|
+
dryRun = true;
|
|
93
|
+
mode = 'plan';
|
|
94
|
+
break;
|
|
95
|
+
case '--exit-code-on-error':
|
|
96
|
+
// H3: exit with code 1 on failure
|
|
97
|
+
exitOnError = true;
|
|
98
|
+
break;
|
|
99
|
+
case '--no-exit-on-error':
|
|
100
|
+
// C5: legacy scripts can opt out of exit-on-error
|
|
101
|
+
exitOnError = false;
|
|
102
|
+
break;
|
|
103
|
+
case '--context':
|
|
104
|
+
// H3: kubectl context
|
|
105
|
+
context = args[++i];
|
|
106
|
+
break;
|
|
107
|
+
case '--workspace':
|
|
108
|
+
// H3: terraform workspace
|
|
109
|
+
workspace = args[++i];
|
|
110
|
+
break;
|
|
111
|
+
case '--namespace':
|
|
112
|
+
case '-n':
|
|
113
|
+
// H3: kubernetes namespace
|
|
114
|
+
namespace = args[++i];
|
|
115
|
+
break;
|
|
116
|
+
case '--notify':
|
|
117
|
+
// H3: webhook URL
|
|
118
|
+
notify = args[++i];
|
|
119
|
+
break;
|
|
120
|
+
case '--notify-slack':
|
|
121
|
+
// H3: slack webhook
|
|
122
|
+
notifySlack = args[++i];
|
|
123
|
+
break;
|
|
124
|
+
case '--budget':
|
|
125
|
+
// G16: cost budget in USD
|
|
126
|
+
budget = parseFloat(args[++i] ?? '0');
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
if (!arg.startsWith('-')) {
|
|
130
|
+
positional.push(arg);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
prompt = positional.join(' ');
|
|
136
|
+
return { prompt, format, autoApprove, stdin, stdinJson, model, mode, maxTurns, timeout, rawToolOutput, schema, dryRun, exitOnError, context, workspace, namespace, notify, notifySlack, budget };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Execute a non-interactive run.
|
|
140
|
+
*/
|
|
141
|
+
export async function executeRun(router, options) {
|
|
142
|
+
// G23 / GAP-16: --schema / --json-schema flag: print the JSON output schema and exit immediately
|
|
143
|
+
if (options.schema) {
|
|
144
|
+
const schema = {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
success: { type: 'boolean', description: 'Whether the agent completed without error' },
|
|
148
|
+
output: { type: 'string', description: 'Final text response from the agent' },
|
|
149
|
+
cost: { type: 'number', description: 'Total cost in USD' },
|
|
150
|
+
turns: { type: 'number', description: 'Number of LLM turns taken' },
|
|
151
|
+
toolCalls: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, success: { type: 'boolean' }, durationMs: { type: 'number' } } } },
|
|
152
|
+
errors: { type: 'array', items: { type: 'string' }, description: 'Any error messages encountered' },
|
|
153
|
+
},
|
|
154
|
+
required: ['success', 'output', 'cost', 'turns', 'toolCalls', 'errors'],
|
|
155
|
+
};
|
|
156
|
+
process.stdout.write(JSON.stringify(schema, null, 2) + '\n');
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
output: '',
|
|
160
|
+
turns: 0,
|
|
161
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
162
|
+
cost: 0,
|
|
163
|
+
interrupted: false,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// H3: Inject context/workspace/namespace into environment before agent loop
|
|
167
|
+
if (options.context)
|
|
168
|
+
process.env.KUBECTL_CONTEXT = options.context;
|
|
169
|
+
if (options.workspace)
|
|
170
|
+
process.env.TF_WORKSPACE = options.workspace;
|
|
171
|
+
if (options.namespace)
|
|
172
|
+
process.env.K8S_NAMESPACE = options.namespace;
|
|
173
|
+
// Get prompt from stdin if requested
|
|
174
|
+
let prompt = options.prompt;
|
|
175
|
+
if (options.stdin && !prompt) {
|
|
176
|
+
const stdinContent = await readStdin();
|
|
177
|
+
// L5: --stdin-json support: parse stdin as { prompt, mode, model, autoApprove, maxTurns }
|
|
178
|
+
if (options.stdinJson && stdinContent) {
|
|
179
|
+
try {
|
|
180
|
+
const config = JSON.parse(stdinContent);
|
|
181
|
+
if (typeof config.prompt === 'string')
|
|
182
|
+
prompt = config.prompt;
|
|
183
|
+
if (config.mode === 'plan' || config.mode === 'build' || config.mode === 'deploy') {
|
|
184
|
+
options = { ...options, mode: config.mode };
|
|
185
|
+
}
|
|
186
|
+
if (typeof config.model === 'string')
|
|
187
|
+
options = { ...options, model: config.model };
|
|
188
|
+
if (typeof config.autoApprove === 'boolean')
|
|
189
|
+
options = { ...options, autoApprove: config.autoApprove };
|
|
190
|
+
if (typeof config.maxTurns === 'number')
|
|
191
|
+
options = { ...options, maxTurns: config.maxTurns };
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// If JSON parse fails, treat stdin as raw prompt text
|
|
195
|
+
prompt = stdinContent;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
prompt = stdinContent;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Expand @file references in the prompt
|
|
203
|
+
prompt = expandFileReferences(prompt, process.cwd());
|
|
204
|
+
if (!prompt) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
output: 'Error: No prompt provided. Usage: nimbus run "your prompt"',
|
|
208
|
+
turns: 0,
|
|
209
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
210
|
+
cost: 0,
|
|
211
|
+
interrupted: false,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Set up tool registry
|
|
215
|
+
const registry = defaultToolRegistry;
|
|
216
|
+
if (registry.size === 0) {
|
|
217
|
+
// Register all built-in tools
|
|
218
|
+
for (const tool of [...standardTools, ...devopsTools]) {
|
|
219
|
+
try {
|
|
220
|
+
registry.register(tool);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
/* skip duplicates */
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Set up permission state
|
|
228
|
+
const permissionState = createPermissionState();
|
|
229
|
+
// Collect output
|
|
230
|
+
const outputParts = [];
|
|
231
|
+
const tableRows = [];
|
|
232
|
+
// G13: Set up timeout AbortController if --timeout was specified
|
|
233
|
+
let timeoutAbortController;
|
|
234
|
+
let timeoutHandle;
|
|
235
|
+
if (options.timeout && options.timeout > 0) {
|
|
236
|
+
timeoutAbortController = new AbortController();
|
|
237
|
+
timeoutHandle = setTimeout(() => {
|
|
238
|
+
timeoutAbortController.abort();
|
|
239
|
+
if (options.format === 'text') {
|
|
240
|
+
process.stderr.write(`\n[Timeout: agent loop aborted after ${options.timeout / 1000}s]\n`);
|
|
241
|
+
}
|
|
242
|
+
}, options.timeout);
|
|
243
|
+
}
|
|
244
|
+
// G15: Track last tool output for --raw-tool-output
|
|
245
|
+
let lastToolName = '';
|
|
246
|
+
let lastToolOutput = '';
|
|
247
|
+
// G23: Track all tool calls for structured JSON output
|
|
248
|
+
const allToolCalls = [];
|
|
249
|
+
// H1: Discover infra context for CI/CD pipelines (best-effort, non-blocking)
|
|
250
|
+
let infraContext;
|
|
251
|
+
try {
|
|
252
|
+
const { discoverInfraContext } = await import('../cli/init');
|
|
253
|
+
infraContext = await discoverInfraContext(process.cwd());
|
|
254
|
+
}
|
|
255
|
+
catch { /* non-critical */ }
|
|
256
|
+
// Run the agent loop
|
|
257
|
+
const result = await runAgentLoop(prompt, [], {
|
|
258
|
+
router,
|
|
259
|
+
toolRegistry: registry,
|
|
260
|
+
mode: options.mode,
|
|
261
|
+
maxTurns: options.maxTurns,
|
|
262
|
+
model: options.model,
|
|
263
|
+
cwd: process.cwd(),
|
|
264
|
+
signal: timeoutAbortController?.signal,
|
|
265
|
+
dryRun: options.dryRun,
|
|
266
|
+
costBudgetUSD: options.budget,
|
|
267
|
+
infraContext,
|
|
268
|
+
onText: text => {
|
|
269
|
+
outputParts.push(text);
|
|
270
|
+
if (options.format === 'text') {
|
|
271
|
+
process.stdout.write(text);
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
onToolCallStart: toolCall => {
|
|
275
|
+
if (options.format === 'text') {
|
|
276
|
+
process.stderr.write(`\n[Tool: ${toolCall.name}]\n`);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
onToolCallEnd: (toolCall, result) => {
|
|
280
|
+
if (options.format === 'text' && result.isError) {
|
|
281
|
+
process.stderr.write(`[Error: ${result.error}]\n`);
|
|
282
|
+
}
|
|
283
|
+
if (options.format === 'table') {
|
|
284
|
+
tableRows.push({
|
|
285
|
+
tool: toolCall.name,
|
|
286
|
+
status: result.isError ? 'error' : 'ok',
|
|
287
|
+
output: (result.output ?? result.error ?? '').slice(0, 80),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// G15: track last tool output
|
|
291
|
+
lastToolName = toolCall.name;
|
|
292
|
+
lastToolOutput = result.isError ? (result.error ?? '') : (result.output ?? '');
|
|
293
|
+
// G23: accumulate all tool calls for structured JSON output
|
|
294
|
+
allToolCalls.push({
|
|
295
|
+
name: toolCall.name,
|
|
296
|
+
input: toolCall.input && typeof toolCall.input === 'object'
|
|
297
|
+
? toolCall.input
|
|
298
|
+
: {},
|
|
299
|
+
output: result.isError ? (result.error ?? '') : (result.output ?? ''),
|
|
300
|
+
isError: result.isError ?? false,
|
|
301
|
+
});
|
|
302
|
+
},
|
|
303
|
+
checkPermission: async (tool, input) => {
|
|
304
|
+
if (options.autoApprove) {
|
|
305
|
+
return 'allow';
|
|
306
|
+
}
|
|
307
|
+
const decision = checkPermission(tool, input, permissionState);
|
|
308
|
+
if (decision === 'ask') {
|
|
309
|
+
// In non-interactive mode without --auto-approve, deny by default
|
|
310
|
+
return 'deny';
|
|
311
|
+
}
|
|
312
|
+
return decision;
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
// G13: Clear the timeout timer if we finished before it fired
|
|
316
|
+
if (timeoutHandle) {
|
|
317
|
+
clearTimeout(timeoutHandle);
|
|
318
|
+
}
|
|
319
|
+
const output = outputParts.join('');
|
|
320
|
+
// G15: --raw-tool-output: print last tool call as JSON to stdout
|
|
321
|
+
if (options.rawToolOutput && lastToolName) {
|
|
322
|
+
console.log(JSON.stringify({ tool: lastToolName, output: lastToolOutput }));
|
|
323
|
+
return {
|
|
324
|
+
success: !result.interrupted,
|
|
325
|
+
output,
|
|
326
|
+
turns: result.turns,
|
|
327
|
+
usage: result.usage,
|
|
328
|
+
cost: result.totalCost,
|
|
329
|
+
interrupted: result.interrupted,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
// Format output
|
|
333
|
+
if (options.format === 'json') {
|
|
334
|
+
// GAP-16 / G23: structured JSON output matching the stable RunJsonOutput schema
|
|
335
|
+
// L2: Extract terraform plan summary from tool outputs for CI-friendly output
|
|
336
|
+
let planSummary;
|
|
337
|
+
const tfPlanCall = allToolCalls.find(tc => tc.name === 'terraform' && tc.output && /Plan:/.test(tc.output));
|
|
338
|
+
if (tfPlanCall?.output) {
|
|
339
|
+
const planLine = tfPlanCall.output.match(/Plan:\s*(\d+)\s*to add,\s*(\d+)\s*to change,\s*(\d+)\s*to destroy/i);
|
|
340
|
+
if (planLine) {
|
|
341
|
+
planSummary = {
|
|
342
|
+
toAdd: parseInt(planLine[1]),
|
|
343
|
+
toChange: parseInt(planLine[2]),
|
|
344
|
+
toDestroy: parseInt(planLine[3]),
|
|
345
|
+
raw: planLine[0],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const jsonResult = {
|
|
350
|
+
success: !result.interrupted,
|
|
351
|
+
output,
|
|
352
|
+
cost: result.totalCost ?? 0,
|
|
353
|
+
turns: result.turns ?? 0,
|
|
354
|
+
toolCalls: allToolCalls.map(tc => ({
|
|
355
|
+
name: tc.name,
|
|
356
|
+
success: !tc.isError,
|
|
357
|
+
durationMs: 0,
|
|
358
|
+
})),
|
|
359
|
+
errors: allToolCalls.filter(tc => tc.isError).map(tc => tc.output).filter(Boolean),
|
|
360
|
+
...(planSummary ? { planSummary } : {}),
|
|
361
|
+
};
|
|
362
|
+
// M3: Build devops_summary from tool calls
|
|
363
|
+
const DEVOPS_TOOL_NAMES = new Set([
|
|
364
|
+
'terraform', 'kubectl', 'helm', 'aws', 'gcloud', 'az',
|
|
365
|
+
'docker', 'secrets', 'cicd', 'monitor', 'gitops', 'cloud_action',
|
|
366
|
+
'logs', 'certs', 'mesh', 'cfn', 'k8s_rbac', 'generate_infra',
|
|
367
|
+
'kubectl_context', 'helm_values', 'cost_estimate', 'cloud_discover',
|
|
368
|
+
]);
|
|
369
|
+
const toolsUsed = [...new Set(allToolCalls.map(tc => tc.name))];
|
|
370
|
+
const devopsToolsUsed = toolsUsed.filter(t => DEVOPS_TOOL_NAMES.has(t));
|
|
371
|
+
if (devopsToolsUsed.length > 0) {
|
|
372
|
+
jsonResult.devops_summary = {
|
|
373
|
+
tools_used: devopsToolsUsed,
|
|
374
|
+
tool_call_count: allToolCalls.length,
|
|
375
|
+
devops_tool_count: allToolCalls.filter(tc => DEVOPS_TOOL_NAMES.has(tc.name)).length,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
// M1: Add infraContext and toolsUsed fields
|
|
379
|
+
jsonResult.toolsUsed = toolsUsed;
|
|
380
|
+
if (infraContext) {
|
|
381
|
+
jsonResult.infraContext = {
|
|
382
|
+
terraformWorkspace: infraContext.terraformWorkspace,
|
|
383
|
+
kubectlContext: infraContext.kubectlContext,
|
|
384
|
+
awsAccount: infraContext.awsAccount,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
console.log(JSON.stringify(jsonResult, null, 2));
|
|
388
|
+
}
|
|
389
|
+
else if (options.format === 'text') {
|
|
390
|
+
// Text was already streamed above
|
|
391
|
+
console.log(''); // Final newline
|
|
392
|
+
}
|
|
393
|
+
else if (options.format === 'table') {
|
|
394
|
+
// ASCII table of tool calls
|
|
395
|
+
const COL_TOOL = 30;
|
|
396
|
+
const COL_STATUS = 6;
|
|
397
|
+
const COL_OUTPUT = 80;
|
|
398
|
+
const divider = `${'-'.repeat(COL_TOOL + 2)}+${'-'.repeat(COL_STATUS + 2)}+${'-'.repeat(COL_OUTPUT + 2)}`;
|
|
399
|
+
const pad = (s, n) => s.slice(0, n).padEnd(n);
|
|
400
|
+
console.log(divider);
|
|
401
|
+
console.log(`| ${pad('Tool', COL_TOOL)} | ${pad('Status', COL_STATUS)} | ${pad('Output', COL_OUTPUT)} |`);
|
|
402
|
+
console.log(divider);
|
|
403
|
+
for (const row of tableRows) {
|
|
404
|
+
console.log(`| ${pad(row.tool, COL_TOOL)} | ${pad(row.status, COL_STATUS)} | ${pad(row.output, COL_OUTPUT)} |`);
|
|
405
|
+
}
|
|
406
|
+
console.log(divider);
|
|
407
|
+
console.log('');
|
|
408
|
+
// Also print final text output
|
|
409
|
+
if (output)
|
|
410
|
+
process.stdout.write(output + '\n');
|
|
411
|
+
}
|
|
412
|
+
const runResult = {
|
|
413
|
+
success: !result.interrupted,
|
|
414
|
+
output,
|
|
415
|
+
turns: result.turns,
|
|
416
|
+
usage: result.usage,
|
|
417
|
+
cost: result.totalCost,
|
|
418
|
+
interrupted: result.interrupted,
|
|
419
|
+
};
|
|
420
|
+
// H3: Fire webhook notifications after run completes
|
|
421
|
+
const duration = Date.now(); // approximate; real duration would need a start time
|
|
422
|
+
const notifyPayload = {
|
|
423
|
+
success: runResult.success,
|
|
424
|
+
output: runResult.output.slice(0, 2000), // truncate for webhook
|
|
425
|
+
cost: runResult.cost,
|
|
426
|
+
duration,
|
|
427
|
+
};
|
|
428
|
+
if (options.notify) {
|
|
429
|
+
try {
|
|
430
|
+
await fetch(options.notify, {
|
|
431
|
+
method: 'POST',
|
|
432
|
+
headers: { 'Content-Type': 'application/json' },
|
|
433
|
+
body: JSON.stringify(notifyPayload),
|
|
434
|
+
signal: AbortSignal.timeout(10_000),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
// Webhook failure is non-fatal
|
|
439
|
+
process.stderr.write(`[Warning: notification webhook failed]\n`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (options.notifySlack) {
|
|
443
|
+
try {
|
|
444
|
+
const slackPayload = {
|
|
445
|
+
text: runResult.success
|
|
446
|
+
? `:white_check_mark: *Nimbus run succeeded*\n${runResult.output.slice(0, 500)}`
|
|
447
|
+
: `:x: *Nimbus run failed*\n${runResult.output.slice(0, 500)}`,
|
|
448
|
+
attachments: [
|
|
449
|
+
{
|
|
450
|
+
fields: [
|
|
451
|
+
{ title: 'Cost', value: `$${runResult.cost.toFixed(4)}`, short: true },
|
|
452
|
+
{ title: 'Turns', value: String(runResult.turns), short: true },
|
|
453
|
+
],
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
};
|
|
457
|
+
await fetch(options.notifySlack, {
|
|
458
|
+
method: 'POST',
|
|
459
|
+
headers: { 'Content-Type': 'application/json' },
|
|
460
|
+
body: JSON.stringify(slackPayload),
|
|
461
|
+
signal: AbortSignal.timeout(10_000),
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
process.stderr.write(`[Warning: Slack notification failed]\n`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// H3: Exit with code 1 if the run failed and --exit-code-on-error is set
|
|
469
|
+
if (options.exitOnError && !runResult.success) {
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
return runResult;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Read all input from stdin.
|
|
476
|
+
*/
|
|
477
|
+
async function readStdin() {
|
|
478
|
+
// If stdin is a TTY (no pipe), resolve immediately with empty string
|
|
479
|
+
if (process.stdin.isTTY) {
|
|
480
|
+
return '';
|
|
481
|
+
}
|
|
482
|
+
const chunks = [];
|
|
483
|
+
return new Promise((resolve, reject) => {
|
|
484
|
+
// 30-second timeout for stdin reads to prevent hanging
|
|
485
|
+
const timeout = setTimeout(() => {
|
|
486
|
+
process.stdin.removeAllListeners();
|
|
487
|
+
resolve(Buffer.concat(chunks).toString('utf-8').trim());
|
|
488
|
+
}, 30_000);
|
|
489
|
+
process.stdin.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
|
490
|
+
process.stdin.on('end', () => {
|
|
491
|
+
clearTimeout(timeout);
|
|
492
|
+
resolve(Buffer.concat(chunks).toString('utf-8').trim());
|
|
493
|
+
});
|
|
494
|
+
process.stdin.on('error', err => {
|
|
495
|
+
clearTimeout(timeout);
|
|
496
|
+
// On error, use whatever we've collected so far
|
|
497
|
+
if (chunks.length > 0) {
|
|
498
|
+
resolve(Buffer.concat(chunks).toString('utf-8').trim());
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
reject(err);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Basic Auth Middleware for `nimbus serve`
|
|
3
|
+
*
|
|
4
|
+
* Provides optional HTTP Basic Authentication for the headless API server.
|
|
5
|
+
* Disabled by default for local development; enabled via `--auth user:pass`.
|
|
6
|
+
*
|
|
7
|
+
* Unauthenticated endpoints (always bypassed):
|
|
8
|
+
* - GET /api/health
|
|
9
|
+
* - GET /api/openapi.json
|
|
10
|
+
* - OPTIONS (CORS preflight)
|
|
11
|
+
*
|
|
12
|
+
* @module cli/serve-auth
|
|
13
|
+
*/
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Paths that are always public (no auth required)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const PUBLIC_PATHS = new Set(['/api/health', '/api/openapi.json']);
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Middleware Factory
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Create an Elysia-compatible `onBeforeHandle` function that enforces
|
|
23
|
+
* HTTP Basic Authentication on protected endpoints.
|
|
24
|
+
*
|
|
25
|
+
* @param options - The username and password to validate against.
|
|
26
|
+
* @returns A handler that short-circuits with 401 when credentials are
|
|
27
|
+
* missing or invalid, or `undefined` to let the request through.
|
|
28
|
+
*/
|
|
29
|
+
export function createAuthMiddleware(options) {
|
|
30
|
+
const expectedToken = btoa(`${options.username}:${options.password}`);
|
|
31
|
+
return ({ request, set }) => {
|
|
32
|
+
const url = new URL(request.url);
|
|
33
|
+
// Skip auth for public endpoints
|
|
34
|
+
if (PUBLIC_PATHS.has(url.pathname)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
// Skip auth for CORS preflight requests
|
|
38
|
+
if (request.method === 'OPTIONS') {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const authHeader = request.headers.get('Authorization');
|
|
42
|
+
if (!authHeader) {
|
|
43
|
+
set.status = 401;
|
|
44
|
+
set.headers = { 'WWW-Authenticate': 'Basic realm="Nimbus API"' };
|
|
45
|
+
return { error: 'Authentication required' };
|
|
46
|
+
}
|
|
47
|
+
const [scheme, token] = authHeader.split(' ');
|
|
48
|
+
if (scheme !== 'Basic' || token !== expectedToken) {
|
|
49
|
+
set.status = 401;
|
|
50
|
+
set.headers = { 'WWW-Authenticate': 'Basic realm="Nimbus API"' };
|
|
51
|
+
return { error: 'Invalid credentials' };
|
|
52
|
+
}
|
|
53
|
+
// Credentials valid -- proceed
|
|
54
|
+
return undefined;
|
|
55
|
+
};
|
|
56
|
+
}
|