@build-astron-co/nimbus 0.4.1 → 0.4.3
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/CHANGELOG.md +268 -89
- package/README.md +26 -567
- package/dist/src/agent/compaction-agent.js +24 -12
- package/dist/src/agent/context-manager.js +2 -1
- package/dist/src/agent/expand-files.js +2 -1
- package/dist/src/agent/loop.js +71 -33
- package/dist/src/agent/permissions.js +4 -2
- package/dist/src/agent/system-prompt.js +34 -17
- package/dist/src/app.js +1 -1
- package/dist/src/auth/keychain.js +8 -4
- package/dist/src/auth/store.js +70 -107
- package/dist/src/cli/init.js +35 -19
- package/dist/src/cli/run.js +18 -10
- package/dist/src/cli/serve.js +4 -2
- package/dist/src/cli.js +52 -11
- package/dist/src/commands/alias.js +5 -3
- package/dist/src/commands/audit/index.js +2 -1
- package/dist/src/commands/aws-terraform.js +36 -18
- package/dist/src/commands/completions.js +1 -1
- package/dist/src/commands/config.js +3 -2
- package/dist/src/commands/connect-github.js +92 -0
- package/dist/src/commands/cost/index.js +3 -2
- package/dist/src/commands/deploy.js +15 -10
- package/dist/src/commands/doctor.js +9 -6
- package/dist/src/commands/drift/index.js +2 -1
- package/dist/src/commands/export.js +5 -3
- package/dist/src/commands/generate-terraform.js +110 -2
- package/dist/src/commands/import.js +3 -3
- package/dist/src/commands/incident.js +10 -5
- package/dist/src/commands/login.js +8 -93
- package/dist/src/commands/logs.js +16 -8
- package/dist/src/commands/onboarding.js +6 -4
- package/dist/src/commands/pipeline.js +6 -3
- package/dist/src/commands/plugin.js +3 -2
- package/dist/src/commands/profile.js +27 -14
- package/dist/src/commands/questionnaire.js +1 -1
- package/dist/src/commands/rollback.js +3 -2
- package/dist/src/commands/rollout.js +5 -3
- package/dist/src/commands/runbook.js +17 -10
- package/dist/src/commands/schedule.js +10 -5
- package/dist/src/commands/status.js +2 -1
- package/dist/src/commands/team-context.js +12 -7
- package/dist/src/commands/template.js +1 -1
- package/dist/src/commands/tf/index.js +6 -3
- package/dist/src/commands/upgrade.js +5 -3
- package/dist/src/commands/version.js +6 -3
- package/dist/src/commands/watch.js +6 -3
- package/dist/src/compat/sqlite.js +5 -3
- package/dist/src/config/mode-store.js +2 -1
- package/dist/src/config/profiles.js +4 -2
- package/dist/src/config/types.js +2 -1
- package/dist/src/engine/executor.js +8 -4
- package/dist/src/engine/planner.js +9 -5
- package/dist/src/llm/providers/anthropic.js +6 -3
- package/dist/src/llm/providers/ollama.js +1 -1
- package/dist/src/llm/router.js +22 -7
- package/dist/src/nimbus.js +1 -0
- package/dist/src/sessions/manager.js +6 -3
- package/dist/src/sharing/viewer.js +2 -1
- package/dist/src/tools/file-ops.js +1 -2
- package/dist/src/tools/schemas/devops.js +197 -108
- package/dist/src/tools/schemas/standard.js +1 -1
- package/dist/src/ui/App.js +25 -13
- package/dist/src/ui/FileDiffModal.js +22 -11
- package/dist/src/ui/HelpModal.js +2 -1
- package/dist/src/ui/InputBox.js +6 -3
- package/dist/src/ui/MessageList.js +40 -20
- package/dist/src/ui/TerminalPane.js +2 -1
- package/dist/src/ui/ToolCallDisplay.js +12 -6
- package/dist/src/ui/TreePane.js +2 -1
- package/dist/src/ui/ink/index.js +37 -21
- package/dist/src/version.js +1 -1
- package/dist/src/watcher/index.js +8 -4
- package/package.json +3 -5
- package/src/__tests__/alias.test.ts +0 -133
- package/src/__tests__/app.test.ts +0 -76
- package/src/__tests__/audit.test.ts +0 -877
- package/src/__tests__/circuit-breaker.test.ts +0 -116
- package/src/__tests__/cli-run.test.ts +0 -351
- package/src/__tests__/compat-sqlite.test.ts +0 -68
- package/src/__tests__/context-manager.test.ts +0 -632
- package/src/__tests__/context.test.ts +0 -242
- package/src/__tests__/devops-terminal-gaps.test.ts +0 -718
- package/src/__tests__/doctor.test.ts +0 -48
- package/src/__tests__/enterprise.test.ts +0 -401
- package/src/__tests__/export.test.ts +0 -236
- package/src/__tests__/gap-11-18-20.test.ts +0 -958
- package/src/__tests__/generator.test.ts +0 -433
- package/src/__tests__/helm-streaming.test.ts +0 -127
- package/src/__tests__/hooks.test.ts +0 -582
- package/src/__tests__/incident.test.ts +0 -179
- package/src/__tests__/init.test.ts +0 -487
- package/src/__tests__/intent-parser.test.ts +0 -229
- package/src/__tests__/llm-router.test.ts +0 -209
- package/src/__tests__/logs.test.ts +0 -107
- package/src/__tests__/loop-errors.test.ts +0 -244
- package/src/__tests__/lsp.test.ts +0 -293
- package/src/__tests__/modes.test.ts +0 -336
- package/src/__tests__/perf-optimizations.test.ts +0 -847
- package/src/__tests__/permissions.test.ts +0 -338
- package/src/__tests__/pipeline.test.ts +0 -50
- package/src/__tests__/polish-phase3.test.ts +0 -340
- package/src/__tests__/profile.test.ts +0 -237
- package/src/__tests__/rollback.test.ts +0 -83
- package/src/__tests__/runbook.test.ts +0 -219
- package/src/__tests__/schedule.test.ts +0 -206
- package/src/__tests__/serve.test.ts +0 -275
- package/src/__tests__/sessions.test.ts +0 -322
- package/src/__tests__/sharing.test.ts +0 -340
- package/src/__tests__/snapshots.test.ts +0 -581
- package/src/__tests__/standalone-migration.test.ts +0 -199
- package/src/__tests__/state-db.test.ts +0 -334
- package/src/__tests__/status.test.ts +0 -158
- package/src/__tests__/stream-with-tools.test.ts +0 -778
- package/src/__tests__/subagents.test.ts +0 -176
- package/src/__tests__/system-prompt.test.ts +0 -248
- package/src/__tests__/terminal-gap-v2.test.ts +0 -395
- package/src/__tests__/terminal-parity.test.ts +0 -393
- package/src/__tests__/tf-apply.test.ts +0 -187
- package/src/__tests__/tool-converter.test.ts +0 -256
- package/src/__tests__/tool-schemas.test.ts +0 -602
- package/src/__tests__/tools.test.ts +0 -144
- package/src/__tests__/version-json.test.ts +0 -184
- package/src/__tests__/version.test.ts +0 -49
- package/src/__tests__/watch.test.ts +0 -129
- package/src/agent/compaction-agent.ts +0 -266
- package/src/agent/context-manager.ts +0 -499
- package/src/agent/context.ts +0 -427
- package/src/agent/deploy-preview.ts +0 -487
- package/src/agent/expand-files.ts +0 -108
- package/src/agent/index.ts +0 -68
- package/src/agent/loop.ts +0 -1998
- package/src/agent/modes.ts +0 -429
- package/src/agent/permissions.ts +0 -513
- package/src/agent/subagents/base.ts +0 -116
- package/src/agent/subagents/cost.ts +0 -51
- package/src/agent/subagents/explore.ts +0 -42
- package/src/agent/subagents/general.ts +0 -54
- package/src/agent/subagents/index.ts +0 -102
- package/src/agent/subagents/infra.ts +0 -59
- package/src/agent/subagents/security.ts +0 -69
- package/src/agent/system-prompt.ts +0 -990
- package/src/app.ts +0 -180
- package/src/audit/activity-log.ts +0 -290
- package/src/audit/compliance-checker.ts +0 -540
- package/src/audit/cost-tracker.ts +0 -318
- package/src/audit/index.ts +0 -23
- package/src/audit/security-scanner.ts +0 -641
- package/src/auth/guard.ts +0 -75
- package/src/auth/index.ts +0 -56
- package/src/auth/keychain.ts +0 -82
- package/src/auth/oauth.ts +0 -465
- package/src/auth/providers.ts +0 -470
- package/src/auth/sso.ts +0 -113
- package/src/auth/store.ts +0 -505
- package/src/auth/types.ts +0 -187
- package/src/build.ts +0 -141
- package/src/cli/index.ts +0 -16
- package/src/cli/init.ts +0 -1227
- package/src/cli/openapi-spec.ts +0 -356
- package/src/cli/run.ts +0 -628
- package/src/cli/serve-auth.ts +0 -80
- package/src/cli/serve.ts +0 -539
- package/src/cli/web.ts +0 -71
- package/src/cli.ts +0 -1728
- package/src/clients/core-engine-client.ts +0 -227
- package/src/clients/enterprise-client.ts +0 -334
- package/src/clients/generator-client.ts +0 -351
- package/src/clients/git-client.ts +0 -627
- package/src/clients/github-client.ts +0 -410
- package/src/clients/helm-client.ts +0 -504
- package/src/clients/index.ts +0 -80
- package/src/clients/k8s-client.ts +0 -497
- package/src/clients/llm-client.ts +0 -161
- package/src/clients/rest-client.ts +0 -130
- package/src/clients/service-discovery.ts +0 -38
- package/src/clients/terraform-client.ts +0 -482
- package/src/clients/tools-client.ts +0 -1843
- package/src/clients/ws-client.ts +0 -115
- package/src/commands/alias.ts +0 -100
- package/src/commands/analyze/index.ts +0 -352
- package/src/commands/apply/helm.ts +0 -473
- package/src/commands/apply/index.ts +0 -213
- package/src/commands/apply/k8s.ts +0 -454
- package/src/commands/apply/terraform.ts +0 -582
- package/src/commands/ask.ts +0 -167
- package/src/commands/audit/index.ts +0 -357
- package/src/commands/auth-cloud.ts +0 -407
- package/src/commands/auth-list.ts +0 -134
- package/src/commands/auth-profile.ts +0 -121
- package/src/commands/auth-refresh.ts +0 -187
- package/src/commands/auth-status.ts +0 -141
- package/src/commands/aws/ec2.ts +0 -501
- package/src/commands/aws/iam.ts +0 -397
- package/src/commands/aws/index.ts +0 -133
- package/src/commands/aws/lambda.ts +0 -396
- package/src/commands/aws/rds.ts +0 -439
- package/src/commands/aws/s3.ts +0 -439
- package/src/commands/aws/vpc.ts +0 -393
- package/src/commands/aws-discover.ts +0 -542
- package/src/commands/aws-terraform.ts +0 -755
- package/src/commands/azure/aks.ts +0 -376
- package/src/commands/azure/functions.ts +0 -253
- package/src/commands/azure/index.ts +0 -116
- package/src/commands/azure/storage.ts +0 -478
- package/src/commands/azure/vm.ts +0 -355
- package/src/commands/billing/index.ts +0 -256
- package/src/commands/chat.ts +0 -320
- package/src/commands/completions.ts +0 -268
- package/src/commands/config.ts +0 -372
- package/src/commands/cost/cloud-cost-estimator.ts +0 -266
- package/src/commands/cost/estimator.ts +0 -79
- package/src/commands/cost/index.ts +0 -810
- package/src/commands/cost/parsers/terraform.ts +0 -273
- package/src/commands/cost/parsers/types.ts +0 -25
- package/src/commands/cost/pricing/aws.ts +0 -544
- package/src/commands/cost/pricing/azure.ts +0 -499
- package/src/commands/cost/pricing/gcp.ts +0 -396
- package/src/commands/cost/pricing/index.ts +0 -40
- package/src/commands/demo.ts +0 -250
- package/src/commands/deploy.ts +0 -260
- package/src/commands/doctor.ts +0 -1386
- package/src/commands/drift/index.ts +0 -787
- package/src/commands/explain.ts +0 -277
- package/src/commands/export.ts +0 -146
- package/src/commands/feedback.ts +0 -389
- package/src/commands/fix.ts +0 -324
- package/src/commands/fs/index.ts +0 -402
- package/src/commands/gcp/compute.ts +0 -325
- package/src/commands/gcp/functions.ts +0 -271
- package/src/commands/gcp/gke.ts +0 -438
- package/src/commands/gcp/iam.ts +0 -344
- package/src/commands/gcp/index.ts +0 -129
- package/src/commands/gcp/storage.ts +0 -284
- package/src/commands/generate-helm.ts +0 -1249
- package/src/commands/generate-k8s.ts +0 -1508
- package/src/commands/generate-terraform.ts +0 -1202
- package/src/commands/gh/index.ts +0 -863
- package/src/commands/git/index.ts +0 -1343
- package/src/commands/helm/index.ts +0 -1126
- package/src/commands/help.ts +0 -715
- package/src/commands/history.ts +0 -149
- package/src/commands/import.ts +0 -868
- package/src/commands/incident.ts +0 -166
- package/src/commands/index.ts +0 -367
- package/src/commands/init.ts +0 -1051
- package/src/commands/k8s/index.ts +0 -1137
- package/src/commands/login.ts +0 -716
- package/src/commands/logout.ts +0 -83
- package/src/commands/logs.ts +0 -167
- package/src/commands/onboarding.ts +0 -405
- package/src/commands/pipeline.ts +0 -186
- package/src/commands/plan/display.ts +0 -279
- package/src/commands/plan/index.ts +0 -599
- package/src/commands/plugin.ts +0 -398
- package/src/commands/preview.ts +0 -452
- package/src/commands/profile.ts +0 -342
- package/src/commands/questionnaire.ts +0 -1172
- package/src/commands/resume.ts +0 -47
- package/src/commands/rollback.ts +0 -315
- package/src/commands/rollout.ts +0 -88
- package/src/commands/runbook.ts +0 -346
- package/src/commands/schedule.ts +0 -236
- package/src/commands/status.ts +0 -252
- package/src/commands/team/index.ts +0 -346
- package/src/commands/team-context.ts +0 -220
- package/src/commands/template.ts +0 -233
- package/src/commands/tf/index.ts +0 -1093
- package/src/commands/upgrade.ts +0 -607
- package/src/commands/usage/index.ts +0 -134
- package/src/commands/version.ts +0 -174
- package/src/commands/watch.ts +0 -153
- package/src/compat/index.ts +0 -2
- package/src/compat/runtime.ts +0 -12
- package/src/compat/sqlite.ts +0 -177
- package/src/config/index.ts +0 -17
- package/src/config/manager.ts +0 -530
- package/src/config/mode-store.ts +0 -62
- package/src/config/profiles.ts +0 -84
- package/src/config/safety-policy.ts +0 -358
- package/src/config/schema.ts +0 -125
- package/src/config/types.ts +0 -609
- package/src/config/workspace-state.ts +0 -53
- package/src/context/context-db.ts +0 -199
- package/src/demo/index.ts +0 -349
- package/src/demo/scenarios/full-journey.ts +0 -229
- package/src/demo/scenarios/getting-started.ts +0 -127
- package/src/demo/scenarios/helm-release.ts +0 -341
- package/src/demo/scenarios/k8s-deployment.ts +0 -194
- package/src/demo/scenarios/terraform-vpc.ts +0 -170
- package/src/demo/types.ts +0 -92
- package/src/engine/cost-estimator.ts +0 -480
- package/src/engine/diagram-generator.ts +0 -256
- package/src/engine/drift-detector.ts +0 -902
- package/src/engine/executor.ts +0 -1066
- package/src/engine/index.ts +0 -76
- package/src/engine/orchestrator.ts +0 -636
- package/src/engine/planner.ts +0 -787
- package/src/engine/safety.ts +0 -743
- package/src/engine/verifier.ts +0 -770
- package/src/enterprise/audit.ts +0 -348
- package/src/enterprise/auth.ts +0 -270
- package/src/enterprise/billing.ts +0 -822
- package/src/enterprise/index.ts +0 -17
- package/src/enterprise/teams.ts +0 -443
- package/src/generator/best-practices.ts +0 -1608
- package/src/generator/helm.ts +0 -630
- package/src/generator/index.ts +0 -37
- package/src/generator/intent-parser.ts +0 -514
- package/src/generator/kubernetes.ts +0 -976
- package/src/generator/terraform.ts +0 -1875
- package/src/history/index.ts +0 -8
- package/src/history/manager.ts +0 -250
- package/src/history/types.ts +0 -34
- package/src/hooks/config.ts +0 -432
- package/src/hooks/engine.ts +0 -392
- package/src/hooks/index.ts +0 -4
- package/src/llm/auth-bridge.ts +0 -198
- package/src/llm/circuit-breaker.ts +0 -140
- package/src/llm/config-loader.ts +0 -201
- package/src/llm/cost-calculator.ts +0 -171
- package/src/llm/index.ts +0 -8
- package/src/llm/model-aliases.ts +0 -115
- package/src/llm/provider-registry.ts +0 -63
- package/src/llm/providers/anthropic.ts +0 -462
- package/src/llm/providers/bedrock.ts +0 -477
- package/src/llm/providers/google.ts +0 -405
- package/src/llm/providers/ollama.ts +0 -767
- package/src/llm/providers/openai-compatible.ts +0 -340
- package/src/llm/providers/openai.ts +0 -328
- package/src/llm/providers/openrouter.ts +0 -338
- package/src/llm/router.ts +0 -1104
- package/src/llm/types.ts +0 -232
- package/src/lsp/client.ts +0 -298
- package/src/lsp/languages.ts +0 -119
- package/src/lsp/manager.ts +0 -294
- package/src/mcp/client.ts +0 -402
- package/src/mcp/index.ts +0 -5
- package/src/mcp/manager.ts +0 -133
- package/src/nimbus.ts +0 -233
- package/src/plugins/index.ts +0 -27
- package/src/plugins/loader.ts +0 -334
- package/src/plugins/manager.ts +0 -376
- package/src/plugins/types.ts +0 -284
- package/src/scanners/cicd-scanner.ts +0 -258
- package/src/scanners/cloud-scanner.ts +0 -466
- package/src/scanners/framework-scanner.ts +0 -469
- package/src/scanners/iac-scanner.ts +0 -388
- package/src/scanners/index.ts +0 -539
- package/src/scanners/language-scanner.ts +0 -276
- package/src/scanners/package-manager-scanner.ts +0 -277
- package/src/scanners/types.ts +0 -172
- package/src/sessions/manager.ts +0 -472
- package/src/sessions/types.ts +0 -44
- package/src/sharing/sync.ts +0 -300
- package/src/sharing/viewer.ts +0 -163
- package/src/snapshots/index.ts +0 -2
- package/src/snapshots/manager.ts +0 -530
- package/src/state/artifacts.ts +0 -147
- package/src/state/audit.ts +0 -137
- package/src/state/billing.ts +0 -240
- package/src/state/checkpoints.ts +0 -117
- package/src/state/config.ts +0 -67
- package/src/state/conversations.ts +0 -14
- package/src/state/credentials.ts +0 -154
- package/src/state/db.ts +0 -58
- package/src/state/index.ts +0 -26
- package/src/state/messages.ts +0 -115
- package/src/state/projects.ts +0 -123
- package/src/state/schema.ts +0 -236
- package/src/state/sessions.ts +0 -147
- package/src/state/teams.ts +0 -200
- package/src/telemetry.ts +0 -108
- package/src/tools/aws-ops.ts +0 -952
- package/src/tools/azure-ops.ts +0 -579
- package/src/tools/file-ops.ts +0 -615
- package/src/tools/gcp-ops.ts +0 -625
- package/src/tools/git-ops.ts +0 -773
- package/src/tools/github-ops.ts +0 -799
- package/src/tools/helm-ops.ts +0 -943
- package/src/tools/index.ts +0 -17
- package/src/tools/k8s-ops.ts +0 -819
- package/src/tools/schemas/converter.ts +0 -184
- package/src/tools/schemas/devops.ts +0 -3502
- package/src/tools/schemas/index.ts +0 -73
- package/src/tools/schemas/standard.ts +0 -1148
- package/src/tools/schemas/types.ts +0 -735
- package/src/tools/spawn-exec.ts +0 -148
- package/src/tools/terraform-ops.ts +0 -862
- package/src/types/ambient.d.ts +0 -193
- package/src/types/config.ts +0 -83
- package/src/types/drift.ts +0 -116
- package/src/types/enterprise.ts +0 -335
- package/src/types/index.ts +0 -20
- package/src/types/plan.ts +0 -44
- package/src/types/request.ts +0 -65
- package/src/types/response.ts +0 -54
- package/src/types/service.ts +0 -51
- package/src/ui/App.tsx +0 -2114
- package/src/ui/DeployPreview.tsx +0 -174
- package/src/ui/FileDiffModal.tsx +0 -162
- package/src/ui/Header.tsx +0 -131
- package/src/ui/HelpModal.tsx +0 -57
- package/src/ui/InputBox.tsx +0 -503
- package/src/ui/MessageList.tsx +0 -1032
- package/src/ui/PermissionPrompt.tsx +0 -163
- package/src/ui/StatusBar.tsx +0 -277
- package/src/ui/TerminalPane.tsx +0 -84
- package/src/ui/ToolCallDisplay.tsx +0 -643
- package/src/ui/TreePane.tsx +0 -132
- package/src/ui/chat-ui.ts +0 -850
- package/src/ui/index.ts +0 -33
- package/src/ui/ink/index.ts +0 -1444
- package/src/ui/streaming.ts +0 -176
- package/src/ui/theme.ts +0 -104
- package/src/ui/types.ts +0 -75
- package/src/utils/analytics.ts +0 -72
- package/src/utils/cost-warning.ts +0 -27
- package/src/utils/env.ts +0 -46
- package/src/utils/errors.ts +0 -69
- package/src/utils/event-bus.ts +0 -38
- package/src/utils/index.ts +0 -24
- package/src/utils/logger.ts +0 -171
- package/src/utils/rate-limiter.ts +0 -121
- package/src/utils/service-auth.ts +0 -49
- package/src/utils/validation.ts +0 -53
- package/src/version.ts +0 -4
- package/src/watcher/index.ts +0 -214
- package/src/wizard/approval.ts +0 -383
- package/src/wizard/index.ts +0 -25
- package/src/wizard/prompts.ts +0 -338
- package/src/wizard/types.ts +0 -172
- package/src/wizard/ui.ts +0 -556
- package/src/wizard/wizard.ts +0 -304
- package/tsconfig.json +0 -24
package/src/cli/run.ts
DELETED
|
@@ -1,628 +0,0 @@
|
|
|
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
|
-
|
|
15
|
-
import { runAgentLoop } from '../agent/loop';
|
|
16
|
-
import { createPermissionState, checkPermission } from '../agent/permissions';
|
|
17
|
-
import { defaultToolRegistry } from '../tools/schemas/types';
|
|
18
|
-
import { standardTools } from '../tools/schemas/standard';
|
|
19
|
-
import { devopsTools } from '../tools/schemas/devops';
|
|
20
|
-
import type { AgentMode } from '../agent/system-prompt';
|
|
21
|
-
import type { LLMRouter } from '../llm/router';
|
|
22
|
-
import { expandFileReferences } from '../agent/expand-files';
|
|
23
|
-
|
|
24
|
-
/** JSON output schema for `nimbus run --format json` */
|
|
25
|
-
export interface RunJsonOutput {
|
|
26
|
-
success: boolean; // whether the agent completed without error
|
|
27
|
-
output: string; // final text response from the agent
|
|
28
|
-
cost: number; // total cost in USD
|
|
29
|
-
turns: number; // number of LLM turns taken
|
|
30
|
-
toolCalls: Array<{ // all tool calls made during the run
|
|
31
|
-
name: string;
|
|
32
|
-
success: boolean;
|
|
33
|
-
durationMs: number;
|
|
34
|
-
}>;
|
|
35
|
-
errors: string[]; // any error messages encountered
|
|
36
|
-
/** L2: Terraform plan summary extracted from plan output (CI-friendly) */
|
|
37
|
-
planSummary?: {
|
|
38
|
-
toAdd: number;
|
|
39
|
-
toChange: number;
|
|
40
|
-
toDestroy: number;
|
|
41
|
-
raw: string;
|
|
42
|
-
};
|
|
43
|
-
/** M3: Summary of DevOps tools invoked during the run */
|
|
44
|
-
devops_summary?: {
|
|
45
|
-
tools_used: string[];
|
|
46
|
-
tool_call_count: number;
|
|
47
|
-
devops_tool_count: number;
|
|
48
|
-
};
|
|
49
|
-
/** M1: Infrastructure context active during the run */
|
|
50
|
-
infraContext?: {
|
|
51
|
-
terraformWorkspace?: string;
|
|
52
|
-
kubectlContext?: string;
|
|
53
|
-
awsAccount?: string;
|
|
54
|
-
};
|
|
55
|
-
/** M1: Unique DevOps tool names invoked */
|
|
56
|
-
toolsUsed?: string[];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Options parsed from command-line arguments */
|
|
60
|
-
export interface RunOptions {
|
|
61
|
-
/** The prompt to execute */
|
|
62
|
-
prompt: string;
|
|
63
|
-
/** Output format */
|
|
64
|
-
format: 'text' | 'json' | 'table';
|
|
65
|
-
/** Skip permission prompts — auto-approve everything (--non-interactive is an alias) */
|
|
66
|
-
autoApprove: boolean;
|
|
67
|
-
/** Read prompt from stdin */
|
|
68
|
-
stdin: boolean;
|
|
69
|
-
/** Parse stdin as JSON config object { prompt, mode, model, autoApprove, maxTurns } */
|
|
70
|
-
stdinJson: boolean;
|
|
71
|
-
/** Model override */
|
|
72
|
-
model?: string;
|
|
73
|
-
/** Agent mode override */
|
|
74
|
-
mode: AgentMode;
|
|
75
|
-
/** Maximum turns */
|
|
76
|
-
maxTurns: number;
|
|
77
|
-
/** G13: Abort agent loop after this many milliseconds */
|
|
78
|
-
timeout?: number;
|
|
79
|
-
/** G15: Print last tool output as JSON instead of prose */
|
|
80
|
-
rawToolOutput?: boolean;
|
|
81
|
-
/** G23: Print the JSON output schema and exit */
|
|
82
|
-
schema?: boolean;
|
|
83
|
-
/** M1: Dry-run mode — forces plan mode and instructs agent not to mutate anything */
|
|
84
|
-
dryRun?: boolean;
|
|
85
|
-
/** H3: Exit with code 1 on agent failure */
|
|
86
|
-
exitOnError?: boolean;
|
|
87
|
-
/** H3: kubectl context to inject before running */
|
|
88
|
-
context?: string;
|
|
89
|
-
/** H3: Terraform workspace to inject before running */
|
|
90
|
-
workspace?: string;
|
|
91
|
-
/** H3: Kubernetes namespace to inject before running */
|
|
92
|
-
namespace?: string;
|
|
93
|
-
/** H3: Webhook URL to POST result to after completion */
|
|
94
|
-
notify?: string;
|
|
95
|
-
/** H3: Slack webhook URL to POST formatted result to */
|
|
96
|
-
notifySlack?: string;
|
|
97
|
-
/** G16: Maximum cost in USD per session */
|
|
98
|
-
budget?: number;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Result of a non-interactive run */
|
|
102
|
-
export interface RunResult {
|
|
103
|
-
/** Whether the run completed successfully */
|
|
104
|
-
success: boolean;
|
|
105
|
-
/** The final output text */
|
|
106
|
-
output: string;
|
|
107
|
-
/** Number of turns taken */
|
|
108
|
-
turns: number;
|
|
109
|
-
/** Token usage */
|
|
110
|
-
usage: {
|
|
111
|
-
promptTokens: number;
|
|
112
|
-
completionTokens: number;
|
|
113
|
-
totalTokens: number;
|
|
114
|
-
};
|
|
115
|
-
/** Cost in USD */
|
|
116
|
-
cost: number;
|
|
117
|
-
/** Whether the run was interrupted */
|
|
118
|
-
interrupted: boolean;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Parse `nimbus run` CLI arguments.
|
|
123
|
-
*/
|
|
124
|
-
export function parseRunArgs(args: string[]): RunOptions {
|
|
125
|
-
let prompt = '';
|
|
126
|
-
let format: 'text' | 'json' | 'table' = 'text';
|
|
127
|
-
let autoApprove = false;
|
|
128
|
-
let stdin = false;
|
|
129
|
-
let stdinJson = false;
|
|
130
|
-
let model: string | undefined;
|
|
131
|
-
let mode: AgentMode = 'build';
|
|
132
|
-
let maxTurns = 50;
|
|
133
|
-
let timeout: number | undefined;
|
|
134
|
-
let rawToolOutput = false;
|
|
135
|
-
let schema = false;
|
|
136
|
-
let dryRun = false;
|
|
137
|
-
let exitOnError = true; // C5: default true for CI/CD POSIX convention
|
|
138
|
-
let context: string | undefined;
|
|
139
|
-
let workspace: string | undefined;
|
|
140
|
-
let namespace: string | undefined;
|
|
141
|
-
let notify: string | undefined;
|
|
142
|
-
let notifySlack: string | undefined;
|
|
143
|
-
let budget: number | undefined;
|
|
144
|
-
|
|
145
|
-
const positional: string[] = [];
|
|
146
|
-
|
|
147
|
-
for (let i = 0; i < args.length; i++) {
|
|
148
|
-
const arg = args[i];
|
|
149
|
-
|
|
150
|
-
switch (arg) {
|
|
151
|
-
case '--format':
|
|
152
|
-
format = (args[++i] ?? 'text') as 'text' | 'json' | 'table';
|
|
153
|
-
break;
|
|
154
|
-
case '--json':
|
|
155
|
-
format = 'json';
|
|
156
|
-
break;
|
|
157
|
-
case '--auto-approve':
|
|
158
|
-
case '-y':
|
|
159
|
-
case '--non-interactive':
|
|
160
|
-
autoApprove = true;
|
|
161
|
-
break;
|
|
162
|
-
case '--stdin':
|
|
163
|
-
stdin = true;
|
|
164
|
-
break;
|
|
165
|
-
case '--stdin-json':
|
|
166
|
-
stdinJson = true;
|
|
167
|
-
stdin = true; // also read stdin
|
|
168
|
-
break;
|
|
169
|
-
case '--model':
|
|
170
|
-
model = args[++i];
|
|
171
|
-
break;
|
|
172
|
-
case '--mode':
|
|
173
|
-
mode = (args[++i] ?? 'build') as AgentMode;
|
|
174
|
-
break;
|
|
175
|
-
case '--max-turns':
|
|
176
|
-
maxTurns = parseInt(args[++i] ?? '50', 10);
|
|
177
|
-
break;
|
|
178
|
-
case '--timeout':
|
|
179
|
-
// G13: timeout in seconds, converted to ms
|
|
180
|
-
timeout = parseInt(args[++i] ?? '0', 10) * 1000;
|
|
181
|
-
break;
|
|
182
|
-
case '--raw-tool-output':
|
|
183
|
-
// G15: print last tool output as JSON
|
|
184
|
-
rawToolOutput = true;
|
|
185
|
-
break;
|
|
186
|
-
case '--schema':
|
|
187
|
-
// G23: print the JSON output schema and exit
|
|
188
|
-
schema = true;
|
|
189
|
-
break;
|
|
190
|
-
case '--json-schema':
|
|
191
|
-
// GAP-16: print the stable JSON output schema and exit
|
|
192
|
-
schema = true;
|
|
193
|
-
break;
|
|
194
|
-
case '--dry-run':
|
|
195
|
-
// M1: dry-run forces plan mode — no mutations allowed
|
|
196
|
-
dryRun = true;
|
|
197
|
-
mode = 'plan';
|
|
198
|
-
break;
|
|
199
|
-
case '--exit-code-on-error':
|
|
200
|
-
// H3: exit with code 1 on failure
|
|
201
|
-
exitOnError = true;
|
|
202
|
-
break;
|
|
203
|
-
case '--no-exit-on-error':
|
|
204
|
-
// C5: legacy scripts can opt out of exit-on-error
|
|
205
|
-
exitOnError = false;
|
|
206
|
-
break;
|
|
207
|
-
case '--context':
|
|
208
|
-
// H3: kubectl context
|
|
209
|
-
context = args[++i];
|
|
210
|
-
break;
|
|
211
|
-
case '--workspace':
|
|
212
|
-
// H3: terraform workspace
|
|
213
|
-
workspace = args[++i];
|
|
214
|
-
break;
|
|
215
|
-
case '--namespace':
|
|
216
|
-
case '-n':
|
|
217
|
-
// H3: kubernetes namespace
|
|
218
|
-
namespace = args[++i];
|
|
219
|
-
break;
|
|
220
|
-
case '--notify':
|
|
221
|
-
// H3: webhook URL
|
|
222
|
-
notify = args[++i];
|
|
223
|
-
break;
|
|
224
|
-
case '--notify-slack':
|
|
225
|
-
// H3: slack webhook
|
|
226
|
-
notifySlack = args[++i];
|
|
227
|
-
break;
|
|
228
|
-
case '--budget':
|
|
229
|
-
// G16: cost budget in USD
|
|
230
|
-
budget = parseFloat(args[++i] ?? '0');
|
|
231
|
-
break;
|
|
232
|
-
default:
|
|
233
|
-
if (!arg.startsWith('-')) {
|
|
234
|
-
positional.push(arg);
|
|
235
|
-
}
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
prompt = positional.join(' ');
|
|
241
|
-
|
|
242
|
-
return { prompt, format, autoApprove, stdin, stdinJson, model, mode, maxTurns, timeout, rawToolOutput, schema, dryRun, exitOnError, context, workspace, namespace, notify, notifySlack, budget };
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Execute a non-interactive run.
|
|
247
|
-
*/
|
|
248
|
-
export async function executeRun(router: LLMRouter, options: RunOptions): Promise<RunResult> {
|
|
249
|
-
// G23 / GAP-16: --schema / --json-schema flag: print the JSON output schema and exit immediately
|
|
250
|
-
if (options.schema) {
|
|
251
|
-
const schema = {
|
|
252
|
-
type: 'object',
|
|
253
|
-
properties: {
|
|
254
|
-
success: { type: 'boolean', description: 'Whether the agent completed without error' },
|
|
255
|
-
output: { type: 'string', description: 'Final text response from the agent' },
|
|
256
|
-
cost: { type: 'number', description: 'Total cost in USD' },
|
|
257
|
-
turns: { type: 'number', description: 'Number of LLM turns taken' },
|
|
258
|
-
toolCalls: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, success: { type: 'boolean' }, durationMs: { type: 'number' } } } },
|
|
259
|
-
errors: { type: 'array', items: { type: 'string' }, description: 'Any error messages encountered' },
|
|
260
|
-
},
|
|
261
|
-
required: ['success', 'output', 'cost', 'turns', 'toolCalls', 'errors'],
|
|
262
|
-
};
|
|
263
|
-
process.stdout.write(JSON.stringify(schema, null, 2) + '\n');
|
|
264
|
-
return {
|
|
265
|
-
success: true,
|
|
266
|
-
output: '',
|
|
267
|
-
turns: 0,
|
|
268
|
-
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
269
|
-
cost: 0,
|
|
270
|
-
interrupted: false,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// H3: Inject context/workspace/namespace into environment before agent loop
|
|
275
|
-
if (options.context) process.env.KUBECTL_CONTEXT = options.context;
|
|
276
|
-
if (options.workspace) process.env.TF_WORKSPACE = options.workspace;
|
|
277
|
-
if (options.namespace) process.env.K8S_NAMESPACE = options.namespace;
|
|
278
|
-
|
|
279
|
-
// Get prompt from stdin if requested
|
|
280
|
-
let prompt = options.prompt;
|
|
281
|
-
if (options.stdin && !prompt) {
|
|
282
|
-
const stdinContent = await readStdin();
|
|
283
|
-
|
|
284
|
-
// L5: --stdin-json support: parse stdin as { prompt, mode, model, autoApprove, maxTurns }
|
|
285
|
-
if (options.stdinJson && stdinContent) {
|
|
286
|
-
try {
|
|
287
|
-
const config = JSON.parse(stdinContent) as Record<string, unknown>;
|
|
288
|
-
if (typeof config.prompt === 'string') prompt = config.prompt;
|
|
289
|
-
if (config.mode === 'plan' || config.mode === 'build' || config.mode === 'deploy') {
|
|
290
|
-
options = { ...options, mode: config.mode };
|
|
291
|
-
}
|
|
292
|
-
if (typeof config.model === 'string') options = { ...options, model: config.model };
|
|
293
|
-
if (typeof config.autoApprove === 'boolean') options = { ...options, autoApprove: config.autoApprove };
|
|
294
|
-
if (typeof config.maxTurns === 'number') options = { ...options, maxTurns: config.maxTurns };
|
|
295
|
-
} catch {
|
|
296
|
-
// If JSON parse fails, treat stdin as raw prompt text
|
|
297
|
-
prompt = stdinContent;
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
prompt = stdinContent;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Expand @file references in the prompt
|
|
305
|
-
prompt = expandFileReferences(prompt, process.cwd());
|
|
306
|
-
|
|
307
|
-
if (!prompt) {
|
|
308
|
-
return {
|
|
309
|
-
success: false,
|
|
310
|
-
output: 'Error: No prompt provided. Usage: nimbus run "your prompt"',
|
|
311
|
-
turns: 0,
|
|
312
|
-
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
313
|
-
cost: 0,
|
|
314
|
-
interrupted: false,
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Set up tool registry
|
|
319
|
-
const registry = defaultToolRegistry;
|
|
320
|
-
if (registry.size === 0) {
|
|
321
|
-
// Register all built-in tools
|
|
322
|
-
for (const tool of [...standardTools, ...devopsTools]) {
|
|
323
|
-
try {
|
|
324
|
-
registry.register(tool);
|
|
325
|
-
} catch {
|
|
326
|
-
/* skip duplicates */
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Set up permission state
|
|
332
|
-
const permissionState = createPermissionState();
|
|
333
|
-
|
|
334
|
-
// Collect output
|
|
335
|
-
const outputParts: string[] = [];
|
|
336
|
-
const tableRows: Array<{ tool: string; status: string; output: string }> = [];
|
|
337
|
-
|
|
338
|
-
// G13: Set up timeout AbortController if --timeout was specified
|
|
339
|
-
let timeoutAbortController: AbortController | undefined;
|
|
340
|
-
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
341
|
-
if (options.timeout && options.timeout > 0) {
|
|
342
|
-
timeoutAbortController = new AbortController();
|
|
343
|
-
timeoutHandle = setTimeout(() => {
|
|
344
|
-
timeoutAbortController!.abort();
|
|
345
|
-
if (options.format === 'text') {
|
|
346
|
-
process.stderr.write(`\n[Timeout: agent loop aborted after ${options.timeout! / 1000}s]\n`);
|
|
347
|
-
}
|
|
348
|
-
}, options.timeout);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// G15: Track last tool output for --raw-tool-output
|
|
352
|
-
let lastToolName = '';
|
|
353
|
-
let lastToolOutput = '';
|
|
354
|
-
|
|
355
|
-
// G23: Track all tool calls for structured JSON output
|
|
356
|
-
const allToolCalls: Array<{ name: string; input: Record<string, unknown>; output: string; isError: boolean }> = [];
|
|
357
|
-
|
|
358
|
-
// H1: Discover infra context for CI/CD pipelines (best-effort, non-blocking)
|
|
359
|
-
let infraContext: import('../cli/init').InfraContext | undefined;
|
|
360
|
-
try {
|
|
361
|
-
const { discoverInfraContext } = await import('../cli/init');
|
|
362
|
-
infraContext = await discoverInfraContext(process.cwd());
|
|
363
|
-
} catch { /* non-critical */ }
|
|
364
|
-
|
|
365
|
-
// Run the agent loop
|
|
366
|
-
const result = await runAgentLoop(prompt, [], {
|
|
367
|
-
router,
|
|
368
|
-
toolRegistry: registry,
|
|
369
|
-
mode: options.mode,
|
|
370
|
-
maxTurns: options.maxTurns,
|
|
371
|
-
model: options.model,
|
|
372
|
-
cwd: process.cwd(),
|
|
373
|
-
signal: timeoutAbortController?.signal,
|
|
374
|
-
dryRun: options.dryRun,
|
|
375
|
-
costBudgetUSD: options.budget,
|
|
376
|
-
infraContext,
|
|
377
|
-
|
|
378
|
-
onText: text => {
|
|
379
|
-
outputParts.push(text);
|
|
380
|
-
if (options.format === 'text') {
|
|
381
|
-
process.stdout.write(text);
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
|
|
385
|
-
onToolCallStart: toolCall => {
|
|
386
|
-
if (options.format === 'text') {
|
|
387
|
-
process.stderr.write(`\n[Tool: ${toolCall.name}]\n`);
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
|
|
391
|
-
onToolCallEnd: (toolCall, result) => {
|
|
392
|
-
if (options.format === 'text' && result.isError) {
|
|
393
|
-
process.stderr.write(`[Error: ${result.error}]\n`);
|
|
394
|
-
}
|
|
395
|
-
if (options.format === 'table') {
|
|
396
|
-
tableRows.push({
|
|
397
|
-
tool: toolCall.name,
|
|
398
|
-
status: result.isError ? 'error' : 'ok',
|
|
399
|
-
output: (result.output ?? result.error ?? '').slice(0, 80),
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
// G15: track last tool output
|
|
403
|
-
lastToolName = toolCall.name;
|
|
404
|
-
lastToolOutput = result.isError ? (result.error ?? '') : (result.output ?? '');
|
|
405
|
-
// G23: accumulate all tool calls for structured JSON output
|
|
406
|
-
allToolCalls.push({
|
|
407
|
-
name: toolCall.name,
|
|
408
|
-
input: toolCall.input && typeof toolCall.input === 'object'
|
|
409
|
-
? (toolCall.input as Record<string, unknown>)
|
|
410
|
-
: {},
|
|
411
|
-
output: result.isError ? (result.error ?? '') : (result.output ?? ''),
|
|
412
|
-
isError: result.isError ?? false,
|
|
413
|
-
});
|
|
414
|
-
},
|
|
415
|
-
|
|
416
|
-
checkPermission: async (tool, input) => {
|
|
417
|
-
if (options.autoApprove) {
|
|
418
|
-
return 'allow';
|
|
419
|
-
}
|
|
420
|
-
const decision = checkPermission(tool, input, permissionState);
|
|
421
|
-
if (decision === 'ask') {
|
|
422
|
-
// In non-interactive mode without --auto-approve, deny by default
|
|
423
|
-
return 'deny';
|
|
424
|
-
}
|
|
425
|
-
return decision;
|
|
426
|
-
},
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// G13: Clear the timeout timer if we finished before it fired
|
|
430
|
-
if (timeoutHandle) {
|
|
431
|
-
clearTimeout(timeoutHandle);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const output = outputParts.join('');
|
|
435
|
-
|
|
436
|
-
// G15: --raw-tool-output: print last tool call as JSON to stdout
|
|
437
|
-
if (options.rawToolOutput && lastToolName) {
|
|
438
|
-
console.log(JSON.stringify({ tool: lastToolName, output: lastToolOutput }));
|
|
439
|
-
return {
|
|
440
|
-
success: !result.interrupted,
|
|
441
|
-
output,
|
|
442
|
-
turns: result.turns,
|
|
443
|
-
usage: result.usage,
|
|
444
|
-
cost: result.totalCost,
|
|
445
|
-
interrupted: result.interrupted,
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Format output
|
|
450
|
-
if (options.format === 'json') {
|
|
451
|
-
// GAP-16 / G23: structured JSON output matching the stable RunJsonOutput schema
|
|
452
|
-
// L2: Extract terraform plan summary from tool outputs for CI-friendly output
|
|
453
|
-
let planSummary: RunJsonOutput['planSummary'];
|
|
454
|
-
const tfPlanCall = allToolCalls.find(tc => tc.name === 'terraform' && tc.output && /Plan:/.test(tc.output));
|
|
455
|
-
if (tfPlanCall?.output) {
|
|
456
|
-
const planLine = tfPlanCall.output.match(/Plan:\s*(\d+)\s*to add,\s*(\d+)\s*to change,\s*(\d+)\s*to destroy/i);
|
|
457
|
-
if (planLine) {
|
|
458
|
-
planSummary = {
|
|
459
|
-
toAdd: parseInt(planLine[1]),
|
|
460
|
-
toChange: parseInt(planLine[2]),
|
|
461
|
-
toDestroy: parseInt(planLine[3]),
|
|
462
|
-
raw: planLine[0],
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
const jsonResult: RunJsonOutput = {
|
|
467
|
-
success: !result.interrupted,
|
|
468
|
-
output,
|
|
469
|
-
cost: result.totalCost ?? 0,
|
|
470
|
-
turns: result.turns ?? 0,
|
|
471
|
-
toolCalls: allToolCalls.map(tc => ({
|
|
472
|
-
name: tc.name,
|
|
473
|
-
success: !tc.isError,
|
|
474
|
-
durationMs: 0,
|
|
475
|
-
})),
|
|
476
|
-
errors: allToolCalls.filter(tc => tc.isError).map(tc => tc.output).filter(Boolean),
|
|
477
|
-
...(planSummary ? { planSummary } : {}),
|
|
478
|
-
};
|
|
479
|
-
// M3: Build devops_summary from tool calls
|
|
480
|
-
const DEVOPS_TOOL_NAMES = new Set([
|
|
481
|
-
'terraform', 'kubectl', 'helm', 'aws', 'gcloud', 'az',
|
|
482
|
-
'docker', 'secrets', 'cicd', 'monitor', 'gitops', 'cloud_action',
|
|
483
|
-
'logs', 'certs', 'mesh', 'cfn', 'k8s_rbac', 'generate_infra',
|
|
484
|
-
'kubectl_context', 'helm_values', 'cost_estimate', 'cloud_discover',
|
|
485
|
-
]);
|
|
486
|
-
const toolsUsed = [...new Set(allToolCalls.map(tc => tc.name))];
|
|
487
|
-
const devopsToolsUsed = toolsUsed.filter(t => DEVOPS_TOOL_NAMES.has(t));
|
|
488
|
-
if (devopsToolsUsed.length > 0) {
|
|
489
|
-
jsonResult.devops_summary = {
|
|
490
|
-
tools_used: devopsToolsUsed,
|
|
491
|
-
tool_call_count: allToolCalls.length,
|
|
492
|
-
devops_tool_count: allToolCalls.filter(tc => DEVOPS_TOOL_NAMES.has(tc.name)).length,
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
// M1: Add infraContext and toolsUsed fields
|
|
496
|
-
jsonResult.toolsUsed = toolsUsed;
|
|
497
|
-
if (infraContext) {
|
|
498
|
-
jsonResult.infraContext = {
|
|
499
|
-
terraformWorkspace: infraContext.terraformWorkspace,
|
|
500
|
-
kubectlContext: infraContext.kubectlContext,
|
|
501
|
-
awsAccount: infraContext.awsAccount,
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
console.log(JSON.stringify(jsonResult, null, 2));
|
|
505
|
-
} else if (options.format === 'text') {
|
|
506
|
-
// Text was already streamed above
|
|
507
|
-
console.log(''); // Final newline
|
|
508
|
-
} else if (options.format === 'table') {
|
|
509
|
-
// ASCII table of tool calls
|
|
510
|
-
const COL_TOOL = 30;
|
|
511
|
-
const COL_STATUS = 6;
|
|
512
|
-
const COL_OUTPUT = 80;
|
|
513
|
-
const divider = `${'-'.repeat(COL_TOOL + 2)}+${'-'.repeat(COL_STATUS + 2)}+${'-'.repeat(COL_OUTPUT + 2)}`;
|
|
514
|
-
const pad = (s: string, n: number) => s.slice(0, n).padEnd(n);
|
|
515
|
-
console.log(divider);
|
|
516
|
-
console.log(`| ${pad('Tool', COL_TOOL)} | ${pad('Status', COL_STATUS)} | ${pad('Output', COL_OUTPUT)} |`);
|
|
517
|
-
console.log(divider);
|
|
518
|
-
for (const row of tableRows) {
|
|
519
|
-
console.log(`| ${pad(row.tool, COL_TOOL)} | ${pad(row.status, COL_STATUS)} | ${pad(row.output, COL_OUTPUT)} |`);
|
|
520
|
-
}
|
|
521
|
-
console.log(divider);
|
|
522
|
-
console.log('');
|
|
523
|
-
// Also print final text output
|
|
524
|
-
if (output) process.stdout.write(output + '\n');
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const runResult: RunResult = {
|
|
528
|
-
success: !result.interrupted,
|
|
529
|
-
output,
|
|
530
|
-
turns: result.turns,
|
|
531
|
-
usage: result.usage,
|
|
532
|
-
cost: result.totalCost,
|
|
533
|
-
interrupted: result.interrupted,
|
|
534
|
-
};
|
|
535
|
-
|
|
536
|
-
// H3: Fire webhook notifications after run completes
|
|
537
|
-
const duration = Date.now(); // approximate; real duration would need a start time
|
|
538
|
-
const notifyPayload = {
|
|
539
|
-
success: runResult.success,
|
|
540
|
-
output: runResult.output.slice(0, 2000), // truncate for webhook
|
|
541
|
-
cost: runResult.cost,
|
|
542
|
-
duration,
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
if (options.notify) {
|
|
546
|
-
try {
|
|
547
|
-
await fetch(options.notify, {
|
|
548
|
-
method: 'POST',
|
|
549
|
-
headers: { 'Content-Type': 'application/json' },
|
|
550
|
-
body: JSON.stringify(notifyPayload),
|
|
551
|
-
signal: AbortSignal.timeout(10_000),
|
|
552
|
-
});
|
|
553
|
-
} catch {
|
|
554
|
-
// Webhook failure is non-fatal
|
|
555
|
-
process.stderr.write(`[Warning: notification webhook failed]\n`);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
if (options.notifySlack) {
|
|
560
|
-
try {
|
|
561
|
-
const slackPayload = {
|
|
562
|
-
text: runResult.success
|
|
563
|
-
? `:white_check_mark: *Nimbus run succeeded*\n${runResult.output.slice(0, 500)}`
|
|
564
|
-
: `:x: *Nimbus run failed*\n${runResult.output.slice(0, 500)}`,
|
|
565
|
-
attachments: [
|
|
566
|
-
{
|
|
567
|
-
fields: [
|
|
568
|
-
{ title: 'Cost', value: `$${runResult.cost.toFixed(4)}`, short: true },
|
|
569
|
-
{ title: 'Turns', value: String(runResult.turns), short: true },
|
|
570
|
-
],
|
|
571
|
-
},
|
|
572
|
-
],
|
|
573
|
-
};
|
|
574
|
-
await fetch(options.notifySlack, {
|
|
575
|
-
method: 'POST',
|
|
576
|
-
headers: { 'Content-Type': 'application/json' },
|
|
577
|
-
body: JSON.stringify(slackPayload),
|
|
578
|
-
signal: AbortSignal.timeout(10_000),
|
|
579
|
-
});
|
|
580
|
-
} catch {
|
|
581
|
-
process.stderr.write(`[Warning: Slack notification failed]\n`);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// H3: Exit with code 1 if the run failed and --exit-code-on-error is set
|
|
586
|
-
if (options.exitOnError && !runResult.success) {
|
|
587
|
-
process.exit(1);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return runResult;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Read all input from stdin.
|
|
595
|
-
*/
|
|
596
|
-
async function readStdin(): Promise<string> {
|
|
597
|
-
// If stdin is a TTY (no pipe), resolve immediately with empty string
|
|
598
|
-
if (process.stdin.isTTY) {
|
|
599
|
-
return '';
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
const chunks: Buffer[] = [];
|
|
603
|
-
|
|
604
|
-
return new Promise((resolve, reject) => {
|
|
605
|
-
// 30-second timeout for stdin reads to prevent hanging
|
|
606
|
-
const timeout = setTimeout(() => {
|
|
607
|
-
process.stdin.removeAllListeners();
|
|
608
|
-
resolve(Buffer.concat(chunks).toString('utf-8').trim());
|
|
609
|
-
}, 30_000);
|
|
610
|
-
|
|
611
|
-
process.stdin.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
|
612
|
-
|
|
613
|
-
process.stdin.on('end', () => {
|
|
614
|
-
clearTimeout(timeout);
|
|
615
|
-
resolve(Buffer.concat(chunks).toString('utf-8').trim());
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
process.stdin.on('error', err => {
|
|
619
|
-
clearTimeout(timeout);
|
|
620
|
-
// On error, use whatever we've collected so far
|
|
621
|
-
if (chunks.length > 0) {
|
|
622
|
-
resolve(Buffer.concat(chunks).toString('utf-8').trim());
|
|
623
|
-
} else {
|
|
624
|
-
reject(err);
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
}
|
package/src/cli/serve-auth.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
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
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Public Types
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
|
|
19
|
-
/** Credentials for HTTP Basic Auth. */
|
|
20
|
-
export interface ServeAuthOptions {
|
|
21
|
-
/** Username for Basic Auth. */
|
|
22
|
-
readonly username: string;
|
|
23
|
-
/** Password for Basic Auth. */
|
|
24
|
-
readonly password: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Paths that are always public (no auth required)
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
const PUBLIC_PATHS = new Set(['/api/health', '/api/openapi.json']);
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Middleware Factory
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Create an Elysia-compatible `onBeforeHandle` function that enforces
|
|
39
|
-
* HTTP Basic Authentication on protected endpoints.
|
|
40
|
-
*
|
|
41
|
-
* @param options - The username and password to validate against.
|
|
42
|
-
* @returns A handler that short-circuits with 401 when credentials are
|
|
43
|
-
* missing or invalid, or `undefined` to let the request through.
|
|
44
|
-
*/
|
|
45
|
-
export function createAuthMiddleware(
|
|
46
|
-
options: ServeAuthOptions
|
|
47
|
-
): (ctx: { request: Request; set: any }) => { error: string } | undefined {
|
|
48
|
-
const expectedToken = btoa(`${options.username}:${options.password}`);
|
|
49
|
-
|
|
50
|
-
return ({ request, set }: { request: Request; set: any }) => {
|
|
51
|
-
const url = new URL(request.url);
|
|
52
|
-
|
|
53
|
-
// Skip auth for public endpoints
|
|
54
|
-
if (PUBLIC_PATHS.has(url.pathname)) {
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Skip auth for CORS preflight requests
|
|
59
|
-
if (request.method === 'OPTIONS') {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const authHeader = request.headers.get('Authorization');
|
|
64
|
-
if (!authHeader) {
|
|
65
|
-
set.status = 401;
|
|
66
|
-
set.headers = { 'WWW-Authenticate': 'Basic realm="Nimbus API"' };
|
|
67
|
-
return { error: 'Authentication required' };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const [scheme, token] = authHeader.split(' ');
|
|
71
|
-
if (scheme !== 'Basic' || token !== expectedToken) {
|
|
72
|
-
set.status = 401;
|
|
73
|
-
set.headers = { 'WWW-Authenticate': 'Basic realm="Nimbus API"' };
|
|
74
|
-
return { error: 'Invalid credentials' };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Credentials valid -- proceed
|
|
78
|
-
return undefined;
|
|
79
|
-
};
|
|
80
|
-
}
|