cdk-local 0.156.0 → 0.158.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 +160 -98
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/local-studio-DXVr171Y.d.ts.map +1 -1
- package/dist/{local-studio-BhFxp-jM.js → local-studio-vhvbqvPb.js} +2152 -1648
- package/dist/local-studio-vhvbqvPb.js.map +1 -0
- 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,310 @@ function decodePart(s) {
|
|
|
22765
22852
|
}
|
|
22766
22853
|
}
|
|
22767
22854
|
|
|
22855
|
+
//#endregion
|
|
22856
|
+
//#region src/local/response-headers-policy.ts
|
|
22857
|
+
function resolveResponseHeadersPolicyHeaders(template, responseHeadersPolicyId) {
|
|
22858
|
+
const rhpId = pickRhpRefLogicalId(responseHeadersPolicyId);
|
|
22859
|
+
if (!rhpId) return void 0;
|
|
22860
|
+
const rhpResource = (template.Resources ?? {})[rhpId];
|
|
22861
|
+
if (!rhpResource || rhpResource.Type !== "AWS::CloudFront::ResponseHeadersPolicy") return;
|
|
22862
|
+
const rhpConfig = asRecord((rhpResource.Properties ?? {})["ResponseHeadersPolicyConfig"]);
|
|
22863
|
+
if (!rhpConfig) return void 0;
|
|
22864
|
+
const skipped = [];
|
|
22865
|
+
const set = [...parseSecurityHeaders(asRecord(rhpConfig["SecurityHeadersConfig"]), skipped), ...parseCustomHeaders(asRecord(rhpConfig["CustomHeadersConfig"]), skipped)];
|
|
22866
|
+
const remove = parseRemoveHeaders(asRecord(rhpConfig["RemoveHeadersConfig"]), skipped);
|
|
22867
|
+
if (set.length === 0 && remove.length === 0 && skipped.length === 0) return void 0;
|
|
22868
|
+
return {
|
|
22869
|
+
remove,
|
|
22870
|
+
set,
|
|
22871
|
+
skipped
|
|
22872
|
+
};
|
|
22873
|
+
}
|
|
22874
|
+
function applyResponseHeadersPolicyHeaders(res, policy, onInvalid) {
|
|
22875
|
+
for (const name of policy.remove) res.removeHeader(name);
|
|
22876
|
+
for (const header of policy.set) {
|
|
22877
|
+
if (!header.override && res.hasHeader(header.name)) continue;
|
|
22878
|
+
try {
|
|
22879
|
+
res.setHeader(header.name, header.value);
|
|
22880
|
+
} catch (err) {
|
|
22881
|
+
onInvalid?.(header.name, err);
|
|
22882
|
+
}
|
|
22883
|
+
}
|
|
22884
|
+
}
|
|
22885
|
+
function parseSecurityHeaders(config, skipped) {
|
|
22886
|
+
if (!config) return [];
|
|
22887
|
+
const out = [];
|
|
22888
|
+
const hsts = asRecord(config["StrictTransportSecurity"]);
|
|
22889
|
+
const maxAge = hsts ? toNumber(hsts["AccessControlMaxAgeSec"]) : void 0;
|
|
22890
|
+
if (hsts && maxAge !== void 0) {
|
|
22891
|
+
let value = `max-age=${maxAge}`;
|
|
22892
|
+
if (toBool(hsts["IncludeSubdomains"])) value += "; includeSubDomains";
|
|
22893
|
+
if (toBool(hsts["Preload"])) value += "; preload";
|
|
22894
|
+
out.push({
|
|
22895
|
+
name: "strict-transport-security",
|
|
22896
|
+
value,
|
|
22897
|
+
override: toBool(hsts["Override"])
|
|
22898
|
+
});
|
|
22899
|
+
} else if (hsts) skipped.push("strict-transport-security");
|
|
22900
|
+
const csp = asRecord(config["ContentSecurityPolicy"]);
|
|
22901
|
+
if (csp && typeof csp["ContentSecurityPolicy"] === "string") out.push({
|
|
22902
|
+
name: "content-security-policy",
|
|
22903
|
+
value: csp["ContentSecurityPolicy"],
|
|
22904
|
+
override: toBool(csp["Override"])
|
|
22905
|
+
});
|
|
22906
|
+
else if (csp) skipped.push("content-security-policy");
|
|
22907
|
+
const cto = asRecord(config["ContentTypeOptions"]);
|
|
22908
|
+
if (cto) out.push({
|
|
22909
|
+
name: "x-content-type-options",
|
|
22910
|
+
value: "nosniff",
|
|
22911
|
+
override: toBool(cto["Override"])
|
|
22912
|
+
});
|
|
22913
|
+
const frame = asRecord(config["FrameOptions"]);
|
|
22914
|
+
if (frame && typeof frame["FrameOption"] === "string") out.push({
|
|
22915
|
+
name: "x-frame-options",
|
|
22916
|
+
value: frame["FrameOption"],
|
|
22917
|
+
override: toBool(frame["Override"])
|
|
22918
|
+
});
|
|
22919
|
+
else if (frame) skipped.push("x-frame-options");
|
|
22920
|
+
const referrer = asRecord(config["ReferrerPolicy"]);
|
|
22921
|
+
if (referrer && typeof referrer["ReferrerPolicy"] === "string") out.push({
|
|
22922
|
+
name: "referrer-policy",
|
|
22923
|
+
value: referrer["ReferrerPolicy"],
|
|
22924
|
+
override: toBool(referrer["Override"])
|
|
22925
|
+
});
|
|
22926
|
+
else if (referrer) skipped.push("referrer-policy");
|
|
22927
|
+
const xss = asRecord(config["XSSProtection"]);
|
|
22928
|
+
const reportUri = xss?.["ReportUri"];
|
|
22929
|
+
if (xss && reportUri !== void 0 && typeof reportUri !== "string") skipped.push("x-xss-protection");
|
|
22930
|
+
else if (xss && xss["Protection"] !== void 0) {
|
|
22931
|
+
let value = toBool(xss["Protection"]) ? "1" : "0";
|
|
22932
|
+
if (value === "1") {
|
|
22933
|
+
if (toBool(xss["ModeBlock"])) value += "; mode=block";
|
|
22934
|
+
else if (typeof xss["ReportUri"] === "string" && xss["ReportUri"] !== "") value += `; report=${xss["ReportUri"]}`;
|
|
22935
|
+
}
|
|
22936
|
+
out.push({
|
|
22937
|
+
name: "x-xss-protection",
|
|
22938
|
+
value,
|
|
22939
|
+
override: toBool(xss["Override"])
|
|
22940
|
+
});
|
|
22941
|
+
}
|
|
22942
|
+
return out;
|
|
22943
|
+
}
|
|
22944
|
+
function parseCustomHeaders(config, skipped) {
|
|
22945
|
+
const items = config?.["Items"];
|
|
22946
|
+
if (!Array.isArray(items)) return [];
|
|
22947
|
+
const out = [];
|
|
22948
|
+
for (const item of items) {
|
|
22949
|
+
const h = asRecord(item);
|
|
22950
|
+
if (!h || typeof h["Header"] !== "string" || h["Header"] === "") {
|
|
22951
|
+
if (h && h["Header"] !== void 0) skipped.push("(unresolved custom header name)");
|
|
22952
|
+
continue;
|
|
22953
|
+
}
|
|
22954
|
+
if (h["Value"] !== void 0 && typeof h["Value"] !== "string") {
|
|
22955
|
+
skipped.push(h["Header"].toLowerCase());
|
|
22956
|
+
continue;
|
|
22957
|
+
}
|
|
22958
|
+
out.push({
|
|
22959
|
+
name: h["Header"].toLowerCase(),
|
|
22960
|
+
value: typeof h["Value"] === "string" ? h["Value"] : "",
|
|
22961
|
+
override: toBool(h["Override"])
|
|
22962
|
+
});
|
|
22963
|
+
}
|
|
22964
|
+
return out;
|
|
22965
|
+
}
|
|
22966
|
+
function parseRemoveHeaders(config, skipped) {
|
|
22967
|
+
const items = config?.["Items"];
|
|
22968
|
+
if (!Array.isArray(items)) return [];
|
|
22969
|
+
const out = [];
|
|
22970
|
+
for (const item of items) {
|
|
22971
|
+
const h = asRecord(item);
|
|
22972
|
+
if (h && typeof h["Header"] === "string" && h["Header"] !== "") out.push(h["Header"].toLowerCase());
|
|
22973
|
+
else if (h && h["Header"] !== void 0) skipped.push("(unresolved remove header name)");
|
|
22974
|
+
}
|
|
22975
|
+
return out;
|
|
22976
|
+
}
|
|
22977
|
+
function asRecord(value) {
|
|
22978
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
22979
|
+
}
|
|
22980
|
+
function toBool(value) {
|
|
22981
|
+
return value === true || value === "true";
|
|
22982
|
+
}
|
|
22983
|
+
function toNumber(value) {
|
|
22984
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
22985
|
+
if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
|
|
22986
|
+
}
|
|
22987
|
+
|
|
22988
|
+
//#endregion
|
|
22989
|
+
//#region src/local/web-function-resolver.ts
|
|
22990
|
+
const WEB_FUNCTION_TYPE = "AWS::Lambda::WebFunction";
|
|
22991
|
+
const WEB_FUNCTION_REVISION_TYPE = "AWS::Lambda::WebFunctionRevision";
|
|
22992
|
+
const WEB_FUNCTION_ENDPOINT_TYPE = "AWS::Lambda::WebFunctionEndpoint";
|
|
22993
|
+
const WEB_TYPES = [
|
|
22994
|
+
WEB_FUNCTION_ENDPOINT_TYPE,
|
|
22995
|
+
WEB_FUNCTION_TYPE,
|
|
22996
|
+
WEB_FUNCTION_REVISION_TYPE
|
|
22997
|
+
];
|
|
22998
|
+
var WebFunctionResolutionError = class WebFunctionResolutionError extends Error {
|
|
22999
|
+
constructor(message) {
|
|
23000
|
+
super(message);
|
|
23001
|
+
this.name = "WebFunctionResolutionError";
|
|
23002
|
+
Object.setPrototypeOf(this, WebFunctionResolutionError.prototype);
|
|
23003
|
+
}
|
|
23004
|
+
};
|
|
23005
|
+
function resolveWebFunctionTarget(target, stacks, revisionFlag) {
|
|
23006
|
+
if (stacks.length === 0) throw new WebFunctionResolutionError("No stacks found in the synthesized assembly.");
|
|
23007
|
+
const parsed = parseTarget(target);
|
|
23008
|
+
const stack = pickStack$1(parsed, stacks, target);
|
|
23009
|
+
const resources = stack.template.Resources ?? {};
|
|
23010
|
+
const { logicalId, resource } = matchWebResource(parsed, target, stack, resources);
|
|
23011
|
+
const functions = logicalIdsOfType(resources, WEB_FUNCTION_TYPE);
|
|
23012
|
+
const linked = (value) => linkedFunctionLogicalId(value, resources, functions);
|
|
23013
|
+
let functionLogicalId;
|
|
23014
|
+
let endpoint;
|
|
23015
|
+
let revisionLogicalId;
|
|
23016
|
+
let selection;
|
|
23017
|
+
if (resource.Type === "AWS::Lambda::WebFunctionRevision") {
|
|
23018
|
+
functionLogicalId = linked(resource.Properties?.["FunctionName"]);
|
|
23019
|
+
revisionLogicalId = logicalId;
|
|
23020
|
+
selection = "target-revision";
|
|
23021
|
+
} else if (resource.Type === "AWS::Lambda::WebFunctionEndpoint") {
|
|
23022
|
+
functionLogicalId = linked(resource.Properties?.["FunctionName"]);
|
|
23023
|
+
endpoint = readEndpoint(logicalId, resource, resources);
|
|
23024
|
+
} else functionLogicalId = logicalId;
|
|
23025
|
+
const revisionsOfFunction = () => functionLogicalId === void 0 ? [] : logicalIdsOfType(resources, WEB_FUNCTION_REVISION_TYPE).filter((id) => linked(resources[id]?.Properties?.["FunctionName"]) === functionLogicalId);
|
|
23026
|
+
const endpointsOfFunction = () => functionLogicalId === void 0 ? [] : logicalIdsOfType(resources, WEB_FUNCTION_ENDPOINT_TYPE).filter((id) => linked(resources[id]?.Properties?.["FunctionName"]) === functionLogicalId);
|
|
23027
|
+
if (revisionFlag !== void 0) {
|
|
23028
|
+
const flagged = resources[revisionFlag];
|
|
23029
|
+
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)}`);
|
|
23030
|
+
const owner = linked(flagged.Properties?.["FunctionName"]);
|
|
23031
|
+
if (functionLogicalId !== void 0 && owner !== functionLogicalId) throw new WebFunctionResolutionError(`--revision ${displayIdent(revisionFlag)} belongs to a different web function than ${displayIdent(logicalId)}. ${describeRevisions(revisionsOfFunction(), stack)}`);
|
|
23032
|
+
functionLogicalId ??= owner;
|
|
23033
|
+
revisionLogicalId = revisionFlag;
|
|
23034
|
+
selection = "revision-flag";
|
|
23035
|
+
}
|
|
23036
|
+
if (revisionLogicalId === void 0 && endpoint !== void 0) {
|
|
23037
|
+
revisionLogicalId = pickHighestWeight(endpoint);
|
|
23038
|
+
if (revisionLogicalId !== void 0) selection = "endpoint-weight";
|
|
23039
|
+
}
|
|
23040
|
+
if (revisionLogicalId === void 0) {
|
|
23041
|
+
const revisions = revisionsOfFunction();
|
|
23042
|
+
if (revisions.length === 1) {
|
|
23043
|
+
revisionLogicalId = revisions[0];
|
|
23044
|
+
selection = "only-revision";
|
|
23045
|
+
} else if (endpoint === void 0 && revisions.length > 1) {
|
|
23046
|
+
const endpoints = endpointsOfFunction();
|
|
23047
|
+
if (endpoints.length === 1) {
|
|
23048
|
+
endpoint = readEndpoint(endpoints[0], resources[endpoints[0]], resources);
|
|
23049
|
+
revisionLogicalId = pickHighestWeight(endpoint);
|
|
23050
|
+
if (revisionLogicalId !== void 0) selection = "endpoint-weight";
|
|
23051
|
+
}
|
|
23052
|
+
}
|
|
23053
|
+
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)}`);
|
|
23054
|
+
}
|
|
23055
|
+
if (endpoint === void 0) {
|
|
23056
|
+
const routing = endpointsOfFunction().map((id) => readEndpoint(id, resources[id], resources)).filter((e) => e.weights.some((w) => w.revisionLogicalId === revisionLogicalId));
|
|
23057
|
+
if (routing.length === 1) endpoint = routing[0];
|
|
23058
|
+
}
|
|
23059
|
+
const fnName = (functionLogicalId !== void 0 ? resources[functionLogicalId] : void 0)?.Properties?.["FunctionName"];
|
|
23060
|
+
return {
|
|
23061
|
+
stack,
|
|
23062
|
+
functionLogicalId,
|
|
23063
|
+
functionName: typeof fnName === "string" && fnName.length > 0 ? fnName : functionLogicalId ?? revisionLogicalId,
|
|
23064
|
+
revisionLogicalId,
|
|
23065
|
+
revision: resources[revisionLogicalId],
|
|
23066
|
+
endpoint,
|
|
23067
|
+
selection: selection ?? "only-revision"
|
|
23068
|
+
};
|
|
23069
|
+
}
|
|
23070
|
+
function pickStack$1(parsed, stacks, target) {
|
|
23071
|
+
const selection = selectTargetStack(parsed, target, stacks);
|
|
23072
|
+
switch (selection.kind) {
|
|
23073
|
+
case "found": return selection.stack;
|
|
23074
|
+
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)}.`);
|
|
23075
|
+
case "not-found": throw new WebFunctionResolutionError(`Stack ${displayStackName(selection.pattern)} not found. Available stacks: ${describeStacks(stacks)}.`);
|
|
23076
|
+
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.`);
|
|
23077
|
+
}
|
|
23078
|
+
}
|
|
23079
|
+
function matchWebResource(parsed, target, stack, resources) {
|
|
23080
|
+
if (parsed.isPath) {
|
|
23081
|
+
const matches = resolveCdkPathToLogicalIds(parsed.pathOrId, buildCdkPathIndex(stack.template)).filter(({ logicalId }) => WEB_TYPES.includes(resources[logicalId]?.Type ?? ""));
|
|
23082
|
+
for (const type of WEB_TYPES) {
|
|
23083
|
+
const ofType = matches.filter(({ logicalId }) => resources[logicalId].Type === type);
|
|
23084
|
+
if (ofType.length === 1) {
|
|
23085
|
+
const id = ofType[0].logicalId;
|
|
23086
|
+
return {
|
|
23087
|
+
logicalId: id,
|
|
23088
|
+
resource: resources[id]
|
|
23089
|
+
};
|
|
23090
|
+
}
|
|
23091
|
+
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.`);
|
|
23092
|
+
}
|
|
23093
|
+
throw notFoundError(target, stack, resources);
|
|
23094
|
+
}
|
|
23095
|
+
const resource = resources[parsed.pathOrId];
|
|
23096
|
+
if (!resource) throw notFoundError(target, stack, resources);
|
|
23097
|
+
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.`);
|
|
23098
|
+
return {
|
|
23099
|
+
logicalId: parsed.pathOrId,
|
|
23100
|
+
resource
|
|
23101
|
+
};
|
|
23102
|
+
}
|
|
23103
|
+
function notFoundError(target, stack, resources) {
|
|
23104
|
+
const available = WEB_TYPES.flatMap((t) => logicalIdsOfType(resources, t));
|
|
23105
|
+
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)}.`;
|
|
23106
|
+
return new WebFunctionResolutionError(`Target ${displayStackName(target)} not found. ${hint}`);
|
|
23107
|
+
}
|
|
23108
|
+
function logicalIdsOfType(resources, type) {
|
|
23109
|
+
return Object.keys(resources).filter((id) => resources[id]?.Type === type);
|
|
23110
|
+
}
|
|
23111
|
+
function linkedFunctionLogicalId(value, resources, functions) {
|
|
23112
|
+
const ref = referencedLogicalId(value);
|
|
23113
|
+
if (ref !== void 0) return functions.includes(ref) ? ref : void 0;
|
|
23114
|
+
if (typeof value !== "string") return void 0;
|
|
23115
|
+
const byName = functions.filter((id) => resources[id]?.Properties?.["FunctionName"] === value);
|
|
23116
|
+
return byName.length === 1 ? byName[0] : void 0;
|
|
23117
|
+
}
|
|
23118
|
+
function referencedLogicalId(value) {
|
|
23119
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
23120
|
+
const obj = value;
|
|
23121
|
+
if (typeof obj["Ref"] === "string") return obj["Ref"];
|
|
23122
|
+
const getAtt = obj["Fn::GetAtt"];
|
|
23123
|
+
if (Array.isArray(getAtt) && typeof getAtt[0] === "string") return getAtt[0];
|
|
23124
|
+
if (typeof getAtt === "string") return getAtt.split(".")[0];
|
|
23125
|
+
}
|
|
23126
|
+
function readEndpoint(logicalId, resource, resources) {
|
|
23127
|
+
const props = resource.Properties ?? {};
|
|
23128
|
+
const rawWeights = props["RevisionWeights"];
|
|
23129
|
+
const weights = [];
|
|
23130
|
+
if (Array.isArray(rawWeights)) for (const entry of rawWeights) {
|
|
23131
|
+
if (!entry || typeof entry !== "object") continue;
|
|
23132
|
+
const e = entry;
|
|
23133
|
+
const ref = referencedLogicalId(e["RevisionId"]);
|
|
23134
|
+
weights.push({
|
|
23135
|
+
revisionLogicalId: ref !== void 0 && resources[ref]?.Type === "AWS::Lambda::WebFunctionRevision" ? ref : void 0,
|
|
23136
|
+
weight: typeof e["Weight"] === "number" ? e["Weight"] : 0
|
|
23137
|
+
});
|
|
23138
|
+
}
|
|
23139
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
23140
|
+
return {
|
|
23141
|
+
logicalId,
|
|
23142
|
+
endpointType: str(props["EndpointType"]),
|
|
23143
|
+
authType: str(props["AuthType"]),
|
|
23144
|
+
weights
|
|
23145
|
+
};
|
|
23146
|
+
}
|
|
23147
|
+
function pickHighestWeight(endpoint) {
|
|
23148
|
+
let best;
|
|
23149
|
+
for (const w of endpoint.weights) {
|
|
23150
|
+
if (w.revisionLogicalId === void 0) continue;
|
|
23151
|
+
if (best === void 0 || w.weight > best.weight) best = w;
|
|
23152
|
+
}
|
|
23153
|
+
return best?.revisionLogicalId;
|
|
23154
|
+
}
|
|
23155
|
+
function describeRevisions(revisions, stack) {
|
|
23156
|
+
return revisions.length > 0 ? `Revisions: ${revisions.map((id) => displayIdent(id)).join(", ")}.` : `No ${WEB_FUNCTION_REVISION_TYPE} linked to it in ${displayStackName(stack.stackName)}.`;
|
|
23157
|
+
}
|
|
23158
|
+
|
|
22768
23159
|
//#endregion
|
|
22769
23160
|
//#region src/local/cloudfront-resolver.ts
|
|
22770
23161
|
const CLOUDFRONT_DISTRIBUTION_TYPE = "AWS::CloudFront::Distribution";
|
|
@@ -22864,6 +23255,11 @@ function resolveBehavior(behavior, pathPattern, functions, distLogicalId, templa
|
|
|
22864
23255
|
if (pathPattern !== void 0) resolved.pathPattern = pathPattern;
|
|
22865
23256
|
const cors = resolveResponseHeadersPolicyCors(template, behavior["ResponseHeadersPolicyId"]);
|
|
22866
23257
|
if (cors) resolved.cors = cors;
|
|
23258
|
+
const responseHeaders = resolveResponseHeadersPolicyHeaders(template, behavior["ResponseHeadersPolicyId"]);
|
|
23259
|
+
if (responseHeaders) {
|
|
23260
|
+
resolved.responseHeaders = responseHeaders;
|
|
23261
|
+
if (responseHeaders.skipped.length > 0) getLogger().warn(`Cache behavior ${displayIdent(pathPattern ?? "default")} of distribution ${displayIdent(distLogicalId)}: response headers policy entr(y/ies) ${responseHeaders.skipped.map((h) => displayIdent(h)).join(", ")} are not literals in the template (a CDK token); cdk-local does not apply them (the header is neither sent nor removed).`);
|
|
23262
|
+
}
|
|
22867
23263
|
if (typeof behavior["ViewerProtocolPolicy"] === "string") resolved.viewerProtocolPolicy = behavior["ViewerProtocolPolicy"];
|
|
22868
23264
|
const assocs = Array.isArray(behavior["FunctionAssociations"]) ? behavior["FunctionAssociations"] : [];
|
|
22869
23265
|
for (const a of assocs) {
|
|
@@ -22949,6 +23345,18 @@ function resolveOrigins(dc, template, stack, overrides) {
|
|
|
22949
23345
|
out.set(originId, lambdaUrl);
|
|
22950
23346
|
continue;
|
|
22951
23347
|
}
|
|
23348
|
+
const endpointLogicalId = pickWebFunctionEndpointLogicalIdFromOrigin(origin["DomainName"], template);
|
|
23349
|
+
if (endpointLogicalId) {
|
|
23350
|
+
const customConfig = origin["CustomOriginConfig"];
|
|
23351
|
+
const readTimeout = customConfig && typeof customConfig === "object" ? customConfig["OriginReadTimeout"] : void 0;
|
|
23352
|
+
out.set(originId, {
|
|
23353
|
+
kind: "web-function",
|
|
23354
|
+
originId,
|
|
23355
|
+
endpointLogicalId,
|
|
23356
|
+
...typeof readTimeout === "number" && readTimeout > 0 && { readTimeoutSeconds: readTimeout }
|
|
23357
|
+
});
|
|
23358
|
+
continue;
|
|
23359
|
+
}
|
|
22952
23360
|
out.set(originId, {
|
|
22953
23361
|
kind: "custom",
|
|
22954
23362
|
originId,
|
|
@@ -23017,13 +23425,11 @@ function resolveBucketDeploymentDirs(template, manifest, stack, bucketLogicalId)
|
|
|
23017
23425
|
const { manifestDir, assetOutdir } = assetPathDirs(stack);
|
|
23018
23426
|
const dirs = [];
|
|
23019
23427
|
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) {
|
|
23428
|
+
if (!isBucketDeploymentResource(resource)) continue;
|
|
23429
|
+
if (refLogicalId((resource.Properties ?? {})["DestinationBucketName"]) !== bucketLogicalId) continue;
|
|
23430
|
+
for (const key of bucketDeploymentSourceObjectKeys(resource)) {
|
|
23025
23431
|
if (typeof key !== "string") continue;
|
|
23026
|
-
const asset =
|
|
23432
|
+
const asset = findFileAssetForObjectKey(manifest, key)?.asset;
|
|
23027
23433
|
if (!asset) continue;
|
|
23028
23434
|
dirs.push(resolveAssetSourcePath({
|
|
23029
23435
|
manifestDir,
|
|
@@ -23040,11 +23446,6 @@ function resolveBucketDeploymentDirs(template, manifest, stack, bucketLogicalId)
|
|
|
23040
23446
|
}
|
|
23041
23447
|
return dirs;
|
|
23042
23448
|
}
|
|
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
23449
|
function pickBucketLogicalIdFromOrigin(origin, template) {
|
|
23049
23450
|
const candidate = getAttLogicalId(origin["DomainName"]);
|
|
23050
23451
|
if (!candidate) return void 0;
|
|
@@ -23072,6 +23473,11 @@ function resolveLambdaUrlOrigin(origin, template, originId) {
|
|
|
23072
23473
|
functionUrlLogicalId: urlLogicalId
|
|
23073
23474
|
};
|
|
23074
23475
|
}
|
|
23476
|
+
function pickWebFunctionEndpointLogicalIdFromOrigin(value, template) {
|
|
23477
|
+
const getAtt = asIntrinsic(value, "Fn::GetAtt");
|
|
23478
|
+
if (!Array.isArray(getAtt) || getAtt.length !== 2 || typeof getAtt[0] !== "string" || getAtt[1] !== "DomainName") return;
|
|
23479
|
+
return (template.Resources ?? {})[getAtt[0]]?.Type === "AWS::Lambda::WebFunctionEndpoint" ? getAtt[0] : void 0;
|
|
23480
|
+
}
|
|
23075
23481
|
function pickFunctionUrlLogicalIdFromOrigin(value) {
|
|
23076
23482
|
const select = asIntrinsic(value, "Fn::Select");
|
|
23077
23483
|
if (!Array.isArray(select) || select.length !== 2 || select[0] !== 2) return void 0;
|
|
@@ -23389,11 +23795,92 @@ function normalizeHeaders(headers) {
|
|
|
23389
23795
|
}
|
|
23390
23796
|
|
|
23391
23797
|
//#endregion
|
|
23392
|
-
//#region src/local/cloudfront-
|
|
23393
|
-
|
|
23394
|
-
|
|
23395
|
-
const
|
|
23396
|
-
const
|
|
23798
|
+
//#region src/local/cloudfront-web-function-origin.ts
|
|
23799
|
+
const DEFAULT_ORIGIN_READ_TIMEOUT_SECONDS = 30;
|
|
23800
|
+
function serveWebFunctionOrigin(args) {
|
|
23801
|
+
const { target, request: request$3 } = args;
|
|
23802
|
+
const timeoutMs = (args.readTimeoutSeconds ?? 30) * 1e3;
|
|
23803
|
+
const headers = buildOriginRequestHeaders(request$3, target);
|
|
23804
|
+
if (request$3.body.length > 0) headers["content-length"] = String(request$3.body.length);
|
|
23805
|
+
const path = request$3.querystring ? `${request$3.uri}?${request$3.querystring}` : request$3.uri;
|
|
23806
|
+
return new Promise((resolveResult) => {
|
|
23807
|
+
let settled = false;
|
|
23808
|
+
const settle = (result) => {
|
|
23809
|
+
if (settled) return;
|
|
23810
|
+
settled = true;
|
|
23811
|
+
resolveResult(result);
|
|
23812
|
+
};
|
|
23813
|
+
const req = request({
|
|
23814
|
+
host: target.host,
|
|
23815
|
+
port: target.port,
|
|
23816
|
+
method: request$3.method,
|
|
23817
|
+
path,
|
|
23818
|
+
headers,
|
|
23819
|
+
agent: false
|
|
23820
|
+
}, (res) => {
|
|
23821
|
+
const chunks = [];
|
|
23822
|
+
res.on("data", (c) => chunks.push(c));
|
|
23823
|
+
res.on("end", () => settle({
|
|
23824
|
+
statusCode: res.statusCode ?? 502,
|
|
23825
|
+
...splitResponseHeaders(res.headers),
|
|
23826
|
+
body: Buffer.concat(chunks)
|
|
23827
|
+
}));
|
|
23828
|
+
res.on("error", (err) => settle(originError(502, err.message)));
|
|
23829
|
+
});
|
|
23830
|
+
req.setTimeout(timeoutMs, () => {
|
|
23831
|
+
settle(originError(504, `the web function server did not answer within the origin read timeout (${timeoutMs / 1e3}s)`));
|
|
23832
|
+
req.destroy();
|
|
23833
|
+
});
|
|
23834
|
+
req.on("error", (err) => settle(originError(502, err.message)));
|
|
23835
|
+
req.end(request$3.body.length > 0 ? request$3.body : void 0);
|
|
23836
|
+
});
|
|
23837
|
+
}
|
|
23838
|
+
function buildOriginRequestHeaders(request, target) {
|
|
23839
|
+
const headers = {};
|
|
23840
|
+
for (const [name, value] of Object.entries(request.headers)) if (value !== void 0) headers[name.toLowerCase()] = value;
|
|
23841
|
+
stripHopByHopHeaders(headers);
|
|
23842
|
+
delete headers["content-length"];
|
|
23843
|
+
headers["host"] = formatAuthority(target.host, target.port);
|
|
23844
|
+
if (request.sourceIp !== void 0) {
|
|
23845
|
+
const prior = headers["x-forwarded-for"];
|
|
23846
|
+
const priorValue = Array.isArray(prior) ? prior.join(", ") : prior;
|
|
23847
|
+
headers["x-forwarded-for"] = priorValue ? `${priorValue}, ${request.sourceIp}` : request.sourceIp;
|
|
23848
|
+
}
|
|
23849
|
+
const out = {};
|
|
23850
|
+
for (const [name, value] of Object.entries(headers)) if (value !== void 0) out[name] = value;
|
|
23851
|
+
return out;
|
|
23852
|
+
}
|
|
23853
|
+
function splitResponseHeaders(raw) {
|
|
23854
|
+
const copy = { ...raw };
|
|
23855
|
+
stripHopByHopHeaders(copy);
|
|
23856
|
+
delete copy["content-length"];
|
|
23857
|
+
const cookies = copy["set-cookie"];
|
|
23858
|
+
delete copy["set-cookie"];
|
|
23859
|
+
const headers = {};
|
|
23860
|
+
for (const [name, value] of Object.entries(copy)) {
|
|
23861
|
+
if (value === void 0) continue;
|
|
23862
|
+
headers[name] = Array.isArray(value) ? value.join(", ") : value;
|
|
23863
|
+
}
|
|
23864
|
+
return {
|
|
23865
|
+
headers,
|
|
23866
|
+
cookies: cookies === void 0 ? [] : Array.isArray(cookies) ? cookies : [cookies]
|
|
23867
|
+
};
|
|
23868
|
+
}
|
|
23869
|
+
function originError(statusCode, detail) {
|
|
23870
|
+
return {
|
|
23871
|
+
statusCode,
|
|
23872
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
23873
|
+
cookies: [],
|
|
23874
|
+
body: Buffer.from(`${statusCode === 502 ? "Bad Gateway" : "Gateway Timeout"}: ${detail.replace(/[\r\n]+/g, " ")}\n`)
|
|
23875
|
+
};
|
|
23876
|
+
}
|
|
23877
|
+
|
|
23878
|
+
//#endregion
|
|
23879
|
+
//#region src/local/cloudfront-static-origin.ts
|
|
23880
|
+
function serveFromStaticOrigin(input) {
|
|
23881
|
+
const key = uriToKey(input.uri, input.defaultRootObject);
|
|
23882
|
+
const containLinks = input.containLinks === true;
|
|
23883
|
+
const hideIn = new Set(input.hideDotfilesIn ?? []);
|
|
23397
23884
|
const direct = readKey(input.localDirs, key, containLinks, hideIn);
|
|
23398
23885
|
if (direct) return {
|
|
23399
23886
|
statusCode: 200,
|
|
@@ -23557,9 +24044,10 @@ async function startCloudFrontServer(options) {
|
|
|
23557
24044
|
const lambdaInvokers = options.lambdaInvokers ?? /* @__PURE__ */ new Map();
|
|
23558
24045
|
const edgeInvokers = options.edgeInvokers ?? /* @__PURE__ */ new Map();
|
|
23559
24046
|
const s3OriginReaders = options.s3OriginReaders ?? /* @__PURE__ */ new Map();
|
|
24047
|
+
const webFunctionOrigins = options.webFunctionOrigins ?? /* @__PURE__ */ new Map();
|
|
23560
24048
|
const state = { distribution: options.distribution };
|
|
23561
24049
|
const handler = (req, res) => {
|
|
23562
|
-
handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger).catch((err) => {
|
|
24050
|
+
handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, webFunctionOrigins, logger).catch((err) => {
|
|
23563
24051
|
logger.warn(`Request handling failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23564
24052
|
if (!res.headersSent) {
|
|
23565
24053
|
res.statusCode = 500;
|
|
@@ -23588,7 +24076,7 @@ async function startCloudFrontServer(options) {
|
|
|
23588
24076
|
}
|
|
23589
24077
|
};
|
|
23590
24078
|
}
|
|
23591
|
-
async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, logger) {
|
|
24079
|
+
async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3OriginReaders, webFunctionOrigins, logger) {
|
|
23592
24080
|
const distribution = state.distribution;
|
|
23593
24081
|
const rawUrl = req.url ?? "/";
|
|
23594
24082
|
const queryIdx = rawUrl.indexOf("?");
|
|
@@ -23680,6 +24168,7 @@ async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3
|
|
|
23680
24168
|
distribution,
|
|
23681
24169
|
lambdaInvokers,
|
|
23682
24170
|
s3OriginReaders,
|
|
24171
|
+
webFunctionOrigins,
|
|
23683
24172
|
logger
|
|
23684
24173
|
});
|
|
23685
24174
|
if (!originResult) return writePlain(res, 502, originUnavailableMessage(origin, behavior));
|
|
@@ -23737,11 +24226,15 @@ async function handleRequest$2(req, res, state, lambdaInvokers, edgeInvokers, s3
|
|
|
23737
24226
|
} catch (err) {
|
|
23738
24227
|
logger.warn(`Skipping invalid Set-Cookie header(s): ${err instanceof Error ? err.message : String(err)}`);
|
|
23739
24228
|
}
|
|
24229
|
+
applyResponseHeadersPolicy(res, behavior, req, logger);
|
|
24230
|
+
res.end(finalBody);
|
|
24231
|
+
}
|
|
24232
|
+
function applyResponseHeadersPolicy(res, behavior, req, logger) {
|
|
24233
|
+
if (behavior.responseHeaders) applyResponseHeadersPolicyHeaders(res, behavior.responseHeaders, (name, err) => logger.warn(`Skipping invalid response headers policy header ${displayUntrustedValue(name)}: ${flattenToOneLine(err instanceof Error ? err.message : String(err))}`));
|
|
23740
24234
|
if (behavior.cors) {
|
|
23741
24235
|
const origin = req.headers.origin;
|
|
23742
24236
|
applyCorsResponseHeadersFromConfig(res, behavior.cors, typeof origin === "string" ? origin : void 0);
|
|
23743
24237
|
}
|
|
23744
|
-
res.end(finalBody);
|
|
23745
24238
|
}
|
|
23746
24239
|
async function runEdgeResponseStage(assoc, eventType, ctx, edgeInvokers, logger) {
|
|
23747
24240
|
const invoke = edgeInvokers.get(assoc.functionLogicalId);
|
|
@@ -23769,10 +24262,7 @@ function writeEdgeResponse(res, result, behavior, req, logger) {
|
|
|
23769
24262
|
if (result.setCookies.length > 0) try {
|
|
23770
24263
|
res.setHeader("set-cookie", result.setCookies);
|
|
23771
24264
|
} catch {}
|
|
23772
|
-
|
|
23773
|
-
const origin = req.headers.origin;
|
|
23774
|
-
applyCorsResponseHeadersFromConfig(res, behavior.cors, typeof origin === "string" ? origin : void 0);
|
|
23775
|
-
}
|
|
24265
|
+
applyResponseHeadersPolicy(res, behavior, req, logger);
|
|
23776
24266
|
res.end(result.body);
|
|
23777
24267
|
}
|
|
23778
24268
|
function cfHeadersToRawMap(cf) {
|
|
@@ -23835,6 +24325,28 @@ async function serveFromOrigin(origin, args) {
|
|
|
23835
24325
|
...result.cookies.length > 0 && { setCookies: result.cookies }
|
|
23836
24326
|
};
|
|
23837
24327
|
}
|
|
24328
|
+
if (origin.kind === "web-function") {
|
|
24329
|
+
const target = args.webFunctionOrigins.get(origin.endpointLogicalId);
|
|
24330
|
+
if (!target) return void 0;
|
|
24331
|
+
const result = await serveWebFunctionOrigin({
|
|
24332
|
+
target,
|
|
24333
|
+
request: {
|
|
24334
|
+
method: args.method,
|
|
24335
|
+
uri: args.uri,
|
|
24336
|
+
querystring: args.querystring,
|
|
24337
|
+
headers: args.headers,
|
|
24338
|
+
body: await args.readBody(),
|
|
24339
|
+
sourceIp: args.sourceIp
|
|
24340
|
+
},
|
|
24341
|
+
...origin.readTimeoutSeconds !== void 0 && { readTimeoutSeconds: origin.readTimeoutSeconds }
|
|
24342
|
+
});
|
|
24343
|
+
return {
|
|
24344
|
+
statusCode: result.statusCode,
|
|
24345
|
+
headers: result.headers,
|
|
24346
|
+
body: result.body,
|
|
24347
|
+
...result.cookies.length > 0 && { setCookies: result.cookies }
|
|
24348
|
+
};
|
|
24349
|
+
}
|
|
23838
24350
|
if (origin.kind === "s3-deployed") {
|
|
23839
24351
|
const reader = args.s3OriginReaders.get(origin.originId);
|
|
23840
24352
|
if (!reader) return void 0;
|
|
@@ -23867,7 +24379,8 @@ async function serveFromOrigin(origin, args) {
|
|
|
23867
24379
|
function originUnavailableMessage(origin, behavior) {
|
|
23868
24380
|
if (!origin) return `Behavior ${behavior.pathPattern === void 0 ? "(default)" : displayUntrustedValue(behavior.pathPattern)} targets unknown origin ${displayIdent(behavior.targetOriginId)}.\n`;
|
|
23869
24381
|
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 === "
|
|
24382
|
+
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`;
|
|
24383
|
+
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
24384
|
`;
|
|
23872
24385
|
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
24386
|
`;
|
|
@@ -24161,1649 +24674,1706 @@ function idFromArn(arn) {
|
|
|
24161
24674
|
}
|
|
24162
24675
|
|
|
24163
24676
|
//#endregion
|
|
24164
|
-
//#region src/
|
|
24165
|
-
|
|
24166
|
-
|
|
24167
|
-
|
|
24168
|
-
|
|
24169
|
-
};
|
|
24170
|
-
|
|
24171
|
-
const
|
|
24172
|
-
|
|
24173
|
-
|
|
24174
|
-
|
|
24175
|
-
|
|
24176
|
-
|
|
24177
|
-
|
|
24677
|
+
//#region src/local/web-function-service-config.ts
|
|
24678
|
+
const WEB_FUNCTION_HTTP_ENDPOINT_ENV = "AWS_LAMBDA_HTTP_ENDPOINT";
|
|
24679
|
+
const APPLICATION_MANAGED_AUTH = "ApplicationManaged";
|
|
24680
|
+
const HOME_REGION_ENDPOINT = "HomeRegion";
|
|
24681
|
+
function readWebFunctionServiceConfig(revision) {
|
|
24682
|
+
const sc = asObject$2(revision.Properties?.["ServiceConfig"]) ?? {};
|
|
24683
|
+
const envRaw = sc["EnvironmentVariables"];
|
|
24684
|
+
const envObj = asObject$2(envRaw);
|
|
24685
|
+
const envKeys = envObj ? Object.keys(envObj) : [];
|
|
24686
|
+
const env = envKeys.length === 1 && (envKeys[0] === "Ref" || envKeys[0].startsWith("Fn::")) ? void 0 : envObj;
|
|
24687
|
+
const logGroup = asObject$2(asObject$2(sc["TelemetryConfig"])?.["LoggingConfig"])?.["LogGroup"];
|
|
24688
|
+
return {
|
|
24689
|
+
environmentVariables: env,
|
|
24690
|
+
environmentUnreadable: envRaw !== void 0 && env === void 0,
|
|
24691
|
+
executionRoleArn: sc["ExecutionRoleArn"],
|
|
24692
|
+
egressNetworkConnectorArn: sc["EgressNetworkConnectorArn"],
|
|
24693
|
+
timeoutSeconds: typeof sc["TimeoutSeconds"] === "number" ? sc["TimeoutSeconds"] : void 0,
|
|
24694
|
+
logGroup: typeof logGroup === "string" && logGroup.length > 0 ? logGroup : void 0
|
|
24695
|
+
};
|
|
24178
24696
|
}
|
|
24179
|
-
function
|
|
24180
|
-
|
|
24181
|
-
|
|
24182
|
-
|
|
24183
|
-
|
|
24184
|
-
|
|
24185
|
-
|
|
24186
|
-
|
|
24697
|
+
function buildWebFunctionLambdaView(resolved, config, runtime) {
|
|
24698
|
+
return {
|
|
24699
|
+
kind: "zip",
|
|
24700
|
+
stack: resolved.stack,
|
|
24701
|
+
logicalId: resolved.revisionLogicalId,
|
|
24702
|
+
resource: {
|
|
24703
|
+
Type: resolved.revision.Type,
|
|
24704
|
+
Properties: {
|
|
24705
|
+
...config.environmentVariables !== void 0 && { Environment: { Variables: config.environmentVariables } },
|
|
24706
|
+
...config.executionRoleArn !== void 0 && { Role: config.executionRoleArn }
|
|
24707
|
+
},
|
|
24708
|
+
...resolved.revision.Metadata !== void 0 && { Metadata: resolved.revision.Metadata }
|
|
24709
|
+
},
|
|
24710
|
+
memoryMb: 128,
|
|
24711
|
+
timeoutSec: config.timeoutSeconds ?? 0,
|
|
24712
|
+
layers: [],
|
|
24713
|
+
runtime,
|
|
24714
|
+
handler: "",
|
|
24715
|
+
codePath: null,
|
|
24716
|
+
architecture: "arm64"
|
|
24717
|
+
};
|
|
24187
24718
|
}
|
|
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;
|
|
24719
|
+
function webFunctionAssumeRole(assumeRole, config) {
|
|
24720
|
+
if (assumeRole === true && isIamRoleArn(config.executionRoleArn)) return config.executionRoleArn;
|
|
24721
|
+
return assumeRole;
|
|
24203
24722
|
}
|
|
24204
|
-
function
|
|
24205
|
-
|
|
24206
|
-
|
|
24207
|
-
|
|
24208
|
-
|
|
24209
|
-
|
|
24210
|
-
|
|
24211
|
-
|
|
24212
|
-
|
|
24213
|
-
|
|
24214
|
-
|
|
24215
|
-
|
|
24216
|
-
|
|
24217
|
-
|
|
24723
|
+
function finalizeWebFunctionEnv(lambdaEnv, opts) {
|
|
24724
|
+
const env = { ...lambdaEnv };
|
|
24725
|
+
delete env["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"];
|
|
24726
|
+
delete env["AWS_LAMBDA_FUNCTION_VERSION"];
|
|
24727
|
+
delete env["AWS_LAMBDA_FUNCTION_TIMEOUT"];
|
|
24728
|
+
env["AWS_LAMBDA_FUNCTION_NAME"] = opts.functionName;
|
|
24729
|
+
env["AWS_LAMBDA_LOG_GROUP_NAME"] = opts.logGroup ?? `/aws/lambda/${opts.functionName}`;
|
|
24730
|
+
if (opts.timeoutSeconds !== void 0) env["AWS_LAMBDA_FUNCTION_TIMEOUT"] = String(opts.timeoutSeconds);
|
|
24731
|
+
const replacedHttpEndpoint = Object.hasOwn(lambdaEnv, WEB_FUNCTION_HTTP_ENDPOINT_ENV);
|
|
24732
|
+
env[WEB_FUNCTION_HTTP_ENDPOINT_ENV] = `0.0.0.0:${opts.containerPort}`;
|
|
24733
|
+
return {
|
|
24734
|
+
env,
|
|
24735
|
+
replacedHttpEndpoint
|
|
24736
|
+
};
|
|
24737
|
+
}
|
|
24738
|
+
function webFunctionFidelityWarnings(resolved, config) {
|
|
24739
|
+
const out = [];
|
|
24740
|
+
const rev = displayIdent(resolved.revisionLogicalId);
|
|
24741
|
+
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.`);
|
|
24742
|
+
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.`);
|
|
24743
|
+
const endpoint = resolved.endpoint;
|
|
24744
|
+
if (endpoint) {
|
|
24745
|
+
const ep = displayIdent(endpoint.logicalId);
|
|
24746
|
+
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.`);
|
|
24747
|
+
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.`);
|
|
24748
|
+
const routed = endpoint.weights.filter((w) => w.weight > 0);
|
|
24749
|
+
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
24750
|
}
|
|
24219
24751
|
return out;
|
|
24220
24752
|
}
|
|
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);
|
|
24753
|
+
function asObject$2(value) {
|
|
24754
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
24251
24755
|
}
|
|
24252
|
-
|
|
24253
|
-
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
|
|
24258
|
-
|
|
24259
|
-
|
|
24260
|
-
|
|
24261
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24265
|
-
|
|
24266
|
-
|
|
24267
|
-
|
|
24756
|
+
|
|
24757
|
+
//#endregion
|
|
24758
|
+
//#region src/local/web-function-artifact.ts
|
|
24759
|
+
const SUPPORTED_CODE_CONFIG_KEYS = ["S3Object"];
|
|
24760
|
+
function parseWebFunctionBuildConfig(revisionLogicalId, buildConfig, template) {
|
|
24761
|
+
const subject = `Web function revision ${displayIdent(revisionLogicalId)}`;
|
|
24762
|
+
const build = asObject$1(buildConfig);
|
|
24763
|
+
const runtime = asObject$1(build?.["RuntimeConfig"])?.["Runtime"];
|
|
24764
|
+
if (typeof runtime !== "string" || runtime.length === 0) throw new CdkLocalError(`${subject} has no literal BuildConfig.RuntimeConfig.Runtime.`, "LOCAL_START_WEB_FUNCTION_NO_RUNTIME");
|
|
24765
|
+
const codeConfig = asObject$1(build?.["CodeConfig"]);
|
|
24766
|
+
if (!codeConfig) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
|
|
24767
|
+
const unknownKeys = Object.keys(codeConfig).filter((k) => !SUPPORTED_CODE_CONFIG_KEYS.includes(k));
|
|
24768
|
+
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");
|
|
24769
|
+
const s3 = asObject$1(codeConfig["S3Object"]);
|
|
24770
|
+
if (!s3) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig.S3Object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
|
|
24771
|
+
const rawKey = s3["Key"];
|
|
24772
|
+
let key;
|
|
24773
|
+
let deployment;
|
|
24774
|
+
if (typeof rawKey === "string" && rawKey.length > 0) key = rawKey;
|
|
24775
|
+
else {
|
|
24776
|
+
const viaDeployment = template ? resolveBucketDeploymentObjectKey(template, rawKey) : void 0;
|
|
24777
|
+
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");
|
|
24778
|
+
key = bucketDeploymentSourceKey(subject, viaDeployment);
|
|
24779
|
+
deployment = {
|
|
24780
|
+
logicalId: viaDeployment.deploymentLogicalId,
|
|
24781
|
+
index: viaDeployment.index
|
|
24268
24782
|
};
|
|
24269
24783
|
}
|
|
24270
|
-
const
|
|
24271
|
-
if (!res || res.Type !== "AWS::CloudFront::Distribution") throw notFound(target, stack, resources);
|
|
24784
|
+
const versionId = s3["VersionId"];
|
|
24272
24785
|
return {
|
|
24273
|
-
|
|
24274
|
-
|
|
24786
|
+
runtime,
|
|
24787
|
+
s3Object: {
|
|
24788
|
+
bucket: s3["Bucket"],
|
|
24789
|
+
key,
|
|
24790
|
+
...typeof versionId === "string" && { versionId },
|
|
24791
|
+
...deployment !== void 0 && { deployment }
|
|
24792
|
+
}
|
|
24275
24793
|
};
|
|
24276
24794
|
}
|
|
24277
|
-
function
|
|
24278
|
-
const
|
|
24279
|
-
switch (
|
|
24280
|
-
case "
|
|
24281
|
-
case "
|
|
24282
|
-
case "
|
|
24283
|
-
case "
|
|
24795
|
+
function bucketDeploymentSourceKey(subject, r) {
|
|
24796
|
+
const named = `CodeConfig.S3Object.Key selects source ${r.index} of BucketDeployment ${displayIdent(r.deploymentLogicalId)}`;
|
|
24797
|
+
switch (r.kind) {
|
|
24798
|
+
case "resolved": return r.sourceObjectKey;
|
|
24799
|
+
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");
|
|
24800
|
+
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");
|
|
24801
|
+
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");
|
|
24802
|
+
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");
|
|
24803
|
+
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
24804
|
}
|
|
24285
24805
|
}
|
|
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
|
-
}
|
|
24806
|
+
async function resolveWebFunctionArtifact(stack, revisionLogicalId, code, loader = new AssetManifestLoader()) {
|
|
24807
|
+
const subject = `Web function revision ${displayUntrustedValue(revisionLogicalId)} code bundle`;
|
|
24808
|
+
const { deployment } = code.s3Object;
|
|
24809
|
+
const keyOrigin = deployment ? ` (source ${deployment.index} of BucketDeployment ${displayIdent(deployment.logicalId)})` : "";
|
|
24810
|
+
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");
|
|
24811
|
+
const manifestPath = stack.assetManifestPath;
|
|
24812
|
+
if (!manifestPath) throw notFound();
|
|
24813
|
+
const manifestDir = dirname$1(manifestPath);
|
|
24814
|
+
const manifest = await loader.loadStackManifest(stack, manifestDir);
|
|
24815
|
+
const match = manifest ? findFileAssetForObjectKey(manifest, code.s3Object.key) : void 0;
|
|
24816
|
+
if (!match) throw notFound();
|
|
24817
|
+
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");
|
|
24818
|
+
const sourcePath = resolveAssetSourcePath({
|
|
24819
|
+
manifestDir,
|
|
24820
|
+
value: match.asset.source.path,
|
|
24821
|
+
assetOutdir: assetPathDirs(stack).assetOutdir,
|
|
24822
|
+
absolute: "honour-warn",
|
|
24823
|
+
field: "source.path",
|
|
24824
|
+
subject,
|
|
24825
|
+
action: "mount it",
|
|
24826
|
+
sink: "bind-mount that directory into the web function container",
|
|
24827
|
+
wrapError: (message) => new CdkLocalError(message, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_ESCAPES_ASSEMBLY")
|
|
24828
|
+
});
|
|
24829
|
+
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");
|
|
24830
|
+
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");
|
|
24831
|
+
const materialized = materializeAssetCodeDir(sourcePath);
|
|
24318
24832
|
return {
|
|
24319
|
-
|
|
24320
|
-
|
|
24833
|
+
kind: "zip",
|
|
24834
|
+
runtime: code.runtime,
|
|
24835
|
+
codeDir: materialized.dir,
|
|
24836
|
+
...materialized.tmpDir !== void 0 && { tmpDir: materialized.tmpDir },
|
|
24837
|
+
assetHash: match.hash
|
|
24321
24838
|
};
|
|
24322
24839
|
}
|
|
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) }
|
|
24840
|
+
function disposeWebFunctionArtifact(artifact) {
|
|
24841
|
+
if (artifact?.kind !== "zip" || artifact.tmpDir === void 0) return;
|
|
24842
|
+
try {
|
|
24843
|
+
if (existsSync$1(artifact.tmpDir)) rmSync$1(artifact.tmpDir, {
|
|
24844
|
+
recursive: true,
|
|
24845
|
+
force: true
|
|
24347
24846
|
});
|
|
24348
|
-
|
|
24349
|
-
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24847
|
+
} catch {}
|
|
24848
|
+
}
|
|
24849
|
+
function asObject$1(value) {
|
|
24850
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
24851
|
+
}
|
|
24852
|
+
function describeValue$1(value) {
|
|
24853
|
+
if (typeof value === "string") return displayUntrustedValue(value);
|
|
24854
|
+
let json;
|
|
24855
|
+
try {
|
|
24856
|
+
json = JSON.stringify(value);
|
|
24857
|
+
} catch {
|
|
24858
|
+
json = void 0;
|
|
24354
24859
|
}
|
|
24355
|
-
return
|
|
24356
|
-
invokers,
|
|
24357
|
-
runners
|
|
24358
|
-
};
|
|
24860
|
+
return displayUntrustedValue(json ?? String(value));
|
|
24359
24861
|
}
|
|
24360
|
-
|
|
24862
|
+
|
|
24863
|
+
//#endregion
|
|
24864
|
+
//#region src/local/web-function-runtime.ts
|
|
24865
|
+
const NODE_CODE_MOUNT = "/var/task";
|
|
24866
|
+
const NODE_ENTRYPOINT_ENV = "AWS_LAMBDA_NODEJS_ENTRYPOINT";
|
|
24867
|
+
const NODE_DEFAULT_ENTRY = "index.js";
|
|
24868
|
+
function buildNodeEntryFileStart(runtime, env, codeDir, fileExists = existsSync$1) {
|
|
24869
|
+
const configured = env[NODE_ENTRYPOINT_ENV];
|
|
24870
|
+
const entryFile = configured !== void 0 && configured.length > 0 ? configured : NODE_DEFAULT_ENTRY;
|
|
24871
|
+
const hostPath = resolve$1(codeDir, entryFile);
|
|
24872
|
+
const rel = relative$1(codeDir, hostPath);
|
|
24873
|
+
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");
|
|
24874
|
+
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");
|
|
24875
|
+
const posixRel = sep$1 === "\\" ? rel.split("\\").join("/") : rel;
|
|
24361
24876
|
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 }
|
|
24877
|
+
entryPoint: ["/var/lang/bin/node"],
|
|
24878
|
+
cmd: [posix.join(NODE_CODE_MOUNT, posixRel)],
|
|
24879
|
+
label: entryFile
|
|
24368
24880
|
};
|
|
24369
24881
|
}
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24882
|
+
const WEB_FUNCTION_RUNTIMES = { "nodejs24.x": {
|
|
24883
|
+
runtime: "nodejs24.x",
|
|
24884
|
+
baseImage: "public.ecr.aws/lambda/nodejs:24",
|
|
24885
|
+
codeMountPath: NODE_CODE_MOUNT,
|
|
24886
|
+
architectures: ["arm64"],
|
|
24887
|
+
buildStart: (env, codeDir, deps) => buildNodeEntryFileStart("nodejs24.x", env, codeDir, deps?.fileExists)
|
|
24888
|
+
} };
|
|
24889
|
+
function supportedWebFunctionRuntimes(table = WEB_FUNCTION_RUNTIMES) {
|
|
24890
|
+
return Object.keys(table).sort();
|
|
24378
24891
|
}
|
|
24379
|
-
function
|
|
24380
|
-
const
|
|
24381
|
-
|
|
24382
|
-
|
|
24892
|
+
function resolveWebFunctionRuntime(runtime, table = WEB_FUNCTION_RUNTIMES) {
|
|
24893
|
+
const spec = Object.hasOwn(table, runtime) ? table[runtime] : void 0;
|
|
24894
|
+
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");
|
|
24895
|
+
return spec;
|
|
24383
24896
|
}
|
|
24384
|
-
function
|
|
24385
|
-
|
|
24386
|
-
const flags = stateSourceFlagNames(extraStateProviders).join(" or ");
|
|
24387
|
-
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.`);
|
|
24388
|
-
}
|
|
24897
|
+
function defaultWebFunctionPlatform(spec) {
|
|
24898
|
+
return spec.architectures[0] === "x86_64" ? "linux/amd64" : "linux/arm64";
|
|
24389
24899
|
}
|
|
24390
|
-
|
|
24391
|
-
|
|
24392
|
-
|
|
24393
|
-
|
|
24394
|
-
|
|
24395
|
-
|
|
24396
|
-
|
|
24397
|
-
|
|
24398
|
-
|
|
24399
|
-
const
|
|
24400
|
-
let record;
|
|
24401
|
-
try {
|
|
24402
|
-
record = provider ? await provider.load(distribution.stackName, synthRegion) : void 0;
|
|
24403
|
-
} finally {
|
|
24404
|
-
provider?.dispose();
|
|
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}.`);
|
|
24439
|
-
}
|
|
24900
|
+
|
|
24901
|
+
//#endregion
|
|
24902
|
+
//#region src/local/web-function-runner.ts
|
|
24903
|
+
const execFileAsync$1 = promisify$1(execFile$1);
|
|
24904
|
+
const WEB_FUNCTION_CONTAINER_PORT = 8080;
|
|
24905
|
+
const READY_FAILURE_LOG_LINES = 20;
|
|
24906
|
+
const MAX_PROBE_TIMEOUT_MS = 1e4;
|
|
24907
|
+
function planWebFunctionContainer(artifact, spec, env, deps) {
|
|
24908
|
+
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");
|
|
24909
|
+
const start = spec.buildStart(env, artifact.codeDir, deps);
|
|
24440
24910
|
return {
|
|
24441
|
-
|
|
24442
|
-
|
|
24911
|
+
image: spec.baseImage,
|
|
24912
|
+
mounts: [{
|
|
24913
|
+
hostPath: artifact.codeDir,
|
|
24914
|
+
containerPath: spec.codeMountPath,
|
|
24915
|
+
readOnly: true
|
|
24916
|
+
}],
|
|
24917
|
+
entryPoint: start.entryPoint,
|
|
24918
|
+
cmd: start.cmd,
|
|
24919
|
+
workingDir: spec.codeMountPath,
|
|
24920
|
+
label: start.label
|
|
24443
24921
|
};
|
|
24444
24922
|
}
|
|
24445
|
-
function
|
|
24446
|
-
|
|
24447
|
-
|
|
24448
|
-
|
|
24449
|
-
|
|
24450
|
-
|
|
24451
|
-
|
|
24452
|
-
|
|
24453
|
-
|
|
24454
|
-
}
|
|
24455
|
-
|
|
24456
|
-
|
|
24457
|
-
|
|
24458
|
-
|
|
24459
|
-
|
|
24460
|
-
|
|
24461
|
-
roleArn: options.roleArn,
|
|
24462
|
-
region: options.region,
|
|
24463
|
-
profile: options.profile
|
|
24923
|
+
async function startWebFunctionContainer(opts) {
|
|
24924
|
+
const { plan } = opts;
|
|
24925
|
+
return runDetached({
|
|
24926
|
+
image: plan.image,
|
|
24927
|
+
mounts: plan.mounts,
|
|
24928
|
+
env: opts.env,
|
|
24929
|
+
...opts.sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys: opts.sensitiveEnvKeys },
|
|
24930
|
+
entryPoint: plan.entryPoint,
|
|
24931
|
+
cmd: plan.cmd,
|
|
24932
|
+
...plan.workingDir !== void 0 && { workingDir: plan.workingDir },
|
|
24933
|
+
hostPort: opts.hostPort,
|
|
24934
|
+
containerPort: WEB_FUNCTION_CONTAINER_PORT,
|
|
24935
|
+
host: opts.host,
|
|
24936
|
+
platform: opts.platform,
|
|
24937
|
+
name: opts.name,
|
|
24938
|
+
autoRemove: false
|
|
24464
24939
|
});
|
|
24465
|
-
|
|
24466
|
-
|
|
24467
|
-
|
|
24468
|
-
|
|
24469
|
-
|
|
24470
|
-
|
|
24471
|
-
|
|
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.`)
|
|
24486
|
-
});
|
|
24487
|
-
target = chosen;
|
|
24488
|
-
const { stack, logicalId } = resolveCloudFrontTarget(chosen, stacks);
|
|
24489
|
-
return {
|
|
24490
|
-
distribution: resolveCloudFrontDistribution({
|
|
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
|
|
24513
|
-
});
|
|
24514
|
-
server = await startCloudFrontServer({
|
|
24515
|
-
distribution: initial.distribution,
|
|
24516
|
-
host: options.host,
|
|
24517
|
-
port: basePort,
|
|
24518
|
-
...tls && { tls },
|
|
24519
|
-
...lambdaInvokers.size > 0 && { lambdaInvokers },
|
|
24520
|
-
...edgeInvokers.size > 0 && { edgeInvokers },
|
|
24521
|
-
...deployedS3.readers.size > 0 && { s3OriginReaders: deployedS3.readers }
|
|
24522
|
-
});
|
|
24523
|
-
} catch (err) {
|
|
24524
|
-
await Promise.all([...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch(() => void 0)));
|
|
24525
|
-
throw err;
|
|
24940
|
+
}
|
|
24941
|
+
var RecentOutput = class {
|
|
24942
|
+
lines = [];
|
|
24943
|
+
partial = "";
|
|
24944
|
+
max;
|
|
24945
|
+
constructor(max = 20) {
|
|
24946
|
+
this.max = max;
|
|
24526
24947
|
}
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
|
|
24530
|
-
|
|
24531
|
-
|
|
24532
|
-
|
|
24533
|
-
|
|
24534
|
-
|
|
24535
|
-
output: options.output,
|
|
24536
|
-
watchConfig: resolveWatchConfig()
|
|
24537
|
-
});
|
|
24538
|
-
watcher = createFileWatcher({
|
|
24539
|
-
paths: [watchRoot],
|
|
24540
|
-
ignored,
|
|
24541
|
-
shouldTrigger,
|
|
24542
|
-
onChange: () => {
|
|
24543
|
-
logger.info("Detected source change; reloading...");
|
|
24544
|
-
reloadChain = reloadChain.then(async () => {
|
|
24545
|
-
try {
|
|
24546
|
-
const reloaded = await synthAndResolve();
|
|
24547
|
-
annotateDeployedS3Origins(reloaded.distribution, deployedS3.buckets);
|
|
24548
|
-
for (const reader of deployedS3.readers.values()) reader.clearCache();
|
|
24549
|
-
warnUnsupported(reloaded.distribution, extraStateProviders);
|
|
24550
|
-
await attachKvsModules(reloaded.distribution, reloaded.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
24551
|
-
server.update(reloaded.distribution);
|
|
24552
|
-
logger.info("Reload complete.");
|
|
24553
|
-
} catch (err) {
|
|
24554
|
-
logger.warn(`Reload failed; keeping the previous version serving: ${err instanceof Error ? err.message : String(err)}`);
|
|
24555
|
-
}
|
|
24556
|
-
}).catch(() => void 0);
|
|
24557
|
-
}
|
|
24558
|
-
});
|
|
24559
|
-
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}).`);
|
|
24948
|
+
push(chunk) {
|
|
24949
|
+
const parts = (this.partial + chunk.toString()).split("\n");
|
|
24950
|
+
this.partial = parts.pop() ?? "";
|
|
24951
|
+
for (const line of parts) this.add(line);
|
|
24952
|
+
if (this.partial.length > 4096) {
|
|
24953
|
+
this.add(this.partial);
|
|
24954
|
+
this.partial = "";
|
|
24955
|
+
}
|
|
24560
24956
|
}
|
|
24561
|
-
|
|
24562
|
-
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
|
|
24566
|
-
if (
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
|
|
24571
|
-
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24575
|
-
await
|
|
24576
|
-
|
|
24577
|
-
|
|
24578
|
-
|
|
24579
|
-
|
|
24580
|
-
|
|
24581
|
-
|
|
24582
|
-
function
|
|
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;
|
|
24957
|
+
tail() {
|
|
24958
|
+
return this.partial.length > 0 ? [...this.lines, this.partial].slice(-this.max) : this.lines;
|
|
24959
|
+
}
|
|
24960
|
+
add(line) {
|
|
24961
|
+
this.lines.push(line.replace(/\r$/, ""));
|
|
24962
|
+
if (this.lines.length > this.max) this.lines.shift();
|
|
24963
|
+
}
|
|
24964
|
+
};
|
|
24965
|
+
async function waitForWebFunctionReady(args, deps = defaultReadyDeps) {
|
|
24966
|
+
const deadline = deps.now() + args.timeoutMs;
|
|
24967
|
+
let attempt = 0;
|
|
24968
|
+
let last = "refused";
|
|
24969
|
+
while (deps.now() < deadline) {
|
|
24970
|
+
const remaining = Math.max(1, deadline - deps.now());
|
|
24971
|
+
last = await deps.probe(args.host, args.port, Math.min(remaining, MAX_PROBE_TIMEOUT_MS));
|
|
24972
|
+
if (last === "answered") return;
|
|
24973
|
+
attempt++;
|
|
24974
|
+
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);
|
|
24975
|
+
if (last === "refused") await deps.sleep(250);
|
|
24976
|
+
}
|
|
24977
|
+
const authority = formatAuthority(args.host, args.port);
|
|
24978
|
+
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);
|
|
24595
24979
|
}
|
|
24596
|
-
function
|
|
24597
|
-
|
|
24980
|
+
async function containerFailure(message, containerId, recent, fetchLogs = readContainerLogTail, code = "LOCAL_START_WEB_FUNCTION_NOT_READY") {
|
|
24981
|
+
let lines = [];
|
|
24982
|
+
try {
|
|
24983
|
+
lines = await fetchLogs(containerId, 20);
|
|
24984
|
+
} catch {
|
|
24985
|
+
lines = [];
|
|
24986
|
+
}
|
|
24987
|
+
if (lines.length === 0) lines = recent.tail();
|
|
24988
|
+
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);
|
|
24598
24989
|
}
|
|
24599
|
-
|
|
24600
|
-
|
|
24601
|
-
|
|
24602
|
-
|
|
24603
|
-
|
|
24604
|
-
|
|
24605
|
-
|
|
24990
|
+
function watchContainerExit(containerId) {
|
|
24991
|
+
let stopped = false;
|
|
24992
|
+
const proc = spawn$1(getDockerCmd(), ["wait", containerId], { stdio: [
|
|
24993
|
+
"ignore",
|
|
24994
|
+
"pipe",
|
|
24995
|
+
"ignore"
|
|
24996
|
+
] });
|
|
24997
|
+
let out = "";
|
|
24998
|
+
proc.stdout?.on("data", (c) => out += c.toString());
|
|
24999
|
+
return {
|
|
25000
|
+
exited: new Promise((resolveExit) => {
|
|
25001
|
+
const done = () => {
|
|
25002
|
+
if (stopped) return;
|
|
25003
|
+
const code = Number.parseInt(out.trim(), 10);
|
|
25004
|
+
resolveExit(Number.isNaN(code) ? void 0 : code);
|
|
25005
|
+
};
|
|
25006
|
+
proc.on("close", done);
|
|
25007
|
+
proc.on("error", done);
|
|
25008
|
+
}),
|
|
25009
|
+
stop: () => {
|
|
25010
|
+
stopped = true;
|
|
25011
|
+
if (!proc.killed) proc.kill("SIGTERM");
|
|
25012
|
+
}
|
|
25013
|
+
};
|
|
24606
25014
|
}
|
|
24607
|
-
function
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
|
|
24611
|
-
|
|
24612
|
-
|
|
24613
|
-
|
|
24614
|
-
|
|
24615
|
-
|
|
24616
|
-
|
|
24617
|
-
|
|
24618
|
-
|
|
24619
|
-
|
|
24620
|
-
|
|
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
|
-
});
|
|
24651
|
-
return {
|
|
24652
|
-
path,
|
|
24653
|
-
close: () => new Promise((res) => {
|
|
24654
|
-
for (const closeLeg of liveCloses) closeLeg();
|
|
24655
|
-
liveCloses.clear();
|
|
24656
|
-
wss.close(() => res());
|
|
24657
|
-
})
|
|
24658
|
-
};
|
|
24659
|
-
}
|
|
24660
|
-
function startAgentCoreWsBridge(config) {
|
|
24661
|
-
const host = config.host ?? "127.0.0.1";
|
|
24662
|
-
const httpServer = createServer((_req, res) => {
|
|
24663
|
-
res.writeHead(426, { "Content-Type": "text/plain" });
|
|
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
|
-
});
|
|
24682
|
-
}
|
|
24683
|
-
|
|
24684
|
-
//#endregion
|
|
24685
|
-
//#region src/local/agentcore-http-server.ts
|
|
24686
|
-
const DEFAULT_ROUTES = [{
|
|
24687
|
-
method: "POST",
|
|
24688
|
-
path: "/invocations"
|
|
24689
|
-
}, {
|
|
24690
|
-
method: "GET",
|
|
24691
|
-
path: "/ping"
|
|
24692
|
-
}];
|
|
24693
|
-
function wireUpstreamResponse(upstream, clientRes) {
|
|
24694
|
-
upstream.on("response", (upRes) => {
|
|
24695
|
-
clientRes.writeHead(upRes.statusCode ?? 502, upRes.headers);
|
|
24696
|
-
upRes.on("error", () => clientRes.destroy());
|
|
24697
|
-
upRes.pipe(clientRes);
|
|
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
|
-
});
|
|
24704
|
-
}
|
|
24705
|
-
function proxyToContainer(clientReq, clientRes, config, upstreamPath, perRequest) {
|
|
24706
|
-
const sessionId = config.sessionId ?? randomUUID();
|
|
24707
|
-
const headers = { ...clientReq.headers };
|
|
24708
|
-
delete headers["host"];
|
|
24709
|
-
headers[AGENTCORE_SESSION_ID_HEADER] = sessionId;
|
|
24710
|
-
const authorization = perRequest?.authorization ?? config.authorization;
|
|
24711
|
-
if (authorization) headers["authorization"] = authorization;
|
|
24712
|
-
if (config.signRequest && clientReq.method === "POST") {
|
|
24713
|
-
const signRequest = config.signRequest;
|
|
24714
|
-
const chunks = [];
|
|
24715
|
-
clientReq.on("data", (c) => chunks.push(c));
|
|
24716
|
-
clientReq.on("error", (err) => {
|
|
24717
|
-
getLogger().debug(`agentcore http serve client error: ${err.message}`);
|
|
24718
|
-
if (!clientRes.headersSent) {
|
|
24719
|
-
clientRes.writeHead(400, { "content-type": "application/json" });
|
|
24720
|
-
clientRes.end(JSON.stringify({ error: `client error: ${err.message}` }));
|
|
24721
|
-
}
|
|
24722
|
-
});
|
|
24723
|
-
clientReq.on("end", () => {
|
|
24724
|
-
const body = Buffer.concat(chunks);
|
|
24725
|
-
signRequest({
|
|
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
|
-
});
|
|
24754
|
-
});
|
|
24755
|
-
return;
|
|
24756
|
-
}
|
|
24757
|
-
const upstream = request({
|
|
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);
|
|
24770
|
-
}
|
|
24771
|
-
function startAgentCoreHttpServer(config) {
|
|
24772
|
-
const host = config.host ?? "127.0.0.1";
|
|
24773
|
-
const routes = config.routes ?? DEFAULT_ROUTES;
|
|
24774
|
-
const attachWs = config.attachWs ?? true;
|
|
24775
|
-
const notFoundHint = buildNotFoundHint(routes, attachWs);
|
|
24776
|
-
const httpServer = createServer((req, res) => {
|
|
24777
|
-
const path = (req.url ?? "/").split("?")[0];
|
|
24778
|
-
const match = routes.find((r) => r.method === req.method && r.path === path);
|
|
24779
|
-
if (!match) {
|
|
24780
|
-
res.writeHead(404, { "content-type": "application/json" });
|
|
24781
|
-
res.end(JSON.stringify({
|
|
24782
|
-
error: "not found",
|
|
24783
|
-
hint: notFoundHint
|
|
24784
|
-
}));
|
|
24785
|
-
return;
|
|
24786
|
-
}
|
|
24787
|
-
if (config.authCheck && req.method === "POST") {
|
|
24788
|
-
req.on("error", (err) => getLogger().debug(`agentcore http serve client error (auth gate): ${err.message}`));
|
|
24789
|
-
config.authCheck(req.headers).then((result) => {
|
|
24790
|
-
if (!result.allow) {
|
|
24791
|
-
req.resume();
|
|
24792
|
-
res.writeHead(result.status ?? 403, { "content-type": "application/json" });
|
|
24793
|
-
res.end(JSON.stringify({ error: result.message ?? "forbidden" }));
|
|
24794
|
-
return;
|
|
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;
|
|
24807
|
-
}
|
|
24808
|
-
proxyToContainer(req, res, config, match.path);
|
|
24809
|
-
});
|
|
24810
|
-
const bridge = attachWs ? attachAgentCoreWsBridge(httpServer, {
|
|
24811
|
-
containerHost: config.containerHost,
|
|
24812
|
-
containerPort: () => config.containerPort,
|
|
24813
|
-
...config.sessionId && { sessionId: config.sessionId },
|
|
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
|
-
});
|
|
24835
|
-
});
|
|
24836
|
-
}
|
|
24837
|
-
function buildNotFoundHint(routes, attachWs) {
|
|
24838
|
-
const base = routes.map((r) => `${r.method} ${r.path}`).join(" or ");
|
|
24839
|
-
return attachWs ? `${base} (WebSocket: connect to /ws)` : base;
|
|
24840
|
-
}
|
|
24841
|
-
|
|
24842
|
-
//#endregion
|
|
24843
|
-
//#region src/local/agentcore-serve-auth.ts
|
|
24844
|
-
function buildAgentCoreServeAuthCheck(authorizer, opts = {}) {
|
|
24845
|
-
const injectedBearer = opts.bearerToken ? `Bearer ${opts.bearerToken}` : void 0;
|
|
24846
|
-
if (opts.noVerifyAuth === true) return async () => ({
|
|
24847
|
-
allow: true,
|
|
24848
|
-
...injectedBearer && { authorization: injectedBearer }
|
|
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
|
-
};
|
|
24870
|
-
return {
|
|
24871
|
-
allow: true,
|
|
24872
|
-
authorization: header
|
|
24873
|
-
};
|
|
24874
|
-
};
|
|
24875
|
-
}
|
|
24876
|
-
function selectServeInboundAuth(resolved, options, sigv4Active, buildAuthCheck = buildAgentCoreServeAuthCheck) {
|
|
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 };
|
|
24887
|
-
return {
|
|
24888
|
-
...bearerHeader && { bridgeAuthorization: bearerHeader },
|
|
24889
|
-
sign: false
|
|
24890
|
-
};
|
|
24891
|
-
}
|
|
24892
|
-
function readAuthorizationHeader(headers) {
|
|
24893
|
-
const raw = headers["authorization"];
|
|
24894
|
-
const v = Array.isArray(raw) ? raw[0] : raw;
|
|
24895
|
-
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
24896
|
-
}
|
|
24897
|
-
|
|
24898
|
-
//#endregion
|
|
24899
|
-
//#region src/cli/commands/local-start-agentcore.ts
|
|
24900
|
-
function parsePort$2(raw) {
|
|
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;
|
|
24904
|
-
}
|
|
24905
|
-
function resolveAgentCoreServePlan(protocol) {
|
|
24906
|
-
if (protocol === "MCP") return {
|
|
24907
|
-
containerPort: MCP_CONTAINER_PORT,
|
|
24908
|
-
containerPortLabel: `${MCP_CONTAINER_PORT}${MCP_PATH}`,
|
|
24909
|
-
routes: [{
|
|
24910
|
-
method: "POST",
|
|
24911
|
-
path: MCP_PATH
|
|
24912
|
-
}],
|
|
24913
|
-
attachWs: false,
|
|
24914
|
-
readyPath: MCP_PATH
|
|
24915
|
-
};
|
|
24916
|
-
if (protocol === "A2A") return {
|
|
24917
|
-
containerPort: A2A_CONTAINER_PORT,
|
|
24918
|
-
containerPortLabel: `${A2A_CONTAINER_PORT}${"/"}`,
|
|
24919
|
-
routes: [{
|
|
24920
|
-
method: "POST",
|
|
24921
|
-
path: "/"
|
|
24922
|
-
}],
|
|
24923
|
-
attachWs: false,
|
|
24924
|
-
readyPath: "/"
|
|
24925
|
-
};
|
|
24926
|
-
return {
|
|
24927
|
-
containerPort: void 0,
|
|
24928
|
-
containerPortLabel: "8080",
|
|
24929
|
-
routes: [{
|
|
24930
|
-
method: "POST",
|
|
24931
|
-
path: "/invocations"
|
|
24932
|
-
}, {
|
|
24933
|
-
method: "GET",
|
|
24934
|
-
path: "/ping"
|
|
24935
|
-
}],
|
|
24936
|
-
attachWs: true,
|
|
24937
|
-
readyPath: void 0
|
|
24938
|
-
};
|
|
24939
|
-
}
|
|
24940
|
-
async function localStartAgentCoreCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
24941
|
-
const logger = getLogger();
|
|
24942
|
-
if (options.verbose) logger.setLevel("debug");
|
|
24943
|
-
let containerId;
|
|
24944
|
-
let stopLogs;
|
|
24945
|
-
let server;
|
|
24946
|
-
let profileCredsFile;
|
|
24947
|
-
let stateProvider;
|
|
24948
|
-
let watcher;
|
|
24949
|
-
let reloadChain = Promise.resolve();
|
|
24950
|
-
let shuttingDown = false;
|
|
24951
|
-
let tornDown = false;
|
|
24952
|
-
const teardown = async () => {
|
|
24953
|
-
if (tornDown) return;
|
|
24954
|
-
tornDown = true;
|
|
24955
|
-
if (watcher) try {
|
|
24956
|
-
await watcher.close();
|
|
24957
|
-
} catch (err) {
|
|
24958
|
-
logger.debug(`watcher close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24959
|
-
}
|
|
24960
|
-
try {
|
|
24961
|
-
await reloadChain;
|
|
24962
|
-
} catch {}
|
|
24963
|
-
if (server) try {
|
|
24964
|
-
await server.close();
|
|
24965
|
-
} catch (err) {
|
|
24966
|
-
logger.debug(`server close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24967
|
-
}
|
|
24968
|
-
if (stopLogs) try {
|
|
24969
|
-
stopLogs();
|
|
24970
|
-
} catch (err) {
|
|
24971
|
-
logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24972
|
-
}
|
|
24973
|
-
if (containerId) try {
|
|
24974
|
-
await removeContainer(containerId);
|
|
24975
|
-
} catch (err) {
|
|
24976
|
-
logger.debug(`removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24977
|
-
}
|
|
24978
|
-
if (stateProvider) try {
|
|
24979
|
-
stateProvider.dispose();
|
|
24980
|
-
} catch {}
|
|
24981
|
-
if (profileCredsFile) try {
|
|
24982
|
-
await profileCredsFile.dispose();
|
|
24983
|
-
} catch {}
|
|
24984
|
-
};
|
|
24985
|
-
await applyRoleArnIfSet({
|
|
24986
|
-
roleArn: options.roleArn,
|
|
24987
|
-
region: options.region,
|
|
24988
|
-
profile: options.profile
|
|
24989
|
-
});
|
|
24990
|
-
await ensureDockerAvailable();
|
|
24991
|
-
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
24992
|
-
if (options.profile && profileCredentials) profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);
|
|
24993
|
-
const appCmd = resolveApp(options.app);
|
|
24994
|
-
if (!appCmd) throw new Error(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
|
|
24995
|
-
logger.info("Synthesizing CDK app...");
|
|
24996
|
-
const synthesizer = hostSynthesizer ?? new Synthesizer();
|
|
24997
|
-
const context = parseContextOptions(options.context);
|
|
24998
|
-
const synthOpts = {
|
|
24999
|
-
app: appCmd,
|
|
25000
|
-
output: options.output,
|
|
25001
|
-
...options.region && { region: options.region },
|
|
25002
|
-
...options.profile && { profile: options.profile },
|
|
25003
|
-
...Object.keys(context).length > 0 && { context }
|
|
25004
|
-
};
|
|
25005
|
-
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
25006
|
-
const resolvedTarget = await resolveSingleTarget(target, {
|
|
25007
|
-
entries: listTargets(stacks).agentCoreRuntimes,
|
|
25008
|
-
message: "Select an AgentCore Runtime to serve",
|
|
25009
|
-
noun: "AgentCore Runtimes",
|
|
25010
|
-
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")
|
|
25011
|
-
});
|
|
25012
|
-
const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
25013
|
-
stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
|
|
25014
|
-
const { context: imageContext, loaded: loadedState } = stateProvider && candidate ? await buildAgentCoreImageContext(candidate, stateProvider, options) : {
|
|
25015
|
-
context: void 0,
|
|
25016
|
-
loaded: void 0
|
|
25017
|
-
};
|
|
25018
|
-
const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);
|
|
25019
|
-
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.logicalId}`)} (${resolved.protocol})`);
|
|
25020
|
-
const plan = resolveAgentCoreServePlan(resolved.protocol);
|
|
25021
|
-
const sigv4Context = await resolveAgentCoreSigV4Context(options, resolved, loadedState, stateProvider);
|
|
25022
|
-
const authPlan = selectServeInboundAuth(resolved, options, sigv4Context !== void 0);
|
|
25023
|
-
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)" : ""}.`);
|
|
25024
|
-
await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);
|
|
25025
|
-
const image = await resolveAgentCoreImage(resolved, options, loadedState, stateProvider);
|
|
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);
|
|
25050
|
-
};
|
|
25051
|
-
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
25052
|
-
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
25053
|
-
try {
|
|
25054
|
-
if (plan.readyPath === void 0) await waitForAgentCorePing(containerHost, containerHostPort, options.timeout);
|
|
25055
|
-
else await waitForAgentCoreHttpReady(containerHost, containerHostPort, plan.readyPath, options.timeout);
|
|
25056
|
-
const signRequest = authPlan.sign && sigv4Context ? async ({ method, path, body, sessionId }) => {
|
|
25057
|
-
const signed = await signAgentCoreInvocation({
|
|
25058
|
-
credentials: sigv4Context.credentials,
|
|
25059
|
-
region: sigv4Context.region,
|
|
25060
|
-
host: containerHost,
|
|
25061
|
-
port: containerHostPort,
|
|
25062
|
-
path,
|
|
25063
|
-
body,
|
|
25064
|
-
sessionId,
|
|
25065
|
-
method
|
|
25066
|
-
});
|
|
25067
|
-
const h = {
|
|
25068
|
-
Authorization: signed.authorization,
|
|
25069
|
-
"X-Amz-Date": signed.amzDate,
|
|
25070
|
-
"X-Amz-Content-Sha256": signed.amzContentSha256
|
|
25071
|
-
};
|
|
25072
|
-
if (signed.amzSecurityToken) h["X-Amz-Security-Token"] = signed.amzSecurityToken;
|
|
25073
|
-
return h;
|
|
25074
|
-
} : void 0;
|
|
25075
|
-
server = await startAgentCoreHttpServer({
|
|
25076
|
-
containerHost,
|
|
25077
|
-
containerPort: containerHostPort,
|
|
25078
|
-
host: options.host,
|
|
25079
|
-
port: options.port,
|
|
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 }
|
|
25086
|
-
});
|
|
25087
|
-
} catch (err) {
|
|
25088
|
-
await teardown();
|
|
25089
|
-
throw err;
|
|
25090
|
-
}
|
|
25091
|
-
if (server.wsUrl) {
|
|
25092
|
-
logger.info(`Server listening on ${server.wsUrl} (${displayIdent(resolved.logicalId)} (AgentCore WebSocket))`);
|
|
25093
|
-
logger.info(`HTTP contract served on ${server.httpUrl} — POST ${server.httpUrl}/invocations, GET ${server.httpUrl}/ping`);
|
|
25094
|
-
} else {
|
|
25095
|
-
const contractPath = plan.routes[0]?.path ?? "/";
|
|
25096
|
-
const contractUrl = `${server.httpUrl}${contractPath}`;
|
|
25097
|
-
logger.info(`Server listening on ${server.httpUrl} (${displayIdent(resolved.logicalId)})`);
|
|
25098
|
-
logger.info(`${resolved.protocol} contract served on ${contractUrl} — POST ${contractUrl}`);
|
|
25099
|
-
}
|
|
25100
|
-
logger.info("Press ^C to shut down.");
|
|
25101
|
-
if (options.watch) {
|
|
25102
|
-
const cdkOutDir = options.output;
|
|
25103
|
-
const assetLoader = new AssetManifestLoader();
|
|
25104
|
-
let currentAssetHash = await deriveOldAssetHash({
|
|
25105
|
-
resolvedTarget,
|
|
25106
|
-
resolved,
|
|
25107
|
-
stacks,
|
|
25108
|
-
cdkOutDir,
|
|
25109
|
-
assetLoader
|
|
25015
|
+
function probeHttp(host, port, timeoutMs) {
|
|
25016
|
+
return new Promise((resolveProbe) => {
|
|
25017
|
+
let timedOut = false;
|
|
25018
|
+
let connected = false;
|
|
25019
|
+
const req = request$2({
|
|
25020
|
+
host,
|
|
25021
|
+
port,
|
|
25022
|
+
path: "/",
|
|
25023
|
+
method: "GET",
|
|
25024
|
+
timeout: timeoutMs,
|
|
25025
|
+
headers: { connection: "close" }
|
|
25026
|
+
}, (res) => {
|
|
25027
|
+
res.resume();
|
|
25028
|
+
resolveProbe("answered");
|
|
25110
25029
|
});
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
|
|
25115
|
-
const watchRoot = process.cwd();
|
|
25116
|
-
const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
|
|
25117
|
-
watchRoot,
|
|
25118
|
-
output: options.output,
|
|
25119
|
-
watchConfig: resolveWatchConfig()
|
|
25030
|
+
req.on("socket", (socket) => {
|
|
25031
|
+
socket.once("connect", () => {
|
|
25032
|
+
connected = true;
|
|
25033
|
+
});
|
|
25120
25034
|
});
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25124
|
-
ignored,
|
|
25125
|
-
shouldTrigger,
|
|
25126
|
-
onChange: (changedPaths) => {
|
|
25127
|
-
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
|
-
try {
|
|
25163
|
-
if (verdict.kind === "soft-reload") {
|
|
25164
|
-
if (!containerId) throw new CdkLocalError("Reload: no live container to docker cp / docker restart into.", "LOCAL_START_AGENTCORE_WATCH_NO_CONTAINER");
|
|
25165
|
-
await softReloadAgentContainer(containerId, verdict.newAssetSourceDir);
|
|
25166
|
-
await waitReady(containerHostPort);
|
|
25167
|
-
logger.info("Reload: soft-reloaded the agent container (docker cp + docker restart; container ID + host port preserved).");
|
|
25168
|
-
} else {
|
|
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;
|
|
25203
|
-
logger.info("Reload complete.");
|
|
25204
|
-
} catch (err) {
|
|
25205
|
-
logger.error(`Reload failed: ${err instanceof Error ? err.message : String(err)}. The serve stays up; edit + save again to retry.`);
|
|
25206
|
-
}
|
|
25207
|
-
});
|
|
25208
|
-
}
|
|
25035
|
+
req.on("timeout", () => {
|
|
25036
|
+
timedOut = true;
|
|
25037
|
+
req.destroy();
|
|
25209
25038
|
});
|
|
25210
|
-
|
|
25211
|
-
|
|
25039
|
+
req.on("error", () => resolveProbe(timedOut && connected ? "timeout" : "refused"));
|
|
25040
|
+
req.end();
|
|
25041
|
+
});
|
|
25212
25042
|
}
|
|
25213
|
-
function
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
25218
|
-
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
return cmd;
|
|
25043
|
+
async function containerIsRunning(containerId) {
|
|
25044
|
+
try {
|
|
25045
|
+
const { stdout } = await execFileAsync$1(getDockerCmd(), [
|
|
25046
|
+
"inspect",
|
|
25047
|
+
"-f",
|
|
25048
|
+
"{{.State.Running}}",
|
|
25049
|
+
containerId
|
|
25050
|
+
]);
|
|
25051
|
+
return stdout.trim() === "true";
|
|
25052
|
+
} catch {
|
|
25053
|
+
return false;
|
|
25054
|
+
}
|
|
25226
25055
|
}
|
|
25227
|
-
function
|
|
25228
|
-
return
|
|
25056
|
+
function readContainerLogTail(containerId, lines, spawnFn = spawn$1) {
|
|
25057
|
+
return new Promise((resolveLogs, rejectLogs) => {
|
|
25058
|
+
const proc = spawnFn(getDockerCmd(), [
|
|
25059
|
+
"logs",
|
|
25060
|
+
"--tail",
|
|
25061
|
+
String(lines),
|
|
25062
|
+
containerId
|
|
25063
|
+
], { stdio: [
|
|
25064
|
+
"ignore",
|
|
25065
|
+
"pipe",
|
|
25066
|
+
"pipe"
|
|
25067
|
+
] });
|
|
25068
|
+
const chunks = [];
|
|
25069
|
+
let size = 0;
|
|
25070
|
+
const take = (c) => {
|
|
25071
|
+
if (size > 4 * 1024 * 1024) return;
|
|
25072
|
+
chunks.push(c);
|
|
25073
|
+
size += c.length;
|
|
25074
|
+
};
|
|
25075
|
+
proc.stdout?.on("data", take);
|
|
25076
|
+
proc.stderr?.on("data", take);
|
|
25077
|
+
proc.on("error", rejectLogs);
|
|
25078
|
+
proc.on("close", (code) => {
|
|
25079
|
+
if (code !== 0) {
|
|
25080
|
+
rejectLogs(/* @__PURE__ */ new Error(`docker logs exited ${code}`));
|
|
25081
|
+
return;
|
|
25082
|
+
}
|
|
25083
|
+
resolveLogs(Buffer.concat(chunks).toString().split("\n").map((l) => l.replace(/\r$/, "")).filter((l) => l.length > 0).slice(-lines));
|
|
25084
|
+
});
|
|
25085
|
+
});
|
|
25229
25086
|
}
|
|
25087
|
+
const defaultReadyDeps = {
|
|
25088
|
+
probe: probeHttp,
|
|
25089
|
+
isRunning: containerIsRunning,
|
|
25090
|
+
fetchLogs: readContainerLogTail,
|
|
25091
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
25092
|
+
now: () => Date.now()
|
|
25093
|
+
};
|
|
25230
25094
|
|
|
25231
25095
|
//#endregion
|
|
25232
|
-
//#region src/
|
|
25233
|
-
|
|
25234
|
-
const
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25096
|
+
//#region src/cli/commands/web-function-serve.ts
|
|
25097
|
+
function prepareWebFunctionServe(resolved) {
|
|
25098
|
+
const code = parseWebFunctionBuildConfig(resolved.revisionLogicalId, resolved.revision.Properties?.["BuildConfig"], resolved.stack.template);
|
|
25099
|
+
return {
|
|
25100
|
+
resolved,
|
|
25101
|
+
code,
|
|
25102
|
+
spec: resolveWebFunctionRuntime(code.runtime),
|
|
25103
|
+
serviceConfig: readWebFunctionServiceConfig(resolved.revision)
|
|
25104
|
+
};
|
|
25105
|
+
}
|
|
25106
|
+
function webFunctionConnectHost(host) {
|
|
25107
|
+
if (host === "0.0.0.0") return "127.0.0.1";
|
|
25108
|
+
if (host === "::" || host === "[::]") return "::1";
|
|
25109
|
+
return host;
|
|
25110
|
+
}
|
|
25111
|
+
var WebFunctionServe = class {
|
|
25112
|
+
artifact;
|
|
25113
|
+
artifactInFlight;
|
|
25114
|
+
runInFlight;
|
|
25115
|
+
id;
|
|
25116
|
+
name;
|
|
25117
|
+
stopLogs;
|
|
25118
|
+
exitWatch;
|
|
25119
|
+
teardownPromise;
|
|
25120
|
+
waitingOnRunFlag = false;
|
|
25121
|
+
recent = new RecentOutput();
|
|
25122
|
+
prepared;
|
|
25123
|
+
options;
|
|
25124
|
+
constructor(prepared, options) {
|
|
25125
|
+
this.prepared = prepared;
|
|
25126
|
+
this.options = options;
|
|
25246
25127
|
}
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
|
|
25128
|
+
get waitingOnRun() {
|
|
25129
|
+
return this.waitingOnRunFlag;
|
|
25130
|
+
}
|
|
25131
|
+
get containerName() {
|
|
25132
|
+
return this.name;
|
|
25133
|
+
}
|
|
25134
|
+
get tornDown() {
|
|
25135
|
+
return this.teardownPromise !== void 0;
|
|
25136
|
+
}
|
|
25137
|
+
get exited() {
|
|
25138
|
+
return this.exitWatch?.exited;
|
|
25139
|
+
}
|
|
25140
|
+
async start() {
|
|
25141
|
+
const logger = getLogger();
|
|
25142
|
+
const { resolved, code, spec, serviceConfig } = this.prepared;
|
|
25143
|
+
const opts = this.options;
|
|
25144
|
+
this.artifactInFlight = resolveWebFunctionArtifact(resolved.stack, resolved.revisionLogicalId, code);
|
|
25145
|
+
this.artifact = await this.artifactInFlight;
|
|
25146
|
+
if (this.tornDown) return void 0;
|
|
25147
|
+
const assumeRole = webFunctionAssumeRole(opts.envOptions.assumeRole, serviceConfig);
|
|
25148
|
+
const containerEnv = await resolveLambdaContainerEnv(buildWebFunctionLambdaView(resolved, serviceConfig, spec.runtime), {
|
|
25149
|
+
...opts.envOptions,
|
|
25150
|
+
...assumeRole !== void 0 && { assumeRole },
|
|
25151
|
+
deployedFunctionLookups: false
|
|
25152
|
+
}, opts.profileCredentials, opts.extraStateProviders);
|
|
25153
|
+
const { env, replacedHttpEndpoint } = finalizeWebFunctionEnv(containerEnv.env, {
|
|
25154
|
+
functionName: resolved.functionName,
|
|
25155
|
+
containerPort: WEB_FUNCTION_CONTAINER_PORT,
|
|
25156
|
+
timeoutSeconds: serviceConfig.timeoutSeconds,
|
|
25157
|
+
logGroup: serviceConfig.logGroup
|
|
25158
|
+
});
|
|
25159
|
+
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.`);
|
|
25160
|
+
const plan = planWebFunctionContainer(this.artifact, spec, env);
|
|
25161
|
+
const platform = opts.platform ?? defaultWebFunctionPlatform(spec);
|
|
25162
|
+
await pullImage(plan.image, opts.skipPull, platform);
|
|
25163
|
+
if (this.tornDown) return void 0;
|
|
25164
|
+
const hostPort = opts.port === 0 ? await pickFreePort() : opts.port;
|
|
25165
|
+
if (this.tornDown) return void 0;
|
|
25166
|
+
const name = this.name = `${getEmbedConfig().resourceNamePrefix}-webfn-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
25167
|
+
logger.info(`Starting web function container (image=${plan.image}, start=${plan.label}, port=${hostPort} -> ${WEB_FUNCTION_CONTAINER_PORT})...`);
|
|
25168
|
+
this.runInFlight = startWebFunctionContainer({
|
|
25169
|
+
plan,
|
|
25170
|
+
env,
|
|
25171
|
+
sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys),
|
|
25172
|
+
host: opts.host,
|
|
25173
|
+
hostPort,
|
|
25174
|
+
platform,
|
|
25175
|
+
name
|
|
25176
|
+
});
|
|
25177
|
+
const id = await this.runInFlight;
|
|
25178
|
+
if (this.tornDown) return void 0;
|
|
25179
|
+
this.id = id;
|
|
25180
|
+
this.stopLogs = streamLogs(id, { onOutput: (chunk) => this.recent.push(chunk) });
|
|
25181
|
+
this.exitWatch = watchContainerExit(id);
|
|
25182
|
+
const host = webFunctionConnectHost(opts.host);
|
|
25183
|
+
await waitForWebFunctionReady({
|
|
25184
|
+
containerId: id,
|
|
25185
|
+
host,
|
|
25186
|
+
port: hostPort,
|
|
25187
|
+
timeoutMs: opts.timeoutMs,
|
|
25188
|
+
recent: this.recent,
|
|
25189
|
+
label: plan.label
|
|
25190
|
+
});
|
|
25191
|
+
if (this.tornDown) return void 0;
|
|
25192
|
+
return {
|
|
25193
|
+
host,
|
|
25194
|
+
port: hostPort
|
|
25195
|
+
};
|
|
25278
25196
|
}
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
if (revisionLogicalId !== void 0) selection = "endpoint-weight";
|
|
25197
|
+
exitFailure(message) {
|
|
25198
|
+
return containerFailure(message, this.id ?? "", this.recent, void 0, "LOCAL_START_WEB_FUNCTION_EXITED");
|
|
25282
25199
|
}
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25200
|
+
disposeArtifactNow() {
|
|
25201
|
+
disposeWebFunctionArtifact(this.artifact);
|
|
25202
|
+
}
|
|
25203
|
+
teardown() {
|
|
25204
|
+
return this.teardownPromise ??= this.doTeardown();
|
|
25205
|
+
}
|
|
25206
|
+
async doTeardown() {
|
|
25207
|
+
const logger = getLogger();
|
|
25208
|
+
this.exitWatch?.stop();
|
|
25209
|
+
if (this.artifact === void 0 && this.artifactInFlight !== void 0) try {
|
|
25210
|
+
this.artifact = await this.artifactInFlight;
|
|
25211
|
+
} catch {}
|
|
25212
|
+
if (this.id === void 0 && this.runInFlight !== void 0) {
|
|
25213
|
+
this.waitingOnRunFlag = true;
|
|
25214
|
+
try {
|
|
25215
|
+
this.id = await this.runInFlight;
|
|
25216
|
+
} catch {} finally {
|
|
25217
|
+
this.waitingOnRunFlag = false;
|
|
25294
25218
|
}
|
|
25295
25219
|
}
|
|
25296
|
-
if (
|
|
25220
|
+
if (this.stopLogs) try {
|
|
25221
|
+
this.stopLogs();
|
|
25222
|
+
} catch (err) {
|
|
25223
|
+
logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25224
|
+
}
|
|
25225
|
+
if (this.id) await removeContainer(this.id);
|
|
25226
|
+
disposeWebFunctionArtifact(this.artifact);
|
|
25297
25227
|
}
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25228
|
+
};
|
|
25229
|
+
|
|
25230
|
+
//#endregion
|
|
25231
|
+
//#region src/cli/commands/local-start-cloudfront.ts
|
|
25232
|
+
var LocalStartCloudFrontError = class extends CdkLocalError {
|
|
25233
|
+
constructor(message, cause) {
|
|
25234
|
+
super(message, "LOCAL_START_CLOUDFRONT_ERROR", cause);
|
|
25301
25235
|
}
|
|
25302
|
-
|
|
25236
|
+
};
|
|
25237
|
+
function parseOriginOverrides(values) {
|
|
25238
|
+
const out = /* @__PURE__ */ new Map();
|
|
25239
|
+
for (const raw of values ?? []) {
|
|
25240
|
+
const eq = raw.indexOf("=");
|
|
25241
|
+
if (eq <= 0 || eq === raw.length - 1) throw new LocalStartCloudFrontError(`Invalid --origin '${raw}'. Expected <originId>=<dir> (e.g. MyOrigin=./site).`);
|
|
25242
|
+
out.set(raw.slice(0, eq).trim(), raw.slice(eq + 1).trim());
|
|
25243
|
+
}
|
|
25244
|
+
return out;
|
|
25245
|
+
}
|
|
25246
|
+
function parseKvsFileOverrides(values) {
|
|
25247
|
+
const out = /* @__PURE__ */ new Map();
|
|
25248
|
+
for (const raw of values ?? []) {
|
|
25249
|
+
const eq = raw.indexOf("=");
|
|
25250
|
+
if (eq <= 0 || eq === raw.length - 1) throw new LocalStartCloudFrontError(`Invalid --kvs-file '${raw}'. Expected <key>=<file.json> (e.g. RoutesKvs=./kvs.json).`);
|
|
25251
|
+
out.set(raw.slice(0, eq).trim(), raw.slice(eq + 1).trim());
|
|
25252
|
+
}
|
|
25253
|
+
return out;
|
|
25254
|
+
}
|
|
25255
|
+
function collectKvsStoreIdentities(template) {
|
|
25256
|
+
const out = [];
|
|
25257
|
+
for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {
|
|
25258
|
+
if (resource.Type !== "AWS::CloudFront::KeyValueStore") continue;
|
|
25259
|
+
const cdkPath = readCdkPath(resource) || void 0;
|
|
25260
|
+
const constructPath = cdkPath?.endsWith("/Resource") ? cdkPath.slice(0, -9) : cdkPath;
|
|
25261
|
+
const constructId = constructPath?.split("/").pop();
|
|
25262
|
+
out.push({
|
|
25263
|
+
logicalId,
|
|
25264
|
+
...cdkPath !== void 0 && { cdkPath },
|
|
25265
|
+
...constructPath !== void 0 && { constructPath },
|
|
25266
|
+
...constructId !== void 0 && constructId !== "" && { constructId }
|
|
25267
|
+
});
|
|
25268
|
+
}
|
|
25269
|
+
return out;
|
|
25270
|
+
}
|
|
25271
|
+
function normalizeKvsFileKeys(kvsFiles, template) {
|
|
25272
|
+
if (kvsFiles.size === 0) return kvsFiles;
|
|
25273
|
+
const stores = collectKvsStoreIdentities(template);
|
|
25274
|
+
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)";
|
|
25275
|
+
const out = /* @__PURE__ */ new Map();
|
|
25276
|
+
for (const [key, filePath] of kvsFiles) {
|
|
25277
|
+
const matched = [
|
|
25278
|
+
stores.filter((s) => s.logicalId === key),
|
|
25279
|
+
stores.filter((s) => s.constructPath === key || s.cdkPath === key),
|
|
25280
|
+
stores.filter((s) => s.constructId === key)
|
|
25281
|
+
].find((t) => t.length > 0) ?? [];
|
|
25282
|
+
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()}.`);
|
|
25283
|
+
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.`);
|
|
25284
|
+
out.set(matched[0].logicalId, filePath);
|
|
25285
|
+
}
|
|
25286
|
+
return out;
|
|
25287
|
+
}
|
|
25288
|
+
async function attachKvsModules(distribution, stacks, options, profileCredentials, logger, extraStateProviders) {
|
|
25289
|
+
const stack = stacks.find((s) => s.stackName === distribution.stackName);
|
|
25290
|
+
const synthRegion = stack?.region;
|
|
25291
|
+
const kvsFiles = stack ? normalizeKvsFileKeys(parseKvsFileOverrides(options.kvsFile), stack.template) : parseKvsFileOverrides(options.kvsFile);
|
|
25292
|
+
const resolveDeployedKvs = isStateSourceActive(options, extraStateProviders) ? async (kvsLogicalId) => {
|
|
25293
|
+
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
|
|
25294
|
+
if (!provider) return void 0;
|
|
25295
|
+
let record;
|
|
25296
|
+
try {
|
|
25297
|
+
record = await provider.load(distribution.stackName, synthRegion);
|
|
25298
|
+
} finally {
|
|
25299
|
+
provider.dispose();
|
|
25300
|
+
}
|
|
25301
|
+
const physicalId = record?.resources[kvsLogicalId]?.physicalId;
|
|
25302
|
+
if (!physicalId) return void 0;
|
|
25303
|
+
if (physicalId.startsWith("arn:")) {
|
|
25304
|
+
const id = idFromArn(physicalId);
|
|
25305
|
+
return {
|
|
25306
|
+
arn: physicalId,
|
|
25307
|
+
...id !== void 0 && { id }
|
|
25308
|
+
};
|
|
25309
|
+
}
|
|
25310
|
+
return resolveDeployedKvsArnByName(physicalId, { ...profileCredentials !== void 0 && { credentials: profileCredentials } });
|
|
25311
|
+
} : void 0;
|
|
25312
|
+
const { warnings } = await resolveKvsModulesForDistribution(distribution, {
|
|
25313
|
+
...kvsFiles.size > 0 && { kvsFiles },
|
|
25314
|
+
...resolveDeployedKvs !== void 0 && { resolveDeployedKvs },
|
|
25315
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
25316
|
+
});
|
|
25317
|
+
for (const warning of warnings) logger.warn(warning);
|
|
25318
|
+
}
|
|
25319
|
+
function resolveCloudFrontTarget(target, stacks) {
|
|
25320
|
+
if (stacks.length === 0) throw new LocalStartCloudFrontError("No stacks found in the synthesized assembly.");
|
|
25321
|
+
const parsed = parseEcsTarget(target);
|
|
25322
|
+
const stack = pickStack(parsed, stacks, target);
|
|
25323
|
+
const resources = stack.template.Resources ?? {};
|
|
25324
|
+
if (parsed.isPath) {
|
|
25325
|
+
const index = buildCdkPathIndex(stack.template);
|
|
25326
|
+
const dists = resolveCdkPathToLogicalIds(parsed.pathOrId, index).filter(({ logicalId }) => {
|
|
25327
|
+
const r = resources[logicalId];
|
|
25328
|
+
return r !== void 0 && r.Type === "AWS::CloudFront::Distribution";
|
|
25329
|
+
});
|
|
25330
|
+
if (dists.length === 0) throw notFound(target, stack, resources);
|
|
25331
|
+
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.`);
|
|
25332
|
+
return {
|
|
25333
|
+
stack,
|
|
25334
|
+
logicalId: dists[0].logicalId
|
|
25335
|
+
};
|
|
25336
|
+
}
|
|
25337
|
+
const res = resources[parsed.pathOrId];
|
|
25338
|
+
if (!res || res.Type !== "AWS::CloudFront::Distribution") throw notFound(target, stack, resources);
|
|
25303
25339
|
return {
|
|
25304
25340
|
stack,
|
|
25305
|
-
|
|
25306
|
-
functionName: typeof fnName === "string" && fnName.length > 0 ? fnName : functionLogicalId ?? revisionLogicalId,
|
|
25307
|
-
revisionLogicalId,
|
|
25308
|
-
revision: resources[revisionLogicalId],
|
|
25309
|
-
endpoint,
|
|
25310
|
-
selection: selection ?? "only-revision"
|
|
25341
|
+
logicalId: parsed.pathOrId
|
|
25311
25342
|
};
|
|
25312
25343
|
}
|
|
25313
25344
|
function pickStack(parsed, stacks, target) {
|
|
25314
25345
|
const selection = selectTargetStack(parsed, target, stacks);
|
|
25315
25346
|
switch (selection.kind) {
|
|
25316
25347
|
case "found": return selection.stack;
|
|
25317
|
-
case "no-prefix": throw new
|
|
25318
|
-
case "not-found": throw new
|
|
25319
|
-
case "ambiguous": throw new
|
|
25348
|
+
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'.`);
|
|
25349
|
+
case "not-found": throw new LocalStartCloudFrontError(`No stack matches '${selection.pattern}'. Available stacks: ${describeStacks(stacks)}.`);
|
|
25350
|
+
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).`);
|
|
25320
25351
|
}
|
|
25321
25352
|
}
|
|
25322
|
-
function
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
25332
|
-
|
|
25333
|
-
|
|
25334
|
-
|
|
25353
|
+
function notFound(target, stack, resources) {
|
|
25354
|
+
const dists = Object.entries(resources).filter(([, r]) => r.Type === CLOUDFRONT_DISTRIBUTION_TYPE).map(([logicalId]) => logicalId);
|
|
25355
|
+
const available = dists.length > 0 ? ` Available distributions in ${stack.stackName}: ${dists.join(", ")}.` : ` ${stack.stackName} declares no ${CLOUDFRONT_DISTRIBUTION_TYPE} resources.`;
|
|
25356
|
+
return new LocalStartCloudFrontError(`Target '${target}' did not match a CloudFront distribution in ${stack.stackName}.${available}`);
|
|
25357
|
+
}
|
|
25358
|
+
async function bootLambdaUrlOrigins(distribution, stacks, opts) {
|
|
25359
|
+
const logger = getLogger();
|
|
25360
|
+
const invokers = /* @__PURE__ */ new Map();
|
|
25361
|
+
const runners = [];
|
|
25362
|
+
const functionLogicalIds = /* @__PURE__ */ new Set();
|
|
25363
|
+
for (const origin of distribution.origins.values()) if (origin.kind === "lambda-url") functionLogicalIds.add(origin.functionLogicalId);
|
|
25364
|
+
for (const functionLogicalId of functionLogicalIds) {
|
|
25365
|
+
let runner;
|
|
25366
|
+
try {
|
|
25367
|
+
const lambda = resolveLambdaTarget(`${distribution.stackName}:${functionLogicalId}`, stacks);
|
|
25368
|
+
const containerEnv = await resolveLambdaContainerEnv(lambda, opts.envOptions, opts.profileCredentials, opts.extraStateProviders);
|
|
25369
|
+
runner = createFrontDoorLambdaRunner(lambda, {
|
|
25370
|
+
containerHost: opts.containerHost,
|
|
25371
|
+
skipPull: opts.skipPull,
|
|
25372
|
+
...opts.envOptions.region !== void 0 && { region: opts.envOptions.region },
|
|
25373
|
+
containerEnv: containerEnv.env,
|
|
25374
|
+
...containerEnv.sensitiveEnvKeys.length > 0 && { sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys) }
|
|
25375
|
+
});
|
|
25376
|
+
logger.info(`Booting Lambda Function URL origin container for ${functionLogicalId} (the backing Lambda runs locally via RIE)...`);
|
|
25377
|
+
await runner.start();
|
|
25378
|
+
} catch (err) {
|
|
25379
|
+
await Promise.all(runners.map((r) => r.stop().catch(() => void 0)));
|
|
25380
|
+
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);
|
|
25335
25381
|
}
|
|
25336
|
-
|
|
25382
|
+
runners.push(runner);
|
|
25383
|
+
invokers.set(functionLogicalId, (event) => runner.invoke(event));
|
|
25384
|
+
}
|
|
25385
|
+
return {
|
|
25386
|
+
invokers,
|
|
25387
|
+
runners
|
|
25388
|
+
};
|
|
25389
|
+
}
|
|
25390
|
+
async function drainWebFunctionServes(serves) {
|
|
25391
|
+
let done = 0;
|
|
25392
|
+
while (done < serves.length) {
|
|
25393
|
+
const batch = serves.slice(done);
|
|
25394
|
+
done = serves.length;
|
|
25395
|
+
await Promise.all(batch.map((s) => s.teardown().catch(() => void 0)));
|
|
25396
|
+
}
|
|
25397
|
+
}
|
|
25398
|
+
const WEB_FUNCTION_ORIGIN_READY_TIMEOUT_MS = 6e4;
|
|
25399
|
+
async function bootWebFunctionOrigins(distribution, stacks, opts) {
|
|
25400
|
+
const logger = getLogger();
|
|
25401
|
+
const targets = /* @__PURE__ */ new Map();
|
|
25402
|
+
const serves = opts.sink ?? [];
|
|
25403
|
+
const endpointLogicalIds = /* @__PURE__ */ new Set();
|
|
25404
|
+
for (const origin of distribution.origins.values()) if (origin.kind === "web-function") endpointLogicalIds.add(origin.endpointLogicalId);
|
|
25405
|
+
if (endpointLogicalIds.size === 0) return {
|
|
25406
|
+
targets,
|
|
25407
|
+
serves
|
|
25408
|
+
};
|
|
25409
|
+
for (const endpointLogicalId of endpointLogicalIds) try {
|
|
25410
|
+
if (serves.length === 0) await ensureDockerAvailable();
|
|
25411
|
+
const resolved = resolveWebFunctionTarget(`${distribution.stackName}:${endpointLogicalId}`, stacks);
|
|
25412
|
+
const prepared = prepareWebFunctionServe(resolved);
|
|
25413
|
+
for (const line of webFunctionFidelityWarnings(resolved, prepared.serviceConfig)) logger.warn(line);
|
|
25414
|
+
logger.info(`Booting Lambda Web Function endpoint origin server for ${displayIdent(endpointLogicalId)} (revision ${displayIdent(resolved.revisionLogicalId)}, ${prepared.spec.runtime})...`);
|
|
25415
|
+
const serve = new WebFunctionServe(prepared, {
|
|
25416
|
+
envOptions: opts.envOptions,
|
|
25417
|
+
...opts.profileCredentials !== void 0 && { profileCredentials: opts.profileCredentials },
|
|
25418
|
+
...opts.extraStateProviders !== void 0 && { extraStateProviders: opts.extraStateProviders },
|
|
25419
|
+
host: opts.containerHost,
|
|
25420
|
+
port: 0,
|
|
25421
|
+
skipPull: opts.skipPull,
|
|
25422
|
+
timeoutMs: opts.timeoutMs ?? WEB_FUNCTION_ORIGIN_READY_TIMEOUT_MS
|
|
25423
|
+
});
|
|
25424
|
+
serves.push(serve);
|
|
25425
|
+
const address = await serve.start();
|
|
25426
|
+
if (!address) throw new Error("the server was stopped while starting");
|
|
25427
|
+
targets.set(endpointLogicalId, address);
|
|
25428
|
+
serve.exited?.then((code) => {
|
|
25429
|
+
if (serve.tornDown) return;
|
|
25430
|
+
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.`);
|
|
25431
|
+
});
|
|
25432
|
+
} catch (err) {
|
|
25433
|
+
await Promise.all(serves.map((s) => s.teardown().catch(() => void 0)));
|
|
25434
|
+
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);
|
|
25435
|
+
}
|
|
25436
|
+
return {
|
|
25437
|
+
targets,
|
|
25438
|
+
serves
|
|
25439
|
+
};
|
|
25440
|
+
}
|
|
25441
|
+
async function bootLambdaEdgeFunctions(distribution, stacks, opts) {
|
|
25442
|
+
const logger = getLogger();
|
|
25443
|
+
const invokers = /* @__PURE__ */ new Map();
|
|
25444
|
+
const runners = [];
|
|
25445
|
+
const functionLogicalIds = /* @__PURE__ */ new Set();
|
|
25446
|
+
for (const behavior of distribution.behaviors) {
|
|
25447
|
+
const edge = behavior.lambdaEdge;
|
|
25448
|
+
if (!edge) continue;
|
|
25449
|
+
for (const assoc of [
|
|
25450
|
+
edge.viewerRequest,
|
|
25451
|
+
edge.originRequest,
|
|
25452
|
+
edge.originResponse,
|
|
25453
|
+
edge.viewerResponse
|
|
25454
|
+
]) if (assoc) functionLogicalIds.add(assoc.functionLogicalId);
|
|
25455
|
+
}
|
|
25456
|
+
for (const functionLogicalId of functionLogicalIds) try {
|
|
25457
|
+
const lambda = resolveLambdaTarget(`${distribution.stackName}:${functionLogicalId}`, stacks);
|
|
25458
|
+
const containerEnv = await resolveLambdaContainerEnv(lambda, opts.envOptions, opts.profileCredentials, opts.extraStateProviders);
|
|
25459
|
+
const runner = createFrontDoorLambdaRunner(lambda, {
|
|
25460
|
+
containerHost: opts.containerHost,
|
|
25461
|
+
skipPull: opts.skipPull,
|
|
25462
|
+
...opts.envOptions.region !== void 0 && { region: opts.envOptions.region },
|
|
25463
|
+
containerEnv: containerEnv.env,
|
|
25464
|
+
...containerEnv.sensitiveEnvKeys.length > 0 && { sensitiveEnvKeys: new Set(containerEnv.sensitiveEnvKeys) }
|
|
25465
|
+
});
|
|
25466
|
+
logger.info(`Booting Lambda@Edge container for ${functionLogicalId} (the function runs locally via RIE)...`);
|
|
25467
|
+
await runner.start();
|
|
25468
|
+
runners.push(runner);
|
|
25469
|
+
invokers.set(functionLogicalId, (event) => runner.invoke(event));
|
|
25470
|
+
} catch (err) {
|
|
25471
|
+
logger.warn(`Could not boot Lambda@Edge function ${displayIdent(functionLogicalId)}; its stage will be skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
25337
25472
|
}
|
|
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
25473
|
return {
|
|
25342
|
-
|
|
25343
|
-
|
|
25474
|
+
invokers,
|
|
25475
|
+
runners
|
|
25344
25476
|
};
|
|
25345
25477
|
}
|
|
25346
|
-
function
|
|
25347
|
-
|
|
25348
|
-
|
|
25349
|
-
|
|
25478
|
+
function buildLambdaEnvOptions(options, extraStateProviders) {
|
|
25479
|
+
return {
|
|
25480
|
+
...hostStateSourceFields(options, extraStateProviders),
|
|
25481
|
+
...options.fromCfnStack !== void 0 && { fromCfnStack: options.fromCfnStack },
|
|
25482
|
+
...options.assumeRole !== void 0 && { assumeRole: options.assumeRole },
|
|
25483
|
+
...options.region !== void 0 && { region: options.region },
|
|
25484
|
+
...options.profile !== void 0 && { profile: options.profile },
|
|
25485
|
+
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion }
|
|
25486
|
+
};
|
|
25350
25487
|
}
|
|
25351
|
-
function
|
|
25352
|
-
return
|
|
25488
|
+
function buildLambdaBootOptions(options, profileCredentials, extraStateProviders) {
|
|
25489
|
+
return {
|
|
25490
|
+
containerHost: options.host,
|
|
25491
|
+
skipPull: options.pull === false,
|
|
25492
|
+
envOptions: buildLambdaEnvOptions(options, extraStateProviders),
|
|
25493
|
+
...profileCredentials !== void 0 && { profileCredentials },
|
|
25494
|
+
...extraStateProviders !== void 0 && { extraStateProviders }
|
|
25495
|
+
};
|
|
25353
25496
|
}
|
|
25354
|
-
function
|
|
25355
|
-
const
|
|
25356
|
-
if (
|
|
25357
|
-
if (
|
|
25358
|
-
const byName = functions.filter((id) => resources[id]?.Properties?.["FunctionName"] === value);
|
|
25359
|
-
return byName.length === 1 ? byName[0] : void 0;
|
|
25497
|
+
function warnUnsupported(distribution, extraStateProviders) {
|
|
25498
|
+
const logger = getLogger();
|
|
25499
|
+
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.`);
|
|
25500
|
+
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.`);
|
|
25360
25501
|
}
|
|
25361
|
-
function
|
|
25362
|
-
if (
|
|
25363
|
-
|
|
25364
|
-
|
|
25365
|
-
|
|
25366
|
-
if (Array.isArray(getAtt) && typeof getAtt[0] === "string") return getAtt[0];
|
|
25367
|
-
if (typeof getAtt === "string") return getAtt.split(".")[0];
|
|
25502
|
+
function warnUnusedCacheOrigin(options, extraStateProviders) {
|
|
25503
|
+
if (options.cacheOrigin === true && !isStateSourceActive(options, extraStateProviders)) {
|
|
25504
|
+
const flags = stateSourceFlagNames(extraStateProviders).join(" or ");
|
|
25505
|
+
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.`);
|
|
25506
|
+
}
|
|
25368
25507
|
}
|
|
25369
|
-
function
|
|
25370
|
-
const
|
|
25371
|
-
const
|
|
25372
|
-
const
|
|
25373
|
-
if (
|
|
25374
|
-
|
|
25375
|
-
|
|
25376
|
-
|
|
25377
|
-
|
|
25378
|
-
|
|
25379
|
-
|
|
25508
|
+
async function resolveDeployedS3Origins(distribution, stacks, options, profileCredentials, logger, extraStateProviders) {
|
|
25509
|
+
const readers = /* @__PURE__ */ new Map();
|
|
25510
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
25511
|
+
const stateFlag = activeStateSourceFlag(options, extraStateProviders);
|
|
25512
|
+
if (stateFlag === void 0) return {
|
|
25513
|
+
readers,
|
|
25514
|
+
buckets
|
|
25515
|
+
};
|
|
25516
|
+
const synthRegion = stacks.find((s) => s.stackName === distribution.stackName)?.region;
|
|
25517
|
+
const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
|
|
25518
|
+
let record;
|
|
25519
|
+
try {
|
|
25520
|
+
record = provider ? await provider.load(distribution.stackName, synthRegion) : void 0;
|
|
25521
|
+
} finally {
|
|
25522
|
+
provider?.dispose();
|
|
25523
|
+
}
|
|
25524
|
+
const region = options.stackRegion ?? options.region ?? synthRegion ?? record?.region;
|
|
25525
|
+
const distributionId = record?.resources[distribution.logicalId]?.physicalId;
|
|
25526
|
+
for (const origin of [...distribution.origins.values()]) {
|
|
25527
|
+
if (origin.kind !== "s3-unresolved") continue;
|
|
25528
|
+
let bucketName;
|
|
25529
|
+
let via;
|
|
25530
|
+
if (origin.bucketLogicalId !== void 0) {
|
|
25531
|
+
bucketName = record?.resources[origin.bucketLogicalId]?.physicalId;
|
|
25532
|
+
via = "deployed state";
|
|
25533
|
+
} else if (origin.bucketName !== void 0) {
|
|
25534
|
+
bucketName = origin.bucketName;
|
|
25535
|
+
via = "the origin's DomainName";
|
|
25536
|
+
} else if (origin.deployedConfigOnly && distributionId) {
|
|
25537
|
+
bucketName = await resolveDeployedOriginBucket({
|
|
25538
|
+
distributionId,
|
|
25539
|
+
originId: origin.originId,
|
|
25540
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials }
|
|
25541
|
+
});
|
|
25542
|
+
via = "GetDistributionConfig";
|
|
25543
|
+
} else continue;
|
|
25544
|
+
if (!bucketName) continue;
|
|
25545
|
+
readers.set(origin.originId, createS3OriginReader(bucketName, {
|
|
25546
|
+
...region !== void 0 && { region },
|
|
25547
|
+
...profileCredentials !== void 0 && { credentials: profileCredentials },
|
|
25548
|
+
...options.cacheOrigin === true && { cache: true }
|
|
25549
|
+
}));
|
|
25550
|
+
buckets.set(origin.originId, bucketName);
|
|
25551
|
+
distribution.origins.set(origin.originId, {
|
|
25552
|
+
kind: "s3-deployed",
|
|
25553
|
+
originId: origin.originId,
|
|
25554
|
+
bucketName
|
|
25380
25555
|
});
|
|
25556
|
+
logger.info(`Origin ${displayIdent(origin.originId)}: no local BucketDeployment source; serving from deployed S3 (bucket=${bucketName}, resolved via ${via}) on demand under ${stateFlag}.`);
|
|
25381
25557
|
}
|
|
25382
|
-
const str = (v) => typeof v === "string" ? v : void 0;
|
|
25383
25558
|
return {
|
|
25384
|
-
|
|
25385
|
-
|
|
25386
|
-
authType: str(props["AuthType"]),
|
|
25387
|
-
weights
|
|
25559
|
+
readers,
|
|
25560
|
+
buckets
|
|
25388
25561
|
};
|
|
25389
25562
|
}
|
|
25390
|
-
function
|
|
25391
|
-
|
|
25392
|
-
|
|
25393
|
-
if (
|
|
25394
|
-
|
|
25563
|
+
function annotateDeployedS3Origins(distribution, buckets) {
|
|
25564
|
+
for (const [originId, bucketName] of buckets) {
|
|
25565
|
+
const origin = distribution.origins.get(originId);
|
|
25566
|
+
if (origin && origin.kind === "s3-unresolved") distribution.origins.set(originId, {
|
|
25567
|
+
kind: "s3-deployed",
|
|
25568
|
+
originId,
|
|
25569
|
+
bucketName
|
|
25570
|
+
});
|
|
25395
25571
|
}
|
|
25396
|
-
return best?.revisionLogicalId;
|
|
25397
25572
|
}
|
|
25398
|
-
function
|
|
25399
|
-
|
|
25573
|
+
async function localStartCloudFrontCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
25574
|
+
const logger = getLogger();
|
|
25575
|
+
if (options.verbose) logger.setLevel("debug");
|
|
25576
|
+
const originOverrides = parseOriginOverrides(options.origin);
|
|
25577
|
+
const tlsRequested = options.tls === true || options.tlsCert !== void 0 || options.tlsKey !== void 0;
|
|
25578
|
+
await applyRoleArnIfSet({
|
|
25579
|
+
roleArn: options.roleArn,
|
|
25580
|
+
region: options.region,
|
|
25581
|
+
profile: options.profile
|
|
25582
|
+
});
|
|
25583
|
+
const appCmd = resolveApp(options.app);
|
|
25584
|
+
if (!appCmd) throw new LocalStartCloudFrontError(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
|
|
25585
|
+
const basePort = parseInt(options.port, 10);
|
|
25586
|
+
if (!Number.isFinite(basePort) || basePort < 0 || basePort > 65535) throw new LocalStartCloudFrontError(`--port must be 0..65535 (got ${options.port}).`);
|
|
25587
|
+
const synthAndResolve = async () => {
|
|
25588
|
+
logger.info("Synthesizing CDK app...");
|
|
25589
|
+
const synthesizer = hostSynthesizer ?? new Synthesizer();
|
|
25590
|
+
const context = parseContextOptions(options.context);
|
|
25591
|
+
const synthOpts = {
|
|
25592
|
+
app: appCmd,
|
|
25593
|
+
output: options.output,
|
|
25594
|
+
...options.region && { region: options.region },
|
|
25595
|
+
...options.profile && { profile: options.profile },
|
|
25596
|
+
...Object.keys(context).length > 0 && { context }
|
|
25597
|
+
};
|
|
25598
|
+
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
25599
|
+
const chosen = await resolveSingleTarget(target, {
|
|
25600
|
+
entries: listTargets(stacks).cloudFrontDistributions,
|
|
25601
|
+
message: "Select a CloudFront distribution to serve",
|
|
25602
|
+
noun: "CloudFront distributions",
|
|
25603
|
+
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.`)
|
|
25604
|
+
});
|
|
25605
|
+
target = chosen;
|
|
25606
|
+
const { stack, logicalId } = resolveCloudFrontTarget(chosen, stacks);
|
|
25607
|
+
return {
|
|
25608
|
+
distribution: resolveCloudFrontDistribution({
|
|
25609
|
+
stack,
|
|
25610
|
+
logicalId,
|
|
25611
|
+
originOverrides
|
|
25612
|
+
}),
|
|
25613
|
+
stacks
|
|
25614
|
+
};
|
|
25615
|
+
};
|
|
25616
|
+
const initial = await synthAndResolve();
|
|
25617
|
+
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
25618
|
+
const deployedS3 = await resolveDeployedS3Origins(initial.distribution, initial.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
25619
|
+
warnUnsupported(initial.distribution, extraStateProviders);
|
|
25620
|
+
warnUnusedCacheOrigin(options, extraStateProviders);
|
|
25621
|
+
await attachKvsModules(initial.distribution, initial.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
25622
|
+
const bootOpts = buildLambdaBootOptions(options, profileCredentials, extraStateProviders);
|
|
25623
|
+
const webFunctionServes = [];
|
|
25624
|
+
let bootAborted = false;
|
|
25625
|
+
const onBootSignal = (signal) => {
|
|
25626
|
+
if (bootAborted) return;
|
|
25627
|
+
bootAborted = true;
|
|
25628
|
+
logger.info(`Received ${signal} during start-up; removing the web function containers...`);
|
|
25629
|
+
drainWebFunctionServes(webFunctionServes).then(() => process.exit(signal === "SIGINT" ? 130 : 0));
|
|
25630
|
+
};
|
|
25631
|
+
const bootSigint = () => onBootSignal("SIGINT");
|
|
25632
|
+
const bootSigterm = () => onBootSignal("SIGTERM");
|
|
25633
|
+
const disarmBootSignals = () => {
|
|
25634
|
+
process.off("SIGINT", bootSigint);
|
|
25635
|
+
process.off("SIGTERM", bootSigterm);
|
|
25636
|
+
};
|
|
25637
|
+
if ([...initial.distribution.origins.values()].some((o) => o.kind === "web-function")) {
|
|
25638
|
+
process.on("SIGINT", bootSigint);
|
|
25639
|
+
process.on("SIGTERM", bootSigterm);
|
|
25640
|
+
}
|
|
25641
|
+
const { invokers: lambdaInvokers, runners: lambdaRunners } = await bootLambdaUrlOrigins(initial.distribution, initial.stacks, bootOpts);
|
|
25642
|
+
const { invokers: edgeInvokers, runners: edgeRunners } = await bootLambdaEdgeFunctions(initial.distribution, initial.stacks, bootOpts);
|
|
25643
|
+
let server;
|
|
25644
|
+
try {
|
|
25645
|
+
const webFunctions = await bootWebFunctionOrigins(initial.distribution, initial.stacks, {
|
|
25646
|
+
...bootOpts,
|
|
25647
|
+
sink: webFunctionServes
|
|
25648
|
+
});
|
|
25649
|
+
let tls;
|
|
25650
|
+
if (tlsRequested) tls = await resolveFrontDoorTlsMaterials({
|
|
25651
|
+
certPath: options.tlsCert,
|
|
25652
|
+
keyPath: options.tlsKey
|
|
25653
|
+
});
|
|
25654
|
+
server = await startCloudFrontServer({
|
|
25655
|
+
distribution: initial.distribution,
|
|
25656
|
+
host: options.host,
|
|
25657
|
+
port: basePort,
|
|
25658
|
+
...tls && { tls },
|
|
25659
|
+
...lambdaInvokers.size > 0 && { lambdaInvokers },
|
|
25660
|
+
...edgeInvokers.size > 0 && { edgeInvokers },
|
|
25661
|
+
...deployedS3.readers.size > 0 && { s3OriginReaders: deployedS3.readers },
|
|
25662
|
+
...webFunctions.targets.size > 0 && { webFunctionOrigins: webFunctions.targets }
|
|
25663
|
+
});
|
|
25664
|
+
} catch (err) {
|
|
25665
|
+
await Promise.all([...[...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch(() => void 0)), ...webFunctionServes.map((s) => s.teardown().catch(() => void 0))]);
|
|
25666
|
+
if (bootAborted) return await new Promise(() => void 0);
|
|
25667
|
+
disarmBootSignals();
|
|
25668
|
+
throw err;
|
|
25669
|
+
}
|
|
25670
|
+
disarmBootSignals();
|
|
25671
|
+
if (bootAborted) return await new Promise(() => void 0);
|
|
25672
|
+
process.stdout.write(`CloudFront distribution serving on ${server.url} (${displayIdent(initial.distribution.logicalId)})\n`);
|
|
25673
|
+
process.stdout.write("^C to stop.\n");
|
|
25674
|
+
let watcher;
|
|
25675
|
+
let reloadChain = Promise.resolve();
|
|
25676
|
+
if (options.watch) {
|
|
25677
|
+
const watchRoot = process.cwd();
|
|
25678
|
+
const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
|
|
25679
|
+
watchRoot,
|
|
25680
|
+
output: options.output,
|
|
25681
|
+
watchConfig: resolveWatchConfig()
|
|
25682
|
+
});
|
|
25683
|
+
watcher = createFileWatcher({
|
|
25684
|
+
paths: [watchRoot],
|
|
25685
|
+
ignored,
|
|
25686
|
+
shouldTrigger,
|
|
25687
|
+
onChange: () => {
|
|
25688
|
+
logger.info("Detected source change; reloading...");
|
|
25689
|
+
reloadChain = reloadChain.then(async () => {
|
|
25690
|
+
try {
|
|
25691
|
+
const reloaded = await synthAndResolve();
|
|
25692
|
+
annotateDeployedS3Origins(reloaded.distribution, deployedS3.buckets);
|
|
25693
|
+
for (const reader of deployedS3.readers.values()) reader.clearCache();
|
|
25694
|
+
warnUnsupported(reloaded.distribution, extraStateProviders);
|
|
25695
|
+
await attachKvsModules(reloaded.distribution, reloaded.stacks, options, profileCredentials, logger, extraStateProviders);
|
|
25696
|
+
server.update(reloaded.distribution);
|
|
25697
|
+
logger.info("Reload complete.");
|
|
25698
|
+
} catch (err) {
|
|
25699
|
+
logger.warn(`Reload failed; keeping the previous version serving: ${err instanceof Error ? err.message : String(err)}`);
|
|
25700
|
+
}
|
|
25701
|
+
}).catch(() => void 0);
|
|
25702
|
+
}
|
|
25703
|
+
});
|
|
25704
|
+
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}).`);
|
|
25705
|
+
}
|
|
25706
|
+
let shuttingDown = false;
|
|
25707
|
+
const shutdown = async (signal, exitCode) => {
|
|
25708
|
+
if (shuttingDown) return;
|
|
25709
|
+
shuttingDown = true;
|
|
25710
|
+
logger.info(`Received ${signal}, shutting down...`);
|
|
25711
|
+
if (watcher) try {
|
|
25712
|
+
await watcher.close();
|
|
25713
|
+
} catch {}
|
|
25714
|
+
try {
|
|
25715
|
+
await server.close();
|
|
25716
|
+
} catch {}
|
|
25717
|
+
await Promise.all([...[...lambdaRunners, ...edgeRunners].map((r) => r.stop().catch((err) => {
|
|
25718
|
+
logger.debug(`Lambda runner stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25719
|
+
})), ...webFunctionServes.map((s) => s.teardown().catch((err) => {
|
|
25720
|
+
logger.debug(`Web function server teardown failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25721
|
+
}))]);
|
|
25722
|
+
await Promise.all([...deployedS3.readers.values()].map((reader) => reader.close()));
|
|
25723
|
+
process.exit(exitCode);
|
|
25724
|
+
};
|
|
25725
|
+
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
25726
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
25727
|
+
await new Promise(() => void 0);
|
|
25728
|
+
}
|
|
25729
|
+
function createLocalStartCloudFrontCommand(opts = {}) {
|
|
25730
|
+
setEmbedConfig(opts.embedConfig);
|
|
25731
|
+
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) => {
|
|
25732
|
+
await localStartCloudFrontCommand(target, options, opts.extraStateProviders, opts.synthesizer);
|
|
25733
|
+
}));
|
|
25734
|
+
addStartCloudFrontSpecificOptions(cmd);
|
|
25735
|
+
[
|
|
25736
|
+
...commonOptions(),
|
|
25737
|
+
...appOptions(),
|
|
25738
|
+
...contextOptions
|
|
25739
|
+
].forEach((opt) => cmd.addOption(opt));
|
|
25740
|
+
cmd.addOption(regionOption);
|
|
25741
|
+
return cmd;
|
|
25742
|
+
}
|
|
25743
|
+
function addStartCloudFrontSpecificOptions(cmd) {
|
|
25744
|
+
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));
|
|
25400
25745
|
}
|
|
25401
25746
|
|
|
25402
25747
|
//#endregion
|
|
25403
|
-
//#region src/local/
|
|
25404
|
-
const
|
|
25405
|
-
function
|
|
25406
|
-
|
|
25407
|
-
|
|
25408
|
-
const runtime = asObject$2(build?.["RuntimeConfig"])?.["Runtime"];
|
|
25409
|
-
if (typeof runtime !== "string" || runtime.length === 0) throw new CdkLocalError(`${subject} has no literal BuildConfig.RuntimeConfig.Runtime.`, "LOCAL_START_WEB_FUNCTION_NO_RUNTIME");
|
|
25410
|
-
const codeConfig = asObject$2(build?.["CodeConfig"]);
|
|
25411
|
-
if (!codeConfig) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
|
|
25412
|
-
const unknownKeys = Object.keys(codeConfig).filter((k) => !SUPPORTED_CODE_CONFIG_KEYS.includes(k));
|
|
25413
|
-
if (unknownKeys.length > 0) throw new CdkLocalError(`${subject} uses BuildConfig.CodeConfig.${unknownKeys.map((k) => displayIdent(k)).join(", ")}, which ${getEmbedConfig().cliName} start-web-function does not support yet. Supported: ${SUPPORTED_CODE_CONFIG_KEYS.join(", ")}.`, "LOCAL_START_WEB_FUNCTION_UNSUPPORTED_CODE_CONFIG");
|
|
25414
|
-
const s3 = asObject$2(codeConfig["S3Object"]);
|
|
25415
|
-
if (!s3) throw new CdkLocalError(`${subject} has no BuildConfig.CodeConfig.S3Object.`, "LOCAL_START_WEB_FUNCTION_NO_CODE_CONFIG");
|
|
25416
|
-
const key = s3["Key"];
|
|
25417
|
-
if (typeof key !== "string" || key.length === 0) throw new CdkLocalError(`${subject} has a CodeConfig.S3Object.Key that is not a literal string (${describeValue$1(key)}). The code must come from a CDK asset in this app (for example an s3assets.Asset passing asset.s3BucketName / asset.s3ObjectKey), whose key synthesizes as a literal.`, "LOCAL_START_WEB_FUNCTION_CODE_KEY_NOT_LITERAL");
|
|
25418
|
-
const versionId = s3["VersionId"];
|
|
25419
|
-
return {
|
|
25420
|
-
runtime,
|
|
25421
|
-
s3Object: {
|
|
25422
|
-
bucket: s3["Bucket"],
|
|
25423
|
-
key,
|
|
25424
|
-
...typeof versionId === "string" && { versionId }
|
|
25425
|
-
}
|
|
25426
|
-
};
|
|
25427
|
-
}
|
|
25428
|
-
function findFileAssetForObjectKey(manifest, key) {
|
|
25429
|
-
for (const [hash, asset] of Object.entries(manifest.files ?? {})) for (const dest of Object.values(asset.destinations ?? {})) if (dest.objectKey === key || dest.objectKey.endsWith(`/${key}`)) return {
|
|
25430
|
-
hash,
|
|
25431
|
-
asset
|
|
25432
|
-
};
|
|
25433
|
-
const hash = key.replace(/^.*\//, "").replace(/\.zip$/, "");
|
|
25434
|
-
const byHash = Object.hasOwn(manifest.files ?? {}, hash) ? manifest.files[hash] : void 0;
|
|
25435
|
-
return byHash ? {
|
|
25436
|
-
hash,
|
|
25437
|
-
asset: byHash
|
|
25438
|
-
} : void 0;
|
|
25748
|
+
//#region src/local/agentcore-ws-bridge.ts
|
|
25749
|
+
const DEFAULT_PATH = "/ws";
|
|
25750
|
+
function decodeBrowserFrame(data, isBinary) {
|
|
25751
|
+
if (typeof data === "string") return data;
|
|
25752
|
+
return (Buffer.isBuffer(data) ? data : Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data)).toString("utf-8");
|
|
25439
25753
|
}
|
|
25440
|
-
|
|
25441
|
-
const
|
|
25442
|
-
const
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
const
|
|
25447
|
-
|
|
25448
|
-
|
|
25449
|
-
|
|
25450
|
-
|
|
25451
|
-
|
|
25452
|
-
|
|
25453
|
-
|
|
25454
|
-
|
|
25455
|
-
|
|
25456
|
-
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
|
|
25754
|
+
function attachAgentCoreWsBridge(httpServer, config) {
|
|
25755
|
+
const path = config.path ?? DEFAULT_PATH;
|
|
25756
|
+
const wss = new WebSocketServer({
|
|
25757
|
+
server: httpServer,
|
|
25758
|
+
path
|
|
25759
|
+
});
|
|
25760
|
+
const liveCloses = /* @__PURE__ */ new Set();
|
|
25761
|
+
wss.on("connection", (browser) => {
|
|
25762
|
+
const sessionId = config.sessionId ?? randomUUID();
|
|
25763
|
+
const containerPort = typeof config.containerPort === "function" ? config.containerPort() : config.containerPort;
|
|
25764
|
+
const handle = bridgeAgentCoreWs(config.containerHost, containerPort, {
|
|
25765
|
+
sessionId,
|
|
25766
|
+
...config.authorization && { authorization: config.authorization },
|
|
25767
|
+
...config.webSocketImpl && { webSocketImpl: config.webSocketImpl },
|
|
25768
|
+
onMessage: (text) => {
|
|
25769
|
+
if (browser.readyState === browser.OPEN) browser.send(text);
|
|
25770
|
+
},
|
|
25771
|
+
onClose: () => {
|
|
25772
|
+
try {
|
|
25773
|
+
browser.close();
|
|
25774
|
+
} catch {}
|
|
25775
|
+
},
|
|
25776
|
+
onError: (err) => {
|
|
25777
|
+
if (browser.readyState === browser.OPEN) try {
|
|
25778
|
+
browser.send(`[bridge error] ${err.message}`);
|
|
25779
|
+
} catch {}
|
|
25780
|
+
try {
|
|
25781
|
+
browser.close();
|
|
25782
|
+
} catch {}
|
|
25783
|
+
}
|
|
25784
|
+
});
|
|
25785
|
+
liveCloses.add(handle.close);
|
|
25786
|
+
browser.on("message", (data, isBinary) => {
|
|
25787
|
+
handle.send(decodeBrowserFrame(data, isBinary));
|
|
25788
|
+
});
|
|
25789
|
+
browser.on("close", () => {
|
|
25790
|
+
liveCloses.delete(handle.close);
|
|
25791
|
+
handle.close();
|
|
25792
|
+
});
|
|
25793
|
+
browser.on("error", () => {
|
|
25794
|
+
liveCloses.delete(handle.close);
|
|
25795
|
+
handle.close();
|
|
25796
|
+
});
|
|
25460
25797
|
});
|
|
25461
|
-
if (!existsSync$1(sourcePath)) throw new CdkLocalError(`${subject} source ${displayUntrustedValue(sourcePath)} does not exist. Re-synthesize the app and retry.`, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_MISSING");
|
|
25462
|
-
if (!statSync$1(sourcePath).isDirectory() && !sourcePath.endsWith(".zip")) throw new CdkLocalError(`${subject} source ${displayUntrustedValue(sourcePath)} is neither a directory nor a .zip archive, so it is not a code bundle.`, "LOCAL_START_WEB_FUNCTION_CODE_SOURCE_NOT_BUNDLE");
|
|
25463
|
-
const materialized = materializeAssetCodeDir(sourcePath);
|
|
25464
25798
|
return {
|
|
25465
|
-
|
|
25466
|
-
|
|
25467
|
-
|
|
25468
|
-
|
|
25469
|
-
|
|
25799
|
+
path,
|
|
25800
|
+
close: () => new Promise((res) => {
|
|
25801
|
+
for (const closeLeg of liveCloses) closeLeg();
|
|
25802
|
+
liveCloses.clear();
|
|
25803
|
+
wss.close(() => res());
|
|
25804
|
+
})
|
|
25470
25805
|
};
|
|
25471
25806
|
}
|
|
25472
|
-
function
|
|
25473
|
-
|
|
25474
|
-
|
|
25475
|
-
|
|
25476
|
-
|
|
25477
|
-
|
|
25807
|
+
function startAgentCoreWsBridge(config) {
|
|
25808
|
+
const host = config.host ?? "127.0.0.1";
|
|
25809
|
+
const httpServer = createServer((_req, res) => {
|
|
25810
|
+
res.writeHead(426, { "Content-Type": "text/plain" });
|
|
25811
|
+
res.end("Upgrade required: connect over WebSocket.\n");
|
|
25812
|
+
});
|
|
25813
|
+
const attached = attachAgentCoreWsBridge(httpServer, config);
|
|
25814
|
+
return new Promise((resolve, reject) => {
|
|
25815
|
+
httpServer.once("error", reject);
|
|
25816
|
+
httpServer.listen(config.port ?? 0, host, () => {
|
|
25817
|
+
httpServer.removeListener("error", reject);
|
|
25818
|
+
httpServer.on("error", (err) => getLogger().debug(`agentcore-ws bridge server error: ${err.message}`));
|
|
25819
|
+
const port = httpServer.address().port;
|
|
25820
|
+
resolve({
|
|
25821
|
+
url: `ws://${formatAuthority(host, port)}${attached.path}`,
|
|
25822
|
+
port,
|
|
25823
|
+
close: () => new Promise((res) => {
|
|
25824
|
+
attached.close().then(() => httpServer.close(() => res()));
|
|
25825
|
+
})
|
|
25826
|
+
});
|
|
25478
25827
|
});
|
|
25479
|
-
}
|
|
25480
|
-
}
|
|
25481
|
-
function asObject$2(value) {
|
|
25482
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
25483
|
-
}
|
|
25484
|
-
function describeValue$1(value) {
|
|
25485
|
-
if (typeof value === "string") return displayUntrustedValue(value);
|
|
25486
|
-
let json;
|
|
25487
|
-
try {
|
|
25488
|
-
json = JSON.stringify(value);
|
|
25489
|
-
} catch {
|
|
25490
|
-
json = void 0;
|
|
25491
|
-
}
|
|
25492
|
-
return displayUntrustedValue(json ?? String(value));
|
|
25828
|
+
});
|
|
25493
25829
|
}
|
|
25494
25830
|
|
|
25495
25831
|
//#endregion
|
|
25496
|
-
//#region src/local/
|
|
25497
|
-
const
|
|
25498
|
-
|
|
25499
|
-
|
|
25500
|
-
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25832
|
+
//#region src/local/agentcore-http-server.ts
|
|
25833
|
+
const DEFAULT_ROUTES = [{
|
|
25834
|
+
method: "POST",
|
|
25835
|
+
path: "/invocations"
|
|
25836
|
+
}, {
|
|
25837
|
+
method: "GET",
|
|
25838
|
+
path: "/ping"
|
|
25839
|
+
}];
|
|
25840
|
+
function wireUpstreamResponse(upstream, clientRes) {
|
|
25841
|
+
upstream.on("response", (upRes) => {
|
|
25842
|
+
clientRes.writeHead(upRes.statusCode ?? 502, upRes.headers);
|
|
25843
|
+
upRes.on("error", () => clientRes.destroy());
|
|
25844
|
+
upRes.pipe(clientRes);
|
|
25845
|
+
});
|
|
25846
|
+
upstream.on("error", (err) => {
|
|
25847
|
+
getLogger().debug(`agentcore http serve upstream error: ${err.message}`);
|
|
25848
|
+
if (!clientRes.headersSent) clientRes.writeHead(502, { "content-type": "application/json" });
|
|
25849
|
+
clientRes.end(JSON.stringify({ error: `upstream error: ${err.message}` }));
|
|
25850
|
+
});
|
|
25513
25851
|
}
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
|
|
25521
|
-
|
|
25522
|
-
|
|
25852
|
+
function proxyToContainer(clientReq, clientRes, config, upstreamPath, perRequest) {
|
|
25853
|
+
const sessionId = config.sessionId ?? randomUUID();
|
|
25854
|
+
const headers = { ...clientReq.headers };
|
|
25855
|
+
delete headers["host"];
|
|
25856
|
+
headers[AGENTCORE_SESSION_ID_HEADER] = sessionId;
|
|
25857
|
+
const authorization = perRequest?.authorization ?? config.authorization;
|
|
25858
|
+
if (authorization) headers["authorization"] = authorization;
|
|
25859
|
+
if (config.signRequest && clientReq.method === "POST") {
|
|
25860
|
+
const signRequest = config.signRequest;
|
|
25861
|
+
const chunks = [];
|
|
25862
|
+
clientReq.on("data", (c) => chunks.push(c));
|
|
25863
|
+
clientReq.on("error", (err) => {
|
|
25864
|
+
getLogger().debug(`agentcore http serve client error: ${err.message}`);
|
|
25865
|
+
if (!clientRes.headersSent) {
|
|
25866
|
+
clientRes.writeHead(400, { "content-type": "application/json" });
|
|
25867
|
+
clientRes.end(JSON.stringify({ error: `client error: ${err.message}` }));
|
|
25868
|
+
}
|
|
25869
|
+
});
|
|
25870
|
+
clientReq.on("end", () => {
|
|
25871
|
+
const body = Buffer.concat(chunks);
|
|
25872
|
+
signRequest({
|
|
25873
|
+
method: "POST",
|
|
25874
|
+
path: upstreamPath,
|
|
25875
|
+
body,
|
|
25876
|
+
sessionId
|
|
25877
|
+
}).then((signed) => {
|
|
25878
|
+
const signedHeaders = {
|
|
25879
|
+
...headers,
|
|
25880
|
+
...signed
|
|
25881
|
+
};
|
|
25882
|
+
delete signedHeaders["transfer-encoding"];
|
|
25883
|
+
signedHeaders["content-length"] = String(body.length);
|
|
25884
|
+
const upstream = request({
|
|
25885
|
+
host: config.containerHost,
|
|
25886
|
+
port: config.containerPort,
|
|
25887
|
+
path: upstreamPath,
|
|
25888
|
+
method: "POST",
|
|
25889
|
+
headers: signedHeaders
|
|
25890
|
+
});
|
|
25891
|
+
wireUpstreamResponse(upstream, clientRes);
|
|
25892
|
+
upstream.end(body);
|
|
25893
|
+
}).catch((err) => {
|
|
25894
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25895
|
+
getLogger().debug(`agentcore http serve sigv4 sign error: ${msg}`);
|
|
25896
|
+
if (!clientRes.headersSent) {
|
|
25897
|
+
clientRes.writeHead(500, { "content-type": "application/json" });
|
|
25898
|
+
clientRes.end(JSON.stringify({ error: `sigv4 signing failed: ${msg}` }));
|
|
25899
|
+
}
|
|
25900
|
+
});
|
|
25901
|
+
});
|
|
25902
|
+
return;
|
|
25903
|
+
}
|
|
25904
|
+
const upstream = request({
|
|
25905
|
+
host: config.containerHost,
|
|
25906
|
+
port: config.containerPort,
|
|
25907
|
+
path: upstreamPath,
|
|
25908
|
+
method: clientReq.method,
|
|
25909
|
+
headers
|
|
25910
|
+
});
|
|
25911
|
+
wireUpstreamResponse(upstream, clientRes);
|
|
25912
|
+
clientReq.on("error", (err) => {
|
|
25913
|
+
getLogger().debug(`agentcore http serve client error: ${err.message}`);
|
|
25914
|
+
upstream.destroy();
|
|
25915
|
+
});
|
|
25916
|
+
clientReq.pipe(upstream);
|
|
25523
25917
|
}
|
|
25524
|
-
function
|
|
25525
|
-
const
|
|
25526
|
-
|
|
25527
|
-
|
|
25918
|
+
function startAgentCoreHttpServer(config) {
|
|
25919
|
+
const host = config.host ?? "127.0.0.1";
|
|
25920
|
+
const routes = config.routes ?? DEFAULT_ROUTES;
|
|
25921
|
+
const attachWs = config.attachWs ?? true;
|
|
25922
|
+
const notFoundHint = buildNotFoundHint(routes, attachWs);
|
|
25923
|
+
const httpServer = createServer((req, res) => {
|
|
25924
|
+
const path = (req.url ?? "/").split("?")[0];
|
|
25925
|
+
const match = routes.find((r) => r.method === req.method && r.path === path);
|
|
25926
|
+
if (!match) {
|
|
25927
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
25928
|
+
res.end(JSON.stringify({
|
|
25929
|
+
error: "not found",
|
|
25930
|
+
hint: notFoundHint
|
|
25931
|
+
}));
|
|
25932
|
+
return;
|
|
25933
|
+
}
|
|
25934
|
+
if (config.authCheck && req.method === "POST") {
|
|
25935
|
+
req.on("error", (err) => getLogger().debug(`agentcore http serve client error (auth gate): ${err.message}`));
|
|
25936
|
+
config.authCheck(req.headers).then((result) => {
|
|
25937
|
+
if (!result.allow) {
|
|
25938
|
+
req.resume();
|
|
25939
|
+
res.writeHead(result.status ?? 403, { "content-type": "application/json" });
|
|
25940
|
+
res.end(JSON.stringify({ error: result.message ?? "forbidden" }));
|
|
25941
|
+
return;
|
|
25942
|
+
}
|
|
25943
|
+
proxyToContainer(req, res, config, match.path, { ...result.authorization && { authorization: result.authorization } });
|
|
25944
|
+
}).catch((err) => {
|
|
25945
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25946
|
+
getLogger().debug(`agentcore http serve auth-check error: ${msg}`);
|
|
25947
|
+
req.resume();
|
|
25948
|
+
if (!res.headersSent) {
|
|
25949
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
25950
|
+
res.end(JSON.stringify({ error: `auth check failed: ${msg}` }));
|
|
25951
|
+
}
|
|
25952
|
+
});
|
|
25953
|
+
return;
|
|
25954
|
+
}
|
|
25955
|
+
proxyToContainer(req, res, config, match.path);
|
|
25956
|
+
});
|
|
25957
|
+
const bridge = attachWs ? attachAgentCoreWsBridge(httpServer, {
|
|
25958
|
+
containerHost: config.containerHost,
|
|
25959
|
+
containerPort: () => config.containerPort,
|
|
25960
|
+
...config.sessionId && { sessionId: config.sessionId },
|
|
25961
|
+
...config.authorization && { authorization: config.authorization }
|
|
25962
|
+
}) : void 0;
|
|
25963
|
+
return new Promise((resolve, reject) => {
|
|
25964
|
+
httpServer.once("error", reject);
|
|
25965
|
+
httpServer.listen(config.port ?? 0, host, () => {
|
|
25966
|
+
httpServer.removeListener("error", reject);
|
|
25967
|
+
httpServer.on("error", (err) => getLogger().debug(`agentcore http serve server error: ${err.message}`));
|
|
25968
|
+
const port = httpServer.address().port;
|
|
25969
|
+
resolve({
|
|
25970
|
+
httpUrl: `http://${formatAuthority(host, port)}`,
|
|
25971
|
+
...bridge && { wsUrl: `ws://${formatAuthority(host, port)}${bridge.path}` },
|
|
25972
|
+
port,
|
|
25973
|
+
setContainerPort: (newPort) => {
|
|
25974
|
+
config.containerPort = newPort;
|
|
25975
|
+
},
|
|
25976
|
+
close: () => new Promise((res) => {
|
|
25977
|
+
if (bridge) bridge.close().then(() => httpServer.close(() => res()));
|
|
25978
|
+
else httpServer.close(() => res());
|
|
25979
|
+
})
|
|
25980
|
+
});
|
|
25981
|
+
});
|
|
25982
|
+
});
|
|
25528
25983
|
}
|
|
25529
|
-
function
|
|
25530
|
-
|
|
25984
|
+
function buildNotFoundHint(routes, attachWs) {
|
|
25985
|
+
const base = routes.map((r) => `${r.method} ${r.path}`).join(" or ");
|
|
25986
|
+
return attachWs ? `${base} (WebSocket: connect to /ws)` : base;
|
|
25531
25987
|
}
|
|
25532
25988
|
|
|
25533
25989
|
//#endregion
|
|
25534
|
-
//#region src/local/
|
|
25535
|
-
|
|
25536
|
-
const
|
|
25537
|
-
|
|
25538
|
-
|
|
25539
|
-
|
|
25540
|
-
|
|
25541
|
-
const
|
|
25542
|
-
const
|
|
25543
|
-
|
|
25544
|
-
|
|
25545
|
-
|
|
25546
|
-
|
|
25547
|
-
|
|
25548
|
-
|
|
25549
|
-
|
|
25550
|
-
|
|
25551
|
-
|
|
25990
|
+
//#region src/local/agentcore-serve-auth.ts
|
|
25991
|
+
function buildAgentCoreServeAuthCheck(authorizer, opts = {}) {
|
|
25992
|
+
const injectedBearer = opts.bearerToken ? `Bearer ${opts.bearerToken}` : void 0;
|
|
25993
|
+
if (opts.noVerifyAuth === true) return async () => ({
|
|
25994
|
+
allow: true,
|
|
25995
|
+
...injectedBearer && { authorization: injectedBearer }
|
|
25996
|
+
});
|
|
25997
|
+
const jwksCache = opts.jwksCache ?? createJwksCache();
|
|
25998
|
+
const warnedAt = opts.warnedAt ?? /* @__PURE__ */ new Map();
|
|
25999
|
+
return async (headers) => {
|
|
26000
|
+
const header = readAuthorizationHeader(headers) ?? injectedBearer;
|
|
26001
|
+
if (!header) return {
|
|
26002
|
+
allow: false,
|
|
26003
|
+
status: 401,
|
|
26004
|
+
message: "Missing Authorization: this runtime declares a customJwtAuthorizer. Send a Bearer JWT, or start the serve with --bearer-token / --no-verify-auth."
|
|
26005
|
+
};
|
|
26006
|
+
if (!(await verifyJwtViaDiscovery({
|
|
26007
|
+
discoveryUrl: authorizer.discoveryUrl,
|
|
26008
|
+
...authorizer.allowedAudience && { allowedAudience: authorizer.allowedAudience },
|
|
26009
|
+
...authorizer.allowedClients && { allowedClients: authorizer.allowedClients },
|
|
26010
|
+
...authorizer.allowedScopes && { allowedScopes: authorizer.allowedScopes },
|
|
26011
|
+
...authorizer.customClaims && { customClaims: authorizer.customClaims }
|
|
26012
|
+
}, header, jwksCache, { warnedAt })).allow) return {
|
|
26013
|
+
allow: false,
|
|
26014
|
+
status: 403,
|
|
26015
|
+
message: `Forbidden: the inbound JWT was rejected by the runtime's customJwtAuthorizer (signature / issuer / expiry / audience / scope check failed against ${authorizer.discoveryUrl}).`
|
|
26016
|
+
};
|
|
26017
|
+
return {
|
|
26018
|
+
allow: true,
|
|
26019
|
+
authorization: header
|
|
26020
|
+
};
|
|
25552
26021
|
};
|
|
25553
26022
|
}
|
|
25554
|
-
function
|
|
25555
|
-
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
...config.executionRoleArn !== void 0 && { Role: config.executionRoleArn }
|
|
25564
|
-
},
|
|
25565
|
-
...resolved.revision.Metadata !== void 0 && { Metadata: resolved.revision.Metadata }
|
|
25566
|
-
},
|
|
25567
|
-
memoryMb: 128,
|
|
25568
|
-
timeoutSec: config.timeoutSeconds ?? 0,
|
|
25569
|
-
layers: [],
|
|
25570
|
-
runtime,
|
|
25571
|
-
handler: "",
|
|
25572
|
-
codePath: null,
|
|
25573
|
-
architecture: "arm64"
|
|
26023
|
+
function selectServeInboundAuth(resolved, options, sigv4Active, buildAuthCheck = buildAgentCoreServeAuthCheck) {
|
|
26024
|
+
const bearerHeader = options.bearerToken ? `Bearer ${options.bearerToken}` : void 0;
|
|
26025
|
+
if (resolved.jwtAuthorizer) return {
|
|
26026
|
+
authCheck: buildAuthCheck(resolved.jwtAuthorizer, {
|
|
26027
|
+
...options.verifyAuth === false && { noVerifyAuth: true },
|
|
26028
|
+
...options.bearerToken && { bearerToken: options.bearerToken }
|
|
26029
|
+
}),
|
|
26030
|
+
...bearerHeader && { bridgeAuthorization: bearerHeader },
|
|
26031
|
+
sign: false
|
|
25574
26032
|
};
|
|
25575
|
-
}
|
|
25576
|
-
function webFunctionAssumeRole(assumeRole, config) {
|
|
25577
|
-
if (assumeRole === true && isIamRoleArn(config.executionRoleArn)) return config.executionRoleArn;
|
|
25578
|
-
return assumeRole;
|
|
25579
|
-
}
|
|
25580
|
-
function finalizeWebFunctionEnv(lambdaEnv, opts) {
|
|
25581
|
-
const env = { ...lambdaEnv };
|
|
25582
|
-
delete env["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"];
|
|
25583
|
-
delete env["AWS_LAMBDA_FUNCTION_VERSION"];
|
|
25584
|
-
delete env["AWS_LAMBDA_FUNCTION_TIMEOUT"];
|
|
25585
|
-
env["AWS_LAMBDA_FUNCTION_NAME"] = opts.functionName;
|
|
25586
|
-
env["AWS_LAMBDA_LOG_GROUP_NAME"] = opts.logGroup ?? `/aws/lambda/${opts.functionName}`;
|
|
25587
|
-
if (opts.timeoutSeconds !== void 0) env["AWS_LAMBDA_FUNCTION_TIMEOUT"] = String(opts.timeoutSeconds);
|
|
25588
|
-
const replacedHttpEndpoint = Object.hasOwn(lambdaEnv, WEB_FUNCTION_HTTP_ENDPOINT_ENV);
|
|
25589
|
-
env[WEB_FUNCTION_HTTP_ENDPOINT_ENV] = `0.0.0.0:${opts.containerPort}`;
|
|
26033
|
+
if (sigv4Active) return { sign: true };
|
|
25590
26034
|
return {
|
|
25591
|
-
|
|
25592
|
-
|
|
26035
|
+
...bearerHeader && { bridgeAuthorization: bearerHeader },
|
|
26036
|
+
sign: false
|
|
25593
26037
|
};
|
|
25594
26038
|
}
|
|
25595
|
-
function
|
|
25596
|
-
const
|
|
25597
|
-
const
|
|
25598
|
-
|
|
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;
|
|
26039
|
+
function readAuthorizationHeader(headers) {
|
|
26040
|
+
const raw = headers["authorization"];
|
|
26041
|
+
const v = Array.isArray(raw) ? raw[0] : raw;
|
|
26042
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
25612
26043
|
}
|
|
25613
26044
|
|
|
25614
26045
|
//#endregion
|
|
25615
|
-
//#region src/local
|
|
25616
|
-
|
|
25617
|
-
const
|
|
25618
|
-
|
|
25619
|
-
|
|
25620
|
-
|
|
25621
|
-
|
|
25622
|
-
|
|
26046
|
+
//#region src/cli/commands/local-start-agentcore.ts
|
|
26047
|
+
function parsePort$2(raw) {
|
|
26048
|
+
const n = Number(raw);
|
|
26049
|
+
if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
|
|
26050
|
+
return n;
|
|
26051
|
+
}
|
|
26052
|
+
function resolveAgentCoreServePlan(protocol) {
|
|
26053
|
+
if (protocol === "MCP") return {
|
|
26054
|
+
containerPort: MCP_CONTAINER_PORT,
|
|
26055
|
+
containerPortLabel: `${MCP_CONTAINER_PORT}${MCP_PATH}`,
|
|
26056
|
+
routes: [{
|
|
26057
|
+
method: "POST",
|
|
26058
|
+
path: MCP_PATH
|
|
26059
|
+
}],
|
|
26060
|
+
attachWs: false,
|
|
26061
|
+
readyPath: MCP_PATH
|
|
26062
|
+
};
|
|
26063
|
+
if (protocol === "A2A") return {
|
|
26064
|
+
containerPort: A2A_CONTAINER_PORT,
|
|
26065
|
+
containerPortLabel: `${A2A_CONTAINER_PORT}${"/"}`,
|
|
26066
|
+
routes: [{
|
|
26067
|
+
method: "POST",
|
|
26068
|
+
path: "/"
|
|
26069
|
+
}],
|
|
26070
|
+
attachWs: false,
|
|
26071
|
+
readyPath: "/"
|
|
26072
|
+
};
|
|
25623
26073
|
return {
|
|
25624
|
-
|
|
25625
|
-
|
|
25626
|
-
|
|
25627
|
-
|
|
25628
|
-
|
|
26074
|
+
containerPort: void 0,
|
|
26075
|
+
containerPortLabel: "8080",
|
|
26076
|
+
routes: [{
|
|
26077
|
+
method: "POST",
|
|
26078
|
+
path: "/invocations"
|
|
26079
|
+
}, {
|
|
26080
|
+
method: "GET",
|
|
26081
|
+
path: "/ping"
|
|
25629
26082
|
}],
|
|
25630
|
-
|
|
25631
|
-
|
|
25632
|
-
workingDir: spec.codeMountPath,
|
|
25633
|
-
label: start.label
|
|
26083
|
+
attachWs: true,
|
|
26084
|
+
readyPath: void 0
|
|
25634
26085
|
};
|
|
25635
26086
|
}
|
|
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 = "";
|
|
26087
|
+
async function localStartAgentCoreCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
26088
|
+
const logger = getLogger();
|
|
26089
|
+
if (options.verbose) logger.setLevel("debug");
|
|
26090
|
+
let containerId;
|
|
26091
|
+
let stopLogs;
|
|
26092
|
+
let server;
|
|
26093
|
+
let profileCredsFile;
|
|
26094
|
+
let stateProvider;
|
|
26095
|
+
let watcher;
|
|
26096
|
+
let reloadChain = Promise.resolve();
|
|
26097
|
+
let shuttingDown = false;
|
|
26098
|
+
let tornDown = false;
|
|
26099
|
+
const teardown = async () => {
|
|
26100
|
+
if (tornDown) return;
|
|
26101
|
+
tornDown = true;
|
|
26102
|
+
if (watcher) try {
|
|
26103
|
+
await watcher.close();
|
|
26104
|
+
} catch (err) {
|
|
26105
|
+
logger.debug(`watcher close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25668
26106
|
}
|
|
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");
|
|
26107
|
+
try {
|
|
26108
|
+
await reloadChain;
|
|
26109
|
+
} catch {}
|
|
26110
|
+
if (server) try {
|
|
26111
|
+
await server.close();
|
|
26112
|
+
} catch (err) {
|
|
26113
|
+
logger.debug(`server close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26114
|
+
}
|
|
26115
|
+
if (stopLogs) try {
|
|
26116
|
+
stopLogs();
|
|
26117
|
+
} catch (err) {
|
|
26118
|
+
logger.debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26119
|
+
}
|
|
26120
|
+
if (containerId) try {
|
|
26121
|
+
await removeContainer(containerId);
|
|
26122
|
+
} catch (err) {
|
|
26123
|
+
logger.debug(`removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25725
26124
|
}
|
|
26125
|
+
if (stateProvider) try {
|
|
26126
|
+
stateProvider.dispose();
|
|
26127
|
+
} catch {}
|
|
26128
|
+
if (profileCredsFile) try {
|
|
26129
|
+
await profileCredsFile.dispose();
|
|
26130
|
+
} catch {}
|
|
25726
26131
|
};
|
|
25727
|
-
|
|
25728
|
-
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
25739
|
-
|
|
25740
|
-
|
|
25741
|
-
|
|
25742
|
-
|
|
25743
|
-
|
|
25744
|
-
|
|
25745
|
-
|
|
26132
|
+
await applyRoleArnIfSet({
|
|
26133
|
+
roleArn: options.roleArn,
|
|
26134
|
+
region: options.region,
|
|
26135
|
+
profile: options.profile
|
|
26136
|
+
});
|
|
26137
|
+
await ensureDockerAvailable();
|
|
26138
|
+
const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
|
|
26139
|
+
if (options.profile && profileCredentials) profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);
|
|
26140
|
+
const appCmd = resolveApp(options.app);
|
|
26141
|
+
if (!appCmd) throw new Error(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`);
|
|
26142
|
+
logger.info("Synthesizing CDK app...");
|
|
26143
|
+
const synthesizer = hostSynthesizer ?? new Synthesizer();
|
|
26144
|
+
const context = parseContextOptions(options.context);
|
|
26145
|
+
const synthOpts = {
|
|
26146
|
+
app: appCmd,
|
|
26147
|
+
output: options.output,
|
|
26148
|
+
...options.region && { region: options.region },
|
|
26149
|
+
...options.profile && { profile: options.profile },
|
|
26150
|
+
...Object.keys(context).length > 0 && { context }
|
|
26151
|
+
};
|
|
26152
|
+
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
26153
|
+
const resolvedTarget = await resolveSingleTarget(target, {
|
|
26154
|
+
entries: listTargets(stacks).agentCoreRuntimes,
|
|
26155
|
+
message: "Select an AgentCore Runtime to serve",
|
|
26156
|
+
noun: "AgentCore Runtimes",
|
|
26157
|
+
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")
|
|
26158
|
+
});
|
|
26159
|
+
const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
26160
|
+
stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
|
|
26161
|
+
const { context: imageContext, loaded: loadedState } = stateProvider && candidate ? await buildAgentCoreImageContext(candidate, stateProvider, options) : {
|
|
26162
|
+
context: void 0,
|
|
26163
|
+
loaded: void 0
|
|
26164
|
+
};
|
|
26165
|
+
const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);
|
|
26166
|
+
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.logicalId}`)} (${resolved.protocol})`);
|
|
26167
|
+
const plan = resolveAgentCoreServePlan(resolved.protocol);
|
|
26168
|
+
const sigv4Context = await resolveAgentCoreSigV4Context(options, resolved, loadedState, stateProvider);
|
|
26169
|
+
const authPlan = selectServeInboundAuth(resolved, options, sigv4Context !== void 0);
|
|
26170
|
+
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)" : ""}.`);
|
|
26171
|
+
await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);
|
|
26172
|
+
const image = await resolveAgentCoreImage(resolved, options, loadedState, stateProvider);
|
|
26173
|
+
const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext);
|
|
26174
|
+
let containerHostPort = await pickFreePort();
|
|
26175
|
+
const containerHost = options.containerHost;
|
|
26176
|
+
const containerName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
26177
|
+
logger.info(`Starting agent container (image=${image}, port=${containerHostPort} -> ${plan.containerPortLabel})...`);
|
|
26178
|
+
containerId = await runDetached({
|
|
26179
|
+
image,
|
|
26180
|
+
mounts: [],
|
|
26181
|
+
env: dockerEnv,
|
|
26182
|
+
cmd: [],
|
|
26183
|
+
hostPort: containerHostPort,
|
|
26184
|
+
host: containerHost,
|
|
26185
|
+
platform: options.platform,
|
|
26186
|
+
name: containerName,
|
|
26187
|
+
...plan.containerPort !== void 0 && { containerPort: plan.containerPort },
|
|
26188
|
+
...sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys }
|
|
26189
|
+
});
|
|
26190
|
+
stopLogs = streamLogs(containerId);
|
|
26191
|
+
const shutdown = async (signal, exitCode) => {
|
|
26192
|
+
if (shuttingDown) return;
|
|
26193
|
+
shuttingDown = true;
|
|
26194
|
+
logger.info(`Received ${signal}, shutting down...`);
|
|
26195
|
+
await teardown();
|
|
26196
|
+
process.exit(exitCode);
|
|
26197
|
+
};
|
|
26198
|
+
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
26199
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
26200
|
+
try {
|
|
26201
|
+
if (plan.readyPath === void 0) await waitForAgentCorePing(containerHost, containerHostPort, options.timeout);
|
|
26202
|
+
else await waitForAgentCoreHttpReady(containerHost, containerHostPort, plan.readyPath, options.timeout);
|
|
26203
|
+
const signRequest = authPlan.sign && sigv4Context ? async ({ method, path, body, sessionId }) => {
|
|
26204
|
+
const signed = await signAgentCoreInvocation({
|
|
26205
|
+
credentials: sigv4Context.credentials,
|
|
26206
|
+
region: sigv4Context.region,
|
|
26207
|
+
host: containerHost,
|
|
26208
|
+
port: containerHostPort,
|
|
26209
|
+
path,
|
|
26210
|
+
body,
|
|
26211
|
+
sessionId,
|
|
26212
|
+
method
|
|
25746
26213
|
});
|
|
26214
|
+
const h = {
|
|
26215
|
+
Authorization: signed.authorization,
|
|
26216
|
+
"X-Amz-Date": signed.amzDate,
|
|
26217
|
+
"X-Amz-Content-Sha256": signed.amzContentSha256
|
|
26218
|
+
};
|
|
26219
|
+
if (signed.amzSecurityToken) h["X-Amz-Security-Token"] = signed.amzSecurityToken;
|
|
26220
|
+
return h;
|
|
26221
|
+
} : void 0;
|
|
26222
|
+
server = await startAgentCoreHttpServer({
|
|
26223
|
+
containerHost,
|
|
26224
|
+
containerPort: containerHostPort,
|
|
26225
|
+
host: options.host,
|
|
26226
|
+
port: options.port,
|
|
26227
|
+
routes: plan.routes,
|
|
26228
|
+
attachWs: plan.attachWs,
|
|
26229
|
+
...authPlan.bridgeAuthorization && { authorization: authPlan.bridgeAuthorization },
|
|
26230
|
+
...authPlan.authCheck && { authCheck: authPlan.authCheck },
|
|
26231
|
+
...signRequest && { signRequest },
|
|
26232
|
+
...options.sessionId && { sessionId: options.sessionId }
|
|
25747
26233
|
});
|
|
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;
|
|
26234
|
+
} catch (err) {
|
|
26235
|
+
await teardown();
|
|
26236
|
+
throw err;
|
|
25767
26237
|
}
|
|
25768
|
-
|
|
25769
|
-
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25773
|
-
|
|
25774
|
-
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25779
|
-
|
|
25780
|
-
|
|
25781
|
-
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
26238
|
+
if (server.wsUrl) {
|
|
26239
|
+
logger.info(`Server listening on ${server.wsUrl} (${displayIdent(resolved.logicalId)} (AgentCore WebSocket))`);
|
|
26240
|
+
logger.info(`HTTP contract served on ${server.httpUrl} — POST ${server.httpUrl}/invocations, GET ${server.httpUrl}/ping`);
|
|
26241
|
+
} else {
|
|
26242
|
+
const contractPath = plan.routes[0]?.path ?? "/";
|
|
26243
|
+
const contractUrl = `${server.httpUrl}${contractPath}`;
|
|
26244
|
+
logger.info(`Server listening on ${server.httpUrl} (${displayIdent(resolved.logicalId)})`);
|
|
26245
|
+
logger.info(`${resolved.protocol} contract served on ${contractUrl} — POST ${contractUrl}`);
|
|
26246
|
+
}
|
|
26247
|
+
logger.info("Press ^C to shut down.");
|
|
26248
|
+
if (options.watch) {
|
|
26249
|
+
const cdkOutDir = options.output;
|
|
26250
|
+
const assetLoader = new AssetManifestLoader();
|
|
26251
|
+
let currentAssetHash = await deriveOldAssetHash({
|
|
26252
|
+
resolvedTarget,
|
|
26253
|
+
resolved,
|
|
26254
|
+
stacks,
|
|
26255
|
+
cdkOutDir,
|
|
26256
|
+
assetLoader
|
|
26257
|
+
});
|
|
26258
|
+
const waitReady = async (port) => {
|
|
26259
|
+
if (plan.readyPath === void 0) await waitForAgentCorePing(containerHost, port, options.timeout);
|
|
26260
|
+
else await waitForAgentCoreHttpReady(containerHost, port, plan.readyPath, options.timeout);
|
|
25787
26261
|
};
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
26262
|
+
const watchRoot = process.cwd();
|
|
26263
|
+
const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
|
|
26264
|
+
watchRoot,
|
|
26265
|
+
output: options.output,
|
|
26266
|
+
watchConfig: resolveWatchConfig()
|
|
26267
|
+
});
|
|
26268
|
+
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}). The warm container reloads in place; the serve stays up.`);
|
|
26269
|
+
watcher = createFileWatcher({
|
|
26270
|
+
paths: [watchRoot],
|
|
26271
|
+
ignored,
|
|
26272
|
+
shouldTrigger,
|
|
26273
|
+
onChange: (changedPaths) => {
|
|
26274
|
+
reloadChain = reloadChain.then(async () => {
|
|
26275
|
+
if (tornDown || shuttingDown) return;
|
|
26276
|
+
let freshStacks;
|
|
26277
|
+
let freshResolved;
|
|
26278
|
+
let freshLoaded;
|
|
26279
|
+
let freshImageContext;
|
|
26280
|
+
let verdict;
|
|
26281
|
+
let nextAssetHash = currentAssetHash;
|
|
26282
|
+
try {
|
|
26283
|
+
freshStacks = (await synthesizer.synthesize(synthOpts)).stacks;
|
|
26284
|
+
const freshCandidate = pickAgentCoreCandidateStack(resolvedTarget, freshStacks);
|
|
26285
|
+
const ctx = stateProvider && freshCandidate ? await buildAgentCoreImageContext(freshCandidate, stateProvider, options) : {
|
|
26286
|
+
context: void 0,
|
|
26287
|
+
loaded: void 0
|
|
26288
|
+
};
|
|
26289
|
+
freshImageContext = ctx.context;
|
|
26290
|
+
freshLoaded = ctx.loaded;
|
|
26291
|
+
freshResolved = resolveAgentCoreTarget(resolvedTarget, freshStacks, freshImageContext);
|
|
26292
|
+
await resolveFromS3BucketIntrinsic(freshResolved, stateProvider, freshLoaded, freshImageContext);
|
|
26293
|
+
const assetCtx = await loadAgentCoreAssetContext({
|
|
26294
|
+
resolvedTarget,
|
|
26295
|
+
resolved: freshResolved,
|
|
26296
|
+
stacks: freshStacks,
|
|
26297
|
+
cdkOutDir,
|
|
26298
|
+
assetLoader,
|
|
26299
|
+
oldAssetHash: currentAssetHash
|
|
26300
|
+
});
|
|
26301
|
+
if (assetCtx?.newAssetHash !== void 0) nextAssetHash = assetCtx.newAssetHash;
|
|
26302
|
+
verdict = classifySourceChange(changedPaths, assetCtx);
|
|
26303
|
+
logger.info(`Detected source change (${changedPaths.length} path(s)); verdict=${verdict.kind} (${verdict.reason}).`);
|
|
26304
|
+
} catch (err) {
|
|
26305
|
+
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.`);
|
|
26306
|
+
return;
|
|
26307
|
+
}
|
|
26308
|
+
if (tornDown || shuttingDown) return;
|
|
26309
|
+
try {
|
|
26310
|
+
if (verdict.kind === "soft-reload") {
|
|
26311
|
+
if (!containerId) throw new CdkLocalError("Reload: no live container to docker cp / docker restart into.", "LOCAL_START_AGENTCORE_WATCH_NO_CONTAINER");
|
|
26312
|
+
await softReloadAgentContainer(containerId, verdict.newAssetSourceDir);
|
|
26313
|
+
await waitReady(containerHostPort);
|
|
26314
|
+
logger.info("Reload: soft-reloaded the agent container (docker cp + docker restart; container ID + host port preserved).");
|
|
26315
|
+
} else {
|
|
26316
|
+
if (stopLogs) {
|
|
26317
|
+
try {
|
|
26318
|
+
stopLogs();
|
|
26319
|
+
} catch {}
|
|
26320
|
+
stopLogs = void 0;
|
|
26321
|
+
}
|
|
26322
|
+
if (containerId) {
|
|
26323
|
+
await removeContainer(containerId);
|
|
26324
|
+
containerId = void 0;
|
|
26325
|
+
}
|
|
26326
|
+
const newImage = await resolveAgentCoreImage(freshResolved, options, freshLoaded, stateProvider);
|
|
26327
|
+
const { env: newEnv, sensitiveEnvKeys: newSensitive } = await buildContainerEnv(freshResolved, options, profileCredentials, profileCredsFile, stateProvider, freshLoaded, freshImageContext);
|
|
26328
|
+
const newPort = await pickFreePort();
|
|
26329
|
+
const newName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
26330
|
+
logger.info(`Reload: starting agent container (image=${newImage}, port=${newPort} -> ${plan.containerPortLabel})...`);
|
|
26331
|
+
containerId = await runDetached({
|
|
26332
|
+
image: newImage,
|
|
26333
|
+
mounts: [],
|
|
26334
|
+
env: newEnv,
|
|
26335
|
+
cmd: [],
|
|
26336
|
+
hostPort: newPort,
|
|
26337
|
+
host: containerHost,
|
|
26338
|
+
platform: options.platform,
|
|
26339
|
+
name: newName,
|
|
26340
|
+
...plan.containerPort !== void 0 && { containerPort: plan.containerPort },
|
|
26341
|
+
...newSensitive.size > 0 && { sensitiveEnvKeys: newSensitive }
|
|
26342
|
+
});
|
|
26343
|
+
stopLogs = streamLogs(containerId);
|
|
26344
|
+
containerHostPort = newPort;
|
|
26345
|
+
await waitReady(newPort);
|
|
26346
|
+
server?.setContainerPort(newPort);
|
|
26347
|
+
logger.info(`Reload: rebuilt the agent container; serve re-pointed to port ${newPort}.`);
|
|
26348
|
+
}
|
|
26349
|
+
currentAssetHash = nextAssetHash;
|
|
26350
|
+
logger.info("Reload complete.");
|
|
26351
|
+
} catch (err) {
|
|
26352
|
+
logger.error(`Reload failed: ${err instanceof Error ? err.message : String(err)}. The serve stays up; edit + save again to retry.`);
|
|
26353
|
+
}
|
|
26354
|
+
});
|
|
25795
26355
|
}
|
|
25796
|
-
resolveLogs(Buffer.concat(chunks).toString().split("\n").map((l) => l.replace(/\r$/, "")).filter((l) => l.length > 0).slice(-lines));
|
|
25797
26356
|
});
|
|
25798
|
-
}
|
|
26357
|
+
}
|
|
26358
|
+
await new Promise(() => void 0);
|
|
26359
|
+
}
|
|
26360
|
+
function createLocalStartAgentCoreCommand(opts = {}) {
|
|
26361
|
+
setEmbedConfig(opts.embedConfig);
|
|
26362
|
+
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) => {
|
|
26363
|
+
await localStartAgentCoreCommand(target, options, opts.extraStateProviders, opts.synthesizer);
|
|
26364
|
+
}));
|
|
26365
|
+
addStartAgentCoreSpecificOptions(cmd);
|
|
26366
|
+
[
|
|
26367
|
+
...commonOptions(),
|
|
26368
|
+
...appOptions(),
|
|
26369
|
+
...contextOptions
|
|
26370
|
+
].forEach((opt) => cmd.addOption(opt));
|
|
26371
|
+
cmd.addOption(regionOption);
|
|
26372
|
+
return cmd;
|
|
26373
|
+
}
|
|
26374
|
+
function addStartAgentCoreSpecificOptions(cmd) {
|
|
26375
|
+
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
26376
|
}
|
|
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
26377
|
|
|
25808
26378
|
//#endregion
|
|
25809
26379
|
//#region src/cli/commands/local-start-web-function.ts
|
|
@@ -25812,43 +26382,10 @@ function parsePort$1(raw) {
|
|
|
25812
26382
|
if (!Number.isInteger(n) || n < 0 || n > 65535) throw new InvalidArgumentError(`--port must be an integer 0-65535, got '${raw}'.`);
|
|
25813
26383
|
return n;
|
|
25814
26384
|
}
|
|
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
26385
|
async function localStartWebFunctionCommand(target, options, extraStateProviders, hostSynthesizer) {
|
|
25821
26386
|
const logger = getLogger();
|
|
25822
26387
|
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
26388
|
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
26389
|
await applyRoleArnIfSet({
|
|
25853
26390
|
roleArn: options.roleArn,
|
|
25854
26391
|
region: options.region,
|
|
@@ -25875,93 +26412,60 @@ async function localStartWebFunctionCommand(target, options, extraStateProviders
|
|
|
25875
26412
|
noun: "Lambda web functions",
|
|
25876
26413
|
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
26414
|
}), stacks, options.revision);
|
|
25878
|
-
const
|
|
25879
|
-
const spec = resolveWebFunctionRuntime(code.runtime);
|
|
26415
|
+
const prepared = prepareWebFunctionServe(resolved);
|
|
25880
26416
|
const selectionNote = {
|
|
25881
26417
|
"target-revision": "the target revision",
|
|
25882
26418
|
"revision-flag": "--revision",
|
|
25883
26419
|
"endpoint-weight": `highest weight on endpoint ${displayIdent(resolved.endpoint?.logicalId ?? "")}`,
|
|
25884
26420
|
"only-revision": "the function's only revision"
|
|
25885
26421
|
};
|
|
25886
|
-
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.revisionLogicalId}`)} (${spec.runtime}; revision chosen by ${selectionNote[resolved.selection]})`);
|
|
25887
|
-
const
|
|
25888
|
-
|
|
25889
|
-
|
|
26422
|
+
logger.info(`Target: ${displayStackName(`${resolved.stack.stackName}/${resolved.revisionLogicalId}`)} (${prepared.spec.runtime}; revision chosen by ${selectionNote[resolved.selection]})`);
|
|
26423
|
+
for (const line of webFunctionFidelityWarnings(resolved, prepared.serviceConfig)) logger.warn(line);
|
|
26424
|
+
const serve = new WebFunctionServe(prepared, {
|
|
26425
|
+
envOptions: options,
|
|
26426
|
+
...profileCredentials !== void 0 && { profileCredentials },
|
|
26427
|
+
...extraStateProviders !== void 0 && { extraStateProviders },
|
|
26428
|
+
host: options.host,
|
|
26429
|
+
port: options.port,
|
|
26430
|
+
...options.platform !== void 0 && { platform: options.platform },
|
|
26431
|
+
skipPull: options.pull === false,
|
|
26432
|
+
timeoutMs: options.timeout
|
|
26433
|
+
});
|
|
26434
|
+
let shuttingDown = false;
|
|
25890
26435
|
const shutdown = async (signal, exitCode) => {
|
|
25891
26436
|
if (shuttingDown) {
|
|
25892
|
-
if (!waitingOnRun) {
|
|
26437
|
+
if (!serve.waitingOnRun) {
|
|
25893
26438
|
logger.info(`Received ${signal} again; still removing the container, please wait...`);
|
|
25894
26439
|
return;
|
|
25895
26440
|
}
|
|
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>"}`);
|
|
26441
|
+
serve.disposeArtifactNow();
|
|
26442
|
+
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
26443
|
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
25899
26444
|
return;
|
|
25900
26445
|
}
|
|
25901
26446
|
shuttingDown = true;
|
|
25902
26447
|
logger.info(`Received ${signal}, shutting down...`);
|
|
25903
|
-
await teardown();
|
|
26448
|
+
await serve.teardown();
|
|
25904
26449
|
process.exit(exitCode);
|
|
25905
26450
|
};
|
|
25906
26451
|
process.on("SIGINT", () => void shutdown("SIGINT", 130));
|
|
25907
26452
|
process.on("SIGTERM", () => void shutdown("SIGTERM", 0));
|
|
25908
|
-
const recent = new RecentOutput();
|
|
25909
26453
|
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))`);
|
|
26454
|
+
const address = await serve.start();
|
|
26455
|
+
if (!address) return await forever();
|
|
26456
|
+
logger.info(`Server listening on http://${formatAuthority(address.host, address.port)} (${displayIdent(resolved.functionName)} (Lambda web function))`);
|
|
25954
26457
|
logger.info("Press ^C to shut down.");
|
|
25955
26458
|
} catch (err) {
|
|
25956
26459
|
if (shuttingDown) return await forever();
|
|
25957
|
-
await teardown();
|
|
26460
|
+
await serve.teardown();
|
|
25958
26461
|
throw err;
|
|
25959
26462
|
}
|
|
25960
|
-
|
|
25961
|
-
|
|
25962
|
-
|
|
25963
|
-
|
|
25964
|
-
await
|
|
26463
|
+
const exited = serve.exited;
|
|
26464
|
+
if (!exited) return await forever();
|
|
26465
|
+
const exitCode = await exited;
|
|
26466
|
+
if (shuttingDown || serve.tornDown) return await forever();
|
|
26467
|
+
const failure = await serve.exitFailure(`The web function container exited (exit code ${exitCode ?? "unknown"}) while serving.`);
|
|
26468
|
+
await serve.teardown();
|
|
25965
26469
|
throw failure;
|
|
25966
26470
|
}
|
|
25967
26471
|
function createLocalStartWebFunctionCommand(opts = {}) {
|
|
@@ -32483,5 +32987,5 @@ function addStudioSpecificOptions(cmd) {
|
|
|
32483
32987
|
}
|
|
32484
32988
|
|
|
32485
32989
|
//#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-
|
|
32990
|
+
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 };
|
|
32991
|
+
//# sourceMappingURL=local-studio-vhvbqvPb.js.map
|