@build-astron-co/nimbus 0.4.2 → 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/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 +6 -3
- 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/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/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/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 -609
- 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 -234
- 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
|
@@ -1,602 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Schema Tests
|
|
3
|
-
*
|
|
4
|
-
* Validates tool definitions, ToolRegistry, permission tiers, and schema
|
|
5
|
-
* structures for both standard and DevOps tools.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, test, it, expect, beforeEach } from 'vitest';
|
|
9
|
-
import { z } from 'zod';
|
|
10
|
-
import {
|
|
11
|
-
ToolRegistry,
|
|
12
|
-
PERMISSION_TIER_ORDER,
|
|
13
|
-
permissionTierIndex,
|
|
14
|
-
type ToolDefinition,
|
|
15
|
-
} from '../tools/schemas/types';
|
|
16
|
-
import { standardTools } from '../tools/schemas/standard';
|
|
17
|
-
import { devopsTools, formatKubectlPodsOutput, formatHelmListOutput } from '../tools/schemas/devops';
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Helpers
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
/** Create a minimal valid ToolDefinition for testing. */
|
|
24
|
-
function makeTool(name: string, overrides?: Partial<ToolDefinition>): ToolDefinition {
|
|
25
|
-
return {
|
|
26
|
-
name,
|
|
27
|
-
description: `Test tool: ${name}`,
|
|
28
|
-
inputSchema: z.object({}),
|
|
29
|
-
execute: async () => ({ output: 'ok', isError: false }),
|
|
30
|
-
permissionTier: 'auto_allow',
|
|
31
|
-
category: 'standard',
|
|
32
|
-
...overrides,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ===========================================================================
|
|
37
|
-
// ToolRegistry
|
|
38
|
-
// ===========================================================================
|
|
39
|
-
|
|
40
|
-
describe('ToolRegistry', () => {
|
|
41
|
-
let registry: ToolRegistry;
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
registry = new ToolRegistry();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('register and get a tool', () => {
|
|
48
|
-
const tool = makeTool('alpha');
|
|
49
|
-
registry.register(tool);
|
|
50
|
-
expect(registry.get('alpha')).toBe(tool);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('get returns undefined for unknown tool', () => {
|
|
54
|
-
expect(registry.get('nonexistent')).toBeUndefined();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('getAll returns all registered tools', () => {
|
|
58
|
-
registry.register(makeTool('a'));
|
|
59
|
-
registry.register(makeTool('b'));
|
|
60
|
-
registry.register(makeTool('c'));
|
|
61
|
-
const all = registry.getAll();
|
|
62
|
-
expect(all).toHaveLength(3);
|
|
63
|
-
expect(all.map(t => t.name)).toEqual(['a', 'b', 'c']);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('getByCategory filters correctly', () => {
|
|
67
|
-
registry.register(makeTool('s1', { category: 'standard' }));
|
|
68
|
-
registry.register(makeTool('d1', { category: 'devops' }));
|
|
69
|
-
registry.register(makeTool('s2', { category: 'standard' }));
|
|
70
|
-
expect(registry.getByCategory('standard')).toHaveLength(2);
|
|
71
|
-
expect(registry.getByCategory('devops')).toHaveLength(1);
|
|
72
|
-
expect(registry.getByCategory('mcp')).toHaveLength(0);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('getByPermissionTier filters correctly', () => {
|
|
76
|
-
registry.register(makeTool('t1', { permissionTier: 'auto_allow' }));
|
|
77
|
-
registry.register(makeTool('t2', { permissionTier: 'ask_once' }));
|
|
78
|
-
registry.register(makeTool('t3', { permissionTier: 'auto_allow' }));
|
|
79
|
-
expect(registry.getByPermissionTier('auto_allow')).toHaveLength(2);
|
|
80
|
-
expect(registry.getByPermissionTier('ask_once')).toHaveLength(1);
|
|
81
|
-
expect(registry.getByPermissionTier('always_ask')).toHaveLength(0);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('getNames returns tool names in insertion order', () => {
|
|
85
|
-
registry.register(makeTool('z'));
|
|
86
|
-
registry.register(makeTool('a'));
|
|
87
|
-
registry.register(makeTool('m'));
|
|
88
|
-
expect(registry.getNames()).toEqual(['z', 'a', 'm']);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('unregister removes a tool and returns true', () => {
|
|
92
|
-
registry.register(makeTool('x'));
|
|
93
|
-
expect(registry.unregister('x')).toBe(true);
|
|
94
|
-
expect(registry.get('x')).toBeUndefined();
|
|
95
|
-
expect(registry.size).toBe(0);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('unregister returns false for non-existent tool', () => {
|
|
99
|
-
expect(registry.unregister('nope')).toBe(false);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('clear removes all tools', () => {
|
|
103
|
-
registry.register(makeTool('a'));
|
|
104
|
-
registry.register(makeTool('b'));
|
|
105
|
-
registry.clear();
|
|
106
|
-
expect(registry.size).toBe(0);
|
|
107
|
-
expect(registry.getAll()).toEqual([]);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('size reflects the number of registered tools', () => {
|
|
111
|
-
expect(registry.size).toBe(0);
|
|
112
|
-
registry.register(makeTool('one'));
|
|
113
|
-
expect(registry.size).toBe(1);
|
|
114
|
-
registry.register(makeTool('two'));
|
|
115
|
-
expect(registry.size).toBe(2);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('throws on duplicate registration', () => {
|
|
119
|
-
registry.register(makeTool('dup'));
|
|
120
|
-
expect(() => registry.register(makeTool('dup'))).toThrow(/already registered/);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// ===========================================================================
|
|
125
|
-
// PERMISSION_TIER_ORDER & permissionTierIndex
|
|
126
|
-
// ===========================================================================
|
|
127
|
-
|
|
128
|
-
describe('PERMISSION_TIER_ORDER', () => {
|
|
129
|
-
test('is ordered from least to most restrictive', () => {
|
|
130
|
-
expect(PERMISSION_TIER_ORDER).toEqual(['auto_allow', 'ask_once', 'always_ask', 'blocked']);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('has exactly 4 tiers', () => {
|
|
134
|
-
expect(PERMISSION_TIER_ORDER).toHaveLength(4);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('permissionTierIndex', () => {
|
|
139
|
-
test('returns 0 for auto_allow', () => {
|
|
140
|
-
expect(permissionTierIndex('auto_allow')).toBe(0);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test('returns 1 for ask_once', () => {
|
|
144
|
-
expect(permissionTierIndex('ask_once')).toBe(1);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('returns 2 for always_ask', () => {
|
|
148
|
-
expect(permissionTierIndex('always_ask')).toBe(2);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test('returns 3 for blocked', () => {
|
|
152
|
-
expect(permissionTierIndex('blocked')).toBe(3);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('indices are strictly ascending', () => {
|
|
156
|
-
const indices = PERMISSION_TIER_ORDER.map(permissionTierIndex);
|
|
157
|
-
for (let i = 1; i < indices.length; i++) {
|
|
158
|
-
expect(indices[i]).toBeGreaterThan(indices[i - 1]);
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// ===========================================================================
|
|
164
|
-
// Standard Tool Schema Validation
|
|
165
|
-
// ===========================================================================
|
|
166
|
-
|
|
167
|
-
describe('Standard tool schemas', () => {
|
|
168
|
-
/** Helper to find a standard tool by name. */
|
|
169
|
-
function findStandard(name: string): ToolDefinition {
|
|
170
|
-
const t = standardTools.find(t => t.name === name);
|
|
171
|
-
if (!t) {
|
|
172
|
-
throw new Error(`Standard tool '${name}' not found`);
|
|
173
|
-
}
|
|
174
|
-
return t;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
test('read_file: path is required, offset/limit are optional numbers', () => {
|
|
178
|
-
const schema = findStandard('read_file').inputSchema;
|
|
179
|
-
// Valid with just path
|
|
180
|
-
expect(() => schema.parse({ path: '/tmp/f.txt' })).not.toThrow();
|
|
181
|
-
// Valid with offset and limit
|
|
182
|
-
expect(() => schema.parse({ path: '/tmp/f.txt', offset: 5, limit: 10 })).not.toThrow();
|
|
183
|
-
// Missing path -> error
|
|
184
|
-
expect(() => schema.parse({})).toThrow();
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('edit_file: path, old_string, new_string are all required', () => {
|
|
188
|
-
const schema = findStandard('edit_file').inputSchema;
|
|
189
|
-
expect(() => schema.parse({ path: 'f', old_string: 'a', new_string: 'b' })).not.toThrow();
|
|
190
|
-
expect(() => schema.parse({ path: 'f', old_string: 'a' })).toThrow();
|
|
191
|
-
expect(() => schema.parse({ path: 'f' })).toThrow();
|
|
192
|
-
expect(() => schema.parse({})).toThrow();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test('multi_edit: edits array structure is validated', () => {
|
|
196
|
-
const schema = findStandard('multi_edit').inputSchema;
|
|
197
|
-
expect(() =>
|
|
198
|
-
schema.parse({
|
|
199
|
-
path: 'f',
|
|
200
|
-
edits: [{ old_string: 'a', new_string: 'b' }],
|
|
201
|
-
})
|
|
202
|
-
).not.toThrow();
|
|
203
|
-
// edits missing -> error
|
|
204
|
-
expect(() => schema.parse({ path: 'f' })).toThrow();
|
|
205
|
-
// empty edits array is valid
|
|
206
|
-
expect(() => schema.parse({ path: 'f', edits: [] })).not.toThrow();
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('write_file: path and content are required', () => {
|
|
210
|
-
const schema = findStandard('write_file').inputSchema;
|
|
211
|
-
expect(() => schema.parse({ path: '/tmp/x', content: 'hello' })).not.toThrow();
|
|
212
|
-
expect(() => schema.parse({ path: '/tmp/x' })).toThrow();
|
|
213
|
-
expect(() => schema.parse({ content: 'hello' })).toThrow();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test('bash: command is required, timeout defaults to 120000', () => {
|
|
217
|
-
const schema = findStandard('bash').inputSchema;
|
|
218
|
-
const result = schema.parse({ command: 'echo hi' }) as { command: string; timeout: number };
|
|
219
|
-
expect(result.command).toBe('echo hi');
|
|
220
|
-
expect(result.timeout).toBe(120_000);
|
|
221
|
-
expect(() => schema.parse({})).toThrow();
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
test('glob: pattern is required', () => {
|
|
225
|
-
const schema = findStandard('glob').inputSchema;
|
|
226
|
-
expect(() => schema.parse({ pattern: '**/*.ts' })).not.toThrow();
|
|
227
|
-
expect(() => schema.parse({})).toThrow();
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test('grep: pattern is required', () => {
|
|
231
|
-
const schema = findStandard('grep').inputSchema;
|
|
232
|
-
expect(() => schema.parse({ pattern: 'TODO' })).not.toThrow();
|
|
233
|
-
expect(() => schema.parse({})).toThrow();
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test('list_dir: path is required', () => {
|
|
237
|
-
const schema = findStandard('list_dir').inputSchema;
|
|
238
|
-
expect(() => schema.parse({ path: '/tmp' })).not.toThrow();
|
|
239
|
-
expect(() => schema.parse({})).toThrow();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('webfetch: url is required and must be valid URL', () => {
|
|
243
|
-
const schema = findStandard('webfetch').inputSchema;
|
|
244
|
-
expect(() => schema.parse({ url: 'https://example.com' })).not.toThrow();
|
|
245
|
-
expect(() => schema.parse({ url: 'not-a-url' })).toThrow();
|
|
246
|
-
expect(() => schema.parse({})).toThrow();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('todo_read: accepts empty input', () => {
|
|
250
|
-
const schema = findStandard('todo_read').inputSchema;
|
|
251
|
-
expect(() => schema.parse({})).not.toThrow();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test('todo_write: validates tasks array with subject and status enum', () => {
|
|
255
|
-
const schema = findStandard('todo_write').inputSchema;
|
|
256
|
-
expect(() =>
|
|
257
|
-
schema.parse({
|
|
258
|
-
tasks: [{ subject: 'Fix bug', status: 'pending' }],
|
|
259
|
-
})
|
|
260
|
-
).not.toThrow();
|
|
261
|
-
// Invalid status
|
|
262
|
-
expect(() =>
|
|
263
|
-
schema.parse({
|
|
264
|
-
tasks: [{ subject: 'X', status: 'invalid_status' }],
|
|
265
|
-
})
|
|
266
|
-
).toThrow();
|
|
267
|
-
// Missing tasks
|
|
268
|
-
expect(() => schema.parse({})).toThrow();
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// ===========================================================================
|
|
273
|
-
// DevOps Tool Schema Validation
|
|
274
|
-
// ===========================================================================
|
|
275
|
-
|
|
276
|
-
describe('DevOps tool schemas', () => {
|
|
277
|
-
/** Helper to find a devops tool by name. */
|
|
278
|
-
function findDevops(name: string): ToolDefinition {
|
|
279
|
-
const t = devopsTools.find(t => t.name === name);
|
|
280
|
-
if (!t) {
|
|
281
|
-
throw new Error(`DevOps tool '${name}' not found`);
|
|
282
|
-
}
|
|
283
|
-
return t;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
test('terraform: action enum and workdir are required', () => {
|
|
287
|
-
const schema = findDevops('terraform').inputSchema;
|
|
288
|
-
expect(() => schema.parse({ action: 'plan', workdir: '/infra' })).not.toThrow();
|
|
289
|
-
expect(() => schema.parse({ action: 'invalid', workdir: '/infra' })).toThrow();
|
|
290
|
-
expect(() => schema.parse({ action: 'plan' })).toThrow();
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
test('kubectl: action enum is validated', () => {
|
|
294
|
-
const schema = findDevops('kubectl').inputSchema;
|
|
295
|
-
expect(() => schema.parse({ action: 'get' })).not.toThrow();
|
|
296
|
-
expect(() => schema.parse({ action: 'invalid_action' })).toThrow();
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test('helm: action enum is validated', () => {
|
|
300
|
-
const schema = findDevops('helm').inputSchema;
|
|
301
|
-
expect(() => schema.parse({ action: 'list' })).not.toThrow();
|
|
302
|
-
expect(() => schema.parse({ action: 'invalid_action' })).toThrow();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test('cloud_discover: provider enum and resource_type are required', () => {
|
|
306
|
-
const schema = findDevops('cloud_discover').inputSchema;
|
|
307
|
-
expect(() => schema.parse({ provider: 'aws', resource_type: 'ec2' })).not.toThrow();
|
|
308
|
-
expect(() => schema.parse({ provider: 'invalid', resource_type: 'ec2' })).toThrow();
|
|
309
|
-
expect(() => schema.parse({ provider: 'aws' })).toThrow();
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
test('cost_estimate: plan_file and workdir are both optional', () => {
|
|
313
|
-
const schema = findDevops('cost_estimate').inputSchema;
|
|
314
|
-
expect(() => schema.parse({})).not.toThrow();
|
|
315
|
-
expect(() => schema.parse({ plan_file: '/plan.tfplan' })).not.toThrow();
|
|
316
|
-
expect(() => schema.parse({ workdir: '/infra' })).not.toThrow();
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
test('drift_detect: workdir is required', () => {
|
|
320
|
-
const schema = findDevops('drift_detect').inputSchema;
|
|
321
|
-
expect(() => schema.parse({ workdir: '/infra' })).not.toThrow();
|
|
322
|
-
expect(() => schema.parse({})).toThrow();
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test('deploy_preview: action and workdir are required', () => {
|
|
326
|
-
const schema = findDevops('deploy_preview').inputSchema;
|
|
327
|
-
expect(() => schema.parse({ action: 'terraform apply', workdir: '/infra' })).not.toThrow();
|
|
328
|
-
expect(() => schema.parse({ action: 'terraform apply' })).toThrow();
|
|
329
|
-
expect(() => schema.parse({ workdir: '/infra' })).toThrow();
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
test('git: action enum is validated', () => {
|
|
333
|
-
const schema = findDevops('git').inputSchema;
|
|
334
|
-
expect(() => schema.parse({ action: 'status' })).not.toThrow();
|
|
335
|
-
expect(() => schema.parse({ action: 'bad_action' })).toThrow();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
test('task: prompt is required, agent enum is optional', () => {
|
|
339
|
-
const schema = findDevops('task').inputSchema;
|
|
340
|
-
expect(() => schema.parse({ prompt: 'do something' })).not.toThrow();
|
|
341
|
-
expect(() => schema.parse({ prompt: 'x', agent: 'explore' })).not.toThrow();
|
|
342
|
-
expect(() => schema.parse({ prompt: 'x', agent: 'invalid' })).toThrow();
|
|
343
|
-
expect(() => schema.parse({})).toThrow();
|
|
344
|
-
});
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// ===========================================================================
|
|
348
|
-
// Tool Counts and Metadata
|
|
349
|
-
// ===========================================================================
|
|
350
|
-
|
|
351
|
-
describe('Tool counts and metadata', () => {
|
|
352
|
-
test('standardTools has exactly 12 tools', () => {
|
|
353
|
-
expect(standardTools).toHaveLength(12);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
test('devopsTools has exactly 28 tools', () => {
|
|
357
|
-
expect(devopsTools).toHaveLength(28);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
test('all standard tools have category "standard"', () => {
|
|
361
|
-
for (const tool of standardTools) {
|
|
362
|
-
expect(tool.category).toBe('standard');
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
test('all devops tools have category "devops"', () => {
|
|
367
|
-
for (const tool of devopsTools) {
|
|
368
|
-
expect(tool.category).toBe('devops');
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test('all tools have valid permissionTier values', () => {
|
|
373
|
-
const validTiers = new Set<string>(PERMISSION_TIER_ORDER);
|
|
374
|
-
for (const tool of [...standardTools, ...devopsTools]) {
|
|
375
|
-
expect(validTiers.has(tool.permissionTier)).toBe(true);
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test('all tools have non-empty name and description', () => {
|
|
380
|
-
for (const tool of [...standardTools, ...devopsTools]) {
|
|
381
|
-
expect(tool.name.length).toBeGreaterThan(0);
|
|
382
|
-
expect(tool.description.length).toBeGreaterThan(0);
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
test('all tool names are unique across standard and devops', () => {
|
|
387
|
-
const allNames = [...standardTools, ...devopsTools].map(t => t.name);
|
|
388
|
-
const uniqueNames = new Set(allNames);
|
|
389
|
-
expect(uniqueNames.size).toBe(allNames.length);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test('all tools have an execute function', () => {
|
|
393
|
-
for (const tool of [...standardTools, ...devopsTools]) {
|
|
394
|
-
expect(typeof tool.execute).toBe('function');
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
// ---------------------------------------------------------------------------
|
|
400
|
-
// C2: infraContext in ToolExecuteContext
|
|
401
|
-
// ---------------------------------------------------------------------------
|
|
402
|
-
|
|
403
|
-
describe('infraContext in ToolExecuteContext (C2)', () => {
|
|
404
|
-
it('ToolExecuteContext type has infraContext field', async () => {
|
|
405
|
-
const { readFileSync } = await import('node:fs');
|
|
406
|
-
const { join } = await import('node:path');
|
|
407
|
-
const src = readFileSync(join(process.cwd(), 'src/tools/schemas/types.ts'), 'utf-8');
|
|
408
|
-
expect(src).toContain('infraContext?:');
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
it('kubectl tool uses contextFlag from infraContext', async () => {
|
|
412
|
-
const { readFileSync } = await import('node:fs');
|
|
413
|
-
const { join } = await import('node:path');
|
|
414
|
-
const src = readFileSync(join(process.cwd(), 'src/tools/schemas/devops.ts'), 'utf-8');
|
|
415
|
-
expect(src).toContain('ctx?.infraContext?.kubectlContext');
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it('terraform tool reads sessionWorkspace from infraContext', async () => {
|
|
419
|
-
const { readFileSync } = await import('node:fs');
|
|
420
|
-
const { join } = await import('node:path');
|
|
421
|
-
const src = readFileSync(join(process.cwd(), 'src/tools/schemas/devops.ts'), 'utf-8');
|
|
422
|
-
expect(src).toContain('ctx?.infraContext?.terraformWorkspace');
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
it('generateInfraTool is in devopsTools array', async () => {
|
|
426
|
-
const { devopsTools } = await import('../tools/schemas/devops');
|
|
427
|
-
const names = devopsTools.map(t => t.name);
|
|
428
|
-
expect(names).toContain('generate_infra');
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('generateInfraTool has ask_once permissionTier', async () => {
|
|
432
|
-
const { devopsTools } = await import('../tools/schemas/devops');
|
|
433
|
-
const tool = devopsTools.find(t => t.name === 'generate_infra');
|
|
434
|
-
expect(tool?.permissionTier).toBe('ask_once');
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
it('generateInfraTool schema accepts terraform/kubernetes/helm types', async () => {
|
|
438
|
-
const { devopsTools } = await import('../tools/schemas/devops');
|
|
439
|
-
const tool = devopsTools.find(t => t.name === 'generate_infra');
|
|
440
|
-
expect(tool).toBeDefined();
|
|
441
|
-
// The schema should parse valid input without throwing
|
|
442
|
-
const { z } = await import('zod');
|
|
443
|
-
// Just verify the tool exists and has a schema
|
|
444
|
-
expect(tool?.inputSchema).toBeDefined();
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
// ---------------------------------------------------------------------------
|
|
449
|
-
// M2: Docker build streaming
|
|
450
|
-
// ---------------------------------------------------------------------------
|
|
451
|
-
|
|
452
|
-
describe('docker build streaming (M2)', () => {
|
|
453
|
-
it('dockerTool execute accepts ctx parameter', async () => {
|
|
454
|
-
const { readFileSync } = await import('node:fs');
|
|
455
|
-
const { join } = await import('node:path');
|
|
456
|
-
const src = readFileSync(join(process.cwd(), 'src/tools/schemas/devops.ts'), 'utf-8');
|
|
457
|
-
expect(src).toContain("input.action === 'build' && ctx?.onProgress");
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
it('docker build output filter keeps Step N/M lines', () => {
|
|
461
|
-
// Inline the filter logic to unit test it
|
|
462
|
-
const filterDockerBuildLine = (line: string): boolean => {
|
|
463
|
-
const trimmed = line.trim();
|
|
464
|
-
if (!trimmed) return false;
|
|
465
|
-
return /^Step\s+\d+\/\d+/i.test(trimmed) ||
|
|
466
|
-
/---> Using cache/i.test(trimmed) ||
|
|
467
|
-
/Successfully built/i.test(trimmed) ||
|
|
468
|
-
/Successfully tagged/i.test(trimmed) ||
|
|
469
|
-
/error/i.test(trimmed) ||
|
|
470
|
-
/warning/i.test(trimmed);
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
expect(filterDockerBuildLine('Step 3/12: RUN npm install')).toBe(true);
|
|
474
|
-
expect(filterDockerBuildLine(' ---> Using cache')).toBe(true);
|
|
475
|
-
expect(filterDockerBuildLine('Successfully built abc123')).toBe(true);
|
|
476
|
-
expect(filterDockerBuildLine('Successfully tagged myapp:latest')).toBe(true);
|
|
477
|
-
expect(filterDockerBuildLine(' ---> sha256:abc123def456')).toBe(false);
|
|
478
|
-
expect(filterDockerBuildLine('Removing intermediate container abc123')).toBe(false);
|
|
479
|
-
});
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
// ===========================================================================
|
|
483
|
-
// H6: kubectl pods output formatting
|
|
484
|
-
// ===========================================================================
|
|
485
|
-
|
|
486
|
-
describe('H6 — formatKubectlPodsOutput', () => {
|
|
487
|
-
const sampleOutput = [
|
|
488
|
-
'NAME READY STATUS RESTARTS AGE',
|
|
489
|
-
'web-7d4f6b9c5-abc12 1/1 Running 0 2d',
|
|
490
|
-
'worker-6b5c8d7f4-xyz99 0/1 CrashLoopBackOff 5 1h',
|
|
491
|
-
'db-5f4e3d2c1-pqr55 0/1 Pending 0 30m',
|
|
492
|
-
'init-job-abc 0/1 Init:0/1 0 5m',
|
|
493
|
-
'old-pod-done 0/1 Completed 0 7d',
|
|
494
|
-
'failing-app-xyz 0/1 Error 3 2h',
|
|
495
|
-
'evicted-pod 0/1 Evicted 0 1d',
|
|
496
|
-
].join('\n');
|
|
497
|
-
|
|
498
|
-
it('prefixes Running pods with [OK]', () => {
|
|
499
|
-
const result = formatKubectlPodsOutput(sampleOutput);
|
|
500
|
-
expect(result).toContain('[OK]');
|
|
501
|
-
const lines = result.split('\n');
|
|
502
|
-
const runningLine = lines.find(l => l.includes('Running'));
|
|
503
|
-
expect(runningLine).toMatch(/^\[OK\]/);
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
it('prefixes CrashLoopBackOff pods with [XX]', () => {
|
|
507
|
-
const result = formatKubectlPodsOutput(sampleOutput);
|
|
508
|
-
const lines = result.split('\n');
|
|
509
|
-
const crashLine = lines.find(l => l.includes('CrashLoopBackOff'));
|
|
510
|
-
expect(crashLine).toMatch(/^\[XX\]/);
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
it('prefixes Pending pods with [!!]', () => {
|
|
514
|
-
const result = formatKubectlPodsOutput(sampleOutput);
|
|
515
|
-
const lines = result.split('\n');
|
|
516
|
-
const pendingLine = lines.find(l => l.includes('Pending'));
|
|
517
|
-
expect(pendingLine).toMatch(/^\[!!\]/);
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
it('prefixes Error pods with [XX]', () => {
|
|
521
|
-
const result = formatKubectlPodsOutput(sampleOutput);
|
|
522
|
-
const lines = result.split('\n');
|
|
523
|
-
const errorLine = lines.find(l => l.includes('Error') && !l.includes('CrashLoop'));
|
|
524
|
-
expect(errorLine).toMatch(/^\[XX\]/);
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
it('prefixes Completed pods with [OK]', () => {
|
|
528
|
-
const result = formatKubectlPodsOutput(sampleOutput);
|
|
529
|
-
const lines = result.split('\n');
|
|
530
|
-
const completedLine = lines.find(l => l.includes('Completed'));
|
|
531
|
-
expect(completedLine).toMatch(/^\[OK\]/);
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
it('preserves header line without emoji prefix', () => {
|
|
535
|
-
const result = formatKubectlPodsOutput(sampleOutput);
|
|
536
|
-
const headerLine = result.split('\n')[0];
|
|
537
|
-
expect(headerLine).toBe('NAME READY STATUS RESTARTS AGE');
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
it('returns original line for empty input', () => {
|
|
541
|
-
const result = formatKubectlPodsOutput('');
|
|
542
|
-
expect(result).toBe('');
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
it('handles single Running pod', () => {
|
|
546
|
-
const input = 'NAME READY STATUS RESTARTS AGE\nmypod 1/1 Running 0 1m';
|
|
547
|
-
const result = formatKubectlPodsOutput(input);
|
|
548
|
-
const lines = result.split('\n');
|
|
549
|
-
expect(lines[1]).toMatch(/^\[OK\]/);
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
// ===========================================================================
|
|
554
|
-
// H6: helm list output formatting
|
|
555
|
-
// ===========================================================================
|
|
556
|
-
|
|
557
|
-
describe('H6 — formatHelmListOutput', () => {
|
|
558
|
-
const sampleJson = JSON.stringify([
|
|
559
|
-
{ name: 'nginx', namespace: 'default', revision: '3', status: 'deployed', chart: 'nginx-1.2.0', app_version: '1.21', updated: '2024-01-01' },
|
|
560
|
-
{ name: 'redis', namespace: 'cache', revision: '1', status: 'failed', chart: 'redis-7.0.0', app_version: '7.0', updated: '2024-01-02' },
|
|
561
|
-
{ name: 'postgres', namespace: 'db', revision: '2', status: 'pending-upgrade', chart: 'postgresql-12.1.0', app_version: '15', updated: '2024-01-03' },
|
|
562
|
-
]);
|
|
563
|
-
|
|
564
|
-
it('prefixes deployed releases with [OK]', () => {
|
|
565
|
-
const result = formatHelmListOutput(sampleJson);
|
|
566
|
-
expect(result).toContain('[OK]');
|
|
567
|
-
const lines = result.split('\n');
|
|
568
|
-
const deployedLine = lines.find(l => l.includes('deployed'));
|
|
569
|
-
expect(deployedLine).toMatch(/^\[OK\]/);
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
it('prefixes failed releases with [XX]', () => {
|
|
573
|
-
const result = formatHelmListOutput(sampleJson);
|
|
574
|
-
const lines = result.split('\n');
|
|
575
|
-
const failedLine = lines.find(l => l.includes('failed'));
|
|
576
|
-
expect(failedLine).toMatch(/^\[XX\]/);
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
it('prefixes pending releases with [!!]', () => {
|
|
580
|
-
const result = formatHelmListOutput(sampleJson);
|
|
581
|
-
const lines = result.split('\n');
|
|
582
|
-
const pendingLine = lines.find(l => l.includes('pending'));
|
|
583
|
-
expect(pendingLine).toMatch(/^\[!!\]/);
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
it('returns "No Helm releases found." for empty array', () => {
|
|
587
|
-
const result = formatHelmListOutput('[]');
|
|
588
|
-
expect(result).toBe('No Helm releases found.');
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
it('falls back to raw string for invalid JSON', () => {
|
|
592
|
-
const result = formatHelmListOutput('not json');
|
|
593
|
-
expect(result).toBe('not json');
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
it('includes release name and namespace in output', () => {
|
|
597
|
-
const result = formatHelmListOutput(sampleJson);
|
|
598
|
-
expect(result).toContain('nginx');
|
|
599
|
-
expect(result).toContain('default');
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
|