@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,393 @@
1
+ /**
2
+ * AWS VPC Commands
3
+ *
4
+ * VPC and networking operations
5
+ *
6
+ * Usage:
7
+ * nimbus aws vpc list
8
+ * nimbus aws vpc describe <vpc-id>
9
+ */
10
+
11
+ import { logger } from '../../utils';
12
+ import { ui } from '../../wizard/ui';
13
+ import type { AwsCommandOptions } from './index';
14
+
15
+ interface VPC {
16
+ VpcId: string;
17
+ CidrBlock: string;
18
+ State: string;
19
+ IsDefault: boolean;
20
+ Tags?: Array<{ Key: string; Value: string }>;
21
+ }
22
+
23
+ interface Subnet {
24
+ SubnetId: string;
25
+ VpcId: string;
26
+ CidrBlock: string;
27
+ AvailabilityZone: string;
28
+ State: string;
29
+ MapPublicIpOnLaunch: boolean;
30
+ Tags?: Array<{ Key: string; Value: string }>;
31
+ }
32
+
33
+ interface SecurityGroup {
34
+ GroupId: string;
35
+ GroupName: string;
36
+ Description: string;
37
+ VpcId: string;
38
+ }
39
+
40
+ /**
41
+ * VPC command router
42
+ */
43
+ export async function vpcCommand(
44
+ action: string,
45
+ args: string[],
46
+ options: AwsCommandOptions
47
+ ): Promise<void> {
48
+ logger.info('Running VPC command', { action, args, options });
49
+
50
+ switch (action) {
51
+ case 'list':
52
+ case 'ls':
53
+ await listVPCs(options);
54
+ break;
55
+
56
+ case 'describe':
57
+ if (!args[0]) {
58
+ ui.error('VPC ID is required');
59
+ ui.print('Usage: nimbus aws vpc describe <vpc-id>');
60
+ return;
61
+ }
62
+ await describeVPC(args[0], options);
63
+ break;
64
+
65
+ case 'subnets':
66
+ await listSubnets(args[0], options);
67
+ break;
68
+
69
+ case 'security-groups':
70
+ case 'sg':
71
+ await listSecurityGroups(args[0], options);
72
+ break;
73
+
74
+ default:
75
+ showVpcHelp();
76
+ break;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * List all VPCs
82
+ */
83
+ async function listVPCs(options: AwsCommandOptions): Promise<void> {
84
+ ui.header('VPCs');
85
+
86
+ ui.startSpinner({ message: 'Fetching VPCs...' });
87
+
88
+ try {
89
+ const result = await runAwsCommand<{ Vpcs: VPC[] }>('ec2 describe-vpcs', options);
90
+
91
+ const vpcs = result.Vpcs || [];
92
+
93
+ ui.stopSpinnerSuccess(`Found ${vpcs.length} VPC(s)`);
94
+ ui.newLine();
95
+
96
+ if (vpcs.length === 0) {
97
+ ui.info('No VPCs found');
98
+ return;
99
+ }
100
+
101
+ // Display table
102
+ const headers = ['VPC ID', 'Name', 'CIDR', 'State', 'Default'];
103
+ const rows = vpcs.map(vpc => {
104
+ const nameTag = vpc.Tags?.find(t => t.Key === 'Name');
105
+ return [
106
+ vpc.VpcId,
107
+ nameTag?.Value || '-',
108
+ vpc.CidrBlock,
109
+ vpc.State,
110
+ vpc.IsDefault ? 'Yes' : 'No',
111
+ ];
112
+ });
113
+
114
+ displayTable(headers, rows);
115
+ } catch (error) {
116
+ ui.stopSpinnerFail('Failed to list VPCs');
117
+ ui.error((error as Error).message);
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Describe a specific VPC
123
+ */
124
+ async function describeVPC(vpcId: string, options: AwsCommandOptions): Promise<void> {
125
+ ui.header(`VPC: ${vpcId}`);
126
+
127
+ ui.startSpinner({ message: 'Fetching VPC details...' });
128
+
129
+ try {
130
+ const { execFile } = await import('child_process');
131
+ const { promisify } = await import('util');
132
+ const execFileAsync = promisify(execFile);
133
+
134
+ // Get VPC details
135
+ const vpcArgs = ['ec2', 'describe-vpcs', '--vpc-ids', vpcId, '--output', 'json'];
136
+ if (options.profile) {
137
+ vpcArgs.push('--profile', options.profile);
138
+ }
139
+ if (options.region) {
140
+ vpcArgs.push('--region', options.region);
141
+ }
142
+
143
+ const { stdout: vpcOutput } = await execFileAsync('aws', vpcArgs);
144
+ const vpcData = JSON.parse(vpcOutput);
145
+ const vpc = vpcData.Vpcs[0];
146
+
147
+ // Get subnets
148
+ const subnetArgs = [
149
+ 'ec2',
150
+ 'describe-subnets',
151
+ '--filters',
152
+ `Name=vpc-id,Values=${vpcId}`,
153
+ '--output',
154
+ 'json',
155
+ ];
156
+ if (options.profile) {
157
+ subnetArgs.push('--profile', options.profile);
158
+ }
159
+ if (options.region) {
160
+ subnetArgs.push('--region', options.region);
161
+ }
162
+
163
+ const { stdout: subnetOutput } = await execFileAsync('aws', subnetArgs);
164
+ const subnetData = JSON.parse(subnetOutput);
165
+ const subnets = subnetData.Subnets || [];
166
+
167
+ // Get security groups
168
+ const sgArgs = [
169
+ 'ec2',
170
+ 'describe-security-groups',
171
+ '--filters',
172
+ `Name=vpc-id,Values=${vpcId}`,
173
+ '--output',
174
+ 'json',
175
+ ];
176
+ if (options.profile) {
177
+ sgArgs.push('--profile', options.profile);
178
+ }
179
+ if (options.region) {
180
+ sgArgs.push('--region', options.region);
181
+ }
182
+
183
+ const { stdout: sgOutput } = await execFileAsync('aws', sgArgs);
184
+ const sgData = JSON.parse(sgOutput);
185
+ const securityGroups = sgData.SecurityGroups || [];
186
+
187
+ ui.stopSpinnerSuccess('VPC details retrieved');
188
+ ui.newLine();
189
+
190
+ // Display VPC details
191
+ const nameTag = vpc.Tags?.find((t: { Key: string }) => t.Key === 'Name');
192
+ ui.print(ui.bold('VPC Details:'));
193
+ ui.newLine();
194
+ ui.print(` VPC ID: ${vpc.VpcId}`);
195
+ ui.print(` Name: ${nameTag?.Value || '-'}`);
196
+ ui.print(` CIDR Block: ${vpc.CidrBlock}`);
197
+ ui.print(` State: ${vpc.State}`);
198
+ ui.print(` Default: ${vpc.IsDefault ? 'Yes' : 'No'}`);
199
+
200
+ // Display subnets
201
+ if (subnets.length > 0) {
202
+ ui.newLine();
203
+ ui.print(ui.bold(`Subnets (${subnets.length}):`));
204
+ ui.newLine();
205
+
206
+ for (const subnet of subnets) {
207
+ const subnetName =
208
+ subnet.Tags?.find((t: { Key: string }) => t.Key === 'Name')?.Value || '-';
209
+ const publicLabel = subnet.MapPublicIpOnLaunch
210
+ ? ui.color('public', 'green')
211
+ : ui.color('private', 'yellow');
212
+ ui.print(
213
+ ` ${subnet.SubnetId} ${subnetName.padEnd(20)} ${subnet.CidrBlock.padEnd(18)} ${subnet.AvailabilityZone} ${publicLabel}`
214
+ );
215
+ }
216
+ }
217
+
218
+ // Display security groups
219
+ if (securityGroups.length > 0) {
220
+ ui.newLine();
221
+ ui.print(ui.bold(`Security Groups (${securityGroups.length}):`));
222
+ ui.newLine();
223
+
224
+ for (const sg of securityGroups) {
225
+ ui.print(` ${sg.GroupId} ${sg.GroupName.padEnd(25)} ${sg.Description.substring(0, 40)}`);
226
+ }
227
+ }
228
+ } catch (error) {
229
+ ui.stopSpinnerFail('Failed to describe VPC');
230
+ ui.error((error as Error).message);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * List subnets
236
+ */
237
+ async function listSubnets(vpcId: string | undefined, options: AwsCommandOptions): Promise<void> {
238
+ ui.header('Subnets');
239
+
240
+ ui.startSpinner({ message: 'Fetching subnets...' });
241
+
242
+ try {
243
+ let command = 'ec2 describe-subnets';
244
+ if (vpcId) {
245
+ command += ` --filters Name=vpc-id,Values=${vpcId}`;
246
+ }
247
+
248
+ const result = await runAwsCommand<{ Subnets: Subnet[] }>(command, options);
249
+
250
+ const subnets = result.Subnets || [];
251
+
252
+ ui.stopSpinnerSuccess(`Found ${subnets.length} subnet(s)`);
253
+ ui.newLine();
254
+
255
+ if (subnets.length === 0) {
256
+ ui.info('No subnets found');
257
+ return;
258
+ }
259
+
260
+ // Display table
261
+ const headers = ['Subnet ID', 'Name', 'VPC ID', 'CIDR', 'AZ', 'Public'];
262
+ const rows = subnets.map(subnet => {
263
+ const nameTag = subnet.Tags?.find(t => t.Key === 'Name');
264
+ return [
265
+ subnet.SubnetId,
266
+ nameTag?.Value || '-',
267
+ subnet.VpcId,
268
+ subnet.CidrBlock,
269
+ subnet.AvailabilityZone,
270
+ subnet.MapPublicIpOnLaunch ? 'Yes' : 'No',
271
+ ];
272
+ });
273
+
274
+ displayTable(headers, rows);
275
+ } catch (error) {
276
+ ui.stopSpinnerFail('Failed to list subnets');
277
+ ui.error((error as Error).message);
278
+ }
279
+ }
280
+
281
+ /**
282
+ * List security groups
283
+ */
284
+ async function listSecurityGroups(
285
+ vpcId: string | undefined,
286
+ options: AwsCommandOptions
287
+ ): Promise<void> {
288
+ ui.header('Security Groups');
289
+
290
+ ui.startSpinner({ message: 'Fetching security groups...' });
291
+
292
+ try {
293
+ let command = 'ec2 describe-security-groups';
294
+ if (vpcId) {
295
+ command += ` --filters Name=vpc-id,Values=${vpcId}`;
296
+ }
297
+
298
+ const result = await runAwsCommand<{ SecurityGroups: SecurityGroup[] }>(command, options);
299
+
300
+ const securityGroups = result.SecurityGroups || [];
301
+
302
+ ui.stopSpinnerSuccess(`Found ${securityGroups.length} security group(s)`);
303
+ ui.newLine();
304
+
305
+ if (securityGroups.length === 0) {
306
+ ui.info('No security groups found');
307
+ return;
308
+ }
309
+
310
+ // Display table
311
+ const headers = ['Group ID', 'Name', 'VPC ID', 'Description'];
312
+ const rows = securityGroups.map(sg => [
313
+ sg.GroupId,
314
+ sg.GroupName,
315
+ sg.VpcId,
316
+ sg.Description.substring(0, 40),
317
+ ]);
318
+
319
+ displayTable(headers, rows);
320
+ } catch (error) {
321
+ ui.stopSpinnerFail('Failed to list security groups');
322
+ ui.error((error as Error).message);
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Run AWS CLI command and parse JSON output
328
+ */
329
+ async function runAwsCommand<T>(command: string, options: AwsCommandOptions): Promise<T> {
330
+ const { execFile } = await import('child_process');
331
+ const { promisify } = await import('util');
332
+ const execFileAsync = promisify(execFile);
333
+
334
+ const args = command.split(' ');
335
+ const baseCommand = args[0];
336
+ const commandArgs = args.slice(1);
337
+
338
+ // Add common options
339
+ if (options.profile) {
340
+ commandArgs.push('--profile', options.profile);
341
+ }
342
+ if (options.region) {
343
+ commandArgs.push('--region', options.region);
344
+ }
345
+ commandArgs.push('--output', 'json');
346
+
347
+ const { stdout } = await execFileAsync('aws', [baseCommand, ...commandArgs]);
348
+ return JSON.parse(stdout) as T;
349
+ }
350
+
351
+ /**
352
+ * Display a table
353
+ */
354
+ function displayTable(headers: string[], rows: string[][]): void {
355
+ // Calculate column widths
356
+ const colWidths = headers.map((h, i) => {
357
+ const maxDataWidth = Math.max(...rows.map(r => (r[i] || '').length));
358
+ return Math.max(h.length, maxDataWidth);
359
+ });
360
+
361
+ // Print header
362
+ const headerRow = headers.map((h, i) => h.padEnd(colWidths[i])).join(' ');
363
+ ui.print(ui.bold(headerRow));
364
+ ui.print('-'.repeat(headerRow.length));
365
+
366
+ // Print rows
367
+ for (const row of rows) {
368
+ const formattedRow = row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(' ');
369
+ ui.print(formattedRow);
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Show VPC command help
375
+ */
376
+ function showVpcHelp(): void {
377
+ ui.print('Usage: nimbus aws vpc <action> [args]');
378
+ ui.newLine();
379
+
380
+ ui.print(ui.bold('Actions:'));
381
+ ui.print(' list List all VPCs');
382
+ ui.print(' describe <vpc-id> Describe a specific VPC');
383
+ ui.print(' subnets [vpc-id] List subnets');
384
+ ui.print(' security-groups [id] List security groups');
385
+ ui.newLine();
386
+
387
+ ui.print(ui.bold('Examples:'));
388
+ ui.print(' nimbus aws vpc list');
389
+ ui.print(' nimbus aws vpc describe vpc-1234567890abcdef0');
390
+ ui.print(' nimbus aws vpc subnets vpc-1234567890abcdef0');
391
+ }
392
+
393
+ export default vpcCommand;