@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
|
@@ -94,21 +94,27 @@ export function formatKubectlPodsOutput(raw) {
|
|
|
94
94
|
export function formatHelmListOutput(raw) {
|
|
95
95
|
try {
|
|
96
96
|
const releases = JSON.parse(raw);
|
|
97
|
-
if (!Array.isArray(releases) || releases.length === 0)
|
|
97
|
+
if (!Array.isArray(releases) || releases.length === 0) {
|
|
98
98
|
return 'No Helm releases found.';
|
|
99
|
+
}
|
|
99
100
|
const lines = releases.map(r => {
|
|
100
101
|
let emoji;
|
|
101
102
|
const s = r.status?.toLowerCase() ?? '';
|
|
102
|
-
if (s === 'deployed')
|
|
103
|
+
if (s === 'deployed') {
|
|
103
104
|
emoji = '[OK]';
|
|
104
|
-
|
|
105
|
+
}
|
|
106
|
+
else if (s === 'pending-install' || s === 'pending-upgrade') {
|
|
105
107
|
emoji = '[!!]';
|
|
106
|
-
|
|
108
|
+
}
|
|
109
|
+
else if (s === 'failed') {
|
|
107
110
|
emoji = '[XX]';
|
|
108
|
-
|
|
111
|
+
}
|
|
112
|
+
else if (s === 'superseded') {
|
|
109
113
|
emoji = '[~~]';
|
|
110
|
-
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
111
116
|
emoji = ' ';
|
|
117
|
+
}
|
|
112
118
|
return `${emoji} ${r.name} (${r.namespace}) — ${r.chart} rev.${r.revision} [${r.status}]`;
|
|
113
119
|
});
|
|
114
120
|
return lines.join('\n');
|
|
@@ -198,51 +204,57 @@ export const terraformTool = {
|
|
|
198
204
|
// Build the terraform command
|
|
199
205
|
let command;
|
|
200
206
|
if (input.action === 'state-list') {
|
|
201
|
-
command = `terraform -chdir=${input.workdir} state list${input.args ?
|
|
207
|
+
command = `terraform -chdir=${input.workdir} state list${input.args ? ` ${input.args}` : ''}`;
|
|
202
208
|
}
|
|
203
209
|
else if (input.action === 'state-show') {
|
|
204
|
-
if (!input.state_address)
|
|
210
|
+
if (!input.state_address) {
|
|
205
211
|
return err('state-show requires state_address');
|
|
212
|
+
}
|
|
206
213
|
command = `terraform -chdir=${input.workdir} state show "${input.state_address}"`;
|
|
207
214
|
}
|
|
208
215
|
else if (input.action === 'state-rm') {
|
|
209
|
-
if (!input.state_address)
|
|
216
|
+
if (!input.state_address) {
|
|
210
217
|
return err('state-rm requires state_address');
|
|
218
|
+
}
|
|
211
219
|
command = `terraform -chdir=${input.workdir} state rm "${input.state_address}"`;
|
|
212
220
|
}
|
|
213
221
|
else if (input.action === 'state-mv') {
|
|
214
|
-
if (!input.state_address)
|
|
222
|
+
if (!input.state_address) {
|
|
215
223
|
return err('state-mv requires state_address (format: "source dest")');
|
|
224
|
+
}
|
|
216
225
|
command = `terraform -chdir=${input.workdir} state mv ${input.state_address}`;
|
|
217
226
|
}
|
|
218
227
|
else if (input.action === 'state') {
|
|
219
|
-
command = `terraform -chdir=${input.workdir} state${input.args ?
|
|
228
|
+
command = `terraform -chdir=${input.workdir} state${input.args ? ` ${input.args}` : ' list'}`;
|
|
220
229
|
}
|
|
221
230
|
else if (input.action === 'output') {
|
|
222
|
-
command = `terraform -chdir=${input.workdir} output -json${input.output_name ?
|
|
231
|
+
command = `terraform -chdir=${input.workdir} output -json${input.output_name ? ` ${input.output_name}` : ''}`;
|
|
223
232
|
}
|
|
224
233
|
else if (input.action === 'workspace-list') {
|
|
225
234
|
command = `terraform -chdir=${input.workdir} workspace list`;
|
|
226
235
|
}
|
|
227
236
|
else if (input.action === 'workspace-select') {
|
|
228
|
-
if (!input.workspace)
|
|
237
|
+
if (!input.workspace) {
|
|
229
238
|
return err('workspace-select requires workspace name');
|
|
239
|
+
}
|
|
230
240
|
command = `terraform -chdir=${input.workdir} workspace select "${input.workspace}"`;
|
|
231
241
|
}
|
|
232
242
|
else if (input.action === 'workspace-new') {
|
|
233
|
-
if (!input.workspace)
|
|
243
|
+
if (!input.workspace) {
|
|
234
244
|
return err('workspace-new requires workspace name');
|
|
245
|
+
}
|
|
235
246
|
command = `terraform -chdir=${input.workdir} workspace new "${input.workspace}"`;
|
|
236
247
|
}
|
|
237
248
|
else if (input.action === 'providers') {
|
|
238
249
|
command = `terraform -chdir=${input.workdir} providers`;
|
|
239
250
|
}
|
|
240
251
|
else if (input.action === 'graph') {
|
|
241
|
-
command = `terraform -chdir=${input.workdir} graph${input.args ?
|
|
252
|
+
command = `terraform -chdir=${input.workdir} graph${input.args ? ` ${input.args}` : ''}`;
|
|
242
253
|
}
|
|
243
254
|
else if (input.action === 'force-unlock') {
|
|
244
|
-
if (!input.lock_id)
|
|
255
|
+
if (!input.lock_id) {
|
|
245
256
|
return err('force-unlock requires lock_id');
|
|
257
|
+
}
|
|
246
258
|
command = `terraform -chdir=${input.workdir} force-unlock -force "${input.lock_id}"`;
|
|
247
259
|
}
|
|
248
260
|
else {
|
|
@@ -272,8 +284,9 @@ export const terraformTool = {
|
|
|
272
284
|
// Replace the apply command with one that uses the plan file
|
|
273
285
|
// Remove the -auto-approve flag since plan files don't need it
|
|
274
286
|
const applyIdx = parts.indexOf('-auto-approve');
|
|
275
|
-
if (applyIdx !== -1)
|
|
287
|
+
if (applyIdx !== -1) {
|
|
276
288
|
parts.splice(applyIdx, 1);
|
|
289
|
+
}
|
|
277
290
|
parts.push(planFile);
|
|
278
291
|
}
|
|
279
292
|
}
|
|
@@ -381,62 +394,80 @@ export const kubectlTool = {
|
|
|
381
394
|
// Special handling for new actions
|
|
382
395
|
if (input.action === 'patch') {
|
|
383
396
|
const patchType = input.patch_type ?? 'strategic';
|
|
384
|
-
if (!input.patch)
|
|
397
|
+
if (!input.patch) {
|
|
385
398
|
return err('patch action requires patch field with JSON patch string');
|
|
386
|
-
|
|
399
|
+
}
|
|
400
|
+
if (input.resource) {
|
|
387
401
|
parts.push(input.resource);
|
|
388
|
-
|
|
402
|
+
}
|
|
403
|
+
if (input.namespace) {
|
|
389
404
|
parts.push('-n', input.namespace);
|
|
405
|
+
}
|
|
390
406
|
parts.push(`--type=${patchType}`);
|
|
391
407
|
parts.push('-p', `'${input.patch}'`);
|
|
392
408
|
}
|
|
393
409
|
else if (input.action === 'port-forward') {
|
|
394
|
-
if (input.resource)
|
|
410
|
+
if (input.resource) {
|
|
395
411
|
parts.push(input.resource);
|
|
396
|
-
|
|
412
|
+
}
|
|
413
|
+
if (input.namespace) {
|
|
397
414
|
parts.push('-n', input.namespace);
|
|
398
|
-
|
|
415
|
+
}
|
|
416
|
+
if (input.args) {
|
|
399
417
|
parts.push(input.args);
|
|
418
|
+
}
|
|
400
419
|
}
|
|
401
420
|
else if (input.action === 'cp') {
|
|
402
421
|
if (input.local_path && input.container_path) {
|
|
403
422
|
parts.push(input.local_path, input.container_path);
|
|
404
423
|
}
|
|
405
424
|
else {
|
|
406
|
-
if (input.args)
|
|
425
|
+
if (input.args) {
|
|
407
426
|
parts.push(input.args);
|
|
427
|
+
}
|
|
408
428
|
}
|
|
409
429
|
}
|
|
410
430
|
else if (input.action === 'top') {
|
|
411
|
-
if (input.resource)
|
|
431
|
+
if (input.resource) {
|
|
412
432
|
parts.push(input.resource);
|
|
413
|
-
|
|
433
|
+
}
|
|
434
|
+
if (input.namespace) {
|
|
414
435
|
parts.push('-n', input.namespace);
|
|
415
|
-
|
|
436
|
+
}
|
|
437
|
+
if (input.args) {
|
|
416
438
|
parts.push(input.args);
|
|
439
|
+
}
|
|
417
440
|
}
|
|
418
441
|
else if (input.action === 'cordon' || input.action === 'taint') {
|
|
419
|
-
if (input.resource)
|
|
442
|
+
if (input.resource) {
|
|
420
443
|
parts.push(input.resource);
|
|
421
|
-
|
|
444
|
+
}
|
|
445
|
+
if (input.args) {
|
|
422
446
|
parts.push(input.args);
|
|
447
|
+
}
|
|
423
448
|
}
|
|
424
449
|
else if (input.action === 'drain') {
|
|
425
|
-
if (input.resource)
|
|
450
|
+
if (input.resource) {
|
|
426
451
|
parts.push(input.resource);
|
|
452
|
+
}
|
|
427
453
|
parts.push('--ignore-daemonsets', '--delete-emptydir-data');
|
|
428
|
-
if (input.args)
|
|
454
|
+
if (input.args) {
|
|
429
455
|
parts.push(input.args);
|
|
456
|
+
}
|
|
430
457
|
}
|
|
431
458
|
else if (input.action === 'wait') {
|
|
432
|
-
if (input.resource)
|
|
459
|
+
if (input.resource) {
|
|
433
460
|
parts.push(input.resource);
|
|
434
|
-
|
|
461
|
+
}
|
|
462
|
+
if (input.namespace) {
|
|
435
463
|
parts.push('-n', input.namespace);
|
|
436
|
-
|
|
464
|
+
}
|
|
465
|
+
if (input.args) {
|
|
437
466
|
parts.push(input.args);
|
|
438
|
-
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
439
469
|
parts.push('--for=condition=Ready', '--timeout=120s');
|
|
470
|
+
}
|
|
440
471
|
}
|
|
441
472
|
else if (input.action === 'diff') {
|
|
442
473
|
// G12: kubectl diff — exit code 1 means diffs exist (not an error)
|
|
@@ -450,8 +481,9 @@ export const kubectlTool = {
|
|
|
450
481
|
catch (diffErr) {
|
|
451
482
|
const execError = diffErr;
|
|
452
483
|
// Exit code 1 with stdout = normal diff output (changes detected)
|
|
453
|
-
if (execError.code === 1 && execError.stdout)
|
|
484
|
+
if (execError.code === 1 && execError.stdout) {
|
|
454
485
|
return ok(execError.stdout.trim());
|
|
486
|
+
}
|
|
455
487
|
return err(errorMessage(diffErr));
|
|
456
488
|
}
|
|
457
489
|
}
|
|
@@ -477,8 +509,9 @@ export const kubectlTool = {
|
|
|
477
509
|
const timeoutMs = ctx?.timeout ?? defaultKubectlTimeoutMs; // GAP-20: per-tool timeout from NIMBUS.md
|
|
478
510
|
const result = await spawnExec(command, { onChunk: ctx.onProgress, timeout: timeoutMs });
|
|
479
511
|
const combined = [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
480
|
-
if (result.exitCode !== 0)
|
|
512
|
+
if (result.exitCode !== 0) {
|
|
481
513
|
return err(`kubectl command failed:\n${combined}`);
|
|
514
|
+
}
|
|
482
515
|
return ok(combined || '(no output)');
|
|
483
516
|
}
|
|
484
517
|
const cmdEnv = { ...process.env, ...(input.env ?? {}) };
|
|
@@ -537,8 +570,9 @@ export const helmTool = {
|
|
|
537
570
|
// M5: Helm secrets plugin actions (SOPS-encrypted values)
|
|
538
571
|
if (input.action === 'secrets-encrypt' || input.action === 'secrets-decrypt' || input.action === 'secrets-view') {
|
|
539
572
|
const file = input.values;
|
|
540
|
-
if (!file)
|
|
573
|
+
if (!file) {
|
|
541
574
|
return err('helm secrets requires a values file path (values field)');
|
|
575
|
+
}
|
|
542
576
|
const secretsAction = input.action.replace('secrets-', '');
|
|
543
577
|
const command = `helm secrets ${secretsAction} ${file}`;
|
|
544
578
|
const { stdout, stderr } = await execAsync(command, {
|
|
@@ -549,23 +583,26 @@ export const helmTool = {
|
|
|
549
583
|
}
|
|
550
584
|
// New introspection/repo actions
|
|
551
585
|
if (['get-values', 'get-manifest', 'get-all', 'get-hooks'].includes(input.action)) {
|
|
552
|
-
if (!input.release)
|
|
586
|
+
if (!input.release) {
|
|
553
587
|
return err(`${input.action} requires a release name`);
|
|
588
|
+
}
|
|
554
589
|
const subCmd = input.action.replace('get-', 'get ');
|
|
555
590
|
const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
|
|
556
591
|
const { stdout: getOut, stderr: getErr } = await execAsync(`helm ${subCmd} ${input.release}${nsFlag}`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
|
|
557
592
|
return ok([getOut, getErr].filter(Boolean).join('\n') || '(no output)');
|
|
558
593
|
}
|
|
559
594
|
if (input.action === 'status') {
|
|
560
|
-
if (!input.release)
|
|
595
|
+
if (!input.release) {
|
|
561
596
|
return err('status requires a release name');
|
|
597
|
+
}
|
|
562
598
|
const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
|
|
563
599
|
const { stdout: statusOut, stderr: statusErr } = await execAsync(`helm status ${input.release}${nsFlag}`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
|
|
564
600
|
return ok([statusOut, statusErr].filter(Boolean).join('\n') || '(no output)');
|
|
565
601
|
}
|
|
566
602
|
if (input.action === 'history') {
|
|
567
|
-
if (!input.release)
|
|
603
|
+
if (!input.release) {
|
|
568
604
|
return err('history requires a release name');
|
|
605
|
+
}
|
|
569
606
|
const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
|
|
570
607
|
try {
|
|
571
608
|
const { stdout: histOut } = await execAsync(`helm history ${input.release}${nsFlag} --max 10 --output json`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
|
|
@@ -579,15 +616,17 @@ export const helmTool = {
|
|
|
579
616
|
}
|
|
580
617
|
}
|
|
581
618
|
if (input.action === 'test') {
|
|
582
|
-
if (!input.release)
|
|
619
|
+
if (!input.release) {
|
|
583
620
|
return err('test requires a release name');
|
|
621
|
+
}
|
|
584
622
|
const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
|
|
585
623
|
const { stdout: testOut, stderr: testErr } = await execAsync(`helm test ${input.release}${nsFlag}`, { timeout: 120_000, maxBuffer: 5 * 1024 * 1024 });
|
|
586
624
|
return ok([testOut, testErr].filter(Boolean).join('\n') || '(no output)');
|
|
587
625
|
}
|
|
588
626
|
if (input.action === 'repo-add') {
|
|
589
|
-
if (!input.repo_name || !input.repo_url)
|
|
627
|
+
if (!input.repo_name || !input.repo_url) {
|
|
590
628
|
return err('repo-add requires repo_name and repo_url');
|
|
629
|
+
}
|
|
591
630
|
const { stdout: raOut, stderr: raErr } = await execAsync(`helm repo add ${input.repo_name} ${input.repo_url}`, { timeout: 30_000, maxBuffer: 1 * 1024 * 1024 });
|
|
592
631
|
return ok([raOut, raErr].filter(Boolean).join('\n') || '(no output)');
|
|
593
632
|
}
|
|
@@ -601,15 +640,17 @@ export const helmTool = {
|
|
|
601
640
|
}
|
|
602
641
|
if (input.action === 'search-repo') {
|
|
603
642
|
const query = input.chart ?? input.release ?? '';
|
|
604
|
-
if (!query)
|
|
643
|
+
if (!query) {
|
|
605
644
|
return err('search-repo requires chart or release field as search term');
|
|
645
|
+
}
|
|
606
646
|
const { stdout: srOut, stderr: srErr } = await execAsync(`helm search repo ${query}`, { timeout: 30_000, maxBuffer: 2 * 1024 * 1024 });
|
|
607
647
|
return ok([srOut, srErr].filter(Boolean).join('\n') || '(no results)');
|
|
608
648
|
}
|
|
609
649
|
if (input.action === 'show-chart' || input.action === 'show-values') {
|
|
610
650
|
const target = input.chart ?? input.release;
|
|
611
|
-
if (!target)
|
|
651
|
+
if (!target) {
|
|
612
652
|
return err(`${input.action} requires chart or release field`);
|
|
653
|
+
}
|
|
613
654
|
const subCmd = input.action === 'show-chart' ? 'chart' : 'values';
|
|
614
655
|
const { stdout: showOut, stderr: showErr } = await execAsync(`helm show ${subCmd} ${target}`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
|
|
615
656
|
return ok([showOut, showErr].filter(Boolean).join('\n') || '(no output)');
|
|
@@ -786,20 +827,22 @@ export const cloudDiscoverTool = {
|
|
|
786
827
|
const publicIp = item.PublicIpAddress ?? '';
|
|
787
828
|
const privateIp = item.PrivateIpAddress ?? '';
|
|
788
829
|
const sgs = item.SecurityGroups ?? [];
|
|
789
|
-
if (sgs.length > 0)
|
|
830
|
+
if (sgs.length > 0) {
|
|
790
831
|
securityFlags.push('check-sg-rules');
|
|
832
|
+
}
|
|
791
833
|
const flagStr = securityFlags.length > 0 ? ` [${securityFlags.join(', ')}]` : '';
|
|
792
834
|
return ` - EC2: ${name} (${item.InstanceType ?? ''}) ${state}${az ? ` [${az}]` : ''}${publicIp ? ` pub:${publicIp}` : ''}${privateIp ? ` priv:${privateIp}` : ''}${flagStr}`;
|
|
793
835
|
}
|
|
794
836
|
// RDS formatter
|
|
795
837
|
if (item.DBInstanceIdentifier) {
|
|
796
838
|
const id = item.DBInstanceIdentifier;
|
|
797
|
-
const engine = `${item.Engine ?? ''}${item.EngineVersion ?
|
|
839
|
+
const engine = `${item.Engine ?? ''}${item.EngineVersion ? ` ${item.EngineVersion}` : ''}`;
|
|
798
840
|
const status = item.DBInstanceStatus ?? '';
|
|
799
841
|
const multiAz = item.MultiAZ ? 'Multi-AZ' : 'Single-AZ';
|
|
800
842
|
const endpoint = item.Endpoint?.Address ?? '';
|
|
801
|
-
if (!item.StorageEncrypted)
|
|
843
|
+
if (!item.StorageEncrypted) {
|
|
802
844
|
securityFlags.push('unencrypted');
|
|
845
|
+
}
|
|
803
846
|
const flagStr = securityFlags.length > 0 ? ` [${securityFlags.join(', ')}]` : '';
|
|
804
847
|
return ` - RDS: ${id} (${engine}) ${status} ${multiAz}${endpoint ? ` -> ${endpoint}` : ''}${flagStr}`;
|
|
805
848
|
}
|
|
@@ -849,8 +892,7 @@ export const cloudDiscoverTool = {
|
|
|
849
892
|
const region = item.Placement?.AvailabilityZone || item.DBInstanceArn?.split(':')[3] || item.region || '';
|
|
850
893
|
return ` - ${name}${type ? ` (${type})` : ''}${region ? ` [${region}]` : ''}`;
|
|
851
894
|
});
|
|
852
|
-
return ok(`Found ${items.length} resource(s):\n${summary.join('\n')}`
|
|
853
|
-
(items.length > 50 ? `\n\n[+${items.length - 50} more — use specific region/filter to narrow]` : ''));
|
|
895
|
+
return ok(`Found ${items.length} resource(s):\n${summary.join('\n')}${items.length > 50 ? `\n\n[+${items.length - 50} more — use specific region/filter to narrow]` : ''}`);
|
|
854
896
|
}
|
|
855
897
|
catch {
|
|
856
898
|
// Not JSON or failed to parse — return raw output truncated
|
|
@@ -994,8 +1036,9 @@ export const costEstimateTool = {
|
|
|
994
1036
|
}
|
|
995
1037
|
if (input.target === 'lambda') {
|
|
996
1038
|
const fn = input.function_name ?? input.workdir;
|
|
997
|
-
if (!fn)
|
|
1039
|
+
if (!fn) {
|
|
998
1040
|
return err('function_name required for Lambda cost estimation');
|
|
1041
|
+
}
|
|
999
1042
|
try {
|
|
1000
1043
|
const { stdout } = await execAsync(`aws lambda get-function-configuration --function-name ${fn} --output json`, { timeout: 15_000 });
|
|
1001
1044
|
const cfg = JSON.parse(stdout);
|
|
@@ -1155,7 +1198,7 @@ export const driftDetectTool = {
|
|
|
1155
1198
|
timeout: 120_000, maxBuffer: 10 * 1024 * 1024,
|
|
1156
1199
|
});
|
|
1157
1200
|
if (diffOut.trim()) {
|
|
1158
|
-
results.push(
|
|
1201
|
+
results.push(`## Tracked Resource Drift (kubectl diff):\n${diffOut}`);
|
|
1159
1202
|
}
|
|
1160
1203
|
}
|
|
1161
1204
|
catch { /* ignore */ }
|
|
@@ -1166,8 +1209,9 @@ export const driftDetectTool = {
|
|
|
1166
1209
|
const clusterData = JSON.parse(clusterJson);
|
|
1167
1210
|
for (const item of (clusterData.items ?? [])) {
|
|
1168
1211
|
const kind = item.kind ?? 'Unknown';
|
|
1169
|
-
if (!clusterResources[kind])
|
|
1212
|
+
if (!clusterResources[kind]) {
|
|
1170
1213
|
clusterResources[kind] = new Set();
|
|
1214
|
+
}
|
|
1171
1215
|
clusterResources[kind].add(`${item.metadata?.namespace ?? 'default'}/${item.metadata?.name}`);
|
|
1172
1216
|
}
|
|
1173
1217
|
}
|
|
@@ -1182,8 +1226,9 @@ export const driftDetectTool = {
|
|
|
1182
1226
|
try {
|
|
1183
1227
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1184
1228
|
const full = joinPath(dir, entry.name);
|
|
1185
|
-
if (entry.isDirectory())
|
|
1229
|
+
if (entry.isDirectory()) {
|
|
1186
1230
|
scanDir(full);
|
|
1231
|
+
}
|
|
1187
1232
|
else if (entry.name.endsWith('.yaml') || entry.name.endsWith('.yml')) {
|
|
1188
1233
|
const fileContent = readFileSync(full, 'utf-8');
|
|
1189
1234
|
const kindMatch = fileContent.match(/^kind:\s*(\S+)/m);
|
|
@@ -1219,9 +1264,7 @@ export const driftDetectTool = {
|
|
|
1219
1264
|
}
|
|
1220
1265
|
}
|
|
1221
1266
|
if (untracked.length > 0) {
|
|
1222
|
-
results.push(`## Untracked Cluster Resources (${untracked.length} total):\n`
|
|
1223
|
-
untracked.slice(0, 100).map(r => ` - ${r}`).join('\n') +
|
|
1224
|
-
(untracked.length > 100 ? `\n ... and ${untracked.length - 100} more` : ''));
|
|
1267
|
+
results.push(`## Untracked Cluster Resources (${untracked.length} total):\n${untracked.slice(0, 100).map(r => ` - ${r}`).join('\n')}${untracked.length > 100 ? `\n ... and ${untracked.length - 100} more` : ''}`);
|
|
1225
1268
|
}
|
|
1226
1269
|
if (results.length === 0) {
|
|
1227
1270
|
return ok('No drift detected in Kubernetes resources.');
|
|
@@ -1246,8 +1289,9 @@ export const driftDetectTool = {
|
|
|
1246
1289
|
try {
|
|
1247
1290
|
const { stdout } = await execAsync('helm list -A --output json', { timeout: 30_000 });
|
|
1248
1291
|
const releases = JSON.parse(stdout || '[]');
|
|
1249
|
-
if (releases.length === 0)
|
|
1292
|
+
if (releases.length === 0) {
|
|
1250
1293
|
return ok('No Helm releases found.');
|
|
1294
|
+
}
|
|
1251
1295
|
const lines = releases.map(r => ` ${r.name} (${r.namespace}): ${r.status} — ${r.chart}, updated ${r.updated}`);
|
|
1252
1296
|
return ok(`Helm releases:\n${lines.join('\n')}\n\n` +
|
|
1253
1297
|
`Note: Install helm-diff for detailed drift: helm plugin install https://github.com/databus23/helm-diff`);
|
|
@@ -1695,8 +1739,9 @@ export const dockerTool = {
|
|
|
1695
1739
|
const lines = chunk.split('\n');
|
|
1696
1740
|
for (const line of lines) {
|
|
1697
1741
|
const trimmed = line.trim();
|
|
1698
|
-
if (!trimmed)
|
|
1742
|
+
if (!trimmed) {
|
|
1699
1743
|
continue;
|
|
1744
|
+
}
|
|
1700
1745
|
// Keep: Step N/M, Using cache, Successfully built, error, FROM/RUN/COPY step info
|
|
1701
1746
|
if (/^Step\s+\d+\/\d+/i.test(trimmed) ||
|
|
1702
1747
|
/---> Using cache/i.test(trimmed) ||
|
|
@@ -1704,14 +1749,15 @@ export const dockerTool = {
|
|
|
1704
1749
|
/Successfully tagged/i.test(trimmed) ||
|
|
1705
1750
|
/error/i.test(trimmed) ||
|
|
1706
1751
|
/warning/i.test(trimmed)) {
|
|
1707
|
-
ctx.onProgress(line
|
|
1752
|
+
ctx.onProgress(`${line}\n`);
|
|
1708
1753
|
}
|
|
1709
1754
|
}
|
|
1710
1755
|
};
|
|
1711
1756
|
const buildResult = await spawnExec(command, { onChunk: filteredProgress, timeout: ctx?.timeout ?? 300_000 });
|
|
1712
1757
|
const combined = [buildResult.stdout, buildResult.stderr].filter(Boolean).join('\n');
|
|
1713
|
-
if (buildResult.exitCode !== 0)
|
|
1758
|
+
if (buildResult.exitCode !== 0) {
|
|
1714
1759
|
return err(`Docker build failed:\n${combined}`);
|
|
1760
|
+
}
|
|
1715
1761
|
return ok(combined || 'Build complete.');
|
|
1716
1762
|
}
|
|
1717
1763
|
const { stdout, stderr } = await execAsync(command, {
|
|
@@ -1762,8 +1808,9 @@ export const secretsTool = {
|
|
|
1762
1808
|
command = `${nsFlag}vault kv list -format=json ${input.path}`;
|
|
1763
1809
|
break;
|
|
1764
1810
|
case 'put':
|
|
1765
|
-
if (!input.value)
|
|
1811
|
+
if (!input.value) {
|
|
1766
1812
|
return err('value is required for put action');
|
|
1813
|
+
}
|
|
1767
1814
|
command = `${nsFlag}vault kv put ${input.path} value=${input.value}`;
|
|
1768
1815
|
break;
|
|
1769
1816
|
case 'delete':
|
|
@@ -1787,8 +1834,9 @@ export const secretsTool = {
|
|
|
1787
1834
|
command = `aws secretsmanager list-secrets ${regionFlag} --output json`;
|
|
1788
1835
|
break;
|
|
1789
1836
|
case 'put':
|
|
1790
|
-
if (!input.value)
|
|
1837
|
+
if (!input.value) {
|
|
1791
1838
|
return err('value is required for put action');
|
|
1839
|
+
}
|
|
1792
1840
|
command = `aws secretsmanager put-secret-value --secret-id ${input.path} --secret-string '${input.value.replace(/'/g, "'\\''")}' ${regionFlag}`;
|
|
1793
1841
|
break;
|
|
1794
1842
|
case 'delete':
|
|
@@ -1812,8 +1860,9 @@ export const secretsTool = {
|
|
|
1812
1860
|
command = `gcloud secrets list --format=json`;
|
|
1813
1861
|
break;
|
|
1814
1862
|
case 'put':
|
|
1815
|
-
if (!input.value)
|
|
1863
|
+
if (!input.value) {
|
|
1816
1864
|
return err('value is required for put action');
|
|
1865
|
+
}
|
|
1817
1866
|
command = `echo '${input.value.replace(/'/g, "'\\''")}' | gcloud secrets create ${input.path} --data-file=-`;
|
|
1818
1867
|
break;
|
|
1819
1868
|
case 'delete':
|
|
@@ -1837,8 +1886,9 @@ export const secretsTool = {
|
|
|
1837
1886
|
command = `az keyvault secret list ${vaultFlag} --output json`;
|
|
1838
1887
|
break;
|
|
1839
1888
|
case 'put':
|
|
1840
|
-
if (!input.value)
|
|
1889
|
+
if (!input.value) {
|
|
1841
1890
|
return err('value is required for put action');
|
|
1891
|
+
}
|
|
1842
1892
|
command = `az keyvault secret set --name ${input.path} --value '${input.value.replace(/'/g, "'\\''")}' ${vaultFlag}`;
|
|
1843
1893
|
break;
|
|
1844
1894
|
case 'delete':
|
|
@@ -2028,7 +2078,7 @@ export const cicdTool = {
|
|
|
2028
2078
|
const lines = combined.split('\n');
|
|
2029
2079
|
const truncated = lines.length > 200;
|
|
2030
2080
|
const output = truncated
|
|
2031
|
-
? lines.slice(0, 200).join('\n')
|
|
2081
|
+
? `${lines.slice(0, 200).join('\n')}\n\n... truncated (showing first 200 lines)`
|
|
2032
2082
|
: combined;
|
|
2033
2083
|
return ok(output || '(no output)');
|
|
2034
2084
|
}
|
|
@@ -2064,8 +2114,9 @@ export const monitorTool = {
|
|
|
2064
2114
|
const input = monitorSchema.parse(raw);
|
|
2065
2115
|
// Parse relative times
|
|
2066
2116
|
function parseTime(t, defaultSecs) {
|
|
2067
|
-
if (!t)
|
|
2117
|
+
if (!t) {
|
|
2068
2118
|
return Math.floor(Date.now() / 1000) - defaultSecs;
|
|
2119
|
+
}
|
|
2069
2120
|
if (t.startsWith('-')) {
|
|
2070
2121
|
const val = parseInt(t.slice(1));
|
|
2071
2122
|
const unit = t.slice(-1);
|
|
@@ -2122,19 +2173,21 @@ export const monitorTool = {
|
|
|
2122
2173
|
case 'datadog': {
|
|
2123
2174
|
const apiKey = process.env.DD_API_KEY ?? '';
|
|
2124
2175
|
const appKey = process.env.DD_APP_KEY ?? '';
|
|
2125
|
-
if (!apiKey)
|
|
2176
|
+
if (!apiKey) {
|
|
2126
2177
|
return err('DD_API_KEY environment variable not set');
|
|
2178
|
+
}
|
|
2127
2179
|
const q = encodeURIComponent(input.query ?? 'avg:system.cpu.user{*}');
|
|
2128
2180
|
const cmd = `curl -sf -H "DD-API-KEY: ${apiKey}" -H "DD-APPLICATION-KEY: ${appKey}" "https://api.datadoghq.com/api/v1/query?from=${startTs}&to=${endTs}&query=${q}"`;
|
|
2129
2181
|
const { stdout } = await execAsync(cmd, { timeout: 30_000 });
|
|
2130
2182
|
const data = JSON.parse(stdout);
|
|
2131
2183
|
const series = (data.series ?? []).slice(0, 100);
|
|
2132
|
-
return ok(`Datadog query (${series.length} series):\n
|
|
2184
|
+
return ok(`Datadog query (${series.length} series):\n${JSON.stringify(series.map((s) => ({ metric: s.metric, points: s.pointlist.length })), null, 2)}`);
|
|
2133
2185
|
}
|
|
2134
2186
|
case 'newrelic': {
|
|
2135
2187
|
const apiKey = process.env.NEW_RELIC_API_KEY ?? '';
|
|
2136
|
-
if (!apiKey)
|
|
2188
|
+
if (!apiKey) {
|
|
2137
2189
|
return err('NEW_RELIC_API_KEY environment variable not set');
|
|
2190
|
+
}
|
|
2138
2191
|
const nrqlQuery = input.query ?? `SELECT average(cpuPercent) FROM SystemSample SINCE 1 hour ago`;
|
|
2139
2192
|
const body = JSON.stringify({ query: `{ actor { nrql(accounts: 0, query: "${nrqlQuery.replace(/"/g, '\\"')}") { results } } }` });
|
|
2140
2193
|
const cmd = `curl -sf -X POST -H "Content-Type: application/json" -H "API-Key: ${apiKey}" -d '${body.replace(/'/g, "'\\''")}' "https://api.newrelic.com/graphql"`;
|
|
@@ -2144,8 +2197,9 @@ export const monitorTool = {
|
|
|
2144
2197
|
// Gap 5: PagerDuty alert management
|
|
2145
2198
|
case 'pagerduty': {
|
|
2146
2199
|
const pdKey = process.env.PD_API_KEY ?? '';
|
|
2147
|
-
if (!pdKey)
|
|
2200
|
+
if (!pdKey) {
|
|
2148
2201
|
return err('PD_API_KEY environment variable not set');
|
|
2202
|
+
}
|
|
2149
2203
|
const authHeader = `-H "Authorization: Token token=${pdKey}" -H "Accept: application/vnd.pagerduty+json;version=2"`;
|
|
2150
2204
|
switch (input.action) {
|
|
2151
2205
|
case 'incidents':
|
|
@@ -2153,14 +2207,16 @@ export const monitorTool = {
|
|
|
2153
2207
|
case 'alerts':
|
|
2154
2208
|
return ok((await execAsync(`curl -sf ${authHeader} "https://api.pagerduty.com/alerts?limit=25"`, { timeout: 15_000 })).stdout.slice(0, 5000));
|
|
2155
2209
|
case 'ack': {
|
|
2156
|
-
if (!input.incident_id)
|
|
2210
|
+
if (!input.incident_id) {
|
|
2157
2211
|
return err('incident_id required for ack action');
|
|
2212
|
+
}
|
|
2158
2213
|
const body = JSON.stringify({ incident: { type: 'incident_reference', status: 'acknowledged' } });
|
|
2159
2214
|
return ok((await execAsync(`curl -sf -X PUT ${authHeader} -H "Content-Type: application/json" -d '${body}' "https://api.pagerduty.com/incidents/${input.incident_id}"`, { timeout: 15_000 })).stdout.slice(0, 2000));
|
|
2160
2215
|
}
|
|
2161
2216
|
case 'resolve': {
|
|
2162
|
-
if (!input.incident_id)
|
|
2217
|
+
if (!input.incident_id) {
|
|
2163
2218
|
return err('incident_id required for resolve action');
|
|
2219
|
+
}
|
|
2164
2220
|
const body = JSON.stringify({ incident: { type: 'incident_reference', status: 'resolved' } });
|
|
2165
2221
|
return ok((await execAsync(`curl -sf -X PUT ${authHeader} -H "Content-Type: application/json" -d '${body}' "https://api.pagerduty.com/incidents/${input.incident_id}"`, { timeout: 15_000 })).stdout.slice(0, 2000));
|
|
2166
2222
|
}
|
|
@@ -2173,22 +2229,25 @@ export const monitorTool = {
|
|
|
2173
2229
|
// Gap 5: Opsgenie alert management
|
|
2174
2230
|
case 'opsgenie': {
|
|
2175
2231
|
const ogKey = process.env.OPSGENIE_API_KEY ?? '';
|
|
2176
|
-
if (!ogKey)
|
|
2232
|
+
if (!ogKey) {
|
|
2177
2233
|
return err('OPSGENIE_API_KEY environment variable not set');
|
|
2234
|
+
}
|
|
2178
2235
|
const authHeader = `-H "Authorization: GenieKey ${ogKey}"`;
|
|
2179
2236
|
switch (input.action) {
|
|
2180
2237
|
case 'alerts':
|
|
2181
2238
|
case 'incidents':
|
|
2182
2239
|
return ok((await execAsync(`curl -sf ${authHeader} "https://api.opsgenie.com/v2/alerts?limit=25"`, { timeout: 15_000 })).stdout.slice(0, 5000));
|
|
2183
2240
|
case 'ack': {
|
|
2184
|
-
if (!input.incident_id)
|
|
2241
|
+
if (!input.incident_id) {
|
|
2185
2242
|
return err('incident_id required for ack action');
|
|
2243
|
+
}
|
|
2186
2244
|
const body = JSON.stringify({ note: 'Acknowledged via Nimbus' });
|
|
2187
2245
|
return ok((await execAsync(`curl -sf -X POST ${authHeader} -H "Content-Type: application/json" -d '${JSON.stringify(body)}' "https://api.opsgenie.com/v2/alerts/${input.incident_id}/acknowledge"`, { timeout: 15_000 })).stdout.slice(0, 2000));
|
|
2188
2246
|
}
|
|
2189
2247
|
case 'resolve': {
|
|
2190
|
-
if (!input.incident_id)
|
|
2248
|
+
if (!input.incident_id) {
|
|
2191
2249
|
return err('incident_id required for resolve action');
|
|
2250
|
+
}
|
|
2192
2251
|
const body = JSON.stringify({ note: 'Resolved via Nimbus' });
|
|
2193
2252
|
return ok((await execAsync(`curl -sf -X POST ${authHeader} -H "Content-Type: application/json" -d '${JSON.stringify(body)}' "https://api.opsgenie.com/v2/alerts/${input.incident_id}/close"`, { timeout: 15_000 })).stdout.slice(0, 2000));
|
|
2194
2253
|
}
|
|
@@ -2324,7 +2383,7 @@ export const gitopsTool = {
|
|
|
2324
2383
|
const health = app?.status?.health?.status ?? 'Unknown';
|
|
2325
2384
|
const sync = app?.status?.sync?.status ?? 'Unknown';
|
|
2326
2385
|
const conditions = (app?.status?.conditions ?? []).map((c) => ` ${c.type}: ${c.message}`).join('\n');
|
|
2327
|
-
return ok(`App: ${app?.metadata?.name}\nHealth: ${health}\nSync: ${sync}\n${conditions ?
|
|
2386
|
+
return ok(`App: ${app?.metadata?.name}\nHealth: ${health}\nSync: ${sync}\n${conditions ? `Conditions:\n${conditions}` : ''}`);
|
|
2328
2387
|
}
|
|
2329
2388
|
catch {
|
|
2330
2389
|
// Fall through to raw output
|
|
@@ -2508,7 +2567,7 @@ export const logsTool = {
|
|
|
2508
2567
|
const combined = [stdout, stderr].filter(Boolean).join('\n');
|
|
2509
2568
|
const lines = combined.split('\n');
|
|
2510
2569
|
const output = lines.length > maxLines
|
|
2511
|
-
? lines.slice(0, maxLines).join('\n')
|
|
2570
|
+
? `${lines.slice(0, maxLines).join('\n')}\n\n... truncated at ${maxLines} lines`
|
|
2512
2571
|
: combined;
|
|
2513
2572
|
return ok(output || '(no logs found)');
|
|
2514
2573
|
}
|
|
@@ -2916,15 +2975,19 @@ export const awsTool = {
|
|
|
2916
2975
|
try {
|
|
2917
2976
|
const input = awsSchema.parse(raw);
|
|
2918
2977
|
const parts = ['aws', input.service, input.action];
|
|
2919
|
-
if (input.profile)
|
|
2978
|
+
if (input.profile) {
|
|
2920
2979
|
parts.push('--profile', input.profile);
|
|
2921
|
-
|
|
2980
|
+
}
|
|
2981
|
+
else if (process.env.AWS_PROFILE) {
|
|
2922
2982
|
parts.push('--profile', process.env.AWS_PROFILE);
|
|
2923
|
-
|
|
2983
|
+
}
|
|
2984
|
+
if (input.region) {
|
|
2924
2985
|
parts.push('--region', input.region);
|
|
2986
|
+
}
|
|
2925
2987
|
parts.push('--output', input.output ?? 'json');
|
|
2926
|
-
if (input.args)
|
|
2988
|
+
if (input.args) {
|
|
2927
2989
|
parts.push(input.args);
|
|
2990
|
+
}
|
|
2928
2991
|
const command = parts.join(' ');
|
|
2929
2992
|
const env = { ...process.env };
|
|
2930
2993
|
const { stdout, stderr } = await execAsync(command, {
|
|
@@ -2958,13 +3021,16 @@ export const gcloudTool = {
|
|
|
2958
3021
|
try {
|
|
2959
3022
|
const input = gcloudSchema.parse(raw);
|
|
2960
3023
|
const parts = ['gcloud', input.service, input.action];
|
|
2961
|
-
if (input.project)
|
|
3024
|
+
if (input.project) {
|
|
2962
3025
|
parts.push('--project', input.project);
|
|
2963
|
-
|
|
3026
|
+
}
|
|
3027
|
+
if (input.region) {
|
|
2964
3028
|
parts.push('--region', input.region);
|
|
3029
|
+
}
|
|
2965
3030
|
parts.push('--format', input.output ?? 'json');
|
|
2966
|
-
if (input.args)
|
|
3031
|
+
if (input.args) {
|
|
2967
3032
|
parts.push(input.args);
|
|
3033
|
+
}
|
|
2968
3034
|
const command = parts.join(' ');
|
|
2969
3035
|
const { stdout, stderr } = await execAsync(command, {
|
|
2970
3036
|
timeout: 60_000,
|
|
@@ -2996,13 +3062,16 @@ export const azTool = {
|
|
|
2996
3062
|
try {
|
|
2997
3063
|
const input = azSchema.parse(raw);
|
|
2998
3064
|
const parts = ['az', input.service, input.action];
|
|
2999
|
-
if (input.subscription)
|
|
3065
|
+
if (input.subscription) {
|
|
3000
3066
|
parts.push('--subscription', input.subscription);
|
|
3001
|
-
|
|
3067
|
+
}
|
|
3068
|
+
if (input.resource_group) {
|
|
3002
3069
|
parts.push('--resource-group', input.resource_group);
|
|
3070
|
+
}
|
|
3003
3071
|
parts.push('--output', input.output ?? 'json');
|
|
3004
|
-
if (input.args)
|
|
3072
|
+
if (input.args) {
|
|
3005
3073
|
parts.push(input.args);
|
|
3074
|
+
}
|
|
3006
3075
|
const command = parts.join(' ');
|
|
3007
3076
|
const { stdout, stderr } = await execAsync(command, {
|
|
3008
3077
|
timeout: 60_000,
|
|
@@ -3049,21 +3118,25 @@ export const incidentTool = {
|
|
|
3049
3118
|
try {
|
|
3050
3119
|
if (action === 'list') {
|
|
3051
3120
|
const params = new URLSearchParams();
|
|
3052
|
-
if (status)
|
|
3121
|
+
if (status) {
|
|
3053
3122
|
params.set('statuses[]', status);
|
|
3123
|
+
}
|
|
3054
3124
|
params.set('limit', '20');
|
|
3055
3125
|
const res = await fetch(`${baseUrl}/incidents?${params}`, { headers });
|
|
3056
|
-
if (!res.ok)
|
|
3126
|
+
if (!res.ok) {
|
|
3057
3127
|
return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
|
|
3128
|
+
}
|
|
3058
3129
|
const data = await res.json();
|
|
3059
|
-
if (!data.incidents.length)
|
|
3130
|
+
if (!data.incidents.length) {
|
|
3060
3131
|
return ok('No incidents found.');
|
|
3132
|
+
}
|
|
3061
3133
|
return ok(data.incidents.map(i => `[${i.status.toUpperCase()}] ${i.id}: ${i.title} (${i.urgency}) — ${i.created_at}`).join('\n'));
|
|
3062
3134
|
}
|
|
3063
3135
|
if (action === 'get' && id) {
|
|
3064
3136
|
const res = await fetch(`${baseUrl}/incidents/${id}`, { headers });
|
|
3065
|
-
if (!res.ok)
|
|
3137
|
+
if (!res.ok) {
|
|
3066
3138
|
return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
|
|
3139
|
+
}
|
|
3067
3140
|
const data = await res.json();
|
|
3068
3141
|
const inc = data.incident;
|
|
3069
3142
|
return ok(`ID: ${inc.id}\nTitle: ${inc.title}\nStatus: ${inc.status}\nUrgency: ${inc.urgency}\nCreated: ${inc.created_at}\n${inc.body?.details ? `Details: ${inc.body.details}` : ''}`);
|
|
@@ -3073,8 +3146,9 @@ export const incidentTool = {
|
|
|
3073
3146
|
method: 'PUT', headers,
|
|
3074
3147
|
body: JSON.stringify({ incident: { type: 'incident_reference', status: 'acknowledged' } }),
|
|
3075
3148
|
});
|
|
3076
|
-
if (!res.ok)
|
|
3149
|
+
if (!res.ok) {
|
|
3077
3150
|
return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
|
|
3151
|
+
}
|
|
3078
3152
|
return ok(`Incident ${id} acknowledged.`);
|
|
3079
3153
|
}
|
|
3080
3154
|
if (action === 'resolve' && id) {
|
|
@@ -3082,26 +3156,30 @@ export const incidentTool = {
|
|
|
3082
3156
|
method: 'PUT', headers,
|
|
3083
3157
|
body: JSON.stringify({ incident: { type: 'incident_reference', status: 'resolved' } }),
|
|
3084
3158
|
});
|
|
3085
|
-
if (!res.ok)
|
|
3159
|
+
if (!res.ok) {
|
|
3086
3160
|
return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
|
|
3161
|
+
}
|
|
3087
3162
|
return ok(`Incident ${id} resolved.`);
|
|
3088
3163
|
}
|
|
3089
3164
|
if (action === 'create') {
|
|
3090
|
-
if (!title || !service_id)
|
|
3165
|
+
if (!title || !service_id) {
|
|
3091
3166
|
return err('create action requires title and service_id');
|
|
3167
|
+
}
|
|
3092
3168
|
const res = await fetch(`${baseUrl}/incidents`, {
|
|
3093
3169
|
method: 'POST', headers,
|
|
3094
3170
|
body: JSON.stringify({ incident: { type: 'incident', title, urgency: urgency ?? 'high', service: { id: service_id, type: 'service_reference' }, body: body ? { type: 'incident_body', details: body } : undefined } }),
|
|
3095
3171
|
});
|
|
3096
|
-
if (!res.ok)
|
|
3172
|
+
if (!res.ok) {
|
|
3097
3173
|
return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
|
|
3174
|
+
}
|
|
3098
3175
|
const data = await res.json();
|
|
3099
3176
|
return ok(`Incident created: ${data.incident.id}`);
|
|
3100
3177
|
}
|
|
3101
3178
|
if (action === 'on-call') {
|
|
3102
3179
|
const res = await fetch(`${baseUrl}/oncalls?limit=10`, { headers });
|
|
3103
|
-
if (!res.ok)
|
|
3180
|
+
if (!res.ok) {
|
|
3104
3181
|
return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
|
|
3182
|
+
}
|
|
3105
3183
|
const data = await res.json();
|
|
3106
3184
|
return ok(data.oncalls.map(o => `${o.user.summary}${o.schedule?.summary ? ` (${o.schedule.summary})` : ''} until ${o.end}`).join('\n') || 'No on-call data found.');
|
|
3107
3185
|
}
|
|
@@ -3122,20 +3200,24 @@ export const incidentTool = {
|
|
|
3122
3200
|
try {
|
|
3123
3201
|
if (action === 'list') {
|
|
3124
3202
|
const params = new URLSearchParams({ limit: '20', sort: 'createdAt', order: 'desc' });
|
|
3125
|
-
if (status)
|
|
3203
|
+
if (status) {
|
|
3126
3204
|
params.set('query', `status=${status}`);
|
|
3205
|
+
}
|
|
3127
3206
|
const res = await fetch(`${baseUrl}/alerts?${params}`, { headers });
|
|
3128
|
-
if (!res.ok)
|
|
3207
|
+
if (!res.ok) {
|
|
3129
3208
|
return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
|
|
3209
|
+
}
|
|
3130
3210
|
const data = await res.json();
|
|
3131
|
-
if (!data.data.length)
|
|
3211
|
+
if (!data.data.length) {
|
|
3132
3212
|
return ok('No alerts found.');
|
|
3213
|
+
}
|
|
3133
3214
|
return ok(data.data.map(a => `[${a.status.toUpperCase()}] ${a.tinyId}: ${a.message} (${a.priority}) — ${a.createdAt}`).join('\n'));
|
|
3134
3215
|
}
|
|
3135
3216
|
if (action === 'get' && id) {
|
|
3136
3217
|
const res = await fetch(`${baseUrl}/alerts/${id}`, { headers });
|
|
3137
|
-
if (!res.ok)
|
|
3218
|
+
if (!res.ok) {
|
|
3138
3219
|
return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
|
|
3220
|
+
}
|
|
3139
3221
|
const data = await res.json();
|
|
3140
3222
|
const a = data.data;
|
|
3141
3223
|
return ok(`ID: ${a.id}\nMessage: ${a.message}\nStatus: ${a.status}\nPriority: ${a.priority}\nCreated: ${a.createdAt}\n${a.description ? `Description: ${a.description}` : ''}`);
|
|
@@ -3144,34 +3226,39 @@ export const incidentTool = {
|
|
|
3144
3226
|
const res = await fetch(`${baseUrl}/alerts/${id}/acknowledge`, {
|
|
3145
3227
|
method: 'POST', headers, body: JSON.stringify({ note: 'Acknowledged via Nimbus' }),
|
|
3146
3228
|
});
|
|
3147
|
-
if (!res.ok)
|
|
3229
|
+
if (!res.ok) {
|
|
3148
3230
|
return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
|
|
3231
|
+
}
|
|
3149
3232
|
return ok(`Alert ${id} acknowledged.`);
|
|
3150
3233
|
}
|
|
3151
3234
|
if (action === 'resolve' && id) {
|
|
3152
3235
|
const res = await fetch(`${baseUrl}/alerts/${id}/close`, {
|
|
3153
3236
|
method: 'POST', headers, body: JSON.stringify({ note: 'Resolved via Nimbus' }),
|
|
3154
3237
|
});
|
|
3155
|
-
if (!res.ok)
|
|
3238
|
+
if (!res.ok) {
|
|
3156
3239
|
return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
|
|
3240
|
+
}
|
|
3157
3241
|
return ok(`Alert ${id} resolved.`);
|
|
3158
3242
|
}
|
|
3159
3243
|
if (action === 'create') {
|
|
3160
|
-
if (!title)
|
|
3244
|
+
if (!title) {
|
|
3161
3245
|
return err('create action requires title');
|
|
3246
|
+
}
|
|
3162
3247
|
const res = await fetch(`${baseUrl}/alerts`, {
|
|
3163
3248
|
method: 'POST', headers,
|
|
3164
3249
|
body: JSON.stringify({ message: title, description: body, priority: urgency === 'high' ? 'P1' : 'P3', teams: team_id ? [{ id: team_id }] : undefined }),
|
|
3165
3250
|
});
|
|
3166
|
-
if (!res.ok)
|
|
3251
|
+
if (!res.ok) {
|
|
3167
3252
|
return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
|
|
3253
|
+
}
|
|
3168
3254
|
const data = await res.json();
|
|
3169
3255
|
return ok(`Alert created. Request ID: ${data.requestId}`);
|
|
3170
3256
|
}
|
|
3171
3257
|
if (action === 'on-call') {
|
|
3172
3258
|
const res = await fetch(`${baseUrl}/schedules/on-calls`, { headers });
|
|
3173
|
-
if (!res.ok)
|
|
3259
|
+
if (!res.ok) {
|
|
3174
3260
|
return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
|
|
3261
|
+
}
|
|
3175
3262
|
const data = await res.json();
|
|
3176
3263
|
return ok(data.data.map(s => `${s._parent?.name}: ${s.onCallParticipants.map((p) => p.name).join(', ')}`).join('\n') || 'No on-call data.');
|
|
3177
3264
|
}
|
|
@@ -3222,8 +3309,9 @@ export const generateInfraTool = {
|
|
|
3222
3309
|
const files = [];
|
|
3223
3310
|
for (const file of project.files) {
|
|
3224
3311
|
const parts = file.path.split('/').slice(0, -1).join('/');
|
|
3225
|
-
if (parts)
|
|
3312
|
+
if (parts) {
|
|
3226
3313
|
mkdirSync(join(outputDir, parts), { recursive: true });
|
|
3314
|
+
}
|
|
3227
3315
|
const filePath = join(outputDir, file.path);
|
|
3228
3316
|
writeFileSync(filePath, file.content, 'utf-8');
|
|
3229
3317
|
files.push(file.path);
|
|
@@ -3268,8 +3356,9 @@ export const generateInfraTool = {
|
|
|
3268
3356
|
const files = [];
|
|
3269
3357
|
for (const file of chartFiles) {
|
|
3270
3358
|
const parts = file.path.split('/').slice(0, -1).join('/');
|
|
3271
|
-
if (parts)
|
|
3359
|
+
if (parts) {
|
|
3272
3360
|
mkdirSync(join(outputDir, parts), { recursive: true });
|
|
3361
|
+
}
|
|
3273
3362
|
const filePath = join(outputDir, file.path);
|
|
3274
3363
|
writeFileSync(filePath, file.content, 'utf-8');
|
|
3275
3364
|
files.push(file.path);
|