cdk-local 0.154.0 → 0.156.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.
@@ -30,7 +30,7 @@ import { StringDecoder } from "node:string_decoder";
30
30
  import { promisify } from "node:util";
31
31
  import { ECRClient, GetAuthorizationTokenCommand } from "@aws-sdk/client-ecr";
32
32
  import { readFile } from "fs/promises";
33
- import { basename as basename$1, dirname as dirname$1, join as join$1 } from "path";
33
+ import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, posix, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
34
34
  import { WebSocket, WebSocketServer } from "ws";
35
35
  import { Readable } from "node:stream";
36
36
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -46,6 +46,11 @@ import * as vm from "node:vm";
46
46
  import "@aws-sdk/signature-v4a";
47
47
  import { CloudFrontClient, paginateListKeyValueStores } from "@aws-sdk/client-cloudfront";
48
48
  import { CloudFrontKeyValueStoreClient, GetKeyCommand, ResourceNotFoundException } from "@aws-sdk/client-cloudfront-keyvaluestore";
49
+ import { existsSync as existsSync$1, readFileSync as readFileSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
50
+ import { request as request$2 } from "http";
51
+ import { execFile as execFile$1, spawn as spawn$1 } from "child_process";
52
+ import { promisify as promisify$1 } from "util";
53
+ import { randomUUID as randomUUID$1 } from "crypto";
49
54
  import { EventEmitter } from "node:events";
50
55
 
51
56
  //#region src/utils/url-authority.ts
@@ -1595,14 +1600,14 @@ function parseTarget(target) {
1595
1600
  function resolveLambdaTarget(target, stacks) {
1596
1601
  if (stacks.length === 0) throw new LocalInvokeResolutionError("No stacks found in the synthesized assembly.");
1597
1602
  const parsed = parseTarget(target);
1598
- const stack = pickStack$5(parsed, stacks, target);
1603
+ const stack = pickStack$6(parsed, stacks, target);
1599
1604
  const template = stack.template;
1600
1605
  const resources = template.Resources ?? {};
1601
1606
  let match;
1602
1607
  if (parsed.isPath) {
1603
1608
  const index = buildCdkPathIndex(template);
1604
1609
  const lambdaMatches = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId }) => resources[logicalId]?.Type === "AWS::Lambda::Function");
1605
- if (lambdaMatches.length === 0) throw notFoundError$3(target, stack, resources);
1610
+ if (lambdaMatches.length === 0) throw notFoundError$4(target, stack, resources);
1606
1611
  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.");
1607
1612
  const m = lambdaMatches[0];
1608
1613
  match = {
@@ -1611,7 +1616,7 @@ function resolveLambdaTarget(target, stacks) {
1611
1616
  };
1612
1617
  } else {
1613
1618
  const resource = resources[parsed.pathOrId];
1614
- if (!resource) throw notFoundError$3(target, stack, resources);
1619
+ if (!resource) throw notFoundError$4(target, stack, resources);
1615
1620
  match = {
1616
1621
  logicalId: parsed.pathOrId,
1617
1622
  resource
@@ -1624,7 +1629,7 @@ function resolveLambdaTarget(target, stacks) {
1624
1629
  }
1625
1630
  return extractLambdaProperties(stack, logicalId, resource, resources);
1626
1631
  }
1627
- function pickStack$5(parsed, stacks, target) {
1632
+ function pickStack$6(parsed, stacks, target) {
1628
1633
  const selection = selectTargetStack(parsed, target, stacks);
1629
1634
  switch (selection.kind) {
1630
1635
  case "found": return selection.stack;
@@ -1957,7 +1962,7 @@ function describeLayerEntry(entry) {
1957
1962
  return Object.prototype.toString.call(entry);
1958
1963
  }
1959
1964
  }
1960
- function notFoundError$3(target, stack, resources) {
1965
+ function notFoundError$4(target, stack, resources) {
1961
1966
  const lambdas = [];
1962
1967
  for (const [logicalId, resource] of Object.entries(resources)) {
1963
1968
  if (resource.Type !== "AWS::Lambda::Function") continue;
@@ -2002,7 +2007,7 @@ var AgentCoreResolutionError = class AgentCoreResolutionError extends Error {
2002
2007
  function resolveAgentCoreTarget(target, stacks, imageContext) {
2003
2008
  if (stacks.length === 0) throw new AgentCoreResolutionError("No stacks found in the synthesized assembly.");
2004
2009
  const parsed = parseTarget(target);
2005
- const stack = pickStack$4(parsed, stacks, target);
2010
+ const stack = pickStack$5(parsed, stacks, target);
2006
2011
  const resources = stack.template.Resources ?? {};
2007
2012
  const { logicalId, resource } = matchRuntime(parsed, target, stack, resources);
2008
2013
  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.`);
@@ -2011,7 +2016,7 @@ function resolveAgentCoreTarget(target, stacks, imageContext) {
2011
2016
  function pickAgentCoreCandidateStack(target, stacks) {
2012
2017
  return candidateTargetStack(parseTarget(target), target, stacks);
2013
2018
  }
2014
- function pickStack$4(parsed, stacks, target) {
2019
+ function pickStack$5(parsed, stacks, target) {
2015
2020
  const selection = selectTargetStack(parsed, target, stacks);
2016
2021
  switch (selection.kind) {
2017
2022
  case "found": return selection.stack;
@@ -2024,7 +2029,7 @@ function matchRuntime(parsed, target, stack, resources) {
2024
2029
  if (parsed.isPath) {
2025
2030
  const index = buildCdkPathIndex(stack.template);
2026
2031
  const runtimeMatches = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId }) => resources[logicalId]?.Type === AGENTCORE_RUNTIME_TYPE);
2027
- if (runtimeMatches.length === 0) throw notFoundError$2(target, stack, resources);
2032
+ if (runtimeMatches.length === 0) throw notFoundError$3(target, stack, resources);
2028
2033
  if (runtimeMatches.length > 1) throw new AgentCoreResolutionError(`Target '${target}' matches ${runtimeMatches.length} AgentCore Runtimes in ${stack.stackName}: ` + runtimeMatches.map((m) => m.logicalId).join(", ") + ". Refine the path or use the stack:LogicalId form.");
2029
2034
  const m = runtimeMatches[0];
2030
2035
  return {
@@ -2033,13 +2038,13 @@ function matchRuntime(parsed, target, stack, resources) {
2033
2038
  };
2034
2039
  }
2035
2040
  const resource = resources[parsed.pathOrId];
2036
- if (!resource) throw notFoundError$2(target, stack, resources);
2041
+ if (!resource) throw notFoundError$3(target, stack, resources);
2037
2042
  return {
2038
2043
  logicalId: parsed.pathOrId,
2039
2044
  resource
2040
2045
  };
2041
2046
  }
2042
- function notFoundError$2(target, stack, resources) {
2047
+ function notFoundError$3(target, stack, resources) {
2043
2048
  const available = Object.entries(resources).filter(([, r]) => r.Type === AGENTCORE_RUNTIME_TYPE).map(([id]) => id);
2044
2049
  const hint = available.length > 0 ? `Available AgentCore Runtimes in ${stack.stackName}: ${available.join(", ")}.` : `No ${AGENTCORE_RUNTIME_TYPE} resources found in ${stack.stackName}.`;
2045
2050
  return new AgentCoreResolutionError(`Target '${target}' not found. ${hint}`);
@@ -3531,7 +3536,9 @@ function listTargets(stacks) {
3531
3536
  ecsTaskDefinitions: sortEntries(scanByType(stacks, "AWS::ECS::TaskDefinition")),
3532
3537
  agentCoreRuntimes: sortEntries(scanAgentCoreRuntimes(stacks)),
3533
3538
  loadBalancers: sortEntries(scanApplicationLoadBalancers(stacks)),
3534
- cloudFrontDistributions: sortEntries(scanByType(stacks, "AWS::CloudFront::Distribution"))
3539
+ cloudFrontDistributions: sortEntries(scanByType(stacks, "AWS::CloudFront::Distribution")),
3540
+ webFunctions: sortEntries(scanByType(stacks, "AWS::Lambda::WebFunction")),
3541
+ microvmImages: sortEntries(scanByType(stacks, "AWS::Lambda::MicrovmImage"))
3535
3542
  };
3536
3543
  }
3537
3544
  const pathOf$1 = (e) => e.displayPath ?? e.qualifiedId;
@@ -3554,7 +3561,7 @@ function sortApiEntries(entries) {
3554
3561
  });
3555
3562
  }
3556
3563
  function countTargets(listing) {
3557
- return listing.lambdas.length + listing.apis.length + listing.ecsServices.length + listing.ecsTaskDefinitions.length + listing.agentCoreRuntimes.length + listing.loadBalancers.length + listing.cloudFrontDistributions.length;
3564
+ return listing.lambdas.length + listing.apis.length + listing.ecsServices.length + listing.ecsTaskDefinitions.length + listing.agentCoreRuntimes.length + listing.loadBalancers.length + listing.cloudFrontDistributions.length + listing.webFunctions.length + listing.microvmImages.length;
3558
3565
  }
3559
3566
 
3560
3567
  //#endregion
@@ -5095,28 +5102,28 @@ function parseEcsTarget(target) {
5095
5102
  function resolveEcsTaskTarget(target, stacks, context) {
5096
5103
  if (stacks.length === 0) throw new EcsTaskResolutionError("No stacks found in the synthesized assembly.");
5097
5104
  const parsed = parseEcsTarget(target);
5098
- const stack = pickStack$3(parsed, stacks, target);
5105
+ const stack = pickStack$4(parsed, stacks, target);
5099
5106
  const resources = stack.template.Resources ?? {};
5100
5107
  let logicalId;
5101
5108
  let resource;
5102
5109
  if (parsed.isPath) {
5103
5110
  const index = buildCdkPathIndex(stack.template);
5104
5111
  const taskDefs = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId: l }) => resources[l]?.Type === "AWS::ECS::TaskDefinition");
5105
- if (taskDefs.length === 0) throw notFoundError$1(target, stack, resources);
5112
+ if (taskDefs.length === 0) throw notFoundError$2(target, stack, resources);
5106
5113
  if (taskDefs.length > 1) throw new EcsTaskResolutionError(`Target '${target}' matches ${taskDefs.length} task definitions in ${stack.stackName}: ` + taskDefs.map((t) => t.logicalId).join(", ") + ". Refine the path or use the stack:LogicalId form.");
5107
5114
  logicalId = taskDefs[0].logicalId;
5108
5115
  resource = resources[logicalId];
5109
5116
  } else {
5110
5117
  resource = resources[parsed.pathOrId];
5111
- if (!resource) throw notFoundError$1(target, stack, resources);
5118
+ if (!resource) throw notFoundError$2(target, stack, resources);
5112
5119
  logicalId = parsed.pathOrId;
5113
5120
  }
5114
- if (!logicalId || !resource) throw notFoundError$1(target, stack, resources);
5121
+ if (!logicalId || !resource) throw notFoundError$2(target, stack, resources);
5115
5122
  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
5123
  if (resource.Type !== "AWS::ECS::TaskDefinition") throw new EcsTaskResolutionError(`Resource ${displayIdent(logicalId)} in ${stack.stackName} is ${displayIdent(resource.Type)}, not an AWS::ECS::TaskDefinition.`);
5117
5124
  return extractTaskDefinitionProperties(stack, logicalId, resource, context);
5118
5125
  }
5119
- function pickStack$3(parsed, stacks, target) {
5126
+ function pickStack$4(parsed, stacks, target) {
5120
5127
  const selection = selectTargetStack(parsed, target, stacks);
5121
5128
  switch (selection.kind) {
5122
5129
  case "found": return selection.stack;
@@ -5545,7 +5552,7 @@ function pickStringArray$1(value) {
5545
5552
  for (const v of value) if (typeof v === "string") out.push(v);
5546
5553
  return out;
5547
5554
  }
5548
- function notFoundError$1(target, stack, resources) {
5555
+ function notFoundError$2(target, stack, resources) {
5549
5556
  const tasks = [];
5550
5557
  for (const [logicalId, resource] of Object.entries(resources)) {
5551
5558
  if (resource.Type !== "AWS::ECS::TaskDefinition") continue;
@@ -6488,7 +6495,7 @@ function pushMap(hash, name, value) {
6488
6495
 
6489
6496
  //#endregion
6490
6497
  //#region src/local/docker-runner.ts
6491
- const execFileAsync$5 = promisify(execFile);
6498
+ const execFileAsync$7 = promisify(execFile);
6492
6499
  var DockerRunnerError = class DockerRunnerError extends Error {
6493
6500
  constructor(message) {
6494
6501
  super(message);
@@ -6535,7 +6542,7 @@ async function pullImage(image, skipPull, platform) {
6535
6542
  }
6536
6543
  }
6537
6544
  async function runDetached(opts) {
6538
- const args = [
6545
+ const args = opts.autoRemove === false ? ["run", "-d"] : [
6539
6546
  "run",
6540
6547
  "-d",
6541
6548
  "--rm"
@@ -6550,6 +6557,7 @@ async function runDetached(opts) {
6550
6557
  const host = opts.host ?? "127.0.0.1";
6551
6558
  args.push("-p", `${host}:${opts.hostPort}:${opts.containerPort ?? 8080}`);
6552
6559
  if (opts.debugPort !== void 0) args.push("-p", `${host}:${opts.debugPort}:${opts.debugPort}`);
6560
+ for (const extra of opts.extraPorts ?? []) args.push("-p", `${host}:${extra.hostPort}:${extra.containerPort}`);
6553
6561
  for (const mount of opts.mounts) {
6554
6562
  const ro = mount.readOnly ? ":ro" : "";
6555
6563
  args.push("-v", `${mount.hostPath}:${mount.containerPath}${ro}`);
@@ -6579,7 +6587,7 @@ async function runDetached(opts) {
6579
6587
  if (refusal !== void 0) throw new DockerRunnerError(refusal);
6580
6588
  warnFinchArgvExposure(forwardedKeys);
6581
6589
  try {
6582
- const { stdout } = await execFileAsync$5(getDockerCmd(), args, {
6590
+ const { stdout } = await execFileAsync$7(getDockerCmd(), args, {
6583
6591
  maxBuffer: 10 * 1024 * 1024,
6584
6592
  ...execEnvForSecrets(passthroughEnv)
6585
6593
  });
@@ -6620,7 +6628,7 @@ function createPrefixedLineRelay(prefix, out) {
6620
6628
  }
6621
6629
  };
6622
6630
  }
6623
- function streamLogs(containerId) {
6631
+ function streamLogs(containerId, opts = {}) {
6624
6632
  const proc = spawn(getDockerCmd(), [
6625
6633
  "logs",
6626
6634
  "-f",
@@ -6630,9 +6638,14 @@ function streamLogs(containerId) {
6630
6638
  "pipe",
6631
6639
  "pipe"
6632
6640
  ] });
6641
+ if (opts.onOutput) {
6642
+ const tap = opts.onOutput;
6643
+ proc.stdout?.on("data", (chunk) => tap(chunk));
6644
+ proc.stderr?.on("data", (chunk) => tap(chunk));
6645
+ }
6633
6646
  let flush = () => void 0;
6634
- if (process.env["CDKL_PREFIX_CONTAINER_OUTPUT"] === "1") {
6635
- const tag = `[${flattenToOneLine(containerId.slice(0, 12)).replace(/[\]\s]/g, "_")}] `;
6647
+ if (opts.prefix !== void 0 || process.env["CDKL_PREFIX_CONTAINER_OUTPUT"] === "1") {
6648
+ const tag = opts.prefix ?? `[${flattenToOneLine(containerId.slice(0, 12)).replace(/[\]\s]/g, "_")}] `;
6636
6649
  const stdoutRelay = createPrefixedLineRelay(tag, process.stdout);
6637
6650
  const stderrRelay = createPrefixedLineRelay(tag, process.stderr);
6638
6651
  proc.stdout?.on("data", (chunk) => stdoutRelay.push(chunk));
@@ -6656,7 +6669,7 @@ async function removeContainer(containerId) {
6656
6669
  if (!containerId) return;
6657
6670
  const logger = getLogger().child("docker");
6658
6671
  try {
6659
- await execFileAsync$5(getDockerCmd(), [
6672
+ await execFileAsync$7(getDockerCmd(), [
6660
6673
  "rm",
6661
6674
  "-f",
6662
6675
  containerId
@@ -6670,7 +6683,7 @@ async function removeContainer(containerId) {
6670
6683
  async function ensureDockerAvailable() {
6671
6684
  const cmd = getDockerCmd();
6672
6685
  try {
6673
- await execFileAsync$5(cmd, [
6686
+ await execFileAsync$7(cmd, [
6674
6687
  "version",
6675
6688
  "--format",
6676
6689
  "{{.Server.Version}}"
@@ -7203,14 +7216,14 @@ async function handleConnectionsRequest(opts) {
7203
7216
  const { req, res, registry } = opts;
7204
7217
  const parsed = parseConnectionsPath(req.url ?? "");
7205
7218
  if (!parsed) {
7206
- writeJson(res, 404, { message: "Not Found" });
7219
+ writeJson$1(res, 404, { message: "Not Found" });
7207
7220
  return;
7208
7221
  }
7209
7222
  const { connectionId } = parsed;
7210
7223
  const entry = registry.get(connectionId);
7211
7224
  const method = (req.method ?? "").toUpperCase();
7212
7225
  if (!entry) {
7213
- writeJson(res, 410, { message: "GoneException" });
7226
+ writeJson$1(res, 410, { message: "GoneException" });
7214
7227
  return;
7215
7228
  }
7216
7229
  if (method === "POST") {
@@ -7218,17 +7231,17 @@ async function handleConnectionsRequest(opts) {
7218
7231
  try {
7219
7232
  body = await readRequestBody$1(req);
7220
7233
  } catch (err) {
7221
- writeJson(res, 500, { message: `Failed to read request body: ${err instanceof Error ? err.message : String(err)}` });
7234
+ writeJson$1(res, 500, { message: `Failed to read request body: ${err instanceof Error ? err.message : String(err)}` });
7222
7235
  return;
7223
7236
  }
7224
7237
  if (entry.socket.readyState !== entry.socket.OPEN) {
7225
- writeJson(res, 410, { message: "GoneException" });
7238
+ writeJson$1(res, 410, { message: "GoneException" });
7226
7239
  return;
7227
7240
  }
7228
7241
  try {
7229
7242
  entry.socket.send(body);
7230
7243
  } catch (err) {
7231
- writeJson(res, 500, { message: `Failed to deliver to socket: ${err instanceof Error ? err.message : String(err)}` });
7244
+ writeJson$1(res, 500, { message: `Failed to deliver to socket: ${err instanceof Error ? err.message : String(err)}` });
7232
7245
  return;
7233
7246
  }
7234
7247
  res.writeHead(200);
@@ -7239,7 +7252,7 @@ async function handleConnectionsRequest(opts) {
7239
7252
  try {
7240
7253
  entry.socket.close(1e3, "DeleteConnection");
7241
7254
  } catch (err) {
7242
- writeJson(res, 500, { message: `Failed to close socket: ${err instanceof Error ? err.message : String(err)}` });
7255
+ writeJson$1(res, 500, { message: `Failed to close socket: ${err instanceof Error ? err.message : String(err)}` });
7243
7256
  return;
7244
7257
  }
7245
7258
  res.writeHead(204);
@@ -7247,7 +7260,7 @@ async function handleConnectionsRequest(opts) {
7247
7260
  return;
7248
7261
  }
7249
7262
  if (method === "GET") {
7250
- writeJson(res, 200, {
7263
+ writeJson$1(res, 200, {
7251
7264
  ConnectedAt: new Date(entry.connectedAt).toISOString(),
7252
7265
  Identity: { SourceIp: "127.0.0.1" },
7253
7266
  LastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -7255,9 +7268,9 @@ async function handleConnectionsRequest(opts) {
7255
7268
  return;
7256
7269
  }
7257
7270
  res.setHeader("Allow", "POST, GET, DELETE");
7258
- writeJson(res, 405, { message: "MethodNotAllowedException" });
7271
+ writeJson$1(res, 405, { message: "MethodNotAllowedException" });
7259
7272
  }
7260
- function writeJson(res, status, body) {
7273
+ function writeJson$1(res, status, body) {
7261
7274
  const json = JSON.stringify(body);
7262
7275
  res.writeHead(status, {
7263
7276
  "Content-Type": "application/json",
@@ -11039,7 +11052,7 @@ async function startApiServer(opts) {
11039
11052
  const logger = getLogger().child("start-api");
11040
11053
  let currentState = opts.state;
11041
11054
  const requestHandler = (req, res) => {
11042
- handleRequest$2(req, res, currentState, opts).catch((err) => {
11055
+ handleRequest$3(req, res, currentState, opts).catch((err) => {
11043
11056
  logger.error(`Unhandled request error: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
11044
11057
  if (!res.headersSent) writeError$1(res, 502);
11045
11058
  });
@@ -11091,7 +11104,7 @@ async function startApiServer(opts) {
11091
11104
  getServerState: () => currentState
11092
11105
  };
11093
11106
  }
11094
- async function handleRequest$2(req, res, state, opts) {
11107
+ async function handleRequest$3(req, res, state, opts) {
11095
11108
  const logger = getLogger().child("start-api");
11096
11109
  if (opts.preDispatch) try {
11097
11110
  if (await opts.preDispatch(req, res)) return;
@@ -14095,7 +14108,7 @@ async function resolveLambdaContainerEnvWith(lambda, options, profileCredentials
14095
14108
  for (const key of audit.resolvedKeys) logger.debug(`${label}: substituted env var ${key}`);
14096
14109
  let unresolved = audit.unresolved;
14097
14110
  const resolvedKeys = [...audit.resolvedKeys];
14098
- if (unresolved.length > 0 && stateProvider.resolveDeployedFunctionEnv) {
14111
+ if (unresolved.length > 0 && options.deployedFunctionLookups !== false && stateProvider.resolveDeployedFunctionEnv) {
14099
14112
  const physicalId = loaded.resources[lambda.logicalId]?.physicalId;
14100
14113
  if (physicalId) {
14101
14114
  const deployedEnv = await stateProvider.resolveDeployedFunctionEnv(physicalId);
@@ -14137,7 +14150,9 @@ async function resolveLambdaContainerEnvWith(lambda, options, profileCredentials
14137
14150
  });
14138
14151
  let resolvedAssumeRoleArn;
14139
14152
  try {
14140
- resolvedAssumeRoleArn = await resolveAssumeRoleArnForLambda(options.assumeRole, stateForRoleHint, stateProvider, lambda.logicalId);
14153
+ const fromTemplate = options.assumeRole === true && options.deployedFunctionLookups === false && stateForRoleHint ? await resolveTemplateRoleArnFromState(lambda.resource.Properties?.["Role"], stateForRoleHint, lambda.stack.template, () => resolvePseudoParametersForInvoke(lambda.stack.region, options)) : void 0;
14154
+ if (fromTemplate !== void 0) logger.info(`--assume-role: auto-resolved execution role from state: ${flattenToOneLine(fromTemplate)}`);
14155
+ resolvedAssumeRoleArn = fromTemplate ?? await resolveAssumeRoleArnForLambda(options.assumeRole, stateForRoleHint, options.deployedFunctionLookups === false ? void 0 : stateProvider, lambda.logicalId);
14141
14156
  } finally {
14142
14157
  stateProvider?.dispose();
14143
14158
  }
@@ -14248,6 +14263,28 @@ function resolveExecutionRoleArnFromState(state, logicalId, roleProperty = "Role
14248
14263
  }
14249
14264
  }
14250
14265
  }
14266
+ async function resolveTemplateRoleArnFromState(templateRole, state, template, pseudo) {
14267
+ if (isIamRoleArn(templateRole)) return templateRole;
14268
+ if (!templateRole || typeof templateRole !== "object" || Array.isArray(templateRole)) return;
14269
+ const ref = pickReferencedLogicalId(templateRole);
14270
+ if (ref === void 0) return void 0;
14271
+ const roleResource = template.Resources?.[ref];
14272
+ if (roleResource?.Type !== "AWS::IAM::Role") return void 0;
14273
+ const deployed = state.resources[ref];
14274
+ if (!deployed) return void 0;
14275
+ const cached = deployed.attributes?.["Arn"];
14276
+ if (isIamRoleArn(cached)) return cached;
14277
+ const physicalId = deployed.physicalId;
14278
+ if (isIamRoleArn(physicalId)) return physicalId;
14279
+ if (typeof physicalId !== "string" || !/^[\w+=,.@-]{1,64}$/.test(physicalId)) return void 0;
14280
+ const rawPath = roleResource.Properties?.["Path"];
14281
+ if (rawPath !== void 0 && typeof rawPath !== "string") return void 0;
14282
+ const rolePath = rawPath ?? "/";
14283
+ const bag = await pseudo();
14284
+ if (!bag?.accountId) return void 0;
14285
+ const arn = `arn:${bag.partition ?? "aws"}:iam::${bag.accountId}:role${rolePath}${physicalId}`;
14286
+ return isIamRoleArn(arn) ? arn : void 0;
14287
+ }
14251
14288
  function pickReferencedLogicalId(intrinsic) {
14252
14289
  if ("Ref" in intrinsic && typeof intrinsic["Ref"] === "string") return intrinsic["Ref"];
14253
14290
  if ("Fn::GetAtt" in intrinsic) {
@@ -15996,7 +16033,7 @@ async function runAgentCoreWatchLoop(args) {
15996
16033
  }
15997
16034
  }
15998
16035
  }
15999
- const execFileAsync$4 = promisify(execFile);
16036
+ const execFileAsync$6 = promisify(execFile);
16000
16037
  function describeExecError(err) {
16001
16038
  if (!(err instanceof Error)) return String(err);
16002
16039
  const stderr = err.stderr;
@@ -16008,7 +16045,7 @@ async function softReloadAgentContainer(containerId, newAssetSourceDir) {
16008
16045
  const dockerCmd = getDockerCmd();
16009
16046
  let workdir;
16010
16047
  try {
16011
- const { stdout } = await execFileAsync$4(dockerCmd, [
16048
+ const { stdout } = await execFileAsync$6(dockerCmd, [
16012
16049
  "inspect",
16013
16050
  "--format",
16014
16051
  "{{.Config.WorkingDir}}",
@@ -16021,7 +16058,7 @@ async function softReloadAgentContainer(containerId, newAssetSourceDir) {
16021
16058
  const workdirDest = workdir.endsWith("/") ? workdir : `${workdir}/`;
16022
16059
  logger.info(`Soft-reload: docker cp ${flattenToOneLine(newAssetSourceDir)} -> ${containerId}:${workdirDest}; restart.`);
16023
16060
  try {
16024
- await execFileAsync$4(dockerCmd, [
16061
+ await execFileAsync$6(dockerCmd, [
16025
16062
  "cp",
16026
16063
  `${newAssetSourceDir}/.`,
16027
16064
  `${containerId}:${workdirDest}`
@@ -16030,7 +16067,7 @@ async function softReloadAgentContainer(containerId, newAssetSourceDir) {
16030
16067
  throw new CdkLocalError(`softReloadAgentContainer: docker cp into '${containerId}:${workdir}' failed: ${describeExecError(err)}.`, "LOCAL_INVOKE_AGENTCORE_WATCH_SOFT_RELOAD_CP_FAILED");
16031
16068
  }
16032
16069
  try {
16033
- await execFileAsync$4(dockerCmd, ["restart", containerId]);
16070
+ await execFileAsync$6(dockerCmd, ["restart", containerId]);
16034
16071
  } catch (err) {
16035
16072
  throw new CdkLocalError(`softReloadAgentContainer: docker restart of '${containerId}' failed: ${describeExecError(err)}.`, "LOCAL_INVOKE_AGENTCORE_WATCH_SOFT_RELOAD_RESTART_FAILED");
16036
16073
  }
@@ -16206,7 +16243,7 @@ function writePrefixedLines(prefix, buffer, out) {
16206
16243
 
16207
16244
  //#endregion
16208
16245
  //#region src/local/ecs-network.ts
16209
- const execFileAsync$3 = promisify(execFile);
16246
+ const execFileAsync$5 = promisify(execFile);
16210
16247
  const METADATA_ENDPOINT_IMAGE = "amazon/amazon-ecs-local-container-endpoints:latest-amd64";
16211
16248
  const METADATA_ENDPOINT_IP = "169.254.170.2";
16212
16249
  const DEFAULT_METADATA_ENDPOINT_SUBNET = "169.254.170.0/24";
@@ -16242,7 +16279,7 @@ async function sweepOrphanedSvcNetworks(prefix) {
16242
16279
  const filter = `${prefix}-svc-`;
16243
16280
  let names;
16244
16281
  try {
16245
- const { stdout } = await execFileAsync$3(getDockerCmd(), [
16282
+ const { stdout } = await execFileAsync$5(getDockerCmd(), [
16246
16283
  "network",
16247
16284
  "ls",
16248
16285
  "--filter",
@@ -16260,7 +16297,7 @@ async function sweepOrphanedSvcNetworks(prefix) {
16260
16297
  for (const name of names) {
16261
16298
  let attached;
16262
16299
  try {
16263
- const { stdout } = await execFileAsync$3(getDockerCmd(), [
16300
+ const { stdout } = await execFileAsync$5(getDockerCmd(), [
16264
16301
  "network",
16265
16302
  "inspect",
16266
16303
  name,
@@ -16288,7 +16325,7 @@ async function createNetworkAndSidecar(args) {
16288
16325
  await pullImage(METADATA_ENDPOINT_IMAGE, skipPull);
16289
16326
  logger.info(`Creating docker network ${networkName} (subnet ${cidr})...`);
16290
16327
  try {
16291
- await execFileAsync$3(getDockerCmd(), [
16328
+ await execFileAsync$5(getDockerCmd(), [
16292
16329
  "network",
16293
16330
  "create",
16294
16331
  "--driver",
@@ -16325,7 +16362,7 @@ async function createNetworkAndSidecar(args) {
16325
16362
  warnFinchArgvExposure(Object.keys(sidecarPassthroughEnv));
16326
16363
  logger.info(`Starting ECS local-container-endpoints sidecar at ${sidecarIp}...`);
16327
16364
  try {
16328
- const { stdout } = await execFileAsync$3(getDockerCmd(), sidecarArgs, {
16365
+ const { stdout } = await execFileAsync$5(getDockerCmd(), sidecarArgs, {
16329
16366
  maxBuffer: 10 * 1024 * 1024,
16330
16367
  ...execEnvForSecrets(sidecarPassthroughEnv)
16331
16368
  });
@@ -16373,7 +16410,7 @@ async function destroyNetworkOnly(networkName) {
16373
16410
  if (!networkName) return;
16374
16411
  const logger = getLogger().child("ecs-network");
16375
16412
  try {
16376
- await execFileAsync$3(getDockerCmd(), [
16413
+ await execFileAsync$5(getDockerCmd(), [
16377
16414
  "network",
16378
16415
  "rm",
16379
16416
  networkName
@@ -16487,7 +16524,7 @@ async function resolveSsm(entry, shape, client) {
16487
16524
 
16488
16525
  //#endregion
16489
16526
  //#region src/local/ecs-task-runner.ts
16490
- const execFileAsync$2 = promisify(execFile);
16527
+ const execFileAsync$4 = promisify(execFile);
16491
16528
  var EcsTaskRunnerError = class EcsTaskRunnerError extends Error {
16492
16529
  constructor(message) {
16493
16530
  super(message);
@@ -16530,7 +16567,7 @@ async function cleanupEcsRun(state, options) {
16530
16567
  if (state.network && !state.network.ownedByCaller) await destroyTaskNetwork(state.network);
16531
16568
  state.network = void 0;
16532
16569
  for (const v of state.dockerVolumeNames) try {
16533
- await execFileAsync$2(getDockerCmd(), [
16570
+ await execFileAsync$4(getDockerCmd(), [
16534
16571
  "volume",
16535
16572
  "rm",
16536
16573
  v
@@ -16681,7 +16718,7 @@ async function runEcsTask(task, options, state) {
16681
16718
  let id;
16682
16719
  warnFinchArgvExposure(Object.keys(sensitiveEnv));
16683
16720
  try {
16684
- const { stdout } = await execFileAsync$2(getDockerCmd(), args, {
16721
+ const { stdout } = await execFileAsync$4(getDockerCmd(), args, {
16685
16722
  maxBuffer: 10 * 1024 * 1024,
16686
16723
  ...execEnvForSecrets(sensitiveEnv)
16687
16724
  });
@@ -16773,7 +16810,7 @@ async function waitForContainerHealthy(containerId, displayName) {
16773
16810
  let lastStatus = "";
16774
16811
  while (Date.now() < deadline) {
16775
16812
  try {
16776
- const { stdout } = await execFileAsync$2(getDockerCmd(), [
16813
+ const { stdout } = await execFileAsync$4(getDockerCmd(), [
16777
16814
  "inspect",
16778
16815
  "--format",
16779
16816
  "{{.State.Health.Status}}",
@@ -16796,7 +16833,7 @@ async function waitForContainerHealthy(containerId, displayName) {
16796
16833
  }
16797
16834
  async function waitForContainerExit(containerId) {
16798
16835
  try {
16799
- const { stdout } = await execFileAsync$2(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
16836
+ const { stdout } = await execFileAsync$4(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
16800
16837
  const code = Number.parseInt(stdout.trim(), 10);
16801
16838
  return Number.isFinite(code) ? code : 1;
16802
16839
  } catch (err) {
@@ -16806,7 +16843,7 @@ async function waitForContainerExit(containerId) {
16806
16843
  }
16807
16844
  async function stopContainer(containerId, graceSeconds) {
16808
16845
  try {
16809
- await execFileAsync$2(getDockerCmd(), [
16846
+ await execFileAsync$4(getDockerCmd(), [
16810
16847
  "stop",
16811
16848
  "-t",
16812
16849
  String(graceSeconds),
@@ -16901,7 +16938,7 @@ async function realizeDockerVolumes(volumes, state) {
16901
16938
  const dockerVolumeName = `${getEmbedConfig().resourceNamePrefix}-${v.name}-${randHex(4)}`;
16902
16939
  args.push(dockerVolumeName);
16903
16940
  try {
16904
- await execFileAsync$2(getDockerCmd(), args);
16941
+ await execFileAsync$4(getDockerCmd(), args);
16905
16942
  state.dockerVolumeNames.push(dockerVolumeName);
16906
16943
  logger.debug(`Created docker volume ${displayIdent(dockerVolumeName)} for task volume ${displayIdent(v.name)}`);
16907
16944
  } catch (err) {
@@ -17095,23 +17132,23 @@ function shellJoin(parts) {
17095
17132
  function resolveEcsServiceTarget(target, stacks, context, options) {
17096
17133
  if (stacks.length === 0) throw new EcsTaskResolutionError("No stacks found in the synthesized assembly.");
17097
17134
  const parsed = parseEcsTarget(target);
17098
- const stack = pickStack$2(parsed, stacks, target);
17135
+ const stack = pickStack$3(parsed, stacks, target);
17099
17136
  const resources = stack.template.Resources ?? {};
17100
17137
  let serviceLogicalId;
17101
17138
  let serviceResource;
17102
17139
  if (parsed.isPath) {
17103
17140
  const index = buildCdkPathIndex(stack.template);
17104
17141
  const services = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId: l }) => resources[l]?.Type === "AWS::ECS::Service");
17105
- if (services.length === 0) throw notFoundError(target, stack, resources, parsed);
17142
+ if (services.length === 0) throw notFoundError$1(target, stack, resources, parsed);
17106
17143
  if (services.length > 1) throw new EcsTaskResolutionError(`Target '${target}' matches ${services.length} ECS services in ${stack.stackName}: ` + services.map((s) => s.logicalId).join(", ") + ". Refine the path or use the stack:LogicalId form.");
17107
17144
  serviceLogicalId = services[0].logicalId;
17108
17145
  serviceResource = resources[serviceLogicalId];
17109
17146
  } else {
17110
17147
  serviceResource = resources[parsed.pathOrId];
17111
- if (!serviceResource) throw notFoundError(target, stack, resources, parsed);
17148
+ if (!serviceResource) throw notFoundError$1(target, stack, resources, parsed);
17112
17149
  serviceLogicalId = parsed.pathOrId;
17113
17150
  }
17114
- if (!serviceLogicalId || !serviceResource) throw notFoundError(target, stack, resources, parsed);
17151
+ if (!serviceLogicalId || !serviceResource) throw notFoundError$1(target, stack, resources, parsed);
17115
17152
  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
17153
  if (serviceResource.Type !== "AWS::ECS::Service") throw new EcsTaskResolutionError(`Resource ${displayIdent(serviceLogicalId)} in ${stack.stackName} is ${displayIdent(serviceResource.Type)}, not an AWS::ECS::Service.`);
17117
17154
  return extractServiceProperties(stack, serviceLogicalId, serviceResource, stacks, context, options);
@@ -17267,7 +17304,7 @@ function deriveServiceDisplayName(rawServiceName, serviceLogicalId, metadata) {
17267
17304
  }
17268
17305
  return serviceLogicalId;
17269
17306
  }
17270
- function pickStack$2(parsed, stacks, target) {
17307
+ function pickStack$3(parsed, stacks, target) {
17271
17308
  const selection = selectTargetStack(parsed, target, stacks);
17272
17309
  switch (selection.kind) {
17273
17310
  case "found": return selection.stack;
@@ -17276,7 +17313,7 @@ function pickStack$2(parsed, stacks, target) {
17276
17313
  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).`);
17277
17314
  }
17278
17315
  }
17279
- function notFoundError(target, stack, resources, parsed) {
17316
+ function notFoundError$1(target, stack, resources, parsed) {
17280
17317
  const services = [];
17281
17318
  for (const [logicalId, r] of Object.entries(resources)) {
17282
17319
  if (r.Type !== "AWS::ECS::Service") continue;
@@ -17301,11 +17338,11 @@ function notFoundError(target, stack, resources, parsed) {
17301
17338
 
17302
17339
  //#endregion
17303
17340
  //#region src/local/docker-inspect.ts
17304
- const execFileAsync$1 = promisify(execFile);
17341
+ const execFileAsync$3 = promisify(execFile);
17305
17342
  async function getContainerNetworkIp(containerId, networkName) {
17306
17343
  const format = `{{with index .NetworkSettings.Networks "${networkName}"}}{{.IPAddress}}{{end}}`;
17307
17344
  try {
17308
- const { stdout } = await execFileAsync$1(getDockerCmd(), [
17345
+ const { stdout } = await execFileAsync$3(getDockerCmd(), [
17309
17346
  "inspect",
17310
17347
  "--format",
17311
17348
  format,
@@ -17321,7 +17358,7 @@ async function getContainerNetworkIp(containerId, networkName) {
17321
17358
  async function getPublishedHostPort(containerId, containerPort, protocol = "tcp") {
17322
17359
  const format = `{{with index .NetworkSettings.Ports "${`${containerPort}/${protocol}`}"}}{{with index . 0}}{{.HostPort}}{{end}}{{end}}`;
17323
17360
  try {
17324
- const { stdout } = await execFileAsync$1(getDockerCmd(), [
17361
+ const { stdout } = await execFileAsync$3(getDockerCmd(), [
17325
17362
  "inspect",
17326
17363
  "--format",
17327
17364
  format,
@@ -18619,7 +18656,7 @@ function buildHeaderMaps(headers) {
18619
18656
  multi
18620
18657
  };
18621
18658
  }
18622
- function firstHeader(headers, name) {
18659
+ function firstHeader$1(headers, name) {
18623
18660
  const lower = name.toLowerCase();
18624
18661
  for (const [k, v] of Object.entries(headers)) if (k.toLowerCase() === lower) return v[0];
18625
18662
  }
@@ -18627,8 +18664,8 @@ function buildAlbLambdaEvent(req, opts) {
18627
18664
  const { path, rawQuery } = splitRawUrl(req.rawUrl);
18628
18665
  const query = parseQuery(rawQuery);
18629
18666
  const headerMaps = buildHeaderMaps(req.headers);
18630
- const contentEncoding = firstHeader(req.headers, "content-encoding");
18631
- const contentType = firstHeader(req.headers, "content-type") ?? "";
18667
+ const contentEncoding = firstHeader$1(req.headers, "content-encoding");
18668
+ const contentType = firstHeader$1(req.headers, "content-type") ?? "";
18632
18669
  const isBase64Encoded = req.body.length > 0 && (contentEncoding !== void 0 ? true : !isTextualContentType(contentType));
18633
18670
  const body = isBase64Encoded ? req.body.toString("base64") : req.body.toString("utf-8");
18634
18671
  const event = {
@@ -18731,7 +18768,7 @@ async function startFrontDoorServer(opts) {
18731
18768
  const scheme = opts.tls ? "https" : "http";
18732
18769
  server.on("connection", (socket) => socket.setNoDelay(true));
18733
18770
  server.on("upgrade", (req, clientSocket, head) => {
18734
- handleUpgrade(req, clientSocket, head, effectiveOpts).catch((err) => {
18771
+ handleUpgrade$1(req, clientSocket, head, effectiveOpts).catch((err) => {
18735
18772
  logger.debug(`front-door upgrade error: ${err instanceof Error ? err.message : String(err)}`);
18736
18773
  if (!clientSocket.destroyed) clientSocket.destroy();
18737
18774
  });
@@ -19075,7 +19112,7 @@ function writeError(res, statusCode, message) {
19075
19112
  res.writeHead(statusCode, { "content-type": "text/plain; charset=utf-8" });
19076
19113
  res.end(`${message}\n`);
19077
19114
  }
19078
- function handleUpgrade(req, clientSocket, head, opts) {
19115
+ function handleUpgrade$1(req, clientSocket, head, opts) {
19079
19116
  clientSocket.setNoDelay?.(true);
19080
19117
  if (!opts.route) {
19081
19118
  writeRawHttpError(clientSocket, 404, "Not Found");
@@ -19262,7 +19299,9 @@ function sanitizeRawHeaderValue(value) {
19262
19299
  const STATUS_TEXT$1 = {
19263
19300
  301: "Moved Permanently",
19264
19301
  302: "Found",
19302
+ 400: "Bad Request",
19265
19303
  401: "Unauthorized",
19304
+ 403: "Forbidden",
19266
19305
  404: "Not Found",
19267
19306
  502: "Bad Gateway",
19268
19307
  503: "Service Unavailable"
@@ -19516,7 +19555,7 @@ function matchBitPrefix(a, b, prefixLength) {
19516
19555
 
19517
19556
  //#endregion
19518
19557
  //#region src/local/front-door-tls.ts
19519
- const execFileAsync = promisify(execFile);
19558
+ const execFileAsync$2 = promisify(execFile);
19520
19559
  async function resolveFrontDoorTlsMaterials(opts) {
19521
19560
  const hasCert = opts.certPath !== void 0 && opts.certPath !== "";
19522
19561
  const hasKey = opts.keyPath !== void 0 && opts.keyPath !== "";
@@ -19606,7 +19645,7 @@ function readCertNotAfter(certPath) {
19606
19645
  }
19607
19646
  }
19608
19647
  async function runOpenssl(args) {
19609
- await execFileAsync("openssl", args, { timeout: 3e4 });
19648
+ await execFileAsync$2("openssl", args, { timeout: 3e4 });
19610
19649
  }
19611
19650
  function readPemOrThrow(path, flagName) {
19612
19651
  try {
@@ -21809,7 +21848,7 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
21809
21848
  if (resource.Type !== LISTENER_TYPE) continue;
21810
21849
  const props = resource.Properties ?? {};
21811
21850
  if (refOf(props["LoadBalancerArn"]) !== albLogicalId) continue;
21812
- const port = parsePort$1(props["Port"]);
21851
+ const port = parsePort$3(props["Port"]);
21813
21852
  if (port === void 0) continue;
21814
21853
  const protocol = typeof props["Protocol"] === "string" ? props["Protocol"] : "HTTP";
21815
21854
  if (protocol !== "HTTP" && protocol !== "HTTPS") {
@@ -22174,7 +22213,7 @@ function refOf(raw) {
22174
22213
  if (typeof ref === "string" && ref.length > 0) return ref;
22175
22214
  }
22176
22215
  }
22177
- function parsePort$1(raw) {
22216
+ function parsePort$3(raw) {
22178
22217
  if (typeof raw === "number" && Number.isInteger(raw) && raw >= 1 && raw <= 65535) return raw;
22179
22218
  if (typeof raw === "string" && /^\d+$/.test(raw)) {
22180
22219
  const n = parseInt(raw, 10);
@@ -22182,7 +22221,7 @@ function parsePort$1(raw) {
22182
22221
  }
22183
22222
  }
22184
22223
  function parseContainerPort(raw) {
22185
- return parsePort$1(raw);
22224
+ return parsePort$3(raw);
22186
22225
  }
22187
22226
  function parseWeight(raw) {
22188
22227
  if (typeof raw === "number" && Number.isFinite(raw)) return raw < 0 ? 0 : raw;
@@ -22276,7 +22315,7 @@ function parseLbPortOverrides(values) {
22276
22315
  function resolveAlbTarget(target, stacks) {
22277
22316
  if (stacks.length === 0) throw new LocalStartServiceError("No stacks found in the synthesized assembly.");
22278
22317
  const parsed = parseEcsTarget(target);
22279
- const stack = pickStack$1(parsed, stacks, target);
22318
+ const stack = pickStack$2(parsed, stacks, target);
22280
22319
  const resources = stack.template.Resources ?? {};
22281
22320
  if (parsed.isPath) {
22282
22321
  const index = buildCdkPathIndex(stack.template);
@@ -22298,7 +22337,7 @@ function resolveAlbTarget(target, stacks) {
22298
22337
  albLogicalId: parsed.pathOrId
22299
22338
  };
22300
22339
  }
22301
- function pickStack$1(parsed, stacks, target) {
22340
+ function pickStack$2(parsed, stacks, target) {
22302
22341
  const selection = selectTargetStack(parsed, target, stacks);
22303
22342
  switch (selection.kind) {
22304
22343
  case "found": return selection.stack;
@@ -23520,7 +23559,7 @@ async function startCloudFrontServer(options) {
23520
23559
  const s3OriginReaders = options.s3OriginReaders ?? /* @__PURE__ */ new Map();
23521
23560
  const state = { distribution: options.distribution };
23522
23561
  const handler = (req, res) => {
23523
- handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger).catch((err) => {
23562
+ handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger).catch((err) => {
23524
23563
  logger.warn(`Request handling failed: ${err instanceof Error ? err.message : String(err)}`);
23525
23564
  if (!res.headersSent) {
23526
23565
  res.statusCode = 500;
@@ -23549,7 +23588,7 @@ async function startCloudFrontServer(options) {
23549
23588
  }
23550
23589
  };
23551
23590
  }
23552
- async function handleRequest$1(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger) {
23591
+ async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger) {
23553
23592
  const distribution = state.distribution;
23554
23593
  const rawUrl = req.url ?? "/";
23555
23594
  const queryIdx = rawUrl.indexOf("?");
@@ -24213,7 +24252,7 @@ async function attachKvsModules(distribution, stacks, options, profileCredential
24213
24252
  function resolveCloudFrontTarget(target, stacks) {
24214
24253
  if (stacks.length === 0) throw new LocalStartCloudFrontError("No stacks found in the synthesized assembly.");
24215
24254
  const parsed = parseEcsTarget(target);
24216
- const stack = pickStack(parsed, stacks, target);
24255
+ const stack = pickStack$1(parsed, stacks, target);
24217
24256
  const resources = stack.template.Resources ?? {};
24218
24257
  if (parsed.isPath) {
24219
24258
  const index = buildCdkPathIndex(stack.template);
@@ -24235,7 +24274,7 @@ function resolveCloudFrontTarget(target, stacks) {
24235
24274
  logicalId: parsed.pathOrId
24236
24275
  };
24237
24276
  }
24238
- function pickStack(parsed, stacks, target) {
24277
+ function pickStack$1(parsed, stacks, target) {
24239
24278
  const selection = selectTargetStack(parsed, target, stacks);
24240
24279
  switch (selection.kind) {
24241
24280
  case "found": return selection.stack;
@@ -24858,7 +24897,7 @@ function readAuthorizationHeader(headers) {
24858
24897
 
24859
24898
  //#endregion
24860
24899
  //#region src/cli/commands/local-start-agentcore.ts
24861
- function parsePort(raw) {
24900
+ function parsePort$2(raw) {
24862
24901
  const n = Number(raw);
24863
24902
  if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
24864
24903
  return n;
@@ -25186,7 +25225,1949 @@ function createLocalStartAgentCoreCommand(opts = {}) {
25186
25225
  return cmd;
25187
25226
  }
25188
25227
  function addStartAgentCoreSpecificOptions(cmd) {
25189
- return cmd.addOption(new Option("--port <n>", "Serve bind port the client connects to (HTTP contract + /ws on the same port). Default 0 (OS-assigned).").default(0).argParser(parsePort)).addOption(new Option("--host <ip>", "Serve bind host. Default 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--session-id <id>", "Pin one AgentCore runtime session id header value for every forwarded request / /ws connection (default: a fresh random UUID each, so each is its own session).")).addOption(new Option("--bearer-token <jwt>", "Bearer JWT to present when the runtime declares a customJwtAuthorizer. Verified against the runtime's OIDC discovery URL before the container starts, then injected as Authorization: Bearer <jwt> on every forwarded request and the container /ws upgrade.")).addOption(new Option("--no-verify-auth", "Skip inbound JWT verification even when the runtime declares a customJwtAuthorizer (local-dev escape hatch). A --bearer-token, if given, is still forwarded.")).addOption(new Option("--sigv4", "Sign each forwarded request with AWS SigV4 (service bedrock-agentcore) when the runtime declares NO customJwtAuthorizer, so the warm container sees the same Authorization / X-Amz-* headers the cloud receives. Mutually exclusive with --bearer-token; ignored (with a warning) when a customJwtAuthorizer is declared.")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--platform <platform>", "docker --platform for the agent container (linux/amd64 or linux/arm64). Defaults to linux/arm64 because the cloud AgentCore Runtime requires arm64.").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).addOption(new Option("--watch", "Re-synth + reload the warm container in place on a CDK source change, keeping the serve up. A per-firing classifier picks rebuild (boot a fresh container, re-point the serve) vs soft-reload (docker cp + docker restart the same container) — the same pattern as start-service / invoke-agentcore --ws --watch.")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for the local-build path")).addOption(new Option("--no-build", "Skip docker build on the local-asset path (use the previously-built tag). No-op for the ECR / registry pull paths.")).addOption(new Option("--container-host <host>", "Host IP used to bind the agent container port. Must be a numeric IP. Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--timeout <ms>", "Maximum time in milliseconds to wait for the container to become ready (the GET /ping readiness deadline). Default 120000.").default(12e4).argParser(parseTimeoutMs)).addOption(new Option("--assume-role [arn]", "Assume the runtime's execution role and forward STS-issued temp credentials to the container. (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's literal RoleArn; (3) `--no-assume-role` opts out. Off by default.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack and substitute Ref / Fn::ImportValue in env vars / image URIs with the deployed physical IDs / exports. Bare form uses the resolved stack name.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
25228
+ return cmd.addOption(new Option("--port <n>", "Serve bind port the client connects to (HTTP contract + /ws on the same port). Default 0 (OS-assigned).").default(0).argParser(parsePort$2)).addOption(new Option("--host <ip>", "Serve bind host. Default 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--session-id <id>", "Pin one AgentCore runtime session id header value for every forwarded request / /ws connection (default: a fresh random UUID each, so each is its own session).")).addOption(new Option("--bearer-token <jwt>", "Bearer JWT to present when the runtime declares a customJwtAuthorizer. Verified against the runtime's OIDC discovery URL before the container starts, then injected as Authorization: Bearer <jwt> on every forwarded request and the container /ws upgrade.")).addOption(new Option("--no-verify-auth", "Skip inbound JWT verification even when the runtime declares a customJwtAuthorizer (local-dev escape hatch). A --bearer-token, if given, is still forwarded.")).addOption(new Option("--sigv4", "Sign each forwarded request with AWS SigV4 (service bedrock-agentcore) when the runtime declares NO customJwtAuthorizer, so the warm container sees the same Authorization / X-Amz-* headers the cloud receives. Mutually exclusive with --bearer-token; ignored (with a warning) when a customJwtAuthorizer is declared.")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--platform <platform>", "docker --platform for the agent container (linux/amd64 or linux/arm64). Defaults to linux/arm64 because the cloud AgentCore Runtime requires arm64.").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).addOption(new Option("--watch", "Re-synth + reload the warm container in place on a CDK source change, keeping the serve up. A per-firing classifier picks rebuild (boot a fresh container, re-point the serve) vs soft-reload (docker cp + docker restart the same container) — the same pattern as start-service / invoke-agentcore --ws --watch.")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for the local-build path")).addOption(new Option("--no-build", "Skip docker build on the local-asset path (use the previously-built tag). No-op for the ECR / registry pull paths.")).addOption(new Option("--container-host <host>", "Host IP used to bind the agent container port. Must be a numeric IP. Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--timeout <ms>", "Maximum time in milliseconds to wait for the container to become ready (the GET /ping readiness deadline). Default 120000.").default(12e4).argParser(parseTimeoutMs)).addOption(new Option("--assume-role [arn]", "Assume the runtime's execution role and forward STS-issued temp credentials to the container. (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's literal RoleArn; (3) `--no-assume-role` opts out. Off by default.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack and substitute Ref / Fn::ImportValue in env vars / image URIs with the deployed physical IDs / exports. Bare form uses the resolved stack name.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
25229
+ }
25230
+
25231
+ //#endregion
25232
+ //#region src/local/web-function-resolver.ts
25233
+ const WEB_FUNCTION_TYPE = "AWS::Lambda::WebFunction";
25234
+ const WEB_FUNCTION_REVISION_TYPE = "AWS::Lambda::WebFunctionRevision";
25235
+ const WEB_FUNCTION_ENDPOINT_TYPE = "AWS::Lambda::WebFunctionEndpoint";
25236
+ const WEB_TYPES = [
25237
+ WEB_FUNCTION_ENDPOINT_TYPE,
25238
+ WEB_FUNCTION_TYPE,
25239
+ WEB_FUNCTION_REVISION_TYPE
25240
+ ];
25241
+ var WebFunctionResolutionError = class WebFunctionResolutionError extends Error {
25242
+ constructor(message) {
25243
+ super(message);
25244
+ this.name = "WebFunctionResolutionError";
25245
+ Object.setPrototypeOf(this, WebFunctionResolutionError.prototype);
25246
+ }
25247
+ };
25248
+ function resolveWebFunctionTarget(target, stacks, revisionFlag) {
25249
+ if (stacks.length === 0) throw new WebFunctionResolutionError("No stacks found in the synthesized assembly.");
25250
+ const parsed = parseTarget(target);
25251
+ const stack = pickStack(parsed, stacks, target);
25252
+ const resources = stack.template.Resources ?? {};
25253
+ const { logicalId, resource } = matchWebResource(parsed, target, stack, resources);
25254
+ const functions = logicalIdsOfType(resources, WEB_FUNCTION_TYPE);
25255
+ const linked = (value) => linkedFunctionLogicalId(value, resources, functions);
25256
+ let functionLogicalId;
25257
+ let endpoint;
25258
+ let revisionLogicalId;
25259
+ let selection;
25260
+ if (resource.Type === "AWS::Lambda::WebFunctionRevision") {
25261
+ functionLogicalId = linked(resource.Properties?.["FunctionName"]);
25262
+ revisionLogicalId = logicalId;
25263
+ selection = "target-revision";
25264
+ } else if (resource.Type === "AWS::Lambda::WebFunctionEndpoint") {
25265
+ functionLogicalId = linked(resource.Properties?.["FunctionName"]);
25266
+ endpoint = readEndpoint(logicalId, resource, resources);
25267
+ } else functionLogicalId = logicalId;
25268
+ const revisionsOfFunction = () => functionLogicalId === void 0 ? [] : logicalIdsOfType(resources, WEB_FUNCTION_REVISION_TYPE).filter((id) => linked(resources[id]?.Properties?.["FunctionName"]) === functionLogicalId);
25269
+ const endpointsOfFunction = () => functionLogicalId === void 0 ? [] : logicalIdsOfType(resources, WEB_FUNCTION_ENDPOINT_TYPE).filter((id) => linked(resources[id]?.Properties?.["FunctionName"]) === functionLogicalId);
25270
+ if (revisionFlag !== void 0) {
25271
+ const flagged = resources[revisionFlag];
25272
+ if (!flagged || flagged.Type !== "AWS::Lambda::WebFunctionRevision") throw new WebFunctionResolutionError(`--revision ${displayIdent(revisionFlag)} is not an ${WEB_FUNCTION_REVISION_TYPE} logical id in ${displayStackName(stack.stackName)}. ${describeRevisions(revisionsOfFunction(), stack)}`);
25273
+ const owner = linked(flagged.Properties?.["FunctionName"]);
25274
+ if (functionLogicalId !== void 0 && owner !== functionLogicalId) throw new WebFunctionResolutionError(`--revision ${displayIdent(revisionFlag)} belongs to a different web function than ${displayIdent(logicalId)}. ${describeRevisions(revisionsOfFunction(), stack)}`);
25275
+ functionLogicalId ??= owner;
25276
+ revisionLogicalId = revisionFlag;
25277
+ selection = "revision-flag";
25278
+ }
25279
+ if (revisionLogicalId === void 0 && endpoint !== void 0) {
25280
+ revisionLogicalId = pickHighestWeight(endpoint);
25281
+ if (revisionLogicalId !== void 0) selection = "endpoint-weight";
25282
+ }
25283
+ if (revisionLogicalId === void 0) {
25284
+ const revisions = revisionsOfFunction();
25285
+ if (revisions.length === 1) {
25286
+ revisionLogicalId = revisions[0];
25287
+ selection = "only-revision";
25288
+ } else if (endpoint === void 0 && revisions.length > 1) {
25289
+ const endpoints = endpointsOfFunction();
25290
+ if (endpoints.length === 1) {
25291
+ endpoint = readEndpoint(endpoints[0], resources[endpoints[0]], resources);
25292
+ revisionLogicalId = pickHighestWeight(endpoint);
25293
+ if (revisionLogicalId !== void 0) selection = "endpoint-weight";
25294
+ }
25295
+ }
25296
+ if (revisionLogicalId === void 0) throw new WebFunctionResolutionError(revisions.length === 0 ? `${displayIdent(logicalId)} in ${displayStackName(stack.stackName)} resolves to no ${WEB_FUNCTION_REVISION_TYPE} in this template, so there is no code to serve. Target the revision itself, or pass --revision <logicalId>.` : `${displayIdent(logicalId)} has ${revisions.length} revisions and no single endpoint that picks one. Pass --revision <logicalId>, or target one of the function's endpoints. ${describeRevisions(revisions, stack)}`);
25297
+ }
25298
+ if (endpoint === void 0) {
25299
+ const routing = endpointsOfFunction().map((id) => readEndpoint(id, resources[id], resources)).filter((e) => e.weights.some((w) => w.revisionLogicalId === revisionLogicalId));
25300
+ if (routing.length === 1) endpoint = routing[0];
25301
+ }
25302
+ const fnName = (functionLogicalId !== void 0 ? resources[functionLogicalId] : void 0)?.Properties?.["FunctionName"];
25303
+ return {
25304
+ stack,
25305
+ functionLogicalId,
25306
+ functionName: typeof fnName === "string" && fnName.length > 0 ? fnName : functionLogicalId ?? revisionLogicalId,
25307
+ revisionLogicalId,
25308
+ revision: resources[revisionLogicalId],
25309
+ endpoint,
25310
+ selection: selection ?? "only-revision"
25311
+ };
25312
+ }
25313
+ function pickStack(parsed, stacks, target) {
25314
+ const selection = selectTargetStack(parsed, target, stacks);
25315
+ switch (selection.kind) {
25316
+ case "found": return selection.stack;
25317
+ case "no-prefix": throw new WebFunctionResolutionError(`Multiple stacks in app, target ${displayIdent(parsed.pathOrId)} is missing a stack prefix. Use StackName:<logicalId> or StackName/... (path form). Available stacks: ${describeStacks(stacks)}.`);
25318
+ case "not-found": throw new WebFunctionResolutionError(`Stack ${displayStackName(selection.pattern)} not found. Available stacks: ${describeStacks(stacks)}.`);
25319
+ case "ambiguous": throw new WebFunctionResolutionError(`Stack pattern ${displayStackName(selection.pattern)} matched ${selection.matched.length} stacks: ${describeStacks(selection.matched)}. Use a more specific pattern, or the stack's display path.`);
25320
+ }
25321
+ }
25322
+ function matchWebResource(parsed, target, stack, resources) {
25323
+ if (parsed.isPath) {
25324
+ const matches = resolveCdkPathToLogicalIds(parsed.pathOrId, buildCdkPathIndex(stack.template)).filter(({ logicalId }) => WEB_TYPES.includes(resources[logicalId]?.Type ?? ""));
25325
+ for (const type of WEB_TYPES) {
25326
+ const ofType = matches.filter(({ logicalId }) => resources[logicalId].Type === type);
25327
+ if (ofType.length === 1) {
25328
+ const id = ofType[0].logicalId;
25329
+ return {
25330
+ logicalId: id,
25331
+ resource: resources[id]
25332
+ };
25333
+ }
25334
+ if (ofType.length > 1) throw new WebFunctionResolutionError(`Target ${displayStackName(target)} matches ${ofType.length} ${type} resources: ${ofType.map((m) => displayIdent(m.logicalId)).join(", ")}. Refine the path or use the StackName:<logicalId> form.`);
25335
+ }
25336
+ throw notFoundError(target, stack, resources);
25337
+ }
25338
+ const resource = resources[parsed.pathOrId];
25339
+ if (!resource) throw notFoundError(target, stack, resources);
25340
+ if (!WEB_TYPES.includes(resource.Type)) throw new WebFunctionResolutionError(`Resource ${displayIdent(parsed.pathOrId)} in ${displayStackName(stack.stackName)} is ${displayIdent(resource.Type)}, not a Lambda web function. ${getEmbedConfig().cliName} start-web-function serves ${WEB_TYPES.join(" / ")} targets.`);
25341
+ return {
25342
+ logicalId: parsed.pathOrId,
25343
+ resource
25344
+ };
25345
+ }
25346
+ function notFoundError(target, stack, resources) {
25347
+ const available = WEB_TYPES.flatMap((t) => logicalIdsOfType(resources, t));
25348
+ const hint = available.length > 0 ? `Web function resources in ${displayStackName(stack.stackName)}: ${available.map((id) => displayIdent(id)).join(", ")}.` : `No ${WEB_FUNCTION_TYPE} resources found in ${displayStackName(stack.stackName)}.`;
25349
+ return new WebFunctionResolutionError(`Target ${displayStackName(target)} not found. ${hint}`);
25350
+ }
25351
+ function logicalIdsOfType(resources, type) {
25352
+ return Object.keys(resources).filter((id) => resources[id]?.Type === type);
25353
+ }
25354
+ function linkedFunctionLogicalId(value, resources, functions) {
25355
+ const ref = referencedLogicalId(value);
25356
+ if (ref !== void 0) return functions.includes(ref) ? ref : void 0;
25357
+ if (typeof value !== "string") return void 0;
25358
+ const byName = functions.filter((id) => resources[id]?.Properties?.["FunctionName"] === value);
25359
+ return byName.length === 1 ? byName[0] : void 0;
25360
+ }
25361
+ function referencedLogicalId(value) {
25362
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
25363
+ const obj = value;
25364
+ if (typeof obj["Ref"] === "string") return obj["Ref"];
25365
+ const getAtt = obj["Fn::GetAtt"];
25366
+ if (Array.isArray(getAtt) && typeof getAtt[0] === "string") return getAtt[0];
25367
+ if (typeof getAtt === "string") return getAtt.split(".")[0];
25368
+ }
25369
+ function readEndpoint(logicalId, resource, resources) {
25370
+ const props = resource.Properties ?? {};
25371
+ const rawWeights = props["RevisionWeights"];
25372
+ const weights = [];
25373
+ if (Array.isArray(rawWeights)) for (const entry of rawWeights) {
25374
+ if (!entry || typeof entry !== "object") continue;
25375
+ const e = entry;
25376
+ const ref = referencedLogicalId(e["RevisionId"]);
25377
+ weights.push({
25378
+ revisionLogicalId: ref !== void 0 && resources[ref]?.Type === "AWS::Lambda::WebFunctionRevision" ? ref : void 0,
25379
+ weight: typeof e["Weight"] === "number" ? e["Weight"] : 0
25380
+ });
25381
+ }
25382
+ const str = (v) => typeof v === "string" ? v : void 0;
25383
+ return {
25384
+ logicalId,
25385
+ endpointType: str(props["EndpointType"]),
25386
+ authType: str(props["AuthType"]),
25387
+ weights
25388
+ };
25389
+ }
25390
+ function pickHighestWeight(endpoint) {
25391
+ let best;
25392
+ for (const w of endpoint.weights) {
25393
+ if (w.revisionLogicalId === void 0) continue;
25394
+ if (best === void 0 || w.weight > best.weight) best = w;
25395
+ }
25396
+ return best?.revisionLogicalId;
25397
+ }
25398
+ function describeRevisions(revisions, stack) {
25399
+ return revisions.length > 0 ? `Revisions: ${revisions.map((id) => displayIdent(id)).join(", ")}.` : `No ${WEB_FUNCTION_REVISION_TYPE} linked to it in ${displayStackName(stack.stackName)}.`;
25400
+ }
25401
+
25402
+ //#endregion
25403
+ //#region src/local/web-function-artifact.ts
25404
+ const SUPPORTED_CODE_CONFIG_KEYS = ["S3Object"];
25405
+ function parseWebFunctionBuildConfig(revisionLogicalId, buildConfig) {
25406
+ const subject = `Web function revision ${displayIdent(revisionLogicalId)}`;
25407
+ const build = asObject$2(buildConfig);
25408
+ const runtime = asObject$2(build?.["RuntimeConfig"])?.["Runtime"];
25409
+ if (typeof runtime !== "string" || runtime.length === 0) throw new CdkLocalError(`${subject} has no literal BuildConfig.RuntimeConfig.Runtime.`, "LOCAL_START_WEB_FUNCTION_NO_RUNTIME");
25410
+ const codeConfig = asObject$2(build?.["CodeConfig"]);
25411
+ if (!codeConfig) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
25412
+ const unknownKeys = Object.keys(codeConfig).filter((k) => !SUPPORTED_CODE_CONFIG_KEYS.includes(k));
25413
+ if (unknownKeys.length > 0) throw new CdkLocalError(`${subject} uses BuildConfig.CodeConfig.${unknownKeys.map((k) => displayIdent(k)).join(", ")}, which ${getEmbedConfig().cliName} start-web-function does not support yet. Supported: ${SUPPORTED_CODE_CONFIG_KEYS.join(", ")}.`, "LOCAL_START_WEB_FUNCTION_UNSUPPORTED_CODE_CONFIG");
25414
+ const s3 = asObject$2(codeConfig["S3Object"]);
25415
+ if (!s3) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig.S3Object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
25416
+ const key = s3["Key"];
25417
+ if (typeof key !== "string" || key.length === 0) throw new CdkLocalError(`${subject} has a CodeConfig.S3Object.Key that is not a literal string (${describeValue$1(key)}). The code must come from a CDK asset in this app (for example an s3assets.Asset passing asset.s3BucketName / asset.s3ObjectKey), whose key synthesizes as a literal.`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_NOT_LITERAL");
25418
+ const versionId = s3["VersionId"];
25419
+ return {
25420
+ runtime,
25421
+ s3Object: {
25422
+ bucket: s3["Bucket"],
25423
+ key,
25424
+ ...typeof versionId === "string" && { versionId }
25425
+ }
25426
+ };
25427
+ }
25428
+ function findFileAssetForObjectKey(manifest, key) {
25429
+ for (const [hash, asset] of Object.entries(manifest.files ?? {})) for (const dest of Object.values(asset.destinations ?? {})) if (dest.objectKey === key || dest.objectKey.endsWith(`/${key}`)) return {
25430
+ hash,
25431
+ asset
25432
+ };
25433
+ const hash = key.replace(/^.*\//, "").replace(/\.zip$/, "");
25434
+ const byHash = Object.hasOwn(manifest.files ?? {}, hash) ? manifest.files[hash] : void 0;
25435
+ return byHash ? {
25436
+ hash,
25437
+ asset: byHash
25438
+ } : void 0;
25439
+ }
25440
+ async function resolveWebFunctionArtifact(stack, revisionLogicalId, code, loader = new AssetManifestLoader()) {
25441
+ const subject = `Web function revision ${displayUntrustedValue(revisionLogicalId)} code bundle`;
25442
+ const notFound = () => new CdkLocalError(`${subject} (S3Object bucket ${describeValue$1(code.s3Object.bucket)}, key ${displayUntrustedValue(code.s3Object.key)}) matches no file asset in this app's cdk.out. ${getEmbedConfig().cliName} start-web-function runs code that comes from a CDK asset in this app (for example an s3assets.Asset of a directory); it does not download deployed code. Re-synthesize the app, or point the revision at an asset.`, "LOCAL_START_WEB_FUNCTION_CODE_ASSET_NOT_FOUND");
25443
+ const manifestPath = stack.assetManifestPath;
25444
+ if (!manifestPath) throw notFound();
25445
+ const manifestDir = dirname$1(manifestPath);
25446
+ const manifest = await loader.loadStackManifest(stack, manifestDir);
25447
+ const match = manifest ? findFileAssetForObjectKey(manifest, code.s3Object.key) : void 0;
25448
+ if (!match) throw notFound();
25449
+ if (typeof match.asset.source.path !== "string" || match.asset.source.path.length === 0) throw new CdkLocalError(`${subject} matches file asset ${displayIdent(match.hash)}, which has no source.path (it is produced by source.executable at publish time), so there is no staged bundle in cdk.out to mount. Give the revision an asset of a directory or a .zip file (for example s3assets.Asset with path: '<dir>').`, "LOCAL_START_WEB_FUNCTION_CODE_ASSET_NO_PATH");
25450
+ const sourcePath = resolveAssetSourcePath({
25451
+ manifestDir,
25452
+ value: match.asset.source.path,
25453
+ assetOutdir: assetPathDirs(stack).assetOutdir,
25454
+ absolute: "honour-warn",
25455
+ field: "source.path",
25456
+ subject,
25457
+ action: "mount it",
25458
+ sink: "bind-mount that directory into the web function container",
25459
+ wrapError: (message) => new CdkLocalError(message, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_ESCAPES_ASSEMBLY")
25460
+ });
25461
+ if (!existsSync$1(sourcePath)) throw new CdkLocalError(`${subject} source ${displayUntrustedValue(sourcePath)} does not exist. Re-synthesize the app and retry.`, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_MISSING");
25462
+ if (!statSync$1(sourcePath).isDirectory() && !sourcePath.endsWith(".zip")) throw new CdkLocalError(`${subject} source ${displayUntrustedValue(sourcePath)} is neither a directory nor a .zip archive, so it is not a code bundle.`, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_NOT_BUNDLE");
25463
+ const materialized = materializeAssetCodeDir(sourcePath);
25464
+ return {
25465
+ kind: "zip",
25466
+ runtime: code.runtime,
25467
+ codeDir: materialized.dir,
25468
+ ...materialized.tmpDir !== void 0 && { tmpDir: materialized.tmpDir },
25469
+ assetHash: match.hash
25470
+ };
25471
+ }
25472
+ function disposeWebFunctionArtifact(artifact) {
25473
+ if (artifact?.kind !== "zip" || artifact.tmpDir === void 0) return;
25474
+ try {
25475
+ if (existsSync$1(artifact.tmpDir)) rmSync$1(artifact.tmpDir, {
25476
+ recursive: true,
25477
+ force: true
25478
+ });
25479
+ } catch {}
25480
+ }
25481
+ function asObject$2(value) {
25482
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
25483
+ }
25484
+ function describeValue$1(value) {
25485
+ if (typeof value === "string") return displayUntrustedValue(value);
25486
+ let json;
25487
+ try {
25488
+ json = JSON.stringify(value);
25489
+ } catch {
25490
+ json = void 0;
25491
+ }
25492
+ return displayUntrustedValue(json ?? String(value));
25493
+ }
25494
+
25495
+ //#endregion
25496
+ //#region src/local/web-function-runtime.ts
25497
+ const NODE_CODE_MOUNT = "/var/task";
25498
+ const NODE_ENTRYPOINT_ENV = "AWS_LAMBDA_NODEJS_ENTRYPOINT";
25499
+ const NODE_DEFAULT_ENTRY = "index.js";
25500
+ function buildNodeEntryFileStart(runtime, env, codeDir, fileExists = existsSync$1) {
25501
+ const configured = env[NODE_ENTRYPOINT_ENV];
25502
+ const entryFile = configured !== void 0 && configured.length > 0 ? configured : NODE_DEFAULT_ENTRY;
25503
+ const hostPath = resolve$1(codeDir, entryFile);
25504
+ const rel = relative$1(codeDir, hostPath);
25505
+ if (isAbsolute$1(entryFile) || rel === "" || rel === ".." || rel.startsWith(`..${sep$1}`) || isAbsolute$1(rel)) throw new CdkLocalError(`${NODE_ENTRYPOINT_ENV}=${displayUntrustedValue(entryFile)} must name a file inside the code bundle, relative to its root.`, "LOCAL_START_WEB_FUNCTION_ENTRYPOINT_OUTSIDE_BUNDLE");
25506
+ if (!fileExists(hostPath)) throw new CdkLocalError(`The ${runtime} web function entry file ${displayUntrustedValue(entryFile)} was not found in the code bundle ${displayUntrustedValue(codeDir)}. The runtime loads ${NODE_DEFAULT_ENTRY} from the package root unless ${NODE_ENTRYPOINT_ENV} names another file.`, "LOCAL_START_WEB_FUNCTION_ENTRYPOINT_MISSING");
25507
+ const posixRel = sep$1 === "\\" ? rel.split("\\").join("/") : rel;
25508
+ return {
25509
+ entryPoint: ["/var/lang/bin/node"],
25510
+ cmd: [posix.join(NODE_CODE_MOUNT, posixRel)],
25511
+ label: entryFile
25512
+ };
25513
+ }
25514
+ const WEB_FUNCTION_RUNTIMES = { "nodejs24.x": {
25515
+ runtime: "nodejs24.x",
25516
+ baseImage: "public.ecr.aws/lambda/nodejs:24",
25517
+ codeMountPath: NODE_CODE_MOUNT,
25518
+ architectures: ["arm64"],
25519
+ buildStart: (env, codeDir, deps) => buildNodeEntryFileStart("nodejs24.x", env, codeDir, deps?.fileExists)
25520
+ } };
25521
+ function supportedWebFunctionRuntimes(table = WEB_FUNCTION_RUNTIMES) {
25522
+ return Object.keys(table).sort();
25523
+ }
25524
+ function resolveWebFunctionRuntime(runtime, table = WEB_FUNCTION_RUNTIMES) {
25525
+ const spec = Object.hasOwn(table, runtime) ? table[runtime] : void 0;
25526
+ if (!spec) throw new CdkLocalError(`Web function runtime ${displayIdent(runtime)} is not supported by ${getEmbedConfig().cliName} start-web-function yet. Supported: ${supportedWebFunctionRuntimes(table).join(", ")}.`, "LOCAL_START_WEB_FUNCTION_UNSUPPORTED_RUNTIME");
25527
+ return spec;
25528
+ }
25529
+ function defaultWebFunctionPlatform(spec) {
25530
+ return spec.architectures[0] === "x86_64" ? "linux/amd64" : "linux/arm64";
25531
+ }
25532
+
25533
+ //#endregion
25534
+ //#region src/local/web-function-service-config.ts
25535
+ const WEB_FUNCTION_HTTP_ENDPOINT_ENV = "AWS_LAMBDA_HTTP_ENDPOINT";
25536
+ const APPLICATION_MANAGED_AUTH = "ApplicationManaged";
25537
+ const HOME_REGION_ENDPOINT = "HomeRegion";
25538
+ function readWebFunctionServiceConfig(revision) {
25539
+ const sc = asObject$1(revision.Properties?.["ServiceConfig"]) ?? {};
25540
+ const envRaw = sc["EnvironmentVariables"];
25541
+ const envObj = asObject$1(envRaw);
25542
+ const envKeys = envObj ? Object.keys(envObj) : [];
25543
+ const env = envKeys.length === 1 && (envKeys[0] === "Ref" || envKeys[0].startsWith("Fn::")) ? void 0 : envObj;
25544
+ const logGroup = asObject$1(asObject$1(sc["TelemetryConfig"])?.["LoggingConfig"])?.["LogGroup"];
25545
+ return {
25546
+ environmentVariables: env,
25547
+ environmentUnreadable: envRaw !== void 0 && env === void 0,
25548
+ executionRoleArn: sc["ExecutionRoleArn"],
25549
+ egressNetworkConnectorArn: sc["EgressNetworkConnectorArn"],
25550
+ timeoutSeconds: typeof sc["TimeoutSeconds"] === "number" ? sc["TimeoutSeconds"] : void 0,
25551
+ logGroup: typeof logGroup === "string" && logGroup.length > 0 ? logGroup : void 0
25552
+ };
25553
+ }
25554
+ function buildWebFunctionLambdaView(resolved, config, runtime) {
25555
+ return {
25556
+ kind: "zip",
25557
+ stack: resolved.stack,
25558
+ logicalId: resolved.revisionLogicalId,
25559
+ resource: {
25560
+ Type: resolved.revision.Type,
25561
+ Properties: {
25562
+ ...config.environmentVariables !== void 0 && { Environment: { Variables: config.environmentVariables } },
25563
+ ...config.executionRoleArn !== void 0 && { Role: config.executionRoleArn }
25564
+ },
25565
+ ...resolved.revision.Metadata !== void 0 && { Metadata: resolved.revision.Metadata }
25566
+ },
25567
+ memoryMb: 128,
25568
+ timeoutSec: config.timeoutSeconds ?? 0,
25569
+ layers: [],
25570
+ runtime,
25571
+ handler: "",
25572
+ codePath: null,
25573
+ architecture: "arm64"
25574
+ };
25575
+ }
25576
+ function webFunctionAssumeRole(assumeRole, config) {
25577
+ if (assumeRole === true && isIamRoleArn(config.executionRoleArn)) return config.executionRoleArn;
25578
+ return assumeRole;
25579
+ }
25580
+ function finalizeWebFunctionEnv(lambdaEnv, opts) {
25581
+ const env = { ...lambdaEnv };
25582
+ delete env["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"];
25583
+ delete env["AWS_LAMBDA_FUNCTION_VERSION"];
25584
+ delete env["AWS_LAMBDA_FUNCTION_TIMEOUT"];
25585
+ env["AWS_LAMBDA_FUNCTION_NAME"] = opts.functionName;
25586
+ env["AWS_LAMBDA_LOG_GROUP_NAME"] = opts.logGroup ?? `/aws/lambda/${opts.functionName}`;
25587
+ if (opts.timeoutSeconds !== void 0) env["AWS_LAMBDA_FUNCTION_TIMEOUT"] = String(opts.timeoutSeconds);
25588
+ const replacedHttpEndpoint = Object.hasOwn(lambdaEnv, WEB_FUNCTION_HTTP_ENDPOINT_ENV);
25589
+ env[WEB_FUNCTION_HTTP_ENDPOINT_ENV] = `0.0.0.0:${opts.containerPort}`;
25590
+ return {
25591
+ env,
25592
+ replacedHttpEndpoint
25593
+ };
25594
+ }
25595
+ function webFunctionFidelityWarnings(resolved, config) {
25596
+ const out = [];
25597
+ const rev = displayIdent(resolved.revisionLogicalId);
25598
+ if (config.egressNetworkConnectorArn !== void 0) out.push(`Revision ${rev} sets ServiceConfig.EgressNetworkConnectorArn; the network connector is not emulated, so the container reaches the network through Docker's default bridge.`);
25599
+ if (config.environmentUnreadable) out.push(`Revision ${rev} declares ServiceConfig.EnvironmentVariables as an intrinsic rather than a map; none of its variables reach the container. Supply them with --env-vars.`);
25600
+ const endpoint = resolved.endpoint;
25601
+ if (endpoint) {
25602
+ const ep = displayIdent(endpoint.logicalId);
25603
+ if (endpoint.endpointType !== void 0 && endpoint.endpointType !== "HomeRegion") out.push(`Endpoint ${ep} has EndpointType ${displayIdent(endpoint.endpointType)}; only a single ${HOME_REGION_ENDPOINT} endpoint is reproduced, on one local port.`);
25604
+ if (endpoint.authType !== void 0 && endpoint.authType !== "ApplicationManaged") out.push(`Endpoint ${ep} has AuthType ${displayIdent(endpoint.authType)}; it is not enforced locally — every request reaches the server unauthenticated.`);
25605
+ const routed = endpoint.weights.filter((w) => w.weight > 0);
25606
+ if (routed.length > 1) out.push(`Endpoint ${ep} splits traffic across ${routed.length} revisions; weighted routing is not emulated — every request goes to revision ${rev}.`);
25607
+ }
25608
+ return out;
25609
+ }
25610
+ function asObject$1(value) {
25611
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
25612
+ }
25613
+
25614
+ //#endregion
25615
+ //#region src/local/web-function-runner.ts
25616
+ const execFileAsync$1 = promisify$1(execFile$1);
25617
+ const WEB_FUNCTION_CONTAINER_PORT = 8080;
25618
+ const READY_FAILURE_LOG_LINES = 20;
25619
+ const MAX_PROBE_TIMEOUT_MS = 1e4;
25620
+ function planWebFunctionContainer(artifact, spec, env, deps) {
25621
+ if (artifact.kind !== "zip") throw new CdkLocalError("A container-image web function source is not supported yet; only a zip bundle from CodeConfig.S3Object runs locally.", "LOCAL_START_WEB_FUNCTION_UNSUPPORTED_ARTIFACT");
25622
+ const start = spec.buildStart(env, artifact.codeDir, deps);
25623
+ return {
25624
+ image: spec.baseImage,
25625
+ mounts: [{
25626
+ hostPath: artifact.codeDir,
25627
+ containerPath: spec.codeMountPath,
25628
+ readOnly: true
25629
+ }],
25630
+ entryPoint: start.entryPoint,
25631
+ cmd: start.cmd,
25632
+ workingDir: spec.codeMountPath,
25633
+ label: start.label
25634
+ };
25635
+ }
25636
+ async function startWebFunctionContainer(opts) {
25637
+ const { plan } = opts;
25638
+ return runDetached({
25639
+ image: plan.image,
25640
+ mounts: plan.mounts,
25641
+ env: opts.env,
25642
+ ...opts.sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys: opts.sensitiveEnvKeys },
25643
+ entryPoint: plan.entryPoint,
25644
+ cmd: plan.cmd,
25645
+ ...plan.workingDir !== void 0 && { workingDir: plan.workingDir },
25646
+ hostPort: opts.hostPort,
25647
+ containerPort: WEB_FUNCTION_CONTAINER_PORT,
25648
+ host: opts.host,
25649
+ platform: opts.platform,
25650
+ name: opts.name,
25651
+ autoRemove: false
25652
+ });
25653
+ }
25654
+ var RecentOutput = class {
25655
+ lines = [];
25656
+ partial = "";
25657
+ max;
25658
+ constructor(max = 20) {
25659
+ this.max = max;
25660
+ }
25661
+ push(chunk) {
25662
+ const parts = (this.partial + chunk.toString()).split("\n");
25663
+ this.partial = parts.pop() ?? "";
25664
+ for (const line of parts) this.add(line);
25665
+ if (this.partial.length > 4096) {
25666
+ this.add(this.partial);
25667
+ this.partial = "";
25668
+ }
25669
+ }
25670
+ tail() {
25671
+ return this.partial.length > 0 ? [...this.lines, this.partial].slice(-this.max) : this.lines;
25672
+ }
25673
+ add(line) {
25674
+ this.lines.push(line.replace(/\r$/, ""));
25675
+ if (this.lines.length > this.max) this.lines.shift();
25676
+ }
25677
+ };
25678
+ async function waitForWebFunctionReady(args, deps = defaultReadyDeps) {
25679
+ const deadline = deps.now() + args.timeoutMs;
25680
+ let attempt = 0;
25681
+ let last = "refused";
25682
+ while (deps.now() < deadline) {
25683
+ const remaining = Math.max(1, deadline - deps.now());
25684
+ last = await deps.probe(args.host, args.port, Math.min(remaining, MAX_PROBE_TIMEOUT_MS));
25685
+ if (last === "answered") return;
25686
+ attempt++;
25687
+ if (attempt % 4 === 0 && !await deps.isRunning(args.containerId)) throw await containerFailure(`The web function container exited before its server listened on AWS_LAMBDA_HTTP_ENDPOINT (started ${args.label}).`, args.containerId, args.recent, deps.fetchLogs);
25688
+ if (last === "refused") await deps.sleep(250);
25689
+ }
25690
+ const authority = formatAuthority(args.host, args.port);
25691
+ throw await containerFailure(last === "timeout" ? `The web function server on ${authority} accepted the connection but did not answer GET / within ${args.timeoutMs}ms — it is listening but slow to respond. Raise --timeout, or make / answer sooner (started ${args.label}).` : `The web function server never accepted an HTTP connection on ${authority} within ${args.timeoutMs}ms. It must listen on the host:port given in AWS_LAMBDA_HTTP_ENDPOINT (started ${args.label}).`, args.containerId, args.recent, deps.fetchLogs);
25692
+ }
25693
+ async function containerFailure(message, containerId, recent, fetchLogs = readContainerLogTail, code = "LOCAL_START_WEB_FUNCTION_NOT_READY") {
25694
+ let lines = [];
25695
+ try {
25696
+ lines = await fetchLogs(containerId, 20);
25697
+ } catch {
25698
+ lines = [];
25699
+ }
25700
+ if (lines.length === 0) lines = recent.tail();
25701
+ return new CdkLocalError(`${message}${lines.length > 0 ? ` Last ${lines.length} line(s) of container output:\n${lines.map((l) => ` ${displayToolOutput(l)}`).join("\n")}` : " The container printed nothing."}`, code);
25702
+ }
25703
+ function watchContainerExit(containerId) {
25704
+ let stopped = false;
25705
+ const proc = spawn$1(getDockerCmd(), ["wait", containerId], { stdio: [
25706
+ "ignore",
25707
+ "pipe",
25708
+ "ignore"
25709
+ ] });
25710
+ let out = "";
25711
+ proc.stdout?.on("data", (c) => out += c.toString());
25712
+ return {
25713
+ exited: new Promise((resolveExit) => {
25714
+ const done = () => {
25715
+ if (stopped) return;
25716
+ const code = Number.parseInt(out.trim(), 10);
25717
+ resolveExit(Number.isNaN(code) ? void 0 : code);
25718
+ };
25719
+ proc.on("close", done);
25720
+ proc.on("error", done);
25721
+ }),
25722
+ stop: () => {
25723
+ stopped = true;
25724
+ if (!proc.killed) proc.kill("SIGTERM");
25725
+ }
25726
+ };
25727
+ }
25728
+ function probeHttp(host, port, timeoutMs) {
25729
+ return new Promise((resolveProbe) => {
25730
+ let timedOut = false;
25731
+ let connected = false;
25732
+ const req = request$2({
25733
+ host,
25734
+ port,
25735
+ path: "/",
25736
+ method: "GET",
25737
+ timeout: timeoutMs,
25738
+ headers: { connection: "close" }
25739
+ }, (res) => {
25740
+ res.resume();
25741
+ resolveProbe("answered");
25742
+ });
25743
+ req.on("socket", (socket) => {
25744
+ socket.once("connect", () => {
25745
+ connected = true;
25746
+ });
25747
+ });
25748
+ req.on("timeout", () => {
25749
+ timedOut = true;
25750
+ req.destroy();
25751
+ });
25752
+ req.on("error", () => resolveProbe(timedOut && connected ? "timeout" : "refused"));
25753
+ req.end();
25754
+ });
25755
+ }
25756
+ async function containerIsRunning(containerId) {
25757
+ try {
25758
+ const { stdout } = await execFileAsync$1(getDockerCmd(), [
25759
+ "inspect",
25760
+ "-f",
25761
+ "{{.State.Running}}",
25762
+ containerId
25763
+ ]);
25764
+ return stdout.trim() === "true";
25765
+ } catch {
25766
+ return false;
25767
+ }
25768
+ }
25769
+ function readContainerLogTail(containerId, lines, spawnFn = spawn$1) {
25770
+ return new Promise((resolveLogs, rejectLogs) => {
25771
+ const proc = spawnFn(getDockerCmd(), [
25772
+ "logs",
25773
+ "--tail",
25774
+ String(lines),
25775
+ containerId
25776
+ ], { stdio: [
25777
+ "ignore",
25778
+ "pipe",
25779
+ "pipe"
25780
+ ] });
25781
+ const chunks = [];
25782
+ let size = 0;
25783
+ const take = (c) => {
25784
+ if (size > 4 * 1024 * 1024) return;
25785
+ chunks.push(c);
25786
+ size += c.length;
25787
+ };
25788
+ proc.stdout?.on("data", take);
25789
+ proc.stderr?.on("data", take);
25790
+ proc.on("error", rejectLogs);
25791
+ proc.on("close", (code) => {
25792
+ if (code !== 0) {
25793
+ rejectLogs(/* @__PURE__ */ new Error(`docker logs exited ${code}`));
25794
+ return;
25795
+ }
25796
+ resolveLogs(Buffer.concat(chunks).toString().split("\n").map((l) => l.replace(/\r$/, "")).filter((l) => l.length > 0).slice(-lines));
25797
+ });
25798
+ });
25799
+ }
25800
+ const defaultReadyDeps = {
25801
+ probe: probeHttp,
25802
+ isRunning: containerIsRunning,
25803
+ fetchLogs: readContainerLogTail,
25804
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
25805
+ now: () => Date.now()
25806
+ };
25807
+
25808
+ //#endregion
25809
+ //#region src/cli/commands/local-start-web-function.ts
25810
+ function parsePort$1(raw) {
25811
+ const n = Number(raw);
25812
+ if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
25813
+ return n;
25814
+ }
25815
+ function connectHost$1(host) {
25816
+ if (host === "0.0.0.0") return "127.0.0.1";
25817
+ if (host === "::" || host === "[::]") return "::1";
25818
+ return host;
25819
+ }
25820
+ async function localStartWebFunctionCommand(target, options, extraStateProviders, hostSynthesizer) {
25821
+ const logger = getLogger();
25822
+ if (options.verbose) logger.setLevel("debug");
25823
+ let containerId;
25824
+ let stopLogs;
25825
+ let exitWatch;
25826
+ let artifact;
25827
+ let runInFlight;
25828
+ let teardownPromise;
25829
+ let shuttingDown = false;
25830
+ let waitingOnRun = false;
25831
+ let containerName;
25832
+ const forever = () => new Promise(() => void 0);
25833
+ const doTeardown = async () => {
25834
+ exitWatch?.stop();
25835
+ if (containerId === void 0 && runInFlight !== void 0) {
25836
+ waitingOnRun = true;
25837
+ try {
25838
+ containerId = await runInFlight;
25839
+ } catch {} finally {
25840
+ waitingOnRun = false;
25841
+ }
25842
+ }
25843
+ if (stopLogs) try {
25844
+ stopLogs();
25845
+ } catch (err) {
25846
+ logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
25847
+ }
25848
+ if (containerId) await removeContainer(containerId);
25849
+ disposeWebFunctionArtifact(artifact);
25850
+ };
25851
+ const teardown = () => teardownPromise ??= doTeardown();
25852
+ await applyRoleArnIfSet({
25853
+ roleArn: options.roleArn,
25854
+ region: options.region,
25855
+ profile: options.profile
25856
+ });
25857
+ await ensureDockerAvailable();
25858
+ const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
25859
+ const appCmd = resolveApp(options.app);
25860
+ if (!appCmd) throw new Error(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
25861
+ logger.info("Synthesizing CDK app...");
25862
+ const synthesizer = hostSynthesizer ?? new Synthesizer();
25863
+ const context = parseContextOptions(options.context);
25864
+ const synthOpts = {
25865
+ app: appCmd,
25866
+ output: options.output,
25867
+ ...options.region && { region: options.region },
25868
+ ...options.profile && { profile: options.profile },
25869
+ ...Object.keys(context).length > 0 && { context }
25870
+ };
25871
+ const { stacks } = await synthesizer.synthesize(synthOpts);
25872
+ const resolved = resolveWebFunctionTarget(await resolveSingleTarget(target, {
25873
+ entries: listTargets(stacks).webFunctions,
25874
+ message: "Select a Lambda web function to serve",
25875
+ noun: "Lambda web functions",
25876
+ onMissing: () => new CdkLocalError(`${getEmbedConfig().cliName} start-web-function requires a <target> (a WebFunction, WebFunctionEndpoint or WebFunctionRevision display path or logical ID). Run \`${getEmbedConfig().cliName} list\` to see them, or run it in a TTY to pick interactively.`, "LOCAL_START_WEB_FUNCTION_TARGET_REQUIRED")
25877
+ }), stacks, options.revision);
25878
+ const code = parseWebFunctionBuildConfig(resolved.revisionLogicalId, resolved.revision.Properties?.["BuildConfig"]);
25879
+ const spec = resolveWebFunctionRuntime(code.runtime);
25880
+ const selectionNote = {
25881
+ "target-revision": "the target revision",
25882
+ "revision-flag": "--revision",
25883
+ "endpoint-weight": `highest weight on endpoint ${displayIdent(resolved.endpoint?.logicalId ?? "")}`,
25884
+ "only-revision": "the function's only revision"
25885
+ };
25886
+ logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.revisionLogicalId}`)} (${spec.runtime}; revision chosen by ${selectionNote[resolved.selection]})`);
25887
+ const serviceConfig = readWebFunctionServiceConfig(resolved.revision);
25888
+ for (const line of webFunctionFidelityWarnings(resolved, serviceConfig)) logger.warn(line);
25889
+ artifact = await resolveWebFunctionArtifact(resolved.stack, resolved.revisionLogicalId, code);
25890
+ const shutdown = async (signal, exitCode) => {
25891
+ if (shuttingDown) {
25892
+ if (!waitingOnRun) {
25893
+ logger.info(`Received ${signal} again; still removing the container, please wait...`);
25894
+ return;
25895
+ }
25896
+ disposeWebFunctionArtifact(artifact);
25897
+ logger.warn(`Received ${signal} again; exiting without waiting for docker run to return. The container may still be starting, and its environment holds the forwarded AWS credentials. Remove it by its name (this works even if it is created after this exit): docker rm -f ${containerName ?? "<name>"}`);
25898
+ process.exit(signal === "SIGINT" ? 130 : 143);
25899
+ return;
25900
+ }
25901
+ shuttingDown = true;
25902
+ logger.info(`Received ${signal}, shutting down...`);
25903
+ await teardown();
25904
+ process.exit(exitCode);
25905
+ };
25906
+ process.on("SIGINT", () => void shutdown("SIGINT", 130));
25907
+ process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
25908
+ const recent = new RecentOutput();
25909
+ try {
25910
+ const assumeRole = webFunctionAssumeRole(options.assumeRole, serviceConfig);
25911
+ const containerEnv = await resolveLambdaContainerEnv(buildWebFunctionLambdaView(resolved, serviceConfig, spec.runtime), {
25912
+ ...options,
25913
+ ...assumeRole !== void 0 && { assumeRole },
25914
+ deployedFunctionLookups: false
25915
+ }, profileCredentials, extraStateProviders);
25916
+ const { env, replacedHttpEndpoint } = finalizeWebFunctionEnv(containerEnv.env, {
25917
+ functionName: resolved.functionName,
25918
+ containerPort: WEB_FUNCTION_CONTAINER_PORT,
25919
+ timeoutSeconds: serviceConfig.timeoutSeconds,
25920
+ logGroup: serviceConfig.logGroup
25921
+ });
25922
+ if (replacedHttpEndpoint) logger.warn(`The revision's ${WEB_FUNCTION_HTTP_ENDPOINT_ENV} is replaced with 0.0.0.0:${WEB_FUNCTION_CONTAINER_PORT}: the runtime owns that variable, and the local port is published to it.`);
25923
+ const plan = planWebFunctionContainer(artifact, spec, env);
25924
+ const platform = options.platform ?? defaultWebFunctionPlatform(spec);
25925
+ await pullImage(plan.image, options.pull === false, platform);
25926
+ if (teardownPromise) return await forever();
25927
+ const hostPort = options.port === 0 ? await pickFreePort() : options.port;
25928
+ const name = containerName = `${getEmbedConfig().resourceNamePrefix}-webfn-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
25929
+ logger.info(`Starting web function container (image=${plan.image}, start=${plan.label}, port=${hostPort} -> ${WEB_FUNCTION_CONTAINER_PORT})...`);
25930
+ runInFlight = startWebFunctionContainer({
25931
+ plan,
25932
+ env,
25933
+ sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys),
25934
+ host: options.host,
25935
+ hostPort,
25936
+ platform,
25937
+ name
25938
+ });
25939
+ const id = await runInFlight;
25940
+ if (teardownPromise) return await forever();
25941
+ containerId = id;
25942
+ stopLogs = streamLogs(id, { onOutput: (chunk) => recent.push(chunk) });
25943
+ exitWatch = watchContainerExit(id);
25944
+ const probeHost = connectHost$1(options.host);
25945
+ await waitForWebFunctionReady({
25946
+ containerId: id,
25947
+ host: probeHost,
25948
+ port: hostPort,
25949
+ timeoutMs: options.timeout,
25950
+ recent,
25951
+ label: plan.label
25952
+ });
25953
+ logger.info(`Server listening on http://${formatAuthority(probeHost, hostPort)} (${displayIdent(resolved.functionName)} (Lambda web function))`);
25954
+ logger.info("Press ^C to shut down.");
25955
+ } catch (err) {
25956
+ if (shuttingDown) return await forever();
25957
+ await teardown();
25958
+ throw err;
25959
+ }
25960
+ if (!exitWatch || !containerId) return await forever();
25961
+ const exitCode = await exitWatch.exited;
25962
+ if (shuttingDown || teardownPromise) return await forever();
25963
+ const failure = await containerFailure(`The web function container exited (exit code ${exitCode ?? "unknown"}) while serving.`, containerId, recent, void 0, "LOCAL_START_WEB_FUNCTION_EXITED");
25964
+ await teardown();
25965
+ throw failure;
25966
+ }
25967
+ function createLocalStartWebFunctionCommand(opts = {}) {
25968
+ setEmbedConfig(opts.embedConfig);
25969
+ const cmd = new Command("start-web-function").description("Serve an AWS Lambda Web Function locally. Resolves a WebFunction, WebFunctionEndpoint or WebFunctionRevision target to one revision (an endpoint serves its highest-weight revision; --revision overrides), runs its code bundle from cdk.out in a container whose server listens on AWS_LAMBDA_HTTP_ENDPOINT, and publishes it on a local port with the revision's ServiceConfig environment. Target accepts a CDK display path or stack-qualified logical ID; single-stack apps may omit the prefix. Omit <target> in a TTY to pick from a list. Runs until ^C.").argument("[target]", "CDK display path or stack-qualified logical ID of the WebFunction / WebFunctionEndpoint / WebFunctionRevision (omit to pick interactively in a TTY)").action(withErrorHandling(async (target, options) => {
25970
+ await localStartWebFunctionCommand(target, options, opts.extraStateProviders, opts.synthesizer);
25971
+ }));
25972
+ addStartWebFunctionSpecificOptions(cmd);
25973
+ [
25974
+ ...commonOptions(),
25975
+ ...appOptions(),
25976
+ ...contextOptions
25977
+ ].forEach((opt) => cmd.addOption(opt));
25978
+ cmd.addOption(regionOption);
25979
+ return cmd;
25980
+ }
25981
+ function addStartWebFunctionSpecificOptions(cmd) {
25982
+ return cmd.addOption(new Option("--revision <logicalId>", "Serve this WebFunctionRevision (logical ID) instead of the one the endpoint weights pick.")).addOption(new Option("--port <n>", "Local port to serve on. Default 0 (OS-assigned).").default(0).argParser(parsePort$1)).addOption(new Option("--host <ip>", "Address to publish the port on.").default("127.0.0.1")).addOption(new Option("--platform <platform>", "docker --platform for the server container. Defaults to the runtime's cloud architecture (linux/arm64 for nodejs24.x).").choices(["linux/amd64", "linux/arm64"])).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"RevisionLogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--no-pull", "Skip docker pull of the runtime base image (use the cached image)")).addOption(new Option("--timeout <ms>", "Maximum time in milliseconds to wait for the server to answer HTTP. Default 60000.").default(6e4).argParser(parseTimeoutMs)).addOption(new Option("--assume-role [arn]", "Assume the revision's execution role and forward STS-issued temp credentials to the container. (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the revision's ServiceConfig.ExecutionRoleArn: a literal ARN as written, or the Fn::GetAtt / Ref CDK synthesizes resolved against --from-cfn-stack deployed state; (3) `--no-assume-role` opts out. Off by default.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Bare form uses the resolved stack name.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
25983
+ }
25984
+
25985
+ //#endregion
25986
+ //#region src/local/microvm-image.ts
25987
+ const MICROVM_IMAGE_TYPE = "AWS::Lambda::MicrovmImage";
25988
+ const MICROVM_DEFAULT_INGRESS_PORT = 8080;
25989
+ var MicrovmResolutionError = class MicrovmResolutionError extends Error {
25990
+ constructor(message) {
25991
+ super(message);
25992
+ this.name = "MicrovmResolutionError";
25993
+ Object.setPrototypeOf(this, MicrovmResolutionError.prototype);
25994
+ }
25995
+ };
25996
+ function resolveMicrovmImageTarget(target, stacks) {
25997
+ if (stacks.length === 0) throw new MicrovmResolutionError("No stacks found in the synthesized assembly.");
25998
+ const parsed = parseTarget(target);
25999
+ const selection = selectTargetStack(parsed, target, stacks);
26000
+ let stack;
26001
+ switch (selection.kind) {
26002
+ case "found":
26003
+ stack = selection.stack;
26004
+ break;
26005
+ case "no-prefix": throw new MicrovmResolutionError(`Multiple stacks in app, target ${displayIdent(parsed.pathOrId)} is missing a stack prefix. Use StackName:<logicalId> or StackName/... (path form). Available stacks: ${describeStacks(stacks)}.`);
26006
+ case "not-found": throw new MicrovmResolutionError(`Stack ${displayStackName(selection.pattern)} not found. Available stacks: ${describeStacks(stacks)}.`);
26007
+ case "ambiguous": throw new MicrovmResolutionError(`Stack pattern ${displayStackName(selection.pattern)} matched ${selection.matched.length} stacks: ${describeStacks(selection.matched)}. Use a more specific pattern, or the stack's display path.`);
26008
+ }
26009
+ const resources = stack.template.Resources ?? {};
26010
+ const images = Object.keys(resources).filter((id) => resources[id]?.Type === MICROVM_IMAGE_TYPE);
26011
+ const notFound = () => new MicrovmResolutionError(`Target ${displayStackName(target)} not found. ` + (images.length > 0 ? `MicroVM images in ${displayStackName(stack.stackName)}: ${images.map((id) => displayIdent(id)).join(", ")}.` : `No ${MICROVM_IMAGE_TYPE} resources found in ${displayStackName(stack.stackName)}.`));
26012
+ if (parsed.isPath) {
26013
+ const matches = resolveCdkPathToLogicalIds(parsed.pathOrId, buildCdkPathIndex(stack.template)).filter(({ logicalId }) => resources[logicalId]?.Type === MICROVM_IMAGE_TYPE);
26014
+ if (matches.length === 1) {
26015
+ const id = matches[0].logicalId;
26016
+ return {
26017
+ stack,
26018
+ logicalId: id,
26019
+ resource: resources[id]
26020
+ };
26021
+ }
26022
+ if (matches.length > 1) throw new MicrovmResolutionError(`Target ${displayStackName(target)} matches ${matches.length} ${MICROVM_IMAGE_TYPE} resources: ${matches.map((m) => displayIdent(m.logicalId)).join(", ")}. Refine the path or use the StackName:<logicalId> form.`);
26023
+ throw notFound();
26024
+ }
26025
+ const resource = resources[parsed.pathOrId];
26026
+ if (!resource) throw notFound();
26027
+ if (resource.Type !== "AWS::Lambda::MicrovmImage") throw new MicrovmResolutionError(`Resource ${displayIdent(parsed.pathOrId)} in ${displayStackName(stack.stackName)} is ${displayIdent(resource.Type)}, not a MicroVM image. ${getEmbedConfig().cliName} start-microvm runs ${MICROVM_IMAGE_TYPE} targets.`);
26028
+ return {
26029
+ stack,
26030
+ logicalId: parsed.pathOrId,
26031
+ resource
26032
+ };
26033
+ }
26034
+ const HOOK_KEYS = {
26035
+ ready: {
26036
+ group: "MicrovmImageHooks",
26037
+ key: "Ready"
26038
+ },
26039
+ validate: {
26040
+ group: "MicrovmImageHooks",
26041
+ key: "Validate"
26042
+ },
26043
+ run: {
26044
+ group: "MicrovmHooks",
26045
+ key: "Run"
26046
+ },
26047
+ suspend: {
26048
+ group: "MicrovmHooks",
26049
+ key: "Suspend"
26050
+ },
26051
+ resume: {
26052
+ group: "MicrovmHooks",
26053
+ key: "Resume"
26054
+ },
26055
+ terminate: {
26056
+ group: "MicrovmHooks",
26057
+ key: "Terminate"
26058
+ }
26059
+ };
26060
+ function readMicrovmImageConfig(logicalId, resource) {
26061
+ const subject = `MicroVM image ${displayIdent(logicalId)}`;
26062
+ const props = resource.Properties ?? {};
26063
+ const name = typeof props["Name"] === "string" && props["Name"].length > 0 ? props["Name"] : logicalId;
26064
+ const uri = asObject(props["CodeArtifact"])?.["Uri"];
26065
+ const codeKey = s3KeyOfUri(uri);
26066
+ if (codeKey === void 0) throw new CdkLocalError(`${subject} has a CodeArtifact.Uri whose object key is not a literal (${describeValue(uri)}). The code must come from a CDK asset in this app (for example an s3assets.Asset of a directory holding the Dockerfile, passing asset.s3ObjectUrl), whose key synthesizes as a literal.`, "LOCAL_START_MICROVM_CODE_URI_NOT_LITERAL");
26067
+ const cpu = props["CpuConfigurations"];
26068
+ const archRaw = Array.isArray(cpu) ? asObject(cpu[0])?.["Architecture"] : void 0;
26069
+ const architecture = typeof archRaw === "string" ? normalizeArchitecture(archRaw) : void 0;
26070
+ const environment = {};
26071
+ const envRaw = props["EnvironmentVariables"];
26072
+ const unemulated = [];
26073
+ if (Array.isArray(envRaw)) for (const entry of envRaw) {
26074
+ const e = asObject(entry);
26075
+ if (e && typeof e["Key"] === "string") environment[e["Key"]] = e["Value"];
26076
+ }
26077
+ else if (envRaw !== void 0) unemulated.push("EnvironmentVariables is an intrinsic rather than a list, so none of its variables reach the container (supply them with --env-vars)");
26078
+ const hooksObj = asObject(props["Hooks"]);
26079
+ const hooks = {};
26080
+ for (const [hook, where] of Object.entries(HOOK_KEYS)) {
26081
+ const group = asObject(hooksObj?.[where.group]);
26082
+ const value = group?.[where.key];
26083
+ if (value === void 0 || value === "DISABLED") continue;
26084
+ if (value !== "ENABLED") throw new CdkLocalError(`${subject} has Hooks.${where.group}.${where.key} ${describeValue(value)}; only the literal ENABLED or DISABLED can run locally.`, "LOCAL_START_MICROVM_HOOK_VALUE");
26085
+ const timeout = group?.[`${where.key}TimeoutInSeconds`];
26086
+ hooks[hook] = { timeoutSeconds: typeof timeout === "number" && timeout > 0 ? timeout : void 0 };
26087
+ }
26088
+ const port = hooksObj?.["Port"];
26089
+ const hooksPort = typeof port === "number" && Number.isInteger(port) && port > 0 && port < 65536 ? port : void 0;
26090
+ if (Object.keys(hooks).length > 0 && hooksPort === void 0) throw new CdkLocalError(`${subject} enables lifecycle hooks (${Object.keys(hooks).join(", ")}) but has no literal Hooks.Port. Lambda requires the hooks port whenever a hook is configured; set Hooks.Port to the port the application serves /aws/lambda-microvms/runtime/v1/ on.`, "LOCAL_START_MICROVM_NO_HOOKS_PORT");
26091
+ const present = (key) => {
26092
+ const v = props[key];
26093
+ if (v === void 0 || v === null) return false;
26094
+ if (Array.isArray(v)) return v.length > 0;
26095
+ if (typeof v === "object") return Object.keys(v).length > 0;
26096
+ return true;
26097
+ };
26098
+ if (present("EgressNetworkConnectors")) unemulated.push("EgressNetworkConnectors (the container uses Docker's default network egress)");
26099
+ if (present("AdditionalOsCapabilities")) unemulated.push("AdditionalOsCapabilities (the container runs with Docker's default capabilities)");
26100
+ if (present("Resources")) unemulated.push("Resources sizing (the container gets no memory limit)");
26101
+ if (present("BaseImageVersion")) unemulated.push("BaseImageVersion (the local build uses whatever the Dockerfile names)");
26102
+ if (present("Logging")) unemulated.push("Logging (container output is printed here instead of sent to CloudWatch Logs)");
26103
+ return {
26104
+ logicalId,
26105
+ name,
26106
+ codeKey,
26107
+ architecture,
26108
+ environment,
26109
+ hooksPort,
26110
+ hooks,
26111
+ unemulated
26112
+ };
26113
+ }
26114
+ function normalizeArchitecture(raw) {
26115
+ const s = raw.toLowerCase().replace(/_/g, "");
26116
+ if (s === "arm64") return "arm64";
26117
+ if (s === "x8664") return "x86_64";
26118
+ }
26119
+ function s3KeyOfUri(value) {
26120
+ const flat = flattenTemplateString(value);
26121
+ if (flat === void 0) return void 0;
26122
+ const m = /^s3:\/\/[^/]*\/(.+)$/.exec(flat);
26123
+ if (!m) return void 0;
26124
+ const key = m[1];
26125
+ return key.includes("\0") || key.includes("${") ? void 0 : key;
26126
+ }
26127
+ function flattenTemplateString(value) {
26128
+ if (typeof value === "string") return value;
26129
+ const obj = asObject(value);
26130
+ if (!obj) return void 0;
26131
+ const join = obj["Fn::Join"];
26132
+ if (Array.isArray(join) && typeof join[0] === "string" && Array.isArray(join[1])) return join[1].map((p) => flattenTemplateString(p) ?? "\0").join(join[0]);
26133
+ const sub = obj["Fn::Sub"];
26134
+ if (typeof sub === "string") return sub.replace(/\$\{[^}]*\}/g, "\0");
26135
+ if (Array.isArray(sub) && typeof sub[0] === "string") return sub[0].replace(/\$\{[^}]*\}/g, "\0");
26136
+ return "\0";
26137
+ }
26138
+ async function resolveMicrovmCodeArtifact(stack, config, loader = new AssetManifestLoader()) {
26139
+ const subject = `MicroVM image ${displayUntrustedValue(config.logicalId)} code artifact`;
26140
+ const notFound = () => new CdkLocalError(`${subject} (key ${displayUntrustedValue(config.codeKey)}) matches no file asset in this app's cdk.out. ${getEmbedConfig().cliName} start-microvm builds code that comes from a CDK asset in this app (for example an s3assets.Asset of a directory holding the Dockerfile); it does not download deployed code. Re-synthesize the app, or point CodeArtifact.Uri at an asset.`, "LOCAL_START_MICROVM_CODE_ASSET_NOT_FOUND");
26141
+ const manifestPath = stack.assetManifestPath;
26142
+ if (!manifestPath) throw notFound();
26143
+ const manifestDir = dirname$1(manifestPath);
26144
+ const manifest = await loader.loadStackManifest(stack, manifestDir);
26145
+ const match = manifest ? findFileAssetForObjectKey(manifest, config.codeKey) : void 0;
26146
+ if (!match) throw notFound();
26147
+ if (typeof match.asset.source.path !== "string" || match.asset.source.path.length === 0) throw new CdkLocalError(`${subject} matches file asset ${displayIdent(match.hash)}, which has no source.path (it is produced by source.executable at publish time), so there is no staged bundle in cdk.out to build. Give the image an asset of a directory or a .zip file.`, "LOCAL_START_MICROVM_CODE_ASSET_NO_PATH");
26148
+ const sourcePath = resolveAssetSourcePath({
26149
+ manifestDir,
26150
+ value: match.asset.source.path,
26151
+ assetOutdir: assetPathDirs(stack).assetOutdir,
26152
+ absolute: "honour-warn",
26153
+ field: "source.path",
26154
+ subject,
26155
+ action: "build it",
26156
+ sink: "send that directory to docker build as the MicroVM image context",
26157
+ wrapError: (message) => new CdkLocalError(message, "LOCAL_START_MICROVM_CODE_SOURCE_ESCAPES_ASSEMBLY")
26158
+ });
26159
+ if (!existsSync$1(sourcePath)) throw new CdkLocalError(`${subject} source ${displayUntrustedValue(sourcePath)} does not exist. Re-synthesize the app and retry.`, "LOCAL_START_MICROVM_CODE_SOURCE_MISSING");
26160
+ if (!statSync$1(sourcePath).isDirectory() && !sourcePath.endsWith(".zip")) throw new CdkLocalError(`${subject} source ${displayUntrustedValue(sourcePath)} is neither a directory nor a .zip archive, so it is not a code bundle.`, "LOCAL_START_MICROVM_CODE_SOURCE_NOT_BUNDLE");
26161
+ const materialized = materializeAssetCodeDir(sourcePath);
26162
+ const artifact = {
26163
+ contextDir: materialized.dir,
26164
+ ...materialized.tmpDir !== void 0 && { tmpDir: materialized.tmpDir },
26165
+ assetHash: match.hash
26166
+ };
26167
+ if (!existsSync$1(join$1(materialized.dir, "Dockerfile"))) {
26168
+ disposeMicrovmCodeArtifact(artifact);
26169
+ throw new CdkLocalError(`${subject} has no Dockerfile at its root. A MicroVM image's code artifact is a zip with a Dockerfile at the root plus the application files.`, "LOCAL_START_MICROVM_NO_DOCKERFILE");
26170
+ }
26171
+ return artifact;
26172
+ }
26173
+ function disposeMicrovmCodeArtifact(artifact) {
26174
+ if (artifact?.tmpDir === void 0) return;
26175
+ try {
26176
+ if (existsSync$1(artifact.tmpDir)) rmSync$1(artifact.tmpDir, {
26177
+ recursive: true,
26178
+ force: true
26179
+ });
26180
+ } catch {}
26181
+ }
26182
+ function asObject(value) {
26183
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
26184
+ }
26185
+ function describeValue(value) {
26186
+ if (typeof value === "string") return displayUntrustedValue(value);
26187
+ let json;
26188
+ try {
26189
+ json = JSON.stringify(value);
26190
+ } catch {
26191
+ json = void 0;
26192
+ }
26193
+ return displayUntrustedValue(json ?? String(value));
26194
+ }
26195
+
26196
+ //#endregion
26197
+ //#region src/local/microvm-hooks.ts
26198
+ const MICROVM_HOOK_PATH_PREFIX = "/aws/lambda-microvms/runtime/v1/";
26199
+ const MAX_RUN_HOOK_PAYLOAD_BYTES = 16 * 1024;
26200
+ const HOOK_RETRY_INTERVAL_MS = 250;
26201
+ var MicrovmHookError = class MicrovmHookError extends CdkLocalError {
26202
+ constructor(message, code = "LOCAL_START_MICROVM_HOOK_FAILED") {
26203
+ super(message, code);
26204
+ this.name = "MicrovmHookError";
26205
+ Object.setPrototypeOf(this, MicrovmHookError.prototype);
26206
+ }
26207
+ };
26208
+ function generateMicrovmId() {
26209
+ return `mvm-${randomUUID$1()}`;
26210
+ }
26211
+ function runHookBody(microvmId, runHookPayload) {
26212
+ return JSON.stringify({
26213
+ microvmId,
26214
+ ...runHookPayload !== void 0 && { runHookPayload }
26215
+ });
26216
+ }
26217
+ async function pollBuildHook(hook, endpoint, timeoutMs, deps = defaultHookDeps) {
26218
+ const path = `${MICROVM_HOOK_PATH_PREFIX}${hook}`;
26219
+ const deadline = deps.now() + timeoutMs;
26220
+ let attempts = 0;
26221
+ let last = { kind: "refused" };
26222
+ while (deps.now() < deadline) {
26223
+ const remaining = Math.max(1, deadline - deps.now());
26224
+ last = await deps.post(endpoint.host, endpoint.port, path, void 0, remaining);
26225
+ attempts++;
26226
+ if (last.kind === "status" && last.status === 200) return attempts;
26227
+ if (last.kind === "timeout") throw new MicrovmHookError(`The /${hook} hook held the request open for ${timeoutMs}ms without answering. Answer 503 immediately while not ready (the cloud ends the image build when a held request reaches the timeout), or raise ${timeoutField(hook)}.`, "LOCAL_START_MICROVM_HOOK_TIMEOUT");
26228
+ if (last.kind === "status" && last.status !== 503) throw new MicrovmHookError(`The /${hook} hook answered HTTP ${last.status}; it must answer 200 when done or 503 to be retried.${bodySuffix(last.body)}`);
26229
+ if (deps.isAlive && attempts % 4 === 0 && !await deps.isAlive()) throw new MicrovmHookError(`The MicroVM container exited while cdk-local polled its /${hook} hook.`, "LOCAL_START_MICROVM_EXITED");
26230
+ await deps.sleep(250);
26231
+ }
26232
+ const lastSeen = last.kind === "status" ? `its last answer was HTTP ${last.status}` : `nothing answered on ${formatAuthority(endpoint.host, endpoint.port)} (the hooks port)`;
26233
+ throw new MicrovmHookError(`The /${hook} hook did not answer 200 within ${timeoutMs}ms (${timeoutField(hook)}); ${lastSeen}.`, "LOCAL_START_MICROVM_HOOK_TIMEOUT");
26234
+ }
26235
+ async function callRuntimeHook(hook, endpoint, timeoutMs, body, deps = defaultHookDeps) {
26236
+ const path = `${MICROVM_HOOK_PATH_PREFIX}${hook}`;
26237
+ const result = await deps.post(endpoint.host, endpoint.port, path, body, timeoutMs);
26238
+ if (result.kind === "status" && result.status === 200) return;
26239
+ if (result.kind === "timeout") throw new MicrovmHookError(`The /${hook} hook did not answer within ${timeoutMs}ms (${timeoutField(hook)}).`, "LOCAL_START_MICROVM_HOOK_TIMEOUT");
26240
+ if (result.kind === "refused") throw new MicrovmHookError(`The /${hook} hook could not be reached: nothing answered on the hooks port (${formatAuthority(endpoint.host, endpoint.port)}).`);
26241
+ throw new MicrovmHookError(`The /${hook} hook answered HTTP ${result.status}; it must answer 200.${bodySuffix(result.body)}`);
26242
+ }
26243
+ async function waitForAppAnswer(endpoint, timeoutMs, deps = {
26244
+ probe: probeHttp,
26245
+ sleep: defaultHookDeps.sleep,
26246
+ now: defaultHookDeps.now
26247
+ }) {
26248
+ const deadline = deps.now() + timeoutMs;
26249
+ let attempt = 0;
26250
+ while (deps.now() < deadline) {
26251
+ const remaining = Math.max(1, deadline - deps.now());
26252
+ const outcome = await deps.probe(endpoint.host, endpoint.port, Math.min(remaining, 1e4));
26253
+ if (outcome === "answered") return;
26254
+ attempt++;
26255
+ if (deps.isAlive && attempt % 4 === 0 && !await deps.isAlive()) throw new MicrovmHookError("The MicroVM container exited before its application answered HTTP.", "LOCAL_START_MICROVM_EXITED");
26256
+ if (outcome === "refused") await deps.sleep(250);
26257
+ }
26258
+ throw new MicrovmHookError(`The MicroVM application never answered HTTP on ${formatAuthority(endpoint.host, endpoint.port)} within ${timeoutMs}ms. With no /ready hook, cdk-local waits for the app to answer before driving the next hook; raise --timeout, or enable the /ready hook.`, "LOCAL_START_MICROVM_NOT_READY");
26259
+ }
26260
+ function timeoutField(hook) {
26261
+ return `${hook.charAt(0).toUpperCase() + hook.slice(1)}TimeoutInSeconds, or --timeout when it is unset`;
26262
+ }
26263
+ function bodySuffix(body) {
26264
+ const trimmed = body.trim();
26265
+ if (trimmed.length === 0) return "";
26266
+ return ` Body: ${displayToolOutput(trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed)}`;
26267
+ }
26268
+ function postHook(host, port, path, body, timeoutMs) {
26269
+ return new Promise((resolvePost) => {
26270
+ let settled = false;
26271
+ const settle = (r) => {
26272
+ if (settled) return;
26273
+ settled = true;
26274
+ clearTimeout(timer);
26275
+ resolvePost(r);
26276
+ };
26277
+ const payload = body ?? "";
26278
+ const req = request$2({
26279
+ host,
26280
+ port,
26281
+ path,
26282
+ method: "POST",
26283
+ headers: {
26284
+ connection: "close",
26285
+ "content-length": Buffer.byteLength(payload),
26286
+ ...body !== void 0 && { "content-type": "application/json" }
26287
+ }
26288
+ }, (res) => {
26289
+ const chunks = [];
26290
+ let size = 0;
26291
+ res.on("data", (c) => {
26292
+ if (size < 64 * 1024) chunks.push(c);
26293
+ size += c.length;
26294
+ });
26295
+ res.on("end", () => settle({
26296
+ kind: "status",
26297
+ status: res.statusCode ?? 0,
26298
+ body: Buffer.concat(chunks).toString("utf-8")
26299
+ }));
26300
+ res.on("error", () => settle({
26301
+ kind: "status",
26302
+ status: res.statusCode ?? 0,
26303
+ body: ""
26304
+ }));
26305
+ });
26306
+ const timer = setTimeout(() => {
26307
+ settle({ kind: "timeout" });
26308
+ req.destroy();
26309
+ }, timeoutMs);
26310
+ req.on("error", () => settle({ kind: "refused" }));
26311
+ req.end(payload);
26312
+ });
26313
+ }
26314
+ const defaultHookDeps = {
26315
+ post: postHook,
26316
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
26317
+ now: () => Date.now()
26318
+ };
26319
+
26320
+ //#endregion
26321
+ //#region src/local/microvm-front-door.ts
26322
+ const MICROVM_CONTROL_PATH = "/_cdkl/microvm";
26323
+ const PROXY_PORT_HEADER = "x-aws-proxy-port";
26324
+ const MICROVM_WS_BASE_PROTOCOL = "lambda-microvms";
26325
+ const CLOSE_GRACE_MS = 1e3;
26326
+ function selectTargetPort(headers) {
26327
+ const fromHeader = firstHeader(headers[PROXY_PORT_HEADER]);
26328
+ const fromProtocol = fromHeader === void 0 ? subprotocols(headers).find((p) => p.startsWith(`${MICROVM_WS_BASE_PROTOCOL}.port.`)) : void 0;
26329
+ const raw = fromHeader ?? (fromProtocol !== void 0 ? fromProtocol.slice(`${"lambda-microvms"}.port.`.length) : void 0);
26330
+ if (raw === void 0) return {
26331
+ ok: true,
26332
+ port: MICROVM_DEFAULT_INGRESS_PORT
26333
+ };
26334
+ const trimmed = raw.trim();
26335
+ const n = /^\d{1,5}$/.test(trimmed) ? Number(trimmed) : NaN;
26336
+ if (!Number.isInteger(n) || n < 1 || n > 65535) return {
26337
+ ok: false,
26338
+ status: 400,
26339
+ message: `Invalid ${fromHeader === void 0 ? "WebSocket subprotocol lambda-microvms.port." : "X-aws-proxy-port"} value '${trimmed.slice(0, 32)}': it must be a port number 1-65535.`
26340
+ };
26341
+ return {
26342
+ ok: true,
26343
+ port: n
26344
+ };
26345
+ }
26346
+ function stripMicrovmProxyHeaders(headers) {
26347
+ for (const name of Object.keys(headers)) if (name.toLowerCase().startsWith("x-aws-proxy-")) delete headers[name];
26348
+ const offered = subprotocols(headers);
26349
+ if (offered.length === 0) return false;
26350
+ const kept = offered.filter((p) => !isMicrovmProtocol(p));
26351
+ if (kept.length > 0) headers["sec-websocket-protocol"] = kept.join(", ");
26352
+ else delete headers["sec-websocket-protocol"];
26353
+ return offered.includes(MICROVM_WS_BASE_PROTOCOL);
26354
+ }
26355
+ function isMicrovmProtocol(p) {
26356
+ return p === "lambda-microvms" || p.startsWith(`${"lambda-microvms"}.`);
26357
+ }
26358
+ function subprotocols(headers) {
26359
+ const raw = headers["sec-websocket-protocol"];
26360
+ const joined = Array.isArray(raw) ? raw.join(",") : raw;
26361
+ if (!joined) return [];
26362
+ return joined.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
26363
+ }
26364
+ function firstHeader(value) {
26365
+ if (Array.isArray(value)) return value[0];
26366
+ if (typeof value === "string") return value.split(",")[0];
26367
+ }
26368
+ function startMicrovmFrontDoor(opts) {
26369
+ const logger = getLogger().child("microvm");
26370
+ const bound = { port: opts.port };
26371
+ const server = createServer((req, res) => {
26372
+ handleRequest$1(req, res, opts, bound.port).catch((err) => {
26373
+ logger.debug(`microvm front door error: ${err instanceof Error ? err.message : String(err)}`);
26374
+ if (!res.headersSent) writeText(res, 502, "The MicroVM front door failed to forward the request.");
26375
+ else res.destroy();
26376
+ });
26377
+ });
26378
+ const upgraded = /* @__PURE__ */ new Set();
26379
+ const track = (sock) => {
26380
+ upgraded.add(sock);
26381
+ sock.once("close", () => upgraded.delete(sock));
26382
+ };
26383
+ server.on("upgrade", (req, socket, head) => {
26384
+ track(socket);
26385
+ handleUpgrade(req, socket, head, opts, track).catch((err) => {
26386
+ logger.debug(`microvm upgrade error: ${err instanceof Error ? err.message : String(err)}`);
26387
+ writeRawHttpError(socket, 502, "The MicroVM front door failed to forward the upgrade.");
26388
+ });
26389
+ });
26390
+ return new Promise((resolveStart, rejectStart) => {
26391
+ server.once("error", rejectStart);
26392
+ server.listen(opts.port, opts.host, () => {
26393
+ server.off("error", rejectStart);
26394
+ const address = server.address();
26395
+ const port = typeof address === "object" && address !== null ? address.port : opts.port;
26396
+ bound.port = port;
26397
+ resolveStart({
26398
+ port,
26399
+ close: () => new Promise((resolveClose) => {
26400
+ server.close(() => resolveClose());
26401
+ server.closeIdleConnections();
26402
+ for (const sock of upgraded) sock.end();
26403
+ setTimeout(() => {
26404
+ server.closeAllConnections();
26405
+ for (const sock of upgraded) sock.destroy();
26406
+ }, CLOSE_GRACE_MS).unref();
26407
+ })
26408
+ });
26409
+ });
26410
+ });
26411
+ }
26412
+ async function admit(opts) {
26413
+ const id = opts.target.microvmId;
26414
+ let state = opts.target.state();
26415
+ if ((state === "suspended" || state === "suspending" || state === "resuming") && opts.autoResume) {
26416
+ try {
26417
+ await opts.target.resume();
26418
+ } catch (err) {
26419
+ const now = opts.target.state();
26420
+ if (err instanceof MicrovmStateError && (now === "terminating" || now === "terminated")) return {
26421
+ status: 503,
26422
+ message: `MicroVM ${id} is ${now}.`
26423
+ };
26424
+ return {
26425
+ status: 502,
26426
+ message: `MicroVM ${id} could not be resumed for this request: ${err instanceof Error ? err.message : String(err)}`
26427
+ };
26428
+ }
26429
+ state = opts.target.state();
26430
+ }
26431
+ if (state === "running") return void 0;
26432
+ if (state === "terminating" || state === "terminated") return {
26433
+ status: 503,
26434
+ message: `MicroVM ${id} is ${state}.`
26435
+ };
26436
+ return {
26437
+ status: 503,
26438
+ message: `MicroVM ${id} is ${state}. Resume it with POST ${MICROVM_CONTROL_PATH}/resume (or start with --auto-resume to resume on the first request).`
26439
+ };
26440
+ }
26441
+ async function handleRequest$1(req, res, opts, boundPort) {
26442
+ const path = (req.url ?? "/").split("?")[0];
26443
+ if (path === "/_cdkl/microvm" || path.startsWith(`${"/_cdkl/microvm"}/`)) {
26444
+ const refusal = controlRequestRefusal(req.headers, opts.host, boundPort);
26445
+ if (refusal !== void 0) {
26446
+ req.resume();
26447
+ writeText(res, 403, refusal);
26448
+ return;
26449
+ }
26450
+ await handleControl(req, res, path, opts);
26451
+ return;
26452
+ }
26453
+ const selection = selectTargetPort(req.headers);
26454
+ if (!selection.ok) {
26455
+ req.resume();
26456
+ writeText(res, selection.status, selection.message);
26457
+ return;
26458
+ }
26459
+ const upstream = opts.target.upstreamFor(selection.port);
26460
+ if (!upstream) {
26461
+ req.resume();
26462
+ writeText(res, 403, notRoutableMessage(selection.port, opts.target));
26463
+ return;
26464
+ }
26465
+ const refusal = await admit(opts);
26466
+ if (refusal) {
26467
+ req.resume();
26468
+ writeText(res, refusal.status, refusal.message);
26469
+ return;
26470
+ }
26471
+ const headers = { ...req.headers };
26472
+ stripHopByHopHeaders(headers);
26473
+ stripMicrovmProxyHeaders(headers);
26474
+ await new Promise((resolveForward) => {
26475
+ const proxyReq = request({
26476
+ host: upstream.host,
26477
+ port: upstream.port,
26478
+ method: req.method,
26479
+ path: req.url,
26480
+ headers
26481
+ }, (proxyRes) => {
26482
+ const resHeaders = { ...proxyRes.headers };
26483
+ stripHopByHopHeaders(resHeaders);
26484
+ res.writeHead(proxyRes.statusCode ?? 502, resHeaders);
26485
+ proxyRes.pipe(res);
26486
+ proxyRes.on("end", resolveForward);
26487
+ proxyRes.on("error", () => {
26488
+ if (!res.writableEnded) res.destroy();
26489
+ resolveForward();
26490
+ });
26491
+ });
26492
+ proxyReq.on("error", () => {
26493
+ if (!res.headersSent) writeText(res, 502, `The application in MicroVM ${opts.target.microvmId} is not responding on port ${selection.port}.`);
26494
+ else if (!res.writableEnded) res.destroy();
26495
+ resolveForward();
26496
+ });
26497
+ res.on("close", () => {
26498
+ if (!res.writableEnded) proxyReq.destroy();
26499
+ resolveForward();
26500
+ });
26501
+ req.pipe(proxyReq);
26502
+ });
26503
+ }
26504
+ async function handleControl(req, res, path, opts) {
26505
+ req.resume();
26506
+ const target = opts.target;
26507
+ const action = path.slice(14).replace(/^\//, "");
26508
+ if (action === "") {
26509
+ if (req.method !== "GET" && req.method !== "HEAD") {
26510
+ writeText(res, 405, `Use GET ${MICROVM_CONTROL_PATH}.`);
26511
+ return;
26512
+ }
26513
+ writeJson(res, 200, {
26514
+ microvmId: target.microvmId,
26515
+ state: target.state(),
26516
+ ports: target.routablePorts()
26517
+ });
26518
+ return;
26519
+ }
26520
+ if (action !== "suspend" && action !== "resume" && action !== "terminate") {
26521
+ writeText(res, 404, `Unknown control action '${action.slice(0, 32)}'. Use POST ${MICROVM_CONTROL_PATH}/suspend, /resume or /terminate.`);
26522
+ return;
26523
+ }
26524
+ if (req.method !== "POST") {
26525
+ writeText(res, 405, `Use POST ${MICROVM_CONTROL_PATH}/${action}.`);
26526
+ return;
26527
+ }
26528
+ try {
26529
+ await (action === "suspend" ? target.suspend() : action === "resume" ? target.resume() : target.terminate());
26530
+ } catch (err) {
26531
+ const message = err instanceof Error ? err.message : String(err);
26532
+ writeText(res, err instanceof MicrovmStateError ? 409 : 502, message);
26533
+ return;
26534
+ }
26535
+ writeJson(res, 200, {
26536
+ microvmId: target.microvmId,
26537
+ state: target.state()
26538
+ });
26539
+ }
26540
+ const LOOPBACK_NAMES = [
26541
+ "localhost",
26542
+ "127.0.0.1",
26543
+ "[::1]"
26544
+ ];
26545
+ function isWildcard(host) {
26546
+ return host === "0.0.0.0" || host === "::" || host === "[::]";
26547
+ }
26548
+ function isIpLiteral(name) {
26549
+ return /^\d{1,3}(\.\d{1,3}){3}$/.test(name) || /^\[[0-9a-f:.]+\]$/i.test(name);
26550
+ }
26551
+ function controlRequestRefusal(headers, boundHost, boundPort) {
26552
+ const fetchSite = firstHeader(headers["sec-fetch-site"])?.trim().toLowerCase();
26553
+ if (fetchSite !== void 0 && fetchSite !== "same-origin" && fetchSite !== "none") return `Refusing a ${fetchSite} request to the MicroVM control endpoint: it accepts requests from this origin only (curl, or a page this front door served).`;
26554
+ const host = (Array.isArray(headers.host) ? headers.host[0] : headers.host)?.trim().toLowerCase();
26555
+ const m = host === void 0 ? null : /^(\[[^\]]+\]|[^:]+)(?::(\d+))?$/.exec(host);
26556
+ const name = m?.[1];
26557
+ const port = m ? m[2] !== void 0 ? Number(m[2]) : 80 : NaN;
26558
+ const bindName = boundHost.includes(":") && !boundHost.startsWith("[") ? `[${boundHost}]` : boundHost;
26559
+ const loopbackBind = isWildcard(boundHost) || LOOPBACK_NAMES.includes(bindName.toLowerCase());
26560
+ if (!(name !== void 0 && (name === bindName.toLowerCase() || loopbackBind && LOOPBACK_NAMES.includes(name) || isWildcard(boundHost) && isIpLiteral(name))) || port !== boundPort) return `Refusing a MicroVM control request whose Host header (${(host ?? "<none>").slice(0, 64)}) is not this front door's address.`;
26561
+ const origin = firstHeader(headers.origin)?.trim().toLowerCase();
26562
+ if (origin !== void 0 && origin !== `http://${host}`) return `Refusing a MicroVM control request from origin ${origin.slice(0, 64)}: it accepts requests from this origin only.`;
26563
+ }
26564
+ var MicrovmStateError = class MicrovmStateError extends Error {
26565
+ constructor(message) {
26566
+ super(message);
26567
+ this.name = "MicrovmStateError";
26568
+ Object.setPrototypeOf(this, MicrovmStateError.prototype);
26569
+ }
26570
+ };
26571
+ async function handleUpgrade(req, clientSocket, head, opts, track) {
26572
+ const selection = selectTargetPort(req.headers);
26573
+ if (!selection.ok) {
26574
+ writeRawHttpError(clientSocket, selection.status, selection.message);
26575
+ return;
26576
+ }
26577
+ const upstream = opts.target.upstreamFor(selection.port);
26578
+ if (!upstream) {
26579
+ writeRawHttpError(clientSocket, 403, notRoutableMessage(selection.port, opts.target));
26580
+ return;
26581
+ }
26582
+ const refusal = await admit(opts);
26583
+ if (refusal) {
26584
+ writeRawHttpError(clientSocket, refusal.status, refusal.message);
26585
+ return;
26586
+ }
26587
+ const headers = { ...req.headers };
26588
+ const offeredBase = stripMicrovmProxyHeaders(headers);
26589
+ const appProtocolsLeft = headers["sec-websocket-protocol"] !== void 0;
26590
+ const upstreamSocket = connect({
26591
+ host: upstream.host,
26592
+ port: upstream.port
26593
+ });
26594
+ track(upstreamSocket);
26595
+ upstreamSocket.setNoDelay(true);
26596
+ let connected = false;
26597
+ let responseHead = Buffer.alloc(0);
26598
+ upstreamSocket.on("connect", () => {
26599
+ connected = true;
26600
+ const lines = [`${req.method ?? "GET"} ${req.url ?? "/"} HTTP/${req.httpVersion}`];
26601
+ for (const [name, value] of Object.entries(headers)) {
26602
+ if (value === void 0) continue;
26603
+ for (const v of Array.isArray(value) ? value : [value]) lines.push(`${name}: ${v}`);
26604
+ }
26605
+ upstreamSocket.write(`${lines.join("\r\n")}\r\n\r\n`);
26606
+ if (head.length > 0) upstreamSocket.write(head);
26607
+ clientSocket.pipe(upstreamSocket, { end: false });
26608
+ });
26609
+ const onHead = (chunk) => {
26610
+ responseHead = Buffer.concat([responseHead, chunk]);
26611
+ const end = responseHead.indexOf("\r\n\r\n");
26612
+ if (end === -1 && responseHead.length < 64 * 1024) return;
26613
+ upstreamSocket.pause();
26614
+ upstreamSocket.off("data", onHead);
26615
+ const out = end === -1 ? responseHead : Buffer.concat([Buffer.from(rewriteUpgradeResponseHead(responseHead.subarray(0, end).toString("latin1"), offeredBase && !appProtocolsLeft) + "\r\n\r\n", "latin1"), responseHead.subarray(end + 4)]);
26616
+ responseHead = void 0;
26617
+ clientSocket.write(out);
26618
+ upstreamSocket.pipe(clientSocket);
26619
+ };
26620
+ upstreamSocket.on("data", onHead);
26621
+ const closeBoth = () => {
26622
+ if (!clientSocket.destroyed) clientSocket.destroy();
26623
+ if (!upstreamSocket.destroyed) upstreamSocket.destroy();
26624
+ };
26625
+ let answered = false;
26626
+ const answer502 = (why) => {
26627
+ if (answered || clientSocket.destroyed) return;
26628
+ answered = true;
26629
+ writeRawHttpError(clientSocket, 502, `The application in MicroVM ${opts.target.microvmId} ${why} on port ${selection.port}.`);
26630
+ };
26631
+ upstreamSocket.on("end", () => clientSocket.unpipe(upstreamSocket));
26632
+ upstreamSocket.on("error", () => {
26633
+ if (!connected) return answer502("is not responding");
26634
+ if (responseHead === void 0) closeBoth();
26635
+ });
26636
+ upstreamSocket.on("close", (hadError) => {
26637
+ if (responseHead !== void 0) return answer502("closed the connection before answering the upgrade");
26638
+ if (hadError) closeBoth();
26639
+ else if (!clientSocket.writableEnded) clientSocket.end();
26640
+ });
26641
+ clientSocket.on("error", closeBoth);
26642
+ clientSocket.on("end", () => {
26643
+ if (!upstreamSocket.destroyed && !upstreamSocket.writableEnded) upstreamSocket.end();
26644
+ });
26645
+ clientSocket.on("close", () => {
26646
+ if (!upstreamSocket.destroyed) upstreamSocket.destroy();
26647
+ });
26648
+ }
26649
+ function rewriteUpgradeResponseHead(head, addBase) {
26650
+ if (!addBase) return head;
26651
+ const lines = head.split("\r\n");
26652
+ if (!/^HTTP\/1\.[01] 101\b/.test(lines[0] ?? "")) return head;
26653
+ if (lines.some((l) => /^sec-websocket-protocol\s*:/i.test(l))) return head;
26654
+ return [...lines, `Sec-WebSocket-Protocol: ${MICROVM_WS_BASE_PROTOCOL}`].join("\r\n");
26655
+ }
26656
+ function notRoutableMessage(port, target) {
26657
+ return `Port ${port} of MicroVM ${target.microvmId} is not reachable locally. cdk-local publishes 8080, Hooks.Port, the image's EXPOSE ports and every --proxy-port; published: ${target.routablePorts().join(", ")}.`;
26658
+ }
26659
+ function writeText(res, status, message) {
26660
+ res.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
26661
+ res.end(`${message}\n`);
26662
+ }
26663
+ function writeJson(res, status, body) {
26664
+ res.writeHead(status, { "content-type": "application/json" });
26665
+ res.end(JSON.stringify(body));
26666
+ }
26667
+
26668
+ //#endregion
26669
+ //#region src/local/microvm-runner.ts
26670
+ const execFileAsync = promisify$1(execFile$1);
26671
+ const DEFAULT_TERMINATE_HOOK_TIMEOUT_MS = 1e4;
26672
+ function microvmImageTag(artifact) {
26673
+ return `${getEmbedConfig().resourceNamePrefix}-microvm-${artifact.assetHash.slice(0, 16).toLowerCase()}`;
26674
+ }
26675
+ async function buildMicrovmImage(opts) {
26676
+ const tag = microvmImageTag(opts.artifact);
26677
+ const parent = dirname$1(opts.artifact.contextDir);
26678
+ getLogger().info(`Building MicroVM image ${tag}${opts.platform ? ` (platform=${opts.platform})` : ""}...`);
26679
+ await buildDockerImage({ source: {
26680
+ directory: basename$1(opts.artifact.contextDir),
26681
+ ...opts.noCache && { cacheDisabled: true }
26682
+ } }, parent, {
26683
+ tag,
26684
+ ...opts.platform !== void 0 && { platform: opts.platform },
26685
+ assetOutdir: parent,
26686
+ progressLabel: "Building MicroVM image",
26687
+ wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for the MicroVM image's Dockerfile: ${stderr}`)
26688
+ });
26689
+ return tag;
26690
+ }
26691
+ async function readExposedPorts(image) {
26692
+ try {
26693
+ const { stdout } = await execFileAsync(getDockerCmd(), [
26694
+ "image",
26695
+ "inspect",
26696
+ "-f",
26697
+ "{{json .Config.ExposedPorts}}",
26698
+ image
26699
+ ]);
26700
+ const parsed = JSON.parse(stdout.trim() || "null");
26701
+ if (!parsed) return [];
26702
+ return Object.keys(parsed).filter((k) => k.endsWith("/tcp") || !k.includes("/")).map((k) => Number(k.split("/")[0])).filter((n) => Number.isInteger(n) && n > 0 && n < 65536);
26703
+ } catch {
26704
+ return [];
26705
+ }
26706
+ }
26707
+ function planMicrovmPorts(hooksPort, exposed, extra) {
26708
+ const out = [MICROVM_DEFAULT_INGRESS_PORT];
26709
+ for (const p of [
26710
+ ...hooksPort !== void 0 ? [hooksPort] : [],
26711
+ ...exposed,
26712
+ ...extra
26713
+ ]) if (!out.includes(p)) out.push(p);
26714
+ return out;
26715
+ }
26716
+ async function setContainerPaused(containerId, paused) {
26717
+ try {
26718
+ await execFileAsync(getDockerCmd(), [paused ? "pause" : "unpause", containerId]);
26719
+ } catch (error) {
26720
+ const err = error;
26721
+ throw new CdkLocalError(`docker ${paused ? "pause" : "unpause"} failed: ${displayToolOutput(err.stderr?.trim() || err.message || String(error))}`, "LOCAL_START_MICROVM_PAUSE_FAILED");
26722
+ }
26723
+ }
26724
+ const defaultDeps = {
26725
+ setPaused: setContainerPaused,
26726
+ remove: removeContainer
26727
+ };
26728
+ var LocalMicrovm = class {
26729
+ microvmId;
26730
+ containerId;
26731
+ current = "booting";
26732
+ resuming;
26733
+ suspending;
26734
+ paused = false;
26735
+ terminating;
26736
+ opts;
26737
+ deps;
26738
+ constructor(opts, deps = {}) {
26739
+ this.opts = opts;
26740
+ this.deps = {
26741
+ ...defaultDeps,
26742
+ ...deps
26743
+ };
26744
+ this.microvmId = opts.microvmId;
26745
+ this.containerId = opts.containerId;
26746
+ }
26747
+ state() {
26748
+ return this.current === "booting" ? "resuming" : this.current;
26749
+ }
26750
+ upstreamFor(containerPort) {
26751
+ return this.opts.ports.get(containerPort);
26752
+ }
26753
+ routablePorts() {
26754
+ return [...this.opts.ports.keys()];
26755
+ }
26756
+ log(message) {
26757
+ getLogger().info(`MicroVM ${this.microvmId}: ${message}`);
26758
+ }
26759
+ hookEndpoint() {
26760
+ const port = this.opts.config.hooksPort;
26761
+ const endpoint = port !== void 0 ? this.opts.ports.get(port) : void 0;
26762
+ if (!endpoint) throw new CdkLocalError("The hooks port is not published.", "LOCAL_START_MICROVM_HOOK_FAILED");
26763
+ return endpoint;
26764
+ }
26765
+ hookTimeoutMs(hook) {
26766
+ const seconds = this.opts.config.hooks[hook]?.timeoutSeconds;
26767
+ if (seconds !== void 0) return seconds * 1e3;
26768
+ return hook === "terminate" ? DEFAULT_TERMINATE_HOOK_TIMEOUT_MS : this.opts.timeoutMs;
26769
+ }
26770
+ hookDeps() {
26771
+ return {
26772
+ ...this.deps.hooks ?? defaultHookDeps,
26773
+ ...this.deps.isAlive && { isAlive: this.deps.isAlive }
26774
+ };
26775
+ }
26776
+ async runtimeHook(hook, body) {
26777
+ if (!this.opts.config.hooks[hook]) return false;
26778
+ await callRuntimeHook(hook, this.hookEndpoint(), this.hookTimeoutMs(hook), body, this.hookDeps());
26779
+ this.log(`/${hook} hook answered 200`);
26780
+ return true;
26781
+ }
26782
+ async boot(runHookPayload) {
26783
+ const { hooks } = this.opts.config;
26784
+ const deps = this.hookDeps();
26785
+ if (hooks.ready) {
26786
+ const attempts = await pollBuildHook("ready", this.hookEndpoint(), this.hookTimeoutMs("ready"), deps);
26787
+ this.log(`/ready hook answered 200 after ${attempts} attempt(s)`);
26788
+ } else {
26789
+ const port = this.opts.config.hooksPort ?? 8080;
26790
+ const endpoint = this.opts.ports.get(port);
26791
+ await (this.deps.waitForAnswer ?? waitForAppAnswer)(endpoint, this.opts.timeoutMs, {
26792
+ probe: probeHttp,
26793
+ sleep: deps.sleep,
26794
+ now: deps.now,
26795
+ ...this.deps.isAlive && { isAlive: this.deps.isAlive }
26796
+ });
26797
+ this.log("application answered HTTP (no /ready hook)");
26798
+ }
26799
+ if (hooks.validate) {
26800
+ const attempts = await pollBuildHook("validate", this.hookEndpoint(), this.hookTimeoutMs("validate"), deps);
26801
+ this.log(`/validate hook answered 200 after ${attempts} attempt(s)`);
26802
+ }
26803
+ await this.runtimeHook("run", runHookBody(this.microvmId, runHookPayload));
26804
+ if (this.current === "booting") this.current = "running";
26805
+ }
26806
+ async setPaused(paused) {
26807
+ await this.deps.setPaused(this.containerId, paused);
26808
+ this.paused = paused;
26809
+ }
26810
+ suspend() {
26811
+ if (this.current !== "running") return Promise.reject(new MicrovmStateError(`MicroVM ${this.microvmId} is ${this.state()}; only a running MicroVM can be suspended.`));
26812
+ this.current = "suspending";
26813
+ this.suspending = (async () => {
26814
+ try {
26815
+ await this.runtimeHook("suspend");
26816
+ await this.setPaused(true);
26817
+ } catch (err) {
26818
+ if (this.current === "suspending") this.current = "running";
26819
+ throw err;
26820
+ } finally {
26821
+ this.suspending = void 0;
26822
+ }
26823
+ if (this.current === "suspending") {
26824
+ this.current = "suspended";
26825
+ this.log("suspended (container paused)");
26826
+ }
26827
+ })();
26828
+ return this.suspending;
26829
+ }
26830
+ resume() {
26831
+ if (this.current === "resuming" && this.resuming) return this.resuming;
26832
+ if (this.current === "suspending" && this.suspending) return this.suspending.then(() => this.resume(), () => void 0);
26833
+ if (this.current !== "suspended") return Promise.reject(new MicrovmStateError(`MicroVM ${this.microvmId} is ${this.state()}; only a suspended MicroVM can be resumed.`));
26834
+ this.current = "resuming";
26835
+ this.resuming = (async () => {
26836
+ try {
26837
+ await this.setPaused(false);
26838
+ try {
26839
+ await this.runtimeHook("resume");
26840
+ } catch (err) {
26841
+ if (this.current === "resuming") await this.setPaused(true).catch(() => void 0);
26842
+ throw err;
26843
+ }
26844
+ } catch (err) {
26845
+ if (this.current === "resuming") this.current = "suspended";
26846
+ throw err;
26847
+ } finally {
26848
+ this.resuming = void 0;
26849
+ }
26850
+ if (this.current === "resuming") {
26851
+ this.current = "running";
26852
+ this.log("resumed");
26853
+ }
26854
+ })();
26855
+ return this.resuming;
26856
+ }
26857
+ terminate() {
26858
+ this.terminating ??= (async () => {
26859
+ const wasBooting = this.current === "booting";
26860
+ this.current = "terminating";
26861
+ await Promise.allSettled([this.suspending, this.resuming].filter((p) => p !== void 0));
26862
+ try {
26863
+ if (this.paused) await this.setPaused(false).catch(() => void 0);
26864
+ if (!wasBooting) await this.runtimeHook("terminate");
26865
+ } catch (err) {
26866
+ getLogger().warn(`MicroVM ${this.microvmId}: ${err instanceof Error ? err.message : String(err)} Removing its container anyway.`);
26867
+ }
26868
+ await this.deps.remove(this.containerId);
26869
+ this.current = "terminated";
26870
+ this.log("terminated (container removed)");
26871
+ this.opts.onTerminated?.(this);
26872
+ })();
26873
+ return this.terminating;
26874
+ }
26875
+ };
26876
+
26877
+ //#endregion
26878
+ //#region src/cli/commands/local-start-microvm.ts
26879
+ function parsePort(raw) {
26880
+ const n = Number(raw);
26881
+ if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
26882
+ return n;
26883
+ }
26884
+ function parseCount(raw) {
26885
+ const n = Number(raw);
26886
+ if (!Number.isInteger(n) || n < 1 || n > 64) throw new InvalidArgumentError(`--count must be an integer 1-64, got '${raw}'.`);
26887
+ return n;
26888
+ }
26889
+ function parseProxyPort(raw, previous) {
26890
+ const n = Number(raw);
26891
+ if (!Number.isInteger(n) || n < 1 || n > 65535) throw new InvalidArgumentError(`--proxy-port must be an integer 1-65535, got '${raw}'.`);
26892
+ return [...previous ?? [], n];
26893
+ }
26894
+ function connectHost(host) {
26895
+ if (host === "0.0.0.0") return "127.0.0.1";
26896
+ if (host === "::" || host === "[::]") return "::1";
26897
+ return host;
26898
+ }
26899
+ function readRunHookPayload(options, readFile = (p) => readFileSync$1(p, "utf-8")) {
26900
+ if (options.runHookPayload !== void 0 && options.runHookPayloadFile !== void 0) throw new CdkLocalError("--run-hook-payload and --run-hook-payload-file are mutually exclusive.", "LOCAL_START_MICROVM_PAYLOAD_CONFLICT");
26901
+ let payload = options.runHookPayload;
26902
+ if (options.runHookPayloadFile !== void 0) try {
26903
+ payload = readFile(options.runHookPayloadFile);
26904
+ } catch (err) {
26905
+ throw new CdkLocalError(`Cannot read --run-hook-payload-file ${displayUntrustedValue(options.runHookPayloadFile)}: ${err instanceof Error ? err.message : String(err)}`, "LOCAL_START_MICROVM_PAYLOAD_UNREADABLE");
26906
+ }
26907
+ if (payload !== void 0 && Buffer.byteLength(payload) > 16384) throw new CdkLocalError(`The run hook payload is ${Buffer.byteLength(payload)} bytes; run-microvm accepts at most ${MAX_RUN_HOOK_PAYLOAD_BYTES} (16 KB).`, "LOCAL_START_MICROVM_PAYLOAD_TOO_LARGE");
26908
+ return payload;
26909
+ }
26910
+ function buildMicrovmLambdaView(resolved, config) {
26911
+ return {
26912
+ kind: "zip",
26913
+ stack: resolved.stack,
26914
+ logicalId: resolved.logicalId,
26915
+ resource: {
26916
+ Type: resolved.resource.Type,
26917
+ Properties: { Environment: { Variables: config.environment } },
26918
+ ...resolved.resource.Metadata !== void 0 && { Metadata: resolved.resource.Metadata }
26919
+ },
26920
+ memoryMb: 128,
26921
+ timeoutSec: 0,
26922
+ layers: [],
26923
+ runtime: "",
26924
+ handler: "",
26925
+ codePath: null,
26926
+ architecture: config.architecture ?? "arm64"
26927
+ };
26928
+ }
26929
+ const LAMBDA_RUNTIME_ENV_KEYS = [
26930
+ "AWS_LAMBDA_FUNCTION_NAME",
26931
+ "AWS_LAMBDA_FUNCTION_MEMORY_SIZE",
26932
+ "AWS_LAMBDA_FUNCTION_TIMEOUT",
26933
+ "AWS_LAMBDA_FUNCTION_VERSION",
26934
+ "AWS_LAMBDA_LOG_GROUP_NAME",
26935
+ "AWS_LAMBDA_LOG_STREAM_NAME"
26936
+ ];
26937
+ function finalizeMicrovmEnv(lambdaEnv, declared) {
26938
+ const env = { ...lambdaEnv };
26939
+ for (const key of LAMBDA_RUNTIME_ENV_KEYS) if (!Object.hasOwn(declared, key)) delete env[key];
26940
+ return env;
26941
+ }
26942
+ function microvmFidelityWarnings(config) {
26943
+ const image = displayIdent(config.logicalId);
26944
+ const out = [
26945
+ `MicroVM image ${image}: each MicroVM is a Docker container started fresh from the built image, not a MicroVM restored from a snapshot — state the app initializes before /ready answers 200 is NOT shared across MicroVMs (each one runs its own startup), and there is no VM isolation or memory checkpoint (suspend is docker pause).`,
26946
+ `MicroVM image ${image}: requests are accepted without X-aws-proxy-auth — the JWE token the cloud endpoint requires is not checked locally (the header is still stripped).`,
26947
+ `MicroVM image ${image}: the idle policy is not emulated — a MicroVM suspends, resumes and terminates only through ${MICROVM_CONTROL_PATH}/suspend|resume|terminate (and --auto-resume).`,
26948
+ `MicroVM image ${image}: the Dockerfile is built by the local Docker engine, not on the Lambda-managed MicroVM base OS (BaseImageArn).`
26949
+ ];
26950
+ if (config.hooks.validate) out.push(`MicroVM image ${image}: /validate is called on the same container as /ready (the cloud calls it on a fresh MicroVM started from the built snapshot).`);
26951
+ for (const what of config.unemulated) out.push(`MicroVM image ${image}: ${what} is not emulated.`);
26952
+ return out;
26953
+ }
26954
+ async function localStartMicrovmCommand(target, options, extraStateProviders, hostSynthesizer) {
26955
+ const logger = getLogger();
26956
+ if (options.verbose) logger.setLevel("debug");
26957
+ const runHookPayload = readRunHookPayload(options);
26958
+ const vms = [];
26959
+ const frontDoors = /* @__PURE__ */ new Map();
26960
+ const logStops = [];
26961
+ const exitWatches = [];
26962
+ const bareContainers = /* @__PURE__ */ new Set();
26963
+ let runInFlight;
26964
+ let containerName;
26965
+ let artifact;
26966
+ let teardownPromise;
26967
+ let shuttingDown = false;
26968
+ let waitingOnRun = false;
26969
+ const forever = () => new Promise(() => void 0);
26970
+ const doTeardown = async () => {
26971
+ for (const w of exitWatches) w.stop();
26972
+ if (runInFlight !== void 0) {
26973
+ waitingOnRun = true;
26974
+ try {
26975
+ bareContainers.add(await runInFlight);
26976
+ } catch {} finally {
26977
+ waitingOnRun = false;
26978
+ }
26979
+ }
26980
+ await Promise.allSettled(vms.map((vm) => vm.terminate()));
26981
+ for (const vm of vms) bareContainers.delete(vm.containerId);
26982
+ await Promise.allSettled([...bareContainers].map((id) => removeContainer(id)));
26983
+ await Promise.allSettled([...frontDoors.values()].map((fd) => fd.close()));
26984
+ for (const stop of logStops) try {
26985
+ stop();
26986
+ } catch (err) {
26987
+ logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
26988
+ }
26989
+ disposeMicrovmCodeArtifact(artifact);
26990
+ };
26991
+ const teardown = () => teardownPromise ??= doTeardown();
26992
+ await applyRoleArnIfSet({
26993
+ roleArn: options.roleArn,
26994
+ region: options.region,
26995
+ profile: options.profile
26996
+ });
26997
+ await ensureDockerAvailable();
26998
+ const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
26999
+ const appCmd = resolveApp(options.app);
27000
+ if (!appCmd) throw new Error(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
27001
+ logger.info("Synthesizing CDK app...");
27002
+ const synthesizer = hostSynthesizer ?? new Synthesizer();
27003
+ const context = parseContextOptions(options.context);
27004
+ const synthOpts = {
27005
+ app: appCmd,
27006
+ output: options.output,
27007
+ ...options.region && { region: options.region },
27008
+ ...options.profile && { profile: options.profile },
27009
+ ...Object.keys(context).length > 0 && { context }
27010
+ };
27011
+ const { stacks } = await synthesizer.synthesize(synthOpts);
27012
+ const resolved = resolveMicrovmImageTarget(await resolveSingleTarget(target, {
27013
+ entries: listTargets(stacks).microvmImages,
27014
+ message: "Select a Lambda MicroVM image to run",
27015
+ noun: "Lambda MicroVM images",
27016
+ onMissing: () => new CdkLocalError(`${getEmbedConfig().cliName} start-microvm requires a <target> (a MicrovmImage display path or logical ID). Run \`${getEmbedConfig().cliName} list\` to see them, or run it in a TTY to pick interactively.`, "LOCAL_START_MICROVM_TARGET_REQUIRED")
27017
+ }), stacks);
27018
+ const config = readMicrovmImageConfig(resolved.logicalId, resolved.resource);
27019
+ const platform = options.platform ?? (config.architecture !== void 0 ? architectureToPlatform(config.architecture) : void 0);
27020
+ logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.logicalId}`)} (MicroVM image ${displayIdent(config.name)}; hooks: ${Object.keys(config.hooks).join(", ") || "none"}${platform ? `; ${platform}` : ""})`);
27021
+ for (const line of microvmFidelityWarnings(config)) logger.warn(line);
27022
+ artifact = await resolveMicrovmCodeArtifact(resolved.stack, config);
27023
+ const shutdown = async (signal, exitCode) => {
27024
+ if (shuttingDown) {
27025
+ if (!waitingOnRun) {
27026
+ logger.info(`Received ${signal} again; still terminating the MicroVMs, please wait...`);
27027
+ return;
27028
+ }
27029
+ disposeMicrovmCodeArtifact(artifact);
27030
+ logger.warn(`Received ${signal} again; exiting without waiting for docker run to return. The container may still be starting, and its environment holds the forwarded AWS credentials. Remove it by its name (this works even if it is created after this exit): docker rm -f ${containerName ?? "<name>"}`);
27031
+ process.exit(signal === "SIGINT" ? 130 : 143);
27032
+ return;
27033
+ }
27034
+ shuttingDown = true;
27035
+ logger.info(`Received ${signal}, terminating ${vms.length} MicroVM(s)...`);
27036
+ await teardown();
27037
+ process.exit(exitCode);
27038
+ };
27039
+ process.on("SIGINT", () => void shutdown("SIGINT", 130));
27040
+ process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
27041
+ const recents = /* @__PURE__ */ new Map();
27042
+ let allTerminated;
27043
+ const everyVmTerminated = new Promise((r) => allTerminated = r);
27044
+ const checkAllTerminated = () => {
27045
+ if (vms.length === options.count && vms.every((v) => v.state() === "terminated")) allTerminated();
27046
+ };
27047
+ let unexpectedExit;
27048
+ const anyUnexpectedExit = new Promise((r) => unexpectedExit = r);
27049
+ const probeHost = connectHost(options.host);
27050
+ try {
27051
+ const containerEnv = await resolveLambdaContainerEnv(buildMicrovmLambdaView(resolved, config), {
27052
+ ...options,
27053
+ deployedFunctionLookups: false
27054
+ }, profileCredentials, extraStateProviders);
27055
+ const env = finalizeMicrovmEnv(containerEnv.env, config.environment);
27056
+ const image = await buildMicrovmImage({
27057
+ artifact,
27058
+ platform,
27059
+ noCache: options.buildCache === false
27060
+ });
27061
+ if (teardownPromise) return await forever();
27062
+ const containerPorts = planMicrovmPorts(config.hooksPort, await readExposedPorts(image), options.proxyPort ?? []);
27063
+ logger.info(`Publishing container ports ${containerPorts.join(", ")} to the front door(s).`);
27064
+ for (let i = 0; i < options.count; i++) {
27065
+ const microvmId = generateMicrovmId();
27066
+ const hostPorts = /* @__PURE__ */ new Map();
27067
+ for (const cp of containerPorts) hostPorts.set(cp, {
27068
+ host: "127.0.0.1",
27069
+ port: await pickFreePort()
27070
+ });
27071
+ const [first, ...rest] = containerPorts;
27072
+ const name = containerName = `${getEmbedConfig().resourceNamePrefix}-microvm-${process.pid}-${i}-${Math.random().toString(36).slice(2, 8)}`;
27073
+ logger.info(`Starting MicroVM ${microvmId} (container ${name})...`);
27074
+ runInFlight = runDetached({
27075
+ image,
27076
+ mounts: [],
27077
+ env,
27078
+ ...containerEnv.sensitiveEnvKeys.length > 0 && { sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys) },
27079
+ cmd: [],
27080
+ hostPort: hostPorts.get(first).port,
27081
+ containerPort: first,
27082
+ extraPorts: rest.map((cp) => ({
27083
+ hostPort: hostPorts.get(cp).port,
27084
+ containerPort: cp
27085
+ })),
27086
+ host: "127.0.0.1",
27087
+ ...platform !== void 0 && { platform },
27088
+ name,
27089
+ autoRemove: false
27090
+ });
27091
+ const containerId = await runInFlight;
27092
+ runInFlight = void 0;
27093
+ bareContainers.add(containerId);
27094
+ if (teardownPromise) return await forever();
27095
+ const recent = new RecentOutput();
27096
+ recents.set(containerId, recent);
27097
+ logStops.push(streamLogs(containerId, {
27098
+ onOutput: (chunk) => recent.push(chunk),
27099
+ ...options.count > 1 && { prefix: `[${microvmId.slice(0, 12)}] ` }
27100
+ }));
27101
+ const watch = watchContainerExit(containerId);
27102
+ exitWatches.push(watch);
27103
+ let exited = false;
27104
+ const vm = new LocalMicrovm({
27105
+ microvmId,
27106
+ containerId,
27107
+ ports: hostPorts,
27108
+ config,
27109
+ timeoutMs: options.timeout,
27110
+ onTerminated: checkAllTerminated
27111
+ }, { isAlive: async () => !exited });
27112
+ vms.push(vm);
27113
+ bareContainers.delete(containerId);
27114
+ watch.exited.then((code) => {
27115
+ exited = true;
27116
+ const s = vm.state();
27117
+ if (s !== "terminating" && s !== "terminated") unexpectedExit({
27118
+ vm,
27119
+ code
27120
+ });
27121
+ });
27122
+ await vm.boot(runHookPayload);
27123
+ if (teardownPromise) return await forever();
27124
+ const fd = await startMicrovmFrontDoor({
27125
+ host: options.host,
27126
+ port: options.port === 0 ? 0 : options.port + i,
27127
+ target: vm,
27128
+ autoResume: options.autoResume === true
27129
+ });
27130
+ frontDoors.set(vm, fd);
27131
+ const base = `http://${formatAuthority(probeHost, fd.port)}`;
27132
+ logger.info(`MicroVM ${microvmId} listening on ${base} (control: ${base}${MICROVM_CONTROL_PATH})`);
27133
+ }
27134
+ logger.info("Press ^C to terminate the MicroVM(s).");
27135
+ checkAllTerminated();
27136
+ } catch (err) {
27137
+ if (shuttingDown) return await forever();
27138
+ const failed = vms.at(-1);
27139
+ const wrapped = err instanceof MicrovmHookError && failed ? await containerFailure(err.message, failed.containerId, recents.get(failed.containerId) ?? new RecentOutput()).then((f) => new CdkLocalError(f.message, err.code)) : err;
27140
+ await teardown();
27141
+ throw wrapped;
27142
+ }
27143
+ const outcome = await Promise.race([everyVmTerminated.then(() => void 0), anyUnexpectedExit]);
27144
+ if (shuttingDown) return await forever();
27145
+ if (outcome === void 0) {
27146
+ logger.info("Every MicroVM is terminated; shutting down.");
27147
+ await teardown();
27148
+ return;
27149
+ }
27150
+ if (teardownPromise) return await forever();
27151
+ const failure = await containerFailure(`MicroVM ${outcome.vm.microvmId}'s container exited (exit code ${outcome.code ?? "unknown"}) while running.`, outcome.vm.containerId, recents.get(outcome.vm.containerId) ?? new RecentOutput());
27152
+ await teardown();
27153
+ throw new CdkLocalError(failure.message, "LOCAL_START_MICROVM_EXITED");
27154
+ }
27155
+ function createLocalStartMicrovmCommand(opts = {}) {
27156
+ setEmbedConfig(opts.embedConfig);
27157
+ const cmd = new Command("start-microvm").description("Run AWS Lambda MicroVMs locally. Builds a MicrovmImage code artifact (a Dockerfile plus the app, from cdk.out) once, starts --count containers with its EnvironmentVariables, drives the /ready, /validate and /run lifecycle hooks in the cloud order, and serves each MicroVM on its own local URL: X-aws-proxy-port / lambda-microvms.port.N routing (default 8080), X-aws-proxy-* stripping, and a control endpoint for suspend / resume / terminate. Target accepts a CDK display path or stack-qualified logical ID; single-stack apps may omit the prefix. Omit <target> in a TTY to pick from a list. Runs until ^C, which calls /terminate.").argument("[target]", "CDK display path or stack-qualified logical ID of the MicrovmImage (omit to pick interactively in a TTY)").action(withErrorHandling(async (target, options) => {
27158
+ await localStartMicrovmCommand(target, options, opts.extraStateProviders, opts.synthesizer);
27159
+ }));
27160
+ addStartMicrovmSpecificOptions(cmd);
27161
+ [
27162
+ ...commonOptions(),
27163
+ ...appOptions(),
27164
+ ...contextOptions
27165
+ ].forEach((opt) => cmd.addOption(opt));
27166
+ cmd.addOption(regionOption);
27167
+ return cmd;
27168
+ }
27169
+ function addStartMicrovmSpecificOptions(cmd) {
27170
+ return cmd.addOption(new Option("--count <n>", "Number of independent MicroVMs to run, each with its own URL.").default(1).argParser(parseCount)).addOption(new Option("--run-hook-payload <string>", "The runHookPayload string delivered to each MicroVM's /run hook (max 16 KB).")).addOption(new Option("--run-hook-payload-file <path>", "Read the runHookPayload from a file (mutually exclusive with --run-hook-payload).")).addOption(new Option("--port <n>", "Local port of the first MicroVM; MicroVM i listens on <n>+i. Default 0 (OS-assigned).").default(0).argParser(parsePort)).addOption(new Option("--host <ip>", "Address the MicroVM URLs listen on.").default("127.0.0.1")).addOption(new Option("--platform <platform>", "docker --platform for the build and the containers. Defaults to the image's CpuConfigurations architecture.").choices(["linux/amd64", "linux/arm64"])).addOption(new Option("--proxy-port <port>", "An extra container port X-aws-proxy-port may route to (repeatable). 8080, Hooks.Port and the image's EXPOSE ports are always routable.").argParser(parseProxyPort)).addOption(new Option("--auto-resume", "Resume a suspended MicroVM on its next request instead of answering 503 (autoResumeEnabled).")).addOption(new Option("--no-build-cache", "Build the image with docker build --no-cache.")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"MicrovmImageLogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--timeout <ms>", "Default deadline in milliseconds for each hook without its own <Hook>TimeoutInSeconds (/terminate defaults to 10000), and for the app to answer HTTP when there is no /ready hook. Default 60000.").default(6e4).argParser(parseTimeoutMs)).addOption(new Option("--assume-role <arn>", "Assume this role (the MicroVM's execution role, a run-microvm parameter rather than part of the template) and forward STS-issued temp credentials to the containers. Off by default: the shell credentials are forwarded.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Bare form uses the resolved stack name.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
25190
27171
  }
25191
27172
 
25192
27173
  //#endregion
@@ -25215,7 +27196,7 @@ async function localListCommand(options) {
25215
27196
  process.stdout.write(`${formatTargetListing(listing, getEmbedConfig().cliName, { long: options.long })}\n`);
25216
27197
  }
25217
27198
  function formatTargetListing(listing, cliName, options = {}) {
25218
- if (countTargets(listing) === 0) return `No runnable targets (Lambda functions, APIs, ECS services / tasks, AgentCore Runtimes, load balancers, CloudFront distributions) found in this CDK app.`;
27199
+ if (countTargets(listing) === 0) return `No runnable targets (Lambda functions, APIs, ECS services / tasks, AgentCore Runtimes, load balancers, CloudFront distributions, Lambda web functions, Lambda MicroVM images) found in this CDK app.`;
25219
27200
  const long = options.long ?? false;
25220
27201
  return "\n" + [
25221
27202
  formatSection("Lambda Functions", `${cliName} invoke <target>`, listing.lambdas, long),
@@ -25224,7 +27205,9 @@ function formatTargetListing(listing, cliName, options = {}) {
25224
27205
  formatSection("ECS Task Definitions", `${cliName} run-task <target>`, listing.ecsTaskDefinitions, long),
25225
27206
  formatSection("AgentCore Runtimes", `${cliName} invoke-agentcore <target>`, listing.agentCoreRuntimes, long),
25226
27207
  formatSection("Application Load Balancers", `${cliName} start-alb <target...>`, listing.loadBalancers, long),
25227
- formatSection("CloudFront Distributions", `${cliName} start-cloudfront <target>`, listing.cloudFrontDistributions, long)
27208
+ formatSection("CloudFront Distributions", `${cliName} start-cloudfront <target>`, listing.cloudFrontDistributions, long),
27209
+ formatSection("Lambda Web Functions", `${cliName} start-web-function <target>`, listing.webFunctions, long),
27210
+ formatSection("Lambda MicroVM Images", `${cliName} start-microvm <target>`, listing.microvmImages, long)
25228
27211
  ].filter((lines) => lines.length > 0).map((lines) => lines.join("\n")).join("\n\n");
25229
27212
  }
25230
27213
  function formatSection(title, command, entries, long) {
@@ -30500,5 +32483,5 @@ function addStudioSpecificOptions(cmd) {
30500
32483
  }
30501
32484
 
30502
32485
  //#endregion
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 };
30504
- //# sourceMappingURL=local-studio-CJfNARFz.js.map
32486
+ export { postHook as $, resolveDynamicReferencesInEnv as $a, architectureToPlatform as $i, parseRestartPolicy as $n, createAuthorizerCache as $r, resolveCloudFrontTarget as $t, microvmFidelityWarnings as A, pickRefLogicalId as Aa, matchPreflight as Ai, stripCloudFrontImport as An, MCP_PATH as Ar, supportedWebFunctionRuntimes as At, MICROVM_WS_BASE_PROTOCOL as B, derivePseudoParametersFromRegion as Ba, VtlEvaluationError as Bi, resolveAlbFrontDoor as Bn, downloadAndExtractS3Bundle as Br, addStartAgentCoreSpecificOptions as Bt, addListSpecificOptions as C, availableWebSocketApiIdentifiers as Ca, invokeRequestAuthorizer as Ci, pickKvsLogicalIdFromArn as Cn, createLocalInvokeAgentCoreCommand as Cr, readWebFunctionServiceConfig as Ct, buildMicrovmLambdaView as D, parseSelectionExpressionPath as Da, buildCorsConfigByApiId as Di, compileCloudFrontFunction as Dn, A2A_PATH as Dr, buildNodeEntryFileStart as Dt, addStartMicrovmSpecificOptions as E, filterWebSocketApisByIdentifiers as Ea, applyCorsResponseHeaders as Ei, resolveCloudFrontDistribution as En, A2A_CONTAINER_PORT as Er, WEB_FUNCTION_RUNTIMES as Et, microvmImageTag as F, AGENTCORE_MCP_PROTOCOL as Fa, buildRestV1Event as Fi, albStrategy as Fn, signAgentCoreInvocation as Fr, WEB_FUNCTION_ENDPOINT_TYPE as Ft, startMicrovmFrontDoor as G, DynamicReferenceResolutionError as Ga, bufferToBody as Gi, createLocalRunTaskCommand as Gn, toCmdArgv as Gr, attachAgentCoreWsBridge as Gt, PROXY_PORT_HEADER as H, substituteImagePlaceholders as Ha, HOST_GATEWAY_MIN_VERSION as Hi, createLocalStartServiceCommand as Hn, buildAgentCoreCodeImage as Hr, buildAgentCoreServeAuthCheck as Ht, planMicrovmPorts as I, AGENTCORE_RUNTIME_TYPE as Ia, evaluateResponseParameters as Ii, createLocalStartAlbCommand as In, AGENTCORE_SESSION_ID_HEADER as Ir, WEB_FUNCTION_REVISION_TYPE as It, MICROVM_HOOK_PATH_PREFIX as J, deployedFillMayBeSecret as Ja, handleConnectionsRequest as Ji, addEcsAssumeRoleOptions as Jn, createLocalInvokeCommand as Jr, addStartCloudFrontSpecificOptions as Jt, stripMicrovmProxyHeaders as K, DynamicReferenceResolver as Ka, ConnectionRegistry as Ki, MAX_TASKS_SUBNET_RANGE_CAP as Kn, classifySourceChange as Kr, startAgentCoreWsBridge as Kt, readExposedPorts as L, AgentCoreResolutionError as La, pickResponseTemplate as Li, parseLbPortOverrides as Ln, invokeAgentCore as Lr, WEB_FUNCTION_TYPE as Lt, DEFAULT_TERMINATE_HOOK_TIMEOUT_MS as M, AGENTCORE_A2A_PROTOCOL as Ma, translateLambdaResponse as Mi, createLocalFileKvsDataSource as Mn, mcpInvokeOnce as Mr, findFileAssetForObjectKey as Mt, LocalMicrovm as N, AGENTCORE_AGUI_PROTOCOL as Na, applyAuthorizerOverlay as Ni, createUnboundCloudFrontModule as Nn, parseSseForJsonRpc as Nr, parseWebFunctionBuildConfig as Nt, createLocalStartMicrovmCommand as O, webSocketApiMatchesIdentifier as Oa, buildCorsConfigFromCloudFrontChain as Oi, runViewerRequest as On, a2aInvokeOnce as Or, defaultWebFunctionPlatform as Ot, buildMicrovmImage as P, AGENTCORE_HTTP_PROTOCOL as Pa, buildHttpApiV2Event as Pi, addAlbSpecificOptions as Pn, AGENTCORE_SIGV4_SERVICE as Pr, resolveWebFunctionArtifact as Pt, pollBuildHook as Q, parseDynamicReference as Qa, buildMessageEvent as Qi, parseMaxTasks as Qn, resolveApiTargetSubset as Qr, parseOriginOverrides as Qt, setContainerPaused as R, pickAgentCoreCandidateStack as Ra, selectIntegrationResponse as Ri, resolveAlbTarget as Rn, waitForAgentCoreHttpReady as Rr, WebFunctionResolutionError as Rt, StudioEventBus as S, listTargets as Sa, evaluateCachedLambdaPolicy as Si, pickFunctionUrlLogicalIdFromOrigin as Sn, addInvokeAgentCoreSpecificOptions as Sr, finalizeWebFunctionEnv as St, formatTargetListing as T, discoverWebSocketApisOrThrow as Ta, attachAuthorizers as Ti, pickTargetFunctionLogicalId as Tn, invokeAgentCoreWs as Tr, webFunctionFidelityWarnings as Tt, rewriteUpgradeResponseHead as U, tryResolveImageFnJoin as Ua, probeHostGatewaySupport as Ui, serviceStrategy as Un, computeCodeImageTag as Ur, selectServeInboundAuth as Ut, MicrovmStateError as V, formatStateRemedy as Va, HOST_DOCKER_INTERNAL_GATEWAY as Vi, addStartServiceSpecificOptions as Vn, SUPPORTED_CODE_RUNTIMES as Vr, createLocalStartAgentCoreCommand as Vt, selectTargetPort as W, LocalInvokeBuildError as Wa, resolveHostGatewayExtraHosts as Wi, addRunTaskSpecificOptions as Wn, renderCodeDockerfile as Wr, startAgentCoreHttpServer as Wt, callRuntimeHook as X, keysNotFromTemplate as Xa, buildConnectEvent as Xi, buildEcsImageResolutionContext$1 as Xn, createLocalStartApiCommand as Xr, normalizeKvsFileKeys as Xt, MicrovmHookError as Y, firstUsableRegion as Ya, parseConnectionsPath as Yi, addImageOverrideOptions as Yn, addStartApiSpecificOptions as Yr, createLocalStartCloudFrontCommand as Yt, generateMicrovmId as Z, keysOverriddenBy as Za, buildDisconnectEvent as Zi, ecsClusterOption as Zn, createWatchPredicates as Zr, parseKvsFileOverrides as Zt, filterStudioTargetGroups as _, collectSsmParameterRefs as _a, verifyCognitoJwt as _i, httpHeadersToEdge as _n, DEFAULT_SHADOW_READY_TIMEOUT_MS as _r, startWebFunctionContainer as _t, createLocalStudioCommand as a, substituteAgainstState as aa, availableApiIdentifiers as ai, classifyS3Error as an, recordAssumedRole as ao, canonicalizeImageOverrideKeys as ar, disposeMicrovmCodeArtifact as at, renderStudioHtml as b, resolveSingleTarget as ba, buildMethodArn as bi, extractKvsAssociations as bn, getContainerNetworkIp as br, WEB_FUNCTION_HTTP_ENDPOINT_ENV as bt, startStudioProxy as c, substituteEnvVarsFromStateAsync as ca, groupRoutesByServer as ci, startCloudFrontServer as cn, workloadStsClientConfig as co, parseImageOverrideFlags as cr, resolveMicrovmImageTarget as ct, createStudioDispatcher as d, isCfnFlagPresent as da, resolveSelectionExpression as di, serveLambdaUrlOrigin as dn, buildProxyClientConfig as do, describePinnedImageUri as dr, createLocalStartWebFunctionCommand as dt, buildContainerImage as ea, createFileWatcher as ei, idFromArn as en, withoutKeys as eo, resolveEcsAssumeRoleOption as er, runHookBody as et, filterStudioCustomResources as f, rejectExplicitCfnStackWithMultipleStacks as fa, resolveServiceIntegrationParameters as fi, applyEdgeRequestResult as fn, isProxyEnvConfigured as fo, imageOverrideKeyAliases as fr, RecentOutput as ft, annotatePinnedEcsTargets as g, CfnLocalStateProvider as ga, createJwksCache as gi, edgeHeadersToHttp as gn, CloudMapRegistry as gr, readContainerLogTail as gt, annotateEcsTaskPinnedTargets as h, resolveCfnStackName as ha, buildJwksUrlFromIssuer as hi, buildEdgeResponseEvent as hn, buildCloudMapIndex as hr, planWebFunctionContainer as ht, coerceStopRequest as i, EcsTaskResolutionError as ia, resolveEnvVars$1 as ii, resolveDeployedOriginBucket as in, readEnvCallerCredentials as io, buildImageOverrideTag as ir, MicrovmResolutionError as it, readRunHookPayload as j, resolveLambdaArnIntrinsic as ja, matchRoute as ji, createCloudFrontModule as jn, MCP_PROTOCOL_VERSION as jr, disposeWebFunctionArtifact as jt, finalizeMicrovmEnv as k, discoverRoutes as ka, isFunctionUrlOacFronted as ki, runViewerResponse as kn, MCP_CONTAINER_PORT as kr, resolveWebFunctionRuntime as kt, relayServeRequest as l, LocalStateSourceError as la, readMtlsMaterialsFromDisk as li, resolveErrorResponseCandidates as ln, buildStsClientConfig as lo, resolveImageOverrides as lr, s3KeyOfUri as lt, annotateAlbPinnedBackingServices as m, resolveCfnRegion as ma, buildCognitoJwksUrl as mi, buildEdgeRequestEvent as mn, listPinnedTargets as mr, containerFailure as mt, coerceRunRequest as n, resolveRuntimeFileExtension as na, buildStageMap as ni, createDeployedKvsDataSource as nn, assumedRoleRecorded as no, runEcsServiceEmulator as nr, MICROVM_DEFAULT_INGRESS_PORT as nt, resolveServeBaseUrl as o, substituteAgainstStateAsync as oa, filterRoutesByApiIdentifier as oi, createS3OriginReader as on, resetWorkloadIdentity as oo, enforceImageOverrideOrphans as or, readMicrovmImageConfig as ot, isCustomResourceLambdaTarget as p, resolveCfnFallbackRegion as pa, defaultCredentialsLoader as pi, applyEdgeResponseResult as pn, isLocalCdkAssetImage as pr, WEB_FUNCTION_CONTAINER_PORT as pt, MAX_RUN_HOOK_PAYLOAD_BYTES as q, containsDynamicReference as qa, buildMgmtEndpointEnvUrl as qi, addCommonEcsServiceOptions as qn, addInvokeSpecificOptions as qr, LocalStartCloudFrontError as qt, coerceServeRequest as r, resolveRuntimeImage as ra, materializeLayerFromArn as ri, resolveDeployedKvsArnByName as rn, deployAccountStsClientConfig as ro, ImageOverrideError as rr, MICROVM_IMAGE_TYPE as rt, createStudioServeManager as s, substituteEnvVarsFromState as sa, filterRoutesByApiIdentifiers as si, matchBehavior as sn, workloadClientConfig as so, mergeForService as sr, resolveMicrovmCodeArtifact as st, addStudioSpecificOptions as t, resolveRuntimeCodeMountPath as ta, attachStageContext as ti, resolveKvsModulesForDistribution as tn, SECOND_ASSUME_MESSAGE as to, resolveSharedSidecarCredentials as tr, waitForAppAnswer as tt, reinvoke as u, createLocalStateProvider as ua, startApiServer as ui, serveFromStaticOrigin as un, resolveProfileCredentials as uo, runImageOverrideBuilds as ur, addStartWebFunctionSpecificOptions as ut, startStudioServer as v, resolveSsmParameters as va, verifyJwtAuthorizer as vi, CLOUDFRONT_DISTRIBUTION_TYPE as vn, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as vr, waitForWebFunctionReady as vt, createLocalListCommand as w, discoverWebSocketApis as wa, invokeTokenAuthorizer as wi, pickLambdaEdgeFunctionLogicalId as wn, bridgeAgentCoreWs as wr, webFunctionAssumeRole as wt, createStudioStore as x, countTargets as xa, computeRequestIdentityHash as xi, isCloudFrontDistribution as xn, attachContainerLogStreamer as xr, buildWebFunctionLambdaView as xt, toStudioTargetGroups as y, resolveWatchConfig as ya, verifyJwtViaDiscovery as yi, describeS3OriginDomain as yn, setShadowReadyTimeoutMs as yr, watchContainerExit as yt, MICROVM_CONTROL_PATH as z, resolveAgentCoreTarget as za, tryParseStatus as zi, isApplicationLoadBalancer as zn, waitForAgentCorePing as zr, resolveWebFunctionTarget as zt };
32487
+ //# sourceMappingURL=local-studio-BhFxp-jM.js.map