@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,439 @@
1
+ /**
2
+ * AWS RDS Commands
3
+ *
4
+ * RDS database operations with cost warnings before billable actions
5
+ *
6
+ * Usage:
7
+ * nimbus aws rds list
8
+ * nimbus aws rds describe <db-identifier>
9
+ * nimbus aws rds start <db-identifier>
10
+ * nimbus aws rds stop <db-identifier>
11
+ */
12
+
13
+ import { logger } from '../../utils';
14
+ import { ui } from '../../wizard/ui';
15
+ import { confirm } from '../../wizard/prompts';
16
+ import {
17
+ loadSafetyPolicy,
18
+ evaluateSafety,
19
+ type SafetyContext,
20
+ type SafetyCheckResult,
21
+ } from '../../config/safety-policy';
22
+ import { promptForApproval } from '../../wizard/approval';
23
+ import { estimateCloudCost, formatCostWarning } from '../cost/cloud-cost-estimator';
24
+ import type { AwsCommandOptions } from './index';
25
+
26
+ interface RDSInstance {
27
+ DBInstanceIdentifier: string;
28
+ DBInstanceClass: string;
29
+ Engine: string;
30
+ EngineVersion: string;
31
+ DBInstanceStatus: string;
32
+ Endpoint?: {
33
+ Address: string;
34
+ Port: number;
35
+ };
36
+ AllocatedStorage: number;
37
+ AvailabilityZone: string;
38
+ MultiAZ: boolean;
39
+ }
40
+
41
+ /**
42
+ * RDS command router
43
+ */
44
+ export async function rdsCommand(
45
+ action: string,
46
+ args: string[],
47
+ options: AwsCommandOptions
48
+ ): Promise<void> {
49
+ logger.info('Running RDS command', { action, args, options });
50
+
51
+ switch (action) {
52
+ case 'list':
53
+ case 'ls':
54
+ await listInstances(options);
55
+ break;
56
+
57
+ case 'describe':
58
+ if (!args[0]) {
59
+ ui.error('DB instance identifier is required');
60
+ ui.print('Usage: nimbus aws rds describe <db-identifier>');
61
+ return;
62
+ }
63
+ await describeInstance(args[0], options);
64
+ break;
65
+
66
+ case 'start':
67
+ if (!args[0]) {
68
+ ui.error('DB instance identifier is required');
69
+ ui.print('Usage: nimbus aws rds start <db-identifier>');
70
+ return;
71
+ }
72
+ await startInstance(args[0], options);
73
+ break;
74
+
75
+ case 'stop':
76
+ if (!args[0]) {
77
+ ui.error('DB instance identifier is required');
78
+ ui.print('Usage: nimbus aws rds stop <db-identifier>');
79
+ return;
80
+ }
81
+ await stopInstance(args[0], options);
82
+ break;
83
+
84
+ default:
85
+ showRdsHelp();
86
+ break;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Display a cost warning for an RDS instance class using the cloud cost estimator.
92
+ */
93
+ function displayRdsCostWarning(instanceClass: string, multiAz: boolean): void {
94
+ const estimate = estimateCloudCost('rds:StartDBInstance', {
95
+ instanceClass,
96
+ multiAz,
97
+ });
98
+ if (estimate) {
99
+ const color = estimate.monthly > 200 ? 'red' : estimate.monthly > 50 ? 'yellow' : 'green';
100
+ ui.newLine();
101
+ ui.print(ui.bold(' Estimated Cost:'));
102
+ ui.print(` Hourly: ${ui.color(`$${estimate.hourly.toFixed(4)}/hr`, color)}`);
103
+ ui.print(
104
+ ` Monthly: ${ui.color(`$${estimate.monthly.toFixed(2)}/mo`, color)} (on-demand${multiAz ? ', Multi-AZ' : ''}, approximate)`
105
+ );
106
+ ui.newLine();
107
+ }
108
+ }
109
+
110
+ /**
111
+ * List all RDS instances
112
+ */
113
+ async function listInstances(options: AwsCommandOptions): Promise<void> {
114
+ ui.header('RDS Instances');
115
+
116
+ ui.startSpinner({ message: 'Fetching RDS instances...' });
117
+
118
+ try {
119
+ const result = await runAwsCommand<{ DBInstances: RDSInstance[] }>(
120
+ 'rds describe-db-instances',
121
+ options
122
+ );
123
+
124
+ const instances = result.DBInstances || [];
125
+
126
+ ui.stopSpinnerSuccess(`Found ${instances.length} instance(s)`);
127
+ ui.newLine();
128
+
129
+ if (instances.length === 0) {
130
+ ui.info('No RDS instances found');
131
+ return;
132
+ }
133
+
134
+ // Display table
135
+ displayInstanceTable(instances);
136
+ } catch (error) {
137
+ ui.stopSpinnerFail('Failed to list instances');
138
+ ui.error((error as Error).message);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Describe a specific RDS instance
144
+ */
145
+ async function describeInstance(identifier: string, options: AwsCommandOptions): Promise<void> {
146
+ ui.header(`RDS Instance: ${identifier}`);
147
+
148
+ ui.startSpinner({ message: 'Fetching instance details...' });
149
+
150
+ try {
151
+ const result = await runAwsCommand<{ DBInstances: RDSInstance[] }>(
152
+ `rds describe-db-instances --db-instance-identifier ${identifier}`,
153
+ options
154
+ );
155
+
156
+ const instances = result.DBInstances || [];
157
+
158
+ ui.stopSpinnerSuccess('Instance details retrieved');
159
+ ui.newLine();
160
+
161
+ if (instances.length === 0) {
162
+ ui.error(`Instance ${identifier} not found`);
163
+ return;
164
+ }
165
+
166
+ const instance = instances[0];
167
+
168
+ // Display instance details
169
+ ui.print(ui.bold('Instance Details:'));
170
+ ui.newLine();
171
+ ui.print(` Identifier: ${instance.DBInstanceIdentifier}`);
172
+ ui.print(` Class: ${instance.DBInstanceClass}`);
173
+ ui.print(` Engine: ${instance.Engine} ${instance.EngineVersion}`);
174
+ ui.print(` Status: ${formatStatus(instance.DBInstanceStatus)}`);
175
+ ui.print(` Storage: ${instance.AllocatedStorage} GB`);
176
+ ui.print(` AZ: ${instance.AvailabilityZone}`);
177
+ ui.print(` Multi-AZ: ${instance.MultiAZ ? 'Yes' : 'No'}`);
178
+
179
+ if (instance.Endpoint) {
180
+ ui.newLine();
181
+ ui.print(ui.bold('Endpoint:'));
182
+ ui.print(` Address: ${instance.Endpoint.Address}`);
183
+ ui.print(` Port: ${instance.Endpoint.Port}`);
184
+ }
185
+
186
+ // Show cost estimate
187
+ displayRdsCostWarning(instance.DBInstanceClass, instance.MultiAZ);
188
+ } catch (error) {
189
+ ui.stopSpinnerFail('Failed to describe instance');
190
+ ui.error((error as Error).message);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Start an RDS instance
196
+ */
197
+ async function startInstance(identifier: string, options: AwsCommandOptions): Promise<void> {
198
+ ui.header(`Start RDS Instance: ${identifier}`);
199
+
200
+ // Try to get instance class for cost estimate
201
+ try {
202
+ const result = await runAwsCommand<{ DBInstances: RDSInstance[] }>(
203
+ `rds describe-db-instances --db-instance-identifier ${identifier}`,
204
+ options
205
+ );
206
+ const instances = result.DBInstances || [];
207
+ if (instances.length > 0) {
208
+ const instance = instances[0];
209
+ const estimate = estimateCloudCost('rds:StartDBInstance', {
210
+ instanceClass: instance.DBInstanceClass,
211
+ multiAz: instance.MultiAZ,
212
+ });
213
+ if (estimate) {
214
+ ui.newLine();
215
+ ui.warning(formatCostWarning(estimate));
216
+ ui.newLine();
217
+ }
218
+ }
219
+ } catch {
220
+ // Non-critical, continue without cost estimate
221
+ }
222
+
223
+ const proceed = await confirm({
224
+ message: `Start instance ${identifier}?`,
225
+ defaultValue: true,
226
+ });
227
+
228
+ if (!proceed) {
229
+ ui.info('Operation cancelled');
230
+ return;
231
+ }
232
+
233
+ ui.startSpinner({ message: 'Starting instance...' });
234
+
235
+ try {
236
+ await runAwsCommand(`rds start-db-instance --db-instance-identifier ${identifier}`, options);
237
+
238
+ ui.stopSpinnerSuccess('Instance starting');
239
+ ui.info(`Instance ${identifier} is now starting`);
240
+ } catch (error) {
241
+ ui.stopSpinnerFail('Failed to start instance');
242
+ ui.error((error as Error).message);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Stop an RDS instance
248
+ */
249
+ async function stopInstance(identifier: string, options: AwsCommandOptions): Promise<void> {
250
+ ui.header(`Stop RDS Instance: ${identifier}`);
251
+
252
+ // Show cost estimate for the instance being stopped
253
+ try {
254
+ const result = await runAwsCommand<{ DBInstances: RDSInstance[] }>(
255
+ `rds describe-db-instances --db-instance-identifier ${identifier}`,
256
+ options
257
+ );
258
+ const instances = result.DBInstances || [];
259
+ if (instances.length > 0) {
260
+ const instance = instances[0];
261
+ displayRdsCostWarning(instance.DBInstanceClass, instance.MultiAZ);
262
+ ui.info('Stopping this instance will stop incurring compute charges.');
263
+ ui.newLine();
264
+ }
265
+ } catch {
266
+ // Non-critical, continue without cost estimate
267
+ }
268
+
269
+ // Run safety checks
270
+ const safetyResult = await runSafetyCheck('stop', identifier, options);
271
+
272
+ if (safetyResult.requiresApproval) {
273
+ const approval = await promptForApproval({
274
+ title: 'Stop RDS Instance',
275
+ operation: `rds stop ${identifier}`,
276
+ risks: safetyResult.risks,
277
+ });
278
+
279
+ if (!approval.approved) {
280
+ ui.info('Operation cancelled');
281
+ return;
282
+ }
283
+ } else {
284
+ const proceed = await confirm({
285
+ message: `Stop instance ${identifier}?`,
286
+ defaultValue: false,
287
+ });
288
+
289
+ if (!proceed) {
290
+ ui.info('Operation cancelled');
291
+ return;
292
+ }
293
+ }
294
+
295
+ ui.startSpinner({ message: 'Stopping instance...' });
296
+
297
+ try {
298
+ await runAwsCommand(`rds stop-db-instance --db-instance-identifier ${identifier}`, options);
299
+
300
+ ui.stopSpinnerSuccess('Instance stopping');
301
+ ui.info(`Instance ${identifier} is now stopping`);
302
+ } catch (error) {
303
+ ui.stopSpinnerFail('Failed to stop instance');
304
+ ui.error((error as Error).message);
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Run safety check for RDS operation
310
+ */
311
+ async function runSafetyCheck(
312
+ operation: string,
313
+ identifier: string,
314
+ options: AwsCommandOptions
315
+ ): Promise<SafetyCheckResult> {
316
+ const policy = loadSafetyPolicy();
317
+
318
+ const context: SafetyContext = {
319
+ operation,
320
+ type: 'aws',
321
+ resources: [identifier],
322
+ metadata: {
323
+ service: 'rds',
324
+ region: options.region,
325
+ },
326
+ };
327
+
328
+ return evaluateSafety(context, policy);
329
+ }
330
+
331
+ /**
332
+ * Run AWS CLI command and parse JSON output
333
+ */
334
+ async function runAwsCommand<T>(command: string, options: AwsCommandOptions): Promise<T> {
335
+ const { execFile } = await import('child_process');
336
+ const { promisify } = await import('util');
337
+ const execFileAsync = promisify(execFile);
338
+
339
+ const args = command.split(' ');
340
+ const baseCommand = args[0];
341
+ const commandArgs = args.slice(1);
342
+
343
+ // Add common options
344
+ if (options.profile) {
345
+ commandArgs.push('--profile', options.profile);
346
+ }
347
+ if (options.region) {
348
+ commandArgs.push('--region', options.region);
349
+ }
350
+ commandArgs.push('--output', 'json');
351
+
352
+ const { stdout } = await execFileAsync('aws', [baseCommand, ...commandArgs]);
353
+ return JSON.parse(stdout) as T;
354
+ }
355
+
356
+ /**
357
+ * Display instances in a table format
358
+ */
359
+ function displayInstanceTable(instances: RDSInstance[]): void {
360
+ const headers = ['Identifier', 'Engine', 'Class', 'Status', 'Storage', 'Multi-AZ'];
361
+ const rows = instances.map(inst => [
362
+ inst.DBInstanceIdentifier,
363
+ `${inst.Engine} ${inst.EngineVersion}`,
364
+ inst.DBInstanceClass,
365
+ inst.DBInstanceStatus,
366
+ `${inst.AllocatedStorage} GB`,
367
+ inst.MultiAZ ? 'Yes' : 'No',
368
+ ]);
369
+
370
+ // Print header
371
+ const headerRow = headers
372
+ .map((h, i) => {
373
+ const maxWidth = Math.max(h.length, ...rows.map(r => r[i].length));
374
+ return h.padEnd(maxWidth);
375
+ })
376
+ .join(' ');
377
+
378
+ ui.print(ui.bold(headerRow));
379
+ ui.print('-'.repeat(headerRow.length));
380
+
381
+ // Print rows
382
+ for (const row of rows) {
383
+ const formattedRow = row
384
+ .map((cell, i) => {
385
+ const maxWidth = Math.max(headers[i].length, ...rows.map(r => r[i].length));
386
+ if (i === 3) {
387
+ // Status column - colorize
388
+ return formatStatus(cell).padEnd(maxWidth + 10);
389
+ }
390
+ return cell.padEnd(maxWidth);
391
+ })
392
+ .join(' ');
393
+
394
+ ui.print(formattedRow);
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Format instance status with color
400
+ */
401
+ function formatStatus(status: string): string {
402
+ switch (status) {
403
+ case 'available':
404
+ return ui.color(status, 'green');
405
+ case 'stopped':
406
+ return ui.color(status, 'red');
407
+ case 'starting':
408
+ case 'stopping':
409
+ case 'creating':
410
+ case 'modifying':
411
+ return ui.color(status, 'yellow');
412
+ case 'deleting':
413
+ return ui.color(status, 'gray');
414
+ default:
415
+ return status;
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Show RDS command help
421
+ */
422
+ function showRdsHelp(): void {
423
+ ui.print('Usage: nimbus aws rds <action> [args]');
424
+ ui.newLine();
425
+
426
+ ui.print(ui.bold('Actions:'));
427
+ ui.print(' list List all RDS instances');
428
+ ui.print(' describe <id> Describe a specific instance');
429
+ ui.print(' start <id> Start an instance');
430
+ ui.print(' stop <id> Stop an instance');
431
+ ui.newLine();
432
+
433
+ ui.print(ui.bold('Examples:'));
434
+ ui.print(' nimbus aws rds list');
435
+ ui.print(' nimbus aws rds describe my-database');
436
+ ui.print(' nimbus aws rds stop my-database');
437
+ }
438
+
439
+ export default rdsCommand;