cdk-local 0.153.2 → 0.154.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.
@@ -1,11 +1,13 @@
1
- import { C as sanitizeServiceExceptionMessage, S as isAwsServiceException, _ as resolveAssemblyPath, a as isDockerClientEnvKey, b as describeCredentialLoadFailure, c as runDockerStreaming, d as absoluteAssemblyPathEscape, f as assemblyPathEscape, g as renderAssemblyPathEscape, h as namesTheSameDirectory, i as getDockerCmd, l as spawnStreaming, m as isPasteableCommandArg, n as finchSecretArgvRefusal, o as isMalformedEnvKey, p as displayUntrustedValue, r as formatDockerLoginError, s as runDockerForeground, u as warnFinchArgvExposure, v as clampErrorName, w as stringifyThrown, x as flattenToOneLine, y as describeAwsFailureForWarn } from "./docker-cmd-BSdu45mc.js";
2
- import { a as setEmbedConfig, r as getEmbedConfig, t as getLogger } from "./logger-CwFXOD1w.js";
1
+ import { C as sanitizeServiceExceptionMessage, S as isAwsServiceException, _ as resolveAssemblyPath, a as isDockerClientEnvKey, b as describeCredentialLoadFailure, c as runDockerStreaming, d as absoluteAssemblyPathEscape, f as assemblyPathEscape, g as renderAssemblyPathEscape, h as namesTheSameDirectory, i as getDockerCmd, l as spawnStreaming, m as isPasteableCommandArg, n as finchSecretArgvRefusal, o as isMalformedEnvKey, p as displayUntrustedValue, r as formatDockerLoginError, s as runDockerForeground, u as warnFinchArgvExposure, v as clampErrorName, w as stringifyThrown, x as flattenToOneLine, y as describeAwsFailureForWarn } from "./docker-cmd-CoZdbeos.js";
2
+ import { a as displayIdent, c as getEmbedConfig, i as STACK_REF_MAX_CODE_POINTS, o as displayStackName, s as displayToolOutput, t as getLogger, u as setEmbedConfig } from "./logger-Co5XVwco.js";
3
3
  import { chmodSync, cpSync, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { homedir, tmpdir } from "node:os";
5
5
  import * as path$1 from "node:path";
6
6
  import path, { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
7
- import { Command, Option } from "commander";
7
+ import { Command, InvalidArgumentError, Option } from "commander";
8
8
  import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
9
+ import * as nodeCrypto from "node:crypto";
10
+ import { X509Certificate, createHash, createHmac, createPublicKey, createVerify, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
9
11
  import { Agent, createServer, request } from "node:http";
10
12
  import { Agent as Agent$1, createServer as createServer$1, request as request$1 } from "node:https";
11
13
  import { brotliDecompressSync, gunzipSync, inflateSync } from "node:zlib";
@@ -24,12 +26,11 @@ import { GetFunctionConfigurationCommand, LambdaClient } from "@aws-sdk/client-l
24
26
  import { BedrockAgentCoreControlClient, GetAgentRuntimeCommand } from "@aws-sdk/client-bedrock-agentcore-control";
25
27
  import { execFile, spawn } from "node:child_process";
26
28
  import { connect, createServer as createServer$2 } from "node:net";
29
+ import { StringDecoder } from "node:string_decoder";
27
30
  import { promisify } from "node:util";
28
- import * as nodeCrypto from "node:crypto";
29
- import { X509Certificate, createHash, createHmac, createPublicKey, createVerify, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
30
31
  import { ECRClient, GetAuthorizationTokenCommand } from "@aws-sdk/client-ecr";
31
32
  import { readFile } from "fs/promises";
32
- import { join as join$1 } from "path";
33
+ import { basename as basename$1, dirname as dirname$1, join as join$1 } from "path";
33
34
  import { WebSocket, WebSocketServer } from "ws";
34
35
  import { Readable } from "node:stream";
35
36
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -674,14 +675,14 @@ function parseAssumeRoleToken(raw, previous) {
674
675
  const eqIndex = raw.indexOf("=");
675
676
  if (eqIndex === -1) {
676
677
  const bare = raw.trim();
677
- if (!isIamRoleArn(bare)) throw new Error(`Invalid --assume-role value "${raw}": expected an IAM role ARN like arn:aws:iam::123456789012:role/MyRole, or LogicalId=<arn>.`);
678
+ if (!isIamRoleArn(bare)) throw new InvalidArgumentError(`Invalid --assume-role value "${raw}": expected an IAM role ARN like arn:aws:iam::123456789012:role/MyRole, or LogicalId=<arn>.`);
678
679
  acc.globalArn = bare;
679
680
  return acc;
680
681
  }
681
682
  const logicalId = raw.substring(0, eqIndex).trim();
682
683
  const arn = raw.substring(eqIndex + 1).trim();
683
- if (!/^[A-Za-z][A-Za-z0-9]*$/.test(logicalId)) throw new Error(`Invalid --assume-role value "${raw}": left-hand side "${logicalId}" must be a CloudFormation logical ID (alphanumeric, leading letter).`);
684
- if (!isIamRoleArn(arn)) throw new Error(`Invalid --assume-role value "${raw}": right-hand side "${arn}" must be an IAM role ARN like arn:aws:iam::123456789012:role/MyRole.`);
684
+ if (!/^[A-Za-z][A-Za-z0-9]*$/.test(logicalId)) throw new InvalidArgumentError(`Invalid --assume-role value "${raw}": left-hand side "${logicalId}" must be a CloudFormation logical ID (alphanumeric, leading letter).`);
685
+ if (!isIamRoleArn(arn)) throw new InvalidArgumentError(`Invalid --assume-role value "${raw}": right-hand side "${arn}" must be an IAM role ARN like arn:aws:iam::123456789012:role/MyRole.`);
685
686
  acc.perLambda[logicalId] = arn;
686
687
  return acc;
687
688
  }
@@ -1160,23 +1161,106 @@ function resolveCdkPathToLogicalIds(input, index) {
1160
1161
  cdkPath
1161
1162
  }));
1162
1163
  }
1164
+ function stackForConstructPath(path, stacks) {
1165
+ let best;
1166
+ let bestRank = -1;
1167
+ for (const s of stacks) {
1168
+ const names = [[s.displayName, 1], [s.stackName, 0]];
1169
+ for (const [name, preference] of names) {
1170
+ if (typeof name !== "string" || name.length === 0) continue;
1171
+ if (!path.startsWith(`${name}/`)) continue;
1172
+ const rank = name.length * 2 + preference;
1173
+ if (rank > bestRank) {
1174
+ best = s;
1175
+ bestRank = rank;
1176
+ }
1177
+ }
1178
+ }
1179
+ return best;
1180
+ }
1181
+ function stackForPathFormTarget(parsed, target, stacks) {
1182
+ if (!parsed.isPath || parsed.pathOrId !== target) return void 0;
1183
+ return stackForConstructPath(parsed.pathOrId, stacks);
1184
+ }
1163
1185
 
1164
1186
  //#endregion
1165
1187
  //#region src/cli/stack-matcher.ts
1166
1188
  function matchStacks(stacks, patterns) {
1167
1189
  if (patterns.length === 0) return [];
1168
- const seen = /* @__PURE__ */ new Set();
1169
- const result = [];
1170
- for (const stack of stacks) if (patterns.some((pattern) => stackMatchesPattern(stack, pattern)) && !seen.has(stack.stackName)) {
1171
- seen.add(stack.stackName);
1172
- result.push(stack);
1190
+ const selected = /* @__PURE__ */ new Set();
1191
+ for (const pattern of patterns) for (const stack of stacksForPattern(stacks, pattern)) selected.add(stack);
1192
+ return stacks.filter((s) => selected.has(s));
1193
+ }
1194
+ function stacksForPattern(stacks, pattern) {
1195
+ const byId = stacks.filter((s) => pathGlobMatches(pattern, s.displayName ?? s.stackName));
1196
+ return byId.length > 0 ? byId : stacks.filter((s) => s.stackName === pattern);
1197
+ }
1198
+ function describeStack(stack) {
1199
+ const name = displayUntrustedValue(stack.stackName);
1200
+ if (stack.displayName && stack.displayName !== stack.stackName) return `${name} (${displayUntrustedValue(stack.displayName)})`;
1201
+ return name;
1202
+ }
1203
+ function describeStacks(stacks) {
1204
+ return stacks.map(describeStack).join(", ");
1205
+ }
1206
+ function selectTargetStack(parsed, target, stacks) {
1207
+ if (parsed.stackPattern === null) return stacks.length === 1 ? {
1208
+ kind: "found",
1209
+ stack: stacks[0]
1210
+ } : { kind: "no-prefix" };
1211
+ const byPrefix = stackForPathFormTarget(parsed, target, stacks);
1212
+ if (byPrefix) return {
1213
+ kind: "found",
1214
+ stack: byPrefix
1215
+ };
1216
+ const matched = matchStacks([...stacks], [parsed.stackPattern]);
1217
+ if (matched.length === 1) return {
1218
+ kind: "found",
1219
+ stack: matched[0]
1220
+ };
1221
+ if (matched.length === 0) return {
1222
+ kind: "not-found",
1223
+ pattern: parsed.stackPattern
1224
+ };
1225
+ return {
1226
+ kind: "ambiguous",
1227
+ pattern: parsed.stackPattern,
1228
+ matched
1229
+ };
1230
+ }
1231
+ function candidateTargetStack(parsed, target, stacks) {
1232
+ const selection = selectTargetStack(parsed, target, stacks);
1233
+ return selection.kind === "found" ? selection.stack : void 0;
1234
+ }
1235
+ function globMatches(pattern, subject) {
1236
+ const segments = pattern.split("*");
1237
+ if (segments.length === 1) return pattern === subject;
1238
+ const head = segments[0];
1239
+ const tail = segments[segments.length - 1];
1240
+ if (subject.length < head.length + tail.length) return false;
1241
+ if (!subject.startsWith(head) || !subject.endsWith(tail)) return false;
1242
+ const end = subject.length - tail.length;
1243
+ let cursor = head.length;
1244
+ for (let i = 1; i < segments.length - 1; i++) {
1245
+ const segment = segments[i];
1246
+ if (segment === "") continue;
1247
+ const at = subject.indexOf(segment, cursor);
1248
+ if (at === -1 || at + segment.length > end) return false;
1249
+ cursor = at + segment.length;
1173
1250
  }
1174
- return result;
1251
+ return true;
1175
1252
  }
1176
- function stackMatchesPattern(stack, pattern) {
1177
- const target = pattern.includes("/") ? stack.displayName ?? stack.stackName : stack.stackName;
1178
- if (pattern.includes("*")) return new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(target);
1179
- return target === pattern;
1253
+ function pathGlobMatches(pattern, subject) {
1254
+ const p = pattern.split("/");
1255
+ const s = subject.split("/");
1256
+ let can = s.map(() => false).concat(true);
1257
+ for (let i = p.length - 1; i >= 0; i--) {
1258
+ const next = new Array(s.length + 1).fill(false);
1259
+ if (p[i] === "**") for (let j = s.length; j >= 0; j--) next[j] = can[j] || j < s.length && next[j + 1];
1260
+ else for (let j = 0; j < s.length; j++) next[j] = can[j + 1] && globMatches(p[i], s[j]);
1261
+ can = next;
1262
+ }
1263
+ return can[0];
1180
1264
  }
1181
1265
 
1182
1266
  //#endregion
@@ -1472,7 +1556,7 @@ function resolveLambdaArchitecture(props, logicalId) {
1472
1556
  const first = arches[0];
1473
1557
  if (first === "arm64") return "arm64";
1474
1558
  if (first === "x86_64") return "x86_64";
1475
- throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has unsupported Architectures value '${String(first)}'. ${getEmbedConfig().cliName} supports x86_64 and arm64.`);
1559
+ throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has unsupported Architectures value ${displayIdent(String(first))}. ${getEmbedConfig().cliName} supports x86_64 and arm64.`);
1476
1560
  }
1477
1561
  return "x86_64";
1478
1562
  }
@@ -1511,7 +1595,7 @@ function parseTarget(target) {
1511
1595
  function resolveLambdaTarget(target, stacks) {
1512
1596
  if (stacks.length === 0) throw new LocalInvokeResolutionError("No stacks found in the synthesized assembly.");
1513
1597
  const parsed = parseTarget(target);
1514
- const stack = pickStack$5(parsed, stacks);
1598
+ const stack = pickStack$5(parsed, stacks, target);
1515
1599
  const template = stack.template;
1516
1600
  const resources = template.Resources ?? {};
1517
1601
  let match;
@@ -1519,7 +1603,7 @@ function resolveLambdaTarget(target, stacks) {
1519
1603
  const index = buildCdkPathIndex(template);
1520
1604
  const lambdaMatches = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId }) => resources[logicalId]?.Type === "AWS::Lambda::Function");
1521
1605
  if (lambdaMatches.length === 0) throw notFoundError$3(target, stack, resources);
1522
- if (lambdaMatches.length > 1) throw new LocalInvokeResolutionError(`Target '${target}' matches ${lambdaMatches.length} Lambda functions in ${stack.stackName}: ` + lambdaMatches.map((m) => m.logicalId).join(", ") + ". Refine the path or use the stack:LogicalId form.");
1606
+ if (lambdaMatches.length > 1) throw new LocalInvokeResolutionError(`Target '${target}' matches ${lambdaMatches.length} Lambda functions in ${displayStackName(stack.stackName)}: ` + lambdaMatches.map((m) => displayIdent(m.logicalId, { listMember: true })).join(", ") + ". Refine the path or use the stack:LogicalId form.");
1523
1607
  const m = lambdaMatches[0];
1524
1608
  match = {
1525
1609
  logicalId: m.logicalId,
@@ -1535,20 +1619,19 @@ function resolveLambdaTarget(target, stacks) {
1535
1619
  }
1536
1620
  const { logicalId, resource } = match;
1537
1621
  if (resource.Type !== "AWS::Lambda::Function") {
1538
- if (resource.Type.startsWith("Custom::")) throw new LocalInvokeResolutionError(`Resource '${logicalId}' in ${stack.stackName} is a Custom Resource (${resource.Type}), not a Lambda function. Custom Resources are invoked by the deploy framework, not by users. If you want to test the underlying handler, target the ServiceToken Lambda directly.`);
1539
- throw new LocalInvokeResolutionError(`Resource '${logicalId}' in ${stack.stackName} is ${resource.Type}, not a Lambda function. ${getEmbedConfig().cliName} only invokes AWS::Lambda::Function resources.`);
1622
+ if (resource.Type.startsWith("Custom::")) throw new LocalInvokeResolutionError(`Resource ${displayIdent(logicalId)} in ${displayStackName(stack.stackName)} is a Custom Resource (${displayIdent(resource.Type)}), not a Lambda function. Custom Resources are invoked by the deploy framework, not by users. If you want to test the underlying handler, target the ServiceToken Lambda directly.`);
1623
+ throw new LocalInvokeResolutionError(`Resource ${displayIdent(logicalId)} in ${displayStackName(stack.stackName)} is ${displayIdent(resource.Type)}, not a Lambda function. ${getEmbedConfig().cliName} only invokes AWS::Lambda::Function resources.`);
1540
1624
  }
1541
1625
  return extractLambdaProperties(stack, logicalId, resource, resources);
1542
1626
  }
1543
- function pickStack$5(parsed, stacks) {
1544
- if (parsed.stackPattern === null) {
1545
- if (stacks.length === 1) return stacks[0];
1546
- throw new LocalInvokeResolutionError(`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(", ")}.`);
1627
+ function pickStack$5(parsed, stacks, target) {
1628
+ const selection = selectTargetStack(parsed, target, stacks);
1629
+ switch (selection.kind) {
1630
+ case "found": return selection.stack;
1631
+ case "no-prefix": throw new LocalInvokeResolutionError(`Multiple stacks in app, target '${parsed.pathOrId}' is missing a stack prefix. Use 'StackName:${parsed.pathOrId}' or 'StackName/...' (path form). Available stacks: ${describeStacks(stacks)}.`);
1632
+ case "not-found": throw new LocalInvokeResolutionError(`Stack '${selection.pattern}' not found. Available stacks: ${describeStacks(stacks)}.`);
1633
+ case "ambiguous": throw new LocalInvokeResolutionError(`Stack pattern '${selection.pattern}' matched ${selection.matched.length} stacks: ` + describeStacks(selection.matched) + ". Use a more specific pattern, or the stack's display path (path form).");
1547
1634
  }
1548
- const matched = matchStacks(stacks, [parsed.stackPattern]);
1549
- if (matched.length === 0) throw new LocalInvokeResolutionError(`Stack '${parsed.stackPattern}' not found. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
1550
- if (matched.length > 1) throw new LocalInvokeResolutionError(`Stack pattern '${parsed.stackPattern}' matched ${matched.length} stacks: ` + matched.map((s) => s.stackName).join(", ") + ". Use a more specific pattern.");
1551
- return matched[0];
1552
1635
  }
1553
1636
  function extractLambdaProperties(stack, logicalId, resource, resources) {
1554
1637
  const props = resource.Properties ?? {};
@@ -1569,8 +1652,8 @@ function extractLambdaProperties(stack, logicalId, resource, resources) {
1569
1652
  });
1570
1653
  const runtime = typeof props["Runtime"] === "string" ? props["Runtime"] : "";
1571
1654
  const handler = typeof props["Handler"] === "string" ? props["Handler"] : "";
1572
- if (!runtime) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has no Runtime property and no Code.ImageUri. ${getEmbedConfig().productName} cannot tell if this is a ZIP or a container Lambda.`);
1573
- if (!handler) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has no Handler property.`);
1655
+ if (!runtime) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has no Runtime property and no Code.ImageUri. ${getEmbedConfig().productName} cannot tell if this is a ZIP or a container Lambda.`);
1656
+ if (!handler) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has no Handler property.`);
1574
1657
  const inlineCode = typeof code["ZipFile"] === "string" ? code["ZipFile"] : void 0;
1575
1658
  let codePath = null;
1576
1659
  if (!inlineCode) codePath = resolveAssetCodePath$1(stack, logicalId, resource, { allowZip: true });
@@ -1599,7 +1682,7 @@ function extractEphemeralStorageMb(props, logicalId) {
1599
1682
  const size = raw["Size"];
1600
1683
  if (typeof size !== "number") return;
1601
1684
  if (!Number.isFinite(size) || size < 1) return void 0;
1602
- if (size > 10240) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has Properties.EphemeralStorage.Size = ${size} MiB, which exceeds the AWS limit of 10240 MiB. AWS would reject the function at deploy time; cap the value to <= 10240 (10 GiB) and retry.`);
1685
+ if (size > 10240) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has Properties.EphemeralStorage.Size = ${size} MiB, which exceeds the AWS limit of 10240 MiB. AWS would reject the function at deploy time; cap the value to <= 10240 (10 GiB) and retry.`);
1603
1686
  return Math.floor(size);
1604
1687
  }
1605
1688
  function extractImageUri$1(value, logicalId, stackName, resources, region) {
@@ -1613,10 +1696,10 @@ function extractImageUri$1(value, logicalId, stackName, resources, region) {
1613
1696
  const pseudoParameters = derivePseudoParametersFromRegion(region);
1614
1697
  const joinResolved = tryResolveImageFnJoin(value, resources, pseudoParameters ? { pseudoParameters } : void 0);
1615
1698
  if (joinResolved.kind === "resolved") return joinResolved.uri;
1616
- if (joinResolved.kind === "needs-state") throw new LocalInvokeResolutionError(`Lambda '${logicalId}' in ${stackName} references same-stack ECR repository '${joinResolved.repoLogicalId}' via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — deploy the stack first (so ${getEmbedConfig().productName} records the repository physical id), rebuild via lambda.DockerImageCode.fromImageAsset, or pin a public image.`);
1617
- if (joinResolved.kind === "unsupported-join") throw new LocalInvokeResolutionError(`Lambda '${logicalId}' in ${stackName} has an unsupported Fn::Join Code.ImageUri shape: ${joinResolved.reason}. ${getEmbedConfig().cliName} recognizes the canonical CDK 2.x lambda.DockerImageCode.fromEcr Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
1699
+ if (joinResolved.kind === "needs-state") throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} in ${displayStackName(stackName)} references same-stack ECR repository ${displayIdent(joinResolved.repoLogicalId)} via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — deploy the stack first (so ${getEmbedConfig().productName} records the repository physical id), rebuild via lambda.DockerImageCode.fromImageAsset, or pin a public image.`);
1700
+ if (joinResolved.kind === "unsupported-join") throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} in ${displayStackName(stackName)} has an unsupported Fn::Join Code.ImageUri shape: ${joinResolved.reason}. ${getEmbedConfig().cliName} recognizes the canonical CDK 2.x lambda.DockerImageCode.fromEcr Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
1618
1701
  const accountIdHint = pseudoParameters ? ` (likely \${AWS::AccountId}, which ${getEmbedConfig().productName} cannot derive without --from-state or STS)` : ` (${getEmbedConfig().productName} could not derive AWS pseudo parameters because stack.region was undefined)`;
1619
- throw new LocalInvokeResolutionError(`Lambda '${logicalId}' in ${stackName} has an Fn::Join Code.ImageUri that ${getEmbedConfig().cliName} cannot resolve${accountIdHint}. Workarounds: deploy first and run with --from-state, or pin a fully-literal public image URI.`);
1702
+ throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} in ${displayStackName(stackName)} has an Fn::Join Code.ImageUri that ${getEmbedConfig().cliName} cannot resolve${accountIdHint}. Workarounds: deploy first and run with --from-state, or pin a fully-literal public image URI.`);
1620
1703
  }
1621
1704
  }
1622
1705
  }
@@ -1804,13 +1887,13 @@ function parseZipUnixModes(bytes) {
1804
1887
  function resolveSafeZipEntryPath(root, entry) {
1805
1888
  const dest = normalize(join(root, entry));
1806
1889
  const rootWithSep = root.endsWith(sep) ? root : root + sep;
1807
- if (dest !== root && !dest.startsWith(rootWithSep)) throw new LocalInvokeResolutionError(`Refusing to extract a Lambda ZIP asset entry that escapes the target dir: '${entry}'.`);
1890
+ if (dest !== root && !dest.startsWith(rootWithSep)) throw new LocalInvokeResolutionError(`Refusing to extract a Lambda ZIP asset entry that escapes the target dir: ${displayUntrustedValue(entry)}.`);
1808
1891
  return dest;
1809
1892
  }
1810
1893
  function resolveLambdaLayers(stack, logicalId, props) {
1811
1894
  const layers = props["Layers"];
1812
1895
  if (layers === void 0) return [];
1813
- if (!Array.isArray(layers)) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has a non-array Layers property. Expected an array of LayerVersion references.`);
1896
+ if (!Array.isArray(layers)) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has a non-array Layers property. Expected an array of LayerVersion references.`);
1814
1897
  if (layers.length === 0) return [];
1815
1898
  const resources = stack.template.Resources ?? {};
1816
1899
  const out = [];
@@ -1818,7 +1901,7 @@ function resolveLambdaLayers(stack, logicalId, props) {
1818
1901
  const entry = layers[i];
1819
1902
  if (typeof entry === "string") {
1820
1903
  const parsed = parseLayerVersionArn(entry);
1821
- if (!parsed) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has a Layers entry [${i}] ${getEmbedConfig().productName} cannot resolve locally: literal string '${entry}'. Expected a same-stack Ref / Fn::GetAtt to an AWS::Lambda::LayerVersion OR a literal layer-version ARN of the form arn:<partition>:lambda:<region>:<account>:layer:<name>:<version>, whose partition agrees with its region.`);
1904
+ if (!parsed) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has a Layers entry [${i}] ${getEmbedConfig().productName} cannot resolve locally: literal string ${displayUntrustedValue(entry)}. Expected a same-stack Ref / Fn::GetAtt to an AWS::Lambda::LayerVersion OR a literal layer-version ARN of the form arn:<partition>:lambda:<region>:<account>:layer:<name>:<version>, whose partition agrees with its region.`);
1822
1905
  out.push({
1823
1906
  kind: "arn",
1824
1907
  logicalId: parsed.arn,
@@ -1827,10 +1910,10 @@ function resolveLambdaLayers(stack, logicalId, props) {
1827
1910
  continue;
1828
1911
  }
1829
1912
  const layerLogicalId = pickLayerLogicalId(entry);
1830
- if (!layerLogicalId) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' has a Layers entry [${i}] ${getEmbedConfig().productName} cannot resolve locally: ${describeLayerEntry(entry)}. Expected a same-stack Ref / Fn::GetAtt to an AWS::Lambda::LayerVersion OR a literal layer-version ARN of the form arn:<partition>:lambda:<region>:<account>:layer:<name>:<version>, whose partition agrees with its region.`);
1913
+ if (!layerLogicalId) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} has a Layers entry [${i}] ${getEmbedConfig().productName} cannot resolve locally: ${describeLayerEntry(entry)}. Expected a same-stack Ref / Fn::GetAtt to an AWS::Lambda::LayerVersion OR a literal layer-version ARN of the form arn:<partition>:lambda:<region>:<account>:layer:<name>:<version>, whose partition agrees with its region.`);
1831
1914
  const layerResource = resources[layerLogicalId];
1832
- if (!layerResource) throw new LocalInvokeResolutionError(`Lambda '${logicalId}' Layers entry [${i}] references '${layerLogicalId}', but no resource with that logical ID exists in stack '${stack.stackName}'.`);
1833
- if (layerResource.Type !== "AWS::Lambda::LayerVersion") throw new LocalInvokeResolutionError(`Lambda '${logicalId}' Layers entry [${i}] references '${layerLogicalId}' (${layerResource.Type}), which is not an AWS::Lambda::LayerVersion.`);
1915
+ if (!layerResource) throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} Layers entry [${i}] references ${displayIdent(layerLogicalId)}, but no resource with that logical ID exists in stack ${displayStackName(stack.stackName)}.`);
1916
+ if (layerResource.Type !== "AWS::Lambda::LayerVersion") throw new LocalInvokeResolutionError(`Lambda ${displayIdent(logicalId)} Layers entry [${i}] references ${displayIdent(layerLogicalId)} (${displayIdent(layerResource.Type)}), which is not an AWS::Lambda::LayerVersion.`);
1834
1917
  const assetPath = resolveAssetCodePath$1(stack, layerLogicalId, layerResource);
1835
1918
  out.push({
1836
1919
  kind: "asset",
@@ -1864,7 +1947,7 @@ function pickLayerLogicalId(entry) {
1864
1947
  }
1865
1948
  }
1866
1949
  function describeLayerEntry(entry) {
1867
- if (typeof entry === "string") return `literal ARN '${entry}'`;
1950
+ if (typeof entry === "string") return `literal ARN ${displayUntrustedValue(entry)}`;
1868
1951
  if (entry === null) return "null";
1869
1952
  if (typeof entry !== "object") return stringifyValue(entry);
1870
1953
  try {
@@ -1885,12 +1968,13 @@ function notFoundError$3(target, stack, resources) {
1885
1968
  logicalId
1886
1969
  });
1887
1970
  }
1888
- let msg = `target '${target}' did not match any Lambda function in ${stack.stackName}.\n\n`;
1889
- if (lambdas.length === 0) msg += `Stack ${stack.stackName} has no Lambda functions.`;
1971
+ let msg = `target '${target}' did not match any Lambda function in ${displayStackName(stack.stackName)}.\n\n`;
1972
+ if (lambdas.length === 0) msg += `Stack ${displayStackName(stack.stackName)} has no Lambda functions.`;
1890
1973
  else {
1891
- const width = Math.max(...lambdas.map((l) => l.displayPath.length));
1892
- msg += `Available Lambda functions in ${stack.stackName}:\n`;
1893
- for (const l of lambdas) msg += ` ${l.displayPath.padEnd(width)} (${l.logicalId})\n`;
1974
+ const rendered = lambdas.map((l) => displayStackName(l.displayPath));
1975
+ const width = Math.max(...rendered.map((p) => p.length));
1976
+ msg += `Available Lambda functions in ${displayStackName(stack.stackName)}:\n`;
1977
+ for (const [idx, l] of lambdas.entries()) msg += ` ${rendered[idx].padEnd(width)} (${displayIdent(l.logicalId)})\n`;
1894
1978
  }
1895
1979
  return new LocalInvokeResolutionError(msg.trimEnd());
1896
1980
  }
@@ -1918,27 +2002,23 @@ var AgentCoreResolutionError = class AgentCoreResolutionError extends Error {
1918
2002
  function resolveAgentCoreTarget(target, stacks, imageContext) {
1919
2003
  if (stacks.length === 0) throw new AgentCoreResolutionError("No stacks found in the synthesized assembly.");
1920
2004
  const parsed = parseTarget(target);
1921
- const stack = pickStack$4(parsed, stacks);
2005
+ const stack = pickStack$4(parsed, stacks, target);
1922
2006
  const resources = stack.template.Resources ?? {};
1923
2007
  const { logicalId, resource } = matchRuntime(parsed, target, stack, resources);
1924
- if (resource.Type !== "AWS::BedrockAgentCore::Runtime") throw new AgentCoreResolutionError(`Resource '${logicalId}' in ${stack.stackName} is ${resource.Type}, not ${AGENTCORE_RUNTIME_TYPE}. ${getEmbedConfig().cliName} invoke-agentcore only runs Bedrock AgentCore Runtime resources.`);
2008
+ if (resource.Type !== "AWS::BedrockAgentCore::Runtime") throw new AgentCoreResolutionError(`Resource ${displayIdent(logicalId)} in ${stack.stackName} is ${resource.Type}, not ${AGENTCORE_RUNTIME_TYPE}. ${getEmbedConfig().cliName} invoke-agentcore only runs Bedrock AgentCore Runtime resources.`);
1925
2009
  return extractRuntimeProperties(stack, logicalId, resource, resources, imageContext);
1926
2010
  }
1927
2011
  function pickAgentCoreCandidateStack(target, stacks) {
1928
- const parsed = parseTarget(target);
1929
- if (parsed.stackPattern === null) return stacks.length === 1 ? stacks[0] : void 0;
1930
- const matched = matchStacks(stacks, [parsed.stackPattern]);
1931
- return matched.length === 1 ? matched[0] : void 0;
2012
+ return candidateTargetStack(parseTarget(target), target, stacks);
1932
2013
  }
1933
- function pickStack$4(parsed, stacks) {
1934
- if (parsed.stackPattern === null) {
1935
- if (stacks.length === 1) return stacks[0];
1936
- throw new AgentCoreResolutionError(`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(", ")}.`);
2014
+ function pickStack$4(parsed, stacks, target) {
2015
+ const selection = selectTargetStack(parsed, target, stacks);
2016
+ switch (selection.kind) {
2017
+ case "found": return selection.stack;
2018
+ case "no-prefix": throw new AgentCoreResolutionError(`Multiple stacks in app, target '${parsed.pathOrId}' is missing a stack prefix. Use 'StackName:${parsed.pathOrId}' or 'StackName/...' (path form). Available stacks: ${describeStacks(stacks)}.`);
2019
+ case "not-found": throw new AgentCoreResolutionError(`Stack '${selection.pattern}' not found. Available stacks: ${describeStacks(stacks)}.`);
2020
+ case "ambiguous": throw new AgentCoreResolutionError(`Stack pattern '${selection.pattern}' matched ${selection.matched.length} stacks: ` + describeStacks(selection.matched) + ". Use a more specific pattern, or the stack's display path (path form).");
1937
2021
  }
1938
- const matched = matchStacks(stacks, [parsed.stackPattern]);
1939
- if (matched.length === 0) throw new AgentCoreResolutionError(`Stack '${parsed.stackPattern}' not found. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
1940
- if (matched.length > 1) throw new AgentCoreResolutionError(`Stack pattern '${parsed.stackPattern}' matched ${matched.length} stacks: ` + matched.map((s) => s.stackName).join(", ") + ". Use a more specific pattern.");
1941
- return matched[0];
1942
2022
  }
1943
2023
  function matchRuntime(parsed, target, stack, resources) {
1944
2024
  if (parsed.isPath) {
@@ -1989,7 +2069,7 @@ function extractJwtAuthorizer(authorizerConfig, logicalId) {
1989
2069
  const cfg = jwt;
1990
2070
  const discoveryUrl = cfg["DiscoveryUrl"];
1991
2071
  if (typeof discoveryUrl !== "string" || discoveryUrl.length === 0) {
1992
- getLogger().warn(`AgentCore Runtime '${logicalId}' declares a CustomJWTAuthorizer whose DiscoveryUrl is not a literal string; ${getEmbedConfig().cliName} invoke-agentcore cannot verify inbound JWTs against it and will skip auth.`);
2072
+ getLogger().warn(`AgentCore Runtime ${displayIdent(logicalId)} declares a CustomJWTAuthorizer whose DiscoveryUrl is not a literal string; ${getEmbedConfig().cliName} invoke-agentcore cannot verify inbound JWTs against it and will skip auth.`);
1993
2073
  return;
1994
2074
  }
1995
2075
  const toStringArray = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : void 0;
@@ -2016,7 +2096,7 @@ function extractCustomClaims(raw, logicalId) {
2016
2096
  const matchObj = e["AuthorizingClaimMatchValue"];
2017
2097
  if (typeof name !== "string" || name.length === 0) continue;
2018
2098
  if (valueType !== "STRING" && valueType !== "STRING_ARRAY") {
2019
- getLogger().warn(`AgentCore Runtime '${logicalId}' CustomClaims entry '${name}' has unsupported InboundTokenClaimValueType '${String(valueType)}' (expected STRING / STRING_ARRAY); skipping.`);
2099
+ getLogger().warn(`AgentCore Runtime ${displayIdent(logicalId)} CustomClaims entry ${displayIdent(name)} has unsupported InboundTokenClaimValueType ${displayIdent(String(valueType))} (expected STRING / STRING_ARRAY); skipping.`);
2020
2100
  continue;
2021
2101
  }
2022
2102
  if (!matchObj || typeof matchObj !== "object" || Array.isArray(matchObj)) continue;
@@ -2024,7 +2104,7 @@ function extractCustomClaims(raw, logicalId) {
2024
2104
  const operator = m["ClaimMatchOperator"];
2025
2105
  const matchValue = m["ClaimMatchValue"];
2026
2106
  if (operator !== "EQUALS" && operator !== "CONTAINS" && operator !== "CONTAINS_ANY") {
2027
- getLogger().warn(`AgentCore Runtime '${logicalId}' CustomClaims entry '${name}' has unsupported ClaimMatchOperator '${String(operator)}' (expected EQUALS / CONTAINS / CONTAINS_ANY); skipping.`);
2107
+ getLogger().warn(`AgentCore Runtime ${displayIdent(logicalId)} CustomClaims entry ${displayIdent(name)} has unsupported ClaimMatchOperator ${displayIdent(String(operator))} (expected EQUALS / CONTAINS / CONTAINS_ANY); skipping.`);
2028
2108
  continue;
2029
2109
  }
2030
2110
  if (!matchValue || typeof matchValue !== "object" || Array.isArray(matchValue)) continue;
@@ -2041,7 +2121,7 @@ function extractCustomClaims(raw, logicalId) {
2041
2121
  if (typeof s === "string" && s.length > 0) value = s;
2042
2122
  }
2043
2123
  if (value === void 0) {
2044
- getLogger().warn(`AgentCore Runtime '${logicalId}' CustomClaims entry '${name}' has no usable MatchValueString / MatchValueStringList for operator ${operator}; skipping.`);
2124
+ getLogger().warn(`AgentCore Runtime ${displayIdent(logicalId)} CustomClaims entry ${displayIdent(name)} has no usable MatchValueString / MatchValueStringList for operator ${operator}; skipping.`);
2045
2125
  continue;
2046
2126
  }
2047
2127
  out.push({
@@ -2055,21 +2135,21 @@ function extractCustomClaims(raw, logicalId) {
2055
2135
  }
2056
2136
  function extractProtocol(value, logicalId, stackName) {
2057
2137
  if (value === void 0 || value === null) return AGENTCORE_HTTP_PROTOCOL;
2058
- if (typeof value !== "string") throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a non-string ProtocolConfiguration. ${getEmbedConfig().cliName} invoke-agentcore supports the ${SUPPORTED_AGENTCORE_PROTOCOLS.join(" / ")} protocols.`);
2059
- if (!SUPPORTED_AGENTCORE_PROTOCOLS.includes(value)) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} uses the ${value} protocol. ${getEmbedConfig().cliName} invoke-agentcore supports the ${SUPPORTED_AGENTCORE_PROTOCOLS.join(" / ")} protocols.`);
2138
+ if (typeof value !== "string") throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has a non-string ProtocolConfiguration. ${getEmbedConfig().cliName} invoke-agentcore supports the ${SUPPORTED_AGENTCORE_PROTOCOLS.join(" / ")} protocols.`);
2139
+ if (!SUPPORTED_AGENTCORE_PROTOCOLS.includes(value)) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} uses the ${value} protocol. ${getEmbedConfig().cliName} invoke-agentcore supports the ${SUPPORTED_AGENTCORE_PROTOCOLS.join(" / ")} protocols.`);
2060
2140
  return value;
2061
2141
  }
2062
2142
  function extractArtifact(artifact, logicalId, stackName, resources, region, imageContext) {
2063
- if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has no AgentRuntimeArtifact.`);
2143
+ if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has no AgentRuntimeArtifact.`);
2064
2144
  const art = artifact;
2065
2145
  if (art["CodeConfiguration"] && !art["ContainerConfiguration"]) return {
2066
2146
  kind: "code",
2067
2147
  codeArtifact: extractCodeArtifact(art["CodeConfiguration"], logicalId, stackName)
2068
2148
  };
2069
2149
  const container = art["ContainerConfiguration"];
2070
- if (!container || typeof container !== "object" || Array.isArray(container)) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has no ContainerConfiguration in its AgentRuntimeArtifact.`);
2150
+ if (!container || typeof container !== "object" || Array.isArray(container)) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has no ContainerConfiguration in its AgentRuntimeArtifact.`);
2071
2151
  const uri = resolveImageUri(container["ContainerUri"], logicalId, stackName, resources, region, imageContext);
2072
- if (uri === void 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a ContainerConfiguration.ContainerUri that ${getEmbedConfig().cliName} invoke-agentcore cannot resolve. v1 resolves a literal image URI, an Fn::Sub asset URI (the fromAsset / Dockerfile path), an imported-ECR Fn::Join, and a same-stack AWS::ECR::Repository Fn::Join under --from-cfn-stack — build the agent as a fromAsset image, or pin a literal / imported ECR image URI.`);
2152
+ if (uri === void 0) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has a ContainerConfiguration.ContainerUri that ${getEmbedConfig().cliName} invoke-agentcore cannot resolve. v1 resolves a literal image URI, an Fn::Sub asset URI (the fromAsset / Dockerfile path), an imported-ECR Fn::Join, and a same-stack AWS::ECR::Repository Fn::Join under --from-cfn-stack — build the agent as a fromAsset image, or pin a literal / imported ECR image URI.`);
2073
2153
  return {
2074
2154
  kind: "container",
2075
2155
  containerUri: uri
@@ -2078,14 +2158,14 @@ function extractArtifact(artifact, logicalId, stackName, resources, region, imag
2078
2158
  function extractCodeArtifact(codeConfig, logicalId, stackName) {
2079
2159
  const cfg = codeConfig && typeof codeConfig === "object" && !Array.isArray(codeConfig) ? codeConfig : {};
2080
2160
  const runtime = cfg["Runtime"];
2081
- if (typeof runtime !== "string" || runtime.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a CodeConfiguration with no string Runtime.`);
2161
+ if (typeof runtime !== "string" || runtime.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has a CodeConfiguration with no string Runtime.`);
2082
2162
  const entryPointRaw = cfg["EntryPoint"];
2083
2163
  const entryPoint = Array.isArray(entryPointRaw) ? entryPointRaw.filter((x) => typeof x === "string") : [];
2084
- if (entryPoint.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a CodeConfiguration with no EntryPoint.`);
2164
+ if (entryPoint.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has a CodeConfiguration with no EntryPoint.`);
2085
2165
  const s3 = cfg["Code"] && typeof cfg["Code"] === "object" ? cfg["Code"]["S3"] : void 0;
2086
2166
  const s3Obj = s3 && typeof s3 === "object" && !Array.isArray(s3) ? s3 : {};
2087
2167
  const prefix = s3Obj["Prefix"];
2088
- if (typeof prefix !== "string" || prefix.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a CodeConfiguration whose Code.S3.Prefix is not a literal string. ${getEmbedConfig().cliName} invoke-agentcore needs a literal object key — re-synthesize a fromCodeAsset bundle, or pass a literal bucket + key for a fromS3 bundle.`);
2168
+ if (typeof prefix !== "string" || prefix.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} has a CodeConfiguration whose Code.S3.Prefix is not a literal string. ${getEmbedConfig().cliName} invoke-agentcore needs a literal object key — re-synthesize a fromCodeAsset bundle, or pass a literal bucket + key for a fromS3 bundle.`);
2089
2169
  const codeAssetHash = prefix.replace(/^.*\//, "").replace(/\.zip$/, "");
2090
2170
  const bucket = s3Obj["Bucket"];
2091
2171
  const versionId = s3Obj["VersionId"];
@@ -2127,7 +2207,7 @@ function resolveImageUri(value, logicalId, stackName, resources, region, imageCo
2127
2207
  })();
2128
2208
  const joinResolved = tryResolveImageFnJoin(value, resources, context);
2129
2209
  if (joinResolved.kind === "resolved") return joinResolved.uri;
2130
- if (joinResolved.kind === "needs-state") throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} references same-stack ECR repository '${joinResolved.repoLogicalId}' via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — ` + formatStateRemedy(context) + ", build via Runtime.fromAsset, or pin a literal / imported ECR image URI.");
2210
+ if (joinResolved.kind === "needs-state") throw new AgentCoreResolutionError(`AgentCore Runtime ${displayIdent(logicalId)} in ${stackName} references same-stack ECR repository ${displayIdent(joinResolved.repoLogicalId)} via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — ` + formatStateRemedy(context) + ", build via Runtime.fromAsset, or pin a literal / imported ECR image URI.");
2131
2211
  }
2132
2212
  }
2133
2213
 
@@ -2536,13 +2616,13 @@ function applyResponseParameters(base, responseParameters, responseCtx) {
2536
2616
  }
2537
2617
  const headerMatch = /^(append|overwrite|remove):header\.(.+)$/i.exec(key);
2538
2618
  if (!headerMatch || !headerMatch[1] || !headerMatch[2]) {
2539
- logger.warn(`ResponseParameters: key '${key}' does not match the expected shape '(append|overwrite|remove):header.<name>' or 'overwrite:statuscode'; the value was dropped. Check for typos (e.g. 'appen:' vs 'append:').`);
2619
+ logger.warn(`ResponseParameters: key ${displayIdent(key)} does not match the expected shape '(append|overwrite|remove):header.<name>' or 'overwrite:statuscode'; the value was dropped. Check for typos (e.g. 'appen:' vs 'append:').`);
2540
2620
  continue;
2541
2621
  }
2542
2622
  const op = headerMatch[1].toLowerCase();
2543
2623
  const name = headerMatch[2].toLowerCase();
2544
2624
  if (isReservedHeader(name)) {
2545
- logger.warn(`ResponseParameters: header '${name}' is reserved by API Gateway and was skipped`);
2625
+ logger.warn(`ResponseParameters: header ${displayIdent(name)} is reserved by API Gateway and was skipped`);
2546
2626
  continue;
2547
2627
  }
2548
2628
  if (op === "remove") delete headers[name];
@@ -2650,10 +2730,10 @@ function discoverRoutes(stacks) {
2650
2730
  function discoverRestV1Method(logicalId, resource, template, stackName) {
2651
2731
  const props = resource.Properties ?? {};
2652
2732
  const integration = props["Integration"];
2653
- if (!integration) throw new Error(`${stackName}/${logicalId} (AWS::ApiGateway::Method): missing Integration property`);
2733
+ if (!integration) throw new Error(`${displayStackName(`${stackName}/${logicalId}`)} (AWS::ApiGateway::Method): missing Integration property`);
2654
2734
  const restApiId = props["RestApiId"];
2655
2735
  const restApiLogicalId = pickRefLogicalId(restApiId);
2656
- if (!restApiLogicalId) throw new Error(`${stackName}/${logicalId} (AWS::ApiGateway::Method): RestApiId must be a { Ref: '...' } reference (got ${shortJson$1(restApiId)}).`);
2736
+ if (!restApiLogicalId) throw new Error(`${displayStackName(`${stackName}/${logicalId}`)} (AWS::ApiGateway::Method): RestApiId must be a { Ref: '...' } reference (got ${shortJson$1(restApiId)}).`);
2657
2737
  const resourceId = props["ResourceId"];
2658
2738
  const path = buildRestV1Path(resourceId, restApiLogicalId, template, stackName, logicalId);
2659
2739
  const httpMethod = stringifyValue(props["HttpMethod"] ?? "ANY");
@@ -2745,7 +2825,7 @@ function discoverRestV1Method(logicalId, resource, template, stackName) {
2745
2825
  method: httpMethod,
2746
2826
  pathPattern: path,
2747
2827
  lambdaLogicalId: "",
2748
- unsupported: { reason: `${stackName}/${logicalId}: unknown REST v1 integration type '${String(integrationType)}' (expected AWS_PROXY / AWS / HTTP / HTTP_PROXY / MOCK).` }
2828
+ unsupported: { reason: `${displayStackName(`${stackName}/${logicalId}`)}: unknown REST v1 integration type ${displayIdent(String(integrationType))} (expected AWS_PROXY / AWS / HTTP / HTTP_PROXY / MOCK).` }
2749
2829
  }];
2750
2830
  const integrationUri = integration["Uri"];
2751
2831
  const arnOutcome = resolveLambdaArnOutcome(integrationUri);
@@ -2754,7 +2834,7 @@ function discoverRestV1Method(logicalId, resource, template, stackName) {
2754
2834
  method: httpMethod,
2755
2835
  pathPattern: path,
2756
2836
  lambdaLogicalId: "",
2757
- unsupported: { reason: `${stackName}/${logicalId}.Integration.Uri: ${arnOutcome.detail} (got ${shortJson$1(integrationUri)}). Lambda Arn intrinsics on cross-stack / imported references are not resolvable locally; deploy the producer stack and use \`${getEmbedConfig().cliName} invoke --from-state\` shapes if you need it.` }
2837
+ unsupported: { reason: `${displayStackName(`${stackName}/${logicalId}`)}.Integration.Uri: ${arnOutcome.detail} (got ${shortJson$1(integrationUri)}). Lambda Arn intrinsics on cross-stack / imported references are not resolvable locally; deploy the producer stack and use \`${getEmbedConfig().cliName} invoke --from-state\` shapes if you need it.` }
2758
2838
  }];
2759
2839
  return [{
2760
2840
  ...baseRoute,
@@ -2804,7 +2884,7 @@ function buildHttpProxyIntegrationConfig(integration, stackName, logicalId) {
2804
2884
  const uri = integration["Uri"];
2805
2885
  if (typeof uri !== "string" || uri.length === 0) return {
2806
2886
  kind: "unsupported",
2807
- reason: `${stackName}/${logicalId}: HTTP_PROXY Integration.Uri must be a literal string in v1 (${getEmbedConfig().cliName} start-api does not resolve Fn::Sub / Fn::Join in HTTP_PROXY Uris); got ${shortJson$1(uri)}.`
2887
+ reason: `${displayStackName(`${stackName}/${logicalId}`)}: HTTP_PROXY Integration.Uri must be a literal string in v1 (${getEmbedConfig().cliName} start-api does not resolve Fn::Sub / Fn::Join in HTTP_PROXY Uris); got ${shortJson$1(uri)}.`
2808
2888
  };
2809
2889
  const integrationHttpMethod = pickStringField(integration, "IntegrationHttpMethod");
2810
2890
  const requestParameters = pickStringRecord(integration["RequestParameters"]);
@@ -2824,7 +2904,7 @@ function buildHttpIntegrationConfig(integration, stackName, logicalId) {
2824
2904
  const uri = integration["Uri"];
2825
2905
  if (typeof uri !== "string" || uri.length === 0) return {
2826
2906
  kind: "unsupported",
2827
- reason: `${stackName}/${logicalId}: HTTP Integration.Uri must be a literal string in v1 (${getEmbedConfig().cliName} start-api does not resolve Fn::Sub / Fn::Join in HTTP Uris); got ${shortJson$1(uri)}.`
2907
+ reason: `${displayStackName(`${stackName}/${logicalId}`)}: HTTP Integration.Uri must be a literal string in v1 (${getEmbedConfig().cliName} start-api does not resolve Fn::Sub / Fn::Join in HTTP Uris); got ${shortJson$1(uri)}.`
2828
2908
  };
2829
2909
  const integrationHttpMethod = pickStringField(integration, "IntegrationHttpMethod");
2830
2910
  const requestParameters = pickStringRecord(integration["RequestParameters"]);
@@ -2846,12 +2926,12 @@ function buildAwsIntegrationConfig(integration, stackName, logicalId) {
2846
2926
  const uri = integration["Uri"];
2847
2927
  if (!uriContainsLambdaMarker(uri)) return {
2848
2928
  kind: "unsupported",
2849
- reason: `${stackName}/${logicalId}: REST v1 AWS integration targeting a non-Lambda service (Uri ${shortJson$1(uri)}) is not emulated locally in ${getEmbedConfig().binaryName} v1. Lambda non-proxy AWS integrations are supported; direct AWS service integrations (S3 / SQS / SNS / DynamoDB) require deploying to AWS. See https://github.com/go-to-k/cdk-local/blob/main/docs/local-emulation.md.`
2929
+ reason: `${displayStackName(`${stackName}/${logicalId}`)}: REST v1 AWS integration targeting a non-Lambda service (Uri ${shortJson$1(uri)}) is not emulated locally in ${getEmbedConfig().binaryName} v1. Lambda non-proxy AWS integrations are supported; direct AWS service integrations (S3 / SQS / SNS / DynamoDB) require deploying to AWS. See https://github.com/go-to-k/cdk-local/blob/main/docs/local-emulation.md.`
2850
2930
  };
2851
2931
  const arnOutcome = resolveLambdaArnOutcome(uri);
2852
2932
  if (arnOutcome.kind === "unsupported") return {
2853
2933
  kind: "unsupported",
2854
- reason: `${stackName}/${logicalId}.Integration.Uri: ${arnOutcome.detail} (got ${shortJson$1(uri)}). Lambda Arn intrinsics on cross-stack / imported references are not resolvable locally.`
2934
+ reason: `${displayStackName(`${stackName}/${logicalId}`)}.Integration.Uri: ${arnOutcome.detail} (got ${shortJson$1(uri)}). Lambda Arn intrinsics on cross-stack / imported references are not resolvable locally.`
2855
2935
  };
2856
2936
  const requestTemplates = pickStringRecord(integration["RequestTemplates"]);
2857
2937
  const responses = readIntegrationResponses(integration);
@@ -2933,19 +3013,19 @@ function buildRestV1Path(resourceIdIntrinsic, restApiLogicalId, template, stackN
2933
3013
  }
2934
3014
  }
2935
3015
  const resourceLogicalId = pickRefLogicalId(resourceIdIntrinsic);
2936
- if (!resourceLogicalId) throw new Error(`${stackName}/${methodLogicalId}: ResourceId must be { Ref: '...' } or { 'Fn::GetAtt': [..., 'RootResourceId'] } (got ${shortJson$1(resourceIdIntrinsic)}).`);
3016
+ if (!resourceLogicalId) throw new Error(`${displayStackName(`${stackName}/${methodLogicalId}`)}: ResourceId must be { Ref: '...' } or { 'Fn::GetAtt': [..., 'RootResourceId'] } (got ${shortJson$1(resourceIdIntrinsic)}).`);
2937
3017
  const segments = [];
2938
3018
  const visited = /* @__PURE__ */ new Set();
2939
3019
  let cursor = resourceLogicalId;
2940
3020
  while (cursor && cursor !== restApiLogicalId) {
2941
- if (visited.has(cursor)) throw new Error(`${stackName}/${methodLogicalId}: cycle detected in AWS::ApiGateway::Resource ParentId chain at ${cursor}`);
3021
+ if (visited.has(cursor)) throw new Error(`${displayStackName(`${stackName}/${methodLogicalId}`)}: cycle detected in AWS::ApiGateway::Resource ParentId chain at ${displayIdent(cursor)}`);
2942
3022
  visited.add(cursor);
2943
3023
  const node = template.Resources?.[cursor];
2944
- if (!node) throw new Error(`${stackName}/${methodLogicalId}: ParentId chain references missing resource '${cursor}'`);
2945
- if (node.Type !== "AWS::ApiGateway::Resource") throw new Error(`${stackName}/${methodLogicalId}: ParentId chain hit ${node.Type} (expected AWS::ApiGateway::Resource or RestApi root)`);
3024
+ if (!node) throw new Error(`${displayStackName(`${stackName}/${methodLogicalId}`)}: ParentId chain references missing resource ${displayIdent(cursor)}`);
3025
+ if (node.Type !== "AWS::ApiGateway::Resource") throw new Error(`${displayStackName(`${stackName}/${methodLogicalId}`)}: ParentId chain hit ${node.Type} (expected AWS::ApiGateway::Resource or RestApi root)`);
2946
3026
  const nodeProps = node.Properties ?? {};
2947
3027
  const pathPart = nodeProps["PathPart"];
2948
- if (typeof pathPart !== "string") throw new Error(`${stackName}/${methodLogicalId}: AWS::ApiGateway::Resource '${cursor}' missing PathPart`);
3028
+ if (typeof pathPart !== "string") throw new Error(`${displayStackName(`${stackName}/${methodLogicalId}`)}: AWS::ApiGateway::Resource ${displayIdent(cursor)} missing PathPart`);
2949
3029
  segments.unshift(pathPart);
2950
3030
  const parentId = nodeProps["ParentId"];
2951
3031
  if (parentId && typeof parentId === "object" && !Array.isArray(parentId) && "Fn::GetAtt" in parentId) {
@@ -2972,9 +3052,9 @@ function discoverHttpApiRoute(logicalId, resource, template, stackName) {
2972
3052
  const props = resource.Properties ?? {};
2973
3053
  const apiId = props["ApiId"];
2974
3054
  const apiLogicalId = pickRefLogicalId(apiId);
2975
- if (!apiLogicalId) throw new Error(`${stackName}/${logicalId} (AWS::ApiGatewayV2::Route): ApiId must be { Ref: '...' } (got ${shortJson$1(apiId)}).`);
3055
+ if (!apiLogicalId) throw new Error(`${displayStackName(`${stackName}/${logicalId}`)} (AWS::ApiGatewayV2::Route): ApiId must be { Ref: '...' } (got ${shortJson$1(apiId)}).`);
2976
3056
  const routeKey = props["RouteKey"];
2977
- if (typeof routeKey !== "string" || routeKey.length === 0) throw new Error(`${stackName}/${logicalId} (AWS::ApiGatewayV2::Route): RouteKey must be a string`);
3057
+ if (typeof routeKey !== "string" || routeKey.length === 0) throw new Error(`${displayStackName(`${stackName}/${logicalId}`)} (AWS::ApiGatewayV2::Route): RouteKey must be a string`);
2978
3058
  const apiCdkPath = readApiCdkPath$1(apiLogicalId, template);
2979
3059
  const apiResource = template.Resources?.[apiLogicalId];
2980
3060
  if (apiResource?.Type === "AWS::ApiGatewayV2::Api") {
@@ -2993,22 +3073,22 @@ function discoverHttpApiRoute(logicalId, resource, template, stackName) {
2993
3073
  declaredAt: `${stackName}/${logicalId}`
2994
3074
  };
2995
3075
  const target = props["Target"];
2996
- const integrationLogicalId = parseHttpApiTargetIntegration(target, `${stackName}/${logicalId}.Target`);
3076
+ const integrationLogicalId = parseHttpApiTargetIntegration(target, `${displayStackName(`${stackName}/${logicalId}`)}.Target`);
2997
3077
  const integration = template.Resources?.[integrationLogicalId];
2998
- if (!integration || integration.Type !== "AWS::ApiGatewayV2::Integration") throw new Error(`${stackName}/${logicalId} (AWS::ApiGatewayV2::Route): Target points at '${integrationLogicalId}' which is not an AWS::ApiGatewayV2::Integration`);
3078
+ if (!integration || integration.Type !== "AWS::ApiGatewayV2::Integration") throw new Error(`${displayStackName(`${stackName}/${logicalId}`)} (AWS::ApiGatewayV2::Route): Target points at ${displayIdent(integrationLogicalId)} which is not an AWS::ApiGatewayV2::Integration`);
2999
3079
  const integrationProps = integration.Properties ?? {};
3000
3080
  const integrationType = integrationProps["IntegrationType"];
3001
3081
  if (integrationType !== "AWS_PROXY") return [{
3002
3082
  ...baseRoute,
3003
3083
  lambdaLogicalId: "",
3004
- unsupported: { reason: `${stackName}/${logicalId}: HTTP API v2 integration type '${String(integrationType)}' is not supported (only AWS_PROXY).` }
3084
+ unsupported: { reason: `${displayStackName(`${stackName}/${logicalId}`)}: HTTP API v2 integration type ${displayIdent(String(integrationType))} is not supported (only AWS_PROXY).` }
3005
3085
  }];
3006
3086
  if (integrationProps["IntegrationSubtype"] !== void 0) return [classifyServiceIntegrationRoute(baseRoute, integrationProps, stackName, logicalId, integrationLogicalId)];
3007
3087
  const arnOutcome = resolveLambdaArnOutcome(integrationProps["IntegrationUri"]);
3008
3088
  if (arnOutcome.kind === "unsupported") return [{
3009
3089
  ...baseRoute,
3010
3090
  lambdaLogicalId: "",
3011
- unsupported: { reason: `${stackName}/${integrationLogicalId}.IntegrationUri: ${arnOutcome.detail} (got ${shortJson$1(integrationProps["IntegrationUri"])}). Lambda Arn intrinsics on cross-stack / imported references are not resolvable locally.` }
3091
+ unsupported: { reason: `${displayStackName(`${stackName}/${integrationLogicalId}`)}.IntegrationUri: ${arnOutcome.detail} (got ${shortJson$1(integrationProps["IntegrationUri"])}). Lambda Arn intrinsics on cross-stack / imported references are not resolvable locally.` }
3012
3092
  }];
3013
3093
  return [{
3014
3094
  ...baseRoute,
@@ -3021,13 +3101,13 @@ function classifyServiceIntegrationRoute(baseRoute, integrationProps, stackName,
3021
3101
  if (!isSupportedSubtype(subtypeRaw)) return {
3022
3102
  ...baseRoute,
3023
3103
  lambdaLogicalId: "",
3024
- unsupported: { reason: `${declaredAt}: HTTP API v2 service integration subtype '${stringifyValue(subtypeRaw)}' is not supported by ${getEmbedConfig().cliName} start-api (see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-aws-services-reference.html for the supported list).` }
3104
+ unsupported: { reason: `${displayStackName(declaredAt)}: HTTP API v2 service integration subtype ${displayIdent(stringifyValue(subtypeRaw))} is not supported by ${getEmbedConfig().cliName} start-api (see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-aws-services-reference.html for the supported list).` }
3025
3105
  };
3026
3106
  const requestParameters = integrationProps["RequestParameters"];
3027
3107
  if (!requestParameters || typeof requestParameters !== "object" || Array.isArray(requestParameters)) return {
3028
3108
  ...baseRoute,
3029
3109
  lambdaLogicalId: "",
3030
- unsupported: { reason: `${stackName}/${integrationLogicalId}: HTTP API v2 service integration '${subtypeRaw}' is missing RequestParameters or it is not an object — cannot dispatch to the SDK without input mapping.` }
3110
+ unsupported: { reason: `${displayStackName(`${stackName}/${integrationLogicalId}`)}: HTTP API v2 service integration ${displayIdent(subtypeRaw)} is missing RequestParameters or it is not an object — cannot dispatch to the SDK without input mapping.` }
3031
3111
  };
3032
3112
  const responseParameters = integrationProps["ResponseParameters"];
3033
3113
  const validatedResponseParameters = responseParameters && typeof responseParameters === "object" && !Array.isArray(responseParameters) ? responseParameters : void 0;
@@ -3045,7 +3125,7 @@ function discoverFunctionUrl(logicalId, resource, template, stackName) {
3045
3125
  const props = resource.Properties ?? {};
3046
3126
  const targetArn = props["TargetFunctionArn"];
3047
3127
  const arnOutcome = resolveLambdaArnOutcome(targetArn);
3048
- if (arnOutcome.kind === "unsupported") throw new Error(`${stackName}/${logicalId}.TargetFunctionArn: ${arnOutcome.detail} (got ${shortJson$1(targetArn)}).`);
3128
+ if (arnOutcome.kind === "unsupported") throw new Error(`${displayStackName(`${stackName}/${logicalId}`)}.TargetFunctionArn: ${arnOutcome.detail} (got ${shortJson$1(targetArn)}).`);
3049
3129
  const lambdaLogicalId = arnOutcome.logicalId;
3050
3130
  const lambdaCdkPath = readApiCdkPath$1(lambdaLogicalId, template);
3051
3131
  const baseRoute = {
@@ -3063,7 +3143,7 @@ function discoverFunctionUrl(logicalId, resource, template, stackName) {
3063
3143
  if (authType !== "NONE" && authType !== "AWS_IAM") return [{
3064
3144
  ...baseRoute,
3065
3145
  lambdaLogicalId,
3066
- unsupported: { reason: `${stackName}/${logicalId}: AuthType ${shortJson$1(authType)} is not a recognized Function URL auth type (expected 'NONE' or 'AWS_IAM').` }
3146
+ unsupported: { reason: `${displayStackName(`${stackName}/${logicalId}`)}: AuthType ${shortJson$1(authType)} is not a recognized Function URL auth type (expected 'NONE' or 'AWS_IAM').` }
3067
3147
  }];
3068
3148
  const invokeModeRaw = props["InvokeMode"];
3069
3149
  let invokeMode = "BUFFERED";
@@ -3071,7 +3151,7 @@ function discoverFunctionUrl(logicalId, resource, template, stackName) {
3071
3151
  else if (invokeModeRaw !== void 0 && invokeModeRaw !== "BUFFERED") return [{
3072
3152
  ...baseRoute,
3073
3153
  lambdaLogicalId,
3074
- unsupported: { reason: `${stackName}/${logicalId}: InvokeMode ${shortJson$1(invokeModeRaw)} is not a recognized value (expected 'BUFFERED' or 'RESPONSE_STREAM').` }
3154
+ unsupported: { reason: `${displayStackName(`${stackName}/${logicalId}`)}: InvokeMode ${shortJson$1(invokeModeRaw)} is not a recognized value (expected 'BUFFERED' or 'RESPONSE_STREAM').` }
3075
3155
  }];
3076
3156
  return [{
3077
3157
  ...baseRoute,
@@ -3092,7 +3172,7 @@ function parseHttpApiTargetIntegration(target, location) {
3092
3172
  if (typeof target === "string") {
3093
3173
  const m = /^integrations\/(.+)$/.exec(target);
3094
3174
  if (m) return m[1];
3095
- throw new Error(`${location}: literal Target '${target}' must start with 'integrations/'`);
3175
+ throw new Error(`${location}: literal Target ${displayIdent(target)} must start with 'integrations/'`);
3096
3176
  }
3097
3177
  if (target && typeof target === "object" && !Array.isArray(target)) {
3098
3178
  const obj = target;
@@ -3140,7 +3220,7 @@ function parseRouteKey(routeKey) {
3140
3220
  pathPattern: "$default"
3141
3221
  };
3142
3222
  const m = /^([A-Za-z]+)\s+(\S+)$/.exec(routeKey);
3143
- if (!m) throw new Error(`RouteKey '${routeKey}' is malformed: expected '<METHOD> <path>' (e.g. 'GET /items/{id}') or '$default'.`);
3223
+ if (!m) throw new Error(`RouteKey ${displayIdent(routeKey)} is malformed: expected '<METHOD> <path>' (e.g. 'GET /items/{id}') or '$default'.`);
3144
3224
  return {
3145
3225
  method: m[1].toUpperCase(),
3146
3226
  pathPattern: m[2]
@@ -3194,7 +3274,7 @@ function discoverOneApi(logicalId, resource, template, stackName) {
3194
3274
  const stage = pickStage$1(logicalId, template);
3195
3275
  const apiCdkPath = readApiCdkPath(logicalId, template);
3196
3276
  const routes = collectRoutesForApi(logicalId, template, stackName);
3197
- if (routes.length === 0) throw new Error(`${declaredAt}: WebSocket API has no AWS::ApiGatewayV2::Route children — at least one route (typically '$connect') is required to dispatch.`);
3277
+ if (routes.length === 0) throw new Error(`${displayStackName(declaredAt)}: WebSocket API has no AWS::ApiGatewayV2::Route children — at least one route (typically '$connect') is required to dispatch.`);
3198
3278
  const authRoutes = collectAuthRoutesForApi(logicalId, template, stackName);
3199
3279
  const unsupported = authRoutes.length > 0 ? { reason: `WebSocket API requires authorizer support, which ${getEmbedConfig().binaryName} v1 does not emulate. Affected route(s): ${authRoutes.map((r) => `${r.routeKey} [AuthorizationType=${r.authorizationType}]`).join(", ")}. The API will be discovered but no upgrade requests will be accepted on this server.` } : void 0;
3200
3280
  return {
@@ -3235,7 +3315,7 @@ function collectAuthRoutesForApi(apiLogicalId, template, _stackName) {
3235
3315
  return result;
3236
3316
  }
3237
3317
  function assertSupportedSelectionExpression(expr, declaredAt) {
3238
- if (!/^\$request\.body(?:\.[A-Za-z_][A-Za-z0-9_]*)+$/.test(expr)) throw new Error(`${declaredAt}: RouteSelectionExpression '${expr}' is not supported in ${getEmbedConfig().cliName} start-api v1 — only '$request.body.<key>' shapes (optionally nested via dots) are recognized. File a follow-up issue if you need '$request.header.X' / '$context.X' / array-index access.`);
3318
+ if (!/^\$request\.body(?:\.[A-Za-z_][A-Za-z0-9_]*)+$/.test(expr)) throw new Error(`${displayStackName(declaredAt)}: RouteSelectionExpression ${displayIdent(expr)} is not supported in ${getEmbedConfig().cliName} start-api v1 — only '$request.body.<key>' shapes (optionally nested via dots) are recognized. File a follow-up issue if you need '$request.header.X' / '$context.X' / array-index access.`);
3239
3319
  }
3240
3320
  function parseSelectionExpressionPath(expr) {
3241
3321
  const m = /^\$request\.body\.(.+)$/.exec(expr);
@@ -3264,17 +3344,17 @@ function collectRoutesForApi(apiLogicalId, template, stackName) {
3264
3344
  if (pickRefLogicalId(props["ApiId"]) !== apiLogicalId) continue;
3265
3345
  const declaredAt = `${stackName}/${routeLogicalId}`;
3266
3346
  const routeKey = props["RouteKey"];
3267
- if (typeof routeKey !== "string" || routeKey.length === 0) throw new Error(`${declaredAt}: RouteKey must be a non-empty string.`);
3268
- if (seenKeys.has(routeKey)) throw new Error(`${declaredAt}: WebSocket API has duplicate RouteKey '${routeKey}' — each RouteKey may appear at most once per API.`);
3347
+ if (typeof routeKey !== "string" || routeKey.length === 0) throw new Error(`${displayStackName(declaredAt)}: RouteKey must be a non-empty string.`);
3348
+ if (seenKeys.has(routeKey)) throw new Error(`${displayStackName(declaredAt)}: WebSocket API has duplicate RouteKey ${displayIdent(routeKey)} — each RouteKey may appear at most once per API.`);
3269
3349
  seenKeys.add(routeKey);
3270
3350
  const targetLogicalId = parseRouteTarget(props["Target"], declaredAt);
3271
3351
  const integration = resources[targetLogicalId];
3272
- if (!integration || integration.Type !== "AWS::ApiGatewayV2::Integration") throw new Error(`${declaredAt}: Target points at '${targetLogicalId}' which is not an AWS::ApiGatewayV2::Integration.`);
3352
+ if (!integration || integration.Type !== "AWS::ApiGatewayV2::Integration") throw new Error(`${displayStackName(declaredAt)}: Target points at ${displayIdent(targetLogicalId)} which is not an AWS::ApiGatewayV2::Integration.`);
3273
3353
  const integrationProps = integration.Properties ?? {};
3274
3354
  const integrationType = integrationProps["IntegrationType"];
3275
- if (integrationType !== "AWS_PROXY") throw new Error(`${declaredAt}: WebSocket route IntegrationType '${String(integrationType)}' is not supported in ${getEmbedConfig().cliName} start-api v1 — only AWS_PROXY (Lambda) integrations are emulated.`);
3355
+ if (integrationType !== "AWS_PROXY") throw new Error(`${displayStackName(declaredAt)}: WebSocket route IntegrationType ${displayIdent(String(integrationType))} is not supported in ${getEmbedConfig().cliName} start-api v1 — only AWS_PROXY (Lambda) integrations are emulated.`);
3276
3356
  const arnOutcome = resolveLambdaArnIntrinsic(integrationProps["IntegrationUri"]);
3277
- if (arnOutcome.kind === "unsupported") throw new Error(`${stackName}/${targetLogicalId}.IntegrationUri: ${arnOutcome.detail} — WebSocket routes must point at a same-template Lambda.`);
3357
+ if (arnOutcome.kind === "unsupported") throw new Error(`${displayStackName(`${stackName}/${targetLogicalId}`)}.IntegrationUri: ${arnOutcome.detail} — WebSocket routes must point at a same-template Lambda.`);
3278
3358
  result.push({
3279
3359
  routeKey,
3280
3360
  targetLambdaLogicalId: arnOutcome.logicalId,
@@ -3288,7 +3368,7 @@ function parseRouteTarget(target, location) {
3288
3368
  if (typeof target === "string") {
3289
3369
  const m = /^integrations\/(.+)$/.exec(target);
3290
3370
  if (m) return m[1];
3291
- throw new Error(`${location}: literal Target '${target}' must start with 'integrations/'.`);
3371
+ throw new Error(`${displayStackName(location)}: literal Target ${displayIdent(target)} must start with 'integrations/'.`);
3292
3372
  }
3293
3373
  if (target && typeof target === "object" && !Array.isArray(target)) {
3294
3374
  const obj = target;
@@ -3329,7 +3409,7 @@ function parseRouteTarget(target, location) {
3329
3409
  }
3330
3410
  }
3331
3411
  }
3332
- throw new Error(`${location}: Target must be 'integrations/<id>' literal, Fn::Join with the documented shapes, or Fn::Sub with an 'integrations/\${...}' template.`);
3412
+ throw new Error(`${displayStackName(location)}: Target must be 'integrations/<id>' literal, Fn::Join with the documented shapes, or Fn::Sub with an 'integrations/\${...}' template.`);
3333
3413
  }
3334
3414
  function readApiCdkPath(logicalId, template) {
3335
3415
  const resource = template.Resources?.[logicalId];
@@ -3412,7 +3492,7 @@ function scanAgentCoreRuntimes(stacks) {
3412
3492
  function listApiSurfaces(stacks) {
3413
3493
  const byKey = /* @__PURE__ */ new Map();
3414
3494
  const add = (stackName, logicalId, cdkPath, kind) => {
3415
- const key = `${stackName}:${logicalId}`;
3495
+ const key = cdkPath ?? `${stackName}:${logicalId}`;
3416
3496
  if (!byKey.has(key)) byKey.set(key, makeEntry(stackName, logicalId, cdkPath, kind));
3417
3497
  };
3418
3498
  try {
@@ -3582,7 +3662,7 @@ async function resolveMultiTarget(provided, params) {
3582
3662
  async function loadSynthDeps() {
3583
3663
  const [toolkitLib, { CdklIoHost }, { AssetManifestArtifact }] = await Promise.all([
3584
3664
  import("@aws-cdk/toolkit-lib"),
3585
- import("./cdkl-io-host-CDXI-7cC.js"),
3665
+ import("./cdkl-io-host-DFQf5KWv.js"),
3586
3666
  import("@aws-cdk/cloud-assembly-api")
3587
3667
  ]);
3588
3668
  return {
@@ -3628,12 +3708,12 @@ var AssemblyReader = class {
3628
3708
  }
3629
3709
  };
3630
3710
  function collectStacks(assembly, assetManifestClass) {
3631
- return assembly.stacks.map((stack) => mapStackArtifact(stack, assembly.directory, assetManifestClass));
3711
+ return assembly.stacksRecursively.map((stack) => mapStackArtifact(stack, assembly.directory, assetManifestClass));
3632
3712
  }
3633
3713
  function mapStackArtifact(stack, assetOutdir, assetManifestClass) {
3634
3714
  const info = {
3635
3715
  stackName: stack.stackName,
3636
- displayName: stack.displayName ?? stack.id,
3716
+ displayName: stack.hierarchicalId,
3637
3717
  artifactId: stack.id,
3638
3718
  template: stack.template,
3639
3719
  dependencyNames: stack.dependencies.map((d) => d.id),
@@ -3823,7 +3903,10 @@ async function resolveSsmParameters(client, refs, label) {
3823
3903
  const invalid = resp.InvalidParameters ?? [];
3824
3904
  if (invalid.length > 0) logger.warn(`${label}: SSM GetParameters reported invalid parameter name(s): ${invalid.join(", ")}. Ref to the matching CloudFormation parameter(s) will warn-and-drop (was the SSM parameter created?).`);
3825
3905
  } catch (err) {
3826
- logger.warn(`${label}: SSM GetParameters(${flattenToOneLine(names.join(", "))}) failed: ${describeAwsFailureForWarn(err, "SSM GetParameters")}. Ref to the matching CloudFormation parameter(s) will warn-and-drop (grant ssm:GetParameters or override via --env-vars).`);
3906
+ logger.warn(`${label}: SSM GetParameters(${names.map((n) => displayIdent(n, {
3907
+ listMember: true,
3908
+ maxCodePoints: 1011
3909
+ })).join(", ")}) failed: ${describeAwsFailureForWarn(err, "SSM GetParameters")}. Ref to the matching CloudFormation parameter(s) will warn-and-drop (grant ssm:GetParameters or override via --env-vars).`);
3827
3910
  continue;
3828
3911
  }
3829
3912
  for (const p of resolved) {
@@ -3953,19 +4036,19 @@ var CfnLocalStateProvider = class {
3953
4036
  resourceMap = buildResourceStateMap(await fetchAllStackResources(client, this.cfnStackName));
3954
4037
  } catch (err) {
3955
4038
  const detail = formatAwsErrorForWarn(err, "CloudFormation ListStackResources");
3956
- this.lastLoadError = `ListStackResources(${this.cfnStackName}) failed: ${detail} (region='${this.region}')`;
3957
- logger.warn(`${this.label}: ListStackResources(${this.cfnStackName}) failed: ${detail}. Was the stack deployed in region '${this.region}'? Falling back.`);
4039
+ this.lastLoadError = `ListStackResources(${displayStackName(this.cfnStackName)}) failed: ${detail} (region=${displayIdent(this.region)})`;
4040
+ logger.warn(`${this.label}: ListStackResources(${displayStackName(this.cfnStackName)}) failed: ${detail}. Was the stack deployed in region ${displayIdent(this.region)}? Falling back.`);
3958
4041
  return;
3959
4042
  }
3960
4043
  let outputs;
3961
4044
  try {
3962
4045
  const stack = (await client.send(new DescribeStacksCommand({ StackName: this.cfnStackName }))).Stacks?.[0];
3963
4046
  if (!stack) {
3964
- logger.warn(`${this.label}: DescribeStacks(${this.cfnStackName}) returned no stack; outputs will be empty.`);
4047
+ logger.warn(`${this.label}: DescribeStacks(${displayStackName(this.cfnStackName)}) returned no stack; outputs will be empty.`);
3965
4048
  outputs = {};
3966
4049
  } else outputs = buildOutputsMap(stack.Outputs ?? []);
3967
4050
  } catch (err) {
3968
- logger.warn(`${this.label}: DescribeStacks(${this.cfnStackName}) failed: ${formatAwsErrorForWarn(err, "CloudFormation DescribeStacks")}. Outputs will be empty (Fn::GetStackOutput cannot resolve).`);
4051
+ logger.warn(`${this.label}: DescribeStacks(${displayStackName(this.cfnStackName)}) failed: ${formatAwsErrorForWarn(err, "CloudFormation DescribeStacks")}. Outputs will be empty (Fn::GetStackOutput cannot resolve).`);
3969
4052
  outputs = {};
3970
4053
  }
3971
4054
  return {
@@ -4207,7 +4290,7 @@ function substituteAgainstState(value, contextOrResources) {
4207
4290
  };
4208
4291
  return {
4209
4292
  kind: "unresolved",
4210
- reason: `unsupported intrinsic '${intrinsic}' (supported: Ref, Fn::GetAtt, Fn::Sub, Fn::Join, Fn::Select, Fn::Split)`
4293
+ reason: `unsupported intrinsic ${displayIdent(intrinsic)} (supported: Ref, Fn::GetAtt, Fn::Sub, Fn::Join, Fn::Select, Fn::Split)`
4211
4294
  };
4212
4295
  }
4213
4296
  function isContext(v) {
@@ -4238,13 +4321,13 @@ function resolveRef(arg, context) {
4238
4321
  }
4239
4322
  return {
4240
4323
  kind: "unresolved",
4241
- reason: `Ref '${arg}': no record in the state source (was the resource deployed, or is it a CloudFormation parameter that could not be resolved — e.g. an SSM-backed AWS::SSM::Parameter::Value parameter whose SSM value was not readable?)`
4324
+ reason: `Ref ${displayIdent(arg)}: no record in the state source (was the resource deployed, or is it a CloudFormation parameter that could not be resolved — e.g. an SSM-backed AWS::SSM::Parameter::Value parameter whose SSM value was not readable?)`
4242
4325
  };
4243
4326
  }
4244
4327
  function resolvePseudoParameter(name, pseudo) {
4245
4328
  if (!pseudo) return {
4246
4329
  kind: "unresolved",
4247
- reason: `Ref '${name}': pseudo parameter not supplied (need an active state source, e.g. --from-cfn-stack)`
4330
+ reason: `Ref ${displayIdent(name)}: pseudo parameter not supplied (need an active state source, e.g. --from-cfn-stack)`
4248
4331
  };
4249
4332
  switch (name) {
4250
4333
  case "AWS::AccountId":
@@ -4273,12 +4356,12 @@ function resolvePseudoParameter(name, pseudo) {
4273
4356
  break;
4274
4357
  default: return {
4275
4358
  kind: "unresolved",
4276
- reason: `Ref '${name}': pseudo parameter not supported (supported: AWS::AccountId, AWS::Region, AWS::Partition, AWS::URLSuffix)`
4359
+ reason: `Ref ${displayIdent(name)}: pseudo parameter not supported (supported: AWS::AccountId, AWS::Region, AWS::Partition, AWS::URLSuffix)`
4277
4360
  };
4278
4361
  }
4279
4362
  return {
4280
4363
  kind: "unresolved",
4281
- reason: `Ref '${name}': pseudo parameter value not resolved`
4364
+ reason: `Ref ${displayIdent(name)}: pseudo parameter value not resolved`
4282
4365
  };
4283
4366
  }
4284
4367
  function resolveGetAtt(arg, context) {
@@ -4295,7 +4378,7 @@ function resolveGetAtt(arg, context) {
4295
4378
  const dot = arg.indexOf(".");
4296
4379
  if (dot <= 0 || dot === arg.length - 1) return {
4297
4380
  kind: "unresolved",
4298
- reason: `Fn::GetAtt string form must be '<LogicalId>.<Attribute>', got '${arg}'`
4381
+ reason: `Fn::GetAtt string form must be '<LogicalId>.<Attribute>', got ${displayIdent(arg)}`
4299
4382
  };
4300
4383
  logicalId = arg.slice(0, dot);
4301
4384
  attr = arg.slice(dot + 1);
@@ -4306,12 +4389,12 @@ function resolveGetAtt(arg, context) {
4306
4389
  const resource = context.resources[logicalId];
4307
4390
  if (!resource) return {
4308
4391
  kind: "unresolved",
4309
- reason: `Fn::GetAtt '${logicalId}.${attr}': no record in the state source`
4392
+ reason: `Fn::GetAtt ${displayIdent(`${logicalId}.${attr}`)}: no record in the state source`
4310
4393
  };
4311
4394
  const cached = resource.attributes?.[attr];
4312
4395
  if (cached === void 0) return {
4313
4396
  kind: "unresolved",
4314
- reason: `Fn::GetAtt '${logicalId}.${attr}': attribute not captured in the state source at deploy time`
4397
+ reason: `Fn::GetAtt ${displayIdent(`${logicalId}.${attr}`)}: attribute not captured in the state source at deploy time`
4315
4398
  };
4316
4399
  if (typeof cached === "string" || typeof cached === "number" || typeof cached === "boolean") return {
4317
4400
  kind: "literal",
@@ -4346,7 +4429,7 @@ function resolveSub(arg, context) {
4346
4429
  const sub = substituteAgainstState(bindings[placeholder], context);
4347
4430
  if (sub.kind !== "literal") return {
4348
4431
  kind: "unresolved",
4349
- reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
4432
+ reason: `Fn::Sub placeholder \${${displayIdent(placeholder)}}: ${sub.reason}`
4350
4433
  };
4351
4434
  resolutions.set(placeholder, String(sub.value));
4352
4435
  continue;
@@ -4355,7 +4438,7 @@ function resolveSub(arg, context) {
4355
4438
  const sub = resolvePseudoParameter(placeholder, context.pseudoParameters);
4356
4439
  if (sub.kind !== "literal") return {
4357
4440
  kind: "unresolved",
4358
- reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
4441
+ reason: `Fn::Sub placeholder \${${displayIdent(placeholder)}}: ${sub.reason}`
4359
4442
  };
4360
4443
  resolutions.set(placeholder, String(sub.value));
4361
4444
  continue;
@@ -4364,14 +4447,14 @@ function resolveSub(arg, context) {
4364
4447
  const sub = resolveRef(placeholder, context);
4365
4448
  if (sub.kind !== "literal") return {
4366
4449
  kind: "unresolved",
4367
- reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
4450
+ reason: `Fn::Sub placeholder \${${displayIdent(placeholder)}}: ${sub.reason}`
4368
4451
  };
4369
4452
  resolutions.set(placeholder, String(sub.value));
4370
4453
  } else {
4371
4454
  const sub = resolveGetAtt(placeholder, context);
4372
4455
  if (sub.kind !== "literal") return {
4373
4456
  kind: "unresolved",
4374
- reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
4457
+ reason: `Fn::Sub placeholder \${${displayIdent(placeholder)}}: ${sub.reason}`
4375
4458
  };
4376
4459
  resolutions.set(placeholder, String(sub.value));
4377
4460
  }
@@ -4510,7 +4593,7 @@ async function substituteAgainstStateAsync(value, contextOrResources) {
4510
4593
  if (intrinsic === "Fn::GetStackOutput") return resolveGetStackOutputAsync(arg, context);
4511
4594
  return {
4512
4595
  kind: "unresolved",
4513
- reason: `unsupported intrinsic '${intrinsic}' (supported: Ref, Fn::GetAtt, Fn::Sub, Fn::Join, Fn::Select, Fn::Split, Fn::ImportValue, Fn::GetStackOutput)`
4596
+ reason: `unsupported intrinsic ${displayIdent(intrinsic)} (supported: Ref, Fn::GetAtt, Fn::Sub, Fn::Join, Fn::Select, Fn::Split, Fn::ImportValue, Fn::GetStackOutput)`
4514
4597
  };
4515
4598
  }
4516
4599
  async function resolveImportValueAsync(arg, context) {
@@ -5012,7 +5095,7 @@ function parseEcsTarget(target) {
5012
5095
  function resolveEcsTaskTarget(target, stacks, context) {
5013
5096
  if (stacks.length === 0) throw new EcsTaskResolutionError("No stacks found in the synthesized assembly.");
5014
5097
  const parsed = parseEcsTarget(target);
5015
- const stack = pickStack$3(parsed, stacks);
5098
+ const stack = pickStack$3(parsed, stacks, target);
5016
5099
  const resources = stack.template.Resources ?? {};
5017
5100
  let logicalId;
5018
5101
  let resource;
@@ -5029,19 +5112,18 @@ function resolveEcsTaskTarget(target, stacks, context) {
5029
5112
  logicalId = parsed.pathOrId;
5030
5113
  }
5031
5114
  if (!logicalId || !resource) throw notFoundError$1(target, stack, resources);
5032
- if (resource.Type === "AWS::Lambda::Function") throw new EcsTaskResolutionError(`Resource '${logicalId}' in ${stack.stackName} is a Lambda function, not an ECS task definition. Use \`${getEmbedConfig().cliName} invoke\` for Lambda; \`${getEmbedConfig().cliName} run-task\` is ECS only.`);
5033
- if (resource.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`Resource '${logicalId}' in ${stack.stackName} is ${resource.Type}, not an AWS::ECS::TaskDefinition.`);
5115
+ if (resource.Type === "AWS::Lambda::Function") throw new EcsTaskResolutionError(`Resource ${displayIdent(logicalId)} in ${stack.stackName} is a Lambda function, not an ECS task definition. Use \`${getEmbedConfig().cliName} invoke\` for Lambda; \`${getEmbedConfig().cliName} run-task\` is ECS only.`);
5116
+ if (resource.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`Resource ${displayIdent(logicalId)} in ${stack.stackName} is ${displayIdent(resource.Type)}, not an AWS::ECS::TaskDefinition.`);
5034
5117
  return extractTaskDefinitionProperties(stack, logicalId, resource, context);
5035
5118
  }
5036
- function pickStack$3(parsed, stacks) {
5037
- if (parsed.stackPattern === null) {
5038
- if (stacks.length === 1) return stacks[0];
5039
- throw new EcsTaskResolutionError(`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(", ")}.`);
5119
+ function pickStack$3(parsed, stacks, target) {
5120
+ const selection = selectTargetStack(parsed, target, stacks);
5121
+ switch (selection.kind) {
5122
+ case "found": return selection.stack;
5123
+ case "no-prefix": throw new EcsTaskResolutionError(`Multiple stacks in app, target '${parsed.pathOrId}' is missing a stack prefix. Use 'StackName:${parsed.pathOrId}' or 'StackName/...' (path form). Available stacks: ${describeStacks(stacks)}.`);
5124
+ case "not-found": throw new EcsTaskResolutionError(`Stack '${selection.pattern}' not found. Available stacks: ${describeStacks(stacks)}.`);
5125
+ case "ambiguous": throw new EcsTaskResolutionError(`Stack pattern '${selection.pattern}' matched ${selection.matched.length} stacks: ` + describeStacks(selection.matched) + ". Use a more specific pattern, or the stack's display path (path form).");
5040
5126
  }
5041
- const matched = matchStacks(stacks, [parsed.stackPattern]);
5042
- if (matched.length === 0) throw new EcsTaskResolutionError(`Stack '${parsed.stackPattern}' not found. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
5043
- if (matched.length > 1) throw new EcsTaskResolutionError(`Stack pattern '${parsed.stackPattern}' matched ${matched.length} stacks: ` + matched.map((s) => s.stackName).join(", ") + ". Use a more specific pattern.");
5044
- return matched[0];
5045
5127
  }
5046
5128
  function extractTaskDefinitionProperties(stack, logicalId, resource, context) {
5047
5129
  const props = resource.Properties ?? {};
@@ -5050,22 +5132,22 @@ function extractTaskDefinitionProperties(stack, logicalId, resource, context) {
5050
5132
  const rawNetworkMode = pickString(props["NetworkMode"]) ?? "bridge";
5051
5133
  let networkMode;
5052
5134
  if (rawNetworkMode === "bridge" || rawNetworkMode === "awsvpc" || rawNetworkMode === "host" || rawNetworkMode === "none") networkMode = rawNetworkMode;
5053
- else throw new EcsTaskResolutionError(`Task definition '${logicalId}' has unsupported NetworkMode '${rawNetworkMode}'. Supported values: bridge / awsvpc / host / none.`);
5054
- if (networkMode === "awsvpc") warnings.push(`NetworkMode 'awsvpc' on '${logicalId}' is mapped to docker bridge locally — docker cannot emulate ENI-per-task. AWS SDK calls still reach public endpoints via the developer network.`);
5055
- if (props["ProxyConfiguration"]) warnings.push(`Task definition '${logicalId}' declares 'ProxyConfiguration' (custom Envoy / App Mesh bootstrap), which is NOT honored by '${getEmbedConfig().cliName} start-service' / '${getEmbedConfig().cliName} run-task' in v1. Local execution will run without the configured proxy. See design doc § 2 for the rationale.`);
5135
+ else throw new EcsTaskResolutionError(`Task definition ${displayIdent(logicalId)} has unsupported NetworkMode ${displayIdent(rawNetworkMode)}. Supported values: bridge / awsvpc / host / none.`);
5136
+ if (networkMode === "awsvpc") warnings.push(`NetworkMode 'awsvpc' on ${displayIdent(logicalId)} is mapped to docker bridge locally — docker cannot emulate ENI-per-task. AWS SDK calls still reach public endpoints via the developer network.`);
5137
+ if (props["ProxyConfiguration"]) warnings.push(`Task definition ${displayIdent(logicalId)} declares 'ProxyConfiguration' (custom Envoy / App Mesh bootstrap), which is NOT honored by '${getEmbedConfig().cliName} start-service' / '${getEmbedConfig().cliName} run-task' in v1. Local execution will run without the configured proxy. See design doc § 2 for the rationale.`);
5056
5138
  const resources = stack.template.Resources ?? {};
5057
5139
  const subContext = buildSubstitutionContextFromImageContext(context);
5058
5140
  const taskRoleArn = resolveRoleArn(props["TaskRoleArn"], resources, subContext);
5059
5141
  const executionRoleArn = resolveRoleArn(props["ExecutionRoleArn"], resources, subContext);
5060
5142
  const runtimePlatform = parseRuntimePlatform(props["RuntimePlatform"]);
5061
5143
  const rawContainers = props["ContainerDefinitions"];
5062
- if (!Array.isArray(rawContainers) || rawContainers.length === 0) throw new EcsTaskResolutionError(`Task definition '${logicalId}' has no ContainerDefinitions.`);
5144
+ if (!Array.isArray(rawContainers) || rawContainers.length === 0) throw new EcsTaskResolutionError(`Task definition ${displayIdent(logicalId)} has no ContainerDefinitions.`);
5063
5145
  const containers = rawContainers.map((c, idx) => parseContainerDefinition(c, idx, logicalId, resources, stack, context));
5064
- for (const ctr of containers) for (const w of ctr.warnings) warnings.push(`Container '${ctr.name}': ${w}`);
5146
+ for (const ctr of containers) for (const w of ctr.warnings) warnings.push(`Container ${displayIdent(ctr.name)}: ${w}`);
5065
5147
  const rawVolumes = props["Volumes"];
5066
5148
  const volumes = Array.isArray(rawVolumes) ? rawVolumes.map((v, idx) => parseVolume(v, idx, logicalId, subContext)) : [];
5067
5149
  const containerNames = new Set(containers.map((c) => c.name));
5068
- for (const c of containers) for (const d of c.dependsOn) if (!containerNames.has(d.containerName)) throw new EcsTaskResolutionError(`Container '${c.name}' depends on '${d.containerName}', which is not defined in task '${logicalId}'.`);
5150
+ for (const c of containers) for (const d of c.dependsOn) if (!containerNames.has(d.containerName)) throw new EcsTaskResolutionError(`Container ${displayIdent(c.name)} depends on ${displayIdent(d.containerName)}, which is not defined in task ${displayIdent(logicalId)}.`);
5069
5151
  const out = {
5070
5152
  stack,
5071
5153
  taskDefinitionLogicalId: logicalId,
@@ -5095,10 +5177,10 @@ function parseRuntimePlatform(value) {
5095
5177
  };
5096
5178
  }
5097
5179
  function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, context) {
5098
- if (!raw || typeof raw !== "object") throw new EcsTaskResolutionError(`Task '${taskLogicalId}' ContainerDefinitions[${idx}] is not an object.`);
5180
+ if (!raw || typeof raw !== "object") throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} ContainerDefinitions[${idx}] is not an object.`);
5099
5181
  const c = raw;
5100
5182
  const name = pickString(c["Name"]);
5101
- if (!name) throw new EcsTaskResolutionError(`Task '${taskLogicalId}' ContainerDefinitions[${idx}] has no Name.`);
5183
+ if (!name) throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} ContainerDefinitions[${idx}] has no Name.`);
5102
5184
  const image = parseContainerImage(c["Image"], name, taskLogicalId, resources, stack, context);
5103
5185
  const command = pickStringArray$1(c["Command"]);
5104
5186
  const entryPoint = pickStringArray$1(c["EntryPoint"]);
@@ -5202,7 +5284,7 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
5202
5284
  const containerName = pickString(d["ContainerName"]);
5203
5285
  const condition = pickString(d["Condition"]);
5204
5286
  if (!containerName || !condition) continue;
5205
- if (condition !== "START" && condition !== "COMPLETE" && condition !== "SUCCESS" && condition !== "HEALTHY") throw new EcsTaskResolutionError(`Container '${name}' has invalid DependsOn condition '${condition}'. Accepted values: START / COMPLETE / SUCCESS / HEALTHY.`);
5287
+ if (condition !== "START" && condition !== "COMPLETE" && condition !== "SUCCESS" && condition !== "HEALTHY") throw new EcsTaskResolutionError(`Container ${displayIdent(name)} has invalid DependsOn condition ${displayIdent(condition)}. Accepted values: START / COMPLETE / SUCCESS / HEALTHY.`);
5206
5288
  dependsOn.push({
5207
5289
  containerName,
5208
5290
  condition
@@ -5240,8 +5322,8 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
5240
5322
  });
5241
5323
  }
5242
5324
  const warnings = [];
5243
- for (const d of droppedEnvKeys) warnings.push(`Environment '${d.key}' dropped: ${d.reason}`);
5244
- for (const d of droppedSecretKeys) warnings.push(`Secret '${d.key}' dropped: ${d.reason}`);
5325
+ for (const d of droppedEnvKeys) warnings.push(`Environment ${displayIdent(d.key)} dropped: ${d.reason}`);
5326
+ for (const d of droppedSecretKeys) warnings.push(`Secret ${displayIdent(d.key)} dropped: ${d.reason}`);
5245
5327
  const out = {
5246
5328
  name,
5247
5329
  image,
@@ -5278,10 +5360,10 @@ function parseContainerImage(raw, containerName, taskLogicalId, resources, _stac
5278
5360
  if (getAttImage) return classifyResolvedImage(getAttImage);
5279
5361
  const joinResolved = tryResolveImageFnJoin(raw, resources, context);
5280
5362
  if (joinResolved.kind === "resolved") return classifyResolvedImage(joinResolved.uri);
5281
- if (joinResolved.kind === "needs-state") throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' references same-stack ECR repository '${joinResolved.repoLogicalId}' via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — ` + formatStateRemedy(context) + ", build via ContainerImage.fromAsset, or pin a public image.");
5282
- if (joinResolved.kind === "unsupported-join") throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an unsupported Fn::Join Image shape: ${joinResolved.reason}. ${getEmbedConfig().cliName} recognizes the canonical CDK 2.x ContainerImage.fromEcrRepository Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
5363
+ if (joinResolved.kind === "needs-state") throw new EcsTaskResolutionError(`Container ${displayIdent(containerName)} in task ${displayIdent(taskLogicalId)} references same-stack ECR repository ${displayIdent(joinResolved.repoLogicalId)} via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — ` + formatStateRemedy(context) + ", build via ContainerImage.fromAsset, or pin a public image.");
5364
+ if (joinResolved.kind === "unsupported-join") throw new EcsTaskResolutionError(`Container ${displayIdent(containerName)} in task ${displayIdent(taskLogicalId)} has an unsupported Fn::Join Image shape: ${joinResolved.reason}. ${getEmbedConfig().cliName} recognizes the canonical CDK 2.x ContainerImage.fromEcrRepository Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
5283
5365
  const flat = extractImageString(raw);
5284
- if (!flat) throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an unparseable Image property. ${getEmbedConfig().cliName} supports flat string images, single-key Fn::Sub bodies, and CDK-asset Image references.`);
5366
+ if (!flat) throw new EcsTaskResolutionError(`Container ${displayIdent(containerName)} in task ${displayIdent(taskLogicalId)} has an unparseable Image property. ${getEmbedConfig().cliName} supports flat string images, single-key Fn::Sub bodies, and CDK-asset Image references.`);
5285
5367
  if (flat.includes("cdk-hnb659fds-container-assets-")) {
5286
5368
  const hashMatch = /:([a-f0-9]{8,})$/.exec(flat);
5287
5369
  const out = { kind: "cdk-asset" };
@@ -5291,9 +5373,9 @@ function parseContainerImage(raw, containerName, taskLogicalId, resources, _stac
5291
5373
  const substituted = substituteImagePlaceholders(flat, resources, context);
5292
5374
  if (substituted.includes("${")) {
5293
5375
  const unresolvedRepoRef = findUnresolvedEcrRepositoryRef(substituted, resources);
5294
- if (unresolvedRepoRef) throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' references same-stack ECR repository '${unresolvedRepoRef}'. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — ` + formatStateRemedy(context) + ", build via ContainerImage.fromAsset, or pin a public image.");
5295
- if (substituted.includes("AWS::")) throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an Image that references AWS pseudo parameters (${substituted}). ${getEmbedConfig().productName} could not resolve them: confirm AWS credentials are configured so STS GetCallerIdentity succeeds, and that --region / AWS_REGION names the target region. Workaround: build the image locally (ContainerImage.fromAsset) or pin a public image.`);
5296
- throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an Image with unresolved \${...} placeholders (${substituted}). ${getEmbedConfig().cliName} only resolves AWS pseudo parameters and same-stack AWS::ECR::Repository refs.`);
5376
+ if (unresolvedRepoRef) throw new EcsTaskResolutionError(`Container ${displayIdent(containerName)} in task ${displayIdent(taskLogicalId)} references same-stack ECR repository ${displayIdent(unresolvedRepoRef)}. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — ` + formatStateRemedy(context) + ", build via ContainerImage.fromAsset, or pin a public image.");
5377
+ if (substituted.includes("AWS::")) throw new EcsTaskResolutionError(`Container ${displayIdent(containerName)} in task ${displayIdent(taskLogicalId)} has an Image that references AWS pseudo parameters (${displayUntrustedValue(substituted)}). ${getEmbedConfig().productName} could not resolve them: confirm AWS credentials are configured so STS GetCallerIdentity succeeds, and that --region / AWS_REGION names the target region. Workaround: build the image locally (ContainerImage.fromAsset) or pin a public image.`);
5378
+ throw new EcsTaskResolutionError(`Container ${displayIdent(containerName)} in task ${displayIdent(taskLogicalId)} has an Image with unresolved \${...} placeholders (${displayUntrustedValue(substituted)}). ${getEmbedConfig().cliName} only resolves AWS pseudo parameters and same-stack AWS::ECR::Repository refs.`);
5297
5379
  }
5298
5380
  const ecrHost = parseEcrRegistryHost(substituted);
5299
5381
  if (ecrHost) return {
@@ -5375,12 +5457,12 @@ function extractImageString(value) {
5375
5457
  }
5376
5458
  }
5377
5459
  function parseVolume(raw, idx, taskLogicalId, subContext) {
5378
- if (!raw || typeof raw !== "object") throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] is not an object.`);
5460
+ if (!raw || typeof raw !== "object") throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] is not an object.`);
5379
5461
  const v = raw;
5380
5462
  const name = pickString(v["Name"]);
5381
- if (!name) throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] has no Name.`);
5382
- if (v["EFSVolumeConfiguration"]) throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] '${name}' uses EFSVolumeConfiguration, which ${getEmbedConfig().cliName} 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.`);
5383
- if (v["FSxWindowsFileServerVolumeConfiguration"]) throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] '${name}' uses FSxWindowsFileServerVolumeConfiguration, which ${getEmbedConfig().cliName} cannot proxy locally.`);
5463
+ if (!name) throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] has no Name.`);
5464
+ if (v["EFSVolumeConfiguration"]) throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] ${displayIdent(name)} uses EFSVolumeConfiguration, which ${getEmbedConfig().cliName} 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.`);
5465
+ if (v["FSxWindowsFileServerVolumeConfiguration"]) throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] ${displayIdent(name)} uses FSxWindowsFileServerVolumeConfiguration, which ${getEmbedConfig().cliName} cannot proxy locally.`);
5384
5466
  const dockerCfg = v["DockerVolumeConfiguration"];
5385
5467
  if (dockerCfg && typeof dockerCfg === "object") {
5386
5468
  const d = dockerCfg;
@@ -5422,11 +5504,11 @@ function parseVolume(raw, idx, taskLogicalId, subContext) {
5422
5504
  function resolveVolumeSourcePath(raw, volumeName, idx, taskLogicalId, subContext) {
5423
5505
  if (raw === void 0 || raw === null) return void 0;
5424
5506
  if (typeof raw === "string") return raw.length > 0 ? raw : void 0;
5425
- if (typeof raw !== "object") throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] '${volumeName}' Host.SourcePath must be a string or a CFn intrinsic, got ${typeof raw}.`);
5426
- if (!subContext) throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] '${volumeName}' Host.SourcePath is a CFn intrinsic (Ref/Fn::Sub/Fn::Join/Fn::GetAtt). Pass --from-state to substitute it against the deployed state + AWS pseudo parameters.`);
5507
+ if (typeof raw !== "object") throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] ${displayIdent(volumeName)} Host.SourcePath must be a string or a CFn intrinsic, got ${typeof raw}.`);
5508
+ if (!subContext) throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] ${displayIdent(volumeName)} Host.SourcePath is a CFn intrinsic (Ref/Fn::Sub/Fn::Join/Fn::GetAtt). Pass --from-state to substitute it against the deployed state + AWS pseudo parameters.`);
5427
5509
  const sub = substituteAgainstState(raw, subContext);
5428
5510
  if (sub.kind === "literal" && typeof sub.value === "string" && sub.value.length > 0) return sub.value;
5429
- throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] '${volumeName}' Host.SourcePath could not be resolved: ` + (sub.kind === "literal" ? "resolved to non-string / empty value" : sub.reason));
5511
+ throw new EcsTaskResolutionError(`Task ${displayIdent(taskLogicalId)} Volumes[${idx}] ${displayIdent(volumeName)} Host.SourcePath could not be resolved: ` + (sub.kind === "literal" ? "resolved to non-string / empty value" : sub.reason));
5430
5512
  }
5431
5513
  const TASK_ROLE_ACCOUNT_PLACEHOLDER = "${AWS::AccountId}";
5432
5514
  function resolveRoleArn(value, resources, subContext) {
@@ -5479,7 +5561,7 @@ function notFoundError$1(target, stack, resources) {
5479
5561
  else {
5480
5562
  const width = Math.max(...tasks.map((t) => t.displayPath.length));
5481
5563
  msg += `Available task definitions in ${stack.stackName}:\n`;
5482
- for (const t of tasks) msg += ` ${t.displayPath.padEnd(width)} (${t.logicalId})\n`;
5564
+ for (const t of tasks) msg += ` ${t.displayPath.padEnd(width)} (${displayIdent(t.logicalId)})\n`;
5483
5565
  }
5484
5566
  return new EcsTaskResolutionError(msg.trimEnd());
5485
5567
  }
@@ -5539,13 +5621,13 @@ async function applyCrossStackResolverToTask(task, context) {
5539
5621
  }
5540
5622
  if (resolvedEnvKeys.size > 0 || resolvedSecretNames.size > 0) {
5541
5623
  container.warnings = container.warnings.filter((w) => {
5542
- for (const k of resolvedEnvKeys) if (w.startsWith(`Environment '${k}' dropped:`)) return false;
5543
- for (const k of resolvedSecretNames) if (w.startsWith(`Secret '${k}' dropped:`)) return false;
5624
+ for (const k of resolvedEnvKeys) if (w.startsWith(`Environment ${displayIdent(k)} dropped:`)) return false;
5625
+ for (const k of resolvedSecretNames) if (w.startsWith(`Secret ${displayIdent(k)} dropped:`)) return false;
5544
5626
  return true;
5545
5627
  });
5546
5628
  task.warnings = task.warnings.filter((w) => {
5547
- for (const k of resolvedEnvKeys) if (w.startsWith(`Container '${containerName}': Environment '${k}' dropped:`)) return false;
5548
- for (const k of resolvedSecretNames) if (w.startsWith(`Container '${containerName}': Secret '${k}' dropped:`)) return false;
5629
+ for (const k of resolvedEnvKeys) if (w.startsWith(`Container ${displayIdent(containerName)}: Environment ${displayIdent(k)} dropped:`)) return false;
5630
+ for (const k of resolvedSecretNames) if (w.startsWith(`Container ${displayIdent(containerName)}: Secret ${displayIdent(k)} dropped:`)) return false;
5549
5631
  return true;
5550
5632
  });
5551
5633
  }
@@ -5642,7 +5724,7 @@ function resolveRuntimeImage(runtime) {
5642
5724
  }
5643
5725
  function resolveRuntimeFileExtension(runtime) {
5644
5726
  const spec = resolveRuntimeSpec(runtime);
5645
- if (spec.fileExtension === null) throw new UnsupportedRuntimeError(runtime, `Inline 'Code.ZipFile' is not supported for runtime '${runtime}'. The Lambda Handler shape for this runtime names a compiled artifact (a JVM class, a .NET assembly, or — for the OS-only \`provided.*\` runtimes — an arbitrary \`bootstrap\` binary) that cannot be expressed as a single inline source file. Use \`lambda.Code.fromAsset(<dir>)\` with a directory containing the compiled output (.class hierarchy / JAR / DLL / native binary).`);
5727
+ if (spec.fileExtension === null) throw new UnsupportedRuntimeError(runtime, `Inline 'Code.ZipFile' is not supported for runtime ${displayIdent(runtime)}. The Lambda Handler shape for this runtime names a compiled artifact (a JVM class, a .NET assembly, or — for the OS-only \`provided.*\` runtimes — an arbitrary \`bootstrap\` binary) that cannot be expressed as a single inline source file. Use \`lambda.Code.fromAsset(<dir>)\` with a directory containing the compiled output (.class hierarchy / JAR / DLL / native binary).`);
5646
5728
  return spec.fileExtension;
5647
5729
  }
5648
5730
  function resolveRuntimeSpec(runtime) {
@@ -5650,7 +5732,7 @@ function resolveRuntimeSpec(runtime) {
5650
5732
  const spec = SUPPORTED_RUNTIMES[runtime];
5651
5733
  if (spec) return spec;
5652
5734
  if (runtime === "go1.x") throw new UnsupportedRuntimeError(runtime, "Runtime 'go1.x' was deprecated by AWS Lambda on 2024-01-08 and is no longer available. Migrate to the OS-only runtime: build your Go program as a `bootstrap` binary and set the CDK runtime to `lambda.Runtime.PROVIDED_AL2023` (or `lambda.Runtime.PROVIDED_AL2`). See https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html");
5653
- throw new UnsupportedRuntimeError(runtime, `Unknown runtime '${runtime}'. ${getEmbedConfig().cliName} invoke supports nodejs18.x / nodejs20.x / nodejs22.x / nodejs24.x / python3.11 / python3.12 / python3.13 / python3.14 / ruby3.2 / ruby3.3 / java8.al2 / java11 / java17 / java21 / dotnet6 / dotnet8 / provided.al2 / provided.al2023.`);
5735
+ throw new UnsupportedRuntimeError(runtime, `Unknown runtime ${displayIdent(runtime)}. ${getEmbedConfig().cliName} invoke supports nodejs18.x / nodejs20.x / nodejs22.x / nodejs24.x / python3.11 / python3.12 / python3.13 / python3.14 / ruby3.2 / ruby3.3 / java8.al2 / java11 / java17 / java21 / dotnet6 / dotnet8 / provided.al2 / provided.al2023.`);
5654
5736
  }
5655
5737
  function resolveRuntimeCodeMountPath(runtime) {
5656
5738
  resolveRuntimeSpec(runtime);
@@ -6101,7 +6183,7 @@ async function buildDockerImage(asset, cdkOutDir, options) {
6101
6183
  result = await spawnStreaming(cmd, args, { cwd });
6102
6184
  } catch (err) {
6103
6185
  const e = err;
6104
- throw options.wrapError(e.stderr || e.message || String(err));
6186
+ throw options.wrapError(displayToolOutput(e.stderr || e.message || String(err)));
6105
6187
  }
6106
6188
  const tag = result.stdout.trim();
6107
6189
  if (!tag) throw options.wrapError(`docker build executable produced no output (expected the local image tag on stdout): ${displayUntrustedValue(cmd)}`);
@@ -6113,7 +6195,7 @@ async function buildDockerImage(asset, cdkOutDir, options) {
6113
6195
  const contextDir = contextDirectory(source.directory, `send that directory to docker build as the context of an image ${getEmbedConfig().productName} then runs locally`);
6114
6196
  buildArgs.push(".");
6115
6197
  warnEscapingBuildKitPaths(source, contextDir, assetOutdir);
6116
- logger.debug(`${getDockerCmd()} ${redactDockerArgvValues(buildArgs).join(" ")} (cwd=${displayUntrustedValue(contextDir)})`);
6198
+ logger.debug(`${getDockerCmd()} ${redactDockerArgvValues(buildArgs).map((a) => displayUntrustedValue(a)).join(" ")} (cwd=${displayUntrustedValue(contextDir)})`);
6117
6199
  try {
6118
6200
  await runDockerStreaming(buildArgs, {
6119
6201
  cwd: contextDir,
@@ -6122,7 +6204,7 @@ async function buildDockerImage(asset, cdkOutDir, options) {
6122
6204
  });
6123
6205
  } catch (err) {
6124
6206
  const e = err;
6125
- throw options.wrapError(e.stderr || e.message || String(err));
6207
+ throw options.wrapError(displayToolOutput(e.stderr || e.message || String(err)));
6126
6208
  }
6127
6209
  return options.tag;
6128
6210
  }
@@ -6175,7 +6257,7 @@ function isCredentialFresh(creds) {
6175
6257
  async function pullEcrImage(imageUri, options) {
6176
6258
  const logger = getLogger().child("ecr-puller");
6177
6259
  const parsed = parseEcrUri(imageUri);
6178
- if (!parsed) throw new LocalInvokeBuildError(`Image URI '${imageUri}' is not an ECR URI. ${getEmbedConfig().cliName} invoke v1 only authenticates against ECR for the deployed-image fallback path.`);
6260
+ if (!parsed) throw new LocalInvokeBuildError(`Image URI ${displayUntrustedValue(imageUri)} is not an ECR URI. ${getEmbedConfig().cliName} invoke v1 only authenticates against ECR for the deployed-image fallback path.`);
6179
6261
  const canonicalUri = parsed.canonicalUri;
6180
6262
  const callerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"];
6181
6263
  if (options.skipPull) {
@@ -6299,7 +6381,7 @@ async function verifyImageInLocalCache(imageUri) {
6299
6381
  imageUri
6300
6382
  ]);
6301
6383
  } catch {
6302
- throw new LocalInvokeBuildError(`Image '${imageUri}' is not in the local docker cache and --no-pull was set. Either remove --no-pull (${getEmbedConfig().productName} will pull from ECR) or pre-pull the image manually with \`docker pull\`.`);
6384
+ throw new LocalInvokeBuildError(`Image ${displayUntrustedValue(imageUri)} is not in the local docker cache and --no-pull was set. Either remove --no-pull (${getEmbedConfig().productName} will pull from ECR) or pre-pull the image manually with \`docker pull\`.`);
6303
6385
  }
6304
6386
  }
6305
6387
  async function isImageInLocalCache(imageRef) {
@@ -6323,7 +6405,7 @@ async function buildContainerImage(asset, cdkOutDir, options) {
6323
6405
  const logger = getLogger().child("local-invoke-build");
6324
6406
  if (options.noBuild === true) {
6325
6407
  logger.info(`Skipping docker build (--no-build). Verifying ${tag} is in local registry...`);
6326
- if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image '${tag}' not in local registry and --no-build is set; remove --no-build or run \`docker build\` manually first.`);
6408
+ if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image ${displayIdent(tag)} not in local registry and --no-build is set; remove --no-build or run \`docker build\` manually first.`);
6327
6409
  logger.debug(`Local tag ${tag} is cached; skipping build.`);
6328
6410
  return tag;
6329
6411
  }
@@ -6337,7 +6419,7 @@ async function buildContainerImage(asset, cdkOutDir, options) {
6337
6419
  ...options.assetOutdir !== void 0 && { assetOutdir: options.assetOutdir }
6338
6420
  });
6339
6421
  if (actualTag !== tag) {
6340
- logger.debug(`Re-tagging executable-built image '${actualTag}' → '${tag}'`);
6422
+ logger.debug(`Re-tagging executable-built image ${displayIdent(actualTag)} → ${displayIdent(tag)}`);
6341
6423
  try {
6342
6424
  await runDockerStreaming([
6343
6425
  "tag",
@@ -6346,7 +6428,7 @@ async function buildContainerImage(asset, cdkOutDir, options) {
6346
6428
  ]);
6347
6429
  } catch (err) {
6348
6430
  const e = err;
6349
- throw new LocalInvokeBuildError(`docker tag failed re-tagging '${actualTag}' → '${tag}': ${e.stderr?.trim() || e.message || String(err)}`);
6431
+ throw new LocalInvokeBuildError(`docker tag failed re-tagging ${displayIdent(actualTag)} → ${displayIdent(tag)}: ${displayToolOutput(e.stderr?.trim() || e.message || String(err))}`);
6350
6432
  }
6351
6433
  }
6352
6434
  return tag;
@@ -6365,7 +6447,7 @@ function warnIfEmulatedPlatform(platform, opts = {}) {
6365
6447
  if (warnedEmulatedPlatforms.has(platform)) return;
6366
6448
  warnedEmulatedPlatforms.add(platform);
6367
6449
  const logger = opts.logger ?? getLogger();
6368
- const what = opts.label ? `'${opts.label}' ` : "";
6450
+ const what = opts.label ? `${displayIdent(opts.label)} ` : "";
6369
6451
  logger.warn(`Running ${what}under ${platform} emulation on a ${host} host (the declared architecture differs from the host). Emulation is slower and some compiled binaries can fail under it. If the container does not run correctly, enable Rosetta for x86/amd64 emulation in your Docker engine settings, or build the function for ${host}.`);
6370
6452
  }
6371
6453
  function computeLocalTag(source) {
@@ -6417,12 +6499,12 @@ var DockerRunnerError = class DockerRunnerError extends Error {
6417
6499
  async function pullImage(image, skipPull, platform) {
6418
6500
  const logger = getLogger().child("docker");
6419
6501
  if (skipPull) {
6420
- logger.debug(`Skipping docker pull for ${image} (--no-pull)`);
6502
+ logger.debug(`Skipping docker pull for ${displayIdent(image)} (--no-pull)`);
6421
6503
  return;
6422
6504
  }
6423
6505
  const platformArgs = platform ? ["--platform", platform] : [];
6424
6506
  if (getLogger().getLevel() === "debug") {
6425
- logger.info(`Pulling ${image}${platform ? ` (${platform})` : ""}...`);
6507
+ logger.info(`Pulling ${displayIdent(image)}${platform ? ` (${displayIdent(platform)})` : ""}...`);
6426
6508
  try {
6427
6509
  await runDockerForeground([
6428
6510
  "pull",
@@ -6430,11 +6512,12 @@ async function pullImage(image, skipPull, platform) {
6430
6512
  image
6431
6513
  ]);
6432
6514
  } catch (err) {
6433
- throw new DockerRunnerError(`docker pull ${image} failed: ${err.message}`);
6515
+ const e = err;
6516
+ throw new DockerRunnerError(`docker pull ${displayIdent(image)} failed: ${e.message}`);
6434
6517
  }
6435
6518
  return;
6436
6519
  }
6437
- logger.debug(`Pulling ${image} (silent — pass --verbose to stream progress)`);
6520
+ logger.debug(`Pulling ${displayIdent(image)} (silent — pass --verbose to stream progress)`);
6438
6521
  try {
6439
6522
  await runDockerStreaming([
6440
6523
  "pull",
@@ -6442,13 +6525,13 @@ async function pullImage(image, skipPull, platform) {
6442
6525
  image
6443
6526
  ], {
6444
6527
  streamLive: false,
6445
- progressLabel: `Pulling ${image}`
6528
+ progressLabel: `Pulling ${displayIdent(image)}`
6446
6529
  });
6447
6530
  } catch (err) {
6448
6531
  const e = err;
6449
- if (e.exitCode === void 0 || e.exitCode === null) throw new DockerRunnerError(`docker pull ${image} failed: ${e.message ?? String(err)}`);
6532
+ if (e.exitCode === void 0 || e.exitCode === null) throw new DockerRunnerError(`docker pull ${displayIdent(image)} failed: ${displayToolOutput(e.message ?? String(err))}`);
6450
6533
  const detail = e.stderr?.trim() || e.stdout?.trim() || "(no output)";
6451
- throw new DockerRunnerError(`docker pull ${image} exited with code ${e.exitCode}: ${detail}`);
6534
+ throw new DockerRunnerError(`docker pull ${displayIdent(image)} exited with code ${e.exitCode}: ${displayToolOutput(detail)}`);
6452
6535
  }
6453
6536
  }
6454
6537
  async function runDetached(opts) {
@@ -6486,7 +6569,7 @@ async function runDetached(opts) {
6486
6569
  }
6487
6570
  args.push(opts.image, ...entryPointTail, ...opts.cmd);
6488
6571
  const logger = getLogger().child("docker");
6489
- logger.debug(flattenToOneLine(`${getDockerCmd()} ${redactDockerArgvValues(args).join(" ")}`));
6572
+ logger.debug(`${getDockerCmd()} ${redactDockerArgvValues(args).map((a) => displayUntrustedValue(a)).join(" ")}`);
6490
6573
  if (collisions.length > 0) logger.warn(describeRefusedSensitiveEnvKeys(`Container for image ${displayUntrustedValue(opts.image)}`, collisions));
6491
6574
  const forwardedKeys = Object.keys(passthroughEnv);
6492
6575
  const refusal = finchSecretArgvRefusal(forwardedKeys.filter((k) => opts.sensitiveEnvKeys?.has(k)), {
@@ -6502,8 +6585,40 @@ async function runDetached(opts) {
6502
6585
  });
6503
6586
  return stdout.trim();
6504
6587
  } catch (error) {
6505
- throw new DockerRunnerError(`docker run failed: ${error.stderr?.trim() || describeExitWithoutArgv(error)}`);
6506
- }
6588
+ throw new DockerRunnerError(`docker run failed: ${displayToolOutput(error.stderr?.trim() || describeExitWithoutArgv(error))}`);
6589
+ }
6590
+ }
6591
+ const PREFIX_CONTAINER_OUTPUT_ENV = "CDKL_PREFIX_CONTAINER_OUTPUT";
6592
+ const MAX_HELD_LINE_CHARS = 64 * 1024;
6593
+ function createPrefixedLineRelay(prefix, out) {
6594
+ const decoder = new StringDecoder("utf8");
6595
+ let pending = "";
6596
+ const emit = (text) => {
6597
+ let nl = text.indexOf("\n");
6598
+ if (nl !== -1) nl += pending.length;
6599
+ pending += text;
6600
+ while (nl !== -1) {
6601
+ out.write(`${prefix}${pending.slice(0, nl + 1)}`);
6602
+ pending = pending.slice(nl + 1);
6603
+ nl = pending.indexOf("\n");
6604
+ }
6605
+ while (pending.length >= MAX_HELD_LINE_CHARS) {
6606
+ const high = pending.charCodeAt(MAX_HELD_LINE_CHARS - 1);
6607
+ const cut = high >= 55296 && high <= 56319 ? MAX_HELD_LINE_CHARS - 1 : MAX_HELD_LINE_CHARS;
6608
+ out.write(`${prefix}${pending.slice(0, cut)}\n`);
6609
+ pending = pending.slice(cut);
6610
+ }
6611
+ };
6612
+ return {
6613
+ push: (chunk) => emit(decoder.write(chunk)),
6614
+ flush: () => {
6615
+ emit(decoder.end());
6616
+ if (pending.length > 0) {
6617
+ out.write(`${prefix}${pending}\n`);
6618
+ pending = "";
6619
+ }
6620
+ }
6621
+ };
6507
6622
  }
6508
6623
  function streamLogs(containerId) {
6509
6624
  const proc = spawn(getDockerCmd(), [
@@ -6515,10 +6630,25 @@ function streamLogs(containerId) {
6515
6630
  "pipe",
6516
6631
  "pipe"
6517
6632
  ] });
6518
- proc.stdout?.on("data", (chunk) => process.stdout.write(chunk));
6519
- proc.stderr?.on("data", (chunk) => process.stderr.write(chunk));
6633
+ let flush = () => void 0;
6634
+ if (process.env["CDKL_PREFIX_CONTAINER_OUTPUT"] === "1") {
6635
+ const tag = `[${flattenToOneLine(containerId.slice(0, 12)).replace(/[\]\s]/g, "_")}] `;
6636
+ const stdoutRelay = createPrefixedLineRelay(tag, process.stdout);
6637
+ const stderrRelay = createPrefixedLineRelay(tag, process.stderr);
6638
+ proc.stdout?.on("data", (chunk) => stdoutRelay.push(chunk));
6639
+ proc.stderr?.on("data", (chunk) => stderrRelay.push(chunk));
6640
+ flush = () => {
6641
+ stdoutRelay.flush();
6642
+ stderrRelay.flush();
6643
+ };
6644
+ proc.on("close", () => flush());
6645
+ } else {
6646
+ proc.stdout?.on("data", (chunk) => process.stdout.write(chunk));
6647
+ proc.stderr?.on("data", (chunk) => process.stderr.write(chunk));
6648
+ }
6520
6649
  proc.on("error", () => {});
6521
6650
  return () => {
6651
+ flush();
6522
6652
  if (!proc.killed) proc.kill("SIGTERM");
6523
6653
  };
6524
6654
  }
@@ -6604,29 +6734,33 @@ function execEnvForSecrets(passthrough) {
6604
6734
  //#region src/assets/asset-manifest-loader.ts
6605
6735
  var AssetManifestLoader = class {
6606
6736
  logger = getLogger().child("AssetManifestLoader");
6607
- async loadManifest(cdkOutputDir, stackName) {
6608
- const resolved = resolveAssemblyPath(cdkOutputDir, `${stackName}.assets.json`);
6737
+ async loadStackManifest(stack, fallbackDir) {
6738
+ if (stack.assetManifestPath) return this.loadManifest(dirname$1(stack.assetManifestPath), stack.stackName, basename$1(stack.assetManifestPath));
6739
+ return this.loadManifest(fallbackDir, stack.stackName);
6740
+ }
6741
+ async loadManifest(cdkOutputDir, stackName, fileName = `${stackName}.assets.json`) {
6742
+ const resolved = resolveAssemblyPath(cdkOutputDir, fileName);
6609
6743
  if (!resolved.contained) throw new Error(`Refusing to read the asset manifest for stack ${displayUntrustedValue(stackName)}: it ` + renderAssemblyPathEscape(resolved, cdkOutputDir, "read it"));
6610
- const manifestPath = join$1(cdkOutputDir, `${stackName}.assets.json`);
6744
+ const manifestPath = join$1(cdkOutputDir, fileName);
6611
6745
  try {
6612
- this.logger.debug(`Loading asset manifest from: ${manifestPath}`);
6746
+ this.logger.debug(`Loading asset manifest from: ${displayUntrustedValue(manifestPath)}`);
6613
6747
  const content = await readFile(manifestPath, "utf-8");
6614
6748
  const manifest = JSON.parse(content);
6615
6749
  this.logger.debug(`Loaded asset manifest: ${Object.keys(manifest.files).length} file assets, ${Object.keys(manifest.dockerImages).length} docker image assets`);
6616
6750
  return manifest;
6617
6751
  } catch (error) {
6618
6752
  if (error.code === "ENOENT") {
6619
- this.logger.debug(`Asset manifest not found: ${manifestPath}`);
6753
+ this.logger.debug(`Asset manifest not found: ${displayUntrustedValue(manifestPath)}`);
6620
6754
  return null;
6621
6755
  }
6622
- throw new Error(`Failed to load asset manifest from ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`);
6756
+ throw new Error(`Failed to load asset manifest from ${displayUntrustedValue(manifestPath)}: ${error instanceof Error ? error.message : String(error)}`);
6623
6757
  }
6624
6758
  }
6625
6759
  getFileAssets(manifest) {
6626
6760
  const fileAssets = /* @__PURE__ */ new Map();
6627
6761
  for (const [assetHash, asset] of Object.entries(manifest.files)) {
6628
6762
  if (asset.source.path.endsWith(".json") || asset.source.path.endsWith(".template.json")) {
6629
- this.logger.debug(`Skipping CloudFormation template asset: ${asset.displayName}`);
6763
+ this.logger.debug(`Skipping CloudFormation template asset: ${displayStackName(asset.displayName)}`);
6630
6764
  continue;
6631
6765
  }
6632
6766
  fileAssets.set(assetHash, asset);
@@ -7226,7 +7360,7 @@ function attachWebSocketServer(opts) {
7226
7360
  try {
7227
7361
  ws.close(1008, "Forbidden");
7228
7362
  } catch {}
7229
- logger.info(`WebSocket $connect denied for connection ${connectionId} on ${cfg.api.declaredAt} — authorizer returned a non-allow verdict. Client was closed with code 1008.`);
7363
+ logger.info(`WebSocket $connect denied for connection ${connectionId} on ${displayStackName(cfg.api.declaredAt)} — authorizer returned a non-allow verdict. Client was closed with code 1008.`);
7230
7364
  return;
7231
7365
  }
7232
7366
  }
@@ -7245,7 +7379,7 @@ function attachWebSocketServer(opts) {
7245
7379
  stage: cfg.api.stage
7246
7380
  };
7247
7381
  registry.register(entry);
7248
- logger.debug(`WebSocket connected: ${connectionId} (${cfg.api.declaredAt}, stage=${cfg.api.stage})`);
7382
+ logger.debug(`WebSocket connected: ${connectionId} (${displayStackName(cfg.api.declaredAt)}, stage=${cfg.api.stage})`);
7249
7383
  ws.on("message", (raw, isBinary) => {
7250
7384
  const { body, isBase64Encoded } = bufferToBody(raw, isBinary);
7251
7385
  logger.debug(`WebSocket message received for connection ${connectionId}: ${body.slice(0, 200)}`);
@@ -7636,10 +7770,10 @@ var VtlEvaluator = class {
7636
7770
  handleSetDirective(block, after) {
7637
7771
  const { args, end } = this.readParenArgs(block, after);
7638
7772
  const eq = args.indexOf("=");
7639
- if (eq === -1) throw new VtlEvaluationError(`#set requires '=': got #set(${args})`);
7773
+ if (eq === -1) throw new VtlEvaluationError(`#set requires '=': got #set(${displayUntrustedValue(args)})`);
7640
7774
  const left = args.slice(0, eq).trim();
7641
7775
  const right = args.slice(eq + 1).trim();
7642
- if (!left.startsWith("$")) throw new VtlEvaluationError(`#set left side must be a $var reference (got '${left}')`);
7776
+ if (!left.startsWith("$")) throw new VtlEvaluationError(`#set left side must be a $var reference (got ${displayUntrustedValue(left)})`);
7643
7777
  const varName = left.slice(1).replace(/^\{/, "").replace(/\}$/, "");
7644
7778
  const value = this.evaluateExpression(right);
7645
7779
  this.scopes[this.scopes.length - 1].set(varName, value);
@@ -7923,7 +8057,7 @@ var VtlEvaluator = class {
7923
8057
  return this.callValueAsMethod(value, argsRaw, refPath);
7924
8058
  }
7925
8059
  }
7926
- throw new VtlEvaluationError(`Could not evaluate VTL sub-expression: '${trimmed}'`);
8060
+ throw new VtlEvaluationError(`Could not evaluate VTL sub-expression: ${displayUntrustedValue(trimmed)}`);
7927
8061
  }
7928
8062
  unescapeStringLiteral(s) {
7929
8063
  return s.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\\\/g, "\\").replace(/\\"/g, "\"").replace(/\\'/g, "'");
@@ -8103,7 +8237,7 @@ function buildVtlInput(body, headers, querystring, pathParams) {
8103
8237
  function applyJsonPath(root, expr) {
8104
8238
  const trimmed = expr.trim();
8105
8239
  if (trimmed === "$" || trimmed.length === 0) return root;
8106
- if (!trimmed.startsWith("$")) throw new VtlEvaluationError(`JSONPath must start with '$': got '${trimmed}'`);
8240
+ if (!trimmed.startsWith("$")) throw new VtlEvaluationError(`JSONPath must start with '$': got ${displayUntrustedValue(trimmed)}`);
8107
8241
  let cursor = root;
8108
8242
  let i = 1;
8109
8243
  while (i < trimmed.length) {
@@ -8111,25 +8245,25 @@ function applyJsonPath(root, expr) {
8111
8245
  if (c === ".") {
8112
8246
  i++;
8113
8247
  const m = /^[a-zA-Z_][a-zA-Z_0-9]*/.exec(trimmed.slice(i));
8114
- if (!m) throw new VtlEvaluationError(`Unsupported JSONPath syntax at position ${i}: '${trimmed}' (${getEmbedConfig().productName} supports $, $.field, $.field.sub, $.array[index] only).`);
8248
+ if (!m) throw new VtlEvaluationError(`Unsupported JSONPath syntax at position ${i}: ${displayUntrustedValue(trimmed)} (${getEmbedConfig().productName} supports $, $.field, $.field.sub, $.array[index] only).`);
8115
8249
  cursor = lookupField(cursor, m[0]);
8116
8250
  i += m[0].length;
8117
8251
  continue;
8118
8252
  }
8119
8253
  if (c === "[") {
8120
8254
  const close = trimmed.indexOf("]", i);
8121
- if (close === -1) throw new VtlEvaluationError(`Unterminated [ in JSONPath: '${trimmed}'`);
8255
+ if (close === -1) throw new VtlEvaluationError(`Unterminated [ in JSONPath: ${displayUntrustedValue(trimmed)}`);
8122
8256
  const inside = trimmed.slice(i + 1, close).trim();
8123
8257
  if (/^-?\d+$/.test(inside)) {
8124
8258
  const idx = Number(inside);
8125
8259
  if (Array.isArray(cursor)) cursor = cursor[idx];
8126
8260
  else cursor = null;
8127
8261
  } else if (inside.startsWith("\"") && inside.endsWith("\"") || inside.startsWith("'") && inside.endsWith("'")) cursor = lookupField(cursor, inside.slice(1, -1));
8128
- else throw new VtlEvaluationError(`Unsupported JSONPath bracket expression: '${inside}' (${getEmbedConfig().productName} supports integer indices and quoted string keys only).`);
8262
+ else throw new VtlEvaluationError(`Unsupported JSONPath bracket expression: ${displayUntrustedValue(inside)} (${getEmbedConfig().productName} supports integer indices and quoted string keys only).`);
8129
8263
  i = close + 1;
8130
8264
  continue;
8131
8265
  }
8132
- throw new VtlEvaluationError(`Unexpected character in JSONPath at position ${i}: '${trimmed}'`);
8266
+ throw new VtlEvaluationError(`Unexpected character in JSONPath at position ${i}: ${displayUntrustedValue(trimmed)}`);
8133
8267
  }
8134
8268
  return cursor;
8135
8269
  }
@@ -8203,7 +8337,7 @@ function evaluateResponseParameters(responseParameters, opts = {}) {
8203
8337
  out[headerName] = value.slice(1, -1);
8204
8338
  continue;
8205
8339
  }
8206
- opts.onUnsupported?.(key, value, `ResponseParameter value '${value}' is a mapping expression (integration.response.* / context.*) which ${getEmbedConfig().cliName} start-api does not emulate. Only single-quoted literals are honored.`);
8340
+ opts.onUnsupported?.(key, value, `ResponseParameter value ${displayIdent(value)} is a mapping expression (integration.response.* / context.*) which ${getEmbedConfig().cliName} start-api does not emulate. Only single-quoted literals are honored.`);
8207
8341
  }
8208
8342
  return out;
8209
8343
  }
@@ -8581,16 +8715,16 @@ function applyRequestParameters(requestParameters, req, out) {
8581
8715
  for (const [key, value] of Object.entries(requestParameters)) {
8582
8716
  const resolved = resolveRequestParameterValue(value, req);
8583
8717
  if (resolved === void 0) {
8584
- logger.warn(`RequestParameter '${key}' value '${value}' is not a recognized mapping; skipping.`);
8718
+ logger.warn(`RequestParameter ${displayIdent(key)} value ${displayIdent(value)} is not a recognized mapping; skipping.`);
8585
8719
  continue;
8586
8720
  }
8587
8721
  const headerMatch = /^integration\.request\.header\.(.+)$/.exec(key);
8588
8722
  const queryMatch = /^integration\.request\.querystring\.(.+)$/.exec(key);
8589
8723
  const pathMatch = /^integration\.request\.path\.(.+)$/.exec(key);
8590
8724
  if (headerMatch) out.headers[headerMatch[1].toLowerCase()] = resolved;
8591
- else if (queryMatch) logger.warn(`RequestParameter '${key}' (querystring rewrite) is recognized but ${getEmbedConfig().productName} applies querystring rewrites only via URI placeholder substitution; ignoring.`);
8592
- else if (pathMatch) logger.warn(`RequestParameter '${key}' (path rewrite) is recognized but ${getEmbedConfig().productName} substitutes path placeholders via {param} in the URI; ignoring.`);
8593
- else logger.warn(`Unsupported RequestParameter key '${key}'; skipping.`);
8725
+ else if (queryMatch) logger.warn(`RequestParameter ${displayIdent(key)} (querystring rewrite) is recognized but ${getEmbedConfig().productName} applies querystring rewrites only via URI placeholder substitution; ignoring.`);
8726
+ else if (pathMatch) logger.warn(`RequestParameter ${displayIdent(key)} (path rewrite) is recognized but ${getEmbedConfig().productName} substitutes path placeholders via {param} in the URI; ignoring.`);
8727
+ else logger.warn(`Unsupported RequestParameter key ${displayIdent(key)}; skipping.`);
8594
8728
  }
8595
8729
  }
8596
8730
  function resolveRequestParameterValue(raw, req) {
@@ -8735,7 +8869,7 @@ function createContainerPool(specs, options) {
8735
8869
  try {
8736
8870
  handle.stopLogStream();
8737
8871
  } catch (err) {
8738
- logger.debug(`stopLogStream(${handle.containerName}) failed: ${err instanceof Error ? err.message : String(err)}`);
8872
+ logger.debug(`stopLogStream(${displayIdent(handle.containerName)}) failed: ${err instanceof Error ? err.message : String(err)}`);
8739
8873
  }
8740
8874
  try {
8741
8875
  await removeContainer(handle.containerId);
@@ -8767,7 +8901,7 @@ function createContainerPool(specs, options) {
8767
8901
  return {
8768
8902
  async acquire(logicalId) {
8769
8903
  const entry = entries.get(logicalId);
8770
- if (!entry) throw new Error(`containerPool.acquire: no spec registered for Lambda '${logicalId}'. This is a bug — every reachable route's Lambda should be registered at server boot.`);
8904
+ if (!entry) throw new Error(`containerPool.acquire: no spec registered for Lambda ${displayIdent(logicalId)}. This is a bug — every reachable route's Lambda should be registered at server boot.`);
8771
8905
  if (entry.warm.length > 0) {
8772
8906
  const handle = entry.warm.shift();
8773
8907
  entry.inUse.add(handle);
@@ -9627,13 +9761,13 @@ function buildAuthorizerContextShape(authorizer, result) {
9627
9761
  //#region src/local/authorizer-resolver.ts
9628
9762
  function resolveRestV1Authorizer(authorizerLogicalId, template, stackName, declaredAt) {
9629
9763
  const authResource = template.Resources?.[authorizerLogicalId];
9630
- if (!authResource || authResource.Type !== "AWS::ApiGateway::Authorizer") throw new RouteDiscoveryError(`${declaredAt}: AuthorizerId '${authorizerLogicalId}' does not point at an AWS::ApiGateway::Authorizer in stack '${stackName}'.`);
9764
+ if (!authResource || authResource.Type !== "AWS::ApiGateway::Authorizer") throw new RouteDiscoveryError(`${displayStackName(declaredAt)}: AuthorizerId ${displayIdent(authorizerLogicalId)} does not point at an AWS::ApiGateway::Authorizer in stack ${displayStackName(stackName)}.`);
9631
9765
  const props = authResource.Properties ?? {};
9632
9766
  const type = props["Type"];
9633
9767
  if (type === "TOKEN") return {
9634
9768
  kind: "lambda-token",
9635
9769
  logicalId: authorizerLogicalId,
9636
- lambdaLogicalId: resolveLambdaArn(props["AuthorizerUri"], `${stackName}/${authorizerLogicalId}.AuthorizerUri`),
9770
+ lambdaLogicalId: resolveLambdaArn(props["AuthorizerUri"], `${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}.AuthorizerUri`),
9637
9771
  tokenHeader: parseRestV1HeaderSelector(typeof props["IdentitySource"] === "string" ? props["IdentitySource"] : "method.request.header.Authorization", stackName, authorizerLogicalId),
9638
9772
  resultTtlSeconds: parseTtl(props["AuthorizerResultTtlInSeconds"], 300, 3600),
9639
9773
  declaredAt
@@ -9641,7 +9775,7 @@ function resolveRestV1Authorizer(authorizerLogicalId, template, stackName, decla
9641
9775
  if (type === "REQUEST") return {
9642
9776
  kind: "lambda-request",
9643
9777
  logicalId: authorizerLogicalId,
9644
- lambdaLogicalId: resolveLambdaArn(props["AuthorizerUri"], `${stackName}/${authorizerLogicalId}.AuthorizerUri`),
9778
+ lambdaLogicalId: resolveLambdaArn(props["AuthorizerUri"], `${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}.AuthorizerUri`),
9645
9779
  identitySources: parseRestV1IdentitySources(typeof props["IdentitySource"] === "string" ? props["IdentitySource"] : ""),
9646
9780
  resultTtlSeconds: parseTtl(props["AuthorizerResultTtlInSeconds"], 300, 3600),
9647
9781
  apiVersion: "v1",
@@ -9649,10 +9783,10 @@ function resolveRestV1Authorizer(authorizerLogicalId, template, stackName, decla
9649
9783
  };
9650
9784
  if (type === "COGNITO_USER_POOLS") {
9651
9785
  const arns = props["ProviderARNs"];
9652
- if (!Array.isArray(arns) || arns.length === 0) throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: COGNITO_USER_POOLS authorizer is missing ProviderARNs.`);
9786
+ if (!Array.isArray(arns) || arns.length === 0) throw new RouteDiscoveryError(`${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}: COGNITO_USER_POOLS authorizer is missing ProviderARNs.`);
9653
9787
  const pools = arns.map((entry, idx) => {
9654
- const arn = pickStringFromArn(entry, `${stackName}/${authorizerLogicalId}.ProviderARNs[${idx}]`);
9655
- const parsed = parseCognitoUserPoolArn(arn, `${stackName}/${authorizerLogicalId}.ProviderARNs[${idx}]`);
9788
+ const arn = pickStringFromArn(entry, `${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}.ProviderARNs[${idx}]`);
9789
+ const parsed = parseCognitoUserPoolArn(arn, `${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}.ProviderARNs[${idx}]`);
9656
9790
  return {
9657
9791
  userPoolArn: arn,
9658
9792
  region: parsed.region,
@@ -9670,17 +9804,17 @@ function resolveRestV1Authorizer(authorizerLogicalId, template, stackName, decla
9670
9804
  declaredAt
9671
9805
  };
9672
9806
  }
9673
- throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: AWS::ApiGateway::Authorizer.Type '${String(type)}' is not supported by ${getEmbedConfig().cliName} start-api (only TOKEN / REQUEST / COGNITO_USER_POOLS are accepted at the Authorizer resource).`);
9807
+ throw new RouteDiscoveryError(`${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}: AWS::ApiGateway::Authorizer.Type ${displayIdent(stringifyValue(type))} is not supported by ${getEmbedConfig().cliName} start-api (only TOKEN / REQUEST / COGNITO_USER_POOLS are accepted at the Authorizer resource).`);
9674
9808
  }
9675
9809
  function resolveHttpApiAuthorizer(authorizerLogicalId, routeAuthorizationScopes, template, stackName, declaredAt) {
9676
9810
  const authResource = template.Resources?.[authorizerLogicalId];
9677
- if (!authResource || authResource.Type !== "AWS::ApiGatewayV2::Authorizer") throw new RouteDiscoveryError(`${declaredAt}: AuthorizerId '${authorizerLogicalId}' does not point at an AWS::ApiGatewayV2::Authorizer in stack '${stackName}'.`);
9811
+ if (!authResource || authResource.Type !== "AWS::ApiGatewayV2::Authorizer") throw new RouteDiscoveryError(`${displayStackName(declaredAt)}: AuthorizerId ${displayIdent(authorizerLogicalId)} does not point at an AWS::ApiGatewayV2::Authorizer in stack ${displayStackName(stackName)}.`);
9678
9812
  const props = authResource.Properties ?? {};
9679
9813
  const authType = props["AuthorizerType"];
9680
9814
  if (authType === "REQUEST") return {
9681
9815
  kind: "lambda-request",
9682
9816
  logicalId: authorizerLogicalId,
9683
- lambdaLogicalId: resolveLambdaArn(props["AuthorizerUri"], `${stackName}/${authorizerLogicalId}.AuthorizerUri`),
9817
+ lambdaLogicalId: resolveLambdaArn(props["AuthorizerUri"], `${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}.AuthorizerUri`),
9684
9818
  identitySources: parseHttpV2IdentitySources(props["IdentitySource"]),
9685
9819
  resultTtlSeconds: parseTtl(props["AuthorizerResultTtlInSeconds"], 0, 3600),
9686
9820
  apiVersion: "v2",
@@ -9688,10 +9822,10 @@ function resolveHttpApiAuthorizer(authorizerLogicalId, routeAuthorizationScopes,
9688
9822
  };
9689
9823
  if (authType === "JWT") {
9690
9824
  const jwt = props["JwtConfiguration"];
9691
- if (!jwt || typeof jwt !== "object") throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: AWS::ApiGatewayV2::Authorizer.JwtConfiguration is required for AuthorizerType=JWT.`);
9825
+ if (!jwt || typeof jwt !== "object") throw new RouteDiscoveryError(`${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}: AWS::ApiGatewayV2::Authorizer.JwtConfiguration is required for AuthorizerType=JWT.`);
9692
9826
  const obj = jwt;
9693
9827
  const issuer = obj["Issuer"];
9694
- if (typeof issuer !== "string" || issuer.length === 0) throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: JwtConfiguration.Issuer must be a string.`);
9828
+ if (typeof issuer !== "string" || issuer.length === 0) throw new RouteDiscoveryError(`${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}: JwtConfiguration.Issuer must be a string.`);
9695
9829
  const audienceRaw = obj["Audience"];
9696
9830
  const audience = Array.isArray(audienceRaw) ? audienceRaw.filter((s) => typeof s === "string") : [];
9697
9831
  const cognito = parseCognitoIssuer(issuer);
@@ -9707,7 +9841,7 @@ function resolveHttpApiAuthorizer(authorizerLogicalId, routeAuthorizationScopes,
9707
9841
  declaredAt
9708
9842
  };
9709
9843
  }
9710
- throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: AWS::ApiGatewayV2::Authorizer.AuthorizerType '${String(authType)}' is not supported by ${getEmbedConfig().cliName} start-api (only REQUEST / JWT).`);
9844
+ throw new RouteDiscoveryError(`${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}: AWS::ApiGatewayV2::Authorizer.AuthorizerType ${displayIdent(stringifyValue(authType))} is not supported by ${getEmbedConfig().cliName} start-api (only REQUEST / JWT).`);
9711
9845
  }
9712
9846
  var AuthorizerLambdaUnresolvableError = class AuthorizerLambdaUnresolvableError extends RouteDiscoveryError {
9713
9847
  reason;
@@ -9725,7 +9859,7 @@ function resolveLambdaArn(value, location) {
9725
9859
  }
9726
9860
  function parseRestV1HeaderSelector(identitySource, stackName, authorizerLogicalId) {
9727
9861
  const m = /^method\.request\.header\.([A-Za-z0-9-_]+)$/.exec(identitySource.trim());
9728
- if (!m) throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: TOKEN authorizer IdentitySource '${identitySource}' must be 'method.request.header.<HeaderName>'.`);
9862
+ if (!m) throw new RouteDiscoveryError(`${displayStackName(stackName)}/${displayIdent(authorizerLogicalId)}: TOKEN authorizer IdentitySource ${displayIdent(identitySource)} must be 'method.request.header.<HeaderName>'.`);
9729
9863
  return m[1].toLowerCase();
9730
9864
  }
9731
9865
  function parseRestV1IdentitySources(raw) {
@@ -9824,7 +9958,7 @@ function pickStringFromArn(value, location) {
9824
9958
  const arg = obj["Fn::GetAtt"];
9825
9959
  if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn") {
9826
9960
  const logicalId = arg[0];
9827
- getLogger().warn(`${location}: uses Fn::GetAtt against logical ID '${logicalId}'. ${getEmbedConfig().cliName} start-api cannot resolve the deployed user pool ARN — synthesizing an unreachable placeholder so JWKS pass-through admits every token. For real signature verification, set 'providerArns: [pool.userPoolArn]' explicitly on the CDK construct.`);
9961
+ getLogger().warn(`${location}: uses Fn::GetAtt against logical ID ${displayIdent(logicalId)}. ${getEmbedConfig().cliName} start-api cannot resolve the deployed user pool ARN — synthesizing an unreachable placeholder so JWKS pass-through admits every token. For real signature verification, set 'providerArns: [pool.userPoolArn]' explicitly on the CDK construct.`);
9828
9962
  return `arn:aws:cognito-idp:us-east-1:000000000000:userpool/us-east-1_${getEmbedConfig().binaryName}placeholder${logicalId}`;
9829
9963
  }
9830
9964
  }
@@ -9866,7 +10000,7 @@ function attachAuthorizers(stacks, routes) {
9866
10000
  if (err instanceof AuthorizerLambdaUnresolvableError) {
9867
10001
  out.push({ route: {
9868
10002
  ...route,
9869
- unsupported: { reason: `${route.declaredAt}: authorizer Lambda Arn unresolvable — ${err.reason}` }
10003
+ unsupported: { reason: `${displayStackName(route.declaredAt)}: authorizer Lambda Arn unresolvable — ${err.reason}` }
9870
10004
  } });
9871
10005
  continue;
9872
10006
  }
@@ -9906,7 +10040,7 @@ function detectRestV1Authorizer(methodResource, methodLogicalId, stack) {
9906
10040
  };
9907
10041
  const authorizerId = props["AuthorizerId"];
9908
10042
  const refLogicalId = pickRefLogicalId(authorizerId);
9909
- if (!refLogicalId) throw new RouteDiscoveryError(`${stack.stackName}/${methodLogicalId}: AuthorizationType='${stringifyValue(authType)}' but AuthorizerId is missing or not a {Ref:...}.`);
10043
+ if (!refLogicalId) throw new RouteDiscoveryError(`${displayStackName(stack.stackName)}/${displayIdent(methodLogicalId)}: AuthorizationType=${displayIdent(stringifyValue(authType))} but AuthorizerId is missing or not a {Ref:...}.`);
9910
10044
  return resolveRestV1Authorizer(refLogicalId, stack.template, stack.stackName, `${stack.stackName}/${methodLogicalId}`);
9911
10045
  }
9912
10046
  function detectHttpApiAuthorizer(routeResource, routeLogicalId, stack) {
@@ -9915,7 +10049,7 @@ function detectHttpApiAuthorizer(routeResource, routeLogicalId, stack) {
9915
10049
  if (authType === void 0 || authType === "NONE") return void 0;
9916
10050
  const authorizerId = props["AuthorizerId"];
9917
10051
  const refLogicalId = pickRefLogicalId(authorizerId);
9918
- if (!refLogicalId) throw new RouteDiscoveryError(`${stack.stackName}/${routeLogicalId}: AuthorizationType='${stringifyValue(authType)}' but AuthorizerId is missing or not a {Ref:...}.`);
10052
+ if (!refLogicalId) throw new RouteDiscoveryError(`${displayStackName(stack.stackName)}/${displayIdent(routeLogicalId)}: AuthorizationType=${displayIdent(stringifyValue(authType))} but AuthorizerId is missing or not a {Ref:...}.`);
9919
10053
  const scopesRaw = props["AuthorizationScopes"];
9920
10054
  return resolveHttpApiAuthorizer(refLogicalId, Array.isArray(scopesRaw) ? scopesRaw.filter((s) => typeof s === "string") : void 0, stack.template, stack.stackName, `${stack.stackName}/${routeLogicalId}`);
9921
10055
  }
@@ -11011,7 +11145,7 @@ async function handleRequest$2(req, res, state, opts) {
11011
11145
  try {
11012
11146
  outcome = await runAuthorizerPass(authorizer, snapshot, matchCtx, state, opts, baseEvent["requestContext"]);
11013
11147
  } catch (err) {
11014
- logger.error(`Authorizer ${authorizer.logicalId} threw for ${match.route.declaredAt}: ${err instanceof Error ? err.message : String(err)}`);
11148
+ logger.error(`Authorizer ${displayIdent(authorizer.logicalId)} threw for ${displayStackName(match.route.declaredAt)}: ${err instanceof Error ? err.message : String(err)}`);
11015
11149
  applyCors();
11016
11150
  writeAuthRejection(res, match.route.apiVersion, "policy-deny", authorizer.kind);
11017
11151
  return;
@@ -11036,7 +11170,7 @@ async function handleRequest$2(req, res, state, opts) {
11036
11170
  applyCors();
11037
11171
  writeIntegrationOutcome(res, outcome);
11038
11172
  } catch (err) {
11039
- logger.error(`REST v1 ${match.route.restV1Integration.kind} dispatch failed for ${match.route.declaredAt}: ${err instanceof Error ? err.message : String(err)}`);
11173
+ logger.error(`REST v1 ${match.route.restV1Integration.kind} dispatch failed for ${displayStackName(match.route.declaredAt)}: ${err instanceof Error ? err.message : String(err)}`);
11040
11174
  if (!res.headersSent) {
11041
11175
  applyCors();
11042
11176
  writeError$1(res, 502);
@@ -11048,7 +11182,7 @@ async function handleRequest$2(req, res, state, opts) {
11048
11182
  try {
11049
11183
  handle = await state.pool.acquire(match.route.lambdaLogicalId);
11050
11184
  } catch (err) {
11051
- logger.error(`Failed to acquire container for ${match.route.lambdaLogicalId}: ${err instanceof Error ? err.message : String(err)}`);
11185
+ logger.error(`Failed to acquire container for ${displayIdent(match.route.lambdaLogicalId)}: ${err instanceof Error ? err.message : String(err)}`);
11052
11186
  applyCors();
11053
11187
  writeError$1(res, 502);
11054
11188
  return;
@@ -11067,7 +11201,7 @@ async function handleRequest$2(req, res, state, opts) {
11067
11201
  }
11068
11202
  return;
11069
11203
  } catch (err) {
11070
- logger.error(`RIE streaming invoke failed for ${match.route.lambdaLogicalId}: ${err instanceof Error ? err.message : String(err)}`);
11204
+ logger.error(`RIE streaming invoke failed for ${displayIdent(match.route.lambdaLogicalId)}: ${err instanceof Error ? err.message : String(err)}`);
11071
11205
  if (!res.headersSent) {
11072
11206
  applyCors();
11073
11207
  writeError$1(res, 502);
@@ -11084,7 +11218,7 @@ async function handleRequest$2(req, res, state, opts) {
11084
11218
  applyCors();
11085
11219
  res.end(translated.body);
11086
11220
  } catch (err) {
11087
- logger.error(`RIE invoke failed for ${match.route.lambdaLogicalId}: ${err instanceof Error ? err.message : String(err)}`);
11221
+ logger.error(`RIE invoke failed for ${displayIdent(match.route.lambdaLogicalId)}: ${err instanceof Error ? err.message : String(err)}`);
11088
11222
  if (!res.headersSent) {
11089
11223
  applyCors();
11090
11224
  writeError$1(res, 502);
@@ -11277,7 +11411,7 @@ async function runAuthorizerPass(authorizer, snapshot, matchCtx, state, opts, re
11277
11411
  }
11278
11412
  if (authorizer.kind === "iam") {
11279
11413
  if (!opts.sigV4CredentialsLoader) {
11280
- getLogger().debug(`AWS_IAM authorizer for ${matchCtx.route.declaredAt}: no SigV4 credentials loader configured — denying.`);
11414
+ getLogger().debug(`AWS_IAM authorizer for ${displayStackName(matchCtx.route.declaredAt)}: no SigV4 credentials loader configured — denying.`);
11281
11415
  return {
11282
11416
  result: { allow: false },
11283
11417
  denyKind: "policy-deny"
@@ -11487,7 +11621,7 @@ async function handleServiceIntegrationRequest(req, res, match, bodyBuf, opts, a
11487
11621
  };
11488
11622
  const outcome = resolveServiceIntegrationParameters(svc.requestParameters, ctx);
11489
11623
  if (outcome.kind === "error") {
11490
- getLogger().warn(`[${route.declaredAt}] ${outcome.reason}`);
11624
+ getLogger().warn(`[${displayStackName(route.declaredAt)}] ${outcome.reason}`);
11491
11625
  const body = JSON.stringify({
11492
11626
  message: "Invalid integration mapping",
11493
11627
  reason: outcome.reason
@@ -11638,17 +11772,17 @@ function groupRoutesByServer(routes) {
11638
11772
  identifier = r.lambdaLogicalId;
11639
11773
  serverKey = `function-url:${stackPrefix}${identifier}`;
11640
11774
  kind = "function-url";
11641
- displayName = `${identifier} (Function URL)`;
11775
+ displayName = `${displayIdent(identifier)} (Function URL)`;
11642
11776
  } else if (r.source === "http-api") {
11643
11777
  identifier = r.apiLogicalId ?? "<unknown>";
11644
11778
  serverKey = `http-api:${stackPrefix}${identifier}`;
11645
11779
  kind = "http-api";
11646
- displayName = `${identifier} (HTTP API v2)`;
11780
+ displayName = `${displayIdent(identifier)} (HTTP API v2)`;
11647
11781
  } else {
11648
11782
  identifier = r.apiLogicalId ?? "<unknown>";
11649
11783
  serverKey = `rest-v1:${stackPrefix}${identifier}`;
11650
11784
  kind = "rest-v1";
11651
- displayName = `${identifier} (REST API v1)`;
11785
+ displayName = `${displayIdent(identifier)} (REST API v1)`;
11652
11786
  }
11653
11787
  const existing = byKey.get(serverKey);
11654
11788
  if (existing) existing.routes.push(rwa);
@@ -11764,10 +11898,10 @@ async function materializeLayerFromArn(layer, options = {}) {
11764
11898
  let credentials;
11765
11899
  if (options.roleArn) try {
11766
11900
  credentials = await assumeRoleForLayer(options.roleArn, layer.region, options);
11767
- logger.debug(`Layer ${layer.arn}: assumed role ${options.roleArn} for GetLayerVersion`);
11901
+ logger.debug(`Layer ${displayUntrustedValue(layer.arn)}: assumed role ${options.roleArn} for GetLayerVersion`);
11768
11902
  } catch (err) {
11769
11903
  if (err instanceof LayerMaterializationError) throw err;
11770
- throw new LayerMaterializationError(`Layer ${layer.arn}: STS AssumeRole(${flattenToOneLine(options.roleArn)}) failed: ${layerFailureDetail(err, "STS AssumeRole (--layer-role-arn)")}. Check the role trust policy permits your principal and sts:AssumeRole is allowed.`);
11904
+ throw new LayerMaterializationError(`Layer ${displayUntrustedValue(layer.arn)}: STS AssumeRole(${flattenToOneLine(options.roleArn)}) failed: ${layerFailureDetail(err, "STS AssumeRole (--layer-role-arn)")}. Check the role trust policy permits your principal and sts:AssumeRole is allowed.`);
11771
11905
  }
11772
11906
  let presignedUrl;
11773
11907
  try {
@@ -11775,13 +11909,13 @@ async function materializeLayerFromArn(layer, options = {}) {
11775
11909
  } catch (err) {
11776
11910
  if (err instanceof LayerMaterializationError) throw err;
11777
11911
  const hint = looksLikeAccessDenied(err) ? " GetLayerVersion access denied; check the credentials / role can read the layer (grant lambda:GetLayerVersion on the layer ARN, or pass --layer-role-arn <arn> to assume a role in the layer account)." : "";
11778
- throw new LayerMaterializationError(`Layer ${layer.arn}: GetLayerVersion failed in region ${layer.region}: ${layerFailureDetail(err, "Lambda GetLayerVersion")}.${hint}`);
11912
+ throw new LayerMaterializationError(`Layer ${displayUntrustedValue(layer.arn)}: GetLayerVersion failed in region ${layer.region}: ${layerFailureDetail(err, "Lambda GetLayerVersion")}.${hint}`);
11779
11913
  }
11780
11914
  let zipBytes;
11781
11915
  try {
11782
11916
  zipBytes = await downloadPresignedZip(presignedUrl, options);
11783
11917
  } catch (err) {
11784
- throw new LayerMaterializationError(`Layer ${layer.arn}: failed to download layer ZIP from the presigned URL: ${sanitizeServiceExceptionMessage(errMsg(err))}.`);
11918
+ throw new LayerMaterializationError(`Layer ${displayUntrustedValue(layer.arn)}: failed to download layer ZIP from the presigned URL: ${sanitizeServiceExceptionMessage(errMsg(err))}.`);
11785
11919
  }
11786
11920
  const dir = await mkdtemp(join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-arn-layer-${layer.name}-${layer.version}-`));
11787
11921
  try {
@@ -11793,7 +11927,7 @@ async function materializeLayerFromArn(layer, options = {}) {
11793
11927
  force: true
11794
11928
  });
11795
11929
  } catch {}
11796
- throw new LayerMaterializationError(`Layer ${layer.arn}: failed to unzip layer contents into '${dir}': ${errMsg(err)}.`);
11930
+ throw new LayerMaterializationError(`Layer ${displayUntrustedValue(layer.arn)}: failed to unzip layer contents into '${dir}': ${errMsg(err)}.`);
11797
11931
  }
11798
11932
  return dir;
11799
11933
  }
@@ -11801,7 +11935,7 @@ async function fetchLayerContentUrl(layer, credentials, options) {
11801
11935
  const client = (options.lambdaClientFactory ?? await defaultLambdaClientFactory())(layer.region, credentials);
11802
11936
  try {
11803
11937
  const versionSuffix = /^(.*):(\d+)$/.exec(layer.arn);
11804
- if (!versionSuffix) throw new LayerMaterializationError(`Layer ${layer.arn}: not a layer-version ARN (no ':<version>' suffix to strip). Expected arn:<partition>:lambda:<region>:<account>:layer:<name>:<version>.`);
11938
+ if (!versionSuffix) throw new LayerMaterializationError(`Layer ${displayUntrustedValue(layer.arn)}: not a layer-version ARN (no ':<version>' suffix to strip). Expected arn:<partition>:lambda:<region>:<account>:layer:<name>:<version>.`);
11805
11939
  const versionLessArn = versionSuffix[1];
11806
11940
  const command = await buildGetLayerVersionCommand(versionLessArn, Number(layer.version));
11807
11941
  const url = (await client.send(command))?.Content?.Location;
@@ -11899,14 +12033,14 @@ async function unzipBufferToDirectory(zipBytes, destDir) {
11899
12033
  cursor += 46 + fileNameLength + extraFieldLength + fileCommentLength;
11900
12034
  parsed++;
11901
12035
  const targetPath = resolve(destAbsolute, normalize(fileName));
11902
- if (!targetPath.startsWith(destAbsolute + (destAbsolute.endsWith("/") ? "" : "/"))) throw new Error(`Refusing to extract entry '${fileName}' — path escapes the destination directory`);
11903
- if ((externalAttrs >>> 16 & 61440) === 40960) throw new Error(`Refusing to extract symlink entry '${fileName}' from layer ZIP (security)`);
12036
+ if (!targetPath.startsWith(destAbsolute + (destAbsolute.endsWith("/") ? "" : "/"))) throw new Error(`Refusing to extract entry ${displayUntrustedValue(fileName)} — path escapes the destination directory`);
12037
+ if ((externalAttrs >>> 16 & 61440) === 40960) throw new Error(`Refusing to extract symlink entry ${displayUntrustedValue(fileName)} from layer ZIP (security)`);
11904
12038
  if (fileName.endsWith("/")) {
11905
12039
  await mkdir(targetPath, { recursive: true });
11906
12040
  continue;
11907
12041
  }
11908
12042
  await mkdir(dirname(targetPath), { recursive: true });
11909
- if (view.getUint32(localHeaderOffset, true) !== 67324752) throw new Error(`Corrupt ZIP: missing Local File Header for '${fileName}'`);
12043
+ if (view.getUint32(localHeaderOffset, true) !== 67324752) throw new Error(`Corrupt ZIP: missing Local File Header for ${displayUntrustedValue(fileName)}`);
11910
12044
  const lfhFileNameLength = view.getUint16(localHeaderOffset + 26, true);
11911
12045
  const lfhExtraFieldLength = view.getUint16(localHeaderOffset + 28, true);
11912
12046
  const dataOffset = localHeaderOffset + 30 + lfhFileNameLength + lfhExtraFieldLength;
@@ -11914,8 +12048,8 @@ async function unzipBufferToDirectory(zipBytes, destDir) {
11914
12048
  let payload;
11915
12049
  if (compressionMethod === 0) payload = compressedData;
11916
12050
  else if (compressionMethod === 8) payload = await inflateRaw(compressedData);
11917
- else throw new Error(`Unsupported ZIP compression method ${compressionMethod} for entry '${fileName}' (only STORE and DEFLATE supported)`);
11918
- if (payload.length !== uncompressedSize && compressionMethod !== 0) throw new Error(`ZIP entry '${fileName}': inflate produced ${payload.length} bytes, expected ${uncompressedSize}`);
12051
+ else throw new Error(`Unsupported ZIP compression method ${compressionMethod} for entry ${displayUntrustedValue(fileName)} (only STORE and DEFLATE supported)`);
12052
+ if (payload.length !== uncompressedSize && compressionMethod !== 0) throw new Error(`ZIP entry ${displayUntrustedValue(fileName)}: inflate produced ${payload.length} bytes, expected ${uncompressedSize}`);
11919
12053
  await pipeline(Readable.from(payload), createWriteStream(targetPath));
11920
12054
  }
11921
12055
  }
@@ -12253,32 +12387,18 @@ async function localStartApiCommand(targets, options, extraStateProviders) {
12253
12387
  interactivePicked.value = true;
12254
12388
  }
12255
12389
  const cfnStackFallback = typeof options.fromCfnStack === "string" ? options.fromCfnStack : void 0;
12256
- const targetStackPrefix = deriveSynthStackPrefix(effectiveTargets);
12257
- const targetStacks = pickTargetStacks(stacks, options.stack, cfnStackFallback, targetStackPrefix, options.allStacks || shouldSynthAllStacks(effectiveTargets, options.stack, cfnStackFallback));
12390
+ const targetStackPrefix = deriveSynthStackPrefix(effectiveTargets, stacks);
12391
+ const targetStacks = pickTargetStacks(stacks, options.stack, cfnStackFallback, targetStackPrefix, options.allStacks || shouldSynthAllStacks(effectiveTargets, options.stack, cfnStackFallback, stacks));
12258
12392
  if (targetStacks.length === 0) throw new Error("No stacks matched. Pass --stack <name> (or --from-cfn-stack <name>) or run from a single-stack app.");
12259
12393
  const routedStackNames = targetStacks.map((s) => s.stackName);
12260
12394
  tryEmitFromCfnRedundancyTipOnce(options.fromCfnStack, routedStackNames, fromCfnTipEmitted, (routedStackName) => {
12261
- logger.info(`tip: --from-cfn-stack value matches the routed stack name (${routedStackName}); you can omit the value: \`${getEmbedConfig().cliName} start-api ... --from-cfn-stack\` (bare flag) resolves to the same value.`);
12395
+ logger.info(`tip: --from-cfn-stack value matches the routed stack name (${displayStackName(routedStackName)}); you can omit the value: \`${getEmbedConfig().cliName} start-api ... --from-cfn-stack\` (bare flag) resolves to the same value.`);
12262
12396
  });
12263
12397
  const routes = discoverRoutes(targetStacks);
12264
12398
  const wsDiscovery = discoverWebSocketApis(targetStacks);
12265
12399
  if (wsDiscovery.errors.length > 0) for (const e of wsDiscovery.errors) logger.warn(`WebSocket discovery: ${e}`);
12266
12400
  let webSocketApis = wsDiscovery.apis;
12267
12401
  if (routes.length === 0 && webSocketApis.length === 0) throw new Error(`No supported API routes were discovered. ${getEmbedConfig().cliName} start-api supports AWS::ApiGateway::* (REST v1), AWS::ApiGatewayV2::* (HTTP + WebSocket), and AWS::Lambda::Url (Function URL) with AWS_PROXY integrations only.`);
12268
- const stageMap = /* @__PURE__ */ new Map();
12269
- for (const stack of targetStacks) {
12270
- const m = buildStageMap(stack.template, options.stage);
12271
- for (const [k, v] of m) stageMap.set(k, v);
12272
- }
12273
- if (options.stage) {
12274
- const missingApis = /* @__PURE__ */ new Set();
12275
- for (const r of routes) {
12276
- if (!r.apiLogicalId) continue;
12277
- if (!stageMap.has(r.apiLogicalId)) missingApis.add(r.apiLogicalId);
12278
- }
12279
- for (const apiId of missingApis) logger.warn(`--stage '${options.stage}' did not match any Stage on API '${apiId}'; routes on that API will get stageVariables: null.`);
12280
- }
12281
- attachStageContext(routes, stageMap);
12282
12402
  let routesWithAuth = attachAuthorizers(targetStacks, routes);
12283
12403
  if (apiFilters.length > 0) {
12284
12404
  const { filtered, filteredWebSocketApis, unmatched } = resolveApiTargetSubset(routesWithAuth, apiFilters, targetStacks.map((s) => s.stackName), webSocketApis);
@@ -12293,23 +12413,40 @@ async function localStartApiCommand(targets, options, extraStateProviders) {
12293
12413
  routesWithAuth = filtered;
12294
12414
  webSocketApis = filteredWebSocketApis;
12295
12415
  }
12416
+ const servedRoutes = routesWithAuth.map((r) => r.route);
12417
+ const servedStacks = stacksServing(targetStacks, servedRoutes, webSocketApis);
12418
+ const stageMap = /* @__PURE__ */ new Map();
12419
+ for (const stack of servedStacks) {
12420
+ const m = buildStageMap(stack.template, options.stage);
12421
+ for (const [k, v] of m) stageMap.set(k, v);
12422
+ }
12423
+ if (options.stage) {
12424
+ const missingApis = /* @__PURE__ */ new Set();
12425
+ for (const r of servedRoutes) {
12426
+ if (!r.apiLogicalId) continue;
12427
+ if (!stageMap.has(r.apiLogicalId)) missingApis.add(r.apiLogicalId);
12428
+ }
12429
+ for (const apiId of missingApis) logger.warn(`--stage '${options.stage}' did not match any Stage on API ${displayIdent(apiId)}; routes on that API will get stageVariables: null.`);
12430
+ }
12431
+ attachStageContext(servedRoutes, stageMap);
12296
12432
  const corsConfigByApiId = /* @__PURE__ */ new Map();
12297
- for (const stack of targetStacks) {
12433
+ for (const stack of servedStacks) {
12298
12434
  const fromCloudFront = buildCorsConfigFromCloudFrontChain(stack.template);
12299
12435
  for (const [k, v] of fromCloudFront) corsConfigByApiId.set(k, v);
12300
12436
  const direct = buildCorsConfigByApiId(stack.template);
12301
12437
  for (const [k, v] of direct) corsConfigByApiId.set(k, v);
12302
12438
  }
12303
- const stateByStack = isCfnFlagPresent(options) || hasExtraStateProviderActive(options, extraStateProviders) ? await loadStateForRoutedStacks(targetStacks, routes, routesWithAuth, options, extraStateProviders) : /* @__PURE__ */ new Map();
12439
+ assertServedLogicalIdsUnambiguous(servedStacks, servedRoutes, routesWithAuth, webSocketApis);
12440
+ const stateByStack = isCfnFlagPresent(options) || hasExtraStateProviderActive(options, extraStateProviders) ? await loadStateForRoutedStacks(servedStacks, servedRoutes, routesWithAuth, options, extraStateProviders) : /* @__PURE__ */ new Map();
12304
12441
  const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
12305
12442
  if (options.profile && profileCredentials && !profileCredsFile) profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);
12306
- const lambdaIds = uniqueLambdaIds(routes, routesWithAuth, webSocketApis);
12443
+ const lambdaIds = uniqueLambdaIds(servedRoutes, routesWithAuth, webSocketApis);
12307
12444
  const specs = /* @__PURE__ */ new Map();
12308
12445
  for (let i = 0; i < lambdaIds.length; i++) {
12309
12446
  const logicalId = lambdaIds[i];
12310
12447
  const spec = await buildContainerSpec({
12311
12448
  logicalId,
12312
- stacks: targetStacks,
12449
+ stacks: servedStacks,
12313
12450
  overrides,
12314
12451
  assumeRole: normalizedAssumeRole,
12315
12452
  containerHost: options.containerHost,
@@ -12338,7 +12475,7 @@ async function localStartApiCommand(targets, options, extraStateProviders) {
12338
12475
  specs,
12339
12476
  corsConfigByApiId,
12340
12477
  webSocketApis,
12341
- stacks: targetStacks
12478
+ stacks: servedStacks
12342
12479
  };
12343
12480
  };
12344
12481
  const buildPool = (specs) => {
@@ -12648,10 +12785,11 @@ function createWatchPredicates(args) {
12648
12785
  function pickTargetStacks(stacks, pattern, cfnStackFallback, targetFallback, allStacks) {
12649
12786
  if (allStacks) return stacks;
12650
12787
  const effective = pattern ?? cfnStackFallback ?? targetFallback;
12788
+ if (effective !== void 0 && typeof effective !== "string") return [effective];
12651
12789
  if (effective) return matchStacks(stacks, [effective]);
12652
12790
  if (stacks.length === 1) return stacks;
12653
12791
  if (stacks.length === 0) return [];
12654
- throw new Error(`Multi-stack app: pass --stack <name>, --from-cfn-stack <name>, a stack-qualified target like "<StackName>/<construct>", or --all-stacks to serve every stack. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
12792
+ throw new Error(`Multi-stack app: pass --stack <name>, --from-cfn-stack <name>, a stack-qualified target like "<StackName>/<construct>", or --all-stacks to serve every stack. Available stacks: ${describeStacks(stacks)}.`);
12655
12793
  }
12656
12794
  function allStacksConflicts(options, targets, apiFilters) {
12657
12795
  if (!options.allStacks) return [];
@@ -12677,20 +12815,25 @@ function resolveApiTargetSubset(routes, identifiers, stackNames, webSocketApis =
12677
12815
  unmatched: identifiers.filter((id) => filterRoutesByApiIdentifiers(routes, [id]).length === 0 && filterWebSocketApisByIdentifiers(webSocketApis, [id]).length === 0)
12678
12816
  };
12679
12817
  }
12680
- function deriveSynthStackPrefix(targets) {
12818
+ function targetStackHint(target, stacks) {
12819
+ const slash = target.indexOf("/");
12820
+ if (slash === -1) return void 0;
12821
+ return stackForConstructPath(target, stacks) ?? target.slice(0, slash);
12822
+ }
12823
+ function deriveSynthStackPrefix(targets, stacks) {
12681
12824
  if (targets.length === 0) return void 0;
12682
- const prefixes = /* @__PURE__ */ new Set();
12825
+ const hints = /* @__PURE__ */ new Set();
12683
12826
  for (const t of targets) {
12684
- const slash = t.indexOf("/");
12685
- if (slash === -1) return void 0;
12686
- prefixes.add(t.slice(0, slash));
12827
+ const hint = targetStackHint(t, stacks);
12828
+ if (hint === void 0) return void 0;
12829
+ hints.add(hint);
12687
12830
  }
12688
- return prefixes.size === 1 ? [...prefixes][0] : void 0;
12831
+ return hints.size === 1 ? [...hints][0] : void 0;
12689
12832
  }
12690
- function shouldSynthAllStacks(targets, stackPattern, cfnStackFallback) {
12833
+ function shouldSynthAllStacks(targets, stackPattern, cfnStackFallback, stacks) {
12691
12834
  if (targets.length === 0) return false;
12692
12835
  if (stackPattern !== void 0 || cfnStackFallback !== void 0) return false;
12693
- return deriveSynthStackPrefix(targets) === void 0;
12836
+ return deriveSynthStackPrefix(targets, stacks) === void 0;
12694
12837
  }
12695
12838
  function shouldPromptBareMultiSelect(apiFilters, allStacks, isTty) {
12696
12839
  return apiFilters.length === 0 && !allStacks && isTty;
@@ -12709,6 +12852,34 @@ function tryEmitFromCfnRedundancyTipOnce(fromCfnStack, routedStackNames, emitted
12709
12852
  emit(routedStackName);
12710
12853
  emittedRef.value = true;
12711
12854
  }
12855
+ function stacksServing(stacks, routes, webSocketApis) {
12856
+ const names = /* @__PURE__ */ new Set();
12857
+ for (const r of routes) {
12858
+ if (r.apiStackName === void 0) return [...stacks];
12859
+ names.add(r.apiStackName);
12860
+ }
12861
+ for (const api of webSocketApis) names.add(api.apiStackName);
12862
+ return stacks.filter((s) => names.has(s.stackName));
12863
+ }
12864
+ function stacksDeclaring(logicalId, stacks, type) {
12865
+ return stacks.filter((stack) => {
12866
+ const resource = stack.template.Resources?.[logicalId];
12867
+ return resource !== void 0 && (type === void 0 || resource.Type === type);
12868
+ });
12869
+ }
12870
+ function assertServedLogicalIdsUnambiguous(stacks, routes, routesWithAuth, webSocketApis = []) {
12871
+ if (stacks.length < 2) return;
12872
+ const checks = [];
12873
+ for (const id of uniqueLambdaIds(routes, routesWithAuth, webSocketApis)) checks.push([id, "AWS::Lambda::Function"]);
12874
+ const apiIds = /* @__PURE__ */ new Set();
12875
+ for (const { route } of routesWithAuth) if (route.source !== "function-url" && route.apiLogicalId) apiIds.add(route.apiLogicalId);
12876
+ for (const api of webSocketApis) apiIds.add(api.apiLogicalId);
12877
+ for (const id of apiIds) checks.push([id, void 0]);
12878
+ for (const [logicalId, type] of checks) {
12879
+ const owners = stacksDeclaring(logicalId, stacks, type);
12880
+ if (owners.length > 1) throw new Error(`Logical ID ${displayUntrustedValue(logicalId)} is declared by ${owners.length} of the served stacks (${describeStacks(owners)}), and ${getEmbedConfig().cliName} start-api cannot serve two resources under one logical ID. Serve one stack at a time: pass --stack '<stack display path>' or a stack-qualified target.`);
12881
+ }
12882
+ }
12712
12883
  function uniqueLambdaIds(routes, routesWithAuth, webSocketApis = []) {
12713
12884
  const seen = /* @__PURE__ */ new Set();
12714
12885
  const out = [];
@@ -12804,16 +12975,16 @@ function resolveStartApiAssumeRoleArn(args) {
12804
12975
  const roleProp = (lambdaResource.Properties ?? {})["Role"];
12805
12976
  if (typeof roleProp === "string") {
12806
12977
  if (isIamRoleArn(roleProp)) return roleProp;
12807
- getLogger().warn(`--assume-role: the template Role for '${logicalId}' is not a well-formed IAM role ARN: ${describeRejectedRoleArn(roleProp)}. Ignoring it.`);
12978
+ getLogger().warn(`--assume-role: the template Role for ${displayIdent(logicalId)} is not a well-formed IAM role ARN: ${describeRejectedRoleArn(roleProp)}. Ignoring it.`);
12808
12979
  }
12809
12980
  if (stateBundle) {
12810
12981
  const fromState = resolveExecutionRoleArnFromState(stateBundle.state, logicalId);
12811
12982
  if (fromState) {
12812
- getLogger().info(`--assume-role: auto-resolved execution role for '${logicalId}' from state: ${flattenToOneLine(fromState)}`);
12983
+ getLogger().info(`--assume-role: auto-resolved execution role for ${displayIdent(logicalId)} from state: ${flattenToOneLine(fromState)}`);
12813
12984
  return fromState;
12814
12985
  }
12815
12986
  }
12816
- getLogger().warn(`--assume-role: could not auto-resolve the execution role ARN for '${logicalId}'. Pair --assume-role with --from-cfn-stack (or a host-provided state-source extension) so the deployed Role's ARN can be looked up, OR pin the ARN explicitly with --assume-role ${logicalId}=<arn>. Falling back to the developer's shell credentials for this Lambda.`);
12987
+ getLogger().warn(`--assume-role: could not auto-resolve the execution role ARN for ${displayIdent(logicalId)}. Pair --assume-role with --from-cfn-stack (or a host-provided state-source extension) so the deployed Role's ARN can be looked up, OR pin the ARN explicitly with --assume-role ${logicalId}=<arn>. Falling back to the developer's shell credentials for this Lambda.`);
12817
12988
  }
12818
12989
  async function buildContainerSpec(args) {
12819
12990
  const { logicalId, stacks, overrides, assumeRole, containerHost, debugPort, stsRegion, inlineTmpDirs, layerTmpDirs, stateByStack, skipPull, profile, layerRoleArn, profileCredentials, profileCredsFile, profileRegion, stackRegionOverride } = args;
@@ -12869,7 +13040,7 @@ async function buildContainerSpec(args) {
12869
13040
  unresolved,
12870
13041
  sensitiveKeys: [...audit.sensitiveKeys, ...deployedSecretKeys]
12871
13042
  };
12872
- for (const { key, reason } of unresolved) getLogger().warn(`Lambda ${logicalId}: state source could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
13043
+ for (const { key, reason } of unresolved) getLogger().warn(`Lambda ${displayIdent(logicalId)}: state source could not substitute env var ${displayIdent(key)} (${reason}). Override it via --env-vars or it will be dropped.`);
12873
13044
  }
12874
13045
  const lambdaCdkPath = readCdkPathOrUndefined(lambda.resource);
12875
13046
  const envResult = resolveEnvVars$1(logicalId, lambdaCdkPath, templateEnv, overrides);
@@ -12978,7 +13149,7 @@ async function resolveContainerImageForStartApi(lambda, skipPull, profile, optio
12978
13149
  assetOutdir: localBuild.assetOutdir,
12979
13150
  ...options?.noBuild !== void 0 && { noBuild: options.noBuild }
12980
13151
  }) };
12981
- if (!parseEcrUri(lambda.imageUri)) throw new Error(`Container Lambda '${lambda.logicalId}' has no matching asset in cdk.out, and Code.ImageUri '${lambda.imageUri}' is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. Re-synthesize the CDK app (so cdk.out includes the build context) or deploy the image to ECR first.`);
13152
+ if (!parseEcrUri(lambda.imageUri)) throw new Error(`Container Lambda ${displayIdent(lambda.logicalId)} has no matching asset in cdk.out, and Code.ImageUri ${displayUntrustedValue(String(lambda.imageUri))} is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. Re-synthesize the CDK app (so cdk.out includes the build context) or deploy the image to ECR first.`);
12982
13153
  logger.info(`No matching cdk.out asset for ${lambda.imageUri}; falling back to ECR pull...`);
12983
13154
  return { imageRef: await pullEcrImage(lambda.imageUri, {
12984
13155
  skipPull,
@@ -12991,7 +13162,7 @@ async function resolveLocalBuildPlan$1(lambda) {
12991
13162
  const manifestPath = lambda.stack.assetManifestPath;
12992
13163
  if (!manifestPath) return void 0;
12993
13164
  const cdkOutDir = path$1.dirname(manifestPath);
12994
- const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, lambda.stack.stackName);
13165
+ const manifest = await new AssetManifestLoader().loadStackManifest(lambda.stack, cdkOutDir);
12995
13166
  if (!manifest) return void 0;
12996
13167
  const entry = getDockerImageBySourceHash(manifest, lambda.imageUri);
12997
13168
  if (!entry) return void 0;
@@ -13034,9 +13205,10 @@ async function materializeLambdaLayers$1(layers, layerTmpDirs, layerRoleArn) {
13034
13205
  return dir;
13035
13206
  }
13036
13207
  function resolveLambdaByLogicalId(logicalId, stacks) {
13037
- for (const stack of stacks) {
13038
- const resource = stack.template.Resources?.[logicalId];
13039
- if (!resource || resource.Type !== "AWS::Lambda::Function") continue;
13208
+ const owners = stacksDeclaring(logicalId, stacks, "AWS::Lambda::Function");
13209
+ if (owners.length > 1) throw new Error(`Lambda ${displayUntrustedValue(logicalId)} is declared by ${owners.length} stacks (${describeStacks(owners)}); pass --stack '<stack display path>' to pick one.`);
13210
+ for (const stack of owners) {
13211
+ const resource = stack.template.Resources[logicalId];
13040
13212
  const props = resource.Properties ?? {};
13041
13213
  const memoryMb = typeof props["MemorySize"] === "number" ? props["MemorySize"] : 128;
13042
13214
  const timeoutSec = typeof props["Timeout"] === "number" ? props["Timeout"] : 3;
@@ -13053,8 +13225,8 @@ function resolveLambdaByLogicalId(logicalId, stacks) {
13053
13225
  });
13054
13226
  const runtime = typeof props["Runtime"] === "string" ? props["Runtime"] : "";
13055
13227
  const handler = typeof props["Handler"] === "string" ? props["Handler"] : "";
13056
- if (!runtime) throw new Error(`Lambda '${logicalId}' has no Runtime property and no Code.ImageUri. ${getEmbedConfig().cliName} start-api cannot tell if this is a ZIP or a container Lambda.`);
13057
- if (!handler) throw new Error(`Lambda '${logicalId}' has no Handler property.`);
13228
+ if (!runtime) throw new Error(`Lambda ${displayIdent(logicalId)} has no Runtime property and no Code.ImageUri. ${getEmbedConfig().cliName} start-api cannot tell if this is a ZIP or a container Lambda.`);
13229
+ if (!handler) throw new Error(`Lambda ${displayIdent(logicalId)} has no Handler property.`);
13058
13230
  const inlineCode = typeof code["ZipFile"] === "string" ? code["ZipFile"] : void 0;
13059
13231
  let codePath = null;
13060
13232
  if (!inlineCode) codePath = resolveAssetCodePath(stack, logicalId, resource);
@@ -13077,7 +13249,7 @@ function resolveLambdaByLogicalId(logicalId, stacks) {
13077
13249
  ...ephemeralStorageMb !== void 0 && { ephemeralStorageMb }
13078
13250
  };
13079
13251
  }
13080
- throw new Error(`No AWS::Lambda::Function resource named '${logicalId}' found in target stacks. This is likely a synthesis bug — the route-discovery phase resolved a route to this logical ID.`);
13252
+ throw new Error(`No AWS::Lambda::Function resource named ${displayIdent(logicalId)} found in target stacks. This is likely a synthesis bug — the route-discovery phase resolved a route to this logical ID.`);
13081
13253
  }
13082
13254
  function extractImageUri(value, logicalId, stackName, resources, region) {
13083
13255
  if (typeof value === "string" && value.length > 0) return value;
@@ -13090,10 +13262,10 @@ function extractImageUri(value, logicalId, stackName, resources, region) {
13090
13262
  const pseudoParameters = derivePseudoParametersFromRegion(region);
13091
13263
  const joinResolved = tryResolveImageFnJoin(value, resources, pseudoParameters ? { pseudoParameters } : void 0);
13092
13264
  if (joinResolved.kind === "resolved") return joinResolved.uri;
13093
- if (joinResolved.kind === "needs-state") throw new Error(`Lambda '${logicalId}' in ${stackName} references same-stack ECR repository '${joinResolved.repoLogicalId}' via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — deploy the stack first, rebuild via lambda.DockerImageCode.fromImageAsset, or pin a public image.`);
13094
- if (joinResolved.kind === "unsupported-join") throw new Error(`Lambda '${logicalId}' in ${stackName} has an unsupported Fn::Join Code.ImageUri shape: ${joinResolved.reason}. ${getEmbedConfig().cliName} recognizes the canonical CDK 2.x lambda.DockerImageCode.fromEcr Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
13265
+ if (joinResolved.kind === "needs-state") throw new Error(`Lambda ${displayIdent(logicalId)} in ${displayStackName(stackName)} references same-stack ECR repository ${displayIdent(joinResolved.repoLogicalId)} via Fn::Join. ${getEmbedConfig().cliName} cannot resolve the repository URI without state — deploy the stack first, rebuild via lambda.DockerImageCode.fromImageAsset, or pin a public image.`);
13266
+ if (joinResolved.kind === "unsupported-join") throw new Error(`Lambda ${displayIdent(logicalId)} in ${displayStackName(stackName)} has an unsupported Fn::Join Code.ImageUri shape: ${joinResolved.reason}. ${getEmbedConfig().cliName} recognizes the canonical CDK 2.x lambda.DockerImageCode.fromEcr Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
13095
13267
  const accountIdHint = pseudoParameters ? ` (likely \${AWS::AccountId}, which ${getEmbedConfig().binaryName} cannot derive without a state source or STS)` : ` (${getEmbedConfig().binaryName} could not derive AWS pseudo parameters because stack.region was undefined)`;
13096
- throw new Error(`Lambda '${logicalId}' in ${stackName} has an Fn::Join Code.ImageUri that ${getEmbedConfig().cliName} cannot resolve${accountIdHint}. Workarounds: deploy first and run with a state-source flag (e.g. --from-cfn-stack or a host-provided extension), or pin a fully-literal public image URI.`);
13268
+ throw new Error(`Lambda ${displayIdent(logicalId)} in ${displayStackName(stackName)} has an Fn::Join Code.ImageUri that ${getEmbedConfig().cliName} cannot resolve${accountIdHint}. Workarounds: deploy first and run with a state-source flag (e.g. --from-cfn-stack or a host-provided extension), or pin a fully-literal public image URI.`);
13097
13269
  }
13098
13270
  }
13099
13271
  }
@@ -13110,7 +13282,7 @@ function resolveImageLambda(args) {
13110
13282
  const first = arches[0];
13111
13283
  if (first === "arm64") architecture = "arm64";
13112
13284
  else if (first === "x86_64") architecture = "x86_64";
13113
- else throw new Error(`Lambda '${logicalId}' has unsupported Architectures value '${String(first)}'. ${getEmbedConfig().cliName} start-api supports x86_64 and arm64.`);
13285
+ else throw new Error(`Lambda ${displayIdent(logicalId)} has unsupported Architectures value ${displayIdent(String(first))}. ${getEmbedConfig().cliName} start-api supports x86_64 and arm64.`);
13114
13286
  }
13115
13287
  const ephemeralStorageMb = extractEphemeralStorageMb(props, logicalId);
13116
13288
  return {
@@ -13142,8 +13314,8 @@ function printRouteTable(routes) {
13142
13314
  const pathWidth = Math.max(...sorted.map((r) => r.pathPattern.length), 8);
13143
13315
  process.stdout.write("Discovered routes:\n");
13144
13316
  for (const r of sorted) {
13145
- const sourceLabel = r.source === "http-api" ? "HTTP API" : r.source === "rest-v1" ? `REST v1, stage '${r.stage}'` : "Function URL";
13146
- const target = r.mockCors ? "[MOCK CORS preflight]" : r.unsupported ? "[501 Not Implemented]" : r.serviceIntegration ? `[${r.serviceIntegration.subtype}]` : r.restV1Integration ? formatRestV1IntegrationLabel(r.restV1Integration) : r.lambdaLogicalId;
13317
+ const sourceLabel = r.source === "http-api" ? "HTTP API" : r.source === "rest-v1" ? `REST v1, stage ${displayIdent(r.stage)}` : "Function URL";
13318
+ const target = r.mockCors ? "[MOCK CORS preflight]" : r.unsupported ? "[501 Not Implemented]" : r.serviceIntegration ? `[${displayIdent(r.serviceIntegration.subtype)}]` : r.restV1Integration ? formatRestV1IntegrationLabel(r.restV1Integration) : r.lambdaLogicalId;
13147
13319
  process.stdout.write(` ${r.method.padEnd(methodWidth)} ${r.pathPattern.padEnd(pathWidth)} -> ${target} (${sourceLabel})\n`);
13148
13320
  }
13149
13321
  process.stdout.write("\n");
@@ -13151,14 +13323,14 @@ function printRouteTable(routes) {
13151
13323
  function formatRestV1IntegrationLabel(integration) {
13152
13324
  switch (integration.kind) {
13153
13325
  case "mock": return "[MOCK]";
13154
- case "http-proxy": return `[HTTP_PROXY ${integration.uri}]`;
13155
- case "http": return `[HTTP ${integration.uri}]`;
13156
- case "aws-lambda": return `${integration.lambdaLogicalId} [AWS]`;
13326
+ case "http-proxy": return `[HTTP_PROXY ${displayUntrustedValue(String(integration.uri))}]`;
13327
+ case "http": return `[HTTP ${displayUntrustedValue(String(integration.uri))}]`;
13328
+ case "aws-lambda": return `${displayIdent(integration.lambdaLogicalId)} [AWS]`;
13157
13329
  }
13158
13330
  }
13159
13331
  function materializeInlineCode$2(handler, source, fileExtension, tmpDirsOut) {
13160
13332
  const lastDot = handler.lastIndexOf(".");
13161
- if (lastDot <= 0) throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);
13333
+ if (lastDot <= 0) throw new Error(`Handler ${displayUntrustedValue(handler)} is malformed: expected '<modulePath>.<exportName>'.`);
13162
13334
  const modulePath = handler.substring(0, lastDot);
13163
13335
  const dir = mkdtempSync(path$1.join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-start-api-`));
13164
13336
  tmpDirsOut.add(dir);
@@ -13378,7 +13550,7 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options,
13378
13550
  if (ssmParameters.secureStringLogicalIds.length > 0) bundle.ssmSecureStringLogicalIds = ssmParameters.secureStringLogicalIds;
13379
13551
  }
13380
13552
  out.set(stackName, bundle);
13381
- logger.debug(`${provider.label}: loaded state for ${stackName} (${loaded.region})`);
13553
+ logger.debug(`${provider.label}: loaded state for ${displayStackName(stackName)} (${loaded.region})`);
13382
13554
  } finally {
13383
13555
  provider.dispose();
13384
13556
  }
@@ -13545,7 +13717,7 @@ async function localInvokeCommand(target, options, extraStateProviders) {
13545
13717
  onMissing: () => new CdkLocalError(`${getEmbedConfig().cliName} invoke requires a <target> (a Lambda display path or logical ID). Run \`${getEmbedConfig().cliName} list\` to see them, or run it in a TTY to pick interactively.`, "LOCAL_INVOKE_TARGET_REQUIRED")
13546
13718
  }), stacks);
13547
13719
  const targetLabel = lambda.kind === "zip" ? lambda.runtime : "container image";
13548
- logger.info(`Target: ${lambda.stack.stackName}/${lambda.logicalId} (${targetLabel})`);
13720
+ logger.info(`Target: ${displayStackName(`${lambda.stack.stackName}/${lambda.logicalId}`)} (${targetLabel})`);
13549
13721
  imagePlan = await resolveImagePlan(lambda, options);
13550
13722
  const containerEnv = await resolveLambdaContainerEnv(lambda, options, profileCredentials, extraStateProviders);
13551
13723
  const dockerEnv = containerEnv.env;
@@ -13717,7 +13889,7 @@ async function resolveContainerImagePlan$1(lambda, options) {
13717
13889
  assetOutdir: localBuild.assetOutdir
13718
13890
  });
13719
13891
  else {
13720
- if (!parseEcrUri(lambda.imageUri)) throw new Error(`Container Lambda '${lambda.logicalId}' has no matching asset in cdk.out, and Code.ImageUri '${lambda.imageUri}' is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. Re-synthesize the CDK app (so cdk.out includes the build context) or deploy the image to ECR first.`);
13892
+ if (!parseEcrUri(lambda.imageUri)) throw new Error(`Container Lambda ${displayIdent(lambda.logicalId)} has no matching asset in cdk.out, and Code.ImageUri ${displayUntrustedValue(String(lambda.imageUri))} is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. Re-synthesize the CDK app (so cdk.out includes the build context) or deploy the image to ECR first.`);
13721
13893
  logger.info(`No matching cdk.out asset for ${lambda.imageUri}; falling back to ECR pull (same-acct/region only)...`);
13722
13894
  imageRef = await pullEcrImage(lambda.imageUri, {
13723
13895
  skipPull: options.pull === false,
@@ -13742,7 +13914,7 @@ async function resolveLocalBuildPlan(lambda) {
13742
13914
  const manifestPath = lambda.stack.assetManifestPath;
13743
13915
  if (!manifestPath) return void 0;
13744
13916
  const cdkOutDir = dirname(manifestPath);
13745
- const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, lambda.stack.stackName);
13917
+ const manifest = await new AssetManifestLoader().loadStackManifest(lambda.stack, cdkOutDir);
13746
13918
  if (!manifest) return void 0;
13747
13919
  const entry = getDockerImageBySourceHash(manifest, lambda.imageUri);
13748
13920
  if (!entry) return void 0;
@@ -13959,7 +14131,7 @@ async function resolveLambdaContainerEnvWith(lambda, options, profileCredentials
13959
14131
  for (const key of keysNotFromTemplate(templateEnv, envResult.resolved)) plaintextKeys.add(key);
13960
14132
  const dynamic = await resolveDynamicReferencesInEnv(envResult.resolved, {
13961
14133
  region: firstUsableRegion(ownerRegion, options.stackRegion, lambda.stack.region, options.region, profileCredentials?.region),
13962
- label: `Lambda ${lambda.logicalId}`,
14134
+ label: `Lambda ${displayIdent(lambda.logicalId)}`,
13963
14135
  skipKeys: plaintextKeys,
13964
14136
  resolver: dynamicRefs
13965
14137
  });
@@ -14020,7 +14192,7 @@ async function resolveLambdaContainerEnvWith(lambda, options, profileCredentials
14020
14192
  }
14021
14193
  function materializeInlineCode$1(handler, source, fileExtension) {
14022
14194
  const lastDot = handler.lastIndexOf(".");
14023
- if (lastDot <= 0) throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);
14195
+ if (lastDot <= 0) throw new Error(`Handler ${displayUntrustedValue(handler)} is malformed: expected '<modulePath>.<exportName>'.`);
14024
14196
  const modulePath = handler.substring(0, lastDot);
14025
14197
  const dir = mkdtempSync(path$1.join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-invoke-`));
14026
14198
  const filePath = path$1.join(dir, `${modulePath}${fileExtension}`);
@@ -14049,13 +14221,13 @@ async function resolveAssumeRoleArnForLambda(assumeRole, stateForRoleHint, state
14049
14221
  const fnPhysicalId = stateForRoleHint.resources[lambdaLogicalId]?.physicalId;
14050
14222
  if (stateProvider?.resolveLambdaExecutionRoleArn && fnPhysicalId) {
14051
14223
  const liveArn = await stateProvider.resolveLambdaExecutionRoleArn(fnPhysicalId);
14052
- if (liveArn !== void 0 && !isIamRoleArn(liveArn)) logger.warn(`--assume-role: GetFunctionConfiguration for '${lambdaLogicalId}' produced a value that is not a well-formed IAM role ARN: ${describeRejectedRoleArn(liveArn)}. Ignoring it.`);
14224
+ if (liveArn !== void 0 && !isIamRoleArn(liveArn)) logger.warn(`--assume-role: GetFunctionConfiguration for ${displayIdent(lambdaLogicalId)} produced a value that is not a well-formed IAM role ARN: ${describeRejectedRoleArn(liveArn)}. Ignoring it.`);
14053
14225
  else if (liveArn) {
14054
14226
  logger.info(`--assume-role: auto-resolved execution role from GetFunctionConfiguration: ${flattenToOneLine(liveArn)}`);
14055
14227
  return liveArn;
14056
14228
  }
14057
14229
  }
14058
- logger.warn(`--assume-role: could not resolve the execution role ARN for '${lambdaLogicalId}'. Pass the ARN explicitly: --assume-role <arn>. Falling back to the developer's shell credentials.`);
14230
+ logger.warn(`--assume-role: could not resolve the execution role ARN for ${displayIdent(lambdaLogicalId)}. Pass the ARN explicitly: --assume-role <arn>. Falling back to the developer's shell credentials.`);
14059
14231
  }
14060
14232
  function resolveExecutionRoleArnFromState(state, logicalId, roleProperty = "Role") {
14061
14233
  const lambda = state.resources[logicalId];
@@ -14064,7 +14236,7 @@ function resolveExecutionRoleArnFromState(state, logicalId, roleProperty = "Role
14064
14236
  const roleRef = lambda.properties?.[roleProperty] ?? lambda.observedProperties?.[roleProperty];
14065
14237
  if (typeof roleRef === "string") {
14066
14238
  if (isIamRoleArn(roleRef)) return roleRef;
14067
- logger.warn(`Deployed state for '${logicalId}' carries a ${roleProperty} that is not a well-formed IAM role ARN: ${describeRejectedRoleArn(roleRef)}. Ignoring it.`);
14239
+ logger.warn(`Deployed state for ${displayIdent(logicalId)} carries a ${displayIdent(roleProperty)} that is not a well-formed IAM role ARN: ${describeRejectedRoleArn(roleRef)}. Ignoring it.`);
14068
14240
  return;
14069
14241
  }
14070
14242
  if (typeof roleRef === "object" && roleRef !== null) {
@@ -14072,7 +14244,7 @@ function resolveExecutionRoleArnFromState(state, logicalId, roleProperty = "Role
14072
14244
  if (refLogicalId) {
14073
14245
  const cached = state.resources[refLogicalId]?.attributes?.["Arn"];
14074
14246
  if (isIamRoleArn(cached)) return cached;
14075
- if (typeof cached === "string" && cached.length > 0) logger.warn(`The cached Arn attribute of '${refLogicalId}' is not a well-formed IAM role ARN: ${describeRejectedRoleArn(cached)}. Ignoring it.`);
14247
+ if (typeof cached === "string" && cached.length > 0) logger.warn(`The cached Arn attribute of ${displayIdent(refLogicalId)} is not a well-formed IAM role ARN: ${describeRejectedRoleArn(cached)}. Ignoring it.`);
14076
14248
  }
14077
14249
  }
14078
14250
  }
@@ -14213,7 +14385,7 @@ const SUPPORTED_CODE_RUNTIMES = Object.keys(RUNTIME_BASE_IMAGES);
14213
14385
  async function buildAgentCoreCodeImage(options) {
14214
14386
  const logger = getLogger();
14215
14387
  const base = RUNTIME_BASE_IMAGES[options.runtime];
14216
- if (!base) throw new LocalInvokeBuildError(`AgentCore CodeConfiguration runtime '${options.runtime}' is not supported for local execution. Supported runtimes: ${SUPPORTED_CODE_RUNTIMES.join(", ")}.`);
14388
+ if (!base) throw new LocalInvokeBuildError(`AgentCore CodeConfiguration runtime ${displayIdent(options.runtime)} is not supported for local execution. Supported runtimes: ${SUPPORTED_CODE_RUNTIMES.join(", ")}.`);
14217
14389
  const isNode = options.runtime.startsWith("NODE");
14218
14390
  await warnIfDependenciesNotVendored(options.sourceDir, options.runtime, isNode, logger);
14219
14391
  const dockerfile = renderCodeDockerfile(base, options.entryPoint, isNode);
@@ -14221,7 +14393,7 @@ async function buildAgentCoreCodeImage(options) {
14221
14393
  const platform = options.architecture === "x86_64" ? "linux/amd64" : "linux/arm64";
14222
14394
  if (options.noBuild === true) {
14223
14395
  logger.info(`Skipping docker build (--no-build). Verifying ${tag} is in local registry...`);
14224
- if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image '${tag}' not in local registry and --no-build is set; remove --no-build or run the build manually first.`);
14396
+ if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image ${displayIdent(tag)} not in local registry and --no-build is set; remove --no-build or run the build manually first.`);
14225
14397
  return tag;
14226
14398
  }
14227
14399
  logger.info(`Building agent image from source (runtime=${options.runtime}, platform=${platform})...`);
@@ -14242,7 +14414,7 @@ async function buildAgentCoreCodeImage(options) {
14242
14414
  ], { progressLabel: `Building agent image (runtime=${options.runtime})` });
14243
14415
  } catch (err) {
14244
14416
  const stderr = err.stderr?.trim();
14245
- throw new LocalInvokeBuildError(`docker build failed for AgentCore code artifact (${displayUntrustedValue(options.sourceDir)})${stderr ? `: ${stderr}` : ""}`);
14417
+ throw new LocalInvokeBuildError(`docker build failed for AgentCore code artifact (${displayUntrustedValue(options.sourceDir)})${stderr ? `: ${displayToolOutput(stderr)}` : ""}`);
14246
14418
  } finally {
14247
14419
  await rm(buildDir, {
14248
14420
  recursive: true,
@@ -14352,7 +14524,7 @@ function formatRef(location) {
14352
14524
  function resolveSafeEntryPath(root, entry) {
14353
14525
  const dest = normalize(join(root, entry));
14354
14526
  const rootWithSep = root.endsWith(sep) ? root : root + sep;
14355
- if (dest !== root && !dest.startsWith(rootWithSep)) throw new CdkLocalError(`Refusing to extract a fromS3 bundle entry that escapes the target dir: '${entry}'.`, "LOCAL_INVOKE_AGENTCORE_S3_BUNDLE_ZIP_SLIP");
14527
+ if (dest !== root && !dest.startsWith(rootWithSep)) throw new CdkLocalError(`Refusing to extract a fromS3 bundle entry that escapes the target dir: ${displayUntrustedValue(entry)}.`, "LOCAL_INVOKE_AGENTCORE_S3_BUNDLE_ZIP_SLIP");
14356
14528
  return dest;
14357
14529
  }
14358
14530
  function defaultFetchObject$1(options) {
@@ -14928,7 +15100,7 @@ async function invokeAgentCoreWs(host, port, event, options) {
14928
15100
  //#region src/cli/commands/local-invoke-agentcore.ts
14929
15101
  function parseTimeoutMs(raw) {
14930
15102
  const parsed = Number(raw);
14931
- if (!Number.isInteger(parsed) || parsed <= 0) throw new CdkLocalError(`--timeout must be a positive integer number of milliseconds (got '${raw}').`, "LOCAL_INVOKE_AGENTCORE_TIMEOUT_INVALID");
15103
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new InvalidArgumentError(`--timeout must be a positive integer number of milliseconds (got '${raw}').`);
14932
15104
  return parsed;
14933
15105
  }
14934
15106
  async function localInvokeAgentCoreCommand(target, options, extraStateProviders) {
@@ -14998,7 +15170,7 @@ async function localInvokeAgentCoreCommand(target, options, extraStateProviders)
14998
15170
  loaded: void 0
14999
15171
  };
15000
15172
  const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);
15001
- logger.info(`Target: ${resolved.stack.stackName}/${resolved.logicalId} (${resolved.protocol})`);
15173
+ logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.logicalId}`)} (${resolved.protocol})`);
15002
15174
  const isMcp = resolved.protocol === "MCP";
15003
15175
  const isA2a = resolved.protocol === "A2A";
15004
15176
  if (resolved.protocol === "AGUI") logger.info("AGUI runtime: routing through the HTTP /invocations + /ws path (AG-UI wire is SSE / WebSocket on port 8080).");
@@ -15160,10 +15332,10 @@ async function resolveInboundAuthorization(resolved, options) {
15160
15332
  const header = options.bearerToken ? `Bearer ${options.bearerToken}` : void 0;
15161
15333
  if (!authorizer) return header;
15162
15334
  if (options.verifyAuth === false) {
15163
- logger.warn(`Runtime '${resolved.logicalId}' declares a customJwtAuthorizer, but --no-verify-auth was set — skipping inbound JWT verification (local-dev escape hatch).`);
15335
+ logger.warn(`Runtime ${displayIdent(resolved.logicalId)} declares a customJwtAuthorizer, but --no-verify-auth was set — skipping inbound JWT verification (local-dev escape hatch).`);
15164
15336
  return header;
15165
15337
  }
15166
- if (!header) throw new CdkLocalError(`Runtime '${resolved.logicalId}' requires an inbound JWT (customJwtAuthorizer). Pass --bearer-token <jwt>, or --no-verify-auth to skip verification for local dev.`, "LOCAL_INVOKE_AGENTCORE_AUTH_REQUIRED");
15338
+ if (!header) throw new CdkLocalError(`Runtime ${displayIdent(resolved.logicalId)} requires an inbound JWT (customJwtAuthorizer). Pass --bearer-token <jwt>, or --no-verify-auth to skip verification for local dev.`, "LOCAL_INVOKE_AGENTCORE_AUTH_REQUIRED");
15167
15339
  if (!(await verifyJwtViaDiscovery({
15168
15340
  discoveryUrl: authorizer.discoveryUrl,
15169
15341
  ...authorizer.allowedAudience && { allowedAudience: authorizer.allowedAudience },
@@ -15178,7 +15350,7 @@ async function resolveAgentCoreSigV4Context(options, resolved, loaded, stateProv
15178
15350
  if (!options.sigv4) return void 0;
15179
15351
  if (options.bearerToken) throw new CdkLocalError(`--sigv4 and --bearer-token are mutually exclusive: pick one inbound auth.`, "LOCAL_INVOKE_AGENTCORE_AUTH_CONFLICT");
15180
15352
  if (resolved.jwtAuthorizer) {
15181
- getLogger().warn(`Runtime '${resolved.logicalId}' declares a customJwtAuthorizer; --sigv4 ignored (JWT path takes precedence).`);
15353
+ getLogger().warn(`Runtime ${displayIdent(resolved.logicalId)} declares a customJwtAuthorizer; --sigv4 ignored (JWT path takes precedence).`);
15182
15354
  return;
15183
15355
  }
15184
15356
  const region = options.region ?? options.stackRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? resolved.stack.region;
@@ -15249,11 +15421,11 @@ async function resolveAgentCoreImage(resolved, options, loaded, stateProvider) {
15249
15421
  const architecture = platformToArchitecture(options.platform);
15250
15422
  if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture, loaded, stateProvider);
15251
15423
  const containerUri = resolved.containerUri;
15252
- if (containerUri === void 0) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' has neither a container image nor a code artifact to run.`, "LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT");
15424
+ if (containerUri === void 0) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} has neither a container image nor a code artifact to run.`, "LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT");
15253
15425
  const manifestPath = resolved.stack.assetManifestPath;
15254
15426
  if (manifestPath) {
15255
15427
  const cdkOutDir = dirname(manifestPath);
15256
- const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, resolved.stack.stackName);
15428
+ const manifest = await new AssetManifestLoader().loadStackManifest(resolved.stack, cdkOutDir);
15257
15429
  if (manifest) {
15258
15430
  const entry = getDockerImageBySourceHash(manifest, containerUri);
15259
15431
  if (entry) return buildContainerImage(entry.asset, cdkOutDir, {
@@ -15278,13 +15450,13 @@ async function resolveAgentCoreImage(resolved, options, loaded, stateProvider) {
15278
15450
  async function resolveAgentCoreCodeImage(resolved, code, options, architecture, loaded, stateProvider) {
15279
15451
  if (code.s3Source) return resolveAgentCoreCodeImageFromS3(resolved, code, code.s3Source, options, architecture, loaded, stateProvider);
15280
15452
  const manifestPath = resolved.stack.assetManifestPath;
15281
- if (!manifestPath) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' uses a code artifact, but its stack has no asset manifest in cdk.out to read the bundle source from.`, "LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST");
15453
+ if (!manifestPath) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} uses a code artifact, but its stack has no asset manifest in cdk.out to read the bundle source from.`, "LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST");
15282
15454
  const cdkOutDir = dirname(manifestPath);
15283
15455
  const loader = new AssetManifestLoader();
15284
- const manifest = await loader.loadManifest(cdkOutDir, resolved.stack.stackName);
15456
+ const manifest = await loader.loadStackManifest(resolved.stack, cdkOutDir);
15285
15457
  const fileAssets = manifest ? loader.getFileAssets(manifest) : void 0;
15286
15458
  const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey$1(fileAssets, code.codeAssetHash) : void 0;
15287
- if (!asset) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' code bundle (asset ${code.codeAssetHash}) was not found in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of a fromCodeAsset bundle — re-synthesize the app so the asset is staged in cdk.out and retry. (A fromS3 bundle is downloaded from S3 instead; this runtime has no literal Code.S3.Bucket.)`, "LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND");
15459
+ if (!asset) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} code bundle (asset ${displayIdent(code.codeAssetHash)}) was not found in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of a fromCodeAsset bundle — re-synthesize the app so the asset is staged in cdk.out and retry. (A fromS3 bundle is downloaded from S3 instead; this runtime has no literal Code.S3.Bucket.)`, "LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND");
15288
15460
  const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset, {
15289
15461
  assetOutdir: assetPathDirs(resolved.stack).assetOutdir,
15290
15462
  subject: `AgentCore Runtime ${displayUntrustedValue(resolved.logicalId)} code bundle`,
@@ -15301,7 +15473,7 @@ async function resolveAgentCoreCodeImage(resolved, code, options, architecture,
15301
15473
  }
15302
15474
  async function resolveAgentCoreCodeImageFromS3(resolved, code, s3Source, options, architecture, loaded, stateProvider) {
15303
15475
  const logger = getLogger();
15304
- if (typeof s3Source.bucket !== "string" || s3Source.bucket.length === 0) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' fromS3 bundle reached the image step with no literal bucket. This is a cdk-local bug — please report it.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_UNRESOLVED");
15476
+ if (typeof s3Source.bucket !== "string" || s3Source.bucket.length === 0) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} fromS3 bundle reached the image step with no literal bucket. This is a cdk-local bug — please report it.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_UNRESOLVED");
15305
15477
  const location = {
15306
15478
  bucket: s3Source.bucket,
15307
15479
  key: s3Source.key,
@@ -15408,7 +15580,7 @@ async function resolveFromS3BucketIntrinsic(resolved, stateProvider, loaded, ima
15408
15580
  const s3Source = resolved.codeArtifact?.s3Source;
15409
15581
  if (!s3Source || s3Source.bucketIntrinsic === void 0) return;
15410
15582
  if (s3Source.bucket !== void 0) return;
15411
- if (!stateProvider || !loaded) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' fromS3 bundle's Code.S3.Bucket is an unresolved intrinsic (${describeIntrinsic(s3Source.bucketIntrinsic)}). Pass --from-cfn-stack so its physical bucket name can be resolved against the deployed stack state.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NO_STATE");
15583
+ if (!stateProvider || !loaded) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} fromS3 bundle's Code.S3.Bucket is an unresolved intrinsic (${displayUntrustedValue(describeIntrinsic(s3Source.bucketIntrinsic))}). Pass --from-cfn-stack so its physical bucket name can be resolved against the deployed stack state.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NO_STATE");
15412
15584
  const subContext = {
15413
15585
  resources: imageContext?.stateResources ?? loaded.resources,
15414
15586
  consumerRegion: loaded.region
@@ -15418,8 +15590,8 @@ async function resolveFromS3BucketIntrinsic(resolved, stateProvider, loaded, ima
15418
15590
  const crossStackResolver = await stateProvider.buildCrossStackResolver(loaded.region);
15419
15591
  if (crossStackResolver) subContext.crossStackResolver = crossStackResolver;
15420
15592
  const result = await substituteAgainstStateAsync(s3Source.bucketIntrinsic, subContext);
15421
- if (result.kind !== "literal") throw new CdkLocalError(`Could not resolve AgentCore Runtime '${resolved.logicalId}' fromS3 Code.S3.Bucket intrinsic (${describeIntrinsic(s3Source.bucketIntrinsic)}) against the --from-cfn-stack state: ${result.reason}. Confirm the referenced resource / export exists in the deployed stack.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_UNRESOLVED");
15422
- if (typeof result.value !== "string" || result.value.length === 0) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' fromS3 Code.S3.Bucket intrinsic resolved to a ${typeof result.value} value, not a bucket name string. (${describeIntrinsic(s3Source.bucketIntrinsic)})`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NOT_STRING");
15593
+ if (result.kind !== "literal") throw new CdkLocalError(`Could not resolve AgentCore Runtime ${displayIdent(resolved.logicalId)} fromS3 Code.S3.Bucket intrinsic (${displayUntrustedValue(describeIntrinsic(s3Source.bucketIntrinsic))}) against the --from-cfn-stack state: ${result.reason}. Confirm the referenced resource / export exists in the deployed stack.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_UNRESOLVED");
15594
+ if (typeof result.value !== "string" || result.value.length === 0) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} fromS3 Code.S3.Bucket intrinsic resolved to a ${typeof result.value} value, not a bucket name string. (${displayUntrustedValue(describeIntrinsic(s3Source.bucketIntrinsic))})`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NOT_STRING");
15423
15595
  s3Source.bucket = result.value;
15424
15596
  getLogger().info(`Resolved fromS3 Code.S3.Bucket from state: ${describeIntrinsic(s3Source.bucketIntrinsic)} -> ${result.value}`);
15425
15597
  }
@@ -15508,7 +15680,7 @@ async function resolveAssumeRoleArn(options, resolved, loaded, stateProvider) {
15508
15680
  if (options.assumeRole !== true) return void 0;
15509
15681
  if (resolved.roleArn !== void 0) {
15510
15682
  if (isIamRoleArn(resolved.roleArn)) return resolved.roleArn;
15511
- getLogger().warn(`--assume-role: the template RoleArn for '${resolved.logicalId}' is not a well-formed IAM role ARN: ${describeRejectedRoleArn(resolved.roleArn)}. Ignoring it.`);
15683
+ getLogger().warn(`--assume-role: the template RoleArn for ${displayIdent(resolved.logicalId)} is not a well-formed IAM role ARN: ${describeRejectedRoleArn(resolved.roleArn)}. Ignoring it.`);
15512
15684
  }
15513
15685
  if (loaded) {
15514
15686
  const fromState = resolveExecutionRoleArnFromState(loaded, resolved.logicalId, "RoleArn");
@@ -15519,7 +15691,7 @@ async function resolveAssumeRoleArn(options, resolved, loaded, stateProvider) {
15519
15691
  const runtimePhysicalId = loaded.resources[resolved.logicalId]?.physicalId;
15520
15692
  if (stateProvider?.resolveAgentCoreRuntimeRoleArn && runtimePhysicalId) {
15521
15693
  const liveArn = await stateProvider.resolveAgentCoreRuntimeRoleArn(runtimePhysicalId);
15522
- if (liveArn !== void 0 && !isIamRoleArn(liveArn)) getLogger().warn(`--assume-role: GetAgentRuntime for '${resolved.logicalId}' produced a value that is not a well-formed IAM role ARN: ${describeRejectedRoleArn(liveArn)}. Ignoring it.`);
15694
+ if (liveArn !== void 0 && !isIamRoleArn(liveArn)) getLogger().warn(`--assume-role: GetAgentRuntime for ${displayIdent(resolved.logicalId)} produced a value that is not a well-formed IAM role ARN: ${describeRejectedRoleArn(liveArn)}. Ignoring it.`);
15523
15695
  else if (liveArn) {
15524
15696
  getLogger().info(`--assume-role: auto-resolved execution role from GetAgentRuntime: ${flattenToOneLine(liveArn)}`);
15525
15697
  return liveArn;
@@ -15869,7 +16041,7 @@ async function loadAgentCoreAssetContext(args) {
15869
16041
  if (!newCandidate) return void 0;
15870
16042
  if (resolved.codeArtifact) {
15871
16043
  if (resolved.codeArtifact.s3Source) return void 0;
15872
- const manifest = await assetLoader.loadManifest(cdkOutDir, newCandidate.stackName);
16044
+ const manifest = await assetLoader.loadStackManifest(newCandidate, cdkOutDir);
15873
16045
  if (!manifest) return void 0;
15874
16046
  const asset = assetLoader.getFileAssets(manifest).get(resolved.codeArtifact.codeAssetHash);
15875
16047
  if (!asset) return void 0;
@@ -15886,7 +16058,7 @@ async function loadAgentCoreAssetContext(args) {
15886
16058
  };
15887
16059
  }
15888
16060
  if (resolved.containerUri === void 0) return void 0;
15889
- const manifest = await assetLoader.loadManifest(cdkOutDir, newCandidate.stackName);
16061
+ const manifest = await assetLoader.loadStackManifest(newCandidate, cdkOutDir);
15890
16062
  if (!manifest) return void 0;
15891
16063
  const dockerImageEntry = getDockerImageBySourceHash(manifest, resolved.containerUri);
15892
16064
  if (!dockerImageEntry) return void 0;
@@ -15917,7 +16089,7 @@ async function deriveOldAssetHash(args) {
15917
16089
  if (resolved.containerUri === void 0) return void 0;
15918
16090
  const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
15919
16091
  if (!candidate) return void 0;
15920
- const manifest = await assetLoader.loadManifest(cdkOutDir, candidate.stackName);
16092
+ const manifest = await assetLoader.loadStackManifest(candidate, cdkOutDir);
15921
16093
  if (!manifest) return void 0;
15922
16094
  return getDockerImageBySourceHash(manifest, resolved.containerUri)?.hash;
15923
16095
  }
@@ -16081,7 +16253,7 @@ async function sweepOrphanedSvcNetworks(prefix) {
16081
16253
  names = stdout.split("\n").map((n) => n.trim()).filter((n) => n.length > 0);
16082
16254
  } catch (err) {
16083
16255
  const e = err;
16084
- logger.debug(`docker network ls (sweep) failed: ${e.stderr || e.message || String(err)}`);
16256
+ logger.debug(`docker network ls (sweep) failed: ${displayToolOutput(e.stderr || e.message || String(err))}`);
16085
16257
  return [];
16086
16258
  }
16087
16259
  const swept = [];
@@ -16098,7 +16270,7 @@ async function sweepOrphanedSvcNetworks(prefix) {
16098
16270
  attached = stdout.split(/\s+/).map((c) => c.trim()).filter((c) => c.length > 0);
16099
16271
  } catch (err) {
16100
16272
  const e = err;
16101
- logger.debug(`docker network inspect ${name} (sweep) failed: ${e.stderr || e.message || String(err)}`);
16273
+ logger.debug(`docker network inspect ${name} (sweep) failed: ${displayToolOutput(e.stderr || e.message || String(err))}`);
16102
16274
  continue;
16103
16275
  }
16104
16276
  const sidecarName = `${name}-metadata`;
@@ -16127,7 +16299,7 @@ async function createNetworkAndSidecar(args) {
16127
16299
  ]);
16128
16300
  } catch (err) {
16129
16301
  const e = err;
16130
- throw new DockerRunnerError(`docker network create failed: ${e.stderr?.trim() || e.message || String(err)}. Hint: another ${getEmbedConfig().productName} run may already own subnet ${cidr}; wait for it to finish, or remove the leftover network with \`docker network ls\` + \`docker network rm\`. \`${getEmbedConfig().cliName} start-service\` shares one network across every service in the run; bare \`${getEmbedConfig().cliName} run-task\` uses a per-task network so only one run can be active at a time.`);
16302
+ throw new DockerRunnerError(`docker network create failed: ${displayToolOutput(e.stderr?.trim() || e.message || String(err))}. Hint: another ${getEmbedConfig().productName} run may already own subnet ${cidr}; wait for it to finish, or remove the leftover network with \`docker network ls\` + \`docker network rm\`. \`${getEmbedConfig().cliName} start-service\` shares one network across every service in the run; bare \`${getEmbedConfig().cliName} run-task\` uses a per-task network so only one run can be active at a time.`);
16131
16303
  }
16132
16304
  const sidecarArgs = [
16133
16305
  "run",
@@ -16160,7 +16332,7 @@ async function createNetworkAndSidecar(args) {
16160
16332
  return stdout.trim();
16161
16333
  } catch (err) {
16162
16334
  await destroyNetworkOnly(networkName);
16163
- throw new DockerRunnerError(`Failed to start metadata-endpoints sidecar: ${err.stderr?.trim() || describeExitWithoutArgv(err)}`);
16335
+ throw new DockerRunnerError(`Failed to start metadata-endpoints sidecar: ${displayToolOutput(err.stderr?.trim() || describeExitWithoutArgv(err))}`);
16164
16336
  }
16165
16337
  }
16166
16338
  async function createTaskNetwork(options = {}) {
@@ -16209,7 +16381,7 @@ async function destroyNetworkOnly(networkName) {
16209
16381
  logger.debug(`Removed docker network ${networkName}`);
16210
16382
  } catch (err) {
16211
16383
  const e = err;
16212
- logger.debug(`docker network rm ${networkName} failed: ${e.stderr || e.message || String(err)}`);
16384
+ logger.debug(`docker network rm ${networkName} failed: ${displayToolOutput(e.stderr || e.message || String(err))}`);
16213
16385
  }
16214
16386
  }
16215
16387
 
@@ -16240,7 +16412,7 @@ async function resolveEcsSecrets(entries, options = {}) {
16240
16412
  try {
16241
16413
  return await Promise.all(entries.map(async (entry) => {
16242
16414
  const value = await resolveOne(entry, secretsClient, ssmClient);
16243
- logger.debug(`Resolved secret ${entry.containerName}.${entry.name} (${entry.valueFrom})`);
16415
+ logger.debug(`Resolved secret ${displayIdent(entry.containerName)}.${displayIdent(entry.name)} (${displayUntrustedValue(entry.valueFrom)})`);
16244
16416
  return {
16245
16417
  ...entry,
16246
16418
  value
@@ -16283,20 +16455,20 @@ async function resolveSecretsManager(entry, shape, client) {
16283
16455
  try {
16284
16456
  secretString = (await client.send(new GetSecretValueCommand({ SecretId: shape.baseArn }))).SecretString;
16285
16457
  } catch (err) {
16286
- throw new EcsSecretsResolutionError(`Failed to resolve Secrets Manager secret for container '${entry.containerName}' / env '${entry.name}' (${shape.baseArn}): ${describeAwsFailureForWarn(err, "SecretsManager GetSecretValue")}`);
16458
+ throw new EcsSecretsResolutionError(`Failed to resolve Secrets Manager secret for container ${displayIdent(entry.containerName)} / env ${displayIdent(entry.name)} (${displayUntrustedValue(shape.baseArn)}): ${describeAwsFailureForWarn(err, "SecretsManager GetSecretValue")}`);
16287
16459
  }
16288
- if (secretString === void 0) throw new EcsSecretsResolutionError(`Secrets Manager returned no SecretString for container '${entry.containerName}' / env '${entry.name}' (${shape.baseArn}). Binary secrets are not supported.`);
16460
+ if (secretString === void 0) throw new EcsSecretsResolutionError(`Secrets Manager returned no SecretString for container ${displayIdent(entry.containerName)} / env ${displayIdent(entry.name)} (${displayUntrustedValue(shape.baseArn)}). Binary secrets are not supported.`);
16289
16461
  if (shape.jsonKey === void 0) return secretString;
16290
16462
  let parsed;
16291
16463
  try {
16292
16464
  parsed = JSON.parse(secretString);
16293
16465
  } catch (err) {
16294
16466
  const kind = err instanceof Error ? err.name : "unknown";
16295
- throw new EcsSecretsResolutionError(`Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but the secret value is not valid JSON (${kind}). The parser detail is withheld because it would echo the secret plaintext.`);
16467
+ throw new EcsSecretsResolutionError(`Container ${displayIdent(entry.containerName)} secret ${displayIdent(entry.name)} specified json-key ${displayIdent(shape.jsonKey)} but the secret value is not valid JSON (${kind}). The parser detail is withheld because it would echo the secret plaintext.`);
16296
16468
  }
16297
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new EcsSecretsResolutionError(`Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but the secret root is not a JSON object.`);
16469
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new EcsSecretsResolutionError(`Container ${displayIdent(entry.containerName)} secret ${displayIdent(entry.name)} specified json-key ${displayIdent(shape.jsonKey)} but the secret root is not a JSON object.`);
16298
16470
  const value = parsed[shape.jsonKey];
16299
- if (value === void 0) throw new EcsSecretsResolutionError(`Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but no such key exists in the secret JSON.`);
16471
+ if (value === void 0) throw new EcsSecretsResolutionError(`Container ${displayIdent(entry.containerName)} secret ${displayIdent(entry.name)} specified json-key ${displayIdent(shape.jsonKey)} but no such key exists in the secret JSON.`);
16300
16472
  return typeof value === "string" ? value : JSON.stringify(value);
16301
16473
  }
16302
16474
  async function resolveSsm(entry, shape, client) {
@@ -16305,11 +16477,11 @@ async function resolveSsm(entry, shape, client) {
16305
16477
  Name: shape.name,
16306
16478
  WithDecryption: true
16307
16479
  }))).Parameter?.Value;
16308
- if (value === void 0) throw new EcsSecretsResolutionError(`SSM parameter '${shape.name}' returned no Value for container '${entry.containerName}' / env '${entry.name}'.`);
16480
+ if (value === void 0) throw new EcsSecretsResolutionError(`SSM parameter ${displayUntrustedValue(shape.name)} returned no Value for container ${displayIdent(entry.containerName)} / env ${displayIdent(entry.name)}.`);
16309
16481
  return value;
16310
16482
  } catch (err) {
16311
16483
  if (err instanceof EcsSecretsResolutionError) throw err;
16312
- throw new EcsSecretsResolutionError(`Failed to resolve SSM parameter for container '${entry.containerName}' / env '${entry.name}' (${shape.name}): ${describeAwsFailureForWarn(err, "SSM GetParameter (ECS secret)")}`);
16484
+ throw new EcsSecretsResolutionError(`Failed to resolve SSM parameter for container ${displayIdent(entry.containerName)} / env ${displayIdent(entry.name)} (${displayUntrustedValue(shape.name)}): ${describeAwsFailureForWarn(err, "SSM GetParameter (ECS secret)")}`);
16313
16485
  }
16314
16486
  }
16315
16487
 
@@ -16374,7 +16546,7 @@ function mergeHostGatewayAddHostFlags(addHostFlags, hostGatewayExtraHosts) {
16374
16546
  }
16375
16547
  async function runEcsTask(task, options, state) {
16376
16548
  const logger = getLogger();
16377
- if (task.containers.length === 0) throw new EcsTaskRunnerError(`Task '${task.taskDefinitionLogicalId}' has no containers — nothing to run.`);
16549
+ if (task.containers.length === 0) throw new EcsTaskRunnerError(`Task ${displayIdent(task.taskDefinitionLogicalId)} has no containers — nothing to run.`);
16378
16550
  for (const w of task.warnings) logger.warn(w);
16379
16551
  const startOrder = topoSort(buildDependencyGraph(task.containers), task.containers);
16380
16552
  const finchRefusals = [];
@@ -16465,7 +16637,7 @@ async function runEcsTask(task, options, state) {
16465
16637
  const dockerCmds = /* @__PURE__ */ new Map();
16466
16638
  for (const container of task.containers) {
16467
16639
  const image = imagePlan.get(container.name);
16468
- if (!image) throw new EcsTaskRunnerError(`Internal: no resolved image for container '${container.name}'.`);
16640
+ if (!image) throw new EcsTaskRunnerError(`Internal: no resolved image for container ${displayIdent(container.name)}.`);
16469
16641
  const dynamic = dynamicEnvByContainer.get(container.name);
16470
16642
  const built = buildDockerRunArgs({
16471
16643
  task,
@@ -16505,7 +16677,7 @@ async function runEcsTask(task, options, state) {
16505
16677
  const container = task.containers.find((c) => c.name === containerName);
16506
16678
  await awaitDependencies(container, startedByName);
16507
16679
  const { args, sensitiveEnv } = dockerCmds.get(container.name);
16508
- logger.info(`Starting container '${container.name}' (image=${imagePlan.get(container.name)})`);
16680
+ logger.info(`Starting container ${displayIdent(container.name)} (image=${displayIdent(imagePlan.get(container.name))})`);
16509
16681
  let id;
16510
16682
  warnFinchArgvExposure(Object.keys(sensitiveEnv));
16511
16683
  try {
@@ -16516,7 +16688,7 @@ async function runEcsTask(task, options, state) {
16516
16688
  id = stdout.trim();
16517
16689
  } catch (err) {
16518
16690
  const e = err;
16519
- throw new DockerRunnerError(`docker run failed for container '${container.name}': ${e.stderr?.trim() || describeExitWithoutArgv(err)}`);
16691
+ throw new DockerRunnerError(`docker run failed for container ${displayIdent(container.name)}: ${displayToolOutput(e.stderr?.trim() || describeExitWithoutArgv(err))}`);
16520
16692
  }
16521
16693
  state.startedContainers.push({
16522
16694
  name: container.name,
@@ -16526,7 +16698,7 @@ async function runEcsTask(task, options, state) {
16526
16698
  id,
16527
16699
  container
16528
16700
  });
16529
- if (!options.detach) state.logStoppers.push(attachContainerLogStreamer(`[${container.name}] `, id));
16701
+ if (!options.detach) state.logStoppers.push(attachContainerLogStreamer(`[${displayIdent(container.name)}] `, id));
16530
16702
  }
16531
16703
  if (options.detach) return {
16532
16704
  exitCode: 0,
@@ -16535,7 +16707,7 @@ async function runEcsTask(task, options, state) {
16535
16707
  options.onReady?.();
16536
16708
  const essential = task.containers.find((c) => c.essential) ?? task.containers[0];
16537
16709
  const essentialId = startedByName.get(essential.name)?.id;
16538
- if (!essentialId) throw new EcsTaskRunnerError(`Essential container '${essential.name}' did not start.`);
16710
+ if (!essentialId) throw new EcsTaskRunnerError(`Essential container ${displayIdent(essential.name)} did not start.`);
16539
16711
  return {
16540
16712
  exitCode: await waitForContainerExit(essentialId),
16541
16713
  essentialContainerName: essential.name,
@@ -16578,7 +16750,7 @@ function topoSort(g, containers) {
16578
16750
  async function awaitDependencies(container, started) {
16579
16751
  for (const dep of container.dependsOn) {
16580
16752
  const entry = started.get(dep.containerName);
16581
- if (!entry) throw new EcsTaskRunnerError(`Container '${container.name}' depends on '${dep.containerName}' but the latter never started.`);
16753
+ if (!entry) throw new EcsTaskRunnerError(`Container ${displayIdent(container.name)} depends on ${displayIdent(dep.containerName)} but the latter never started.`);
16582
16754
  switch (dep.condition) {
16583
16755
  case "START": break;
16584
16756
  case "COMPLETE":
@@ -16586,7 +16758,7 @@ async function awaitDependencies(container, started) {
16586
16758
  break;
16587
16759
  case "SUCCESS": {
16588
16760
  const code = await waitForContainerExit(entry.id);
16589
- if (code !== 0) throw new EcsTaskRunnerError(`Container '${container.name}' requires dependency '${dep.containerName}' to exit 0, but it exited ${code}.`);
16761
+ if (code !== 0) throw new EcsTaskRunnerError(`Container ${displayIdent(container.name)} requires dependency ${displayIdent(dep.containerName)} to exit 0, but it exited ${code}.`);
16590
16762
  break;
16591
16763
  }
16592
16764
  case "HEALTHY":
@@ -16609,18 +16781,18 @@ async function waitForContainerHealthy(containerId, displayName) {
16609
16781
  ]);
16610
16782
  const status = stdout.trim();
16611
16783
  if (status !== lastStatus) {
16612
- logger.debug(`Container '${displayName}' health status: ${status}`);
16784
+ logger.debug(`Container ${displayIdent(displayName)} health status: ${status}`);
16613
16785
  lastStatus = status;
16614
16786
  }
16615
16787
  if (status === "healthy") return;
16616
- if (status === "unhealthy") throw new EcsTaskRunnerError(`Container '${displayName}' health status is 'unhealthy'; aborting before dependents start.`);
16788
+ if (status === "unhealthy") throw new EcsTaskRunnerError(`Container ${displayIdent(displayName)} health status is 'unhealthy'; aborting before dependents start.`);
16617
16789
  } catch (err) {
16618
16790
  if (err instanceof EcsTaskRunnerError) throw err;
16619
- logger.debug(`docker inspect on '${displayName}' failed: ${err instanceof Error ? err.message : String(err)}`);
16791
+ logger.debug(`docker inspect on ${displayIdent(displayName)} failed: ${err instanceof Error ? err.message : String(err)}`);
16620
16792
  }
16621
16793
  await sleep$1(1e3);
16622
16794
  }
16623
- throw new EcsTaskRunnerError(`Container '${displayName}' did not become healthy within 5 minutes.`);
16795
+ throw new EcsTaskRunnerError(`Container ${displayIdent(displayName)} did not become healthy within 5 minutes.`);
16624
16796
  }
16625
16797
  async function waitForContainerExit(containerId) {
16626
16798
  try {
@@ -16651,12 +16823,12 @@ async function prepareImages(task, out, options) {
16651
16823
  const overrideTag = options.imageOverrideByContainer?.get(container.name);
16652
16824
  if (overrideTag !== void 0) {
16653
16825
  out.set(container.name, overrideTag);
16654
- logger.debug(`Container '${container.name}' image=${overrideTag} (--image-override)`);
16826
+ logger.debug(`Container ${displayIdent(container.name)} image=${displayIdent(overrideTag)} (--image-override)`);
16655
16827
  continue;
16656
16828
  }
16657
16829
  const image = await prepareOneImage(task, container, options);
16658
16830
  out.set(container.name, image);
16659
- logger.debug(`Container '${container.name}' image=${image}`);
16831
+ logger.debug(`Container ${displayIdent(container.name)} image=${displayIdent(image)}`);
16660
16832
  }
16661
16833
  }
16662
16834
  async function prepareOneImage(task, container, options) {
@@ -16673,30 +16845,30 @@ async function prepareOneImage(task, container, options) {
16673
16845
  });
16674
16846
  case "cdk-asset": {
16675
16847
  const cdkOutDir = task.stack.assetManifestPath ? dirname(task.stack.assetManifestPath) : void 0;
16676
- if (!cdkOutDir) throw new EcsTaskRunnerError(`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.`);
16677
- const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, task.stack.stackName);
16678
- if (!manifest) throw new EcsTaskRunnerError(`No asset manifest at ${cdkOutDir} for stack ${task.stack.stackName}.`);
16848
+ if (!cdkOutDir) throw new EcsTaskRunnerError(`Container ${displayIdent(container.name)} uses a CDK asset image but the stack has no asset manifest. Re-synthesize the app (without \`--output <stale-dir>\`) and retry.`);
16849
+ const manifest = await new AssetManifestLoader().loadStackManifest(task.stack, cdkOutDir);
16850
+ if (!manifest) throw new EcsTaskRunnerError(`No asset manifest at ${displayUntrustedValue(cdkOutDir)} for stack ${displayStackName(task.stack.stackName)}.`);
16679
16851
  const dockerImages = manifest.dockerImages ?? {};
16680
16852
  const entries = Object.entries(dockerImages);
16681
16853
  let asset;
16682
16854
  if (image.assetHash && dockerImages[image.assetHash]) asset = dockerImages[image.assetHash];
16683
16855
  else if (entries.length === 1) asset = entries[0][1];
16684
- if (!asset) throw new EcsTaskRunnerError(`Container '${container.name}' references a CDK asset image but no matching entry was found in cdk.out. Re-synthesize the CDK app and retry.`);
16856
+ if (!asset) throw new EcsTaskRunnerError(`Container ${displayIdent(container.name)} references a CDK asset image but no matching entry was found in cdk.out. Re-synthesize the CDK app and retry.`);
16685
16857
  const tag = `${getEmbedConfig().resourceNamePrefix}-run-task-${(image.assetHash ?? "single").slice(0, 16)}`;
16686
16858
  if (options.skipBuild === true) {
16687
16859
  const logger = getLogger().child("ecs-runner");
16688
- if (image.assetHash === void 0) throw new LocalInvokeBuildError(`Cannot honor --no-build for ECS container '${container.name}': the asset has no stable assetHash (synthetic tag '${tag}' may collide with unrelated prior runs). Drop --no-build, or re-synthesize with an explicit assetHash on the ContainerImage.fromAsset call.`);
16689
- logger.info(`Skipping docker build (--no-build) for container '${container.name}'; verifying ${tag} is in local registry...`);
16690
- if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image '${tag}' not in local registry and --no-build is set (ECS container '${container.name}'); remove --no-build or run \`docker build\` manually first.`);
16691
- logger.debug(`Local tag ${tag} is cached; skipping build for container '${container.name}'.`);
16860
+ if (image.assetHash === void 0) throw new LocalInvokeBuildError(`Cannot honor --no-build for ECS container ${displayIdent(container.name)}: the asset has no stable assetHash (synthetic tag ${displayIdent(tag)} may collide with unrelated prior runs). Drop --no-build, or re-synthesize with an explicit assetHash on the ContainerImage.fromAsset call.`);
16861
+ logger.info(`Skipping docker build (--no-build) for container ${displayIdent(container.name)}; verifying ${displayIdent(tag)} is in local registry...`);
16862
+ if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image ${displayIdent(tag)} not in local registry and --no-build is set (ECS container ${displayIdent(container.name)}); remove --no-build or run \`docker build\` manually first.`);
16863
+ logger.debug(`Local tag ${displayIdent(tag)} is cached; skipping build for container ${displayIdent(container.name)}.`);
16692
16864
  return tag;
16693
16865
  }
16694
16866
  const actualTag = await buildDockerImage(asset, cdkOutDir, {
16695
16867
  tag,
16696
16868
  assetOutdir: assetPathDirs(task.stack).assetOutdir,
16697
16869
  ...options.platformOverride !== void 0 && { platform: options.platformOverride },
16698
- wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for ECS container '${container.name}' (${displayUntrustedValue(asset.source.directory ?? asset.source.executable?.[0] ?? "")}): ${stderr}`),
16699
- progressLabel: `Building container image for '${container.name}'`
16870
+ wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for ECS container ${displayIdent(container.name)} (${displayUntrustedValue(asset.source.directory ?? asset.source.executable?.[0] ?? "")}): ${stderr}`),
16871
+ progressLabel: `Building container image for ${displayIdent(container.name)}`
16700
16872
  });
16701
16873
  if (actualTag !== tag) try {
16702
16874
  await runDockerStreaming([
@@ -16706,7 +16878,7 @@ async function prepareOneImage(task, container, options) {
16706
16878
  ]);
16707
16879
  } catch (err) {
16708
16880
  const e = err;
16709
- throw new LocalInvokeBuildError(`docker tag failed re-tagging '${actualTag}' → '${tag}' for ECS container '${container.name}': ${e.stderr?.trim() || e.message || String(err)}`);
16881
+ throw new LocalInvokeBuildError(`docker tag failed re-tagging ${displayIdent(actualTag)} → ${displayIdent(tag)} for ECS container ${displayIdent(container.name)}: ${displayToolOutput(e.stderr?.trim() || e.message || String(err))}`);
16710
16882
  }
16711
16883
  return tag;
16712
16884
  }
@@ -16731,10 +16903,10 @@ async function realizeDockerVolumes(volumes, state) {
16731
16903
  try {
16732
16904
  await execFileAsync$2(getDockerCmd(), args);
16733
16905
  state.dockerVolumeNames.push(dockerVolumeName);
16734
- logger.debug(`Created docker volume ${dockerVolumeName} for task volume '${v.name}'`);
16906
+ logger.debug(`Created docker volume ${displayIdent(dockerVolumeName)} for task volume ${displayIdent(v.name)}`);
16735
16907
  } catch (err) {
16736
16908
  const e = err;
16737
- throw new DockerRunnerError(`docker volume create failed for '${v.name}': ${e.stderr?.trim() || describeExitWithoutArgv(err)}`);
16909
+ throw new DockerRunnerError(`docker volume create failed for ${displayIdent(v.name)}: ${displayToolOutput(e.stderr?.trim() || describeExitWithoutArgv(err))}`);
16738
16910
  }
16739
16911
  out.set(v.name, {
16740
16912
  ...v,
@@ -16796,7 +16968,7 @@ async function resolvePrivilegedHostPortRemaps(opts) {
16796
16968
  seen.add(cp);
16797
16969
  const freePort = await allocate();
16798
16970
  remaps[cp] = freePort;
16799
- getLogger().child("ecs").warn(`Container '${container.name}' container port ${cp} declares a privileged host port ${declaredHostPort}, which cannot be published locally (macOS Docker Desktop rejects privileged-port publishing; a host port < 1024 needs root). Auto-remapped to host port ${freePort}. Pass --host-port ${cp}=<hostPort> to pin a specific host port.`);
16971
+ getLogger().child("ecs").warn(`Container ${displayIdent(container.name)} container port ${cp} declares a privileged host port ${declaredHostPort}, which cannot be published locally (macOS Docker Desktop rejects privileged-port publishing; a host port < 1024 needs root). Auto-remapped to host port ${freePort}. Pass --host-port ${cp}=<hostPort> to pin a specific host port.`);
16800
16972
  }
16801
16973
  return remaps;
16802
16974
  }
@@ -16829,7 +17001,7 @@ function buildDockerRunArgs(opts) {
16829
17001
  const hostPort = opts.hostPortOverrides?.[pm.containerPort] ?? declaredHostPort;
16830
17002
  const overridden = hostPort !== declaredHostPort;
16831
17003
  const overrideNote = opts.autoRemappedContainerPorts?.has(pm.containerPort) ?? false ? " (privileged-port auto-remap)" : overridden ? " (--host-port override)" : "";
16832
- getLogger().child("ecs").info(`Container '${container.name}' container port ${pm.containerPort} published on ${formatAuthority(containerHost, hostPort)}${overrideNote}. Reach it at ${formatAuthority(containerHost, hostPort)}.`);
17004
+ getLogger().child("ecs").info(`Container ${displayIdent(container.name)} container port ${pm.containerPort} published on ${formatAuthority(containerHost, hostPort)}${overrideNote}. Reach it at ${formatAuthority(containerHost, hostPort)}.`);
16833
17005
  args.push("-p", `${containerHost}:${hostPort}:${pm.containerPort}/${pm.protocol}`);
16834
17006
  publishedEndpoints.push({
16835
17007
  containerName: container.name,
@@ -16923,7 +17095,7 @@ function shellJoin(parts) {
16923
17095
  function resolveEcsServiceTarget(target, stacks, context, options) {
16924
17096
  if (stacks.length === 0) throw new EcsTaskResolutionError("No stacks found in the synthesized assembly.");
16925
17097
  const parsed = parseEcsTarget(target);
16926
- const stack = pickStack$2(parsed, stacks);
17098
+ const stack = pickStack$2(parsed, stacks, target);
16927
17099
  const resources = stack.template.Resources ?? {};
16928
17100
  let serviceLogicalId;
16929
17101
  let serviceResource;
@@ -16940,15 +17112,15 @@ function resolveEcsServiceTarget(target, stacks, context, options) {
16940
17112
  serviceLogicalId = parsed.pathOrId;
16941
17113
  }
16942
17114
  if (!serviceLogicalId || !serviceResource) throw notFoundError(target, stack, resources, parsed);
16943
- if (serviceResource.Type === "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`Resource '${serviceLogicalId}' in ${stack.stackName} is an ECS TaskDefinition, not a Service. Use \`${getEmbedConfig().cliName} run-task\` for one-shot tasks; \`${getEmbedConfig().cliName} start-service\` is Service-only.`);
16944
- if (serviceResource.Type !== "AWS::ECS::Service") throw new EcsTaskResolutionError(`Resource '${serviceLogicalId}' in ${stack.stackName} is ${serviceResource.Type}, not an AWS::ECS::Service.`);
17115
+ if (serviceResource.Type === "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`Resource ${displayIdent(serviceLogicalId)} in ${stack.stackName} is an ECS TaskDefinition, not a Service. Use \`${getEmbedConfig().cliName} run-task\` for one-shot tasks; \`${getEmbedConfig().cliName} start-service\` is Service-only.`);
17116
+ if (serviceResource.Type !== "AWS::ECS::Service") throw new EcsTaskResolutionError(`Resource ${displayIdent(serviceLogicalId)} in ${stack.stackName} is ${displayIdent(serviceResource.Type)}, not an AWS::ECS::Service.`);
16945
17117
  return extractServiceProperties(stack, serviceLogicalId, serviceResource, stacks, context, options);
16946
17118
  }
16947
17119
  function extractServiceProperties(stack, serviceLogicalId, resource, stacks, context, options) {
16948
17120
  const props = resource.Properties ?? {};
16949
17121
  const warnings = [];
16950
17122
  const taskDefRef = props["TaskDefinition"];
16951
- if (taskDefRef === void 0 || taskDefRef === null) throw new EcsTaskResolutionError(`ECS Service '${serviceLogicalId}' in ${stack.stackName} has no TaskDefinition property.`);
17123
+ if (taskDefRef === void 0 || taskDefRef === null) throw new EcsTaskResolutionError(`ECS Service ${displayIdent(serviceLogicalId)} in ${stack.stackName} has no TaskDefinition property.`);
16952
17124
  const taskDefLogicalId = resolveTaskDefinitionReference(taskDefRef, stack, serviceLogicalId);
16953
17125
  const task = resolveEcsTaskTarget(`${stack.stackName}:${taskDefLogicalId}`, stacks, context);
16954
17126
  const desiredCount = parseDesiredCount(props["DesiredCount"], serviceLogicalId);
@@ -16957,7 +17129,7 @@ function extractServiceProperties(stack, serviceLogicalId, resource, stacks, con
16957
17129
  const serviceDisplayName = deriveServiceDisplayName(props["ServiceName"], serviceLogicalId, resource.Metadata);
16958
17130
  if (!options?.suppressLoadBalancerWarning && Array.isArray(props["LoadBalancers"]) && props["LoadBalancers"].length > 0) {
16959
17131
  const { cliName } = getEmbedConfig();
16960
- warnings.push(`ECS Service '${serviceLogicalId}' declares LoadBalancers, but \`${cliName} start-service\` runs the replicas only; no local listener fronts them. Reach the containers via their published ports, or run \`${cliName} start-alb <Stack>/<Alb>\` to boot the same replicas behind a local front-door that round-robins the listener rules.`);
17132
+ warnings.push(`ECS Service ${displayIdent(serviceLogicalId)} declares LoadBalancers, but \`${cliName} start-service\` runs the replicas only; no local listener fronts them. Reach the containers via their published ports, or run \`${cliName} start-alb <Stack>/<Alb>\` to boot the same replicas behind a local front-door that round-robins the listener rules.`);
16961
17133
  }
16962
17134
  const serviceConnect = extractServiceConnect(props["ServiceConnectConfiguration"], task);
16963
17135
  const out = {
@@ -16995,7 +17167,10 @@ function extractServiceConnect(raw, task) {
16995
17167
  const portName = typeof e["PortName"] === "string" ? e["PortName"] : void 0;
16996
17168
  if (!portName) throw new EcsTaskResolutionError(`ServiceConnectConfiguration.Services[] entry has no PortName: ${JSON.stringify(entry)}. Every Service entry must reference a producer-side PortMappings[].Name.`);
16997
17169
  const containerPort = portByName.get(portName);
16998
- if (containerPort === void 0) throw new EcsTaskResolutionError(`ServiceConnectConfiguration.Services[].PortName='${portName}' does not match any PortMappings[].Name on the producer TaskDef (available: ${[...portByName.keys()].join(", ") || "(none)"}).`);
17170
+ if (containerPort === void 0) {
17171
+ const available = [...portByName.keys()].join(", ") || "(none)";
17172
+ throw new EcsTaskResolutionError(`ServiceConnectConfiguration.Services[].PortName=${displayIdent(portName)} does not match any PortMappings[].Name on the producer TaskDef (available: ${available}).`);
17173
+ }
16999
17174
  const clientAliases = [];
17000
17175
  if (Array.isArray(e["ClientAliases"])) for (const ca of e["ClientAliases"]) {
17001
17176
  if (!ca || typeof ca !== "object") continue;
@@ -17027,7 +17202,7 @@ function extractServiceRegistries(raw, serviceLogicalId, warnings) {
17027
17202
  const registryArn = e["RegistryArn"];
17028
17203
  let cloudMapServiceLogicalId;
17029
17204
  if (typeof registryArn === "string") {
17030
- warnings.push(`ECS Service '${serviceLogicalId}' ServiceRegistries[] entry has a literal-string RegistryArn ('${registryArn}'); ${getEmbedConfig().productName} cannot resolve external Cloud Map services locally. Skipping this registration; peer services will not discover this endpoint through the in-process registry. Use Fn::GetAtt: [<CloudMapServiceLogicalId>, "Arn"] instead so ${getEmbedConfig().productName} can resolve the namespace + service name from the synthesized template.`);
17205
+ warnings.push(`ECS Service ${displayIdent(serviceLogicalId)} ServiceRegistries[] entry has a literal-string RegistryArn (${displayUntrustedValue(registryArn)}); ${getEmbedConfig().productName} cannot resolve external Cloud Map services locally. Skipping this registration; peer services will not discover this endpoint through the in-process registry. Use Fn::GetAtt: [<CloudMapServiceLogicalId>, "Arn"] instead so ${getEmbedConfig().productName} can resolve the namespace + service name from the synthesized template.`);
17031
17206
  continue;
17032
17207
  }
17033
17208
  if (registryArn && typeof registryArn === "object" && !Array.isArray(registryArn)) {
@@ -17051,18 +17226,18 @@ function resolveTaskDefinitionReference(taskDefRef, stack, serviceLogicalId) {
17051
17226
  const refValue = taskDefRef["Ref"];
17052
17227
  if (typeof refValue === "string") {
17053
17228
  const target = (stack.template.Resources ?? {})[refValue];
17054
- if (!target) throw new EcsTaskResolutionError(`ECS Service '${serviceLogicalId}' references TaskDefinition '${refValue}' but no such resource exists in ${stack.stackName}.`);
17055
- if (target.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`ECS Service '${serviceLogicalId}' references '${refValue}' as TaskDefinition but it is of type ${target.Type}, not AWS::ECS::TaskDefinition.`);
17229
+ if (!target) throw new EcsTaskResolutionError(`ECS Service ${displayIdent(serviceLogicalId)} references TaskDefinition ${displayIdent(refValue)} but no such resource exists in ${stack.stackName}.`);
17230
+ if (target.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`ECS Service ${displayIdent(serviceLogicalId)} references ${displayIdent(refValue)} as TaskDefinition but it is of type ${displayIdent(target.Type)}, not AWS::ECS::TaskDefinition.`);
17056
17231
  return refValue;
17057
17232
  }
17058
17233
  }
17059
- throw new EcsTaskResolutionError(`ECS Service '${serviceLogicalId}' has an unsupported TaskDefinition reference shape: ${JSON.stringify(taskDefRef)}. ${getEmbedConfig().cliName} start-service v1 supports only Ref to a same-stack AWS::ECS::TaskDefinition; cross-stack TaskDefinitions are deferred.`);
17234
+ throw new EcsTaskResolutionError(`ECS Service ${displayIdent(serviceLogicalId)} has an unsupported TaskDefinition reference shape: ${JSON.stringify(taskDefRef)}. ${getEmbedConfig().cliName} start-service v1 supports only Ref to a same-stack AWS::ECS::TaskDefinition; cross-stack TaskDefinitions are deferred.`);
17060
17235
  }
17061
17236
  function parseDesiredCount(raw, serviceLogicalId) {
17062
17237
  if (raw === void 0 || raw === null) return 1;
17063
17238
  if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0) return Math.floor(raw);
17064
17239
  if (typeof raw === "string" && /^\d+$/.test(raw)) return parseInt(raw, 10);
17065
- throw new EcsTaskResolutionError(`ECS Service '${serviceLogicalId}' has an unsupported DesiredCount value: ${JSON.stringify(raw)}. Must be a non-negative integer.`);
17240
+ throw new EcsTaskResolutionError(`ECS Service ${displayIdent(serviceLogicalId)} has an unsupported DesiredCount value: ${JSON.stringify(raw)}. Must be a non-negative integer.`);
17066
17241
  }
17067
17242
  function parseHealthCheckGrace(raw, _serviceLogicalId) {
17068
17243
  if (raw === void 0 || raw === null) return 30;
@@ -17092,15 +17267,14 @@ function deriveServiceDisplayName(rawServiceName, serviceLogicalId, metadata) {
17092
17267
  }
17093
17268
  return serviceLogicalId;
17094
17269
  }
17095
- function pickStack$2(parsed, stacks) {
17096
- if (parsed.stackPattern === null) {
17097
- if (stacks.length === 1) return stacks[0];
17098
- throw new EcsTaskResolutionError(`Target has no stack prefix, and the assembly contains ${stacks.length} stacks: ${stacks.map((s) => s.stackName).join(", ")}. Pass the target as 'Stack/Path' or 'Stack:LogicalId'.`);
17270
+ function pickStack$2(parsed, stacks, target) {
17271
+ const selection = selectTargetStack(parsed, target, stacks);
17272
+ switch (selection.kind) {
17273
+ case "found": return selection.stack;
17274
+ case "no-prefix": throw new EcsTaskResolutionError(`Target has no stack prefix, and the assembly contains ${stacks.length} stacks: ${describeStacks(stacks)}. Pass the target as 'Stack/Path' or 'Stack:LogicalId'.`);
17275
+ case "not-found": throw new EcsTaskResolutionError(`No stack matches '${selection.pattern}'. Available stacks: ${describeStacks(stacks)}.`);
17276
+ case "ambiguous": throw new EcsTaskResolutionError(`Multiple stacks match '${selection.pattern}': ${describeStacks(selection.matched)}. Refine the pattern, or use the stack's display path (path form).`);
17099
17277
  }
17100
- const matched = matchStacks(stacks, [parsed.stackPattern]);
17101
- if (matched.length === 0) throw new EcsTaskResolutionError(`No stack matches '${parsed.stackPattern}'. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
17102
- if (matched.length > 1) throw new EcsTaskResolutionError(`Multiple stacks match '${parsed.stackPattern}': ${matched.map((s) => s.stackName).join(", ")}. Refine the pattern.`);
17103
- return matched[0];
17104
17278
  }
17105
17279
  function notFoundError(target, stack, resources, parsed) {
17106
17280
  const services = [];
@@ -17117,10 +17291,10 @@ function notFoundError(target, stack, resources, parsed) {
17117
17291
  const width = Math.max(...services.map((s) => s.displayPath.length));
17118
17292
  let msg = `Target '${target}' did not match any ECS Service in ${stack.stackName}.\n\n`;
17119
17293
  msg += `Available services in ${stack.stackName} (CDK path and logical ID):\n`;
17120
- for (const s of services) msg += ` ${s.displayPath.padEnd(width)} (${s.logicalId})\n`;
17294
+ for (const s of services) msg += ` ${s.displayPath.padEnd(width)} (${displayIdent(s.logicalId)})\n`;
17121
17295
  if (parsed.isPath) {
17122
17296
  const tail = parsed.pathOrId.includes("/") ? parsed.pathOrId.slice(parsed.pathOrId.lastIndexOf("/") + 1) : parsed.pathOrId;
17123
- if (services.some((s) => s.logicalId === tail)) msg += `\n'${tail}' is a logical ID, but the 'Stack/Path' form matches CDK construct paths. To target it by logical ID, use the colon form: '${stack.stackName}:${tail}'.`;
17297
+ if (services.some((s) => s.logicalId === tail)) msg += `\n${displayIdent(tail)} is a logical ID, but the 'Stack/Path' form matches CDK construct paths. To target it by logical ID, use the colon form: ${displayStackName(`${stack.stackName}:${tail}`)}.`;
17124
17298
  }
17125
17299
  return new EcsTaskResolutionError(msg.trimEnd());
17126
17300
  }
@@ -17201,8 +17375,8 @@ async function startEcsService(service, options, runState) {
17201
17375
  const logger = getLogger().child("ecs-service");
17202
17376
  for (const w of service.warnings) logger.warn(w);
17203
17377
  const replicaCount = computeReplicaCount(service.desiredCount, options.maxTasks);
17204
- if (replicaCount < service.desiredCount) logger.warn(`Service '${service.serviceName}' template DesiredCount=${service.desiredCount} exceeds --max-tasks=${options.maxTasks}; running ${replicaCount} replica(s) locally. Raise --max-tasks to lift the cap, or accept the reduced concurrency for local dev.`);
17205
- logger.info(`Starting ECS service '${service.serviceName}' with ${replicaCount} replica(s) (restartPolicy=${options.restartPolicy})`);
17378
+ if (replicaCount < service.desiredCount) logger.warn(`Service ${displayIdent(service.serviceName)} template DesiredCount=${service.desiredCount} exceeds --max-tasks=${options.maxTasks}; running ${replicaCount} replica(s) locally. Raise --max-tasks to lift the cap, or accept the reduced concurrency for local dev.`);
17379
+ logger.info(`Starting ECS service ${displayIdent(service.serviceName)} with ${replicaCount} replica(s) (restartPolicy=${options.restartPolicy})`);
17206
17380
  for (let i = 0; i < replicaCount; i++) {
17207
17381
  const instance = {
17208
17382
  index: i,
@@ -17221,7 +17395,7 @@ async function startEcsService(service, options, runState) {
17221
17395
  await bootPromise;
17222
17396
  } catch (err) {
17223
17397
  instance.lastError = err instanceof Error ? err : new Error(String(err));
17224
- throw new EcsServiceRunnerError(`Failed to boot replica ${i} of service '${service.serviceName}': ${instance.lastError.message}`);
17398
+ throw new EcsServiceRunnerError(`Failed to boot replica ${i} of service ${displayIdent(service.serviceName)}: ${instance.lastError.message}`);
17225
17399
  } finally {
17226
17400
  instance.inFlightBoot = void 0;
17227
17401
  }
@@ -17258,7 +17432,7 @@ var ServiceController = class {
17258
17432
  async doShutdown() {
17259
17433
  this.runState.shuttingDown = true;
17260
17434
  const logger = getLogger().child("ecs-service");
17261
- logger.info(`Shutting down service '${this.service.serviceName}'...`);
17435
+ logger.info(`Shutting down service ${displayIdent(this.service.serviceName)}...`);
17262
17436
  for (const r of this.runState.replicas) r.shuttingDown = true;
17263
17437
  const inFlightBoots = this.runState.replicas.map((r) => r.inFlightBoot).filter((p) => p !== void 0);
17264
17438
  if (inFlightBoots.length > 0) {
@@ -17334,10 +17508,10 @@ async function bootReplica(service, options, instance, bootOpts = {}) {
17334
17508
  ...containerNameAliasOmitted.size > 0 ? { containerNameAliasOmitted } : {},
17335
17509
  ...ephemeralPublishContainerPorts.length > 0 ? { ephemeralPublishContainerPorts } : {}
17336
17510
  };
17337
- logger.info(`Booting replica ${instance.index} (${perReplicaCluster})`);
17511
+ logger.info(`Booting replica ${instance.index} (${displayIdent(perReplicaCluster)})`);
17338
17512
  await runEcsTask(service.task, perReplicaTaskOptions, instance.state);
17339
17513
  if (options.streamLogs !== false) for (const started of instance.state.startedContainers) {
17340
- const prefix = `[svc=${service.serviceDisplayName} r=${instance.index} c=${started.name}] `;
17514
+ const prefix = `[svc=${displayStackName(service.serviceDisplayName)} r=${instance.index} c=${displayIdent(started.name)}] `;
17341
17515
  instance.state.logStoppers.push(attachContainerLogStreamer(prefix, started.id, { expectRestart: () => instance.softReloadInProgress === true }));
17342
17516
  }
17343
17517
  if (options.discovery && !bootOpts.deferPublish) await publishReplicaToCloudMap(service, instance, options.discovery, ownerKeyPrefix);
@@ -17362,13 +17536,13 @@ async function publishGatedReplica(service, options, instance, deadline, isShutt
17362
17536
  const port = container.portMappings[0]?.containerPort;
17363
17537
  if (port !== void 0) {
17364
17538
  if (await waitForContainerListening(started.id, port, deadline) === "exited") {
17365
- getLogger().child("ecs-service").debug(`${label}: '${name}' exited before listening; leaving its aliases withheld.`);
17539
+ getLogger().child("ecs-service").debug(`${label}: ${displayIdent(name)} exited before listening; leaving its aliases withheld.`);
17366
17540
  continue;
17367
17541
  }
17368
17542
  }
17369
17543
  if (isShuttingDown()) return;
17370
17544
  const ip = await getContainerNetworkIp(started.id, network.networkName);
17371
- if (!ip) throw new EcsServiceRunnerError(`${label}: no docker IP for '${name}' on ${network.networkName}; cannot attach its Service Connect aliases.`);
17545
+ if (!ip) throw new EcsServiceRunnerError(`${label}: no docker IP for ${displayIdent(name)} on ${network.networkName}; cannot attach its Service Connect aliases.`);
17372
17546
  const aliases = [name];
17373
17547
  for (const a of fullAliases.get(name) ?? []) if (!aliases.includes(a)) aliases.push(a);
17374
17548
  await dockerNetworkDisconnectImpl(network.networkName, started.id);
@@ -17417,7 +17591,7 @@ async function publishReplicaToCloudMap(service, instance, discovery, ownerKeyPr
17417
17591
  if (service.serviceConnect) {
17418
17592
  const ns = service.serviceConnect.namespaceName;
17419
17593
  const index = discovery.cloudMapIndexByStack.get(service.stack.stackName);
17420
- if (index && !index.namespacesByName.has(ns)) logger.warn(`ECS Service '${service.serviceLogicalId}' ServiceConnectConfiguration.Namespace='${ns}' does not match any AWS::ServiceDiscovery::PrivateDnsNamespace declared in stack ${service.stack.stackName}. Publishing under the literal name anyway; peer services using the same literal will still discover this endpoint.`);
17594
+ if (index && !index.namespacesByName.has(ns)) logger.warn(`ECS Service ${displayIdent(service.serviceLogicalId)} ServiceConnectConfiguration.Namespace=${displayIdent(ns)} does not match any AWS::ServiceDiscovery::PrivateDnsNamespace declared in stack ${displayStackName(service.stack.stackName)}. Publishing under the literal name anyway; peer services using the same literal will still discover this endpoint.`);
17421
17595
  let i = 0;
17422
17596
  for (const entry of service.serviceConnect.services) {
17423
17597
  const ownerKey = `${ownerKeyPrefix}:sc:${i}`;
@@ -17434,20 +17608,20 @@ async function publishReplicaToCloudMap(service, instance, discovery, ownerKeyPr
17434
17608
  if (service.serviceRegistries.length > 0) {
17435
17609
  const index = discovery.cloudMapIndexByStack.get(service.stack.stackName);
17436
17610
  if (!index) {
17437
- logger.warn(`ECS Service '${service.serviceLogicalId}' declares ServiceRegistries[] but ${getEmbedConfig().productName} has no Cloud Map index for stack ${service.stack.stackName}. Skipping registration.`);
17611
+ logger.warn(`ECS Service ${displayIdent(service.serviceLogicalId)} declares ServiceRegistries[] but ${getEmbedConfig().productName} has no Cloud Map index for stack ${displayStackName(service.stack.stackName)}. Skipping registration.`);
17438
17612
  return;
17439
17613
  }
17440
17614
  let j = 0;
17441
17615
  for (const reg of service.serviceRegistries) {
17442
17616
  const cm = index.servicesByLogicalId.get(reg.cloudMapServiceLogicalId);
17443
17617
  if (!cm) {
17444
- logger.warn(`ECS Service '${service.serviceLogicalId}' ServiceRegistries[].cloudMapServiceLogicalId='${reg.cloudMapServiceLogicalId}' did not resolve to an AWS::ServiceDiscovery::Service in stack ${service.stack.stackName}. Skipping this registration.`);
17618
+ logger.warn(`ECS Service ${displayIdent(service.serviceLogicalId)} ServiceRegistries[].cloudMapServiceLogicalId=${displayIdent(reg.cloudMapServiceLogicalId)} did not resolve to an AWS::ServiceDiscovery::Service in stack ${displayStackName(service.stack.stackName)}. Skipping this registration.`);
17445
17619
  continue;
17446
17620
  }
17447
17621
  let port = reg.containerPort;
17448
17622
  if (port === void 0 && essential.portMappings.length > 0) port = essential.portMappings[0].containerPort;
17449
17623
  if (port === void 0) {
17450
- logger.warn(`ECS Service '${service.serviceLogicalId}' ServiceRegistries[] entry for Cloud Map service '${cm.logicalId}' has no resolvable container port; skipping.`);
17624
+ logger.warn(`ECS Service ${displayIdent(service.serviceLogicalId)} ServiceRegistries[] entry for Cloud Map service ${displayIdent(cm.logicalId)} has no resolvable container port; skipping.`);
17451
17625
  continue;
17452
17626
  }
17453
17627
  const ownerKey = `${ownerKeyPrefix}:sr:${j}`;
@@ -17467,7 +17641,7 @@ async function publishReplicaToFrontDoor(service, instance, frontDoor, container
17467
17641
  for (const target of frontDoor.pools) {
17468
17642
  const started = instance.state.startedContainers.find((c) => c.name === target.targetContainerName);
17469
17643
  if (!started) {
17470
- logger.warn(`ECS Service '${service.serviceLogicalId}' front-door: container '${target.targetContainerName}' did not start for replica ${instance.index}; the front-door cannot route to it.`);
17644
+ logger.warn(`ECS Service ${displayIdent(service.serviceLogicalId)} front-door: container ${displayIdent(target.targetContainerName)} did not start for replica ${instance.index}; the front-door cannot route to it.`);
17471
17645
  continue;
17472
17646
  }
17473
17647
  let hostPort;
@@ -17478,7 +17652,7 @@ async function publishReplicaToFrontDoor(service, instance, frontDoor, container
17478
17652
  continue;
17479
17653
  }
17480
17654
  if (hostPort === void 0) {
17481
- logger.warn(`Replica ${instance.index}: container port ${target.targetContainerPort} on '${target.targetContainerName}' was not published on a host port; skipping front-door registration for this replica.`);
17655
+ logger.warn(`Replica ${instance.index}: container port ${target.targetContainerPort} on ${displayIdent(target.targetContainerName)} was not published on a host port; skipping front-door registration for this replica.`);
17482
17656
  continue;
17483
17657
  }
17484
17658
  target.pool.register(ownerKeyPrefix, {
@@ -17602,13 +17776,13 @@ async function softReloadReplica(args) {
17602
17776
  }
17603
17777
  const essentialContainers = newService.task.containers.filter((c) => c.essential);
17604
17778
  const containersToCycle = essentialContainers.length > 0 ? essentialContainers : newService.task.containers.length > 0 ? [newService.task.containers[0]] : [];
17605
- if (containersToCycle.length === 0) throw new EcsServiceRunnerError(`softReloadReplica: service '${newService.serviceLogicalId}' has no containers to cycle.`);
17779
+ if (containersToCycle.length === 0) throw new EcsServiceRunnerError(`softReloadReplica: service ${displayIdent(newService.serviceLogicalId)} has no containers to cycle.`);
17606
17780
  const startedById = new Map(instance.state.startedContainers.map((c) => [c.name, c.id]));
17607
17781
  const networkAliasesByContainer = buildNetworkAliasesByContainer(newService);
17608
17782
  const targets = [];
17609
17783
  for (const container of containersToCycle) {
17610
17784
  const id = startedById.get(container.name);
17611
- if (!id) throw new EcsServiceRunnerError(`softReloadReplica: replica r${instance.index} has no started container named '${container.name}' (started: ${[...startedById.keys()].join(", ") || "none"}).`);
17785
+ if (!id) throw new EcsServiceRunnerError(`softReloadReplica: replica r${instance.index} has no started container named ${displayIdent(container.name)} (started: ${[...startedById.keys()].map((k) => displayIdent(k, { listMember: true })).join(", ") || "none"}).`);
17612
17786
  const aliases = [container.name];
17613
17787
  for (const a of networkAliasesByContainer.get(container.name) ?? []) if (!aliases.includes(a)) aliases.push(a);
17614
17788
  const port = container.portMappings[0]?.containerPort;
@@ -17637,13 +17811,13 @@ async function softReloadReplica(args) {
17637
17811
  try {
17638
17812
  workdir = await dockerInspectWorkdirImpl(target.id) || "/";
17639
17813
  } catch (err) {
17640
- throw new EcsServiceRunnerError(`softReloadReplica: docker inspect of container '${target.name}' (${target.id}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17814
+ throw new EcsServiceRunnerError(`softReloadReplica: docker inspect of container ${displayIdent(target.name)} (${target.id}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17641
17815
  }
17642
17816
  const workdirDest = workdir.endsWith("/") ? workdir : `${workdir}/`;
17643
17817
  try {
17644
17818
  await dockerCpImpl(`${sourceDirToCopy}/.`, `${target.id}:${workdirDest}`);
17645
17819
  } catch (err) {
17646
- throw new EcsServiceRunnerError(`softReloadReplica: docker cp into '${target.name}' (${target.id}:${workdir}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17820
+ throw new EcsServiceRunnerError(`softReloadReplica: docker cp into ${displayIdent(target.name)} (${target.id}:${workdir}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17647
17821
  }
17648
17822
  }
17649
17823
  const sharedNetworkName = instance.state.network?.ownedByCaller ? instance.state.network.networkName : void 0;
@@ -17666,7 +17840,7 @@ async function softReloadReplica(args) {
17666
17840
  }
17667
17841
  await dockerStartImpl(target.id);
17668
17842
  } catch (err) {
17669
- throw new EcsServiceRunnerError(`softReloadReplica: docker stop / start of '${target.name}' (${target.id}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17843
+ throw new EcsServiceRunnerError(`softReloadReplica: docker stop / start of ${displayIdent(target.name)} (${target.id}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17670
17844
  }
17671
17845
  const aliasDeadline = Date.now() + shadowReadyTimeoutMs;
17672
17846
  const ready = await waitForReplicaTcpReady(newService, instance, {
@@ -17683,7 +17857,7 @@ async function softReloadReplica(args) {
17683
17857
  plan.attached = false;
17684
17858
  await attachToSharedNetwork(target.id, plan, true);
17685
17859
  } catch (err) {
17686
- throw new EcsServiceRunnerError(`softReloadReplica: restoring the network aliases of '${target.name}' (${target.id}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17860
+ throw new EcsServiceRunnerError(`softReloadReplica: restoring the network aliases of ${displayIdent(target.name)} (${target.id}) failed: ${err instanceof Error ? err.message : String(err)}. This replica is unregistered from Cloud Map + the front-door pool until the next save triggers another reload.`);
17687
17861
  }
17688
17862
  }
17689
17863
  const ownerKeyPrefix = replicaOwnerKeyPrefix(newService, instance);
@@ -17718,7 +17892,7 @@ async function softReloadReplica(args) {
17718
17892
  const defaultDockerInspectWorkdirImpl = async (containerId) => {
17719
17893
  const { execFile } = await import("node:child_process");
17720
17894
  const { promisify } = await import("node:util");
17721
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
17895
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17722
17896
  const { stdout } = await promisify(execFile)(getDockerCmd(), [
17723
17897
  "inspect",
17724
17898
  "--format",
@@ -17731,7 +17905,7 @@ let dockerInspectWorkdirImpl = defaultDockerInspectWorkdirImpl;
17731
17905
  const defaultDockerCpImpl = async (src, dst) => {
17732
17906
  const { execFile } = await import("node:child_process");
17733
17907
  const { promisify } = await import("node:util");
17734
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
17908
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17735
17909
  await promisify(execFile)(getDockerCmd(), [
17736
17910
  "cp",
17737
17911
  src,
@@ -17742,13 +17916,13 @@ let dockerCpImpl = defaultDockerCpImpl;
17742
17916
  const defaultDockerStopImpl = async (containerId) => {
17743
17917
  const { execFile } = await import("node:child_process");
17744
17918
  const { promisify } = await import("node:util");
17745
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
17919
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17746
17920
  await promisify(execFile)(getDockerCmd(), ["stop", containerId]);
17747
17921
  };
17748
17922
  const defaultDockerStartImpl = async (containerId) => {
17749
17923
  const { execFile } = await import("node:child_process");
17750
17924
  const { promisify } = await import("node:util");
17751
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
17925
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17752
17926
  await promisify(execFile)(getDockerCmd(), ["start", containerId]);
17753
17927
  };
17754
17928
  let dockerStopImpl = defaultDockerStopImpl;
@@ -17762,7 +17936,7 @@ async function detachFromSharedNetwork(target, networkName) {
17762
17936
  ip = void 0;
17763
17937
  }
17764
17938
  if (!ip) {
17765
- logger.debug(`Soft-reload: no IP for '${target.name}' (${target.id}) on ${networkName}; restarting it in place without detaching its network aliases first.`);
17939
+ logger.debug(`Soft-reload: no IP for ${displayIdent(target.name)} (${target.id}) on ${networkName}; restarting it in place without detaching its network aliases first.`);
17766
17940
  return;
17767
17941
  }
17768
17942
  let publishesHostPorts;
@@ -17774,7 +17948,7 @@ async function detachFromSharedNetwork(target, networkName) {
17774
17948
  try {
17775
17949
  await dockerNetworkDisconnectImpl(networkName, target.id);
17776
17950
  } catch (err) {
17777
- logger.debug(`Soft-reload: detaching '${target.name}' (${target.id}) from ${networkName} failed: ${err instanceof Error ? err.message : String(err)}. Restarting it in place.`);
17951
+ logger.debug(`Soft-reload: detaching ${displayIdent(target.name)} (${target.id}) from ${networkName} failed: ${err instanceof Error ? err.message : String(err)}. Restarting it in place.`);
17778
17952
  return;
17779
17953
  }
17780
17954
  return {
@@ -17809,7 +17983,7 @@ async function attachToSharedNetwork(containerId, plan, withAliases) {
17809
17983
  const defaultDockerNetworkConnectImpl = async (networkName, containerId, opts) => {
17810
17984
  const { execFile } = await import("node:child_process");
17811
17985
  const { promisify } = await import("node:util");
17812
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
17986
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17813
17987
  const args = ["network", "connect"];
17814
17988
  if (opts.ip) args.push("--ip", opts.ip);
17815
17989
  for (const a of opts.aliases) args.push("--alias", a);
@@ -17820,7 +17994,7 @@ let dockerNetworkConnectImpl = defaultDockerNetworkConnectImpl;
17820
17994
  const defaultDockerInspectPublishesHostPortsImpl = async (containerId) => {
17821
17995
  const { execFile } = await import("node:child_process");
17822
17996
  const { promisify } = await import("node:util");
17823
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
17997
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17824
17998
  const { stdout } = await promisify(execFile)(getDockerCmd(), [
17825
17999
  "inspect",
17826
18000
  "--format",
@@ -17859,7 +18033,7 @@ async function disconnectOldFromSharedNetwork(oldInstance, opts = {}) {
17859
18033
  const defaultDockerNetworkDisconnectImpl = async (networkName, containerId) => {
17860
18034
  const { execFile } = await import("node:child_process");
17861
18035
  const { promisify } = await import("node:util");
17862
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
18036
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17863
18037
  await promisify(execFile)(getDockerCmd(), [
17864
18038
  "network",
17865
18039
  "disconnect",
@@ -17950,7 +18124,7 @@ const LISTEN_PROBE_SCRIPT = [
17950
18124
  async function dockerExecListenProbe(containerId, port, timeoutMs = 1e4) {
17951
18125
  const { execFile } = await import("node:child_process");
17952
18126
  const { promisify } = await import("node:util");
17953
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
18127
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
17954
18128
  try {
17955
18129
  await promisify(execFile)(getDockerCmd(), [
17956
18130
  "exec",
@@ -18104,7 +18278,7 @@ function pickEssentialContainerId(instance, service) {
18104
18278
  const defaultWaitForExitImpl = async (containerId) => {
18105
18279
  const { execFile } = await import("node:child_process");
18106
18280
  const { promisify } = await import("node:util");
18107
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
18281
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
18108
18282
  const { stdout } = await promisify(execFile)(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
18109
18283
  const code = parseInt(stdout.trim(), 10);
18110
18284
  return Number.isFinite(code) ? code : -1;
@@ -18114,7 +18288,7 @@ const EXIT_LOG_TAIL_LINES = 50;
18114
18288
  const defaultReadContainerLogsImpl = async (containerId) => {
18115
18289
  const { execFile } = await import("node:child_process");
18116
18290
  const { promisify } = await import("node:util");
18117
- const { getDockerCmd } = await import("./docker-cmd-BSdu45mc.js").then((n) => n.t);
18291
+ const { getDockerCmd } = await import("./docker-cmd-CoZdbeos.js").then((n) => n.t);
18118
18292
  const { stdout, stderr } = await promisify(execFile)(getDockerCmd(), [
18119
18293
  "logs",
18120
18294
  "--tail",
@@ -18173,7 +18347,7 @@ var CloudMapRegistry = class {
18173
18347
  const existing = this.aliasIndex.get(alias);
18174
18348
  if (existing !== void 0) {
18175
18349
  if (existing === targetFqdn) return;
18176
- getLogger().child("cloud-map-registry").warn(`ClientAlias DnsName collision: '${alias}' was already mapped to '${existing}'; keeping first-wins binding and ignoring new mapping to '${targetFqdn}'. Likely cause: two Service Connect services declared the same ClientAlias.DnsName.`);
18350
+ getLogger().child("cloud-map-registry").warn(`ClientAlias DnsName collision: ${displayIdent(alias)} was already mapped to ${displayIdent(existing)}; keeping first-wins binding and ignoring new mapping to ${displayIdent(targetFqdn)}. Likely cause: two Service Connect services declared the same ClientAlias.DnsName.`);
18177
18351
  return;
18178
18352
  }
18179
18353
  this.aliasIndex.set(alias, targetFqdn);
@@ -18269,18 +18443,18 @@ function buildCloudMapIndex(stack) {
18269
18443
  const warnings = [];
18270
18444
  const resources = stack.template.Resources ?? {};
18271
18445
  for (const [logicalId, resource] of Object.entries(resources)) {
18272
- if (resource.Type === "AWS::ServiceDiscovery::PublicDnsNamespace") throw new EcsTaskResolutionError(`Stack ${stack.stackName}: AWS::ServiceDiscovery::PublicDnsNamespace '${logicalId}' is not supported by local emulation — public DNS defeats the "local" point. Use a PrivateDnsNamespace for ${getEmbedConfig().cliName} start-service.`);
18273
- if (resource.Type === "AWS::ServiceDiscovery::HttpNamespace") throw new EcsTaskResolutionError(`Stack ${stack.stackName}: AWS::ServiceDiscovery::HttpNamespace '${logicalId}' is not supported by local emulation — HttpNamespace uses the AWS Cloud Map DiscoverInstances API directly (no DNS), which would require shimming the AWS SDK inside every container. Use a PrivateDnsNamespace + DnsConfig instead.`);
18446
+ if (resource.Type === "AWS::ServiceDiscovery::PublicDnsNamespace") throw new EcsTaskResolutionError(`Stack ${displayStackName(stack.stackName)}: AWS::ServiceDiscovery::PublicDnsNamespace ${displayIdent(logicalId)} is not supported by local emulation — public DNS defeats the "local" point. Use a PrivateDnsNamespace for ${getEmbedConfig().cliName} start-service.`);
18447
+ if (resource.Type === "AWS::ServiceDiscovery::HttpNamespace") throw new EcsTaskResolutionError(`Stack ${displayStackName(stack.stackName)}: AWS::ServiceDiscovery::HttpNamespace ${displayIdent(logicalId)} is not supported by local emulation — HttpNamespace uses the AWS Cloud Map DiscoverInstances API directly (no DNS), which would require shimming the AWS SDK inside every container. Use a PrivateDnsNamespace + DnsConfig instead.`);
18274
18448
  if (resource.Type !== "AWS::ServiceDiscovery::PrivateDnsNamespace") continue;
18275
18449
  const props = resource.Properties ?? {};
18276
18450
  const name = typeof props["Name"] === "string" ? props["Name"] : void 0;
18277
- if (!name) throw new EcsTaskResolutionError(`Stack ${stack.stackName}: PrivateDnsNamespace '${logicalId}' has no literal Name property. Intrinsic-valued names are not supported (cross-stack / dynamic namespace names require deploy-state resolution which is out of scope for v1).`);
18451
+ if (!name) throw new EcsTaskResolutionError(`Stack ${displayStackName(stack.stackName)}: PrivateDnsNamespace ${displayIdent(logicalId)} has no literal Name property. Intrinsic-valued names are not supported (cross-stack / dynamic namespace names require deploy-state resolution which is out of scope for v1).`);
18278
18452
  const entry = {
18279
18453
  logicalId,
18280
18454
  name
18281
18455
  };
18282
18456
  namespacesByLogicalId.set(logicalId, entry);
18283
- if (namespacesByName.has(name)) warnings.push(`Stack ${stack.stackName}: two PrivateDnsNamespace resources share Name='${name}' ('${namespacesByName.get(name).logicalId}' and '${logicalId}'). Local emulation routes registrations to the first; the second will silently shadow.`);
18457
+ if (namespacesByName.has(name)) warnings.push(`Stack ${displayStackName(stack.stackName)}: two PrivateDnsNamespace resources share Name=${displayIdent(name)} (${displayIdent(namespacesByName.get(name).logicalId)} and ${displayIdent(logicalId)}). Local emulation routes registrations to the first; the second will silently shadow.`);
18284
18458
  else namespacesByName.set(name, entry);
18285
18459
  }
18286
18460
  for (const [logicalId, resource] of Object.entries(resources)) {
@@ -18288,9 +18462,9 @@ function buildCloudMapIndex(stack) {
18288
18462
  const props = resource.Properties ?? {};
18289
18463
  const namespaceLogicalId = resolveNamespaceIdRef(props["NamespaceId"] ?? props["DnsConfig"]?.["NamespaceId"], stack.stackName, logicalId);
18290
18464
  const ns = namespacesByLogicalId.get(namespaceLogicalId);
18291
- if (!ns) throw new EcsTaskResolutionError(`Stack ${stack.stackName}: AWS::ServiceDiscovery::Service '${logicalId}' references NamespaceId '${namespaceLogicalId}' but no PrivateDnsNamespace with that logical id exists in this stack. Cross-stack Cloud Map namespaces are not supported in v1.`);
18465
+ if (!ns) throw new EcsTaskResolutionError(`Stack ${displayStackName(stack.stackName)}: AWS::ServiceDiscovery::Service ${displayIdent(logicalId)} references NamespaceId ${displayIdent(namespaceLogicalId)} but no PrivateDnsNamespace with that logical id exists in this stack. Cross-stack Cloud Map namespaces are not supported in v1.`);
18292
18466
  const name = typeof props["Name"] === "string" ? props["Name"] : void 0;
18293
- if (!name) throw new EcsTaskResolutionError(`Stack ${stack.stackName}: AWS::ServiceDiscovery::Service '${logicalId}' has no literal Name property. Intrinsic-valued names are not supported in v1.`);
18467
+ if (!name) throw new EcsTaskResolutionError(`Stack ${displayStackName(stack.stackName)}: AWS::ServiceDiscovery::Service ${displayIdent(logicalId)} has no literal Name property. Intrinsic-valued names are not supported in v1.`);
18294
18468
  const dnsRecords = extractDnsRecords(props);
18295
18469
  servicesByLogicalId.set(logicalId, {
18296
18470
  logicalId,
@@ -18315,7 +18489,7 @@ function resolveNamespaceIdRef(raw, stackName, serviceLogicalId) {
18315
18489
  const getAtt = obj["Fn::GetAtt"];
18316
18490
  if (Array.isArray(getAtt) && typeof getAtt[0] === "string") return getAtt[0];
18317
18491
  }
18318
- throw new EcsTaskResolutionError(`Stack ${stackName}: AWS::ServiceDiscovery::Service '${serviceLogicalId}' has an unsupported NamespaceId reference shape: ${JSON.stringify(raw)}. Accepted shapes are {Fn::GetAtt: [<NsLogicalId>, 'Id']} or {Ref: <NsLogicalId>} pointing at a same-stack PrivateDnsNamespace.`);
18492
+ throw new EcsTaskResolutionError(`Stack ${displayStackName(stackName)}: AWS::ServiceDiscovery::Service ${displayIdent(serviceLogicalId)} has an unsupported NamespaceId reference shape: ${JSON.stringify(raw)}. Accepted shapes are {Fn::GetAtt: [<NsLogicalId>, 'Id']} or {Ref: <NsLogicalId>} pointing at a same-stack PrivateDnsNamespace.`);
18319
18493
  }
18320
18494
  function extractDnsRecords(serviceProps) {
18321
18495
  const dnsConfig = serviceProps["DnsConfig"];
@@ -18857,8 +19031,8 @@ function handleLambdaRequest(req, res, lambda, opts) {
18857
19031
  res.end(translated.body);
18858
19032
  done();
18859
19033
  } catch (err) {
18860
- logger.debug(`Lambda target '${lambda.label}' request failed: ${err instanceof Error ? err.message : String(err)}`);
18861
- if (!res.headersSent) writeError(res, 502, `Lambda target '${lambda.label}' behind ${opts.label} failed.`);
19034
+ logger.debug(`Lambda target ${displayIdent(lambda.label)} request failed: ${err instanceof Error ? err.message : String(err)}`);
19035
+ if (!res.headersSent) writeError(res, 502, `Lambda target ${displayIdent(lambda.label)} behind ${opts.label} failed.`);
18862
19036
  else if (!res.writableEnded) res.destroy();
18863
19037
  done();
18864
19038
  }
@@ -18933,7 +19107,7 @@ function handleUpgrade(req, clientSocket, head, opts) {
18933
19107
  return Promise.resolve();
18934
19108
  }
18935
19109
  if ("lambda" in picked) {
18936
- writeRawHttpError(clientSocket, 502, `WebSocket upgrade is not supported for Lambda target groups (${picked.lambda.label}).`);
19110
+ writeRawHttpError(clientSocket, 502, `WebSocket upgrade is not supported for Lambda target groups (${displayIdent(picked.lambda.label)}).`);
18937
19111
  return Promise.resolve();
18938
19112
  }
18939
19113
  return bridgeWebSocket(req, clientSocket, head, picked.pool, opts);
@@ -19462,7 +19636,7 @@ function forwardAwsEnv() {
19462
19636
  }
19463
19637
  function materializeInlineCode(handler, source, fileExtension) {
19464
19638
  const lastDot = handler.lastIndexOf(".");
19465
- if (lastDot <= 0) throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);
19639
+ if (lastDot <= 0) throw new Error(`Handler ${displayIdent(handler)} is malformed: expected '<modulePath>.<exportName>'.`);
19466
19640
  const modulePath = handler.substring(0, lastDot);
19467
19641
  const dir = mkdtempSync(path$1.join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-alb-lambda-`));
19468
19642
  const filePath = path$1.join(dir, `${modulePath}${fileExtension}`);
@@ -19507,7 +19681,7 @@ async function resolveContainerImagePlan(lambda, opts) {
19507
19681
  let localBuilt = false;
19508
19682
  if (manifestPath) {
19509
19683
  const cdkOutDir = path$1.dirname(manifestPath);
19510
- const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, lambda.stack.stackName);
19684
+ const manifest = await new AssetManifestLoader().loadStackManifest(lambda.stack, cdkOutDir);
19511
19685
  const entry = manifest ? getDockerImageBySourceHash(manifest, lambda.imageUri) : void 0;
19512
19686
  if (entry) {
19513
19687
  imageRef = await buildContainerImage(entry.asset, cdkOutDir, {
@@ -19518,8 +19692,8 @@ async function resolveContainerImagePlan(lambda, opts) {
19518
19692
  }
19519
19693
  }
19520
19694
  if (!localBuilt) {
19521
- if (!parseEcrUri(lambda.imageUri)) throw new Error(`Container Lambda '${lambda.logicalId}' has no matching asset in cdk.out, and Code.ImageUri '${lambda.imageUri}' is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. Re-synthesize the CDK app or deploy the image to ECR first.`);
19522
- logger.info(`No matching cdk.out asset for ${lambda.imageUri}; falling back to ECR pull...`);
19695
+ if (!parseEcrUri(lambda.imageUri)) throw new Error(`Container Lambda ${displayIdent(lambda.logicalId)} has no matching asset in cdk.out, and Code.ImageUri ${displayUntrustedValue(lambda.imageUri)} is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. Re-synthesize the CDK app or deploy the image to ECR first.`);
19696
+ logger.info(`No matching cdk.out asset for ${displayUntrustedValue(lambda.imageUri)}; falling back to ECR pull...`);
19523
19697
  imageRef = await pullEcrImage(lambda.imageUri, {
19524
19698
  skipPull: opts.skipPull === true,
19525
19699
  ...opts.region !== void 0 && { region: opts.region },
@@ -19594,7 +19768,7 @@ function createFrontDoorLambdaRunner(lambda, opts) {
19594
19768
  await starting;
19595
19769
  },
19596
19770
  async invoke(event, timeoutMs) {
19597
- if (!containerId || hostPort === void 0) throw new Error(`FrontDoorLambdaRunner('${lambda.logicalId}').invoke called before start() completed.`);
19771
+ if (!containerId || hostPort === void 0) throw new Error(`FrontDoorLambdaRunner(${displayIdent(lambda.logicalId)}).invoke called before start() completed.`);
19598
19772
  return (await invokeRie(opts.containerHost, hostPort, event, timeoutMs ?? defaultTimeoutMs)).payload;
19599
19773
  },
19600
19774
  async stop() {
@@ -19603,7 +19777,7 @@ function createFrontDoorLambdaRunner(lambda, opts) {
19603
19777
  try {
19604
19778
  stopLogStream?.();
19605
19779
  } catch (err) {
19606
- logger.debug(`stopLogStream(${lambda.logicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
19780
+ logger.debug(`stopLogStream(${displayIdent(lambda.logicalId)}) failed: ${err instanceof Error ? err.message : String(err)}`);
19607
19781
  }
19608
19782
  if (containerId) {
19609
19783
  try {
@@ -19879,9 +20053,9 @@ function canonicalizeImageOverrideKeys(rawFlags, pinnedAliases, knownTargets = [
19879
20053
  if (exact.has(key)) return key;
19880
20054
  const matches = targetsByAlias.get(key);
19881
20055
  if (!matches) return key;
19882
- if (matches.size > 1) throw new ImageOverrideError(`${flag} key '${key}' matches more than one pinned target (${[...matches].join(", ")}). Name the target in full instead.`);
20056
+ if (matches.size > 1) throw new ImageOverrideError(`${flag} key '${key}' matches more than one pinned target (${[...matches].map((m) => displayIdent(m, { listMember: true })).join(", ")}). Name the target in full instead.`);
19883
20057
  const target = [...matches][0];
19884
- logger.debug(`${flag} key '${key}' resolved to pinned target '${target}'.`);
20058
+ logger.debug(`${flag} key '${key}' resolved to pinned target ${displayStackName(target)}.`);
19885
20059
  return target;
19886
20060
  };
19887
20061
  const explicit = /* @__PURE__ */ new Map();
@@ -19889,7 +20063,7 @@ function canonicalizeImageOverrideKeys(rawFlags, pinnedAliases, knownTargets = [
19889
20063
  for (const [key, dockerfile] of rawFlags.explicit) {
19890
20064
  const target = canonical(key, "--image-override");
19891
20065
  const earlier = explicitKeyByTarget.get(target);
19892
- if (earlier !== void 0) throw new ImageOverrideError(`--image-override keys '${earlier}' and '${key}' both name the pinned target '${target}'. Name each service target at most once.`);
20066
+ if (earlier !== void 0) throw new ImageOverrideError(`--image-override keys '${earlier}' and '${key}' both name the pinned target ${displayStackName(target)}. Name each service target at most once.`);
19893
20067
  explicitKeyByTarget.set(target, key);
19894
20068
  explicit.set(target, dockerfile);
19895
20069
  }
@@ -19906,7 +20080,7 @@ function canonicalizeImageOverrideKeys(rawFlags, pinnedAliases, knownTargets = [
19906
20080
  continue;
19907
20081
  }
19908
20082
  const conflict = (what) => {
19909
- throw new ImageOverrideError(`Per-service image-override flags give ${what} two different values for the pinned target '${target}' (one of them keyed '${key}'). Pass it once.`);
20083
+ throw new ImageOverrideError(`Per-service image-override flags give ${what} two different values for the pinned target ${displayStackName(target)} (one of them keyed '${key}'). Pass it once.`);
19910
20084
  };
19911
20085
  for (const [k, v] of overlay.buildArgs) {
19912
20086
  if (into.buildArgs.has(k) && into.buildArgs.get(k) !== v) conflict(`build arg '${k}'`);
@@ -19951,7 +20125,7 @@ async function resolveImageOverrides(args) {
19951
20125
  const options = uncovered.map((t) => ({
19952
20126
  value: t,
19953
20127
  label: t,
19954
- ...labels.get(t) ? { hint: labels.get(t) } : {}
20128
+ ...labels.get(t) ? { hint: displayUntrustedValue(labels.get(t)) } : {}
19955
20129
  }));
19956
20130
  const picked = await multiselect({
19957
20131
  message: `Pick the pinned target(s) to bind to '${dockerfileRaw}' (space to toggle, enter to confirm; empty cancels):`,
@@ -19977,7 +20151,7 @@ async function resolveImageOverrides(args) {
19977
20151
  }
19978
20152
  const label = labels.get(target);
19979
20153
  const value = await promptForOverridePath({
19980
- headline: `Detected pinned image on '${target}'` + (label ? ` (${label})` : "") + ".",
20154
+ headline: `Detected pinned image on ${displayStackName(target)}` + (label ? ` (${displayUntrustedValue(String(label))})` : "") + ".",
19981
20155
  discoveredDockerfiles
19982
20156
  });
19983
20157
  if (!value) continue;
@@ -20159,16 +20333,16 @@ async function runImageOverrideBuilds(overrides) {
20159
20333
  for (const [id, src] of entry.buildSecrets.entries()) args.push("--secret", `id=${id},src=${src}`);
20160
20334
  if (entry.targetStage !== void 0) args.push("--target", entry.targetStage);
20161
20335
  args.push(".");
20162
- logger.info(`Building override image for '${target}' from '${entry.dockerfile}' (tag=${tag})...`);
20336
+ logger.info(`Building override image for ${displayStackName(target)} from '${entry.dockerfile}' (tag=${tag})...`);
20163
20337
  try {
20164
20338
  await runDockerStreaming(args, {
20165
20339
  cwd: entry.contextDir,
20166
20340
  env: { BUILDX_NO_DEFAULT_ATTESTATIONS: "1" },
20167
- progressLabel: `Building override image for '${target}'`
20341
+ progressLabel: `Building override image for ${displayStackName(target)}`
20168
20342
  });
20169
20343
  } catch (err) {
20170
20344
  const e = err;
20171
- const wrapped = new ImageOverrideError(`docker build failed for --image-override '${target}' (Dockerfile=${entry.dockerfile}): ` + (e.stderr?.trim() || e.message || String(err)));
20345
+ const wrapped = new ImageOverrideError(`docker build failed for --image-override ${displayStackName(target)} (Dockerfile=${entry.dockerfile}): ` + displayToolOutput(e.stderr?.trim() || e.message || String(err)));
20172
20346
  for (const priorTag of builtTags) try {
20173
20347
  await runDockerStreaming([
20174
20348
  "image",
@@ -20354,8 +20528,8 @@ async function runEcsServiceEmulator(targets, options, strategy, extraStateProvi
20354
20528
  })));
20355
20529
  for (const entry of pinnedEntries) {
20356
20530
  if (imageOverrideTags.has(entry.target)) continue;
20357
- const uriDisplay = entry.label ? `\`${entry.label}\`` : "a deployed registry";
20358
- logger.warn(`'${entry.target}': running image is pinned to a deployed registry (${uriDisplay}). To iterate on local source, either pass \`--image-override <dockerfile>\` (or \`<service>=<dockerfile>\`) to substitute a local build, or drop \`--from-cfn-stack\` and switch the CDK app to \`ContainerImage.fromAsset(...)\`.`);
20531
+ const uriDisplay = entry.label ? displayUntrustedValue(entry.label) : "a deployed registry";
20532
+ logger.warn(`${displayStackName(entry.target)}: running image is pinned to a deployed registry (${uriDisplay}). To iterate on local source, either pass \`--image-override <dockerfile>\` (or \`<service>=<dockerfile>\`) to substitute a local build, or drop \`--from-cfn-stack\` and switch the CDK app to \`ContainerImage.fromAsset(...)\`.`);
20359
20533
  uncoveredPinnedTargets.push(entry.target);
20360
20534
  }
20361
20535
  enforceStrictOverrides(options.strictOverrides === true, uncoveredPinnedTargets);
@@ -20421,7 +20595,7 @@ async function reloadAllServices(args) {
20421
20595
  const newTag = (await runImageOverrideBuilds(/* @__PURE__ */ new Map([[target, entry]]))).get(target);
20422
20596
  if (newTag !== void 0) imageOverrideTags.set(target, newTag);
20423
20597
  } catch (err) {
20424
- logger.warn(`Reload of '${target}': --image-override rebuild failed; keeping the previous local image. (${err instanceof Error ? err.message : String(err)})`);
20598
+ logger.warn(`Reload of ${displayStackName(target)}: --image-override rebuild failed; keeping the previous local image. (${err instanceof Error ? err.message : String(err)})`);
20425
20599
  }
20426
20600
  const { boots: newBoots, warnings } = strategy.resolveBoots(stacks, resolvedTargets);
20427
20601
  for (const w of warnings) logger.warn(w);
@@ -20437,12 +20611,12 @@ async function reloadAllServices(args) {
20437
20611
  for (const pt of perTarget) {
20438
20612
  const newBoot = newBootByTarget.get(pt.boot.target);
20439
20613
  if (!newBoot) {
20440
- logger.warn(`Reload: target '${pt.boot.target}' no longer resolves to a service in the synthesized app; keeping the previous replica(s) serving.`);
20614
+ logger.warn(`Reload: target ${displayStackName(pt.boot.target)} no longer resolves to a service in the synthesized app; keeping the previous replica(s) serving.`);
20441
20615
  continue;
20442
20616
  }
20443
20617
  const controller = pt.controller;
20444
20618
  if (!controller) {
20445
- 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.`);
20619
+ logger.warn(`Reload: target ${displayStackName(pt.boot.target)} has no live controller (previous boot likely failed); skipping roll. \`^C\` and re-run start-service to recover.`);
20446
20620
  continue;
20447
20621
  }
20448
20622
  let verdict = {
@@ -20458,16 +20632,16 @@ async function reloadAllServices(args) {
20458
20632
  assetLoader,
20459
20633
  logger
20460
20634
  }));
20461
- logger.info(`Reload of '${newBoot.target}': verdict=${verdict.kind} (${verdict.reason}).`);
20635
+ logger.info(`Reload of ${displayStackName(newBoot.target)}: verdict=${verdict.kind} (${verdict.reason}).`);
20462
20636
  } catch (err) {
20463
- logger.warn(`Reload of '${newBoot.target}': classifier context unavailable (${err instanceof Error ? err.message : String(err)}); falling back to rebuild.`);
20637
+ logger.warn(`Reload of ${displayStackName(newBoot.target)}: classifier context unavailable (${err instanceof Error ? err.message : String(err)}); falling back to rebuild.`);
20464
20638
  verdict = {
20465
20639
  kind: "rebuild",
20466
20640
  reason: "classifier context unavailable; falling back to rebuild"
20467
20641
  };
20468
20642
  }
20469
20643
  if (verdict.kind === "rebuild" && verdict.reason === "target image is not a CDK docker-image asset" && !isLocalCdkAssetImage(controller.service) && !imageOverrideTags.has(newBoot.target)) {
20470
- logger.info(`Reload skipped for '${newBoot.target}' (no-op): image pinned to deployed registry; no local rebuild possible.`);
20644
+ logger.info(`Reload skipped for ${displayStackName(newBoot.target)} (no-op): image pinned to deployed registry; no local rebuild possible.`);
20471
20645
  continue;
20472
20646
  }
20473
20647
  await rollOneTarget({
@@ -20491,41 +20665,41 @@ async function reloadAllServices(args) {
20491
20665
  async function loadAssetContextForTarget(args) {
20492
20666
  const { target, controller, stacks, cdkOutDir, assetLoader, logger } = args;
20493
20667
  const parsed = parseEcsTarget(target);
20494
- const candidate = pickCandidateStack$1(parsed.stackPattern, stacks);
20668
+ const candidate = pickCandidateStack$1(target, stacks);
20495
20669
  if (!candidate) {
20496
- logger.debug(`loadAssetContext: stack pattern '${parsed.stackPattern}' from target '${target}' not in the assembly. Classifier will see no asset context (rebuild).`);
20670
+ logger.debug(`loadAssetContext: stack pattern '${parsed.stackPattern}' from target ${displayStackName(target)} not in the assembly. Classifier will see no asset context (rebuild).`);
20497
20671
  return;
20498
20672
  }
20499
20673
  let newService;
20500
20674
  try {
20501
20675
  newService = resolveEcsServiceTarget(target, stacks, void 0, { suppressLoadBalancerWarning: true });
20502
20676
  } catch (err) {
20503
- logger.debug(`loadAssetContext: resolveEcsServiceTarget threw for target '${target}' against the new stacks: ${err instanceof Error ? err.message : String(err)}. Classifier will see no asset context (rebuild).`);
20677
+ logger.debug(`loadAssetContext: resolveEcsServiceTarget threw for target ${displayStackName(target)} against the new stacks: ${err instanceof Error ? err.message : String(err)}. Classifier will see no asset context (rebuild).`);
20504
20678
  return;
20505
20679
  }
20506
20680
  const essential = newService.task.containers.find((c) => c.essential) ?? newService.task.containers[0];
20507
20681
  if (!essential) {
20508
- logger.debug(`loadAssetContext: task definition for target '${target}' has no containers. Classifier will see no asset context (rebuild).`);
20682
+ logger.debug(`loadAssetContext: task definition for target ${displayStackName(target)} has no containers. Classifier will see no asset context (rebuild).`);
20509
20683
  return;
20510
20684
  }
20511
20685
  if (essential.image.kind !== "cdk-asset" || !essential.image.assetHash) {
20512
20686
  const assetHashStr = essential.image.kind === "cdk-asset" ? essential.image.assetHash ?? "undefined" : "n/a";
20513
- logger.debug(`loadAssetContext: target '${target}' essential image is not a CDK asset (kind='${essential.image.kind}', assetHash=${assetHashStr}). Classifier will see no asset context (rebuild).`);
20687
+ logger.debug(`loadAssetContext: target ${displayStackName(target)} essential image is not a CDK asset (kind='${essential.image.kind}', assetHash=${assetHashStr}). Classifier will see no asset context (rebuild).`);
20514
20688
  return;
20515
20689
  }
20516
20690
  const newAssetHash = essential.image.assetHash;
20517
- const manifest = await assetLoader.loadManifest(cdkOutDir, candidate.stackName);
20691
+ const manifest = await assetLoader.loadStackManifest(candidate, cdkOutDir);
20518
20692
  if (!manifest) {
20519
- logger.debug(`loadAssetContext: asset manifest missing for stack '${candidate.stackName}' under '${cdkOutDir}'. Classifier will see no asset context (rebuild).`);
20693
+ logger.debug(`loadAssetContext: asset manifest missing for stack ${displayStackName(candidate.stackName)} under ${displayUntrustedValue(cdkOutDir)}. Classifier will see no asset context (rebuild).`);
20520
20694
  return;
20521
20695
  }
20522
20696
  const newDockerImage = manifest.dockerImages?.[newAssetHash];
20523
20697
  if (!newDockerImage) {
20524
- logger.debug(`loadAssetContext: asset hash '${newAssetHash}' not present in stack '${candidate.stackName}' dockerImages. Classifier will see no asset context (rebuild).`);
20698
+ logger.debug(`loadAssetContext: asset hash ${displayIdent(newAssetHash)} not present in stack ${displayStackName(candidate.stackName)} dockerImages. Classifier will see no asset context (rebuild).`);
20525
20699
  return;
20526
20700
  }
20527
20701
  if (!newDockerImage.source.directory) {
20528
- logger.debug(`loadAssetContext: docker asset '${newAssetHash}' for stack '${candidate.stackName}' is executable-mode (no source.directory). Classifier will see no asset context (rebuild).`);
20702
+ logger.debug(`loadAssetContext: docker asset ${displayIdent(newAssetHash)} for stack ${displayStackName(candidate.stackName)} is executable-mode (no source.directory). Classifier will see no asset context (rebuild).`);
20529
20703
  return;
20530
20704
  }
20531
20705
  const newAssetSourceDir = resolveAssetSourcePath({
@@ -20555,7 +20729,7 @@ async function loadAssetContextForTarget(args) {
20555
20729
  }
20556
20730
  async function rollOneTarget(args) {
20557
20731
  const { controller, newBoot, stacks, options, discovery, skipPull, extraStateProviders, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning, verdict, imageOverrideTag, logger } = args;
20558
- const candidate = pickCandidateStack$1(parseEcsTarget(newBoot.target).stackPattern, stacks);
20732
+ const candidate = pickCandidateStack$1(newBoot.target, stacks);
20559
20733
  const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
20560
20734
  try {
20561
20735
  let resolved;
@@ -20566,22 +20740,22 @@ async function rollOneTarget(args) {
20566
20740
  });
20567
20741
  } catch (err) {
20568
20742
  const reason = err instanceof Error ? err.message : String(err);
20569
- logger.error(`Reload of '${newBoot.target}' was rejected: ${reason}. Existing replica(s) keep serving.`);
20743
+ logger.error(`Reload of ${displayStackName(newBoot.target)} was rejected: ${reason}. Existing replica(s) keep serving.`);
20570
20744
  return;
20571
20745
  }
20572
20746
  const { service: newService, runnerOpts: newRunnerOpts } = resolved;
20573
20747
  const oldReplicas = controller.runState.replicas.filter((r) => !r.shuttingDown);
20574
20748
  if (oldReplicas.length === 0) {
20575
- logger.warn(`Reload of '${newBoot.target}': no live replicas to roll (all shutting down). \`^C\` and re-run start-service to recover.`);
20749
+ logger.warn(`Reload of ${displayStackName(newBoot.target)}: no live replicas to roll (all shutting down). \`^C\` and re-run start-service to recover.`);
20576
20750
  return;
20577
20751
  }
20578
- 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.`);
20579
- if (verdict.kind === "soft-reload") logger.info(`Reload of '${newBoot.target}': soft-reloading ${oldReplicas.length} replica(s) one at a time (docker cp source → restart → TCP-ready probe; no rebuild).`);
20580
- else logger.info(`Reload of '${newBoot.target}': rolling ${oldReplicas.length} replica(s) one at a time (start new shadow → swap registrations → stop old).`);
20752
+ if (newService.desiredCount !== oldReplicas.length) logger.warn(`Reload of ${displayStackName(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.`);
20753
+ if (verdict.kind === "soft-reload") logger.info(`Reload of ${displayStackName(newBoot.target)}: soft-reloading ${oldReplicas.length} replica(s) one at a time (docker cp source → restart → TCP-ready probe; no rebuild).`);
20754
+ else logger.info(`Reload of ${displayStackName(newBoot.target)}: rolling ${oldReplicas.length} replica(s) one at a time (start new shadow → swap registrations → stop old).`);
20581
20755
  for (const oldInstance of oldReplicas) {
20582
20756
  const idx = controller.runState.replicas.indexOf(oldInstance);
20583
20757
  if (idx === -1) {
20584
- logger.warn(`Reload of '${newBoot.target}': replica r${oldInstance.index} (gen ${oldInstance.generation}) vanished before its roll; skipping.`);
20758
+ logger.warn(`Reload of ${displayStackName(newBoot.target)}: replica r${oldInstance.index} (gen ${oldInstance.generation}) vanished before its roll; skipping.`);
20585
20759
  continue;
20586
20760
  }
20587
20761
  try {
@@ -20599,7 +20773,7 @@ async function rollOneTarget(args) {
20599
20773
  });
20600
20774
  } catch (err) {
20601
20775
  const reason = err instanceof Error ? err.message : String(err);
20602
- logger.error(`Reload of '${newBoot.target}' replica r${oldInstance.index}: ${reason}. The old replica keeps serving; remaining replicas will still be rolled.`);
20776
+ logger.error(`Reload of ${displayStackName(newBoot.target)} replica r${oldInstance.index}: ${reason}. The old replica keeps serving; remaining replicas will still be rolled.`);
20603
20777
  }
20604
20778
  }
20605
20779
  } finally {
@@ -20607,7 +20781,7 @@ async function rollOneTarget(args) {
20607
20781
  }
20608
20782
  }
20609
20783
  async function bootOneTarget(boot, runState, stacks, options, discovery, skipPull, extraStateProviders, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning, imageOverrideTag) {
20610
- const candidate = pickCandidateStack$1(parseEcsTarget(boot.target).stackPattern, stacks);
20784
+ const candidate = pickCandidateStack$1(boot.target, stacks);
20611
20785
  const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
20612
20786
  try {
20613
20787
  return await runOneTarget(boot, runState, stacks, options, discovery, skipPull, stateProvider, profileCredsFile, frontDoorPools, suppressLoadBalancerWarning, imageOverrideTag);
@@ -20625,8 +20799,8 @@ async function resolveServiceAndRunnerOpts(boot, stacks, options, discovery, ski
20625
20799
  const quiet = opts.quiet === true;
20626
20800
  const imageContext = await buildEcsImageResolutionContext$1(target, stacks, options, stateProvider);
20627
20801
  const service = resolveEcsServiceTarget(target, stacks, imageContext, { suppressLoadBalancerWarning });
20628
- if (!quiet) logger.info(`Target: ${service.stack.stackName}/${service.serviceLogicalId} (service=${service.serviceName}, desiredCount=${service.desiredCount}, task=${service.task.taskDefinitionLogicalId})`);
20629
- if (!quiet && service.serviceConnect) logger.info(`Service Connect: namespace='${service.serviceConnect.namespaceName}', ${service.serviceConnect.services.length} service(s) registered for peer discovery.`);
20802
+ if (!quiet) logger.info(`Target: ${displayStackName(`${service.stack.stackName}/${service.serviceLogicalId}`)} (service=${displayIdent(service.serviceName)}, desiredCount=${service.desiredCount}, task=${displayIdent(service.task.taskDefinitionLogicalId)})`);
20803
+ if (!quiet && service.serviceConnect) logger.info(`Service Connect: namespace=${displayIdent(service.serviceConnect.namespaceName)}, ${service.serviceConnect.services.length} service(s) registered for peer discovery.`);
20630
20804
  if (!quiet && service.serviceRegistries.length > 0) logger.info(`Cloud Map: ${service.serviceRegistries.length} ServiceRegistry binding(s).`);
20631
20805
  const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === service.stack.stackName) ?? service.stack);
20632
20806
  if (stateProvider && taskNeeds.needsCrossStackResolver) {
@@ -20658,7 +20832,7 @@ async function resolveServiceAndRunnerOpts(boot, stacks, options, discovery, ski
20658
20832
  let assumedCredentials;
20659
20833
  let resolvedRoleArn;
20660
20834
  if (effectiveAssumeRole === true) {
20661
- if (!service.task.taskRoleArn) throw new LocalStartServiceError(`--assume-role passed without an ARN but service '${service.serviceLogicalId}' has no resolvable TaskRoleArn. Pass the ARN explicitly: --assume-role <arn>`);
20835
+ if (!service.task.taskRoleArn) throw new LocalStartServiceError(`--assume-role passed without an ARN but service ${displayIdent(service.serviceLogicalId)} has no resolvable TaskRoleArn. Pass the ARN explicitly: --assume-role <arn>`);
20662
20836
  resolvedRoleArn = await resolveTaskRoleArnForOptions$1(service.task.taskRoleArn, options);
20663
20837
  assumedCredentials = await assumeTaskRole$1(resolvedRoleArn, options.region, options.profile);
20664
20838
  } else if (typeof effectiveAssumeRole === "string") {
@@ -20884,14 +21058,14 @@ async function buildFrontDoor(plan, options, logger, extraStateProviders) {
20884
21058
  }
20885
21059
  for (const runner of lambdaRegistry.values()) {
20886
21060
  bootingLambda = flattenToOneLine(runner.logicalId);
20887
- logger.info(`Booting Lambda target '${bootingLambda}' behind the ALB front-door...`);
21061
+ logger.info(`Booting Lambda target ${displayIdent(bootingLambda)} behind the ALB front-door...`);
20888
21062
  await runner.start();
20889
21063
  }
20890
21064
  bootingLambda = void 0;
20891
21065
  } catch (err) {
20892
21066
  await Promise.allSettled(servers.map((s) => s.close()));
20893
21067
  await Promise.allSettled([...lambdaRegistry.values()].map((r) => r.stop()));
20894
- if (bootingLambda !== void 0) throw new LocalStartServiceError(`Failed to boot ALB Lambda target '${bootingLambda}': ${err instanceof Error ? err.message : String(err)}`);
21068
+ if (bootingLambda !== void 0) throw new LocalStartServiceError(`Failed to boot ALB Lambda target ${displayIdent(bootingLambda)}: ${err instanceof Error ? err.message : String(err)}`);
20895
21069
  throw new LocalStartServiceError(`Failed to start ALB front-door: ${err instanceof Error ? err.message : String(err)}. If a listener port is privileged (< 1024), remap it to a non-privileged host port with --lb-port <listenerPort>=<hostPort> (e.g. --lb-port 80=8080).`);
20896
21070
  }
20897
21071
  const frontDoorByService = /* @__PURE__ */ new Map();
@@ -20930,11 +21104,14 @@ function describeAction(action) {
20930
21104
  return `weighted forward [${action.targets.map((t) => `${describeTargetShort(t)}@${t.weight}`).join(", ")}]`;
20931
21105
  }
20932
21106
  function describeTarget(t) {
20933
- if (t.kind === "lambda") return `Lambda ${t.lambda.logicalId} (invoke)`;
20934
- return `${t.serviceTarget} (container ${t.targetContainerName}:${t.targetContainerPort}) (round-robin)`;
21107
+ if (t.kind === "lambda") return `Lambda ${displayIdent(t.lambda.logicalId)} (invoke)`;
21108
+ return `${displayStackName(t.serviceTarget)} (container ${displayIdent(t.targetContainerName)}:${t.targetContainerPort}) (round-robin)`;
20935
21109
  }
20936
21110
  function describeTargetShort(t) {
20937
- return t.kind === "lambda" ? `Lambda ${t.lambda.logicalId}` : t.serviceTarget;
21111
+ return t.kind === "lambda" ? `Lambda ${displayIdent(t.lambda.logicalId, { listMember: true })}` : displayIdent(t.serviceTarget, {
21112
+ listMember: true,
21113
+ maxCodePoints: STACK_REF_MAX_CODE_POINTS
21114
+ });
20938
21115
  }
20939
21116
  function buildRuleSummary(rule) {
20940
21117
  const conditions = [];
@@ -20975,7 +21152,7 @@ function describeRuleActionForSummary(action) {
20975
21152
  return `forward weighted [${action.targets.map((t) => `${describeForwardTargetForSummary(t)}@${t.weight}`).join(", ")}]`;
20976
21153
  }
20977
21154
  function describeForwardTargetForSummary(t) {
20978
- return t.kind === "lambda" ? `<Lambda: ${t.lambda.logicalId}>` : `<ECS: ${t.serviceTarget}>`;
21155
+ return t.kind === "lambda" ? `<Lambda: ${displayIdent(t.lambda.logicalId)}>` : `<ECS: ${displayStackName(t.serviceTarget)}>`;
20979
21156
  }
20980
21157
  async function resolvePlaceholderAccount$1(arn, region, profile, readFromCfnStack) {
20981
21158
  if (!arn.includes("${AWS::AccountId}")) return arn;
@@ -21010,7 +21187,7 @@ async function assumeTaskRole$1(roleArn, region, profile) {
21010
21187
  }
21011
21188
  async function buildEcsImageResolutionContext$1(target, stacks, options, stateProvider, opts = {}) {
21012
21189
  const logger = getLogger();
21013
- const candidate = pickCandidateStack$1(parseEcsTarget(target).stackPattern, stacks);
21190
+ const candidate = pickCandidateStack$1(target, stacks);
21014
21191
  if (!candidate) return void 0;
21015
21192
  const needs = detectEcsImageResolutionNeeds(candidate);
21016
21193
  if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
@@ -21055,13 +21232,8 @@ async function buildEcsImageResolutionContext$1(target, stacks, options, statePr
21055
21232
  }
21056
21233
  return ctx;
21057
21234
  }
21058
- function pickCandidateStack$1(stackPattern, stacks) {
21059
- if (stackPattern === null) {
21060
- if (stacks.length === 1) return stacks[0];
21061
- return;
21062
- }
21063
- const matched = matchStacks(stacks, [stackPattern]);
21064
- if (matched.length === 1) return matched[0];
21235
+ function pickCandidateStack$1(target, stacks) {
21236
+ return candidateTargetStack(parseEcsTarget(target), target, stacks);
21065
21237
  }
21066
21238
  async function resolveCallerAccountId$1(region, profile, readFromCfnStack) {
21067
21239
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
@@ -21117,18 +21289,18 @@ function logEndpointsBanner(perTarget, frontDoorServers, logger) {
21117
21289
  }
21118
21290
  function parsePositiveInt(raw, flagName) {
21119
21291
  const parsed = Number(raw);
21120
- if (!Number.isInteger(parsed) || parsed < 1) throw new LocalStartServiceError(`${flagName} must be a positive integer (got '${raw}').`);
21292
+ if (!Number.isInteger(parsed) || parsed < 1) throw new InvalidArgumentError(`${flagName} must be a positive integer (got '${raw}').`);
21121
21293
  return parsed;
21122
21294
  }
21123
21295
  const MAX_TASKS_SUBNET_RANGE_CAP = 83;
21124
21296
  function parseMaxTasks(raw) {
21125
21297
  const parsed = parsePositiveInt(raw, "--max-tasks");
21126
- if (parsed > 83) throw new LocalStartServiceError(`--max-tasks ${parsed} exceeds the per-replica link-local /24 subnet allocator's range (${83}). Lower --max-tasks to <= ${83}.`);
21298
+ if (parsed > 83) throw new InvalidArgumentError(`--max-tasks ${parsed} exceeds the per-replica link-local /24 subnet allocator's range (${83}). Lower --max-tasks to <= ${83}.`);
21127
21299
  return parsed;
21128
21300
  }
21129
21301
  function parseRestartPolicy(raw) {
21130
21302
  if (raw === "on-failure" || raw === "always" || raw === "none") return raw;
21131
- throw new LocalStartServiceError(`--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`);
21303
+ throw new InvalidArgumentError(`--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`);
21132
21304
  }
21133
21305
  function parseShadowReadyTimeout(raw) {
21134
21306
  return parsePositiveInt(raw, "--shadow-ready-timeout");
@@ -21151,7 +21323,7 @@ async function resolveAndBuildImageOverrides(args) {
21151
21323
  const { perTarget, stacks, options, extraStateProviders, logger } = args;
21152
21324
  const resolvedForPeek = [];
21153
21325
  for (const target of perTarget) {
21154
- const candidate = pickCandidateStack$1(parseEcsTarget(target).stackPattern, stacks);
21326
+ const candidate = pickCandidateStack$1(target, stacks);
21155
21327
  const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
21156
21328
  let imageContext;
21157
21329
  let service;
@@ -21159,7 +21331,7 @@ async function resolveAndBuildImageOverrides(args) {
21159
21331
  imageContext = await buildEcsImageResolutionContext$1(target, stacks, options, stateProvider, { suppressStateWarning: true });
21160
21332
  service = resolveEcsServiceTarget(target, stacks, imageContext, { suppressLoadBalancerWarning: true });
21161
21333
  } catch (err) {
21162
- logger.debug(`--image-override peek failed for '${target}': ${err instanceof Error ? err.message : String(err)}.`);
21334
+ logger.debug(`--image-override peek failed for ${displayStackName(target)}: ${err instanceof Error ? err.message : String(err)}.`);
21163
21335
  continue;
21164
21336
  } finally {
21165
21337
  if (stateProvider) stateProvider.dispose();
@@ -21296,11 +21468,11 @@ async function localRunTaskCommand(target, options, extraStateProviders) {
21296
21468
  noun: "ECS task definitions",
21297
21469
  onMissing: () => new CdkLocalError(`${getEmbedConfig().cliName} run-task requires a <target> (an ECS task definition display path or logical ID). Run \`${getEmbedConfig().cliName} list\` to see them, or run it in a TTY to pick interactively.`, "LOCAL_RUN_TASK_TARGET_REQUIRED")
21298
21470
  });
21299
- const candidate = pickCandidateStack(parseEcsTarget(resolvedTarget).stackPattern, stacks);
21471
+ const candidate = pickCandidateStack(resolvedTarget, stacks);
21300
21472
  stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
21301
21473
  const imageContext = await buildEcsImageResolutionContext(candidate, stateProvider, options);
21302
21474
  const task = resolveEcsTaskTarget(resolvedTarget, stacks, imageContext);
21303
- logger.info(`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`);
21475
+ logger.info(`Target: ${displayStackName(`${task.stack.stackName}/${task.taskDefinitionLogicalId}`)} (family=${displayIdent(task.family)}, containers=${task.containers.length})`);
21304
21476
  const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack);
21305
21477
  if (stateProvider && taskNeeds.needsCrossStackResolver) {
21306
21478
  const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? task.stack.region ?? "us-east-1";
@@ -21395,7 +21567,7 @@ async function localRunTaskCommand(target, options, extraStateProviders) {
21395
21567
  sigintCount = 99;
21396
21568
  return;
21397
21569
  }
21398
- if (result.essentialContainerName) logger.info(`Essential container '${result.essentialContainerName}' exited with code ${result.exitCode}.`);
21570
+ if (result.essentialContainerName) logger.info(`Essential container ${displayIdent(result.essentialContainerName)} exited with code ${result.exitCode}.`);
21399
21571
  if (result.exitCode !== 0) process.exitCode = result.exitCode;
21400
21572
  } finally {
21401
21573
  if (sigintHandler) {
@@ -21488,13 +21660,8 @@ async function buildEcsImageResolutionContext(candidate, stateProvider, options)
21488
21660
  else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
21489
21661
  return ctx;
21490
21662
  }
21491
- function pickCandidateStack(stackPattern, stacks) {
21492
- if (stackPattern === null) {
21493
- if (stacks.length === 1) return stacks[0];
21494
- return;
21495
- }
21496
- const matched = matchStacks(stacks, [stackPattern]);
21497
- if (matched.length === 1) return matched[0];
21663
+ function pickCandidateStack(target, stacks) {
21664
+ return candidateTargetStack(parseEcsTarget(target), target, stacks);
21498
21665
  }
21499
21666
  async function resolveCallerAccountId(region, profile, readFromCfnStack) {
21500
21667
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
@@ -21646,15 +21813,15 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
21646
21813
  if (port === void 0) continue;
21647
21814
  const protocol = typeof props["Protocol"] === "string" ? props["Protocol"] : "HTTP";
21648
21815
  if (protocol !== "HTTP" && protocol !== "HTTPS") {
21649
- warnings.push(`Listener '${listenerLogicalId}' on port ${port} uses protocol ${protocol}; the local ALB front-door supports HTTP and HTTPS listeners only (TLS / NLB-style listeners are not served). Skipping it.`);
21816
+ warnings.push(`Listener ${displayIdent(listenerLogicalId)} on port ${port} uses protocol ${displayIdent(protocol)}; the local ALB front-door supports HTTP and HTTPS listeners only (TLS / NLB-style listeners are not served). Skipping it.`);
21650
21817
  continue;
21651
21818
  }
21652
- if (protocol === "HTTPS" && Array.isArray(props["Certificates"]) && props["Certificates"].length > 0) warnings.push(`Listener '${listenerLogicalId}' on port ${port} declares ACM Certificates which are not retrievable locally. The front-door terminates TLS with --tls-cert/--tls-key or an auto-generated self-signed cert.`);
21653
- const defaultResolved = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener '${listenerLogicalId}' (port ${port}) default action`, warnings);
21819
+ if (protocol === "HTTPS" && Array.isArray(props["Certificates"]) && props["Certificates"].length > 0) warnings.push(`Listener ${displayIdent(listenerLogicalId)} on port ${port} declares ACM Certificates which are not retrievable locally. The front-door terminates TLS with --tls-cert/--tls-key or an auto-generated self-signed cert.`);
21820
+ const defaultResolved = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener ${displayIdent(listenerLogicalId)} (port ${port}) default action`, warnings);
21654
21821
  const rules = [];
21655
21822
  for (const { ruleLogicalId, ruleProps } of rulesByListener.get(listenerLogicalId) ?? []) {
21656
21823
  const priority = parsePriority(ruleProps["Priority"]);
21657
- const ruleLabel = `Listener rule '${ruleLogicalId}' (priority ${priority})`;
21824
+ const ruleLabel = `Listener rule ${displayIdent(ruleLogicalId)} (priority ${priority})`;
21658
21825
  const parsed = parseRuleConditions(ruleProps["Conditions"], ruleLabel, warnings);
21659
21826
  if (!parsed) continue;
21660
21827
  const { pathPatterns, hostPatterns, httpHeaderConditions, httpRequestMethods, queryStringConditions, sourceIpCidrs } = parsed;
@@ -21711,7 +21878,7 @@ function resolveAction(actions, resources, tgToService, stackName, label, warnin
21711
21878
  if (type === "forward") terminal = resolveForwardAction(a, resources, tgToService, stackName, label, warnings);
21712
21879
  else if (type === "redirect") terminal = resolveRedirectAction(a, label, warnings);
21713
21880
  else if (type === "fixed-response") terminal = resolveFixedResponseAction(a);
21714
- else if (typeof type === "string") warnings.push(`${label} uses an unsupported action type '${type}'. The local ALB front-door supports forward / redirect / fixed-response actions only. Skipping it.`);
21881
+ else if (typeof type === "string") warnings.push(`${label} uses an unsupported action type ${displayIdent(type)}. The local ALB front-door supports forward / redirect / fixed-response actions only. Skipping it.`);
21715
21882
  if (terminal) return authGuard ? {
21716
21883
  action: terminal,
21717
21884
  authGuard
@@ -21729,7 +21896,7 @@ function resolveForwardAction(action, resources, tgToService, stackName, label,
21729
21896
  for (const { tgRef, weight } of refs) {
21730
21897
  const tg = resources[tgRef];
21731
21898
  if (!tg || tg.Type !== TARGET_GROUP_TYPE) {
21732
- warnings.push(`${label} forwards to target group '${tgRef}', but no ${TARGET_GROUP_TYPE} with that logical id exists in ${stackName}. Skipping that target group.`);
21899
+ warnings.push(`${label} forwards to target group ${displayIdent(tgRef)}, but no ${TARGET_GROUP_TYPE} with that logical id exists in ${displayStackName(stackName)}. Skipping that target group.`);
21733
21900
  continue;
21734
21901
  }
21735
21902
  const tgProps = tg.Properties ?? {};
@@ -21743,7 +21910,7 @@ function resolveForwardAction(action, resources, tgToService, stackName, label,
21743
21910
  }
21744
21911
  const backing = tgToService.get(tgRef);
21745
21912
  if (!backing) {
21746
- warnings.push(`${label} forwards to target group '${tgRef}', which is not referenced by any ${SERVICE_TYPE}.LoadBalancers[] in ${stackName}; cdk-local has no ECS service to front behind it. Skipping that target group.`);
21913
+ warnings.push(`${label} forwards to target group ${displayIdent(tgRef)}, which is not referenced by any ${SERVICE_TYPE}.LoadBalancers[] in ${displayStackName(stackName)}; cdk-local has no ECS service to front behind it. Skipping that target group.`);
21747
21914
  continue;
21748
21915
  }
21749
21916
  targets.push({
@@ -21764,12 +21931,12 @@ function resolveForwardAction(action, resources, tgToService, stackName, label,
21764
21931
  function resolveLambdaForwardTarget(tgProps, tgRef, resources, stackName, label, warnings) {
21765
21932
  const lambdaLogicalId = resolveLambdaTargetLogicalId(tgProps["Targets"]);
21766
21933
  if (!lambdaLogicalId) {
21767
- warnings.push(`${label} forwards to a Lambda target group '${tgRef}', but its Targets[].Id is not an in-stack { "Fn::GetAtt": [<FnLogicalId>, "Arn"] } reference; the local ALB front-door supports an in-stack Lambda target only (literal / imported ARNs deferred). Skipping that target group.`);
21934
+ warnings.push(`${label} forwards to a Lambda target group ${displayIdent(tgRef)}, but its Targets[].Id is not an in-stack { "Fn::GetAtt": [<FnLogicalId>, "Arn"] } reference; the local ALB front-door supports an in-stack Lambda target only (literal / imported ARNs deferred). Skipping that target group.`);
21768
21935
  return;
21769
21936
  }
21770
21937
  const lambda = resources[lambdaLogicalId];
21771
21938
  if (!lambda || lambda.Type !== LAMBDA_FUNCTION_TYPE$1) {
21772
- warnings.push(`${label} forwards to Lambda target group '${tgRef}', whose target resolves to '${lambdaLogicalId}', but no ${LAMBDA_FUNCTION_TYPE$1} with that logical id exists in ${stackName}. Skipping that target group.`);
21939
+ warnings.push(`${label} forwards to Lambda target group ${displayIdent(tgRef)}, whose target resolves to ${displayIdent(lambdaLogicalId)}, but no ${LAMBDA_FUNCTION_TYPE$1} with that logical id exists in ${displayStackName(stackName)}. Skipping that target group.`);
21773
21940
  return;
21774
21941
  }
21775
21942
  return {
@@ -21907,7 +22074,7 @@ function parseRuleConditions(conditions, ruleLabel, warnings) {
21907
22074
  }
21908
22075
  out.sourceIpCidrs.push(...values);
21909
22076
  } else {
21910
- warnings.push(`${ruleLabel} uses unsupported condition field '${field}'. Skipping the rule.`);
22077
+ warnings.push(`${ruleLabel} uses unsupported condition field ${displayIdent(field)}. Skipping the rule.`);
21911
22078
  return;
21912
22079
  }
21913
22080
  }
@@ -22054,7 +22221,7 @@ function parseAuthenticateAction(action, type, label, warnings) {
22054
22221
  }
22055
22222
  const match = COGNITO_USERPOOL_ARN.exec(userPoolArn);
22056
22223
  if (!match) {
22057
- warnings.push(`${label}: authenticate-cognito UserPoolArn '${userPoolArn}' is not in the expected arn:...:cognito-idp:<region>:<account>:userpool/<pool-id> shape; skipping guard.`);
22224
+ warnings.push(`${label}: authenticate-cognito UserPoolArn ${displayUntrustedValue(userPoolArn)} is not in the expected arn:...:cognito-idp:<region>:<account>:userpool/<pool-id> shape; skipping guard.`);
22058
22225
  return;
22059
22226
  }
22060
22227
  const region = match[1];
@@ -22109,7 +22276,7 @@ function parseLbPortOverrides(values) {
22109
22276
  function resolveAlbTarget(target, stacks) {
22110
22277
  if (stacks.length === 0) throw new LocalStartServiceError("No stacks found in the synthesized assembly.");
22111
22278
  const parsed = parseEcsTarget(target);
22112
- const stack = pickStack$1(parsed.stackPattern, stacks, target);
22279
+ const stack = pickStack$1(parsed, stacks, target);
22113
22280
  const resources = stack.template.Resources ?? {};
22114
22281
  if (parsed.isPath) {
22115
22282
  const index = buildCdkPathIndex(stack.template);
@@ -22131,15 +22298,14 @@ function resolveAlbTarget(target, stacks) {
22131
22298
  albLogicalId: parsed.pathOrId
22132
22299
  };
22133
22300
  }
22134
- function pickStack$1(stackPattern, stacks, target) {
22135
- if (stackPattern === null) {
22136
- if (stacks.length === 1) return stacks[0];
22137
- throw new LocalStartServiceError(`Target '${target}' has no stack prefix, and the assembly contains ${stacks.length} stacks: ${stacks.map((s) => s.stackName).join(", ")}. Pass it as 'Stack/Path' or 'Stack:LogicalId'.`);
22301
+ function pickStack$1(parsed, stacks, target) {
22302
+ const selection = selectTargetStack(parsed, target, stacks);
22303
+ switch (selection.kind) {
22304
+ case "found": return selection.stack;
22305
+ case "no-prefix": throw new LocalStartServiceError(`Target '${target}' has no stack prefix, and the assembly contains ${stacks.length} stacks: ${describeStacks(stacks)}. Pass it as 'Stack/Path' or 'Stack:LogicalId'.`);
22306
+ case "not-found": throw new LocalStartServiceError(`No stack matches '${selection.pattern}'. Available stacks: ${describeStacks(stacks)}.`);
22307
+ case "ambiguous": throw new LocalStartServiceError(`Multiple stacks match '${selection.pattern}': ${describeStacks(selection.matched)}. Refine the pattern, or use the stack's display path (path form).`);
22138
22308
  }
22139
- const matched = matchStacks(stacks, [stackPattern]);
22140
- if (matched.length === 0) throw new LocalStartServiceError(`No stack matches '${stackPattern}'. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
22141
- if (matched.length > 1) throw new LocalStartServiceError(`Multiple stacks match '${stackPattern}': ${matched.map((s) => s.stackName).join(", ")}. Refine the pattern.`);
22142
- return matched[0];
22143
22309
  }
22144
22310
  function notFound$1(target, stack, resources) {
22145
22311
  const albs = Object.entries(resources).filter(([, r]) => r.Type === "AWS::ElasticLoadBalancingV2::LoadBalancer").map(([logicalId]) => logicalId);
@@ -22267,7 +22433,7 @@ function createCloudFrontModule(sources) {
22267
22433
  } };
22268
22434
  }
22269
22435
  function createUnboundCloudFrontModule(functionLogicalId) {
22270
- const fail = () => Promise.reject(/* @__PURE__ */ new Error(`CloudFront Function '${functionLogicalId}' reads from a KeyValueStore (cf.kvs()), but no KeyValueStore binding is available locally. Pass --from-cfn-stack to read the deployed store, or --kvs-file <id>=<file.json> to back it with a local JSON map.`));
22436
+ const fail = () => Promise.reject(/* @__PURE__ */ new Error(`CloudFront Function ${displayIdent(functionLogicalId)} reads from a KeyValueStore (cf.kvs()), but no KeyValueStore binding is available locally. Pass --from-cfn-stack to read the deployed store, or --kvs-file <id>=<file.json> to back it with a local JSON map.`));
22271
22437
  const handle = {
22272
22438
  get: () => fail(),
22273
22439
  exists: () => fail(),
@@ -22282,17 +22448,17 @@ function pickSource(sources, kvsId) {
22282
22448
  const byId = sources.find((s) => s.kvsId === kvsId);
22283
22449
  if (byId) return byId;
22284
22450
  if (sources.length === 1) return sources[0];
22285
- throw new Error(`cf.kvs('${kvsId}'): no associated KeyValueStore matches this id locally (associated: ${sources.map((s) => s.kvsId ?? s.label).join(", ")}).`);
22451
+ throw new Error(`cf.kvs(${displayIdent(kvsId)}): no associated KeyValueStore matches this id locally (associated: ${sources.map((s) => s.kvsId ?? s.label).join(", ")}).`);
22286
22452
  }
22287
22453
  function makeHandle(source) {
22288
22454
  return {
22289
22455
  async get(key, options) {
22290
22456
  const value = await source.getValue(key);
22291
- if (value === void 0) throw new Error(`cf.kvs().get('${key}'): key not found in ${source.label}.`);
22457
+ if (value === void 0) throw new Error(`cf.kvs().get(${displayIdent(key)}): key not found in ${source.label}.`);
22292
22458
  if (options?.format === "json") try {
22293
22459
  return JSON.parse(value);
22294
22460
  } catch (err) {
22295
- throw new Error(`cf.kvs().get('${key}', { format: 'json' }): value is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
22461
+ throw new Error(`cf.kvs().get(${displayIdent(key)}, { format: 'json' }): value is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
22296
22462
  }
22297
22463
  return value;
22298
22464
  },
@@ -22365,7 +22531,7 @@ function compileCloudFrontFunction(logicalId, code, runtime) {
22365
22531
  try {
22366
22532
  script = new vm.Script(`${source}\n;handler(${EVENT_GLOBAL})`, { filename: `cloudfront-function-${logicalId}.js` });
22367
22533
  } catch (err) {
22368
- throw new Error(`CloudFront Function '${logicalId}' failed to compile: ${err instanceof Error ? err.message : String(err)}`);
22534
+ throw new Error(`CloudFront Function ${displayIdent(logicalId)} failed to compile: ${err instanceof Error ? err.message : String(err)}`);
22369
22535
  }
22370
22536
  let hasHandler;
22371
22537
  try {
@@ -22377,9 +22543,9 @@ function compileCloudFrontFunction(logicalId, code, runtime) {
22377
22543
  const probeContext = vm.createContext(probeGlobals);
22378
22544
  hasHandler = new vm.Script(`${source}\n;typeof handler === 'function'`).runInContext(probeContext, { timeout: FUNCTION_TIMEOUT_MS });
22379
22545
  } catch (err) {
22380
- throw new Error(`CloudFront Function '${logicalId}' failed to compile: ${err instanceof Error ? err.message : String(err)}`);
22546
+ throw new Error(`CloudFront Function ${displayIdent(logicalId)} failed to compile: ${err instanceof Error ? err.message : String(err)}`);
22381
22547
  }
22382
- if (hasHandler !== true) throw new Error(`CloudFront Function '${logicalId}' does not declare a 'handler' function. A CloudFront Function must export \`function handler(event) { ... }\`.`);
22548
+ if (hasHandler !== true) throw new Error(`CloudFront Function ${displayIdent(logicalId)} does not declare a 'handler' function. A CloudFront Function must export \`function handler(event) { ... }\`.`);
22383
22549
  return {
22384
22550
  logicalId,
22385
22551
  runtime,
@@ -22399,12 +22565,12 @@ async function invokeCloudFrontFunction(fn, event) {
22399
22565
  try {
22400
22566
  result = fn.script.runInContext(context, { timeout: FUNCTION_TIMEOUT_MS });
22401
22567
  } catch (err) {
22402
- throw new Error(`CloudFront Function '${fn.logicalId}' threw at request time: ${err instanceof Error ? err.message : String(err)}`);
22568
+ throw new Error(`CloudFront Function ${displayIdent(fn.logicalId)} threw at request time: ${err instanceof Error ? err.message : String(err)}`);
22403
22569
  }
22404
22570
  try {
22405
22571
  return result instanceof Promise ? await result : result;
22406
22572
  } catch (err) {
22407
- throw new Error(`CloudFront Function '${fn.logicalId}' threw at request time: ${err instanceof Error ? err.message : String(err)}`);
22573
+ throw new Error(`CloudFront Function ${displayIdent(fn.logicalId)} threw at request time: ${err instanceof Error ? err.message : String(err)}`);
22408
22574
  }
22409
22575
  }
22410
22576
  async function runViewerRequest(fn, event) {
@@ -22572,9 +22738,9 @@ function resolveCloudFrontDistribution(args) {
22572
22738
  const { stack, logicalId } = args;
22573
22739
  const template = stack.template;
22574
22740
  const resource = (template.Resources ?? {})[logicalId];
22575
- if (!resource || resource.Type !== "AWS::CloudFront::Distribution") throw new Error(`Resource '${logicalId}' in stack ${stack.stackName} is not an ${CLOUDFRONT_DISTRIBUTION_TYPE}.`);
22741
+ if (!resource || resource.Type !== "AWS::CloudFront::Distribution") throw new Error(`Resource ${displayIdent(logicalId)} in stack ${displayStackName(stack.stackName)} is not an ${CLOUDFRONT_DISTRIBUTION_TYPE}.`);
22576
22742
  const distConfig = (resource.Properties ?? {})["DistributionConfig"];
22577
- if (!distConfig || typeof distConfig !== "object") throw new Error(`Distribution '${logicalId}' has no DistributionConfig.`);
22743
+ if (!distConfig || typeof distConfig !== "object") throw new Error(`Distribution ${displayIdent(logicalId)} has no DistributionConfig.`);
22578
22744
  const dc = distConfig;
22579
22745
  const behaviors = resolveBehaviors(dc, compileDistributionFunctions(template), logicalId, template);
22580
22746
  const origins = resolveOrigins(dc, template, stack, args.originOverrides ?? /* @__PURE__ */ new Map());
@@ -22596,7 +22762,7 @@ function compileDistributionFunctions(template) {
22596
22762
  const props = resource.Properties ?? {};
22597
22763
  const code = props["FunctionCode"];
22598
22764
  if (typeof code !== "string") {
22599
- getLogger().warn(`CloudFront Function '${logicalId}' has a non-inline FunctionCode; cdk-local can only run inline function code. Skipping.`);
22765
+ getLogger().warn(`CloudFront Function ${displayIdent(logicalId)} has a non-inline FunctionCode; cdk-local can only run inline function code. Skipping.`);
22600
22766
  continue;
22601
22767
  }
22602
22768
  const config = props["FunctionConfig"];
@@ -22645,7 +22811,7 @@ function resolveBehaviors(dc, functions, distLogicalId, template) {
22645
22811
  if (!b || typeof b !== "object") continue;
22646
22812
  const behavior = b;
22647
22813
  if (typeof behavior["PathPattern"] !== "string") {
22648
- getLogger().warn(`Distribution '${distLogicalId}': a cache behavior has no literal PathPattern; cdk-local cannot route it and is skipping it.`);
22814
+ getLogger().warn(`Distribution ${displayIdent(distLogicalId)}: a cache behavior has no literal PathPattern; cdk-local cannot route it and is skipping it.`);
22649
22815
  continue;
22650
22816
  }
22651
22817
  behaviors.push(resolveBehavior(behavior, behavior["PathPattern"], functions, distLogicalId, template));
@@ -22667,7 +22833,7 @@ function resolveBehavior(behavior, pathPattern, functions, distLogicalId, templa
22667
22833
  const eventType = assoc["EventType"];
22668
22834
  const fnLogicalId = pickFunctionLogicalIdFromArn(assoc["FunctionARN"] ?? assoc["FunctionArn"]);
22669
22835
  if (!fnLogicalId) {
22670
- getLogger().warn(`Distribution '${distLogicalId}': a FunctionAssociation references a function ARN cdk-local could not resolve to a local AWS::CloudFront::Function; it will not run.`);
22836
+ getLogger().warn(`Distribution ${displayIdent(distLogicalId)}: a FunctionAssociation references a function ARN cdk-local could not resolve to a local AWS::CloudFront::Function; it will not run.`);
22671
22837
  continue;
22672
22838
  }
22673
22839
  const fn = functions.get(fnLogicalId);
@@ -22691,7 +22857,7 @@ function resolveLambdaEdgeAssociations(behavior, template, distLogicalId) {
22691
22857
  const eventType = assoc["EventType"];
22692
22858
  const functionLogicalId = pickLambdaEdgeFunctionLogicalId(assoc["LambdaFunctionARN"], template);
22693
22859
  if (!functionLogicalId) {
22694
- getLogger().warn(`Distribution '${distLogicalId}': a Lambda@Edge association references a LambdaFunctionARN cdk-local could not resolve to a local AWS::Lambda::Function; it will not run.`);
22860
+ getLogger().warn(`Distribution ${displayIdent(distLogicalId)}: a Lambda@Edge association references a LambdaFunctionARN cdk-local could not resolve to a local AWS::Lambda::Function; it will not run.`);
22695
22861
  continue;
22696
22862
  }
22697
22863
  const entry = {
@@ -23445,7 +23611,7 @@ async function handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, s3
23445
23611
  if (!assoc) continue;
23446
23612
  const invoke = edgeInvokers.get(assoc.functionLogicalId);
23447
23613
  if (!invoke) {
23448
- logger.warn(`Lambda@Edge ${eventType} function '${assoc.functionLogicalId}' was not booted (it failed to boot at start-up, see the boot warning, or was added after start-up); skipping. Restart start-cloudfront.`);
23614
+ logger.warn(`Lambda@Edge ${eventType} function ${displayIdent(assoc.functionLogicalId)} was not booted (it failed to boot at start-up, see the boot warning, or was added after start-up); skipping. Restart start-cloudfront.`);
23449
23615
  continue;
23450
23616
  }
23451
23617
  if (assoc.includeBody && edgeInput.body === void 0) edgeInput = {
@@ -23541,7 +23707,7 @@ async function handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, s3
23541
23707
  async function runEdgeResponseStage(assoc, eventType, ctx, edgeInvokers, logger) {
23542
23708
  const invoke = edgeInvokers.get(assoc.functionLogicalId);
23543
23709
  if (!invoke) {
23544
- logger.warn(`Lambda@Edge ${eventType} function '${assoc.functionLogicalId}' was not booted (it failed to boot at start-up, see the boot warning, or was added after start-up); skipping. Restart start-cloudfront.`);
23710
+ logger.warn(`Lambda@Edge ${eventType} function ${displayIdent(assoc.functionLogicalId)} was not booted (it failed to boot at start-up, see the boot warning, or was added after start-up); skipping. Restart start-cloudfront.`);
23545
23711
  return;
23546
23712
  }
23547
23713
  return applyEdgeResponseResult(await invoke(buildEdgeResponseEvent({
@@ -23601,7 +23767,7 @@ function setHeadersSafely(res, headers, logger) {
23601
23767
  for (const [name, value] of Object.entries(headers)) try {
23602
23768
  res.setHeader(name, value.replace(/[\r\n]/g, ""));
23603
23769
  } catch (err) {
23604
- logger.warn(`Skipping invalid response header '${name}': ${err instanceof Error ? err.message : String(err)}`);
23770
+ logger.warn(`Skipping invalid response header ${displayIdent(name)}: ${flattenToOneLine(err instanceof Error ? err.message : String(err))}`);
23605
23771
  }
23606
23772
  }
23607
23773
  async function serveFromOrigin(origin, args) {
@@ -23660,13 +23826,13 @@ async function serveFromOrigin(origin, args) {
23660
23826
  };
23661
23827
  }
23662
23828
  function originUnavailableMessage(origin, behavior) {
23663
- if (!origin) return `Behavior ${behavior.pathPattern ?? "(default)"} targets unknown origin '${behavior.targetOriginId}'.\n`;
23664
- if (origin.kind === "lambda-url") return `Origin '${origin.originId}' is a Lambda Function URL origin whose backing function '${origin.functionLogicalId}' was not booted (it was added after start-up). Restart start-cloudfront.\n`;
23665
- if (origin.kind === "custom") return `Origin '${origin.originId}' is a custom (non-S3) origin (${origin.domainName}). cdkl start-cloudfront serves S3 origins and Lambda Function URL origins only.
23829
+ if (!origin) return `Behavior ${behavior.pathPattern === void 0 ? "(default)" : displayUntrustedValue(behavior.pathPattern)} targets unknown origin ${displayIdent(behavior.targetOriginId)}.\n`;
23830
+ if (origin.kind === "lambda-url") return `Origin ${displayIdent(origin.originId)} is a Lambda Function URL origin whose backing function ${displayIdent(origin.functionLogicalId)} was not booted (it was added after start-up). Restart start-cloudfront.\n`;
23831
+ if (origin.kind === "custom") return `Origin ${displayIdent(origin.originId)} is a custom (non-S3) origin (${displayUntrustedValue(String(origin.domainName))}). cdkl start-cloudfront serves S3 origins and Lambda Function URL origins only.
23666
23832
  `;
23667
- if (origin.kind === "s3-deployed") return `Origin '${origin.originId}' is a deployed-S3 origin (bucket '${origin.bucketName}') whose reader was not booted (it was promoted after start-up). Restart start-cloudfront.
23833
+ if (origin.kind === "s3-deployed") return `Origin ${displayIdent(origin.originId)} is a deployed-S3 origin (bucket ${displayIdent(origin.bucketName)}) whose reader was not booted (it was promoted after start-up). Restart start-cloudfront.
23668
23834
  `;
23669
- return `Origin '${origin.originId}' is an S3 origin with no resolvable local source. Point it at a directory with --origin ${origin.originId}=<dir>.\n`;
23835
+ return `Origin ${displayIdent(origin.originId)} is an S3 origin with no resolvable local source. Point it at a directory with --origin ${origin.originId}=<dir>.\n`;
23670
23836
  }
23671
23837
  function matchBehavior(behaviors, uri) {
23672
23838
  let fallback;
@@ -23875,7 +24041,7 @@ function createDeployedKvsDataSource(options) {
23875
24041
  }))).Value;
23876
24042
  } catch (err) {
23877
24043
  if (isKeyNotFound(err)) return void 0;
23878
- throw new Error(`cf.kvs().get('${key}') against ${flattenToOneLine(options.kvsArn)} failed: ${describeAwsFailureForWarn(err, "CloudFront KeyValueStore GetKey")}`);
24044
+ throw new Error(`cf.kvs().get(${displayIdent(key)}) against ${flattenToOneLine(options.kvsArn)} failed: ${describeAwsFailureForWarn(err, "CloudFront KeyValueStore GetKey")}`);
23879
24045
  }
23880
24046
  }
23881
24047
  };
@@ -23892,7 +24058,7 @@ async function resolveDeployedKvsArnByName(name, options = {}) {
23892
24058
  ...item.Id !== void 0 && { id: item.Id }
23893
24059
  };
23894
24060
  } catch (err) {
23895
- getLogger().debug(`ListKeyValueStores lookup for '${name}' failed: ${flattenToOneLine(stringifyThrown(err))}`);
24061
+ getLogger().debug(`ListKeyValueStores lookup for ${displayIdent(name)} failed: ${flattenToOneLine(stringifyThrown(err))}`);
23896
24062
  }
23897
24063
  }
23898
24064
  function isKeyNotFound(err) {
@@ -23915,7 +24081,7 @@ async function resolveKvsModulesForDistribution(distribution, options) {
23915
24081
  if (source) sources.push(source);
23916
24082
  else {
23917
24083
  const ref = assoc.kvsLogicalId ?? "<store>";
23918
- warnings.push(`CloudFront Function '${fn.logicalId}' reads KeyValueStore '${ref}', but no binding resolved it — cf.kvs() reads will fail. Pass --from-cfn-stack to read the deployed store, or --kvs-file ${ref}=<file.json> for a local map.`);
24084
+ warnings.push(`CloudFront Function ${displayIdent(fn.logicalId)} reads KeyValueStore ${displayIdent(ref)}, but no binding resolved it — cf.kvs() reads will fail. Pass --from-cfn-stack to read the deployed store, or --kvs-file ${ref}=<file.json> for a local map.`);
23919
24085
  }
23920
24086
  }
23921
24087
  if (sources.length > 0) fn.cloudfrontModule = createCloudFrontModule(sources);
@@ -23999,7 +24165,7 @@ function collectKvsStoreIdentities(template) {
23999
24165
  function normalizeKvsFileKeys(kvsFiles, template) {
24000
24166
  if (kvsFiles.size === 0) return kvsFiles;
24001
24167
  const stores = collectKvsStoreIdentities(template);
24002
- const candidates = () => stores.map((s) => s.constructPath ? `${s.logicalId} (${s.constructPath})` : s.logicalId).join(", ") || "(none in this distribution)";
24168
+ const candidates = () => stores.map((s) => s.constructPath ? `${displayIdent(s.logicalId, { listMember: true })} (${displayStackName(s.constructPath)})` : displayIdent(s.logicalId, { listMember: true })).join(", ") || "(none in this distribution)";
24003
24169
  const out = /* @__PURE__ */ new Map();
24004
24170
  for (const [key, filePath] of kvsFiles) {
24005
24171
  const matched = [
@@ -24008,7 +24174,7 @@ function normalizeKvsFileKeys(kvsFiles, template) {
24008
24174
  stores.filter((s) => s.constructId === key)
24009
24175
  ].find((t) => t.length > 0) ?? [];
24010
24176
  if (matched.length === 0) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' matched no AWS::CloudFront::KeyValueStore in the distribution. Pass a store's logical id, construct path, or bare construct id. Candidates: ${candidates()}.`);
24011
- if (matched.length > 1) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' is ambiguous — it matched ${matched.length} KeyValueStores (${matched.map((s) => s.logicalId).join(", ")}). Use the logical id or full construct path.`);
24177
+ if (matched.length > 1) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' is ambiguous — it matched ${matched.length} KeyValueStores (${matched.map((s) => displayIdent(s.logicalId, { listMember: true })).join(", ")}). Use the logical id or full construct path.`);
24012
24178
  out.set(matched[0].logicalId, filePath);
24013
24179
  }
24014
24180
  return out;
@@ -24047,7 +24213,7 @@ async function attachKvsModules(distribution, stacks, options, profileCredential
24047
24213
  function resolveCloudFrontTarget(target, stacks) {
24048
24214
  if (stacks.length === 0) throw new LocalStartCloudFrontError("No stacks found in the synthesized assembly.");
24049
24215
  const parsed = parseEcsTarget(target);
24050
- const stack = pickStack(parsed.stackPattern, stacks, target);
24216
+ const stack = pickStack(parsed, stacks, target);
24051
24217
  const resources = stack.template.Resources ?? {};
24052
24218
  if (parsed.isPath) {
24053
24219
  const index = buildCdkPathIndex(stack.template);
@@ -24069,15 +24235,14 @@ function resolveCloudFrontTarget(target, stacks) {
24069
24235
  logicalId: parsed.pathOrId
24070
24236
  };
24071
24237
  }
24072
- function pickStack(stackPattern, stacks, target) {
24073
- if (stackPattern === null) {
24074
- if (stacks.length === 1) return stacks[0];
24075
- throw new LocalStartCloudFrontError(`Target '${target}' has no stack prefix, and the assembly contains ${stacks.length} stacks: ${stacks.map((s) => s.stackName).join(", ")}. Pass it as 'Stack/Path' or 'Stack:LogicalId'.`);
24238
+ function pickStack(parsed, stacks, target) {
24239
+ const selection = selectTargetStack(parsed, target, stacks);
24240
+ switch (selection.kind) {
24241
+ case "found": return selection.stack;
24242
+ case "no-prefix": throw new LocalStartCloudFrontError(`Target '${target}' has no stack prefix, and the assembly contains ${stacks.length} stacks: ${describeStacks(stacks)}. Pass it as 'Stack/Path' or 'Stack:LogicalId'.`);
24243
+ case "not-found": throw new LocalStartCloudFrontError(`No stack matches '${selection.pattern}'. Available stacks: ${describeStacks(stacks)}.`);
24244
+ case "ambiguous": throw new LocalStartCloudFrontError(`Multiple stacks match '${selection.pattern}': ${describeStacks(selection.matched)}. Refine the pattern, or use the stack's display path (path form).`);
24076
24245
  }
24077
- const matched = matchStacks(stacks, [stackPattern]);
24078
- if (matched.length === 0) throw new LocalStartCloudFrontError(`No stack matches '${stackPattern}'. Available stacks: ${stacks.map((s) => s.stackName).join(", ")}.`);
24079
- if (matched.length > 1) throw new LocalStartCloudFrontError(`Multiple stacks match '${stackPattern}': ${matched.map((s) => s.stackName).join(", ")}. Refine the pattern.`);
24080
- return matched[0];
24081
24246
  }
24082
24247
  function notFound(target, stack, resources) {
24083
24248
  const dists = Object.entries(resources).filter(([, r]) => r.Type === CLOUDFRONT_DISTRIBUTION_TYPE).map(([logicalId]) => logicalId);
@@ -24106,7 +24271,7 @@ async function bootLambdaUrlOrigins(distribution, stacks, opts) {
24106
24271
  await runner.start();
24107
24272
  } catch (err) {
24108
24273
  await Promise.all(runners.map((r) => r.stop().catch(() => void 0)));
24109
- throw new LocalStartCloudFrontError(`Failed to boot the Lambda Function URL origin's backing function '${functionLogicalId}': ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
24274
+ throw new LocalStartCloudFrontError(`Failed to boot the Lambda Function URL origin's backing function ${displayIdent(functionLogicalId)}: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
24110
24275
  }
24111
24276
  runners.push(runner);
24112
24277
  invokers.set(functionLogicalId, (event) => runner.invoke(event));
@@ -24146,7 +24311,7 @@ async function bootLambdaEdgeFunctions(distribution, stacks, opts) {
24146
24311
  runners.push(runner);
24147
24312
  invokers.set(functionLogicalId, (event) => runner.invoke(event));
24148
24313
  } catch (err) {
24149
- logger.warn(`Could not boot Lambda@Edge function '${functionLogicalId}'; its stage will be skipped: ${err instanceof Error ? err.message : String(err)}`);
24314
+ logger.warn(`Could not boot Lambda@Edge function ${displayIdent(functionLogicalId)}; its stage will be skipped: ${err instanceof Error ? err.message : String(err)}`);
24150
24315
  }
24151
24316
  return {
24152
24317
  invokers,
@@ -24174,8 +24339,8 @@ function buildLambdaBootOptions(options, profileCredentials, extraStateProviders
24174
24339
  }
24175
24340
  function warnUnsupported(distribution, extraStateProviders) {
24176
24341
  const logger = getLogger();
24177
- for (const origin of distribution.origins.values()) if (origin.kind === "custom") logger.warn(`Origin '${origin.originId}' is a custom (non-S3, non-Lambda-Function-URL) origin (${origin.domainName}); ${getEmbedConfig().cliName} start-cloudfront serves S3 origins and Lambda Function URL origins only. Requests routed to it return 502.`);
24178
- else if (origin.kind === "s3-unresolved") logger.warn(`Origin '${origin.originId}' is an S3 origin with no resolvable local source (no BucketDeployment found, or its source could not be located in the cloud assembly). Pass ${stateSourceFlagNames(extraStateProviders).join(" or ")} to serve the deployed bucket from real S3 on demand, or point it at a local directory with --origin ${origin.originId}=<dir>. Requests routed to it return 502.`);
24342
+ for (const origin of distribution.origins.values()) if (origin.kind === "custom") logger.warn(`Origin ${displayIdent(origin.originId)} is a custom (non-S3, non-Lambda-Function-URL) origin (${displayUntrustedValue(String(origin.domainName))}); ${getEmbedConfig().cliName} start-cloudfront serves S3 origins and Lambda Function URL origins only. Requests routed to it return 502.`);
24343
+ else if (origin.kind === "s3-unresolved") logger.warn(`Origin ${displayIdent(origin.originId)} is an S3 origin with no resolvable local source (no BucketDeployment found, or its source could not be located in the cloud assembly). Pass ${stateSourceFlagNames(extraStateProviders).join(" or ")} to serve the deployed bucket from real S3 on demand, or point it at a local directory with --origin ${origin.originId}=<dir>. Requests routed to it return 502.`);
24179
24344
  }
24180
24345
  function warnUnusedCacheOrigin(options, extraStateProviders) {
24181
24346
  if (options.cacheOrigin === true && !isStateSourceActive(options, extraStateProviders)) {
@@ -24231,7 +24396,7 @@ async function resolveDeployedS3Origins(distribution, stacks, options, profileCr
24231
24396
  originId: origin.originId,
24232
24397
  bucketName
24233
24398
  });
24234
- logger.info(`Origin '${origin.originId}': no local BucketDeployment source; serving from deployed S3 (bucket=${bucketName}, resolved via ${via}) on demand under ${stateFlag}.`);
24399
+ logger.info(`Origin ${displayIdent(origin.originId)}: no local BucketDeployment source; serving from deployed S3 (bucket=${bucketName}, resolved via ${via}) on demand under ${stateFlag}.`);
24235
24400
  }
24236
24401
  return {
24237
24402
  readers,
@@ -24320,7 +24485,7 @@ async function localStartCloudFrontCommand(target, options, extraStateProviders,
24320
24485
  await Promise.all([...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch(() => void 0)));
24321
24486
  throw err;
24322
24487
  }
24323
- process.stdout.write(`CloudFront distribution serving on ${server.url} (${initial.distribution.logicalId})\n`);
24488
+ process.stdout.write(`CloudFront distribution serving on ${server.url} (${displayIdent(initial.distribution.logicalId)})\n`);
24324
24489
  process.stdout.write("^C to stop.\n");
24325
24490
  let watcher;
24326
24491
  let reloadChain = Promise.resolve();
@@ -24695,7 +24860,7 @@ function readAuthorizationHeader(headers) {
24695
24860
  //#region src/cli/commands/local-start-agentcore.ts
24696
24861
  function parsePort(raw) {
24697
24862
  const n = Number(raw);
24698
- if (!Number.isInteger(n) || n < 0 || n > 65535) throw new CdkLocalError(`--port must be an integer 0-65535, got '${raw}'.`, "INVALID_PORT");
24863
+ if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
24699
24864
  return n;
24700
24865
  }
24701
24866
  function resolveAgentCoreServePlan(protocol) {
@@ -24812,7 +24977,7 @@ async function localStartAgentCoreCommand(target, options, extraStateProviders,
24812
24977
  loaded: void 0
24813
24978
  };
24814
24979
  const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);
24815
- logger.info(`Target: ${resolved.stack.stackName}/${resolved.logicalId} (${resolved.protocol})`);
24980
+ logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.logicalId}`)} (${resolved.protocol})`);
24816
24981
  const plan = resolveAgentCoreServePlan(resolved.protocol);
24817
24982
  const sigv4Context = await resolveAgentCoreSigV4Context(options, resolved, loadedState, stateProvider);
24818
24983
  const authPlan = selectServeInboundAuth(resolved, options, sigv4Context !== void 0);
@@ -24885,12 +25050,12 @@ async function localStartAgentCoreCommand(target, options, extraStateProviders,
24885
25050
  throw err;
24886
25051
  }
24887
25052
  if (server.wsUrl) {
24888
- logger.info(`Server listening on ${server.wsUrl} (${resolved.logicalId} (AgentCore WebSocket))`);
25053
+ logger.info(`Server listening on ${server.wsUrl} (${displayIdent(resolved.logicalId)} (AgentCore WebSocket))`);
24889
25054
  logger.info(`HTTP contract served on ${server.httpUrl} — POST ${server.httpUrl}/invocations, GET ${server.httpUrl}/ping`);
24890
25055
  } else {
24891
25056
  const contractPath = plan.routes[0]?.path ?? "/";
24892
25057
  const contractUrl = `${server.httpUrl}${contractPath}`;
24893
- logger.info(`Server listening on ${server.httpUrl} (${resolved.logicalId})`);
25058
+ logger.info(`Server listening on ${server.httpUrl} (${displayIdent(resolved.logicalId)})`);
24894
25059
  logger.info(`${resolved.protocol} contract served on ${contractUrl} — POST ${contractUrl}`);
24895
25060
  }
24896
25061
  logger.info("Press ^C to shut down.");
@@ -25066,9 +25231,9 @@ function formatSection(title, command, entries, long) {
25066
25231
  if (entries.length === 0) return [];
25067
25232
  const lines = [`${title} -> ${command}`];
25068
25233
  for (const entry of entries) {
25069
- const primary = entry.displayPath ?? entry.qualifiedId;
25070
- lines.push(entry.kind ? ` ${primary} (${entry.kind})` : ` ${primary}`);
25071
- if (long && entry.displayPath) lines.push(` ${entry.qualifiedId}`);
25234
+ const shown = displayStackName(entry.displayPath ?? entry.qualifiedId);
25235
+ lines.push(entry.kind ? ` ${shown} (${entry.kind})` : ` ${shown}`);
25236
+ if (long && entry.displayPath) lines.push(` ${displayStackName(entry.qualifiedId)}`);
25072
25237
  }
25073
25238
  return lines;
25074
25239
  }
@@ -28846,13 +29011,15 @@ function runChild(spawnFn, nodeBin, argv, cwd, invocationId, target, bus, clock)
28846
29011
  return new Promise((resolve, reject) => {
28847
29012
  let child;
28848
29013
  try {
29014
+ const env = {
29015
+ ...process.env,
29016
+ CDKL_LOG_LEVEL: "warn",
29017
+ CDKL_LOG_STREAM: "stderr"
29018
+ };
29019
+ delete env["CDKL_PREFIX_CONTAINER_OUTPUT"];
28849
29020
  child = spawnFn(nodeBin, argv, {
28850
29021
  cwd,
28851
- env: {
28852
- ...process.env,
28853
- CDKL_LOG_LEVEL: "warn",
28854
- CDKL_LOG_STREAM: "stderr"
28855
- }
29022
+ env
28856
29023
  });
28857
29024
  } catch (err) {
28858
29025
  reject(err instanceof Error ? err : new Error(String(err)));
@@ -29307,16 +29474,16 @@ const SERVE_SPECS = {
29307
29474
  extraEndpointRe: /^HTTP contract served on (https?:\/\/\S+)/
29308
29475
  }
29309
29476
  };
29310
- const PUBLISHED_ENDPOINT_RE = /^Container '[^']*' container port \d+ published on ((?:\d{1,3}(?:\.\d{1,3}){3}|\[[0-9A-Fa-f:.]{2,45}\]):\d+)/;
29477
+ const PUBLISHED_ENDPOINT_RE = /^Container (?:[A-Za-z0-9:_@./+=,~-]+|"(?:[^"\\]|\\.)*")(?: \[cut: [^\]]*\])? container port \d+ published on ((?:\d{1,3}(?:\.\d{1,3}){3}|\[[0-9A-Fa-f:.]{2,45}\]):\d+)/;
29311
29478
  function parsePublishedHostEndpoint(line) {
29312
29479
  const m = PUBLISHED_ENDPOINT_RE.exec(line);
29313
29480
  return m ? `http://${m[1]}` : void 0;
29314
29481
  }
29315
29482
  function describeForeignEndpointRefusal(targetId, endpoint, what) {
29316
- const quoted = `'${describeEndpointForMessage(endpoint)}'`;
29483
+ const quoted = displayUntrustedValue(describeEndpointForMessage(endpoint));
29317
29484
  const outcome = "No endpoint was adopted and no capture proxy was started.";
29318
- if (!parsesAsUrl(endpoint)) return `refused an unparseable ${what} ${quoted} from '${targetId}': it is not a URL, so studio cannot tell where a request to it would go. ${outcome}`;
29319
- return `refused a non-loopback ${what} ${quoted} from '${targetId}': a serve child always listens on this machine, so studio will not send requests there. ${outcome}`;
29485
+ if (!parsesAsUrl(endpoint)) return `refused an unparseable ${what} ${quoted} from ${displayStackName(targetId)}: it is not a URL, so studio cannot tell where a request to it would go. ${outcome}`;
29486
+ return `refused a non-loopback ${what} ${quoted} from ${displayStackName(targetId)}: a serve child always listens on this machine, so studio will not send requests there. ${outcome}`;
29320
29487
  }
29321
29488
  function parsesAsUrl(value) {
29322
29489
  try {
@@ -29425,7 +29592,7 @@ function createStudioServeManager(config) {
29425
29592
  const spec = SERVE_SPECS[req.kind];
29426
29593
  if (!spec) throw new Error(`Serving '${req.kind}' targets from studio is not supported (serve kinds: ${Object.keys(SERVE_SPECS).join(", ")}).`);
29427
29594
  const existing = entries.get(req.targetId);
29428
- if (existing && existing.status !== "stopped" && existing.status !== "error") throw new Error(`'${req.targetId}' is already running.`);
29595
+ if (existing && existing.status !== "stopped" && existing.status !== "error") throw new Error(`${displayStackName(req.targetId)} is already running.`);
29429
29596
  const startedAt = clock();
29430
29597
  let envDir;
29431
29598
  const envVars = resolveEnvVars(req.kind, req.options);
@@ -29440,7 +29607,8 @@ function createStudioServeManager(config) {
29440
29607
  cwd,
29441
29608
  env: {
29442
29609
  ...process.env,
29443
- CDKL_LOG_STREAM: "stdout"
29610
+ CDKL_LOG_STREAM: "stdout",
29611
+ CDKL_PREFIX_CONTAINER_OUTPUT: "1"
29444
29612
  }
29445
29613
  });
29446
29614
  } catch (err) {
@@ -29492,7 +29660,7 @@ function createStudioServeManager(config) {
29492
29660
  stopChild(child, stopGraceMs, setTimeoutFn, clearTimeoutFn);
29493
29661
  closeProxies(entry);
29494
29662
  if (entries.get(req.targetId) === entry) entries.delete(req.targetId);
29495
- reject(/* @__PURE__ */ new Error(`'${req.targetId}' did not start within ${readyTimeoutMs}ms.`));
29663
+ reject(/* @__PURE__ */ new Error(`${displayStackName(req.targetId)} did not start within ${readyTimeoutMs}ms.`));
29496
29664
  }, readyTimeoutMs);
29497
29665
  timer.unref?.();
29498
29666
  const becomeRunning = () => {
@@ -29609,7 +29777,7 @@ function createStudioServeManager(config) {
29609
29777
  settled = true;
29610
29778
  clearTimeoutFn(timer);
29611
29779
  if (entry.stopping) {
29612
- reject(/* @__PURE__ */ new Error(`'${req.targetId}' was stopped before it finished starting.`));
29780
+ reject(/* @__PURE__ */ new Error(`${displayStackName(req.targetId)} was stopped before it finished starting.`));
29613
29781
  return;
29614
29782
  }
29615
29783
  entry.status = "error";
@@ -29631,7 +29799,7 @@ function createStudioServeManager(config) {
29631
29799
  }
29632
29800
  async function stop(req) {
29633
29801
  const entry = entries.get(req.targetId);
29634
- if (!entry) throw new Error(`'${req.targetId}' is not running.`);
29802
+ if (!entry) throw new Error(`${displayStackName(req.targetId)} is not running.`);
29635
29803
  entry.stopping = true;
29636
29804
  entries.delete(req.targetId);
29637
29805
  await Promise.all([closeProxies(entry), stopChild(entry.child, stopGraceMs, setTimeoutFn, clearTimeoutFn)]);
@@ -29944,10 +30112,7 @@ function resolveBootAssemblyDir(appCmd, output) {
29944
30112
  if (isDir(outPath)) return outPath;
29945
30113
  }
29946
30114
  function resolveEcsServiceStack(targetId, stacks) {
29947
- const parsed = parseEcsTarget(targetId);
29948
- if (parsed.stackPattern === null) return stacks.length === 1 ? stacks[0] : void 0;
29949
- const matched = matchStacks(stacks, [parsed.stackPattern]);
29950
- return matched.length === 1 ? matched[0] : void 0;
30115
+ return candidateTargetStack(parseEcsTarget(targetId), targetId, stacks);
29951
30116
  }
29952
30117
  async function prepareEcsImageContexts(args) {
29953
30118
  const { serviceIds, stacks, options, logger } = args;
@@ -29974,7 +30139,7 @@ async function prepareEcsImageContexts(args) {
29974
30139
  const ctx = await buildEcsImageResolutionContext(stack, stateProvider, options);
29975
30140
  contextByStack.set(stack.stackName, ctx);
29976
30141
  } catch (err) {
29977
- logger.warn(`studio: could not build deployed-state image context for stack '${stack.stackName}'; ECS services in it resolve against the synthed template only. ${describeAwsFailureForWarn(err, "deployed-state image context (CloudFormation + SSM)")}`);
30142
+ logger.warn(`studio: could not build deployed-state image context for stack ${displayStackName(stack.stackName)}; ECS services in it resolve against the synthed template only. ${describeAwsFailureForWarn(err, "deployed-state image context (CloudFormation + SSM)")}`);
29978
30143
  contextByStack.set(stack.stackName, void 0);
29979
30144
  } finally {
29980
30145
  stateProvider?.dispose();
@@ -29990,7 +30155,7 @@ function makePinClassifier(args) {
29990
30155
  const context = stack ? contextByStack.get(stack.stackName) : void 0;
29991
30156
  return !isLocalCdkAssetImage(resolveEcsServiceTarget(id, stacks, context));
29992
30157
  } catch (err) {
29993
- logger.warn(`studio: could not classify image-pin status for ECS service '${id}'; leaving it unmarked (the image-override picker will not be offered).${pinClassifyStateHint(stateBound)} ${err instanceof Error ? err.message : String(err)}`);
30158
+ logger.warn(`studio: could not classify image-pin status for ECS service ${displayStackName(id)}; leaving it unmarked (the image-override picker will not be offered).${pinClassifyStateHint(stateBound)} ${err instanceof Error ? err.message : String(err)}`);
29994
30159
  if (stateBound === false) onUnresolved?.(id);
29995
30160
  return false;
29996
30161
  }
@@ -30010,7 +30175,7 @@ function makeTaskPinClassifier(args) {
30010
30175
  const representative = task.containers.find((c) => c.essential) ?? task.containers[0];
30011
30176
  return representative !== void 0 && representative.image.kind !== "cdk-asset";
30012
30177
  } catch (err) {
30013
- logger.warn(`studio: could not classify image-pin status for ECS task definition '${id}'; leaving it unmarked (the image-override picker will not be offered).${pinClassifyStateHint(stateBound)} ${err instanceof Error ? err.message : String(err)}`);
30178
+ logger.warn(`studio: could not classify image-pin status for ECS task definition ${displayStackName(id)}; leaving it unmarked (the image-override picker will not be offered).${pinClassifyStateHint(stateBound)} ${err instanceof Error ? err.message : String(err)}`);
30014
30179
  if (stateBound === false) onUnresolved?.(id);
30015
30180
  return false;
30016
30181
  }
@@ -30041,7 +30206,7 @@ function makeAlbBackingPinnedResolver(args) {
30041
30206
  }
30042
30207
  return out;
30043
30208
  } catch (err) {
30044
- logger.warn(`studio: could not resolve ALB '${albEntry.id}' backing services for the image-override picker; the alb composer will not offer one. ${err instanceof Error ? err.message : String(err)}`);
30209
+ logger.warn(`studio: could not resolve ALB ${displayStackName(albEntry.id)} backing services for the image-override picker; the alb composer will not offer one. ${err instanceof Error ? err.message : String(err)}`);
30045
30210
  return [];
30046
30211
  }
30047
30212
  };
@@ -30336,4 +30501,4 @@ function addStudioSpecificOptions(cmd) {
30336
30501
 
30337
30502
  //#endregion
30338
30503
  export { applyEdgeResponseResult as $, defaultCredentialsLoader as $n, resolveCfnFallbackRegion as $r, isLocalCdkAssetImage as $t, startAgentCoreHttpServer as A, LocalInvokeBuildError as Ai, renderCodeDockerfile as An, resolveHostGatewayExtraHosts as Ar, addRunTaskSpecificOptions as At, idFromArn as B, withoutKeys as Bi, createFileWatcher as Bn, buildContainerImage as Br, resolveEcsAssumeRoleOption as Bt, addListSpecificOptions as C, AgentCoreResolutionError as Ci, invokeAgentCore as Cn, pickResponseTemplate as Cr, parseLbPortOverrides as Ct, createLocalStartAgentCoreCommand as D, formatStateRemedy as Di, SUPPORTED_CODE_RUNTIMES as Dn, HOST_DOCKER_INTERNAL_GATEWAY as Dr, addStartServiceSpecificOptions as Dt, addStartAgentCoreSpecificOptions as E, derivePseudoParametersFromRegion as Ei, downloadAndExtractS3Bundle as En, VtlEvaluationError as Er, resolveAlbFrontDoor as Et, createLocalStartCloudFrontCommand as F, firstUsableRegion as Fi, addStartApiSpecificOptions as Fn, parseConnectionsPath as Fr, addImageOverrideOptions as Ft, classifyS3Error as G, recordAssumedRole as Gi, availableApiIdentifiers as Gn, substituteAgainstState as Gr, canonicalizeImageOverrideKeys as Gt, createDeployedKvsDataSource as H, assumedRoleRecorded as Hi, buildStageMap as Hn, resolveRuntimeFileExtension as Hr, runEcsServiceEmulator as Ht, normalizeKvsFileKeys as I, keysNotFromTemplate as Ii, createLocalStartApiCommand as In, buildConnectEvent as Ir, buildEcsImageResolutionContext$1 as It, startCloudFrontServer as J, workloadStsClientConfig as Ji, groupRoutesByServer as Jn, substituteEnvVarsFromStateAsync as Jr, parseImageOverrideFlags as Jt, createS3OriginReader as K, resetWorkloadIdentity as Ki, filterRoutesByApiIdentifier as Kn, substituteAgainstStateAsync as Kr, enforceImageOverrideOrphans as Kt, parseKvsFileOverrides as L, keysOverriddenBy as Li, createWatchPredicates as Ln, buildDisconnectEvent as Lr, ecsClusterOption as Lt, startAgentCoreWsBridge as M, DynamicReferenceResolver as Mi, classifySourceChange as Mn, ConnectionRegistry as Mr, MAX_TASKS_SUBNET_RANGE_CAP as Mt, LocalStartCloudFrontError as N, containsDynamicReference as Ni, addInvokeSpecificOptions as Nn, buildMgmtEndpointEnvUrl as Nr, addCommonEcsServiceOptions as Nt, buildAgentCoreServeAuthCheck as O, substituteImagePlaceholders as Oi, buildAgentCoreCodeImage as On, HOST_GATEWAY_MIN_VERSION as Or, createLocalStartServiceCommand as Ot, addStartCloudFrontSpecificOptions as P, deployedFillMayBeSecret as Pi, createLocalInvokeCommand as Pn, handleConnectionsRequest as Pr, addEcsAssumeRoleOptions as Pt, applyEdgeRequestResult as Q, isProxyEnvConfigured as Qi, resolveServiceIntegrationParameters as Qn, rejectExplicitCfnStackWithMultipleStacks as Qr, imageOverrideKeyAliases as Qt, parseOriginOverrides as R, parseDynamicReference as Ri, resolveApiTargetSubset as Rn, buildMessageEvent as Rr, parseMaxTasks as Rt, StudioEventBus as S, AGENTCORE_RUNTIME_TYPE as Si, AGENTCORE_SESSION_ID_HEADER as Sn, evaluateResponseParameters as Sr, createLocalStartAlbCommand as St, formatTargetListing as T, resolveAgentCoreTarget as Ti, waitForAgentCorePing as Tn, tryParseStatus as Tr, isApplicationLoadBalancer as Tt, resolveDeployedKvsArnByName as U, deployAccountStsClientConfig as Ui, materializeLayerFromArn as Un, resolveRuntimeImage as Ur, ImageOverrideError as Ut, resolveKvsModulesForDistribution as V, SECOND_ASSUME_MESSAGE as Vi, attachStageContext as Vn, resolveRuntimeCodeMountPath as Vr, resolveSharedSidecarCredentials as Vt, resolveDeployedOriginBucket as W, readEnvCallerCredentials as Wi, resolveEnvVars$1 as Wn, EcsTaskResolutionError as Wr, buildImageOverrideTag as Wt, serveFromStaticOrigin as X, resolveProfileCredentials as Xi, startApiServer as Xn, createLocalStateProvider as Xr, runImageOverrideBuilds as Xt, resolveErrorResponseCandidates as Y, buildStsClientConfig as Yi, readMtlsMaterialsFromDisk as Yn, LocalStateSourceError as Yr, resolveImageOverrides as Yt, serveLambdaUrlOrigin as Z, buildProxyClientConfig as Zi, resolveSelectionExpression as Zn, isCfnFlagPresent as Zr, describePinnedImageUri as Zt, filterStudioTargetGroups as _, resolveLambdaArnIntrinsic as _i, MCP_PROTOCOL_VERSION as _n, matchRoute as _r, createCloudFrontModule as _t, createLocalStudioCommand as a, resolveWatchConfig as ai, setShadowReadyTimeoutMs as an, verifyJwtViaDiscovery as ar, describeS3OriginDomain as at, renderStudioHtml as b, AGENTCORE_HTTP_PROTOCOL as bi, AGENTCORE_SIGV4_SERVICE as bn, buildHttpApiV2Event as br, addAlbSpecificOptions as bt, startStudioProxy as c, listTargets as ci, addInvokeAgentCoreSpecificOptions as cn, evaluateCachedLambdaPolicy as cr, pickFunctionUrlLogicalIdFromOrigin as ct, createStudioDispatcher as d, discoverWebSocketApisOrThrow as di, invokeAgentCoreWs as dn, attachAuthorizers as dr, pickTargetFunctionLogicalId as dt, resolveCfnRegion as ei, listPinnedTargets as en, buildCognitoJwksUrl as er, buildEdgeRequestEvent as et, filterStudioCustomResources as f, filterWebSocketApisByIdentifiers as fi, A2A_CONTAINER_PORT as fn, applyCorsResponseHeaders as fr, resolveCloudFrontDistribution as ft, annotatePinnedEcsTargets as g, pickRefLogicalId as gi, MCP_PATH as gn, matchPreflight as gr, stripCloudFrontImport as gt, annotateEcsTaskPinnedTargets as h, discoverRoutes as hi, MCP_CONTAINER_PORT as hn, isFunctionUrlOacFronted as hr, runViewerResponse as ht, coerceStopRequest as i, resolveSsmParameters as ii, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as in, verifyJwtAuthorizer as ir, CLOUDFRONT_DISTRIBUTION_TYPE as it, attachAgentCoreWsBridge as j, DynamicReferenceResolutionError as ji, toCmdArgv as jn, bufferToBody as jr, createLocalRunTaskCommand as jt, selectServeInboundAuth as k, tryResolveImageFnJoin as ki, computeCodeImageTag as kn, probeHostGatewaySupport as kr, serviceStrategy as kt, relayServeRequest as l, availableWebSocketApiIdentifiers as li, createLocalInvokeAgentCoreCommand as ln, invokeRequestAuthorizer as lr, pickKvsLogicalIdFromArn as lt, annotateAlbPinnedBackingServices as m, webSocketApiMatchesIdentifier as mi, a2aInvokeOnce as mn, buildCorsConfigFromCloudFrontChain as mr, runViewerRequest as mt, coerceRunRequest as n, CfnLocalStateProvider as ni, CloudMapRegistry as nn, createJwksCache as nr, edgeHeadersToHttp as nt, resolveServeBaseUrl as o, resolveSingleTarget as oi, getContainerNetworkIp as on, buildMethodArn as or, extractKvsAssociations as ot, isCustomResourceLambdaTarget as p, parseSelectionExpressionPath as pi, A2A_PATH as pn, buildCorsConfigByApiId as pr, compileCloudFrontFunction as pt, matchBehavior as q, workloadClientConfig as qi, filterRoutesByApiIdentifiers as qn, substituteEnvVarsFromState as qr, mergeForService as qt, coerceServeRequest as r, collectSsmParameterRefs as ri, DEFAULT_SHADOW_READY_TIMEOUT_MS as rn, verifyCognitoJwt as rr, httpHeadersToEdge as rt, createStudioServeManager as s, countTargets as si, attachContainerLogStreamer as sn, computeRequestIdentityHash as sr, isCloudFrontDistribution as st, addStudioSpecificOptions as t, resolveCfnStackName as ti, buildCloudMapIndex as tn, buildJwksUrlFromIssuer as tr, buildEdgeResponseEvent as tt, reinvoke as u, discoverWebSocketApis as ui, bridgeAgentCoreWs as un, invokeTokenAuthorizer as ur, pickLambdaEdgeFunctionLogicalId as ut, startStudioServer as v, AGENTCORE_A2A_PROTOCOL as vi, mcpInvokeOnce as vn, translateLambdaResponse as vr, createLocalFileKvsDataSource as vt, createLocalListCommand as w, pickAgentCoreCandidateStack as wi, waitForAgentCoreHttpReady as wn, selectIntegrationResponse as wr, resolveAlbTarget as wt, createStudioStore as x, AGENTCORE_MCP_PROTOCOL as xi, signAgentCoreInvocation as xn, buildRestV1Event as xr, albStrategy as xt, toStudioTargetGroups as y, AGENTCORE_AGUI_PROTOCOL as yi, parseSseForJsonRpc as yn, applyAuthorizerOverlay as yr, createUnboundCloudFrontModule as yt, resolveCloudFrontTarget as z, resolveDynamicReferencesInEnv as zi, createAuthorizerCache as zn, architectureToPlatform as zr, parseRestartPolicy as zt };
30339
- //# sourceMappingURL=local-studio-DCTVck5l.js.map
30504
+ //# sourceMappingURL=local-studio-CJfNARFz.js.map