@go-to-k/cdkd 0.189.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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { _ as withSkipPrefix, a as runDockerStreaming, c as getLogger, d as getLiveRenderer, f as PATTERN_B_NAME_PROPERTIES, g as generateResourceNameWithFallback, h as generateResourceName, i as runDockerForeground, n as formatDockerLoginError, p as PATTERN_B_RESOURCE_TYPES, r as getDockerCmd, u as runStackBuffered, v as withStackName } from "./docker-cmd-iDMcWcre.js";
|
|
3
|
-
import { A as S3StateBackend, B as resolveCaptureObservedState, C as assertRegionMatch, D as DagBuilder, E as DiffCalculator, F as buildDockerImage, G as CFN_TEMPLATE_BODY_LIMIT, H as resolveStateBucketWithDefault, I as Synthesizer, J as findLargeInlineResources, K as CFN_TEMPLATE_URL_LIMIT, L as getDefaultStateBucketName, M as AssetPublisher, N as stringifyValue, O as TemplateParser, P as WorkGraph, Q as resolveBucketRegion, R as getLegacyStateBucketName, S as CloudControlProvider, T as applyRoleArnIfSet, U as resolveStateBucketWithDefaultAndSource, V as resolveSkipPrefix, W as warnDeprecatedNoPrefixCliFlag, X as AssemblyReader, Y as uploadCfnTemplate, _ as matchesCdkPath, a as withRetry, at as LocalStartServiceError, b as ProviderRegistry, bt as withErrorHandling, c as bold, ct as NestedStackChildDirectDestroyError, d as green, dt as ResourceTimeoutError, et as CdkdError, f as red, ft as ResourceUpdateNotSupportedError, g as CDK_PATH_TAG, h as collectInlinePolicyNamesManagedBySiblings, i as withResourceDeadline, it as LocalMigrateError, j as shouldRetainResource, k as LockManager, l as cyan, lt as PartialFailureError, m as IAMRoleProvider, mt as StackTerminationProtectionError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as IMPLICIT_DELETE_DEPENDENCIES, p as yellow, pt as StackHasActiveImportsError, q as MIGRATE_TMP_PREFIX, r as DeployEngine, rt as LocalInvokeBuildError$1, s as formatResourceLine, st as MissingCdkCliError, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as gray, ut as ProvisioningError, v as normalizeAwsTagsToCfn, w as IntrinsicFunctionResolver, x as findActionableSilentDrops, y as resolveExplicitPhysicalId, yt as normalizeAwsError, z as resolveApp } from "./deploy-engine-
|
|
3
|
+
import { A as S3StateBackend, B as resolveCaptureObservedState, C as assertRegionMatch, D as DagBuilder, E as DiffCalculator, F as buildDockerImage, G as CFN_TEMPLATE_BODY_LIMIT, H as resolveStateBucketWithDefault, I as Synthesizer, J as findLargeInlineResources, K as CFN_TEMPLATE_URL_LIMIT, L as getDefaultStateBucketName, M as AssetPublisher, N as stringifyValue, O as TemplateParser, P as WorkGraph, Q as resolveBucketRegion, R as getLegacyStateBucketName, S as CloudControlProvider, T as applyRoleArnIfSet, U as resolveStateBucketWithDefaultAndSource, V as resolveSkipPrefix, W as warnDeprecatedNoPrefixCliFlag, X as AssemblyReader, Y as uploadCfnTemplate, _ as matchesCdkPath, a as withRetry, at as LocalStartServiceError, b as ProviderRegistry, bt as withErrorHandling, c as bold, ct as NestedStackChildDirectDestroyError, d as green, dt as ResourceTimeoutError, et as CdkdError, f as red, ft as ResourceUpdateNotSupportedError, g as CDK_PATH_TAG, h as collectInlinePolicyNamesManagedBySiblings, i as withResourceDeadline, it as LocalMigrateError, j as shouldRetainResource, k as LockManager, l as cyan, lt as PartialFailureError, m as IAMRoleProvider, mt as StackTerminationProtectionError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as IMPLICIT_DELETE_DEPENDENCIES, p as yellow, pt as StackHasActiveImportsError, q as MIGRATE_TMP_PREFIX, r as DeployEngine, rt as LocalInvokeBuildError$1, s as formatResourceLine, st as MissingCdkCliError, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as gray, ut as ProvisioningError, v as normalizeAwsTagsToCfn, w as IntrinsicFunctionResolver, x as findActionableSilentDrops, y as resolveExplicitPhysicalId, yt as normalizeAwsError, z as resolveApp } from "./deploy-engine-CapyHC2m.js";
|
|
4
4
|
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-B15NAPbL.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
@@ -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";
|
|
@@ -12358,7 +12358,8 @@ var EventBridgeBusProvider = class {
|
|
|
12358
12358
|
"Description",
|
|
12359
12359
|
"KmsKeyIdentifier",
|
|
12360
12360
|
"Policy",
|
|
12361
|
-
"DeadLetterConfig"
|
|
12361
|
+
"DeadLetterConfig",
|
|
12362
|
+
"LogConfig"
|
|
12362
12363
|
])]]);
|
|
12363
12364
|
constructor() {
|
|
12364
12365
|
this.eventBridgeClient = getAwsClients().eventBridge;
|
|
@@ -12375,6 +12376,7 @@ var EventBridgeBusProvider = class {
|
|
|
12375
12376
|
if (properties["Tags"]) createParams.Tags = properties["Tags"];
|
|
12376
12377
|
const dlcCreate = sanitizeDeadLetterConfig(properties["DeadLetterConfig"]);
|
|
12377
12378
|
if (dlcCreate) createParams.DeadLetterConfig = dlcCreate;
|
|
12379
|
+
if (properties["LogConfig"] !== void 0) createParams.LogConfig = properties["LogConfig"];
|
|
12378
12380
|
const eventBusArn = (await this.eventBridgeClient.send(new CreateEventBusCommand(createParams))).EventBusArn ?? "";
|
|
12379
12381
|
if (properties["Policy"]) {}
|
|
12380
12382
|
return {
|
|
@@ -12394,7 +12396,8 @@ var EventBridgeBusProvider = class {
|
|
|
12394
12396
|
const descChanged = properties["Description"] !== previousProperties["Description"];
|
|
12395
12397
|
const kmsChanged = properties["KmsKeyIdentifier"] !== previousProperties["KmsKeyIdentifier"];
|
|
12396
12398
|
const dlcChanged = JSON.stringify(properties["DeadLetterConfig"]) !== JSON.stringify(previousProperties["DeadLetterConfig"]);
|
|
12397
|
-
|
|
12399
|
+
const logCfgChanged = JSON.stringify(properties["LogConfig"]) !== JSON.stringify(previousProperties["LogConfig"]);
|
|
12400
|
+
if (descChanged || kmsChanged || dlcChanged || logCfgChanged) {
|
|
12398
12401
|
const updateParams = { Name: physicalId };
|
|
12399
12402
|
if (properties["Description"] !== void 0) updateParams.Description = properties["Description"];
|
|
12400
12403
|
if (properties["KmsKeyIdentifier"] !== void 0) updateParams.KmsKeyIdentifier = properties["KmsKeyIdentifier"];
|
|
@@ -12402,6 +12405,7 @@ var EventBridgeBusProvider = class {
|
|
|
12402
12405
|
const dlcUpdate = sanitizeDeadLetterConfig(properties["DeadLetterConfig"]);
|
|
12403
12406
|
if (dlcUpdate) updateParams.DeadLetterConfig = dlcUpdate;
|
|
12404
12407
|
}
|
|
12408
|
+
if (properties["LogConfig"] !== void 0) updateParams.LogConfig = properties["LogConfig"];
|
|
12405
12409
|
await this.eventBridgeClient.send(new UpdateEventBusCommand(updateParams));
|
|
12406
12410
|
}
|
|
12407
12411
|
const newTags = properties["Tags"];
|
|
@@ -12516,6 +12520,12 @@ var EventBridgeBusProvider = class {
|
|
|
12516
12520
|
result["Description"] = resp.Description ?? "";
|
|
12517
12521
|
result["KmsKeyIdentifier"] = resp.KmsKeyIdentifier ?? "";
|
|
12518
12522
|
result["DeadLetterConfig"] = { Arn: resp.DeadLetterConfig?.Arn ?? "" };
|
|
12523
|
+
if (resp.LogConfig !== void 0) {
|
|
12524
|
+
const lc = {};
|
|
12525
|
+
if (resp.LogConfig.IncludeDetail !== void 0) lc["IncludeDetail"] = resp.LogConfig.IncludeDetail;
|
|
12526
|
+
if (resp.LogConfig.Level !== void 0) lc["Level"] = resp.LogConfig.Level;
|
|
12527
|
+
if (Object.keys(lc).length > 0) result["LogConfig"] = lc;
|
|
12528
|
+
}
|
|
12519
12529
|
if (resp.Policy) try {
|
|
12520
12530
|
result["Policy"] = JSON.parse(resp.Policy);
|
|
12521
12531
|
} catch {
|
|
@@ -44382,7 +44392,7 @@ async function runDetached(opts) {
|
|
|
44382
44392
|
if (opts.platform) args.push("--platform", opts.platform);
|
|
44383
44393
|
if (opts.extraHosts) for (const entry of opts.extraHosts) args.push("--add-host", `${entry.host}:${entry.ip}`);
|
|
44384
44394
|
const host = opts.host ?? "127.0.0.1";
|
|
44385
|
-
args.push("-p", `${host}:${opts.hostPort}
|
|
44395
|
+
args.push("-p", `${host}:${opts.hostPort}:${opts.containerPort ?? 8080}`);
|
|
44386
44396
|
if (opts.debugPort !== void 0) args.push("-p", `${host}:${opts.debugPort}:${opts.debugPort}`);
|
|
44387
44397
|
for (const mount of opts.mounts) {
|
|
44388
44398
|
const ro = mount.readOnly ? ":ro" : "";
|
|
@@ -44392,7 +44402,12 @@ async function runDetached(opts) {
|
|
|
44392
44402
|
const ro = mount.readOnly ? ":ro" : "";
|
|
44393
44403
|
args.push("-v", `${mount.hostPath}:${mount.containerPath}${ro}`);
|
|
44394
44404
|
}
|
|
44395
|
-
|
|
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}`);
|
|
44396
44411
|
if (opts.tmpfs) args.push("--tmpfs", `${opts.tmpfs.target}:rw,size=${opts.tmpfs.sizeMb}m`);
|
|
44397
44412
|
if (opts.workingDir) args.push("--workdir", opts.workingDir);
|
|
44398
44413
|
let entryPointTail = [];
|
|
@@ -44403,7 +44418,13 @@ async function runDetached(opts) {
|
|
|
44403
44418
|
args.push(opts.image, ...entryPointTail, ...opts.cmd);
|
|
44404
44419
|
getLogger().child("docker").debug(`${getDockerCmd()} ${redactAwsCredentialsInArgs(args).join(" ")}`);
|
|
44405
44420
|
try {
|
|
44406
|
-
const { stdout } = await execFileAsync$3(getDockerCmd(), args, {
|
|
44421
|
+
const { stdout } = await execFileAsync$3(getDockerCmd(), args, {
|
|
44422
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
44423
|
+
env: {
|
|
44424
|
+
...process.env,
|
|
44425
|
+
...passthroughEnv
|
|
44426
|
+
}
|
|
44427
|
+
});
|
|
44407
44428
|
return stdout.trim();
|
|
44408
44429
|
} catch (error) {
|
|
44409
44430
|
const err = error;
|
|
@@ -44511,6 +44532,22 @@ const REDACTED_ENV_KEYS = new Set([
|
|
|
44511
44532
|
"AWS_SESSION_TOKEN"
|
|
44512
44533
|
]);
|
|
44513
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
|
+
/**
|
|
44514
44551
|
* Returns a copy of `args` with any `-e <KEY>=<value>` pair whose KEY is
|
|
44515
44552
|
* in {@link REDACTED_ENV_KEYS} replaced with `-e <KEY>=***`. The actual
|
|
44516
44553
|
* `args` passed to `spawn` are never mutated — this is for log output
|
|
@@ -46060,7 +46097,7 @@ async function localStartApiCommand(target, options) {
|
|
|
46060
46097
|
await ensureDockerAvailable();
|
|
46061
46098
|
const appCmd = resolveApp(options.app);
|
|
46062
46099
|
if (!appCmd) throw new Error("No CDK app specified. Pass --app, set CDKD_APP, or add \"app\" to cdk.json.");
|
|
46063
|
-
const overrides = readEnvOverridesFile$
|
|
46100
|
+
const overrides = readEnvOverridesFile$4(options.envVars);
|
|
46064
46101
|
const debugPortBase = options.debugPortBase ? parseDebugPort(options.debugPortBase) : void 0;
|
|
46065
46102
|
const perLambdaConcurrency = parsePerLambdaConcurrency(options.perLambdaConcurrency);
|
|
46066
46103
|
const inlineTmpDirs = /* @__PURE__ */ new Set();
|
|
@@ -46679,7 +46716,7 @@ async function buildContainerSpec(args) {
|
|
|
46679
46716
|
dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
|
|
46680
46717
|
if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
|
|
46681
46718
|
} else {
|
|
46682
|
-
forwardAwsEnv$
|
|
46719
|
+
forwardAwsEnv$2(dockerEnv);
|
|
46683
46720
|
if (profileCredentials) {
|
|
46684
46721
|
dockerEnv["AWS_ACCESS_KEY_ID"] = profileCredentials.accessKeyId;
|
|
46685
46722
|
dockerEnv["AWS_SECRET_ACCESS_KEY"] = profileCredentials.secretAccessKey;
|
|
@@ -47026,7 +47063,7 @@ function getTemplateEnv$1(resource) {
|
|
|
47026
47063
|
return vars;
|
|
47027
47064
|
}
|
|
47028
47065
|
/** Read the SAM-shape `--env-vars` JSON file. */
|
|
47029
|
-
function readEnvOverridesFile$
|
|
47066
|
+
function readEnvOverridesFile$4(filePath) {
|
|
47030
47067
|
if (!filePath) return void 0;
|
|
47031
47068
|
let raw;
|
|
47032
47069
|
try {
|
|
@@ -47048,7 +47085,7 @@ function readEnvOverridesFile$3(filePath) {
|
|
|
47048
47085
|
* handler's AWS SDK calls can authenticate. Used when --assume-role is
|
|
47049
47086
|
* NOT set for that Lambda — SAM-compatible default.
|
|
47050
47087
|
*/
|
|
47051
|
-
function forwardAwsEnv$
|
|
47088
|
+
function forwardAwsEnv$2(env) {
|
|
47052
47089
|
for (const key of [
|
|
47053
47090
|
"AWS_ACCESS_KEY_ID",
|
|
47054
47091
|
"AWS_SECRET_ACCESS_KEY",
|
|
@@ -48419,7 +48456,7 @@ async function localRunTaskCommand(target, options) {
|
|
|
48419
48456
|
}
|
|
48420
48457
|
const sidecarCredentials = await resolveSidecarCredentials(options, assumedCredentials);
|
|
48421
48458
|
if (options.profile && sidecarCredentials && !assumedCredentials) profileCredsFile = await writeProfileCredentialsFile(options.profile, sidecarCredentials);
|
|
48422
|
-
const envOverrides = readEnvOverridesFile$
|
|
48459
|
+
const envOverrides = readEnvOverridesFile$3(options.envVars);
|
|
48423
48460
|
const runOpts = {
|
|
48424
48461
|
cluster: options.cluster,
|
|
48425
48462
|
containerHost: options.containerHost,
|
|
@@ -48519,7 +48556,7 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
|
|
|
48519
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.");
|
|
48520
48557
|
let accountId;
|
|
48521
48558
|
try {
|
|
48522
|
-
accountId = await resolveCallerAccountId$
|
|
48559
|
+
accountId = await resolveCallerAccountId$2(region);
|
|
48523
48560
|
} catch (err) {
|
|
48524
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.`);
|
|
48525
48562
|
}
|
|
@@ -48549,7 +48586,7 @@ function pickCandidateStack$1(stackPattern, stacks) {
|
|
|
48549
48586
|
const matched = matchStacks(stacks, [stackPattern]);
|
|
48550
48587
|
if (matched.length === 1) return matched[0];
|
|
48551
48588
|
}
|
|
48552
|
-
async function resolveCallerAccountId$
|
|
48589
|
+
async function resolveCallerAccountId$2(region) {
|
|
48553
48590
|
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
48554
48591
|
const sts = new STSClient({ ...region && { region } });
|
|
48555
48592
|
try {
|
|
@@ -48563,7 +48600,7 @@ async function resolveCallerAccountId$1(region) {
|
|
|
48563
48600
|
* `cdkd local invoke --env-vars`: top-level keys are container names, with
|
|
48564
48601
|
* `Parameters` reserved for global entries.
|
|
48565
48602
|
*/
|
|
48566
|
-
function readEnvOverridesFile$
|
|
48603
|
+
function readEnvOverridesFile$3(filePath) {
|
|
48567
48604
|
if (!filePath) return void 0;
|
|
48568
48605
|
let raw;
|
|
48569
48606
|
try {
|
|
@@ -49478,7 +49515,7 @@ async function runOneTarget(target, runState, stacks, options, discovery, skipPu
|
|
|
49478
49515
|
resolvedRoleArn = options.assumeTaskRole;
|
|
49479
49516
|
assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);
|
|
49480
49517
|
}
|
|
49481
|
-
const envOverrides = readEnvOverridesFile$
|
|
49518
|
+
const envOverrides = readEnvOverridesFile$2(options.envVars);
|
|
49482
49519
|
const taskOpts = {
|
|
49483
49520
|
cluster: options.cluster,
|
|
49484
49521
|
containerHost: options.containerHost,
|
|
@@ -49554,7 +49591,7 @@ async function buildEcsImageResolutionContext(target, stacks, options, stateProv
|
|
|
49554
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.");
|
|
49555
49592
|
let accountId;
|
|
49556
49593
|
try {
|
|
49557
|
-
accountId = await resolveCallerAccountId(region);
|
|
49594
|
+
accountId = await resolveCallerAccountId$1(region);
|
|
49558
49595
|
} catch (err) {
|
|
49559
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.`);
|
|
49560
49597
|
}
|
|
@@ -49584,7 +49621,7 @@ function pickCandidateStack(stackPattern, stacks) {
|
|
|
49584
49621
|
const matched = matchStacks(stacks, [stackPattern]);
|
|
49585
49622
|
if (matched.length === 1) return matched[0];
|
|
49586
49623
|
}
|
|
49587
|
-
async function resolveCallerAccountId(region) {
|
|
49624
|
+
async function resolveCallerAccountId$1(region) {
|
|
49588
49625
|
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
49589
49626
|
const sts = new STSClient({ ...region && { region } });
|
|
49590
49627
|
try {
|
|
@@ -49593,7 +49630,7 @@ async function resolveCallerAccountId(region) {
|
|
|
49593
49630
|
sts.destroy();
|
|
49594
49631
|
}
|
|
49595
49632
|
}
|
|
49596
|
-
function readEnvOverridesFile$
|
|
49633
|
+
function readEnvOverridesFile$2(filePath) {
|
|
49597
49634
|
if (!filePath) return void 0;
|
|
49598
49635
|
let raw;
|
|
49599
49636
|
try {
|
|
@@ -49683,6 +49720,812 @@ function createLocalStartServiceCommand() {
|
|
|
49683
49720
|
return cmd;
|
|
49684
49721
|
}
|
|
49685
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
|
+
|
|
49686
50529
|
//#endregion
|
|
49687
50530
|
//#region src/cli/commands/local-invoke.ts
|
|
49688
50531
|
/**
|
|
@@ -50437,10 +51280,10 @@ function suggestAssumeRoleFromState(state, logicalId) {
|
|
|
50437
51280
|
*
|
|
50438
51281
|
* Exported for unit testing.
|
|
50439
51282
|
*/
|
|
50440
|
-
function resolveExecutionRoleArnFromState(state, logicalId) {
|
|
51283
|
+
function resolveExecutionRoleArnFromState(state, logicalId, roleProperty = "Role") {
|
|
50441
51284
|
const lambda = state.resources[logicalId];
|
|
50442
51285
|
if (!lambda) return void 0;
|
|
50443
|
-
const roleRef = lambda.properties?.[
|
|
51286
|
+
const roleRef = lambda.properties?.[roleProperty] ?? lambda.observedProperties?.[roleProperty];
|
|
50444
51287
|
if (typeof roleRef === "string" && roleRef.startsWith("arn:")) return roleRef;
|
|
50445
51288
|
if (typeof roleRef === "object" && roleRef !== null) {
|
|
50446
51289
|
const refLogicalId = pickReferencedLogicalId(roleRef);
|
|
@@ -50485,6 +51328,7 @@ function createLocalCommand() {
|
|
|
50485
51328
|
local.addCommand(createLocalStartApiCommand());
|
|
50486
51329
|
local.addCommand(createLocalRunTaskCommand());
|
|
50487
51330
|
local.addCommand(createLocalStartServiceCommand());
|
|
51331
|
+
local.addCommand(createLocalInvokeAgentCoreCommand());
|
|
50488
51332
|
return local;
|
|
50489
51333
|
}
|
|
50490
51334
|
|
|
@@ -51597,7 +52441,7 @@ function reorderArgs(argv) {
|
|
|
51597
52441
|
*/
|
|
51598
52442
|
async function main() {
|
|
51599
52443
|
const program = new Command();
|
|
51600
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
52444
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.191.0");
|
|
51601
52445
|
program.addCommand(createBootstrapCommand());
|
|
51602
52446
|
program.addCommand(createSynthCommand());
|
|
51603
52447
|
program.addCommand(createListCommand());
|