@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 S3 Commands
3
+ *
4
+ * S3 bucket and object operations
5
+ *
6
+ * Usage:
7
+ * nimbus aws s3 ls
8
+ * nimbus aws s3 ls <bucket>
9
+ * nimbus aws s3 cp <src> <dst>
10
+ * nimbus aws s3 rm <path>
11
+ * nimbus aws s3 mb <bucket>
12
+ */
13
+
14
+ import { logger } from '../../utils';
15
+ import { ui } from '../../wizard/ui';
16
+ import { confirm } from '../../wizard/prompts';
17
+ import {
18
+ loadSafetyPolicy,
19
+ evaluateSafety,
20
+ type SafetyContext,
21
+ type SafetyCheckResult,
22
+ } from '../../config/safety-policy';
23
+ import { promptForApproval } from '../../wizard/approval';
24
+ import type { AwsCommandOptions } from './index';
25
+
26
+ interface S3Bucket {
27
+ Name: string;
28
+ CreationDate: string;
29
+ }
30
+
31
+ interface S3Object {
32
+ Key: string;
33
+ Size: number;
34
+ LastModified: string;
35
+ StorageClass: string;
36
+ }
37
+
38
+ /**
39
+ * S3 command router
40
+ */
41
+ export async function s3Command(
42
+ action: string,
43
+ args: string[],
44
+ options: AwsCommandOptions
45
+ ): Promise<void> {
46
+ logger.info('Running S3 command', { action, args, options });
47
+
48
+ switch (action) {
49
+ case 'ls':
50
+ case 'list':
51
+ if (args[0]) {
52
+ await listObjects(args[0], options);
53
+ } else {
54
+ await listBuckets(options);
55
+ }
56
+ break;
57
+
58
+ case 'cp':
59
+ case 'copy':
60
+ if (args.length < 2) {
61
+ ui.error('Source and destination are required');
62
+ ui.print('Usage: nimbus aws s3 cp <source> <destination>');
63
+ return;
64
+ }
65
+ await copyObject(args[0], args[1], options);
66
+ break;
67
+
68
+ case 'rm':
69
+ case 'remove':
70
+ if (!args[0]) {
71
+ ui.error('S3 path is required');
72
+ ui.print('Usage: nimbus aws s3 rm <s3://bucket/key>');
73
+ return;
74
+ }
75
+ await removeObject(args[0], options);
76
+ break;
77
+
78
+ case 'mb':
79
+ if (!args[0]) {
80
+ ui.error('Bucket name is required');
81
+ ui.print('Usage: nimbus aws s3 mb <bucket-name>');
82
+ return;
83
+ }
84
+ await makeBucket(args[0], options);
85
+ break;
86
+
87
+ case 'rb':
88
+ if (!args[0]) {
89
+ ui.error('Bucket name is required');
90
+ ui.print('Usage: nimbus aws s3 rb <bucket-name>');
91
+ return;
92
+ }
93
+ await removeBucket(args[0], options);
94
+ break;
95
+
96
+ default:
97
+ showS3Help();
98
+ break;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * List all S3 buckets
104
+ */
105
+ async function listBuckets(options: AwsCommandOptions): Promise<void> {
106
+ ui.header('S3 Buckets');
107
+
108
+ ui.startSpinner({ message: 'Fetching S3 buckets...' });
109
+
110
+ try {
111
+ const result = await runAwsCommand<{ Buckets: S3Bucket[] }>('s3api list-buckets', options);
112
+
113
+ const buckets = result.Buckets || [];
114
+
115
+ ui.stopSpinnerSuccess(`Found ${buckets.length} bucket(s)`);
116
+ ui.newLine();
117
+
118
+ if (buckets.length === 0) {
119
+ ui.info('No S3 buckets found');
120
+ return;
121
+ }
122
+
123
+ // Display table
124
+ ui.print(ui.bold(`${'Name'.padEnd(50)}Created`));
125
+ ui.print('-'.repeat(70));
126
+
127
+ for (const bucket of buckets) {
128
+ const date = new Date(bucket.CreationDate).toLocaleDateString();
129
+ ui.print(`${bucket.Name.padEnd(50)}${date}`);
130
+ }
131
+ } catch (error) {
132
+ ui.stopSpinnerFail('Failed to list buckets');
133
+ ui.error((error as Error).message);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * List objects in a bucket
139
+ */
140
+ async function listObjects(bucket: string, options: AwsCommandOptions): Promise<void> {
141
+ // Remove s3:// prefix if present
142
+ const bucketName = bucket.replace(/^s3:\/\//, '').split('/')[0];
143
+ const prefix = bucket
144
+ .replace(/^s3:\/\//, '')
145
+ .split('/')
146
+ .slice(1)
147
+ .join('/');
148
+
149
+ ui.header(`S3 Objects: ${bucketName}`);
150
+
151
+ ui.startSpinner({ message: 'Fetching objects...' });
152
+
153
+ try {
154
+ let command = `s3api list-objects-v2 --bucket ${bucketName}`;
155
+ if (prefix) {
156
+ command += ` --prefix ${prefix}`;
157
+ }
158
+
159
+ const result = await runAwsCommand<{ Contents?: S3Object[] }>(command, options);
160
+
161
+ const objects = result.Contents || [];
162
+
163
+ ui.stopSpinnerSuccess(`Found ${objects.length} object(s)`);
164
+ ui.newLine();
165
+
166
+ if (objects.length === 0) {
167
+ ui.info('No objects found');
168
+ return;
169
+ }
170
+
171
+ // Display table
172
+ ui.print(ui.bold(`${'Key'.padEnd(50) + 'Size'.padEnd(15)}Modified`));
173
+ ui.print('-'.repeat(80));
174
+
175
+ for (const obj of objects) {
176
+ const size = formatBytes(obj.Size);
177
+ const date = new Date(obj.LastModified).toLocaleDateString();
178
+ ui.print(`${obj.Key.padEnd(50)}${size.padEnd(15)}${date}`);
179
+ }
180
+ } catch (error) {
181
+ ui.stopSpinnerFail('Failed to list objects');
182
+ ui.error((error as Error).message);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Copy object
188
+ */
189
+ async function copyObject(
190
+ source: string,
191
+ destination: string,
192
+ options: AwsCommandOptions
193
+ ): Promise<void> {
194
+ ui.header('S3 Copy');
195
+ ui.info(`Source: ${source}`);
196
+ ui.info(`Destination: ${destination}`);
197
+ ui.newLine();
198
+
199
+ const proceed = await confirm({
200
+ message: 'Proceed with copy?',
201
+ defaultValue: true,
202
+ });
203
+
204
+ if (!proceed) {
205
+ ui.info('Operation cancelled');
206
+ return;
207
+ }
208
+
209
+ ui.startSpinner({ message: 'Copying...' });
210
+
211
+ try {
212
+ await runAwsS3Command(`cp ${source} ${destination}`, options);
213
+
214
+ ui.stopSpinnerSuccess('Copy complete');
215
+ } catch (error) {
216
+ ui.stopSpinnerFail('Copy failed');
217
+ ui.error((error as Error).message);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Remove object
223
+ */
224
+ async function removeObject(path: string, options: AwsCommandOptions): Promise<void> {
225
+ ui.header('S3 Remove');
226
+ ui.warning(`This will delete: ${path}`);
227
+ ui.newLine();
228
+
229
+ // Run safety checks
230
+ const safetyResult = await runSafetyCheck('delete', path, options);
231
+
232
+ if (safetyResult.requiresApproval) {
233
+ const approval = await promptForApproval({
234
+ title: 'Delete S3 Object',
235
+ operation: `s3 rm ${path}`,
236
+ risks: safetyResult.risks,
237
+ });
238
+
239
+ if (!approval.approved) {
240
+ ui.info('Operation cancelled');
241
+ return;
242
+ }
243
+ } else {
244
+ const proceed = await confirm({
245
+ message: 'Delete this object?',
246
+ defaultValue: false,
247
+ });
248
+
249
+ if (!proceed) {
250
+ ui.info('Operation cancelled');
251
+ return;
252
+ }
253
+ }
254
+
255
+ ui.startSpinner({ message: 'Deleting...' });
256
+
257
+ try {
258
+ await runAwsS3Command(`rm ${path}`, options);
259
+
260
+ ui.stopSpinnerSuccess('Object deleted');
261
+ } catch (error) {
262
+ ui.stopSpinnerFail('Delete failed');
263
+ ui.error((error as Error).message);
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Create a bucket
269
+ */
270
+ async function makeBucket(bucketName: string, options: AwsCommandOptions): Promise<void> {
271
+ ui.header('Create S3 Bucket');
272
+ ui.info(`Bucket: ${bucketName}`);
273
+ ui.newLine();
274
+
275
+ const proceed = await confirm({
276
+ message: 'Create this bucket?',
277
+ defaultValue: true,
278
+ });
279
+
280
+ if (!proceed) {
281
+ ui.info('Operation cancelled');
282
+ return;
283
+ }
284
+
285
+ ui.startSpinner({ message: 'Creating bucket...' });
286
+
287
+ try {
288
+ await runAwsS3Command(`mb s3://${bucketName}`, options);
289
+
290
+ ui.stopSpinnerSuccess('Bucket created');
291
+ } catch (error) {
292
+ ui.stopSpinnerFail('Failed to create bucket');
293
+ ui.error((error as Error).message);
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Remove a bucket
299
+ */
300
+ async function removeBucket(bucketName: string, options: AwsCommandOptions): Promise<void> {
301
+ ui.header('Remove S3 Bucket');
302
+ ui.warning(`This will delete bucket: ${bucketName}`);
303
+ ui.newLine();
304
+
305
+ // Run safety checks
306
+ const safetyResult = await runSafetyCheck('delete', bucketName, options);
307
+
308
+ const approval = await promptForApproval({
309
+ title: 'Delete S3 Bucket',
310
+ operation: `s3 rb ${bucketName}`,
311
+ risks: safetyResult.risks,
312
+ requireConfirmation: true,
313
+ confirmationWord: 'delete',
314
+ });
315
+
316
+ if (!approval.approved) {
317
+ ui.info('Operation cancelled');
318
+ return;
319
+ }
320
+
321
+ ui.startSpinner({ message: 'Removing bucket...' });
322
+
323
+ try {
324
+ await runAwsS3Command(`rb s3://${bucketName} --force`, options);
325
+
326
+ ui.stopSpinnerSuccess('Bucket removed');
327
+ } catch (error) {
328
+ ui.stopSpinnerFail('Failed to remove bucket');
329
+ ui.error((error as Error).message);
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Run safety check for S3 operation
335
+ */
336
+ async function runSafetyCheck(
337
+ operation: string,
338
+ resource: string,
339
+ options: AwsCommandOptions
340
+ ): Promise<SafetyCheckResult> {
341
+ const policy = loadSafetyPolicy();
342
+
343
+ const context: SafetyContext = {
344
+ operation,
345
+ type: 'aws',
346
+ resources: [resource],
347
+ metadata: {
348
+ service: 's3',
349
+ region: options.region,
350
+ },
351
+ };
352
+
353
+ return evaluateSafety(context, policy);
354
+ }
355
+
356
+ /**
357
+ * Run AWS CLI command and parse JSON output
358
+ */
359
+ async function runAwsCommand<T>(command: string, options: AwsCommandOptions): Promise<T> {
360
+ const { execFile } = await import('child_process');
361
+ const { promisify } = await import('util');
362
+ const execFileAsync = promisify(execFile);
363
+
364
+ const args = command.split(' ');
365
+ const baseCommand = args[0];
366
+ const commandArgs = args.slice(1);
367
+
368
+ // Add common options
369
+ if (options.profile) {
370
+ commandArgs.push('--profile', options.profile);
371
+ }
372
+ if (options.region) {
373
+ commandArgs.push('--region', options.region);
374
+ }
375
+ commandArgs.push('--output', 'json');
376
+
377
+ const { stdout } = await execFileAsync('aws', [baseCommand, ...commandArgs]);
378
+ return JSON.parse(stdout) as T;
379
+ }
380
+
381
+ /**
382
+ * Run AWS S3 high-level command
383
+ */
384
+ async function runAwsS3Command(command: string, options: AwsCommandOptions): Promise<void> {
385
+ const { execFile } = await import('child_process');
386
+ const { promisify } = await import('util');
387
+ const execFileAsync = promisify(execFile);
388
+
389
+ const args = command.split(' ');
390
+
391
+ // Add common options
392
+ if (options.profile) {
393
+ args.push('--profile', options.profile);
394
+ }
395
+ if (options.region) {
396
+ args.push('--region', options.region);
397
+ }
398
+
399
+ await execFileAsync('aws', ['s3', ...args]);
400
+ }
401
+
402
+ /**
403
+ * Format bytes to human readable
404
+ */
405
+ function formatBytes(bytes: number): string {
406
+ if (bytes === 0) {
407
+ return '0 B';
408
+ }
409
+
410
+ const k = 1024;
411
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
412
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
413
+
414
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
415
+ }
416
+
417
+ /**
418
+ * Show S3 command help
419
+ */
420
+ function showS3Help(): void {
421
+ ui.print('Usage: nimbus aws s3 <action> [args]');
422
+ ui.newLine();
423
+
424
+ ui.print(ui.bold('Actions:'));
425
+ ui.print(' ls [bucket] List buckets or objects');
426
+ ui.print(' cp <src> <dst> Copy files/objects');
427
+ ui.print(' rm <path> Remove an object');
428
+ ui.print(' mb <bucket> Create a bucket');
429
+ ui.print(' rb <bucket> Remove a bucket');
430
+ ui.newLine();
431
+
432
+ ui.print(ui.bold('Examples:'));
433
+ ui.print(' nimbus aws s3 ls');
434
+ ui.print(' nimbus aws s3 ls s3://my-bucket/prefix/');
435
+ ui.print(' nimbus aws s3 cp file.txt s3://my-bucket/');
436
+ ui.print(' nimbus aws s3 rm s3://my-bucket/file.txt');
437
+ }
438
+
439
+ export default s3Command;