cdk-local 0.156.0 → 0.157.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +147 -98
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/{local-studio-BhFxp-jM.js → local-studio-DKMdamzH.js} +1982 -1617
- package/dist/local-studio-DKMdamzH.js.map +1 -0
- package/dist/local-studio-DXVr171Y.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/local-studio-BhFxp-jM.js.map +0 -1
|
@@ -15492,7 +15492,7 @@ async function resolveAgentCoreCodeImage(resolved, code, options, architecture,
|
|
|
15492
15492
|
const loader = new AssetManifestLoader();
|
|
15493
15493
|
const manifest = await loader.loadStackManifest(resolved.stack, cdkOutDir);
|
|
15494
15494
|
const fileAssets = manifest ? loader.getFileAssets(manifest) : void 0;
|
|
15495
|
-
const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey
|
|
15495
|
+
const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey(fileAssets, code.codeAssetHash) : void 0;
|
|
15496
15496
|
if (!asset) throw new CdkLocalError(`AgentCore Runtime ${displayIdent(resolved.logicalId)} code bundle (asset ${displayIdent(code.codeAssetHash)}) was not found in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of a fromCodeAsset bundle — re-synthesize the app so the asset is staged in cdk.out and retry. (A fromS3 bundle is downloaded from S3 instead; this runtime has no literal Code.S3.Bucket.)`, "LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND");
|
|
15497
15497
|
const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset, {
|
|
15498
15498
|
assetOutdir: assetPathDirs(resolved.stack).assetOutdir,
|
|
@@ -15542,7 +15542,7 @@ async function resolveAgentCoreCodeImageFromS3(resolved, code, s3Source, options
|
|
|
15542
15542
|
await bundle.cleanup();
|
|
15543
15543
|
}
|
|
15544
15544
|
}
|
|
15545
|
-
function findFileAssetByObjectKey
|
|
15545
|
+
function findFileAssetByObjectKey(fileAssets, hash) {
|
|
15546
15546
|
const zip = `${hash}.zip`;
|
|
15547
15547
|
for (const asset of fileAssets.values()) if (Object.values(asset.destinations).some((d) => d.objectKey === zip || d.objectKey.endsWith(`/${zip}`))) return asset;
|
|
15548
15548
|
}
|
|
@@ -22464,6 +22464,93 @@ function addAlbSpecificOptions(cmd) {
|
|
|
22464
22464
|
return cmd.addOption(new Option("--lb-port <listenerPort=hostPort...>", "Bind the local front-door on a specific host port (e.g. 80=8080); repeatable. Default: host port == ALB listener port. Use this on macOS to remap a privileged listener port (< 1024) to a non-privileged host port.")).addOption(new Option("--tls", "Terminate TLS locally for cloud-HTTPS listeners. Default: a cloud-HTTPS listener is served over plain HTTP locally (X-Forwarded-Proto: https is preserved so the upstream app still sees the deployed listener protocol). Implied by --tls-cert / --tls-key. Use this when local-dev cookies need Secure / SameSite=None, when the upstream app inspects TLS metadata, or for mTLS / SNI testing — otherwise plain HTTP is friendlier (no self-signed cert warnings in curl / browser).")).addOption(new Option("--tls-cert <path>", "PEM-encoded server certificate for HTTPS front-door listeners. Implies --tls. Must be set together with --tls-key. Pass --tls alone (without --tls-cert / --tls-key) to auto-generate a self-signed cert (cached under $XDG_CACHE_HOME/cdk-local/alb-https/, default ~/.cache/cdk-local/alb-https/); requires openssl on PATH. The deployed Listener Certificates[] are NOT fetched (ACM private keys are not retrievable by design). The auto-generated cert lists DNS:localhost,IP:127.0.0.1 as SubjectAltName, so a client validating a non-loopback --container-host will fail the SAN check — pass --tls-cert / --tls-key with a SAN covering that host instead.")).addOption(new Option("--tls-key <path>", "PEM-encoded server private key matching --tls-cert. Implies --tls. Must be set together with --tls-cert.")).addOption(new Option("--no-verify-auth", "Disable local enforcement of authenticate-cognito / authenticate-oidc actions. Every request is served as if the auth check passed. Useful for local dev where you do not want to mint a Bearer token at all.")).addOption(new Option("--bearer-token <jwt>", "Default Bearer JWT this command INJECTS only when an inbound request has none (the default-when-missing role) — `cdkl start-alb` is the local ALB front-door RECEIVING outside-in requests, so this token is the fallback the front-door slots in as Authorization: Bearer <jwt> if the caller did not already supply one. Verified against the same JWKS / OIDC discovery URL the deployed ALB would (signature + iss + aud + exp). Cookie pass-through (AWSELBAuthSessionCookie-*) also bypasses the guard. Contrast with `cdkl invoke-agentcore --bearer-token`, where the role is reversed — that command is the outbound client and ALWAYS presents this token (the supplier).")).addOption(new Option("--watch", "Hot-reload: re-synth + per-replica reload of every ECS service behind the ALB when the CDK source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). A per-firing classifier picks the per-replica primitive: source-only edits on interpreted-language handlers (Node/Python/Ruby/shell) take a bind-mount FAST PATH (`docker cp` the new source into each replica + restart it; no rebuild, its front-door pool entry is drained and re-published once it listens again). Dockerfile / dependency manifest / compiled-language source / ambiguous edits fall through to the rebuild rolling primitive — boot a shadow under a bumped generation suffix, wait for its container port to accept a TCP connection, atomically register it in the front-door pool, then drop the old entry and retire the old container. Either path rolls one replica at a time, so a continuous external request stream against the listener port sees zero connection refusals across the reload. The host front-door (TLS, JWKS cache, Lambda-target containers, listener sockets) stays up across the reload. Lambda target groups behind the ALB are a no-op on reload (the warm RIE container keeps its boot-time image). Off by default; existing replica(s) keep serving when synth fails mid-reload.").default(false));
|
|
22465
22465
|
}
|
|
22466
22466
|
|
|
22467
|
+
//#endregion
|
|
22468
|
+
//#region src/local/s3-object-asset.ts
|
|
22469
|
+
function findFileAssetForObjectKey(manifest, key) {
|
|
22470
|
+
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 {
|
|
22471
|
+
hash,
|
|
22472
|
+
asset
|
|
22473
|
+
};
|
|
22474
|
+
const hash = key.replace(/^.*\//, "").replace(/\.zip$/, "");
|
|
22475
|
+
const byHash = Object.hasOwn(manifest.files ?? {}, hash) ? manifest.files[hash] : void 0;
|
|
22476
|
+
return byHash ? {
|
|
22477
|
+
hash,
|
|
22478
|
+
asset: byHash
|
|
22479
|
+
} : void 0;
|
|
22480
|
+
}
|
|
22481
|
+
function isBucketDeploymentResource(resource) {
|
|
22482
|
+
return resource !== void 0 && String(resource.Type).startsWith("Custom::CDKBucketDeployment");
|
|
22483
|
+
}
|
|
22484
|
+
function bucketDeploymentSourceObjectKeys(resource) {
|
|
22485
|
+
const keys = resource.Properties?.["SourceObjectKeys"];
|
|
22486
|
+
return Array.isArray(keys) ? keys : [];
|
|
22487
|
+
}
|
|
22488
|
+
function resolveBucketDeploymentObjectKey(template, value) {
|
|
22489
|
+
const select = asObject$3(value)?.["Fn::Select"];
|
|
22490
|
+
if (!Array.isArray(select) || select.length !== 2) return void 0;
|
|
22491
|
+
const index = selectIndex(select[0]);
|
|
22492
|
+
const deploymentLogicalId = sourceObjectKeysGetAttTarget(select[1]);
|
|
22493
|
+
if (index === void 0 || deploymentLogicalId === void 0) return void 0;
|
|
22494
|
+
const resources = template.Resources ?? {};
|
|
22495
|
+
const deployment = Object.hasOwn(resources, deploymentLogicalId) ? resources[deploymentLogicalId] : void 0;
|
|
22496
|
+
if (!deployment || !isBucketDeploymentResource(deployment)) return void 0;
|
|
22497
|
+
const props = deployment.Properties ?? {};
|
|
22498
|
+
if (handlerFlag(props["Extract"])) return {
|
|
22499
|
+
kind: "extracts",
|
|
22500
|
+
deploymentLogicalId,
|
|
22501
|
+
index
|
|
22502
|
+
};
|
|
22503
|
+
if (!handlerFlag(props["OutputObjectKeys"])) return {
|
|
22504
|
+
kind: "object-keys-not-output",
|
|
22505
|
+
deploymentLogicalId,
|
|
22506
|
+
index
|
|
22507
|
+
};
|
|
22508
|
+
const prefix = props["DestinationBucketKeyPrefix"];
|
|
22509
|
+
if (prefix !== void 0 && prefix !== "" && prefix !== "/") return {
|
|
22510
|
+
kind: "prefixed-destination",
|
|
22511
|
+
deploymentLogicalId,
|
|
22512
|
+
index
|
|
22513
|
+
};
|
|
22514
|
+
const keys = bucketDeploymentSourceObjectKeys(deployment);
|
|
22515
|
+
if (index >= keys.length) return {
|
|
22516
|
+
kind: "index-out-of-range",
|
|
22517
|
+
deploymentLogicalId,
|
|
22518
|
+
index,
|
|
22519
|
+
sourceCount: keys.length
|
|
22520
|
+
};
|
|
22521
|
+
const sourceObjectKey = keys[index];
|
|
22522
|
+
if (typeof sourceObjectKey !== "string" || sourceObjectKey.length === 0) return {
|
|
22523
|
+
kind: "source-key-not-literal",
|
|
22524
|
+
deploymentLogicalId,
|
|
22525
|
+
index
|
|
22526
|
+
};
|
|
22527
|
+
return {
|
|
22528
|
+
kind: "resolved",
|
|
22529
|
+
deploymentLogicalId,
|
|
22530
|
+
index,
|
|
22531
|
+
sourceObjectKey
|
|
22532
|
+
};
|
|
22533
|
+
}
|
|
22534
|
+
function handlerFlag(value) {
|
|
22535
|
+
if (value === void 0 || value === true || value === "true") return true;
|
|
22536
|
+
return typeof value === "object" && value !== null;
|
|
22537
|
+
}
|
|
22538
|
+
function selectIndex(value) {
|
|
22539
|
+
const n = typeof value === "number" ? value : typeof value === "string" && /^\d+$/.test(value) ? Number(value) : NaN;
|
|
22540
|
+
return Number.isSafeInteger(n) && n >= 0 ? n : void 0;
|
|
22541
|
+
}
|
|
22542
|
+
function sourceObjectKeysGetAttTarget(value) {
|
|
22543
|
+
const getAtt = asObject$3(value)?.["Fn::GetAtt"];
|
|
22544
|
+
if (Array.isArray(getAtt)) return getAtt.length === 2 && typeof getAtt[0] === "string" && getAtt[1] === "SourceObjectKeys" ? getAtt[0] : void 0;
|
|
22545
|
+
if (typeof getAtt === "string" && getAtt.endsWith(".SourceObjectKeys")) {
|
|
22546
|
+
const id = getAtt.slice(0, -17);
|
|
22547
|
+
return id.length > 0 && !id.includes(".") ? id : void 0;
|
|
22548
|
+
}
|
|
22549
|
+
}
|
|
22550
|
+
function asObject$3(value) {
|
|
22551
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
22552
|
+
}
|
|
22553
|
+
|
|
22467
22554
|
//#endregion
|
|
22468
22555
|
//#region src/local/cloudfront-kvs.ts
|
|
22469
22556
|
function createCloudFrontModule(sources) {
|
|
@@ -22765,6 +22852,177 @@ function decodePart(s) {
|
|
|
22765
22852
|
}
|
|
22766
22853
|
}
|
|
22767
22854
|
|
|
22855
|
+
//#endregion
|
|
22856
|
+
//#region src/local/web-function-resolver.ts
|
|
22857
|
+
const WEB_FUNCTION_TYPE = "AWS::Lambda::WebFunction";
|
|
22858
|
+
const WEB_FUNCTION_REVISION_TYPE = "AWS::Lambda::WebFunctionRevision";
|
|
22859
|
+
const WEB_FUNCTION_ENDPOINT_TYPE = "AWS::Lambda::WebFunctionEndpoint";
|
|
22860
|
+
const WEB_TYPES = [
|
|
22861
|
+
WEB_FUNCTION_ENDPOINT_TYPE,
|
|
22862
|
+
WEB_FUNCTION_TYPE,
|
|
22863
|
+
WEB_FUNCTION_REVISION_TYPE
|
|
22864
|
+
];
|
|
22865
|
+
var WebFunctionResolutionError = class WebFunctionResolutionError extends Error {
|
|
22866
|
+
constructor(message) {
|
|
22867
|
+
super(message);
|
|
22868
|
+
this.name = "WebFunctionResolutionError";
|
|
22869
|
+
Object.setPrototypeOf(this, WebFunctionResolutionError.prototype);
|
|
22870
|
+
}
|
|
22871
|
+
};
|
|
22872
|
+
function resolveWebFunctionTarget(target, stacks, revisionFlag) {
|
|
22873
|
+
if (stacks.length === 0) throw new WebFunctionResolutionError("No stacks found in the synthesized assembly.");
|
|
22874
|
+
const parsed = parseTarget(target);
|
|
22875
|
+
const stack = pickStack$1(parsed, stacks, target);
|
|
22876
|
+
const resources = stack.template.Resources ?? {};
|
|
22877
|
+
const { logicalId, resource } = matchWebResource(parsed, target, stack, resources);
|
|
22878
|
+
const functions = logicalIdsOfType(resources, WEB_FUNCTION_TYPE);
|
|
22879
|
+
const linked = (value) => linkedFunctionLogicalId(value, resources, functions);
|
|
22880
|
+
let functionLogicalId;
|
|
22881
|
+
let endpoint;
|
|
22882
|
+
let revisionLogicalId;
|
|
22883
|
+
let selection;
|
|
22884
|
+
if (resource.Type === "AWS::Lambda::WebFunctionRevision") {
|
|
22885
|
+
functionLogicalId = linked(resource.Properties?.["FunctionName"]);
|
|
22886
|
+
revisionLogicalId = logicalId;
|
|
22887
|
+
selection = "target-revision";
|
|
22888
|
+
} else if (resource.Type === "AWS::Lambda::WebFunctionEndpoint") {
|
|
22889
|
+
functionLogicalId = linked(resource.Properties?.["FunctionName"]);
|
|
22890
|
+
endpoint = readEndpoint(logicalId, resource, resources);
|
|
22891
|
+
} else functionLogicalId = logicalId;
|
|
22892
|
+
const revisionsOfFunction = () => functionLogicalId === void 0 ? [] : logicalIdsOfType(resources, WEB_FUNCTION_REVISION_TYPE).filter((id) => linked(resources[id]?.Properties?.["FunctionName"]) === functionLogicalId);
|
|
22893
|
+
const endpointsOfFunction = () => functionLogicalId === void 0 ? [] : logicalIdsOfType(resources, WEB_FUNCTION_ENDPOINT_TYPE).filter((id) => linked(resources[id]?.Properties?.["FunctionName"]) === functionLogicalId);
|
|
22894
|
+
if (revisionFlag !== void 0) {
|
|
22895
|
+
const flagged = resources[revisionFlag];
|
|
22896
|
+
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)}`);
|
|
22897
|
+
const owner = linked(flagged.Properties?.["FunctionName"]);
|
|
22898
|
+
if (functionLogicalId !== void 0 && owner !== functionLogicalId) throw new WebFunctionResolutionError(`--revision ${displayIdent(revisionFlag)} belongs to a different web function than ${displayIdent(logicalId)}. ${describeRevisions(revisionsOfFunction(), stack)}`);
|
|
22899
|
+
functionLogicalId ??= owner;
|
|
22900
|
+
revisionLogicalId = revisionFlag;
|
|
22901
|
+
selection = "revision-flag";
|
|
22902
|
+
}
|
|
22903
|
+
if (revisionLogicalId === void 0 && endpoint !== void 0) {
|
|
22904
|
+
revisionLogicalId = pickHighestWeight(endpoint);
|
|
22905
|
+
if (revisionLogicalId !== void 0) selection = "endpoint-weight";
|
|
22906
|
+
}
|
|
22907
|
+
if (revisionLogicalId === void 0) {
|
|
22908
|
+
const revisions = revisionsOfFunction();
|
|
22909
|
+
if (revisions.length === 1) {
|
|
22910
|
+
revisionLogicalId = revisions[0];
|
|
22911
|
+
selection = "only-revision";
|
|
22912
|
+
} else if (endpoint === void 0 && revisions.length > 1) {
|
|
22913
|
+
const endpoints = endpointsOfFunction();
|
|
22914
|
+
if (endpoints.length === 1) {
|
|
22915
|
+
endpoint = readEndpoint(endpoints[0], resources[endpoints[0]], resources);
|
|
22916
|
+
revisionLogicalId = pickHighestWeight(endpoint);
|
|
22917
|
+
if (revisionLogicalId !== void 0) selection = "endpoint-weight";
|
|
22918
|
+
}
|
|
22919
|
+
}
|
|
22920
|
+
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)}`);
|
|
22921
|
+
}
|
|
22922
|
+
if (endpoint === void 0) {
|
|
22923
|
+
const routing = endpointsOfFunction().map((id) => readEndpoint(id, resources[id], resources)).filter((e) => e.weights.some((w) => w.revisionLogicalId === revisionLogicalId));
|
|
22924
|
+
if (routing.length === 1) endpoint = routing[0];
|
|
22925
|
+
}
|
|
22926
|
+
const fnName = (functionLogicalId !== void 0 ? resources[functionLogicalId] : void 0)?.Properties?.["FunctionName"];
|
|
22927
|
+
return {
|
|
22928
|
+
stack,
|
|
22929
|
+
functionLogicalId,
|
|
22930
|
+
functionName: typeof fnName === "string" && fnName.length > 0 ? fnName : functionLogicalId ?? revisionLogicalId,
|
|
22931
|
+
revisionLogicalId,
|
|
22932
|
+
revision: resources[revisionLogicalId],
|
|
22933
|
+
endpoint,
|
|
22934
|
+
selection: selection ?? "only-revision"
|
|
22935
|
+
};
|
|
22936
|
+
}
|
|
22937
|
+
function pickStack$1(parsed, stacks, target) {
|
|
22938
|
+
const selection = selectTargetStack(parsed, target, stacks);
|
|
22939
|
+
switch (selection.kind) {
|
|
22940
|
+
case "found": return selection.stack;
|
|
22941
|
+
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)}.`);
|
|
22942
|
+
case "not-found": throw new WebFunctionResolutionError(`Stack ${displayStackName(selection.pattern)} not found. Available stacks: ${describeStacks(stacks)}.`);
|
|
22943
|
+
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.`);
|
|
22944
|
+
}
|
|
22945
|
+
}
|
|
22946
|
+
function matchWebResource(parsed, target, stack, resources) {
|
|
22947
|
+
if (parsed.isPath) {
|
|
22948
|
+
const matches = resolveCdkPathToLogicalIds(parsed.pathOrId, buildCdkPathIndex(stack.template)).filter(({ logicalId }) => WEB_TYPES.includes(resources[logicalId]?.Type ?? ""));
|
|
22949
|
+
for (const type of WEB_TYPES) {
|
|
22950
|
+
const ofType = matches.filter(({ logicalId }) => resources[logicalId].Type === type);
|
|
22951
|
+
if (ofType.length === 1) {
|
|
22952
|
+
const id = ofType[0].logicalId;
|
|
22953
|
+
return {
|
|
22954
|
+
logicalId: id,
|
|
22955
|
+
resource: resources[id]
|
|
22956
|
+
};
|
|
22957
|
+
}
|
|
22958
|
+
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.`);
|
|
22959
|
+
}
|
|
22960
|
+
throw notFoundError(target, stack, resources);
|
|
22961
|
+
}
|
|
22962
|
+
const resource = resources[parsed.pathOrId];
|
|
22963
|
+
if (!resource) throw notFoundError(target, stack, resources);
|
|
22964
|
+
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.`);
|
|
22965
|
+
return {
|
|
22966
|
+
logicalId: parsed.pathOrId,
|
|
22967
|
+
resource
|
|
22968
|
+
};
|
|
22969
|
+
}
|
|
22970
|
+
function notFoundError(target, stack, resources) {
|
|
22971
|
+
const available = WEB_TYPES.flatMap((t) => logicalIdsOfType(resources, t));
|
|
22972
|
+
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)}.`;
|
|
22973
|
+
return new WebFunctionResolutionError(`Target ${displayStackName(target)} not found. ${hint}`);
|
|
22974
|
+
}
|
|
22975
|
+
function logicalIdsOfType(resources, type) {
|
|
22976
|
+
return Object.keys(resources).filter((id) => resources[id]?.Type === type);
|
|
22977
|
+
}
|
|
22978
|
+
function linkedFunctionLogicalId(value, resources, functions) {
|
|
22979
|
+
const ref = referencedLogicalId(value);
|
|
22980
|
+
if (ref !== void 0) return functions.includes(ref) ? ref : void 0;
|
|
22981
|
+
if (typeof value !== "string") return void 0;
|
|
22982
|
+
const byName = functions.filter((id) => resources[id]?.Properties?.["FunctionName"] === value);
|
|
22983
|
+
return byName.length === 1 ? byName[0] : void 0;
|
|
22984
|
+
}
|
|
22985
|
+
function referencedLogicalId(value) {
|
|
22986
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
22987
|
+
const obj = value;
|
|
22988
|
+
if (typeof obj["Ref"] === "string") return obj["Ref"];
|
|
22989
|
+
const getAtt = obj["Fn::GetAtt"];
|
|
22990
|
+
if (Array.isArray(getAtt) && typeof getAtt[0] === "string") return getAtt[0];
|
|
22991
|
+
if (typeof getAtt === "string") return getAtt.split(".")[0];
|
|
22992
|
+
}
|
|
22993
|
+
function readEndpoint(logicalId, resource, resources) {
|
|
22994
|
+
const props = resource.Properties ?? {};
|
|
22995
|
+
const rawWeights = props["RevisionWeights"];
|
|
22996
|
+
const weights = [];
|
|
22997
|
+
if (Array.isArray(rawWeights)) for (const entry of rawWeights) {
|
|
22998
|
+
if (!entry || typeof entry !== "object") continue;
|
|
22999
|
+
const e = entry;
|
|
23000
|
+
const ref = referencedLogicalId(e["RevisionId"]);
|
|
23001
|
+
weights.push({
|
|
23002
|
+
revisionLogicalId: ref !== void 0 && resources[ref]?.Type === "AWS::Lambda::WebFunctionRevision" ? ref : void 0,
|
|
23003
|
+
weight: typeof e["Weight"] === "number" ? e["Weight"] : 0
|
|
23004
|
+
});
|
|
23005
|
+
}
|
|
23006
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
23007
|
+
return {
|
|
23008
|
+
logicalId,
|
|
23009
|
+
endpointType: str(props["EndpointType"]),
|
|
23010
|
+
authType: str(props["AuthType"]),
|
|
23011
|
+
weights
|
|
23012
|
+
};
|
|
23013
|
+
}
|
|
23014
|
+
function pickHighestWeight(endpoint) {
|
|
23015
|
+
let best;
|
|
23016
|
+
for (const w of endpoint.weights) {
|
|
23017
|
+
if (w.revisionLogicalId === void 0) continue;
|
|
23018
|
+
if (best === void 0 || w.weight > best.weight) best = w;
|
|
23019
|
+
}
|
|
23020
|
+
return best?.revisionLogicalId;
|
|
23021
|
+
}
|
|
23022
|
+
function describeRevisions(revisions, stack) {
|
|
23023
|
+
return revisions.length > 0 ? `Revisions: ${revisions.map((id) => displayIdent(id)).join(", ")}.` : `No ${WEB_FUNCTION_REVISION_TYPE} linked to it in ${displayStackName(stack.stackName)}.`;
|
|
23024
|
+
}
|
|
23025
|
+
|
|
22768
23026
|
//#endregion
|
|
22769
23027
|
//#region src/local/cloudfront-resolver.ts
|
|
22770
23028
|
const CLOUDFRONT_DISTRIBUTION_TYPE = "AWS::CloudFront::Distribution";
|
|
@@ -22949,6 +23207,18 @@ function resolveOrigins(dc, template, stack, overrides) {
|
|
|
22949
23207
|
out.set(originId, lambdaUrl);
|
|
22950
23208
|
continue;
|
|
22951
23209
|
}
|
|
23210
|
+
const endpointLogicalId = pickWebFunctionEndpointLogicalIdFromOrigin(origin["DomainName"], template);
|
|
23211
|
+
if (endpointLogicalId) {
|
|
23212
|
+
const customConfig = origin["CustomOriginConfig"];
|
|
23213
|
+
const readTimeout = customConfig && typeof customConfig === "object" ? customConfig["OriginReadTimeout"] : void 0;
|
|
23214
|
+
out.set(originId, {
|
|
23215
|
+
kind: "web-function",
|
|
23216
|
+
originId,
|
|
23217
|
+
endpointLogicalId,
|
|
23218
|
+
...typeof readTimeout === "number" && readTimeout > 0 && { readTimeoutSeconds: readTimeout }
|
|
23219
|
+
});
|
|
23220
|
+
continue;
|
|
23221
|
+
}
|
|
22952
23222
|
out.set(originId, {
|
|
22953
23223
|
kind: "custom",
|
|
22954
23224
|
originId,
|
|
@@ -23017,13 +23287,11 @@ function resolveBucketDeploymentDirs(template, manifest, stack, bucketLogicalId)
|
|
|
23017
23287
|
const { manifestDir, assetOutdir } = assetPathDirs(stack);
|
|
23018
23288
|
const dirs = [];
|
|
23019
23289
|
for (const resource of Object.values(template.Resources ?? {})) {
|
|
23020
|
-
if (!
|
|
23021
|
-
|
|
23022
|
-
|
|
23023
|
-
const keys = Array.isArray(props["SourceObjectKeys"]) ? props["SourceObjectKeys"] : [];
|
|
23024
|
-
for (const key of keys) {
|
|
23290
|
+
if (!isBucketDeploymentResource(resource)) continue;
|
|
23291
|
+
if (refLogicalId((resource.Properties ?? {})["DestinationBucketName"]) !== bucketLogicalId) continue;
|
|
23292
|
+
for (const key of bucketDeploymentSourceObjectKeys(resource)) {
|
|
23025
23293
|
if (typeof key !== "string") continue;
|
|
23026
|
-
const asset =
|
|
23294
|
+
const asset = findFileAssetForObjectKey(manifest, key)?.asset;
|
|
23027
23295
|
if (!asset) continue;
|
|
23028
23296
|
dirs.push(resolveAssetSourcePath({
|
|
23029
23297
|
manifestDir,
|
|
@@ -23040,11 +23308,6 @@ function resolveBucketDeploymentDirs(template, manifest, stack, bucketLogicalId)
|
|
|
23040
23308
|
}
|
|
23041
23309
|
return dirs;
|
|
23042
23310
|
}
|
|
23043
|
-
function findFileAssetByObjectKey(manifest, objectKey) {
|
|
23044
|
-
for (const asset of Object.values(manifest.files ?? {})) for (const dest of Object.values(asset.destinations ?? {})) if (dest.objectKey === objectKey) return asset;
|
|
23045
|
-
const hash = objectKey.replace(/\.zip$/, "");
|
|
23046
|
-
return manifest.files?.[hash];
|
|
23047
|
-
}
|
|
23048
23311
|
function pickBucketLogicalIdFromOrigin(origin, template) {
|
|
23049
23312
|
const candidate = getAttLogicalId(origin["DomainName"]);
|
|
23050
23313
|
if (!candidate) return void 0;
|
|
@@ -23072,6 +23335,11 @@ function resolveLambdaUrlOrigin(origin, template, originId) {
|
|
|
23072
23335
|
functionUrlLogicalId: urlLogicalId
|
|
23073
23336
|
};
|
|
23074
23337
|
}
|
|
23338
|
+
function pickWebFunctionEndpointLogicalIdFromOrigin(value, template) {
|
|
23339
|
+
const getAtt = asIntrinsic(value, "Fn::GetAtt");
|
|
23340
|
+
if (!Array.isArray(getAtt) || getAtt.length !== 2 || typeof getAtt[0] !== "string" || getAtt[1] !== "DomainName") return;
|
|
23341
|
+
return (template.Resources ?? {})[getAtt[0]]?.Type === "AWS::Lambda::WebFunctionEndpoint" ? getAtt[0] : void 0;
|
|
23342
|
+
}
|
|
23075
23343
|
function pickFunctionUrlLogicalIdFromOrigin(value) {
|
|
23076
23344
|
const select = asIntrinsic(value, "Fn::Select");
|
|
23077
23345
|
if (!Array.isArray(select) || select.length !== 2 || select[0] !== 2) return void 0;
|
|
@@ -23388,6 +23656,87 @@ function normalizeHeaders(headers) {
|
|
|
23388
23656
|
return out;
|
|
23389
23657
|
}
|
|
23390
23658
|
|
|
23659
|
+
//#endregion
|
|
23660
|
+
//#region src/local/cloudfront-web-function-origin.ts
|
|
23661
|
+
const DEFAULT_ORIGIN_READ_TIMEOUT_SECONDS = 30;
|
|
23662
|
+
function serveWebFunctionOrigin(args) {
|
|
23663
|
+
const { target, request: request$3 } = args;
|
|
23664
|
+
const timeoutMs = (args.readTimeoutSeconds ?? 30) * 1e3;
|
|
23665
|
+
const headers = buildOriginRequestHeaders(request$3, target);
|
|
23666
|
+
if (request$3.body.length > 0) headers["content-length"] = String(request$3.body.length);
|
|
23667
|
+
const path = request$3.querystring ? `${request$3.uri}?${request$3.querystring}` : request$3.uri;
|
|
23668
|
+
return new Promise((resolveResult) => {
|
|
23669
|
+
let settled = false;
|
|
23670
|
+
const settle = (result) => {
|
|
23671
|
+
if (settled) return;
|
|
23672
|
+
settled = true;
|
|
23673
|
+
resolveResult(result);
|
|
23674
|
+
};
|
|
23675
|
+
const req = request({
|
|
23676
|
+
host: target.host,
|
|
23677
|
+
port: target.port,
|
|
23678
|
+
method: request$3.method,
|
|
23679
|
+
path,
|
|
23680
|
+
headers,
|
|
23681
|
+
agent: false
|
|
23682
|
+
}, (res) => {
|
|
23683
|
+
const chunks = [];
|
|
23684
|
+
res.on("data", (c) => chunks.push(c));
|
|
23685
|
+
res.on("end", () => settle({
|
|
23686
|
+
statusCode: res.statusCode ?? 502,
|
|
23687
|
+
...splitResponseHeaders(res.headers),
|
|
23688
|
+
body: Buffer.concat(chunks)
|
|
23689
|
+
}));
|
|
23690
|
+
res.on("error", (err) => settle(originError(502, err.message)));
|
|
23691
|
+
});
|
|
23692
|
+
req.setTimeout(timeoutMs, () => {
|
|
23693
|
+
settle(originError(504, `the web function server did not answer within the origin read timeout (${timeoutMs / 1e3}s)`));
|
|
23694
|
+
req.destroy();
|
|
23695
|
+
});
|
|
23696
|
+
req.on("error", (err) => settle(originError(502, err.message)));
|
|
23697
|
+
req.end(request$3.body.length > 0 ? request$3.body : void 0);
|
|
23698
|
+
});
|
|
23699
|
+
}
|
|
23700
|
+
function buildOriginRequestHeaders(request, target) {
|
|
23701
|
+
const headers = {};
|
|
23702
|
+
for (const [name, value] of Object.entries(request.headers)) if (value !== void 0) headers[name.toLowerCase()] = value;
|
|
23703
|
+
stripHopByHopHeaders(headers);
|
|
23704
|
+
delete headers["content-length"];
|
|
23705
|
+
headers["host"] = formatAuthority(target.host, target.port);
|
|
23706
|
+
if (request.sourceIp !== void 0) {
|
|
23707
|
+
const prior = headers["x-forwarded-for"];
|
|
23708
|
+
const priorValue = Array.isArray(prior) ? prior.join(", ") : prior;
|
|
23709
|
+
headers["x-forwarded-for"] = priorValue ? `${priorValue}, ${request.sourceIp}` : request.sourceIp;
|
|
23710
|
+
}
|
|
23711
|
+
const out = {};
|
|
23712
|
+
for (const [name, value] of Object.entries(headers)) if (value !== void 0) out[name] = value;
|
|
23713
|
+
return out;
|
|
23714
|
+
}
|
|
23715
|
+
function splitResponseHeaders(raw) {
|
|
23716
|
+
const copy = { ...raw };
|
|
23717
|
+
stripHopByHopHeaders(copy);
|
|
23718
|
+
delete copy["content-length"];
|
|
23719
|
+
const cookies = copy["set-cookie"];
|
|
23720
|
+
delete copy["set-cookie"];
|
|
23721
|
+
const headers = {};
|
|
23722
|
+
for (const [name, value] of Object.entries(copy)) {
|
|
23723
|
+
if (value === void 0) continue;
|
|
23724
|
+
headers[name] = Array.isArray(value) ? value.join(", ") : value;
|
|
23725
|
+
}
|
|
23726
|
+
return {
|
|
23727
|
+
headers,
|
|
23728
|
+
cookies: cookies === void 0 ? [] : Array.isArray(cookies) ? cookies : [cookies]
|
|
23729
|
+
};
|
|
23730
|
+
}
|
|
23731
|
+
function originError(statusCode, detail) {
|
|
23732
|
+
return {
|
|
23733
|
+
statusCode,
|
|
23734
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
23735
|
+
cookies: [],
|
|
23736
|
+
body: Buffer.from(`${statusCode === 502 ? "Bad Gateway" : "Gateway Timeout"}: ${detail.replace(/[\r\n]+/g, " ")}\n`)
|
|
23737
|
+
};
|
|
23738
|
+
}
|
|
23739
|
+
|
|
23391
23740
|
//#endregion
|
|
23392
23741
|
//#region src/local/cloudfront-static-origin.ts
|
|
23393
23742
|
function serveFromStaticOrigin(input) {
|
|
@@ -23557,9 +23906,10 @@ async function startCloudFrontServer(options) {
|
|
|
23557
23906
|
const lambdaInvokers = options.lambdaInvokers ?? /* @__PURE__ */ new Map();
|
|
23558
23907
|
const edgeInvokers = options.edgeInvokers ?? /* @__PURE__ */ new Map();
|
|
23559
23908
|
const s3OriginReaders = options.s3OriginReaders ?? /* @__PURE__ */ new Map();
|
|
23909
|
+
const webFunctionOrigins = options.webFunctionOrigins ?? /* @__PURE__ */ new Map();
|
|
23560
23910
|
const state = { distribution: options.distribution };
|
|
23561
23911
|
const handler = (req, res) => {
|
|
23562
|
-
handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger).catch((err) => {
|
|
23912
|
+
handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, webFunctionOrigins, logger).catch((err) => {
|
|
23563
23913
|
logger.warn(`Request handling failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23564
23914
|
if (!res.headersSent) {
|
|
23565
23915
|
res.statusCode = 500;
|
|
@@ -23588,7 +23938,7 @@ async function startCloudFrontServer(options) {
|
|
|
23588
23938
|
}
|
|
23589
23939
|
};
|
|
23590
23940
|
}
|
|
23591
|
-
async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger) {
|
|
23941
|
+
async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, webFunctionOrigins, logger) {
|
|
23592
23942
|
const distribution = state.distribution;
|
|
23593
23943
|
const rawUrl = req.url ?? "/";
|
|
23594
23944
|
const queryIdx = rawUrl.indexOf("?");
|
|
@@ -23680,6 +24030,7 @@ async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3
|
|
|
23680
24030
|
distribution,
|
|
23681
24031
|
lambdaInvokers,
|
|
23682
24032
|
s3OriginReaders,
|
|
24033
|
+
webFunctionOrigins,
|
|
23683
24034
|
logger
|
|
23684
24035
|
});
|
|
23685
24036
|
if (!originResult) return writePlain(res, 502, originUnavailableMessage(origin, behavior));
|
|
@@ -23835,20 +24186,42 @@ async function serveFromOrigin(origin, args) {
|
|
|
23835
24186
|
...result.cookies.length > 0 && { setCookies: result.cookies }
|
|
23836
24187
|
};
|
|
23837
24188
|
}
|
|
23838
|
-
if (origin.kind === "
|
|
23839
|
-
const
|
|
23840
|
-
if (!
|
|
23841
|
-
const result = await
|
|
23842
|
-
|
|
23843
|
-
|
|
23844
|
-
|
|
23845
|
-
|
|
23846
|
-
|
|
23847
|
-
|
|
23848
|
-
|
|
23849
|
-
|
|
23850
|
-
|
|
23851
|
-
|
|
24189
|
+
if (origin.kind === "web-function") {
|
|
24190
|
+
const target = args.webFunctionOrigins.get(origin.endpointLogicalId);
|
|
24191
|
+
if (!target) return void 0;
|
|
24192
|
+
const result = await serveWebFunctionOrigin({
|
|
24193
|
+
target,
|
|
24194
|
+
request: {
|
|
24195
|
+
method: args.method,
|
|
24196
|
+
uri: args.uri,
|
|
24197
|
+
querystring: args.querystring,
|
|
24198
|
+
headers: args.headers,
|
|
24199
|
+
body: await args.readBody(),
|
|
24200
|
+
sourceIp: args.sourceIp
|
|
24201
|
+
},
|
|
24202
|
+
...origin.readTimeoutSeconds !== void 0 && { readTimeoutSeconds: origin.readTimeoutSeconds }
|
|
24203
|
+
});
|
|
24204
|
+
return {
|
|
24205
|
+
statusCode: result.statusCode,
|
|
24206
|
+
headers: result.headers,
|
|
24207
|
+
body: result.body,
|
|
24208
|
+
...result.cookies.length > 0 && { setCookies: result.cookies }
|
|
24209
|
+
};
|
|
24210
|
+
}
|
|
24211
|
+
if (origin.kind === "s3-deployed") {
|
|
24212
|
+
const reader = args.s3OriginReaders.get(origin.originId);
|
|
24213
|
+
if (!reader) return void 0;
|
|
24214
|
+
const result = await reader({
|
|
24215
|
+
uri: args.uri,
|
|
24216
|
+
...distribution.defaultRootObject !== void 0 && { defaultRootObject: distribution.defaultRootObject },
|
|
24217
|
+
customErrorResponses: distribution.customErrorResponses
|
|
24218
|
+
});
|
|
24219
|
+
return {
|
|
24220
|
+
statusCode: result.statusCode,
|
|
24221
|
+
headers: result.headers,
|
|
24222
|
+
body: result.body
|
|
24223
|
+
};
|
|
24224
|
+
}
|
|
23852
24225
|
if (origin.kind !== "s3") return void 0;
|
|
23853
24226
|
const result = serveFromStaticOrigin({
|
|
23854
24227
|
localDirs: origin.localDirs,
|
|
@@ -23867,7 +24240,8 @@ async function serveFromOrigin(origin, args) {
|
|
|
23867
24240
|
function originUnavailableMessage(origin, behavior) {
|
|
23868
24241
|
if (!origin) return `Behavior ${behavior.pathPattern === void 0 ? "(default)" : displayUntrustedValue(behavior.pathPattern)} targets unknown origin ${displayIdent(behavior.targetOriginId)}.\n`;
|
|
23869
24242
|
if (origin.kind === "lambda-url") return `Origin ${displayIdent(origin.originId)} is a Lambda Function URL origin whose backing function ${displayIdent(origin.functionLogicalId)} was not booted (it was added after start-up). Restart start-cloudfront.\n`;
|
|
23870
|
-
if (origin.kind === "
|
|
24243
|
+
if (origin.kind === "web-function") return `Origin ${displayIdent(origin.originId)} is a Lambda Web Function endpoint origin whose endpoint ${displayIdent(origin.endpointLogicalId)} was not booted (it was added after start-up). Restart start-cloudfront.\n`;
|
|
24244
|
+
if (origin.kind === "custom") return `Origin ${displayIdent(origin.originId)} is a custom (non-S3) origin (${displayUntrustedValue(String(origin.domainName))}). cdkl start-cloudfront serves S3, Lambda Function URL and Lambda Web Function endpoint origins only.
|
|
23871
24245
|
`;
|
|
23872
24246
|
if (origin.kind === "s3-deployed") return `Origin ${displayIdent(origin.originId)} is a deployed-S3 origin (bucket ${displayIdent(origin.bucketName)}) whose reader was not booted (it was promoted after start-up). Restart start-cloudfront.
|
|
23873
24247
|
`;
|
|
@@ -24161,1061 +24535,1064 @@ function idFromArn(arn) {
|
|
|
24161
24535
|
}
|
|
24162
24536
|
|
|
24163
24537
|
//#endregion
|
|
24164
|
-
//#region src/
|
|
24165
|
-
|
|
24166
|
-
|
|
24167
|
-
|
|
24168
|
-
|
|
24169
|
-
};
|
|
24170
|
-
|
|
24171
|
-
const
|
|
24172
|
-
|
|
24173
|
-
|
|
24174
|
-
|
|
24175
|
-
|
|
24176
|
-
|
|
24177
|
-
|
|
24538
|
+
//#region src/local/web-function-service-config.ts
|
|
24539
|
+
const WEB_FUNCTION_HTTP_ENDPOINT_ENV = "AWS_LAMBDA_HTTP_ENDPOINT";
|
|
24540
|
+
const APPLICATION_MANAGED_AUTH = "ApplicationManaged";
|
|
24541
|
+
const HOME_REGION_ENDPOINT = "HomeRegion";
|
|
24542
|
+
function readWebFunctionServiceConfig(revision) {
|
|
24543
|
+
const sc = asObject$2(revision.Properties?.["ServiceConfig"]) ?? {};
|
|
24544
|
+
const envRaw = sc["EnvironmentVariables"];
|
|
24545
|
+
const envObj = asObject$2(envRaw);
|
|
24546
|
+
const envKeys = envObj ? Object.keys(envObj) : [];
|
|
24547
|
+
const env = envKeys.length === 1 && (envKeys[0] === "Ref" || envKeys[0].startsWith("Fn::")) ? void 0 : envObj;
|
|
24548
|
+
const logGroup = asObject$2(asObject$2(sc["TelemetryConfig"])?.["LoggingConfig"])?.["LogGroup"];
|
|
24549
|
+
return {
|
|
24550
|
+
environmentVariables: env,
|
|
24551
|
+
environmentUnreadable: envRaw !== void 0 && env === void 0,
|
|
24552
|
+
executionRoleArn: sc["ExecutionRoleArn"],
|
|
24553
|
+
egressNetworkConnectorArn: sc["EgressNetworkConnectorArn"],
|
|
24554
|
+
timeoutSeconds: typeof sc["TimeoutSeconds"] === "number" ? sc["TimeoutSeconds"] : void 0,
|
|
24555
|
+
logGroup: typeof logGroup === "string" && logGroup.length > 0 ? logGroup : void 0
|
|
24556
|
+
};
|
|
24178
24557
|
}
|
|
24179
|
-
function
|
|
24180
|
-
|
|
24181
|
-
|
|
24182
|
-
|
|
24183
|
-
|
|
24184
|
-
|
|
24185
|
-
|
|
24186
|
-
|
|
24558
|
+
function buildWebFunctionLambdaView(resolved, config, runtime) {
|
|
24559
|
+
return {
|
|
24560
|
+
kind: "zip",
|
|
24561
|
+
stack: resolved.stack,
|
|
24562
|
+
logicalId: resolved.revisionLogicalId,
|
|
24563
|
+
resource: {
|
|
24564
|
+
Type: resolved.revision.Type,
|
|
24565
|
+
Properties: {
|
|
24566
|
+
...config.environmentVariables !== void 0 && { Environment: { Variables: config.environmentVariables } },
|
|
24567
|
+
...config.executionRoleArn !== void 0 && { Role: config.executionRoleArn }
|
|
24568
|
+
},
|
|
24569
|
+
...resolved.revision.Metadata !== void 0 && { Metadata: resolved.revision.Metadata }
|
|
24570
|
+
},
|
|
24571
|
+
memoryMb: 128,
|
|
24572
|
+
timeoutSec: config.timeoutSeconds ?? 0,
|
|
24573
|
+
layers: [],
|
|
24574
|
+
runtime,
|
|
24575
|
+
handler: "",
|
|
24576
|
+
codePath: null,
|
|
24577
|
+
architecture: "arm64"
|
|
24578
|
+
};
|
|
24187
24579
|
}
|
|
24188
|
-
function
|
|
24189
|
-
|
|
24190
|
-
|
|
24191
|
-
if (resource.Type !== "AWS::CloudFront::KeyValueStore") continue;
|
|
24192
|
-
const cdkPath = readCdkPath(resource) || void 0;
|
|
24193
|
-
const constructPath = cdkPath?.endsWith("/Resource") ? cdkPath.slice(0, -9) : cdkPath;
|
|
24194
|
-
const constructId = constructPath?.split("/").pop();
|
|
24195
|
-
out.push({
|
|
24196
|
-
logicalId,
|
|
24197
|
-
...cdkPath !== void 0 && { cdkPath },
|
|
24198
|
-
...constructPath !== void 0 && { constructPath },
|
|
24199
|
-
...constructId !== void 0 && constructId !== "" && { constructId }
|
|
24200
|
-
});
|
|
24201
|
-
}
|
|
24202
|
-
return out;
|
|
24580
|
+
function webFunctionAssumeRole(assumeRole, config) {
|
|
24581
|
+
if (assumeRole === true && isIamRoleArn(config.executionRoleArn)) return config.executionRoleArn;
|
|
24582
|
+
return assumeRole;
|
|
24203
24583
|
}
|
|
24204
|
-
function
|
|
24205
|
-
|
|
24206
|
-
|
|
24207
|
-
|
|
24208
|
-
|
|
24209
|
-
|
|
24210
|
-
|
|
24211
|
-
|
|
24212
|
-
|
|
24213
|
-
|
|
24214
|
-
|
|
24215
|
-
|
|
24216
|
-
|
|
24217
|
-
|
|
24584
|
+
function finalizeWebFunctionEnv(lambdaEnv, opts) {
|
|
24585
|
+
const env = { ...lambdaEnv };
|
|
24586
|
+
delete env["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"];
|
|
24587
|
+
delete env["AWS_LAMBDA_FUNCTION_VERSION"];
|
|
24588
|
+
delete env["AWS_LAMBDA_FUNCTION_TIMEOUT"];
|
|
24589
|
+
env["AWS_LAMBDA_FUNCTION_NAME"] = opts.functionName;
|
|
24590
|
+
env["AWS_LAMBDA_LOG_GROUP_NAME"] = opts.logGroup ?? `/aws/lambda/${opts.functionName}`;
|
|
24591
|
+
if (opts.timeoutSeconds !== void 0) env["AWS_LAMBDA_FUNCTION_TIMEOUT"] = String(opts.timeoutSeconds);
|
|
24592
|
+
const replacedHttpEndpoint = Object.hasOwn(lambdaEnv, WEB_FUNCTION_HTTP_ENDPOINT_ENV);
|
|
24593
|
+
env[WEB_FUNCTION_HTTP_ENDPOINT_ENV] = `0.0.0.0:${opts.containerPort}`;
|
|
24594
|
+
return {
|
|
24595
|
+
env,
|
|
24596
|
+
replacedHttpEndpoint
|
|
24597
|
+
};
|
|
24598
|
+
}
|
|
24599
|
+
function webFunctionFidelityWarnings(resolved, config) {
|
|
24600
|
+
const out = [];
|
|
24601
|
+
const rev = displayIdent(resolved.revisionLogicalId);
|
|
24602
|
+
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.`);
|
|
24603
|
+
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.`);
|
|
24604
|
+
const endpoint = resolved.endpoint;
|
|
24605
|
+
if (endpoint) {
|
|
24606
|
+
const ep = displayIdent(endpoint.logicalId);
|
|
24607
|
+
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.`);
|
|
24608
|
+
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.`);
|
|
24609
|
+
const routed = endpoint.weights.filter((w) => w.weight > 0);
|
|
24610
|
+
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}.`);
|
|
24218
24611
|
}
|
|
24219
24612
|
return out;
|
|
24220
24613
|
}
|
|
24221
|
-
|
|
24222
|
-
|
|
24223
|
-
const synthRegion = stack?.region;
|
|
24224
|
-
const kvsFiles = stack ? normalizeKvsFileKeys(parseKvsFileOverrides(options.kvsFile), stack.template) : parseKvsFileOverrides(options.kvsFile);
|
|
24225
|
-
const resolveDeployedKvs = isStateSourceActive(options, extraStateProviders) ? async (kvsLogicalId) => {
|
|
24226
|
-
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
|
|
24227
|
-
if (!provider) return void 0;
|
|
24228
|
-
let record;
|
|
24229
|
-
try {
|
|
24230
|
-
record = await provider.load(distribution.stackName, synthRegion);
|
|
24231
|
-
} finally {
|
|
24232
|
-
provider.dispose();
|
|
24233
|
-
}
|
|
24234
|
-
const physicalId = record?.resources[kvsLogicalId]?.physicalId;
|
|
24235
|
-
if (!physicalId) return void 0;
|
|
24236
|
-
if (physicalId.startsWith("arn:")) {
|
|
24237
|
-
const id = idFromArn(physicalId);
|
|
24238
|
-
return {
|
|
24239
|
-
arn: physicalId,
|
|
24240
|
-
...id !== void 0 && { id }
|
|
24241
|
-
};
|
|
24242
|
-
}
|
|
24243
|
-
return resolveDeployedKvsArnByName(physicalId, { ...profileCredentials !== void 0 && { credentials: profileCredentials } });
|
|
24244
|
-
} : void 0;
|
|
24245
|
-
const { warnings } = await resolveKvsModulesForDistribution(distribution, {
|
|
24246
|
-
...kvsFiles.size > 0 && { kvsFiles },
|
|
24247
|
-
...resolveDeployedKvs !== void 0 && { resolveDeployedKvs },
|
|
24248
|
-
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
24249
|
-
});
|
|
24250
|
-
for (const warning of warnings) logger.warn(warning);
|
|
24614
|
+
function asObject$2(value) {
|
|
24615
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
24251
24616
|
}
|
|
24252
|
-
|
|
24253
|
-
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
|
|
24258
|
-
|
|
24259
|
-
|
|
24260
|
-
|
|
24261
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24265
|
-
|
|
24266
|
-
|
|
24267
|
-
|
|
24617
|
+
|
|
24618
|
+
//#endregion
|
|
24619
|
+
//#region src/local/web-function-artifact.ts
|
|
24620
|
+
const SUPPORTED_CODE_CONFIG_KEYS = ["S3Object"];
|
|
24621
|
+
function parseWebFunctionBuildConfig(revisionLogicalId, buildConfig, template) {
|
|
24622
|
+
const subject = `Web function revision ${displayIdent(revisionLogicalId)}`;
|
|
24623
|
+
const build = asObject$1(buildConfig);
|
|
24624
|
+
const runtime = asObject$1(build?.["RuntimeConfig"])?.["Runtime"];
|
|
24625
|
+
if (typeof runtime !== "string" || runtime.length === 0) throw new CdkLocalError(`${subject} has no literal BuildConfig.RuntimeConfig.Runtime.`, "LOCAL_START_WEB_FUNCTION_NO_RUNTIME");
|
|
24626
|
+
const codeConfig = asObject$1(build?.["CodeConfig"]);
|
|
24627
|
+
if (!codeConfig) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
|
|
24628
|
+
const unknownKeys = Object.keys(codeConfig).filter((k) => !SUPPORTED_CODE_CONFIG_KEYS.includes(k));
|
|
24629
|
+
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");
|
|
24630
|
+
const s3 = asObject$1(codeConfig["S3Object"]);
|
|
24631
|
+
if (!s3) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig.S3Object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
|
|
24632
|
+
const rawKey = s3["Key"];
|
|
24633
|
+
let key;
|
|
24634
|
+
let deployment;
|
|
24635
|
+
if (typeof rawKey === "string" && rawKey.length > 0) key = rawKey;
|
|
24636
|
+
else {
|
|
24637
|
+
const viaDeployment = template ? resolveBucketDeploymentObjectKey(template, rawKey) : void 0;
|
|
24638
|
+
if (viaDeployment === void 0) throw new CdkLocalError(`${subject} has a CodeConfig.S3Object.Key that is not a literal string (${describeValue$1(rawKey)}). The code must come from a CDK asset in this app: an s3assets.Asset passing asset.s3BucketName / asset.s3ObjectKey, whose key synthesizes as a literal` + (template ? ", or a BucketDeployment with extract: false passing Fn.select(<i>, deployment.objectKeys), which resolves to its i-th source asset." : "."), "LOCAL_START_WEB_FUNCTION_CODE_KEY_NOT_LITERAL");
|
|
24639
|
+
key = bucketDeploymentSourceKey(subject, viaDeployment);
|
|
24640
|
+
deployment = {
|
|
24641
|
+
logicalId: viaDeployment.deploymentLogicalId,
|
|
24642
|
+
index: viaDeployment.index
|
|
24268
24643
|
};
|
|
24269
24644
|
}
|
|
24270
|
-
const
|
|
24271
|
-
if (!res || res.Type !== "AWS::CloudFront::Distribution") throw notFound(target, stack, resources);
|
|
24645
|
+
const versionId = s3["VersionId"];
|
|
24272
24646
|
return {
|
|
24273
|
-
|
|
24274
|
-
|
|
24647
|
+
runtime,
|
|
24648
|
+
s3Object: {
|
|
24649
|
+
bucket: s3["Bucket"],
|
|
24650
|
+
key,
|
|
24651
|
+
...typeof versionId === "string" && { versionId },
|
|
24652
|
+
...deployment !== void 0 && { deployment }
|
|
24653
|
+
}
|
|
24275
24654
|
};
|
|
24276
24655
|
}
|
|
24277
|
-
function
|
|
24278
|
-
const
|
|
24279
|
-
switch (
|
|
24280
|
-
case "
|
|
24281
|
-
case "
|
|
24282
|
-
case "
|
|
24283
|
-
case "
|
|
24656
|
+
function bucketDeploymentSourceKey(subject, r) {
|
|
24657
|
+
const named = `CodeConfig.S3Object.Key selects source ${r.index} of BucketDeployment ${displayIdent(r.deploymentLogicalId)}`;
|
|
24658
|
+
switch (r.kind) {
|
|
24659
|
+
case "resolved": return r.sourceObjectKey;
|
|
24660
|
+
case "extracts": throw new CdkLocalError(`${subject} ${named}, which does not set extract to a literal false, so it unpacks its sources and no object in the destination bucket holds that source's zip (Lambda could not read it either). Set extract: false on the BucketDeployment so it copies the zip as-is.`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_DEPLOYMENT_EXTRACTS");
|
|
24661
|
+
case "prefixed-destination": throw new CdkLocalError(`${subject} ${named}, which sets a destinationKeyPrefix: the zip lands at <prefix><key> while objectKeys returns <key>, so the selected key names no object (Lambda could not read it either). Remove destinationKeyPrefix from the deployment.`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_DEPLOYMENT_SOURCE_UNRESOLVED");
|
|
24662
|
+
case "object-keys-not-output": throw new CdkLocalError(`${subject} ${named}, which sets outputObjectKeys: false, so its SourceObjectKeys attribute is empty and the selection has no value. Remove outputObjectKeys: false.`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_DEPLOYMENT_SOURCE_UNRESOLVED");
|
|
24663
|
+
case "index-out-of-range": throw new CdkLocalError(`${subject} ${named}, which has only ${r.sourceCount} source(s).`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_DEPLOYMENT_SOURCE_UNRESOLVED");
|
|
24664
|
+
case "source-key-not-literal": throw new CdkLocalError(`${subject} ${named}, whose SourceObjectKeys entry is not a literal string, so it is not a CDK asset of this app. Deploy the code with s3deploy.Source.asset(...).`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_DEPLOYMENT_SOURCE_UNRESOLVED");
|
|
24284
24665
|
}
|
|
24285
24666
|
}
|
|
24286
|
-
function
|
|
24287
|
-
const
|
|
24288
|
-
const
|
|
24289
|
-
|
|
24290
|
-
}
|
|
24291
|
-
|
|
24292
|
-
|
|
24293
|
-
const
|
|
24294
|
-
const
|
|
24295
|
-
const
|
|
24296
|
-
|
|
24297
|
-
|
|
24298
|
-
|
|
24299
|
-
|
|
24300
|
-
|
|
24301
|
-
|
|
24302
|
-
|
|
24303
|
-
|
|
24304
|
-
|
|
24305
|
-
|
|
24306
|
-
|
|
24307
|
-
|
|
24308
|
-
|
|
24309
|
-
|
|
24310
|
-
|
|
24311
|
-
|
|
24312
|
-
await Promise.all(runners.map((r) => r.stop().catch(() => void 0)));
|
|
24313
|
-
throw new LocalStartCloudFrontError(`Failed to boot the Lambda Function URL origin's backing function ${displayIdent(functionLogicalId)}: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
|
|
24314
|
-
}
|
|
24315
|
-
runners.push(runner);
|
|
24316
|
-
invokers.set(functionLogicalId, (event) => runner.invoke(event));
|
|
24317
|
-
}
|
|
24667
|
+
async function resolveWebFunctionArtifact(stack, revisionLogicalId, code, loader = new AssetManifestLoader()) {
|
|
24668
|
+
const subject = `Web function revision ${displayUntrustedValue(revisionLogicalId)} code bundle`;
|
|
24669
|
+
const { deployment } = code.s3Object;
|
|
24670
|
+
const keyOrigin = deployment ? ` (source ${deployment.index} of BucketDeployment ${displayIdent(deployment.logicalId)})` : "";
|
|
24671
|
+
const notFound = () => new CdkLocalError(`${subject} (S3Object bucket ${describeValue$1(code.s3Object.bucket)}, key ${displayUntrustedValue(code.s3Object.key)}${keyOrigin}) 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");
|
|
24672
|
+
const manifestPath = stack.assetManifestPath;
|
|
24673
|
+
if (!manifestPath) throw notFound();
|
|
24674
|
+
const manifestDir = dirname$1(manifestPath);
|
|
24675
|
+
const manifest = await loader.loadStackManifest(stack, manifestDir);
|
|
24676
|
+
const match = manifest ? findFileAssetForObjectKey(manifest, code.s3Object.key) : void 0;
|
|
24677
|
+
if (!match) throw notFound();
|
|
24678
|
+
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");
|
|
24679
|
+
const sourcePath = resolveAssetSourcePath({
|
|
24680
|
+
manifestDir,
|
|
24681
|
+
value: match.asset.source.path,
|
|
24682
|
+
assetOutdir: assetPathDirs(stack).assetOutdir,
|
|
24683
|
+
absolute: "honour-warn",
|
|
24684
|
+
field: "source.path",
|
|
24685
|
+
subject,
|
|
24686
|
+
action: "mount it",
|
|
24687
|
+
sink: "bind-mount that directory into the web function container",
|
|
24688
|
+
wrapError: (message) => new CdkLocalError(message, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_ESCAPES_ASSEMBLY")
|
|
24689
|
+
});
|
|
24690
|
+
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");
|
|
24691
|
+
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");
|
|
24692
|
+
const materialized = materializeAssetCodeDir(sourcePath);
|
|
24318
24693
|
return {
|
|
24319
|
-
|
|
24320
|
-
|
|
24694
|
+
kind: "zip",
|
|
24695
|
+
runtime: code.runtime,
|
|
24696
|
+
codeDir: materialized.dir,
|
|
24697
|
+
...materialized.tmpDir !== void 0 && { tmpDir: materialized.tmpDir },
|
|
24698
|
+
assetHash: match.hash
|
|
24321
24699
|
};
|
|
24322
24700
|
}
|
|
24323
|
-
|
|
24324
|
-
|
|
24325
|
-
|
|
24326
|
-
|
|
24327
|
-
|
|
24328
|
-
|
|
24329
|
-
const edge = behavior.lambdaEdge;
|
|
24330
|
-
if (!edge) continue;
|
|
24331
|
-
for (const assoc of [
|
|
24332
|
-
edge.viewerRequest,
|
|
24333
|
-
edge.originRequest,
|
|
24334
|
-
edge.originResponse,
|
|
24335
|
-
edge.viewerResponse
|
|
24336
|
-
]) if (assoc) functionLogicalIds.add(assoc.functionLogicalId);
|
|
24337
|
-
}
|
|
24338
|
-
for (const functionLogicalId of functionLogicalIds) try {
|
|
24339
|
-
const lambda = resolveLambdaTarget(`${distribution.stackName}:${functionLogicalId}`, stacks);
|
|
24340
|
-
const containerEnv = await resolveLambdaContainerEnv(lambda, opts.envOptions, opts.profileCredentials, opts.extraStateProviders);
|
|
24341
|
-
const runner = createFrontDoorLambdaRunner(lambda, {
|
|
24342
|
-
containerHost: opts.containerHost,
|
|
24343
|
-
skipPull: opts.skipPull,
|
|
24344
|
-
...opts.envOptions.region !== void 0 && { region: opts.envOptions.region },
|
|
24345
|
-
containerEnv: containerEnv.env,
|
|
24346
|
-
...containerEnv.sensitiveEnvKeys.length > 0 && { sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys) }
|
|
24701
|
+
function disposeWebFunctionArtifact(artifact) {
|
|
24702
|
+
if (artifact?.kind !== "zip" || artifact.tmpDir === void 0) return;
|
|
24703
|
+
try {
|
|
24704
|
+
if (existsSync$1(artifact.tmpDir)) rmSync$1(artifact.tmpDir, {
|
|
24705
|
+
recursive: true,
|
|
24706
|
+
force: true
|
|
24347
24707
|
});
|
|
24348
|
-
|
|
24349
|
-
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24708
|
+
} catch {}
|
|
24709
|
+
}
|
|
24710
|
+
function asObject$1(value) {
|
|
24711
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
24712
|
+
}
|
|
24713
|
+
function describeValue$1(value) {
|
|
24714
|
+
if (typeof value === "string") return displayUntrustedValue(value);
|
|
24715
|
+
let json;
|
|
24716
|
+
try {
|
|
24717
|
+
json = JSON.stringify(value);
|
|
24718
|
+
} catch {
|
|
24719
|
+
json = void 0;
|
|
24354
24720
|
}
|
|
24355
|
-
return
|
|
24356
|
-
invokers,
|
|
24357
|
-
runners
|
|
24358
|
-
};
|
|
24721
|
+
return displayUntrustedValue(json ?? String(value));
|
|
24359
24722
|
}
|
|
24360
|
-
|
|
24723
|
+
|
|
24724
|
+
//#endregion
|
|
24725
|
+
//#region src/local/web-function-runtime.ts
|
|
24726
|
+
const NODE_CODE_MOUNT = "/var/task";
|
|
24727
|
+
const NODE_ENTRYPOINT_ENV = "AWS_LAMBDA_NODEJS_ENTRYPOINT";
|
|
24728
|
+
const NODE_DEFAULT_ENTRY = "index.js";
|
|
24729
|
+
function buildNodeEntryFileStart(runtime, env, codeDir, fileExists = existsSync$1) {
|
|
24730
|
+
const configured = env[NODE_ENTRYPOINT_ENV];
|
|
24731
|
+
const entryFile = configured !== void 0 && configured.length > 0 ? configured : NODE_DEFAULT_ENTRY;
|
|
24732
|
+
const hostPath = resolve$1(codeDir, entryFile);
|
|
24733
|
+
const rel = relative$1(codeDir, hostPath);
|
|
24734
|
+
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");
|
|
24735
|
+
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");
|
|
24736
|
+
const posixRel = sep$1 === "\\" ? rel.split("\\").join("/") : rel;
|
|
24361
24737
|
return {
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
...options.region !== void 0 && { region: options.region },
|
|
24366
|
-
...options.profile !== void 0 && { profile: options.profile },
|
|
24367
|
-
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion }
|
|
24738
|
+
entryPoint: ["/var/lang/bin/node"],
|
|
24739
|
+
cmd: [posix.join(NODE_CODE_MOUNT, posixRel)],
|
|
24740
|
+
label: entryFile
|
|
24368
24741
|
};
|
|
24369
24742
|
}
|
|
24370
|
-
|
|
24743
|
+
const WEB_FUNCTION_RUNTIMES = { "nodejs24.x": {
|
|
24744
|
+
runtime: "nodejs24.x",
|
|
24745
|
+
baseImage: "public.ecr.aws/lambda/nodejs:24",
|
|
24746
|
+
codeMountPath: NODE_CODE_MOUNT,
|
|
24747
|
+
architectures: ["arm64"],
|
|
24748
|
+
buildStart: (env, codeDir, deps) => buildNodeEntryFileStart("nodejs24.x", env, codeDir, deps?.fileExists)
|
|
24749
|
+
} };
|
|
24750
|
+
function supportedWebFunctionRuntimes(table = WEB_FUNCTION_RUNTIMES) {
|
|
24751
|
+
return Object.keys(table).sort();
|
|
24752
|
+
}
|
|
24753
|
+
function resolveWebFunctionRuntime(runtime, table = WEB_FUNCTION_RUNTIMES) {
|
|
24754
|
+
const spec = Object.hasOwn(table, runtime) ? table[runtime] : void 0;
|
|
24755
|
+
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");
|
|
24756
|
+
return spec;
|
|
24757
|
+
}
|
|
24758
|
+
function defaultWebFunctionPlatform(spec) {
|
|
24759
|
+
return spec.architectures[0] === "x86_64" ? "linux/amd64" : "linux/arm64";
|
|
24760
|
+
}
|
|
24761
|
+
|
|
24762
|
+
//#endregion
|
|
24763
|
+
//#region src/local/web-function-runner.ts
|
|
24764
|
+
const execFileAsync$1 = promisify$1(execFile$1);
|
|
24765
|
+
const WEB_FUNCTION_CONTAINER_PORT = 8080;
|
|
24766
|
+
const READY_FAILURE_LOG_LINES = 20;
|
|
24767
|
+
const MAX_PROBE_TIMEOUT_MS = 1e4;
|
|
24768
|
+
function planWebFunctionContainer(artifact, spec, env, deps) {
|
|
24769
|
+
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");
|
|
24770
|
+
const start = spec.buildStart(env, artifact.codeDir, deps);
|
|
24371
24771
|
return {
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24772
|
+
image: spec.baseImage,
|
|
24773
|
+
mounts: [{
|
|
24774
|
+
hostPath: artifact.codeDir,
|
|
24775
|
+
containerPath: spec.codeMountPath,
|
|
24776
|
+
readOnly: true
|
|
24777
|
+
}],
|
|
24778
|
+
entryPoint: start.entryPoint,
|
|
24779
|
+
cmd: start.cmd,
|
|
24780
|
+
workingDir: spec.codeMountPath,
|
|
24781
|
+
label: start.label
|
|
24377
24782
|
};
|
|
24378
24783
|
}
|
|
24379
|
-
function
|
|
24380
|
-
const
|
|
24381
|
-
|
|
24382
|
-
|
|
24784
|
+
async function startWebFunctionContainer(opts) {
|
|
24785
|
+
const { plan } = opts;
|
|
24786
|
+
return runDetached({
|
|
24787
|
+
image: plan.image,
|
|
24788
|
+
mounts: plan.mounts,
|
|
24789
|
+
env: opts.env,
|
|
24790
|
+
...opts.sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys: opts.sensitiveEnvKeys },
|
|
24791
|
+
entryPoint: plan.entryPoint,
|
|
24792
|
+
cmd: plan.cmd,
|
|
24793
|
+
...plan.workingDir !== void 0 && { workingDir: plan.workingDir },
|
|
24794
|
+
hostPort: opts.hostPort,
|
|
24795
|
+
containerPort: WEB_FUNCTION_CONTAINER_PORT,
|
|
24796
|
+
host: opts.host,
|
|
24797
|
+
platform: opts.platform,
|
|
24798
|
+
name: opts.name,
|
|
24799
|
+
autoRemove: false
|
|
24800
|
+
});
|
|
24383
24801
|
}
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24802
|
+
var RecentOutput = class {
|
|
24803
|
+
lines = [];
|
|
24804
|
+
partial = "";
|
|
24805
|
+
max;
|
|
24806
|
+
constructor(max = 20) {
|
|
24807
|
+
this.max = max;
|
|
24808
|
+
}
|
|
24809
|
+
push(chunk) {
|
|
24810
|
+
const parts = (this.partial + chunk.toString()).split("\n");
|
|
24811
|
+
this.partial = parts.pop() ?? "";
|
|
24812
|
+
for (const line of parts) this.add(line);
|
|
24813
|
+
if (this.partial.length > 4096) {
|
|
24814
|
+
this.add(this.partial);
|
|
24815
|
+
this.partial = "";
|
|
24816
|
+
}
|
|
24817
|
+
}
|
|
24818
|
+
tail() {
|
|
24819
|
+
return this.partial.length > 0 ? [...this.lines, this.partial].slice(-this.max) : this.lines;
|
|
24820
|
+
}
|
|
24821
|
+
add(line) {
|
|
24822
|
+
this.lines.push(line.replace(/\r$/, ""));
|
|
24823
|
+
if (this.lines.length > this.max) this.lines.shift();
|
|
24824
|
+
}
|
|
24825
|
+
};
|
|
24826
|
+
async function waitForWebFunctionReady(args, deps = defaultReadyDeps) {
|
|
24827
|
+
const deadline = deps.now() + args.timeoutMs;
|
|
24828
|
+
let attempt = 0;
|
|
24829
|
+
let last = "refused";
|
|
24830
|
+
while (deps.now() < deadline) {
|
|
24831
|
+
const remaining = Math.max(1, deadline - deps.now());
|
|
24832
|
+
last = await deps.probe(args.host, args.port, Math.min(remaining, MAX_PROBE_TIMEOUT_MS));
|
|
24833
|
+
if (last === "answered") return;
|
|
24834
|
+
attempt++;
|
|
24835
|
+
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);
|
|
24836
|
+
if (last === "refused") await deps.sleep(250);
|
|
24388
24837
|
}
|
|
24838
|
+
const authority = formatAuthority(args.host, args.port);
|
|
24839
|
+
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);
|
|
24389
24840
|
}
|
|
24390
|
-
async function
|
|
24391
|
-
|
|
24392
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
24393
|
-
const stateFlag = activeStateSourceFlag(options, extraStateProviders);
|
|
24394
|
-
if (stateFlag === void 0) return {
|
|
24395
|
-
readers,
|
|
24396
|
-
buckets
|
|
24397
|
-
};
|
|
24398
|
-
const synthRegion = stacks.find((s) => s.stackName === distribution.stackName)?.region;
|
|
24399
|
-
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
|
|
24400
|
-
let record;
|
|
24841
|
+
async function containerFailure(message, containerId, recent, fetchLogs = readContainerLogTail, code = "LOCAL_START_WEB_FUNCTION_NOT_READY") {
|
|
24842
|
+
let lines = [];
|
|
24401
24843
|
try {
|
|
24402
|
-
|
|
24403
|
-
}
|
|
24404
|
-
|
|
24405
|
-
}
|
|
24406
|
-
const region = options.stackRegion ?? options.region ?? synthRegion ?? record?.region;
|
|
24407
|
-
const distributionId = record?.resources[distribution.logicalId]?.physicalId;
|
|
24408
|
-
for (const origin of [...distribution.origins.values()]) {
|
|
24409
|
-
if (origin.kind !== "s3-unresolved") continue;
|
|
24410
|
-
let bucketName;
|
|
24411
|
-
let via;
|
|
24412
|
-
if (origin.bucketLogicalId !== void 0) {
|
|
24413
|
-
bucketName = record?.resources[origin.bucketLogicalId]?.physicalId;
|
|
24414
|
-
via = "deployed state";
|
|
24415
|
-
} else if (origin.bucketName !== void 0) {
|
|
24416
|
-
bucketName = origin.bucketName;
|
|
24417
|
-
via = "the origin's DomainName";
|
|
24418
|
-
} else if (origin.deployedConfigOnly && distributionId) {
|
|
24419
|
-
bucketName = await resolveDeployedOriginBucket({
|
|
24420
|
-
distributionId,
|
|
24421
|
-
originId: origin.originId,
|
|
24422
|
-
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
24423
|
-
});
|
|
24424
|
-
via = "GetDistributionConfig";
|
|
24425
|
-
} else continue;
|
|
24426
|
-
if (!bucketName) continue;
|
|
24427
|
-
readers.set(origin.originId, createS3OriginReader(bucketName, {
|
|
24428
|
-
...region !== void 0 && { region },
|
|
24429
|
-
...profileCredentials !== void 0 && { credentials: profileCredentials },
|
|
24430
|
-
...options.cacheOrigin === true && { cache: true }
|
|
24431
|
-
}));
|
|
24432
|
-
buckets.set(origin.originId, bucketName);
|
|
24433
|
-
distribution.origins.set(origin.originId, {
|
|
24434
|
-
kind: "s3-deployed",
|
|
24435
|
-
originId: origin.originId,
|
|
24436
|
-
bucketName
|
|
24437
|
-
});
|
|
24438
|
-
logger.info(`Origin ${displayIdent(origin.originId)}: no local BucketDeployment source; serving from deployed S3 (bucket=${bucketName}, resolved via ${via}) on demand under ${stateFlag}.`);
|
|
24844
|
+
lines = await fetchLogs(containerId, 20);
|
|
24845
|
+
} catch {
|
|
24846
|
+
lines = [];
|
|
24439
24847
|
}
|
|
24848
|
+
if (lines.length === 0) lines = recent.tail();
|
|
24849
|
+
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);
|
|
24850
|
+
}
|
|
24851
|
+
function watchContainerExit(containerId) {
|
|
24852
|
+
let stopped = false;
|
|
24853
|
+
const proc = spawn$1(getDockerCmd(), ["wait", containerId], { stdio: [
|
|
24854
|
+
"ignore",
|
|
24855
|
+
"pipe",
|
|
24856
|
+
"ignore"
|
|
24857
|
+
] });
|
|
24858
|
+
let out = "";
|
|
24859
|
+
proc.stdout?.on("data", (c) => out += c.toString());
|
|
24440
24860
|
return {
|
|
24441
|
-
|
|
24442
|
-
|
|
24861
|
+
exited: new Promise((resolveExit) => {
|
|
24862
|
+
const done = () => {
|
|
24863
|
+
if (stopped) return;
|
|
24864
|
+
const code = Number.parseInt(out.trim(), 10);
|
|
24865
|
+
resolveExit(Number.isNaN(code) ? void 0 : code);
|
|
24866
|
+
};
|
|
24867
|
+
proc.on("close", done);
|
|
24868
|
+
proc.on("error", done);
|
|
24869
|
+
}),
|
|
24870
|
+
stop: () => {
|
|
24871
|
+
stopped = true;
|
|
24872
|
+
if (!proc.killed) proc.kill("SIGTERM");
|
|
24873
|
+
}
|
|
24443
24874
|
};
|
|
24444
24875
|
}
|
|
24445
|
-
function
|
|
24446
|
-
|
|
24447
|
-
|
|
24448
|
-
|
|
24449
|
-
|
|
24450
|
-
|
|
24451
|
-
|
|
24452
|
-
|
|
24453
|
-
|
|
24454
|
-
|
|
24455
|
-
|
|
24456
|
-
|
|
24457
|
-
|
|
24458
|
-
|
|
24459
|
-
const tlsRequested = options.tls === true || options.tlsCert !== void 0 || options.tlsKey !== void 0;
|
|
24460
|
-
await applyRoleArnIfSet({
|
|
24461
|
-
roleArn: options.roleArn,
|
|
24462
|
-
region: options.region,
|
|
24463
|
-
profile: options.profile
|
|
24464
|
-
});
|
|
24465
|
-
const appCmd = resolveApp(options.app);
|
|
24466
|
-
if (!appCmd) throw new LocalStartCloudFrontError(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
|
|
24467
|
-
const basePort = parseInt(options.port, 10);
|
|
24468
|
-
if (!Number.isFinite(basePort) || basePort < 0 || basePort > 65535) throw new LocalStartCloudFrontError(`--port must be 0..65535 (got ${options.port}).`);
|
|
24469
|
-
const synthAndResolve = async () => {
|
|
24470
|
-
logger.info("Synthesizing CDK app...");
|
|
24471
|
-
const synthesizer = hostSynthesizer ?? new Synthesizer();
|
|
24472
|
-
const context = parseContextOptions(options.context);
|
|
24473
|
-
const synthOpts = {
|
|
24474
|
-
app: appCmd,
|
|
24475
|
-
output: options.output,
|
|
24476
|
-
...options.region && { region: options.region },
|
|
24477
|
-
...options.profile && { profile: options.profile },
|
|
24478
|
-
...Object.keys(context).length > 0 && { context }
|
|
24479
|
-
};
|
|
24480
|
-
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
24481
|
-
const chosen = await resolveSingleTarget(target, {
|
|
24482
|
-
entries: listTargets(stacks).cloudFrontDistributions,
|
|
24483
|
-
message: "Select a CloudFront distribution to serve",
|
|
24484
|
-
noun: "CloudFront distributions",
|
|
24485
|
-
onMissing: () => new LocalStartCloudFrontError(`${getEmbedConfig().cliName} start-cloudfront requires a <target>. Pass a distribution path like 'Stack/MyDist', or run it in a TTY to pick interactively.`)
|
|
24876
|
+
function probeHttp(host, port, timeoutMs) {
|
|
24877
|
+
return new Promise((resolveProbe) => {
|
|
24878
|
+
let timedOut = false;
|
|
24879
|
+
let connected = false;
|
|
24880
|
+
const req = request$2({
|
|
24881
|
+
host,
|
|
24882
|
+
port,
|
|
24883
|
+
path: "/",
|
|
24884
|
+
method: "GET",
|
|
24885
|
+
timeout: timeoutMs,
|
|
24886
|
+
headers: { connection: "close" }
|
|
24887
|
+
}, (res) => {
|
|
24888
|
+
res.resume();
|
|
24889
|
+
resolveProbe("answered");
|
|
24486
24890
|
});
|
|
24487
|
-
|
|
24488
|
-
|
|
24489
|
-
|
|
24490
|
-
|
|
24491
|
-
stack,
|
|
24492
|
-
logicalId,
|
|
24493
|
-
originOverrides
|
|
24494
|
-
}),
|
|
24495
|
-
stacks
|
|
24496
|
-
};
|
|
24497
|
-
};
|
|
24498
|
-
const initial = await synthAndResolve();
|
|
24499
|
-
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
24500
|
-
const deployedS3 = await resolveDeployedS3Origins(initial.distribution, initial.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
24501
|
-
warnUnsupported(initial.distribution, extraStateProviders);
|
|
24502
|
-
warnUnusedCacheOrigin(options, extraStateProviders);
|
|
24503
|
-
await attachKvsModules(initial.distribution, initial.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
24504
|
-
const bootOpts = buildLambdaBootOptions(options, profileCredentials, extraStateProviders);
|
|
24505
|
-
const { invokers: lambdaInvokers, runners: lambdaRunners } = await bootLambdaUrlOrigins(initial.distribution, initial.stacks, bootOpts);
|
|
24506
|
-
const { invokers: edgeInvokers, runners: edgeRunners } = await bootLambdaEdgeFunctions(initial.distribution, initial.stacks, bootOpts);
|
|
24507
|
-
let server;
|
|
24508
|
-
try {
|
|
24509
|
-
let tls;
|
|
24510
|
-
if (tlsRequested) tls = await resolveFrontDoorTlsMaterials({
|
|
24511
|
-
certPath: options.tlsCert,
|
|
24512
|
-
keyPath: options.tlsKey
|
|
24891
|
+
req.on("socket", (socket) => {
|
|
24892
|
+
socket.once("connect", () => {
|
|
24893
|
+
connected = true;
|
|
24894
|
+
});
|
|
24513
24895
|
});
|
|
24514
|
-
|
|
24515
|
-
|
|
24516
|
-
|
|
24517
|
-
port: basePort,
|
|
24518
|
-
...tls && { tls },
|
|
24519
|
-
...lambdaInvokers.size > 0 && { lambdaInvokers },
|
|
24520
|
-
...edgeInvokers.size > 0 && { edgeInvokers },
|
|
24521
|
-
...deployedS3.readers.size > 0 && { s3OriginReaders: deployedS3.readers }
|
|
24896
|
+
req.on("timeout", () => {
|
|
24897
|
+
timedOut = true;
|
|
24898
|
+
req.destroy();
|
|
24522
24899
|
});
|
|
24523
|
-
|
|
24524
|
-
|
|
24525
|
-
|
|
24900
|
+
req.on("error", () => resolveProbe(timedOut && connected ? "timeout" : "refused"));
|
|
24901
|
+
req.end();
|
|
24902
|
+
});
|
|
24903
|
+
}
|
|
24904
|
+
async function containerIsRunning(containerId) {
|
|
24905
|
+
try {
|
|
24906
|
+
const { stdout } = await execFileAsync$1(getDockerCmd(), [
|
|
24907
|
+
"inspect",
|
|
24908
|
+
"-f",
|
|
24909
|
+
"{{.State.Running}}",
|
|
24910
|
+
containerId
|
|
24911
|
+
]);
|
|
24912
|
+
return stdout.trim() === "true";
|
|
24913
|
+
} catch {
|
|
24914
|
+
return false;
|
|
24526
24915
|
}
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
|
|
24530
|
-
|
|
24531
|
-
|
|
24532
|
-
|
|
24533
|
-
|
|
24534
|
-
|
|
24535
|
-
|
|
24536
|
-
|
|
24537
|
-
|
|
24538
|
-
|
|
24539
|
-
|
|
24540
|
-
|
|
24541
|
-
|
|
24542
|
-
|
|
24543
|
-
|
|
24544
|
-
|
|
24545
|
-
|
|
24546
|
-
|
|
24547
|
-
|
|
24548
|
-
|
|
24549
|
-
|
|
24550
|
-
|
|
24551
|
-
|
|
24552
|
-
|
|
24553
|
-
|
|
24554
|
-
logger.warn(`Reload failed; keeping the previous version serving: ${err instanceof Error ? err.message : String(err)}`);
|
|
24555
|
-
}
|
|
24556
|
-
}).catch(() => void 0);
|
|
24916
|
+
}
|
|
24917
|
+
function readContainerLogTail(containerId, lines, spawnFn = spawn$1) {
|
|
24918
|
+
return new Promise((resolveLogs, rejectLogs) => {
|
|
24919
|
+
const proc = spawnFn(getDockerCmd(), [
|
|
24920
|
+
"logs",
|
|
24921
|
+
"--tail",
|
|
24922
|
+
String(lines),
|
|
24923
|
+
containerId
|
|
24924
|
+
], { stdio: [
|
|
24925
|
+
"ignore",
|
|
24926
|
+
"pipe",
|
|
24927
|
+
"pipe"
|
|
24928
|
+
] });
|
|
24929
|
+
const chunks = [];
|
|
24930
|
+
let size = 0;
|
|
24931
|
+
const take = (c) => {
|
|
24932
|
+
if (size > 4 * 1024 * 1024) return;
|
|
24933
|
+
chunks.push(c);
|
|
24934
|
+
size += c.length;
|
|
24935
|
+
};
|
|
24936
|
+
proc.stdout?.on("data", take);
|
|
24937
|
+
proc.stderr?.on("data", take);
|
|
24938
|
+
proc.on("error", rejectLogs);
|
|
24939
|
+
proc.on("close", (code) => {
|
|
24940
|
+
if (code !== 0) {
|
|
24941
|
+
rejectLogs(/* @__PURE__ */ new Error(`docker logs exited ${code}`));
|
|
24942
|
+
return;
|
|
24557
24943
|
}
|
|
24944
|
+
resolveLogs(Buffer.concat(chunks).toString().split("\n").map((l) => l.replace(/\r$/, "")).filter((l) => l.length > 0).slice(-lines));
|
|
24558
24945
|
});
|
|
24559
|
-
|
|
24560
|
-
}
|
|
24561
|
-
let shuttingDown = false;
|
|
24562
|
-
const shutdown = async (signal, exitCode) => {
|
|
24563
|
-
if (shuttingDown) return;
|
|
24564
|
-
shuttingDown = true;
|
|
24565
|
-
logger.info(`Received ${signal}, shutting down...`);
|
|
24566
|
-
if (watcher) try {
|
|
24567
|
-
await watcher.close();
|
|
24568
|
-
} catch {}
|
|
24569
|
-
try {
|
|
24570
|
-
await server.close();
|
|
24571
|
-
} catch {}
|
|
24572
|
-
await Promise.all([...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch((err) => {
|
|
24573
|
-
logger.debug(`Lambda runner stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24574
|
-
})));
|
|
24575
|
-
await Promise.all([...deployedS3.readers.values()].map((reader) => reader.close()));
|
|
24576
|
-
process.exit(exitCode);
|
|
24577
|
-
};
|
|
24578
|
-
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
24579
|
-
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
24580
|
-
await new Promise(() => void 0);
|
|
24581
|
-
}
|
|
24582
|
-
function createLocalStartCloudFrontCommand(opts = {}) {
|
|
24583
|
-
setEmbedConfig(opts.embedConfig);
|
|
24584
|
-
const cmd = new Command("start-cloudfront").description("Run a long-running local server that serves a CloudFront distribution: its S3 origin content (resolved from the BucketDeployment source in the cloud assembly) and its Lambda Function URL origins (the backing Lambda is run locally via RIE), plus its viewer-request / viewer-response CloudFront Functions, reproducing the distribution routing locally so a rewrite / routing change is verifiable in seconds. Serves S3 and Lambda Function URL origins, and runs Lambda@Edge functions in local RIE containers. Both Lambda kinds need Docker: a Function URL origin that fails to boot stops start-up, while a Lambda@Edge function that fails to boot is warn-and-skipped (its stage does not run). Other custom origins are not run (warn-and-skip; requests routed to one return 502). Tip: omit the target in a terminal to pick interactively.").argument("[target]", "CloudFront distribution to serve. Accepts the CDK Construct path ('MyStack/MyDist'), an ancestor prefix, or the stack-qualified logical id ('MyStack:MyDist'). When omitted in a TTY, an interactive picker opens.").action(withErrorHandling(async (target, options) => {
|
|
24585
|
-
await localStartCloudFrontCommand(target, options, opts.extraStateProviders, opts.synthesizer);
|
|
24586
|
-
}));
|
|
24587
|
-
addStartCloudFrontSpecificOptions(cmd);
|
|
24588
|
-
[
|
|
24589
|
-
...commonOptions(),
|
|
24590
|
-
...appOptions(),
|
|
24591
|
-
...contextOptions
|
|
24592
|
-
].forEach((opt) => cmd.addOption(opt));
|
|
24593
|
-
cmd.addOption(regionOption);
|
|
24594
|
-
return cmd;
|
|
24595
|
-
}
|
|
24596
|
-
function addStartCloudFrontSpecificOptions(cmd) {
|
|
24597
|
-
return cmd.addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--origin <originId=dir>", "Point a distribution origin at a local directory (repeatable). Use when cdk-local cannot resolve the origin's BucketDeployment source automatically (content uploaded out of band, or a non-CDK bucket).").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--kvs-file <key=file.json>", "Back a CloudFront Function's KeyValueStore reads (cf.kvs().get()) with a local JSON map (repeatable). The key is a KeyValueStore handle — its AWS::CloudFront::KeyValueStore resource logical id, its construct path, or its bare construct id; the file is a flat { \"key\": \"value\" } object. The AWS-free alternative to --from-cfn-stack, which instead reads the deployed store via the GetKey API.").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--tls", "Terminate real TLS (HTTPS). Uses --tls-cert / --tls-key when supplied, else an auto-generated self-signed cert.").default(false)).addOption(new Option("--tls-cert <path>", "PEM server certificate for --tls (implies --tls).")).addOption(new Option("--tls-key <path>", "PEM server private key for --tls (implies --tls).")).addOption(new Option("--no-pull", "Skip 'docker pull' for a Lambda Function URL origin's base image (use the locally cached image).")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind to a deployed CloudFormation stack (ListStackResources). Resolves an S3 origin that has no local BucketDeployment source to its deployed bucket and serves it from real S3 on demand (the front/back-split case: files uploaded out of band), and resolves a Lambda Function URL / Lambda@Edge function's env vars to the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass a value when the CFn stack name differs.")).addOption(new Option("--cache-origin", "For a deployed-S3 origin (served from real S3 under --from-cfn-stack): keep fetched objects in memory for the session instead of re-reading on every request. Faster repeat reads / fewer S3 GETs, but an out-of-band S3 content change is not reflected until a --watch reload (which clears the cache) or a restart. Off by default (every request re-reads, always current). Not CloudFront CDN caching.").default(false)).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--assume-role [arn]", "Assume a Lambda Function URL origin's deployed execution role and forward STS-issued temp credentials into its container so the handler runs with the deployed permissions. Three forms: `--assume-role <arn>` (explicit ARN); `--assume-role` (bare, auto-resolves from state — requires --from-cfn-stack); `--no-assume-role` (opt out). Off by default (the dev shell credentials are forwarded).")).addOption(new Option("--watch", "Hot-reload: re-synth + re-resolve the distribution when the CDK app's source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). The server keeps the previous version serving when synth fails mid-reload.").default(false));
|
|
24946
|
+
});
|
|
24598
24947
|
}
|
|
24948
|
+
const defaultReadyDeps = {
|
|
24949
|
+
probe: probeHttp,
|
|
24950
|
+
isRunning: containerIsRunning,
|
|
24951
|
+
fetchLogs: readContainerLogTail,
|
|
24952
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
24953
|
+
now: () => Date.now()
|
|
24954
|
+
};
|
|
24599
24955
|
|
|
24600
24956
|
//#endregion
|
|
24601
|
-
//#region src/
|
|
24602
|
-
|
|
24603
|
-
|
|
24604
|
-
if (typeof data === "string") return data;
|
|
24605
|
-
return (Buffer.isBuffer(data) ? data : Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data)).toString("utf-8");
|
|
24606
|
-
}
|
|
24607
|
-
function attachAgentCoreWsBridge(httpServer, config) {
|
|
24608
|
-
const path = config.path ?? DEFAULT_PATH;
|
|
24609
|
-
const wss = new WebSocketServer({
|
|
24610
|
-
server: httpServer,
|
|
24611
|
-
path
|
|
24612
|
-
});
|
|
24613
|
-
const liveCloses = /* @__PURE__ */ new Set();
|
|
24614
|
-
wss.on("connection", (browser) => {
|
|
24615
|
-
const sessionId = config.sessionId ?? randomUUID();
|
|
24616
|
-
const containerPort = typeof config.containerPort === "function" ? config.containerPort() : config.containerPort;
|
|
24617
|
-
const handle = bridgeAgentCoreWs(config.containerHost, containerPort, {
|
|
24618
|
-
sessionId,
|
|
24619
|
-
...config.authorization && { authorization: config.authorization },
|
|
24620
|
-
...config.webSocketImpl && { webSocketImpl: config.webSocketImpl },
|
|
24621
|
-
onMessage: (text) => {
|
|
24622
|
-
if (browser.readyState === browser.OPEN) browser.send(text);
|
|
24623
|
-
},
|
|
24624
|
-
onClose: () => {
|
|
24625
|
-
try {
|
|
24626
|
-
browser.close();
|
|
24627
|
-
} catch {}
|
|
24628
|
-
},
|
|
24629
|
-
onError: (err) => {
|
|
24630
|
-
if (browser.readyState === browser.OPEN) try {
|
|
24631
|
-
browser.send(`[bridge error] ${err.message}`);
|
|
24632
|
-
} catch {}
|
|
24633
|
-
try {
|
|
24634
|
-
browser.close();
|
|
24635
|
-
} catch {}
|
|
24636
|
-
}
|
|
24637
|
-
});
|
|
24638
|
-
liveCloses.add(handle.close);
|
|
24639
|
-
browser.on("message", (data, isBinary) => {
|
|
24640
|
-
handle.send(decodeBrowserFrame(data, isBinary));
|
|
24641
|
-
});
|
|
24642
|
-
browser.on("close", () => {
|
|
24643
|
-
liveCloses.delete(handle.close);
|
|
24644
|
-
handle.close();
|
|
24645
|
-
});
|
|
24646
|
-
browser.on("error", () => {
|
|
24647
|
-
liveCloses.delete(handle.close);
|
|
24648
|
-
handle.close();
|
|
24649
|
-
});
|
|
24650
|
-
});
|
|
24957
|
+
//#region src/cli/commands/web-function-serve.ts
|
|
24958
|
+
function prepareWebFunctionServe(resolved) {
|
|
24959
|
+
const code = parseWebFunctionBuildConfig(resolved.revisionLogicalId, resolved.revision.Properties?.["BuildConfig"], resolved.stack.template);
|
|
24651
24960
|
return {
|
|
24652
|
-
|
|
24653
|
-
|
|
24654
|
-
|
|
24655
|
-
|
|
24656
|
-
wss.close(() => res());
|
|
24657
|
-
})
|
|
24961
|
+
resolved,
|
|
24962
|
+
code,
|
|
24963
|
+
spec: resolveWebFunctionRuntime(code.runtime),
|
|
24964
|
+
serviceConfig: readWebFunctionServiceConfig(resolved.revision)
|
|
24658
24965
|
};
|
|
24659
24966
|
}
|
|
24660
|
-
function
|
|
24661
|
-
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
res.end("Upgrade required: connect over WebSocket.\n");
|
|
24665
|
-
});
|
|
24666
|
-
const attached = attachAgentCoreWsBridge(httpServer, config);
|
|
24667
|
-
return new Promise((resolve, reject) => {
|
|
24668
|
-
httpServer.once("error", reject);
|
|
24669
|
-
httpServer.listen(config.port ?? 0, host, () => {
|
|
24670
|
-
httpServer.removeListener("error", reject);
|
|
24671
|
-
httpServer.on("error", (err) => getLogger().debug(`agentcore-ws bridge server error: ${err.message}`));
|
|
24672
|
-
const port = httpServer.address().port;
|
|
24673
|
-
resolve({
|
|
24674
|
-
url: `ws://${formatAuthority(host, port)}${attached.path}`,
|
|
24675
|
-
port,
|
|
24676
|
-
close: () => new Promise((res) => {
|
|
24677
|
-
attached.close().then(() => httpServer.close(() => res()));
|
|
24678
|
-
})
|
|
24679
|
-
});
|
|
24680
|
-
});
|
|
24681
|
-
});
|
|
24967
|
+
function webFunctionConnectHost(host) {
|
|
24968
|
+
if (host === "0.0.0.0") return "127.0.0.1";
|
|
24969
|
+
if (host === "::" || host === "[::]") return "::1";
|
|
24970
|
+
return host;
|
|
24682
24971
|
}
|
|
24972
|
+
var WebFunctionServe = class {
|
|
24973
|
+
artifact;
|
|
24974
|
+
artifactInFlight;
|
|
24975
|
+
runInFlight;
|
|
24976
|
+
id;
|
|
24977
|
+
name;
|
|
24978
|
+
stopLogs;
|
|
24979
|
+
exitWatch;
|
|
24980
|
+
teardownPromise;
|
|
24981
|
+
waitingOnRunFlag = false;
|
|
24982
|
+
recent = new RecentOutput();
|
|
24983
|
+
prepared;
|
|
24984
|
+
options;
|
|
24985
|
+
constructor(prepared, options) {
|
|
24986
|
+
this.prepared = prepared;
|
|
24987
|
+
this.options = options;
|
|
24988
|
+
}
|
|
24989
|
+
get waitingOnRun() {
|
|
24990
|
+
return this.waitingOnRunFlag;
|
|
24991
|
+
}
|
|
24992
|
+
get containerName() {
|
|
24993
|
+
return this.name;
|
|
24994
|
+
}
|
|
24995
|
+
get tornDown() {
|
|
24996
|
+
return this.teardownPromise !== void 0;
|
|
24997
|
+
}
|
|
24998
|
+
get exited() {
|
|
24999
|
+
return this.exitWatch?.exited;
|
|
25000
|
+
}
|
|
25001
|
+
async start() {
|
|
25002
|
+
const logger = getLogger();
|
|
25003
|
+
const { resolved, code, spec, serviceConfig } = this.prepared;
|
|
25004
|
+
const opts = this.options;
|
|
25005
|
+
this.artifactInFlight = resolveWebFunctionArtifact(resolved.stack, resolved.revisionLogicalId, code);
|
|
25006
|
+
this.artifact = await this.artifactInFlight;
|
|
25007
|
+
if (this.tornDown) return void 0;
|
|
25008
|
+
const assumeRole = webFunctionAssumeRole(opts.envOptions.assumeRole, serviceConfig);
|
|
25009
|
+
const containerEnv = await resolveLambdaContainerEnv(buildWebFunctionLambdaView(resolved, serviceConfig, spec.runtime), {
|
|
25010
|
+
...opts.envOptions,
|
|
25011
|
+
...assumeRole !== void 0 && { assumeRole },
|
|
25012
|
+
deployedFunctionLookups: false
|
|
25013
|
+
}, opts.profileCredentials, opts.extraStateProviders);
|
|
25014
|
+
const { env, replacedHttpEndpoint } = finalizeWebFunctionEnv(containerEnv.env, {
|
|
25015
|
+
functionName: resolved.functionName,
|
|
25016
|
+
containerPort: WEB_FUNCTION_CONTAINER_PORT,
|
|
25017
|
+
timeoutSeconds: serviceConfig.timeoutSeconds,
|
|
25018
|
+
logGroup: serviceConfig.logGroup
|
|
25019
|
+
});
|
|
25020
|
+
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.`);
|
|
25021
|
+
const plan = planWebFunctionContainer(this.artifact, spec, env);
|
|
25022
|
+
const platform = opts.platform ?? defaultWebFunctionPlatform(spec);
|
|
25023
|
+
await pullImage(plan.image, opts.skipPull, platform);
|
|
25024
|
+
if (this.tornDown) return void 0;
|
|
25025
|
+
const hostPort = opts.port === 0 ? await pickFreePort() : opts.port;
|
|
25026
|
+
if (this.tornDown) return void 0;
|
|
25027
|
+
const name = this.name = `${getEmbedConfig().resourceNamePrefix}-webfn-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
25028
|
+
logger.info(`Starting web function container (image=${plan.image}, start=${plan.label}, port=${hostPort} -> ${WEB_FUNCTION_CONTAINER_PORT})...`);
|
|
25029
|
+
this.runInFlight = startWebFunctionContainer({
|
|
25030
|
+
plan,
|
|
25031
|
+
env,
|
|
25032
|
+
sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys),
|
|
25033
|
+
host: opts.host,
|
|
25034
|
+
hostPort,
|
|
25035
|
+
platform,
|
|
25036
|
+
name
|
|
25037
|
+
});
|
|
25038
|
+
const id = await this.runInFlight;
|
|
25039
|
+
if (this.tornDown) return void 0;
|
|
25040
|
+
this.id = id;
|
|
25041
|
+
this.stopLogs = streamLogs(id, { onOutput: (chunk) => this.recent.push(chunk) });
|
|
25042
|
+
this.exitWatch = watchContainerExit(id);
|
|
25043
|
+
const host = webFunctionConnectHost(opts.host);
|
|
25044
|
+
await waitForWebFunctionReady({
|
|
25045
|
+
containerId: id,
|
|
25046
|
+
host,
|
|
25047
|
+
port: hostPort,
|
|
25048
|
+
timeoutMs: opts.timeoutMs,
|
|
25049
|
+
recent: this.recent,
|
|
25050
|
+
label: plan.label
|
|
25051
|
+
});
|
|
25052
|
+
if (this.tornDown) return void 0;
|
|
25053
|
+
return {
|
|
25054
|
+
host,
|
|
25055
|
+
port: hostPort
|
|
25056
|
+
};
|
|
25057
|
+
}
|
|
25058
|
+
exitFailure(message) {
|
|
25059
|
+
return containerFailure(message, this.id ?? "", this.recent, void 0, "LOCAL_START_WEB_FUNCTION_EXITED");
|
|
25060
|
+
}
|
|
25061
|
+
disposeArtifactNow() {
|
|
25062
|
+
disposeWebFunctionArtifact(this.artifact);
|
|
25063
|
+
}
|
|
25064
|
+
teardown() {
|
|
25065
|
+
return this.teardownPromise ??= this.doTeardown();
|
|
25066
|
+
}
|
|
25067
|
+
async doTeardown() {
|
|
25068
|
+
const logger = getLogger();
|
|
25069
|
+
this.exitWatch?.stop();
|
|
25070
|
+
if (this.artifact === void 0 && this.artifactInFlight !== void 0) try {
|
|
25071
|
+
this.artifact = await this.artifactInFlight;
|
|
25072
|
+
} catch {}
|
|
25073
|
+
if (this.id === void 0 && this.runInFlight !== void 0) {
|
|
25074
|
+
this.waitingOnRunFlag = true;
|
|
25075
|
+
try {
|
|
25076
|
+
this.id = await this.runInFlight;
|
|
25077
|
+
} catch {} finally {
|
|
25078
|
+
this.waitingOnRunFlag = false;
|
|
25079
|
+
}
|
|
25080
|
+
}
|
|
25081
|
+
if (this.stopLogs) try {
|
|
25082
|
+
this.stopLogs();
|
|
25083
|
+
} catch (err) {
|
|
25084
|
+
logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25085
|
+
}
|
|
25086
|
+
if (this.id) await removeContainer(this.id);
|
|
25087
|
+
disposeWebFunctionArtifact(this.artifact);
|
|
25088
|
+
}
|
|
25089
|
+
};
|
|
24683
25090
|
|
|
24684
25091
|
//#endregion
|
|
24685
|
-
//#region src/local
|
|
24686
|
-
|
|
24687
|
-
|
|
24688
|
-
|
|
24689
|
-
}
|
|
24690
|
-
|
|
24691
|
-
|
|
24692
|
-
|
|
24693
|
-
|
|
24694
|
-
|
|
24695
|
-
|
|
24696
|
-
|
|
24697
|
-
|
|
24698
|
-
|
|
24699
|
-
upstream.on("error", (err) => {
|
|
24700
|
-
getLogger().debug(`agentcore http serve upstream error: ${err.message}`);
|
|
24701
|
-
if (!clientRes.headersSent) clientRes.writeHead(502, { "content-type": "application/json" });
|
|
24702
|
-
clientRes.end(JSON.stringify({ error: `upstream error: ${err.message}` }));
|
|
24703
|
-
});
|
|
25092
|
+
//#region src/cli/commands/local-start-cloudfront.ts
|
|
25093
|
+
var LocalStartCloudFrontError = class extends CdkLocalError {
|
|
25094
|
+
constructor(message, cause) {
|
|
25095
|
+
super(message, "LOCAL_START_CLOUDFRONT_ERROR", cause);
|
|
25096
|
+
}
|
|
25097
|
+
};
|
|
25098
|
+
function parseOriginOverrides(values) {
|
|
25099
|
+
const out = /* @__PURE__ */ new Map();
|
|
25100
|
+
for (const raw of values ?? []) {
|
|
25101
|
+
const eq = raw.indexOf("=");
|
|
25102
|
+
if (eq <= 0 || eq === raw.length - 1) throw new LocalStartCloudFrontError(`Invalid --origin '${raw}'. Expected <originId>=<dir> (e.g. MyOrigin=./site).`);
|
|
25103
|
+
out.set(raw.slice(0, eq).trim(), raw.slice(eq + 1).trim());
|
|
25104
|
+
}
|
|
25105
|
+
return out;
|
|
24704
25106
|
}
|
|
24705
|
-
function
|
|
24706
|
-
const
|
|
24707
|
-
const
|
|
24708
|
-
|
|
24709
|
-
|
|
24710
|
-
|
|
24711
|
-
|
|
24712
|
-
|
|
24713
|
-
|
|
24714
|
-
|
|
24715
|
-
|
|
24716
|
-
|
|
24717
|
-
|
|
24718
|
-
|
|
24719
|
-
|
|
24720
|
-
|
|
24721
|
-
|
|
24722
|
-
|
|
24723
|
-
|
|
24724
|
-
|
|
24725
|
-
|
|
24726
|
-
method: "POST",
|
|
24727
|
-
path: upstreamPath,
|
|
24728
|
-
body,
|
|
24729
|
-
sessionId
|
|
24730
|
-
}).then((signed) => {
|
|
24731
|
-
const signedHeaders = {
|
|
24732
|
-
...headers,
|
|
24733
|
-
...signed
|
|
24734
|
-
};
|
|
24735
|
-
delete signedHeaders["transfer-encoding"];
|
|
24736
|
-
signedHeaders["content-length"] = String(body.length);
|
|
24737
|
-
const upstream = request({
|
|
24738
|
-
host: config.containerHost,
|
|
24739
|
-
port: config.containerPort,
|
|
24740
|
-
path: upstreamPath,
|
|
24741
|
-
method: "POST",
|
|
24742
|
-
headers: signedHeaders
|
|
24743
|
-
});
|
|
24744
|
-
wireUpstreamResponse(upstream, clientRes);
|
|
24745
|
-
upstream.end(body);
|
|
24746
|
-
}).catch((err) => {
|
|
24747
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
24748
|
-
getLogger().debug(`agentcore http serve sigv4 sign error: ${msg}`);
|
|
24749
|
-
if (!clientRes.headersSent) {
|
|
24750
|
-
clientRes.writeHead(500, { "content-type": "application/json" });
|
|
24751
|
-
clientRes.end(JSON.stringify({ error: `sigv4 signing failed: ${msg}` }));
|
|
24752
|
-
}
|
|
24753
|
-
});
|
|
25107
|
+
function parseKvsFileOverrides(values) {
|
|
25108
|
+
const out = /* @__PURE__ */ new Map();
|
|
25109
|
+
for (const raw of values ?? []) {
|
|
25110
|
+
const eq = raw.indexOf("=");
|
|
25111
|
+
if (eq <= 0 || eq === raw.length - 1) throw new LocalStartCloudFrontError(`Invalid --kvs-file '${raw}'. Expected <key>=<file.json> (e.g. RoutesKvs=./kvs.json).`);
|
|
25112
|
+
out.set(raw.slice(0, eq).trim(), raw.slice(eq + 1).trim());
|
|
25113
|
+
}
|
|
25114
|
+
return out;
|
|
25115
|
+
}
|
|
25116
|
+
function collectKvsStoreIdentities(template) {
|
|
25117
|
+
const out = [];
|
|
25118
|
+
for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {
|
|
25119
|
+
if (resource.Type !== "AWS::CloudFront::KeyValueStore") continue;
|
|
25120
|
+
const cdkPath = readCdkPath(resource) || void 0;
|
|
25121
|
+
const constructPath = cdkPath?.endsWith("/Resource") ? cdkPath.slice(0, -9) : cdkPath;
|
|
25122
|
+
const constructId = constructPath?.split("/").pop();
|
|
25123
|
+
out.push({
|
|
25124
|
+
logicalId,
|
|
25125
|
+
...cdkPath !== void 0 && { cdkPath },
|
|
25126
|
+
...constructPath !== void 0 && { constructPath },
|
|
25127
|
+
...constructId !== void 0 && constructId !== "" && { constructId }
|
|
24754
25128
|
});
|
|
24755
|
-
return;
|
|
24756
25129
|
}
|
|
24757
|
-
|
|
24758
|
-
host: config.containerHost,
|
|
24759
|
-
port: config.containerPort,
|
|
24760
|
-
path: upstreamPath,
|
|
24761
|
-
method: clientReq.method,
|
|
24762
|
-
headers
|
|
24763
|
-
});
|
|
24764
|
-
wireUpstreamResponse(upstream, clientRes);
|
|
24765
|
-
clientReq.on("error", (err) => {
|
|
24766
|
-
getLogger().debug(`agentcore http serve client error: ${err.message}`);
|
|
24767
|
-
upstream.destroy();
|
|
24768
|
-
});
|
|
24769
|
-
clientReq.pipe(upstream);
|
|
25130
|
+
return out;
|
|
24770
25131
|
}
|
|
24771
|
-
function
|
|
24772
|
-
|
|
24773
|
-
const
|
|
24774
|
-
const
|
|
24775
|
-
const
|
|
24776
|
-
const
|
|
24777
|
-
const
|
|
24778
|
-
|
|
24779
|
-
|
|
24780
|
-
|
|
24781
|
-
|
|
24782
|
-
|
|
24783
|
-
|
|
24784
|
-
|
|
24785
|
-
|
|
25132
|
+
function normalizeKvsFileKeys(kvsFiles, template) {
|
|
25133
|
+
if (kvsFiles.size === 0) return kvsFiles;
|
|
25134
|
+
const stores = collectKvsStoreIdentities(template);
|
|
25135
|
+
const candidates = () => stores.map((s) => s.constructPath ? `${displayIdent(s.logicalId, { listMember: true })} (${displayStackName(s.constructPath)})` : displayIdent(s.logicalId, { listMember: true })).join(", ") || "(none in this distribution)";
|
|
25136
|
+
const out = /* @__PURE__ */ new Map();
|
|
25137
|
+
for (const [key, filePath] of kvsFiles) {
|
|
25138
|
+
const matched = [
|
|
25139
|
+
stores.filter((s) => s.logicalId === key),
|
|
25140
|
+
stores.filter((s) => s.constructPath === key || s.cdkPath === key),
|
|
25141
|
+
stores.filter((s) => s.constructId === key)
|
|
25142
|
+
].find((t) => t.length > 0) ?? [];
|
|
25143
|
+
if (matched.length === 0) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' matched no AWS::CloudFront::KeyValueStore in the distribution. Pass a store's logical id, construct path, or bare construct id. Candidates: ${candidates()}.`);
|
|
25144
|
+
if (matched.length > 1) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' is ambiguous — it matched ${matched.length} KeyValueStores (${matched.map((s) => displayIdent(s.logicalId, { listMember: true })).join(", ")}). Use the logical id or full construct path.`);
|
|
25145
|
+
out.set(matched[0].logicalId, filePath);
|
|
25146
|
+
}
|
|
25147
|
+
return out;
|
|
25148
|
+
}
|
|
25149
|
+
async function attachKvsModules(distribution, stacks, options, profileCredentials, logger, extraStateProviders) {
|
|
25150
|
+
const stack = stacks.find((s) => s.stackName === distribution.stackName);
|
|
25151
|
+
const synthRegion = stack?.region;
|
|
25152
|
+
const kvsFiles = stack ? normalizeKvsFileKeys(parseKvsFileOverrides(options.kvsFile), stack.template) : parseKvsFileOverrides(options.kvsFile);
|
|
25153
|
+
const resolveDeployedKvs = isStateSourceActive(options, extraStateProviders) ? async (kvsLogicalId) => {
|
|
25154
|
+
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
|
|
25155
|
+
if (!provider) return void 0;
|
|
25156
|
+
let record;
|
|
25157
|
+
try {
|
|
25158
|
+
record = await provider.load(distribution.stackName, synthRegion);
|
|
25159
|
+
} finally {
|
|
25160
|
+
provider.dispose();
|
|
24786
25161
|
}
|
|
24787
|
-
|
|
24788
|
-
|
|
24789
|
-
|
|
24790
|
-
|
|
24791
|
-
|
|
24792
|
-
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
}
|
|
24796
|
-
proxyToContainer(req, res, config, match.path, { ...result.authorization && { authorization: result.authorization } });
|
|
24797
|
-
}).catch((err) => {
|
|
24798
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
24799
|
-
getLogger().debug(`agentcore http serve auth-check error: ${msg}`);
|
|
24800
|
-
req.resume();
|
|
24801
|
-
if (!res.headersSent) {
|
|
24802
|
-
res.writeHead(500, { "content-type": "application/json" });
|
|
24803
|
-
res.end(JSON.stringify({ error: `auth check failed: ${msg}` }));
|
|
24804
|
-
}
|
|
24805
|
-
});
|
|
24806
|
-
return;
|
|
25162
|
+
const physicalId = record?.resources[kvsLogicalId]?.physicalId;
|
|
25163
|
+
if (!physicalId) return void 0;
|
|
25164
|
+
if (physicalId.startsWith("arn:")) {
|
|
25165
|
+
const id = idFromArn(physicalId);
|
|
25166
|
+
return {
|
|
25167
|
+
arn: physicalId,
|
|
25168
|
+
...id !== void 0 && { id }
|
|
25169
|
+
};
|
|
24807
25170
|
}
|
|
24808
|
-
|
|
24809
|
-
}
|
|
24810
|
-
const
|
|
24811
|
-
|
|
24812
|
-
|
|
24813
|
-
...
|
|
24814
|
-
...config.authorization && { authorization: config.authorization }
|
|
24815
|
-
}) : void 0;
|
|
24816
|
-
return new Promise((resolve, reject) => {
|
|
24817
|
-
httpServer.once("error", reject);
|
|
24818
|
-
httpServer.listen(config.port ?? 0, host, () => {
|
|
24819
|
-
httpServer.removeListener("error", reject);
|
|
24820
|
-
httpServer.on("error", (err) => getLogger().debug(`agentcore http serve server error: ${err.message}`));
|
|
24821
|
-
const port = httpServer.address().port;
|
|
24822
|
-
resolve({
|
|
24823
|
-
httpUrl: `http://${formatAuthority(host, port)}`,
|
|
24824
|
-
...bridge && { wsUrl: `ws://${formatAuthority(host, port)}${bridge.path}` },
|
|
24825
|
-
port,
|
|
24826
|
-
setContainerPort: (newPort) => {
|
|
24827
|
-
config.containerPort = newPort;
|
|
24828
|
-
},
|
|
24829
|
-
close: () => new Promise((res) => {
|
|
24830
|
-
if (bridge) bridge.close().then(() => httpServer.close(() => res()));
|
|
24831
|
-
else httpServer.close(() => res());
|
|
24832
|
-
})
|
|
24833
|
-
});
|
|
24834
|
-
});
|
|
25171
|
+
return resolveDeployedKvsArnByName(physicalId, { ...profileCredentials !== void 0 && { credentials: profileCredentials } });
|
|
25172
|
+
} : void 0;
|
|
25173
|
+
const { warnings } = await resolveKvsModulesForDistribution(distribution, {
|
|
25174
|
+
...kvsFiles.size > 0 && { kvsFiles },
|
|
25175
|
+
...resolveDeployedKvs !== void 0 && { resolveDeployedKvs },
|
|
25176
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
24835
25177
|
});
|
|
25178
|
+
for (const warning of warnings) logger.warn(warning);
|
|
24836
25179
|
}
|
|
24837
|
-
function
|
|
24838
|
-
|
|
24839
|
-
|
|
24840
|
-
|
|
24841
|
-
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
|
|
24845
|
-
|
|
24846
|
-
|
|
24847
|
-
|
|
24848
|
-
|
|
24849
|
-
|
|
24850
|
-
const jwksCache = opts.jwksCache ?? createJwksCache();
|
|
24851
|
-
const warnedAt = opts.warnedAt ?? /* @__PURE__ */ new Map();
|
|
24852
|
-
return async (headers) => {
|
|
24853
|
-
const header = readAuthorizationHeader(headers) ?? injectedBearer;
|
|
24854
|
-
if (!header) return {
|
|
24855
|
-
allow: false,
|
|
24856
|
-
status: 401,
|
|
24857
|
-
message: "Missing Authorization: this runtime declares a customJwtAuthorizer. Send a Bearer JWT, or start the serve with --bearer-token / --no-verify-auth."
|
|
24858
|
-
};
|
|
24859
|
-
if (!(await verifyJwtViaDiscovery({
|
|
24860
|
-
discoveryUrl: authorizer.discoveryUrl,
|
|
24861
|
-
...authorizer.allowedAudience && { allowedAudience: authorizer.allowedAudience },
|
|
24862
|
-
...authorizer.allowedClients && { allowedClients: authorizer.allowedClients },
|
|
24863
|
-
...authorizer.allowedScopes && { allowedScopes: authorizer.allowedScopes },
|
|
24864
|
-
...authorizer.customClaims && { customClaims: authorizer.customClaims }
|
|
24865
|
-
}, header, jwksCache, { warnedAt })).allow) return {
|
|
24866
|
-
allow: false,
|
|
24867
|
-
status: 403,
|
|
24868
|
-
message: `Forbidden: the inbound JWT was rejected by the runtime's customJwtAuthorizer (signature / issuer / expiry / audience / scope check failed against ${authorizer.discoveryUrl}).`
|
|
24869
|
-
};
|
|
25180
|
+
function resolveCloudFrontTarget(target, stacks) {
|
|
25181
|
+
if (stacks.length === 0) throw new LocalStartCloudFrontError("No stacks found in the synthesized assembly.");
|
|
25182
|
+
const parsed = parseEcsTarget(target);
|
|
25183
|
+
const stack = pickStack(parsed, stacks, target);
|
|
25184
|
+
const resources = stack.template.Resources ?? {};
|
|
25185
|
+
if (parsed.isPath) {
|
|
25186
|
+
const index = buildCdkPathIndex(stack.template);
|
|
25187
|
+
const dists = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId }) => {
|
|
25188
|
+
const r = resources[logicalId];
|
|
25189
|
+
return r !== void 0 && r.Type === "AWS::CloudFront::Distribution";
|
|
25190
|
+
});
|
|
25191
|
+
if (dists.length === 0) throw notFound(target, stack, resources);
|
|
25192
|
+
if (dists.length > 1) throw new LocalStartCloudFrontError(`Target '${target}' matches ${dists.length} distributions in ${stack.stackName}: ${dists.map((d) => d.logicalId).join(", ")}. Refine the path or use the stack:LogicalId form.`);
|
|
24870
25193
|
return {
|
|
24871
|
-
|
|
24872
|
-
|
|
25194
|
+
stack,
|
|
25195
|
+
logicalId: dists[0].logicalId
|
|
24873
25196
|
};
|
|
24874
|
-
}
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
const bearerHeader = options.bearerToken ? `Bearer ${options.bearerToken}` : void 0;
|
|
24878
|
-
if (resolved.jwtAuthorizer) return {
|
|
24879
|
-
authCheck: buildAuthCheck(resolved.jwtAuthorizer, {
|
|
24880
|
-
...options.verifyAuth === false && { noVerifyAuth: true },
|
|
24881
|
-
...options.bearerToken && { bearerToken: options.bearerToken }
|
|
24882
|
-
}),
|
|
24883
|
-
...bearerHeader && { bridgeAuthorization: bearerHeader },
|
|
24884
|
-
sign: false
|
|
24885
|
-
};
|
|
24886
|
-
if (sigv4Active) return { sign: true };
|
|
25197
|
+
}
|
|
25198
|
+
const res = resources[parsed.pathOrId];
|
|
25199
|
+
if (!res || res.Type !== "AWS::CloudFront::Distribution") throw notFound(target, stack, resources);
|
|
24887
25200
|
return {
|
|
24888
|
-
|
|
24889
|
-
|
|
25201
|
+
stack,
|
|
25202
|
+
logicalId: parsed.pathOrId
|
|
24890
25203
|
};
|
|
24891
25204
|
}
|
|
24892
|
-
function
|
|
24893
|
-
const
|
|
24894
|
-
|
|
24895
|
-
|
|
25205
|
+
function pickStack(parsed, stacks, target) {
|
|
25206
|
+
const selection = selectTargetStack(parsed, target, stacks);
|
|
25207
|
+
switch (selection.kind) {
|
|
25208
|
+
case "found": return selection.stack;
|
|
25209
|
+
case "no-prefix": throw new LocalStartCloudFrontError(`Target '${target}' has no stack prefix, and the assembly contains ${stacks.length} stacks: ${describeStacks(stacks)}. Pass it as 'Stack/Path' or 'Stack:LogicalId'.`);
|
|
25210
|
+
case "not-found": throw new LocalStartCloudFrontError(`No stack matches '${selection.pattern}'. Available stacks: ${describeStacks(stacks)}.`);
|
|
25211
|
+
case "ambiguous": throw new LocalStartCloudFrontError(`Multiple stacks match '${selection.pattern}': ${describeStacks(selection.matched)}. Refine the pattern, or use the stack's display path (path form).`);
|
|
25212
|
+
}
|
|
24896
25213
|
}
|
|
24897
|
-
|
|
24898
|
-
|
|
24899
|
-
|
|
24900
|
-
|
|
24901
|
-
const n = Number(raw);
|
|
24902
|
-
if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
|
|
24903
|
-
return n;
|
|
25214
|
+
function notFound(target, stack, resources) {
|
|
25215
|
+
const dists = Object.entries(resources).filter(([, r]) => r.Type === CLOUDFRONT_DISTRIBUTION_TYPE).map(([logicalId]) => logicalId);
|
|
25216
|
+
const available = dists.length > 0 ? ` Available distributions in ${stack.stackName}: ${dists.join(", ")}.` : ` ${stack.stackName} declares no ${CLOUDFRONT_DISTRIBUTION_TYPE} resources.`;
|
|
25217
|
+
return new LocalStartCloudFrontError(`Target '${target}' did not match a CloudFront distribution in ${stack.stackName}.${available}`);
|
|
24904
25218
|
}
|
|
24905
|
-
function
|
|
24906
|
-
|
|
24907
|
-
|
|
24908
|
-
|
|
24909
|
-
|
|
24910
|
-
|
|
24911
|
-
|
|
24912
|
-
|
|
24913
|
-
|
|
24914
|
-
|
|
24915
|
-
|
|
24916
|
-
|
|
24917
|
-
|
|
24918
|
-
|
|
24919
|
-
|
|
24920
|
-
|
|
24921
|
-
|
|
24922
|
-
|
|
24923
|
-
|
|
24924
|
-
|
|
24925
|
-
|
|
25219
|
+
async function bootLambdaUrlOrigins(distribution, stacks, opts) {
|
|
25220
|
+
const logger = getLogger();
|
|
25221
|
+
const invokers = /* @__PURE__ */ new Map();
|
|
25222
|
+
const runners = [];
|
|
25223
|
+
const functionLogicalIds = /* @__PURE__ */ new Set();
|
|
25224
|
+
for (const origin of distribution.origins.values()) if (origin.kind === "lambda-url") functionLogicalIds.add(origin.functionLogicalId);
|
|
25225
|
+
for (const functionLogicalId of functionLogicalIds) {
|
|
25226
|
+
let runner;
|
|
25227
|
+
try {
|
|
25228
|
+
const lambda = resolveLambdaTarget(`${distribution.stackName}:${functionLogicalId}`, stacks);
|
|
25229
|
+
const containerEnv = await resolveLambdaContainerEnv(lambda, opts.envOptions, opts.profileCredentials, opts.extraStateProviders);
|
|
25230
|
+
runner = createFrontDoorLambdaRunner(lambda, {
|
|
25231
|
+
containerHost: opts.containerHost,
|
|
25232
|
+
skipPull: opts.skipPull,
|
|
25233
|
+
...opts.envOptions.region !== void 0 && { region: opts.envOptions.region },
|
|
25234
|
+
containerEnv: containerEnv.env,
|
|
25235
|
+
...containerEnv.sensitiveEnvKeys.length > 0 && { sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys) }
|
|
25236
|
+
});
|
|
25237
|
+
logger.info(`Booting Lambda Function URL origin container for ${functionLogicalId} (the backing Lambda runs locally via RIE)...`);
|
|
25238
|
+
await runner.start();
|
|
25239
|
+
} catch (err) {
|
|
25240
|
+
await Promise.all(runners.map((r) => r.stop().catch(() => void 0)));
|
|
25241
|
+
throw new LocalStartCloudFrontError(`Failed to boot the Lambda Function URL origin's backing function ${displayIdent(functionLogicalId)}: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
|
|
25242
|
+
}
|
|
25243
|
+
runners.push(runner);
|
|
25244
|
+
invokers.set(functionLogicalId, (event) => runner.invoke(event));
|
|
25245
|
+
}
|
|
24926
25246
|
return {
|
|
24927
|
-
|
|
24928
|
-
|
|
24929
|
-
routes: [{
|
|
24930
|
-
method: "POST",
|
|
24931
|
-
path: "/invocations"
|
|
24932
|
-
}, {
|
|
24933
|
-
method: "GET",
|
|
24934
|
-
path: "/ping"
|
|
24935
|
-
}],
|
|
24936
|
-
attachWs: true,
|
|
24937
|
-
readyPath: void 0
|
|
25247
|
+
invokers,
|
|
25248
|
+
runners
|
|
24938
25249
|
};
|
|
24939
25250
|
}
|
|
24940
|
-
async function
|
|
25251
|
+
async function drainWebFunctionServes(serves) {
|
|
25252
|
+
let done = 0;
|
|
25253
|
+
while (done < serves.length) {
|
|
25254
|
+
const batch = serves.slice(done);
|
|
25255
|
+
done = serves.length;
|
|
25256
|
+
await Promise.all(batch.map((s) => s.teardown().catch(() => void 0)));
|
|
25257
|
+
}
|
|
25258
|
+
}
|
|
25259
|
+
const WEB_FUNCTION_ORIGIN_READY_TIMEOUT_MS = 6e4;
|
|
25260
|
+
async function bootWebFunctionOrigins(distribution, stacks, opts) {
|
|
24941
25261
|
const logger = getLogger();
|
|
24942
|
-
|
|
24943
|
-
|
|
24944
|
-
|
|
24945
|
-
|
|
24946
|
-
|
|
24947
|
-
|
|
24948
|
-
|
|
24949
|
-
|
|
24950
|
-
|
|
24951
|
-
|
|
24952
|
-
|
|
24953
|
-
|
|
24954
|
-
|
|
24955
|
-
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
|
|
24960
|
-
|
|
24961
|
-
|
|
24962
|
-
|
|
24963
|
-
|
|
24964
|
-
|
|
24965
|
-
|
|
24966
|
-
|
|
24967
|
-
|
|
24968
|
-
|
|
24969
|
-
|
|
24970
|
-
|
|
24971
|
-
logger.
|
|
24972
|
-
}
|
|
24973
|
-
|
|
24974
|
-
|
|
24975
|
-
}
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
} catch {}
|
|
24981
|
-
if (profileCredsFile) try {
|
|
24982
|
-
await profileCredsFile.dispose();
|
|
24983
|
-
} catch {}
|
|
25262
|
+
const targets = /* @__PURE__ */ new Map();
|
|
25263
|
+
const serves = opts.sink ?? [];
|
|
25264
|
+
const endpointLogicalIds = /* @__PURE__ */ new Set();
|
|
25265
|
+
for (const origin of distribution.origins.values()) if (origin.kind === "web-function") endpointLogicalIds.add(origin.endpointLogicalId);
|
|
25266
|
+
if (endpointLogicalIds.size === 0) return {
|
|
25267
|
+
targets,
|
|
25268
|
+
serves
|
|
25269
|
+
};
|
|
25270
|
+
for (const endpointLogicalId of endpointLogicalIds) try {
|
|
25271
|
+
if (serves.length === 0) await ensureDockerAvailable();
|
|
25272
|
+
const resolved = resolveWebFunctionTarget(`${distribution.stackName}:${endpointLogicalId}`, stacks);
|
|
25273
|
+
const prepared = prepareWebFunctionServe(resolved);
|
|
25274
|
+
for (const line of webFunctionFidelityWarnings(resolved, prepared.serviceConfig)) logger.warn(line);
|
|
25275
|
+
logger.info(`Booting Lambda Web Function endpoint origin server for ${displayIdent(endpointLogicalId)} (revision ${displayIdent(resolved.revisionLogicalId)}, ${prepared.spec.runtime})...`);
|
|
25276
|
+
const serve = new WebFunctionServe(prepared, {
|
|
25277
|
+
envOptions: opts.envOptions,
|
|
25278
|
+
...opts.profileCredentials !== void 0 && { profileCredentials: opts.profileCredentials },
|
|
25279
|
+
...opts.extraStateProviders !== void 0 && { extraStateProviders: opts.extraStateProviders },
|
|
25280
|
+
host: opts.containerHost,
|
|
25281
|
+
port: 0,
|
|
25282
|
+
skipPull: opts.skipPull,
|
|
25283
|
+
timeoutMs: opts.timeoutMs ?? WEB_FUNCTION_ORIGIN_READY_TIMEOUT_MS
|
|
25284
|
+
});
|
|
25285
|
+
serves.push(serve);
|
|
25286
|
+
const address = await serve.start();
|
|
25287
|
+
if (!address) throw new Error("the server was stopped while starting");
|
|
25288
|
+
targets.set(endpointLogicalId, address);
|
|
25289
|
+
serve.exited?.then((code) => {
|
|
25290
|
+
if (serve.tornDown) return;
|
|
25291
|
+
logger.warn(`The Lambda Web Function endpoint origin server for ${displayIdent(endpointLogicalId)} exited (exit code ${code ?? "unknown"}); requests routed to it return 502. Restart start-cloudfront.`);
|
|
25292
|
+
});
|
|
25293
|
+
} catch (err) {
|
|
25294
|
+
await Promise.all(serves.map((s) => s.teardown().catch(() => void 0)));
|
|
25295
|
+
throw new LocalStartCloudFrontError(`Failed to boot the Lambda Web Function endpoint origin ${displayIdent(endpointLogicalId)}: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
|
|
25296
|
+
}
|
|
25297
|
+
return {
|
|
25298
|
+
targets,
|
|
25299
|
+
serves
|
|
24984
25300
|
};
|
|
24985
|
-
|
|
24986
|
-
|
|
24987
|
-
|
|
24988
|
-
|
|
24989
|
-
|
|
24990
|
-
|
|
24991
|
-
const
|
|
24992
|
-
|
|
24993
|
-
|
|
24994
|
-
|
|
24995
|
-
|
|
24996
|
-
|
|
24997
|
-
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
|
|
25001
|
-
|
|
25002
|
-
|
|
25003
|
-
|
|
25301
|
+
}
|
|
25302
|
+
async function bootLambdaEdgeFunctions(distribution, stacks, opts) {
|
|
25303
|
+
const logger = getLogger();
|
|
25304
|
+
const invokers = /* @__PURE__ */ new Map();
|
|
25305
|
+
const runners = [];
|
|
25306
|
+
const functionLogicalIds = /* @__PURE__ */ new Set();
|
|
25307
|
+
for (const behavior of distribution.behaviors) {
|
|
25308
|
+
const edge = behavior.lambdaEdge;
|
|
25309
|
+
if (!edge) continue;
|
|
25310
|
+
for (const assoc of [
|
|
25311
|
+
edge.viewerRequest,
|
|
25312
|
+
edge.originRequest,
|
|
25313
|
+
edge.originResponse,
|
|
25314
|
+
edge.viewerResponse
|
|
25315
|
+
]) if (assoc) functionLogicalIds.add(assoc.functionLogicalId);
|
|
25316
|
+
}
|
|
25317
|
+
for (const functionLogicalId of functionLogicalIds) try {
|
|
25318
|
+
const lambda = resolveLambdaTarget(`${distribution.stackName}:${functionLogicalId}`, stacks);
|
|
25319
|
+
const containerEnv = await resolveLambdaContainerEnv(lambda, opts.envOptions, opts.profileCredentials, opts.extraStateProviders);
|
|
25320
|
+
const runner = createFrontDoorLambdaRunner(lambda, {
|
|
25321
|
+
containerHost: opts.containerHost,
|
|
25322
|
+
skipPull: opts.skipPull,
|
|
25323
|
+
...opts.envOptions.region !== void 0 && { region: opts.envOptions.region },
|
|
25324
|
+
containerEnv: containerEnv.env,
|
|
25325
|
+
...containerEnv.sensitiveEnvKeys.length > 0 && { sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys) }
|
|
25326
|
+
});
|
|
25327
|
+
logger.info(`Booting Lambda@Edge container for ${functionLogicalId} (the function runs locally via RIE)...`);
|
|
25328
|
+
await runner.start();
|
|
25329
|
+
runners.push(runner);
|
|
25330
|
+
invokers.set(functionLogicalId, (event) => runner.invoke(event));
|
|
25331
|
+
} catch (err) {
|
|
25332
|
+
logger.warn(`Could not boot Lambda@Edge function ${displayIdent(functionLogicalId)}; its stage will be skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
25333
|
+
}
|
|
25334
|
+
return {
|
|
25335
|
+
invokers,
|
|
25336
|
+
runners
|
|
25004
25337
|
};
|
|
25005
|
-
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
const { context: imageContext, loaded: loadedState } = stateProvider && candidate ? await buildAgentCoreImageContext(candidate, stateProvider, options) : {
|
|
25015
|
-
context: void 0,
|
|
25016
|
-
loaded: void 0
|
|
25338
|
+
}
|
|
25339
|
+
function buildLambdaEnvOptions(options, extraStateProviders) {
|
|
25340
|
+
return {
|
|
25341
|
+
...hostStateSourceFields(options, extraStateProviders),
|
|
25342
|
+
...options.fromCfnStack !== void 0 && { fromCfnStack: options.fromCfnStack },
|
|
25343
|
+
...options.assumeRole !== void 0 && { assumeRole: options.assumeRole },
|
|
25344
|
+
...options.region !== void 0 && { region: options.region },
|
|
25345
|
+
...options.profile !== void 0 && { profile: options.profile },
|
|
25346
|
+
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion }
|
|
25017
25347
|
};
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
|
|
25021
|
-
|
|
25022
|
-
|
|
25023
|
-
|
|
25024
|
-
|
|
25025
|
-
|
|
25026
|
-
const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext);
|
|
25027
|
-
let containerHostPort = await pickFreePort();
|
|
25028
|
-
const containerHost = options.containerHost;
|
|
25029
|
-
const containerName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
25030
|
-
logger.info(`Starting agent container (image=${image}, port=${containerHostPort} -> ${plan.containerPortLabel})...`);
|
|
25031
|
-
containerId = await runDetached({
|
|
25032
|
-
image,
|
|
25033
|
-
mounts: [],
|
|
25034
|
-
env: dockerEnv,
|
|
25035
|
-
cmd: [],
|
|
25036
|
-
hostPort: containerHostPort,
|
|
25037
|
-
host: containerHost,
|
|
25038
|
-
platform: options.platform,
|
|
25039
|
-
name: containerName,
|
|
25040
|
-
...plan.containerPort !== void 0 && { containerPort: plan.containerPort },
|
|
25041
|
-
...sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys }
|
|
25042
|
-
});
|
|
25043
|
-
stopLogs = streamLogs(containerId);
|
|
25044
|
-
const shutdown = async (signal, exitCode) => {
|
|
25045
|
-
if (shuttingDown) return;
|
|
25046
|
-
shuttingDown = true;
|
|
25047
|
-
logger.info(`Received ${signal}, shutting down...`);
|
|
25048
|
-
await teardown();
|
|
25049
|
-
process.exit(exitCode);
|
|
25348
|
+
}
|
|
25349
|
+
function buildLambdaBootOptions(options, profileCredentials, extraStateProviders) {
|
|
25350
|
+
return {
|
|
25351
|
+
containerHost: options.host,
|
|
25352
|
+
skipPull: options.pull === false,
|
|
25353
|
+
envOptions: buildLambdaEnvOptions(options, extraStateProviders),
|
|
25354
|
+
...profileCredentials !== void 0 && { profileCredentials },
|
|
25355
|
+
...extraStateProviders !== void 0 && { extraStateProviders }
|
|
25050
25356
|
};
|
|
25051
|
-
|
|
25052
|
-
|
|
25357
|
+
}
|
|
25358
|
+
function warnUnsupported(distribution, extraStateProviders) {
|
|
25359
|
+
const logger = getLogger();
|
|
25360
|
+
for (const origin of distribution.origins.values()) if (origin.kind === "custom") logger.warn(`Origin ${displayIdent(origin.originId)} is a custom (non-S3, non-Lambda) origin (${displayUntrustedValue(String(origin.domainName))}); ${getEmbedConfig().cliName} start-cloudfront serves S3, Lambda Function URL and Lambda Web Function endpoint origins only. Requests routed to it return 502.`);
|
|
25361
|
+
else if (origin.kind === "s3-unresolved") logger.warn(`Origin ${displayIdent(origin.originId)} is an S3 origin with no resolvable local source (no BucketDeployment found, or its source could not be located in the cloud assembly). Pass ${stateSourceFlagNames(extraStateProviders).join(" or ")} to serve the deployed bucket from real S3 on demand, or point it at a local directory with --origin ${origin.originId}=<dir>. Requests routed to it return 502.`);
|
|
25362
|
+
}
|
|
25363
|
+
function warnUnusedCacheOrigin(options, extraStateProviders) {
|
|
25364
|
+
if (options.cacheOrigin === true && !isStateSourceActive(options, extraStateProviders)) {
|
|
25365
|
+
const flags = stateSourceFlagNames(extraStateProviders).join(" or ");
|
|
25366
|
+
getLogger().warn(`--cache-origin has no effect without ${flags}: it caches a deployed-S3 read-through origin, which is only built under a state source. A local BucketDeployment / --origin <id>=<dir> origin serves from disk and is not cached. Pass ${flags} to enable the deployed-S3 origin (then --cache-origin applies), or drop --cache-origin.`);
|
|
25367
|
+
}
|
|
25368
|
+
}
|
|
25369
|
+
async function resolveDeployedS3Origins(distribution, stacks, options, profileCredentials, logger, extraStateProviders) {
|
|
25370
|
+
const readers = /* @__PURE__ */ new Map();
|
|
25371
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
25372
|
+
const stateFlag = activeStateSourceFlag(options, extraStateProviders);
|
|
25373
|
+
if (stateFlag === void 0) return {
|
|
25374
|
+
readers,
|
|
25375
|
+
buckets
|
|
25376
|
+
};
|
|
25377
|
+
const synthRegion = stacks.find((s) => s.stackName === distribution.stackName)?.region;
|
|
25378
|
+
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
|
|
25379
|
+
let record;
|
|
25053
25380
|
try {
|
|
25054
|
-
|
|
25055
|
-
|
|
25056
|
-
|
|
25057
|
-
|
|
25058
|
-
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25381
|
+
record = provider ? await provider.load(distribution.stackName, synthRegion) : void 0;
|
|
25382
|
+
} finally {
|
|
25383
|
+
provider?.dispose();
|
|
25384
|
+
}
|
|
25385
|
+
const region = options.stackRegion ?? options.region ?? synthRegion ?? record?.region;
|
|
25386
|
+
const distributionId = record?.resources[distribution.logicalId]?.physicalId;
|
|
25387
|
+
for (const origin of [...distribution.origins.values()]) {
|
|
25388
|
+
if (origin.kind !== "s3-unresolved") continue;
|
|
25389
|
+
let bucketName;
|
|
25390
|
+
let via;
|
|
25391
|
+
if (origin.bucketLogicalId !== void 0) {
|
|
25392
|
+
bucketName = record?.resources[origin.bucketLogicalId]?.physicalId;
|
|
25393
|
+
via = "deployed state";
|
|
25394
|
+
} else if (origin.bucketName !== void 0) {
|
|
25395
|
+
bucketName = origin.bucketName;
|
|
25396
|
+
via = "the origin's DomainName";
|
|
25397
|
+
} else if (origin.deployedConfigOnly && distributionId) {
|
|
25398
|
+
bucketName = await resolveDeployedOriginBucket({
|
|
25399
|
+
distributionId,
|
|
25400
|
+
originId: origin.originId,
|
|
25401
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
25066
25402
|
});
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25071
|
-
}
|
|
25072
|
-
|
|
25073
|
-
|
|
25074
|
-
}
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
routes: plan.routes,
|
|
25081
|
-
attachWs: plan.attachWs,
|
|
25082
|
-
...authPlan.bridgeAuthorization && { authorization: authPlan.bridgeAuthorization },
|
|
25083
|
-
...authPlan.authCheck && { authCheck: authPlan.authCheck },
|
|
25084
|
-
...signRequest && { signRequest },
|
|
25085
|
-
...options.sessionId && { sessionId: options.sessionId }
|
|
25403
|
+
via = "GetDistributionConfig";
|
|
25404
|
+
} else continue;
|
|
25405
|
+
if (!bucketName) continue;
|
|
25406
|
+
readers.set(origin.originId, createS3OriginReader(bucketName, {
|
|
25407
|
+
...region !== void 0 && { region },
|
|
25408
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials },
|
|
25409
|
+
...options.cacheOrigin === true && { cache: true }
|
|
25410
|
+
}));
|
|
25411
|
+
buckets.set(origin.originId, bucketName);
|
|
25412
|
+
distribution.origins.set(origin.originId, {
|
|
25413
|
+
kind: "s3-deployed",
|
|
25414
|
+
originId: origin.originId,
|
|
25415
|
+
bucketName
|
|
25086
25416
|
});
|
|
25087
|
-
|
|
25088
|
-
await teardown();
|
|
25089
|
-
throw err;
|
|
25417
|
+
logger.info(`Origin ${displayIdent(origin.originId)}: no local BucketDeployment source; serving from deployed S3 (bucket=${bucketName}, resolved via ${via}) on demand under ${stateFlag}.`);
|
|
25090
25418
|
}
|
|
25091
|
-
|
|
25092
|
-
|
|
25093
|
-
|
|
25094
|
-
}
|
|
25095
|
-
|
|
25096
|
-
|
|
25097
|
-
|
|
25098
|
-
|
|
25419
|
+
return {
|
|
25420
|
+
readers,
|
|
25421
|
+
buckets
|
|
25422
|
+
};
|
|
25423
|
+
}
|
|
25424
|
+
function annotateDeployedS3Origins(distribution, buckets) {
|
|
25425
|
+
for (const [originId, bucketName] of buckets) {
|
|
25426
|
+
const origin = distribution.origins.get(originId);
|
|
25427
|
+
if (origin && origin.kind === "s3-unresolved") distribution.origins.set(originId, {
|
|
25428
|
+
kind: "s3-deployed",
|
|
25429
|
+
originId,
|
|
25430
|
+
bucketName
|
|
25431
|
+
});
|
|
25099
25432
|
}
|
|
25100
|
-
|
|
25101
|
-
|
|
25102
|
-
|
|
25103
|
-
|
|
25104
|
-
|
|
25105
|
-
|
|
25106
|
-
|
|
25107
|
-
|
|
25108
|
-
|
|
25109
|
-
|
|
25433
|
+
}
|
|
25434
|
+
async function localStartCloudFrontCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
25435
|
+
const logger = getLogger();
|
|
25436
|
+
if (options.verbose) logger.setLevel("debug");
|
|
25437
|
+
const originOverrides = parseOriginOverrides(options.origin);
|
|
25438
|
+
const tlsRequested = options.tls === true || options.tlsCert !== void 0 || options.tlsKey !== void 0;
|
|
25439
|
+
await applyRoleArnIfSet({
|
|
25440
|
+
roleArn: options.roleArn,
|
|
25441
|
+
region: options.region,
|
|
25442
|
+
profile: options.profile
|
|
25443
|
+
});
|
|
25444
|
+
const appCmd = resolveApp(options.app);
|
|
25445
|
+
if (!appCmd) throw new LocalStartCloudFrontError(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
|
|
25446
|
+
const basePort = parseInt(options.port, 10);
|
|
25447
|
+
if (!Number.isFinite(basePort) || basePort < 0 || basePort > 65535) throw new LocalStartCloudFrontError(`--port must be 0..65535 (got ${options.port}).`);
|
|
25448
|
+
const synthAndResolve = async () => {
|
|
25449
|
+
logger.info("Synthesizing CDK app...");
|
|
25450
|
+
const synthesizer = hostSynthesizer ?? new Synthesizer();
|
|
25451
|
+
const context = parseContextOptions(options.context);
|
|
25452
|
+
const synthOpts = {
|
|
25453
|
+
app: appCmd,
|
|
25454
|
+
output: options.output,
|
|
25455
|
+
...options.region && { region: options.region },
|
|
25456
|
+
...options.profile && { profile: options.profile },
|
|
25457
|
+
...Object.keys(context).length > 0 && { context }
|
|
25458
|
+
};
|
|
25459
|
+
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
25460
|
+
const chosen = await resolveSingleTarget(target, {
|
|
25461
|
+
entries: listTargets(stacks).cloudFrontDistributions,
|
|
25462
|
+
message: "Select a CloudFront distribution to serve",
|
|
25463
|
+
noun: "CloudFront distributions",
|
|
25464
|
+
onMissing: () => new LocalStartCloudFrontError(`${getEmbedConfig().cliName} start-cloudfront requires a <target>. Pass a distribution path like 'Stack/MyDist', or run it in a TTY to pick interactively.`)
|
|
25110
25465
|
});
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25466
|
+
target = chosen;
|
|
25467
|
+
const { stack, logicalId } = resolveCloudFrontTarget(chosen, stacks);
|
|
25468
|
+
return {
|
|
25469
|
+
distribution: resolveCloudFrontDistribution({
|
|
25470
|
+
stack,
|
|
25471
|
+
logicalId,
|
|
25472
|
+
originOverrides
|
|
25473
|
+
}),
|
|
25474
|
+
stacks
|
|
25114
25475
|
};
|
|
25476
|
+
};
|
|
25477
|
+
const initial = await synthAndResolve();
|
|
25478
|
+
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
25479
|
+
const deployedS3 = await resolveDeployedS3Origins(initial.distribution, initial.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
25480
|
+
warnUnsupported(initial.distribution, extraStateProviders);
|
|
25481
|
+
warnUnusedCacheOrigin(options, extraStateProviders);
|
|
25482
|
+
await attachKvsModules(initial.distribution, initial.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
25483
|
+
const bootOpts = buildLambdaBootOptions(options, profileCredentials, extraStateProviders);
|
|
25484
|
+
const webFunctionServes = [];
|
|
25485
|
+
let bootAborted = false;
|
|
25486
|
+
const onBootSignal = (signal) => {
|
|
25487
|
+
if (bootAborted) return;
|
|
25488
|
+
bootAborted = true;
|
|
25489
|
+
logger.info(`Received ${signal} during start-up; removing the web function containers...`);
|
|
25490
|
+
drainWebFunctionServes(webFunctionServes).then(() => process.exit(signal === "SIGINT" ? 130 : 0));
|
|
25491
|
+
};
|
|
25492
|
+
const bootSigint = () => onBootSignal("SIGINT");
|
|
25493
|
+
const bootSigterm = () => onBootSignal("SIGTERM");
|
|
25494
|
+
const disarmBootSignals = () => {
|
|
25495
|
+
process.off("SIGINT", bootSigint);
|
|
25496
|
+
process.off("SIGTERM", bootSigterm);
|
|
25497
|
+
};
|
|
25498
|
+
if ([...initial.distribution.origins.values()].some((o) => o.kind === "web-function")) {
|
|
25499
|
+
process.on("SIGINT", bootSigint);
|
|
25500
|
+
process.on("SIGTERM", bootSigterm);
|
|
25501
|
+
}
|
|
25502
|
+
const { invokers: lambdaInvokers, runners: lambdaRunners } = await bootLambdaUrlOrigins(initial.distribution, initial.stacks, bootOpts);
|
|
25503
|
+
const { invokers: edgeInvokers, runners: edgeRunners } = await bootLambdaEdgeFunctions(initial.distribution, initial.stacks, bootOpts);
|
|
25504
|
+
let server;
|
|
25505
|
+
try {
|
|
25506
|
+
const webFunctions = await bootWebFunctionOrigins(initial.distribution, initial.stacks, {
|
|
25507
|
+
...bootOpts,
|
|
25508
|
+
sink: webFunctionServes
|
|
25509
|
+
});
|
|
25510
|
+
let tls;
|
|
25511
|
+
if (tlsRequested) tls = await resolveFrontDoorTlsMaterials({
|
|
25512
|
+
certPath: options.tlsCert,
|
|
25513
|
+
keyPath: options.tlsKey
|
|
25514
|
+
});
|
|
25515
|
+
server = await startCloudFrontServer({
|
|
25516
|
+
distribution: initial.distribution,
|
|
25517
|
+
host: options.host,
|
|
25518
|
+
port: basePort,
|
|
25519
|
+
...tls && { tls },
|
|
25520
|
+
...lambdaInvokers.size > 0 && { lambdaInvokers },
|
|
25521
|
+
...edgeInvokers.size > 0 && { edgeInvokers },
|
|
25522
|
+
...deployedS3.readers.size > 0 && { s3OriginReaders: deployedS3.readers },
|
|
25523
|
+
...webFunctions.targets.size > 0 && { webFunctionOrigins: webFunctions.targets }
|
|
25524
|
+
});
|
|
25525
|
+
} catch (err) {
|
|
25526
|
+
await Promise.all([...[...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch(() => void 0)), ...webFunctionServes.map((s) => s.teardown().catch(() => void 0))]);
|
|
25527
|
+
if (bootAborted) return await new Promise(() => void 0);
|
|
25528
|
+
disarmBootSignals();
|
|
25529
|
+
throw err;
|
|
25530
|
+
}
|
|
25531
|
+
disarmBootSignals();
|
|
25532
|
+
if (bootAborted) return await new Promise(() => void 0);
|
|
25533
|
+
process.stdout.write(`CloudFront distribution serving on ${server.url} (${displayIdent(initial.distribution.logicalId)})\n`);
|
|
25534
|
+
process.stdout.write("^C to stop.\n");
|
|
25535
|
+
let watcher;
|
|
25536
|
+
let reloadChain = Promise.resolve();
|
|
25537
|
+
if (options.watch) {
|
|
25115
25538
|
const watchRoot = process.cwd();
|
|
25116
25539
|
const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
|
|
25117
25540
|
watchRoot,
|
|
25118
25541
|
output: options.output,
|
|
25119
25542
|
watchConfig: resolveWatchConfig()
|
|
25120
25543
|
});
|
|
25121
|
-
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}). The warm container reloads in place; the serve stays up.`);
|
|
25122
25544
|
watcher = createFileWatcher({
|
|
25123
25545
|
paths: [watchRoot],
|
|
25124
25546
|
ignored,
|
|
25125
25547
|
shouldTrigger,
|
|
25126
|
-
onChange: (
|
|
25548
|
+
onChange: () => {
|
|
25549
|
+
logger.info("Detected source change; reloading...");
|
|
25127
25550
|
reloadChain = reloadChain.then(async () => {
|
|
25128
|
-
if (tornDown || shuttingDown) return;
|
|
25129
|
-
let freshStacks;
|
|
25130
|
-
let freshResolved;
|
|
25131
|
-
let freshLoaded;
|
|
25132
|
-
let freshImageContext;
|
|
25133
|
-
let verdict;
|
|
25134
|
-
let nextAssetHash = currentAssetHash;
|
|
25135
|
-
try {
|
|
25136
|
-
freshStacks = (await synthesizer.synthesize(synthOpts)).stacks;
|
|
25137
|
-
const freshCandidate = pickAgentCoreCandidateStack(resolvedTarget, freshStacks);
|
|
25138
|
-
const ctx = stateProvider && freshCandidate ? await buildAgentCoreImageContext(freshCandidate, stateProvider, options) : {
|
|
25139
|
-
context: void 0,
|
|
25140
|
-
loaded: void 0
|
|
25141
|
-
};
|
|
25142
|
-
freshImageContext = ctx.context;
|
|
25143
|
-
freshLoaded = ctx.loaded;
|
|
25144
|
-
freshResolved = resolveAgentCoreTarget(resolvedTarget, freshStacks, freshImageContext);
|
|
25145
|
-
await resolveFromS3BucketIntrinsic(freshResolved, stateProvider, freshLoaded, freshImageContext);
|
|
25146
|
-
const assetCtx = await loadAgentCoreAssetContext({
|
|
25147
|
-
resolvedTarget,
|
|
25148
|
-
resolved: freshResolved,
|
|
25149
|
-
stacks: freshStacks,
|
|
25150
|
-
cdkOutDir,
|
|
25151
|
-
assetLoader,
|
|
25152
|
-
oldAssetHash: currentAssetHash
|
|
25153
|
-
});
|
|
25154
|
-
if (assetCtx?.newAssetHash !== void 0) nextAssetHash = assetCtx.newAssetHash;
|
|
25155
|
-
verdict = classifySourceChange(changedPaths, assetCtx);
|
|
25156
|
-
logger.info(`Detected source change (${changedPaths.length} path(s)); verdict=${verdict.kind} (${verdict.reason}).`);
|
|
25157
|
-
} catch (err) {
|
|
25158
|
-
logger.error(`Reload: re-synth / classify failed (${err instanceof Error ? err.message : String(err)}); skipping this change. The serve stays up against the current container.`);
|
|
25159
|
-
return;
|
|
25160
|
-
}
|
|
25161
|
-
if (tornDown || shuttingDown) return;
|
|
25162
25551
|
try {
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
if (stopLogs) {
|
|
25170
|
-
try {
|
|
25171
|
-
stopLogs();
|
|
25172
|
-
} catch {}
|
|
25173
|
-
stopLogs = void 0;
|
|
25174
|
-
}
|
|
25175
|
-
if (containerId) {
|
|
25176
|
-
await removeContainer(containerId);
|
|
25177
|
-
containerId = void 0;
|
|
25178
|
-
}
|
|
25179
|
-
const newImage = await resolveAgentCoreImage(freshResolved, options, freshLoaded, stateProvider);
|
|
25180
|
-
const { env: newEnv, sensitiveEnvKeys: newSensitive } = await buildContainerEnv(freshResolved, options, profileCredentials, profileCredsFile, stateProvider, freshLoaded, freshImageContext);
|
|
25181
|
-
const newPort = await pickFreePort();
|
|
25182
|
-
const newName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
25183
|
-
logger.info(`Reload: starting agent container (image=${newImage}, port=${newPort} -> ${plan.containerPortLabel})...`);
|
|
25184
|
-
containerId = await runDetached({
|
|
25185
|
-
image: newImage,
|
|
25186
|
-
mounts: [],
|
|
25187
|
-
env: newEnv,
|
|
25188
|
-
cmd: [],
|
|
25189
|
-
hostPort: newPort,
|
|
25190
|
-
host: containerHost,
|
|
25191
|
-
platform: options.platform,
|
|
25192
|
-
name: newName,
|
|
25193
|
-
...plan.containerPort !== void 0 && { containerPort: plan.containerPort },
|
|
25194
|
-
...newSensitive.size > 0 && { sensitiveEnvKeys: newSensitive }
|
|
25195
|
-
});
|
|
25196
|
-
stopLogs = streamLogs(containerId);
|
|
25197
|
-
containerHostPort = newPort;
|
|
25198
|
-
await waitReady(newPort);
|
|
25199
|
-
server?.setContainerPort(newPort);
|
|
25200
|
-
logger.info(`Reload: rebuilt the agent container; serve re-pointed to port ${newPort}.`);
|
|
25201
|
-
}
|
|
25202
|
-
currentAssetHash = nextAssetHash;
|
|
25552
|
+
const reloaded = await synthAndResolve();
|
|
25553
|
+
annotateDeployedS3Origins(reloaded.distribution, deployedS3.buckets);
|
|
25554
|
+
for (const reader of deployedS3.readers.values()) reader.clearCache();
|
|
25555
|
+
warnUnsupported(reloaded.distribution, extraStateProviders);
|
|
25556
|
+
await attachKvsModules(reloaded.distribution, reloaded.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
25557
|
+
server.update(reloaded.distribution);
|
|
25203
25558
|
logger.info("Reload complete.");
|
|
25204
25559
|
} catch (err) {
|
|
25205
|
-
logger.
|
|
25560
|
+
logger.warn(`Reload failed; keeping the previous version serving: ${err instanceof Error ? err.message : String(err)}`);
|
|
25206
25561
|
}
|
|
25207
|
-
});
|
|
25562
|
+
}).catch(() => void 0);
|
|
25208
25563
|
}
|
|
25209
25564
|
});
|
|
25565
|
+
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}).`);
|
|
25210
25566
|
}
|
|
25567
|
+
let shuttingDown = false;
|
|
25568
|
+
const shutdown = async (signal, exitCode) => {
|
|
25569
|
+
if (shuttingDown) return;
|
|
25570
|
+
shuttingDown = true;
|
|
25571
|
+
logger.info(`Received ${signal}, shutting down...`);
|
|
25572
|
+
if (watcher) try {
|
|
25573
|
+
await watcher.close();
|
|
25574
|
+
} catch {}
|
|
25575
|
+
try {
|
|
25576
|
+
await server.close();
|
|
25577
|
+
} catch {}
|
|
25578
|
+
await Promise.all([...[...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch((err) => {
|
|
25579
|
+
logger.debug(`Lambda runner stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25580
|
+
})), ...webFunctionServes.map((s) => s.teardown().catch((err) => {
|
|
25581
|
+
logger.debug(`Web function server teardown failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25582
|
+
}))]);
|
|
25583
|
+
await Promise.all([...deployedS3.readers.values()].map((reader) => reader.close()));
|
|
25584
|
+
process.exit(exitCode);
|
|
25585
|
+
};
|
|
25586
|
+
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
25587
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
25211
25588
|
await new Promise(() => void 0);
|
|
25212
25589
|
}
|
|
25213
|
-
function
|
|
25590
|
+
function createLocalStartCloudFrontCommand(opts = {}) {
|
|
25214
25591
|
setEmbedConfig(opts.embedConfig);
|
|
25215
|
-
const cmd = new Command("start-
|
|
25216
|
-
await
|
|
25592
|
+
const cmd = new Command("start-cloudfront").description("Run a long-running local server that serves a CloudFront distribution: its S3 origin content (resolved from the BucketDeployment source in the cloud assembly), its Lambda Function URL origins (the backing Lambda is run locally via RIE) and its Lambda Web Function endpoint origins (the revision the endpoint weights pick, run as start-web-function runs it), plus its viewer-request / viewer-response CloudFront Functions, reproducing the distribution routing locally so a rewrite / routing change is verifiable in seconds. Serves S3, Lambda Function URL and Web Function endpoint origins, and runs Lambda@Edge functions in local RIE containers. The Lambda kinds need Docker: a Function URL origin that fails to boot stops start-up (as does a Web Function origin), while a Lambda@Edge function that fails to boot is warn-and-skipped (its stage does not run). Other custom origins are not run (warn-and-skip; requests routed to one return 502). Tip: omit the target in a terminal to pick interactively.").argument("[target]", "CloudFront distribution to serve. Accepts the CDK Construct path ('MyStack/MyDist'), an ancestor prefix, or the stack-qualified logical id ('MyStack:MyDist'). When omitted in a TTY, an interactive picker opens.").action(withErrorHandling(async (target, options) => {
|
|
25593
|
+
await localStartCloudFrontCommand(target, options, opts.extraStateProviders, opts.synthesizer);
|
|
25217
25594
|
}));
|
|
25218
|
-
|
|
25595
|
+
addStartCloudFrontSpecificOptions(cmd);
|
|
25219
25596
|
[
|
|
25220
25597
|
...commonOptions(),
|
|
25221
25598
|
...appOptions(),
|
|
@@ -25224,586 +25601,640 @@ function createLocalStartAgentCoreCommand(opts = {}) {
|
|
|
25224
25601
|
cmd.addOption(regionOption);
|
|
25225
25602
|
return cmd;
|
|
25226
25603
|
}
|
|
25227
|
-
function
|
|
25228
|
-
return cmd.addOption(new Option("--port <
|
|
25604
|
+
function addStartCloudFrontSpecificOptions(cmd) {
|
|
25605
|
+
return cmd.addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--origin <originId=dir>", "Point a distribution origin at a local directory (repeatable). Use when cdk-local cannot resolve the origin's BucketDeployment source automatically (content uploaded out of band, or a non-CDK bucket).").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--kvs-file <key=file.json>", "Back a CloudFront Function's KeyValueStore reads (cf.kvs().get()) with a local JSON map (repeatable). The key is a KeyValueStore handle — its AWS::CloudFront::KeyValueStore resource logical id, its construct path, or its bare construct id; the file is a flat { \"key\": \"value\" } object. The AWS-free alternative to --from-cfn-stack, which instead reads the deployed store via the GetKey API.").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--tls", "Terminate real TLS (HTTPS). Uses --tls-cert / --tls-key when supplied, else an auto-generated self-signed cert.").default(false)).addOption(new Option("--tls-cert <path>", "PEM server certificate for --tls (implies --tls).")).addOption(new Option("--tls-key <path>", "PEM server private key for --tls (implies --tls).")).addOption(new Option("--no-pull", "Skip 'docker pull' for a Lambda Function URL / Web Function origin's base image (use the locally cached image).")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind to a deployed CloudFormation stack (ListStackResources). Resolves an S3 origin that has no local BucketDeployment source to its deployed bucket and serves it from real S3 on demand (the front/back-split case: files uploaded out of band), and resolves a Lambda Function URL / Lambda@Edge function's or a Web Function origin revision's env vars to the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass a value when the CFn stack name differs.")).addOption(new Option("--cache-origin", "For a deployed-S3 origin (served from real S3 under --from-cfn-stack): keep fetched objects in memory for the session instead of re-reading on every request. Faster repeat reads / fewer S3 GETs, but an out-of-band S3 content change is not reflected until a --watch reload (which clears the cache) or a restart. Off by default (every request re-reads, always current). Not CloudFront CDN caching.").default(false)).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--assume-role [arn]", "Assume a Lambda Function URL origin's (or a Web Function origin revision's) execution role and forward STS-issued temp credentials into its container so the handler runs with the deployed permissions. Three forms: `--assume-role <arn>` (explicit ARN); `--assume-role` (bare, auto-resolves from state — requires --from-cfn-stack); `--no-assume-role` (opt out). Off by default (the dev shell credentials are forwarded).")).addOption(new Option("--watch", "Hot-reload: re-synth + re-resolve the distribution when the CDK app's source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). The server keeps the previous version serving when synth fails mid-reload.").default(false));
|
|
25229
25606
|
}
|
|
25230
25607
|
|
|
25231
25608
|
//#endregion
|
|
25232
|
-
//#region src/local/
|
|
25233
|
-
const
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
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
|
-
}
|
|
25609
|
+
//#region src/local/agentcore-ws-bridge.ts
|
|
25610
|
+
const DEFAULT_PATH = "/ws";
|
|
25611
|
+
function decodeBrowserFrame(data, isBinary) {
|
|
25612
|
+
if (typeof data === "string") return data;
|
|
25613
|
+
return (Buffer.isBuffer(data) ? data : Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data)).toString("utf-8");
|
|
25321
25614
|
}
|
|
25322
|
-
function
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
25332
|
-
|
|
25615
|
+
function attachAgentCoreWsBridge(httpServer, config) {
|
|
25616
|
+
const path = config.path ?? DEFAULT_PATH;
|
|
25617
|
+
const wss = new WebSocketServer({
|
|
25618
|
+
server: httpServer,
|
|
25619
|
+
path
|
|
25620
|
+
});
|
|
25621
|
+
const liveCloses = /* @__PURE__ */ new Set();
|
|
25622
|
+
wss.on("connection", (browser) => {
|
|
25623
|
+
const sessionId = config.sessionId ?? randomUUID();
|
|
25624
|
+
const containerPort = typeof config.containerPort === "function" ? config.containerPort() : config.containerPort;
|
|
25625
|
+
const handle = bridgeAgentCoreWs(config.containerHost, containerPort, {
|
|
25626
|
+
sessionId,
|
|
25627
|
+
...config.authorization && { authorization: config.authorization },
|
|
25628
|
+
...config.webSocketImpl && { webSocketImpl: config.webSocketImpl },
|
|
25629
|
+
onMessage: (text) => {
|
|
25630
|
+
if (browser.readyState === browser.OPEN) browser.send(text);
|
|
25631
|
+
},
|
|
25632
|
+
onClose: () => {
|
|
25633
|
+
try {
|
|
25634
|
+
browser.close();
|
|
25635
|
+
} catch {}
|
|
25636
|
+
},
|
|
25637
|
+
onError: (err) => {
|
|
25638
|
+
if (browser.readyState === browser.OPEN) try {
|
|
25639
|
+
browser.send(`[bridge error] ${err.message}`);
|
|
25640
|
+
} catch {}
|
|
25641
|
+
try {
|
|
25642
|
+
browser.close();
|
|
25643
|
+
} catch {}
|
|
25333
25644
|
}
|
|
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
25645
|
});
|
|
25381
|
-
|
|
25382
|
-
|
|
25646
|
+
liveCloses.add(handle.close);
|
|
25647
|
+
browser.on("message", (data, isBinary) => {
|
|
25648
|
+
handle.send(decodeBrowserFrame(data, isBinary));
|
|
25649
|
+
});
|
|
25650
|
+
browser.on("close", () => {
|
|
25651
|
+
liveCloses.delete(handle.close);
|
|
25652
|
+
handle.close();
|
|
25653
|
+
});
|
|
25654
|
+
browser.on("error", () => {
|
|
25655
|
+
liveCloses.delete(handle.close);
|
|
25656
|
+
handle.close();
|
|
25657
|
+
});
|
|
25658
|
+
});
|
|
25383
25659
|
return {
|
|
25384
|
-
|
|
25385
|
-
|
|
25386
|
-
|
|
25387
|
-
|
|
25660
|
+
path,
|
|
25661
|
+
close: () => new Promise((res) => {
|
|
25662
|
+
for (const closeLeg of liveCloses) closeLeg();
|
|
25663
|
+
liveCloses.clear();
|
|
25664
|
+
wss.close(() => res());
|
|
25665
|
+
})
|
|
25388
25666
|
};
|
|
25389
25667
|
}
|
|
25390
|
-
function
|
|
25391
|
-
|
|
25392
|
-
|
|
25393
|
-
|
|
25394
|
-
|
|
25395
|
-
}
|
|
25396
|
-
|
|
25397
|
-
|
|
25398
|
-
|
|
25399
|
-
|
|
25668
|
+
function startAgentCoreWsBridge(config) {
|
|
25669
|
+
const host = config.host ?? "127.0.0.1";
|
|
25670
|
+
const httpServer = createServer((_req, res) => {
|
|
25671
|
+
res.writeHead(426, { "Content-Type": "text/plain" });
|
|
25672
|
+
res.end("Upgrade required: connect over WebSocket.\n");
|
|
25673
|
+
});
|
|
25674
|
+
const attached = attachAgentCoreWsBridge(httpServer, config);
|
|
25675
|
+
return new Promise((resolve, reject) => {
|
|
25676
|
+
httpServer.once("error", reject);
|
|
25677
|
+
httpServer.listen(config.port ?? 0, host, () => {
|
|
25678
|
+
httpServer.removeListener("error", reject);
|
|
25679
|
+
httpServer.on("error", (err) => getLogger().debug(`agentcore-ws bridge server error: ${err.message}`));
|
|
25680
|
+
const port = httpServer.address().port;
|
|
25681
|
+
resolve({
|
|
25682
|
+
url: `ws://${formatAuthority(host, port)}${attached.path}`,
|
|
25683
|
+
port,
|
|
25684
|
+
close: () => new Promise((res) => {
|
|
25685
|
+
attached.close().then(() => httpServer.close(() => res()));
|
|
25686
|
+
})
|
|
25687
|
+
});
|
|
25688
|
+
});
|
|
25689
|
+
});
|
|
25400
25690
|
}
|
|
25401
25691
|
|
|
25402
25692
|
//#endregion
|
|
25403
|
-
//#region src/local/
|
|
25404
|
-
const
|
|
25405
|
-
|
|
25406
|
-
|
|
25407
|
-
|
|
25408
|
-
|
|
25409
|
-
|
|
25410
|
-
|
|
25411
|
-
|
|
25412
|
-
|
|
25413
|
-
|
|
25414
|
-
|
|
25415
|
-
|
|
25416
|
-
|
|
25417
|
-
|
|
25418
|
-
|
|
25419
|
-
|
|
25420
|
-
|
|
25421
|
-
|
|
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;
|
|
25693
|
+
//#region src/local/agentcore-http-server.ts
|
|
25694
|
+
const DEFAULT_ROUTES = [{
|
|
25695
|
+
method: "POST",
|
|
25696
|
+
path: "/invocations"
|
|
25697
|
+
}, {
|
|
25698
|
+
method: "GET",
|
|
25699
|
+
path: "/ping"
|
|
25700
|
+
}];
|
|
25701
|
+
function wireUpstreamResponse(upstream, clientRes) {
|
|
25702
|
+
upstream.on("response", (upRes) => {
|
|
25703
|
+
clientRes.writeHead(upRes.statusCode ?? 502, upRes.headers);
|
|
25704
|
+
upRes.on("error", () => clientRes.destroy());
|
|
25705
|
+
upRes.pipe(clientRes);
|
|
25706
|
+
});
|
|
25707
|
+
upstream.on("error", (err) => {
|
|
25708
|
+
getLogger().debug(`agentcore http serve upstream error: ${err.message}`);
|
|
25709
|
+
if (!clientRes.headersSent) clientRes.writeHead(502, { "content-type": "application/json" });
|
|
25710
|
+
clientRes.end(JSON.stringify({ error: `upstream error: ${err.message}` }));
|
|
25711
|
+
});
|
|
25439
25712
|
}
|
|
25440
|
-
|
|
25441
|
-
const
|
|
25442
|
-
const
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
const
|
|
25446
|
-
|
|
25447
|
-
|
|
25448
|
-
|
|
25449
|
-
|
|
25450
|
-
|
|
25451
|
-
|
|
25452
|
-
|
|
25453
|
-
|
|
25454
|
-
|
|
25455
|
-
|
|
25456
|
-
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
|
|
25713
|
+
function proxyToContainer(clientReq, clientRes, config, upstreamPath, perRequest) {
|
|
25714
|
+
const sessionId = config.sessionId ?? randomUUID();
|
|
25715
|
+
const headers = { ...clientReq.headers };
|
|
25716
|
+
delete headers["host"];
|
|
25717
|
+
headers[AGENTCORE_SESSION_ID_HEADER] = sessionId;
|
|
25718
|
+
const authorization = perRequest?.authorization ?? config.authorization;
|
|
25719
|
+
if (authorization) headers["authorization"] = authorization;
|
|
25720
|
+
if (config.signRequest && clientReq.method === "POST") {
|
|
25721
|
+
const signRequest = config.signRequest;
|
|
25722
|
+
const chunks = [];
|
|
25723
|
+
clientReq.on("data", (c) => chunks.push(c));
|
|
25724
|
+
clientReq.on("error", (err) => {
|
|
25725
|
+
getLogger().debug(`agentcore http serve client error: ${err.message}`);
|
|
25726
|
+
if (!clientRes.headersSent) {
|
|
25727
|
+
clientRes.writeHead(400, { "content-type": "application/json" });
|
|
25728
|
+
clientRes.end(JSON.stringify({ error: `client error: ${err.message}` }));
|
|
25729
|
+
}
|
|
25730
|
+
});
|
|
25731
|
+
clientReq.on("end", () => {
|
|
25732
|
+
const body = Buffer.concat(chunks);
|
|
25733
|
+
signRequest({
|
|
25734
|
+
method: "POST",
|
|
25735
|
+
path: upstreamPath,
|
|
25736
|
+
body,
|
|
25737
|
+
sessionId
|
|
25738
|
+
}).then((signed) => {
|
|
25739
|
+
const signedHeaders = {
|
|
25740
|
+
...headers,
|
|
25741
|
+
...signed
|
|
25742
|
+
};
|
|
25743
|
+
delete signedHeaders["transfer-encoding"];
|
|
25744
|
+
signedHeaders["content-length"] = String(body.length);
|
|
25745
|
+
const upstream = request({
|
|
25746
|
+
host: config.containerHost,
|
|
25747
|
+
port: config.containerPort,
|
|
25748
|
+
path: upstreamPath,
|
|
25749
|
+
method: "POST",
|
|
25750
|
+
headers: signedHeaders
|
|
25751
|
+
});
|
|
25752
|
+
wireUpstreamResponse(upstream, clientRes);
|
|
25753
|
+
upstream.end(body);
|
|
25754
|
+
}).catch((err) => {
|
|
25755
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25756
|
+
getLogger().debug(`agentcore http serve sigv4 sign error: ${msg}`);
|
|
25757
|
+
if (!clientRes.headersSent) {
|
|
25758
|
+
clientRes.writeHead(500, { "content-type": "application/json" });
|
|
25759
|
+
clientRes.end(JSON.stringify({ error: `sigv4 signing failed: ${msg}` }));
|
|
25760
|
+
}
|
|
25761
|
+
});
|
|
25762
|
+
});
|
|
25763
|
+
return;
|
|
25764
|
+
}
|
|
25765
|
+
const upstream = request({
|
|
25766
|
+
host: config.containerHost,
|
|
25767
|
+
port: config.containerPort,
|
|
25768
|
+
path: upstreamPath,
|
|
25769
|
+
method: clientReq.method,
|
|
25770
|
+
headers
|
|
25460
25771
|
});
|
|
25461
|
-
|
|
25462
|
-
|
|
25463
|
-
|
|
25464
|
-
|
|
25465
|
-
|
|
25466
|
-
|
|
25467
|
-
codeDir: materialized.dir,
|
|
25468
|
-
...materialized.tmpDir !== void 0 && { tmpDir: materialized.tmpDir },
|
|
25469
|
-
assetHash: match.hash
|
|
25470
|
-
};
|
|
25772
|
+
wireUpstreamResponse(upstream, clientRes);
|
|
25773
|
+
clientReq.on("error", (err) => {
|
|
25774
|
+
getLogger().debug(`agentcore http serve client error: ${err.message}`);
|
|
25775
|
+
upstream.destroy();
|
|
25776
|
+
});
|
|
25777
|
+
clientReq.pipe(upstream);
|
|
25471
25778
|
}
|
|
25472
|
-
function
|
|
25473
|
-
|
|
25474
|
-
|
|
25475
|
-
|
|
25476
|
-
|
|
25477
|
-
|
|
25779
|
+
function startAgentCoreHttpServer(config) {
|
|
25780
|
+
const host = config.host ?? "127.0.0.1";
|
|
25781
|
+
const routes = config.routes ?? DEFAULT_ROUTES;
|
|
25782
|
+
const attachWs = config.attachWs ?? true;
|
|
25783
|
+
const notFoundHint = buildNotFoundHint(routes, attachWs);
|
|
25784
|
+
const httpServer = createServer((req, res) => {
|
|
25785
|
+
const path = (req.url ?? "/").split("?")[0];
|
|
25786
|
+
const match = routes.find((r) => r.method === req.method && r.path === path);
|
|
25787
|
+
if (!match) {
|
|
25788
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
25789
|
+
res.end(JSON.stringify({
|
|
25790
|
+
error: "not found",
|
|
25791
|
+
hint: notFoundHint
|
|
25792
|
+
}));
|
|
25793
|
+
return;
|
|
25794
|
+
}
|
|
25795
|
+
if (config.authCheck && req.method === "POST") {
|
|
25796
|
+
req.on("error", (err) => getLogger().debug(`agentcore http serve client error (auth gate): ${err.message}`));
|
|
25797
|
+
config.authCheck(req.headers).then((result) => {
|
|
25798
|
+
if (!result.allow) {
|
|
25799
|
+
req.resume();
|
|
25800
|
+
res.writeHead(result.status ?? 403, { "content-type": "application/json" });
|
|
25801
|
+
res.end(JSON.stringify({ error: result.message ?? "forbidden" }));
|
|
25802
|
+
return;
|
|
25803
|
+
}
|
|
25804
|
+
proxyToContainer(req, res, config, match.path, { ...result.authorization && { authorization: result.authorization } });
|
|
25805
|
+
}).catch((err) => {
|
|
25806
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25807
|
+
getLogger().debug(`agentcore http serve auth-check error: ${msg}`);
|
|
25808
|
+
req.resume();
|
|
25809
|
+
if (!res.headersSent) {
|
|
25810
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
25811
|
+
res.end(JSON.stringify({ error: `auth check failed: ${msg}` }));
|
|
25812
|
+
}
|
|
25813
|
+
});
|
|
25814
|
+
return;
|
|
25815
|
+
}
|
|
25816
|
+
proxyToContainer(req, res, config, match.path);
|
|
25817
|
+
});
|
|
25818
|
+
const bridge = attachWs ? attachAgentCoreWsBridge(httpServer, {
|
|
25819
|
+
containerHost: config.containerHost,
|
|
25820
|
+
containerPort: () => config.containerPort,
|
|
25821
|
+
...config.sessionId && { sessionId: config.sessionId },
|
|
25822
|
+
...config.authorization && { authorization: config.authorization }
|
|
25823
|
+
}) : void 0;
|
|
25824
|
+
return new Promise((resolve, reject) => {
|
|
25825
|
+
httpServer.once("error", reject);
|
|
25826
|
+
httpServer.listen(config.port ?? 0, host, () => {
|
|
25827
|
+
httpServer.removeListener("error", reject);
|
|
25828
|
+
httpServer.on("error", (err) => getLogger().debug(`agentcore http serve server error: ${err.message}`));
|
|
25829
|
+
const port = httpServer.address().port;
|
|
25830
|
+
resolve({
|
|
25831
|
+
httpUrl: `http://${formatAuthority(host, port)}`,
|
|
25832
|
+
...bridge && { wsUrl: `ws://${formatAuthority(host, port)}${bridge.path}` },
|
|
25833
|
+
port,
|
|
25834
|
+
setContainerPort: (newPort) => {
|
|
25835
|
+
config.containerPort = newPort;
|
|
25836
|
+
},
|
|
25837
|
+
close: () => new Promise((res) => {
|
|
25838
|
+
if (bridge) bridge.close().then(() => httpServer.close(() => res()));
|
|
25839
|
+
else httpServer.close(() => res());
|
|
25840
|
+
})
|
|
25841
|
+
});
|
|
25478
25842
|
});
|
|
25479
|
-
}
|
|
25480
|
-
}
|
|
25481
|
-
function asObject$2(value) {
|
|
25482
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
25843
|
+
});
|
|
25483
25844
|
}
|
|
25484
|
-
function
|
|
25485
|
-
|
|
25486
|
-
|
|
25487
|
-
try {
|
|
25488
|
-
json = JSON.stringify(value);
|
|
25489
|
-
} catch {
|
|
25490
|
-
json = void 0;
|
|
25491
|
-
}
|
|
25492
|
-
return displayUntrustedValue(json ?? String(value));
|
|
25845
|
+
function buildNotFoundHint(routes, attachWs) {
|
|
25846
|
+
const base = routes.map((r) => `${r.method} ${r.path}`).join(" or ");
|
|
25847
|
+
return attachWs ? `${base} (WebSocket: connect to /ws)` : base;
|
|
25493
25848
|
}
|
|
25494
25849
|
|
|
25495
25850
|
//#endregion
|
|
25496
|
-
//#region src/local/
|
|
25497
|
-
|
|
25498
|
-
const
|
|
25499
|
-
|
|
25500
|
-
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
const
|
|
25504
|
-
const
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25851
|
+
//#region src/local/agentcore-serve-auth.ts
|
|
25852
|
+
function buildAgentCoreServeAuthCheck(authorizer, opts = {}) {
|
|
25853
|
+
const injectedBearer = opts.bearerToken ? `Bearer ${opts.bearerToken}` : void 0;
|
|
25854
|
+
if (opts.noVerifyAuth === true) return async () => ({
|
|
25855
|
+
allow: true,
|
|
25856
|
+
...injectedBearer && { authorization: injectedBearer }
|
|
25857
|
+
});
|
|
25858
|
+
const jwksCache = opts.jwksCache ?? createJwksCache();
|
|
25859
|
+
const warnedAt = opts.warnedAt ?? /* @__PURE__ */ new Map();
|
|
25860
|
+
return async (headers) => {
|
|
25861
|
+
const header = readAuthorizationHeader(headers) ?? injectedBearer;
|
|
25862
|
+
if (!header) return {
|
|
25863
|
+
allow: false,
|
|
25864
|
+
status: 401,
|
|
25865
|
+
message: "Missing Authorization: this runtime declares a customJwtAuthorizer. Send a Bearer JWT, or start the serve with --bearer-token / --no-verify-auth."
|
|
25866
|
+
};
|
|
25867
|
+
if (!(await verifyJwtViaDiscovery({
|
|
25868
|
+
discoveryUrl: authorizer.discoveryUrl,
|
|
25869
|
+
...authorizer.allowedAudience && { allowedAudience: authorizer.allowedAudience },
|
|
25870
|
+
...authorizer.allowedClients && { allowedClients: authorizer.allowedClients },
|
|
25871
|
+
...authorizer.allowedScopes && { allowedScopes: authorizer.allowedScopes },
|
|
25872
|
+
...authorizer.customClaims && { customClaims: authorizer.customClaims }
|
|
25873
|
+
}, header, jwksCache, { warnedAt })).allow) return {
|
|
25874
|
+
allow: false,
|
|
25875
|
+
status: 403,
|
|
25876
|
+
message: `Forbidden: the inbound JWT was rejected by the runtime's customJwtAuthorizer (signature / issuer / expiry / audience / scope check failed against ${authorizer.discoveryUrl}).`
|
|
25877
|
+
};
|
|
25878
|
+
return {
|
|
25879
|
+
allow: true,
|
|
25880
|
+
authorization: header
|
|
25881
|
+
};
|
|
25512
25882
|
};
|
|
25513
25883
|
}
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
}
|
|
25521
|
-
|
|
25522
|
-
|
|
25884
|
+
function selectServeInboundAuth(resolved, options, sigv4Active, buildAuthCheck = buildAgentCoreServeAuthCheck) {
|
|
25885
|
+
const bearerHeader = options.bearerToken ? `Bearer ${options.bearerToken}` : void 0;
|
|
25886
|
+
if (resolved.jwtAuthorizer) return {
|
|
25887
|
+
authCheck: buildAuthCheck(resolved.jwtAuthorizer, {
|
|
25888
|
+
...options.verifyAuth === false && { noVerifyAuth: true },
|
|
25889
|
+
...options.bearerToken && { bearerToken: options.bearerToken }
|
|
25890
|
+
}),
|
|
25891
|
+
...bearerHeader && { bridgeAuthorization: bearerHeader },
|
|
25892
|
+
sign: false
|
|
25893
|
+
};
|
|
25894
|
+
if (sigv4Active) return { sign: true };
|
|
25895
|
+
return {
|
|
25896
|
+
...bearerHeader && { bridgeAuthorization: bearerHeader },
|
|
25897
|
+
sign: false
|
|
25898
|
+
};
|
|
25523
25899
|
}
|
|
25524
|
-
function
|
|
25525
|
-
const
|
|
25526
|
-
|
|
25527
|
-
return
|
|
25528
|
-
}
|
|
25529
|
-
function defaultWebFunctionPlatform(spec) {
|
|
25530
|
-
return spec.architectures[0] === "x86_64" ? "linux/amd64" : "linux/arm64";
|
|
25900
|
+
function readAuthorizationHeader(headers) {
|
|
25901
|
+
const raw = headers["authorization"];
|
|
25902
|
+
const v = Array.isArray(raw) ? raw[0] : raw;
|
|
25903
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
25531
25904
|
}
|
|
25532
25905
|
|
|
25533
25906
|
//#endregion
|
|
25534
|
-
//#region src/local
|
|
25535
|
-
|
|
25536
|
-
const
|
|
25537
|
-
|
|
25538
|
-
|
|
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
|
-
};
|
|
25907
|
+
//#region src/cli/commands/local-start-agentcore.ts
|
|
25908
|
+
function parsePort$2(raw) {
|
|
25909
|
+
const n = Number(raw);
|
|
25910
|
+
if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
|
|
25911
|
+
return n;
|
|
25553
25912
|
}
|
|
25554
|
-
function
|
|
25555
|
-
return {
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
|
|
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"
|
|
25913
|
+
function resolveAgentCoreServePlan(protocol) {
|
|
25914
|
+
if (protocol === "MCP") return {
|
|
25915
|
+
containerPort: MCP_CONTAINER_PORT,
|
|
25916
|
+
containerPortLabel: `${MCP_CONTAINER_PORT}${MCP_PATH}`,
|
|
25917
|
+
routes: [{
|
|
25918
|
+
method: "POST",
|
|
25919
|
+
path: MCP_PATH
|
|
25920
|
+
}],
|
|
25921
|
+
attachWs: false,
|
|
25922
|
+
readyPath: MCP_PATH
|
|
25574
25923
|
};
|
|
25575
|
-
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
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
|
|
25924
|
+
if (protocol === "A2A") return {
|
|
25925
|
+
containerPort: A2A_CONTAINER_PORT,
|
|
25926
|
+
containerPortLabel: `${A2A_CONTAINER_PORT}${"/"}`,
|
|
25927
|
+
routes: [{
|
|
25928
|
+
method: "POST",
|
|
25929
|
+
path: "/"
|
|
25930
|
+
}],
|
|
25931
|
+
attachWs: false,
|
|
25932
|
+
readyPath: "/"
|
|
25593
25933
|
};
|
|
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
25934
|
return {
|
|
25624
|
-
|
|
25625
|
-
|
|
25626
|
-
|
|
25627
|
-
|
|
25628
|
-
|
|
25935
|
+
containerPort: void 0,
|
|
25936
|
+
containerPortLabel: "8080",
|
|
25937
|
+
routes: [{
|
|
25938
|
+
method: "POST",
|
|
25939
|
+
path: "/invocations"
|
|
25940
|
+
}, {
|
|
25941
|
+
method: "GET",
|
|
25942
|
+
path: "/ping"
|
|
25629
25943
|
}],
|
|
25630
|
-
|
|
25631
|
-
|
|
25632
|
-
workingDir: spec.codeMountPath,
|
|
25633
|
-
label: start.label
|
|
25944
|
+
attachWs: true,
|
|
25945
|
+
readyPath: void 0
|
|
25634
25946
|
};
|
|
25635
25947
|
}
|
|
25636
|
-
async function
|
|
25637
|
-
const
|
|
25638
|
-
|
|
25639
|
-
|
|
25640
|
-
|
|
25641
|
-
|
|
25642
|
-
|
|
25643
|
-
|
|
25644
|
-
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
}
|
|
25654
|
-
|
|
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 = "";
|
|
25948
|
+
async function localStartAgentCoreCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
25949
|
+
const logger = getLogger();
|
|
25950
|
+
if (options.verbose) logger.setLevel("debug");
|
|
25951
|
+
let containerId;
|
|
25952
|
+
let stopLogs;
|
|
25953
|
+
let server;
|
|
25954
|
+
let profileCredsFile;
|
|
25955
|
+
let stateProvider;
|
|
25956
|
+
let watcher;
|
|
25957
|
+
let reloadChain = Promise.resolve();
|
|
25958
|
+
let shuttingDown = false;
|
|
25959
|
+
let tornDown = false;
|
|
25960
|
+
const teardown = async () => {
|
|
25961
|
+
if (tornDown) return;
|
|
25962
|
+
tornDown = true;
|
|
25963
|
+
if (watcher) try {
|
|
25964
|
+
await watcher.close();
|
|
25965
|
+
} catch (err) {
|
|
25966
|
+
logger.debug(`watcher close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25668
25967
|
}
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
|
|
25673
|
-
|
|
25674
|
-
|
|
25675
|
-
|
|
25676
|
-
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
|
|
25681
|
-
|
|
25682
|
-
|
|
25683
|
-
|
|
25684
|
-
|
|
25685
|
-
|
|
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");
|
|
25968
|
+
try {
|
|
25969
|
+
await reloadChain;
|
|
25970
|
+
} catch {}
|
|
25971
|
+
if (server) try {
|
|
25972
|
+
await server.close();
|
|
25973
|
+
} catch (err) {
|
|
25974
|
+
logger.debug(`server close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25975
|
+
}
|
|
25976
|
+
if (stopLogs) try {
|
|
25977
|
+
stopLogs();
|
|
25978
|
+
} catch (err) {
|
|
25979
|
+
logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25980
|
+
}
|
|
25981
|
+
if (containerId) try {
|
|
25982
|
+
await removeContainer(containerId);
|
|
25983
|
+
} catch (err) {
|
|
25984
|
+
logger.debug(`removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25725
25985
|
}
|
|
25986
|
+
if (stateProvider) try {
|
|
25987
|
+
stateProvider.dispose();
|
|
25988
|
+
} catch {}
|
|
25989
|
+
if (profileCredsFile) try {
|
|
25990
|
+
await profileCredsFile.dispose();
|
|
25991
|
+
} catch {}
|
|
25726
25992
|
};
|
|
25727
|
-
|
|
25728
|
-
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
25739
|
-
|
|
25740
|
-
|
|
25741
|
-
|
|
25742
|
-
|
|
25743
|
-
|
|
25744
|
-
|
|
25745
|
-
|
|
25993
|
+
await applyRoleArnIfSet({
|
|
25994
|
+
roleArn: options.roleArn,
|
|
25995
|
+
region: options.region,
|
|
25996
|
+
profile: options.profile
|
|
25997
|
+
});
|
|
25998
|
+
await ensureDockerAvailable();
|
|
25999
|
+
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
26000
|
+
if (options.profile && profileCredentials) profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);
|
|
26001
|
+
const appCmd = resolveApp(options.app);
|
|
26002
|
+
if (!appCmd) throw new Error(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
|
|
26003
|
+
logger.info("Synthesizing CDK app...");
|
|
26004
|
+
const synthesizer = hostSynthesizer ?? new Synthesizer();
|
|
26005
|
+
const context = parseContextOptions(options.context);
|
|
26006
|
+
const synthOpts = {
|
|
26007
|
+
app: appCmd,
|
|
26008
|
+
output: options.output,
|
|
26009
|
+
...options.region && { region: options.region },
|
|
26010
|
+
...options.profile && { profile: options.profile },
|
|
26011
|
+
...Object.keys(context).length > 0 && { context }
|
|
26012
|
+
};
|
|
26013
|
+
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
26014
|
+
const resolvedTarget = await resolveSingleTarget(target, {
|
|
26015
|
+
entries: listTargets(stacks).agentCoreRuntimes,
|
|
26016
|
+
message: "Select an AgentCore Runtime to serve",
|
|
26017
|
+
noun: "AgentCore Runtimes",
|
|
26018
|
+
onMissing: () => new CdkLocalError(`${getEmbedConfig().cliName} start-agentcore requires a <target> (an AgentCore Runtime display path or logical ID). Run \`${getEmbedConfig().cliName} list\` to see them, or run it in a TTY to pick interactively.`, "LOCAL_START_AGENTCORE_TARGET_REQUIRED")
|
|
26019
|
+
});
|
|
26020
|
+
const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
26021
|
+
stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
|
|
26022
|
+
const { context: imageContext, loaded: loadedState } = stateProvider && candidate ? await buildAgentCoreImageContext(candidate, stateProvider, options) : {
|
|
26023
|
+
context: void 0,
|
|
26024
|
+
loaded: void 0
|
|
26025
|
+
};
|
|
26026
|
+
const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);
|
|
26027
|
+
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.logicalId}`)} (${resolved.protocol})`);
|
|
26028
|
+
const plan = resolveAgentCoreServePlan(resolved.protocol);
|
|
26029
|
+
const sigv4Context = await resolveAgentCoreSigV4Context(options, resolved, loadedState, stateProvider);
|
|
26030
|
+
const authPlan = selectServeInboundAuth(resolved, options, sigv4Context !== void 0);
|
|
26031
|
+
if (authPlan.authCheck && resolved.jwtAuthorizer) logger.info(`Inbound JWT: each request verified per-request against ${resolved.jwtAuthorizer.discoveryUrl}${options.verifyAuth === false ? " (verification DISABLED via --no-verify-auth)" : ""}.`);
|
|
26032
|
+
await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);
|
|
26033
|
+
const image = await resolveAgentCoreImage(resolved, options, loadedState, stateProvider);
|
|
26034
|
+
const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext);
|
|
26035
|
+
let containerHostPort = await pickFreePort();
|
|
26036
|
+
const containerHost = options.containerHost;
|
|
26037
|
+
const containerName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
26038
|
+
logger.info(`Starting agent container (image=${image}, port=${containerHostPort} -> ${plan.containerPortLabel})...`);
|
|
26039
|
+
containerId = await runDetached({
|
|
26040
|
+
image,
|
|
26041
|
+
mounts: [],
|
|
26042
|
+
env: dockerEnv,
|
|
26043
|
+
cmd: [],
|
|
26044
|
+
hostPort: containerHostPort,
|
|
26045
|
+
host: containerHost,
|
|
26046
|
+
platform: options.platform,
|
|
26047
|
+
name: containerName,
|
|
26048
|
+
...plan.containerPort !== void 0 && { containerPort: plan.containerPort },
|
|
26049
|
+
...sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys }
|
|
26050
|
+
});
|
|
26051
|
+
stopLogs = streamLogs(containerId);
|
|
26052
|
+
const shutdown = async (signal, exitCode) => {
|
|
26053
|
+
if (shuttingDown) return;
|
|
26054
|
+
shuttingDown = true;
|
|
26055
|
+
logger.info(`Received ${signal}, shutting down...`);
|
|
26056
|
+
await teardown();
|
|
26057
|
+
process.exit(exitCode);
|
|
26058
|
+
};
|
|
26059
|
+
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
26060
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
26061
|
+
try {
|
|
26062
|
+
if (plan.readyPath === void 0) await waitForAgentCorePing(containerHost, containerHostPort, options.timeout);
|
|
26063
|
+
else await waitForAgentCoreHttpReady(containerHost, containerHostPort, plan.readyPath, options.timeout);
|
|
26064
|
+
const signRequest = authPlan.sign && sigv4Context ? async ({ method, path, body, sessionId }) => {
|
|
26065
|
+
const signed = await signAgentCoreInvocation({
|
|
26066
|
+
credentials: sigv4Context.credentials,
|
|
26067
|
+
region: sigv4Context.region,
|
|
26068
|
+
host: containerHost,
|
|
26069
|
+
port: containerHostPort,
|
|
26070
|
+
path,
|
|
26071
|
+
body,
|
|
26072
|
+
sessionId,
|
|
26073
|
+
method
|
|
25746
26074
|
});
|
|
26075
|
+
const h = {
|
|
26076
|
+
Authorization: signed.authorization,
|
|
26077
|
+
"X-Amz-Date": signed.amzDate,
|
|
26078
|
+
"X-Amz-Content-Sha256": signed.amzContentSha256
|
|
26079
|
+
};
|
|
26080
|
+
if (signed.amzSecurityToken) h["X-Amz-Security-Token"] = signed.amzSecurityToken;
|
|
26081
|
+
return h;
|
|
26082
|
+
} : void 0;
|
|
26083
|
+
server = await startAgentCoreHttpServer({
|
|
26084
|
+
containerHost,
|
|
26085
|
+
containerPort: containerHostPort,
|
|
26086
|
+
host: options.host,
|
|
26087
|
+
port: options.port,
|
|
26088
|
+
routes: plan.routes,
|
|
26089
|
+
attachWs: plan.attachWs,
|
|
26090
|
+
...authPlan.bridgeAuthorization && { authorization: authPlan.bridgeAuthorization },
|
|
26091
|
+
...authPlan.authCheck && { authCheck: authPlan.authCheck },
|
|
26092
|
+
...signRequest && { signRequest },
|
|
26093
|
+
...options.sessionId && { sessionId: options.sessionId }
|
|
25747
26094
|
});
|
|
25748
|
-
|
|
25749
|
-
|
|
25750
|
-
|
|
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;
|
|
26095
|
+
} catch (err) {
|
|
26096
|
+
await teardown();
|
|
26097
|
+
throw err;
|
|
25767
26098
|
}
|
|
25768
|
-
|
|
25769
|
-
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25773
|
-
|
|
25774
|
-
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25779
|
-
|
|
25780
|
-
|
|
25781
|
-
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
26099
|
+
if (server.wsUrl) {
|
|
26100
|
+
logger.info(`Server listening on ${server.wsUrl} (${displayIdent(resolved.logicalId)} (AgentCore WebSocket))`);
|
|
26101
|
+
logger.info(`HTTP contract served on ${server.httpUrl} — POST ${server.httpUrl}/invocations, GET ${server.httpUrl}/ping`);
|
|
26102
|
+
} else {
|
|
26103
|
+
const contractPath = plan.routes[0]?.path ?? "/";
|
|
26104
|
+
const contractUrl = `${server.httpUrl}${contractPath}`;
|
|
26105
|
+
logger.info(`Server listening on ${server.httpUrl} (${displayIdent(resolved.logicalId)})`);
|
|
26106
|
+
logger.info(`${resolved.protocol} contract served on ${contractUrl} — POST ${contractUrl}`);
|
|
26107
|
+
}
|
|
26108
|
+
logger.info("Press ^C to shut down.");
|
|
26109
|
+
if (options.watch) {
|
|
26110
|
+
const cdkOutDir = options.output;
|
|
26111
|
+
const assetLoader = new AssetManifestLoader();
|
|
26112
|
+
let currentAssetHash = await deriveOldAssetHash({
|
|
26113
|
+
resolvedTarget,
|
|
26114
|
+
resolved,
|
|
26115
|
+
stacks,
|
|
26116
|
+
cdkOutDir,
|
|
26117
|
+
assetLoader
|
|
26118
|
+
});
|
|
26119
|
+
const waitReady = async (port) => {
|
|
26120
|
+
if (plan.readyPath === void 0) await waitForAgentCorePing(containerHost, port, options.timeout);
|
|
26121
|
+
else await waitForAgentCoreHttpReady(containerHost, port, plan.readyPath, options.timeout);
|
|
25787
26122
|
};
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
26123
|
+
const watchRoot = process.cwd();
|
|
26124
|
+
const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
|
|
26125
|
+
watchRoot,
|
|
26126
|
+
output: options.output,
|
|
26127
|
+
watchConfig: resolveWatchConfig()
|
|
26128
|
+
});
|
|
26129
|
+
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}). The warm container reloads in place; the serve stays up.`);
|
|
26130
|
+
watcher = createFileWatcher({
|
|
26131
|
+
paths: [watchRoot],
|
|
26132
|
+
ignored,
|
|
26133
|
+
shouldTrigger,
|
|
26134
|
+
onChange: (changedPaths) => {
|
|
26135
|
+
reloadChain = reloadChain.then(async () => {
|
|
26136
|
+
if (tornDown || shuttingDown) return;
|
|
26137
|
+
let freshStacks;
|
|
26138
|
+
let freshResolved;
|
|
26139
|
+
let freshLoaded;
|
|
26140
|
+
let freshImageContext;
|
|
26141
|
+
let verdict;
|
|
26142
|
+
let nextAssetHash = currentAssetHash;
|
|
26143
|
+
try {
|
|
26144
|
+
freshStacks = (await synthesizer.synthesize(synthOpts)).stacks;
|
|
26145
|
+
const freshCandidate = pickAgentCoreCandidateStack(resolvedTarget, freshStacks);
|
|
26146
|
+
const ctx = stateProvider && freshCandidate ? await buildAgentCoreImageContext(freshCandidate, stateProvider, options) : {
|
|
26147
|
+
context: void 0,
|
|
26148
|
+
loaded: void 0
|
|
26149
|
+
};
|
|
26150
|
+
freshImageContext = ctx.context;
|
|
26151
|
+
freshLoaded = ctx.loaded;
|
|
26152
|
+
freshResolved = resolveAgentCoreTarget(resolvedTarget, freshStacks, freshImageContext);
|
|
26153
|
+
await resolveFromS3BucketIntrinsic(freshResolved, stateProvider, freshLoaded, freshImageContext);
|
|
26154
|
+
const assetCtx = await loadAgentCoreAssetContext({
|
|
26155
|
+
resolvedTarget,
|
|
26156
|
+
resolved: freshResolved,
|
|
26157
|
+
stacks: freshStacks,
|
|
26158
|
+
cdkOutDir,
|
|
26159
|
+
assetLoader,
|
|
26160
|
+
oldAssetHash: currentAssetHash
|
|
26161
|
+
});
|
|
26162
|
+
if (assetCtx?.newAssetHash !== void 0) nextAssetHash = assetCtx.newAssetHash;
|
|
26163
|
+
verdict = classifySourceChange(changedPaths, assetCtx);
|
|
26164
|
+
logger.info(`Detected source change (${changedPaths.length} path(s)); verdict=${verdict.kind} (${verdict.reason}).`);
|
|
26165
|
+
} catch (err) {
|
|
26166
|
+
logger.error(`Reload: re-synth / classify failed (${err instanceof Error ? err.message : String(err)}); skipping this change. The serve stays up against the current container.`);
|
|
26167
|
+
return;
|
|
26168
|
+
}
|
|
26169
|
+
if (tornDown || shuttingDown) return;
|
|
26170
|
+
try {
|
|
26171
|
+
if (verdict.kind === "soft-reload") {
|
|
26172
|
+
if (!containerId) throw new CdkLocalError("Reload: no live container to docker cp / docker restart into.", "LOCAL_START_AGENTCORE_WATCH_NO_CONTAINER");
|
|
26173
|
+
await softReloadAgentContainer(containerId, verdict.newAssetSourceDir);
|
|
26174
|
+
await waitReady(containerHostPort);
|
|
26175
|
+
logger.info("Reload: soft-reloaded the agent container (docker cp + docker restart; container ID + host port preserved).");
|
|
26176
|
+
} else {
|
|
26177
|
+
if (stopLogs) {
|
|
26178
|
+
try {
|
|
26179
|
+
stopLogs();
|
|
26180
|
+
} catch {}
|
|
26181
|
+
stopLogs = void 0;
|
|
26182
|
+
}
|
|
26183
|
+
if (containerId) {
|
|
26184
|
+
await removeContainer(containerId);
|
|
26185
|
+
containerId = void 0;
|
|
26186
|
+
}
|
|
26187
|
+
const newImage = await resolveAgentCoreImage(freshResolved, options, freshLoaded, stateProvider);
|
|
26188
|
+
const { env: newEnv, sensitiveEnvKeys: newSensitive } = await buildContainerEnv(freshResolved, options, profileCredentials, profileCredsFile, stateProvider, freshLoaded, freshImageContext);
|
|
26189
|
+
const newPort = await pickFreePort();
|
|
26190
|
+
const newName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
26191
|
+
logger.info(`Reload: starting agent container (image=${newImage}, port=${newPort} -> ${plan.containerPortLabel})...`);
|
|
26192
|
+
containerId = await runDetached({
|
|
26193
|
+
image: newImage,
|
|
26194
|
+
mounts: [],
|
|
26195
|
+
env: newEnv,
|
|
26196
|
+
cmd: [],
|
|
26197
|
+
hostPort: newPort,
|
|
26198
|
+
host: containerHost,
|
|
26199
|
+
platform: options.platform,
|
|
26200
|
+
name: newName,
|
|
26201
|
+
...plan.containerPort !== void 0 && { containerPort: plan.containerPort },
|
|
26202
|
+
...newSensitive.size > 0 && { sensitiveEnvKeys: newSensitive }
|
|
26203
|
+
});
|
|
26204
|
+
stopLogs = streamLogs(containerId);
|
|
26205
|
+
containerHostPort = newPort;
|
|
26206
|
+
await waitReady(newPort);
|
|
26207
|
+
server?.setContainerPort(newPort);
|
|
26208
|
+
logger.info(`Reload: rebuilt the agent container; serve re-pointed to port ${newPort}.`);
|
|
26209
|
+
}
|
|
26210
|
+
currentAssetHash = nextAssetHash;
|
|
26211
|
+
logger.info("Reload complete.");
|
|
26212
|
+
} catch (err) {
|
|
26213
|
+
logger.error(`Reload failed: ${err instanceof Error ? err.message : String(err)}. The serve stays up; edit + save again to retry.`);
|
|
26214
|
+
}
|
|
26215
|
+
});
|
|
25795
26216
|
}
|
|
25796
|
-
resolveLogs(Buffer.concat(chunks).toString().split("\n").map((l) => l.replace(/\r$/, "")).filter((l) => l.length > 0).slice(-lines));
|
|
25797
26217
|
});
|
|
25798
|
-
}
|
|
26218
|
+
}
|
|
26219
|
+
await new Promise(() => void 0);
|
|
26220
|
+
}
|
|
26221
|
+
function createLocalStartAgentCoreCommand(opts = {}) {
|
|
26222
|
+
setEmbedConfig(opts.embedConfig);
|
|
26223
|
+
const cmd = new Command("start-agentcore").description("Serve a Bedrock AgentCore Runtime's contract locally against a warm container. Boots the AWS::BedrockAgentCore::Runtime container ONCE (same image / env / credential resolution as invoke-agentcore) and keeps it warm, so a client can hit it repeatedly. HTTP / AGUI runtimes serve POST /invocations + GET /ping plus the bidirectional /ws endpoint behind a host bridge (injects the AgentCore session-id, and Authorization under a customJwtAuthorizer, so a header-less client like a browser can connect); MCP runtimes serve POST /mcp, A2A POST /. Target accepts a CDK display path (MyStack/MyAgent) 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 AgentCore Runtime to serve (omit to pick interactively in a TTY)").action(withErrorHandling(async (target, options) => {
|
|
26224
|
+
await localStartAgentCoreCommand(target, options, opts.extraStateProviders, opts.synthesizer);
|
|
26225
|
+
}));
|
|
26226
|
+
addStartAgentCoreSpecificOptions(cmd);
|
|
26227
|
+
[
|
|
26228
|
+
...commonOptions(),
|
|
26229
|
+
...appOptions(),
|
|
26230
|
+
...contextOptions
|
|
26231
|
+
].forEach((opt) => cmd.addOption(opt));
|
|
26232
|
+
cmd.addOption(regionOption);
|
|
26233
|
+
return cmd;
|
|
26234
|
+
}
|
|
26235
|
+
function addStartAgentCoreSpecificOptions(cmd) {
|
|
26236
|
+
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."));
|
|
25799
26237
|
}
|
|
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
26238
|
|
|
25808
26239
|
//#endregion
|
|
25809
26240
|
//#region src/cli/commands/local-start-web-function.ts
|
|
@@ -25812,43 +26243,10 @@ function parsePort$1(raw) {
|
|
|
25812
26243
|
if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
|
|
25813
26244
|
return n;
|
|
25814
26245
|
}
|
|
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
26246
|
async function localStartWebFunctionCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
25821
26247
|
const logger = getLogger();
|
|
25822
26248
|
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
26249
|
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
26250
|
await applyRoleArnIfSet({
|
|
25853
26251
|
roleArn: options.roleArn,
|
|
25854
26252
|
region: options.region,
|
|
@@ -25875,93 +26273,60 @@ async function localStartWebFunctionCommand(target, options, extraStateProviders
|
|
|
25875
26273
|
noun: "Lambda web functions",
|
|
25876
26274
|
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
26275
|
}), stacks, options.revision);
|
|
25878
|
-
const
|
|
25879
|
-
const spec = resolveWebFunctionRuntime(code.runtime);
|
|
26276
|
+
const prepared = prepareWebFunctionServe(resolved);
|
|
25880
26277
|
const selectionNote = {
|
|
25881
26278
|
"target-revision": "the target revision",
|
|
25882
26279
|
"revision-flag": "--revision",
|
|
25883
26280
|
"endpoint-weight": `highest weight on endpoint ${displayIdent(resolved.endpoint?.logicalId ?? "")}`,
|
|
25884
26281
|
"only-revision": "the function's only revision"
|
|
25885
26282
|
};
|
|
25886
|
-
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.revisionLogicalId}`)} (${spec.runtime}; revision chosen by ${selectionNote[resolved.selection]})`);
|
|
25887
|
-
const
|
|
25888
|
-
|
|
25889
|
-
|
|
26283
|
+
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.revisionLogicalId}`)} (${prepared.spec.runtime}; revision chosen by ${selectionNote[resolved.selection]})`);
|
|
26284
|
+
for (const line of webFunctionFidelityWarnings(resolved, prepared.serviceConfig)) logger.warn(line);
|
|
26285
|
+
const serve = new WebFunctionServe(prepared, {
|
|
26286
|
+
envOptions: options,
|
|
26287
|
+
...profileCredentials !== void 0 && { profileCredentials },
|
|
26288
|
+
...extraStateProviders !== void 0 && { extraStateProviders },
|
|
26289
|
+
host: options.host,
|
|
26290
|
+
port: options.port,
|
|
26291
|
+
...options.platform !== void 0 && { platform: options.platform },
|
|
26292
|
+
skipPull: options.pull === false,
|
|
26293
|
+
timeoutMs: options.timeout
|
|
26294
|
+
});
|
|
26295
|
+
let shuttingDown = false;
|
|
25890
26296
|
const shutdown = async (signal, exitCode) => {
|
|
25891
26297
|
if (shuttingDown) {
|
|
25892
|
-
if (!waitingOnRun) {
|
|
26298
|
+
if (!serve.waitingOnRun) {
|
|
25893
26299
|
logger.info(`Received ${signal} again; still removing the container, please wait...`);
|
|
25894
26300
|
return;
|
|
25895
26301
|
}
|
|
25896
|
-
|
|
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>"}`);
|
|
26302
|
+
serve.disposeArtifactNow();
|
|
26303
|
+
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 ${serve.containerName ?? "<name>"}`);
|
|
25898
26304
|
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
25899
26305
|
return;
|
|
25900
26306
|
}
|
|
25901
26307
|
shuttingDown = true;
|
|
25902
26308
|
logger.info(`Received ${signal}, shutting down...`);
|
|
25903
|
-
await teardown();
|
|
26309
|
+
await serve.teardown();
|
|
25904
26310
|
process.exit(exitCode);
|
|
25905
26311
|
};
|
|
25906
26312
|
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
25907
26313
|
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
25908
|
-
const recent = new RecentOutput();
|
|
25909
26314
|
try {
|
|
25910
|
-
const
|
|
25911
|
-
|
|
25912
|
-
|
|
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))`);
|
|
26315
|
+
const address = await serve.start();
|
|
26316
|
+
if (!address) return await forever();
|
|
26317
|
+
logger.info(`Server listening on http://${formatAuthority(address.host, address.port)} (${displayIdent(resolved.functionName)} (Lambda web function))`);
|
|
25954
26318
|
logger.info("Press ^C to shut down.");
|
|
25955
26319
|
} catch (err) {
|
|
25956
26320
|
if (shuttingDown) return await forever();
|
|
25957
|
-
await teardown();
|
|
26321
|
+
await serve.teardown();
|
|
25958
26322
|
throw err;
|
|
25959
26323
|
}
|
|
25960
|
-
|
|
25961
|
-
|
|
25962
|
-
|
|
25963
|
-
|
|
25964
|
-
await
|
|
26324
|
+
const exited = serve.exited;
|
|
26325
|
+
if (!exited) return await forever();
|
|
26326
|
+
const exitCode = await exited;
|
|
26327
|
+
if (shuttingDown || serve.tornDown) return await forever();
|
|
26328
|
+
const failure = await serve.exitFailure(`The web function container exited (exit code ${exitCode ?? "unknown"}) while serving.`);
|
|
26329
|
+
await serve.teardown();
|
|
25965
26330
|
throw failure;
|
|
25966
26331
|
}
|
|
25967
26332
|
function createLocalStartWebFunctionCommand(opts = {}) {
|
|
@@ -32483,5 +32848,5 @@ function addStudioSpecificOptions(cmd) {
|
|
|
32483
32848
|
}
|
|
32484
32849
|
|
|
32485
32850
|
//#endregion
|
|
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-
|
|
32851
|
+
export { postHook as $, firstUsableRegion as $a, parseConnectionsPath as $i, addImageOverrideOptions as $n, addStartApiSpecificOptions as $r, classifyS3Error as $t, microvmFidelityWarnings as A, filterWebSocketApisByIdentifiers as Aa, applyCorsResponseHeaders as Ai, stripCloudFrontImport as An, A2A_CONTAINER_PORT as Ar, readContainerLogTail as At, MICROVM_WS_BASE_PROTOCOL as B, AGENTCORE_RUNTIME_TYPE as Ba, evaluateResponseParameters as Bi, createLocalStartAlbCommand as Bn, AGENTCORE_SESSION_ID_HEADER as Br, parseWebFunctionBuildConfig as Bt, addListSpecificOptions as C, resolveWatchConfig as Ca, verifyJwtViaDiscovery as Ci, WEB_FUNCTION_REVISION_TYPE as Cn, setShadowReadyTimeoutMs as Cr, parseKvsFileOverrides as Ct, buildMicrovmLambdaView as D, availableWebSocketApiIdentifiers as Da, invokeRequestAuthorizer as Di, compileCloudFrontFunction as Dn, createLocalInvokeAgentCoreCommand as Dr, WEB_FUNCTION_CONTAINER_PORT as Dt, addStartMicrovmSpecificOptions as E, listTargets as Ea, evaluateCachedLambdaPolicy as Ei, resolveWebFunctionTarget as En, addInvokeAgentCoreSpecificOptions as Er, RecentOutput as Et, microvmImageTag as F, resolveLambdaArnIntrinsic as Fa, matchRoute as Fi, findFileAssetForObjectKey as Fn, MCP_PROTOCOL_VERSION as Fr, buildNodeEntryFileStart as Ft, startMicrovmFrontDoor as G, formatStateRemedy as Ga, HOST_DOCKER_INTERNAL_GATEWAY as Gi, addStartServiceSpecificOptions as Gn, SUPPORTED_CODE_RUNTIMES as Gr, readWebFunctionServiceConfig as Gt, PROXY_PORT_HEADER as H, pickAgentCoreCandidateStack as Ha, selectIntegrationResponse as Hi, resolveAlbTarget as Hn, waitForAgentCoreHttpReady as Hr, WEB_FUNCTION_HTTP_ENDPOINT_ENV as Ht, planMicrovmPorts as I, AGENTCORE_A2A_PROTOCOL as Ia, translateLambdaResponse as Ii, isBucketDeploymentResource as In, mcpInvokeOnce as Ir, defaultWebFunctionPlatform as It, MICROVM_HOOK_PATH_PREFIX as J, LocalInvokeBuildError as Ja, resolveHostGatewayExtraHosts as Ji, addRunTaskSpecificOptions as Jn, renderCodeDockerfile as Jr, idFromArn as Jt, stripMicrovmProxyHeaders as K, substituteImagePlaceholders as Ka, HOST_GATEWAY_MIN_VERSION as Ki, createLocalStartServiceCommand as Kn, buildAgentCoreCodeImage as Kr, webFunctionAssumeRole as Kt, readExposedPorts as L, AGENTCORE_AGUI_PROTOCOL as La, applyAuthorizerOverlay as Li, resolveBucketDeploymentObjectKey as Ln, parseSseForJsonRpc as Lr, resolveWebFunctionRuntime as Lt, DEFAULT_TERMINATE_HOOK_TIMEOUT_MS as M, webSocketApiMatchesIdentifier as Ma, buildCorsConfigFromCloudFrontChain as Mi, createLocalFileKvsDataSource as Mn, a2aInvokeOnce as Mr, waitForWebFunctionReady as Mt, LocalMicrovm as N, discoverRoutes as Na, isFunctionUrlOacFronted as Ni, createUnboundCloudFrontModule as Nn, MCP_CONTAINER_PORT as Nr, watchContainerExit as Nt, createLocalStartMicrovmCommand as O, discoverWebSocketApis as Oa, invokeTokenAuthorizer as Oi, runViewerRequest as On, bridgeAgentCoreWs as Or, containerFailure as Ot, buildMicrovmImage as P, pickRefLogicalId as Pa, matchPreflight as Pi, bucketDeploymentSourceObjectKeys as Pn, MCP_PATH as Pr, WEB_FUNCTION_RUNTIMES as Pt, pollBuildHook as Q, deployedFillMayBeSecret as Qa, handleConnectionsRequest as Qi, addEcsAssumeRoleOptions as Qn, createLocalInvokeCommand as Qr, resolveDeployedOriginBucket as Qt, setContainerPaused as R, AGENTCORE_HTTP_PROTOCOL as Ra, buildHttpApiV2Event as Ri, addAlbSpecificOptions as Rn, AGENTCORE_SIGV4_SERVICE as Rr, supportedWebFunctionRuntimes as Rt, StudioEventBus as S, resolveSsmParameters as Sa, verifyJwtAuthorizer as Si, WEB_FUNCTION_ENDPOINT_TYPE as Sn, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as Sr, normalizeKvsFileKeys as St, formatTargetListing as T, countTargets as Ta, computeRequestIdentityHash as Ti, WebFunctionResolutionError as Tn, attachContainerLogStreamer as Tr, resolveCloudFrontTarget as Tt, rewriteUpgradeResponseHead as U, resolveAgentCoreTarget as Ua, tryParseStatus as Ui, isApplicationLoadBalancer as Un, waitForAgentCorePing as Ur, buildWebFunctionLambdaView as Ut, MicrovmStateError as V, AgentCoreResolutionError as Va, pickResponseTemplate as Vi, parseLbPortOverrides as Vn, invokeAgentCore as Vr, resolveWebFunctionArtifact as Vt, selectTargetPort as W, derivePseudoParametersFromRegion as Wa, VtlEvaluationError as Wi, resolveAlbFrontDoor as Wn, downloadAndExtractS3Bundle as Wr, finalizeWebFunctionEnv as Wt, callRuntimeHook as X, DynamicReferenceResolver as Xa, ConnectionRegistry as Xi, MAX_TASKS_SUBNET_RANGE_CAP as Xn, classifySourceChange as Xr, createDeployedKvsDataSource as Xt, MicrovmHookError as Y, DynamicReferenceResolutionError as Ya, bufferToBody as Yi, createLocalRunTaskCommand as Yn, toCmdArgv as Yr, resolveKvsModulesForDistribution as Yt, generateMicrovmId as Z, containsDynamicReference as Za, buildMgmtEndpointEnvUrl as Zi, addCommonEcsServiceOptions as Zn, addInvokeSpecificOptions as Zr, resolveDeployedKvsArnByName as Zt, filterStudioTargetGroups as _, resolveCfnFallbackRegion as _a, defaultCredentialsLoader as _i, pickKvsLogicalIdFromArn as _n, isLocalCdkAssetImage as _r, attachAgentCoreWsBridge as _t, createLocalStudioCommand as a, resolveRuntimeCodeMountPath as aa, attachStageContext as ai, serveLambdaUrlOrigin as an, SECOND_ASSUME_MESSAGE as ao, resolveSharedSidecarCredentials as ar, disposeMicrovmCodeArtifact as at, renderStudioHtml as b, CfnLocalStateProvider as ba, createJwksCache as bi, pickWebFunctionEndpointLogicalIdFromOrigin as bn, CloudMapRegistry as br, addStartCloudFrontSpecificOptions as bt, startStudioProxy as c, EcsTaskResolutionError as ca, resolveEnvVars$1 as ci, buildEdgeRequestEvent as cn, readEnvCallerCredentials as co, buildImageOverrideTag as cr, resolveMicrovmImageTarget as ct, createStudioDispatcher as d, substituteEnvVarsFromState as da, filterRoutesByApiIdentifiers as di, httpHeadersToEdge as dn, workloadClientConfig as do, mergeForService as dr, createLocalStartWebFunctionCommand as dt, buildConnectEvent as ea, createLocalStartApiCommand as ei, createS3OriginReader as en, keysNotFromTemplate as eo, buildEcsImageResolutionContext$1 as er, runHookBody as et, filterStudioCustomResources as f, substituteEnvVarsFromStateAsync as fa, groupRoutesByServer as fi, CLOUDFRONT_DISTRIBUTION_TYPE as fn, workloadStsClientConfig as fo, parseImageOverrideFlags as fr, addStartAgentCoreSpecificOptions as ft, annotatePinnedEcsTargets as g, rejectExplicitCfnStackWithMultipleStacks as ga, resolveServiceIntegrationParameters as gi, pickFunctionUrlLogicalIdFromOrigin as gn, isProxyEnvConfigured as go, imageOverrideKeyAliases as gr, startAgentCoreHttpServer as gt, annotateEcsTaskPinnedTargets as h, isCfnFlagPresent as ha, resolveSelectionExpression as hi, isCloudFrontDistribution as hn, buildProxyClientConfig as ho, describePinnedImageUri as hr, selectServeInboundAuth as ht, coerceStopRequest as i, buildContainerImage as ia, createFileWatcher as ii, serveFromStaticOrigin as in, withoutKeys as io, resolveEcsAssumeRoleOption as ir, MicrovmResolutionError as it, readRunHookPayload as j, parseSelectionExpressionPath as ja, buildCorsConfigByApiId as ji, createCloudFrontModule as jn, A2A_PATH as jr, startWebFunctionContainer as jt, finalizeMicrovmEnv as k, discoverWebSocketApisOrThrow as ka, attachAuthorizers as ki, runViewerResponse as kn, invokeAgentCoreWs as kr, planWebFunctionContainer as kt, relayServeRequest as l, substituteAgainstState as la, availableApiIdentifiers as li, buildEdgeResponseEvent as ln, recordAssumedRole as lo, canonicalizeImageOverrideKeys as lr, s3KeyOfUri as lt, annotateAlbPinnedBackingServices as m, createLocalStateProvider as ma, startApiServer as mi, extractKvsAssociations as mn, resolveProfileCredentials as mo, runImageOverrideBuilds as mr, buildAgentCoreServeAuthCheck as mt, coerceRunRequest as n, buildMessageEvent as na, resolveApiTargetSubset as ni, startCloudFrontServer as nn, parseDynamicReference as no, parseMaxTasks as nr, MICROVM_DEFAULT_INGRESS_PORT as nt, resolveServeBaseUrl as o, resolveRuntimeFileExtension as oa, buildStageMap as oi, applyEdgeRequestResult as on, assumedRoleRecorded as oo, runEcsServiceEmulator as or, readMicrovmImageConfig as ot, isCustomResourceLambdaTarget as p, LocalStateSourceError as pa, readMtlsMaterialsFromDisk as pi, describeS3OriginDomain as pn, buildStsClientConfig as po, resolveImageOverrides as pr, createLocalStartAgentCoreCommand as pt, MAX_RUN_HOOK_PAYLOAD_BYTES as q, tryResolveImageFnJoin as qa, probeHostGatewaySupport as qi, serviceStrategy as qn, computeCodeImageTag as qr, webFunctionFidelityWarnings as qt, coerceServeRequest as r, architectureToPlatform as ra, createAuthorizerCache as ri, resolveErrorResponseCandidates as rn, resolveDynamicReferencesInEnv as ro, parseRestartPolicy as rr, MICROVM_IMAGE_TYPE as rt, createStudioServeManager as s, resolveRuntimeImage as sa, materializeLayerFromArn as si, applyEdgeResponseResult as sn, deployAccountStsClientConfig as so, ImageOverrideError as sr, resolveMicrovmCodeArtifact as st, addStudioSpecificOptions as t, buildDisconnectEvent as ta, createWatchPredicates as ti, matchBehavior as tn, keysOverriddenBy as to, ecsClusterOption as tr, waitForAppAnswer as tt, reinvoke as u, substituteAgainstStateAsync as ua, filterRoutesByApiIdentifier as ui, edgeHeadersToHttp as un, resetWorkloadIdentity as uo, enforceImageOverrideOrphans as ur, addStartWebFunctionSpecificOptions as ut, startStudioServer as v, resolveCfnRegion as va, buildCognitoJwksUrl as vi, pickLambdaEdgeFunctionLogicalId as vn, listPinnedTargets as vr, startAgentCoreWsBridge as vt, createLocalListCommand as w, resolveSingleTarget as wa, buildMethodArn as wi, WEB_FUNCTION_TYPE as wn, getContainerNetworkIp as wr, parseOriginOverrides as wt, createStudioStore as x, collectSsmParameterRefs as xa, verifyCognitoJwt as xi, resolveCloudFrontDistribution as xn, DEFAULT_SHADOW_READY_TIMEOUT_MS as xr, createLocalStartCloudFrontCommand as xt, toStudioTargetGroups as y, resolveCfnStackName as ya, buildJwksUrlFromIssuer as yi, pickTargetFunctionLogicalId as yn, buildCloudMapIndex as yr, LocalStartCloudFrontError as yt, MICROVM_CONTROL_PATH as z, AGENTCORE_MCP_PROTOCOL as za, buildRestV1Event as zi, albStrategy as zn, signAgentCoreInvocation as zr, disposeWebFunctionArtifact as zt };
|
|
32852
|
+
//# sourceMappingURL=local-studio-DKMdamzH.js.map
|