cdk-local 0.75.0 → 0.76.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 +6 -3
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +1 -15
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/{local-list-cmhJbUBN.d.ts → local-list-CIg9Zn3R.d.ts} +34 -19
- package/dist/local-list-CIg9Zn3R.d.ts.map +1 -0
- package/dist/{local-list-CFINvNxo.js → local-list-Dh6xjxGf.js} +554 -170
- package/dist/local-list-Dh6xjxGf.js.map +1 -0
- package/package.json +1 -1
- package/dist/local-list-CFINvNxo.js.map +0 -1
- package/dist/local-list-cmhJbUBN.d.ts.map +0 -1
|
@@ -6483,7 +6483,7 @@ function resolveRuntimeCodeMountPath(runtime) {
|
|
|
6483
6483
|
|
|
6484
6484
|
//#endregion
|
|
6485
6485
|
//#region src/local/docker-runner.ts
|
|
6486
|
-
const execFileAsync$
|
|
6486
|
+
const execFileAsync$5 = promisify(execFile);
|
|
6487
6487
|
/**
|
|
6488
6488
|
* Wraps `docker pull` / `docker run` / `docker rm` for `cdkl invoke`.
|
|
6489
6489
|
*
|
|
@@ -6580,7 +6580,7 @@ async function runDetached(opts) {
|
|
|
6580
6580
|
args.push(opts.image, ...entryPointTail, ...opts.cmd);
|
|
6581
6581
|
getLogger().child("docker").debug(`${getDockerCmd()} ${redactAwsCredentialsInArgs(args).join(" ")}`);
|
|
6582
6582
|
try {
|
|
6583
|
-
const { stdout } = await execFileAsync$
|
|
6583
|
+
const { stdout } = await execFileAsync$5(getDockerCmd(), args, {
|
|
6584
6584
|
maxBuffer: 10 * 1024 * 1024,
|
|
6585
6585
|
...execEnvForSecrets(passthroughEnv)
|
|
6586
6586
|
});
|
|
@@ -6620,7 +6620,7 @@ async function removeContainer(containerId) {
|
|
|
6620
6620
|
if (!containerId) return;
|
|
6621
6621
|
const logger = getLogger().child("docker");
|
|
6622
6622
|
try {
|
|
6623
|
-
await execFileAsync$
|
|
6623
|
+
await execFileAsync$5(getDockerCmd(), [
|
|
6624
6624
|
"rm",
|
|
6625
6625
|
"-f",
|
|
6626
6626
|
containerId
|
|
@@ -6639,7 +6639,7 @@ async function removeContainer(containerId) {
|
|
|
6639
6639
|
async function ensureDockerAvailable() {
|
|
6640
6640
|
const cmd = getDockerCmd();
|
|
6641
6641
|
try {
|
|
6642
|
-
await execFileAsync$
|
|
6642
|
+
await execFileAsync$5(cmd, [
|
|
6643
6643
|
"version",
|
|
6644
6644
|
"--format",
|
|
6645
6645
|
"{{.Server.Version}}"
|
|
@@ -17666,6 +17666,167 @@ function addInvokeSpecificOptions(cmd) {
|
|
|
17666
17666
|
return cmd.addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--no-pull", "Skip docker pull. Semantics differ by code path: ZIP Lambdas skip pulling the public Lambda base image; Container Lambdas on the local-build path are a no-op (docker build does not refresh the FROM cache by default); Container Lambdas on the ECR-pull fallback skip docker pull AND error if the image is not in the local cache (re-run without --no-pull or pre-pull manually).")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host IP the host uses to bind the RIE port to. Must be a numeric IP (Docker rejects hostnames here). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from state (requires an active state source); (3) `--no-assume-role` explicitly opts out. Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers. Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in env vars with 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 an explicit value when CFn stack name differs. Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
|
|
17667
17667
|
}
|
|
17668
17668
|
|
|
17669
|
+
//#endregion
|
|
17670
|
+
//#region src/local/source-change-classifier.ts
|
|
17671
|
+
/**
|
|
17672
|
+
* Dependency-manifest basenames recognized by the classifier. A change
|
|
17673
|
+
* to any of these forces a rebuild because the running container's
|
|
17674
|
+
* pre-built dependency layer is no longer in sync with the source.
|
|
17675
|
+
*
|
|
17676
|
+
* Coverage: the package managers commonly used inside a Lambda /
|
|
17677
|
+
* container image — Node (pnpm / npm / yarn), Python (pip / poetry /
|
|
17678
|
+
* pipenv), Ruby (bundler), Go (modules), Rust (cargo), Java / Kotlin
|
|
17679
|
+
* (Maven, Gradle). Adding a new ecosystem? Append its lockfile +
|
|
17680
|
+
* manifest here and add a classifier test row.
|
|
17681
|
+
*/
|
|
17682
|
+
const REBUILD_TRIGGER_BASENAMES = new Set([
|
|
17683
|
+
"package.json",
|
|
17684
|
+
"package-lock.json",
|
|
17685
|
+
"pnpm-lock.yaml",
|
|
17686
|
+
"yarn.lock",
|
|
17687
|
+
"npm-shrinkwrap.json",
|
|
17688
|
+
"requirements.txt",
|
|
17689
|
+
"requirements-dev.txt",
|
|
17690
|
+
"pyproject.toml",
|
|
17691
|
+
"poetry.lock",
|
|
17692
|
+
"Pipfile",
|
|
17693
|
+
"Pipfile.lock",
|
|
17694
|
+
"uv.lock",
|
|
17695
|
+
"Gemfile",
|
|
17696
|
+
"Gemfile.lock",
|
|
17697
|
+
"go.mod",
|
|
17698
|
+
"go.sum",
|
|
17699
|
+
"Cargo.toml",
|
|
17700
|
+
"Cargo.lock",
|
|
17701
|
+
"pom.xml",
|
|
17702
|
+
"build.gradle",
|
|
17703
|
+
"build.gradle.kts",
|
|
17704
|
+
"settings.gradle",
|
|
17705
|
+
"settings.gradle.kts",
|
|
17706
|
+
"Makefile",
|
|
17707
|
+
"CMakeLists.txt"
|
|
17708
|
+
]);
|
|
17709
|
+
/**
|
|
17710
|
+
* Compiled-language source extensions that require a build step
|
|
17711
|
+
* inside `docker build` (typically a `RUN go build` / `cargo build` /
|
|
17712
|
+
* `mvn package` etc.). A copy of the source alone would leave the
|
|
17713
|
+
* running binary stale, so the user's intent must be a rebuild.
|
|
17714
|
+
*
|
|
17715
|
+
* TypeScript source (`.ts` / `.tsx` / `.mts` / `.cts`) is treated as
|
|
17716
|
+
* compiled because the dominant production-container pattern is to
|
|
17717
|
+
* pre-compile the source via a Dockerfile `RUN tsc` / `RUN yarn build`
|
|
17718
|
+
* step, with the runtime executing the emitted `dist/*.js`. Soft-reload
|
|
17719
|
+
* would `docker cp` the new `.ts` into the container's WORKDIR while
|
|
17720
|
+
* the running process keeps reading the OLD `dist/` — a silent
|
|
17721
|
+
* stale-code failure that violates the file's "slow-but-correct beats
|
|
17722
|
+
* fast-but-stale" default policy (lines 14-22). Setups that transpile
|
|
17723
|
+
* at runtime lose the soft-reload fast path under this default; an
|
|
17724
|
+
* opt-in flag to restore it is a possible follow-up but is not in
|
|
17725
|
+
* scope here.
|
|
17726
|
+
*
|
|
17727
|
+
* Interpreted-language runtimes (Node — `.js` / `.mjs` / `.cjs`,
|
|
17728
|
+
* Python — `.py`, Ruby — `.rb`, shell — `.sh`) read source at process
|
|
17729
|
+
* start, so a `docker cp` + `docker restart` cycle picks them up.
|
|
17730
|
+
* Those extensions are NOT in this set.
|
|
17731
|
+
*/
|
|
17732
|
+
const COMPILED_LANGUAGE_EXTENSIONS = new Set([
|
|
17733
|
+
".go",
|
|
17734
|
+
".rs",
|
|
17735
|
+
".java",
|
|
17736
|
+
".kt",
|
|
17737
|
+
".kts",
|
|
17738
|
+
".scala",
|
|
17739
|
+
".cs",
|
|
17740
|
+
".swift",
|
|
17741
|
+
".fs",
|
|
17742
|
+
".fsx",
|
|
17743
|
+
".c",
|
|
17744
|
+
".cc",
|
|
17745
|
+
".cpp",
|
|
17746
|
+
".cxx",
|
|
17747
|
+
".h",
|
|
17748
|
+
".hpp",
|
|
17749
|
+
".zig",
|
|
17750
|
+
".ml",
|
|
17751
|
+
".mli",
|
|
17752
|
+
".elm",
|
|
17753
|
+
".hs",
|
|
17754
|
+
".dart",
|
|
17755
|
+
".ts",
|
|
17756
|
+
".tsx",
|
|
17757
|
+
".mts",
|
|
17758
|
+
".cts"
|
|
17759
|
+
]);
|
|
17760
|
+
/**
|
|
17761
|
+
* Classify a single watcher firing into rebuild vs soft-reload. Pure
|
|
17762
|
+
* + synchronous. The caller (emulator's reload pathway) invokes this
|
|
17763
|
+
* once per target per firing AFTER `cdk synth` has run and the new
|
|
17764
|
+
* asset manifest is on disk.
|
|
17765
|
+
*
|
|
17766
|
+
* Branching:
|
|
17767
|
+
* 1. No asset context (image isn't a CDK asset, or asset lookup
|
|
17768
|
+
* failed) → `rebuild`.
|
|
17769
|
+
* 2. The asset hash didn't change between old and new synths
|
|
17770
|
+
* (`oldAssetHash === newAssetHash`, or `oldAssetHash` missing)
|
|
17771
|
+
* → `rebuild`. Load-bearing guard for "user edited a CDK
|
|
17772
|
+
* construct file (e.g. `lib/stack.ts`) that flipped the task
|
|
17773
|
+
* spec but didn't touch the asset content". Soft-reload would
|
|
17774
|
+
* `docker cp` identical files and `docker restart` the
|
|
17775
|
+
* container with the OLD task spec (env / memory / mounts /
|
|
17776
|
+
* added sidecars are set at `docker create` time, not on
|
|
17777
|
+
* restart) — the user's intent would silently NOT apply.
|
|
17778
|
+
* Forcing rebuild keeps Phase 1-3 semantics exactly for this
|
|
17779
|
+
* case: the rolling primitive boots a shadow with the new task
|
|
17780
|
+
* spec, the user sees their construct edit take effect.
|
|
17781
|
+
* 3. No changed paths (the watcher fired on a debounce flush with
|
|
17782
|
+
* an empty pending set — shouldn't happen in practice, but
|
|
17783
|
+
* defensive) → `rebuild`.
|
|
17784
|
+
* 4. Any changed path's basename matches the Dockerfile or a
|
|
17785
|
+
* dependency manifest → `rebuild`.
|
|
17786
|
+
* 5. Any changed path's extension is a compiled-language source →
|
|
17787
|
+
* `rebuild`.
|
|
17788
|
+
* 6. Else → `soft-reload`.
|
|
17789
|
+
*/
|
|
17790
|
+
function classifySourceChange(changedPaths, ctx) {
|
|
17791
|
+
if (!ctx) return {
|
|
17792
|
+
kind: "rebuild",
|
|
17793
|
+
reason: "target image is not a CDK docker-image asset"
|
|
17794
|
+
};
|
|
17795
|
+
if (!ctx.oldAssetHash || ctx.oldAssetHash === ctx.newAssetHash) return {
|
|
17796
|
+
kind: "rebuild",
|
|
17797
|
+
reason: "asset hash unchanged across the synth (CDK construct edit or unrelated file) — task-spec changes need a fresh `docker create`, which only the rebuild path runs"
|
|
17798
|
+
};
|
|
17799
|
+
if (changedPaths.length === 0) return {
|
|
17800
|
+
kind: "rebuild",
|
|
17801
|
+
reason: "no changed paths reported (defensive default)"
|
|
17802
|
+
};
|
|
17803
|
+
for (const p of changedPaths) {
|
|
17804
|
+
const basename = path.basename(p);
|
|
17805
|
+
if (basename === ctx.dockerFile) return {
|
|
17806
|
+
kind: "rebuild",
|
|
17807
|
+
reason: `Dockerfile edit (${basename})`
|
|
17808
|
+
};
|
|
17809
|
+
if (basename.startsWith("Dockerfile.")) return {
|
|
17810
|
+
kind: "rebuild",
|
|
17811
|
+
reason: `Dockerfile.* edit (${basename})`
|
|
17812
|
+
};
|
|
17813
|
+
if (REBUILD_TRIGGER_BASENAMES.has(basename)) return {
|
|
17814
|
+
kind: "rebuild",
|
|
17815
|
+
reason: `dependency manifest edit (${basename})`
|
|
17816
|
+
};
|
|
17817
|
+
const ext = path.extname(p).toLowerCase();
|
|
17818
|
+
if (COMPILED_LANGUAGE_EXTENSIONS.has(ext)) return {
|
|
17819
|
+
kind: "rebuild",
|
|
17820
|
+
reason: `compiled-language source edit (${basename}) — soft-reload would leave the built binary stale`
|
|
17821
|
+
};
|
|
17822
|
+
}
|
|
17823
|
+
return {
|
|
17824
|
+
kind: "soft-reload",
|
|
17825
|
+
reason: `${changedPaths.length} source-only path(s) — skipping rebuild`,
|
|
17826
|
+
newAssetSourceDir: ctx.newAssetSourceDir
|
|
17827
|
+
};
|
|
17828
|
+
}
|
|
17829
|
+
|
|
17669
17830
|
//#endregion
|
|
17670
17831
|
//#region src/local/agentcore-code-build.ts
|
|
17671
17832
|
/**
|
|
@@ -18434,6 +18595,17 @@ async function invokeAgentCoreWs(host, port, event, options) {
|
|
|
18434
18595
|
reject(/* @__PURE__ */ new Error(`AgentCore /ws at ${url} timed out after ${options.timeoutMs}ms. The agent may be hung or may not close the stream; check container logs.`));
|
|
18435
18596
|
});
|
|
18436
18597
|
}, options.timeoutMs);
|
|
18598
|
+
const onAbort = () => {
|
|
18599
|
+
finish(() => {
|
|
18600
|
+
stopIterator();
|
|
18601
|
+
try {
|
|
18602
|
+
ws.close();
|
|
18603
|
+
} catch {}
|
|
18604
|
+
resolve({ frames });
|
|
18605
|
+
});
|
|
18606
|
+
};
|
|
18607
|
+
if (options.abortSignal) if (options.abortSignal.aborted) onAbort();
|
|
18608
|
+
else options.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
18437
18609
|
let iterator;
|
|
18438
18610
|
const stopIterator = () => {
|
|
18439
18611
|
if (iterator?.return) try {
|
|
@@ -18580,6 +18752,12 @@ async function localInvokeAgentCoreCommand(target, options, extraStateProviders)
|
|
|
18580
18752
|
if ((isMcp || isA2a) && options.ws) logger.warn(`--ws applies only to the HTTP / AGUI protocols; ignoring it for this ${resolved.protocol} runtime.`);
|
|
18581
18753
|
if (options.wsInteractive && !options.ws) logger.warn("--ws-interactive is meaningful only with --ws; ignoring.");
|
|
18582
18754
|
if (options.sigv4 && (isMcp || isA2a || options.ws)) logger.warn("--sigv4 signs the HTTP /invocations request only; ignoring it for the " + (isMcp ? "MCP" : isA2a ? "A2A" : "/ws WebSocket") + " path.");
|
|
18755
|
+
const watchEligible = options.ws === true && !isMcp && !isA2a;
|
|
18756
|
+
const watchActive = options.watch === true && watchEligible;
|
|
18757
|
+
if (options.watch === true && !watchEligible) {
|
|
18758
|
+
const ignoredFor = isMcp ? "MCP" : isA2a ? "A2A" : "single-shot HTTP POST /invocations";
|
|
18759
|
+
logger.warn(`--watch is meaningful only with the long-running --ws / --ws-interactive session paths; ignoring it for the ${ignoredFor} path (single-shot invocations run once and exit).`);
|
|
18760
|
+
}
|
|
18583
18761
|
const sessionId = options.sessionId ?? randomUUID();
|
|
18584
18762
|
const event = await readEvent(options);
|
|
18585
18763
|
const mcpRequest = isMcp ? buildMcpRequest(event) : void 0;
|
|
@@ -18627,7 +18805,70 @@ async function localInvokeAgentCoreCommand(target, options, extraStateProviders)
|
|
|
18627
18805
|
const a2a = await a2aInvokeOnce(containerHost, hostPort, a2aRequest, { requestTimeoutMs: options.timeout });
|
|
18628
18806
|
await new Promise((r) => setTimeout(r, 250));
|
|
18629
18807
|
emitA2aResult(a2a);
|
|
18630
|
-
} else if (options.ws) {
|
|
18808
|
+
} else if (options.ws) if (watchActive) await runAgentCoreWatchLoop({
|
|
18809
|
+
containerHost,
|
|
18810
|
+
hostPort,
|
|
18811
|
+
event,
|
|
18812
|
+
sessionId,
|
|
18813
|
+
timeoutMs: options.timeout,
|
|
18814
|
+
wsInteractive: options.wsInteractive === true,
|
|
18815
|
+
options,
|
|
18816
|
+
resolvedTarget,
|
|
18817
|
+
resolved,
|
|
18818
|
+
synthesizer,
|
|
18819
|
+
synthOpts,
|
|
18820
|
+
stacks,
|
|
18821
|
+
...authorization && { authorization },
|
|
18822
|
+
rebuild: async () => {
|
|
18823
|
+
if (stopLogs) {
|
|
18824
|
+
try {
|
|
18825
|
+
stopLogs();
|
|
18826
|
+
} catch {}
|
|
18827
|
+
stopLogs = void 0;
|
|
18828
|
+
}
|
|
18829
|
+
if (containerId) {
|
|
18830
|
+
await removeContainer(containerId);
|
|
18831
|
+
containerId = void 0;
|
|
18832
|
+
}
|
|
18833
|
+
const { stacks: newStacks } = await synthesizer.synthesize(synthOpts);
|
|
18834
|
+
const newCandidate = pickAgentCoreCandidateStack(resolvedTarget, newStacks);
|
|
18835
|
+
const { context: newImageContext, loaded: newLoaded } = stateProvider && newCandidate ? await buildAgentCoreImageContext(newCandidate, stateProvider, options) : {
|
|
18836
|
+
context: void 0,
|
|
18837
|
+
loaded: void 0
|
|
18838
|
+
};
|
|
18839
|
+
const newResolved = resolveAgentCoreTarget(resolvedTarget, newStacks, newImageContext);
|
|
18840
|
+
await resolveFromS3BucketIntrinsic(newResolved, stateProvider, newLoaded, newImageContext);
|
|
18841
|
+
const newImage = await resolveAgentCoreImage(newResolved, options, newLoaded, stateProvider);
|
|
18842
|
+
const { env: newEnv, sensitiveEnvKeys: newSensitive } = await buildContainerEnv(newResolved, options, profileCredentials, profileCredsFile, stateProvider, newLoaded, newImageContext);
|
|
18843
|
+
const newHostPort = await pickFreePort();
|
|
18844
|
+
const newName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
18845
|
+
logger.info(`Reload: starting agent container (image=${newImage}, port=${newHostPort} -> 8080)...`);
|
|
18846
|
+
containerId = await runDetached({
|
|
18847
|
+
image: newImage,
|
|
18848
|
+
mounts: [],
|
|
18849
|
+
env: newEnv,
|
|
18850
|
+
cmd: [],
|
|
18851
|
+
hostPort: newHostPort,
|
|
18852
|
+
host: containerHost,
|
|
18853
|
+
platform: options.platform,
|
|
18854
|
+
name: newName,
|
|
18855
|
+
...newSensitive.size > 0 && { sensitiveEnvKeys: newSensitive }
|
|
18856
|
+
});
|
|
18857
|
+
stopLogs = streamLogs(containerId);
|
|
18858
|
+
return {
|
|
18859
|
+
containerId,
|
|
18860
|
+
hostPort: newHostPort,
|
|
18861
|
+
stacks: newStacks
|
|
18862
|
+
};
|
|
18863
|
+
},
|
|
18864
|
+
softReload: async (newSourceDir) => {
|
|
18865
|
+
if (!containerId) throw new CdkLocalError("softReload: no live container to docker cp / docker restart into.", "LOCAL_INVOKE_AGENTCORE_WATCH_NO_CONTAINER");
|
|
18866
|
+
await softReloadAgentContainer(containerId, newSourceDir);
|
|
18867
|
+
const { stacks: newStacks } = await synthesizer.synthesize(synthOpts);
|
|
18868
|
+
return { stacks: newStacks };
|
|
18869
|
+
}
|
|
18870
|
+
});
|
|
18871
|
+
else {
|
|
18631
18872
|
await waitForAgentCorePing(containerHost, hostPort);
|
|
18632
18873
|
const frameSource = options.wsInteractive ? readStdinLines() : void 0;
|
|
18633
18874
|
logger.info(options.wsInteractive ? "Opening the agent /ws WebSocket (interactive — stdin lines = follow-up frames; Ctrl-D to end)..." : "Opening the agent /ws WebSocket and streaming frames...");
|
|
@@ -18640,7 +18881,8 @@ async function localInvokeAgentCoreCommand(target, options, extraStateProviders)
|
|
|
18640
18881
|
});
|
|
18641
18882
|
await new Promise((r) => setTimeout(r, 250));
|
|
18642
18883
|
emitWsResult(wsResult);
|
|
18643
|
-
}
|
|
18884
|
+
}
|
|
18885
|
+
else {
|
|
18644
18886
|
await waitForAgentCorePing(containerHost, hostPort);
|
|
18645
18887
|
const additionalHeaders = await buildSigV4HeadersIfRequested(options, resolved, loadedState, containerHost, hostPort, event, sessionId, stateProvider);
|
|
18646
18888
|
const result = await invokeAgentCore(containerHost, hostPort, event, {
|
|
@@ -19298,6 +19540,309 @@ function readEnvOverridesFile$2(filePath) {
|
|
|
19298
19540
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);
|
|
19299
19541
|
return parsed;
|
|
19300
19542
|
}
|
|
19543
|
+
/**
|
|
19544
|
+
* Issue #255 — `cdkl invoke-agentcore --watch` main loop. Wraps the
|
|
19545
|
+
* `/ws` dispatch in a reload-driven loop:
|
|
19546
|
+
*
|
|
19547
|
+
* 1. Open the WebSocket against the current container; stream frames
|
|
19548
|
+
* to stdout. The connection is bound to an abort signal the
|
|
19549
|
+
* watcher fires when a source change is detected.
|
|
19550
|
+
* 2. When the agent closes the stream naturally (no reload pending),
|
|
19551
|
+
* the loop exits — `--watch` does NOT auto-reconnect on a benign
|
|
19552
|
+
* close (the user can `^C` to leave or just re-run the command).
|
|
19553
|
+
* 3. When a reload fires, the abort signal closes the WS cleanly
|
|
19554
|
+
* (resolves with the current frame count, not a reject), then the
|
|
19555
|
+
* classifier picks `'rebuild'` vs `'soft-reload'` and the
|
|
19556
|
+
* corresponding container-swap helper runs. After the swap, the
|
|
19557
|
+
* loop re-opens the WS against the new / restarted container with
|
|
19558
|
+
* the same `--event` first frame + a fresh stdin reader (for
|
|
19559
|
+
* `--ws-interactive`).
|
|
19560
|
+
*
|
|
19561
|
+
* Active `/ws` socket handling: the AgentCore `/ws` protocol does not
|
|
19562
|
+
* define mid-session container handoff, so the only honest local-dev
|
|
19563
|
+
* semantic is "close the socket cleanly and let the client reconnect."
|
|
19564
|
+
* The reload firing logs that loudly so the user sees it.
|
|
19565
|
+
*
|
|
19566
|
+
* `softReload` and `rebuild` are passed as callbacks so the watch loop
|
|
19567
|
+
* itself stays uncoupled from the boot pipeline + the docker helpers
|
|
19568
|
+
* the outer command owns. The host-side teardown (state provider
|
|
19569
|
+
* dispose, container removal, profile creds-file dispose) is the
|
|
19570
|
+
* outer command's `finally` cleanup — the watch loop only swaps the
|
|
19571
|
+
* one agent container.
|
|
19572
|
+
*
|
|
19573
|
+
* @internal — exported so a unit test can drive the loop without the
|
|
19574
|
+
* full Docker + chokidar pipeline.
|
|
19575
|
+
*/
|
|
19576
|
+
async function runAgentCoreWatchLoop(args) {
|
|
19577
|
+
const logger = getLogger();
|
|
19578
|
+
const wsInvoker = args.__wsInvoker ?? invokeAgentCoreWs;
|
|
19579
|
+
const waitForPing = args.__waitForPing ?? waitForAgentCorePing;
|
|
19580
|
+
let currentHostPort = args.hostPort;
|
|
19581
|
+
let currentStacks = args.stacks;
|
|
19582
|
+
let reloadChain = Promise.resolve();
|
|
19583
|
+
let currentAbort;
|
|
19584
|
+
let pendingReload = false;
|
|
19585
|
+
let reloadFailed = false;
|
|
19586
|
+
const watcherFactory = args.__watcherFactory ?? ((onChange) => {
|
|
19587
|
+
const watchRoot = process.cwd();
|
|
19588
|
+
const { ignored, shouldTrigger, excludePatterns } = createWatchPredicates({
|
|
19589
|
+
watchRoot,
|
|
19590
|
+
output: args.options.output,
|
|
19591
|
+
watchConfig: resolveWatchConfig()
|
|
19592
|
+
});
|
|
19593
|
+
logger.info(`Watching ${watchRoot} for source changes (excluding ${excludePatterns.join(", ")}).`);
|
|
19594
|
+
return createFileWatcher({
|
|
19595
|
+
paths: [watchRoot],
|
|
19596
|
+
ignored,
|
|
19597
|
+
shouldTrigger,
|
|
19598
|
+
onChange
|
|
19599
|
+
});
|
|
19600
|
+
});
|
|
19601
|
+
const cdkOutDir = args.options.output;
|
|
19602
|
+
const assetLoader = new AssetManifestLoader();
|
|
19603
|
+
const watcher = watcherFactory((changedPaths) => {
|
|
19604
|
+
reloadChain = reloadChain.then(async () => {
|
|
19605
|
+
let verdict = {
|
|
19606
|
+
kind: "rebuild",
|
|
19607
|
+
reason: "classifier not consulted"
|
|
19608
|
+
};
|
|
19609
|
+
let assetCtx;
|
|
19610
|
+
try {
|
|
19611
|
+
if (args.__classifierContext) assetCtx = await args.__classifierContext(changedPaths);
|
|
19612
|
+
else {
|
|
19613
|
+
const { stacks: freshStacks } = await args.synthesizer.synthesize(args.synthOpts);
|
|
19614
|
+
const oldAssetHash = await deriveOldAssetHash({
|
|
19615
|
+
resolvedTarget: args.resolvedTarget,
|
|
19616
|
+
resolved: args.resolved,
|
|
19617
|
+
stacks: currentStacks,
|
|
19618
|
+
cdkOutDir,
|
|
19619
|
+
assetLoader
|
|
19620
|
+
});
|
|
19621
|
+
assetCtx = await loadAgentCoreAssetContext({
|
|
19622
|
+
resolvedTarget: args.resolvedTarget,
|
|
19623
|
+
resolved: args.resolved,
|
|
19624
|
+
stacks: freshStacks,
|
|
19625
|
+
cdkOutDir,
|
|
19626
|
+
assetLoader,
|
|
19627
|
+
oldAssetHash
|
|
19628
|
+
});
|
|
19629
|
+
}
|
|
19630
|
+
verdict = classifySourceChange(changedPaths, assetCtx);
|
|
19631
|
+
logger.info(`Detected source change (${changedPaths.length} path(s)); verdict=${verdict.kind} (${verdict.reason}).`);
|
|
19632
|
+
} catch (err) {
|
|
19633
|
+
logger.warn(`Reload: classifier context unavailable (${err instanceof Error ? err.message : String(err)}); falling back to rebuild.`);
|
|
19634
|
+
verdict = {
|
|
19635
|
+
kind: "rebuild",
|
|
19636
|
+
reason: "classifier context unavailable; falling back to rebuild"
|
|
19637
|
+
};
|
|
19638
|
+
}
|
|
19639
|
+
pendingReload = true;
|
|
19640
|
+
logger.warn("cdkl invoke-agentcore --watch: source change detected; closing the active /ws socket so the client can reconnect to the rebuilt container.");
|
|
19641
|
+
currentAbort?.abort();
|
|
19642
|
+
try {
|
|
19643
|
+
if (verdict.kind === "soft-reload") {
|
|
19644
|
+
const { stacks: newStacks } = await args.softReload(verdict.newAssetSourceDir);
|
|
19645
|
+
currentStacks = newStacks;
|
|
19646
|
+
logger.info("Reload: soft-reloaded the agent container (docker cp + docker restart; container ID + host port preserved).");
|
|
19647
|
+
} else {
|
|
19648
|
+
const { containerId: _newId, hostPort: newHostPort, stacks: newStacks } = await args.rebuild();
|
|
19649
|
+
currentHostPort = newHostPort;
|
|
19650
|
+
currentStacks = newStacks;
|
|
19651
|
+
logger.info(`Reload: rebuilt the agent container.`);
|
|
19652
|
+
}
|
|
19653
|
+
} catch (err) {
|
|
19654
|
+
logger.error(`Reload failed: ${err instanceof Error ? err.message : String(err)}. The previous container may already be torn down; exiting --watch loop. Re-run cdkl invoke-agentcore --watch to recover.`);
|
|
19655
|
+
reloadFailed = true;
|
|
19656
|
+
currentAbort?.abort();
|
|
19657
|
+
}
|
|
19658
|
+
}).catch((err) => {
|
|
19659
|
+
logger.error(`Reload chain threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
19660
|
+
});
|
|
19661
|
+
});
|
|
19662
|
+
try {
|
|
19663
|
+
let firstIteration = true;
|
|
19664
|
+
while (true) {
|
|
19665
|
+
if (reloadFailed) break;
|
|
19666
|
+
await waitForPing(args.containerHost, currentHostPort);
|
|
19667
|
+
if (firstIteration) {
|
|
19668
|
+
logger.info(args.wsInteractive ? "Opening the agent /ws WebSocket (interactive — stdin lines = follow-up frames; Ctrl-D to end)..." : "Opening the agent /ws WebSocket and streaming frames...");
|
|
19669
|
+
firstIteration = false;
|
|
19670
|
+
} else logger.info(args.wsInteractive ? "Re-opening the agent /ws WebSocket (interactive) against the rebuilt container..." : "Re-opening the agent /ws WebSocket against the rebuilt container...");
|
|
19671
|
+
const abort = new AbortController();
|
|
19672
|
+
currentAbort = abort;
|
|
19673
|
+
pendingReload = false;
|
|
19674
|
+
const frameSource = args.wsInteractive ? readStdinLines() : void 0;
|
|
19675
|
+
try {
|
|
19676
|
+
const result = await wsInvoker(args.containerHost, currentHostPort, args.event, {
|
|
19677
|
+
sessionId: args.sessionId,
|
|
19678
|
+
timeoutMs: args.timeoutMs,
|
|
19679
|
+
onMessage: (text) => process.stdout.write(text),
|
|
19680
|
+
abortSignal: abort.signal,
|
|
19681
|
+
...args.authorization && { authorization: args.authorization },
|
|
19682
|
+
...frameSource && { frameSource }
|
|
19683
|
+
});
|
|
19684
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
19685
|
+
emitWsResult(result);
|
|
19686
|
+
} finally {
|
|
19687
|
+
currentAbort = void 0;
|
|
19688
|
+
}
|
|
19689
|
+
if (!pendingReload) {
|
|
19690
|
+
await reloadChain.catch(() => void 0);
|
|
19691
|
+
if (!pendingReload) break;
|
|
19692
|
+
}
|
|
19693
|
+
await reloadChain.catch(() => void 0);
|
|
19694
|
+
}
|
|
19695
|
+
} finally {
|
|
19696
|
+
try {
|
|
19697
|
+
await watcher.close();
|
|
19698
|
+
} catch (err) {
|
|
19699
|
+
logger.warn(`Watcher close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
19700
|
+
}
|
|
19701
|
+
}
|
|
19702
|
+
}
|
|
19703
|
+
/**
|
|
19704
|
+
* Issue #255 — soft-reload the agent container in place. `docker cp`
|
|
19705
|
+
* the freshly-synthed asset directory contents into the running
|
|
19706
|
+
* container's WORKDIR + `docker restart` the container.
|
|
19707
|
+
*
|
|
19708
|
+
* - WORKDIR is resolved from the live container's image config via
|
|
19709
|
+
* `docker inspect --format '{{.Config.WorkingDir}}'`. An empty
|
|
19710
|
+
* WORKDIR (Docker runtime default) maps to `/`. For
|
|
19711
|
+
* CodeConfiguration runtimes the generated Dockerfile sets
|
|
19712
|
+
* `WORKDIR /app`, so the copy lands there.
|
|
19713
|
+
* - The trailing `/.` on the source ensures CONTENTS are copied
|
|
19714
|
+
* (not the directory itself); the trailing `/` on the dest forces
|
|
19715
|
+
* docker cp to treat it as a directory.
|
|
19716
|
+
* - `docker restart` cycles PID 1 — the new source is picked up by
|
|
19717
|
+
* the interpreted-language runtime on its next startup. The
|
|
19718
|
+
* container ID + host port + network are preserved across the
|
|
19719
|
+
* restart, so the surrounding watch loop's host-port reference
|
|
19720
|
+
* stays valid.
|
|
19721
|
+
*
|
|
19722
|
+
* @internal — exported for unit tests of the docker-cp + docker-restart
|
|
19723
|
+
* shape without standing up a real container.
|
|
19724
|
+
*/
|
|
19725
|
+
const execFileAsync$4 = promisify(execFile);
|
|
19726
|
+
function describeExecError(err) {
|
|
19727
|
+
if (!(err instanceof Error)) return String(err);
|
|
19728
|
+
const stderr = err.stderr;
|
|
19729
|
+
const stderrText = typeof stderr === "string" ? stderr.trim() : stderr instanceof Buffer ? stderr.toString("utf8").trim() : "";
|
|
19730
|
+
return stderrText ? `${err.message}\n${stderrText}` : err.message;
|
|
19731
|
+
}
|
|
19732
|
+
async function softReloadAgentContainer(containerId, newAssetSourceDir) {
|
|
19733
|
+
const logger = getLogger();
|
|
19734
|
+
const dockerCmd = getDockerCmd();
|
|
19735
|
+
let workdir;
|
|
19736
|
+
try {
|
|
19737
|
+
const { stdout } = await execFileAsync$4(dockerCmd, [
|
|
19738
|
+
"inspect",
|
|
19739
|
+
"--format",
|
|
19740
|
+
"{{.Config.WorkingDir}}",
|
|
19741
|
+
containerId
|
|
19742
|
+
]);
|
|
19743
|
+
workdir = stdout.trim() || "/";
|
|
19744
|
+
} catch (err) {
|
|
19745
|
+
throw new CdkLocalError(`softReloadAgentContainer: docker inspect of container '${containerId}' failed: ${describeExecError(err)}.`, "LOCAL_INVOKE_AGENTCORE_WATCH_SOFT_RELOAD_INSPECT_FAILED");
|
|
19746
|
+
}
|
|
19747
|
+
const workdirDest = workdir.endsWith("/") ? workdir : `${workdir}/`;
|
|
19748
|
+
logger.info(`Soft-reload: docker cp ${newAssetSourceDir} -> ${containerId}:${workdirDest}; restart.`);
|
|
19749
|
+
try {
|
|
19750
|
+
await execFileAsync$4(dockerCmd, [
|
|
19751
|
+
"cp",
|
|
19752
|
+
`${newAssetSourceDir}/.`,
|
|
19753
|
+
`${containerId}:${workdirDest}`
|
|
19754
|
+
], { maxBuffer: 64 * 1024 * 1024 });
|
|
19755
|
+
} catch (err) {
|
|
19756
|
+
throw new CdkLocalError(`softReloadAgentContainer: docker cp into '${containerId}:${workdir}' failed: ${describeExecError(err)}.`, "LOCAL_INVOKE_AGENTCORE_WATCH_SOFT_RELOAD_CP_FAILED");
|
|
19757
|
+
}
|
|
19758
|
+
try {
|
|
19759
|
+
await execFileAsync$4(dockerCmd, ["restart", containerId]);
|
|
19760
|
+
} catch (err) {
|
|
19761
|
+
throw new CdkLocalError(`softReloadAgentContainer: docker restart of '${containerId}' failed: ${describeExecError(err)}.`, "LOCAL_INVOKE_AGENTCORE_WATCH_SOFT_RELOAD_RESTART_FAILED");
|
|
19762
|
+
}
|
|
19763
|
+
}
|
|
19764
|
+
/**
|
|
19765
|
+
* Issue #255 — build the per-firing classifier context for the agent
|
|
19766
|
+
* container. Mirrors the ECS service emulator's `loadAssetContextForTarget`
|
|
19767
|
+
* shape: returns `undefined` (and the classifier defaults to `'rebuild'`)
|
|
19768
|
+
* when the runtime's image is not a CDK docker-image asset, or when the
|
|
19769
|
+
* asset manifest lookup misses for either the OLD or NEW synth.
|
|
19770
|
+
*
|
|
19771
|
+
* For a CodeConfiguration (`fromCodeAsset`) runtime we treat the bundle's
|
|
19772
|
+
* `codeAssetHash` as the asset hash + the staged source directory as
|
|
19773
|
+
* `newAssetSourceDir`, and synthesize a `Dockerfile` basename that never
|
|
19774
|
+
* matches a real file (the generated Dockerfile lives in a build tmpdir,
|
|
19775
|
+
* not the source tree, so chokidar can't observe an edit to it). A
|
|
19776
|
+
* `fromS3` bundle has no local source tree, so it returns `undefined` and
|
|
19777
|
+
* the classifier defaults to rebuild.
|
|
19778
|
+
*
|
|
19779
|
+
* Container artifacts go through the same `AssetManifestLoader` lookup
|
|
19780
|
+
* the ECS path uses, then read `source.directory` + `source.dockerFile`
|
|
19781
|
+
* off the matched docker-image entry.
|
|
19782
|
+
*
|
|
19783
|
+
* @internal — exported for the watch loop's classifier dispatch test.
|
|
19784
|
+
*/
|
|
19785
|
+
async function loadAgentCoreAssetContext(args) {
|
|
19786
|
+
const { resolvedTarget, resolved, stacks, cdkOutDir, assetLoader, oldAssetHash } = args;
|
|
19787
|
+
const newCandidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
19788
|
+
if (!newCandidate) return void 0;
|
|
19789
|
+
if (resolved.codeArtifact) {
|
|
19790
|
+
if (resolved.codeArtifact.s3Source) return void 0;
|
|
19791
|
+
const manifest = await assetLoader.loadManifest(cdkOutDir, newCandidate.stackName);
|
|
19792
|
+
if (!manifest) return void 0;
|
|
19793
|
+
const asset = assetLoader.getFileAssets(manifest).get(resolved.codeArtifact.codeAssetHash);
|
|
19794
|
+
if (!asset) return void 0;
|
|
19795
|
+
const sourceDir = assetLoader.getAssetSourcePath(cdkOutDir, asset);
|
|
19796
|
+
return {
|
|
19797
|
+
...oldAssetHash !== void 0 && { oldAssetHash },
|
|
19798
|
+
newAssetHash: resolved.codeArtifact.codeAssetHash,
|
|
19799
|
+
newAssetSourceDir: sourceDir,
|
|
19800
|
+
dockerFile: ".cdkl-agentcore-generated-Dockerfile"
|
|
19801
|
+
};
|
|
19802
|
+
}
|
|
19803
|
+
if (resolved.containerUri === void 0) return void 0;
|
|
19804
|
+
const manifest = await assetLoader.loadManifest(cdkOutDir, newCandidate.stackName);
|
|
19805
|
+
if (!manifest) return void 0;
|
|
19806
|
+
const dockerImageEntry = getDockerImageBySourceHash(manifest, resolved.containerUri);
|
|
19807
|
+
if (!dockerImageEntry) return void 0;
|
|
19808
|
+
const newAssetHash = dockerImageEntry.hash;
|
|
19809
|
+
const newDockerImage = dockerImageEntry.asset;
|
|
19810
|
+
if (!newDockerImage.source.directory) return void 0;
|
|
19811
|
+
const newAssetSourceDir = path.resolve(cdkOutDir, newDockerImage.source.directory);
|
|
19812
|
+
return {
|
|
19813
|
+
...oldAssetHash !== void 0 && { oldAssetHash },
|
|
19814
|
+
newAssetHash,
|
|
19815
|
+
newAssetSourceDir,
|
|
19816
|
+
dockerFile: path.basename(newDockerImage.source.dockerFile ?? "Dockerfile")
|
|
19817
|
+
};
|
|
19818
|
+
}
|
|
19819
|
+
/**
|
|
19820
|
+
* Helper: derive the current asset hash from the live (pre-reload)
|
|
19821
|
+
* stacks for the classifier's `oldAssetHash` field. Returns `undefined`
|
|
19822
|
+
* when the runtime's image is not resolvable as a CDK asset against the
|
|
19823
|
+
* old stacks — the classifier defaults to rebuild in that case, which
|
|
19824
|
+
* is the right answer.
|
|
19825
|
+
*/
|
|
19826
|
+
/**
|
|
19827
|
+
* Async helper: derive the OLD (pre-reload) asset hash for the
|
|
19828
|
+
* classifier's `oldAssetHash` field. Code-artifact runtimes carry the
|
|
19829
|
+
* hash on the boot-time `resolved.codeArtifact.codeAssetHash`; container
|
|
19830
|
+
* runtimes need a manifest lookup against the previous-synth stacks to
|
|
19831
|
+
* extract it. Returns `undefined` when the OLD hash can't be derived
|
|
19832
|
+
* (image is not a CDK asset, manifest unreadable, no candidate stack) —
|
|
19833
|
+
* the classifier treats `undefined` as "force rebuild", which is the
|
|
19834
|
+
* conservative default.
|
|
19835
|
+
*/
|
|
19836
|
+
async function deriveOldAssetHash(args) {
|
|
19837
|
+
const { resolvedTarget, resolved, stacks, cdkOutDir, assetLoader } = args;
|
|
19838
|
+
if (resolved.codeArtifact) return resolved.codeArtifact.codeAssetHash;
|
|
19839
|
+
if (resolved.containerUri === void 0) return void 0;
|
|
19840
|
+
const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
19841
|
+
if (!candidate) return void 0;
|
|
19842
|
+
const manifest = await assetLoader.loadManifest(cdkOutDir, candidate.stackName);
|
|
19843
|
+
if (!manifest) return void 0;
|
|
19844
|
+
return getDockerImageBySourceHash(manifest, resolved.containerUri)?.hash;
|
|
19845
|
+
}
|
|
19301
19846
|
function createLocalInvokeAgentCoreCommand(opts = {}) {
|
|
19302
19847
|
setEmbedConfig(opts.embedConfig);
|
|
19303
19848
|
const cmd = new Command("invoke-agentcore").description("Run a Bedrock AgentCore Runtime container locally and invoke it once over its protocol contract: HTTP (POST /invocations + GET /ping on 8080; SSE / WebSocket are HTTP wire-shape variants on the same port), MCP (POST /mcp Streamable HTTP on 8000), A2A, or AGUI. Resolves the AWS::BedrockAgentCore::Runtime, pulls/builds its container, injects env vars + AWS credentials, and prints the response. For an MCP runtime, runs the session handshake then sends one JSON-RPC request (tools/list by default, or the method/params from --event). Target accepts a CDK display path (MyStack/MyAgent) or stack-qualified logical ID (MyStack:MyAgentRuntime1234). Single-stack apps may omit the stack prefix. Omit <target> in an interactive terminal to pick from a list. Supports the container artifact and the CodeConfiguration managed-runtime artifact (fromCodeAsset, built from source) on all four protocols; the agent calls real AWS for managed services.").argument("[target]", "CDK display path or stack-qualified logical ID of the AgentCore Runtime to invoke (omit to pick interactively in a TTY)").action(withErrorHandling(async (target, options) => {
|
|
@@ -19328,7 +19873,7 @@ function createLocalInvokeAgentCoreCommand(opts = {}) {
|
|
|
19328
19873
|
* in three `--help` clusters. Chainable: returns `cmd`.
|
|
19329
19874
|
*/
|
|
19330
19875
|
function addInvokeAgentCoreSpecificOptions(cmd) {
|
|
19331
|
-
return cmd.addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--session-id <id>", "AgentCore runtime session id header value (default: a random UUID)")).addOption(new Option("--ws", "Stream over the HTTP-protocol agent's bidirectional /ws WebSocket endpoint (on 8080) instead of POST /invocations: send --event as the first frame and print every received frame to stdout until the agent closes. Ignored for an MCP runtime.").default(false)).addOption(new Option("--ws-interactive", "REPL mode for --ws: after the initial --event frame, read additional frames from stdin (one frame per line, trailing newline stripped) and send each as a text frame until stdin EOFs (Ctrl-D) or the agent closes. Only meaningful with --ws.").default(false)).addOption(new Option("--bearer-token <jwt>", "Bearer JWT this command SUPPLIES (the supplier role) when the runtime declares a customJwtAuthorizer — `cdkl invoke-agentcore` is the local-dev client making the outbound call, so it always presents this token to its own invocation. Verified against the runtime's OIDC discovery URL (signature / issuer / expiry / audience) before the container starts, then forwarded to /invocations as Authorization: Bearer <jwt>. Contrast with `cdkl start-alb --bearer-token`, where the role is reversed — the ALB front-door RECEIVES inbound requests and injects this token only as a default when an inbound request has none (default-when-missing).")).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 the /invocations POST with AWS SigV4 (service bedrock-agentcore) using the resolved credentials, matching the cloud default when the runtime declares no customJwtAuthorizer. Opt-in: default unsigned. Mutually exclusive with --bearer-token; ignored on a JWT-protected runtime.").default(false)).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. Override to linux/amd64 only when iterating against an amd64 dev container locally; note the image will not run on the cloud runtime as-is.").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).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 the host uses to bind the agent port to. Must be a numeric IP (Docker rejects hostnames here). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--timeout <ms>", "Per-request timeout in milliseconds. Applied to POST /invocations, POST /mcp, and the /ws open-to-close window. Raise this for long-running agent calls that exceed the default.").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 so the agent runs with the deployed role. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's RoleArn when it is a literal ARN; (3) `--no-assume-role` opts out. Off by default — the developer's shell credentials are forwarded unchanged.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Bare form uses the resolved stack name; pass an explicit value when the CFn stack name differs.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
|
|
19876
|
+
return cmd.addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--session-id <id>", "AgentCore runtime session id header value (default: a random UUID)")).addOption(new Option("--ws", "Stream over the HTTP-protocol agent's bidirectional /ws WebSocket endpoint (on 8080) instead of POST /invocations: send --event as the first frame and print every received frame to stdout until the agent closes. Ignored for an MCP runtime.").default(false)).addOption(new Option("--ws-interactive", "REPL mode for --ws: after the initial --event frame, read additional frames from stdin (one frame per line, trailing newline stripped) and send each as a text frame until stdin EOFs (Ctrl-D) or the agent closes. Only meaningful with --ws.").default(false)).addOption(new Option("--bearer-token <jwt>", "Bearer JWT this command SUPPLIES (the supplier role) when the runtime declares a customJwtAuthorizer — `cdkl invoke-agentcore` is the local-dev client making the outbound call, so it always presents this token to its own invocation. Verified against the runtime's OIDC discovery URL (signature / issuer / expiry / audience) before the container starts, then forwarded to /invocations as Authorization: Bearer <jwt>. Contrast with `cdkl start-alb --bearer-token`, where the role is reversed — the ALB front-door RECEIVES inbound requests and injects this token only as a default when an inbound request has none (default-when-missing).")).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 the /invocations POST with AWS SigV4 (service bedrock-agentcore) using the resolved credentials, matching the cloud default when the runtime declares no customJwtAuthorizer. Opt-in: default unsigned. Mutually exclusive with --bearer-token; ignored on a JWT-protected runtime.").default(false)).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. Override to linux/amd64 only when iterating against an amd64 dev container locally; note the image will not run on the cloud runtime as-is.").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).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 the host uses to bind the agent port to. Must be a numeric IP (Docker rejects hostnames here). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--timeout <ms>", "Per-request timeout in milliseconds. Applied to POST /invocations, POST /mcp, and the /ws open-to-close window. Raise this for long-running agent calls that exceed the default.").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 so the agent runs with the deployed role. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's RoleArn when it is a literal ARN; (3) `--no-assume-role` opts out. Off by default — the developer's shell credentials are forwarded unchanged.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Bare form uses the resolved stack name; pass an explicit value when the CFn stack name differs.")).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("--watch", "Re-synth and reload the agent container on CDK source changes. Only meaningful with the long-running /ws session paths (--ws / --ws-interactive); single-shot POST /invocations, MCP, and A2A invocations run once and exit, so --watch is logged as a no-op WARN for them and the single shot proceeds. The active /ws socket is closed cleanly on every reload firing so the next session connects to the rebuilt container — the honest local-dev semantic.").default(false));
|
|
19332
19877
|
}
|
|
19333
19878
|
|
|
19334
19879
|
//#endregion
|
|
@@ -21939,167 +22484,6 @@ function sleep(ms) {
|
|
|
21939
22484
|
return sleepImpl(ms);
|
|
21940
22485
|
}
|
|
21941
22486
|
|
|
21942
|
-
//#endregion
|
|
21943
|
-
//#region src/local/source-change-classifier.ts
|
|
21944
|
-
/**
|
|
21945
|
-
* Dependency-manifest basenames recognized by the classifier. A change
|
|
21946
|
-
* to any of these forces a rebuild because the running container's
|
|
21947
|
-
* pre-built dependency layer is no longer in sync with the source.
|
|
21948
|
-
*
|
|
21949
|
-
* Coverage: the package managers commonly used inside a Lambda /
|
|
21950
|
-
* container image — Node (pnpm / npm / yarn), Python (pip / poetry /
|
|
21951
|
-
* pipenv), Ruby (bundler), Go (modules), Rust (cargo), Java / Kotlin
|
|
21952
|
-
* (Maven, Gradle). Adding a new ecosystem? Append its lockfile +
|
|
21953
|
-
* manifest here and add a classifier test row.
|
|
21954
|
-
*/
|
|
21955
|
-
const REBUILD_TRIGGER_BASENAMES = new Set([
|
|
21956
|
-
"package.json",
|
|
21957
|
-
"package-lock.json",
|
|
21958
|
-
"pnpm-lock.yaml",
|
|
21959
|
-
"yarn.lock",
|
|
21960
|
-
"npm-shrinkwrap.json",
|
|
21961
|
-
"requirements.txt",
|
|
21962
|
-
"requirements-dev.txt",
|
|
21963
|
-
"pyproject.toml",
|
|
21964
|
-
"poetry.lock",
|
|
21965
|
-
"Pipfile",
|
|
21966
|
-
"Pipfile.lock",
|
|
21967
|
-
"uv.lock",
|
|
21968
|
-
"Gemfile",
|
|
21969
|
-
"Gemfile.lock",
|
|
21970
|
-
"go.mod",
|
|
21971
|
-
"go.sum",
|
|
21972
|
-
"Cargo.toml",
|
|
21973
|
-
"Cargo.lock",
|
|
21974
|
-
"pom.xml",
|
|
21975
|
-
"build.gradle",
|
|
21976
|
-
"build.gradle.kts",
|
|
21977
|
-
"settings.gradle",
|
|
21978
|
-
"settings.gradle.kts",
|
|
21979
|
-
"Makefile",
|
|
21980
|
-
"CMakeLists.txt"
|
|
21981
|
-
]);
|
|
21982
|
-
/**
|
|
21983
|
-
* Compiled-language source extensions that require a build step
|
|
21984
|
-
* inside `docker build` (typically a `RUN go build` / `cargo build` /
|
|
21985
|
-
* `mvn package` etc.). A copy of the source alone would leave the
|
|
21986
|
-
* running binary stale, so the user's intent must be a rebuild.
|
|
21987
|
-
*
|
|
21988
|
-
* TypeScript source (`.ts` / `.tsx` / `.mts` / `.cts`) is treated as
|
|
21989
|
-
* compiled because the dominant production-container pattern is to
|
|
21990
|
-
* pre-compile the source via a Dockerfile `RUN tsc` / `RUN yarn build`
|
|
21991
|
-
* step, with the runtime executing the emitted `dist/*.js`. Soft-reload
|
|
21992
|
-
* would `docker cp` the new `.ts` into the container's WORKDIR while
|
|
21993
|
-
* the running process keeps reading the OLD `dist/` — a silent
|
|
21994
|
-
* stale-code failure that violates the file's "slow-but-correct beats
|
|
21995
|
-
* fast-but-stale" default policy (lines 14-22). Setups that transpile
|
|
21996
|
-
* at runtime lose the soft-reload fast path under this default; an
|
|
21997
|
-
* opt-in flag to restore it is a possible follow-up but is not in
|
|
21998
|
-
* scope here.
|
|
21999
|
-
*
|
|
22000
|
-
* Interpreted-language runtimes (Node — `.js` / `.mjs` / `.cjs`,
|
|
22001
|
-
* Python — `.py`, Ruby — `.rb`, shell — `.sh`) read source at process
|
|
22002
|
-
* start, so a `docker cp` + `docker restart` cycle picks them up.
|
|
22003
|
-
* Those extensions are NOT in this set.
|
|
22004
|
-
*/
|
|
22005
|
-
const COMPILED_LANGUAGE_EXTENSIONS = new Set([
|
|
22006
|
-
".go",
|
|
22007
|
-
".rs",
|
|
22008
|
-
".java",
|
|
22009
|
-
".kt",
|
|
22010
|
-
".kts",
|
|
22011
|
-
".scala",
|
|
22012
|
-
".cs",
|
|
22013
|
-
".swift",
|
|
22014
|
-
".fs",
|
|
22015
|
-
".fsx",
|
|
22016
|
-
".c",
|
|
22017
|
-
".cc",
|
|
22018
|
-
".cpp",
|
|
22019
|
-
".cxx",
|
|
22020
|
-
".h",
|
|
22021
|
-
".hpp",
|
|
22022
|
-
".zig",
|
|
22023
|
-
".ml",
|
|
22024
|
-
".mli",
|
|
22025
|
-
".elm",
|
|
22026
|
-
".hs",
|
|
22027
|
-
".dart",
|
|
22028
|
-
".ts",
|
|
22029
|
-
".tsx",
|
|
22030
|
-
".mts",
|
|
22031
|
-
".cts"
|
|
22032
|
-
]);
|
|
22033
|
-
/**
|
|
22034
|
-
* Classify a single watcher firing into rebuild vs soft-reload. Pure
|
|
22035
|
-
* + synchronous. The caller (emulator's reload pathway) invokes this
|
|
22036
|
-
* once per target per firing AFTER `cdk synth` has run and the new
|
|
22037
|
-
* asset manifest is on disk.
|
|
22038
|
-
*
|
|
22039
|
-
* Branching:
|
|
22040
|
-
* 1. No asset context (image isn't a CDK asset, or asset lookup
|
|
22041
|
-
* failed) → `rebuild`.
|
|
22042
|
-
* 2. The asset hash didn't change between old and new synths
|
|
22043
|
-
* (`oldAssetHash === newAssetHash`, or `oldAssetHash` missing)
|
|
22044
|
-
* → `rebuild`. Load-bearing guard for "user edited a CDK
|
|
22045
|
-
* construct file (e.g. `lib/stack.ts`) that flipped the task
|
|
22046
|
-
* spec but didn't touch the asset content". Soft-reload would
|
|
22047
|
-
* `docker cp` identical files and `docker restart` the
|
|
22048
|
-
* container with the OLD task spec (env / memory / mounts /
|
|
22049
|
-
* added sidecars are set at `docker create` time, not on
|
|
22050
|
-
* restart) — the user's intent would silently NOT apply.
|
|
22051
|
-
* Forcing rebuild keeps Phase 1-3 semantics exactly for this
|
|
22052
|
-
* case: the rolling primitive boots a shadow with the new task
|
|
22053
|
-
* spec, the user sees their construct edit take effect.
|
|
22054
|
-
* 3. No changed paths (the watcher fired on a debounce flush with
|
|
22055
|
-
* an empty pending set — shouldn't happen in practice, but
|
|
22056
|
-
* defensive) → `rebuild`.
|
|
22057
|
-
* 4. Any changed path's basename matches the Dockerfile or a
|
|
22058
|
-
* dependency manifest → `rebuild`.
|
|
22059
|
-
* 5. Any changed path's extension is a compiled-language source →
|
|
22060
|
-
* `rebuild`.
|
|
22061
|
-
* 6. Else → `soft-reload`.
|
|
22062
|
-
*/
|
|
22063
|
-
function classifySourceChange(changedPaths, ctx) {
|
|
22064
|
-
if (!ctx) return {
|
|
22065
|
-
kind: "rebuild",
|
|
22066
|
-
reason: "target image is not a CDK docker-image asset"
|
|
22067
|
-
};
|
|
22068
|
-
if (!ctx.oldAssetHash || ctx.oldAssetHash === ctx.newAssetHash) return {
|
|
22069
|
-
kind: "rebuild",
|
|
22070
|
-
reason: "asset hash unchanged across the synth (CDK construct edit or unrelated file) — task-spec changes need a fresh `docker create`, which only the rebuild path runs"
|
|
22071
|
-
};
|
|
22072
|
-
if (changedPaths.length === 0) return {
|
|
22073
|
-
kind: "rebuild",
|
|
22074
|
-
reason: "no changed paths reported (defensive default)"
|
|
22075
|
-
};
|
|
22076
|
-
for (const p of changedPaths) {
|
|
22077
|
-
const basename = path.basename(p);
|
|
22078
|
-
if (basename === ctx.dockerFile) return {
|
|
22079
|
-
kind: "rebuild",
|
|
22080
|
-
reason: `Dockerfile edit (${basename})`
|
|
22081
|
-
};
|
|
22082
|
-
if (basename.startsWith("Dockerfile.")) return {
|
|
22083
|
-
kind: "rebuild",
|
|
22084
|
-
reason: `Dockerfile.* edit (${basename})`
|
|
22085
|
-
};
|
|
22086
|
-
if (REBUILD_TRIGGER_BASENAMES.has(basename)) return {
|
|
22087
|
-
kind: "rebuild",
|
|
22088
|
-
reason: `dependency manifest edit (${basename})`
|
|
22089
|
-
};
|
|
22090
|
-
const ext = path.extname(p).toLowerCase();
|
|
22091
|
-
if (COMPILED_LANGUAGE_EXTENSIONS.has(ext)) return {
|
|
22092
|
-
kind: "rebuild",
|
|
22093
|
-
reason: `compiled-language source edit (${basename}) — soft-reload would leave the built binary stale`
|
|
22094
|
-
};
|
|
22095
|
-
}
|
|
22096
|
-
return {
|
|
22097
|
-
kind: "soft-reload",
|
|
22098
|
-
reason: `${changedPaths.length} source-only path(s) — skipping rebuild`,
|
|
22099
|
-
newAssetSourceDir: ctx.newAssetSourceDir
|
|
22100
|
-
};
|
|
22101
|
-
}
|
|
22102
|
-
|
|
22103
22487
|
//#endregion
|
|
22104
22488
|
//#region src/local/cloud-map-registry.ts
|
|
22105
22489
|
/**
|
|
@@ -27439,5 +27823,5 @@ function addListSpecificOptions(cmd) {
|
|
|
27439
27823
|
}
|
|
27440
27824
|
|
|
27441
27825
|
//#endregion
|
|
27442
|
-
export {
|
|
27443
|
-
//# sourceMappingURL=local-list-
|
|
27826
|
+
export { mcpInvokeOnce as $, pickAgentCoreCandidateStack as $n, applyAuthorizerOverlay as $t, mergeForService as A, rejectExplicitCfnStackWithMultipleStacks as An, startApiServer as At, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as B, listTargets as Bn, buildMethodArn as Bt, parseRestartPolicy as C, substituteAgainstState as Cn, materializeLayerFromArn as Ct, ImageOverrideError as D, LocalStateSourceError as Dn, filterRoutesByApiIdentifiers as Dt, runEcsServiceEmulator as E, substituteEnvVarsFromStateAsync as En, filterRoutesByApiIdentifier as Et, isLocalCdkAssetImage as F, collectSsmParameterRefs as Fn, buildJwksUrlFromIssuer as Ft, createLocalInvokeAgentCoreCommand as G, pickRefLogicalId as Gn, attachAuthorizers as Gt, getContainerNetworkIp as H, discoverWebSocketApisOrThrow as Hn, evaluateCachedLambdaPolicy as Ht, listPinnedTargets as I, resolveSsmParameters as In, createJwksCache as It, A2A_PATH as J, AGENTCORE_AGUI_PROTOCOL as Jn, buildCorsConfigFromCloudFrontChain as Jt, invokeAgentCoreWs as K, resolveLambdaArnIntrinsic as Kn, applyCorsResponseHeaders as Kt, buildCloudMapIndex as L, resolveWatchConfig as Ln, verifyCognitoJwt as Lt, resolveImageOverrides as M, resolveCfnRegion as Mn, resolveServiceIntegrationParameters as Mt, runImageOverrideBuilds as N, resolveCfnStackName as Nn, defaultCredentialsLoader as Nt, buildImageOverrideTag as O, createLocalStateProvider as On, groupRoutesByServer as Ot, describePinnedImageUri as P, CfnLocalStateProvider as Pn, buildCognitoJwksUrl as Pt, MCP_PROTOCOL_VERSION as Q, AgentCoreResolutionError as Qn, translateLambdaResponse as Qt, CloudMapRegistry as R, resolveSingleTarget as Rn, verifyJwtAuthorizer as Rt, parseMaxTasks as S, EcsTaskResolutionError as Sn, buildStageMap as St, resolveSharedSidecarCredentials as T, substituteEnvVarsFromState as Tn, availableApiIdentifiers as Tt, attachContainerLogStreamer as U, parseSelectionExpressionPath as Un, invokeRequestAuthorizer as Ut, setShadowReadyTimeoutMs as V, discoverWebSocketApis as Vn, computeRequestIdentityHash as Vt, addInvokeAgentCoreSpecificOptions as W, discoverRoutes as Wn, invokeTokenAuthorizer as Wt, MCP_CONTAINER_PORT as X, AGENTCORE_MCP_PROTOCOL as Xn, matchPreflight as Xt, a2aInvokeOnce as Y, AGENTCORE_HTTP_PROTOCOL as Yn, isFunctionUrlOacFronted as Yt, MCP_PATH as Z, AGENTCORE_RUNTIME_TYPE as Zn, matchRoute as Zt, addCommonEcsServiceOptions as _, architectureToPlatform as _n, createWatchPredicates as _t, albStrategy as a, tryParseStatus as an, LocalInvokeBuildError as ar, waitForAgentCorePing as at, buildEcsImageResolutionContext$1 as b, resolveRuntimeFileExtension as bn, createFileWatcher as bt, resolveAlbTarget as c, probeHostGatewaySupport as cn, buildAgentCoreCodeImage as ct, addStartServiceSpecificOptions as d, buildMgmtEndpointEnvUrl as dn, toCmdArgv as dt, buildHttpApiV2Event as en, resolveAgentCoreTarget as er, parseSseForJsonRpc as et, createLocalStartServiceCommand as f, handleConnectionsRequest as fn, classifySourceChange as ft, MAX_TASKS_SUBNET_RANGE_CAP as g, buildMessageEvent as gn, createLocalStartApiCommand as gt, createLocalRunTaskCommand as h, buildDisconnectEvent as hn, addStartApiSpecificOptions as ht, addAlbSpecificOptions as i, selectIntegrationResponse as in, tryResolveImageFnJoin as ir, invokeAgentCore as it, parseImageOverrideFlags as j, resolveCfnFallbackRegion as jn, resolveSelectionExpression as jt, enforceImageOverrideOrphans as k, isCfnFlagPresent as kn, readMtlsMaterialsFromDisk as kt, isApplicationLoadBalancer as l, bufferToBody as ln, computeCodeImageTag as lt, addRunTaskSpecificOptions as m, buildConnectEvent as mn, createLocalInvokeCommand as mt, createLocalListCommand as n, evaluateResponseParameters as nn, formatStateRemedy as nr, signAgentCoreInvocation as nt, createLocalStartAlbCommand as o, VtlEvaluationError as on, buildStsClientConfig as or, downloadAndExtractS3Bundle as ot, serviceStrategy as p, parseConnectionsPath as pn, addInvokeSpecificOptions as pt, A2A_CONTAINER_PORT as q, AGENTCORE_A2A_PROTOCOL as qn, buildCorsConfigByApiId as qt, formatTargetListing as r, pickResponseTemplate as rn, substituteImagePlaceholders as rr, AGENTCORE_SESSION_ID_HEADER as rt, parseLbPortOverrides as s, HOST_GATEWAY_MIN_VERSION as sn, resolveProfileCredentials as sr, SUPPORTED_CODE_RUNTIMES as st, addListSpecificOptions as t, buildRestV1Event as tn, derivePseudoParametersFromRegion as tr, AGENTCORE_SIGV4_SERVICE as tt, resolveAlbFrontDoor as u, ConnectionRegistry as un, renderCodeDockerfile as ut, addEcsAssumeRoleOptions as v, buildContainerImage as vn, resolveApiTargetSubset as vt, resolveEcsAssumeRoleOption as w, substituteAgainstStateAsync as wn, resolveEnvVars as wt, ecsClusterOption as x, resolveRuntimeImage as xn, attachStageContext as xt, addImageOverrideOptions as y, resolveRuntimeCodeMountPath as yn, createAuthorizerCache as yt, DEFAULT_SHADOW_READY_TIMEOUT_MS as z, countTargets as zn, verifyJwtViaDiscovery as zt };
|
|
27827
|
+
//# sourceMappingURL=local-list-Dh6xjxGf.js.map
|