@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.
Files changed (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * GCP Static Pricing Lookup
3
+ *
4
+ * Monthly on-demand pricing based on us-central1 as of 2024.
5
+ * These are approximate list prices used for quick estimation.
6
+ * Install Infracost for real-time, region-aware pricing.
7
+ */
8
+
9
+ import type { TerraformResource } from '../parsers/types';
10
+ import type { PricingResult } from './index';
11
+
12
+ const HOURS_PER_MONTH = 730;
13
+
14
+ // ------------------------------------------------------------------
15
+ // Compute Engine machine type pricing (on-demand, us-central1)
16
+ // ------------------------------------------------------------------
17
+ const GCE_PRICING: Record<string, number> = {
18
+ // E2 shared-core
19
+ 'e2-micro': 6.11,
20
+ 'e2-small': 12.23,
21
+ 'e2-medium': 24.46,
22
+ // E2 standard
23
+ 'e2-standard-2': 48.92,
24
+ 'e2-standard-4': 97.83,
25
+ 'e2-standard-8': 195.67,
26
+ 'e2-standard-16': 391.34,
27
+ // N1 standard
28
+ 'n1-standard-1': 34.67,
29
+ 'n1-standard-2': 69.35,
30
+ 'n1-standard-4': 138.7,
31
+ 'n1-standard-8': 277.4,
32
+ 'n1-standard-16': 554.79,
33
+ // N2 standard
34
+ 'n2-standard-2': 71.54,
35
+ 'n2-standard-4': 143.08,
36
+ 'n2-standard-8': 286.16,
37
+ 'n2-standard-16': 572.32,
38
+ // N2D standard (AMD)
39
+ 'n2d-standard-2': 62.27,
40
+ 'n2d-standard-4': 124.54,
41
+ 'n2d-standard-8': 249.08,
42
+ // C2 compute-optimized
43
+ 'c2-standard-4': 152.44,
44
+ 'c2-standard-8': 304.88,
45
+ 'c2-standard-16': 609.77,
46
+ // M1 memory-optimized
47
+ 'm1-megamem-96': 7636.69,
48
+ // N1 highmem
49
+ 'n1-highmem-2': 93.46,
50
+ 'n1-highmem-4': 186.93,
51
+ 'n1-highmem-8': 373.85,
52
+ // F1/G1 (micro/small)
53
+ 'f1-micro': 3.88,
54
+ 'g1-small': 13.13,
55
+ };
56
+
57
+ // ------------------------------------------------------------------
58
+ // Cloud SQL pricing (on-demand, us-central1)
59
+ // ------------------------------------------------------------------
60
+ const CLOUD_SQL_PRICING: Record<string, number> = {
61
+ 'db-f1-micro': 7.67,
62
+ 'db-g1-small': 25.55,
63
+ 'db-n1-standard-1': 51.1,
64
+ 'db-n1-standard-2': 102.2,
65
+ 'db-n1-standard-4': 204.4,
66
+ 'db-n1-standard-8': 408.8,
67
+ 'db-n1-standard-16': 817.6,
68
+ 'db-n1-highmem-2': 117.8,
69
+ 'db-n1-highmem-4': 235.61,
70
+ 'db-n1-highmem-8': 471.22,
71
+ 'db-custom-1-3840': 51.1,
72
+ 'db-custom-2-7680': 102.2,
73
+ 'db-custom-4-15360': 204.4,
74
+ };
75
+
76
+ // ------------------------------------------------------------------
77
+ // Persistent Disk pricing (per GB/month, us-central1)
78
+ // ------------------------------------------------------------------
79
+ const DISK_PRICING: Record<string, number> = {
80
+ 'pd-standard': 0.04,
81
+ 'pd-balanced': 0.1,
82
+ 'pd-ssd': 0.17,
83
+ 'pd-extreme': 0.125,
84
+ };
85
+
86
+ /**
87
+ * Look up the estimated monthly price for a GCP Terraform resource.
88
+ */
89
+ export function getGCPPrice(resource: TerraformResource): PricingResult | null {
90
+ const { type, attributes } = resource;
91
+
92
+ switch (type) {
93
+ // ----- Compute -----
94
+ case 'google_compute_instance': {
95
+ const machineType = attributes.machine_type || 'e2-medium';
96
+ // machine_type can be a full URL or just the name
97
+ const typeName = machineType.split('/').pop() || machineType;
98
+ const price = GCE_PRICING[typeName];
99
+ if (!price) {
100
+ return {
101
+ monthlyCost: 24.46,
102
+ hourlyCost: 24.46 / HOURS_PER_MONTH,
103
+ description: `GCE ${typeName} (estimated, type not in lookup table)`,
104
+ };
105
+ }
106
+ return {
107
+ monthlyCost: price,
108
+ hourlyCost: price / HOURS_PER_MONTH,
109
+ unit: 'hours',
110
+ description: `GCE ${typeName}`,
111
+ };
112
+ }
113
+
114
+ case 'google_compute_instance_template': {
115
+ const machineType = attributes.machine_type || 'e2-medium';
116
+ const typeName = machineType.split('/').pop() || machineType;
117
+ return {
118
+ monthlyCost: 0,
119
+ hourlyCost: 0,
120
+ description: `Instance template ${typeName} (cost depends on instance group)`,
121
+ };
122
+ }
123
+
124
+ case 'google_compute_instance_group_manager':
125
+ case 'google_compute_region_instance_group_manager': {
126
+ const targetSize = attributes.target_size || 1;
127
+ const perInstance = 24.46; // default e2-medium
128
+ return {
129
+ monthlyCost: targetSize * perInstance,
130
+ hourlyCost: (targetSize * perInstance) / HOURS_PER_MONTH,
131
+ quantity: targetSize,
132
+ unit: 'instances',
133
+ description: `Instance group manager (${targetSize} instances, estimated e2-medium)`,
134
+ };
135
+ }
136
+
137
+ // ----- Database -----
138
+ case 'google_sql_database_instance': {
139
+ const tier = attributes.tier || attributes['settings.tier'] || 'db-n1-standard-1';
140
+ const price = CLOUD_SQL_PRICING[tier] || 51.1;
141
+ const diskSize = attributes.disk_size || attributes['settings.disk_size'] || 10;
142
+ const diskType = attributes.disk_type || attributes['settings.disk_type'] || 'PD_SSD';
143
+ const diskRate = diskType === 'PD_HDD' ? 0.09 : 0.17;
144
+ const diskCost = diskSize * diskRate;
145
+ const ha =
146
+ attributes.availability_type === 'REGIONAL' ||
147
+ attributes['settings.availability_type'] === 'REGIONAL';
148
+ const multiplier = ha ? 2 : 1;
149
+ return {
150
+ monthlyCost: price * multiplier + diskCost,
151
+ hourlyCost: (price * multiplier) / HOURS_PER_MONTH,
152
+ description: `Cloud SQL ${tier}${ha ? ' HA' : ''} + ${diskSize}GB`,
153
+ };
154
+ }
155
+
156
+ case 'google_sql_database': {
157
+ return {
158
+ monthlyCost: 0,
159
+ hourlyCost: 0,
160
+ description: 'Cloud SQL database (no additional cost)',
161
+ };
162
+ }
163
+
164
+ case 'google_spanner_instance': {
165
+ const numNodes = attributes.num_nodes || 1;
166
+ // $0.90/node-hour
167
+ const cost = numNodes * 0.9 * HOURS_PER_MONTH;
168
+ return {
169
+ monthlyCost: cost,
170
+ hourlyCost: numNodes * 0.9,
171
+ quantity: numNodes,
172
+ unit: 'nodes',
173
+ description: `Cloud Spanner (${numNodes} nodes)`,
174
+ };
175
+ }
176
+
177
+ // ----- Storage -----
178
+ case 'google_storage_bucket': {
179
+ // GCS standard: ~$0.020/GB, estimate 100GB
180
+ return {
181
+ monthlyCost: 2.0,
182
+ hourlyCost: 0,
183
+ unit: 'GB',
184
+ description: 'GCS Standard (estimated 100GB baseline)',
185
+ };
186
+ }
187
+
188
+ case 'google_compute_disk': {
189
+ const diskType = attributes.type || 'pd-balanced';
190
+ const size = attributes.size || 10;
191
+ const pricePerGB = DISK_PRICING[diskType] || 0.1;
192
+ return {
193
+ monthlyCost: size * pricePerGB,
194
+ hourlyCost: 0,
195
+ quantity: size,
196
+ unit: 'GB',
197
+ description: `Persistent Disk ${diskType} ${size}GB`,
198
+ };
199
+ }
200
+
201
+ // ----- Networking -----
202
+ case 'google_compute_forwarding_rule':
203
+ case 'google_compute_global_forwarding_rule': {
204
+ // Forwarding rule: ~$0.025/hr
205
+ return {
206
+ monthlyCost: 18.25,
207
+ hourlyCost: 0.025,
208
+ description: 'Forwarding rule',
209
+ };
210
+ }
211
+
212
+ case 'google_compute_router_nat': {
213
+ // Cloud NAT: ~$0.044/hr per gateway + data processing
214
+ const fixedCost = 0.044 * HOURS_PER_MONTH;
215
+ return {
216
+ monthlyCost: fixedCost + 32.12,
217
+ hourlyCost: 0.044,
218
+ description: 'Cloud NAT (fixed + estimated data processing)',
219
+ };
220
+ }
221
+
222
+ case 'google_compute_address':
223
+ case 'google_compute_global_address': {
224
+ // Static IP: free when in use, $0.010/hr when idle
225
+ return {
226
+ monthlyCost: 7.3,
227
+ hourlyCost: 0.01,
228
+ description: 'Static IP (cost if unused)',
229
+ };
230
+ }
231
+
232
+ // ----- Containers -----
233
+ case 'google_container_cluster': {
234
+ // GKE Autopilot: variable; Standard: $0.10/hr cluster management
235
+ const isAutopilot = attributes.enable_autopilot === true;
236
+ if (isAutopilot) {
237
+ return {
238
+ monthlyCost: 73.0,
239
+ hourlyCost: 0.1,
240
+ description: 'GKE Autopilot cluster management',
241
+ };
242
+ }
243
+ return {
244
+ monthlyCost: 73.0,
245
+ hourlyCost: 0.1,
246
+ description: 'GKE Standard cluster management',
247
+ };
248
+ }
249
+
250
+ case 'google_container_node_pool': {
251
+ const nodeCount = attributes.node_count || attributes.initial_node_count || 1;
252
+ const machineType = attributes['node_config.machine_type'] || 'e2-medium';
253
+ const price = GCE_PRICING[machineType] || 24.46;
254
+ return {
255
+ monthlyCost: price * nodeCount,
256
+ hourlyCost: (price * nodeCount) / HOURS_PER_MONTH,
257
+ quantity: nodeCount,
258
+ unit: 'nodes',
259
+ description: `GKE node pool (${nodeCount}x ${machineType})`,
260
+ };
261
+ }
262
+
263
+ case 'google_cloud_run_service':
264
+ case 'google_cloud_run_v2_service': {
265
+ return {
266
+ monthlyCost: 0,
267
+ hourlyCost: 0,
268
+ description: 'Cloud Run (usage-based, $0 at rest)',
269
+ };
270
+ }
271
+
272
+ case 'google_artifact_registry_repository': {
273
+ return {
274
+ monthlyCost: 1.0,
275
+ hourlyCost: 0,
276
+ description: 'Artifact Registry (estimated 10GB images)',
277
+ };
278
+ }
279
+
280
+ // ----- Serverless -----
281
+ case 'google_cloudfunctions_function':
282
+ case 'google_cloudfunctions2_function': {
283
+ return {
284
+ monthlyCost: 0,
285
+ hourlyCost: 0,
286
+ description: 'Cloud Function (usage-based, $0 at rest)',
287
+ };
288
+ }
289
+
290
+ case 'google_pubsub_topic': {
291
+ return {
292
+ monthlyCost: 0,
293
+ hourlyCost: 0,
294
+ description: 'Pub/Sub topic (usage-based, first 10GB free)',
295
+ };
296
+ }
297
+
298
+ case 'google_pubsub_subscription': {
299
+ return {
300
+ monthlyCost: 0,
301
+ hourlyCost: 0,
302
+ description: 'Pub/Sub subscription (usage-based)',
303
+ };
304
+ }
305
+
306
+ // ----- Data -----
307
+ case 'google_bigquery_dataset': {
308
+ return {
309
+ monthlyCost: 0,
310
+ hourlyCost: 0,
311
+ description: 'BigQuery dataset (storage/query usage-based)',
312
+ };
313
+ }
314
+
315
+ case 'google_bigquery_table': {
316
+ return {
317
+ monthlyCost: 0,
318
+ hourlyCost: 0,
319
+ description: 'BigQuery table (storage/query usage-based)',
320
+ };
321
+ }
322
+
323
+ case 'google_dataflow_job': {
324
+ // Highly variable; estimate a small job
325
+ return {
326
+ monthlyCost: 50.0,
327
+ hourlyCost: 50.0 / HOURS_PER_MONTH,
328
+ description: 'Dataflow job (estimated small workload)',
329
+ };
330
+ }
331
+
332
+ case 'google_redis_instance': {
333
+ const memorySizeGb = attributes.memory_size_gb || 1;
334
+ // Basic tier: ~$0.049/GB-hour
335
+ const cost = memorySizeGb * 0.049 * HOURS_PER_MONTH;
336
+ return {
337
+ monthlyCost: cost,
338
+ hourlyCost: memorySizeGb * 0.049,
339
+ quantity: memorySizeGb,
340
+ unit: 'GB',
341
+ description: `Memorystore Redis (${memorySizeGb}GB)`,
342
+ };
343
+ }
344
+
345
+ // ----- Monitoring / Logging -----
346
+ case 'google_monitoring_alert_policy': {
347
+ return { monthlyCost: 0, hourlyCost: 0, description: 'Monitoring alert (no direct cost)' };
348
+ }
349
+
350
+ case 'google_logging_metric': {
351
+ return { monthlyCost: 0, hourlyCost: 0, description: 'Logging metric (no direct cost)' };
352
+ }
353
+
354
+ // ----- Identity / Security (no direct cost) -----
355
+ case 'google_compute_network':
356
+ case 'google_compute_subnetwork':
357
+ case 'google_compute_firewall':
358
+ case 'google_compute_route':
359
+ case 'google_compute_router':
360
+ case 'google_project_iam_member':
361
+ case 'google_project_iam_binding':
362
+ case 'google_project_iam_policy':
363
+ case 'google_service_account':
364
+ case 'google_service_account_iam_member':
365
+ case 'google_kms_key_ring':
366
+ case 'google_kms_crypto_key':
367
+ case 'google_dns_managed_zone':
368
+ case 'google_dns_record_set':
369
+ case 'google_project_service': {
370
+ return { monthlyCost: 0, hourlyCost: 0, description: 'No direct cost' };
371
+ }
372
+
373
+ // ----- VPN -----
374
+ case 'google_compute_vpn_gateway':
375
+ case 'google_compute_ha_vpn_gateway': {
376
+ // VPN gateway: ~$0.075/hr
377
+ return {
378
+ monthlyCost: 0.075 * HOURS_PER_MONTH,
379
+ hourlyCost: 0.075,
380
+ description: 'Cloud VPN gateway',
381
+ };
382
+ }
383
+
384
+ case 'google_compute_vpn_tunnel': {
385
+ // VPN tunnel: ~$0.075/hr
386
+ return {
387
+ monthlyCost: 0.075 * HOURS_PER_MONTH,
388
+ hourlyCost: 0.075,
389
+ description: 'VPN tunnel',
390
+ };
391
+ }
392
+
393
+ default:
394
+ return null;
395
+ }
396
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Unified Pricing Lookup
3
+ *
4
+ * Routes resource pricing lookups to the appropriate cloud provider module.
5
+ */
6
+
7
+ import type { TerraformResource } from '../parsers/types';
8
+ import { getAWSPrice } from './aws';
9
+ import { getGCPPrice } from './gcp';
10
+ import { getAzurePrice } from './azure';
11
+
12
+ export interface PricingResult {
13
+ /** Estimated monthly cost in USD */
14
+ monthlyCost: number;
15
+ /** Estimated hourly cost in USD */
16
+ hourlyCost: number;
17
+ /** Quantity (e.g. GB, instances, nodes) */
18
+ quantity?: number;
19
+ /** Unit label for the quantity */
20
+ unit?: string;
21
+ /** Human-readable pricing description */
22
+ description?: string;
23
+ }
24
+
25
+ /**
26
+ * Get the estimated price for a Terraform resource.
27
+ * Returns null if the resource type is not recognized by any provider module.
28
+ */
29
+ export function getResourcePrice(resource: TerraformResource): PricingResult | null {
30
+ switch (resource.provider) {
31
+ case 'aws':
32
+ return getAWSPrice(resource);
33
+ case 'gcp':
34
+ return getGCPPrice(resource);
35
+ case 'azure':
36
+ return getAzurePrice(resource);
37
+ default:
38
+ return null;
39
+ }
40
+ }
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Demo Command
3
+ *
4
+ * Run demo scenarios for presentations and tutorials
5
+ */
6
+
7
+ import { logger } from '../utils';
8
+ import { ui } from '../wizard/ui';
9
+ import { select } from '../wizard/prompts';
10
+ import {
11
+ getScenarios,
12
+ getScenario,
13
+ runScenario,
14
+ type DemoScenario,
15
+ type DemoOptions as DemoRunOptions,
16
+ } from '../demo';
17
+
18
+ /**
19
+ * Demo command options
20
+ */
21
+ export interface DemoOptions {
22
+ /** Scenario ID to run */
23
+ scenario?: string;
24
+ /** List available scenarios */
25
+ list?: boolean;
26
+ /** Interactive mode (prompt for each step) */
27
+ interactive?: boolean;
28
+ /** Speed: slow, normal, fast */
29
+ speed?: 'slow' | 'normal' | 'fast';
30
+ /** Dry run - don't execute commands */
31
+ dryRun?: boolean;
32
+ /** Show all output (verbose) */
33
+ verbose?: boolean;
34
+ /** Category filter */
35
+ category?: string;
36
+ /** Tag filter */
37
+ tag?: string;
38
+ }
39
+
40
+ /**
41
+ * Parse demo command options from args
42
+ */
43
+ export function parseDemoOptions(args: string[]): DemoOptions {
44
+ const options: DemoOptions = {};
45
+
46
+ for (let i = 0; i < args.length; i++) {
47
+ const arg = args[i];
48
+
49
+ if (arg === '--list' || arg === '-l') {
50
+ options.list = true;
51
+ } else if (arg === '--interactive' || arg === '-i') {
52
+ options.interactive = true;
53
+ } else if (arg === '--dry-run' || arg === '-n') {
54
+ options.dryRun = true;
55
+ } else if (arg === '--verbose' || arg === '-v') {
56
+ options.verbose = true;
57
+ } else if (arg === '--speed' || arg === '-s') {
58
+ const speed = args[++i];
59
+ if (speed === 'slow' || speed === 'normal' || speed === 'fast') {
60
+ options.speed = speed;
61
+ }
62
+ } else if (arg === '--category' || arg === '-c') {
63
+ options.category = args[++i];
64
+ } else if (arg === '--tag' || arg === '-t') {
65
+ options.tag = args[++i];
66
+ } else if (!arg.startsWith('-') && !options.scenario) {
67
+ options.scenario = arg;
68
+ }
69
+ }
70
+
71
+ return options;
72
+ }
73
+
74
+ /**
75
+ * Main demo command
76
+ */
77
+ export async function demoCommand(options: DemoOptions): Promise<void> {
78
+ logger.info('Running demo command', { options });
79
+
80
+ try {
81
+ // Auto-configure when NIMBUS_DEMO_MODE is set
82
+ if (process.env.NIMBUS_DEMO_MODE === 'true') {
83
+ ui.info('Demo mode enabled via NIMBUS_DEMO_MODE');
84
+ options.dryRun = true;
85
+ options.speed = options.speed || 'fast';
86
+ }
87
+
88
+ // Get all scenarios
89
+ let scenarios = getScenarios();
90
+
91
+ // Filter by category
92
+ if (options.category) {
93
+ scenarios = scenarios.filter(s => s.category === options.category);
94
+ }
95
+
96
+ // Filter by tag
97
+ if (options.tag) {
98
+ scenarios = scenarios.filter(s => s.tags?.includes(options.tag!));
99
+ }
100
+
101
+ // List scenarios
102
+ if (options.list) {
103
+ displayScenarioList(scenarios);
104
+ return;
105
+ }
106
+
107
+ // If no scenario specified, show interactive picker
108
+ if (!options.scenario) {
109
+ if (scenarios.length === 0) {
110
+ ui.warning('No scenarios available');
111
+ return;
112
+ }
113
+
114
+ const selectedId = await select<string>({
115
+ message: 'Select a demo scenario:',
116
+ options: scenarios.map(s => ({
117
+ label: s.name,
118
+ value: s.id,
119
+ description: `${s.category} - ${s.description}`,
120
+ })),
121
+ });
122
+
123
+ if (!selectedId) {
124
+ ui.warning('No scenario selected');
125
+ return;
126
+ }
127
+
128
+ options.scenario = selectedId;
129
+ }
130
+
131
+ // Get the scenario
132
+ const scenario = getScenario(options.scenario);
133
+ if (!scenario) {
134
+ ui.error(`Scenario not found: ${options.scenario}`);
135
+ ui.newLine();
136
+ ui.info('Available scenarios:');
137
+ displayScenarioList(scenarios);
138
+ return;
139
+ }
140
+
141
+ // Run the scenario
142
+ const runOptions: DemoRunOptions = {
143
+ interactive: options.interactive ?? true,
144
+ speed: options.speed ?? 'normal',
145
+ dryRun: options.dryRun ?? true, // Default to dry run for safety
146
+ verbose: options.verbose ?? false,
147
+ };
148
+
149
+ await runScenario(scenario, runOptions);
150
+ } catch (error: unknown) {
151
+ const message = error instanceof Error ? error.message : 'Unknown error';
152
+ logger.error('Demo command failed', { error: message });
153
+ ui.error(`Demo failed: ${message}`);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Display list of available scenarios
159
+ */
160
+ function displayScenarioList(scenarios: DemoScenario[]): void {
161
+ ui.newLine();
162
+ ui.print(ui.bold('Available Demo Scenarios'));
163
+ ui.newLine();
164
+
165
+ if (scenarios.length === 0) {
166
+ ui.print(' No scenarios found');
167
+ return;
168
+ }
169
+
170
+ // Group by category
171
+ const byCategory = new Map<string, DemoScenario[]>();
172
+ for (const scenario of scenarios) {
173
+ const list = byCategory.get(scenario.category) || [];
174
+ list.push(scenario);
175
+ byCategory.set(scenario.category, list);
176
+ }
177
+
178
+ // Display each category
179
+ for (const [category, categoryScenarios] of byCategory) {
180
+ ui.print(ui.color(` ${formatCategory(category)}`, 'cyan'));
181
+
182
+ for (const scenario of categoryScenarios) {
183
+ const duration = scenario.duration ? `${scenario.duration} min` : '';
184
+ const tags = scenario.tags?.length ? ui.dim(`[${scenario.tags.join(', ')}]`) : '';
185
+
186
+ ui.print(` ${ui.bold(scenario.id)}`);
187
+ ui.print(` ${scenario.name}`);
188
+ ui.print(` ${ui.dim(scenario.description)}`);
189
+ if (duration || tags) {
190
+ ui.print(` ${duration} ${tags}`);
191
+ }
192
+ ui.newLine();
193
+ }
194
+ }
195
+
196
+ ui.newLine();
197
+ ui.print('Usage:');
198
+ ui.print(' nimbus demo <scenario-id> Run a specific scenario');
199
+ ui.print(' nimbus demo --interactive Interactive mode (pause between steps)');
200
+ ui.print(' nimbus demo --dry-run Show commands without executing');
201
+ ui.print(' nimbus demo --speed slow|fast Control demo speed');
202
+ ui.print(' nimbus demo --category terraform Filter by category');
203
+ ui.print(' nimbus demo --tag aws Filter by tag');
204
+ ui.newLine();
205
+ }
206
+
207
+ /**
208
+ * Format category name for display
209
+ */
210
+ function formatCategory(category: string): string {
211
+ const categoryNames: Record<string, string> = {
212
+ terraform: 'Terraform Demos',
213
+ kubernetes: 'Kubernetes Demos',
214
+ helm: 'Helm Demos',
215
+ aws: 'AWS Demos',
216
+ 'full-journey': 'Full Journey Demos',
217
+ tutorial: 'Tutorials',
218
+ };
219
+
220
+ return categoryNames[category] || category;
221
+ }
222
+
223
+ /**
224
+ * Run a specific demo scenario by ID
225
+ */
226
+ export async function runDemoScenario(
227
+ scenarioId: string,
228
+ options: Partial<DemoOptions> = {}
229
+ ): Promise<void> {
230
+ const scenario = getScenario(scenarioId);
231
+ if (!scenario) {
232
+ throw new Error(`Scenario not found: ${scenarioId}`);
233
+ }
234
+
235
+ const runOptions: DemoRunOptions = {
236
+ interactive: options.interactive ?? true,
237
+ speed: options.speed ?? 'normal',
238
+ dryRun: options.dryRun ?? true,
239
+ verbose: options.verbose ?? false,
240
+ };
241
+
242
+ await runScenario(scenario, runOptions);
243
+ }
244
+
245
+ /**
246
+ * List all demo scenario IDs
247
+ */
248
+ export function listDemoScenarios(): string[] {
249
+ return getScenarios().map(s => s.id);
250
+ }