@go-to-k/cdkd 0.81.0 → 0.82.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
@@ -12448,7 +12448,7 @@ var require_graphql = __commonJS({
12448
12448
  var _validate2 = require_validate2();
12449
12449
  var _execute = require_execute();
12450
12450
  function graphql(args) {
12451
- return new Promise((resolve8) => resolve8(graphqlImpl(args)));
12451
+ return new Promise((resolve9) => resolve9(graphqlImpl(args)));
12452
12452
  }
12453
12453
  function graphqlSync(args) {
12454
12454
  const result = graphqlImpl(args);
@@ -17797,7 +17797,7 @@ var require_graphql2 = __commonJS({
17797
17797
  });
17798
17798
 
17799
17799
  // src/cli/index.ts
17800
- import { Command as Command16 } from "commander";
17800
+ import { Command as Command17 } from "commander";
17801
17801
 
17802
17802
  // src/cli/commands/bootstrap.ts
17803
17803
  import { Command, Option as Option2 } from "commander";
@@ -18869,7 +18869,7 @@ var IntrinsicFunctionResolver = class {
18869
18869
  this.logger.debug(
18870
18870
  `VPC ${physicalId} IPv6 CIDR still associating (attempt ${attempt}/${maxAttempts}), waiting...`
18871
18871
  );
18872
- await new Promise((resolve8) => setTimeout(resolve8, 2e3));
18872
+ await new Promise((resolve9) => setTimeout(resolve9, 2e3));
18873
18873
  }
18874
18874
  this.logger.warn(
18875
18875
  `VPC ${physicalId} IPv6 CIDR did not reach 'associated' state after ${maxAttempts} attempts`
@@ -19754,7 +19754,7 @@ var DagExecutor = class {
19754
19754
  async execute(concurrency, fn, cancelled = () => false) {
19755
19755
  let active = 0;
19756
19756
  const errors = [];
19757
- return new Promise((resolve8, reject) => {
19757
+ return new Promise((resolve9, reject) => {
19758
19758
  const dispatch = () => {
19759
19759
  let changed = true;
19760
19760
  while (changed) {
@@ -19816,7 +19816,7 @@ var DagExecutor = class {
19816
19816
  );
19817
19817
  return;
19818
19818
  }
19819
- resolve8();
19819
+ resolve9();
19820
19820
  }
19821
19821
  };
19822
19822
  dispatch();
@@ -20497,7 +20497,7 @@ Error: ${err.message || "Unknown error"}`,
20497
20497
  * Sleep for specified milliseconds
20498
20498
  */
20499
20499
  sleep(ms) {
20500
- return new Promise((resolve8) => setTimeout(resolve8, ms));
20500
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
20501
20501
  }
20502
20502
  /**
20503
20503
  * Check if a resource type is supported by Cloud Control API
@@ -21231,7 +21231,7 @@ var CustomResourceProvider = class _CustomResourceProvider {
21231
21231
  return result;
21232
21232
  }
21233
21233
  sleep(ms) {
21234
- return new Promise((resolve8) => setTimeout(resolve8, ms));
21234
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
21235
21235
  }
21236
21236
  /**
21237
21237
  * Adopt an existing custom resource into cdkd state.
@@ -21661,12 +21661,12 @@ function isRetryableTransientError(error, message) {
21661
21661
  }
21662
21662
 
21663
21663
  // src/deployment/retry.ts
21664
- var defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
21664
+ var defaultSleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
21665
21665
  async function withRetry(operation, logicalId, opts = {}) {
21666
21666
  const maxRetries = opts.maxRetries ?? 8;
21667
21667
  const initialDelayMs = opts.initialDelayMs ?? 1e3;
21668
21668
  const maxDelayMs = opts.maxDelayMs ?? 8e3;
21669
- const sleep = opts.sleep ?? defaultSleep;
21669
+ const sleep2 = opts.sleep ?? defaultSleep;
21670
21670
  let lastError;
21671
21671
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
21672
21672
  try {
@@ -21686,7 +21686,7 @@ async function withRetry(operation, logicalId, opts = {}) {
21686
21686
  if (opts.isInterrupted?.()) {
21687
21687
  throw opts.onInterrupted ? opts.onInterrupted() : new Error("Interrupted");
21688
21688
  }
21689
- await sleep(Math.min(1e3, delay2 - waited));
21689
+ await sleep2(Math.min(1e3, delay2 - waited));
21690
21690
  }
21691
21691
  }
21692
21692
  }
@@ -21716,7 +21716,7 @@ function validateOptions(opts) {
21716
21716
  async function withResourceDeadline(operation, opts) {
21717
21717
  validateOptions(opts);
21718
21718
  const startedAt = Date.now();
21719
- return new Promise((resolve8, reject) => {
21719
+ return new Promise((resolve9, reject) => {
21720
21720
  let settled = false;
21721
21721
  let warnTimer;
21722
21722
  let timeoutTimer;
@@ -21756,7 +21756,7 @@ async function withResourceDeadline(operation, opts) {
21756
21756
  return;
21757
21757
  settled = true;
21758
21758
  cleanup();
21759
- resolve8(value);
21759
+ resolve9(value);
21760
21760
  },
21761
21761
  (err) => {
21762
21762
  if (settled)
@@ -23791,7 +23791,7 @@ var AppExecutor = class {
23791
23791
  * Spawn subprocess and wait for completion
23792
23792
  */
23793
23793
  spawn(commandLine, env) {
23794
- return new Promise((resolve8, reject) => {
23794
+ return new Promise((resolve9, reject) => {
23795
23795
  const proc = spawn(commandLine, {
23796
23796
  stdio: ["ignore", "pipe", "pipe"],
23797
23797
  shell: true,
@@ -23817,7 +23817,7 @@ var AppExecutor = class {
23817
23817
  });
23818
23818
  proc.on("close", (code) => {
23819
23819
  if (code === 0) {
23820
- resolve8();
23820
+ resolve9();
23821
23821
  } else {
23822
23822
  const stderr = stderrChunks.join("\n");
23823
23823
  reject(
@@ -25339,11 +25339,11 @@ var FileAssetPublisher = class {
25339
25339
  */
25340
25340
  async uploadZip(client, dirPath, bucket, key) {
25341
25341
  const archiver = await import("archiver");
25342
- const body = await new Promise((resolve8, reject) => {
25342
+ const body = await new Promise((resolve9, reject) => {
25343
25343
  const chunks = [];
25344
25344
  const archive = archiver.default("zip", { zlib: { level: 9 } });
25345
25345
  archive.on("data", (chunk) => chunks.push(chunk));
25346
- archive.on("end", () => resolve8(Buffer.concat(chunks)));
25346
+ archive.on("end", () => resolve9(Buffer.concat(chunks)));
25347
25347
  archive.on("error", reject);
25348
25348
  const stat4 = statSync2(dirPath);
25349
25349
  if (stat4.isDirectory()) {
@@ -25528,7 +25528,7 @@ var DockerAssetPublisher = class {
25528
25528
  const token = Buffer.from(authData.authorizationToken, "base64").toString();
25529
25529
  const [username, password] = token.split(":");
25530
25530
  const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.amazonaws.com`;
25531
- await new Promise((resolve8, reject) => {
25531
+ await new Promise((resolve9, reject) => {
25532
25532
  const proc = spawn2(
25533
25533
  "docker",
25534
25534
  ["login", "--username", username, "--password-stdin", endpoint],
@@ -25542,7 +25542,7 @@ var DockerAssetPublisher = class {
25542
25542
  });
25543
25543
  proc.on("close", (code) => {
25544
25544
  if (code === 0) {
25545
- resolve8();
25545
+ resolve9();
25546
25546
  } else {
25547
25547
  reject(new AssetError(`ECR login failed: ${stderr.trim()}`));
25548
25548
  }
@@ -25595,7 +25595,7 @@ var WorkGraph = class {
25595
25595
  async execute(concurrency, fn) {
25596
25596
  const active = { "asset-build": 0, "asset-publish": 0, stack: 0 };
25597
25597
  const errors = [];
25598
- return new Promise((resolve8, reject) => {
25598
+ return new Promise((resolve9, reject) => {
25599
25599
  const dispatch = () => {
25600
25600
  const ready = [];
25601
25601
  for (const node of this.nodes.values()) {
@@ -25667,7 +25667,7 @@ ${msg}`
25667
25667
  );
25668
25668
  return;
25669
25669
  }
25670
- resolve8();
25670
+ resolve9();
25671
25671
  }
25672
25672
  };
25673
25673
  dispatch();
@@ -26658,7 +26658,7 @@ var LockManager = class {
26658
26658
  this.logger.info(
26659
26659
  `Stack '${stackName}' (${region}) is locked by ${lockInfo2.owner}${lockInfo2.operation ? ` (operation: ${lockInfo2.operation})` : ""}. Lock expires in ${this.formatDuration(remainingMs)}. Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`
26660
26660
  );
26661
- await new Promise((resolve8) => setTimeout(resolve8, retryDelay));
26661
+ await new Promise((resolve9) => setTimeout(resolve9, retryDelay));
26662
26662
  continue;
26663
26663
  }
26664
26664
  }
@@ -35152,7 +35152,7 @@ var LambdaFunctionProvider = class {
35152
35152
  return enis;
35153
35153
  }
35154
35154
  sleep(ms) {
35155
- return new Promise((resolve8) => setTimeout(resolve8, ms));
35155
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
35156
35156
  }
35157
35157
  /**
35158
35158
  * Build Lambda Code parameter from CDK properties
@@ -37029,7 +37029,7 @@ var DynamoDBTableProvider = class {
37029
37029
  if (status !== "CREATING") {
37030
37030
  throw new Error(`Unexpected table status: ${status}`);
37031
37031
  }
37032
- await new Promise((resolve8) => setTimeout(resolve8, 1e3));
37032
+ await new Promise((resolve9) => setTimeout(resolve9, 1e3));
37033
37033
  }
37034
37034
  throw new Error(`Table ${tableName} did not reach ACTIVE status within ${maxAttempts} seconds`);
37035
37035
  }
@@ -37049,7 +37049,7 @@ var DynamoDBTableProvider = class {
37049
37049
  if (status === "ACTIVE") {
37050
37050
  return;
37051
37051
  }
37052
- await new Promise((resolve8) => setTimeout(resolve8, 1e3));
37052
+ await new Promise((resolve9) => setTimeout(resolve9, 1e3));
37053
37053
  }
37054
37054
  throw new Error(
37055
37055
  `Table ${tableName} did not reach ACTIVE status within ${maxAttempts} seconds after UpdateTable`
@@ -40229,7 +40229,7 @@ var EC2Provider = class {
40229
40229
  this.logger.debug(
40230
40230
  `VPC ${physicalId} has dependencies (attempt ${attempt}/${maxAttempts}), retrying in ${attempt * 5}s...`
40231
40231
  );
40232
- await new Promise((resolve8) => setTimeout(resolve8, attempt * 5e3));
40232
+ await new Promise((resolve9) => setTimeout(resolve9, attempt * 5e3));
40233
40233
  continue;
40234
40234
  }
40235
40235
  const cause = error instanceof Error ? error : void 0;
@@ -40393,7 +40393,7 @@ var EC2Provider = class {
40393
40393
  this.logger.debug(
40394
40394
  `Subnet ${physicalId} has dependencies (attempt ${attempt}/${maxAttempts}), retrying in ${attempt * 5}s...`
40395
40395
  );
40396
- await new Promise((resolve8) => setTimeout(resolve8, attempt * 5e3));
40396
+ await new Promise((resolve9) => setTimeout(resolve9, attempt * 5e3));
40397
40397
  continue;
40398
40398
  }
40399
40399
  const cause = error instanceof Error ? error : void 0;
@@ -41151,7 +41151,7 @@ var EC2Provider = class {
41151
41151
  this.logger.debug(
41152
41152
  `SecurityGroup ${physicalId} has dependent objects (attempt ${attempt}/${maxAttempts}), retrying in ${attempt * 5}s...`
41153
41153
  );
41154
- await new Promise((resolve8) => setTimeout(resolve8, attempt * 5e3));
41154
+ await new Promise((resolve9) => setTimeout(resolve9, attempt * 5e3));
41155
41155
  continue;
41156
41156
  }
41157
41157
  const cause = error instanceof Error ? error : void 0;
@@ -42095,7 +42095,7 @@ var EC2Provider = class {
42095
42095
  * `setTimeout` globally.
42096
42096
  */
42097
42097
  sleep(ms) {
42098
- return new Promise((resolve8) => setTimeout(resolve8, ms));
42098
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
42099
42099
  }
42100
42100
  /**
42101
42101
  * Check if an error indicates the resource was not found
@@ -44195,7 +44195,7 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
44195
44195
  * Sleep for specified milliseconds
44196
44196
  */
44197
44197
  sleep(ms) {
44198
- return new Promise((resolve8) => setTimeout(resolve8, ms));
44198
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
44199
44199
  }
44200
44200
  /**
44201
44201
  * Convert CloudFormation Tags (Array<{Key, Value}>) to SDK tags (Record<string, string>).
@@ -46087,7 +46087,7 @@ var CloudFrontDistributionProvider = class {
46087
46087
  );
46088
46088
  const sleepEnd = Date.now() + delay2;
46089
46089
  while (Date.now() < sleepEnd && !interrupted) {
46090
- await new Promise((resolve8) => setTimeout(resolve8, 1e3));
46090
+ await new Promise((resolve9) => setTimeout(resolve9, 1e3));
46091
46091
  }
46092
46092
  delay2 = Math.min(delay2 * 1.5, maxDelay);
46093
46093
  }
@@ -49899,7 +49899,7 @@ var RDSProvider = class {
49899
49899
  throw new Error(`Timed out waiting for DBInstance ${dbInstanceIdentifier} to be deleted`);
49900
49900
  }
49901
49901
  sleep(ms) {
49902
- return new Promise((resolve8) => setTimeout(resolve8, ms));
49902
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
49903
49903
  }
49904
49904
  /**
49905
49905
  * Adopt an existing RDS resource into cdkd state.
@@ -50884,7 +50884,7 @@ var DocDBProvider = class {
50884
50884
  throw new Error(`Timed out waiting for DocDB DBInstance ${dbInstanceIdentifier} to be deleted`);
50885
50885
  }
50886
50886
  sleep(ms) {
50887
- return new Promise((resolve8) => setTimeout(resolve8, ms));
50887
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
50888
50888
  }
50889
50889
  /**
50890
50890
  * Adopt an existing DocDB resource into cdkd state.
@@ -51874,7 +51874,7 @@ var NeptuneProvider = class {
51874
51874
  );
51875
51875
  }
51876
51876
  sleep(ms) {
51877
- return new Promise((resolve8) => setTimeout(resolve8, ms));
51877
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
51878
51878
  }
51879
51879
  /**
51880
51880
  * Adopt an existing Neptune resource into cdkd state.
@@ -54414,7 +54414,7 @@ var ElastiCacheProvider = class {
54414
54414
  throw new Error(`Timed out waiting for CacheCluster ${cacheClusterId} to be deleted`);
54415
54415
  }
54416
54416
  sleep(ms) {
54417
- return new Promise((resolve8) => setTimeout(resolve8, ms));
54417
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
54418
54418
  }
54419
54419
  /**
54420
54420
  * Read the AWS-current ElastiCache resource configuration in CFn-property shape.
@@ -55129,7 +55129,7 @@ var ServiceDiscoveryProvider = class {
55129
55129
  logicalId
55130
55130
  );
55131
55131
  }
55132
- await new Promise((resolve8) => setTimeout(resolve8, delay2));
55132
+ await new Promise((resolve9) => setTimeout(resolve9, delay2));
55133
55133
  delay2 = Math.min(delay2 * 2, 1e4);
55134
55134
  }
55135
55135
  throw new ProvisioningError(
@@ -59654,7 +59654,7 @@ var KinesisStreamProvider = class {
59654
59654
  if (status !== "CREATING" && status !== "UPDATING") {
59655
59655
  throw new Error(`Unexpected stream status: ${status}`);
59656
59656
  }
59657
- await new Promise((resolve8) => setTimeout(resolve8, 2e3));
59657
+ await new Promise((resolve9) => setTimeout(resolve9, 2e3));
59658
59658
  }
59659
59659
  throw new Error(
59660
59660
  `Stream ${streamName} did not reach ACTIVE status within ${maxAttempts * 2} seconds`
@@ -59987,7 +59987,7 @@ var KinesisStreamConsumerProvider = class {
59987
59987
  if (status !== "CREATING") {
59988
59988
  throw new Error(`Unexpected consumer status: ${status}`);
59989
59989
  }
59990
- await new Promise((resolve8) => setTimeout(resolve8, 1e3));
59990
+ await new Promise((resolve9) => setTimeout(resolve9, 1e3));
59991
59991
  }
59992
59992
  throw new Error(
59993
59993
  `Consumer ${consumerArn} did not reach ACTIVE status within ${maxAttempts} seconds`
@@ -60292,7 +60292,7 @@ var EFSProvider = class {
60292
60292
  this.logger.debug(
60293
60293
  `FileSystem ${fileSystemId} state: ${fs?.LifeCycleState ?? "unknown"}, waiting...`
60294
60294
  );
60295
- await new Promise((resolve8) => setTimeout(resolve8, pollIntervalMs));
60295
+ await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
60296
60296
  }
60297
60297
  throw new ProvisioningError(
60298
60298
  `Timed out waiting for EFS FileSystem ${fileSystemId} to become available (60s)`,
@@ -60370,7 +60370,7 @@ var EFSProvider = class {
60370
60370
  this.logger.debug(
60371
60371
  `MountTarget ${mountTargetId} state: ${mountTarget?.LifeCycleState ?? "unknown"}, waiting...`
60372
60372
  );
60373
- await new Promise((resolve8) => setTimeout(resolve8, pollIntervalMs));
60373
+ await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
60374
60374
  }
60375
60375
  throw new ProvisioningError(
60376
60376
  `Timed out waiting for EFS MountTarget ${mountTargetId} to become available (120s)`,
@@ -60436,7 +60436,7 @@ var EFSProvider = class {
60436
60436
  }
60437
60437
  throw error;
60438
60438
  }
60439
- await new Promise((resolve8) => setTimeout(resolve8, pollIntervalMs));
60439
+ await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
60440
60440
  }
60441
60441
  this.logger.warn(
60442
60442
  `Timed out waiting for EFS MountTarget ${mountTargetId} deletion for ${logicalId} (120s)`
@@ -61394,7 +61394,7 @@ var FirehoseProvider = class {
61394
61394
  this.logger.debug(
61395
61395
  `Firehose ${logicalId} status: ${status} (attempt ${attempt}/${maxAttempts})`
61396
61396
  );
61397
- await new Promise((resolve8) => setTimeout(resolve8, 2e3));
61397
+ await new Promise((resolve9) => setTimeout(resolve9, 2e3));
61398
61398
  }
61399
61399
  this.logger.warn(`Firehose ${logicalId} did not reach ACTIVE after ${maxAttempts} attempts`);
61400
61400
  }
@@ -64864,7 +64864,7 @@ var ASGProvider = class {
64864
64864
  );
64865
64865
  }
64866
64866
  sleep(ms) {
64867
- return new Promise((resolve8) => setTimeout(resolve8, ms));
64867
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
64868
64868
  }
64869
64869
  // ─── Sub-shape diff helpers ───────────────────────────────────────
64870
64870
  // Each helper is a no-op when before/after JSON is identical (the cheap
@@ -66804,7 +66804,7 @@ Acquiring lock for stack ${stackName}...`);
66804
66804
  logger.debug(
66805
66805
  ` \u23F3 Retrying delete ${logicalId} in ${delay2 / 1e3}s (attempt ${attempt + 1}/${maxAttempts})`
66806
66806
  );
66807
- await new Promise((resolve8) => setTimeout(resolve8, delay2));
66807
+ await new Promise((resolve9) => setTimeout(resolve9, delay2));
66808
66808
  }
66809
66809
  }
66810
66810
  if (lastDeleteError)
@@ -70034,11 +70034,11 @@ async function captureObservedForImportedResources(stackState, providerRegistry,
70034
70034
  }
70035
70035
 
70036
70036
  // src/cli/commands/local-invoke.ts
70037
- import { cpSync as cpSync2, mkdtempSync as mkdtempSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
70037
+ import { cpSync as cpSync2, mkdtempSync as mkdtempSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync8, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
70038
70038
  import { tmpdir as tmpdir3 } from "node:os";
70039
- import { dirname as dirname5 } from "node:path";
70039
+ import { dirname as dirname7 } from "node:path";
70040
70040
  import * as path2 from "node:path";
70041
- import { Command as Command15, Option as Option8 } from "commander";
70041
+ import { Command as Command16, Option as Option9 } from "commander";
70042
70042
 
70043
70043
  // src/local/lambda-resolver.ts
70044
70044
  import { existsSync as existsSync4, statSync as statSync3 } from "node:fs";
@@ -70689,6 +70689,9 @@ async function runDetached(opts) {
70689
70689
  if (opts.name) {
70690
70690
  args.push("--name", opts.name);
70691
70691
  }
70692
+ if (opts.network) {
70693
+ args.push("--network", opts.network);
70694
+ }
70692
70695
  if (opts.platform) {
70693
70696
  args.push("--platform", opts.platform);
70694
70697
  }
@@ -70909,7 +70912,7 @@ async function ecrLogin(client, accountId, region) {
70909
70912
  const token = Buffer.from(authData.authorizationToken, "base64").toString();
70910
70913
  const [username, password] = token.split(":");
70911
70914
  const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.amazonaws.com`;
70912
- await new Promise((resolve8, reject) => {
70915
+ await new Promise((resolve9, reject) => {
70913
70916
  const proc = spawn4("docker", ["login", "--username", username, "--password-stdin", endpoint], {
70914
70917
  stdio: ["pipe", "pipe", "pipe"]
70915
70918
  });
@@ -70919,7 +70922,7 @@ async function ecrLogin(client, accountId, region) {
70919
70922
  });
70920
70923
  proc.on("close", (code) => {
70921
70924
  if (code === 0)
70922
- resolve8();
70925
+ resolve9();
70923
70926
  else
70924
70927
  reject(new LocalInvokeBuildError(`ECR login failed: ${stderr.trim()}`));
70925
70928
  });
@@ -70948,12 +70951,12 @@ async function isImageInLocalCache(imageRef) {
70948
70951
  }
70949
70952
  }
70950
70953
  function runForeground2(cmd, args) {
70951
- return new Promise((resolve8, reject) => {
70954
+ return new Promise((resolve9, reject) => {
70952
70955
  const proc = spawn4(cmd, args, { stdio: "inherit" });
70953
70956
  proc.on("error", (err) => reject(new LocalInvokeBuildError(`${cmd} failed: ${err.message}`)));
70954
70957
  proc.on("close", (code) => {
70955
70958
  if (code === 0)
70956
- resolve8();
70959
+ resolve9();
70957
70960
  else
70958
70961
  reject(new LocalInvokeBuildError(`${cmd} exited with code ${code}`));
70959
70962
  });
@@ -71742,9 +71745,9 @@ function createContainerPool(specs, options) {
71742
71745
  if (disposed) {
71743
71746
  entry.warm.push(handle);
71744
71747
  if (entry.inUse.size === 0) {
71745
- for (const resolve8 of entry.drainResolvers.splice(0, entry.drainResolvers.length)) {
71748
+ for (const resolve9 of entry.drainResolvers.splice(0, entry.drainResolvers.length)) {
71746
71749
  try {
71747
- resolve8();
71750
+ resolve9();
71748
71751
  } catch {
71749
71752
  }
71750
71753
  }
@@ -71760,9 +71763,9 @@ function createContainerPool(specs, options) {
71760
71763
  entry.warm.push(handle);
71761
71764
  resetIdleTimer(entry);
71762
71765
  if (entry.inUse.size === 0 && entry.drainResolvers.length > 0) {
71763
- for (const resolve8 of entry.drainResolvers.splice(0, entry.drainResolvers.length)) {
71766
+ for (const resolve9 of entry.drainResolvers.splice(0, entry.drainResolvers.length)) {
71764
71767
  try {
71765
- resolve8();
71768
+ resolve9();
71766
71769
  } catch {
71767
71770
  }
71768
71771
  }
@@ -74728,9 +74731,9 @@ var NodeFsHandler = class {
74728
74731
  if (this.fsw.closed) {
74729
74732
  return;
74730
74733
  }
74731
- const dirname7 = sp.dirname(file);
74734
+ const dirname9 = sp.dirname(file);
74732
74735
  const basename4 = sp.basename(file);
74733
- const parent = this.fsw._getWatchedDir(dirname7);
74736
+ const parent = this.fsw._getWatchedDir(dirname9);
74734
74737
  let prevStats = stats;
74735
74738
  if (parent.has(basename4))
74736
74739
  return;
@@ -74757,7 +74760,7 @@ var NodeFsHandler = class {
74757
74760
  prevStats = newStats2;
74758
74761
  }
74759
74762
  } catch (error) {
74760
- this.fsw._remove(dirname7, basename4);
74763
+ this.fsw._remove(dirname9, basename4);
74761
74764
  }
74762
74765
  } else if (parent.has(basename4)) {
74763
74766
  const at = newStats.atimeMs;
@@ -74854,7 +74857,7 @@ var NodeFsHandler = class {
74854
74857
  this._addToNodeFs(path3, initialAdd, wh, depth + 1);
74855
74858
  }
74856
74859
  }).on(EV.ERROR, this._boundHandleError);
74857
- return new Promise((resolve8, reject) => {
74860
+ return new Promise((resolve9, reject) => {
74858
74861
  if (!stream)
74859
74862
  return reject();
74860
74863
  stream.once(STR_END, () => {
@@ -74863,7 +74866,7 @@ var NodeFsHandler = class {
74863
74866
  return;
74864
74867
  }
74865
74868
  const wasThrottled = throttler ? throttler.clear() : false;
74866
- resolve8(void 0);
74869
+ resolve9(void 0);
74867
74870
  previous.getChildren().filter((item) => {
74868
74871
  return item !== directory && !current.has(item);
74869
74872
  }).forEach((item) => {
@@ -76604,6 +76607,1477 @@ function createLocalStartApiCommand() {
76604
76607
  return startApi;
76605
76608
  }
76606
76609
 
76610
+ // src/cli/commands/local-run-task.ts
76611
+ import { readFileSync as readFileSync7 } from "node:fs";
76612
+ import { Command as Command15, Option as Option8 } from "commander";
76613
+
76614
+ // src/local/ecs-task-resolver.ts
76615
+ import { dirname as dirname5, isAbsolute as isAbsolute4, resolve as resolve8 } from "node:path";
76616
+ import { existsSync as existsSync5, statSync as statSync4 } from "node:fs";
76617
+ var EcsTaskResolutionError = class _EcsTaskResolutionError extends Error {
76618
+ constructor(message) {
76619
+ super(message);
76620
+ this.name = "EcsTaskResolutionError";
76621
+ Object.setPrototypeOf(this, _EcsTaskResolutionError.prototype);
76622
+ }
76623
+ };
76624
+ function parseEcsTarget(target) {
76625
+ if (typeof target !== "string" || target.length === 0) {
76626
+ throw new EcsTaskResolutionError(
76627
+ "Empty target. Pass a CDK display path (e.g. 'MyStack/MyService/TaskDef') or stack-qualified logical ID (e.g. 'MyStack:MyServiceTaskDefXYZ1234')."
76628
+ );
76629
+ }
76630
+ const colonIdx = target.indexOf(":");
76631
+ const slashIdx = target.indexOf("/");
76632
+ if (colonIdx > 0 && (slashIdx === -1 || colonIdx < slashIdx)) {
76633
+ const stackPattern = target.substring(0, colonIdx);
76634
+ const pathOrId = target.substring(colonIdx + 1);
76635
+ if (pathOrId.length === 0) {
76636
+ throw new EcsTaskResolutionError(`Target '${target}' has no logical ID after ':'.`);
76637
+ }
76638
+ return { stackPattern, pathOrId, isPath: pathOrId.includes("/") };
76639
+ }
76640
+ if (slashIdx > 0) {
76641
+ return { stackPattern: target.substring(0, slashIdx), pathOrId: target, isPath: true };
76642
+ }
76643
+ return { stackPattern: null, pathOrId: target, isPath: false };
76644
+ }
76645
+ function resolveEcsTaskTarget(target, stacks) {
76646
+ if (stacks.length === 0) {
76647
+ throw new EcsTaskResolutionError("No stacks found in the synthesized assembly.");
76648
+ }
76649
+ const parsed = parseEcsTarget(target);
76650
+ const stack = pickStack2(parsed, stacks);
76651
+ const resources = stack.template.Resources ?? {};
76652
+ let logicalId;
76653
+ let resource;
76654
+ if (parsed.isPath) {
76655
+ const index = buildCdkPathIndex(stack.template);
76656
+ const resolved = resolveCdkPathToLogicalIds(parsed.pathOrId, index);
76657
+ const taskDefs = resolved.filter(
76658
+ ({ logicalId: l }) => resources[l]?.Type === "AWS::ECS::TaskDefinition"
76659
+ );
76660
+ if (taskDefs.length === 0) {
76661
+ throw notFoundError2(target, stack, resources);
76662
+ }
76663
+ if (taskDefs.length > 1) {
76664
+ throw new EcsTaskResolutionError(
76665
+ `Target '${target}' matches ${taskDefs.length} task definitions in ${stack.stackName}: ` + taskDefs.map((t) => t.logicalId).join(", ") + ". Refine the path or use the stack:LogicalId form."
76666
+ );
76667
+ }
76668
+ logicalId = taskDefs[0].logicalId;
76669
+ resource = resources[logicalId];
76670
+ } else {
76671
+ resource = resources[parsed.pathOrId];
76672
+ if (!resource)
76673
+ throw notFoundError2(target, stack, resources);
76674
+ logicalId = parsed.pathOrId;
76675
+ }
76676
+ if (!logicalId || !resource)
76677
+ throw notFoundError2(target, stack, resources);
76678
+ if (resource.Type === "AWS::Lambda::Function") {
76679
+ throw new EcsTaskResolutionError(
76680
+ `Resource '${logicalId}' in ${stack.stackName} is a Lambda function, not an ECS task definition. Use \`cdkd local invoke\` for Lambda; \`cdkd local run-task\` is ECS only.`
76681
+ );
76682
+ }
76683
+ if (resource.Type !== "AWS::ECS::TaskDefinition") {
76684
+ throw new EcsTaskResolutionError(
76685
+ `Resource '${logicalId}' in ${stack.stackName} is ${resource.Type}, not an AWS::ECS::TaskDefinition.`
76686
+ );
76687
+ }
76688
+ return extractTaskDefinitionProperties(stack, logicalId, resource);
76689
+ }
76690
+ function pickStack2(parsed, stacks) {
76691
+ if (parsed.stackPattern === null) {
76692
+ if (stacks.length === 1)
76693
+ return stacks[0];
76694
+ throw new EcsTaskResolutionError(
76695
+ `Multiple stacks in app, target '${parsed.pathOrId}' is missing a stack prefix. Use 'StackName:${parsed.pathOrId}' or 'StackName/...' (path form). Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`
76696
+ );
76697
+ }
76698
+ const matched = matchStacks(stacks, [parsed.stackPattern]);
76699
+ if (matched.length === 0) {
76700
+ throw new EcsTaskResolutionError(
76701
+ `Stack '${parsed.stackPattern}' not found. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`
76702
+ );
76703
+ }
76704
+ if (matched.length > 1) {
76705
+ throw new EcsTaskResolutionError(
76706
+ `Stack pattern '${parsed.stackPattern}' matched ${matched.length} stacks: ` + matched.map((s) => s.stackName).join(", ") + ". Use a more specific pattern."
76707
+ );
76708
+ }
76709
+ return matched[0];
76710
+ }
76711
+ function extractTaskDefinitionProperties(stack, logicalId, resource) {
76712
+ const props = resource.Properties ?? {};
76713
+ const warnings = [];
76714
+ const family = pickString(props["Family"]) ?? logicalId;
76715
+ const rawNetworkMode = pickString(props["NetworkMode"]) ?? "bridge";
76716
+ let networkMode;
76717
+ if (rawNetworkMode === "bridge" || rawNetworkMode === "awsvpc" || rawNetworkMode === "host" || rawNetworkMode === "none") {
76718
+ networkMode = rawNetworkMode;
76719
+ } else {
76720
+ throw new EcsTaskResolutionError(
76721
+ `Task definition '${logicalId}' has unsupported NetworkMode '${rawNetworkMode}'. Supported values: bridge / awsvpc / host / none.`
76722
+ );
76723
+ }
76724
+ if (networkMode === "awsvpc") {
76725
+ warnings.push(
76726
+ `NetworkMode 'awsvpc' on '${logicalId}' is mapped to docker bridge locally \u2014 docker cannot emulate ENI-per-task. AWS SDK calls still reach public endpoints via the developer network.`
76727
+ );
76728
+ }
76729
+ const resources = stack.template.Resources ?? {};
76730
+ const taskRoleArn = resolveRoleArn(props["TaskRoleArn"], resources);
76731
+ const executionRoleArn = resolveRoleArn(props["ExecutionRoleArn"], resources);
76732
+ const runtimePlatform = parseRuntimePlatform(props["RuntimePlatform"]);
76733
+ const rawContainers = props["ContainerDefinitions"];
76734
+ if (!Array.isArray(rawContainers) || rawContainers.length === 0) {
76735
+ throw new EcsTaskResolutionError(`Task definition '${logicalId}' has no ContainerDefinitions.`);
76736
+ }
76737
+ const containers = rawContainers.map(
76738
+ (c, idx) => parseContainerDefinition(c, idx, logicalId, resources, stack)
76739
+ );
76740
+ const rawVolumes = props["Volumes"];
76741
+ const volumes = Array.isArray(rawVolumes) ? rawVolumes.map((v, idx) => parseVolume(v, idx, logicalId)) : [];
76742
+ const containerNames = new Set(containers.map((c) => c.name));
76743
+ for (const c of containers) {
76744
+ for (const d of c.dependsOn) {
76745
+ if (!containerNames.has(d.containerName)) {
76746
+ throw new EcsTaskResolutionError(
76747
+ `Container '${c.name}' depends on '${d.containerName}', which is not defined in task '${logicalId}'.`
76748
+ );
76749
+ }
76750
+ }
76751
+ }
76752
+ const out = {
76753
+ stack,
76754
+ taskDefinitionLogicalId: logicalId,
76755
+ resource,
76756
+ family,
76757
+ networkMode,
76758
+ containers,
76759
+ volumes,
76760
+ warnings
76761
+ };
76762
+ if (taskRoleArn !== void 0)
76763
+ out.taskRoleArn = taskRoleArn;
76764
+ if (executionRoleArn !== void 0)
76765
+ out.executionRoleArn = executionRoleArn;
76766
+ if (runtimePlatform !== void 0)
76767
+ out.runtimePlatform = runtimePlatform;
76768
+ return out;
76769
+ }
76770
+ function parseRuntimePlatform(value) {
76771
+ if (!value || typeof value !== "object")
76772
+ return void 0;
76773
+ const obj = value;
76774
+ const cpu = obj["CpuArchitecture"];
76775
+ const os = obj["OperatingSystemFamily"];
76776
+ if (typeof cpu !== "string" || typeof os !== "string")
76777
+ return void 0;
76778
+ if (cpu !== "X86_64" && cpu !== "ARM64")
76779
+ return void 0;
76780
+ if (os !== "LINUX")
76781
+ return void 0;
76782
+ return { cpuArchitecture: cpu, operatingSystemFamily: os };
76783
+ }
76784
+ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack) {
76785
+ if (!raw || typeof raw !== "object") {
76786
+ throw new EcsTaskResolutionError(
76787
+ `Task '${taskLogicalId}' ContainerDefinitions[${idx}] is not an object.`
76788
+ );
76789
+ }
76790
+ const c = raw;
76791
+ const name = pickString(c["Name"]);
76792
+ if (!name) {
76793
+ throw new EcsTaskResolutionError(
76794
+ `Task '${taskLogicalId}' ContainerDefinitions[${idx}] has no Name.`
76795
+ );
76796
+ }
76797
+ const image = parseContainerImage(c["Image"], name, taskLogicalId, resources, stack);
76798
+ const command = pickStringArray2(c["Command"]);
76799
+ const entryPoint = pickStringArray2(c["EntryPoint"]);
76800
+ const workingDirectory = pickString(c["WorkingDirectory"]);
76801
+ const environment = {};
76802
+ if (Array.isArray(c["Environment"])) {
76803
+ for (const entry of c["Environment"]) {
76804
+ if (!entry || typeof entry !== "object")
76805
+ continue;
76806
+ const e = entry;
76807
+ const key = pickString(e["Name"]);
76808
+ const value = e["Value"];
76809
+ if (!key)
76810
+ continue;
76811
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
76812
+ environment[key] = String(value);
76813
+ }
76814
+ }
76815
+ }
76816
+ const secrets = [];
76817
+ if (Array.isArray(c["Secrets"])) {
76818
+ for (const entry of c["Secrets"]) {
76819
+ if (!entry || typeof entry !== "object")
76820
+ continue;
76821
+ const e = entry;
76822
+ const sName = pickString(e["Name"]);
76823
+ const valueFrom = pickString(e["ValueFrom"]);
76824
+ if (sName && valueFrom)
76825
+ secrets.push({ name: sName, valueFrom });
76826
+ }
76827
+ }
76828
+ const portMappings = [];
76829
+ if (Array.isArray(c["PortMappings"])) {
76830
+ for (const entry of c["PortMappings"]) {
76831
+ if (!entry || typeof entry !== "object")
76832
+ continue;
76833
+ const p = entry;
76834
+ const containerPort = typeof p["ContainerPort"] === "number" ? p["ContainerPort"] : void 0;
76835
+ if (containerPort === void 0)
76836
+ continue;
76837
+ const hostPort = typeof p["HostPort"] === "number" ? p["HostPort"] : void 0;
76838
+ const protocol = pickString(p["Protocol"]) === "udp" ? "udp" : "tcp";
76839
+ const pm = { containerPort, protocol };
76840
+ if (hostPort !== void 0)
76841
+ pm.hostPort = hostPort;
76842
+ portMappings.push(pm);
76843
+ }
76844
+ }
76845
+ const mountPoints = [];
76846
+ if (Array.isArray(c["MountPoints"])) {
76847
+ for (const entry of c["MountPoints"]) {
76848
+ if (!entry || typeof entry !== "object")
76849
+ continue;
76850
+ const m = entry;
76851
+ const sourceVolume = pickString(m["SourceVolume"]);
76852
+ const containerPath = pickString(m["ContainerPath"]);
76853
+ if (!sourceVolume || !containerPath)
76854
+ continue;
76855
+ mountPoints.push({
76856
+ sourceVolume,
76857
+ containerPath,
76858
+ readOnly: m["ReadOnly"] === true
76859
+ });
76860
+ }
76861
+ }
76862
+ const dependsOn = [];
76863
+ if (Array.isArray(c["DependsOn"])) {
76864
+ for (const entry of c["DependsOn"]) {
76865
+ if (!entry || typeof entry !== "object")
76866
+ continue;
76867
+ const d = entry;
76868
+ const containerName = pickString(d["ContainerName"]);
76869
+ const condition = pickString(d["Condition"]);
76870
+ if (!containerName || !condition)
76871
+ continue;
76872
+ if (condition !== "START" && condition !== "COMPLETE" && condition !== "SUCCESS" && condition !== "HEALTHY") {
76873
+ throw new EcsTaskResolutionError(
76874
+ `Container '${name}' has invalid DependsOn condition '${condition}'. Accepted values: START / COMPLETE / SUCCESS / HEALTHY.`
76875
+ );
76876
+ }
76877
+ dependsOn.push({ containerName, condition });
76878
+ }
76879
+ }
76880
+ const links = pickStringArray2(c["Links"]) ?? [];
76881
+ const essential = c["Essential"] === false ? false : true;
76882
+ let healthCheck;
76883
+ if (c["HealthCheck"] && typeof c["HealthCheck"] === "object") {
76884
+ const h = c["HealthCheck"];
76885
+ const command2 = pickStringArray2(h["Command"]);
76886
+ if (command2 && command2.length > 0) {
76887
+ healthCheck = { command: command2 };
76888
+ if (typeof h["Interval"] === "number")
76889
+ healthCheck.interval = h["Interval"];
76890
+ if (typeof h["Timeout"] === "number")
76891
+ healthCheck.timeout = h["Timeout"];
76892
+ if (typeof h["Retries"] === "number")
76893
+ healthCheck.retries = h["Retries"];
76894
+ if (typeof h["StartPeriod"] === "number")
76895
+ healthCheck.startPeriod = h["StartPeriod"];
76896
+ }
76897
+ }
76898
+ const user = pickString(c["User"]);
76899
+ const privileged = c["Privileged"] === true ? true : void 0;
76900
+ const readonlyRootFilesystem = c["ReadonlyRootFilesystem"] === true ? true : void 0;
76901
+ const ulimits = [];
76902
+ if (Array.isArray(c["Ulimits"])) {
76903
+ for (const entry of c["Ulimits"]) {
76904
+ if (!entry || typeof entry !== "object")
76905
+ continue;
76906
+ const u = entry;
76907
+ const uName = pickString(u["Name"]);
76908
+ const soft = typeof u["SoftLimit"] === "number" ? u["SoftLimit"] : void 0;
76909
+ const hard = typeof u["HardLimit"] === "number" ? u["HardLimit"] : void 0;
76910
+ if (!uName || soft === void 0 || hard === void 0)
76911
+ continue;
76912
+ ulimits.push({ name: uName, softLimit: soft, hardLimit: hard });
76913
+ }
76914
+ }
76915
+ const out = {
76916
+ name,
76917
+ image,
76918
+ environment,
76919
+ secrets,
76920
+ portMappings,
76921
+ mountPoints,
76922
+ dependsOn,
76923
+ links,
76924
+ essential,
76925
+ ulimits
76926
+ };
76927
+ if (command !== void 0)
76928
+ out.command = command;
76929
+ if (entryPoint !== void 0)
76930
+ out.entryPoint = entryPoint;
76931
+ if (workingDirectory !== void 0)
76932
+ out.workingDirectory = workingDirectory;
76933
+ if (healthCheck !== void 0)
76934
+ out.healthCheck = healthCheck;
76935
+ if (user !== void 0)
76936
+ out.user = user;
76937
+ if (privileged !== void 0)
76938
+ out.privileged = privileged;
76939
+ if (readonlyRootFilesystem !== void 0)
76940
+ out.readonlyRootFilesystem = readonlyRootFilesystem;
76941
+ return out;
76942
+ }
76943
+ function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack) {
76944
+ const flat = extractImageString(raw);
76945
+ if (!flat) {
76946
+ throw new EcsTaskResolutionError(
76947
+ `Container '${containerName}' in task '${taskLogicalId}' has an unparseable Image property. cdkd local run-task v1 supports flat string images, single-key Fn::Sub bodies, and CDK-asset Image references.`
76948
+ );
76949
+ }
76950
+ if (flat.includes("cdk-hnb659fds-container-assets-")) {
76951
+ const hashMatch = /:([a-f0-9]{8,})$/.exec(flat);
76952
+ const out = { kind: "cdk-asset" };
76953
+ if (hashMatch)
76954
+ out.assetHash = hashMatch[1];
76955
+ return out;
76956
+ }
76957
+ const ecrMatch = /^(\d{12})\.dkr\.ecr\.([^.]+)\.amazonaws\.com(?:\.cn)?\//.exec(flat);
76958
+ if (ecrMatch) {
76959
+ return { kind: "ecr", uri: flat, account: ecrMatch[1], region: ecrMatch[2] };
76960
+ }
76961
+ if (flat.includes("${") && flat.includes("AWS::AccountId")) {
76962
+ throw new EcsTaskResolutionError(
76963
+ `Container '${containerName}' in task '${taskLogicalId}' has an Image that references AWS pseudo parameters (${flat}). cdkd local run-task v1 cannot resolve account-scoped ECR repos at synth time. Build the image locally (CDK ContainerImage.fromAsset) or pin to a public image to test locally.`
76964
+ );
76965
+ }
76966
+ for (const [refLogicalId, res] of Object.entries(resources)) {
76967
+ if (res.Type === "AWS::ECR::Repository" && flat.includes(refLogicalId)) {
76968
+ throw new EcsTaskResolutionError(
76969
+ `Container '${containerName}' in task '${taskLogicalId}' references same-stack ECR repository '${refLogicalId}'. cdkd local run-task v1 cannot resolve the repository URI without state \u2014 build via ContainerImage.fromAsset or pin a public image.`
76970
+ );
76971
+ }
76972
+ }
76973
+ return { kind: "public", uri: flat };
76974
+ }
76975
+ function extractImageString(value) {
76976
+ if (typeof value === "string" && value.length > 0)
76977
+ return value;
76978
+ if (value && typeof value === "object") {
76979
+ const obj = value;
76980
+ const sub = obj["Fn::Sub"];
76981
+ if (typeof sub === "string" && sub.length > 0)
76982
+ return sub;
76983
+ if (Array.isArray(sub) && typeof sub[0] === "string")
76984
+ return sub[0];
76985
+ const join11 = obj["Fn::Join"];
76986
+ if (Array.isArray(join11) && join11.length === 2 && Array.isArray(join11[1])) {
76987
+ const sep = typeof join11[0] === "string" ? join11[0] : "";
76988
+ const parts = join11[1].map((p) => typeof p === "string" ? p : extractImageString(p)).filter((p) => p !== void 0);
76989
+ if (parts.length === join11[1].length)
76990
+ return parts.join(sep);
76991
+ }
76992
+ }
76993
+ return void 0;
76994
+ }
76995
+ function parseVolume(raw, idx, taskLogicalId) {
76996
+ if (!raw || typeof raw !== "object") {
76997
+ throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] is not an object.`);
76998
+ }
76999
+ const v = raw;
77000
+ const name = pickString(v["Name"]);
77001
+ if (!name) {
77002
+ throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] has no Name.`);
77003
+ }
77004
+ if (v["EFSVolumeConfiguration"]) {
77005
+ throw new EcsTaskResolutionError(
77006
+ `Task '${taskLogicalId}' Volumes[${idx}] '${name}' uses EFSVolumeConfiguration, which cdkd local run-task cannot proxy locally. Workaround: bind-mount a local directory at the same containerPath via Host: { SourcePath: '<local-path>' }, or override at runtime via --env-vars semantics for a Phase 2 follow-up.`
77007
+ );
77008
+ }
77009
+ if (v["FSxWindowsFileServerVolumeConfiguration"]) {
77010
+ throw new EcsTaskResolutionError(
77011
+ `Task '${taskLogicalId}' Volumes[${idx}] '${name}' uses FSxWindowsFileServerVolumeConfiguration, which cdkd local run-task cannot proxy locally.`
77012
+ );
77013
+ }
77014
+ const dockerCfg = v["DockerVolumeConfiguration"];
77015
+ if (dockerCfg && typeof dockerCfg === "object") {
77016
+ const d = dockerCfg;
77017
+ const scope = pickString(d["Scope"]) === "shared" ? "shared" : "task";
77018
+ const cfg = { scope };
77019
+ if (typeof d["Autoprovision"] === "boolean")
77020
+ cfg.autoprovision = d["Autoprovision"];
77021
+ const driver = pickString(d["Driver"]);
77022
+ if (driver)
77023
+ cfg.driver = driver;
77024
+ if (d["DriverOpts"] && typeof d["DriverOpts"] === "object") {
77025
+ const opts = {};
77026
+ for (const [k, val] of Object.entries(d["DriverOpts"])) {
77027
+ if (typeof val === "string")
77028
+ opts[k] = val;
77029
+ }
77030
+ cfg.driverOpts = opts;
77031
+ }
77032
+ if (d["Labels"] && typeof d["Labels"] === "object") {
77033
+ const labels = {};
77034
+ for (const [k, val] of Object.entries(d["Labels"])) {
77035
+ if (typeof val === "string")
77036
+ labels[k] = val;
77037
+ }
77038
+ cfg.labels = labels;
77039
+ }
77040
+ return { name, kind: "docker", dockerVolumeConfig: cfg };
77041
+ }
77042
+ const host = v["Host"];
77043
+ if (host && typeof host === "object") {
77044
+ const sourcePath = pickString(host["SourcePath"]);
77045
+ if (sourcePath) {
77046
+ const abs = isAbsolute4(sourcePath) ? sourcePath : resolve8(process.cwd(), sourcePath);
77047
+ return { name, kind: "host", hostPath: abs };
77048
+ }
77049
+ }
77050
+ return { name, kind: "host" };
77051
+ }
77052
+ function resolveRoleArn(value, resources) {
77053
+ if (value === void 0 || value === null)
77054
+ return void 0;
77055
+ if (typeof value === "string")
77056
+ return value;
77057
+ if (typeof value !== "object")
77058
+ return void 0;
77059
+ const obj = value;
77060
+ if ("Ref" in obj && typeof obj["Ref"] === "string") {
77061
+ const refLogicalId = obj["Ref"];
77062
+ const role = resources[refLogicalId];
77063
+ if (role?.Type === "AWS::IAM::Role") {
77064
+ return void 0;
77065
+ }
77066
+ }
77067
+ if ("Fn::GetAtt" in obj) {
77068
+ const arg = obj["Fn::GetAtt"];
77069
+ if (Array.isArray(arg) && typeof arg[0] === "string") {
77070
+ const refLogicalId = arg[0];
77071
+ const role = resources[refLogicalId];
77072
+ if (role?.Type === "AWS::IAM::Role") {
77073
+ return void 0;
77074
+ }
77075
+ }
77076
+ }
77077
+ return void 0;
77078
+ }
77079
+ function pickString(value) {
77080
+ return typeof value === "string" && value.length > 0 ? value : void 0;
77081
+ }
77082
+ function pickStringArray2(value) {
77083
+ if (!Array.isArray(value))
77084
+ return void 0;
77085
+ const out = [];
77086
+ for (const v of value) {
77087
+ if (typeof v === "string")
77088
+ out.push(v);
77089
+ }
77090
+ return out;
77091
+ }
77092
+ function notFoundError2(target, stack, resources) {
77093
+ const tasks = [];
77094
+ for (const [logicalId, resource] of Object.entries(resources)) {
77095
+ if (resource.Type !== "AWS::ECS::TaskDefinition")
77096
+ continue;
77097
+ const meta = resource.Metadata;
77098
+ const cdkPath = typeof meta?.["aws:cdk:path"] === "string" ? meta["aws:cdk:path"] : "";
77099
+ tasks.push({ displayPath: cdkPath || logicalId, logicalId });
77100
+ }
77101
+ let msg = `target '${target}' did not match any ECS task definition in ${stack.stackName}.
77102
+
77103
+ `;
77104
+ if (tasks.length === 0) {
77105
+ msg += `Stack ${stack.stackName} has no AWS::ECS::TaskDefinition resources.`;
77106
+ } else {
77107
+ const width = Math.max(...tasks.map((t) => t.displayPath.length));
77108
+ msg += `Available task definitions in ${stack.stackName}:
77109
+ `;
77110
+ for (const t of tasks) {
77111
+ msg += ` ${t.displayPath.padEnd(width)} (${t.logicalId})
77112
+ `;
77113
+ }
77114
+ }
77115
+ return new EcsTaskResolutionError(msg.trimEnd());
77116
+ }
77117
+ function checkVolumeHostPath(hostPath) {
77118
+ try {
77119
+ return existsSync5(hostPath) && statSync4(hostPath).isDirectory();
77120
+ } catch {
77121
+ return false;
77122
+ }
77123
+ }
77124
+
77125
+ // src/local/ecs-task-runner.ts
77126
+ import { execFile as execFile6, spawn as spawn5 } from "node:child_process";
77127
+ import { randomBytes as randomBytes2 } from "node:crypto";
77128
+ import { dirname as dirname6 } from "node:path";
77129
+ import { promisify as promisify6 } from "node:util";
77130
+ import graphlib2 from "graphlib";
77131
+
77132
+ // src/local/ecs-network.ts
77133
+ import { execFile as execFile5 } from "node:child_process";
77134
+ import { randomBytes } from "node:crypto";
77135
+ import { promisify as promisify5 } from "node:util";
77136
+ var execFileAsync5 = promisify5(execFile5);
77137
+ var METADATA_ENDPOINT_IMAGE = "amazon/amazon-ecs-local-container-endpoints:latest-amd64";
77138
+ var METADATA_ENDPOINT_IP = "169.254.170.2";
77139
+ var METADATA_ENDPOINT_SUBNET = "169.254.170.0/24";
77140
+ async function createTaskNetwork(options = {}) {
77141
+ const logger = getLogger().child("ecs-network");
77142
+ const prefix = options.prefix ?? "cdkd-local";
77143
+ const suffix = randomBytes(4).toString("hex");
77144
+ const networkName = `${prefix}-task-${suffix}`;
77145
+ await pullImage(METADATA_ENDPOINT_IMAGE, options.skipPull ?? false);
77146
+ logger.info(`Creating docker network ${networkName} (subnet ${METADATA_ENDPOINT_SUBNET})...`);
77147
+ try {
77148
+ await execFileAsync5("docker", [
77149
+ "network",
77150
+ "create",
77151
+ "--driver",
77152
+ "bridge",
77153
+ "--subnet",
77154
+ METADATA_ENDPOINT_SUBNET,
77155
+ networkName
77156
+ ]);
77157
+ } catch (err) {
77158
+ const e = err;
77159
+ throw new DockerRunnerError(
77160
+ `docker network create failed: ${e.stderr?.trim() || e.message || String(err)}. Hint: another cdkd run-task on the same host may already own subnet ${METADATA_ENDPOINT_SUBNET}; wait for it to finish, or remove the leftover network with \`docker network ls\` + \`docker network rm\`.`
77161
+ );
77162
+ }
77163
+ const sidecarArgs = [
77164
+ "run",
77165
+ "-d",
77166
+ "--rm",
77167
+ "--name",
77168
+ `${networkName}-metadata`,
77169
+ "--network",
77170
+ networkName,
77171
+ "--ip",
77172
+ METADATA_ENDPOINT_IP
77173
+ ];
77174
+ const sidecarEnv = {};
77175
+ if (options.credentials) {
77176
+ sidecarEnv["AWS_ACCESS_KEY_ID"] = options.credentials.accessKeyId;
77177
+ sidecarEnv["AWS_SECRET_ACCESS_KEY"] = options.credentials.secretAccessKey;
77178
+ if (options.credentials.sessionToken) {
77179
+ sidecarEnv["AWS_SESSION_TOKEN"] = options.credentials.sessionToken;
77180
+ }
77181
+ }
77182
+ if (options.cluster)
77183
+ sidecarEnv["CLUSTER"] = options.cluster;
77184
+ for (const [k, v] of Object.entries(sidecarEnv)) {
77185
+ sidecarArgs.push("-e", `${k}=${v}`);
77186
+ }
77187
+ sidecarArgs.push(METADATA_ENDPOINT_IMAGE);
77188
+ logger.info("Starting ECS local-container-endpoints sidecar at 169.254.170.2...");
77189
+ let sidecarContainerId;
77190
+ try {
77191
+ const { stdout } = await execFileAsync5("docker", sidecarArgs, {
77192
+ maxBuffer: 10 * 1024 * 1024
77193
+ });
77194
+ sidecarContainerId = stdout.trim();
77195
+ } catch (err) {
77196
+ await destroyNetworkOnly(networkName);
77197
+ const e = err;
77198
+ throw new DockerRunnerError(
77199
+ `Failed to start metadata-endpoints sidecar: ${e.stderr?.trim() || e.message || String(err)}`
77200
+ );
77201
+ }
77202
+ return { networkName, sidecarContainerId };
77203
+ }
77204
+ function buildMetadataEnv(opts) {
77205
+ const env = {
77206
+ ECS_CONTAINER_METADATA_URI_V4: `http://${METADATA_ENDPOINT_IP}/v4/${opts.containerName}`,
77207
+ ECS_CONTAINER_METADATA_URI: `http://${METADATA_ENDPOINT_IP}/v3/${opts.containerName}`
77208
+ };
77209
+ if (opts.roleArn) {
77210
+ env["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"] = `/role/${encodeURIComponent(opts.roleArn)}`;
77211
+ }
77212
+ if (opts.region)
77213
+ env["AWS_REGION"] = opts.region;
77214
+ return env;
77215
+ }
77216
+ async function destroyTaskNetwork(net) {
77217
+ if (!net)
77218
+ return;
77219
+ await removeContainer(net.sidecarContainerId);
77220
+ await destroyNetworkOnly(net.networkName);
77221
+ }
77222
+ async function destroyNetworkOnly(networkName) {
77223
+ if (!networkName)
77224
+ return;
77225
+ const logger = getLogger().child("ecs-network");
77226
+ try {
77227
+ await execFileAsync5("docker", ["network", "rm", networkName]);
77228
+ logger.debug(`Removed docker network ${networkName}`);
77229
+ } catch (err) {
77230
+ const e = err;
77231
+ logger.debug(
77232
+ `docker network rm ${networkName} failed: ${e.stderr || e.message || String(err)}`
77233
+ );
77234
+ }
77235
+ }
77236
+
77237
+ // src/local/ecs-secrets-resolver.ts
77238
+ import { SecretsManagerClient as SecretsManagerClient3, GetSecretValueCommand as GetSecretValueCommand2 } from "@aws-sdk/client-secrets-manager";
77239
+ import { SSMClient as SSMClient4, GetParameterCommand as GetParameterCommand4 } from "@aws-sdk/client-ssm";
77240
+ var EcsSecretsResolutionError = class _EcsSecretsResolutionError extends Error {
77241
+ constructor(message) {
77242
+ super(message);
77243
+ this.name = "EcsSecretsResolutionError";
77244
+ Object.setPrototypeOf(this, _EcsSecretsResolutionError.prototype);
77245
+ }
77246
+ };
77247
+ async function resolveEcsSecrets(entries, options = {}) {
77248
+ if (entries.length === 0)
77249
+ return [];
77250
+ const logger = getLogger().child("ecs-secrets");
77251
+ const secretsClient = options.secretsManagerClient ?? new SecretsManagerClient3({ ...options.region && { region: options.region } });
77252
+ const ssmClient = options.ssmClient ?? new SSMClient4({ ...options.region && { region: options.region } });
77253
+ const ownsSecretsClient = options.secretsManagerClient === void 0;
77254
+ const ownsSsmClient = options.ssmClient === void 0;
77255
+ try {
77256
+ const results = await Promise.all(
77257
+ entries.map(async (entry) => {
77258
+ const value = await resolveOne(entry, secretsClient, ssmClient);
77259
+ logger.debug(`Resolved secret ${entry.containerName}.${entry.name} (${entry.valueFrom})`);
77260
+ return { ...entry, value };
77261
+ })
77262
+ );
77263
+ return results;
77264
+ } finally {
77265
+ if (ownsSecretsClient)
77266
+ secretsClient.destroy();
77267
+ if (ownsSsmClient)
77268
+ ssmClient.destroy();
77269
+ }
77270
+ }
77271
+ async function resolveOne(entry, secretsClient, ssmClient) {
77272
+ const arn = entry.valueFrom;
77273
+ const shape = classifySecretArn(arn);
77274
+ switch (shape.kind) {
77275
+ case "secrets-manager":
77276
+ return resolveSecretsManager(entry, shape, secretsClient);
77277
+ case "ssm":
77278
+ return resolveSsm(entry, shape, ssmClient);
77279
+ case "unknown":
77280
+ throw new EcsSecretsResolutionError(
77281
+ `Container '${entry.containerName}' secret '${entry.name}' references an unsupported ValueFrom shape '${arn}'. Expected Secrets Manager ARN (optionally with :<json-key>::) or SSM Parameter ARN.`
77282
+ );
77283
+ }
77284
+ }
77285
+ function classifySecretArn(arn) {
77286
+ if (!arn.startsWith("arn:"))
77287
+ return { kind: "unknown" };
77288
+ const smMatch = /^(arn:[^:]+:secretsmanager:[^:]+:\d+:secret:[^:]+)(?::([^:]+)::?)?$/.exec(arn);
77289
+ if (smMatch) {
77290
+ const out = { kind: "secrets-manager", baseArn: smMatch[1] };
77291
+ if (smMatch[2])
77292
+ out.jsonKey = smMatch[2];
77293
+ return out;
77294
+ }
77295
+ const ssmMatch = /^arn:[^:]+:ssm:[^:]+:\d+:parameter(\/.+)$/.exec(arn);
77296
+ if (ssmMatch) {
77297
+ return { kind: "ssm", name: ssmMatch[1] };
77298
+ }
77299
+ return { kind: "unknown" };
77300
+ }
77301
+ async function resolveSecretsManager(entry, shape, client) {
77302
+ let secretString;
77303
+ try {
77304
+ const resp = await client.send(new GetSecretValueCommand2({ SecretId: shape.baseArn }));
77305
+ secretString = resp.SecretString;
77306
+ } catch (err) {
77307
+ throw new EcsSecretsResolutionError(
77308
+ `Failed to resolve Secrets Manager secret for container '${entry.containerName}' / env '${entry.name}' (${shape.baseArn}): ${err instanceof Error ? err.message : String(err)}`
77309
+ );
77310
+ }
77311
+ if (secretString === void 0) {
77312
+ throw new EcsSecretsResolutionError(
77313
+ `Secrets Manager returned no SecretString for container '${entry.containerName}' / env '${entry.name}' (${shape.baseArn}). Binary secrets are not supported.`
77314
+ );
77315
+ }
77316
+ if (shape.jsonKey === void 0)
77317
+ return secretString;
77318
+ let parsed;
77319
+ try {
77320
+ parsed = JSON.parse(secretString);
77321
+ } catch (err) {
77322
+ throw new EcsSecretsResolutionError(
77323
+ `Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but the secret value is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
77324
+ );
77325
+ }
77326
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
77327
+ throw new EcsSecretsResolutionError(
77328
+ `Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but the secret root is not a JSON object.`
77329
+ );
77330
+ }
77331
+ const value = parsed[shape.jsonKey];
77332
+ if (value === void 0) {
77333
+ throw new EcsSecretsResolutionError(
77334
+ `Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but no such key exists in the secret JSON.`
77335
+ );
77336
+ }
77337
+ return typeof value === "string" ? value : JSON.stringify(value);
77338
+ }
77339
+ async function resolveSsm(entry, shape, client) {
77340
+ try {
77341
+ const resp = await client.send(
77342
+ new GetParameterCommand4({ Name: shape.name, WithDecryption: true })
77343
+ );
77344
+ const value = resp.Parameter?.Value;
77345
+ if (value === void 0) {
77346
+ throw new EcsSecretsResolutionError(
77347
+ `SSM parameter '${shape.name}' returned no Value for container '${entry.containerName}' / env '${entry.name}'.`
77348
+ );
77349
+ }
77350
+ return value;
77351
+ } catch (err) {
77352
+ if (err instanceof EcsSecretsResolutionError)
77353
+ throw err;
77354
+ throw new EcsSecretsResolutionError(
77355
+ `Failed to resolve SSM parameter for container '${entry.containerName}' / env '${entry.name}' (${shape.name}): ${err instanceof Error ? err.message : String(err)}`
77356
+ );
77357
+ }
77358
+ }
77359
+
77360
+ // src/local/ecs-task-runner.ts
77361
+ var execFileAsync6 = promisify6(execFile6);
77362
+ var EcsTaskRunnerError = class _EcsTaskRunnerError extends Error {
77363
+ constructor(message) {
77364
+ super(message);
77365
+ this.name = "EcsTaskRunnerError";
77366
+ Object.setPrototypeOf(this, _EcsTaskRunnerError.prototype);
77367
+ }
77368
+ };
77369
+ function createEcsRunState() {
77370
+ return { network: void 0, dockerVolumeNames: [], startedContainers: [], logStoppers: [] };
77371
+ }
77372
+ async function cleanupEcsRun(state, options) {
77373
+ const logger = getLogger().child("ecs-runner");
77374
+ for (const stop of state.logStoppers) {
77375
+ try {
77376
+ stop();
77377
+ } catch (err) {
77378
+ logger.debug(`log stream stop failed: ${err instanceof Error ? err.message : String(err)}`);
77379
+ }
77380
+ }
77381
+ state.logStoppers = [];
77382
+ if (!options.keepRunning) {
77383
+ for (const c of state.startedContainers) {
77384
+ try {
77385
+ await stopContainer(c.id, 10);
77386
+ } catch (err) {
77387
+ logger.debug(
77388
+ `docker stop ${c.id} failed: ${err instanceof Error ? err.message : String(err)}`
77389
+ );
77390
+ }
77391
+ try {
77392
+ await removeContainer(c.id);
77393
+ } catch (err) {
77394
+ logger.debug(
77395
+ `docker rm -f ${c.id} failed: ${err instanceof Error ? err.message : String(err)}`
77396
+ );
77397
+ }
77398
+ }
77399
+ state.startedContainers = [];
77400
+ }
77401
+ await destroyTaskNetwork(state.network);
77402
+ state.network = void 0;
77403
+ for (const v of state.dockerVolumeNames) {
77404
+ try {
77405
+ await execFileAsync6("docker", ["volume", "rm", v]);
77406
+ logger.debug(`Removed docker volume ${v}`);
77407
+ } catch (err) {
77408
+ logger.debug(
77409
+ `docker volume rm ${v} failed: ${err instanceof Error ? err.message : String(err)}`
77410
+ );
77411
+ }
77412
+ }
77413
+ state.dockerVolumeNames = [];
77414
+ }
77415
+ async function runEcsTask(task, options, state) {
77416
+ const logger = getLogger();
77417
+ if (task.containers.length === 0) {
77418
+ throw new EcsTaskRunnerError(
77419
+ `Task '${task.taskDefinitionLogicalId}' has no containers \u2014 nothing to run.`
77420
+ );
77421
+ }
77422
+ for (const w of task.warnings)
77423
+ logger.warn(w);
77424
+ const dag = buildDependencyGraph(task.containers);
77425
+ const startOrder = topoSort(dag, task.containers);
77426
+ const imagePlan = options.imagePlanByContainer ?? /* @__PURE__ */ new Map();
77427
+ if (!options.imagePlanByContainer) {
77428
+ await prepareImages(task, imagePlan, options);
77429
+ }
77430
+ const allSecrets = [];
77431
+ for (const c of task.containers) {
77432
+ for (const s of c.secrets) {
77433
+ allSecrets.push({ containerName: c.name, name: s.name, valueFrom: s.valueFrom });
77434
+ }
77435
+ }
77436
+ const resolvedSecrets = await resolveEcsSecrets(allSecrets, {
77437
+ ...options.region !== void 0 && { region: options.region }
77438
+ });
77439
+ const secretsByContainer = groupSecretsByContainer(resolvedSecrets);
77440
+ const netCreateOpts = {
77441
+ prefix: options.cluster,
77442
+ skipPull: options.skipPull
77443
+ };
77444
+ if (options.taskCredentials)
77445
+ netCreateOpts.credentials = options.taskCredentials;
77446
+ if (options.cluster)
77447
+ netCreateOpts.cluster = options.cluster;
77448
+ state.network = await createTaskNetwork(netCreateOpts);
77449
+ const volumeByName = await realizeDockerVolumes(task.volumes, state);
77450
+ const dockerCmds = /* @__PURE__ */ new Map();
77451
+ for (const container of task.containers) {
77452
+ const image = imagePlan.get(container.name);
77453
+ if (!image) {
77454
+ throw new EcsTaskRunnerError(
77455
+ `Internal: no resolved image for container '${container.name}'.`
77456
+ );
77457
+ }
77458
+ dockerCmds.set(
77459
+ container.name,
77460
+ buildDockerRunArgs({
77461
+ task,
77462
+ container,
77463
+ image,
77464
+ network: state.network.networkName,
77465
+ volumeByName,
77466
+ secrets: secretsByContainer.get(container.name) ?? [],
77467
+ envOverrides: options.envOverrides,
77468
+ containerHost: options.containerHost,
77469
+ roleArn: options.taskRoleArn,
77470
+ platformOverride: options.platformOverride,
77471
+ region: options.region
77472
+ })
77473
+ );
77474
+ }
77475
+ const startedByName = /* @__PURE__ */ new Map();
77476
+ for (const containerName of startOrder) {
77477
+ const container = task.containers.find((c) => c.name === containerName);
77478
+ await awaitDependencies(container, startedByName);
77479
+ const args = dockerCmds.get(container.name);
77480
+ logger.info(`Starting container '${container.name}' (image=${imagePlan.get(container.name)})`);
77481
+ let id;
77482
+ try {
77483
+ const { stdout } = await execFileAsync6("docker", args, { maxBuffer: 10 * 1024 * 1024 });
77484
+ id = stdout.trim();
77485
+ } catch (err) {
77486
+ const e = err;
77487
+ throw new DockerRunnerError(
77488
+ `docker run failed for container '${container.name}': ${e.stderr?.trim() || e.message || String(err)}`
77489
+ );
77490
+ }
77491
+ state.startedContainers.push({ name: container.name, id });
77492
+ startedByName.set(container.name, { id, container });
77493
+ if (!options.detach) {
77494
+ state.logStoppers.push(streamContainerLogs(container.name, id));
77495
+ }
77496
+ }
77497
+ if (options.detach) {
77498
+ return { exitCode: 0, state };
77499
+ }
77500
+ const essential = task.containers.find((c) => c.essential) ?? task.containers[0];
77501
+ const essentialId = startedByName.get(essential.name)?.id;
77502
+ if (!essentialId) {
77503
+ throw new EcsTaskRunnerError(`Essential container '${essential.name}' did not start.`);
77504
+ }
77505
+ const exitCode = await waitForContainerExit(essentialId);
77506
+ return { exitCode, essentialContainerName: essential.name, state };
77507
+ }
77508
+ function buildDependencyGraph(containers) {
77509
+ const g = new graphlib2.Graph({ directed: true });
77510
+ for (const c of containers)
77511
+ g.setNode(c.name);
77512
+ for (const c of containers) {
77513
+ for (const d of c.dependsOn) {
77514
+ g.setEdge(c.name, d.containerName);
77515
+ }
77516
+ }
77517
+ const cycles = graphlib2.alg.findCycles(g);
77518
+ if (cycles.length > 0) {
77519
+ throw new EcsTaskRunnerError(
77520
+ `Cyclic DependsOn detected: ${cycles.map((c) => c.join(" -> ")).join("; ")}`
77521
+ );
77522
+ }
77523
+ return g;
77524
+ }
77525
+ function topoSort(g, containers) {
77526
+ const sorted = graphlib2.alg.topsort(g);
77527
+ const byPosition = /* @__PURE__ */ new Map();
77528
+ containers.forEach((c, idx) => byPosition.set(c.name, idx));
77529
+ return [...sorted].reverse().sort((a, b) => {
77530
+ return (byPosition.get(a) ?? 0) - (byPosition.get(b) ?? 0);
77531
+ });
77532
+ }
77533
+ async function awaitDependencies(container, started) {
77534
+ for (const dep of container.dependsOn) {
77535
+ const entry = started.get(dep.containerName);
77536
+ if (!entry) {
77537
+ throw new EcsTaskRunnerError(
77538
+ `Container '${container.name}' depends on '${dep.containerName}' but the latter never started.`
77539
+ );
77540
+ }
77541
+ switch (dep.condition) {
77542
+ case "START":
77543
+ break;
77544
+ case "COMPLETE":
77545
+ await waitForContainerExit(entry.id);
77546
+ break;
77547
+ case "SUCCESS": {
77548
+ const code = await waitForContainerExit(entry.id);
77549
+ if (code !== 0) {
77550
+ throw new EcsTaskRunnerError(
77551
+ `Container '${container.name}' requires dependency '${dep.containerName}' to exit 0, but it exited ${code}.`
77552
+ );
77553
+ }
77554
+ break;
77555
+ }
77556
+ case "HEALTHY":
77557
+ await waitForContainerHealthy(entry.id, dep.containerName);
77558
+ break;
77559
+ }
77560
+ }
77561
+ }
77562
+ async function waitForContainerHealthy(containerId, displayName) {
77563
+ const logger = getLogger().child("ecs-runner");
77564
+ const deadline = Date.now() + 5 * 60 * 1e3;
77565
+ let lastStatus = "";
77566
+ while (Date.now() < deadline) {
77567
+ try {
77568
+ const { stdout } = await execFileAsync6("docker", [
77569
+ "inspect",
77570
+ "--format",
77571
+ "{{.State.Health.Status}}",
77572
+ containerId
77573
+ ]);
77574
+ const status = stdout.trim();
77575
+ if (status !== lastStatus) {
77576
+ logger.debug(`Container '${displayName}' health status: ${status}`);
77577
+ lastStatus = status;
77578
+ }
77579
+ if (status === "healthy")
77580
+ return;
77581
+ if (status === "unhealthy") {
77582
+ throw new EcsTaskRunnerError(
77583
+ `Container '${displayName}' health status is 'unhealthy'; aborting before dependents start.`
77584
+ );
77585
+ }
77586
+ } catch (err) {
77587
+ if (err instanceof EcsTaskRunnerError)
77588
+ throw err;
77589
+ logger.debug(
77590
+ `docker inspect on '${displayName}' failed: ${err instanceof Error ? err.message : String(err)}`
77591
+ );
77592
+ }
77593
+ await sleep(1e3);
77594
+ }
77595
+ throw new EcsTaskRunnerError(
77596
+ `Container '${displayName}' did not become healthy within 5 minutes.`
77597
+ );
77598
+ }
77599
+ async function waitForContainerExit(containerId) {
77600
+ try {
77601
+ const { stdout } = await execFileAsync6("docker", ["wait", containerId], {
77602
+ maxBuffer: 1024 * 1024
77603
+ });
77604
+ const code = Number.parseInt(stdout.trim(), 10);
77605
+ return Number.isFinite(code) ? code : 1;
77606
+ } catch (err) {
77607
+ const e = err;
77608
+ throw new DockerRunnerError(
77609
+ `docker wait failed: ${e.stderr?.trim() || e.message || String(err)}`
77610
+ );
77611
+ }
77612
+ }
77613
+ async function stopContainer(containerId, graceSeconds) {
77614
+ try {
77615
+ await execFileAsync6("docker", ["stop", "-t", String(graceSeconds), containerId]);
77616
+ } catch {
77617
+ }
77618
+ }
77619
+ function sleep(ms) {
77620
+ return new Promise((res) => setTimeout(res, ms));
77621
+ }
77622
+ function streamContainerLogs(containerName, containerId) {
77623
+ const proc = spawn5("docker", ["logs", "-f", containerId], {
77624
+ stdio: ["ignore", "pipe", "pipe"]
77625
+ });
77626
+ const prefix = `[${containerName}] `;
77627
+ let stdoutBuf = "";
77628
+ let stderrBuf = "";
77629
+ proc.stdout?.on("data", (chunk) => {
77630
+ stdoutBuf = writePrefixed(prefix, stdoutBuf + chunk.toString("utf-8"), process.stdout);
77631
+ });
77632
+ proc.stderr?.on("data", (chunk) => {
77633
+ stderrBuf = writePrefixed(prefix, stderrBuf + chunk.toString("utf-8"), process.stderr);
77634
+ });
77635
+ proc.on("error", () => {
77636
+ });
77637
+ return () => {
77638
+ if (stdoutBuf)
77639
+ process.stdout.write(prefix + stdoutBuf + "\n");
77640
+ if (stderrBuf)
77641
+ process.stderr.write(prefix + stderrBuf + "\n");
77642
+ if (!proc.killed)
77643
+ proc.kill("SIGTERM");
77644
+ };
77645
+ }
77646
+ function writePrefixed(prefix, buffer, out) {
77647
+ const lines = buffer.split("\n");
77648
+ const remainder = lines.pop() ?? "";
77649
+ for (const line of lines) {
77650
+ out.write(prefix + line + "\n");
77651
+ }
77652
+ return remainder;
77653
+ }
77654
+ async function prepareImages(task, out, options) {
77655
+ const logger = getLogger().child("ecs-runner");
77656
+ for (const container of task.containers) {
77657
+ const image = await prepareOneImage(task, container, options);
77658
+ out.set(container.name, image);
77659
+ logger.debug(`Container '${container.name}' image=${image}`);
77660
+ }
77661
+ }
77662
+ async function prepareOneImage(task, container, options) {
77663
+ const image = container.image;
77664
+ switch (image.kind) {
77665
+ case "public": {
77666
+ await pullImage(image.uri, options.skipPull);
77667
+ return image.uri;
77668
+ }
77669
+ case "ecr": {
77670
+ return pullEcrImage(image.uri, {
77671
+ skipPull: options.skipPull,
77672
+ ...options.region !== void 0 && { region: options.region }
77673
+ });
77674
+ }
77675
+ case "cdk-asset": {
77676
+ const cdkOutDir = task.stack.assetManifestPath ? dirname6(task.stack.assetManifestPath) : void 0;
77677
+ if (!cdkOutDir) {
77678
+ throw new EcsTaskRunnerError(
77679
+ `Container '${container.name}' uses a CDK asset image but the stack has no asset manifest. Re-synthesize the app (without \`--output <stale-dir>\`) and retry.`
77680
+ );
77681
+ }
77682
+ const loader = new AssetManifestLoader();
77683
+ const manifest = await loader.loadManifest(cdkOutDir, task.stack.stackName);
77684
+ if (!manifest) {
77685
+ throw new EcsTaskRunnerError(
77686
+ `No asset manifest at ${cdkOutDir} for stack ${task.stack.stackName}.`
77687
+ );
77688
+ }
77689
+ const dockerImages = manifest.dockerImages ?? {};
77690
+ const entries = Object.entries(dockerImages);
77691
+ let asset;
77692
+ if (image.assetHash && dockerImages[image.assetHash]) {
77693
+ asset = dockerImages[image.assetHash];
77694
+ } else if (entries.length === 1) {
77695
+ asset = entries[0][1];
77696
+ }
77697
+ if (!asset) {
77698
+ throw new EcsTaskRunnerError(
77699
+ `Container '${container.name}' references a CDK asset image but no matching entry was found in cdk.out. Re-synthesize the CDK app and retry.`
77700
+ );
77701
+ }
77702
+ const tag = `cdkd-local-run-task-${(image.assetHash ?? "single").slice(0, 16)}`;
77703
+ await buildDockerImage(asset, cdkOutDir, tag, {
77704
+ ...options.platformOverride !== void 0 && { platform: options.platformOverride },
77705
+ wrapError: (stderr) => new LocalInvokeBuildError(
77706
+ `docker build failed for ECS container '${container.name}' (${asset.source.directory}): ${stderr}`
77707
+ )
77708
+ });
77709
+ return tag;
77710
+ }
77711
+ }
77712
+ }
77713
+ async function realizeDockerVolumes(volumes, state) {
77714
+ const logger = getLogger().child("ecs-runner");
77715
+ const out = /* @__PURE__ */ new Map();
77716
+ for (const v of volumes) {
77717
+ if (v.kind === "host") {
77718
+ if (v.hostPath && !checkVolumeHostPath(v.hostPath)) {
77719
+ logger.warn(
77720
+ `Volume '${v.name}': host path '${v.hostPath}' does not exist or is not a directory. Docker will create an anonymous bind mount; create the host path before run-task if you expected to bind-mount it.`
77721
+ );
77722
+ }
77723
+ out.set(v.name, v);
77724
+ continue;
77725
+ }
77726
+ const cfg = v.dockerVolumeConfig;
77727
+ const args = ["volume", "create"];
77728
+ if (cfg?.driver)
77729
+ args.push("--driver", cfg.driver);
77730
+ if (cfg?.driverOpts) {
77731
+ for (const [k, val] of Object.entries(cfg.driverOpts))
77732
+ args.push("--opt", `${k}=${val}`);
77733
+ }
77734
+ if (cfg?.labels) {
77735
+ for (const [k, val] of Object.entries(cfg.labels))
77736
+ args.push("--label", `${k}=${val}`);
77737
+ }
77738
+ const dockerVolumeName = `cdkd-local-${v.name}-${randHex(4)}`;
77739
+ args.push(dockerVolumeName);
77740
+ try {
77741
+ await execFileAsync6("docker", args);
77742
+ state.dockerVolumeNames.push(dockerVolumeName);
77743
+ logger.debug(`Created docker volume ${dockerVolumeName} for task volume '${v.name}'`);
77744
+ } catch (err) {
77745
+ const e = err;
77746
+ throw new DockerRunnerError(
77747
+ `docker volume create failed for '${v.name}': ${e.stderr?.trim() || e.message || String(err)}`
77748
+ );
77749
+ }
77750
+ out.set(v.name, { ...v, dockerVolumeName });
77751
+ }
77752
+ return out;
77753
+ }
77754
+ function randHex(bytes) {
77755
+ return randomBytes2(bytes).toString("hex");
77756
+ }
77757
+ function groupSecretsByContainer(resolved) {
77758
+ const out = /* @__PURE__ */ new Map();
77759
+ for (const r of resolved) {
77760
+ const arr = out.get(r.containerName) ?? [];
77761
+ arr.push({ name: r.name, value: r.value });
77762
+ out.set(r.containerName, arr);
77763
+ }
77764
+ return out;
77765
+ }
77766
+ function buildDockerRunArgs(opts) {
77767
+ const { task, container, image, network, volumeByName, secrets, containerHost, roleArn } = opts;
77768
+ const args = ["run", "-d"];
77769
+ args.push("--name", `cdkd-local-${task.family}-${container.name}-${randHex(3)}`);
77770
+ args.push("--network", network);
77771
+ args.push("--network-alias", container.name);
77772
+ if (opts.platformOverride) {
77773
+ args.push("--platform", opts.platformOverride);
77774
+ } else if (task.runtimePlatform) {
77775
+ args.push(
77776
+ "--platform",
77777
+ task.runtimePlatform.cpuArchitecture === "ARM64" ? "linux/arm64" : "linux/amd64"
77778
+ );
77779
+ }
77780
+ for (const pm of container.portMappings) {
77781
+ const hostPort = pm.hostPort ?? pm.containerPort;
77782
+ args.push("-p", `${containerHost}:${hostPort}:${pm.containerPort}/${pm.protocol}`);
77783
+ }
77784
+ for (const mp of container.mountPoints) {
77785
+ const v = volumeByName.get(mp.sourceVolume);
77786
+ if (!v)
77787
+ continue;
77788
+ if (v.kind === "host") {
77789
+ if (v.hostPath) {
77790
+ const ro = mp.readOnly ? ":ro" : "";
77791
+ args.push("-v", `${v.hostPath}:${mp.containerPath}${ro}`);
77792
+ } else {
77793
+ args.push("-v", mp.containerPath);
77794
+ }
77795
+ } else {
77796
+ const name = v.dockerVolumeName ?? v.name;
77797
+ const ro = mp.readOnly ? ":ro" : "";
77798
+ args.push("-v", `${name}:${mp.containerPath}${ro}`);
77799
+ }
77800
+ }
77801
+ const finalEnv = {};
77802
+ const metaEnv = buildMetadataEnv({
77803
+ containerName: container.name,
77804
+ ...roleArn !== void 0 && { roleArn },
77805
+ ...opts.region !== void 0 && { region: opts.region }
77806
+ });
77807
+ Object.assign(finalEnv, metaEnv);
77808
+ Object.assign(finalEnv, container.environment);
77809
+ for (const s of secrets)
77810
+ finalEnv[s.name] = s.value;
77811
+ const overrides = opts.envOverrides;
77812
+ if (overrides) {
77813
+ applyOverrideMap2(finalEnv, overrides["Parameters"]);
77814
+ applyOverrideMap2(finalEnv, overrides[container.name]);
77815
+ }
77816
+ for (const [k, v] of Object.entries(finalEnv)) {
77817
+ args.push("-e", `${k}=${v}`);
77818
+ }
77819
+ if (container.user)
77820
+ args.push("--user", container.user);
77821
+ if (container.privileged)
77822
+ args.push("--privileged");
77823
+ if (container.readonlyRootFilesystem)
77824
+ args.push("--read-only");
77825
+ if (container.workingDirectory)
77826
+ args.push("--workdir", container.workingDirectory);
77827
+ for (const u of container.ulimits) {
77828
+ args.push("--ulimit", `${u.name}=${u.softLimit}:${u.hardLimit}`);
77829
+ }
77830
+ for (const link of container.links)
77831
+ args.push("--link", link);
77832
+ if (container.healthCheck) {
77833
+ args.push("--health-cmd", shellJoin(container.healthCheck.command));
77834
+ if (container.healthCheck.interval !== void 0) {
77835
+ args.push("--health-interval", `${container.healthCheck.interval}s`);
77836
+ }
77837
+ if (container.healthCheck.timeout !== void 0) {
77838
+ args.push("--health-timeout", `${container.healthCheck.timeout}s`);
77839
+ }
77840
+ if (container.healthCheck.retries !== void 0) {
77841
+ args.push("--health-retries", String(container.healthCheck.retries));
77842
+ }
77843
+ if (container.healthCheck.startPeriod !== void 0) {
77844
+ args.push("--health-start-period", `${container.healthCheck.startPeriod}s`);
77845
+ }
77846
+ }
77847
+ let entryPointTail = [];
77848
+ if (container.entryPoint && container.entryPoint.length > 0) {
77849
+ args.push("--entrypoint", container.entryPoint[0]);
77850
+ entryPointTail = container.entryPoint.slice(1);
77851
+ }
77852
+ args.push(image, ...entryPointTail, ...container.command ?? []);
77853
+ return args;
77854
+ }
77855
+ function applyOverrideMap2(acc, map) {
77856
+ if (!map)
77857
+ return;
77858
+ for (const [k, v] of Object.entries(map)) {
77859
+ if (v === null)
77860
+ delete acc[k];
77861
+ else if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
77862
+ acc[k] = String(v);
77863
+ }
77864
+ }
77865
+ }
77866
+ function shellJoin(parts) {
77867
+ return parts.map((p) => {
77868
+ if (/^[A-Za-z0-9_\-./=:]+$/.test(p))
77869
+ return p;
77870
+ return `'${p.replace(/'/g, "'\\''")}'`;
77871
+ }).join(" ");
77872
+ }
77873
+
77874
+ // src/cli/commands/local-run-task.ts
77875
+ async function localRunTaskCommand(target, options) {
77876
+ const logger = getLogger();
77877
+ if (options.verbose)
77878
+ logger.setLevel("debug");
77879
+ warnIfDeprecatedRegion(options);
77880
+ const state = createEcsRunState();
77881
+ let sigintHandler;
77882
+ let sigintCount = 0;
77883
+ let cleanupPromise;
77884
+ const cleanup = async () => {
77885
+ if (!cleanupPromise) {
77886
+ cleanupPromise = (async () => {
77887
+ try {
77888
+ await cleanupEcsRun(state, { keepRunning: options.keepRunning });
77889
+ } catch (err) {
77890
+ getLogger().debug(`cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
77891
+ }
77892
+ })();
77893
+ }
77894
+ await cleanupPromise;
77895
+ };
77896
+ try {
77897
+ await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
77898
+ await ensureDockerAvailable();
77899
+ const appCmd = resolveApp(options.app);
77900
+ if (!appCmd) {
77901
+ throw new Error('No CDK app specified. Pass --app, set CDKD_APP, or add "app" to cdk.json.');
77902
+ }
77903
+ logger.info("Synthesizing CDK app...");
77904
+ const synthesizer = new Synthesizer();
77905
+ const context = parseContextOptions(options.context);
77906
+ const synthOpts = {
77907
+ app: appCmd,
77908
+ output: options.output,
77909
+ ...options.region && { region: options.region },
77910
+ ...options.profile && { profile: options.profile },
77911
+ ...Object.keys(context).length > 0 && { context }
77912
+ };
77913
+ const { stacks } = await synthesizer.synthesize(synthOpts);
77914
+ const task = resolveEcsTaskTarget(target, stacks);
77915
+ logger.info(
77916
+ `Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`
77917
+ );
77918
+ sigintHandler = () => {
77919
+ sigintCount += 1;
77920
+ if (sigintCount >= 2) {
77921
+ process.stderr.write("Force-exit on second ^C; container cleanup skipped.\n");
77922
+ process.exit(130);
77923
+ }
77924
+ logger.info("Stopping task...");
77925
+ void cleanup().then(() => process.exit(130));
77926
+ };
77927
+ process.on("SIGINT", sigintHandler);
77928
+ let assumedCredentials;
77929
+ let resolvedRoleArn;
77930
+ if (options.assumeTaskRole === true) {
77931
+ if (!task.taskRoleArn) {
77932
+ throw new Error(
77933
+ `--assume-task-role passed without an ARN but the task definition's TaskRoleArn could not be resolved statically. Pass the ARN explicitly: --assume-task-role <arn>`
77934
+ );
77935
+ }
77936
+ resolvedRoleArn = task.taskRoleArn;
77937
+ assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);
77938
+ } else if (typeof options.assumeTaskRole === "string") {
77939
+ resolvedRoleArn = options.assumeTaskRole;
77940
+ assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);
77941
+ }
77942
+ const envOverrides = readEnvOverridesFile2(options.envVars);
77943
+ const runOpts = {
77944
+ cluster: options.cluster,
77945
+ containerHost: options.containerHost,
77946
+ skipPull: options.pull === false,
77947
+ keepRunning: options.keepRunning,
77948
+ detach: options.detach
77949
+ };
77950
+ if (envOverrides)
77951
+ runOpts.envOverrides = envOverrides;
77952
+ if (assumedCredentials)
77953
+ runOpts.taskCredentials = assumedCredentials;
77954
+ if (resolvedRoleArn)
77955
+ runOpts.taskRoleArn = resolvedRoleArn;
77956
+ if (options.platform)
77957
+ runOpts.platformOverride = options.platform;
77958
+ if (options.region)
77959
+ runOpts.region = options.region;
77960
+ const result = await runEcsTask(task, runOpts, state);
77961
+ if (options.detach) {
77962
+ logger.info("Task containers started in detached mode; cdkd is exiting.");
77963
+ logger.info(
77964
+ `Use 'docker ps --filter network=${result.state.network?.networkName ?? "<network>"}' to inspect; tear down with 'docker rm -f' and 'docker network rm'.`
77965
+ );
77966
+ sigintCount = 99;
77967
+ return;
77968
+ }
77969
+ if (result.essentialContainerName) {
77970
+ logger.info(
77971
+ `Essential container '${result.essentialContainerName}' exited with code ${result.exitCode}.`
77972
+ );
77973
+ }
77974
+ if (result.exitCode !== 0) {
77975
+ process.exitCode = result.exitCode;
77976
+ }
77977
+ } finally {
77978
+ if (sigintHandler)
77979
+ process.off("SIGINT", sigintHandler);
77980
+ if (!options.detach)
77981
+ await cleanup();
77982
+ }
77983
+ }
77984
+ async function assumeTaskRole(roleArn, region) {
77985
+ const { STSClient: STSClient11, AssumeRoleCommand: AssumeRoleCommand2 } = await import("@aws-sdk/client-sts");
77986
+ const sts = new STSClient11({ ...region && { region } });
77987
+ try {
77988
+ const response = await sts.send(
77989
+ new AssumeRoleCommand2({
77990
+ RoleArn: roleArn,
77991
+ RoleSessionName: `cdkd-local-run-task-${Date.now()}`,
77992
+ DurationSeconds: 3600
77993
+ })
77994
+ );
77995
+ const creds = response.Credentials;
77996
+ if (!creds?.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) {
77997
+ throw new Error(`AssumeRole(${roleArn}) returned no usable credentials.`);
77998
+ }
77999
+ return {
78000
+ accessKeyId: creds.AccessKeyId,
78001
+ secretAccessKey: creds.SecretAccessKey,
78002
+ sessionToken: creds.SessionToken
78003
+ };
78004
+ } finally {
78005
+ sts.destroy();
78006
+ }
78007
+ }
78008
+ function readEnvOverridesFile2(filePath) {
78009
+ if (!filePath)
78010
+ return void 0;
78011
+ let raw;
78012
+ try {
78013
+ raw = readFileSync7(filePath, "utf-8");
78014
+ } catch (err) {
78015
+ throw new Error(
78016
+ `Failed to read --env-vars file '${filePath}': ${err instanceof Error ? err.message : String(err)}`
78017
+ );
78018
+ }
78019
+ let parsed;
78020
+ try {
78021
+ parsed = JSON.parse(raw);
78022
+ } catch (err) {
78023
+ throw new Error(
78024
+ `Failed to parse --env-vars file '${filePath}' as JSON: ${err instanceof Error ? err.message : String(err)}`
78025
+ );
78026
+ }
78027
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
78028
+ throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);
78029
+ }
78030
+ return parsed;
78031
+ }
78032
+ function createLocalRunTaskCommand() {
78033
+ const cmd = new Command15("run-task").description(
78034
+ "Run an AWS::ECS::TaskDefinition locally \u2014 pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix."
78035
+ ).argument(
78036
+ "<target>",
78037
+ "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run"
78038
+ ).addOption(
78039
+ new Option8(
78040
+ "--cluster <name>",
78041
+ "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix"
78042
+ ).default("cdkd-local")
78043
+ ).addOption(
78044
+ new Option8(
78045
+ "--env-vars <file>",
78046
+ 'JSON env-var overrides (SAM-compatible: {"ContainerName":{"KEY":"VALUE"}, "Parameters":{}})'
78047
+ )
78048
+ ).addOption(
78049
+ new Option8(
78050
+ "--container-host <ip>",
78051
+ "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)"
78052
+ ).default("127.0.0.1")
78053
+ ).addOption(
78054
+ new Option8(
78055
+ "--assume-task-role [arn]",
78056
+ "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override."
78057
+ )
78058
+ ).addOption(
78059
+ new Option8("--no-pull", "Skip docker pull for every container image and the metadata sidecar")
78060
+ ).addOption(
78061
+ new Option8(
78062
+ "--platform <platform>",
78063
+ "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture"
78064
+ )
78065
+ ).addOption(
78066
+ new Option8(
78067
+ "--keep-running",
78068
+ "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems."
78069
+ ).default(false)
78070
+ ).addOption(
78071
+ new Option8(
78072
+ "--detach",
78073
+ "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle."
78074
+ ).default(false)
78075
+ ).action(withErrorHandling(localRunTaskCommand));
78076
+ [...commonOptions, ...appOptions, ...contextOptions].forEach((opt) => cmd.addOption(opt));
78077
+ cmd.addOption(deprecatedRegionOption);
78078
+ return cmd;
78079
+ }
78080
+
76607
78081
  // src/cli/commands/local-invoke.ts
76608
78082
  async function localInvokeCommand(target, options) {
76609
78083
  const logger = getLogger();
@@ -76701,7 +78175,7 @@ async function localInvokeCommand(target, options) {
76701
78175
  }
76702
78176
  }
76703
78177
  }
76704
- const overrides = readEnvOverridesFile2(options.envVars);
78178
+ const overrides = readEnvOverridesFile3(options.envVars);
76705
78179
  const envResult = resolveEnvVars(lambda.logicalId, templateEnv, overrides);
76706
78180
  for (const key of envResult.unresolved) {
76707
78181
  if (stateAudit && stateAudit.unresolved.some((u) => u.key === key))
@@ -76879,7 +78353,7 @@ async function resolveLocalBuildPlan(lambda) {
76879
78353
  const manifestPath = lambda.stack.assetManifestPath;
76880
78354
  if (!manifestPath)
76881
78355
  return void 0;
76882
- const cdkOutDir = dirname5(manifestPath);
78356
+ const cdkOutDir = dirname7(manifestPath);
76883
78357
  const loader = new AssetManifestLoader();
76884
78358
  const manifest = await loader.loadManifest(cdkOutDir, lambda.stack.stackName);
76885
78359
  if (!manifest)
@@ -76899,12 +78373,12 @@ function getTemplateEnv2(resource) {
76899
78373
  return void 0;
76900
78374
  return vars;
76901
78375
  }
76902
- function readEnvOverridesFile2(filePath) {
78376
+ function readEnvOverridesFile3(filePath) {
76903
78377
  if (!filePath)
76904
78378
  return void 0;
76905
78379
  let raw;
76906
78380
  try {
76907
- raw = readFileSync7(filePath, "utf-8");
78381
+ raw = readFileSync8(filePath, "utf-8");
76908
78382
  } catch (err) {
76909
78383
  throw new Error(
76910
78384
  `Failed to read --env-vars file '${filePath}': ${err instanceof Error ? err.message : String(err)}`
@@ -76932,7 +78406,7 @@ async function readEvent(options) {
76932
78406
  return parseEvent(raw, "<stdin>");
76933
78407
  }
76934
78408
  if (options.event) {
76935
- const raw = readFileSync7(options.event, "utf-8");
78409
+ const raw = readFileSync8(options.event, "utf-8");
76936
78410
  return parseEvent(raw, options.event);
76937
78411
  }
76938
78412
  return {};
@@ -77107,40 +78581,40 @@ function pickReferencedLogicalId(intrinsic) {
77107
78581
  return void 0;
77108
78582
  }
77109
78583
  function createLocalCommand() {
77110
- const local = new Command15("local").description(
77111
- "Local Lambda execution against the AWS Lambda Runtime Interface Emulator (Docker required)"
78584
+ const local = new Command16("local").description(
78585
+ "Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)"
77112
78586
  );
77113
- const invoke = new Command15("invoke").description(
78587
+ const invoke = new Command16("invoke").description(
77114
78588
  "Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix."
77115
- ).argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option8("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option8("--event-stdin", "Read event JSON from stdin").default(false)).addOption(
77116
- new Option8(
78589
+ ).argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option9("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option9("--event-stdin", "Read event JSON from stdin").default(false)).addOption(
78590
+ new Option9(
77117
78591
  "--env-vars <file>",
77118
78592
  'JSON env-var overrides (SAM-compatible: {"LogicalId":{"KEY":"VALUE"}})'
77119
78593
  )
77120
78594
  ).addOption(
77121
- new Option8(
78595
+ new Option9(
77122
78596
  "--no-pull",
77123
78597
  "Skip docker pull (use cached image) \u2014 no-op for IMAGE local-build path; `docker build` does not pull base layers by default"
77124
78598
  )
77125
78599
  ).addOption(
77126
- new Option8(
78600
+ new Option9(
77127
78601
  "--no-build",
77128
78602
  "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull."
77129
78603
  )
77130
- ).addOption(new Option8("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(
77131
- new Option8("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")
78604
+ ).addOption(new Option9("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(
78605
+ new Option9("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")
77132
78606
  ).addOption(
77133
- new Option8(
78607
+ new Option9(
77134
78608
  "--assume-role <arn>",
77135
78609
  `Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the "developer admin / function narrow" skew). Off by default \u2014 when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default).`
77136
78610
  )
77137
78611
  ).addOption(
77138
- new Option8(
78612
+ new Option9(
77139
78613
  "--from-state",
77140
78614
  "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default \u2014 keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy."
77141
78615
  ).default(false)
77142
78616
  ).addOption(
77143
- new Option8(
78617
+ new Option9(
77144
78618
  "--stack-region <region>",
77145
78619
  "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions)."
77146
78620
  )
@@ -77151,6 +78625,7 @@ function createLocalCommand() {
77151
78625
  invoke.addOption(deprecatedRegionOption);
77152
78626
  local.addCommand(invoke);
77153
78627
  local.addCommand(createLocalStartApiCommand());
78628
+ local.addCommand(createLocalRunTaskCommand());
77154
78629
  return local;
77155
78630
  }
77156
78631
 
@@ -77182,8 +78657,8 @@ function reorderArgs(argv) {
77182
78657
  return [...prefix, ...cmdAndAfter, ...beforeCmd];
77183
78658
  }
77184
78659
  async function main() {
77185
- const program = new Command16();
77186
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.81.0");
78660
+ const program = new Command17();
78661
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.82.0");
77187
78662
  program.addCommand(createBootstrapCommand());
77188
78663
  program.addCommand(createSynthCommand());
77189
78664
  program.addCommand(createListCommand());