@build-astron-co/nimbus 0.2.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/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Framework Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the demo scenarios framework
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A single step in a demo scenario
|
|
9
|
+
*/
|
|
10
|
+
export interface DemoStep {
|
|
11
|
+
/** Unique identifier for this step */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Title shown before executing */
|
|
14
|
+
title: string;
|
|
15
|
+
/** Description of what this step does */
|
|
16
|
+
description?: string;
|
|
17
|
+
/** Command to execute */
|
|
18
|
+
command: string;
|
|
19
|
+
/** Whether to wait for user input before proceeding */
|
|
20
|
+
waitForInput?: boolean;
|
|
21
|
+
/** Expected output pattern (regex) */
|
|
22
|
+
expectedOutput?: string;
|
|
23
|
+
/** Time to wait after step (ms) */
|
|
24
|
+
delay?: number;
|
|
25
|
+
/** Whether to show command output */
|
|
26
|
+
showOutput?: boolean;
|
|
27
|
+
/** Mock response for demo mode (when not actually executing) */
|
|
28
|
+
mockResponse?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A demo scenario containing multiple steps
|
|
33
|
+
*/
|
|
34
|
+
export interface DemoScenario {
|
|
35
|
+
/** Unique identifier */
|
|
36
|
+
id: string;
|
|
37
|
+
/** Display name */
|
|
38
|
+
name: string;
|
|
39
|
+
/** Short description */
|
|
40
|
+
description: string;
|
|
41
|
+
/** Category for grouping */
|
|
42
|
+
category: 'terraform' | 'kubernetes' | 'helm' | 'aws' | 'full-journey' | 'tutorial';
|
|
43
|
+
/** Ordered list of steps */
|
|
44
|
+
steps: DemoStep[];
|
|
45
|
+
/** Prerequisites (e.g., "aws cli installed") */
|
|
46
|
+
prerequisites?: string[];
|
|
47
|
+
/** Estimated duration in minutes */
|
|
48
|
+
duration?: number;
|
|
49
|
+
/** Tags for filtering */
|
|
50
|
+
tags?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Demo runner options
|
|
55
|
+
*/
|
|
56
|
+
export interface DemoOptions {
|
|
57
|
+
/** Scenario ID to run */
|
|
58
|
+
scenario?: string;
|
|
59
|
+
/** List available scenarios */
|
|
60
|
+
list?: boolean;
|
|
61
|
+
/** Interactive mode (prompt for each step) */
|
|
62
|
+
interactive?: boolean;
|
|
63
|
+
/** Speed: slow, normal, fast */
|
|
64
|
+
speed?: 'slow' | 'normal' | 'fast';
|
|
65
|
+
/** Dry run - don't execute commands */
|
|
66
|
+
dryRun?: boolean;
|
|
67
|
+
/** Show all output (verbose) */
|
|
68
|
+
verbose?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Step execution result
|
|
73
|
+
*/
|
|
74
|
+
export interface StepResult {
|
|
75
|
+
step: DemoStep;
|
|
76
|
+
success: boolean;
|
|
77
|
+
output?: string;
|
|
78
|
+
error?: string;
|
|
79
|
+
duration: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Scenario execution result
|
|
84
|
+
*/
|
|
85
|
+
export interface ScenarioResult {
|
|
86
|
+
scenario: DemoScenario;
|
|
87
|
+
steps: StepResult[];
|
|
88
|
+
success: boolean;
|
|
89
|
+
totalDuration: number;
|
|
90
|
+
startedAt: Date;
|
|
91
|
+
completedAt: Date;
|
|
92
|
+
}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Estimator
|
|
3
|
+
*
|
|
4
|
+
* Standalone module for estimating monthly and annual infrastructure costs
|
|
5
|
+
* based on component type, environment, region, and usage patterns.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from the cost estimation logic in verifier.ts and planner.ts,
|
|
8
|
+
* with expanded capabilities for detailed breakdowns and recommendations.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { logger } from '../utils';
|
|
12
|
+
|
|
13
|
+
// ==========================================
|
|
14
|
+
// Cost Estimator Types
|
|
15
|
+
// ==========================================
|
|
16
|
+
|
|
17
|
+
/** Known infrastructure components with a base monthly cost. */
|
|
18
|
+
export type KnownComponent =
|
|
19
|
+
| 'vpc'
|
|
20
|
+
| 'eks'
|
|
21
|
+
| 'rds'
|
|
22
|
+
| 's3'
|
|
23
|
+
| 'ecs'
|
|
24
|
+
| 'lambda'
|
|
25
|
+
| 'cloudfront'
|
|
26
|
+
| 'elasticache'
|
|
27
|
+
| 'sqs'
|
|
28
|
+
| 'sns';
|
|
29
|
+
|
|
30
|
+
/** The environment tier affects cost multipliers and recommendations. */
|
|
31
|
+
export type EnvironmentTier = 'development' | 'staging' | 'production';
|
|
32
|
+
|
|
33
|
+
/** Cloud provider — affects regional pricing offsets. */
|
|
34
|
+
export type CloudProvider = 'aws' | 'gcp' | 'azure';
|
|
35
|
+
|
|
36
|
+
export interface CostEstimationInput {
|
|
37
|
+
/** List of infrastructure components to estimate (e.g. ['vpc', 'eks', 'rds']) */
|
|
38
|
+
components: string[];
|
|
39
|
+
/** Target environment — affects multipliers */
|
|
40
|
+
environment?: EnvironmentTier | string;
|
|
41
|
+
/** Cloud provider — affects regional pricing offsets */
|
|
42
|
+
provider?: CloudProvider | string;
|
|
43
|
+
/** AWS/GCP/Azure region — used for regional cost adjustments */
|
|
44
|
+
region?: string;
|
|
45
|
+
/** Optional explicit budget cap; used to flag over-budget estimates */
|
|
46
|
+
budgetLimit?: number;
|
|
47
|
+
/** Custom per-component overrides (monthly USD) */
|
|
48
|
+
customCosts?: Record<string, number>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ComponentCostBreakdown {
|
|
52
|
+
component: string;
|
|
53
|
+
baseMonthlyCost: number;
|
|
54
|
+
adjustedMonthlyCost: number;
|
|
55
|
+
environmentMultiplier: number;
|
|
56
|
+
regionalMultiplier: number;
|
|
57
|
+
notes: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface CostEstimate {
|
|
61
|
+
/** Total estimated monthly cost in USD */
|
|
62
|
+
totalMonthlyCost: number;
|
|
63
|
+
/** Total estimated annual cost in USD */
|
|
64
|
+
totalAnnualCost: number;
|
|
65
|
+
/** Per-component cost breakdown */
|
|
66
|
+
breakdown: ComponentCostBreakdown[];
|
|
67
|
+
/** Budget status */
|
|
68
|
+
withinBudget: boolean;
|
|
69
|
+
/** Budget limit used for comparison (if provided) */
|
|
70
|
+
budgetLimit?: number;
|
|
71
|
+
/** Cost optimisation recommendations */
|
|
72
|
+
recommendations: string[];
|
|
73
|
+
/** Environment tier used in the estimate */
|
|
74
|
+
environment: string;
|
|
75
|
+
/** Cloud provider used in the estimate */
|
|
76
|
+
provider: string;
|
|
77
|
+
/** Timestamp of estimate generation */
|
|
78
|
+
generatedAt: Date;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ==========================================
|
|
82
|
+
// Constants
|
|
83
|
+
// ==========================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Base monthly component costs in USD.
|
|
87
|
+
*
|
|
88
|
+
* These are intentionally conservative estimates for the most common
|
|
89
|
+
* instance sizes and usage patterns. Extracted directly from the
|
|
90
|
+
* original verifier.ts cost table and expanded for additional components.
|
|
91
|
+
*
|
|
92
|
+
* Sources:
|
|
93
|
+
* vpc: NAT Gateway ($0.045/h * 730h) ~= $32
|
|
94
|
+
* eks: Control plane only ($0.10/h * 730h) ~= $73
|
|
95
|
+
* rds: db.t3.micro ($0.017/h) + 20GB storage (~$2.30) ~= $15; rounded to $50 with Multi-AZ
|
|
96
|
+
* s3: Minimal storage estimate (< 100GB) ~= $5
|
|
97
|
+
*/
|
|
98
|
+
const BASE_COMPONENT_COSTS: Record<string, number> = {
|
|
99
|
+
vpc: 32, // NAT Gateway
|
|
100
|
+
eks: 73, // Control plane
|
|
101
|
+
rds: 50, // db.t3.micro + storage
|
|
102
|
+
s3: 5, // Minimal storage
|
|
103
|
+
ecs: 30, // Fargate minimal
|
|
104
|
+
lambda: 2, // < 1M invocations/month
|
|
105
|
+
cloudfront: 10, // < 1TB transfer/month
|
|
106
|
+
elasticache: 25, // cache.t3.micro
|
|
107
|
+
sqs: 1, // < 1M requests/month
|
|
108
|
+
sns: 1, // < 1M notifications/month
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Regional cost multipliers relative to us-east-1 (base = 1.0).
|
|
113
|
+
* Approximate — derived from AWS published pricing differentials.
|
|
114
|
+
*/
|
|
115
|
+
const REGIONAL_MULTIPLIERS: Record<string, number> = {
|
|
116
|
+
'us-east-1': 1.0,
|
|
117
|
+
'us-east-2': 1.0,
|
|
118
|
+
'us-west-1': 1.08,
|
|
119
|
+
'us-west-2': 1.0,
|
|
120
|
+
'eu-west-1': 1.06,
|
|
121
|
+
'eu-west-2': 1.1,
|
|
122
|
+
'eu-central-1': 1.08,
|
|
123
|
+
'ap-southeast-1': 1.14,
|
|
124
|
+
'ap-northeast-1': 1.16,
|
|
125
|
+
'ap-south-1': 1.05,
|
|
126
|
+
'sa-east-1': 1.2,
|
|
127
|
+
'ca-central-1': 1.06,
|
|
128
|
+
// GCP regions (approximate relative costs)
|
|
129
|
+
'us-central1': 1.0,
|
|
130
|
+
'us-east1': 1.0,
|
|
131
|
+
'europe-west1': 1.08,
|
|
132
|
+
'asia-east1': 1.12,
|
|
133
|
+
// Azure regions
|
|
134
|
+
eastus: 1.0,
|
|
135
|
+
westus: 1.05,
|
|
136
|
+
westeurope: 1.1,
|
|
137
|
+
southeastasia: 1.12,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Environment multipliers.
|
|
142
|
+
* Production typically uses larger, HA-ready instances; dev uses minimal sizes.
|
|
143
|
+
*/
|
|
144
|
+
const ENVIRONMENT_MULTIPLIERS: Record<string, number> = {
|
|
145
|
+
development: 0.5,
|
|
146
|
+
staging: 0.75,
|
|
147
|
+
production: 1.0,
|
|
148
|
+
prod: 1.0,
|
|
149
|
+
dev: 0.5,
|
|
150
|
+
staging_: 0.75,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Human-readable notes per component explaining the cost assumption.
|
|
155
|
+
*/
|
|
156
|
+
const COMPONENT_NOTES: Record<string, string> = {
|
|
157
|
+
vpc: 'NAT Gateway ($0.045/h) — one AZ; add $32/mo per additional AZ',
|
|
158
|
+
eks: 'EKS control plane only ($0.10/h); node group EC2 costs are additive',
|
|
159
|
+
rds: 'db.t3.micro Multi-AZ estimated; scales significantly with instance class',
|
|
160
|
+
s3: 'Minimal estimate (<100GB, <1M requests); review lifecycle policies for long-term savings',
|
|
161
|
+
ecs: 'Fargate minimal workload; scales linearly with vCPU and memory allocation',
|
|
162
|
+
lambda: 'Under 1M invocations/month; free tier may apply',
|
|
163
|
+
cloudfront: 'Under 1TB egress/month; varies heavily with traffic patterns',
|
|
164
|
+
elasticache: 'cache.t3.micro; consider Reserved Nodes for >30% savings in production',
|
|
165
|
+
sqs: 'Under 1M requests/month; near-zero cost at small scale',
|
|
166
|
+
sns: 'Under 1M notifications/month; near-zero cost at small scale',
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// ==========================================
|
|
170
|
+
// CostEstimator
|
|
171
|
+
// ==========================================
|
|
172
|
+
|
|
173
|
+
export class CostEstimator {
|
|
174
|
+
/**
|
|
175
|
+
* Estimate the monthly and annual cost for a given set of components.
|
|
176
|
+
*/
|
|
177
|
+
estimate(input: CostEstimationInput): CostEstimate {
|
|
178
|
+
const environment = input.environment || 'production';
|
|
179
|
+
const provider = input.provider || 'aws';
|
|
180
|
+
const region = input.region || this.defaultRegion(provider);
|
|
181
|
+
const budgetLimit = input.budgetLimit;
|
|
182
|
+
|
|
183
|
+
logger.info(
|
|
184
|
+
`Estimating cost for ${input.components.length} components ` +
|
|
185
|
+
`(env=${environment}, provider=${provider}, region=${region})`
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const envMultiplier = this.resolveEnvironmentMultiplier(environment);
|
|
189
|
+
const regionMultiplier = this.resolveRegionalMultiplier(region);
|
|
190
|
+
|
|
191
|
+
const breakdown: ComponentCostBreakdown[] = [];
|
|
192
|
+
|
|
193
|
+
for (const component of input.components) {
|
|
194
|
+
const baseCost = this.resolveBaseCost(component, input.customCosts);
|
|
195
|
+
const adjustedCost = Math.round(baseCost * envMultiplier * regionMultiplier * 100) / 100;
|
|
196
|
+
|
|
197
|
+
breakdown.push({
|
|
198
|
+
component,
|
|
199
|
+
baseMonthlyCost: baseCost,
|
|
200
|
+
adjustedMonthlyCost: adjustedCost,
|
|
201
|
+
environmentMultiplier: envMultiplier,
|
|
202
|
+
regionalMultiplier: regionMultiplier,
|
|
203
|
+
notes:
|
|
204
|
+
COMPONENT_NOTES[component] ||
|
|
205
|
+
`Estimate for ${component}; verify against provider pricing`,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const totalMonthlyCost =
|
|
210
|
+
Math.round(breakdown.reduce((sum, b) => sum + b.adjustedMonthlyCost, 0) * 100) / 100;
|
|
211
|
+
|
|
212
|
+
const totalAnnualCost = Math.round(totalMonthlyCost * 12 * 100) / 100;
|
|
213
|
+
|
|
214
|
+
const withinBudget = budgetLimit !== undefined ? totalMonthlyCost <= budgetLimit : true;
|
|
215
|
+
|
|
216
|
+
const recommendations = this.generateRecommendations(input, breakdown, totalMonthlyCost);
|
|
217
|
+
|
|
218
|
+
logger.info(
|
|
219
|
+
`Cost estimate: $${totalMonthlyCost}/mo ($${totalAnnualCost}/yr); ` +
|
|
220
|
+
`${withinBudget ? 'within' : 'exceeds'} budget`
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
totalMonthlyCost,
|
|
225
|
+
totalAnnualCost,
|
|
226
|
+
breakdown,
|
|
227
|
+
withinBudget,
|
|
228
|
+
budgetLimit,
|
|
229
|
+
recommendations,
|
|
230
|
+
environment,
|
|
231
|
+
provider,
|
|
232
|
+
generatedAt: new Date(),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Estimate cost from a flat context object (compatible with verifier/planner usage).
|
|
238
|
+
*
|
|
239
|
+
* This is a convenience wrapper that accepts the same `context` shape used
|
|
240
|
+
* by the Verifier's `runCostChecks` method, allowing the CostEstimator to
|
|
241
|
+
* serve as a drop-in replacement.
|
|
242
|
+
*/
|
|
243
|
+
estimateFromContext(context: Record<string, unknown>): number {
|
|
244
|
+
const components = (context.components as string[]) || [];
|
|
245
|
+
const result = this.estimate({
|
|
246
|
+
components,
|
|
247
|
+
environment: (context.environment as string) || 'production',
|
|
248
|
+
provider: (context.provider as string) || 'aws',
|
|
249
|
+
region: context.region as string | undefined,
|
|
250
|
+
budgetLimit: context.budget_limit as number | undefined,
|
|
251
|
+
});
|
|
252
|
+
return result.totalMonthlyCost;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Format a CostEstimate as a Markdown report.
|
|
257
|
+
*/
|
|
258
|
+
formatAsMarkdown(estimate: CostEstimate): string {
|
|
259
|
+
const lines: string[] = [
|
|
260
|
+
`# Infrastructure Cost Estimate`,
|
|
261
|
+
``,
|
|
262
|
+
`**Environment:** ${estimate.environment}`,
|
|
263
|
+
`**Provider:** ${estimate.provider}`,
|
|
264
|
+
`**Generated:** ${estimate.generatedAt.toISOString()}`,
|
|
265
|
+
``,
|
|
266
|
+
`## Summary`,
|
|
267
|
+
``,
|
|
268
|
+
`| Metric | Value |`,
|
|
269
|
+
`|--------|-------|`,
|
|
270
|
+
`| Monthly Cost | $${estimate.totalMonthlyCost.toFixed(2)} |`,
|
|
271
|
+
`| Annual Cost | $${estimate.totalAnnualCost.toFixed(2)} |`,
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
if (estimate.budgetLimit !== undefined) {
|
|
275
|
+
lines.push(
|
|
276
|
+
`| Budget Limit | $${estimate.budgetLimit.toFixed(2)} |`,
|
|
277
|
+
`| Status | ${estimate.withinBudget ? 'Within Budget' : 'Over Budget'} |`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
lines.push(
|
|
282
|
+
``,
|
|
283
|
+
`## Component Breakdown`,
|
|
284
|
+
``,
|
|
285
|
+
`| Component | Base ($/mo) | Adjusted ($/mo) | Notes |`,
|
|
286
|
+
`|-----------|------------|----------------|-------|`
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
for (const b of estimate.breakdown) {
|
|
290
|
+
lines.push(
|
|
291
|
+
`| ${b.component} | $${b.baseMonthlyCost.toFixed(2)} | $${b.adjustedMonthlyCost.toFixed(2)} | ${b.notes} |`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (estimate.recommendations.length > 0) {
|
|
296
|
+
lines.push(``, `## Recommendations`, ``);
|
|
297
|
+
for (const rec of estimate.recommendations) {
|
|
298
|
+
lines.push(`- ${rec}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return lines.join('\n');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Generate cost optimisation recommendations based on the estimate inputs and results.
|
|
307
|
+
*/
|
|
308
|
+
private generateRecommendations(
|
|
309
|
+
input: CostEstimationInput,
|
|
310
|
+
breakdown: ComponentCostBreakdown[],
|
|
311
|
+
totalMonthlyCost: number
|
|
312
|
+
): string[] {
|
|
313
|
+
const recommendations: string[] = [];
|
|
314
|
+
const env = (input.environment || 'production').toLowerCase();
|
|
315
|
+
const components = input.components.map(c => c.toLowerCase());
|
|
316
|
+
|
|
317
|
+
// Production: Reserved instances
|
|
318
|
+
if (env === 'production' || env === 'prod') {
|
|
319
|
+
const hasExpensive = components.some(c => ['eks', 'rds', 'elasticache'].includes(c));
|
|
320
|
+
if (hasExpensive) {
|
|
321
|
+
recommendations.push(
|
|
322
|
+
'Consider Reserved Instances or Savings Plans for EKS nodes, RDS, and ElastiCache — ' +
|
|
323
|
+
'typically 30-40% savings over on-demand pricing with 1-year commitments.'
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Non-production: Single NAT gateway
|
|
329
|
+
if (env !== 'production' && env !== 'prod' && components.includes('vpc')) {
|
|
330
|
+
recommendations.push(
|
|
331
|
+
'Use a single NAT Gateway in non-production environments to save ~$32/mo per additional AZ.'
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// S3 lifecycle policies
|
|
336
|
+
if (components.includes('s3')) {
|
|
337
|
+
recommendations.push(
|
|
338
|
+
'Configure S3 Lifecycle policies to transition infrequently accessed objects to ' +
|
|
339
|
+
'S3 Intelligent-Tiering or Glacier; can reduce storage costs by 40-60%.'
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Development: spot instances
|
|
344
|
+
if (env === 'development' || env === 'dev') {
|
|
345
|
+
if (components.some(c => ['eks', 'ecs'].includes(c))) {
|
|
346
|
+
recommendations.push(
|
|
347
|
+
'Use Spot Instances for development EKS node groups and ECS Fargate Spot — ' +
|
|
348
|
+
'up to 90% savings with appropriate interruption handling.'
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// High overall cost warning
|
|
354
|
+
if (totalMonthlyCost > 1000) {
|
|
355
|
+
recommendations.push(
|
|
356
|
+
`Total monthly cost $${totalMonthlyCost.toFixed(2)} is significant. ` +
|
|
357
|
+
'Review instance types, enable autoscaling, and run AWS Cost Explorer or GCP Cost Management ' +
|
|
358
|
+
'to identify unexpected spend.'
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Lambda: evaluate if replacing always-on compute
|
|
363
|
+
if (
|
|
364
|
+
components.includes('lambda') &&
|
|
365
|
+
!components.includes('ecs') &&
|
|
366
|
+
!components.includes('eks')
|
|
367
|
+
) {
|
|
368
|
+
recommendations.push(
|
|
369
|
+
'Lambda-only architecture is cost-efficient at low request volumes. ' +
|
|
370
|
+
'Monitor concurrency limits and cold-start latency as traffic grows.'
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// EKS without VPC
|
|
375
|
+
if (components.includes('eks') && !components.includes('vpc')) {
|
|
376
|
+
recommendations.push(
|
|
377
|
+
'EKS clusters require a VPC. If using an existing VPC, ensure its NAT Gateway costs are ' +
|
|
378
|
+
'accounted for separately (typically +$32/mo per AZ).'
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return recommendations;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Resolve base cost for a component, respecting custom overrides.
|
|
387
|
+
*/
|
|
388
|
+
private resolveBaseCost(component: string, customCosts?: Record<string, number>): number {
|
|
389
|
+
if (customCosts && component in customCosts) {
|
|
390
|
+
return customCosts[component];
|
|
391
|
+
}
|
|
392
|
+
return BASE_COMPONENT_COSTS[component.toLowerCase()] ?? 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Resolve environment multiplier from an environment string.
|
|
397
|
+
*/
|
|
398
|
+
private resolveEnvironmentMultiplier(environment: string): number {
|
|
399
|
+
const key = environment.toLowerCase();
|
|
400
|
+
return ENVIRONMENT_MULTIPLIERS[key] ?? 1.0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Resolve regional multiplier from a region string.
|
|
405
|
+
*/
|
|
406
|
+
private resolveRegionalMultiplier(region: string): number {
|
|
407
|
+
return REGIONAL_MULTIPLIERS[region] ?? 1.0;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Return the default region for a given provider.
|
|
412
|
+
*/
|
|
413
|
+
private defaultRegion(provider: string): string {
|
|
414
|
+
switch (provider.toLowerCase()) {
|
|
415
|
+
case 'gcp':
|
|
416
|
+
return 'us-central1';
|
|
417
|
+
case 'azure':
|
|
418
|
+
return 'eastus';
|
|
419
|
+
case 'aws':
|
|
420
|
+
default:
|
|
421
|
+
return 'us-east-1';
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Get the base cost table (useful for display or testing).
|
|
427
|
+
*/
|
|
428
|
+
getBaseCostTable(): Record<string, number> {
|
|
429
|
+
return { ...BASE_COMPONENT_COSTS };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get the regional multiplier table (useful for display or testing).
|
|
434
|
+
*/
|
|
435
|
+
getRegionalMultiplierTable(): Record<string, number> {
|
|
436
|
+
return { ...REGIONAL_MULTIPLIERS };
|
|
437
|
+
}
|
|
438
|
+
}
|