@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,579 @@
1
+ /**
2
+ * Azure Operations — Embedded tool (stripped HTTP wrappers)
3
+ *
4
+ * Merged from services/azure-tools-service/src/azure/compute.ts, storage.ts, aks.ts, iam.ts, network.ts
5
+ * Uses lazy imports for Azure SDK to keep binary size small.
6
+ */
7
+
8
+ import { logger } from '../utils';
9
+
10
+ // ==========================================
11
+ // Shared Types
12
+ // ==========================================
13
+
14
+ export interface AzureOperationResult<T = any> {
15
+ success: boolean;
16
+ data?: T;
17
+ error?: string;
18
+ }
19
+
20
+ export interface AzureConfig {
21
+ subscriptionId?: string;
22
+ }
23
+
24
+ /**
25
+ * Unified Azure Operations class merging Compute, Storage, AKS, IAM, and Network operations.
26
+ * All Azure SDK imports are lazy to minimize binary size.
27
+ */
28
+ export class AzureOperations {
29
+ private subscriptionId: string;
30
+
31
+ constructor(config: AzureConfig = {}) {
32
+ this.subscriptionId = config.subscriptionId || process.env.AZURE_SUBSCRIPTION_ID || '';
33
+ }
34
+
35
+ /**
36
+ * Lazily create an Azure credential
37
+ */
38
+ private async getCredential(): Promise<any> {
39
+ const { DefaultAzureCredential } = await import('@azure/identity');
40
+ return new DefaultAzureCredential();
41
+ }
42
+
43
+ // ==========================================
44
+ // Compute (VM) Operations
45
+ // ==========================================
46
+
47
+ /**
48
+ * List virtual machines, optionally filtered by resource group
49
+ */
50
+ async listVMs(subscriptionId?: string, resourceGroup?: string): Promise<AzureOperationResult> {
51
+ try {
52
+ const subId = subscriptionId || this.subscriptionId;
53
+ if (!subId) {
54
+ return { success: false, error: 'No subscription ID provided' };
55
+ }
56
+
57
+ const { ComputeManagementClient } = await import('@azure/arm-compute');
58
+ const credential = await this.getCredential();
59
+ const client = new ComputeManagementClient(credential, subId);
60
+
61
+ const vms: any[] = [];
62
+
63
+ if (resourceGroup) {
64
+ for await (const vm of client.virtualMachines.list(resourceGroup)) {
65
+ vms.push(this.mapVM(vm));
66
+ }
67
+ } else {
68
+ for await (const vm of client.virtualMachines.listAll()) {
69
+ vms.push(this.mapVM(vm));
70
+ }
71
+ }
72
+
73
+ logger.debug(`Listed ${vms.length} VMs`, { resourceGroup });
74
+ return { success: true, data: { vms, count: vms.length } };
75
+ } catch (error: any) {
76
+ logger.error('Failed to list VMs', error);
77
+ return { success: false, error: error.message || 'Failed to list VMs' };
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Start a virtual machine
83
+ */
84
+ async startVM(
85
+ subscriptionId: string,
86
+ resourceGroup: string,
87
+ vmName: string
88
+ ): Promise<AzureOperationResult> {
89
+ try {
90
+ const subId = subscriptionId || this.subscriptionId;
91
+ if (!subId) {
92
+ return { success: false, error: 'No subscription ID provided' };
93
+ }
94
+
95
+ const { ComputeManagementClient } = await import('@azure/arm-compute');
96
+ const credential = await this.getCredential();
97
+ const client = new ComputeManagementClient(credential, subId);
98
+
99
+ const poller = await client.virtualMachines.beginStart(resourceGroup, vmName);
100
+ await poller.pollUntilDone();
101
+
102
+ logger.info(`Started VM ${vmName} in ${resourceGroup}`);
103
+ return {
104
+ success: true,
105
+ data: { vmName, resourceGroup, action: 'start', status: 'succeeded' },
106
+ };
107
+ } catch (error: any) {
108
+ logger.error('Failed to start VM', { vmName, resourceGroup, error });
109
+ return { success: false, error: error.message || 'Failed to start VM' };
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Stop (deallocate) a virtual machine
115
+ */
116
+ async stopVM(
117
+ subscriptionId: string,
118
+ resourceGroup: string,
119
+ vmName: string
120
+ ): Promise<AzureOperationResult> {
121
+ try {
122
+ const subId = subscriptionId || this.subscriptionId;
123
+ if (!subId) {
124
+ return { success: false, error: 'No subscription ID provided' };
125
+ }
126
+
127
+ const { ComputeManagementClient } = await import('@azure/arm-compute');
128
+ const credential = await this.getCredential();
129
+ const client = new ComputeManagementClient(credential, subId);
130
+
131
+ const poller = await client.virtualMachines.beginDeallocate(resourceGroup, vmName);
132
+ await poller.pollUntilDone();
133
+
134
+ logger.info(`Stopped VM ${vmName} in ${resourceGroup}`);
135
+ return {
136
+ success: true,
137
+ data: { vmName, resourceGroup, action: 'stop', status: 'succeeded' },
138
+ };
139
+ } catch (error: any) {
140
+ logger.error('Failed to stop VM', { vmName, resourceGroup, error });
141
+ return { success: false, error: error.message || 'Failed to stop VM' };
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Map a VM to normalized format
147
+ */
148
+ private mapVM(vm: any): any {
149
+ return {
150
+ id: vm.id,
151
+ name: vm.name,
152
+ location: vm.location,
153
+ type: vm.type,
154
+ vmSize: vm.hardwareProfile?.vmSize,
155
+ osType: vm.storageProfile?.osDisk?.osType,
156
+ provisioningState: vm.provisioningState,
157
+ vmId: vm.vmId,
158
+ tags: vm.tags || {},
159
+ networkInterfaces: vm.networkProfile?.networkInterfaces?.map((nic: any) => ({
160
+ id: nic.id,
161
+ primary: nic.primary,
162
+ })),
163
+ availabilitySet: vm.availabilitySet?.id,
164
+ zones: vm.zones,
165
+ };
166
+ }
167
+
168
+ // ==========================================
169
+ // Storage Operations
170
+ // ==========================================
171
+
172
+ /**
173
+ * List storage accounts, optionally filtered by resource group
174
+ */
175
+ async listStorageAccounts(
176
+ subscriptionId?: string,
177
+ resourceGroup?: string
178
+ ): Promise<AzureOperationResult> {
179
+ try {
180
+ const subId = subscriptionId || this.subscriptionId;
181
+ if (!subId) {
182
+ return { success: false, error: 'No subscription ID provided' };
183
+ }
184
+
185
+ const { StorageManagementClient } = await import('@azure/arm-storage');
186
+ const credential = await this.getCredential();
187
+ const client = new StorageManagementClient(credential, subId);
188
+
189
+ const accounts: any[] = [];
190
+
191
+ if (resourceGroup) {
192
+ for await (const account of client.storageAccounts.listByResourceGroup(resourceGroup)) {
193
+ accounts.push(this.mapStorageAccount(account));
194
+ }
195
+ } else {
196
+ for await (const account of client.storageAccounts.list()) {
197
+ accounts.push(this.mapStorageAccount(account));
198
+ }
199
+ }
200
+
201
+ logger.debug(`Listed ${accounts.length} storage accounts`, { resourceGroup });
202
+ return { success: true, data: { accounts, count: accounts.length } };
203
+ } catch (error: any) {
204
+ logger.error('Failed to list storage accounts', error);
205
+ return { success: false, error: error.message || 'Failed to list storage accounts' };
206
+ }
207
+ }
208
+
209
+ /**
210
+ * List blob containers for a storage account
211
+ */
212
+ async listContainers(
213
+ subscriptionId: string,
214
+ resourceGroup: string,
215
+ accountName: string
216
+ ): Promise<AzureOperationResult> {
217
+ try {
218
+ const subId = subscriptionId || this.subscriptionId;
219
+ if (!subId) {
220
+ return { success: false, error: 'No subscription ID provided' };
221
+ }
222
+
223
+ const { StorageManagementClient } = await import('@azure/arm-storage');
224
+ const credential = await this.getCredential();
225
+ const client = new StorageManagementClient(credential, subId);
226
+
227
+ const containers: any[] = [];
228
+
229
+ for await (const container of client.blobContainers.list(resourceGroup, accountName)) {
230
+ containers.push({
231
+ id: container.id,
232
+ name: container.name,
233
+ type: container.type,
234
+ publicAccess: container.publicAccess,
235
+ leaseState: container.leaseState,
236
+ leaseStatus: container.leaseStatus,
237
+ lastModifiedTime: container.lastModifiedTime,
238
+ hasImmutabilityPolicy: container.hasImmutabilityPolicy,
239
+ hasLegalHold: container.hasLegalHold,
240
+ defaultEncryptionScope: container.defaultEncryptionScope,
241
+ });
242
+ }
243
+
244
+ logger.debug(`Listed ${containers.length} containers for ${accountName}`);
245
+ return { success: true, data: { containers, count: containers.length } };
246
+ } catch (error: any) {
247
+ logger.error('Failed to list containers', { accountName, error });
248
+ return { success: false, error: error.message || 'Failed to list containers' };
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Map a storage account to normalized format
254
+ */
255
+ private mapStorageAccount(account: any): any {
256
+ return {
257
+ id: account.id,
258
+ name: account.name,
259
+ location: account.location,
260
+ type: account.type,
261
+ kind: account.kind,
262
+ sku: account.sku ? { name: account.sku.name, tier: account.sku.tier } : undefined,
263
+ provisioningState: account.provisioningState,
264
+ primaryEndpoints: account.primaryEndpoints,
265
+ creationTime: account.creationTime,
266
+ accessTier: account.accessTier,
267
+ enableHttpsTrafficOnly: account.enableHttpsTrafficOnly,
268
+ minimumTlsVersion: account.minimumTlsVersion,
269
+ allowBlobPublicAccess: account.allowBlobPublicAccess,
270
+ networkRuleSet: account.networkRuleSet
271
+ ? {
272
+ defaultAction: account.networkRuleSet.defaultAction,
273
+ bypass: account.networkRuleSet.bypass,
274
+ }
275
+ : undefined,
276
+ tags: account.tags || {},
277
+ };
278
+ }
279
+
280
+ // ==========================================
281
+ // AKS (Azure Kubernetes Service) Operations
282
+ // ==========================================
283
+
284
+ /**
285
+ * List AKS clusters, optionally filtered by resource group
286
+ */
287
+ async listClusters(
288
+ subscriptionId?: string,
289
+ resourceGroup?: string
290
+ ): Promise<AzureOperationResult> {
291
+ try {
292
+ const subId = subscriptionId || this.subscriptionId;
293
+ if (!subId) {
294
+ return { success: false, error: 'No subscription ID provided' };
295
+ }
296
+
297
+ const { ContainerServiceClient } = await import('@azure/arm-containerservice');
298
+ const credential = await this.getCredential();
299
+ const client = new ContainerServiceClient(credential, subId);
300
+
301
+ const clusters: any[] = [];
302
+
303
+ if (resourceGroup) {
304
+ for await (const cluster of client.managedClusters.listByResourceGroup(resourceGroup)) {
305
+ clusters.push(this.mapAKSCluster(cluster));
306
+ }
307
+ } else {
308
+ for await (const cluster of client.managedClusters.list()) {
309
+ clusters.push(this.mapAKSCluster(cluster));
310
+ }
311
+ }
312
+
313
+ logger.debug(`Listed ${clusters.length} AKS clusters`, { resourceGroup });
314
+ return { success: true, data: { clusters, count: clusters.length } };
315
+ } catch (error: any) {
316
+ logger.error('Failed to list AKS clusters', error);
317
+ return { success: false, error: error.message || 'Failed to list AKS clusters' };
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Get detailed information about a specific AKS cluster
323
+ */
324
+ async describeCluster(
325
+ subscriptionId: string,
326
+ resourceGroup: string,
327
+ clusterName: string
328
+ ): Promise<AzureOperationResult> {
329
+ try {
330
+ const subId = subscriptionId || this.subscriptionId;
331
+ if (!subId) {
332
+ return { success: false, error: 'No subscription ID provided' };
333
+ }
334
+
335
+ const { ContainerServiceClient } = await import('@azure/arm-containerservice');
336
+ const credential = await this.getCredential();
337
+ const client = new ContainerServiceClient(credential, subId);
338
+
339
+ const cluster = await client.managedClusters.get(resourceGroup, clusterName);
340
+
341
+ logger.debug(`Described AKS cluster ${clusterName}`);
342
+ return { success: true, data: this.mapAKSCluster(cluster) };
343
+ } catch (error: any) {
344
+ logger.error('Failed to describe AKS cluster', { clusterName, error });
345
+ return { success: false, error: error.message || 'Failed to describe AKS cluster' };
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Map an AKS cluster to a normalized response format
351
+ */
352
+ private mapAKSCluster(cluster: any): any {
353
+ return {
354
+ id: cluster.id,
355
+ name: cluster.name,
356
+ location: cluster.location,
357
+ type: cluster.type,
358
+ kubernetesVersion: cluster.kubernetesVersion,
359
+ dnsPrefix: cluster.dnsPrefix,
360
+ fqdn: cluster.fqdn,
361
+ provisioningState: cluster.provisioningState,
362
+ powerState: cluster.powerState?.code,
363
+ nodeResourceGroup: cluster.nodeResourceGroup,
364
+ enableRBAC: cluster.enableRBAC,
365
+ networkProfile: cluster.networkProfile
366
+ ? {
367
+ networkPlugin: cluster.networkProfile.networkPlugin,
368
+ networkPolicy: cluster.networkProfile.networkPolicy,
369
+ podCidr: cluster.networkProfile.podCidr,
370
+ serviceCidr: cluster.networkProfile.serviceCidr,
371
+ dnsServiceIP: cluster.networkProfile.dnsServiceIP,
372
+ loadBalancerSku: cluster.networkProfile.loadBalancerSku,
373
+ }
374
+ : undefined,
375
+ agentPoolProfiles: cluster.agentPoolProfiles?.map((pool: any) => ({
376
+ name: pool.name,
377
+ count: pool.count,
378
+ vmSize: pool.vmSize,
379
+ osType: pool.osType,
380
+ osDiskSizeGB: pool.osDiskSizeGB,
381
+ mode: pool.mode,
382
+ maxPods: pool.maxPods,
383
+ enableAutoScaling: pool.enableAutoScaling,
384
+ minCount: pool.minCount,
385
+ maxCount: pool.maxCount,
386
+ availabilityZones: pool.availabilityZones,
387
+ provisioningState: pool.provisioningState,
388
+ })),
389
+ identity: cluster.identity
390
+ ? {
391
+ type: cluster.identity.type,
392
+ principalId: cluster.identity.principalId,
393
+ tenantId: cluster.identity.tenantId,
394
+ }
395
+ : undefined,
396
+ tags: cluster.tags || {},
397
+ };
398
+ }
399
+
400
+ // ==========================================
401
+ // IAM (Role Assignments) Operations
402
+ // ==========================================
403
+
404
+ /**
405
+ * List role assignments for a subscription
406
+ */
407
+ async listRoleAssignments(subscriptionId?: string): Promise<AzureOperationResult> {
408
+ try {
409
+ const subId = subscriptionId || this.subscriptionId;
410
+ if (!subId) {
411
+ return { success: false, error: 'No subscription ID provided' };
412
+ }
413
+
414
+ const credential = await this.getCredential();
415
+ const token = await credential.getToken('https://management.azure.com/.default');
416
+ if (!token) {
417
+ return { success: false, error: 'Failed to acquire authentication token' };
418
+ }
419
+
420
+ const url = `https://management.azure.com/subscriptions/${subId}/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01`;
421
+
422
+ const response = await fetch(url, {
423
+ headers: {
424
+ Authorization: `Bearer ${token.token}`,
425
+ 'Content-Type': 'application/json',
426
+ },
427
+ });
428
+
429
+ if (!response.ok) {
430
+ const errorText = await response.text();
431
+ return { success: false, error: `Azure API error: ${response.status} - ${errorText}` };
432
+ }
433
+
434
+ const data = (await response.json()) as any;
435
+ const roleAssignments = (data.value || []).map((ra: any) => ({
436
+ id: ra.id,
437
+ name: ra.name,
438
+ type: ra.type,
439
+ principalId: ra.properties?.principalId,
440
+ principalType: ra.properties?.principalType,
441
+ roleDefinitionId: ra.properties?.roleDefinitionId,
442
+ scope: ra.properties?.scope,
443
+ createdOn: ra.properties?.createdOn,
444
+ updatedOn: ra.properties?.updatedOn,
445
+ createdBy: ra.properties?.createdBy,
446
+ description: ra.properties?.description,
447
+ }));
448
+
449
+ logger.debug(`Listed ${roleAssignments.length} role assignments`);
450
+ return { success: true, data: { roleAssignments, count: roleAssignments.length } };
451
+ } catch (error: any) {
452
+ logger.error('Failed to list role assignments', error);
453
+ return { success: false, error: error.message || 'Failed to list role assignments' };
454
+ }
455
+ }
456
+
457
+ // ==========================================
458
+ // Network (VNet) Operations
459
+ // ==========================================
460
+
461
+ /**
462
+ * List virtual networks, optionally filtered by resource group
463
+ */
464
+ async listVNets(subscriptionId?: string, resourceGroup?: string): Promise<AzureOperationResult> {
465
+ try {
466
+ const subId = subscriptionId || this.subscriptionId;
467
+ if (!subId) {
468
+ return { success: false, error: 'No subscription ID provided' };
469
+ }
470
+
471
+ const { NetworkManagementClient } = await import('@azure/arm-network');
472
+ const credential = await this.getCredential();
473
+ const client = new NetworkManagementClient(credential, subId);
474
+
475
+ const vnets: any[] = [];
476
+
477
+ if (resourceGroup) {
478
+ for await (const vnet of client.virtualNetworks.list(resourceGroup)) {
479
+ vnets.push(this.mapVNet(vnet));
480
+ }
481
+ } else {
482
+ for await (const vnet of client.virtualNetworks.listAll()) {
483
+ vnets.push(this.mapVNet(vnet));
484
+ }
485
+ }
486
+
487
+ logger.debug(`Listed ${vnets.length} virtual networks`, { resourceGroup });
488
+ return { success: true, data: { vnets, count: vnets.length } };
489
+ } catch (error: any) {
490
+ logger.error('Failed to list virtual networks', error);
491
+ return { success: false, error: error.message || 'Failed to list virtual networks' };
492
+ }
493
+ }
494
+
495
+ /**
496
+ * List subnets for a virtual network
497
+ */
498
+ async listSubnets(
499
+ subscriptionId: string,
500
+ resourceGroup: string,
501
+ vnetName: string
502
+ ): Promise<AzureOperationResult> {
503
+ try {
504
+ const subId = subscriptionId || this.subscriptionId;
505
+ if (!subId) {
506
+ return { success: false, error: 'No subscription ID provided' };
507
+ }
508
+
509
+ const { NetworkManagementClient } = await import('@azure/arm-network');
510
+ const credential = await this.getCredential();
511
+ const client = new NetworkManagementClient(credential, subId);
512
+
513
+ const subnets: any[] = [];
514
+
515
+ for await (const subnet of client.subnets.list(resourceGroup, vnetName)) {
516
+ subnets.push({
517
+ id: subnet.id,
518
+ name: subnet.name,
519
+ type: subnet.type,
520
+ addressPrefix: subnet.addressPrefix,
521
+ addressPrefixes: subnet.addressPrefixes,
522
+ provisioningState: subnet.provisioningState,
523
+ privateEndpointNetworkPolicies: subnet.privateEndpointNetworkPolicies,
524
+ privateLinkServiceNetworkPolicies: subnet.privateLinkServiceNetworkPolicies,
525
+ networkSecurityGroup: subnet.networkSecurityGroup
526
+ ? { id: subnet.networkSecurityGroup.id }
527
+ : undefined,
528
+ routeTable: subnet.routeTable ? { id: subnet.routeTable.id } : undefined,
529
+ serviceEndpoints: subnet.serviceEndpoints?.map((se: any) => ({
530
+ service: se.service,
531
+ locations: se.locations,
532
+ provisioningState: se.provisioningState,
533
+ })),
534
+ delegations: subnet.delegations?.map((d: any) => ({
535
+ name: d.name,
536
+ serviceName: d.serviceName,
537
+ provisioningState: d.provisioningState,
538
+ })),
539
+ });
540
+ }
541
+
542
+ logger.debug(`Listed ${subnets.length} subnets for VNet ${vnetName}`);
543
+ return { success: true, data: { subnets, count: subnets.length } };
544
+ } catch (error: any) {
545
+ logger.error('Failed to list subnets', { vnetName, error });
546
+ return { success: false, error: error.message || 'Failed to list subnets' };
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Map a VNet to a normalized response format
552
+ */
553
+ private mapVNet(vnet: any): any {
554
+ return {
555
+ id: vnet.id,
556
+ name: vnet.name,
557
+ location: vnet.location,
558
+ type: vnet.type,
559
+ provisioningState: vnet.provisioningState,
560
+ addressSpace: vnet.addressSpace?.addressPrefixes,
561
+ dhcpOptions: vnet.dhcpOptions?.dnsServers,
562
+ enableDdosProtection: vnet.enableDdosProtection,
563
+ enableVmProtection: vnet.enableVmProtection,
564
+ subnets: vnet.subnets?.map((s: any) => ({
565
+ id: s.id,
566
+ name: s.name,
567
+ addressPrefix: s.addressPrefix,
568
+ provisioningState: s.provisioningState,
569
+ })),
570
+ virtualNetworkPeerings: vnet.virtualNetworkPeerings?.map((p: any) => ({
571
+ id: p.id,
572
+ name: p.name,
573
+ peeringState: p.peeringState,
574
+ remoteVirtualNetwork: p.remoteVirtualNetwork?.id,
575
+ })),
576
+ tags: vnet.tags || {},
577
+ };
578
+ }
579
+ }