@build-astron-co/nimbus 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/nimbus +26 -10
- package/bin/nimbus.cmd +41 -0
- package/bin/nimbus.mjs +70 -0
- package/completions/nimbus.bash +38 -0
- package/completions/nimbus.fish +48 -0
- package/completions/nimbus.zsh +81 -0
- package/dist/src/agent/compaction-agent.js +215 -0
- package/dist/src/agent/context-manager.js +385 -0
- package/dist/src/agent/context.js +322 -0
- package/dist/src/agent/deploy-preview.js +395 -0
- package/dist/src/agent/expand-files.js +95 -0
- package/dist/src/agent/index.js +18 -0
- package/dist/src/agent/loop.js +1535 -0
- package/dist/src/agent/modes.js +347 -0
- package/dist/src/agent/permissions.js +396 -0
- package/dist/src/agent/subagents/base.js +67 -0
- package/dist/src/agent/subagents/cost.js +45 -0
- package/dist/src/agent/subagents/explore.js +36 -0
- package/dist/src/agent/subagents/general.js +41 -0
- package/dist/src/agent/subagents/index.js +88 -0
- package/dist/src/agent/subagents/infra.js +52 -0
- package/dist/src/agent/subagents/security.js +60 -0
- package/dist/src/agent/system-prompt.js +860 -0
- package/dist/src/app.js +152 -0
- package/dist/src/audit/activity-log.js +209 -0
- package/dist/src/audit/compliance-checker.js +419 -0
- package/dist/src/audit/cost-tracker.js +231 -0
- package/dist/src/audit/index.js +10 -0
- package/dist/src/audit/security-scanner.js +490 -0
- package/dist/src/auth/guard.js +64 -0
- package/dist/src/auth/index.js +19 -0
- package/dist/src/auth/keychain.js +79 -0
- package/dist/src/auth/oauth.js +389 -0
- package/dist/src/auth/providers.js +415 -0
- package/dist/src/auth/sso.js +87 -0
- package/dist/src/auth/store.js +424 -0
- package/dist/src/auth/types.js +5 -0
- package/dist/src/cli/index.js +8 -0
- package/dist/src/cli/init.js +1048 -0
- package/dist/src/cli/openapi-spec.js +346 -0
- package/dist/src/cli/run.js +505 -0
- package/dist/src/cli/serve-auth.js +56 -0
- package/dist/src/cli/serve.js +432 -0
- package/dist/src/cli/web.js +50 -0
- package/dist/src/cli.js +1574 -0
- package/dist/src/clients/core-engine-client.js +156 -0
- package/dist/src/clients/enterprise-client.js +246 -0
- package/dist/src/clients/generator-client.js +219 -0
- package/dist/src/clients/git-client.js +367 -0
- package/dist/src/clients/github-client.js +229 -0
- package/dist/src/clients/helm-client.js +299 -0
- package/dist/src/clients/index.js +18 -0
- package/dist/src/clients/k8s-client.js +270 -0
- package/dist/src/clients/llm-client.js +119 -0
- package/dist/src/clients/rest-client.js +104 -0
- package/dist/src/clients/service-discovery.js +35 -0
- package/dist/src/clients/terraform-client.js +302 -0
- package/dist/src/clients/tools-client.js +1227 -0
- package/dist/src/clients/ws-client.js +93 -0
- package/dist/src/commands/alias.js +91 -0
- package/dist/src/commands/analyze/index.js +313 -0
- package/dist/src/commands/apply/helm.js +375 -0
- package/dist/src/commands/apply/index.js +176 -0
- package/dist/src/commands/apply/k8s.js +350 -0
- package/dist/src/commands/apply/terraform.js +465 -0
- package/dist/src/commands/ask.js +137 -0
- package/dist/src/commands/audit/index.js +322 -0
- package/dist/src/commands/auth-cloud.js +345 -0
- package/dist/src/commands/auth-list.js +112 -0
- package/dist/src/commands/auth-profile.js +104 -0
- package/dist/src/commands/auth-refresh.js +161 -0
- package/dist/src/commands/auth-status.js +122 -0
- package/dist/src/commands/aws/ec2.js +402 -0
- package/dist/src/commands/aws/iam.js +304 -0
- package/dist/src/commands/aws/index.js +108 -0
- package/dist/src/commands/aws/lambda.js +317 -0
- package/dist/src/commands/aws/rds.js +345 -0
- package/dist/src/commands/aws/s3.js +346 -0
- package/dist/src/commands/aws/vpc.js +302 -0
- package/dist/src/commands/aws-discover.js +413 -0
- package/dist/src/commands/aws-terraform.js +618 -0
- package/dist/src/commands/azure/aks.js +305 -0
- package/dist/src/commands/azure/functions.js +200 -0
- package/dist/src/commands/azure/index.js +93 -0
- package/dist/src/commands/azure/storage.js +378 -0
- package/dist/src/commands/azure/vm.js +291 -0
- package/dist/src/commands/billing/index.js +224 -0
- package/dist/src/commands/chat.js +259 -0
- package/dist/src/commands/completions.js +255 -0
- package/dist/src/commands/config.js +291 -0
- package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
- package/dist/src/commands/cost/estimator.js +73 -0
- package/dist/src/commands/cost/index.js +625 -0
- package/dist/src/commands/cost/parsers/terraform.js +234 -0
- package/dist/src/commands/cost/parsers/types.js +4 -0
- package/dist/src/commands/cost/pricing/aws.js +501 -0
- package/dist/src/commands/cost/pricing/azure.js +462 -0
- package/dist/src/commands/cost/pricing/gcp.js +359 -0
- package/dist/src/commands/cost/pricing/index.js +24 -0
- package/dist/src/commands/demo.js +196 -0
- package/dist/src/commands/deploy.js +215 -0
- package/dist/src/commands/doctor.js +1291 -0
- package/dist/src/commands/drift/index.js +674 -0
- package/dist/src/commands/explain.js +235 -0
- package/dist/src/commands/export.js +120 -0
- package/dist/src/commands/feedback.js +319 -0
- package/dist/src/commands/fix.js +263 -0
- package/dist/src/commands/fs/index.js +338 -0
- package/dist/src/commands/gcp/compute.js +266 -0
- package/dist/src/commands/gcp/functions.js +221 -0
- package/dist/src/commands/gcp/gke.js +357 -0
- package/dist/src/commands/gcp/iam.js +295 -0
- package/dist/src/commands/gcp/index.js +105 -0
- package/dist/src/commands/gcp/storage.js +232 -0
- package/dist/src/commands/generate-helm.js +1026 -0
- package/dist/src/commands/generate-k8s.js +1263 -0
- package/dist/src/commands/generate-terraform.js +1058 -0
- package/dist/src/commands/gh/index.js +663 -0
- package/dist/src/commands/git/index.js +1208 -0
- package/dist/src/commands/helm/index.js +985 -0
- package/dist/src/commands/help.js +639 -0
- package/dist/src/commands/history.js +120 -0
- package/dist/src/commands/import.js +782 -0
- package/dist/src/commands/incident.js +144 -0
- package/dist/src/commands/index.js +109 -0
- package/dist/src/commands/init.js +955 -0
- package/dist/src/commands/k8s/index.js +979 -0
- package/dist/src/commands/login.js +588 -0
- package/dist/src/commands/logout.js +61 -0
- package/dist/src/commands/logs.js +160 -0
- package/dist/src/commands/onboarding.js +382 -0
- package/dist/src/commands/pipeline.js +153 -0
- package/dist/src/commands/plan/display.js +216 -0
- package/dist/src/commands/plan/index.js +525 -0
- package/dist/src/commands/plugin.js +325 -0
- package/dist/src/commands/preview.js +356 -0
- package/dist/src/commands/profile.js +297 -0
- package/dist/src/commands/questionnaire.js +1021 -0
- package/dist/src/commands/resume.js +35 -0
- package/dist/src/commands/rollback.js +259 -0
- package/dist/src/commands/rollout.js +74 -0
- package/dist/src/commands/runbook.js +307 -0
- package/dist/src/commands/schedule.js +202 -0
- package/dist/src/commands/status.js +213 -0
- package/dist/src/commands/team/index.js +309 -0
- package/dist/src/commands/team-context.js +200 -0
- package/dist/src/commands/template.js +204 -0
- package/dist/src/commands/tf/index.js +989 -0
- package/dist/src/commands/upgrade.js +515 -0
- package/dist/src/commands/usage/index.js +118 -0
- package/dist/src/commands/version.js +145 -0
- package/dist/src/commands/watch.js +127 -0
- package/dist/src/compat/index.js +2 -0
- package/dist/src/compat/runtime.js +10 -0
- package/dist/src/compat/sqlite.js +144 -0
- package/dist/src/config/index.js +6 -0
- package/dist/src/config/manager.js +469 -0
- package/dist/src/config/mode-store.js +57 -0
- package/dist/src/config/profiles.js +66 -0
- package/dist/src/config/safety-policy.js +251 -0
- package/dist/src/config/schema.js +107 -0
- package/dist/src/config/types.js +311 -0
- package/dist/src/config/workspace-state.js +38 -0
- package/dist/src/context/context-db.js +138 -0
- package/dist/src/demo/index.js +295 -0
- package/dist/src/demo/scenarios/full-journey.js +226 -0
- package/dist/src/demo/scenarios/getting-started.js +124 -0
- package/dist/src/demo/scenarios/helm-release.js +334 -0
- package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
- package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
- package/dist/src/demo/types.js +6 -0
- package/dist/src/engine/cost-estimator.js +334 -0
- package/dist/src/engine/diagram-generator.js +192 -0
- package/dist/src/engine/drift-detector.js +688 -0
- package/dist/src/engine/executor.js +832 -0
- package/dist/src/engine/index.js +39 -0
- package/dist/src/engine/orchestrator.js +436 -0
- package/dist/src/engine/planner.js +616 -0
- package/dist/src/engine/safety.js +609 -0
- package/dist/src/engine/verifier.js +664 -0
- package/dist/src/enterprise/audit.js +241 -0
- package/dist/src/enterprise/auth.js +189 -0
- package/dist/src/enterprise/billing.js +512 -0
- package/dist/src/enterprise/index.js +16 -0
- package/dist/src/enterprise/teams.js +315 -0
- package/dist/src/generator/best-practices.js +1375 -0
- package/dist/src/generator/helm.js +495 -0
- package/dist/src/generator/index.js +11 -0
- package/dist/src/generator/intent-parser.js +420 -0
- package/dist/src/generator/kubernetes.js +773 -0
- package/dist/src/generator/terraform.js +1472 -0
- package/dist/src/history/index.js +6 -0
- package/dist/src/history/manager.js +199 -0
- package/dist/src/history/types.js +6 -0
- package/dist/src/hooks/config.js +318 -0
- package/dist/src/hooks/engine.js +317 -0
- package/dist/src/hooks/index.js +2 -0
- package/dist/src/llm/auth-bridge.js +157 -0
- package/dist/src/llm/circuit-breaker.js +116 -0
- package/dist/src/llm/config-loader.js +172 -0
- package/dist/src/llm/cost-calculator.js +137 -0
- package/dist/src/llm/index.js +7 -0
- package/dist/src/llm/model-aliases.js +99 -0
- package/dist/src/llm/provider-registry.js +57 -0
- package/dist/src/llm/providers/anthropic.js +430 -0
- package/dist/src/llm/providers/bedrock.js +409 -0
- package/dist/src/llm/providers/google.js +344 -0
- package/dist/src/llm/providers/ollama.js +661 -0
- package/dist/src/llm/providers/openai-compatible.js +289 -0
- package/dist/src/llm/providers/openai.js +284 -0
- package/dist/src/llm/providers/openrouter.js +293 -0
- package/dist/src/llm/router.js +844 -0
- package/dist/src/llm/types.js +69 -0
- package/dist/src/lsp/client.js +239 -0
- package/dist/src/lsp/languages.js +95 -0
- package/dist/src/lsp/manager.js +243 -0
- package/dist/src/mcp/client.js +289 -0
- package/dist/src/mcp/index.js +5 -0
- package/dist/src/mcp/manager.js +113 -0
- package/dist/src/nimbus.js +212 -0
- package/dist/src/plugins/index.js +13 -0
- package/dist/src/plugins/loader.js +280 -0
- package/dist/src/plugins/manager.js +282 -0
- package/dist/src/plugins/types.js +23 -0
- package/dist/src/scanners/cicd-scanner.js +230 -0
- package/dist/src/scanners/cloud-scanner.js +415 -0
- package/dist/src/scanners/framework-scanner.js +430 -0
- package/dist/src/scanners/iac-scanner.js +350 -0
- package/dist/src/scanners/index.js +454 -0
- package/dist/src/scanners/language-scanner.js +258 -0
- package/dist/src/scanners/package-manager-scanner.js +252 -0
- package/dist/src/scanners/types.js +6 -0
- package/dist/src/sessions/manager.js +395 -0
- package/dist/src/sessions/types.js +4 -0
- package/dist/src/sharing/sync.js +238 -0
- package/dist/src/sharing/viewer.js +131 -0
- package/dist/src/snapshots/index.js +1 -0
- package/dist/src/snapshots/manager.js +432 -0
- package/dist/src/state/artifacts.js +94 -0
- package/dist/src/state/audit.js +73 -0
- package/dist/src/state/billing.js +126 -0
- package/dist/src/state/checkpoints.js +81 -0
- package/dist/src/state/config.js +58 -0
- package/dist/src/state/conversations.js +7 -0
- package/dist/src/state/credentials.js +96 -0
- package/dist/src/state/db.js +53 -0
- package/dist/src/state/index.js +23 -0
- package/dist/src/state/messages.js +76 -0
- package/dist/src/state/projects.js +92 -0
- package/dist/src/state/schema.js +233 -0
- package/dist/src/state/sessions.js +79 -0
- package/dist/src/state/teams.js +131 -0
- package/dist/src/telemetry.js +91 -0
- package/dist/src/tools/aws-ops.js +747 -0
- package/dist/src/tools/azure-ops.js +491 -0
- package/dist/src/tools/file-ops.js +451 -0
- package/dist/src/tools/gcp-ops.js +559 -0
- package/dist/src/tools/git-ops.js +557 -0
- package/dist/src/tools/github-ops.js +460 -0
- package/dist/src/tools/helm-ops.js +634 -0
- package/dist/src/tools/index.js +16 -0
- package/dist/src/tools/k8s-ops.js +579 -0
- package/dist/src/tools/schemas/converter.js +129 -0
- package/dist/src/tools/schemas/devops.js +3319 -0
- package/dist/src/tools/schemas/index.js +19 -0
- package/dist/src/tools/schemas/standard.js +966 -0
- package/dist/src/tools/schemas/types.js +409 -0
- package/dist/src/tools/spawn-exec.js +109 -0
- package/dist/src/tools/terraform-ops.js +627 -0
- package/dist/src/types/config.js +1 -0
- package/dist/src/types/drift.js +4 -0
- package/dist/src/types/enterprise.js +5 -0
- package/dist/src/types/index.js +14 -0
- package/dist/src/types/plan.js +1 -0
- package/dist/src/types/request.js +1 -0
- package/dist/src/types/response.js +1 -0
- package/dist/src/types/service.js +1 -0
- package/dist/src/ui/App.js +1672 -0
- package/dist/src/ui/DeployPreview.js +60 -0
- package/dist/src/ui/FileDiffModal.js +108 -0
- package/dist/src/ui/Header.js +46 -0
- package/dist/src/ui/HelpModal.js +9 -0
- package/dist/src/ui/InputBox.js +408 -0
- package/dist/src/ui/MessageList.js +795 -0
- package/dist/src/ui/PermissionPrompt.js +72 -0
- package/dist/src/ui/StatusBar.js +109 -0
- package/dist/src/ui/TerminalPane.js +31 -0
- package/dist/src/ui/ToolCallDisplay.js +303 -0
- package/dist/src/ui/TreePane.js +83 -0
- package/dist/src/ui/chat-ui.js +721 -0
- package/dist/src/ui/index.js +11 -0
- package/dist/src/ui/ink/index.js +1325 -0
- package/dist/src/ui/streaming.js +137 -0
- package/dist/src/ui/theme.js +78 -0
- package/dist/src/ui/types.js +7 -0
- package/dist/src/utils/analytics.js +61 -0
- package/dist/src/utils/cost-warning.js +25 -0
- package/dist/src/utils/env.js +42 -0
- package/dist/src/utils/errors.js +54 -0
- package/dist/src/utils/event-bus.js +22 -0
- package/dist/src/utils/index.js +16 -0
- package/dist/src/utils/logger.js +150 -0
- package/dist/src/utils/rate-limiter.js +90 -0
- package/dist/src/utils/service-auth.js +36 -0
- package/dist/src/utils/validation.js +39 -0
- package/dist/src/version.js +3 -0
- package/dist/src/watcher/index.js +192 -0
- package/dist/src/wizard/approval.js +275 -0
- package/dist/src/wizard/index.js +13 -0
- package/dist/src/wizard/prompts.js +273 -0
- package/dist/src/wizard/types.js +4 -0
- package/dist/src/wizard/ui.js +453 -0
- package/dist/src/wizard/wizard.js +227 -0
- package/package.json +31 -23
- package/src/__tests__/alias.test.ts +133 -0
- package/src/__tests__/app.test.ts +1 -1
- package/src/__tests__/audit.test.ts +1 -1
- package/src/__tests__/circuit-breaker.test.ts +1 -1
- package/src/__tests__/cli-run.test.ts +237 -1
- package/src/__tests__/compat-sqlite.test.ts +68 -0
- package/src/__tests__/context-manager.test.ts +131 -1
- package/src/__tests__/context.test.ts +1 -1
- package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
- package/src/__tests__/doctor.test.ts +48 -0
- package/src/__tests__/enterprise.test.ts +1 -1
- package/src/__tests__/export.test.ts +236 -0
- package/src/__tests__/gap-11-18-20.test.ts +958 -0
- package/src/__tests__/generator.test.ts +1 -1
- package/src/__tests__/helm-streaming.test.ts +127 -0
- package/src/__tests__/hooks.test.ts +1 -1
- package/src/__tests__/incident.test.ts +179 -0
- package/src/__tests__/init.test.ts +55 -4
- package/src/__tests__/intent-parser.test.ts +1 -1
- package/src/__tests__/llm-router.test.ts +1 -1
- package/src/__tests__/logs.test.ts +107 -0
- package/src/__tests__/loop-errors.test.ts +244 -0
- package/src/__tests__/lsp.test.ts +1 -1
- package/src/__tests__/modes.test.ts +1 -1
- package/src/__tests__/perf-optimizations.test.ts +847 -0
- package/src/__tests__/permissions.test.ts +1 -1
- package/src/__tests__/pipeline.test.ts +50 -0
- package/src/__tests__/polish-phase3.test.ts +340 -0
- package/src/__tests__/profile.test.ts +237 -0
- package/src/__tests__/rollback.test.ts +83 -0
- package/src/__tests__/runbook.test.ts +219 -0
- package/src/__tests__/schedule.test.ts +206 -0
- package/src/__tests__/serve.test.ts +1 -1
- package/src/__tests__/sessions.test.ts +96 -1
- package/src/__tests__/sharing.test.ts +53 -1
- package/src/__tests__/snapshots.test.ts +1 -1
- package/src/__tests__/standalone-migration.test.ts +199 -0
- package/src/__tests__/state-db.test.ts +1 -1
- package/src/__tests__/status.test.ts +158 -0
- package/src/__tests__/stream-with-tools.test.ts +71 -25
- package/src/__tests__/subagents.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +82 -3
- package/src/__tests__/terminal-gap-v2.test.ts +395 -0
- package/src/__tests__/terminal-parity.test.ts +393 -0
- package/src/__tests__/tf-apply.test.ts +187 -0
- package/src/__tests__/tool-converter.test.ts +1 -1
- package/src/__tests__/tool-schemas.test.ts +209 -4
- package/src/__tests__/tools.test.ts +4 -3
- package/src/__tests__/version-json.test.ts +184 -0
- package/src/__tests__/version.test.ts +1 -1
- package/src/__tests__/watch.test.ts +129 -0
- package/src/agent/compaction-agent.ts +40 -1
- package/src/agent/context-manager.ts +67 -3
- package/src/agent/deploy-preview.ts +62 -1
- package/src/agent/expand-files.ts +108 -0
- package/src/agent/loop.ts +1312 -31
- package/src/agent/permissions.ts +51 -4
- package/src/agent/system-prompt.ts +573 -19
- package/src/app.ts +58 -0
- package/src/audit/security-scanner.ts +45 -0
- package/src/auth/keychain.ts +82 -0
- package/src/auth/oauth.ts +15 -5
- package/src/cli/init.ts +378 -5
- package/src/cli/run.ts +407 -16
- package/src/cli/serve.ts +78 -1
- package/src/cli/web.ts +10 -6
- package/src/cli.ts +312 -1
- package/src/clients/service-discovery.ts +30 -25
- package/src/commands/alias.ts +100 -0
- package/src/commands/audit/index.ts +121 -2
- package/src/commands/auth-cloud.ts +113 -0
- package/src/commands/auth-refresh.ts +187 -0
- package/src/commands/aws-discover.ts +144 -251
- package/src/commands/aws-terraform.ts +68 -118
- package/src/commands/chat.ts +9 -3
- package/src/commands/completions.ts +268 -0
- package/src/commands/config.ts +26 -0
- package/src/commands/cost/index.ts +218 -2
- package/src/commands/deploy.ts +260 -0
- package/src/commands/doctor.ts +744 -152
- package/src/commands/drift/index.ts +371 -23
- package/src/commands/export.ts +146 -0
- package/src/commands/generate-k8s.ts +9 -61
- package/src/commands/generate-terraform.ts +191 -449
- package/src/commands/help.ts +212 -36
- package/src/commands/history.ts +8 -1
- package/src/commands/incident.ts +166 -0
- package/src/commands/init.ts +5 -0
- package/src/commands/login.ts +86 -1
- package/src/commands/logs.ts +167 -0
- package/src/commands/onboarding.ts +211 -34
- package/src/commands/pipeline.ts +186 -0
- package/src/commands/plugin.ts +398 -0
- package/src/commands/profile.ts +342 -0
- package/src/commands/questionnaire.ts +0 -98
- package/src/commands/resume.ts +26 -34
- package/src/commands/rollback.ts +315 -0
- package/src/commands/rollout.ts +88 -0
- package/src/commands/runbook.ts +346 -0
- package/src/commands/schedule.ts +236 -0
- package/src/commands/status.ts +252 -0
- package/src/commands/team-context.ts +220 -0
- package/src/commands/template.ts +58 -57
- package/src/commands/tf/index.ts +70 -11
- package/src/commands/upgrade.ts +57 -0
- package/src/commands/version.ts +54 -50
- package/src/commands/watch.ts +153 -0
- package/src/compat/runtime.ts +1 -1
- package/src/compat/sqlite.ts +75 -5
- package/src/config/mode-store.ts +62 -0
- package/src/config/profiles.ts +84 -0
- package/src/config/types.ts +83 -1
- package/src/config/workspace-state.ts +53 -0
- package/src/engine/cost-estimator.ts +52 -10
- package/src/engine/executor.ts +33 -2
- package/src/engine/planner.ts +68 -1
- package/src/generator/terraform.ts +8 -0
- package/src/history/manager.ts +2 -74
- package/src/hooks/engine.ts +5 -4
- package/src/llm/cost-calculator.ts +2 -2
- package/src/llm/providers/anthropic.ts +50 -21
- package/src/llm/router.ts +76 -7
- package/src/lsp/languages.ts +3 -0
- package/src/lsp/manager.ts +21 -5
- package/src/nimbus.ts +37 -18
- package/src/sessions/manager.ts +108 -1
- package/src/sharing/sync.ts +4 -0
- package/src/sharing/viewer.ts +66 -0
- package/src/tools/file-ops.ts +22 -0
- package/src/tools/schemas/devops.ts +3007 -117
- package/src/tools/schemas/standard.ts +5 -1
- package/src/tools/schemas/types.ts +31 -1
- package/src/tools/spawn-exec.ts +148 -0
- package/src/ui/App.tsx +1183 -66
- package/src/ui/DeployPreview.tsx +62 -57
- package/src/ui/FileDiffModal.tsx +162 -0
- package/src/ui/Header.tsx +87 -24
- package/src/ui/HelpModal.tsx +57 -0
- package/src/ui/InputBox.tsx +163 -10
- package/src/ui/MessageList.tsx +487 -40
- package/src/ui/PermissionPrompt.tsx +17 -5
- package/src/ui/StatusBar.tsx +122 -3
- package/src/ui/TerminalPane.tsx +84 -0
- package/src/ui/ToolCallDisplay.tsx +252 -18
- package/src/ui/TreePane.tsx +132 -0
- package/src/ui/chat-ui.ts +41 -44
- package/src/ui/ink/index.ts +771 -38
- package/src/ui/streaming.ts +1 -1
- package/src/ui/theme.ts +104 -0
- package/src/ui/types.ts +18 -0
- package/src/version.ts +1 -1
- package/src/watcher/index.ts +66 -15
- package/src/wizard/types.ts +1 -0
- package/src/wizard/ui.ts +1 -1
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compliance Checker - Check infrastructure compliance against standard frameworks.
|
|
3
|
+
*
|
|
4
|
+
* Scans Terraform files (and other configuration) for compliance with SOC2,
|
|
5
|
+
* HIPAA, PCI-DSS, GDPR, and CIS benchmark controls. Each control is evaluated
|
|
6
|
+
* as pass, fail, warn, or skip based on the presence or absence of required
|
|
7
|
+
* Terraform configurations.
|
|
8
|
+
*
|
|
9
|
+
* Reports include per-framework pass/fail counts and an overall compliance
|
|
10
|
+
* score expressed as a percentage.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
const CONTROL_DEFINITIONS = [
|
|
15
|
+
// -------------------------------------------------------------------------
|
|
16
|
+
// SOC2 Controls
|
|
17
|
+
// -------------------------------------------------------------------------
|
|
18
|
+
{
|
|
19
|
+
id: 'SOC2-001',
|
|
20
|
+
framework: 'SOC2',
|
|
21
|
+
name: 'Logging enabled',
|
|
22
|
+
description: 'CloudTrail, CloudWatch, or equivalent logging must be configured.',
|
|
23
|
+
passPattern: /(?:aws_cloudtrail|aws_cloudwatch_log_group|google_logging_project_sink|azurerm_monitor_diagnostic_setting)/,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'SOC2-002',
|
|
27
|
+
framework: 'SOC2',
|
|
28
|
+
name: 'Access controls defined',
|
|
29
|
+
description: 'IAM policies, roles, or access control resources must be present.',
|
|
30
|
+
passPattern: /(?:aws_iam_policy|aws_iam_role|google_project_iam|azurerm_role_assignment)/,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'SOC2-003',
|
|
34
|
+
framework: 'SOC2',
|
|
35
|
+
name: 'Encryption at rest',
|
|
36
|
+
description: 'Storage resources must have encryption at rest enabled.',
|
|
37
|
+
passPattern: /(?:server_side_encryption_configuration|storage_encrypted\s*=\s*true|encryption_configuration|kms_key_id|customer_managed_key)/,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'SOC2-004',
|
|
41
|
+
framework: 'SOC2',
|
|
42
|
+
name: 'Backup configuration',
|
|
43
|
+
description: 'Automated backup or snapshot policies must be configured.',
|
|
44
|
+
passPattern: /(?:backup_retention_period|aws_backup_plan|google_sql_database_instance.*backup_configuration|azurerm_backup_policy)/s,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'SOC2-005',
|
|
48
|
+
framework: 'SOC2',
|
|
49
|
+
name: 'Network security groups',
|
|
50
|
+
description: 'Network-level access controls (security groups, NACLs, firewall rules) must be present.',
|
|
51
|
+
passPattern: /(?:aws_security_group|google_compute_firewall|azurerm_network_security_group)/,
|
|
52
|
+
},
|
|
53
|
+
// -------------------------------------------------------------------------
|
|
54
|
+
// HIPAA Controls
|
|
55
|
+
// -------------------------------------------------------------------------
|
|
56
|
+
{
|
|
57
|
+
id: 'HIPAA-001',
|
|
58
|
+
framework: 'HIPAA',
|
|
59
|
+
name: 'Encryption required',
|
|
60
|
+
description: 'All data stores must use encryption at rest and in transit.',
|
|
61
|
+
passPattern: /(?:server_side_encryption_configuration|storage_encrypted\s*=\s*true|ssl_enforcement_enabled|require_ssl)/,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'HIPAA-002',
|
|
65
|
+
framework: 'HIPAA',
|
|
66
|
+
name: 'Audit logging',
|
|
67
|
+
description: 'Comprehensive audit logging must be enabled for all access to PHI.',
|
|
68
|
+
passPattern: /(?:aws_cloudtrail|aws_cloudwatch_log_group|google_logging|azurerm_monitor_diagnostic_setting)/,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'HIPAA-003',
|
|
72
|
+
framework: 'HIPAA',
|
|
73
|
+
name: 'Access controls',
|
|
74
|
+
description: 'Role-based access controls must restrict access to PHI.',
|
|
75
|
+
passPattern: /(?:aws_iam_policy|aws_iam_role|google_project_iam|azurerm_role_assignment)/,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'HIPAA-004',
|
|
79
|
+
framework: 'HIPAA',
|
|
80
|
+
name: 'PHI data handling',
|
|
81
|
+
description: 'Data classification tags or labels must identify PHI resources.',
|
|
82
|
+
passPattern: /(?:tags\s*=\s*\{[^}]*(?:phi|hipaa|sensitive|classification)[^}]*\}|labels\s*=\s*\{[^}]*(?:phi|hipaa|sensitive)[^}]*\})/is,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'HIPAA-005',
|
|
86
|
+
framework: 'HIPAA',
|
|
87
|
+
name: 'Data backup and recovery',
|
|
88
|
+
description: 'PHI data must have backup and disaster recovery plans.',
|
|
89
|
+
passPattern: /(?:backup_retention_period|aws_backup_plan|point_in_time_recovery)/,
|
|
90
|
+
},
|
|
91
|
+
// -------------------------------------------------------------------------
|
|
92
|
+
// PCI-DSS Controls
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
{
|
|
95
|
+
id: 'PCI-001',
|
|
96
|
+
framework: 'PCI',
|
|
97
|
+
name: 'Network segmentation',
|
|
98
|
+
description: 'Cardholder data environments must be segmented from other networks.',
|
|
99
|
+
passPattern: /(?:aws_vpc|aws_subnet|google_compute_network|google_compute_subnetwork|azurerm_virtual_network|azurerm_subnet)/,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'PCI-002',
|
|
103
|
+
framework: 'PCI',
|
|
104
|
+
name: 'Encryption in transit',
|
|
105
|
+
description: 'All cardholder data must be encrypted during transmission.',
|
|
106
|
+
passPattern: /(?:ssl_policy|ssl_certificate|tls_policy|https_only|redirect_all_requests_to.*https|listener.*protocol\s*=\s*["']HTTPS)/s,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'PCI-003',
|
|
110
|
+
framework: 'PCI',
|
|
111
|
+
name: 'Access logging',
|
|
112
|
+
description: 'All access to cardholder data must be logged.',
|
|
113
|
+
passPattern: /(?:access_log|logging\s*\{|enable_logging\s*=\s*true|log_analytics)/,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'PCI-004',
|
|
117
|
+
framework: 'PCI',
|
|
118
|
+
name: 'No wildcard IAM permissions',
|
|
119
|
+
description: 'IAM policies must not use wildcard actions on cardholder data resources.',
|
|
120
|
+
failPattern: /["']Action["']\s*:\s*["']\*["']/,
|
|
121
|
+
invertFail: true,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'PCI-005',
|
|
125
|
+
framework: 'PCI',
|
|
126
|
+
name: 'WAF or firewall configured',
|
|
127
|
+
description: 'Web application firewall or equivalent must protect public-facing applications.',
|
|
128
|
+
passPattern: /(?:aws_wafv2|aws_waf|google_compute_security_policy|azurerm_web_application_firewall_policy)/,
|
|
129
|
+
},
|
|
130
|
+
// -------------------------------------------------------------------------
|
|
131
|
+
// GDPR Controls
|
|
132
|
+
// -------------------------------------------------------------------------
|
|
133
|
+
{
|
|
134
|
+
id: 'GDPR-001',
|
|
135
|
+
framework: 'GDPR',
|
|
136
|
+
name: 'Data retention policies',
|
|
137
|
+
description: 'Resources must define data retention or lifecycle policies.',
|
|
138
|
+
passPattern: /(?:lifecycle_rule|retention_in_days|expiration|ttl|data_retention|lifecycle_policy)/,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'GDPR-002',
|
|
142
|
+
framework: 'GDPR',
|
|
143
|
+
name: 'Consent mechanisms',
|
|
144
|
+
description: 'Infrastructure must support consent management workflows.',
|
|
145
|
+
passPattern: /(?:consent|gdpr|privacy|data_subject|right_to_erasure)/i,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'GDPR-003',
|
|
149
|
+
framework: 'GDPR',
|
|
150
|
+
name: 'Data deletion capability',
|
|
151
|
+
description: 'Resources must support deletion of personal data (right to be forgotten).',
|
|
152
|
+
passPattern: /(?:lifecycle_rule|versioning|object_lock|deletion_protection|prevent_destroy)/,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'GDPR-004',
|
|
156
|
+
framework: 'GDPR',
|
|
157
|
+
name: 'Data processing location',
|
|
158
|
+
description: 'Resources must specify their deployment region to ensure data residency.',
|
|
159
|
+
passPattern: /(?:region\s*=|location\s*=|availability_zone)/,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'GDPR-005',
|
|
163
|
+
framework: 'GDPR',
|
|
164
|
+
name: 'Encryption of personal data',
|
|
165
|
+
description: 'Personal data must be encrypted at rest and in transit.',
|
|
166
|
+
passPattern: /(?:server_side_encryption|storage_encrypted|kms_key|encryption_configuration)/,
|
|
167
|
+
},
|
|
168
|
+
// -------------------------------------------------------------------------
|
|
169
|
+
// CIS Benchmark Controls
|
|
170
|
+
// -------------------------------------------------------------------------
|
|
171
|
+
{
|
|
172
|
+
id: 'CIS-001',
|
|
173
|
+
framework: 'CIS',
|
|
174
|
+
name: 'No public access',
|
|
175
|
+
description: 'Storage and database resources must not be publicly accessible.',
|
|
176
|
+
failPattern: /(?:publicly_accessible\s*=\s*true|acl\s*=\s*["']public-read)/,
|
|
177
|
+
invertFail: true,
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 'CIS-002',
|
|
181
|
+
framework: 'CIS',
|
|
182
|
+
name: 'Minimal IAM permissions',
|
|
183
|
+
description: 'IAM policies should follow least privilege; no wildcard actions.',
|
|
184
|
+
failPattern: /["']Action["']\s*:\s*["']\*["']/,
|
|
185
|
+
invertFail: true,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: 'CIS-003',
|
|
189
|
+
framework: 'CIS',
|
|
190
|
+
name: 'Encryption enabled',
|
|
191
|
+
description: 'All storage and database services must use encryption.',
|
|
192
|
+
passPattern: /(?:server_side_encryption_configuration|storage_encrypted\s*=\s*true|encryption_configuration|kms_key_id)/,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 'CIS-004',
|
|
196
|
+
framework: 'CIS',
|
|
197
|
+
name: 'VPC flow logs enabled',
|
|
198
|
+
description: 'VPC flow logs must be enabled for network traffic monitoring.',
|
|
199
|
+
passPattern: /(?:aws_flow_log|google_compute_subnetwork.*log_config|azurerm_network_watcher_flow_log)/s,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'CIS-005',
|
|
203
|
+
framework: 'CIS',
|
|
204
|
+
name: 'Multi-factor authentication',
|
|
205
|
+
description: 'MFA should be required for IAM users with console access.',
|
|
206
|
+
passPattern: /(?:mfa_delete|mfa_device|condition.*mfa|multi_factor)/i,
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// Helpers
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
const DEFAULT_EXCLUDES = new Set(['node_modules', '.git', 'dist', 'coverage', '.next', 'build']);
|
|
213
|
+
/**
|
|
214
|
+
* Recursively collect Terraform file contents from a directory.
|
|
215
|
+
*
|
|
216
|
+
* @returns Concatenated content of all .tf and .tf.json files, plus a count
|
|
217
|
+
*/
|
|
218
|
+
function collectTerraformContent(dir) {
|
|
219
|
+
const chunks = [];
|
|
220
|
+
let fileCount = 0;
|
|
221
|
+
function walk(currentDir) {
|
|
222
|
+
let entries;
|
|
223
|
+
try {
|
|
224
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
for (const entry of entries) {
|
|
230
|
+
if (entry.isDirectory()) {
|
|
231
|
+
if (DEFAULT_EXCLUDES.has(entry.name)) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
walk(path.join(currentDir, entry.name));
|
|
235
|
+
}
|
|
236
|
+
else if (entry.isFile()) {
|
|
237
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
238
|
+
if (ext === '.tf' || entry.name.endsWith('.tf.json') || ext === '.tfvars') {
|
|
239
|
+
try {
|
|
240
|
+
const content = fs.readFileSync(path.join(currentDir, entry.name), 'utf-8');
|
|
241
|
+
chunks.push(content);
|
|
242
|
+
fileCount++;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// skip unreadable files
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
walk(dir);
|
|
252
|
+
return { content: chunks.join('\n'), fileCount };
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Evaluate a single control definition against the combined Terraform content.
|
|
256
|
+
*/
|
|
257
|
+
function evaluateControl(def, terraformContent, hasTerraformFiles) {
|
|
258
|
+
// If no Terraform files exist, skip the control
|
|
259
|
+
if (!hasTerraformFiles) {
|
|
260
|
+
return {
|
|
261
|
+
id: def.id,
|
|
262
|
+
framework: def.framework,
|
|
263
|
+
name: def.name,
|
|
264
|
+
description: def.description,
|
|
265
|
+
status: 'skip',
|
|
266
|
+
evidence: 'No Terraform files found in the scanned directory.',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
// Controls that use invertFail: pass when failPattern is NOT found
|
|
270
|
+
if (def.invertFail && def.failPattern) {
|
|
271
|
+
const match = def.failPattern.exec(terraformContent);
|
|
272
|
+
if (match) {
|
|
273
|
+
return {
|
|
274
|
+
id: def.id,
|
|
275
|
+
framework: def.framework,
|
|
276
|
+
name: def.name,
|
|
277
|
+
description: def.description,
|
|
278
|
+
status: 'fail',
|
|
279
|
+
evidence: `Found violation: "${match[0].slice(0, 80)}"`,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
id: def.id,
|
|
284
|
+
framework: def.framework,
|
|
285
|
+
name: def.name,
|
|
286
|
+
description: def.description,
|
|
287
|
+
status: 'pass',
|
|
288
|
+
evidence: 'No violations detected.',
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// Standard passPattern check
|
|
292
|
+
if (def.passPattern) {
|
|
293
|
+
const match = def.passPattern.exec(terraformContent);
|
|
294
|
+
if (match) {
|
|
295
|
+
return {
|
|
296
|
+
id: def.id,
|
|
297
|
+
framework: def.framework,
|
|
298
|
+
name: def.name,
|
|
299
|
+
description: def.description,
|
|
300
|
+
status: 'pass',
|
|
301
|
+
evidence: `Found matching configuration: "${match[0].slice(0, 80)}"`,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
id: def.id,
|
|
306
|
+
framework: def.framework,
|
|
307
|
+
name: def.name,
|
|
308
|
+
description: def.description,
|
|
309
|
+
status: 'fail',
|
|
310
|
+
evidence: 'Required configuration not found in Terraform files.',
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
// No pattern defined -- warn
|
|
314
|
+
return {
|
|
315
|
+
id: def.id,
|
|
316
|
+
framework: def.framework,
|
|
317
|
+
name: def.name,
|
|
318
|
+
description: def.description,
|
|
319
|
+
status: 'warn',
|
|
320
|
+
evidence: 'Manual verification required -- no automated check available.',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
// Public API
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
/**
|
|
327
|
+
* Check compliance of infrastructure configurations against one or more frameworks.
|
|
328
|
+
*
|
|
329
|
+
* Scans Terraform files in the specified directory and evaluates each control
|
|
330
|
+
* from the selected frameworks. Returns a compliance report per framework.
|
|
331
|
+
*
|
|
332
|
+
* @param options - Directory and framework selection
|
|
333
|
+
* @returns Array of compliance reports, one per framework
|
|
334
|
+
*/
|
|
335
|
+
export async function checkCompliance(options) {
|
|
336
|
+
const frameworks = options.frameworks ?? ['SOC2', 'HIPAA', 'PCI', 'GDPR', 'CIS'];
|
|
337
|
+
const { content: terraformContent, fileCount } = collectTerraformContent(options.dir);
|
|
338
|
+
const hasTerraformFiles = fileCount > 0;
|
|
339
|
+
const reports = [];
|
|
340
|
+
for (const framework of frameworks) {
|
|
341
|
+
const definitions = CONTROL_DEFINITIONS.filter(d => d.framework === framework);
|
|
342
|
+
const controls = definitions.map(def => evaluateControl(def, terraformContent, hasTerraformFiles));
|
|
343
|
+
const passCount = controls.filter(c => c.status === 'pass').length;
|
|
344
|
+
const failCount = controls.filter(c => c.status === 'fail').length;
|
|
345
|
+
const warnCount = controls.filter(c => c.status === 'warn').length;
|
|
346
|
+
const skipCount = controls.filter(c => c.status === 'skip').length;
|
|
347
|
+
// Score = pass / (pass + fail + warn) * 100, skipped controls excluded
|
|
348
|
+
const evaluatedCount = passCount + failCount + warnCount;
|
|
349
|
+
const score = evaluatedCount > 0 ? Math.round((passCount / evaluatedCount) * 100) : 0;
|
|
350
|
+
reports.push({
|
|
351
|
+
framework,
|
|
352
|
+
controls,
|
|
353
|
+
passCount,
|
|
354
|
+
failCount,
|
|
355
|
+
warnCount,
|
|
356
|
+
skipCount,
|
|
357
|
+
score,
|
|
358
|
+
timestamp: new Date(),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
return reports;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Generate a visual scorecard from one or more compliance reports.
|
|
365
|
+
*
|
|
366
|
+
* Produces a formatted text table showing framework scores, pass/fail counts,
|
|
367
|
+
* and individual control statuses.
|
|
368
|
+
*
|
|
369
|
+
* @param reports - Compliance reports to include in the scorecard
|
|
370
|
+
* @returns Multi-line formatted scorecard string
|
|
371
|
+
*/
|
|
372
|
+
export function generateScorecard(reports) {
|
|
373
|
+
if (reports.length === 0) {
|
|
374
|
+
return 'No compliance reports to display.';
|
|
375
|
+
}
|
|
376
|
+
const statusIcon = {
|
|
377
|
+
pass: '[PASS]',
|
|
378
|
+
fail: '[FAIL]',
|
|
379
|
+
warn: '[WARN]',
|
|
380
|
+
skip: '[SKIP]',
|
|
381
|
+
};
|
|
382
|
+
const lines = ['Compliance Scorecard', '='.repeat(60), ''];
|
|
383
|
+
// Overview table
|
|
384
|
+
lines.push(' Framework Score Pass Fail Warn Skip');
|
|
385
|
+
lines.push(` ${'-'.repeat(50)}`);
|
|
386
|
+
for (const report of reports) {
|
|
387
|
+
const fw = report.framework.padEnd(10);
|
|
388
|
+
const score = `${report.score}%`.padStart(5);
|
|
389
|
+
const pass = String(report.passCount).padStart(4);
|
|
390
|
+
const fail = String(report.failCount).padStart(4);
|
|
391
|
+
const warn = String(report.warnCount).padStart(4);
|
|
392
|
+
const skip = String(report.skipCount).padStart(4);
|
|
393
|
+
lines.push(` ${fw} ${score} ${pass} ${fail} ${warn} ${skip}`);
|
|
394
|
+
}
|
|
395
|
+
lines.push('');
|
|
396
|
+
// Detailed control results
|
|
397
|
+
for (const report of reports) {
|
|
398
|
+
lines.push(`--- ${report.framework} (${report.score}%) ---`);
|
|
399
|
+
lines.push('');
|
|
400
|
+
for (const control of report.controls) {
|
|
401
|
+
lines.push(` ${statusIcon[control.status]} ${control.id}: ${control.name}`);
|
|
402
|
+
lines.push(` ${control.description}`);
|
|
403
|
+
if (control.evidence) {
|
|
404
|
+
lines.push(` Evidence: ${control.evidence}`);
|
|
405
|
+
}
|
|
406
|
+
lines.push('');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Overall summary
|
|
410
|
+
const totalPass = reports.reduce((s, r) => s + r.passCount, 0);
|
|
411
|
+
const totalFail = reports.reduce((s, r) => s + r.failCount, 0);
|
|
412
|
+
const totalWarn = reports.reduce((s, r) => s + r.warnCount, 0);
|
|
413
|
+
const totalSkip = reports.reduce((s, r) => s + r.skipCount, 0);
|
|
414
|
+
const totalEval = totalPass + totalFail + totalWarn;
|
|
415
|
+
const overallScore = totalEval > 0 ? Math.round((totalPass / totalEval) * 100) : 0;
|
|
416
|
+
lines.push('='.repeat(60));
|
|
417
|
+
lines.push(`Overall: ${overallScore}% compliant (${totalPass} pass, ${totalFail} fail, ${totalWarn} warn, ${totalSkip} skip)`);
|
|
418
|
+
return lines.join('\n');
|
|
419
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Tracker - Track LLM and infrastructure costs across sessions.
|
|
3
|
+
*
|
|
4
|
+
* Records cost entries for LLM API calls (with token counts and model info)
|
|
5
|
+
* and infrastructure changes (from terraform plan). Computes daily cost
|
|
6
|
+
* aggregates, monthly projections, and per-session breakdowns.
|
|
7
|
+
*/
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Helpers
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
/**
|
|
12
|
+
* Format a Date as a YYYY-MM-DD string for daily aggregation.
|
|
13
|
+
*/
|
|
14
|
+
function toDateKey(date) {
|
|
15
|
+
const y = date.getFullYear();
|
|
16
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
17
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
18
|
+
return `${y}-${m}-${d}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Format a USD amount with appropriate precision.
|
|
22
|
+
*/
|
|
23
|
+
function formatUSD(amount) {
|
|
24
|
+
if (amount < 0.01 && amount > 0) {
|
|
25
|
+
return `$${amount.toFixed(6)}`;
|
|
26
|
+
}
|
|
27
|
+
return `$${amount.toFixed(4)}`;
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// CostTracker
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Tracks LLM and infrastructure costs across Nimbus sessions.
|
|
34
|
+
*
|
|
35
|
+
* Maintains an in-memory ledger of cost entries and provides methods to
|
|
36
|
+
* compute summaries, daily breakdowns, and monthly projections.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const tracker = new CostTracker();
|
|
41
|
+
*
|
|
42
|
+
* tracker.recordLLMCost({
|
|
43
|
+
* sessionId: 'abc-123',
|
|
44
|
+
* model: 'claude-sonnet-4-20250514',
|
|
45
|
+
* inputTokens: 1500,
|
|
46
|
+
* outputTokens: 800,
|
|
47
|
+
* costUSD: 0.0165,
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* const summary = tracker.getSummary();
|
|
51
|
+
* console.log(tracker.formatSummary(summary));
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export class CostTracker {
|
|
55
|
+
entries = [];
|
|
56
|
+
/**
|
|
57
|
+
* Record a cost entry for an LLM API call.
|
|
58
|
+
*
|
|
59
|
+
* @param params - LLM call details including model, token counts, and cost
|
|
60
|
+
* @returns The created cost entry
|
|
61
|
+
*/
|
|
62
|
+
recordLLMCost(params) {
|
|
63
|
+
const entry = {
|
|
64
|
+
id: crypto.randomUUID(),
|
|
65
|
+
sessionId: params.sessionId,
|
|
66
|
+
timestamp: new Date(),
|
|
67
|
+
category: 'llm',
|
|
68
|
+
description: `LLM call to ${params.model} (${params.inputTokens} in / ${params.outputTokens} out tokens)`,
|
|
69
|
+
amount: params.costUSD,
|
|
70
|
+
inputTokens: params.inputTokens,
|
|
71
|
+
outputTokens: params.outputTokens,
|
|
72
|
+
model: params.model,
|
|
73
|
+
};
|
|
74
|
+
this.entries.push(entry);
|
|
75
|
+
return entry;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Record an infrastructure cost change, typically derived from a terraform plan.
|
|
79
|
+
*
|
|
80
|
+
* @param params - Infrastructure cost details
|
|
81
|
+
* @returns The created cost entry
|
|
82
|
+
*/
|
|
83
|
+
recordInfraCost(params) {
|
|
84
|
+
const entry = {
|
|
85
|
+
id: crypto.randomUUID(),
|
|
86
|
+
sessionId: params.sessionId,
|
|
87
|
+
timestamp: new Date(),
|
|
88
|
+
category: 'infra',
|
|
89
|
+
description: params.description,
|
|
90
|
+
amount: params.monthlyCost,
|
|
91
|
+
};
|
|
92
|
+
this.entries.push(entry);
|
|
93
|
+
return entry;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Compute a cost summary over all entries or a subset filtered by time/session.
|
|
97
|
+
*
|
|
98
|
+
* The monthly projection is calculated by taking the average daily cost
|
|
99
|
+
* and multiplying by 30. If only a single day of data exists, that day's
|
|
100
|
+
* total is used as the daily average.
|
|
101
|
+
*
|
|
102
|
+
* @param options - Optional time range or session filter
|
|
103
|
+
* @returns Aggregated cost summary
|
|
104
|
+
*/
|
|
105
|
+
getSummary(options) {
|
|
106
|
+
let filtered = this.entries;
|
|
107
|
+
if (options?.since) {
|
|
108
|
+
const since = options.since.getTime();
|
|
109
|
+
filtered = filtered.filter(e => e.timestamp.getTime() >= since);
|
|
110
|
+
}
|
|
111
|
+
if (options?.sessionId) {
|
|
112
|
+
filtered = filtered.filter(e => e.sessionId === options.sessionId);
|
|
113
|
+
}
|
|
114
|
+
// Totals
|
|
115
|
+
let totalCost = 0;
|
|
116
|
+
let llmCost = 0;
|
|
117
|
+
let infraCost = 0;
|
|
118
|
+
// Group by session
|
|
119
|
+
const entriesBySession = new Map();
|
|
120
|
+
// Group by date
|
|
121
|
+
const dailyMap = new Map();
|
|
122
|
+
for (const entry of filtered) {
|
|
123
|
+
totalCost += entry.amount;
|
|
124
|
+
if (entry.category === 'llm') {
|
|
125
|
+
llmCost += entry.amount;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
infraCost += entry.amount;
|
|
129
|
+
}
|
|
130
|
+
// Session grouping
|
|
131
|
+
const sessionEntries = entriesBySession.get(entry.sessionId);
|
|
132
|
+
if (sessionEntries) {
|
|
133
|
+
sessionEntries.push(entry);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
entriesBySession.set(entry.sessionId, [entry]);
|
|
137
|
+
}
|
|
138
|
+
// Daily grouping
|
|
139
|
+
const dateKey = toDateKey(entry.timestamp);
|
|
140
|
+
dailyMap.set(dateKey, (dailyMap.get(dateKey) ?? 0) + entry.amount);
|
|
141
|
+
}
|
|
142
|
+
// Sort daily costs chronologically
|
|
143
|
+
const dailyCosts = Array.from(dailyMap.entries())
|
|
144
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
145
|
+
.map(([date, amount]) => ({ date, amount }));
|
|
146
|
+
// Monthly projection: average daily cost * 30
|
|
147
|
+
let monthlyProjection = 0;
|
|
148
|
+
if (dailyCosts.length > 0) {
|
|
149
|
+
const totalDailyCost = dailyCosts.reduce((sum, d) => sum + d.amount, 0);
|
|
150
|
+
const avgDaily = totalDailyCost / dailyCosts.length;
|
|
151
|
+
monthlyProjection = avgDaily * 30;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
totalCost,
|
|
155
|
+
llmCost,
|
|
156
|
+
infraCost,
|
|
157
|
+
entriesBySession,
|
|
158
|
+
dailyCosts,
|
|
159
|
+
monthlyProjection,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Retrieve raw cost entries, optionally filtered by session.
|
|
164
|
+
*
|
|
165
|
+
* @param sessionId - If provided, only return entries for this session
|
|
166
|
+
* @returns Array of cost entries sorted by timestamp (newest first)
|
|
167
|
+
*/
|
|
168
|
+
getEntries(sessionId) {
|
|
169
|
+
let result = [...this.entries];
|
|
170
|
+
if (sessionId) {
|
|
171
|
+
result = result.filter(e => e.sessionId === sessionId);
|
|
172
|
+
}
|
|
173
|
+
return result.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Format a cost summary as a human-readable text report.
|
|
177
|
+
*
|
|
178
|
+
* Includes totals, per-session breakdowns, daily costs, and the
|
|
179
|
+
* projected monthly spend.
|
|
180
|
+
*
|
|
181
|
+
* @param summary - The summary to format
|
|
182
|
+
* @returns Multi-line formatted report
|
|
183
|
+
*/
|
|
184
|
+
formatSummary(summary) {
|
|
185
|
+
const lines = [
|
|
186
|
+
'Cost Summary',
|
|
187
|
+
'='.repeat(50),
|
|
188
|
+
'',
|
|
189
|
+
` Total Cost: ${formatUSD(summary.totalCost)}`,
|
|
190
|
+
` LLM Cost: ${formatUSD(summary.llmCost)}`,
|
|
191
|
+
` Infra Cost: ${formatUSD(summary.infraCost)}`,
|
|
192
|
+
` Monthly Estimate: ${formatUSD(summary.monthlyProjection)}`,
|
|
193
|
+
'',
|
|
194
|
+
];
|
|
195
|
+
// Per-session breakdown
|
|
196
|
+
if (summary.entriesBySession.size > 0) {
|
|
197
|
+
lines.push('Per-Session Breakdown:');
|
|
198
|
+
lines.push('-'.repeat(50));
|
|
199
|
+
for (const [sessionId, entries] of summary.entriesBySession) {
|
|
200
|
+
const sessionTotal = entries.reduce((s, e) => s + e.amount, 0);
|
|
201
|
+
const llm = entries.filter(e => e.category === 'llm');
|
|
202
|
+
const infra = entries.filter(e => e.category === 'infra');
|
|
203
|
+
const totalInputTokens = llm.reduce((s, e) => s + (e.inputTokens ?? 0), 0);
|
|
204
|
+
const totalOutputTokens = llm.reduce((s, e) => s + (e.outputTokens ?? 0), 0);
|
|
205
|
+
lines.push(` Session: ${sessionId}`);
|
|
206
|
+
lines.push(` Total: ${formatUSD(sessionTotal)} (${entries.length} entries)`);
|
|
207
|
+
if (llm.length > 0) {
|
|
208
|
+
const llmTotal = llm.reduce((s, e) => s + e.amount, 0);
|
|
209
|
+
lines.push(` LLM: ${formatUSD(llmTotal)} (${llm.length} calls, ${totalInputTokens} in / ${totalOutputTokens} out tokens)`);
|
|
210
|
+
}
|
|
211
|
+
if (infra.length > 0) {
|
|
212
|
+
const infraTotal = infra.reduce((s, e) => s + e.amount, 0);
|
|
213
|
+
lines.push(` Infra: ${formatUSD(infraTotal)} (${infra.length} changes)`);
|
|
214
|
+
}
|
|
215
|
+
lines.push('');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Daily costs
|
|
219
|
+
if (summary.dailyCosts.length > 0) {
|
|
220
|
+
lines.push('Daily Costs:');
|
|
221
|
+
lines.push('-'.repeat(50));
|
|
222
|
+
for (const day of summary.dailyCosts) {
|
|
223
|
+
lines.push(` ${day.date}: ${formatUSD(day.amount)}`);
|
|
224
|
+
}
|
|
225
|
+
lines.push('');
|
|
226
|
+
}
|
|
227
|
+
lines.push('='.repeat(50));
|
|
228
|
+
lines.push(`Projected monthly cost: ${formatUSD(summary.monthlyProjection)}`);
|
|
229
|
+
return lines.join('\n');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Notebook - barrel exports.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the security scanner, compliance checker, cost tracker, and
|
|
5
|
+
* activity log modules for convenient single-import access.
|
|
6
|
+
*/
|
|
7
|
+
export { scanSecurity, formatFindings } from './security-scanner';
|
|
8
|
+
export { checkCompliance, generateScorecard } from './compliance-checker';
|
|
9
|
+
export { CostTracker } from './cost-tracker';
|
|
10
|
+
export { ActivityLog } from './activity-log';
|