@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.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- 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
|
+
}
|