@go-to-k/cdkd 0.190.0 → 0.191.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/dist/cli.js CHANGED
@@ -62,8 +62,8 @@ import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketComman
62
62
  import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand as ListTablesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$19, NotFoundException as NotFoundException$5, S3TablesClient } from "@aws-sdk/client-s3tables";
63
63
  import { AttachLoadBalancerTargetGroupsCommand, AttachLoadBalancersCommand, AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, CreateOrUpdateTagsCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DeleteTagsCommand as DeleteTagsCommand$1, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachLoadBalancerTargetGroupsCommand, DetachLoadBalancersCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
64
64
  import { Document, Pair, Scalar, YAMLMap, YAMLSeq, parse as parse$1, stringify } from "yaml";
65
- import { createLocalStateProvider, isCfnFlagPresent, rejectExplicitCfnStackWithMultipleStacks, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync } from "cdk-local";
66
- import { CloudMapRegistry, ConnectionRegistry, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, LocalInvokeBuildError, architectureToPlatform, attachAuthorizers, attachStageContext, availableApiIdentifiers, bufferToBody, buildCloudMapIndex, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildJwksUrlFromIssuer, buildMessageEvent, buildMgmtEndpointEnvUrl, buildStageMap, createAuthorizerCache, createFileWatcher, createJwksCache, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, discoverRoutes, discoverWebSocketApis, filterRoutesByApiIdentifier, getContainerNetworkIp, groupRoutesByServer, handleConnectionsRequest, materializeLayerFromArn, parseConnectionsPath, parseSelectionExpressionPath, probeHostGatewaySupport, readMtlsMaterialsFromDisk, resolveEnvVars, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveWatchConfig, startApiServer, substituteImagePlaceholders, tryResolveImageFnJoin } from "cdk-local/internal";
65
+ import { createLocalStateProvider, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resolveCfnFallbackRegion, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync } from "cdk-local";
66
+ import { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_MCP_PROTOCOL, CloudMapRegistry, ConnectionRegistry, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, LocalInvokeBuildError, MCP_CONTAINER_PORT, MCP_PATH, a2aInvokeOnce, architectureToPlatform, attachAuthorizers, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCloudMapIndex, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildJwksUrlFromIssuer, buildMessageEvent, buildMgmtEndpointEnvUrl, buildStageMap, createAuthorizerCache, createFileWatcher, createJwksCache, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, discoverRoutes, discoverWebSocketApis, downloadAndExtractS3Bundle, filterRoutesByApiIdentifier, getContainerNetworkIp, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, materializeLayerFromArn, mcpInvokeOnce, parseConnectionsPath, parseSelectionExpressionPath, pickAgentCoreCandidateStack, probeHostGatewaySupport, readMtlsMaterialsFromDisk, resolveAgentCoreTarget, resolveEnvVars, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSingleTarget, resolveWatchConfig, signAgentCoreInvocation, startApiServer, substituteImagePlaceholders, tryResolveImageFnJoin, verifyJwtViaDiscovery, waitForAgentCorePing } from "cdk-local/internal";
67
67
  import { createServer } from "node:net";
68
68
  import { promisify } from "node:util";
69
69
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -44392,7 +44392,7 @@ async function runDetached(opts) {
44392
44392
  if (opts.platform) args.push("--platform", opts.platform);
44393
44393
  if (opts.extraHosts) for (const entry of opts.extraHosts) args.push("--add-host", `${entry.host}:${entry.ip}`);
44394
44394
  const host = opts.host ?? "127.0.0.1";
44395
- args.push("-p", `${host}:${opts.hostPort}:8080`);
44395
+ args.push("-p", `${host}:${opts.hostPort}:${opts.containerPort ?? 8080}`);
44396
44396
  if (opts.debugPort !== void 0) args.push("-p", `${host}:${opts.debugPort}:${opts.debugPort}`);
44397
44397
  for (const mount of opts.mounts) {
44398
44398
  const ro = mount.readOnly ? ":ro" : "";
@@ -44402,7 +44402,12 @@ async function runDetached(opts) {
44402
44402
  const ro = mount.readOnly ? ":ro" : "";
44403
44403
  args.push("-v", `${mount.hostPath}:${mount.containerPath}${ro}`);
44404
44404
  }
44405
- for (const [k, v] of Object.entries(opts.env)) args.push("-e", `${k}=${v}`);
44405
+ const sensitiveKeys = opts.sensitiveEnvKeys && opts.sensitiveEnvKeys.size > 0 ? new Set([...SENSITIVE_ENV_KEYS, ...opts.sensitiveEnvKeys]) : SENSITIVE_ENV_KEYS;
44406
+ const passthroughEnv = {};
44407
+ for (const [k, v] of Object.entries(opts.env)) if (sensitiveKeys.has(k)) {
44408
+ args.push("-e", k);
44409
+ passthroughEnv[k] = v;
44410
+ } else args.push("-e", `${k}=${v}`);
44406
44411
  if (opts.tmpfs) args.push("--tmpfs", `${opts.tmpfs.target}:rw,size=${opts.tmpfs.sizeMb}m`);
44407
44412
  if (opts.workingDir) args.push("--workdir", opts.workingDir);
44408
44413
  let entryPointTail = [];
@@ -44413,7 +44418,13 @@ async function runDetached(opts) {
44413
44418
  args.push(opts.image, ...entryPointTail, ...opts.cmd);
44414
44419
  getLogger().child("docker").debug(`${getDockerCmd()} ${redactAwsCredentialsInArgs(args).join(" ")}`);
44415
44420
  try {
44416
- const { stdout } = await execFileAsync$3(getDockerCmd(), args, { maxBuffer: 10 * 1024 * 1024 });
44421
+ const { stdout } = await execFileAsync$3(getDockerCmd(), args, {
44422
+ maxBuffer: 10 * 1024 * 1024,
44423
+ env: {
44424
+ ...process.env,
44425
+ ...passthroughEnv
44426
+ }
44427
+ });
44417
44428
  return stdout.trim();
44418
44429
  } catch (error) {
44419
44430
  const err = error;
@@ -44521,6 +44532,22 @@ const REDACTED_ENV_KEYS = new Set([
44521
44532
  "AWS_SESSION_TOKEN"
44522
44533
  ]);
44523
44534
  /**
44535
+ * Built-in sensitive env keys whose VALUES are always routed through
44536
+ * docker's value-from-process-env form (`-e KEY` rather than
44537
+ * `-e KEY=value`) by {@link runDetached}. Mirrors the redaction-only
44538
+ * {@link REDACTED_ENV_KEYS} set above, but stronger: the value is kept
44539
+ * off the docker `run` argv (and therefore off `ps` / `/proc/<pid>/cmdline`
44540
+ * / verbose debug logs) instead of just being masked at log time. A caller
44541
+ * may extend this set via the `sensitiveEnvKeys` option (used by the
44542
+ * AgentCore local-invoke path to keep decrypted `--from-cfn-stack`
44543
+ * SecureString SSM values off the argv).
44544
+ */
44545
+ const SENSITIVE_ENV_KEYS = new Set([
44546
+ "AWS_ACCESS_KEY_ID",
44547
+ "AWS_SECRET_ACCESS_KEY",
44548
+ "AWS_SESSION_TOKEN"
44549
+ ]);
44550
+ /**
44524
44551
  * Returns a copy of `args` with any `-e <KEY>=<value>` pair whose KEY is
44525
44552
  * in {@link REDACTED_ENV_KEYS} replaced with `-e <KEY>=***`. The actual
44526
44553
  * `args` passed to `spawn` are never mutated — this is for log output
@@ -46070,7 +46097,7 @@ async function localStartApiCommand(target, options) {
46070
46097
  await ensureDockerAvailable();
46071
46098
  const appCmd = resolveApp(options.app);
46072
46099
  if (!appCmd) throw new Error("No CDK app specified. Pass --app, set CDKD_APP, or add \"app\" to cdk.json.");
46073
- const overrides = readEnvOverridesFile$3(options.envVars);
46100
+ const overrides = readEnvOverridesFile$4(options.envVars);
46074
46101
  const debugPortBase = options.debugPortBase ? parseDebugPort(options.debugPortBase) : void 0;
46075
46102
  const perLambdaConcurrency = parsePerLambdaConcurrency(options.perLambdaConcurrency);
46076
46103
  const inlineTmpDirs = /* @__PURE__ */ new Set();
@@ -46689,7 +46716,7 @@ async function buildContainerSpec(args) {
46689
46716
  dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
46690
46717
  if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
46691
46718
  } else {
46692
- forwardAwsEnv$1(dockerEnv);
46719
+ forwardAwsEnv$2(dockerEnv);
46693
46720
  if (profileCredentials) {
46694
46721
  dockerEnv["AWS_ACCESS_KEY_ID"] = profileCredentials.accessKeyId;
46695
46722
  dockerEnv["AWS_SECRET_ACCESS_KEY"] = profileCredentials.secretAccessKey;
@@ -47036,7 +47063,7 @@ function getTemplateEnv$1(resource) {
47036
47063
  return vars;
47037
47064
  }
47038
47065
  /** Read the SAM-shape `--env-vars` JSON file. */
47039
- function readEnvOverridesFile$3(filePath) {
47066
+ function readEnvOverridesFile$4(filePath) {
47040
47067
  if (!filePath) return void 0;
47041
47068
  let raw;
47042
47069
  try {
@@ -47058,7 +47085,7 @@ function readEnvOverridesFile$3(filePath) {
47058
47085
  * handler's AWS SDK calls can authenticate. Used when --assume-role is
47059
47086
  * NOT set for that Lambda — SAM-compatible default.
47060
47087
  */
47061
- function forwardAwsEnv$1(env) {
47088
+ function forwardAwsEnv$2(env) {
47062
47089
  for (const key of [
47063
47090
  "AWS_ACCESS_KEY_ID",
47064
47091
  "AWS_SECRET_ACCESS_KEY",
@@ -48429,7 +48456,7 @@ async function localRunTaskCommand(target, options) {
48429
48456
  }
48430
48457
  const sidecarCredentials = await resolveSidecarCredentials(options, assumedCredentials);
48431
48458
  if (options.profile && sidecarCredentials && !assumedCredentials) profileCredsFile = await writeProfileCredentialsFile(options.profile, sidecarCredentials);
48432
- const envOverrides = readEnvOverridesFile$2(options.envVars);
48459
+ const envOverrides = readEnvOverridesFile$3(options.envVars);
48433
48460
  const runOpts = {
48434
48461
  cluster: options.cluster,
48435
48462
  containerHost: options.containerHost,
@@ -48529,7 +48556,7 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
48529
48556
  if (!region) logger.warn("Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack.");
48530
48557
  let accountId;
48531
48558
  try {
48532
- accountId = await resolveCallerAccountId$1(region);
48559
+ accountId = await resolveCallerAccountId$2(region);
48533
48560
  } catch (err) {
48534
48561
  logger.warn(`Resolver needs \${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. Substitution will be skipped; affected env / secret entries will be dropped with per-key warnings.`);
48535
48562
  }
@@ -48559,7 +48586,7 @@ function pickCandidateStack$1(stackPattern, stacks) {
48559
48586
  const matched = matchStacks(stacks, [stackPattern]);
48560
48587
  if (matched.length === 1) return matched[0];
48561
48588
  }
48562
- async function resolveCallerAccountId$1(region) {
48589
+ async function resolveCallerAccountId$2(region) {
48563
48590
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
48564
48591
  const sts = new STSClient({ ...region && { region } });
48565
48592
  try {
@@ -48573,7 +48600,7 @@ async function resolveCallerAccountId$1(region) {
48573
48600
  * `cdkd local invoke --env-vars`: top-level keys are container names, with
48574
48601
  * `Parameters` reserved for global entries.
48575
48602
  */
48576
- function readEnvOverridesFile$2(filePath) {
48603
+ function readEnvOverridesFile$3(filePath) {
48577
48604
  if (!filePath) return void 0;
48578
48605
  let raw;
48579
48606
  try {
@@ -49488,7 +49515,7 @@ async function runOneTarget(target, runState, stacks, options, discovery, skipPu
49488
49515
  resolvedRoleArn = options.assumeTaskRole;
49489
49516
  assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);
49490
49517
  }
49491
- const envOverrides = readEnvOverridesFile$1(options.envVars);
49518
+ const envOverrides = readEnvOverridesFile$2(options.envVars);
49492
49519
  const taskOpts = {
49493
49520
  cluster: options.cluster,
49494
49521
  containerHost: options.containerHost,
@@ -49564,7 +49591,7 @@ async function buildEcsImageResolutionContext(target, stacks, options, stateProv
49564
49591
  if (!region) logger.warn("Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack.");
49565
49592
  let accountId;
49566
49593
  try {
49567
- accountId = await resolveCallerAccountId(region);
49594
+ accountId = await resolveCallerAccountId$1(region);
49568
49595
  } catch (err) {
49569
49596
  logger.warn(`Resolver needs \${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. Substitution will be skipped; affected env / secret entries will be dropped with per-key warnings.`);
49570
49597
  }
@@ -49594,7 +49621,7 @@ function pickCandidateStack(stackPattern, stacks) {
49594
49621
  const matched = matchStacks(stacks, [stackPattern]);
49595
49622
  if (matched.length === 1) return matched[0];
49596
49623
  }
49597
- async function resolveCallerAccountId(region) {
49624
+ async function resolveCallerAccountId$1(region) {
49598
49625
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
49599
49626
  const sts = new STSClient({ ...region && { region } });
49600
49627
  try {
@@ -49603,7 +49630,7 @@ async function resolveCallerAccountId(region) {
49603
49630
  sts.destroy();
49604
49631
  }
49605
49632
  }
49606
- function readEnvOverridesFile$1(filePath) {
49633
+ function readEnvOverridesFile$2(filePath) {
49607
49634
  if (!filePath) return void 0;
49608
49635
  let raw;
49609
49636
  try {
@@ -49693,6 +49720,812 @@ function createLocalStartServiceCommand() {
49693
49720
  return cmd;
49694
49721
  }
49695
49722
 
49723
+ //#endregion
49724
+ //#region src/cli/commands/local-invoke-agentcore.ts
49725
+ /**
49726
+ * Parser for `--timeout <ms>`. Accepts a positive integer; rejects 0,
49727
+ * negatives, fractions, and non-numeric input.
49728
+ */
49729
+ function parseTimeoutMs(raw) {
49730
+ const parsed = Number(raw);
49731
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new CdkdError(`--timeout must be a positive integer number of milliseconds (got '${raw}').`, "LOCAL_INVOKE_AGENTCORE_TIMEOUT_INVALID");
49732
+ return parsed;
49733
+ }
49734
+ /**
49735
+ * `cdkd local invoke-agentcore <target>` — run a Bedrock AgentCore Runtime container
49736
+ * locally and invoke it once over the AgentCore HTTP contract. Resolves
49737
+ * the `AWS::BedrockAgentCore::Runtime`, pulls / builds its container,
49738
+ * starts it on port 8080, waits for `GET /ping`, POSTs the event to
49739
+ * `POST /invocations`, prints the response, and tears down. Covers the
49740
+ * container artifact and the CodeConfiguration managed-runtime artifact
49741
+ * (fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent's
49742
+ * calls to real AWS go to real AWS (credentials injected like `cdkd local invoke`).
49743
+ */
49744
+ async function localInvokeAgentCoreCommand(target, options) {
49745
+ const logger = getLogger();
49746
+ if (options.verbose) logger.setLevel("debug");
49747
+ warnIfDeprecatedRegion(options);
49748
+ let containerId;
49749
+ let stopLogs;
49750
+ let sigintHandler;
49751
+ let profileCredsFile;
49752
+ let stateProvider;
49753
+ const cleanup = singleFlight(async () => {
49754
+ if (stateProvider) try {
49755
+ stateProvider.dispose();
49756
+ } catch (err) {
49757
+ getLogger().debug(`state provider dispose failed: ${err instanceof Error ? err.message : String(err)}`);
49758
+ }
49759
+ if (stopLogs) try {
49760
+ stopLogs();
49761
+ } catch (err) {
49762
+ getLogger().debug(`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`);
49763
+ }
49764
+ if (containerId) try {
49765
+ await removeContainer(containerId);
49766
+ } catch (err) {
49767
+ getLogger().debug(`removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`);
49768
+ }
49769
+ if (profileCredsFile) try {
49770
+ await profileCredsFile.dispose();
49771
+ } catch (err) {
49772
+ getLogger().debug(`Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${err instanceof Error ? err.message : String(err)}`);
49773
+ }
49774
+ }, (err) => {
49775
+ getLogger().debug(`cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
49776
+ });
49777
+ try {
49778
+ await applyRoleArnIfSet({
49779
+ roleArn: options.roleArn,
49780
+ region: options.region
49781
+ });
49782
+ await ensureDockerAvailable();
49783
+ const profileCredentials = options.profile ? await resolveProfileCredentials(options.profile) : void 0;
49784
+ if (options.profile && profileCredentials) profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);
49785
+ const appCmd = resolveApp(options.app);
49786
+ if (!appCmd) throw new CdkdError(`No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add "app" to cdk.json.`, "LOCAL_INVOKE_AGENTCORE_NO_APP");
49787
+ logger.info("Synthesizing CDK app...");
49788
+ const synthesizer = new Synthesizer();
49789
+ const context = parseContextOptions(options.context);
49790
+ const synthOpts = {
49791
+ app: appCmd,
49792
+ output: options.output,
49793
+ ...options.region && { region: options.region },
49794
+ ...options.profile && { profile: options.profile },
49795
+ ...Object.keys(context).length > 0 && { context }
49796
+ };
49797
+ const { stacks } = await synthesizer.synthesize(synthOpts);
49798
+ const resolvedTarget = await resolveSingleTarget(target, {
49799
+ entries: listTargets(stacks).agentCoreRuntimes,
49800
+ message: "Select an AgentCore Runtime to invoke",
49801
+ noun: "AgentCore Runtimes",
49802
+ onMissing: () => new CdkdError(`${getEmbedConfig().cliName} invoke-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_INVOKE_AGENTCORE_TARGET_REQUIRED")
49803
+ });
49804
+ const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
49805
+ stateProvider = createLocalStateProvider$1(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region));
49806
+ const { context: imageContext, loaded: loadedState } = stateProvider && candidate ? await buildAgentCoreImageContext(candidate, stateProvider, options) : {
49807
+ context: void 0,
49808
+ loaded: void 0
49809
+ };
49810
+ const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);
49811
+ logger.info(`Target: ${resolved.stack.stackName}/${resolved.logicalId} (${resolved.protocol})`);
49812
+ const isMcp = resolved.protocol === AGENTCORE_MCP_PROTOCOL;
49813
+ const isA2a = resolved.protocol === AGENTCORE_A2A_PROTOCOL;
49814
+ if (resolved.protocol === AGENTCORE_AGUI_PROTOCOL) logger.info("AGUI runtime: routing through the HTTP /invocations + /ws path (AG-UI wire is SSE / WebSocket on port 8080).");
49815
+ if ((isMcp || isA2a) && options.ws) logger.warn(`--ws applies only to the HTTP / AGUI protocols; ignoring it for this ${resolved.protocol} runtime.`);
49816
+ if (options.wsInteractive && !options.ws) logger.warn("--ws-interactive is meaningful only with --ws; ignoring.");
49817
+ 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.");
49818
+ const sessionId = options.sessionId ?? randomUUID();
49819
+ const event = await readEvent$1(options);
49820
+ const mcpRequest = isMcp ? buildMcpRequest(event) : void 0;
49821
+ const a2aRequest = isA2a ? buildA2aRequest(event) : void 0;
49822
+ let authorization;
49823
+ if (isMcp || isA2a) {
49824
+ if (resolved.jwtAuthorizer || options.bearerToken) {
49825
+ const pathLabel = isMcp ? MCP_PATH : A2A_PATH;
49826
+ logger.info(`${resolved.protocol} runtime: invoking the local container's ${pathLabel} directly (vanilla ${resolved.protocol}). An inbound JWT / --bearer-token is an AgentCore managed-plane concern and is not applied locally.`);
49827
+ }
49828
+ } else authorization = await resolveInboundAuthorization(resolved, options);
49829
+ await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);
49830
+ const image = await resolveAgentCoreImage(resolved, options, loadedState);
49831
+ const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext);
49832
+ const hostPort = await pickFreePort();
49833
+ const containerHost = options.containerHost;
49834
+ const containerName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
49835
+ const containerPort = isMcp ? MCP_CONTAINER_PORT : isA2a ? A2A_CONTAINER_PORT : void 0;
49836
+ const containerPortLabel = isMcp ? `${MCP_CONTAINER_PORT}${MCP_PATH}` : isA2a ? `${A2A_CONTAINER_PORT}${A2A_PATH}` : "8080";
49837
+ logger.info(`Starting agent container (image=${image}, port=${hostPort} -> ${containerPortLabel})...`);
49838
+ containerId = await runDetached({
49839
+ image,
49840
+ mounts: [],
49841
+ env: dockerEnv,
49842
+ cmd: [],
49843
+ hostPort,
49844
+ host: containerHost,
49845
+ platform: options.platform,
49846
+ name: containerName,
49847
+ ...containerPort !== void 0 && { containerPort },
49848
+ ...sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys }
49849
+ });
49850
+ stopLogs = streamLogs(containerId);
49851
+ sigintHandler = () => {
49852
+ cleanup().then(() => process.exit(130));
49853
+ };
49854
+ process.on("SIGINT", sigintHandler);
49855
+ if (isMcp && mcpRequest) {
49856
+ logger.info(`MCP request: ${mcpRequest.method}`);
49857
+ const mcp = await mcpInvokeOnce(containerHost, hostPort, mcpRequest, { requestTimeoutMs: options.timeout });
49858
+ await new Promise((r) => setTimeout(r, 250));
49859
+ emitMcpResult(mcp);
49860
+ } else if (isA2a && a2aRequest) {
49861
+ logger.info(`A2A request: ${a2aRequest.method}`);
49862
+ const a2a = await a2aInvokeOnce(containerHost, hostPort, a2aRequest, { requestTimeoutMs: options.timeout });
49863
+ await new Promise((r) => setTimeout(r, 250));
49864
+ emitA2aResult(a2a);
49865
+ } else if (options.ws) {
49866
+ await waitForAgentCorePing(containerHost, hostPort);
49867
+ const frameSource = options.wsInteractive ? readStdinLines() : void 0;
49868
+ 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...");
49869
+ const wsResult = await invokeAgentCoreWs(containerHost, hostPort, event, {
49870
+ sessionId,
49871
+ timeoutMs: options.timeout,
49872
+ onMessage: (text) => process.stdout.write(text),
49873
+ ...authorization && { authorization },
49874
+ ...frameSource && { frameSource }
49875
+ });
49876
+ await new Promise((r) => setTimeout(r, 250));
49877
+ emitWsResult(wsResult);
49878
+ } else {
49879
+ await waitForAgentCorePing(containerHost, hostPort);
49880
+ const additionalHeaders = await buildSigV4HeadersIfRequested(options, resolved, loadedState, containerHost, hostPort, event, sessionId);
49881
+ const result = await invokeAgentCore(containerHost, hostPort, event, {
49882
+ sessionId,
49883
+ timeoutMs: options.timeout,
49884
+ onChunk: (text) => process.stdout.write(text),
49885
+ ...authorization && { authorization },
49886
+ ...additionalHeaders && { additionalHeaders }
49887
+ });
49888
+ await new Promise((r) => setTimeout(r, 250));
49889
+ emitResult(result);
49890
+ }
49891
+ } finally {
49892
+ if (sigintHandler) process.off("SIGINT", sigintHandler);
49893
+ await cleanup();
49894
+ }
49895
+ }
49896
+ /**
49897
+ * Enforce the runtime's inbound JWT authorizer (when declared) and return
49898
+ * the `Authorization` header to forward to `/invocations`.
49899
+ *
49900
+ * - No authorizer → forward the token verbatim if one was given (no-op
49901
+ * otherwise).
49902
+ * - `--no-verify-auth` → warn + forward without verifying (local-dev escape).
49903
+ * - Authorizer + no token → reject (AgentCore returns 401).
49904
+ * - Authorizer + token → verify against the OIDC discovery URL; reject on
49905
+ * failure (AgentCore returns 403); forward on success. An unreachable
49906
+ * discovery URL falls back to pass-through accept (offline-dev fallback in
49907
+ * {@link verifyJwtViaDiscovery}).
49908
+ *
49909
+ * Exported so a unit test can drive the gate without the full Docker pipeline.
49910
+ */
49911
+ async function resolveInboundAuthorization(resolved, options) {
49912
+ const logger = getLogger();
49913
+ const authorizer = resolved.jwtAuthorizer;
49914
+ const header = options.bearerToken ? `Bearer ${options.bearerToken}` : void 0;
49915
+ if (!authorizer) return header;
49916
+ if (options.verifyAuth === false) {
49917
+ logger.warn(`Runtime '${resolved.logicalId}' declares a customJwtAuthorizer, but --no-verify-auth was set — skipping inbound JWT verification (local-dev escape hatch).`);
49918
+ return header;
49919
+ }
49920
+ if (!header) throw new CdkdError(`Runtime '${resolved.logicalId}' requires an inbound JWT (customJwtAuthorizer). Pass --bearer-token <jwt>, or --no-verify-auth to skip verification for local dev.`, "LOCAL_INVOKE_AGENTCORE_AUTH_REQUIRED");
49921
+ if (!(await verifyJwtViaDiscovery({
49922
+ discoveryUrl: authorizer.discoveryUrl,
49923
+ ...authorizer.allowedAudience && { allowedAudience: authorizer.allowedAudience },
49924
+ ...authorizer.allowedClients && { allowedClients: authorizer.allowedClients },
49925
+ ...authorizer.allowedScopes && { allowedScopes: authorizer.allowedScopes },
49926
+ ...authorizer.customClaims && { customClaims: authorizer.customClaims }
49927
+ }, header, createJwksCache(), { warned: /* @__PURE__ */ new Set() })).allow) throw new CdkdError(`Inbound JWT rejected by the runtime's customJwtAuthorizer (signature / issuer / expiry / audience check failed against ${authorizer.discoveryUrl}).`, "LOCAL_INVOKE_AGENTCORE_AUTH_DENIED");
49928
+ logger.info(`Inbound JWT verified against ${authorizer.discoveryUrl}.`);
49929
+ return header;
49930
+ }
49931
+ /**
49932
+ * Compute the SigV4 headers for the `/invocations` POST when `--sigv4` is
49933
+ * requested. Returns `undefined` (no header overlay) when:
49934
+ *
49935
+ * - `--sigv4` is not set,
49936
+ * - the runtime declares a `customJwtAuthorizer` (the JWT path wins; warns),
49937
+ *
49938
+ * Throws a {@link CdkdError} when `--sigv4` conflicts with
49939
+ * `--bearer-token`, or when no AWS credentials are resolvable for signing.
49940
+ *
49941
+ * Exported so a unit test can drive the gate without the full Docker pipeline.
49942
+ */
49943
+ async function buildSigV4HeadersIfRequested(options, resolved, loaded, host, port, event, sessionId) {
49944
+ if (!options.sigv4) return void 0;
49945
+ if (options.bearerToken) throw new CdkdError(`--sigv4 and --bearer-token are mutually exclusive: pick one inbound auth.`, "LOCAL_INVOKE_AGENTCORE_AUTH_CONFLICT");
49946
+ if (resolved.jwtAuthorizer) {
49947
+ getLogger().warn(`Runtime '${resolved.logicalId}' declares a customJwtAuthorizer; --sigv4 ignored (JWT path takes precedence).`);
49948
+ return;
49949
+ }
49950
+ const region = options.region ?? options.stackRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? resolved.stack.region;
49951
+ if (!region) throw new CdkdError("--sigv4: no region resolved for the AgentCore signing scope. Pass --region <region>, set AWS_REGION, or use --from-cfn-stack with a region-bound stack.", "LOCAL_INVOKE_AGENTCORE_SIGV4_NO_REGION");
49952
+ const signed = await signAgentCoreInvocation({
49953
+ credentials: await resolveHostCredentialsForSigV4(options, resolved, loaded, region),
49954
+ region,
49955
+ host,
49956
+ port,
49957
+ path: "/invocations",
49958
+ body: JSON.stringify(event ?? {}),
49959
+ sessionId
49960
+ });
49961
+ const headers = {
49962
+ Authorization: signed.authorization,
49963
+ "X-Amz-Date": signed.amzDate,
49964
+ "X-Amz-Content-Sha256": signed.amzContentSha256
49965
+ };
49966
+ if (signed.amzSecurityToken) headers["X-Amz-Security-Token"] = signed.amzSecurityToken;
49967
+ getLogger().info(`Signed /invocations with SigV4 (region=${region}).`);
49968
+ return headers;
49969
+ }
49970
+ /**
49971
+ * Resolve credentials for host-side SigV4 signing. Precedence:
49972
+ * 1. `--assume-role` → STS temp creds (warn + fall through on STS failure);
49973
+ * 2. `--profile` → profile creds (sessionToken when the profile carries one);
49974
+ * 3. shell env (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / optional
49975
+ * `AWS_SESSION_TOKEN`).
49976
+ *
49977
+ * Throws a {@link CdkdError} when none are available — `--sigv4` cannot
49978
+ * proceed without credentials, unlike the unsigned path.
49979
+ */
49980
+ async function resolveHostCredentialsForSigV4(options, resolved, loaded, region) {
49981
+ const logger = getLogger();
49982
+ const assumeRoleArn = resolveAssumeRoleArn(options, resolved, loaded);
49983
+ if (assumeRoleArn) try {
49984
+ return await assumeAgentCoreExecutionRole(assumeRoleArn, region);
49985
+ } catch (err) {
49986
+ logger.warn(`--assume-role: STS AssumeRole(${assumeRoleArn}) failed for --sigv4 signing: ${err instanceof Error ? err.message : String(err)}. Falling back to ${options.profile ? `--profile ${options.profile}` : "shell credentials"}.`);
49987
+ }
49988
+ if (options.profile) {
49989
+ const creds = await resolveProfileCredentials(options.profile);
49990
+ if (creds?.accessKeyId && creds.secretAccessKey) return {
49991
+ accessKeyId: creds.accessKeyId,
49992
+ secretAccessKey: creds.secretAccessKey,
49993
+ ...creds.sessionToken && { sessionToken: creds.sessionToken }
49994
+ };
49995
+ }
49996
+ const accessKeyId = process.env["AWS_ACCESS_KEY_ID"];
49997
+ const secretAccessKey = process.env["AWS_SECRET_ACCESS_KEY"];
49998
+ if (accessKeyId && secretAccessKey) {
49999
+ const sessionToken = process.env["AWS_SESSION_TOKEN"];
50000
+ return {
50001
+ accessKeyId,
50002
+ secretAccessKey,
50003
+ ...sessionToken && { sessionToken }
50004
+ };
50005
+ }
50006
+ throw new CdkdError("--sigv4: no AWS credentials available to sign the request. Set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, pass --profile <name>, or pass --assume-role <arn>.", "LOCAL_INVOKE_AGENTCORE_SIGV4_NO_CREDENTIALS");
50007
+ }
50008
+ /**
50009
+ * Acquire the agent image. A CODE artifact (managed runtime) is built from
50010
+ * source — a fromCodeAsset bundle from its cdk.out asset, a fromS3 bundle
50011
+ * downloaded + extracted from S3. A CONTAINER artifact mirrors the
50012
+ * container-Lambda path: build from a local cdk.out asset when the URI matches
50013
+ * one, else pull from ECR, else pull a plain registry image.
50014
+ *
50015
+ * `loaded` is the `--from-cfn-stack` state record (when available) — threaded
50016
+ * through so a bare `--assume-role` can resolve the execution-role ARN from
50017
+ * state for the fromS3 download.
50018
+ */
50019
+ async function resolveAgentCoreImage(resolved, options, loaded) {
50020
+ const logger = getLogger();
50021
+ const architecture = platformToArchitecture(options.platform);
50022
+ if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture, loaded);
50023
+ const containerUri = resolved.containerUri;
50024
+ if (containerUri === void 0) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' has neither a container image nor a code artifact to run.`, "LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT");
50025
+ const manifestPath = resolved.stack.assetManifestPath;
50026
+ if (manifestPath) {
50027
+ const cdkOutDir = dirname(manifestPath);
50028
+ const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, resolved.stack.stackName);
50029
+ if (manifest) {
50030
+ const entry = getDockerImageBySourceHash(manifest, containerUri);
50031
+ if (entry) return buildContainerImage$1(entry.asset, cdkOutDir, {
50032
+ architecture,
50033
+ noBuild: options.build === false
50034
+ });
50035
+ }
50036
+ }
50037
+ if (parseEcrUri(containerUri)) {
50038
+ logger.info(`Pulling agent image from ECR: ${containerUri}`);
50039
+ return pullEcrImage(containerUri, {
50040
+ skipPull: options.pull === false,
50041
+ ...options.region !== void 0 && { region: options.region },
50042
+ ...options.ecrRoleArn !== void 0 && { ecrRoleArn: options.ecrRoleArn },
50043
+ ...options.profile !== void 0 && { profile: options.profile }
50044
+ });
50045
+ }
50046
+ await pullImage(containerUri, options.pull === false);
50047
+ return containerUri;
50048
+ }
50049
+ /**
50050
+ * Build a local image from a `CodeConfiguration` (managed-runtime) bundle.
50051
+ *
50052
+ * - fromS3 (`code.s3Source` set, a literal S3 object): download + extract the
50053
+ * bundle, then run the from-source build over the extracted dir.
50054
+ * - fromCodeAsset: locate the source dir in cdk.out via its asset hash, then
50055
+ * run the same from-source build (generated Dockerfile → install deps → run
50056
+ * EntryPoint).
50057
+ */
50058
+ async function resolveAgentCoreCodeImage(resolved, code, options, architecture, loaded) {
50059
+ if (code.s3Source) return resolveAgentCoreCodeImageFromS3(resolved, code, code.s3Source, options, architecture, loaded);
50060
+ const manifestPath = resolved.stack.assetManifestPath;
50061
+ if (!manifestPath) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' uses a code artifact, but its stack has no asset manifest in cdk.out to read the bundle source from.`, "LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST");
50062
+ const cdkOutDir = dirname(manifestPath);
50063
+ const loader = new AssetManifestLoader();
50064
+ const manifest = await loader.loadManifest(cdkOutDir, resolved.stack.stackName);
50065
+ const fileAssets = manifest ? loader.getFileAssets(manifest) : void 0;
50066
+ const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey(fileAssets, code.codeAssetHash) : void 0;
50067
+ if (!asset) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' code bundle (asset ${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");
50068
+ const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset);
50069
+ if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' code bundle source '${sourceDir}' does not exist or is not a directory. Re-synthesize the app and retry.`, "LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING");
50070
+ return buildAgentCoreCodeImage({
50071
+ sourceDir,
50072
+ runtime: code.runtime,
50073
+ entryPoint: code.entryPoint,
50074
+ architecture,
50075
+ noBuild: options.build === false
50076
+ });
50077
+ }
50078
+ /**
50079
+ * Build a local image from a fromS3 CodeConfiguration bundle: download +
50080
+ * extract the S3 object, run the from-source build over the extracted dir, then
50081
+ * clean up the temp dir.
50082
+ *
50083
+ * Credentials mirror the rest of the command: an `--assume-role` ARN (explicit,
50084
+ * or resolved from `--from-cfn-stack` state for the bare form) yields STS temp
50085
+ * creds for the download; otherwise `--profile` / the default chain is used.
50086
+ * The region is `--region` / `--stack-region` / env / the stack's region.
50087
+ */
50088
+ async function resolveAgentCoreCodeImageFromS3(resolved, code, s3Source, options, architecture, loaded) {
50089
+ const logger = getLogger();
50090
+ if (typeof s3Source.bucket !== "string" || s3Source.bucket.length === 0) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' fromS3 bundle reached the image step with no literal bucket. This is a cdkd bug — please report it.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_UNRESOLVED");
50091
+ const location = {
50092
+ bucket: s3Source.bucket,
50093
+ key: s3Source.key,
50094
+ ...s3Source.versionId !== void 0 && { versionId: s3Source.versionId }
50095
+ };
50096
+ const region = options.region ?? options.stackRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? resolved.stack.region;
50097
+ const assumeRoleArn = resolveAssumeRoleArn(options, resolved, loaded);
50098
+ let credentials;
50099
+ if (assumeRoleArn) try {
50100
+ credentials = await assumeAgentCoreExecutionRole(assumeRoleArn, region);
50101
+ } catch (err) {
50102
+ logger.warn(`--assume-role: STS AssumeRole(${assumeRoleArn}) failed for the fromS3 bundle download: ${err instanceof Error ? err.message : String(err)}. Falling back to ${options.profile ? `--profile ${options.profile}` : "the default credentials"}.`);
50103
+ }
50104
+ const bundle = await downloadAndExtractS3Bundle(location, {
50105
+ ...region !== void 0 && { region },
50106
+ ...options.profile !== void 0 && { profile: options.profile },
50107
+ ...credentials !== void 0 && { credentials }
50108
+ });
50109
+ try {
50110
+ return await buildAgentCoreCodeImage({
50111
+ sourceDir: bundle.dir,
50112
+ runtime: code.runtime,
50113
+ entryPoint: code.entryPoint,
50114
+ architecture,
50115
+ noBuild: options.build === false
50116
+ });
50117
+ } finally {
50118
+ await bundle.cleanup();
50119
+ }
50120
+ }
50121
+ /**
50122
+ * Find the file asset whose destination objectKey is `<hash>.zip` (matching the
50123
+ * `Code.S3.Prefix`'s hash) when the source-hash-keyed lookup misses — covers a
50124
+ * synthesizer whose source hash differs from the destination objectKey.
50125
+ */
50126
+ function findFileAssetByObjectKey(fileAssets, hash) {
50127
+ const zip = `${hash}.zip`;
50128
+ for (const asset of fileAssets.values()) if (Object.values(asset.destinations).some((d) => d.objectKey === zip || d.objectKey.endsWith(`/${zip}`))) return asset;
50129
+ }
50130
+ /**
50131
+ * Build the container env + the set of env keys to keep off the `docker run`
50132
+ * argv. Substitutes `--from-cfn-stack` state into the template env (reusing the
50133
+ * shared state load + image-resolution context — Ref / Fn::Sub / Fn::Join +
50134
+ * SSM parameters, with decrypted SecureString values flagged sensitive),
50135
+ * applies `--env-vars` overrides, then injects AWS credentials (`--assume-role`
50136
+ * STS temp creds — resolving an intrinsic RoleArn from state for bare
50137
+ * `--assume-role` — else `--profile` / dev creds).
50138
+ *
50139
+ * The state provider + loaded record + image context are built once by the
50140
+ * caller and shared here, so this does not re-load state.
50141
+ */
50142
+ async function buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loaded, imageContext) {
50143
+ const logger = getLogger();
50144
+ let templateEnv = resolved.environmentVariables;
50145
+ const sensitiveEnvKeys = /* @__PURE__ */ new Set();
50146
+ if (stateProvider && loaded) {
50147
+ const subContext = {
50148
+ resources: imageContext?.stateResources ?? loaded.resources,
50149
+ consumerRegion: loaded.region
50150
+ };
50151
+ const pseudo = imageContext?.pseudoParameters ?? derivePseudoParametersFromRegion(loaded.region);
50152
+ if (pseudo) subContext.pseudoParameters = pseudo;
50153
+ if (imageContext?.stateParameters) subContext.parameters = imageContext.stateParameters;
50154
+ if (imageContext?.stateSensitiveParameters?.length) subContext.sensitiveParameters = new Set(imageContext.stateSensitiveParameters);
50155
+ const resolver = await stateProvider.buildCrossStackResolver(loaded.region);
50156
+ if (resolver) subContext.crossStackResolver = resolver;
50157
+ const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);
50158
+ templateEnv = env;
50159
+ for (const key of audit.resolvedKeys) logger.debug(`${stateProvider.label}: substituted env var ${key}`);
50160
+ for (const key of audit.sensitiveKeys) sensitiveEnvKeys.add(key);
50161
+ for (const { key, reason } of audit.unresolved) logger.warn(`${stateProvider.label}: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
50162
+ }
50163
+ const overrides = readEnvOverridesFile$1(options.envVars);
50164
+ const cdkPath = readCdkPathOrUndefined(resolved.resource);
50165
+ const envResult = resolveEnvVars(resolved.logicalId, cdkPath, templateEnv, overrides);
50166
+ for (const key of envResult.unresolved) {
50167
+ const overrideKeyExample = cdkPath?.replace(/\/Resource$/, "") ?? resolved.logicalId;
50168
+ logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${overrideKeyExample}":{"${key}":"<literal>"}}), or pass a state-source flag (e.g. --from-cfn-stack) to recover deployed values.`);
50169
+ }
50170
+ const dockerEnv = { ...envResult.resolved };
50171
+ const assumeRoleArn = resolveAssumeRoleArn(options, resolved, loaded);
50172
+ await applyAgentCoreCredentialEnv(dockerEnv, {
50173
+ ...assumeRoleArn !== void 0 && { assumeRoleArn },
50174
+ ...options.region !== void 0 && { region: options.region },
50175
+ ...profileCredentials !== void 0 && { profileCredentials },
50176
+ ...profileCredsFile !== void 0 && { profileCredsFile: {
50177
+ containerPath: profileCredsFile.containerPath,
50178
+ profileName: profileCredsFile.profileName
50179
+ } }
50180
+ });
50181
+ return {
50182
+ env: dockerEnv,
50183
+ sensitiveEnvKeys
50184
+ };
50185
+ }
50186
+ /**
50187
+ * Resolve a fromS3 bundle's intrinsic `Code.S3.Bucket` to a literal bucket
50188
+ * name in place on `resolved.codeArtifact.s3Source.bucket`. Uses the SAME
50189
+ * state-substitution machinery env vars use under `--from-cfn-stack`, so
50190
+ * every cross-stack intrinsic that path supports (`Ref` / `Fn::ImportValue` /
50191
+ * `Fn::GetStackOutput`) is supported transparently here.
50192
+ *
50193
+ * No-op when there is no intrinsic to resolve. Errors when no state is
50194
+ * available, or when the substitution returns a non-string / unresolved value.
50195
+ *
50196
+ * Exported so a unit test can drive the gate without the full Docker pipeline.
50197
+ */
50198
+ async function resolveFromS3BucketIntrinsic(resolved, stateProvider, loaded, imageContext) {
50199
+ const s3Source = resolved.codeArtifact?.s3Source;
50200
+ if (!s3Source || s3Source.bucketIntrinsic === void 0) return;
50201
+ if (s3Source.bucket !== void 0) return;
50202
+ if (!stateProvider || !loaded) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' fromS3 bundle's Code.S3.Bucket is an unresolved intrinsic (${describeIntrinsic(s3Source.bucketIntrinsic)}). Pass --from-cfn-stack so its physical bucket name can be resolved against the deployed stack state.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NO_STATE");
50203
+ const subContext = {
50204
+ resources: imageContext?.stateResources ?? loaded.resources,
50205
+ consumerRegion: loaded.region
50206
+ };
50207
+ const pseudo = imageContext?.pseudoParameters ?? derivePseudoParametersFromRegion(loaded.region);
50208
+ if (pseudo) subContext.pseudoParameters = pseudo;
50209
+ const crossStackResolver = await stateProvider.buildCrossStackResolver(loaded.region);
50210
+ if (crossStackResolver) subContext.crossStackResolver = crossStackResolver;
50211
+ const result = await substituteAgainstStateAsync(s3Source.bucketIntrinsic, subContext);
50212
+ if (result.kind !== "literal") throw new CdkdError(`Could not resolve AgentCore Runtime '${resolved.logicalId}' fromS3 Code.S3.Bucket intrinsic (${describeIntrinsic(s3Source.bucketIntrinsic)}) against the --from-cfn-stack state: ${result.reason}. Confirm the referenced resource / export exists in the deployed stack.`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_UNRESOLVED");
50213
+ if (typeof result.value !== "string" || result.value.length === 0) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' fromS3 Code.S3.Bucket intrinsic resolved to a ${typeof result.value} value, not a bucket name string. (${describeIntrinsic(s3Source.bucketIntrinsic)})`, "LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NOT_STRING");
50214
+ s3Source.bucket = result.value;
50215
+ getLogger().info(`Resolved fromS3 Code.S3.Bucket from state: ${describeIntrinsic(s3Source.bucketIntrinsic)} -> ${result.value}`);
50216
+ }
50217
+ /** Render the intrinsic key for an error / log message (e.g. `Ref:Bucket1`). */
50218
+ function describeIntrinsic(value) {
50219
+ if (!value || typeof value !== "object") return String(value);
50220
+ const obj = value;
50221
+ const key = Object.keys(obj)[0] ?? "?";
50222
+ const arg = obj[key];
50223
+ if (typeof arg === "string") return `${key}:${arg}`;
50224
+ return key;
50225
+ }
50226
+ /**
50227
+ * Build the `--from-cfn-stack` image-resolution context + return the loaded
50228
+ * state record (loaded once, reused by env substitution + role resolution).
50229
+ * Mirrors `run-task`'s `buildEcsImageResolutionContext`: pseudo parameters
50230
+ * (region + STS account id), the deployed resources, and SSM template
50231
+ * parameters (decrypted SecureString logical ids flagged sensitive).
50232
+ */
50233
+ async function buildAgentCoreImageContext(candidate, stateProvider, options) {
50234
+ const logger = getLogger();
50235
+ const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
50236
+ let accountId;
50237
+ try {
50238
+ accountId = await resolveCallerAccountId(region, options.profile);
50239
+ } catch (err) {
50240
+ logger.warn(`--from-cfn-stack: STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. A same-stack ECR image URI referencing \${AWS::AccountId} may not resolve.`);
50241
+ }
50242
+ const context = {};
50243
+ const pseudo = derivePseudoParametersFromRegion(region, accountId);
50244
+ if (pseudo) context.pseudoParameters = pseudo;
50245
+ const loaded = await stateProvider.load(candidate.stackName, candidate.region);
50246
+ if (loaded) {
50247
+ context.stateResources = loaded.resources;
50248
+ if (stateProvider.resolveTemplateSsmParameters) {
50249
+ const ssm = await stateProvider.resolveTemplateSsmParameters(candidate.template);
50250
+ if (Object.keys(ssm.values).length > 0) context.stateParameters = ssm.values;
50251
+ if (ssm.secureStringLogicalIds.length > 0) context.stateSensitiveParameters = ssm.secureStringLogicalIds;
50252
+ }
50253
+ }
50254
+ return {
50255
+ context,
50256
+ loaded: loaded ?? void 0
50257
+ };
50258
+ }
50259
+ /** STS `GetCallerIdentity` for the `${AWS::AccountId}` pseudo parameter (threads `--profile`). */
50260
+ async function resolveCallerAccountId(region, profile) {
50261
+ const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
50262
+ const sts = new STSClient({
50263
+ ...region && { region },
50264
+ ...profile && { profile }
50265
+ });
50266
+ try {
50267
+ return (await sts.send(new GetCallerIdentityCommand({}))).Account;
50268
+ } finally {
50269
+ sts.destroy();
50270
+ }
50271
+ }
50272
+ /**
50273
+ * Inject AWS credentials into the container env. Precedence:
50274
+ * 1. `--assume-role` → STS-issued temp creds for the resolved ARN (on
50275
+ * STS failure, warn + fall through to dev creds).
50276
+ * 2. dev shell creds (`forwardAwsEnv`) + `--profile` overlay
50277
+ * ({@link applyProfileCredentialsOverlay}) + the bind-mounted
50278
+ * credentials-file env so handler `fromIni({ profile })` resolves.
50279
+ *
50280
+ * Exported so a unit test can lock the binding (mock STS) without driving
50281
+ * the full synth + docker pipeline.
50282
+ */
50283
+ async function applyAgentCoreCredentialEnv(dockerEnv, args) {
50284
+ const logger = getLogger();
50285
+ let assumeSucceeded = false;
50286
+ if (args.assumeRoleArn) {
50287
+ const stsRegion = args.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"];
50288
+ try {
50289
+ const creds = await assumeAgentCoreExecutionRole(args.assumeRoleArn, stsRegion);
50290
+ dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
50291
+ dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
50292
+ dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
50293
+ if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
50294
+ assumeSucceeded = true;
50295
+ } catch (err) {
50296
+ logger.warn(`--assume-role: STS AssumeRole(${args.assumeRoleArn}) failed: ${err instanceof Error ? err.message : String(err)}. Falling back to the developer's shell credentials.`);
50297
+ }
50298
+ }
50299
+ if (!assumeSucceeded) {
50300
+ forwardAwsEnv$1(dockerEnv);
50301
+ applyProfileCredentialsOverlay(dockerEnv, args.profileCredentials, false);
50302
+ if (args.profileCredsFile) {
50303
+ dockerEnv["AWS_SHARED_CREDENTIALS_FILE"] = args.profileCredsFile.containerPath;
50304
+ dockerEnv["AWS_PROFILE"] = args.profileCredsFile.profileName;
50305
+ }
50306
+ }
50307
+ }
50308
+ /**
50309
+ * Resolve the role ARN to assume, honoring the three `--assume-role` forms.
50310
+ * Bare `--assume-role` uses the runtime's literal `RoleArn`; when that is an
50311
+ * intrinsic (the common L2 case — `Fn::GetAtt` to an auto-created role) it
50312
+ * resolves the execution-role ARN from `--from-cfn-stack` state, and only
50313
+ * warns + falls back to dev creds when neither is available.
50314
+ */
50315
+ function resolveAssumeRoleArn(options, resolved, loaded) {
50316
+ if (typeof options.assumeRole === "string") return options.assumeRole;
50317
+ if (options.assumeRole === true) {
50318
+ if (resolved.roleArn) return resolved.roleArn;
50319
+ if (loaded) {
50320
+ const fromState = resolveExecutionRoleArnFromState(loaded, resolved.logicalId, "RoleArn");
50321
+ if (fromState) {
50322
+ getLogger().debug(`--assume-role: resolved RoleArn from state: ${fromState}`);
50323
+ return fromState;
50324
+ }
50325
+ }
50326
+ getLogger().warn("--assume-role passed without an ARN, but the runtime's RoleArn is not a literal ARN in the template " + (loaded ? "and could not be resolved from the deployed stack state. " : "and no --from-cfn-stack state is available to resolve it. ") + "Pass the ARN explicitly: --assume-role <arn>. Falling back to the developer's shell credentials.");
50327
+ }
50328
+ }
50329
+ function emitResult(result) {
50330
+ const logger = getLogger();
50331
+ if (result.status >= 400) {
50332
+ logger.warn(`Agent /invocations returned HTTP ${result.status}.`);
50333
+ process.exitCode = 1;
50334
+ }
50335
+ if (result.streamed) {
50336
+ process.stdout.write("\n");
50337
+ return;
50338
+ }
50339
+ process.stdout.write(`${result.raw}\n`);
50340
+ }
50341
+ /**
50342
+ * Finish a `/ws` exchange: the frames were already streamed to stdout via the
50343
+ * onMessage sink, so just terminate with a newline (so the shell prompt resumes
50344
+ * cleanly) and note the frame count at debug level.
50345
+ */
50346
+ function emitWsResult(result) {
50347
+ process.stdout.write("\n");
50348
+ getLogger().debug(`Agent /ws closed after ${result.frames} frame(s).`);
50349
+ }
50350
+ /**
50351
+ * Build the JSON-RPC request to send to an MCP runtime from `--event`:
50352
+ * - no `--event` (empty object) → `tools/list` (discover the server's tools),
50353
+ * - an object with a string `method` → that method + its `params`,
50354
+ * - anything else → a fail-fast error.
50355
+ *
50356
+ * Exported for unit testing.
50357
+ */
50358
+ function buildMcpRequest(event) {
50359
+ if (event === void 0 || event === null) return {
50360
+ method: "tools/list",
50361
+ params: {}
50362
+ };
50363
+ if (typeof event !== "object" || Array.isArray(event)) throw new CdkdError("MCP --event must be a JSON object describing a JSON-RPC request (e.g. {\"method\":\"tools/call\",\"params\":{\"name\":\"...\",\"arguments\":{...}}}).", "LOCAL_INVOKE_AGENTCORE_MCP_EVENT_INVALID");
50364
+ const obj = event;
50365
+ if (Object.keys(obj).length === 0) return {
50366
+ method: "tools/list",
50367
+ params: {}
50368
+ };
50369
+ if (typeof obj["method"] !== "string") throw new CdkdError(`MCP --event must include a string "method" (a JSON-RPC method such as "tools/list" or "tools/call"). Got keys: ${Object.keys(obj).join(", ")}.`, "LOCAL_INVOKE_AGENTCORE_MCP_EVENT_INVALID");
50370
+ return {
50371
+ method: obj["method"],
50372
+ ...obj["params"] !== void 0 && { params: obj["params"] }
50373
+ };
50374
+ }
50375
+ /** Print the MCP JSON-RPC response; exit 1 when it carried a JSON-RPC error. */
50376
+ function emitMcpResult(result) {
50377
+ if (!result.ok) {
50378
+ getLogger().warn("MCP server returned a JSON-RPC error.");
50379
+ process.exitCode = 1;
50380
+ }
50381
+ process.stdout.write(`${result.raw}\n`);
50382
+ }
50383
+ /**
50384
+ * Build the JSON-RPC request to send to an A2A runtime from `--event`:
50385
+ * - no `--event` (empty object) → `agent/getCard` (discover the agent's card),
50386
+ * - an object with a string `method` → that method + its `params`,
50387
+ * - anything else → a fail-fast error.
50388
+ *
50389
+ * Exported for unit testing.
50390
+ */
50391
+ function buildA2aRequest(event) {
50392
+ if (event === void 0 || event === null) return {
50393
+ method: "agent/getCard",
50394
+ params: {}
50395
+ };
50396
+ if (typeof event !== "object" || Array.isArray(event)) throw new CdkdError("A2A --event must be a JSON object describing a JSON-RPC request (e.g. {\"method\":\"tasks/send\",\"params\":{\"id\":\"...\",\"message\":{...}}}).", "LOCAL_INVOKE_AGENTCORE_A2A_EVENT_INVALID");
50397
+ const obj = event;
50398
+ if (Object.keys(obj).length === 0) return {
50399
+ method: "agent/getCard",
50400
+ params: {}
50401
+ };
50402
+ if (typeof obj["method"] !== "string") throw new CdkdError(`A2A --event must include a string "method" (a JSON-RPC method such as "agent/getCard" or "tasks/send"). Got keys: ${Object.keys(obj).join(", ")}.`, "LOCAL_INVOKE_AGENTCORE_A2A_EVENT_INVALID");
50403
+ return {
50404
+ method: obj["method"],
50405
+ ...obj["params"] !== void 0 && { params: obj["params"] }
50406
+ };
50407
+ }
50408
+ /** Print the A2A JSON-RPC response; exit 1 when it carried a JSON-RPC error. */
50409
+ function emitA2aResult(result) {
50410
+ if (!result.ok) {
50411
+ getLogger().warn("A2A server returned a JSON-RPC error.");
50412
+ process.exitCode = 1;
50413
+ }
50414
+ process.stdout.write(`${result.raw}\n`);
50415
+ }
50416
+ /** Map a `--platform` value to the architecture `buildContainerImage` expects. */
50417
+ function platformToArchitecture(platform) {
50418
+ return platform === "linux/amd64" ? "x86_64" : "arm64";
50419
+ }
50420
+ function forwardAwsEnv$1(env) {
50421
+ for (const key of [
50422
+ "AWS_ACCESS_KEY_ID",
50423
+ "AWS_SECRET_ACCESS_KEY",
50424
+ "AWS_SESSION_TOKEN",
50425
+ "AWS_REGION",
50426
+ "AWS_DEFAULT_REGION"
50427
+ ]) {
50428
+ const value = process.env[key];
50429
+ if (value !== void 0) env[key] = value;
50430
+ }
50431
+ }
50432
+ async function assumeAgentCoreExecutionRole(roleArn, region) {
50433
+ const { STSClient, AssumeRoleCommand } = await import("@aws-sdk/client-sts");
50434
+ const sts = new STSClient({ ...region && { region } });
50435
+ try {
50436
+ const creds = (await sts.send(new AssumeRoleCommand({
50437
+ RoleArn: roleArn,
50438
+ RoleSessionName: `${getEmbedConfig().resourceNamePrefix}-invoke-agentcore-${Date.now()}`,
50439
+ DurationSeconds: 3600
50440
+ }))).Credentials;
50441
+ if (!creds?.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) throw new CdkdError(`AssumeRole(${roleArn}) returned no usable credentials.`, "LOCAL_INVOKE_AGENTCORE_ASSUMEROLE_NO_CREDS");
50442
+ return {
50443
+ accessKeyId: creds.AccessKeyId,
50444
+ secretAccessKey: creds.SecretAccessKey,
50445
+ sessionToken: creds.SessionToken
50446
+ };
50447
+ } finally {
50448
+ sts.destroy();
50449
+ }
50450
+ }
50451
+ async function readEvent$1(options) {
50452
+ if (options.event && options.eventStdin) throw new CdkdError("--event and --event-stdin are mutually exclusive.", "LOCAL_INVOKE_AGENTCORE_EVENT_FLAG_CONFLICT");
50453
+ if (options.eventStdin) return parseEvent$1(await readStdin$1(), "<stdin>");
50454
+ if (options.event) {
50455
+ let raw;
50456
+ try {
50457
+ raw = readFileSync(options.event, "utf-8");
50458
+ } catch (err) {
50459
+ throw new CdkdError(`Failed to read --event file '${options.event}': ${err instanceof Error ? err.message : String(err)}`, "LOCAL_INVOKE_AGENTCORE_EVENT_READ_FAILED");
50460
+ }
50461
+ return parseEvent$1(raw, options.event);
50462
+ }
50463
+ return {};
50464
+ }
50465
+ function parseEvent$1(raw, source) {
50466
+ try {
50467
+ return JSON.parse(raw);
50468
+ } catch (err) {
50469
+ throw new CdkdError(`Failed to parse event payload from ${source} as JSON: ${err instanceof Error ? err.message : String(err)}`, "LOCAL_INVOKE_AGENTCORE_EVENT_PARSE_FAILED");
50470
+ }
50471
+ }
50472
+ async function readStdin$1() {
50473
+ const chunks = [];
50474
+ for await (const chunk of process.stdin) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
50475
+ return Buffer.concat(chunks).toString("utf-8");
50476
+ }
50477
+ /**
50478
+ * Read `process.stdin` line-buffered and yield each line as a string (trailing
50479
+ * `\r?\n` stripped). The async iterable completes when stdin EOFs (Ctrl-D /
50480
+ * end-of-pipe), and surrenders the underlying stream when its `return()` is
50481
+ * called — so the WS client can close down the source when the server closes
50482
+ * first without leaving stdin held open.
50483
+ *
50484
+ * Exported so a unit test can drive the iterable shape directly.
50485
+ */
50486
+ async function* readStdinLines() {
50487
+ const { createInterface } = await import("node:readline");
50488
+ const rl = createInterface({
50489
+ input: process.stdin,
50490
+ crlfDelay: Infinity
50491
+ });
50492
+ try {
50493
+ for await (const line of rl) yield line;
50494
+ } finally {
50495
+ rl.close();
50496
+ }
50497
+ }
50498
+ function readEnvOverridesFile$1(filePath) {
50499
+ if (!filePath) return void 0;
50500
+ let raw;
50501
+ try {
50502
+ raw = readFileSync(filePath, "utf-8");
50503
+ } catch (err) {
50504
+ throw new CdkdError(`Failed to read --env-vars file '${filePath}': ${err instanceof Error ? err.message : String(err)}`, "LOCAL_INVOKE_AGENTCORE_ENV_VARS_READ_FAILED");
50505
+ }
50506
+ let parsed;
50507
+ try {
50508
+ parsed = JSON.parse(raw);
50509
+ } catch (err) {
50510
+ throw new CdkdError(`Failed to parse --env-vars file '${filePath}' as JSON: ${err instanceof Error ? err.message : String(err)}`, "LOCAL_INVOKE_AGENTCORE_ENV_VARS_PARSE_FAILED");
50511
+ }
50512
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CdkdError(`--env-vars file '${filePath}' must contain a JSON object at the top level.`, "LOCAL_INVOKE_AGENTCORE_ENV_VARS_NOT_OBJECT");
50513
+ return parsed;
50514
+ }
50515
+ function createLocalInvokeAgentCoreCommand() {
50516
+ 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) or MCP (POST /mcp Streamable HTTP on 8000). 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 the HTTP + MCP 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)").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\"}})")).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 to present when the runtime declares a customJwtAuthorizer. Verified against the runtime OIDC discovery URL (signature / issuer / expiry / audience) before the container starts, then forwarded to /invocations as Authorization: Bearer <jwt>.")).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)").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 to bind the agent port to").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-state", "Load cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue in env vars with the deployed physical IDs / cross-stack exports. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. For CDK apps deployed via the upstream CDK CLI. Bare form uses the resolved stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).action(withErrorHandling(async (target, options) => {
50517
+ await localInvokeAgentCoreCommand(target, options);
50518
+ }));
50519
+ [
50520
+ ...commonOptions,
50521
+ ...appOptions,
50522
+ ...contextOptions,
50523
+ ...stateOptions
50524
+ ].forEach((opt) => cmd.addOption(opt));
50525
+ cmd.addOption(deprecatedRegionOption);
50526
+ return cmd;
50527
+ }
50528
+
49696
50529
  //#endregion
49697
50530
  //#region src/cli/commands/local-invoke.ts
49698
50531
  /**
@@ -50447,10 +51280,10 @@ function suggestAssumeRoleFromState(state, logicalId) {
50447
51280
  *
50448
51281
  * Exported for unit testing.
50449
51282
  */
50450
- function resolveExecutionRoleArnFromState(state, logicalId) {
51283
+ function resolveExecutionRoleArnFromState(state, logicalId, roleProperty = "Role") {
50451
51284
  const lambda = state.resources[logicalId];
50452
51285
  if (!lambda) return void 0;
50453
- const roleRef = lambda.properties?.["Role"] ?? lambda.observedProperties?.["Role"];
51286
+ const roleRef = lambda.properties?.[roleProperty] ?? lambda.observedProperties?.[roleProperty];
50454
51287
  if (typeof roleRef === "string" && roleRef.startsWith("arn:")) return roleRef;
50455
51288
  if (typeof roleRef === "object" && roleRef !== null) {
50456
51289
  const refLogicalId = pickReferencedLogicalId(roleRef);
@@ -50495,6 +51328,7 @@ function createLocalCommand() {
50495
51328
  local.addCommand(createLocalStartApiCommand());
50496
51329
  local.addCommand(createLocalRunTaskCommand());
50497
51330
  local.addCommand(createLocalStartServiceCommand());
51331
+ local.addCommand(createLocalInvokeAgentCoreCommand());
50498
51332
  return local;
50499
51333
  }
50500
51334
 
@@ -51607,7 +52441,7 @@ function reorderArgs(argv) {
51607
52441
  */
51608
52442
  async function main() {
51609
52443
  const program = new Command();
51610
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.190.0");
52444
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.191.0");
51611
52445
  program.addCommand(createBootstrapCommand());
51612
52446
  program.addCommand(createSynthCommand());
51613
52447
  program.addCommand(createListCommand());