@aws/nx-plugin 0.82.1 → 0.83.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.
Files changed (54) hide show
  1. package/LICENSE-THIRD-PARTY +34 -6
  2. package/package.json +1 -1
  3. package/src/infra/app/__snapshots__/generator.spec.ts.snap +4 -4
  4. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +148 -56
  5. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +53 -3
  6. package/src/py/mcp-server/generator.js +1 -0
  7. package/src/py/mcp-server/generator.js.map +1 -1
  8. package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +53 -3
  9. package/src/py/strands-agent/generator.js +1 -0
  10. package/src/py/strands-agent/generator.js.map +1 -1
  11. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +108 -50
  12. package/src/smithy/ts/api/generator.js +2 -0
  13. package/src/smithy/ts/api/generator.js.map +1 -1
  14. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +256 -128
  15. package/src/trpc/backend/generator.js +2 -0
  16. package/src/trpc/backend/generator.js.map +1 -1
  17. package/src/ts/lambda-function/generator.js +2 -0
  18. package/src/ts/lambda-function/generator.js.map +1 -1
  19. package/src/ts/lib/vitest.js +2 -0
  20. package/src/ts/lib/vitest.js.map +1 -1
  21. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +54 -3
  22. package/src/ts/mcp-server/generator.js +7 -1
  23. package/src/ts/mcp-server/generator.js.map +1 -1
  24. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +372 -26
  25. package/src/ts/react-website/app/generator.js +5 -1
  26. package/src/ts/react-website/app/generator.js.map +1 -1
  27. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +2 -2
  28. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +2 -0
  29. package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +52 -3
  30. package/src/ts/strands-agent/generator.js +3 -1
  31. package/src/ts/strands-agent/generator.js.map +1 -1
  32. package/src/utils/__snapshots__/shared-constructs.spec.ts.snap +157 -5
  33. package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +14 -0
  34. package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +37 -2
  35. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +1 -1
  36. package/src/utils/api-constructs/files/cdk/app/apis/http/__apiNameKebabCase__.ts.template +19 -17
  37. package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +28 -21
  38. package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +4 -3
  39. package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +24 -5
  40. package/src/utils/api-constructs/files/cdk/core/api/utils/utils.ts.template +4 -1
  41. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +11 -2
  42. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +11 -2
  43. package/src/utils/files/common/constructs/src/core/runtime-config.ts.template +157 -5
  44. package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +100 -0
  45. package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +19 -17
  46. package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +12 -5
  47. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +2 -2
  48. package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +2 -0
  49. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +2 -1
  50. package/src/utils/versions.d.ts +6 -1
  51. package/src/utils/versions.js +5 -0
  52. package/src/utils/versions.js.map +1 -1
  53. package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +8 -3
  54. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +10 -1
@@ -33,6 +33,7 @@ import {
33
33
  Runtime,
34
34
  RuntimeProps,
35
35
  } from '@aws-cdk/aws-bedrock-agentcore-alpha';
36
+ import { RuntimeConfig } from '../../../core/runtime-config.js';
36
37
 
37
38
  export type SnapshotBedrockAgentProps = Omit<
38
39
  RuntimeProps,
@@ -46,6 +47,8 @@ export class SnapshotBedrockAgent extends Construct {
46
47
  constructor(scope: Construct, id: string, props?: SnapshotBedrockAgentProps) {
47
48
  super(scope, id);
48
49
 
50
+ const rc = RuntimeConfig.ensure(this);
51
+
49
52
  this.dockerImage = AgentRuntimeArtifact.fromAsset(
50
53
  path.dirname(url.fileURLToPath(new URL(import.meta.url))),
51
54
  {
@@ -65,6 +68,17 @@ export class SnapshotBedrockAgent extends Construct {
65
68
  protocolConfiguration: ProtocolType.HTTP,
66
69
  agentRuntimeArtifact: this.dockerImage,
67
70
  ...props,
71
+ environmentVariables: {
72
+ RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
73
+ ...props?.environmentVariables,
74
+ },
75
+ });
76
+
77
+ rc.grantReadAppConfig(this.agentCoreRuntime);
78
+
79
+ rc.set('connection', 'agentRuntimes', {
80
+ ...rc.get('connection').agentRuntimes,
81
+ SnapshotBedrockAgent: this.agentCoreRuntime.agentRuntimeArn,
68
82
  });
69
83
  }
70
84
  }
@@ -111,6 +125,12 @@ variable "tags" {
111
125
  default = {}
112
126
  }
113
127
 
128
+ module "appconfig" {
129
+ source = "../../../core/runtime-config/appconfig"
130
+
131
+ application_name = "TerraformSnapshotAgent-runtime-config"
132
+ }
133
+
114
134
  module "agent_core_runtime" {
115
135
  source = "../../../core/agent-core"
116
136
  agent_runtime_name = "TerraformSnapshotAgent"
@@ -123,9 +143,33 @@ module "agent_core_runtime" {
123
143
  # }
124
144
  # }
125
145
 
126
- env = var.env
127
- additional_iam_policy_statements = var.additional_iam_policy_statements
146
+ env = merge({
147
+ RUNTIME_CONFIG_APP_ID = module.appconfig.application_id
148
+ }, var.env)
149
+ additional_iam_policy_statements = concat([
150
+ {
151
+ Effect = "Allow"
152
+ Action = [
153
+ "appconfig:StartConfigurationSession",
154
+ "appconfig:GetLatestConfiguration"
155
+ ]
156
+ Resource = ["\${module.appconfig.application_arn}/*"]
157
+ }
158
+ ], var.additional_iam_policy_statements)
128
159
  tags = var.tags
160
+
161
+ depends_on = [module.appconfig]
162
+ }
163
+
164
+ # Add agent runtime ARN to runtime config
165
+ module "add_agent_runtime_to_runtime_config" {
166
+ source = "../../../core/runtime-config/entry"
167
+
168
+ namespace = "connection"
169
+ key = "agentRuntimes"
170
+ value = { "TerraformSnapshotAgent" = module.agent_core_runtime.agent_core_runtime_arn }
171
+
172
+ depends_on = [module.agent_core_runtime]
129
173
  }
130
174
 
131
175
  output "agent_core_runtime_role_arn" {
@@ -137,6 +181,11 @@ output "agent_core_runtime_arn" {
137
181
  description = "ARN of the Bedrock Agent Core runtime"
138
182
  value = module.agent_core_runtime.agent_core_runtime_arn
139
183
  }
184
+
185
+ output "appconfig_application_id" {
186
+ description = "AppConfig Application ID for runtime config"
187
+ value = module.appconfig.application_id
188
+ }
140
189
  "
141
190
  `;
142
191
 
@@ -474,7 +523,7 @@ resource "aws_bedrockagentcore_agent_runtime" "agent_runtime" {
474
523
 
475
524
  depends_on = [
476
525
  null_resource.docker_publish,
477
- aws_iam_role_policy.agent_core_runtime_policy
526
+ aws_iam_role_policy_attachment.agent_core_policy
478
527
  ]
479
528
  }
480
529
 
@@ -83,9 +83,11 @@ const tsStrandsAgentGenerator = (tree, options) => tslib_1.__awaiter(void 0, voi
83
83
  'ws',
84
84
  'cors',
85
85
  '@aws-sdk/credential-providers',
86
+ '@aws-sdk/client-appconfigdata',
87
+ '@aws-lambda-powertools/parameters',
86
88
  'aws4fetch',
87
89
  '@modelcontextprotocol/sdk',
88
- ]), (0, versions_1.withVersions)(['tsx', '@types/ws', '@types/cors']));
90
+ ]), (0, versions_1.withVersions)(['tsx', '@types/ws', '@types/cors', '@types/node']));
89
91
  // NB: we assign the local dev port from 8081 as 8080 is used by vscode server, and so conflicts
90
92
  // for those working on remote dev envirionments. The deployed agent in agentcore still runs on
91
93
  // 8080 as per the agentcore contract.
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/ts/strands-agent/generator.ts"],"names":[],"mappings":";;;;AAAA;;;GAGG;AACH,uCASoB;AAEpB,uCAMwB;AACxB,iDAAsE;AACtE,+CAA0D;AAC1D,6CAA2D;AAC3D,mDAAoD;AACpD,qDAAoD;AACpD,sDAAsE;AACtE,yCAAqD;AACrD,qEAA0E;AAC1E,mGAAwF;AACxF,2CAA8C;AAEjC,QAAA,+BAA+B,GAC1C,IAAA,qBAAgB,EAAC,UAAU,CAAC,CAAC;AAExB,MAAM,uBAAuB,GAAG,CACrC,IAAU,EACV,OAAsC,EACV,EAAE;;IAC9B,MAAM,OAAO,GAAG,IAAA,wCAAmC,EAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAE3E,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAA,0BAAiB,EAAC,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CACb,uBAAuB,OAAO,CAAC,OAAO,wDAAwD,CAC/F,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,IAAA,iBAAS,EAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;IACxE,MAAM,IAAI,GAAG,IAAA,iBAAS,EAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC;IACpD,MAAM,kBAAkB,GAAG,IAAA,mBAAW,EAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,MAAM,oCAAoC,GAAG,IAAA,0BAAiB,EAC5D,KAAK,EACL,iBAAiB,CAClB,CAAC;IACF,MAAM,eAAe,GAAG,IAAA,0BAAiB,EACvC,OAAO,CAAC,IAAI,EACZ,oCAAoC,CACrC,CAAC;IACF,MAAM,iBAAiB,GAAG,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAG,IAAA,0BAAiB,EAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD,MAAM,WAAW,GAAG,MAAA,OAAO,CAAC,WAAW,mCAAI,yBAAyB,CAAC;IAErE,yBAAyB;IACzB,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAC5C,eAAe,EACf;QACE,IAAI;QACJ,kBAAkB;QAClB,OAAO;KACR,EACD,EAAE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY,EAAE,CACtD,CAAC;IAEF,IAAI,WAAW,KAAK,yBAAyB,EAAE,CAAC;QAC9C,MAAM,cAAc,GAAG,GAAG,IAAA,uBAAW,EAAC,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;QAE7D,oBAAoB;QACpB,IAAA,kCAAyB,EAAC,IAAI,EAAE,OAAO,EAAE;YACvC,cAAc,EAAE,GAAG,oCAAoC,WAAW;YAClE,eAAe,EAAE,IAAA,0BAAiB,EAAC,OAAO,EAAE,IAAI,CAAC;SAClD,CAAC,CAAC;QAEH,qBAAqB;QACrB,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,EAC/C,eAAe,EACf;YACE,OAAO;YACP,IAAI;SACL,EACD,EAAE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY,EAAE,CACtD,CAAC;QAEF,MAAM,gBAAgB,GAAG,GAAG,iBAAiB,SAAS,CAAC;QAEvD,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG;YAClC,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,iBAAiB;YAC3B,OAAO,EAAE;gBACP,OAAO,EAAE,0CAA0C,cAAc,IAAI,eAAe,8BAA8B;aACnH;YACD,SAAS,EAAE,CAAC,QAAQ,CAAC;SACtB,CAAC;QAEF,IAAA,sCAAiC,EAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;QACvE,IAAA,sCAAiC,EAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE9D,wBAAwB;QACxB,MAAM,WAAW,GAAG,MAAM,IAAA,wBAAkB,EAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QACxE,MAAM,IAAA,6CAAyB,EAAC,IAAI,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAEvD,IAAA,qCAAa,EAAC,IAAI,EAAE;YAClB,kBAAkB,EAAE,IAAI;YACxB,kBAAkB;YAClB,WAAW,EAAE,OAAO,CAAC,IAAI;YACzB,cAAc;YACd,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;IAED,mBAAmB;IACnB,IAAA,qCAA4B,EAC1B,IAAI,EACJ,IAAA,uBAAY,EAAC;QACX,cAAc;QACd,cAAc;QACd,KAAK;QACL,qBAAqB;QACrB,IAAI;QACJ,MAAM;QACN,+BAA+B;QAC/B,WAAW;QACX,2BAA2B;KAC5B,CAAC,EACF,IAAA,uBAAY,EAAC,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAClD,CAAC;IAEF,gGAAgG;IAChG,+FAA+F;IAC/F,sCAAsC;IACtC,MAAM,YAAY,GAAG,IAAA,iBAAU,EAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAErD,IAAA,mCAA0B,EAAC,IAAI,EAAE,OAAO,CAAC,IAAI,kCACxC,OAAO,KACV,OAAO,kCACF,OAAO,CAAC,OAAO,KAClB,CAAC,GAAG,iBAAiB,QAAQ,CAAC,EAAE;gBAC9B,QAAQ,EAAE,iBAAiB;gBAC3B,OAAO,EAAE;oBACP,QAAQ,EAAE,CAAC,eAAe,iBAAiB,WAAW,CAAC;oBACvD,GAAG,EAAE,eAAe;oBACpB,GAAG,EAAE;wBACH,IAAI,EAAE,GAAG,YAAY,EAAE;qBACxB;iBACF;gBACD,UAAU,EAAE,IAAI;aACjB,OAEH,CAAC;IAEH,IAAA,kCAA6B,EAC3B,IAAI,EACJ,OAAO,CAAC,IAAI,EACZ,uCAA+B,EAC/B,oCAAoC,EACpC,iBAAiB,EACjB,EAAE,IAAI,EAAE,YAAY,EAAE,CACvB,CAAC;IAEF,MAAM,IAAA,yCAA+B,EAAC,IAAI,EAAE;QAC1C,uCAA+B;KAChC,CAAC,CAAC;IAEH,MAAM,IAAA,6BAAoB,EAAC,IAAI,CAAC,CAAC;IACjC,OAAO,GAAG,EAAE;QACV,IAAA,4BAAmB,EAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC;AACJ,CAAC,CAAA,CAAC;AAnJW,QAAA,uBAAuB,2BAmJlC;AAEF,kBAAe,+BAAuB,CAAC"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/ts/strands-agent/generator.ts"],"names":[],"mappings":"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"}
@@ -87,13 +87,44 @@ export * from './runtime-config.js';
87
87
  `;
88
88
 
89
89
  exports[`shared-constructs utils > sharedConstructsGenerator > should generate shared constructs when they do not exist > packages/common/constructs/src/core/runtime-config.ts 1`] = `
90
- "import { Stack, Stage } from 'aws-cdk-lib';
91
- import { Construct } from 'constructs';
90
+ "import {
91
+ ArnFormat,
92
+ Aspects,
93
+ CfnOutput,
94
+ Lazy,
95
+ Names,
96
+ Stack,
97
+ Stage,
98
+ } from 'aws-cdk-lib';
99
+ import {
100
+ CfnApplication,
101
+ CfnConfigurationProfile,
102
+ CfnDeployment,
103
+ CfnDeploymentStrategy,
104
+ CfnEnvironment,
105
+ CfnHostedConfigurationVersion,
106
+ } from 'aws-cdk-lib/aws-appconfig';
107
+ import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
108
+ import { Construct, IConstruct } from 'constructs';
92
109
 
93
110
  const RuntimeConfigKey = '__RuntimeConfig__';
94
111
 
112
+ /**
113
+ * Stage-scoped singleton that collects runtime configuration from CDK constructs
114
+ * and delivers it to server-side (AppConfig) and client-side (S3) consumers.
115
+ *
116
+ * Configuration is organised into namespaces (mapped to AppConfig Configuration Profiles):
117
+ * \`\`\`ts
118
+ * const rc = RuntimeConfig.ensure(this);
119
+ * rc.set('connection', 'cognitoProps', { region: '...', userPoolId: '...' });
120
+ * rc.set('tables', 'users', { tableName: '...', arn: '...' });
121
+ * \`\`\`
122
+ */
95
123
  export class RuntimeConfig extends Construct {
96
- private readonly _runtimeConfig: any = {};
124
+ private readonly _namespaces = new Map<string, Record<string, any>>();
125
+ private _appConfigApplicationId?: string;
126
+ private _appConfigApplicationArn?: string;
127
+ private _aspectRegistered = false;
97
128
 
98
129
  static ensure(scope: Construct): RuntimeConfig {
99
130
  const parent = Stage.of(scope) ?? Stack.of(scope);
@@ -113,8 +144,129 @@ export class RuntimeConfig extends Construct {
113
144
  super(scope, id);
114
145
  }
115
146
 
116
- get config(): any {
117
- return this._runtimeConfig;
147
+ /** Sets a key in the given namespace. Creates the namespace if it doesn't exist. */
148
+ set(namespace: string, key: string, value: any): void {
149
+ let data = this._namespaces.get(namespace);
150
+ if (!data) {
151
+ data = {};
152
+ this._namespaces.set(namespace, data);
153
+ }
154
+ data[key] = value;
155
+ }
156
+
157
+ /** Returns the config data for a namespace. Creates it if it doesn't exist. */
158
+ get(namespace: string): Record<string, any> {
159
+ let data = this._namespaces.get(namespace);
160
+ if (!data) {
161
+ data = {};
162
+ this._namespaces.set(namespace, data);
163
+ }
164
+ return data;
165
+ }
166
+
167
+ /** Returns a lazy token resolving to the AppConfig Application ID. */
168
+ get appConfigApplicationId(): string {
169
+ this.ensureAspect();
170
+ return Lazy.string({
171
+ produce: () => {
172
+ if (!this._appConfigApplicationId) {
173
+ throw new Error(
174
+ 'RuntimeConfig AppConfig resources were not created.',
175
+ );
176
+ }
177
+ return this._appConfigApplicationId;
178
+ },
179
+ });
180
+ }
181
+
182
+ /** Grants a server-side consumer permission to read from AppConfig. */
183
+ grantReadAppConfig(grantee: IGrantable): Grant {
184
+ this.ensureAspect();
185
+ return Grant.addToPrincipal({
186
+ grantee,
187
+ actions: [
188
+ 'appconfig:StartConfigurationSession',
189
+ 'appconfig:GetLatestConfiguration',
190
+ ],
191
+ resourceArns: [
192
+ Lazy.string({ produce: () => this._appConfigApplicationArn }),
193
+ ],
194
+ });
195
+ }
196
+
197
+ private ensureAspect(): void {
198
+ if (this._aspectRegistered) return;
199
+ this._aspectRegistered = true;
200
+ let created = false;
201
+
202
+ Aspects.of(this.node.scope!).add({
203
+ visit: (node: IConstruct) => {
204
+ if (created || !(node instanceof Stack)) return;
205
+ created = true;
206
+
207
+ const stack = node;
208
+ const name = Names.uniqueResourceName(this, {
209
+ maxLength: 64,
210
+ separator: '-',
211
+ });
212
+
213
+ const app = new CfnApplication(stack, 'RcAppConfigApp', { name });
214
+ const strategy = new CfnDeploymentStrategy(
215
+ stack,
216
+ 'RcAppConfigStrategy',
217
+ {
218
+ name,
219
+ deploymentDurationInMinutes: 0,
220
+ growthFactor: 100,
221
+ replicateTo: 'NONE',
222
+ finalBakeTimeInMinutes: 0,
223
+ },
224
+ );
225
+ const env = new CfnEnvironment(stack, 'RcAppConfigEnv', {
226
+ applicationId: app.ref,
227
+ name: 'default',
228
+ });
229
+
230
+ for (const [ns, data] of this._namespaces.entries()) {
231
+ const profile = new CfnConfigurationProfile(
232
+ stack,
233
+ \`RcAppConfigProfile\${ns}\`,
234
+ {
235
+ applicationId: app.ref,
236
+ name: ns,
237
+ locationUri: 'hosted',
238
+ type: 'AWS.Freeform',
239
+ },
240
+ );
241
+ const version = new CfnHostedConfigurationVersion(
242
+ stack,
243
+ \`RcAppConfigVersion\${ns}\`,
244
+ {
245
+ applicationId: app.ref,
246
+ configurationProfileId: profile.ref,
247
+ contentType: 'application/json',
248
+ content: Lazy.string({ produce: () => stack.toJsonString(data) }),
249
+ },
250
+ );
251
+ new CfnDeployment(stack, \`RcAppConfigDeploy\${ns}\`, {
252
+ applicationId: app.ref,
253
+ environmentId: env.ref,
254
+ configurationProfileId: profile.ref,
255
+ configurationVersion: version.ref,
256
+ deploymentStrategyId: strategy.ref,
257
+ });
258
+ }
259
+
260
+ new CfnOutput(stack, 'RuntimeConfigApplicationId', { value: app.ref });
261
+ this._appConfigApplicationId = app.ref;
262
+ this._appConfigApplicationArn = stack.formatArn({
263
+ service: 'appconfig',
264
+ resource: 'application',
265
+ resourceName: \`\${app.ref}/*\`,
266
+ arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
267
+ });
268
+ },
269
+ });
118
270
  }
119
271
  }
120
272
  "
@@ -10,6 +10,7 @@ import {
10
10
  Runtime,
11
11
  RuntimeProps,
12
12
  } from '@aws-cdk/aws-bedrock-agentcore-alpha';
13
+ import { RuntimeConfig } from '../../../core/runtime-config.js';
13
14
 
14
15
  export type <%- nameClassName %>Props = Omit<
15
16
  RuntimeProps,
@@ -23,6 +24,8 @@ export class <%- nameClassName %> extends Construct {
23
24
  constructor(scope: Construct, id: string, props?: <%- nameClassName %>Props) {
24
25
  super(scope, id);
25
26
 
27
+ const rc = RuntimeConfig.ensure(this);
28
+
26
29
  this.dockerImage = AgentRuntimeArtifact.fromAsset(
27
30
  path.dirname(url.fileURLToPath(new URL(import.meta.url))),
28
31
  {
@@ -42,6 +45,17 @@ export class <%- nameClassName %> extends Construct {
42
45
  protocolConfiguration: ProtocolType.<%- serverProtocol %>,
43
46
  agentRuntimeArtifact: this.dockerImage,
44
47
  ...props,
48
+ environmentVariables: {
49
+ RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
50
+ ...props?.environmentVariables,
51
+ },
52
+ });
53
+
54
+ rc.grantReadAppConfig(this.agentCoreRuntime);
55
+
56
+ rc.set('connection', 'agentRuntimes', {
57
+ ...rc.get('connection').agentRuntimes,
58
+ <%- nameClassName %>: this.agentCoreRuntime.agentRuntimeArn,
45
59
  });
46
60
  }
47
61
  }
@@ -20,6 +20,12 @@ variable "tags" {
20
20
  default = {}
21
21
  }
22
22
 
23
+ module "appconfig" {
24
+ source = "../../../core/runtime-config/appconfig"
25
+
26
+ application_name = "<%= nameClassName %>-runtime-config"
27
+ }
28
+
23
29
  module "agent_core_runtime" {
24
30
  source = "../../../core/agent-core"
25
31
  agent_runtime_name = "<%= nameClassName %>"
@@ -32,9 +38,33 @@ module "agent_core_runtime" {
32
38
  # }
33
39
  # }
34
40
 
35
- env = var.env
36
- additional_iam_policy_statements = var.additional_iam_policy_statements
41
+ env = merge({
42
+ RUNTIME_CONFIG_APP_ID = module.appconfig.application_id
43
+ }, var.env)
44
+ additional_iam_policy_statements = concat([
45
+ {
46
+ Effect = "Allow"
47
+ Action = [
48
+ "appconfig:StartConfigurationSession",
49
+ "appconfig:GetLatestConfiguration"
50
+ ]
51
+ Resource = ["${module.appconfig.application_arn}/*"]
52
+ }
53
+ ], var.additional_iam_policy_statements)
37
54
  tags = var.tags
55
+
56
+ depends_on = [module.appconfig]
57
+ }
58
+
59
+ # Add agent runtime ARN to runtime config
60
+ module "add_agent_runtime_to_runtime_config" {
61
+ source = "../../../core/runtime-config/entry"
62
+
63
+ namespace = "connection"
64
+ key = "agentRuntimes"
65
+ value = { "<%= nameClassName %>" = module.agent_core_runtime.agent_core_runtime_arn }
66
+
67
+ depends_on = [module.agent_core_runtime]
38
68
  }
39
69
 
40
70
  output "agent_core_runtime_role_arn" {
@@ -46,3 +76,8 @@ output "agent_core_runtime_arn" {
46
76
  description = "ARN of the Bedrock Agent Core runtime"
47
77
  value = module.agent_core_runtime.agent_core_runtime_arn
48
78
  }
79
+
80
+ output "appconfig_application_id" {
81
+ description = "AppConfig Application ID for runtime config"
82
+ value = module.appconfig.application_id
83
+ }
@@ -331,7 +331,7 @@ resource "aws_bedrockagentcore_agent_runtime" "agent_runtime" {
331
331
 
332
332
  depends_on = [
333
333
  null_resource.docker_publish,
334
- aws_iam_role_policy.agent_core_runtime_policy
334
+ aws_iam_role_policy_attachment.agent_core_policy
335
335
  ]
336
336
  }
337
337
 
@@ -13,6 +13,7 @@ import {
13
13
  <%_ } _%>
14
14
  } from 'aws-cdk-lib/aws-lambda';
15
15
  import { Duration<%_ if (backend.type === 'fastapi') { _%>, Stack<%_ } _%> } from 'aws-cdk-lib';
16
+ import { RuntimeConfig } from '../../core/runtime-config.js';
16
17
  import {
17
18
  CorsHttpMethod,
18
19
  CfnApi,
@@ -99,6 +100,7 @@ export class <%= apiNameClassName %><
99
100
  * @returns An IntegrationBuilder with default lambda integrations
100
101
  */
101
102
  public static defaultIntegrations = (scope: Construct) => {
103
+ const rc = RuntimeConfig.ensure(scope);
102
104
  return IntegrationBuilder.http({
103
105
  pattern: '<%= backend.integrationPattern %>',
104
106
  <%_ if (backend.type === 'trpc') { _%>
@@ -137,6 +139,7 @@ export class <%= apiNameClassName %><
137
139
  <%_ } _%>
138
140
  environment: {
139
141
  AWS_CONNECTION_REUSE_ENABLED: '1',
142
+ RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
140
143
  <%_ if (backend.type === 'fastapi') { _%>
141
144
  PORT: '8000',
142
145
  AWS_LWA_INVOKE_MODE: 'buffered',
@@ -148,6 +151,7 @@ export class <%= apiNameClassName %><
148
151
  <%_ const lambdaTarget = backend.type === 'fastapi' ? 'handler.currentVersion' : 'handler'; _%>
149
152
  <%_ const integrationOptions = backend.integrationPattern === 'shared' ? ', { scopePermissionToRoute: false }' : ''; _%>
150
153
  const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
154
+ rc.grantReadAppConfig(handler);
151
155
  <%_ if (backend.type === 'fastapi') { _%>
152
156
  const stack = Stack.of(scope);
153
157
  handler.addLayers(
@@ -206,23 +210,25 @@ export class <%= apiNameClassName %><
206
210
  }
207
211
 
208
212
  /**
209
- * Restricts CORS to the website CloudFront distribution domains
213
+ * Restricts CORS to the provided origins
210
214
  *
211
- * Configures the CloudFront distribution domains as the only permitted CORS origins
212
- * (other than local host with default ports) in the API gateway
213
- * The CORS origins are not configured within the AWS Lambda integrations since
214
- * the associated header is controlled by API Gateway v2
215
+ * Configures the provided CloudFront distribution domains or origin strings
216
+ * as the only permitted CORS origins
217
+ * in the API gateway. The CORS origins are not configured within the AWS Lambda
218
+ * integrations since the associated header is controlled by API Gateway v2.
215
219
  *
216
- * @param cloudFrontDistribution - The CloudFront distribution to grant CORS from
220
+ * @param origins - The origin strings, CloudFront distributions, or objects containing a CloudFront distribution to grant CORS from
217
221
  */
218
222
  public restrictCorsTo(
219
- ...websites: { cloudFrontDistribution: Distribution }[]
223
+ ...origins: (string | Distribution | { cloudFrontDistribution: Distribution })[]
220
224
  ) {
221
- const allowedOrigins = websites
222
- .map(
223
- ({ cloudFrontDistribution }) =>
224
- `https://${cloudFrontDistribution.distributionDomainName}`,
225
- );
225
+ const allowedOrigins = origins.map((origin) =>
226
+ typeof origin === 'string'
227
+ ? origin
228
+ : 'cloudFrontDistribution' in origin
229
+ ? `https://${origin.cloudFrontDistribution.distributionDomainName}`
230
+ : `https://${origin.distributionDomainName}`,
231
+ );
226
232
 
227
233
  const cfnApi = this.api.node.defaultChild;
228
234
  if (!(cfnApi instanceof CfnApi)) {
@@ -232,11 +238,7 @@ export class <%= apiNameClassName %><
232
238
  }
233
239
 
234
240
  cfnApi.corsConfiguration = {
235
- allowOrigins: [
236
- 'http://localhost:4200',
237
- 'http://localhost:4300',
238
- ...allowedOrigins,
239
- ],
241
+ allowOrigins: allowedOrigins,
240
242
  allowMethods: [CorsHttpMethod.ANY],
241
243
  allowHeaders: [
242
244
  'authorization',
@@ -12,9 +12,9 @@ import {
12
12
  SnapStartConf,
13
13
  <%_ } _%>
14
14
  } from 'aws-cdk-lib/aws-lambda';
15
+ import { RuntimeConfig } from '../../core/runtime-config.js';
15
16
  import {
16
17
  AuthorizationType,
17
- Cors,
18
18
  LambdaIntegration,
19
19
  <%_ if (['trpc', 'fastapi'].includes(backend.type)) { _%>
20
20
  ResponseTransferMode,
@@ -23,7 +23,7 @@ import {
23
23
  CognitoUserPoolsAuthorizer,
24
24
  <%_ } _%>
25
25
  } from 'aws-cdk-lib/aws-apigateway';
26
- import { Duration<%_ if (backend.type === 'fastapi') { _%>, Stack<%_ } _%> } from 'aws-cdk-lib';
26
+ import { Aspects, Duration<%_ if (backend.type === 'fastapi') { _%>, Stack<%_ } _%> } from 'aws-cdk-lib';
27
27
  import {
28
28
  PolicyDocument,
29
29
  PolicyStatement,
@@ -42,7 +42,7 @@ import {
42
42
  IntegrationBuilder,
43
43
  RestApiIntegration,
44
44
  } from '../../core/api/utils.js';
45
- import { RestApi } from '../../core/api/rest-api.js';
45
+ import { AddCorsPreflightAspect, RestApi } from '../../core/api/rest-api.js';
46
46
  <%_ if (backend.type === 'trpc') { _%>
47
47
  import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
48
48
  import { AppRouter, appRouter } from '<%= backend.projectAlias %>';
@@ -86,6 +86,8 @@ export interface <%= apiNameClassName %>Props<
86
86
  export class <%= apiNameClassName %><
87
87
  TIntegrations extends ApiIntegrations<Operations, RestApiIntegration>,
88
88
  > extends RestApi<Operations, TIntegrations> {
89
+ private allowedOrigins: readonly string[] = ['*'];
90
+
89
91
  /**
90
92
  <%_ if (backend.integrationPattern === 'shared') { _%>
91
93
  * Creates default integrations for all operations using a single shared
@@ -99,6 +101,7 @@ export class <%= apiNameClassName %><
99
101
  * @returns An IntegrationBuilder with default lambda integrations
100
102
  */
101
103
  public static defaultIntegrations = (scope: Construct) => {
104
+ const rc = RuntimeConfig.ensure(scope);
102
105
  return IntegrationBuilder.rest({
103
106
  pattern: '<%= backend.integrationPattern %>',
104
107
  <%_ if (backend.type === 'trpc') { _%>
@@ -137,6 +140,7 @@ export class <%= apiNameClassName %><
137
140
  <%_ } _%>
138
141
  environment: {
139
142
  AWS_CONNECTION_REUSE_ENABLED: '1',
143
+ RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
140
144
  <%_ if (backend.type === 'fastapi') { _%>
141
145
  PORT: '8000',
142
146
  AWS_LWA_INVOKE_MODE: 'response_stream',
@@ -161,6 +165,7 @@ export class <%= apiNameClassName %><
161
165
  : '';
162
166
  _%>
163
167
  const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
168
+ rc.grantReadAppConfig(handler);
164
169
  <%_ if (backend.type === 'fastapi') { _%>
165
170
  const stack = Stack.of(scope);
166
171
  handler.addLayers(
@@ -198,10 +203,6 @@ export class <%= apiNameClassName %><
198
203
  authorizationType: AuthorizationType.NONE,
199
204
  <%_ } _%>
200
205
  },
201
- defaultCorsPreflightOptions: {
202
- allowOrigins: Cors.ALL_ORIGINS,
203
- allowMethods: Cors.ALL_METHODS,
204
- },
205
206
  deployOptions: {
206
207
  tracingEnabled: true,
207
208
  },
@@ -233,32 +234,38 @@ export class <%= apiNameClassName %><
233
234
  <%_ } _%>
234
235
  ...props,
235
236
  });
237
+ Aspects.of(this).add(new AddCorsPreflightAspect(() => this.allowedOrigins));
236
238
  }
237
239
 
238
240
  /**
239
- * Restricts CORS to the website CloudFront distribution domains
241
+ * Restricts CORS to the provided origins
240
242
  *
241
- * Configures the CloudFront distribution domains as the only permitted CORS origins
242
- * (other than local host) in the AWS Lambda integrations
243
- *
244
- * Note that this restriction is not applied to preflight OPTIONS
243
+ * Configures the provided CloudFront distribution domains or origin strings
244
+ * as the only permitted CORS origins in API Gateway preflight responses and the
245
+ * AWS Lambda integrations.
245
246
  *
246
- * @param websites - The CloudFront distribution to grant CORS from
247
+ * @param origins - The origin strings, CloudFront distributions, or objects containing a CloudFront distribution to grant CORS from
247
248
  */
248
249
  public restrictCorsTo(
249
- ...websites: { cloudFrontDistribution: Distribution }[]
250
+ ...origins: (string | Distribution | { cloudFrontDistribution: Distribution })[]
250
251
  ) {
251
- const allowedOrigins = websites
252
- .map(
253
- ({ cloudFrontDistribution }) =>
254
- `https://${cloudFrontDistribution.distributionDomainName}`,
255
- )
256
- .join(',');
252
+ const allowedOrigins = origins.map((origin) =>
253
+ typeof origin === 'string'
254
+ ? origin
255
+ : 'cloudFrontDistribution' in origin
256
+ ? `https://${origin.cloudFrontDistribution.distributionDomainName}`
257
+ : `https://${origin.distributionDomainName}`,
258
+ );
259
+
260
+ this.allowedOrigins = allowedOrigins;
257
261
 
258
262
  // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
259
263
  Object.values(this.integrations).forEach((integration) => {
260
264
  if ('handler' in integration && integration.handler instanceof Function) {
261
- integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
265
+ integration.handler.addEnvironment(
266
+ 'ALLOWED_ORIGINS',
267
+ allowedOrigins.join(','),
268
+ );
262
269
  }
263
270
  });
264
271
  }
@@ -138,10 +138,11 @@ export class HttpApi<
138
138
  });
139
139
 
140
140
  // Register the API URL in runtime configuration for client discovery
141
- RuntimeConfig.ensure(this).config.apis = {
142
- ...RuntimeConfig.ensure(this).config.apis!,
141
+ const rc = RuntimeConfig.ensure(this);
142
+ rc.set('connection', 'apis', {
143
+ ...rc.get('connection').apis,
143
144
  [apiName]: this.defaultStage.url!,
144
- };
145
+ });
145
146
  }
146
147
 
147
148
  /**