diff --git a/cdk/package.json b/cdk/package.json index be16744a..e99d8234 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -19,6 +19,7 @@ "@aws-sdk/client-bedrock-agentcore": "^3.1078.0", "@aws-sdk/client-bedrock-runtime": "^3.1078.0", "@aws-sdk/client-dynamodb": "^3.1078.0", + "@aws-sdk/client-ec2": "^3.1078.0", "@aws-sdk/client-ecs": "^3.1078.0", "@aws-sdk/client-lambda": "^3.1078.0", "@aws-sdk/client-s3": "^3.1078.0", diff --git a/cdk/src/constructs/agent-vpc.ts b/cdk/src/constructs/agent-vpc.ts index d8d5546e..12d7fc50 100644 --- a/cdk/src/constructs/agent-vpc.ts +++ b/cdk/src/constructs/agent-vpc.ts @@ -32,10 +32,44 @@ const HTTPS_PORT = 443; export interface AgentVpcProps { /** * Maximum number of availability zones to use. + * + * Ignored when {@link availabilityZones} is provided (CDK does not allow + * both `maxAzs` and an explicit zone list on the same VPC). * @default 2 */ readonly maxAzs?: number; + /** + * Explicit list of availability-zone *names* (e.g. `['us-east-1b', 'us-east-1c']`) + * to place the VPC — and therefore the AgentCore Runtime ENIs — into. + * + * AgentCore only supports a subset of the physical availability zones in a + * region, and AZ *names* are aliased per-account to physical zone IDs (so + * `us-east-1a` is not the same physical zone across accounts). When CDK is + * left to pick zones by name (the `maxAzs` default) it can land the Runtime + * subnets in a zone AgentCore does not support, and the + * `AWS::BedrockAgentCore::Runtime` resource fails to stabilize with + * `NotStabilized` ("subnets are in unsupported availability zones"), rolling + * back the whole stack. + * + * Pin this to AZ names whose physical zone IDs are AgentCore-supported to + * make a fresh deploy deterministic regardless of the account's + * name → zone-ID mapping. The supported zone-ID set differs per region and + * can change over time — see the AWS + * {@link https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/security/agentcore-vpc/#supported-availability-zones Supported Availability Zones} + * table, and the per-region `AGENTCORE_SUPPORTED_AZ_IDS` map in + * `constructs/agentcore-azs.ts`. + * + * Callers normally don't set this directly: `resolveAgentCoreAzs` (invoked + * from `main.ts`) auto-selects supported zone names for the target account, + * or honors the validated `agentcore:availabilityZones` context override, and + * passes the result through {@link AgentStackProps.availabilityZones}. + * + * When provided, takes precedence over {@link maxAzs}. + * @default - CDK selects the first `maxAzs` zones by name + */ + readonly availabilityZones?: string[]; + /** * Number of NAT gateways to provision. * @default 1 @@ -71,8 +105,12 @@ export class AgentVpc extends Construct { const removalPolicy = props.removalPolicy ?? RemovalPolicy.DESTROY; // --- VPC --- + // When explicit AZs are provided (to target AgentCore-supported physical + // zones), pass them directly and omit maxAzs — CDK does not allow both. this.vpc = new ec2.Vpc(this, 'Vpc', { - maxAzs, + ...(props.availabilityZones + ? { availabilityZones: props.availabilityZones } + : { maxAzs }), natGateways, restrictDefaultSecurityGroup: true, subnetConfiguration: [ diff --git a/cdk/src/constructs/agentcore-azs.ts b/cdk/src/constructs/agentcore-azs.ts new file mode 100644 index 00000000..fb7ad06a --- /dev/null +++ b/cdk/src/constructs/agentcore-azs.ts @@ -0,0 +1,257 @@ +/** + * MIT No Attribution + * + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { Annotations, Token } from 'aws-cdk-lib'; +import { Construct, Node } from 'constructs'; + +/** + * AgentCore-supported physical **Availability Zone IDs** per region. + * + * AgentCore Runtime (and the built-in Code Interpreter / Browser tools) only + * places its elastic network interfaces in a subset of each region's zones. If + * the VPC subnets land in an unsupported zone the + * `AWS::BedrockAgentCore::Runtime` resource fails to stabilize + * (`NotStabilized` — "subnets are in unsupported availability zones") and rolls + * back the whole stack. + * + * The constraint is published in terms of **zone IDs** (e.g. `use1-az1`), which + * are stable across accounts, NOT zone *names* (e.g. `us-east-1a`) which are + * aliased per-account. Keep this map aligned with the AWS documentation: + * https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/security/agentcore-vpc/#supported-availability-zones + * + * A region absent from this map is treated as "no known constraint" — the + * auto-pin logic leaves AZ selection to CDK's default and operators can still + * pin explicitly via the {@link AGENTCORE_AZS_CONTEXT_KEY} context override. + */ +export const AGENTCORE_SUPPORTED_AZ_IDS: Readonly> = { + 'us-east-1': ['use1-az1', 'use1-az2', 'use1-az4'], + 'us-east-2': ['use2-az1', 'use2-az2', 'use2-az3'], + 'us-west-2': ['usw2-az1', 'usw2-az2', 'usw2-az3'], + 'ap-southeast-1': ['apse1-az1', 'apse1-az2', 'apse1-az3'], + 'ap-southeast-2': ['apse2-az1', 'apse2-az2', 'apse2-az3'], + 'ap-south-1': ['aps1-az1', 'aps1-az2', 'aps1-az3'], + 'ap-northeast-1': ['apne1-az1', 'apne1-az2', 'apne1-az4'], + 'eu-west-1': ['euw1-az1', 'euw1-az2', 'euw1-az3'], + 'eu-central-1': ['euc1-az1', 'euc1-az2', 'euc1-az3'], +}; + +/** + * CDK context key whose value (a JSON array of AZ **names**, e.g. + * `["us-east-1b", "us-east-1c"]`) overrides the auto-selected zones. Set it in + * `cdk.context.json`, `cdk.json` `context`, or via + * `-c 'agentcore:availabilityZones=["us-east-1b","us-east-1c"]'`. + */ +export const AGENTCORE_AZS_CONTEXT_KEY = 'agentcore:availabilityZones'; + +/** Minimum AZ count — AgentCore high-availability guidance is >=2 zones. */ +const MIN_AGENTCORE_AZS = 2; + +/** A single Availability Zone's name and its stable physical zone ID. */ +export interface AvailabilityZoneInfo { + /** Account-aliased zone name, e.g. `us-east-1a`. */ + readonly zoneName: string; + /** Stable physical zone ID, e.g. `use1-az1`. */ + readonly zoneId: string; +} + +/** Signature for the injectable `DescribeAvailabilityZones` lookup. */ +export type DescribeAzsFn = (region: string) => Promise; + +/** Options for {@link resolveAgentCoreAzs}. */ +export interface ResolveAgentCoreAzsOptions { + /** Scope used to read context and surface synth-time warnings (the `App`). */ + readonly scope: Construct; + /** Target account (from `CDK_DEFAULT_ACCOUNT`); undefined/token = env-agnostic. */ + readonly account?: string; + /** Target region (from `CDK_DEFAULT_REGION`); undefined/token = env-agnostic. */ + readonly region?: string; + /** + * Availability-zone lookup. Defaults to a live EC2 + * `DescribeAvailabilityZones` call; injectable so tests need no AWS access. + */ + readonly describeAzs?: DescribeAzsFn; +} + +/** + * Validates and returns the optional {@link AGENTCORE_AZS_CONTEXT_KEY} override. + * + * Mirrors the loud-fail contract of `resolveBedrockModelIds` (bedrock-models.ts): + * a malformed override fails synth with a clear message naming the key and the + * expected JSON shape, rather than silently pinning nothing. + * + * @returns the validated AZ-name array, or `undefined` when the key is unset. + * @throws if the value is not a JSON array, contains a non-string/empty entry, + * or lists fewer than {@link MIN_AGENTCORE_AZS} zones. + */ +export function resolveAgentCoreAzOverride(node: Node): string[] | undefined { + const raw = node.tryGetContext(AGENTCORE_AZS_CONTEXT_KEY); + if (raw === undefined || raw === null) { + return undefined; + } + // `cdk.context.json` delivers a real array, but `-c key=value` on the CLI + // (the recovery path this feature exists for) delivers a raw string. Parse the + // string form so both behave identically. A non-JSON string — a true typo — + // is left as-is and fails the Array.isArray check below with the same clear, + // key-named error. + let override: unknown = raw; + if (typeof raw === 'string') { + try { + override = JSON.parse(raw); + } catch { + override = raw; + } + } + if (!Array.isArray(override)) { + throw new Error( + `Context '${AGENTCORE_AZS_CONTEXT_KEY}' must be a JSON array of availability-zone names ` + + `(e.g. ["us-east-1b", "us-east-1c"]); got ${JSON.stringify(override)}.`, + ); + } + for (const az of override) { + if (typeof az !== 'string' || az.trim().length === 0) { + throw new Error( + `Context '${AGENTCORE_AZS_CONTEXT_KEY}' entries must be non-empty availability-zone-name ` + + `strings; got ${JSON.stringify(az)}.`, + ); + } + } + if (override.length < MIN_AGENTCORE_AZS) { + throw new Error( + `Context '${AGENTCORE_AZS_CONTEXT_KEY}' must list at least ${MIN_AGENTCORE_AZS} zones for ` + + `AgentCore high availability; got ${JSON.stringify(override)}.`, + ); + } + return override as string[]; +} + +/** + * Pure selection: given the account's AZ (name, id) pairs, returns the zone + * *names* whose physical zone IDs are AgentCore-supported for `region`. + * + * Returns an empty array when the region has no known constraint (absent from + * {@link AGENTCORE_SUPPORTED_AZ_IDS}) or none of the account's zones match. + */ +export function selectSupportedAzNames(region: string, zones: readonly AvailabilityZoneInfo[]): string[] { + const supported = AGENTCORE_SUPPORTED_AZ_IDS[region]; + if (!supported) { + return []; + } + const supportedIds = new Set(supported); + return zones.filter(zone => supportedIds.has(zone.zoneId)).map(zone => zone.zoneName); +} + +/** + * Live `DescribeAvailabilityZones` lookup (default {@link DescribeAzsFn}). + * + * The `@aws-sdk/client-ec2` module is imported dynamically so it is only loaded + * when auto-pin actually runs (concrete env, no override) — not during + * env-agnostic synth or in unit tests, which inject their own lookup. + */ +async function defaultDescribeAzs(region: string): Promise { + const { EC2Client, DescribeAvailabilityZonesCommand } = await import('@aws-sdk/client-ec2'); + const client = new EC2Client({ region }); + const response = await client.send( + new DescribeAvailabilityZonesCommand({ + // Standard AZs only — exclude Local Zones / Wavelength / Outposts. + Filters: [{ Name: 'zone-type', Values: ['availability-zone'] }], + }), + ); + const zones: AvailabilityZoneInfo[] = []; + for (const zone of response.AvailabilityZones ?? []) { + if (zone.ZoneName && zone.ZoneId) { + zones.push({ zoneName: zone.ZoneName, zoneId: zone.ZoneId }); + } + } + return zones; +} + +/** + * Resolves the Availability-Zone *names* the AgentCore VPC should pin to. + * + * Resolution order: + * 1. **Operator override** — a validated {@link AGENTCORE_AZS_CONTEXT_KEY} + * context value always wins (works even in env-agnostic synth, which is how + * the CI-built artifact and production stack pin zones). + * 2. **Auto-pin (default path)** — when synth has a concrete account + region, + * resolve the account's name -> zone-ID mapping, intersect it with + * {@link AGENTCORE_SUPPORTED_AZ_IDS} for the region, and pin the first + * {@link MIN_AGENTCORE_AZS} supported zones (matching AgentVpc's default + * `maxAzs`), so a fresh local `cdk deploy` lands only in supported zones + * without account-specific guesswork or widening the topology. + * 3. **Fallback** — env-agnostic synth (token/undefined account or region), an + * unknown region, a failed lookup, or fewer than {@link MIN_AGENTCORE_AZS} + * supported zones all return `undefined`, leaving CDK's default AZ selection + * in place. Degraded cases (2 & 3 boundary) surface a synth warning rather + * than failing, so unaffected regions and offline synth still deploy. + * + * @returns AZ names to pass to `ec2.Vpc({ availabilityZones })`, or `undefined` + * to keep the default `maxAzs` selection. + */ +export async function resolveAgentCoreAzs(options: ResolveAgentCoreAzsOptions): Promise { + const { scope, account, region } = options; + + // 1. Explicit, validated override wins (throws loudly if malformed). + const override = resolveAgentCoreAzOverride(scope.node); + if (override) { + return override; + } + + // 2. Auto-pin needs a concrete account + region. Env-agnostic synth uses + // token placeholders (the CI-built artifact + production stack) — pin via + // the override there instead. + if (!account || !region || Token.isUnresolved(account) || Token.isUnresolved(region)) { + return undefined; + } + + // 3. No published constraint for this region — don't guess. + if (!AGENTCORE_SUPPORTED_AZ_IDS[region]) { + return undefined; + } + + const describeAzs = options.describeAzs ?? defaultDescribeAzs; + + // The return sits outside the try/catch so a failed lookup degrades to the + // default AZ selection (surfaced as a warning) instead of masking the error + // with an empty result inside the catch (ts-silent-success-masking / AI004). + let pinnedZones: string[] | undefined; + try { + const zones = await describeAzs(region); + const names = selectSupportedAzNames(region, zones); + if (names.length >= MIN_AGENTCORE_AZS) { + // Pin exactly MIN_AGENTCORE_AZS zones — the HA floor, matching AgentVpc's + // default `maxAzs` (2). Pinning every supported zone would silently widen + // an account that deploys fine today from 2 AZs to all supported (often 3 + // -> 6 subnets), so cap the selection to keep the topology stable. + pinnedZones = names.slice(0, MIN_AGENTCORE_AZS); + } else { + Annotations.of(scope).addWarning( + `[AgentCore AZs] Found only ${names.length} AgentCore-supported availability zone(s) in ` + + `${region} (need >=${MIN_AGENTCORE_AZS}); using CDK's default AZ selection. Pin zones ` + + `explicitly via context '${AGENTCORE_AZS_CONTEXT_KEY}' if the deploy hits unsupported zones.`, + ); + } + } catch (err) { + Annotations.of(scope).addWarning( + `[AgentCore AZs] Could not resolve AgentCore-supported availability zones for ${region} ` + + `(${err instanceof Error ? err.message : String(err)}); using CDK's default AZ selection. ` + + `Pin zones explicitly via context '${AGENTCORE_AZS_CONTEXT_KEY}' if the deploy hits unsupported zones.`, + ); + } + return pinnedZones; +} diff --git a/cdk/src/main.ts b/cdk/src/main.ts index c8bc2cc8..cc594df4 100644 --- a/cdk/src/main.ts +++ b/cdk/src/main.ts @@ -19,6 +19,7 @@ import { App, Aspects, Tags } from 'aws-cdk-lib'; import { AwsSolutionsChecks } from 'cdk-nag'; +import { resolveAgentCoreAzs } from './constructs/agentcore-azs'; import { AgentStack } from './stacks/agent'; // for development, use account/region from cdk cli @@ -27,53 +28,78 @@ const devEnv = { region: process.env.CDK_DEFAULT_REGION, }; -const app = new App(); +/** + * Synthesizes the app. Async because AgentCore-supported availability zones are + * resolved from the account's zone mapping at synth time (a live + * `DescribeAvailabilityZones` call) when a concrete account/region is bound. + * Env-agnostic synth and the validated context override never touch AWS. + */ +async function main(): Promise { + const app = new App(); + + Aspects.of(app).add(new AwsSolutionsChecks()); + + const stackName = app.node.tryGetContext('stackName') ?? 'backgroundagent-dev'; -Aspects.of(app).add(new AwsSolutionsChecks()); + // Auto-pin the VPC to AgentCore-supported AZs (or honor the validated + // `agentcore:availabilityZones` override). Undefined => CDK default selection. + const availabilityZones = await resolveAgentCoreAzs({ + scope: app, + account: devEnv.account, + region: devEnv.region, + }); -const stackName = app.node.tryGetContext('stackName') ?? 'backgroundagent-dev'; + const stack = new AgentStack( + app, + stackName, + { + env: devEnv, + availabilityZones, + description: 'ABCA Development Stack (uksb-wt64nei4u6)', + }, + ); -const stack = new AgentStack( - app, - stackName, - { - env: devEnv, - description: 'ABCA Development Stack (uksb-wt64nei4u6)', - }, -); + const computeType = app.node.tryGetContext('compute_type') ?? 'agentcore'; -const computeType = app.node.tryGetContext('compute_type') ?? 'agentcore'; + // Route53 Resolver resources where tag changes trigger replacement cascades. + // Config: treats ANY property change (including tags) as requiring replacement. + // Association: depends on Config's physical ID; if Config is replaced, the + // Association update fails on the one-association-per-VPC constraint. + const excludeResourceTypes = [ + 'AWS::Route53Resolver::ResolverQueryLoggingConfig', + 'AWS::Route53Resolver::ResolverQueryLoggingConfigAssociation', + ]; -// Route53 Resolver resources where tag changes trigger replacement cascades. -// Config: treats ANY property change (including tags) as requiring replacement. -// Association: depends on Config's physical ID; if Config is replaced, the -// Association update fails on the one-association-per-VPC constraint. -const excludeResourceTypes = [ - 'AWS::Route53Resolver::ResolverQueryLoggingConfig', - 'AWS::Route53Resolver::ResolverQueryLoggingConfigAssociation', -]; + Tags.of(stack).add('compute_type', computeType, { excludeResourceTypes }); -Tags.of(stack).add('compute_type', computeType, { excludeResourceTypes }); + const githubTagKeys = [ + 'sha', + 'ref', + 'ref-type', + 'actor', + 'head-ref', + 'base-ref', + 'pr-number', + 'run-id', + 'run-attempt', + 'event', + 'workflow', + 'repository', + 'clean', + ] as const; -const githubTagKeys = [ - 'sha', - 'ref', - 'ref-type', - 'actor', - 'head-ref', - 'base-ref', - 'pr-number', - 'run-id', - 'run-attempt', - 'event', - 'workflow', - 'repository', - 'clean', -] as const; + for (const key of githubTagKeys) { + const value = app.node.tryGetContext(`github:${key}`); + Tags.of(stack).add(`github:${key}`, value || 'none', { excludeResourceTypes }); + } -for (const key of githubTagKeys) { - const value = app.node.tryGetContext(`github:${key}`); - Tags.of(stack).add(`github:${key}`, value || 'none', { excludeResourceTypes }); + app.synth(); } -app.synth(); +// Surface any synth-time failure (e.g. a malformed `agentcore:availabilityZones` +// override) as a non-zero exit. `void` satisfies no-floating-promises; throwing +// from the handler triggers an unhandled rejection so the CDK CLI fails loudly. +void main().catch((err: unknown) => { + process.exitCode = 1; + throw err; +}); diff --git a/cdk/src/stacks/agent.ts b/cdk/src/stacks/agent.ts index 1382dce4..05509d0d 100644 --- a/cdk/src/stacks/agent.ts +++ b/cdk/src/stacks/agent.ts @@ -70,8 +70,23 @@ const RUNTIME_SESSION_TIMEOUT_HOURS = 8; /** Index of the stage segment in a split API Gateway URL. */ const API_URL_STAGE_SEGMENT_INDEX = 3; +/** Properties for {@link AgentStack}. */ +export interface AgentStackProps extends StackProps { + /** + * Availability-zone *names* to pin the AgentCore VPC (and therefore the + * Runtime ENIs) into, so they land only in AgentCore-supported zones. + * + * Resolved in `main.ts` via `resolveAgentCoreAzs` — the validated + * `agentcore:availabilityZones` context override, else auto-selected from the + * account's supported zones when synth has a concrete account/region. Leave + * `undefined` (env-agnostic synth, unknown region) to keep CDK's default + * `maxAzs` selection. + */ + readonly availabilityZones?: string[]; +} + export class AgentStack extends Stack { - constructor(scope: Construct, id: string, props: StackProps = {}) { + constructor(scope: Construct, id: string, props: AgentStackProps = {}) { super(scope, id, props); // Build context is repo root (not agent/) so the Dockerfile can COPY @@ -201,8 +216,18 @@ export class AgentStack extends Stack { }, ]); - // Network isolation — VPC with restricted egress - const agentVpc = new AgentVpc(this, 'AgentVpc'); + // Network isolation — VPC with restricted egress. + // AgentCore only supports a subset of physical availability zones per + // region, and AZ *names* are aliased per-account, so the default maxAzs + // selection can land the Runtime ENIs in an unsupported zone and fail the + // deploy. `props.availabilityZones` carries the AZ names resolved in + // main.ts (`resolveAgentCoreAzs`): the validated `agentcore:availabilityZones` + // override, else auto-selected from the account's AgentCore-supported zones + // when synth has a concrete account/region. Left undefined otherwise, so the + // construct keeps CDK's default AZ selection. See constructs/agentcore-azs.ts. + const agentVpc = new AgentVpc(this, 'AgentVpc', { + ...(props.availabilityZones ? { availabilityZones: props.availabilityZones } : {}), + }); // DNS Firewall — domain-level egress filtering (observation mode for initial deployment) const additionalDomains = [...new Set(blueprints.flatMap(b => b.egressAllowlist))]; diff --git a/cdk/test/constructs/agent-vpc.test.ts b/cdk/test/constructs/agent-vpc.test.ts index 49614d8d..1c206662 100644 --- a/cdk/test/constructs/agent-vpc.test.ts +++ b/cdk/test/constructs/agent-vpc.test.ts @@ -149,4 +149,55 @@ describe('AgentVpc with custom props', () => { template.resourceCountIs('AWS::EC2::NatGateway', 2); }); + + test('accepts explicit availabilityZones and ignores maxAzs', () => { + const app = new App(); + const stack = new Stack(app, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + new AgentVpc(stack, 'AgentVpc', { + availabilityZones: ['us-east-1b', 'us-east-1c'], + maxAzs: 3, // should be ignored when availabilityZones is provided + }); + const template = Template.fromStack(stack); + + // 2 explicit AZs × 2 subnet types = 4 subnets + template.resourceCountIs('AWS::EC2::Subnet', 4); + + // Subnets are pinned to the requested AZ *names* — the whole point of the + // fix (a wrong-count assertion would pass even if AZs were unpinned). + template.hasResourceProperties('AWS::EC2::Subnet', { AvailabilityZone: 'us-east-1b' }); + template.hasResourceProperties('AWS::EC2::Subnet', { AvailabilityZone: 'us-east-1c' }); + }); + + test('env-agnostic synth falls back to Fn::GetAZs (no pinning, no crash)', () => { + const app = new App(); + // No env → account/region are tokens. The production AgentStack synthesizes + // this way, so auto-pin is skipped and CDK selects AZs at deploy time. + const stack = new Stack(app, 'TestStack'); + new AgentVpc(stack, 'AgentVpc'); + const template = Template.fromStack(stack); + + // Default maxAzs (2) → 4 subnets; AZ resolved at deploy via Fn::GetAZs. + template.resourceCountIs('AWS::EC2::Subnet', 4); + template.hasResourceProperties('AWS::EC2::Subnet', { + AvailabilityZone: { + 'Fn::Select': Match.arrayWith([{ 'Fn::GetAZs': '' }]), + }, + }); + }); + + test('availabilityZones with 3 zones creates 6 subnets', () => { + const app = new App(); + const stack = new Stack(app, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + new AgentVpc(stack, 'AgentVpc', { + availabilityZones: ['us-east-1b', 'us-east-1c', 'us-east-1d'], + }); + const template = Template.fromStack(stack); + + // 3 AZs × 2 subnet types = 6 subnets + template.resourceCountIs('AWS::EC2::Subnet', 6); + }); }); diff --git a/cdk/test/constructs/agentcore-azs.test.ts b/cdk/test/constructs/agentcore-azs.test.ts new file mode 100644 index 00000000..1bc6a700 --- /dev/null +++ b/cdk/test/constructs/agentcore-azs.test.ts @@ -0,0 +1,215 @@ +/** + * MIT No Attribution + * + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { App, Stack } from 'aws-cdk-lib'; +import { Annotations, Match } from 'aws-cdk-lib/assertions'; +import { + AGENTCORE_AZS_CONTEXT_KEY, + AGENTCORE_SUPPORTED_AZ_IDS, + AvailabilityZoneInfo, + resolveAgentCoreAzOverride, + resolveAgentCoreAzs, + selectSupportedAzNames, +} from '../../src/constructs/agentcore-azs'; + +function nodeWithContext(context?: Record) { + const app = new App({ context }); + return new Stack(app, 'TestStack').node; +} + +function scopeWithContext(context?: Record): Stack { + const app = new App({ context }); + return new Stack(app, 'TestStack'); +} + +// Realistic us-east-1 mapping: supported IDs are use1-az1/az2/az4. +const US_EAST_1_ZONES: AvailabilityZoneInfo[] = [ + { zoneName: 'us-east-1a', zoneId: 'use1-az2' }, // supported + { zoneName: 'us-east-1b', zoneId: 'use1-az4' }, // supported + { zoneName: 'us-east-1c', zoneId: 'use1-az6' }, // unsupported + { zoneName: 'us-east-1d', zoneId: 'use1-az1' }, // supported + { zoneName: 'us-east-1e', zoneId: 'use1-az3' }, // unsupported + { zoneName: 'us-east-1f', zoneId: 'use1-az5' }, // unsupported +]; + +describe('AGENTCORE_SUPPORTED_AZ_IDS', () => { + it('covers the documented AgentCore VPC regions', () => { + for (const region of [ + 'us-east-1', 'us-east-2', 'us-west-2', + 'ap-southeast-1', 'ap-southeast-2', 'ap-south-1', 'ap-northeast-1', + 'eu-west-1', 'eu-central-1', + ]) { + expect(AGENTCORE_SUPPORTED_AZ_IDS[region]).toBeDefined(); + } + }); + + it('lists at least two unique physical zone IDs per region', () => { + for (const [region, ids] of Object.entries(AGENTCORE_SUPPORTED_AZ_IDS)) { + expect(ids.length).toBeGreaterThanOrEqual(2); + expect(new Set(ids).size).toBe(ids.length); + for (const id of ids) { + // Physical zone-ID shape, e.g. use1-az1 (NOT a zone name like us-east-1a). + expect(id).toMatch(/^[a-z]+[0-9]+-az[0-9]+$/); + } + expect(region).toMatch(/^[a-z]{2}-[a-z]+-\d$/); + } + }); +}); + +describe('resolveAgentCoreAzOverride', () => { + it('returns undefined when the context key is unset', () => { + expect(resolveAgentCoreAzOverride(nodeWithContext())).toBeUndefined(); + }); + + it('returns the validated array when provided', () => { + const override = ['us-east-1b', 'us-east-1c']; + expect( + resolveAgentCoreAzOverride(nodeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: override })), + ).toEqual(override); + }); + + it('throws on a bare (non-JSON) string override (typo guard)', () => { + expect(() => + resolveAgentCoreAzOverride(nodeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: 'us-east-1b' })), + ).toThrow(/must be a JSON array/); + }); + + it('parses a JSON-string array (the `-c key=value` CLI form)', () => { + // CDK delivers `-c agentcore:availabilityZones=[...]` as a raw string, not a + // parsed array — the documented mid-rollback recovery path must still work. + expect( + resolveAgentCoreAzOverride(nodeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: '["us-east-1b","us-east-1c"]' })), + ).toEqual(['us-east-1b', 'us-east-1c']); + }); + + it('throws on a JSON string that does not parse to an array', () => { + expect(() => + resolveAgentCoreAzOverride(nodeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: '"us-east-1b"' })), + ).toThrow(/must be a JSON array/); + }); + + it('throws on a non-string / empty entry', () => { + expect(() => + resolveAgentCoreAzOverride(nodeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: ['us-east-1b', ''] })), + ).toThrow(/non-empty availability-zone-name/); + }); + + it('throws when fewer than two zones are listed (HA guidance)', () => { + expect(() => + resolveAgentCoreAzOverride(nodeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: ['us-east-1b'] })), + ).toThrow(/at least 2 zones/); + }); +}); + +describe('selectSupportedAzNames', () => { + it('returns the zone names whose IDs are AgentCore-supported', () => { + expect(selectSupportedAzNames('us-east-1', US_EAST_1_ZONES)).toEqual([ + 'us-east-1a', + 'us-east-1b', + 'us-east-1d', + ]); + }); + + it('returns an empty array for a region with no known constraint', () => { + expect(selectSupportedAzNames('eu-north-1', US_EAST_1_ZONES)).toEqual([]); + }); + + it('returns an empty array when no account zone matches the supported set', () => { + const zones: AvailabilityZoneInfo[] = [{ zoneName: 'us-east-1c', zoneId: 'use1-az6' }]; + expect(selectSupportedAzNames('us-east-1', zones)).toEqual([]); + }); +}); + +describe('resolveAgentCoreAzs', () => { + it('returns the validated override without calling the AZ lookup', async () => { + const describeAzs = jest.fn, [string]>(); + const result = await resolveAgentCoreAzs({ + scope: scopeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: ['us-east-1b', 'us-east-1c'] }), + account: '123456789012', + region: 'us-east-1', + describeAzs, + }); + expect(result).toEqual(['us-east-1b', 'us-east-1c']); + expect(describeAzs).not.toHaveBeenCalled(); + }); + + it('rethrows a malformed override (fails synth loudly)', async () => { + await expect( + resolveAgentCoreAzs({ + scope: scopeWithContext({ [AGENTCORE_AZS_CONTEXT_KEY]: 'us-east-1b' }), + account: '123456789012', + region: 'us-east-1', + }), + ).rejects.toThrow(/must be a JSON array/); + }); + + it('falls back (undefined) for env-agnostic synth without touching AWS', async () => { + const describeAzs = jest.fn, [string]>(); + expect(await resolveAgentCoreAzs({ scope: scopeWithContext(), account: undefined, region: undefined, describeAzs })) + .toBeUndefined(); + expect(await resolveAgentCoreAzs({ scope: scopeWithContext(), account: '123456789012', region: undefined, describeAzs })) + .toBeUndefined(); + expect(describeAzs).not.toHaveBeenCalled(); + }); + + it('falls back (undefined) for a region with no known constraint', async () => { + const describeAzs = jest.fn, [string]>(); + const result = await resolveAgentCoreAzs({ + scope: scopeWithContext(), + account: '123456789012', + region: 'eu-north-1', + describeAzs, + }); + expect(result).toBeUndefined(); + expect(describeAzs).not.toHaveBeenCalled(); + }); + + it('auto-pins the first two supported zone names (capped) for a concrete env', async () => { + // US_EAST_1_ZONES has three supported zones (az2/az4/az1 -> a/b/d); auto-pin + // caps at two to match AgentVpc's default maxAzs and avoid widening topology. + const describeAzs = jest.fn, [string]>().mockResolvedValue(US_EAST_1_ZONES); + const result = await resolveAgentCoreAzs({ + scope: scopeWithContext(), + account: '123456789012', + region: 'us-east-1', + describeAzs, + }); + expect(result).toEqual(['us-east-1a', 'us-east-1b']); + expect(describeAzs).toHaveBeenCalledWith('us-east-1'); + }); + + it('warns and falls back when fewer than two supported zones are found', async () => { + const scope = scopeWithContext(); + const describeAzs = jest.fn, [string]>().mockResolvedValue([ + { zoneName: 'us-east-1a', zoneId: 'use1-az1' }, + { zoneName: 'us-east-1c', zoneId: 'use1-az6' }, + ]); + const result = await resolveAgentCoreAzs({ scope, account: '123456789012', region: 'us-east-1', describeAzs }); + expect(result).toBeUndefined(); + Annotations.fromStack(scope).hasWarning('*', Match.stringLikeRegexp('AgentCore AZs')); + }); + + it('warns and falls back when the AZ lookup fails', async () => { + const scope = scopeWithContext(); + const describeAzs = jest.fn, [string]>().mockRejectedValue(new Error('DescribeAZ boom')); + const result = await resolveAgentCoreAzs({ scope, account: '123456789012', region: 'us-east-1', describeAzs }); + expect(result).toBeUndefined(); + Annotations.fromStack(scope).hasWarning('*', Match.stringLikeRegexp('AgentCore AZs')); + }); +}); diff --git a/docs/guides/DEPLOYMENT_GUIDE.md b/docs/guides/DEPLOYMENT_GUIDE.md index a32b0922..5d3de7e3 100644 --- a/docs/guides/DEPLOYMENT_GUIDE.md +++ b/docs/guides/DEPLOYMENT_GUIDE.md @@ -163,6 +163,35 @@ Triggers via `workflow_run` when `build.yml` completes successfully. The pipelin ## Known deployment issues +### AgentCore unsupported Availability Zones + +**Affects:** Fresh deploys in accounts whose default Availability Zones don't line up with the zones AgentCore supports for the region. + +**Symptom:** The `AWS::BedrockAgentCore::Runtime` resource fails to stabilize (`NotStabilized` — "subnets are in unsupported availability zones") and the stack rolls back. + +**Root cause:** AgentCore Runtime only places its network interfaces in a subset of each region's Availability Zones, published as physical **zone IDs** (e.g. `use1-az1`, `use1-az2`, `use1-az4` for `us-east-1`). Zone IDs are stable across accounts, but zone *names* (`us-east-1a`) are aliased per-account — so `us-east-1a` can map to a different physical zone in your account than in another. Left to its default, CDK picks zones by name and can land the Runtime subnets in an unsupported zone. See the AWS [Supported Availability Zones](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/security/agentcore-vpc/#supported-availability-zones) table for the per-region set. + +**Default behavior (auto-pin) — local deploy only:** When the stack is synthesized with a concrete account and region — a local `cdk deploy` / `mise //cdk:deploy`, which resolves credentials at synth time — it reads your account's zone name-to-ID mapping (`ec2:DescribeAvailabilityZones`) and pins the VPC to the first two AZ *names* whose IDs are AgentCore-supported (two matches AgentVpc's default `maxAzs`, so enabling this doesn't widen an already-working topology). No action needed on that path for the regions in the built-in map. + +**The CI/CD-deployed stack is NOT auto-pinned.** Auto-pin needs a bound account at synth time, and the pipeline doesn't have one: `build.yml` synthesizes `cdk.out` credential-less (env-agnostic), and `deploy.yml` deploys that pre-built artifact (`--app cdk/cdk.out`). So a team deploying through the pipeline falls back to CDK's default AZ selection and **must set the `agentcore:availabilityZones` override to pin zones**. The same applies to any region not yet in the built-in supported-AZ map. To set the override: + +1. Discover your account's zone name-to-ID mapping: + ```bash + aws ec2 describe-availability-zones --region \ + --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output text + ``` +2. Pick at least two zone **names** whose **IDs** are in the AgentCore-supported set for the region (high-availability floor). +3. Set the override — either in `cdk/cdk.context.json`: + ```json + { "agentcore:availabilityZones": ["us-east-1b", "us-east-1c"] } + ``` + or on the CLI (note the value is a JSON array): + ```bash + cdk deploy -c 'agentcore:availabilityZones=["us-east-1b","us-east-1c"]' + ``` + +The override is validated at synth time (both forms parse identically): a non-array, an empty/non-string entry, or fewer than two zones fails the synth with a message naming the key and expected shape. The supported-AZ map and resolution logic live in `cdk/src/constructs/agentcore-azs.ts`. + ### DNS Query Log Config replacement cascade (upgrading from pre-v0.5) **Affects:** Stacks deployed *before* the tag-exclusion fix ([#222](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/pull/222)). Stacks created after this fix are not affected. diff --git a/docs/guides/QUICK_START.mdx b/docs/guides/QUICK_START.mdx index 45e85151..ee0d33ca 100644 --- a/docs/guides/QUICK_START.mdx +++ b/docs/guides/QUICK_START.mdx @@ -72,7 +72,7 @@ If `lib/bin/bgagent.js` is missing, run `mise run build` from `cli/` (or repeat ::: -> **Note:** `mise run build` includes CDK synthesis, which queries AWS for availability zones. Your active AWS credentials must have at least `ec2:DescribeAvailabilityZones` permission, or the build will fail. If you use named profiles, make sure `AWS_PROFILE` is set before running the build. +> **Note:** `mise run build` includes CDK synthesis, which queries AWS for availability zones. Your active AWS credentials must have at least `ec2:DescribeAvailabilityZones` permission, or the build will fail. If you use named profiles, make sure `AWS_PROFILE` is set before running the build. A local deploy uses that same call to auto-pin the VPC to AgentCore-supported zones; if you deploy from an env-agnostic artifact or hit "unsupported availability zones", set the `agentcore:availabilityZones` override — see [Known deployment issues](./DEPLOYMENT_GUIDE.md#agentcore-unsupported-availability-zones). ## Step 2 - Prepare a repository @@ -514,6 +514,7 @@ Here is what the platform did after you ran `node lib/bin/bgagent.js submit`: | CDK deploy prompts for approval and hangs | Non-interactive terminal (CI/CD, scripts) | Pass `--require-approval never` to `cdk deploy` (Step 3 uses it) or use an interactive terminal | | Deploy rolled back; can't redeploy (`ROLLBACK_COMPLETE`) | A first-create failure leaves the stack un-updatable | `mise //cdk:destroy` (or delete the stack), then deploy again. Do **not** force-delete past stuck VPC resources — it orphans the VPC, and VPCs are quota-capped per Region | | Stack stuck in `DELETE_FAILED` on a security group / subnet | AgentCore's service-managed (Hyperplane) ENIs reclaim asynchronously after the runtime is gone | Wait ~20–40 min for AWS to release the ENIs, then retry `mise //cdk:destroy`. You cannot force-detach an `amazon-aws`-owned ENI | +| Runtime rollback: `NotStabilized` / "subnets are in unsupported availability zones" | VPC subnets landed in AZs AgentCore doesn't support (auto-pin skipped for env-agnostic synth, or region not in the built-in map) | Set the `agentcore:availabilityZones` override to supported zone names — see [Known deployment issues](./DEPLOYMENT_GUIDE.md#agentcore-unsupported-availability-zones) | | `put-secret-value` returns double-dot endpoint | `REGION` variable is empty | Set `REGION=us-east-1` (or your actual region) before running the command | | Model / Bedrock errors in logs (`not available on your bedrock`, zero tokens) | Model not entitled for the account or Region, wrong `modelId` shape, or missing Marketplace / FTU steps | Follow **Amazon Bedrock before your first task** above; confirm [model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) and use an [inference profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-use.html) ID such as `us.anthropic.claude-sonnet-4-6` where required; keep `grantInvoke` in `agent.ts` aligned with that model | | `REPO_NOT_ONBOARDED` on task submit | Blueprint `repo` does not match what you passed to the CLI | Confirm `BLUEPRINT_REPO`, CDK context `blueprintRepo`, or the `repo` prop on the `Blueprint` in `cdk/src/stacks/agent.ts` resolves to exactly the same `owner/repo` you pass to the CLI | diff --git a/docs/src/content/docs/getting-started/Deployment-guide.md b/docs/src/content/docs/getting-started/Deployment-guide.md index 01f6e2ce..2035170f 100644 --- a/docs/src/content/docs/getting-started/Deployment-guide.md +++ b/docs/src/content/docs/getting-started/Deployment-guide.md @@ -167,6 +167,35 @@ Triggers via `workflow_run` when `build.yml` completes successfully. The pipelin ## Known deployment issues +### AgentCore unsupported Availability Zones + +**Affects:** Fresh deploys in accounts whose default Availability Zones don't line up with the zones AgentCore supports for the region. + +**Symptom:** The `AWS::BedrockAgentCore::Runtime` resource fails to stabilize (`NotStabilized` — "subnets are in unsupported availability zones") and the stack rolls back. + +**Root cause:** AgentCore Runtime only places its network interfaces in a subset of each region's Availability Zones, published as physical **zone IDs** (e.g. `use1-az1`, `use1-az2`, `use1-az4` for `us-east-1`). Zone IDs are stable across accounts, but zone *names* (`us-east-1a`) are aliased per-account — so `us-east-1a` can map to a different physical zone in your account than in another. Left to its default, CDK picks zones by name and can land the Runtime subnets in an unsupported zone. See the AWS [Supported Availability Zones](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/security/agentcore-vpc/#supported-availability-zones) table for the per-region set. + +**Default behavior (auto-pin) — local deploy only:** When the stack is synthesized with a concrete account and region — a local `cdk deploy` / `mise //cdk:deploy`, which resolves credentials at synth time — it reads your account's zone name-to-ID mapping (`ec2:DescribeAvailabilityZones`) and pins the VPC to the first two AZ *names* whose IDs are AgentCore-supported (two matches AgentVpc's default `maxAzs`, so enabling this doesn't widen an already-working topology). No action needed on that path for the regions in the built-in map. + +**The CI/CD-deployed stack is NOT auto-pinned.** Auto-pin needs a bound account at synth time, and the pipeline doesn't have one: `build.yml` synthesizes `cdk.out` credential-less (env-agnostic), and `deploy.yml` deploys that pre-built artifact (`--app cdk/cdk.out`). So a team deploying through the pipeline falls back to CDK's default AZ selection and **must set the `agentcore:availabilityZones` override to pin zones**. The same applies to any region not yet in the built-in supported-AZ map. To set the override: + +1. Discover your account's zone name-to-ID mapping: + ```bash + aws ec2 describe-availability-zones --region \ + --query 'AvailabilityZones[].[ZoneName,ZoneId]' --output text + ``` +2. Pick at least two zone **names** whose **IDs** are in the AgentCore-supported set for the region (high-availability floor). +3. Set the override — either in `cdk/cdk.context.json`: + ```json + { "agentcore:availabilityZones": ["us-east-1b", "us-east-1c"] } + ``` + or on the CLI (note the value is a JSON array): + ```bash + cdk deploy -c 'agentcore:availabilityZones=["us-east-1b","us-east-1c"]' + ``` + +The override is validated at synth time (both forms parse identically): a non-array, an empty/non-string entry, or fewer than two zones fails the synth with a message naming the key and expected shape. The supported-AZ map and resolution logic live in `cdk/src/constructs/agentcore-azs.ts`. + ### DNS Query Log Config replacement cascade (upgrading from pre-v0.5) **Affects:** Stacks deployed *before* the tag-exclusion fix ([#222](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/pull/222)). Stacks created after this fix are not affected. diff --git a/docs/src/content/docs/getting-started/Quick-start.mdx b/docs/src/content/docs/getting-started/Quick-start.mdx index e12fb871..a1a32ae0 100644 --- a/docs/src/content/docs/getting-started/Quick-start.mdx +++ b/docs/src/content/docs/getting-started/Quick-start.mdx @@ -72,7 +72,7 @@ If `lib/bin/bgagent.js` is missing, run `mise run build` from `cli/` (or repeat ::: -> **Note:** `mise run build` includes CDK synthesis, which queries AWS for availability zones. Your active AWS credentials must have at least `ec2:DescribeAvailabilityZones` permission, or the build will fail. If you use named profiles, make sure `AWS_PROFILE` is set before running the build. +> **Note:** `mise run build` includes CDK synthesis, which queries AWS for availability zones. Your active AWS credentials must have at least `ec2:DescribeAvailabilityZones` permission, or the build will fail. If you use named profiles, make sure `AWS_PROFILE` is set before running the build. A local deploy uses that same call to auto-pin the VPC to AgentCore-supported zones; if you deploy from an env-agnostic artifact or hit "unsupported availability zones", set the `agentcore:availabilityZones` override — see [Known deployment issues](/sample-autonomous-cloud-coding-agents/getting-started/deployment-guide#agentcore-unsupported-availability-zones). ## Step 2 - Prepare a repository @@ -514,6 +514,7 @@ Here is what the platform did after you ran `node lib/bin/bgagent.js submit`: | CDK deploy prompts for approval and hangs | Non-interactive terminal (CI/CD, scripts) | Pass `--require-approval never` to `cdk deploy` (Step 3 uses it) or use an interactive terminal | | Deploy rolled back; can't redeploy (`ROLLBACK_COMPLETE`) | A first-create failure leaves the stack un-updatable | `mise //cdk:destroy` (or delete the stack), then deploy again. Do **not** force-delete past stuck VPC resources — it orphans the VPC, and VPCs are quota-capped per Region | | Stack stuck in `DELETE_FAILED` on a security group / subnet | AgentCore's service-managed (Hyperplane) ENIs reclaim asynchronously after the runtime is gone | Wait ~20–40 min for AWS to release the ENIs, then retry `mise //cdk:destroy`. You cannot force-detach an `amazon-aws`-owned ENI | +| Runtime rollback: `NotStabilized` / "subnets are in unsupported availability zones" | VPC subnets landed in AZs AgentCore doesn't support (auto-pin skipped for env-agnostic synth, or region not in the built-in map) | Set the `agentcore:availabilityZones` override to supported zone names — see [Known deployment issues](/sample-autonomous-cloud-coding-agents/getting-started/deployment-guide#agentcore-unsupported-availability-zones) | | `put-secret-value` returns double-dot endpoint | `REGION` variable is empty | Set `REGION=us-east-1` (or your actual region) before running the command | | Model / Bedrock errors in logs (`not available on your bedrock`, zero tokens) | Model not entitled for the account or Region, wrong `modelId` shape, or missing Marketplace / FTU steps | Follow **Amazon Bedrock before your first task** above; confirm [model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) and use an [inference profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-use.html) ID such as `us.anthropic.claude-sonnet-4-6` where required; keep `grantInvoke` in `agent.ts` aligned with that model | | `REPO_NOT_ONBOARDED` on task submit | Blueprint `repo` does not match what you passed to the CLI | Confirm `BLUEPRINT_REPO`, CDK context `blueprintRepo`, or the `repo` prop on the `Blueprint` in `cdk/src/stacks/agent.ts` resolves to exactly the same `owner/repo` you pass to the CLI | diff --git a/yarn.lock b/yarn.lock index 04b6df25..71cda37c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -478,6 +478,21 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/client-ec2@^3.1078.0": + version "3.1087.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-ec2/-/client-ec2-3.1087.0.tgz#eb234305bbaada457aa7d75d75956c3e210df672" + integrity sha512-4QcrDvc5mBA3mfhWTTiv32ZIYUhVdxT89b1faimTfU29B4e7Kb2//+WtXN1hbIx6W+tZZ/wriwylapRfL2AhEQ== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/credential-provider-node" "^3.972.68" + "@aws-sdk/middleware-sdk-ec2" "^3.972.46" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/fetch-http-handler" "^5.6.5" + "@smithy/node-http-handler" "^4.9.5" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/client-ecs@^3.1078.0": version "3.1081.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-ecs/-/client-ecs-3.1081.0.tgz#05f57706640d9f3e6853fbb5d302c7940b91f32a" @@ -565,6 +580,20 @@ bowser "^2.11.0" tslib "^2.6.2" +"@aws-sdk/core@^3.975.2": + version "3.975.2" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.975.2.tgz#743f5ccf805721da3fd330155f5e374e8dbc8969" + integrity sha512-iyeXwziyjJpixq5OmhsIyrSWx8vwcI7gDo4yRUC3EP7NQtOo9iAJiIEc3G+/HkhtNXqOhofiCK7Lc34Sq+fJWg== + dependencies: + "@aws-sdk/types" "^3.974.1" + "@aws-sdk/xml-builder" "^3.972.35" + "@aws/lambda-invoke-store" "^0.3.0" + "@smithy/core" "^3.29.3" + "@smithy/signature-v4" "^5.6.3" + "@smithy/types" "^4.16.1" + bowser "^2.11.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-env@^3.972.55": version "3.972.55" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.55.tgz#1129acf8860db362a30a02a531387fd399448268" @@ -576,6 +605,17 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-env@^3.972.58": + version "3.972.58" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.58.tgz#74592896db48dc9fec7852ba0e842c06c13f0f52" + integrity sha512-vyGtvK1rY940eq7JT0yIGKuZ+2kpPSJcHibSvGlit5oiMFDamzC7cxBGLl4FLnd6suihMXDI2FSF2dL6TmBqPA== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-http@^3.972.57": version "3.972.57" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.57.tgz#4f73bb2f0e03525ab11e001ac18c39cb120dd14c" @@ -589,6 +629,19 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-http@^3.972.60": + version "3.972.60" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.60.tgz#da1d2646fb1bee732436b75ab5cda18f06554727" + integrity sha512-g9b9YzDrD5pcKiPBJfCSXRfFMrA39eR0guUhZ5SRm+7vMAVc43+effxbcamxBjSd5bUhrdKo5te/yQuWurLXLA== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/fetch-http-handler" "^5.6.5" + "@smithy/node-http-handler" "^4.9.5" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-ini@^3.972.62": version "3.972.62" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.62.tgz#a2ca7fc7a1899c0a4a38e501baa666cf1449f2e9" @@ -608,6 +661,25 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-ini@^3.973.2": + version "3.973.2" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.2.tgz#c66b59528b985baeb620926a4582f8d821f7f5f4" + integrity sha512-Yr7yxNyQ8aHt9Ww0RPFUZx+xiem+vl7vuwhP0tniTijoesJNV5jou9HCgVpI0GEPAF+89TkOvilE5uRrZJnjaw== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/credential-provider-env" "^3.972.58" + "@aws-sdk/credential-provider-http" "^3.972.60" + "@aws-sdk/credential-provider-login" "^3.972.64" + "@aws-sdk/credential-provider-process" "^3.972.58" + "@aws-sdk/credential-provider-sso" "^3.973.2" + "@aws-sdk/credential-provider-web-identity" "^3.972.64" + "@aws-sdk/nested-clients" "^3.997.32" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/credential-provider-imds" "^4.4.7" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-login@^3.972.61": version "3.972.61" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.61.tgz#f5ba696bae9af3505ae5f3e2a70d7e216d0d37f6" @@ -620,6 +692,18 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-login@^3.972.64": + version "3.972.64" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.64.tgz#d323b16bf9857f2248b234e5f83e033f3981d50d" + integrity sha512-YQoSI4d6kXvoenoG/0Jv/PqaAuukHzGmGXGyHBQYeEUNsYovlNAn/Sw1wp/WQbhcQ3HsEMGgjEahvD3igz6ecQ== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/nested-clients" "^3.997.32" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-node@^3.972.61", "@aws-sdk/credential-provider-node@^3.972.64": version "3.972.64" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.64.tgz#282456c24bad616faefe2a6c68701913f8289c10" @@ -637,6 +721,23 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-node@^3.972.68": + version "3.972.68" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.68.tgz#300fd3c97c01f9669c34dc2def1f7e29d3b0ff7c" + integrity sha512-4akjzW9CjorByYfqXBXmYUh/h7Io3U4DtVgGGh9TQraZ7ZlyJqNyHwDRGiUFnHD+BTOeTbCesCa4sJaK7BGZ7A== + dependencies: + "@aws-sdk/credential-provider-env" "^3.972.58" + "@aws-sdk/credential-provider-http" "^3.972.60" + "@aws-sdk/credential-provider-ini" "^3.973.2" + "@aws-sdk/credential-provider-process" "^3.972.58" + "@aws-sdk/credential-provider-sso" "^3.973.2" + "@aws-sdk/credential-provider-web-identity" "^3.972.64" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/credential-provider-imds" "^4.4.7" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-process@^3.972.55": version "3.972.55" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.55.tgz#bebd30d0065ca34f64e14a870f54a09f23cf11e2" @@ -648,6 +749,17 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-process@^3.972.58": + version "3.972.58" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.58.tgz#ac55b2271ea5672e8b00e390f6b00f9f2433ad2b" + integrity sha512-1nYitRCaDmXWUrpBJt6WlcGjLx1JVsMY8rlYuHHsTYTSaYikbixYdQSyINN2VYq1F798uTO9qHAzytL25M8g3A== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-sso@^3.972.61": version "3.972.61" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.61.tgz#8c15846c65d2f03bfca3347626e1fcd1a0f40726" @@ -661,6 +773,19 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-sso@^3.973.2": + version "3.973.2" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.2.tgz#bdf55f712b8ddf70b18be483a4c8432c6e2e876c" + integrity sha512-pjMLaLU/JZi5lVfmR14V1OZqRBTuMHf6AwGNZA0K9hK+JKtO3jcLBarfD8iq5oc8cSowvc/9R32sqMVXZPo6xQ== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/nested-clients" "^3.997.32" + "@aws-sdk/token-providers" "3.1087.0" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-web-identity@^3.972.61": version "3.972.61" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.61.tgz#1938cc425e6156acd673484f10b437c5c4c85158" @@ -673,6 +798,18 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-web-identity@^3.972.64": + version "3.972.64" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.64.tgz#fdeee7ac25a1d9e6c791184ba93eb18c74eeea34" + integrity sha512-7Buc7p0OvDHW7iBsu4b+YdS0WnaFBDGKDfbVQqaac9dkWiSiUtIoarBDsA1RmOVXZijaZJDoHJFIQiicQvWRlQ== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/nested-clients" "^3.997.32" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/dynamodb-codec@^3.973.26", "@aws-sdk/dynamodb-codec@^3.973.29": version "3.973.29" resolved "https://registry.yarnpkg.com/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.29.tgz#ea72996b2e1f2c687254c69c357fa4278e42f1da" @@ -744,6 +881,18 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/middleware-sdk-ec2@^3.972.46": + version "3.972.46" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.972.46.tgz#9fb2f43ab8033084ee9684785ec4d81b9fac5db6" + integrity sha512-H73wH+QnWnXHFKIRpewMkLLczp+Fkmb/bkMutW13ABb3xLKbfPU/TTdOADZ+f0ZE+RbQ6K1y+PfjEziL2cRxPA== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/signature-v4" "^5.6.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/middleware-sdk-s3@^3.972.60": version "3.972.60" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.60.tgz#2aef54e9f4b352268f2dc9a0b3fe628795826512" @@ -783,6 +932,20 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/nested-clients@^3.997.32": + version "3.997.32" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.997.32.tgz#3ddb2ccfc5f8a50041eedc5981c441dd984f419b" + integrity sha512-6Yj2fr9XF67cndITea48rchTdVr3VGx6PN47bIKNinJAjLkmaIlz/4EBPCgJ8UmhVopiXmeAuPLI3+DXDDbMhQ== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/signature-v4-multi-region" "^3.996.40" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/fetch-http-handler" "^5.6.5" + "@smithy/node-http-handler" "^4.9.5" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/s3-presigned-post@^3.1078.0": version "3.1081.0" resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.1081.0.tgz#ed27961af8e772e23745e7ff34f34083bc69c291" @@ -818,6 +981,16 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/signature-v4-multi-region@^3.996.40": + version "3.996.40" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.40.tgz#f8caa07c2463e24d7a13bf2d5a77d7083dc0ecda" + integrity sha512-wrGZ/authosokclY1DXsiWT/1WjfCI22FuZGgdcilF+XLTXs5dCjAtiFYSPsEToZkbm3Lj2YP8PoWg0yoMNu0g== + dependencies: + "@aws-sdk/types" "^3.974.1" + "@smithy/signature-v4" "^5.6.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/token-providers@3.1078.0": version "3.1078.0" resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.1078.0.tgz#554be2f2c42f21191f31aead718bcedf2047926d" @@ -842,6 +1015,18 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/token-providers@3.1087.0": + version "3.1087.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.1087.0.tgz#0cd1b7c41a092340a287a2d009027ef88bc7635b" + integrity sha512-umM+qNq16f2fH+VLM5MqXW4ORNQAjk+TOSto73xbUHcKaU41L48j786r3UWQYlejeJk37NlvRYgxBT+MBkfaYQ== + dependencies: + "@aws-sdk/core" "^3.975.2" + "@aws-sdk/nested-clients" "^3.997.32" + "@aws-sdk/types" "^3.974.1" + "@smithy/core" "^3.29.3" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/types@^3.222.0", "@aws-sdk/types@^3.973.15": version "3.973.15" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.973.15.tgz#98a4860bed33c32c7088924d0ab52f9eabbdf7c3" @@ -850,6 +1035,14 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/types@^3.974.1": + version "3.974.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.974.1.tgz#ac3f8943d2a04c1ced470452494b7723b926714e" + integrity sha512-W0IQZR0eaBqlBFIIofMapaWkw1W0U+Xi4dvW+BqwmCEMd8Ng2U6IhkxuPSjMVnR8klLjfuS9PeZWUl1N6UaZdg== + dependencies: + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws-sdk/util-dynamodb@^3.996.5": version "3.996.5" resolved "https://registry.yarnpkg.com/@aws-sdk/util-dynamodb/-/util-dynamodb-3.996.5.tgz#2ad647e1532c20c76570a878e49f058a8d21e012" @@ -865,6 +1058,14 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@aws-sdk/xml-builder@^3.972.35": + version "3.972.35" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.972.35.tgz#8affa66cb8836168b66dd3a76eeebb8165ed9672" + integrity sha512-pXzaWe3evZhjxDXAlMnqISe/XefTCGwBJG4nFTXaWSgAnMkqPEhxEPqJNhhpGesEvKFhvNpnozJJ4GTL11bRYw== + dependencies: + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@aws/durable-execution-sdk-js@^2.0.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@aws/durable-execution-sdk-js/-/durable-execution-sdk-js-2.1.0.tgz#a020f8f3eae0fd3b577ad55397e454241a9e3029" @@ -2744,6 +2945,14 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@smithy/core@^3.29.3", "@smithy/core@^3.29.4": + version "3.29.4" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.29.4.tgz#ff4f9ba70e0276c892cd3dad5dbef54fc4945464" + integrity sha512-G1GRglAabzEhqghJMBAd54FkRS7SAFGHEwbhcI9r+O+LIMuFsLyXkLZkCoFSgAglRu8s/URVXJB0hglq3ZipIg== + dependencies: + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@smithy/credential-provider-imds@^4.4.5": version "4.4.6" resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.6.tgz#048ad961282016fc2d46d457f43beac8ca8f0e24" @@ -2753,6 +2962,15 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@smithy/credential-provider-imds@^4.4.7": + version "4.4.9" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.9.tgz#7ae40b9e1faab4256197335353463b3788591e13" + integrity sha512-2nfV4qRKiYeXU4zD2vvSCfg5dfp/BuhrM73vt7q9gzBhxs4rbPxXY21wo+kyI3bRmXcEGRnCLTaW8O437jzHIg== + dependencies: + "@smithy/core" "^3.29.4" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@smithy/fetch-http-handler@^5.6.2": version "5.6.3" resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.3.tgz#7b4aec28f89bd1e3aa00dfae0d0bd30079c58341" @@ -2762,6 +2980,15 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@smithy/fetch-http-handler@^5.6.5": + version "5.6.6" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.6.tgz#eee36126fb4f9cd4aee278ae8f20fb6a83f02b07" + integrity sha512-NHLgAlORUFZjn5ZfhYuyyKMlXA1WLYOdGxEhyNxrPpbJzoacGbl0chn1lN2KiZ8mpNVk0tV5607CSYlYs/OFgw== + dependencies: + "@smithy/core" "^3.29.4" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@smithy/is-array-buffer@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" @@ -2778,6 +3005,15 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@smithy/node-http-handler@^4.9.5": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.9.6.tgz#a1756af48d8f1250aaa7225e74a98d95900383fb" + integrity sha512-odd+HYx3OLcXRSEz0ZeF3JQdSYdK8QnRgA2N87cPW7coWIbKfRk7a9VQjfeWQLqnzrDLk23KMEn46p8N7M/JFg== + dependencies: + "@smithy/core" "^3.29.4" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@smithy/protocol-http@^5.5.5": version "5.5.6" resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.5.6.tgz#62db7e22b4446168ad091b8fe90d71b05c0fd8bd" @@ -2795,6 +3031,15 @@ "@smithy/types" "^4.15.1" tslib "^2.6.2" +"@smithy/signature-v4@^5.6.3": + version "5.6.5" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.6.5.tgz#163dce1523903d251c86da30a0725e6801072d20" + integrity sha512-MO5VEhwVl0BN7xVoVeNrZfiUFoQtqxUbgl6/RwOTlMMxCSjblG8twSrVTwz3J4w9WZxd2rBfBAUXjH77agspBg== + dependencies: + "@smithy/core" "^3.29.4" + "@smithy/types" "^4.16.1" + tslib "^2.6.2" + "@smithy/types@^4.15.1": version "4.15.1" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.15.1.tgz#cdad0fdf4c5d1f5788dc21a3f1fb6af523d79da0" @@ -2802,6 +3047,13 @@ dependencies: tslib "^2.6.2" +"@smithy/types@^4.16.1": + version "4.16.1" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.16.1.tgz#19e199c234829a51c085caf63f0bb17bb80187e4" + integrity sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg== + dependencies: + tslib "^2.6.2" + "@smithy/util-buffer-from@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b"