cdk-local 0.65.3 → 0.67.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.
@@ -20968,6 +20968,7 @@ async function startEcsService(service, options, runState) {
20968
20968
  for (let i = 0; i < replicaCount; i++) {
20969
20969
  const instance = {
20970
20970
  index: i,
20971
+ generation: 0,
20971
20972
  state: createEcsRunState(),
20972
20973
  restartCount: 0,
20973
20974
  shuttingDown: false,
@@ -21112,8 +21113,11 @@ function buildNetworkAliasesByContainer(service) {
21112
21113
  */
21113
21114
  async function bootReplica(service, options, instance) {
21114
21115
  const logger = getLogger().child("ecs-service");
21115
- const perReplicaCluster = `${options.taskOptions.cluster}-svc-${service.serviceLogicalId.toLowerCase()}-r${instance.index}`;
21116
- const ownerKeyPrefix = `${service.serviceLogicalId}:r${instance.index}`;
21116
+ const gen = instance.generation;
21117
+ const genSuffix = gen > 0 ? `-g${gen}` : "";
21118
+ const ownerKeyGenSuffix = gen > 0 ? `:g${gen}` : "";
21119
+ const perReplicaCluster = `${options.taskOptions.cluster}-svc-${service.serviceLogicalId.toLowerCase()}-r${instance.index}${genSuffix}`;
21120
+ const ownerKeyPrefix = `${service.serviceLogicalId}:r${instance.index}${ownerKeyGenSuffix}`;
21117
21121
  const addHostFlags = options.discovery?.registry ? options.discovery.registry.buildAddHostFlags(ownerKeyPrefix) : [];
21118
21122
  const sharedNetwork = options.discovery?.sharedNetwork;
21119
21123
  const networkAliasesByContainer = buildNetworkAliasesByContainer(service);
@@ -21264,6 +21268,266 @@ function unregisterReplicaFromFrontDoor(instance, frontDoor) {
21264
21268
  instance.frontDoorOwnerKey = void 0;
21265
21269
  }
21266
21270
  /**
21271
+ * Phase 2 of issue #214 — shadow-replica readiness probe budget. Tested
21272
+ * with busybox httpd's ~50ms listen window and a non-trivial Node
21273
+ * Express startup (~1-3s) — 10s caps the rare slow-start app without
21274
+ * blocking the roll for typo'd configurations that will never listen.
21275
+ *
21276
+ * Mutable so unit tests can shrink the timeout window without
21277
+ * standing up a real clock; production callers leave the defaults.
21278
+ * Exposed via {@link __setShadowReadyConfig} below.
21279
+ */
21280
+ let shadowReadyTimeoutMs = 1e4;
21281
+ let shadowReadyIntervalMs = 100;
21282
+ /**
21283
+ * Phase 2 of issue #214 — per-replica rolling reload primitive used by
21284
+ * `cdkl start-service --watch`. Boots one fresh "shadow" replica under a
21285
+ * bumped generation suffix, atomically swaps Cloud Map / front-door
21286
+ * registrations off the old replica, then stops and cleans up the old
21287
+ * container.
21288
+ *
21289
+ * Sequence:
21290
+ * 1. Locate the old replica by `oldReplicaIndex` (rejects when it's
21291
+ * already shutting down or missing — the reloader must not race
21292
+ * itself across overlapping firings, which the emulator's
21293
+ * `reloadChain` serializer guarantees externally).
21294
+ * 2. Allocate a shadow {@link ServiceReplicaInstance} with the same
21295
+ * logical `index` and `generation = old.generation + 1`. Appended
21296
+ * to `runState.replicas` so a SIGTERM mid-roll tears it down too.
21297
+ * 3. `bootReplica(newService, newOptions, shadow)` boots the new
21298
+ * container, publishes Cloud Map handles under the bumped
21299
+ * generation suffix, and registers the shadow in the front-door
21300
+ * pool. The OLD replica's handles + pool entry stay live during
21301
+ * this window so consumers never see a gap.
21302
+ * 4. Atomically swap: unregister old's Cloud Map handles, drop its
21303
+ * front-door pool entry, mark `oldInstance.shuttingDown = true`
21304
+ * so the watcher exits. The shadow is already serving by this
21305
+ * point.
21306
+ * 5. `cleanupEcsRun(oldInstance.state)` tears the old container +
21307
+ * network down. The shadow remains in `runState.replicas`.
21308
+ * 6. Start the shadow's watcher so restart-on-exit is wired the
21309
+ * same as Phase 1's boot loop.
21310
+ *
21311
+ * Failure modes:
21312
+ * - `bootReplica` fails: keep the old replica serving. Best-effort
21313
+ * teardown of partial shadow state. Re-throws so the reloader can
21314
+ * log and continue with the remaining replicas.
21315
+ * - Old shutdown fails: surfaced via the logger; the shadow is
21316
+ * already live so the service stays available.
21317
+ *
21318
+ * @internal — wired only by the emulator's reload pathway.
21319
+ */
21320
+ async function rollServiceReplica(args) {
21321
+ const { controller, oldReplicaIndex, newService, newOptions } = args;
21322
+ const logger = getLogger().child("ecs-service");
21323
+ const oldInstance = controller.runState.replicas[oldReplicaIndex];
21324
+ if (!oldInstance) throw new EcsServiceRunnerError(`rollServiceReplica: no replica at index ${oldReplicaIndex} (replicas=${controller.runState.replicas.length}).`);
21325
+ if (oldInstance.shuttingDown) {
21326
+ logger.warn(`Rolling replica r${oldInstance.index} (gen ${oldInstance.generation}): retired by its own watcher mid-roll (essential container exited). Skipping this slot; save again to re-boot it.`);
21327
+ return;
21328
+ }
21329
+ const teardownOldFirst = computeReplicaCount(newService.desiredCount, newOptions.maxTasks) === 1;
21330
+ const shadow = {
21331
+ index: oldInstance.index,
21332
+ generation: oldInstance.generation + 1,
21333
+ state: createEcsRunState(),
21334
+ restartCount: 0,
21335
+ shuttingDown: false,
21336
+ inFlightBoot: void 0,
21337
+ cloudMapHandles: [],
21338
+ frontDoorOwnerKey: void 0
21339
+ };
21340
+ controller.runState.replicas.push(shadow);
21341
+ if (teardownOldFirst) {
21342
+ logger.info(`Rolling replica ${shadow.index} (gen ${shadow.generation}): single-replica + host-port publish — tearing old down before shadow boot to avoid host-port collision.`);
21343
+ if (newOptions.discovery) {
21344
+ for (const handle of oldInstance.cloudMapHandles) try {
21345
+ newOptions.discovery.registry.unregister(handle);
21346
+ } catch {}
21347
+ oldInstance.cloudMapHandles = [];
21348
+ }
21349
+ unregisterReplicaFromFrontDoor(oldInstance, newOptions.frontDoor);
21350
+ oldInstance.shuttingDown = true;
21351
+ try {
21352
+ await cleanupEcsRun(oldInstance.state, { keepRunning: newOptions.taskOptions.keepRunning });
21353
+ } catch (err) {
21354
+ logger.warn(`Rolling replica ${oldInstance.index}: cleanup of old (gen ${oldInstance.generation}) failed: ${err instanceof Error ? err.message : String(err)}. Attempting shadow boot anyway.`);
21355
+ }
21356
+ const oldIdx = controller.runState.replicas.indexOf(oldInstance);
21357
+ if (oldIdx !== -1) controller.runState.replicas.splice(oldIdx, 1);
21358
+ } else logger.info(`Rolling replica ${shadow.index} (gen ${shadow.generation}): booting shadow before retiring old.`);
21359
+ const bootPromise = (async () => {
21360
+ await bootReplica(newService, newOptions, shadow);
21361
+ await waitForReplicaTcpReady(newService, shadow, {
21362
+ timeoutMs: shadowReadyTimeoutMs,
21363
+ intervalMs: shadowReadyIntervalMs
21364
+ });
21365
+ })();
21366
+ shadow.inFlightBoot = bootPromise;
21367
+ try {
21368
+ await bootPromise;
21369
+ } catch (err) {
21370
+ const shadowIdx = controller.runState.replicas.indexOf(shadow);
21371
+ if (shadowIdx !== -1) controller.runState.replicas.splice(shadowIdx, 1);
21372
+ try {
21373
+ await cleanupEcsRun(shadow.state, { keepRunning: false });
21374
+ } catch {}
21375
+ if (teardownOldFirst) logger.error(`Rolling replica ${shadow.index}: shadow boot failed and the old replica was already torn down for the single-replica path. Save again with a clean boot to re-start the service.`);
21376
+ throw err;
21377
+ } finally {
21378
+ shadow.inFlightBoot = void 0;
21379
+ }
21380
+ if (teardownOldFirst) {
21381
+ watchReplica(newService, newOptions, shadow, controller.runState);
21382
+ logger.info(`Rolling replica ${shadow.index} (gen ${shadow.generation}): single-replica reload complete.`);
21383
+ return;
21384
+ }
21385
+ if (newOptions.discovery) {
21386
+ for (const handle of oldInstance.cloudMapHandles) try {
21387
+ newOptions.discovery.registry.unregister(handle);
21388
+ } catch {}
21389
+ oldInstance.cloudMapHandles = [];
21390
+ }
21391
+ unregisterReplicaFromFrontDoor(oldInstance, newOptions.frontDoor);
21392
+ await disconnectOldFromSharedNetwork(oldInstance).catch((err) => {
21393
+ logger.debug(`Rolling replica ${oldInstance.index}: shared-network disconnect of old (gen ${oldInstance.generation}) failed: ${err instanceof Error ? err.message : String(err)}. Proceeding with cleanup (the docker-rm step still tears it down).`);
21394
+ });
21395
+ oldInstance.shuttingDown = true;
21396
+ try {
21397
+ await cleanupEcsRun(oldInstance.state, { keepRunning: newOptions.taskOptions.keepRunning });
21398
+ } catch (err) {
21399
+ logger.warn(`Rolling replica ${oldInstance.index}: cleanup of old (gen ${oldInstance.generation}) failed: ${err instanceof Error ? err.message : String(err)}. The shadow is live; the stale container may need a manual \`docker rm\`.`);
21400
+ }
21401
+ const oldIdx = controller.runState.replicas.indexOf(oldInstance);
21402
+ if (oldIdx !== -1) controller.runState.replicas.splice(oldIdx, 1);
21403
+ watchReplica(newService, newOptions, shadow, controller.runState);
21404
+ logger.info(`Rolling replica ${shadow.index} (gen ${shadow.generation}): swap complete; old retired.`);
21405
+ }
21406
+ /**
21407
+ * Phase 2 of issue #214 — disconnect every container of the dying
21408
+ * replica from the shared service network BEFORE `cleanupEcsRun`'s
21409
+ * `docker stop → docker rm` sequence. Docker's embedded DNS strips an
21410
+ * alias the instant a container is disconnected, so a peer resolving
21411
+ * the service's Service Connect / Cloud Map alias right after this
21412
+ * step never picks the dying container's IP — closing the race window
21413
+ * where the alias points at an IP whose app is already gone. Best-
21414
+ * effort: a disconnect failure logs at debug and `cleanupEcsRun`'s
21415
+ * `docker rm -f` will still tear the network membership down.
21416
+ *
21417
+ * No-op for replicas that aren't on a shared network (the defensive
21418
+ * "per-replica /24" fallback path); the per-replica network is
21419
+ * destroyed by `cleanupEcsRun` directly.
21420
+ */
21421
+ async function disconnectOldFromSharedNetwork(oldInstance) {
21422
+ const network = oldInstance.state.network;
21423
+ if (!network || !network.ownedByCaller) return;
21424
+ const networkName = network.networkName;
21425
+ const targets = [];
21426
+ if (network.sidecarContainerId) targets.push(network.sidecarContainerId);
21427
+ for (const c of oldInstance.state.startedContainers) targets.push(c.id);
21428
+ for (const id of targets) try {
21429
+ await dockerNetworkDisconnectImpl(networkName, id);
21430
+ } catch (err) {}
21431
+ }
21432
+ /**
21433
+ * Production `docker network disconnect --force <network> <id>` impl,
21434
+ * extracted as a test-overridable function so the rolling-primitive
21435
+ * unit test can assert this step actually ran (the reviewer flagged
21436
+ * that the test mock previously took the `!ownedByCaller` early-return
21437
+ * path and silently never entered the disconnect branch).
21438
+ */
21439
+ const defaultDockerNetworkDisconnectImpl = async (networkName, containerId) => {
21440
+ const { execFile } = await import("node:child_process");
21441
+ const { promisify } = await import("node:util");
21442
+ const { getDockerCmd } = await import("./docker-cmd-voNPrcRh.js").then((n) => n.t);
21443
+ await promisify(execFile)(getDockerCmd(), [
21444
+ "network",
21445
+ "disconnect",
21446
+ "--force",
21447
+ networkName,
21448
+ containerId
21449
+ ]);
21450
+ };
21451
+ let dockerNetworkDisconnectImpl = defaultDockerNetworkDisconnectImpl;
21452
+ /**
21453
+ * Phase 2 of issue #214 — shadow-replica TCP readiness probe used by
21454
+ * {@link rollServiceReplica} before the atomic registry swap. Polls the
21455
+ * essential container's first port mapping (the one Cloud Map / Service
21456
+ * Connect publishes) via TCP-connect on the shadow's docker network IP,
21457
+ * retrying every `intervalMs` until either the connect succeeds or the
21458
+ * timeout elapses.
21459
+ *
21460
+ * The probe is best-effort: a timeout logs a warn but DOES NOT throw.
21461
+ * Swapping anyway is the lesser evil — the dying old replica's image
21462
+ * is about to be torn down, and the shadow's new image is the user's
21463
+ * intent. A timed-out probe usually means the app inside the new image
21464
+ * has a startup bug; the user will see the connection failures on
21465
+ * their probe / curl and fix the app, then save again. Failing the
21466
+ * roll here would leave the OLD replica running on stale code with no
21467
+ * recovery path other than `^C`.
21468
+ *
21469
+ * Exposed for the unit test pattern: the probe's `connect` impl is
21470
+ * injectable via {@link __setTcpProbeImpl} so the rolling-primitive
21471
+ * unit test can avoid any real TCP socket.
21472
+ */
21473
+ async function waitForReplicaTcpReady(service, shadow, opts) {
21474
+ const logger = getLogger().child("ecs-service");
21475
+ const networkName = shadow.state.network?.networkName;
21476
+ if (!networkName) return;
21477
+ const essential = service.task.containers.find((c) => c.essential) ?? service.task.containers[0];
21478
+ if (!essential || essential.portMappings.length === 0) return;
21479
+ const started = shadow.state.startedContainers.find((c) => c.name === essential.name);
21480
+ if (!started) return;
21481
+ let ip;
21482
+ try {
21483
+ const resolved = await getContainerNetworkIp(started.id, networkName);
21484
+ if (!resolved) return;
21485
+ ip = resolved;
21486
+ } catch (err) {
21487
+ logger.warn(`Shadow replica r${shadow.index} (gen ${shadow.generation}): TCP-ready probe could not resolve docker IP: ${err instanceof Error ? err.message : String(err)}. Proceeding with swap.`);
21488
+ return;
21489
+ }
21490
+ const port = essential.portMappings[0].containerPort;
21491
+ const deadline = Date.now() + opts.timeoutMs;
21492
+ let lastErr;
21493
+ while (Date.now() < deadline) {
21494
+ try {
21495
+ await tcpProbeImpl(ip, port);
21496
+ logger.debug(`Shadow replica r${shadow.index} (gen ${shadow.generation}): TCP probe ${ip}:${port} accepted; proceeding with swap.`);
21497
+ return;
21498
+ } catch (err) {
21499
+ lastErr = err instanceof Error ? err.message : String(err);
21500
+ }
21501
+ await sleep(opts.intervalMs);
21502
+ }
21503
+ logger.warn(`Shadow replica r${shadow.index} (gen ${shadow.generation}): TCP probe ${ip}:${port} did not accept within ${opts.timeoutMs}ms (last: ${lastErr ?? "n/a"}). Swapping anyway — the new image is the user intent. Initial requests after the swap may 502 until the app finishes binding.`);
21504
+ }
21505
+ /**
21506
+ * Default TCP-connect probe used by {@link waitForReplicaTcpReady}.
21507
+ * Opens a socket to `host:port` and resolves on `connect`; rejects on
21508
+ * any error. The socket is destroyed immediately on connect — we don't
21509
+ * want to keep a connection open or send any bytes.
21510
+ */
21511
+ const defaultTcpProbeImpl = async (host, port) => {
21512
+ const { createConnection } = await import("node:net");
21513
+ await new Promise((resolve, reject) => {
21514
+ const socket = createConnection({
21515
+ host,
21516
+ port
21517
+ });
21518
+ const onError = (err) => {
21519
+ socket.destroy();
21520
+ reject(err);
21521
+ };
21522
+ socket.once("connect", () => {
21523
+ socket.destroy();
21524
+ resolve();
21525
+ });
21526
+ socket.once("error", onError);
21527
+ });
21528
+ };
21529
+ let tcpProbeImpl = defaultTcpProbeImpl;
21530
+ /**
21267
21531
  * Long-running watcher loop for one replica. Polls the essential
21268
21532
  * container's exit code via `docker wait`; on exit, decides whether to
21269
21533
  * restart per `restartPolicy` + applies exponential backoff. The loop
@@ -23322,7 +23586,18 @@ async function runEcsServiceEmulator(targets, options, strategy, extraStateProvi
23322
23586
  let frontDoorServers = [];
23323
23587
  let frontDoorByService = /* @__PURE__ */ new Map();
23324
23588
  let frontDoorLambdaRunners = [];
23589
+ let watcher;
23590
+ let reloadChain = Promise.resolve();
23325
23591
  const cleanup = singleFlight(async () => {
23592
+ if (watcher) {
23593
+ try {
23594
+ await watcher.close();
23595
+ } catch (err) {
23596
+ getLogger().warn(`watcher.close() failed: ${err instanceof Error ? err.message : String(err)}`);
23597
+ }
23598
+ watcher = void 0;
23599
+ }
23600
+ await reloadChain.catch(() => void 0);
23326
23601
  await Promise.allSettled(perTarget.map(async (pt) => {
23327
23602
  if (pt.controller) await pt.controller.shutdown();
23328
23603
  else {
@@ -23433,8 +23708,41 @@ async function runEcsServiceEmulator(targets, options, strategy, extraStateProvi
23433
23708
  } else logger.info(`Service(s) running: ${frontDoorLambdaRunners.length} Lambda target(s) behind the ALB front-door.`);
23434
23709
  logEndpointsBanner(perTarget, frontDoorServers, logger);
23435
23710
  logger.info("Press ^C to shut down.");
23436
- if (perTarget.length > 0) await Promise.all(perTarget.map((pt) => pt.controller.waitForShutdown()));
23437
- else await new Promise(() => {});
23711
+ if (options.watch === true && strategy.supportsWatch === true) {
23712
+ const watchRoot = process.cwd();
23713
+ const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
23714
+ watchRoot,
23715
+ output: options.output,
23716
+ watchConfig: resolveWatchConfig()
23717
+ });
23718
+ watcher = createFileWatcher({
23719
+ paths: [watchRoot],
23720
+ ignored,
23721
+ shouldTrigger,
23722
+ onChange: () => {
23723
+ logger.info("Detected source change; reloading service(s)...");
23724
+ reloadChain = reloadChain.then(() => reloadAllServices({
23725
+ perTarget,
23726
+ synthesizer,
23727
+ synthOpts,
23728
+ strategy,
23729
+ resolvedTargets,
23730
+ cloudMapIndexByStack,
23731
+ options,
23732
+ discovery,
23733
+ skipPull,
23734
+ extraStateProviders,
23735
+ profileCredsFile,
23736
+ frontDoorByService,
23737
+ logger
23738
+ })).catch((err) => {
23739
+ logger.error(`reloadAllServices threw: ${err instanceof Error ? err.message : String(err)}`);
23740
+ });
23741
+ }
23742
+ });
23743
+ logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}).`);
23744
+ }
23745
+ await new Promise(() => {});
23438
23746
  } finally {
23439
23747
  if (sigintHandler) {
23440
23748
  process.off("SIGINT", sigintHandler);
@@ -23443,6 +23751,143 @@ async function runEcsServiceEmulator(targets, options, strategy, extraStateProvi
23443
23751
  await cleanup();
23444
23752
  }
23445
23753
  }
23754
+ /**
23755
+ * Phase 2 of issue #214 — multi-replica rolling reload cycle for
23756
+ * `cdkl start-service --watch`. Mirrors start-api's `reloadAllServers`
23757
+ * shape but per-ECS-service, replacing Phase 1's "tear single replica
23758
+ * down, boot fresh" sequence with a per-replica rolling loop so the
23759
+ * service stays available end-to-end:
23760
+ *
23761
+ * 1. Re-runs `synthesizer.synthesize(synthOpts)` once (failure → warn
23762
+ * + keep every replica serving).
23763
+ * 2. Re-runs `strategy.resolveBoots(stacks, resolvedTargets)` so a
23764
+ * target that disappears from the CDK code is detected (warn +
23765
+ * keep previous).
23766
+ * 3. Refreshes `cloudMapIndexByStack` from the new stacks so a peer
23767
+ * service's namespace / discovery-name rename is picked up by the
23768
+ * next shadow replica's Cloud Map publish.
23769
+ * 4. Per-target:
23770
+ * a. Resolves the new (service, runnerOpts) pair against the new
23771
+ * stacks (cross-stack env / assume-task-role / `--env-vars`
23772
+ * all re-resolved fresh).
23773
+ * b. For each existing replica `i` in 0..min(old, new) - 1:
23774
+ * {@link rollServiceReplica} boots a shadow replica with the
23775
+ * new image under a bumped generation suffix, atomically swaps
23776
+ * Cloud Map / front-door registrations, then stops + cleans up
23777
+ * the old replica. Sequential — only one replica is mid-swap
23778
+ * at a time, so peer services + the front-door pool always
23779
+ * have at least N-1 live endpoints during a roll.
23780
+ *
23781
+ * Phase 2 trade-off: when the effective replica count changes mid-roll
23782
+ * (the user bumped `DesiredCount` or `--max-tasks` flips a clamp),
23783
+ * the rolling pathway keeps the existing replicas on the new image
23784
+ * but does not scale up / down to match the new count. A warn surfaces
23785
+ * so the user can `^C` + re-launch to scale; a richer "scale + roll"
23786
+ * mode is left to a follow-up under #214.
23787
+ *
23788
+ * Per-replica boot failure during the roll: the OLD replica stays
23789
+ * live (the shadow was torn down by `rollServiceReplica`), the
23790
+ * remaining replicas are still rolled, and the failure is surfaced
23791
+ * via the logger so the user can fix the source + save again.
23792
+ */
23793
+ async function reloadAllServices(args) {
23794
+ const { perTarget, synthesizer, synthOpts, strategy, resolvedTargets, cloudMapIndexByStack, options, discovery, skipPull, extraStateProviders, profileCredsFile, frontDoorByService, logger } = args;
23795
+ let stacks;
23796
+ try {
23797
+ ({stacks} = await synthesizer.synthesize(synthOpts));
23798
+ } catch (err) {
23799
+ logger.warn(`cdk synth failed during reload; keeping previous version. (${err instanceof Error ? err.message : String(err)})`);
23800
+ return;
23801
+ }
23802
+ const { boots: newBoots, warnings } = strategy.resolveBoots(stacks, resolvedTargets);
23803
+ for (const w of warnings) logger.warn(w);
23804
+ const newBootByTarget = new Map(newBoots.map((b) => [b.target, b]));
23805
+ cloudMapIndexByStack.clear();
23806
+ for (const stack of stacks) {
23807
+ const index = buildCloudMapIndex(stack);
23808
+ cloudMapIndexByStack.set(stack.stackName, index);
23809
+ for (const w of index.warnings) logger.warn(w);
23810
+ }
23811
+ for (const pt of perTarget) {
23812
+ const newBoot = newBootByTarget.get(pt.boot.target);
23813
+ if (!newBoot) {
23814
+ logger.warn(`Reload: target '${pt.boot.target}' no longer resolves to a service in the synthesized app; keeping the previous replica(s) serving.`);
23815
+ continue;
23816
+ }
23817
+ const controller = pt.controller;
23818
+ if (!controller) {
23819
+ logger.warn(`Reload: target '${pt.boot.target}' has no live controller (previous boot likely failed); skipping roll. \`^C\` and re-run start-service to recover.`);
23820
+ continue;
23821
+ }
23822
+ await rollOneTarget({
23823
+ controller,
23824
+ newBoot,
23825
+ stacks,
23826
+ options,
23827
+ discovery,
23828
+ skipPull,
23829
+ extraStateProviders,
23830
+ profileCredsFile,
23831
+ frontDoorPools: frontDoorByService.get(newBoot.target),
23832
+ suppressLoadBalancerWarning: strategy.suppressLoadBalancerWarning === true,
23833
+ logger
23834
+ });
23835
+ }
23836
+ logger.info("Reload complete.");
23837
+ }
23838
+ /**
23839
+ * Phase 2 of issue #214 — roll every replica of one target through the
23840
+ * new task descriptor sequentially. Extracted from {@link reloadAllServices}
23841
+ * so the per-target try/catch logic (synth-failure / resolve-failure /
23842
+ * per-replica boot-failure) stays uniform and readable.
23843
+ *
23844
+ * State-provider lifetime mirrors {@link bootOneTarget}: a fresh
23845
+ * `LocalStateProvider` is created at the top, disposed in `finally`,
23846
+ * even when the resolve / roll throws.
23847
+ */
23848
+ async function rollOneTarget(args) {
23849
+ const { controller, newBoot, stacks, options, discovery, skipPull, extraStateProviders, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning, logger } = args;
23850
+ const candidate = pickCandidateStack(parseEcsTarget(newBoot.target).stackPattern, stacks);
23851
+ const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
23852
+ try {
23853
+ let resolved;
23854
+ try {
23855
+ resolved = await resolveServiceAndRunnerOpts(newBoot, stacks, options, discovery, skipPull, stateProvider, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning, { quiet: true });
23856
+ } catch (err) {
23857
+ const reason = err instanceof Error ? err.message : String(err);
23858
+ logger.error(`Reload of '${newBoot.target}' was rejected: ${reason}. Existing replica(s) keep serving.`);
23859
+ return;
23860
+ }
23861
+ const { service: newService, runnerOpts: newRunnerOpts } = resolved;
23862
+ const oldReplicas = controller.runState.replicas.filter((r) => !r.shuttingDown);
23863
+ if (oldReplicas.length === 0) {
23864
+ logger.warn(`Reload of '${newBoot.target}': no live replicas to roll (all shutting down). \`^C\` and re-run start-service to recover.`);
23865
+ return;
23866
+ }
23867
+ if (newService.desiredCount !== oldReplicas.length) logger.warn(`Reload of '${newBoot.target}': service DesiredCount=${newService.desiredCount} does not match the ${oldReplicas.length} live replica(s); rolling existing replicas only — scale changes during --watch are not yet supported. \`^C\` and re-run start-service to apply the new replica count.`);
23868
+ logger.info(`Reload of '${newBoot.target}': rolling ${oldReplicas.length} replica(s) one at a time (start new shadow → swap registrations → stop old).`);
23869
+ for (const oldInstance of oldReplicas) {
23870
+ const idx = controller.runState.replicas.indexOf(oldInstance);
23871
+ if (idx === -1) {
23872
+ logger.warn(`Reload of '${newBoot.target}': replica r${oldInstance.index} (gen ${oldInstance.generation}) vanished before its roll; skipping.`);
23873
+ continue;
23874
+ }
23875
+ try {
23876
+ await rollServiceReplica({
23877
+ controller,
23878
+ oldReplicaIndex: idx,
23879
+ newService,
23880
+ newOptions: newRunnerOpts
23881
+ });
23882
+ } catch (err) {
23883
+ const reason = err instanceof Error ? err.message : String(err);
23884
+ logger.error(`Reload of '${newBoot.target}' replica r${oldInstance.index}: ${reason}. The old replica keeps serving; remaining replicas will still be rolled.`);
23885
+ }
23886
+ }
23887
+ } finally {
23888
+ if (stateProvider) stateProvider.dispose();
23889
+ }
23890
+ }
23446
23891
  async function bootOneTarget(boot, runState, stacks, options, discovery, skipPull, extraStateProviders, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning) {
23447
23892
  const candidate = pickCandidateStack(parseEcsTarget(boot.target).stackPattern, stacks);
23448
23893
  const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
@@ -23453,13 +23898,41 @@ async function bootOneTarget(boot, runState, stacks, options, discovery, skipPul
23453
23898
  }
23454
23899
  }
23455
23900
  async function runOneTarget(boot, runState, stacks, options, discovery, skipPull, stateProvider, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning) {
23901
+ const { service, runnerOpts } = await resolveServiceAndRunnerOpts(boot, stacks, options, discovery, skipPull, stateProvider, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning);
23902
+ return startEcsService(service, runnerOpts, runState);
23903
+ }
23904
+ /**
23905
+ * Resolve a {@link ServiceBoot} to its `(ResolvedEcsService, ServiceRunnerOptions)`
23906
+ * pair. Shared by the initial boot path (`runOneTarget`) and the
23907
+ * Phase 2 of issue #214 rolling-reload pathway (`reloadAllServices`).
23908
+ *
23909
+ * Walks the same steps the original `runOneTarget` body did:
23910
+ * 1. Build the per-target image-resolution context (resolves
23911
+ * asset / `Fn::Sub` / `--from-cfn-stack` overlays for image URIs).
23912
+ * 2. Resolve the ECS service target into a {@link ResolvedEcsService}.
23913
+ * 3. Apply the cross-stack env / secret resolver when the task
23914
+ * references `Fn::ImportValue` / `Fn::GetStackOutput` across
23915
+ * stacks.
23916
+ * 4. Resolve task-role credentials when `--assume-task-role` is set.
23917
+ * 5. Resolve `--env-vars` overrides.
23918
+ * 6. Compose {@link ServiceRunnerOptions} (including the shared
23919
+ * `discovery` + per-service `frontDoor` pools the rolling
23920
+ * reload depends on for atomic registry swaps).
23921
+ *
23922
+ * Side effects: logs the target descriptor + Service Connect /
23923
+ * ServiceRegistries banners ONLY on the initial boot. The reload
23924
+ * pathway calls this on every save; the banners would otherwise
23925
+ * spam the console once per save. Pass `quiet: true` to skip them.
23926
+ */
23927
+ async function resolveServiceAndRunnerOpts(boot, stacks, options, discovery, skipPull, stateProvider, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning, opts = {}) {
23456
23928
  const logger = getLogger();
23457
23929
  const target = boot.target;
23930
+ const quiet = opts.quiet === true;
23458
23931
  const imageContext = await buildEcsImageResolutionContext(target, stacks, options, stateProvider);
23459
23932
  const service = resolveEcsServiceTarget(target, stacks, imageContext, { suppressLoadBalancerWarning });
23460
- logger.info(`Target: ${service.stack.stackName}/${service.serviceLogicalId} (service=${service.serviceName}, desiredCount=${service.desiredCount}, task=${service.task.taskDefinitionLogicalId})`);
23461
- if (service.serviceConnect) logger.info(`Service Connect: namespace='${service.serviceConnect.namespaceName}', ${service.serviceConnect.services.length} service(s) registered for peer discovery.`);
23462
- if (service.serviceRegistries.length > 0) logger.info(`Cloud Map: ${service.serviceRegistries.length} ServiceRegistry binding(s).`);
23933
+ if (!quiet) logger.info(`Target: ${service.stack.stackName}/${service.serviceLogicalId} (service=${service.serviceName}, desiredCount=${service.desiredCount}, task=${service.task.taskDefinitionLogicalId})`);
23934
+ if (!quiet && service.serviceConnect) logger.info(`Service Connect: namespace='${service.serviceConnect.namespaceName}', ${service.serviceConnect.services.length} service(s) registered for peer discovery.`);
23935
+ if (!quiet && service.serviceRegistries.length > 0) logger.info(`Cloud Map: ${service.serviceRegistries.length} ServiceRegistry binding(s).`);
23463
23936
  const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === service.stack.stackName) ?? service.stack);
23464
23937
  if (stateProvider && taskNeeds.needsCrossStackResolver) {
23465
23938
  const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? service.stack.region ?? "us-east-1";
@@ -23508,13 +23981,16 @@ async function runOneTarget(boot, runState, stacks, options, discovery, skipPull
23508
23981
  containerPath: profileCredsFile.containerPath,
23509
23982
  profileName: profileCredsFile.profileName
23510
23983
  };
23511
- return startEcsService(service, {
23512
- maxTasks: options.maxTasks,
23513
- restartPolicy: options.restartPolicy,
23514
- taskOptions: taskOpts,
23515
- discovery,
23516
- ...frontDoorPools && frontDoorPools.length > 0 ? { frontDoor: { pools: frontDoorPools } } : {}
23517
- }, runState);
23984
+ return {
23985
+ service,
23986
+ runnerOpts: {
23987
+ maxTasks: options.maxTasks,
23988
+ restartPolicy: options.restartPolicy,
23989
+ taskOptions: taskOpts,
23990
+ discovery,
23991
+ ...frontDoorPools && frontDoorPools.length > 0 ? { frontDoor: { pools: frontDoorPools } } : {}
23992
+ }
23993
+ };
23518
23994
  }
23519
23995
  /**
23520
23996
  * Stand up one host-side reverse-proxy server PER LISTENER PORT from the
@@ -23913,6 +24389,75 @@ function addCommonEcsServiceOptions(cmd) {
23913
24389
  return cmd;
23914
24390
  }
23915
24391
 
24392
+ //#endregion
24393
+ //#region src/cli/commands/local-start-service.ts
24394
+ /**
24395
+ * `cdkl start-service` strategy — a pure ECS replica runner. It picks
24396
+ * `AWS::ECS::Service` targets and boots each with NO front-door (the ALB
24397
+ * front-door is its own command, `cdkl start-alb`). This keeps `start-service`
24398
+ * a leaf compute runner, symmetric with `invoke` / `run-task`.
24399
+ *
24400
+ * `supportsWatch: true` opts this strategy into the emulator's `--watch`
24401
+ * reload pathway (Phase 1 + Phase 2 of issue #214 — per-replica rolling
24402
+ * deploy: shadow boot under a bumped generation suffix, TCP-ready probe,
24403
+ * atomic Cloud Map / front-door swap, retire old). `start-alb`'s strategy
24404
+ * intentionally does NOT set this so a `--watch` flag never leaks into
24405
+ * the ALB-front-door path (Phase 3).
24406
+ */
24407
+ function serviceStrategy() {
24408
+ return {
24409
+ pickEntries: (stacks) => listTargets(stacks).ecsServices,
24410
+ pickerMessage: "Select one or more ECS services to run",
24411
+ pickerNoun: "ECS services",
24412
+ onMissing: () => new LocalStartServiceError(`${getEmbedConfig().cliName} start-service requires at least one <target>. Pass one or more service paths like 'Stack/Orders' 'Stack/Frontend', or run it in a TTY to pick interactively.`),
24413
+ resolveBoots: (_stacks, chosenTargets) => ({
24414
+ boots: chosenTargets.map((target) => ({ target })),
24415
+ warnings: []
24416
+ }),
24417
+ lbPortOverrides: {},
24418
+ supportsWatch: true
24419
+ };
24420
+ }
24421
+ /**
24422
+ * `cdkl start-service <Stack/Service>` — Phase 2 of #262. Spins up
24423
+ * `DesiredCount` task replicas locally (clamped by `--max-tasks`) using the
24424
+ * existing `ecs-task-runner` per replica. Long-running; ^C cleans every replica
24425
+ * + sidecar + shared network. Pure compute: to put a local ALB front-door in
24426
+ * front of an ALB-fronted service, use `cdkl start-alb`.
24427
+ */
24428
+ function createLocalStartServiceCommand(opts = {}) {
24429
+ setEmbedConfig(opts.embedConfig);
24430
+ const cmd = new Command("start-service").description(`Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as \`${getEmbedConfig().cliName} run-task\`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay. Omit <targets> in an interactive terminal to multi-select the services from a list. To put a local ALB front-door in front of an ALB-fronted service, use \`${getEmbedConfig().cliName} start-alb\` instead.`).argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run (omit to multi-select interactively in a TTY)").action(withErrorHandling(async (targets, options) => {
24431
+ await runEcsServiceEmulator(targets, options, serviceStrategy(), opts.extraStateProviders);
24432
+ }));
24433
+ addStartServiceSpecificOptions(cmd);
24434
+ return addCommonEcsServiceOptions(cmd);
24435
+ }
24436
+ /**
24437
+ * Register the option block that `cdkl start-service` adds on top of the
24438
+ * shared {@link addCommonEcsServiceOptions} ECS-service common block — the
24439
+ * flags that only make sense for a pure-compute service emulator (no front
24440
+ * door). Shared between `cdkl start-service` and any host CLI (e.g. cdkd's
24441
+ * `local start-service`) that wraps {@link runEcsServiceEmulator} with the
24442
+ * {@link serviceStrategy}, so adding or renaming a `start-service`-only flag
24443
+ * here propagates to every embedder without duplicate `.addOption(...)`
24444
+ * blocks.
24445
+ *
24446
+ * Calling order only affects `--help` presentation (Commander parses
24447
+ * insertion-order-independent). The host-CLI convention is host-specific
24448
+ * options first, then this helper, then {@link addCommonEcsServiceOptions}
24449
+ * — host flags / start-service flags / common flags grouped in three
24450
+ * `--help` clusters. Chainable: returns `cmd`.
24451
+ *
24452
+ * `--watch` is intentionally NOT in the shared
24453
+ * {@link addCommonEcsServiceOptions} block: `start-alb --watch` is not yet
24454
+ * implemented (Phase 3 of issue #214), and the shared block must not
24455
+ * advertise a flag one of its consumers does not honor.
24456
+ */
24457
+ function addStartServiceSpecificOptions(cmd) {
24458
+ return cmd.addOption(new Option("--host-port <containerPort=hostPort...>", "Publish a container port on a specific host port (e.g. 80=8080); repeatable. Default: host port == container port. Use this on macOS to map a privileged container port (< 1024) to a non-privileged host port and avoid the Docker Desktop admin-password prompt. (Single-replica services only — multi-replica services do not publish host ports.)")).addOption(new Option("--watch", "Hot-reload: re-synth + per-replica rolling deploy when the CDK source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). Each replica is rolled one at a time — boot a shadow under a bumped generation suffix, wait for its container port to accept a TCP connection, atomically swap Service-Connect / Cloud Map registrations, then retire the old container — so peer services see zero connection refusals across the reload even on multi-replica services. Off by default; existing replica(s) keep serving when synth fails mid-reload.").default(false));
24459
+ }
24460
+
23916
24461
  //#endregion
23917
24462
  //#region src/local/elb-front-door-resolver.ts
23918
24463
  /**
@@ -24858,5 +25403,5 @@ function addListSpecificOptions(cmd) {
24858
25403
  }
24859
25404
 
24860
25405
  //#endregion
24861
- export { pickResponseTemplate as $, substituteAgainstState as $t, createFileWatcher as A, AgentCoreResolutionError as An, MCP_PATH as At, resolveServiceIntegrationParameters as B, SUPPORTED_CODE_RUNTIMES as Bt, addInvokeAgentCoreSpecificOptions as C, pickRefLogicalId as Cn, isFunctionUrlOacFronted as Ct, createWatchPredicates as D, AGENTCORE_HTTP_PROTOCOL as Dn, A2A_PATH as Dt, createLocalStartApiCommand as E, AGENTCORE_AGUI_PROTOCOL as En, A2A_CONTAINER_PORT as Et, filterRoutesByApiIdentifiers as F, substituteImagePlaceholders as Fn, signAgentCoreInvocation as Ft, invokeRequestAuthorizer as G, addInvokeSpecificOptions as Gt, buildMethodArn as H, computeCodeImageTag as Ht, groupRoutesByServer as I, tryResolveImageFnJoin as In, AGENTCORE_SESSION_ID_HEADER as It, translateLambdaResponse as J, buildContainerImage as Jt, invokeTokenAuthorizer as K, createLocalInvokeCommand as Kt, readMtlsMaterialsFromDisk as L, LocalInvokeBuildError as Ln, invokeAgentCore as Lt, buildStageMap as M, resolveAgentCoreTarget as Mn, mcpInvokeOnce as Mt, availableApiIdentifiers as N, derivePseudoParametersFromRegion as Nn, parseSseForJsonRpc as Nt, resolveApiTargetSubset as O, AGENTCORE_MCP_PROTOCOL as On, a2aInvokeOnce as Ot, filterRoutesByApiIdentifier as P, formatStateRemedy as Pn, AGENTCORE_SIGV4_SERVICE as Pt, evaluateResponseParameters as Q, EcsTaskResolutionError as Qt, startApiServer as R, LocalStartServiceError as Rn, waitForAgentCorePing as Rt, createLocalRunTaskCommand as S, discoverRoutes as Sn, buildCorsConfigFromCloudFrontChain as St, addStartApiSpecificOptions as T, AGENTCORE_A2A_PROTOCOL as Tn, invokeAgentCoreWs as Tt, computeRequestIdentityHash as U, renderCodeDockerfile as Ut, defaultCredentialsLoader as V, buildAgentCoreCodeImage as Vt, evaluateCachedLambdaPolicy as W, toCmdArgv as Wt, buildHttpApiV2Event as X, resolveRuntimeFileExtension as Xt, applyAuthorizerOverlay as Y, resolveRuntimeCodeMountPath as Yt, buildRestV1Event as Z, resolveRuntimeImage as Zt, runEcsServiceEmulator as _, countTargets as _n, verifyJwtAuthorizer as _t, albStrategy as a, LocalStateSourceError as an, bufferToBody as at, getContainerNetworkIp as b, discoverWebSocketApisOrThrow as bn, applyCorsResponseHeaders as bt, resolveAlbTarget as c, rejectExplicitCfnStackWithMultipleStacks as cn, handleConnectionsRequest as ct, MAX_TASKS_SUBNET_RANGE_CAP as d, resolveCfnStackName as dn, buildDisconnectEvent as dt, substituteAgainstStateAsync as en, selectIntegrationResponse as et, addCommonEcsServiceOptions as f, CfnLocalStateProvider as fn, buildMessageEvent as ft, resolveSharedSidecarCredentials as g, resolveSingleTarget as gn, verifyCognitoJwt as gt, parseRestartPolicy as h, resolveWatchConfig as hn, createJwksCache as ht, addAlbSpecificOptions as i, materializeLayerFromArn as in, probeHostGatewaySupport as it, attachStageContext as j, pickAgentCoreCandidateStack as jn, MCP_PROTOCOL_VERSION as jt, createAuthorizerCache as k, AGENTCORE_RUNTIME_TYPE as kn, MCP_CONTAINER_PORT as kt, isApplicationLoadBalancer as l, resolveCfnFallbackRegion as ln, parseConnectionsPath as lt, parseMaxTasks as m, resolveSsmParameters as mn, buildJwksUrlFromIssuer as mt, createLocalListCommand as n, substituteEnvVarsFromStateAsync as nn, VtlEvaluationError as nt, createLocalStartAlbCommand as o, createLocalStateProvider as on, ConnectionRegistry as ot, buildEcsImageResolutionContext as p, collectSsmParameterRefs as pn, buildCognitoJwksUrl as pt, matchRoute as q, architectureToPlatform as qt, formatTargetListing as r, resolveEnvVars as rn, HOST_GATEWAY_MIN_VERSION as rt, parseLbPortOverrides as s, isCfnFlagPresent as sn, buildMgmtEndpointEnvUrl as st, addListSpecificOptions as t, substituteEnvVarsFromState as tn, tryParseStatus as tt, resolveAlbFrontDoor as u, resolveCfnRegion as un, buildConnectEvent as ut, buildCloudMapIndex as v, listTargets as vn, verifyJwtViaDiscovery as vt, createLocalInvokeAgentCoreCommand as w, resolveLambdaArnIntrinsic as wn, matchPreflight as wt, addRunTaskSpecificOptions as x, parseSelectionExpressionPath as xn, buildCorsConfigByApiId as xt, CloudMapRegistry as y, discoverWebSocketApis as yn, attachAuthorizers as yt, resolveSelectionExpression as z, withErrorHandling as zn, downloadAndExtractS3Bundle as zt };
24862
- //# sourceMappingURL=local-list-CJj6-Ska.js.map
25406
+ export { buildHttpApiV2Event as $, resolveRuntimeFileExtension as $t, createWatchPredicates as A, AGENTCORE_HTTP_PROTOCOL as An, A2A_PATH as At, readMtlsMaterialsFromDisk as B, LocalInvokeBuildError as Bn, invokeAgentCore as Bt, getContainerNetworkIp as C, discoverWebSocketApisOrThrow as Cn, applyCorsResponseHeaders as Ct, createLocalInvokeAgentCoreCommand as D, resolveLambdaArnIntrinsic as Dn, matchPreflight as Dt, addInvokeAgentCoreSpecificOptions as E, pickRefLogicalId as En, isFunctionUrlOacFronted as Et, buildStageMap as F, resolveAgentCoreTarget as Fn, mcpInvokeOnce as Ft, buildMethodArn as G, computeCodeImageTag as Gt, resolveSelectionExpression as H, downloadAndExtractS3Bundle as Ht, availableApiIdentifiers as I, derivePseudoParametersFromRegion as In, parseSseForJsonRpc as It, invokeRequestAuthorizer as J, addInvokeSpecificOptions as Jt, computeRequestIdentityHash as K, renderCodeDockerfile as Kt, filterRoutesByApiIdentifier as L, formatStateRemedy as Ln, AGENTCORE_SIGV4_SERVICE as Lt, createAuthorizerCache as M, AGENTCORE_RUNTIME_TYPE as Mn, MCP_CONTAINER_PORT as Mt, createFileWatcher as N, AgentCoreResolutionError as Nn, MCP_PATH as Nt, addStartApiSpecificOptions as O, AGENTCORE_A2A_PROTOCOL as On, invokeAgentCoreWs as Ot, attachStageContext as P, pickAgentCoreCandidateStack as Pn, MCP_PROTOCOL_VERSION as Pt, applyAuthorizerOverlay as Q, resolveRuntimeCodeMountPath as Qt, filterRoutesByApiIdentifiers as R, substituteImagePlaceholders as Rn, signAgentCoreInvocation as Rt, CloudMapRegistry as S, discoverWebSocketApis as Sn, attachAuthorizers as St, createLocalRunTaskCommand as T, discoverRoutes as Tn, buildCorsConfigFromCloudFrontChain as Tt, resolveServiceIntegrationParameters as U, SUPPORTED_CODE_RUNTIMES as Ut, startApiServer as V, waitForAgentCorePing as Vt, defaultCredentialsLoader as W, buildAgentCoreCodeImage as Wt, matchRoute as X, architectureToPlatform as Xt, invokeTokenAuthorizer as Y, createLocalInvokeCommand as Yt, translateLambdaResponse as Z, buildContainerImage as Zt, parseMaxTasks as _, resolveSsmParameters as _n, buildJwksUrlFromIssuer as _t, albStrategy as a, substituteEnvVarsFromStateAsync as an, VtlEvaluationError as at, runEcsServiceEmulator as b, countTargets as bn, verifyJwtAuthorizer as bt, resolveAlbTarget as c, LocalStateSourceError as cn, bufferToBody as ct, addStartServiceSpecificOptions as d, rejectExplicitCfnStackWithMultipleStacks as dn, handleConnectionsRequest as dt, resolveRuntimeImage as en, buildRestV1Event as et, createLocalStartServiceCommand as f, resolveCfnFallbackRegion as fn, parseConnectionsPath as ft, buildEcsImageResolutionContext as g, collectSsmParameterRefs as gn, buildCognitoJwksUrl as gt, addCommonEcsServiceOptions as h, CfnLocalStateProvider as hn, buildMessageEvent as ht, addAlbSpecificOptions as i, substituteEnvVarsFromState as in, tryParseStatus as it, resolveApiTargetSubset as j, AGENTCORE_MCP_PROTOCOL as jn, a2aInvokeOnce as jt, createLocalStartApiCommand as k, AGENTCORE_AGUI_PROTOCOL as kn, A2A_CONTAINER_PORT as kt, isApplicationLoadBalancer as l, createLocalStateProvider as ln, ConnectionRegistry as lt, MAX_TASKS_SUBNET_RANGE_CAP as m, resolveCfnStackName as mn, buildDisconnectEvent as mt, createLocalListCommand as n, substituteAgainstState as nn, pickResponseTemplate as nt, createLocalStartAlbCommand as o, resolveEnvVars as on, HOST_GATEWAY_MIN_VERSION as ot, serviceStrategy as p, resolveCfnRegion as pn, buildConnectEvent as pt, evaluateCachedLambdaPolicy as q, toCmdArgv as qt, formatTargetListing as r, substituteAgainstStateAsync as rn, selectIntegrationResponse as rt, parseLbPortOverrides as s, materializeLayerFromArn as sn, probeHostGatewaySupport as st, addListSpecificOptions as t, EcsTaskResolutionError as tn, evaluateResponseParameters as tt, resolveAlbFrontDoor as u, isCfnFlagPresent as un, buildMgmtEndpointEnvUrl as ut, parseRestartPolicy as v, resolveWatchConfig as vn, createJwksCache as vt, addRunTaskSpecificOptions as w, parseSelectionExpressionPath as wn, buildCorsConfigByApiId as wt, buildCloudMapIndex as x, listTargets as xn, verifyJwtViaDiscovery as xt, resolveSharedSidecarCredentials as y, resolveSingleTarget as yn, verifyCognitoJwt as yt, groupRoutesByServer as z, tryResolveImageFnJoin as zn, AGENTCORE_SESSION_ID_HEADER as zt };
25407
+ //# sourceMappingURL=local-list-faPgnDlc.js.map