@rulebricks/cli 2.0.5 → 2.0.6

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.
@@ -128,27 +128,47 @@ export async function listS3Buckets() {
128
128
  }
129
129
  }
130
130
  /**
131
- * Static fallback for AWS regions
131
+ * Static fallback for AWS regions (c8g Graviton4 ARM64 available or expected)
132
132
  */
133
133
  function getStaticAwsRegions() {
134
134
  return [
135
+ // US regions (c8g Graviton4 available)
135
136
  "us-east-1",
136
137
  "us-east-2",
137
138
  "us-west-1",
138
139
  "us-west-2",
139
- "ap-south-1",
140
- "ap-northeast-1",
141
- "ap-northeast-2",
142
- "ap-northeast-3",
143
- "ap-southeast-1",
144
- "ap-southeast-2",
140
+ // Canada
145
141
  "ca-central-1",
146
- "eu-central-1",
142
+ "ca-west-1",
143
+ // Europe (c8g available)
147
144
  "eu-west-1",
148
145
  "eu-west-2",
149
146
  "eu-west-3",
147
+ "eu-central-1",
148
+ "eu-central-2",
150
149
  "eu-north-1",
150
+ "eu-south-1",
151
+ "eu-south-2",
152
+ // Asia Pacific (c8g available)
153
+ "ap-northeast-1",
154
+ "ap-northeast-2",
155
+ "ap-northeast-3",
156
+ "ap-southeast-1",
157
+ "ap-southeast-2",
158
+ "ap-southeast-3",
159
+ "ap-southeast-4",
160
+ "ap-southeast-5",
161
+ "ap-southeast-7",
162
+ "ap-south-1",
163
+ "ap-south-2",
164
+ "ap-east-1",
165
+ // South America
151
166
  "sa-east-1",
167
+ // Middle East & Africa
168
+ "me-south-1",
169
+ "me-central-1",
170
+ "af-south-1",
171
+ "il-central-1",
152
172
  ];
153
173
  }
154
174
  /**
@@ -171,7 +191,12 @@ export async function listEksClusters(region) {
171
191
  // GCP CLI (gcloud)
172
192
  // ============================================================================
173
193
  /**
174
- * Check if gcloud CLI is installed and authenticated
194
+ * Check if gcloud CLI is installed and fully authenticated
195
+ *
196
+ * For GCP to be considered "authenticated", the user must have:
197
+ * 1. Logged in with `gcloud auth login`
198
+ * 2. Set a default project with `gcloud config set project PROJECT_ID`
199
+ * 3. Configured Application Default Credentials with `gcloud auth application-default login`
175
200
  */
176
201
  export async function checkGcloudCli() {
177
202
  const status = {
@@ -196,16 +221,27 @@ export async function checkGcloudCli() {
196
221
  const config = JSON.parse(configResult.stdout);
197
222
  const account = config.core?.account;
198
223
  const project = config.core?.project;
224
+ // Step 1: Check if logged in
199
225
  if (!account) {
200
226
  status.error = 'Not authenticated - run "gcloud auth login"';
201
227
  return status;
202
228
  }
203
- status.authenticated = true;
204
- status.identity = project ? `Project: ${project}` : `Account: ${account}`;
229
+ // Step 2: Check if project is set
205
230
  if (!project) {
206
231
  status.error =
207
232
  'No default project set - run "gcloud config set project PROJECT_ID"';
233
+ return status;
208
234
  }
235
+ // Step 3: Check Application Default Credentials
236
+ const adcResult = await checkGcpApplicationDefaultCredentials();
237
+ if (!adcResult.configured) {
238
+ status.error =
239
+ 'Application Default Credentials not configured - run "gcloud auth application-default login"';
240
+ return status;
241
+ }
242
+ // All checks passed
243
+ status.authenticated = true;
244
+ status.identity = `Project: ${project}`;
209
245
  }
210
246
  catch {
211
247
  status.error = "Failed to parse gcloud config";
@@ -229,6 +265,39 @@ export async function getGcpProjectId() {
229
265
  return null;
230
266
  }
231
267
  }
268
+ /**
269
+ * Check if GCP Application Default Credentials (ADC) are configured
270
+ * ADC is required for Terraform to authenticate with Google Cloud
271
+ */
272
+ export async function checkGcpApplicationDefaultCredentials() {
273
+ try {
274
+ // Try to get an access token using ADC
275
+ const result = await execCommand("gcloud auth application-default print-access-token");
276
+ if (result.stdout && result.stdout.trim().length > 0) {
277
+ return { configured: true };
278
+ }
279
+ return {
280
+ configured: false,
281
+ error: "Application Default Credentials not configured",
282
+ };
283
+ }
284
+ catch (error) {
285
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
286
+ // Check if it's specifically an authentication error
287
+ if (errorMessage.includes("not found") ||
288
+ errorMessage.includes("not configured") ||
289
+ errorMessage.includes("Could not automatically determine credentials")) {
290
+ return {
291
+ configured: false,
292
+ error: "Application Default Credentials not configured",
293
+ };
294
+ }
295
+ return {
296
+ configured: false,
297
+ error: errorMessage,
298
+ };
299
+ }
300
+ }
232
301
  /**
233
302
  * List available GCP regions
234
303
  */
@@ -266,40 +335,33 @@ export async function listGcsBuckets() {
266
335
  }
267
336
  }
268
337
  /**
269
- * Static fallback for GCP regions
338
+ * Static fallback for GCP regions (C4A/Google Axion ARM64 confirmed availability)
339
+ * Only includes regions with full C4A zone coverage for GKE regional clusters
270
340
  */
271
341
  function getStaticGcpRegions() {
272
342
  return [
273
- "us-central1",
274
- "us-east1",
275
- "us-east4",
276
- "us-west1",
277
- "us-west2",
278
- "us-west3",
279
- "us-west4",
280
- "northamerica-northeast1",
281
- "northamerica-northeast2",
282
- "southamerica-east1",
283
- "southamerica-west1",
284
- "europe-central2",
285
- "europe-north1",
286
- "europe-west1",
287
- "europe-west2",
288
- "europe-west3",
289
- "europe-west4",
290
- "europe-west6",
291
- "europe-southwest1",
292
- "asia-east1",
293
- "asia-east2",
294
- "asia-northeast1",
295
- "asia-northeast2",
296
- "asia-northeast3",
297
- "asia-south1",
298
- "asia-south2",
299
- "asia-southeast1",
300
- "asia-southeast2",
301
- "australia-southeast1",
302
- "australia-southeast2",
343
+ // Tier 1: Full C4A (Google Axion ARM64) availability - 3+ zones confirmed
344
+ // US regions
345
+ "us-central1", // C4A in zones a, b, c, f (best availability)
346
+ "us-east1", // C4A in zones b, c, d
347
+ "us-east4", // C4A in zones a, b, c
348
+ "us-west1", // C4A in zones a, b, c
349
+ "us-west4", // C4A in zones a, b, c
350
+ // North America
351
+ "northamerica-south1", // C4A in zones a, b, c (Mexico)
352
+ // Europe
353
+ "europe-west1", // C4A in zones b, c, d
354
+ "europe-west2", // C4A in zones a, b, c
355
+ "europe-west3", // C4A in zones a, b, c
356
+ "europe-west4", // C4A in zones a, b, c
357
+ "europe-north1", // C4A in zones a, b
358
+ // Asia Pacific
359
+ "asia-east1", // C4A in zones a, b, c
360
+ "asia-northeast1", // C4A in zones b, c
361
+ "asia-south1", // C4A in zones a, b, c
362
+ "asia-southeast1", // C4A in zones a, b, c
363
+ // Australia
364
+ "australia-southeast2", // C4A in zones a, b, c
303
365
  ];
304
366
  }
305
367
  /**
@@ -324,7 +386,13 @@ export async function listGkeClusters(region) {
324
386
  // Azure CLI
325
387
  // ============================================================================
326
388
  /**
327
- * Check if Azure CLI is installed and authenticated
389
+ * Check if Azure CLI is installed and fully authenticated
390
+ *
391
+ * For Azure to be considered "authenticated", the user must have:
392
+ * 1. Logged in with `az login`
393
+ * 2. An active subscription in "Enabled" state
394
+ * 3. Required resource providers registered (Microsoft.ContainerService, etc.)
395
+ * 4. Sufficient vCPU quota for at least the small tier (8 cores)
328
396
  */
329
397
  export async function checkAzureCli() {
330
398
  const status = {
@@ -343,22 +411,47 @@ export async function checkAzureCli() {
343
411
  // Extract version (e.g., "azure-cli 2.51.0")
344
412
  const versionMatch = versionResult.stdout.match(/azure-cli\s+([\d.]+)/);
345
413
  status.version = versionMatch ? versionMatch[1] : undefined;
346
- // Check authentication
414
+ // Step 1: Check authentication and subscription
347
415
  const accountResult = await execCommand("az account show --output json");
348
416
  if (accountResult.stderr && accountResult.stderr.includes("Please run")) {
349
417
  status.error = 'Not authenticated - run "az login"';
350
418
  return status;
351
419
  }
420
+ let subscriptionName;
352
421
  try {
353
422
  const account = JSON.parse(accountResult.stdout);
354
- status.authenticated = true;
355
- status.identity = account.name
356
- ? `Subscription: ${account.name}`
423
+ subscriptionName = account.name;
424
+ // Step 2: Check subscription state is Enabled
425
+ if (account.state !== "Enabled") {
426
+ status.error = `Subscription "${account.name}" is not enabled (state: ${account.state})`;
427
+ return status;
428
+ }
429
+ status.identity = subscriptionName
430
+ ? `Subscription: ${subscriptionName}`
357
431
  : undefined;
358
432
  }
359
433
  catch {
360
434
  status.error = "Failed to parse account info";
435
+ return status;
436
+ }
437
+ // Step 3: Check required resource providers are registered
438
+ const providerCheck = await checkAzureResourceProviders();
439
+ if (!providerCheck.allRegistered) {
440
+ status.error =
441
+ `Resource providers not registered: ${providerCheck.missing.join(", ")}. ` +
442
+ `Run: ${providerCheck.missing.map((p) => `az provider register --namespace ${p}`).join(" && ")}`;
443
+ return status;
444
+ }
445
+ // Step 4: Check minimum vCPU quota (small tier = 8 cores)
446
+ const quotaCheck = await checkAzureVmQuota(AZURE_DEFAULT_QUOTA_CHECK_REGION, AZURE_TIER_CORES.small);
447
+ if (!quotaCheck.sufficient) {
448
+ status.error =
449
+ `Insufficient vCPU quota (${quotaCheck.available}/${quotaCheck.limit} available in ${AZURE_DEFAULT_QUOTA_CHECK_REGION}). ` +
450
+ "Request increase at Azure portal > Subscriptions > Usage + quotas";
451
+ return status;
361
452
  }
453
+ // All checks passed
454
+ status.authenticated = true;
362
455
  }
363
456
  catch (error) {
364
457
  status.error = error instanceof Error ? error.message : "Unknown error";
@@ -426,42 +519,61 @@ export async function listAzureBlobContainers(storageAccount) {
426
519
  }
427
520
  }
428
521
  /**
429
- * Static fallback for Azure regions
522
+ * Static fallback for Azure regions (Dpsv5 ARM64 available or expected)
430
523
  */
431
524
  function getStaticAzureRegions() {
432
525
  return [
526
+ // US regions (Dpsv5 ARM64 available)
433
527
  "eastus",
434
528
  "eastus2",
435
- "centralus",
436
- "northcentralus",
437
- "southcentralus",
438
529
  "westus",
439
530
  "westus2",
440
531
  "westus3",
532
+ "centralus",
533
+ "northcentralus",
534
+ "southcentralus",
535
+ "westcentralus",
536
+ // Canada
441
537
  "canadacentral",
442
538
  "canadaeast",
539
+ // South America
443
540
  "brazilsouth",
541
+ // Europe (Dpsv5 available)
444
542
  "northeurope",
445
543
  "westeurope",
446
544
  "uksouth",
447
545
  "ukwest",
448
546
  "francecentral",
547
+ "francesouth",
449
548
  "germanywestcentral",
549
+ "germanynorth",
450
550
  "switzerlandnorth",
551
+ "switzerlandwest",
451
552
  "norwayeast",
553
+ "norwaywest",
554
+ "swedencentral",
555
+ "polandcentral",
556
+ // Asia Pacific
452
557
  "eastasia",
453
558
  "southeastasia",
454
559
  "japaneast",
455
560
  "japanwest",
456
561
  "koreacentral",
562
+ "koreasouth",
563
+ // Australia
457
564
  "australiaeast",
458
565
  "australiasoutheast",
459
566
  "australiacentral",
567
+ // India
460
568
  "centralindia",
461
569
  "southindia",
462
570
  "westindia",
571
+ // Middle East & Africa
463
572
  "uaenorth",
573
+ "uaecentral",
464
574
  "southafricanorth",
575
+ "qatarcentral",
576
+ "israelcentral",
465
577
  ];
466
578
  }
467
579
  /**
@@ -481,6 +593,104 @@ export async function listAksClusters(resourceGroup) {
481
593
  return [];
482
594
  }
483
595
  }
596
+ /**
597
+ * Required Azure resource providers for AKS deployment
598
+ */
599
+ const AZURE_REQUIRED_PROVIDERS = [
600
+ "Microsoft.ContainerService", // For AKS
601
+ "Microsoft.Network", // For VNets, NSGs
602
+ "Microsoft.ManagedIdentity", // For managed identities
603
+ "Microsoft.Compute", // For VMs
604
+ ];
605
+ /**
606
+ * Azure tier to vCPU core requirements mapping
607
+ */
608
+ export const AZURE_TIER_CORES = {
609
+ small: 8, // 4 nodes × 2 vCPU
610
+ medium: 16, // 4 nodes × 4 vCPU
611
+ large: 40, // 5 nodes × 8 vCPU
612
+ };
613
+ /**
614
+ * Default region used for baseline quota checks when region is not yet selected
615
+ */
616
+ const AZURE_DEFAULT_QUOTA_CHECK_REGION = "eastus";
617
+ /**
618
+ * Check if required Azure resource providers are registered
619
+ */
620
+ export async function checkAzureResourceProviders() {
621
+ const missing = [];
622
+ try {
623
+ for (const provider of AZURE_REQUIRED_PROVIDERS) {
624
+ const result = await execCommand(`az provider show --namespace ${provider} --query "registrationState" --output tsv`);
625
+ const state = result.stdout.trim();
626
+ if (state !== "Registered") {
627
+ missing.push(provider);
628
+ }
629
+ }
630
+ return {
631
+ allRegistered: missing.length === 0,
632
+ missing,
633
+ };
634
+ }
635
+ catch {
636
+ // If we can't check, assume they're not registered
637
+ return {
638
+ allRegistered: false,
639
+ missing: AZURE_REQUIRED_PROVIDERS,
640
+ };
641
+ }
642
+ }
643
+ /**
644
+ * Check Azure VM quota for a specific region
645
+ *
646
+ * @param region - Azure region to check quota for
647
+ * @param requiredCores - Number of vCPUs required
648
+ * @returns Quota check result with availability info
649
+ */
650
+ export async function checkAzureVmQuota(region, requiredCores) {
651
+ try {
652
+ const result = await execCommand(`az vm list-usage --location ${region} --output json`);
653
+ if (result.stderr && !result.stdout) {
654
+ return {
655
+ sufficient: false,
656
+ available: 0,
657
+ limit: 0,
658
+ used: 0,
659
+ error: "Failed to check VM quota",
660
+ };
661
+ }
662
+ const usageList = JSON.parse(result.stdout);
663
+ // Find total regional vCPU quota
664
+ const regionalQuota = usageList.find((u) => u.name.value === "cores" || u.name.localizedValue === "Total Regional vCPUs");
665
+ if (!regionalQuota) {
666
+ return {
667
+ sufficient: false,
668
+ available: 0,
669
+ limit: 0,
670
+ used: 0,
671
+ error: "Could not find regional vCPU quota",
672
+ };
673
+ }
674
+ const used = regionalQuota.currentValue;
675
+ const limit = regionalQuota.limit;
676
+ const available = limit - used;
677
+ return {
678
+ sufficient: available >= requiredCores,
679
+ available,
680
+ limit,
681
+ used,
682
+ };
683
+ }
684
+ catch (error) {
685
+ return {
686
+ sufficient: false,
687
+ available: 0,
688
+ limit: 0,
689
+ used: 0,
690
+ error: error instanceof Error ? error.message : "Failed to check VM quota",
691
+ };
692
+ }
693
+ }
484
694
  // ============================================================================
485
695
  // Aggregated Functions
486
696
  // ============================================================================
@@ -567,8 +777,16 @@ export const CLI_INSTALL_URLS = {
567
777
  */
568
778
  export const CLI_LOGIN_COMMANDS = {
569
779
  aws: "aws configure",
570
- gcp: "gcloud auth login",
571
- azure: "az login",
780
+ gcp: [
781
+ "gcloud auth login",
782
+ "gcloud config set project PROJECT_ID",
783
+ "gcloud auth application-default login",
784
+ ],
785
+ azure: [
786
+ "az login",
787
+ "az account set --subscription YOUR_SUBSCRIPTION_ID",
788
+ "az provider register --namespace Microsoft.ContainerService",
789
+ ],
572
790
  };
573
791
  /**
574
792
  * Check if Terraform is installed
@@ -208,13 +208,24 @@ export async function generateHelmValues(config, options = {}) {
208
208
  // Determine if external-dns should be enabled
209
209
  const externalDnsEnabled = config.dns.autoManage && isSupportedDnsProvider(config.dns.provider);
210
210
  // Determine storage class based on provider
211
+ // Note: GCP uses "hyperdisk-balanced" because C4A instances only support Hyperdisk (not Persistent Disk)
211
212
  const storageClass = config.infrastructure.provider === "aws"
212
213
  ? "gp3"
213
214
  : config.infrastructure.provider === "gcp"
214
- ? "standard"
215
+ ? "hyperdisk-balanced"
215
216
  : config.infrastructure.provider === "azure"
216
217
  ? "managed-premium"
217
218
  : "gp3";
219
+ // ARM64 tolerations for GKE C4A nodes (and other ARM64 providers)
220
+ // GKE automatically taints ARM64 nodes with kubernetes.io/arch=arm64:NoSchedule
221
+ const arm64Tolerations = [
222
+ {
223
+ key: "kubernetes.io/arch",
224
+ operator: "Equal",
225
+ value: "arm64",
226
+ effect: "NoSchedule",
227
+ },
228
+ ];
218
229
  // Build global.supabase configuration
219
230
  const supabaseGlobalConfig = config.database.type === "supabase-cloud"
220
231
  ? {
@@ -305,6 +316,7 @@ export async function generateHelmValues(config, options = {}) {
305
316
  : {}),
306
317
  replicaCount: tierConfig.appReplicas,
307
318
  resources: tierConfig.appResources,
319
+ tolerations: arm64Tolerations,
308
320
  // Logging configuration
309
321
  logging: {
310
322
  enabled: true,
@@ -327,6 +339,7 @@ export async function generateHelmValues(config, options = {}) {
327
339
  : {}),
328
340
  replicas: tierConfig.hpsReplicas,
329
341
  resources: tierConfig.hpsResources,
342
+ tolerations: arm64Tolerations,
330
343
  // HPS Workers with KEDA autoscaling
331
344
  workers: {
332
345
  enabled: true,
@@ -341,6 +354,7 @@ export async function generateHelmValues(config, options = {}) {
341
354
  cpuThreshold: 25,
342
355
  },
343
356
  resources: tierConfig.hpsWorkerResources,
357
+ tolerations: arm64Tolerations,
344
358
  },
345
359
  },
346
360
  // Ingress configuration
@@ -352,6 +366,7 @@ export async function generateHelmValues(config, options = {}) {
352
366
  // Redis configuration
353
367
  redis: {
354
368
  resources: tierConfig.redisResources,
369
+ tolerations: arm64Tolerations,
355
370
  persistence: {
356
371
  enabled: true,
357
372
  size: tierConfig.redisPersistenceSize,
@@ -382,6 +397,7 @@ export async function generateHelmValues(config, options = {}) {
382
397
  controller: {
383
398
  replicaCount: tierConfig.kafkaReplication,
384
399
  resources: tierConfig.kafkaResources,
400
+ tolerations: arm64Tolerations,
385
401
  persistence: {
386
402
  enabled: true,
387
403
  size: tierConfig.kafkaStorage,
@@ -410,6 +426,7 @@ export async function generateHelmValues(config, options = {}) {
410
426
  ingressClass: {
411
427
  name: "traefik",
412
428
  },
429
+ tolerations: arm64Tolerations,
413
430
  autoscaling: {
414
431
  enabled: true,
415
432
  minReplicas: 1,
@@ -450,6 +467,7 @@ export async function generateHelmValues(config, options = {}) {
450
467
  // =============================================================================
451
468
  keda: {
452
469
  enabled: true,
470
+ tolerations: arm64Tolerations,
453
471
  crds: {
454
472
  install: false, // CRDs managed in parent chart
455
473
  },
@@ -460,6 +478,13 @@ export async function generateHelmValues(config, options = {}) {
460
478
  "cert-manager": {
461
479
  enabled: tlsEnabled,
462
480
  installCRDs: false, // CRDs managed in parent chart
481
+ tolerations: arm64Tolerations,
482
+ webhook: {
483
+ tolerations: arm64Tolerations,
484
+ },
485
+ cainjector: {
486
+ tolerations: arm64Tolerations,
487
+ },
463
488
  },
464
489
  // Cluster Issuer for Let's Encrypt
465
490
  clusterIssuer: {
@@ -475,6 +500,7 @@ export async function generateHelmValues(config, options = {}) {
475
500
  role: "Stateless-Aggregator",
476
501
  replicas: tierConfig.vectorReplicas,
477
502
  resources: tierConfig.vectorResources,
503
+ tolerations: arm64Tolerations,
478
504
  service: {
479
505
  enabled: true,
480
506
  ports: [{ name: "api", port: 8686, protocol: "TCP", targetPort: 8686 }],
@@ -527,19 +553,36 @@ export async function generateHelmValues(config, options = {}) {
527
553
  },
528
554
  db: {
529
555
  resources: tierConfig.dbResources,
556
+ tolerations: arm64Tolerations,
530
557
  persistence: {
531
558
  enabled: true,
532
559
  size: tierConfig.dbPersistenceSize,
533
560
  storageClassName: storageClass,
534
561
  },
535
562
  },
563
+ auth: {
564
+ tolerations: arm64Tolerations,
565
+ },
566
+ rest: {
567
+ tolerations: arm64Tolerations,
568
+ },
569
+ realtime: {
570
+ tolerations: arm64Tolerations,
571
+ },
572
+ meta: {
573
+ tolerations: arm64Tolerations,
574
+ },
536
575
  kong: {
576
+ tolerations: arm64Tolerations,
537
577
  ingress: {
538
578
  enabled: true,
539
579
  className: "traefik",
540
580
  annotations: {},
541
581
  },
542
582
  },
583
+ studio: {
584
+ tolerations: arm64Tolerations,
585
+ },
543
586
  }
544
587
  : {}),
545
588
  },
@@ -597,13 +640,14 @@ export async function generateHelmValues(config, options = {}) {
597
640
  : config.infrastructure.provider === "azure"
598
641
  ? "disk.csi.azure.com"
599
642
  : "ebs.csi.aws.com",
600
- type: config.infrastructure.provider === "aws"
601
- ? "gp3"
643
+ // Parameters for the StorageClass - must include type for disk provisioning
644
+ parameters: config.infrastructure.provider === "aws"
645
+ ? { type: "gp3" }
602
646
  : config.infrastructure.provider === "gcp"
603
- ? "pd-ssd"
647
+ ? { type: "hyperdisk-balanced" }
604
648
  : config.infrastructure.provider === "azure"
605
- ? "Premium_LRS"
606
- : "gp3",
649
+ ? { skuName: "Premium_LRS" }
650
+ : { type: "gp3" },
607
651
  fsType: "ext4",
608
652
  reclaimPolicy: "Delete",
609
653
  volumeBindingMode: "WaitForFirstConsumer",
@@ -1,4 +1,4 @@
1
- import { CloudProvider } from '../types/index.js';
1
+ import { CloudProvider, DeploymentConfig } from '../types/index.js';
2
2
  /**
3
3
  * Checks if Terraform is installed
4
4
  */
@@ -24,7 +24,8 @@ export declare function terraformPlan(deploymentName: string): Promise<void>;
24
24
  */
25
25
  export declare function terraformApply(deploymentName: string): Promise<void>;
26
26
  /**
27
- * Destroys Terraform infrastructure
27
+ * Destroys Terraform infrastructure.
28
+ * Runs init first to ensure .terraform folder exists (handles partial deployments).
28
29
  */
29
30
  export declare function terraformDestroy(deploymentName: string): Promise<void>;
30
31
  /**
@@ -32,9 +33,15 @@ export declare function terraformDestroy(deploymentName: string): Promise<void>;
32
33
  */
33
34
  export declare function getTerraformOutputs(deploymentName: string): Promise<Record<string, string>>;
34
35
  /**
35
- * Checks if Terraform state exists for a deployment
36
+ * Checks if Terraform files/state exist for a deployment.
37
+ * Returns true if the terraform directory contains any terraform files,
38
+ * not just the state file. This allows destroy to work on partial infrastructure.
36
39
  */
37
40
  export declare function hasTerraformState(deploymentName: string): Promise<boolean>;
41
+ /**
42
+ * Generates Terraform variables from deployment configuration
43
+ */
44
+ export declare function generateTerraformVars(config: DeploymentConfig): Record<string, unknown>;
38
45
  /**
39
46
  * Updates kubeconfig for the provisioned cluster
40
47
  */