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.
- package/README.md +1 -1
- package/dist/{cdkl-io-host-CDXI-7cC.js → cdkl-io-host-DFQf5KWv.js} +12 -4
- package/dist/cdkl-io-host-DFQf5KWv.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/{docker-cmd-BSdu45mc.js → docker-cmd-CoZdbeos.js} +14 -13
- package/dist/docker-cmd-CoZdbeos.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/{local-studio-DCTVck5l.js → local-studio-CJfNARFz.js} +784 -619
- package/dist/local-studio-CJfNARFz.js.map +1 -0
- package/dist/local-studio-DDFmguSG.d.ts.map +1 -1
- package/dist/{logger-CwFXOD1w.js → logger-Co5XVwco.js} +68 -11
- package/dist/logger-Co5XVwco.js.map +1 -0
- package/package.json +1 -1
- package/dist/cdkl-io-host-CDXI-7cC.js.map +0 -1
- package/dist/docker-cmd-BSdu45mc.js.map +0 -1
- package/dist/local-studio-DCTVck5l.js.map +0 -1
- package/dist/logger-CwFXOD1w.js.map +0 -1
|
@@ -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-
|
|
2
|
-
import { a as
|
|
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
|
|
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
|
|
684
|
-
if (!isIamRoleArn(arn)) throw new
|
|
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
|
|
1169
|
-
const
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
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
|
|
1251
|
+
return true;
|
|
1175
1252
|
}
|
|
1176
|
-
function
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
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
|
|
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
|
|
1539
|
-
throw new LocalInvokeResolutionError(`Resource
|
|
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
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
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
|
|
1573
|
-
if (!handler) throw new LocalInvokeResolutionError(`Lambda
|
|
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
|
|
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
|
|
1617
|
-
if (joinResolved.kind === "unsupported-join") throw new LocalInvokeResolutionError(`Lambda
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1833
|
-
if (layerResource.Type !== "AWS::Lambda::LayerVersion") throw new LocalInvokeResolutionError(`Lambda
|
|
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
|
|
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
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2059
|
-
if (!SUPPORTED_AGENTCORE_PROTOCOLS.includes(value)) throw new AgentCoreResolutionError(`AgentCore Runtime
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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.
|
|
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.
|
|
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(${
|
|
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
|
|
3957
|
-
logger.warn(`${this.label}: ListStackResources(${this.cfnStackName}) failed: ${detail}. Was the stack deployed in region
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5033
|
-
if (resource.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`Resource
|
|
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
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
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
|
|
5054
|
-
if (networkMode === "awsvpc") warnings.push(`NetworkMode 'awsvpc' on
|
|
5055
|
-
if (props["ProxyConfiguration"]) warnings.push(`Task definition
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5244
|
-
for (const d of droppedSecretKeys) warnings.push(`Secret
|
|
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
|
|
5282
|
-
if (joinResolved.kind === "unsupported-join") throw new EcsTaskResolutionError(`Container
|
|
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
|
|
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
|
|
5295
|
-
if (substituted.includes("AWS::")) throw new EcsTaskResolutionError(`Container
|
|
5296
|
-
throw new EcsTaskResolutionError(`Container
|
|
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
|
|
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
|
|
5382
|
-
if (v["EFSVolumeConfiguration"]) throw new EcsTaskResolutionError(`Task
|
|
5383
|
-
if (v["FSxWindowsFileServerVolumeConfiguration"]) throw new EcsTaskResolutionError(`Task
|
|
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
|
|
5426
|
-
if (!subContext) throw new EcsTaskResolutionError(`Task
|
|
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
|
|
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
|
|
5543
|
-
for (const k of resolvedSecretNames) if (w.startsWith(`Secret
|
|
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
|
|
5548
|
-
for (const k of resolvedSecretNames) if (w.startsWith(`Container
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
6519
|
-
|
|
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
|
|
6608
|
-
|
|
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,
|
|
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
|
|
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:
|
|
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
|
|
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}:
|
|
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:
|
|
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:
|
|
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}:
|
|
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
|
|
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
|
|
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
|
|
8592
|
-
else if (pathMatch) logger.warn(`RequestParameter
|
|
8593
|
-
else logger.warn(`Unsupported RequestParameter key
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11903
|
-
if ((externalAttrs >>> 16 & 61440) === 40960) throw new Error(`Refusing to extract symlink entry
|
|
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
|
|
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
|
|
11918
|
-
if (payload.length !== uncompressedSize && compressionMethod !== 0) throw new Error(`ZIP entry
|
|
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
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
12825
|
+
const hints = /* @__PURE__ */ new Set();
|
|
12683
12826
|
for (const t of targets) {
|
|
12684
|
-
const
|
|
12685
|
-
if (
|
|
12686
|
-
|
|
12827
|
+
const hint = targetStackHint(t, stacks);
|
|
12828
|
+
if (hint === void 0) return void 0;
|
|
12829
|
+
hints.add(hint);
|
|
12687
12830
|
}
|
|
12688
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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().
|
|
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
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
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
|
|
13057
|
-
if (!handler) throw new Error(`Lambda
|
|
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
|
|
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
|
|
13094
|
-
if (joinResolved.kind === "unsupported-join") throw new Error(`Lambda
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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().
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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().
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
15422
|
-
if (typeof result.value !== "string" || result.value.length === 0) throw new CdkLocalError(`AgentCore Runtime
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
16677
|
-
const manifest = await new AssetManifestLoader().
|
|
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
|
|
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
|
|
16689
|
-
logger.info(`Skipping docker build (--no-build) for container
|
|
16690
|
-
if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image
|
|
16691
|
-
logger.debug(`Local tag ${tag} is cached; skipping build for container
|
|
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
|
|
16699
|
-
progressLabel: `Building container image for
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
16944
|
-
if (serviceResource.Type !== "AWS::ECS::Service") throw new EcsTaskResolutionError(`Resource
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
17055
|
-
if (target.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`ECS Service
|
|
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
|
|
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
|
|
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
|
-
|
|
17097
|
-
|
|
17098
|
-
|
|
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
|
|
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
|
|
17205
|
-
logger.info(`Starting ECS service
|
|
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
|
|
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
|
|
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}:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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
|
|
18273
|
-
if (resource.Type === "AWS::ServiceDiscovery::HttpNamespace") throw new EcsTaskResolutionError(`Stack ${stack.stackName}: AWS::ServiceDiscovery::HttpNamespace
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
18861
|
-
if (!res.headersSent) writeError(res, 502, `Lambda target
|
|
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
|
|
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().
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
20358
|
-
logger.warn(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
20635
|
+
logger.info(`Reload of ${displayStackName(newBoot.target)}: verdict=${verdict.kind} (${verdict.reason}).`);
|
|
20462
20636
|
} catch (err) {
|
|
20463
|
-
logger.warn(`Reload of
|
|
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
|
|
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(
|
|
20668
|
+
const candidate = pickCandidateStack$1(target, stacks);
|
|
20495
20669
|
if (!candidate) {
|
|
20496
|
-
logger.debug(`loadAssetContext: stack pattern '${parsed.stackPattern}' from target
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
20691
|
+
const manifest = await assetLoader.loadStackManifest(candidate, cdkOutDir);
|
|
20518
20692
|
if (!manifest) {
|
|
20519
|
-
logger.debug(`loadAssetContext: asset manifest missing for stack
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
20579
|
-
if (verdict.kind === "soft-reload") logger.info(`Reload of
|
|
20580
|
-
else logger.info(`Reload of
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
21059
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
21492
|
-
|
|
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
|
|
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
|
|
21653
|
-
const defaultResolved = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
23664
|
-
if (origin.kind === "lambda-url") return `Origin
|
|
23665
|
-
if (origin.kind === "custom") return `Origin
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
|
|
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
|
|
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
|
|
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
|
|
24178
|
-
else if (origin.kind === "s3-unresolved") logger.warn(`Origin
|
|
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
|
|
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
|
|
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
|
|
25070
|
-
lines.push(entry.kind ? ` ${
|
|
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
|
|
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 =
|
|
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
|
|
29319
|
-
return `refused a non-loopback ${what} ${quoted} from
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
30504
|
+
//# sourceMappingURL=local-studio-CJfNARFz.js.map
|