@go-to-k/cdkd 0.11.0 → 0.13.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/dist/cli.js CHANGED
@@ -450,7 +450,7 @@ var init_aws_clients = __esm({
450
450
  import { Command as Command10 } from "commander";
451
451
 
452
452
  // src/cli/commands/bootstrap.ts
453
- import { Command } from "commander";
453
+ import { Command, Option as Option2 } from "commander";
454
454
  import {
455
455
  CreateBucketCommand,
456
456
  HeadBucketCommand,
@@ -476,13 +476,23 @@ function parseContextOptions(contextArgs) {
476
476
  }
477
477
  var commonOptions = [
478
478
  new Option("--verbose", "Enable verbose logging").default(false),
479
- new Option("--region <region>", "AWS region"),
480
479
  new Option("--profile <profile>", "AWS profile"),
481
480
  new Option(
482
481
  "-y, --yes",
483
482
  "Automatically answer interactive prompts with the recommended response (e.g. confirm destroy)"
484
483
  ).default(false)
485
484
  ];
485
+ var deprecatedRegionOption = new Option(
486
+ "--region <region>",
487
+ "[deprecated] No effect on this command; use AWS_REGION or your AWS profile"
488
+ ).hideHelp();
489
+ function warnIfDeprecatedRegion(options) {
490
+ if (options.region !== void 0) {
491
+ process.stderr.write(
492
+ "Warning: --region is deprecated for this command and has no effect. Use the AWS_REGION environment variable or your AWS profile to override the SDK default region.\n"
493
+ );
494
+ }
495
+ }
486
496
  var appOptions = [
487
497
  new Option(
488
498
  "-a, --app <command>",
@@ -960,16 +970,18 @@ function resolveApp(cliApp) {
960
970
  const cdkJson = loadCdkJson();
961
971
  return cdkJson?.app ?? void 0;
962
972
  }
963
- function resolveStateBucket(cliBucket) {
973
+ function resolveStateBucketWithSource(cliBucket) {
964
974
  if (cliBucket)
965
- return cliBucket;
975
+ return { bucket: cliBucket, source: "cli-flag" };
966
976
  const envBucket = process.env["CDKD_STATE_BUCKET"];
967
977
  if (envBucket)
968
- return envBucket;
978
+ return { bucket: envBucket, source: "env" };
969
979
  const cdkJson = loadCdkJson();
970
980
  const cdkdContext = cdkJson?.context?.["cdkd"];
971
981
  const bucket = cdkdContext?.["stateBucket"];
972
- return typeof bucket === "string" ? bucket : void 0;
982
+ if (typeof bucket === "string")
983
+ return { bucket, source: "cdk.json" };
984
+ return void 0;
973
985
  }
974
986
  function getDefaultStateBucketName(accountId) {
975
987
  return `cdkd-state-${accountId}`;
@@ -978,7 +990,10 @@ function getLegacyStateBucketName(accountId, region) {
978
990
  return `cdkd-state-${accountId}-${region}`;
979
991
  }
980
992
  async function resolveStateBucketWithDefault(cliBucket, region) {
981
- const syncResult = resolveStateBucket(cliBucket);
993
+ return (await resolveStateBucketWithDefaultAndSource(cliBucket, region)).bucket;
994
+ }
995
+ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
996
+ const syncResult = resolveStateBucketWithSource(cliBucket);
982
997
  if (syncResult)
983
998
  return syncResult;
984
999
  const logger = getLogger();
@@ -994,14 +1009,14 @@ async function resolveStateBucketWithDefault(cliBucket, region) {
994
1009
  const probe = new S3Client10({ region: "us-east-1" });
995
1010
  try {
996
1011
  if (await bucketExists(probe, newName)) {
997
- logger.info(`State bucket: ${newName}`);
998
- return newName;
1012
+ logger.debug(`State bucket: ${newName}`);
1013
+ return { bucket: newName, source: "default" };
999
1014
  }
1000
1015
  if (await bucketExists(probe, legacyName)) {
1001
1016
  logger.warn(
1002
1017
  `Using legacy state bucket name '${legacyName}'. The default has changed to '${newName}'. Future cdkd versions will drop legacy support; consider migrating with cdkd state migrate-bucket (coming in a future release).`
1003
1018
  );
1004
- return legacyName;
1019
+ return { bucket: legacyName, source: "default-legacy" };
1005
1020
  }
1006
1021
  throw new Error(
1007
1022
  `No cdkd state bucket found for account ${accountId}. Looked for '${newName}' (current default) and '${legacyName}' (legacy default). Run 'cdkd bootstrap' to create '${newName}'.`
@@ -1158,7 +1173,16 @@ function createBootstrapCommand() {
1158
1173
  const cmd = new Command("bootstrap").description("Bootstrap cdkd by creating required S3 bucket for state management").option(
1159
1174
  "--state-bucket <bucket>",
1160
1175
  "Name of S3 bucket to create for state storage (default: cdkd-state-{accountId})"
1161
- ).option("--force", "Force reconfiguration of existing bucket", false).action(withErrorHandling(bootstrapCommand));
1176
+ ).option("--force", "Force reconfiguration of existing bucket", false).addOption(
1177
+ // Bootstrap-specific: needs to know which region to create the bucket
1178
+ // in. After PR 5, `--region` is removed from `commonOptions` and only
1179
+ // re-added explicitly here — every other command resolves the region
1180
+ // from `AWS_REGION` / profile.
1181
+ new Option2(
1182
+ "--region <region>",
1183
+ "AWS region in which to create the state bucket (defaults to AWS_REGION env or us-east-1)"
1184
+ )
1185
+ ).action(withErrorHandling(bootstrapCommand));
1162
1186
  commonOptions.forEach((opt) => cmd.addOption(opt));
1163
1187
  return cmd;
1164
1188
  }
@@ -2493,6 +2517,7 @@ async function synthCommand(options) {
2493
2517
  if (options.verbose) {
2494
2518
  logger.setLevel("debug");
2495
2519
  }
2520
+ warnIfDeprecatedRegion(options);
2496
2521
  const app = resolveApp(options.app);
2497
2522
  if (!app) {
2498
2523
  throw new Error(
@@ -2540,6 +2565,7 @@ Output: ${assemblyDir}`);
2540
2565
  function createSynthCommand() {
2541
2566
  const cmd = new Command2("synth").description("Synthesize CDK app to CloudFormation template").action(withErrorHandling(synthCommand));
2542
2567
  [...commonOptions, ...appOptions, ...contextOptions].forEach((opt) => cmd.addOption(opt));
2568
+ cmd.addOption(deprecatedRegionOption);
2543
2569
  return cmd;
2544
2570
  }
2545
2571
 
@@ -2622,6 +2648,7 @@ async function listCommand(patterns, options) {
2622
2648
  if (options.verbose) {
2623
2649
  logger.setLevel("debug");
2624
2650
  }
2651
+ warnIfDeprecatedRegion(options);
2625
2652
  const app = resolveApp(options.app);
2626
2653
  if (!app) {
2627
2654
  throw new Error(
@@ -2687,6 +2714,7 @@ function createListCommand() {
2687
2714
  "Stack name pattern(s). Accepts physical CloudFormation names (e.g. 'MyStage-Api') or CDK display paths (e.g. 'MyStage/Api'). Supports wildcards (e.g. 'MyStage/*')."
2688
2715
  ).option("-l, --long", "Display environment information for each stack", false).option("-d, --show-dependencies", "Display stack dependency information for each stack", false).option("--json", "Output as JSON instead of YAML for --long / --show-dependencies", false).action(withErrorHandling(listCommand));
2689
2716
  [...commonOptions, ...appOptions, ...contextOptions].forEach((opt) => cmd.addOption(opt));
2717
+ cmd.addOption(deprecatedRegionOption);
2690
2718
  return cmd;
2691
2719
  }
2692
2720
 
@@ -28586,6 +28614,7 @@ async function deployCommand(stacks, options) {
28586
28614
  logger.setLevel("debug");
28587
28615
  process.env["CDKD_NO_LIVE"] = "1";
28588
28616
  }
28617
+ warnIfDeprecatedRegion(options);
28589
28618
  if (!options.wait) {
28590
28619
  process.env["CDKD_NO_WAIT"] = "true";
28591
28620
  }
@@ -28835,6 +28864,7 @@ function createDeployCommand() {
28835
28864
  ...deployOptions,
28836
28865
  ...contextOptions
28837
28866
  ].forEach((opt) => cmd.addOption(opt));
28867
+ cmd.addOption(deprecatedRegionOption);
28838
28868
  return cmd;
28839
28869
  }
28840
28870
 
@@ -28903,6 +28933,7 @@ async function diffCommand(stacks, options) {
28903
28933
  if (options.verbose) {
28904
28934
  logger.setLevel("debug");
28905
28935
  }
28936
+ warnIfDeprecatedRegion(options);
28906
28937
  const app = resolveApp(options.app);
28907
28938
  if (!app) {
28908
28939
  throw new Error(
@@ -29046,19 +29077,219 @@ function createDiffCommand() {
29046
29077
  [...commonOptions, ...appOptions, ...stateOptions, ...stackOptions, ...contextOptions].forEach(
29047
29078
  (opt) => cmd.addOption(opt)
29048
29079
  );
29080
+ cmd.addOption(deprecatedRegionOption);
29049
29081
  return cmd;
29050
29082
  }
29051
29083
 
29052
29084
  // src/cli/commands/destroy.ts
29053
29085
  import { Command as Command6 } from "commander";
29054
29086
  init_aws_clients();
29087
+
29088
+ // src/cli/commands/destroy-runner.ts
29055
29089
  import * as readline from "node:readline/promises";
29090
+ init_aws_clients();
29091
+ async function runDestroyForStack(stackName, state, ctx) {
29092
+ const logger = getLogger();
29093
+ const result = {
29094
+ stackName,
29095
+ cancelled: false,
29096
+ skippedEmpty: false,
29097
+ deletedCount: 0,
29098
+ errorCount: 0
29099
+ };
29100
+ const resourceCount = Object.keys(state.resources).length;
29101
+ const regionForState = state.region ?? ctx.baseRegion;
29102
+ if (resourceCount === 0) {
29103
+ logger.info(`Stack ${stackName} has no resources, cleaning up state...`);
29104
+ await ctx.stateBackend.deleteState(stackName, regionForState);
29105
+ logger.info("\u2713 State deleted");
29106
+ result.skippedEmpty = true;
29107
+ return result;
29108
+ }
29109
+ logger.info(`
29110
+ Resources to be deleted (${resourceCount}):`);
29111
+ for (const [logicalId, resource] of Object.entries(state.resources)) {
29112
+ logger.info(` - ${logicalId} (${resource.resourceType})`);
29113
+ }
29114
+ if (!ctx.skipConfirmation) {
29115
+ const rl = readline.createInterface({
29116
+ input: process.stdin,
29117
+ output: process.stdout
29118
+ });
29119
+ const answer = await rl.question(
29120
+ `
29121
+ Are you sure you want to destroy stack "${stackName}" and delete all ${resourceCount} resources? (Y/n): `
29122
+ );
29123
+ rl.close();
29124
+ const trimmed = answer.trim().toLowerCase();
29125
+ if (trimmed === "n" || trimmed === "no") {
29126
+ logger.info("Destroy cancelled");
29127
+ result.cancelled = true;
29128
+ return result;
29129
+ }
29130
+ }
29131
+ const stackRegion = state.region;
29132
+ let destroyProviderRegistry = ctx.providerRegistry;
29133
+ let destroyAwsClients;
29134
+ if (stackRegion && stackRegion !== ctx.baseRegion) {
29135
+ logger.info(`Stack region: ${stackRegion}`);
29136
+ process.env["AWS_REGION"] = stackRegion;
29137
+ process.env["AWS_DEFAULT_REGION"] = stackRegion;
29138
+ destroyAwsClients = new AwsClients({
29139
+ region: stackRegion,
29140
+ ...ctx.profile && { profile: ctx.profile }
29141
+ });
29142
+ setAwsClients(destroyAwsClients);
29143
+ destroyProviderRegistry = new ProviderRegistry();
29144
+ registerAllProviders(destroyProviderRegistry);
29145
+ destroyProviderRegistry.setCustomResourceResponseBucket(ctx.stateBucket);
29146
+ }
29147
+ logger.info(`
29148
+ Acquiring lock for stack ${stackName}...`);
29149
+ await ctx.lockManager.acquireLock(stackName, regionForState, void 0, "destroy");
29150
+ const renderer = getLiveRenderer();
29151
+ renderer.start();
29152
+ try {
29153
+ logger.info("Building dependency graph...");
29154
+ const template = {
29155
+ AWSTemplateFormatVersion: "2010-09-09",
29156
+ Resources: {}
29157
+ };
29158
+ for (const [logicalId, resource] of Object.entries(state.resources)) {
29159
+ template.Resources[logicalId] = {
29160
+ Type: resource.resourceType,
29161
+ Properties: resource.properties || {},
29162
+ ...resource.dependencies && resource.dependencies.length > 0 && {
29163
+ DependsOn: resource.dependencies
29164
+ }
29165
+ };
29166
+ }
29167
+ const typeToLogicalIds = /* @__PURE__ */ new Map();
29168
+ for (const [logicalId, resource] of Object.entries(state.resources)) {
29169
+ const ids = typeToLogicalIds.get(resource.resourceType) ?? [];
29170
+ ids.push(logicalId);
29171
+ typeToLogicalIds.set(resource.resourceType, ids);
29172
+ }
29173
+ for (const [logicalId, resource] of Object.entries(state.resources)) {
29174
+ const mustDeleteAfter = IMPLICIT_DELETE_DEPENDENCIES[resource.resourceType];
29175
+ if (!mustDeleteAfter)
29176
+ continue;
29177
+ for (const depType of mustDeleteAfter) {
29178
+ const depIds = typeToLogicalIds.get(depType);
29179
+ if (!depIds)
29180
+ continue;
29181
+ for (const depId of depIds) {
29182
+ const existing = template.Resources[depId]?.DependsOn ?? [];
29183
+ const depsArray = Array.isArray(existing) ? existing : [existing];
29184
+ if (!depsArray.includes(logicalId)) {
29185
+ template.Resources[depId] = {
29186
+ ...template.Resources[depId],
29187
+ DependsOn: [...depsArray, logicalId]
29188
+ };
29189
+ logger.debug(
29190
+ `Implicit delete dependency: ${depId} (${depType}) must be deleted before ${logicalId} (${resource.resourceType})`
29191
+ );
29192
+ }
29193
+ }
29194
+ }
29195
+ }
29196
+ const dagBuilder = new DagBuilder();
29197
+ const graph = dagBuilder.buildGraph(template);
29198
+ const executionLevels = dagBuilder.getExecutionLevels(graph);
29199
+ logger.debug(`Dependency graph: ${executionLevels.length} level(s)`);
29200
+ for (let levelIndex = executionLevels.length - 1; levelIndex >= 0; levelIndex--) {
29201
+ const level = executionLevels[levelIndex];
29202
+ if (!level)
29203
+ continue;
29204
+ logger.debug(
29205
+ `Deletion level ${executionLevels.length - levelIndex}/${executionLevels.length} (${level.length} resources)`
29206
+ );
29207
+ const deletePromises = level.map(async (logicalId) => {
29208
+ const resource = state.resources[logicalId];
29209
+ if (!resource) {
29210
+ logger.warn(`Resource ${logicalId} not found in state, skipping`);
29211
+ return;
29212
+ }
29213
+ renderer.addTask(logicalId, `Deleting ${logicalId} (${resource.resourceType})`);
29214
+ try {
29215
+ const provider = destroyProviderRegistry.getProvider(resource.resourceType);
29216
+ let lastDeleteError;
29217
+ for (let attempt = 0; attempt <= 3; attempt++) {
29218
+ try {
29219
+ await provider.delete(
29220
+ logicalId,
29221
+ resource.physicalId,
29222
+ resource.resourceType,
29223
+ resource.properties
29224
+ );
29225
+ lastDeleteError = null;
29226
+ break;
29227
+ } catch (retryError) {
29228
+ lastDeleteError = retryError;
29229
+ const msg = retryError instanceof Error ? retryError.message : String(retryError);
29230
+ const isRetryable = msg.includes("Too Many Requests") || msg.includes("has dependencies") || msg.includes("can't be deleted since") || msg.includes("DependencyViolation");
29231
+ if (!isRetryable || attempt >= 3)
29232
+ break;
29233
+ const delay = 5e3 * Math.pow(2, attempt);
29234
+ logger.debug(
29235
+ ` \u23F3 Retrying delete ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/3)`
29236
+ );
29237
+ await new Promise((resolve4) => setTimeout(resolve4, delay));
29238
+ }
29239
+ }
29240
+ if (lastDeleteError)
29241
+ throw lastDeleteError;
29242
+ renderer.removeTask(logicalId);
29243
+ logger.info(` \u2705 ${logicalId} (${resource.resourceType}) deleted`);
29244
+ result.deletedCount++;
29245
+ } catch (error) {
29246
+ renderer.removeTask(logicalId);
29247
+ const msg = error instanceof Error ? error.message : String(error);
29248
+ if (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException")) {
29249
+ logger.debug(` ${logicalId} already deleted, removing from state`);
29250
+ result.deletedCount++;
29251
+ } else {
29252
+ logger.error(` \u2717 Failed to delete ${logicalId}:`, String(error));
29253
+ result.errorCount++;
29254
+ }
29255
+ } finally {
29256
+ renderer.removeTask(logicalId);
29257
+ }
29258
+ });
29259
+ await Promise.all(deletePromises);
29260
+ }
29261
+ if (result.errorCount === 0) {
29262
+ await ctx.stateBackend.deleteState(stackName, regionForState);
29263
+ logger.debug("State deleted");
29264
+ } else {
29265
+ logger.warn(`${result.errorCount} resource(s) failed to delete. State preserved.`);
29266
+ }
29267
+ logger.info(
29268
+ `
29269
+ \u2713 Stack ${stackName} destroyed (${result.deletedCount} deleted, ${result.errorCount} errors)`
29270
+ );
29271
+ } finally {
29272
+ renderer.stop();
29273
+ logger.debug("Releasing lock...");
29274
+ await ctx.lockManager.releaseLock(stackName, regionForState);
29275
+ if (destroyAwsClients) {
29276
+ destroyAwsClients.destroy();
29277
+ process.env["AWS_REGION"] = ctx.baseRegion;
29278
+ process.env["AWS_DEFAULT_REGION"] = ctx.baseRegion;
29279
+ setAwsClients(ctx.baseAwsClients);
29280
+ }
29281
+ }
29282
+ return result;
29283
+ }
29284
+
29285
+ // src/cli/commands/destroy.ts
29056
29286
  async function destroyCommand(stackArgs, options) {
29057
29287
  const logger = getLogger();
29058
29288
  if (options.verbose) {
29059
29289
  logger.setLevel("debug");
29060
29290
  process.env["CDKD_NO_LIVE"] = "1";
29061
29291
  }
29292
+ warnIfDeprecatedRegion(options);
29062
29293
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
29063
29294
  const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region);
29064
29295
  logger.info("Starting stack destruction...");
@@ -29083,7 +29314,6 @@ async function destroyCommand(stackArgs, options) {
29083
29314
  });
29084
29315
  await stateBackend.verifyBucketExists();
29085
29316
  const lockManager = new LockManager(awsClients.s3, stateConfig);
29086
- const dagBuilder = new DagBuilder();
29087
29317
  const providerRegistry = new ProviderRegistry();
29088
29318
  registerAllProviders(providerRegistry);
29089
29319
  providerRegistry.setCustomResourceResponseBucket(stateBucket);
@@ -29183,189 +29413,16 @@ Preparing to destroy stack: ${stackName}`);
29183
29413
  logger.warn(`No state found for stack ${stackName}, skipping`);
29184
29414
  continue;
29185
29415
  }
29186
- const currentState = stateResult.state;
29187
- const resourceCount = Object.keys(currentState.resources).length;
29188
- if (resourceCount === 0) {
29189
- logger.info(`Stack ${stackName} has no resources, cleaning up state...`);
29190
- await stateBackend.deleteState(stackName, stackTargetRegion);
29191
- logger.info("\u2713 State deleted");
29192
- continue;
29193
- }
29194
- logger.info(`
29195
- Resources to be deleted (${resourceCount}):`);
29196
- for (const [logicalId, resource] of Object.entries(currentState.resources)) {
29197
- logger.info(` - ${logicalId} (${resource.resourceType})`);
29198
- }
29199
- if (!options.yes && !options.force) {
29200
- const rl = readline.createInterface({
29201
- input: process.stdin,
29202
- output: process.stdout
29203
- });
29204
- const answer = await rl.question(
29205
- `
29206
- Are you sure you want to destroy stack "${stackName}" and delete all ${resourceCount} resources? (Y/n): `
29207
- );
29208
- rl.close();
29209
- const trimmed = answer.trim().toLowerCase();
29210
- if (trimmed === "n" || trimmed === "no") {
29211
- logger.info("Destroy cancelled");
29212
- continue;
29213
- }
29214
- }
29215
- const stackRegion = stackTargetRegion;
29216
- let destroyProviderRegistry = providerRegistry;
29217
- let destroyAwsClients;
29218
- if (stackRegion && stackRegion !== region) {
29219
- logger.info(`Stack region: ${stackRegion}`);
29220
- process.env["AWS_REGION"] = stackRegion;
29221
- process.env["AWS_DEFAULT_REGION"] = stackRegion;
29222
- destroyAwsClients = new AwsClients({
29223
- region: stackRegion,
29224
- ...options.profile && { profile: options.profile }
29225
- });
29226
- setAwsClients(destroyAwsClients);
29227
- destroyProviderRegistry = new ProviderRegistry();
29228
- registerAllProviders(destroyProviderRegistry);
29229
- destroyProviderRegistry.setCustomResourceResponseBucket(stateBucket);
29230
- }
29231
- logger.info(`
29232
- Acquiring lock for stack ${stackName}...`);
29233
- await lockManager.acquireLock(stackName, stackRegion, void 0, "destroy");
29234
- const renderer = getLiveRenderer();
29235
- renderer.start();
29236
- try {
29237
- logger.info("Building dependency graph...");
29238
- const template = {
29239
- AWSTemplateFormatVersion: "2010-09-09",
29240
- Resources: {}
29241
- };
29242
- for (const [logicalId, resource] of Object.entries(currentState.resources)) {
29243
- template.Resources[logicalId] = {
29244
- Type: resource.resourceType,
29245
- Properties: resource.properties || {},
29246
- ...resource.dependencies && resource.dependencies.length > 0 && {
29247
- DependsOn: resource.dependencies
29248
- }
29249
- };
29250
- }
29251
- const typeToLogicalIds = /* @__PURE__ */ new Map();
29252
- for (const [logicalId, resource] of Object.entries(currentState.resources)) {
29253
- const ids = typeToLogicalIds.get(resource.resourceType) ?? [];
29254
- ids.push(logicalId);
29255
- typeToLogicalIds.set(resource.resourceType, ids);
29256
- }
29257
- for (const [logicalId, resource] of Object.entries(currentState.resources)) {
29258
- const mustDeleteAfter = IMPLICIT_DELETE_DEPENDENCIES[resource.resourceType];
29259
- if (!mustDeleteAfter)
29260
- continue;
29261
- for (const depType of mustDeleteAfter) {
29262
- const depIds = typeToLogicalIds.get(depType);
29263
- if (!depIds)
29264
- continue;
29265
- for (const depId of depIds) {
29266
- const existing = template.Resources[depId]?.DependsOn ?? [];
29267
- const depsArray = Array.isArray(existing) ? existing : [existing];
29268
- if (!depsArray.includes(logicalId)) {
29269
- template.Resources[depId] = {
29270
- ...template.Resources[depId],
29271
- DependsOn: [...depsArray, logicalId]
29272
- };
29273
- logger.debug(
29274
- `Implicit delete dependency: ${depId} (${depType}) must be deleted before ${logicalId} (${resource.resourceType})`
29275
- );
29276
- }
29277
- }
29278
- }
29279
- }
29280
- const graph = dagBuilder.buildGraph(template);
29281
- const executionLevels = dagBuilder.getExecutionLevels(graph);
29282
- logger.debug(`Dependency graph: ${executionLevels.length} level(s)`);
29283
- let deletedCount = 0;
29284
- let errorCount = 0;
29285
- for (let levelIndex = executionLevels.length - 1; levelIndex >= 0; levelIndex--) {
29286
- const level = executionLevels[levelIndex];
29287
- if (!level) {
29288
- continue;
29289
- }
29290
- logger.debug(
29291
- `Deletion level ${executionLevels.length - levelIndex}/${executionLevels.length} (${level.length} resources)`
29292
- );
29293
- const deletePromises = level.map(async (logicalId) => {
29294
- const resource = currentState.resources[logicalId];
29295
- if (!resource) {
29296
- logger.warn(`Resource ${logicalId} not found in state, skipping`);
29297
- return;
29298
- }
29299
- renderer.addTask(logicalId, `Deleting ${logicalId} (${resource.resourceType})`);
29300
- try {
29301
- const provider = destroyProviderRegistry.getProvider(resource.resourceType);
29302
- let lastDeleteError;
29303
- for (let attempt = 0; attempt <= 3; attempt++) {
29304
- try {
29305
- await provider.delete(
29306
- logicalId,
29307
- resource.physicalId,
29308
- resource.resourceType,
29309
- resource.properties,
29310
- { expectedRegion: currentState.region }
29311
- );
29312
- lastDeleteError = null;
29313
- break;
29314
- } catch (retryError) {
29315
- lastDeleteError = retryError;
29316
- const msg = retryError instanceof Error ? retryError.message : String(retryError);
29317
- const isRetryable = msg.includes("Too Many Requests") || msg.includes("has dependencies") || msg.includes("can't be deleted since") || msg.includes("DependencyViolation");
29318
- if (!isRetryable || attempt >= 3)
29319
- break;
29320
- const delay = 5e3 * Math.pow(2, attempt);
29321
- logger.debug(
29322
- ` \u23F3 Retrying delete ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/3)`
29323
- );
29324
- await new Promise((resolve4) => setTimeout(resolve4, delay));
29325
- }
29326
- }
29327
- if (lastDeleteError)
29328
- throw lastDeleteError;
29329
- renderer.removeTask(logicalId);
29330
- logger.info(` \u2705 ${logicalId} (${resource.resourceType}) deleted`);
29331
- deletedCount++;
29332
- } catch (error) {
29333
- renderer.removeTask(logicalId);
29334
- const msg = error instanceof Error ? error.message : String(error);
29335
- if (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException")) {
29336
- logger.debug(` ${logicalId} already deleted, removing from state`);
29337
- deletedCount++;
29338
- } else {
29339
- logger.error(` \u2717 Failed to delete ${logicalId}:`, String(error));
29340
- errorCount++;
29341
- }
29342
- } finally {
29343
- renderer.removeTask(logicalId);
29344
- }
29345
- });
29346
- await Promise.all(deletePromises);
29347
- }
29348
- if (errorCount === 0) {
29349
- await stateBackend.deleteState(stackName, stackRegion);
29350
- logger.debug("State deleted");
29351
- } else {
29352
- logger.warn(`${errorCount} resource(s) failed to delete. State preserved.`);
29353
- }
29354
- logger.info(
29355
- `
29356
- \u2713 Stack ${stackName} destroyed (${deletedCount} deleted, ${errorCount} errors)`
29357
- );
29358
- } finally {
29359
- renderer.stop();
29360
- logger.debug("Releasing lock...");
29361
- await lockManager.releaseLock(stackName, stackRegion);
29362
- if (destroyAwsClients) {
29363
- destroyAwsClients.destroy();
29364
- process.env["AWS_REGION"] = region;
29365
- process.env["AWS_DEFAULT_REGION"] = region;
29366
- setAwsClients(awsClients);
29367
- }
29368
- }
29416
+ await runDestroyForStack(stackName, stateResult.state, {
29417
+ stateBackend,
29418
+ lockManager,
29419
+ providerRegistry,
29420
+ baseAwsClients: awsClients,
29421
+ baseRegion: region,
29422
+ ...options.profile && { profile: options.profile },
29423
+ stateBucket,
29424
+ skipConfirmation: options.yes || options.force
29425
+ });
29369
29426
  }
29370
29427
  } finally {
29371
29428
  awsClients.destroy();
@@ -29384,16 +29441,18 @@ function createDestroyCommand() {
29384
29441
  ...destroyOptions,
29385
29442
  ...contextOptions
29386
29443
  ].forEach((opt) => cmd.addOption(opt));
29444
+ cmd.addOption(deprecatedRegionOption);
29387
29445
  return cmd;
29388
29446
  }
29389
29447
 
29390
29448
  // src/cli/commands/publish-assets.ts
29391
- import { Option as Option2, Command as Command7 } from "commander";
29449
+ import { Option as Option3, Command as Command7 } from "commander";
29392
29450
  async function publishAssetsCommand(options) {
29393
29451
  const logger = getLogger();
29394
29452
  if (options.verbose) {
29395
29453
  logger.setLevel("debug");
29396
29454
  }
29455
+ warnIfDeprecatedRegion(options);
29397
29456
  logger.info("Publishing assets...");
29398
29457
  logger.debug("Asset manifest path:", options.path);
29399
29458
  const publisher = new AssetPublisher();
@@ -29407,25 +29466,27 @@ async function publishAssetsCommand(options) {
29407
29466
  }
29408
29467
  function createPublishAssetsCommand() {
29409
29468
  const cmd = new Command7("publish-assets").description("Publish assets to S3/ECR from asset manifest").requiredOption("--path <path>", "Path to asset manifest file or directory").addOption(
29410
- new Option2(
29469
+ new Option3(
29411
29470
  "--asset-publish-concurrency <number>",
29412
29471
  "Maximum concurrent asset publish operations"
29413
29472
  ).default(8).argParser((value) => parseInt(value, 10))
29414
29473
  ).addOption(
29415
- new Option2("--image-build-concurrency <number>", "Maximum concurrent Docker image builds").default(4).argParser((value) => parseInt(value, 10))
29474
+ new Option3("--image-build-concurrency <number>", "Maximum concurrent Docker image builds").default(4).argParser((value) => parseInt(value, 10))
29416
29475
  ).action(withErrorHandling(publishAssetsCommand));
29417
29476
  commonOptions.forEach((opt) => cmd.addOption(opt));
29477
+ cmd.addOption(deprecatedRegionOption);
29418
29478
  return cmd;
29419
29479
  }
29420
29480
 
29421
29481
  // src/cli/commands/force-unlock.ts
29422
- import { Command as Command8, Option as Option3 } from "commander";
29482
+ import { Command as Command8, Option as Option4 } from "commander";
29423
29483
  init_aws_clients();
29424
29484
  async function forceUnlockCommand(stackArgs, options) {
29425
29485
  const logger = getLogger();
29426
29486
  if (options.verbose) {
29427
29487
  logger.setLevel("debug");
29428
29488
  }
29489
+ warnIfDeprecatedRegion(options);
29429
29490
  const stackPatterns = stackArgs.length > 0 ? stackArgs : options.stack ? [options.stack] : [];
29430
29491
  if (stackPatterns.length === 0) {
29431
29492
  throw new Error("Stack name is required. Usage: cdkd force-unlock <stack-name>");
@@ -29479,18 +29540,24 @@ async function forceUnlockCommand(stackArgs, options) {
29479
29540
  }
29480
29541
  function createForceUnlockCommand() {
29481
29542
  const cmd = new Command8("force-unlock").description("Force-release a stale lock on a stack").argument("[stacks...]", "Stack name(s) to unlock").addOption(
29482
- new Option3(
29543
+ new Option4(
29483
29544
  "--stack-region <region>",
29484
29545
  "Stack region whose lock to release (use when the same stack name has locks in multiple regions). Defaults to all regions where the stack has state."
29485
29546
  )
29486
29547
  ).action(withErrorHandling(forceUnlockCommand));
29487
29548
  [...commonOptions, ...stateOptions, ...stackOptions].forEach((opt) => cmd.addOption(opt));
29549
+ cmd.addOption(deprecatedRegionOption);
29488
29550
  return cmd;
29489
29551
  }
29490
29552
 
29491
29553
  // src/cli/commands/state.ts
29492
29554
  import * as readline2 from "node:readline/promises";
29493
- import { Command as Command9, Option as Option4 } from "commander";
29555
+ import { Command as Command9, Option as Option5 } from "commander";
29556
+ import {
29557
+ GetBucketLocationCommand as GetBucketLocationCommand2,
29558
+ GetObjectCommand as GetObjectCommand4,
29559
+ ListObjectsV2Command as ListObjectsV2Command3
29560
+ } from "@aws-sdk/client-s3";
29494
29561
  init_aws_clients();
29495
29562
  function formatStackRef(ref) {
29496
29563
  return ref.region ? `${ref.stackName} (${ref.region})` : ref.stackName;
@@ -29520,6 +29587,7 @@ function resolveSingleRegion(stackName, refs, requestedRegion) {
29520
29587
  );
29521
29588
  }
29522
29589
  async function setupStateBackend(options) {
29590
+ warnIfDeprecatedRegion(options);
29523
29591
  const awsClients = new AwsClients({
29524
29592
  ...options.region && { region: options.region },
29525
29593
  ...options.profile && { profile: options.profile }
@@ -29538,6 +29606,8 @@ async function setupStateBackend(options) {
29538
29606
  return {
29539
29607
  stateBackend,
29540
29608
  lockManager,
29609
+ awsClients,
29610
+ region,
29541
29611
  bucket,
29542
29612
  prefix,
29543
29613
  dispose: () => awsClients.destroy()
@@ -29628,6 +29698,7 @@ async function stateListCommand(options) {
29628
29698
  function createStateListCommand() {
29629
29699
  const cmd = new Command9("list").alias("ls").description("List stacks registered in the cdkd state bucket").option("-l, --long", "Show resource count, last-modified time, and lock status", false).option("--json", "Output as JSON", false).action(withErrorHandling(stateListCommand));
29630
29700
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
29701
+ cmd.addOption(deprecatedRegionOption);
29631
29702
  return cmd;
29632
29703
  }
29633
29704
  async function stateResourcesCommand(stackName, options) {
@@ -29731,6 +29802,7 @@ function formatLockSummary(lockInfo) {
29731
29802
  function createStateResourcesCommand() {
29732
29803
  const cmd = new Command9("resources").description("List resources recorded in a stack's state").argument("<stack>", "Stack name (physical CloudFormation name)").option("-l, --long", "Include dependencies and attributes per resource", false).option("--json", "Output as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(stateResourcesCommand));
29733
29804
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
29805
+ cmd.addOption(deprecatedRegionOption);
29734
29806
  return cmd;
29735
29807
  }
29736
29808
  async function stateShowCommand(stackName, options) {
@@ -29818,6 +29890,7 @@ async function stateShowCommand(stackName, options) {
29818
29890
  function createStateShowCommand() {
29819
29891
  const cmd = new Command9("show").description("Show the full cdkd state record for a stack (metadata, outputs, resources)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--json", "Output the raw state and lock as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(stateShowCommand));
29820
29892
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
29893
+ cmd.addOption(deprecatedRegionOption);
29821
29894
  return cmd;
29822
29895
  }
29823
29896
  async function stateRmCommand(stackArgs, options) {
@@ -29892,7 +29965,7 @@ Use 'cdkd destroy ${stackName}' if you want to delete the actual resources.
29892
29965
  }
29893
29966
  }
29894
29967
  function stackRegionOption() {
29895
- return new Option4(
29968
+ return new Option5(
29896
29969
  "--stack-region <region>",
29897
29970
  "Region of the stack record to operate on. Required when the same stack name has state in multiple regions."
29898
29971
  );
@@ -29900,14 +29973,275 @@ function stackRegionOption() {
29900
29973
  function createStateRmCommand() {
29901
29974
  const cmd = new Command9("rm").description("Remove cdkd state for one or more stacks (does NOT delete AWS resources)").argument("<stacks...>", "Stack name(s) to remove from state").option("-f, --force", "Skip confirmation and remove even if the stack is locked", false).addOption(stackRegionOption()).action(withErrorHandling(stateRmCommand));
29902
29975
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
29976
+ cmd.addOption(deprecatedRegionOption);
29977
+ return cmd;
29978
+ }
29979
+ async function stateDestroyCommand(stackArgs, options) {
29980
+ const logger = getLogger();
29981
+ if (options.verbose) {
29982
+ logger.setLevel("debug");
29983
+ process.env["CDKD_NO_LIVE"] = "1";
29984
+ }
29985
+ if (!options.all && stackArgs.length === 0) {
29986
+ throw new Error(
29987
+ "Stack name is required. Usage: cdkd state destroy <stack> [<stack>...] | --all"
29988
+ );
29989
+ }
29990
+ const setup = await setupStateBackend(options);
29991
+ const providerRegistry = new ProviderRegistry();
29992
+ registerAllProviders(providerRegistry);
29993
+ providerRegistry.setCustomResourceResponseBucket(setup.bucket);
29994
+ try {
29995
+ const stateRefs = await setup.stateBackend.listStacks();
29996
+ const knownStackNames = new Set(stateRefs.map((r) => r.stackName));
29997
+ let stackNames;
29998
+ if (options.all) {
29999
+ stackNames = [...knownStackNames].sort();
30000
+ if (stackNames.length === 0) {
30001
+ logger.info("No stacks found in state");
30002
+ return;
30003
+ }
30004
+ } else {
30005
+ const missing = stackArgs.filter((name) => !knownStackNames.has(name));
30006
+ if (missing.length > 0) {
30007
+ throw new Error(
30008
+ `No state found for stack(s): ${missing.join(", ")}. Run 'cdkd state list' to see available stacks.`
30009
+ );
30010
+ }
30011
+ stackNames = stackArgs;
30012
+ }
30013
+ if (options.all && !options.yes) {
30014
+ process.stdout.write(
30015
+ `
30016
+ WARNING: This destroys ${stackNames.length} stack(s) and removes their state records:
30017
+ `
30018
+ );
30019
+ for (const name of stackNames) {
30020
+ process.stdout.write(` - ${name}
30021
+ `);
30022
+ }
30023
+ process.stdout.write("\n");
30024
+ const rl = readline2.createInterface({
30025
+ input: process.stdin,
30026
+ output: process.stdout
30027
+ });
30028
+ const answer = await rl.question(`Destroy all ${stackNames.length} stack(s)? (y/N): `);
30029
+ rl.close();
30030
+ const trimmed = answer.trim().toLowerCase();
30031
+ if (trimmed !== "y" && trimmed !== "yes") {
30032
+ logger.info("Destroy cancelled");
30033
+ return;
30034
+ }
30035
+ }
30036
+ logger.info(`Found ${stackNames.length} stack(s) to destroy: ${stackNames.join(", ")}`);
30037
+ let totalErrors = 0;
30038
+ for (const stackName of stackNames) {
30039
+ const refs = stateRefs.filter((r) => r.stackName === stackName);
30040
+ let targets;
30041
+ if (options.stackRegion) {
30042
+ targets = refs.filter((r) => r.region === options.stackRegion || !r.region);
30043
+ if (targets.length === 0) {
30044
+ logger.warn(
30045
+ `Skipping ${stackName}: no state record matches --stack-region '${options.stackRegion}'`
30046
+ );
30047
+ continue;
30048
+ }
30049
+ } else if (refs.length === 1) {
30050
+ targets = refs;
30051
+ } else {
30052
+ const regions = refs.map((r) => r.region ?? "(legacy)").join(", ");
30053
+ throw new Error(
30054
+ `Stack '${stackName}' has state in multiple regions: ${regions}. Use --region <region> to pick one.`
30055
+ );
30056
+ }
30057
+ for (const ref of targets) {
30058
+ logger.info(
30059
+ `
30060
+ Preparing to destroy stack: ${stackName}${ref.region ? ` (${ref.region})` : ""}`
30061
+ );
30062
+ const stateResult = await setup.stateBackend.getState(
30063
+ stackName,
30064
+ ref.region ?? setup.region
30065
+ );
30066
+ if (!stateResult) {
30067
+ logger.warn(
30068
+ `No state found for stack ${stackName}${ref.region ? ` in ${ref.region}` : ""}, skipping`
30069
+ );
30070
+ continue;
30071
+ }
30072
+ const result = await runDestroyForStack(stackName, stateResult.state, {
30073
+ stateBackend: setup.stateBackend,
30074
+ lockManager: setup.lockManager,
30075
+ providerRegistry,
30076
+ baseAwsClients: setup.awsClients,
30077
+ baseRegion: setup.region,
30078
+ ...options.profile && { profile: options.profile },
30079
+ stateBucket: setup.bucket,
30080
+ // --yes covers both the --all batch prompt above (already consumed)
30081
+ // and the per-stack prompt inside the runner. Per-stack prompts are
30082
+ // skipped when `options.yes` is set OR `--all` was set (the user
30083
+ // already accepted the batch prompt).
30084
+ skipConfirmation: options.yes || options.all === true
30085
+ });
30086
+ totalErrors += result.errorCount;
30087
+ }
30088
+ }
30089
+ if (totalErrors > 0) {
30090
+ throw new Error(
30091
+ `Destroy completed with ${totalErrors} resource error(s). Inspect 'cdkd state show <stack>' and re-run.`
30092
+ );
30093
+ }
30094
+ } finally {
30095
+ setup.dispose();
30096
+ }
30097
+ }
30098
+ function createStateDestroyCommand() {
30099
+ const cmd = new Command9("destroy").description(
30100
+ "Destroy a stack's AWS resources and remove its state record without requiring the CDK app. For removing only the state record (keeping AWS resources intact), use 'cdkd state rm'."
30101
+ ).argument("[stacks...]", "Stack name(s) to destroy (physical CloudFormation names)").option("--all", "Destroy every stack in the state bucket", false).addOption(stackRegionOption()).addHelpText(
30102
+ "after",
30103
+ [
30104
+ "",
30105
+ "Examples:",
30106
+ " cdkd state destroy MyStack",
30107
+ " cdkd state destroy MyStack OtherStack",
30108
+ " cdkd state destroy --all -y",
30109
+ " cdkd state destroy MyStack --state-bucket cdkd-state-test",
30110
+ " cdkd state destroy MyStack --stack-region us-west-2",
30111
+ "",
30112
+ "For removing only the state record (keeping AWS resources intact), use 'cdkd state rm'."
30113
+ ].join("\n")
30114
+ ).action(withErrorHandling(stateDestroyCommand));
30115
+ [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
30116
+ cmd.addOption(deprecatedRegionOption);
30117
+ return cmd;
30118
+ }
30119
+ function formatBucketSource(source) {
30120
+ switch (source) {
30121
+ case "cli-flag":
30122
+ return "--state-bucket flag";
30123
+ case "env":
30124
+ return "CDKD_STATE_BUCKET env";
30125
+ case "cdk.json":
30126
+ return "cdk.json (context.cdkd.stateBucket)";
30127
+ case "default":
30128
+ return "default (account ID from STS)";
30129
+ case "default-legacy":
30130
+ return "default (legacy region-suffixed name; cdkd state migrate-bucket recommended)";
30131
+ }
30132
+ }
30133
+ async function detectBucketRegion(awsClients, bucket) {
30134
+ try {
30135
+ const resp = await awsClients.s3.send(new GetBucketLocationCommand2({ Bucket: bucket }));
30136
+ const constraint = resp.LocationConstraint;
30137
+ if (!constraint)
30138
+ return "us-east-1";
30139
+ if (constraint === "EU")
30140
+ return "eu-west-1";
30141
+ return constraint;
30142
+ } catch {
30143
+ return void 0;
30144
+ }
30145
+ }
30146
+ async function listStateFileKeys(awsClients, bucket, prefix) {
30147
+ const keys = [];
30148
+ let continuationToken;
30149
+ const searchPrefix = `${prefix}/`;
30150
+ do {
30151
+ const resp = await awsClients.s3.send(
30152
+ new ListObjectsV2Command3({
30153
+ Bucket: bucket,
30154
+ Prefix: searchPrefix,
30155
+ ...continuationToken && { ContinuationToken: continuationToken }
30156
+ })
30157
+ );
30158
+ for (const obj of resp.Contents ?? []) {
30159
+ const key = obj.Key;
30160
+ if (typeof key === "string" && key.endsWith("/state.json")) {
30161
+ keys.push(key);
30162
+ }
30163
+ }
30164
+ continuationToken = resp.NextContinuationToken;
30165
+ } while (continuationToken);
30166
+ return keys;
30167
+ }
30168
+ async function readSchemaVersion(awsClients, bucket, keys) {
30169
+ if (keys.length === 0)
30170
+ return "unknown";
30171
+ try {
30172
+ const resp = await awsClients.s3.send(new GetObjectCommand4({ Bucket: bucket, Key: keys[0] }));
30173
+ if (!resp.Body)
30174
+ return "unknown";
30175
+ const body = await resp.Body.transformToString();
30176
+ const parsed = JSON.parse(body);
30177
+ return typeof parsed.version === "number" ? parsed.version : "unknown";
30178
+ } catch {
30179
+ return "unknown";
30180
+ }
30181
+ }
30182
+ async function stateInfoCommand(options) {
30183
+ const logger = getLogger();
30184
+ if (options.verbose)
30185
+ logger.setLevel("debug");
30186
+ const awsClients = new AwsClients({
30187
+ ...options.region && { region: options.region },
30188
+ ...options.profile && { profile: options.profile }
30189
+ });
30190
+ setAwsClients(awsClients);
30191
+ try {
30192
+ const region = options.region || process.env["AWS_REGION"] || "us-east-1";
30193
+ const resolved = await resolveStateBucketWithDefaultAndSource(options.stateBucket, region);
30194
+ const bucket = resolved.bucket;
30195
+ const prefix = options.statePrefix;
30196
+ const stateBackend = new S3StateBackend(awsClients.s3, { bucket, prefix });
30197
+ await stateBackend.verifyBucketExists();
30198
+ const detectedRegion = await detectBucketRegion(awsClients, bucket);
30199
+ const stateFileKeys = await listStateFileKeys(awsClients, bucket, prefix);
30200
+ const schemaVersion = await readSchemaVersion(awsClients, bucket, stateFileKeys);
30201
+ if (options.json) {
30202
+ const json = {
30203
+ bucket,
30204
+ region: detectedRegion ?? null,
30205
+ regionSource: detectedRegion ? "auto-detected" : "unknown",
30206
+ bucketSource: resolved.source,
30207
+ schemaVersion,
30208
+ stackCount: stateFileKeys.length
30209
+ };
30210
+ process.stdout.write(`${JSON.stringify(json, null, 2)}
30211
+ `);
30212
+ return;
30213
+ }
30214
+ const lines = [];
30215
+ lines.push(`State bucket: ${bucket}`);
30216
+ if (detectedRegion) {
30217
+ lines.push(`Region: ${detectedRegion} (auto-detected via GetBucketLocation)`);
30218
+ } else {
30219
+ lines.push("Region: unknown (GetBucketLocation failed or denied)");
30220
+ }
30221
+ lines.push(`Source: ${formatBucketSource(resolved.source)}`);
30222
+ lines.push(`Schema version: ${schemaVersion}`);
30223
+ lines.push(`Stacks: ${stateFileKeys.length}`);
30224
+ process.stdout.write(`${lines.join("\n")}
30225
+ `);
30226
+ } finally {
30227
+ awsClients.destroy();
30228
+ }
30229
+ }
30230
+ function createStateInfoCommand() {
30231
+ const cmd = new Command9("info").description(
30232
+ "Show cdkd state bucket info (bucket name, region, source, schema version, stack count)"
30233
+ ).option("--json", "Output as JSON", false).action(withErrorHandling(stateInfoCommand));
30234
+ [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
29903
30235
  return cmd;
29904
30236
  }
29905
30237
  function createStateCommand() {
29906
30238
  const cmd = new Command9("state").description("Manage cdkd state stored in S3");
30239
+ cmd.addCommand(createStateInfoCommand());
29907
30240
  cmd.addCommand(createStateListCommand());
29908
30241
  cmd.addCommand(createStateResourcesCommand());
29909
30242
  cmd.addCommand(createStateShowCommand());
29910
30243
  cmd.addCommand(createStateRmCommand());
30244
+ cmd.addCommand(createStateDestroyCommand());
29911
30245
  return cmd;
29912
30246
  }
29913
30247
 
@@ -29936,7 +30270,7 @@ function reorderArgs(argv) {
29936
30270
  }
29937
30271
  async function main() {
29938
30272
  const program = new Command10();
29939
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.11.0");
30273
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.13.0");
29940
30274
  program.addCommand(createBootstrapCommand());
29941
30275
  program.addCommand(createSynthCommand());
29942
30276
  program.addCommand(createListCommand());