serverless-bedrock-agentcore-plugin 0.1.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.
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ const { getResourceName, getLogicalId } = require('../utils/naming');
4
+
5
+ /**
6
+ * Build network configuration for BrowserCustom
7
+ *
8
+ * @param {Object} network - The network configuration from serverless.yml
9
+ * @returns {Object} CloudFormation NetworkConfiguration
10
+ */
11
+ function buildBrowserNetworkConfiguration(network = {}) {
12
+ const networkMode = network.networkMode || 'PUBLIC';
13
+
14
+ const config = {
15
+ NetworkMode: networkMode,
16
+ };
17
+
18
+ if (networkMode === 'VPC' && network.vpcConfig) {
19
+ config.VpcConfig = {
20
+ ...(network.vpcConfig.subnetIds && { SubnetIds: network.vpcConfig.subnetIds }),
21
+ ...(network.vpcConfig.securityGroupIds && {
22
+ SecurityGroupIds: network.vpcConfig.securityGroupIds,
23
+ }),
24
+ };
25
+ }
26
+
27
+ return config;
28
+ }
29
+
30
+ /**
31
+ * Build recording configuration for BrowserCustom
32
+ *
33
+ * @param {Object} recording - The recording configuration from serverless.yml
34
+ * @returns {Object|null} CloudFormation RecordingConfig or null
35
+ */
36
+ function buildRecordingConfig(recording) {
37
+ if (!recording) {
38
+ return null;
39
+ }
40
+
41
+ const config = {};
42
+
43
+ if (recording.enabled !== undefined) {
44
+ config.Enabled = recording.enabled;
45
+ }
46
+
47
+ if (recording.s3Location) {
48
+ config.S3Location = {
49
+ Bucket: recording.s3Location.bucket,
50
+ ...(recording.s3Location.prefix && { Prefix: recording.s3Location.prefix }),
51
+ };
52
+ }
53
+
54
+ return Object.keys(config).length > 0 ? config : null;
55
+ }
56
+
57
+ /**
58
+ * Build browser signing configuration
59
+ *
60
+ * @param {Object} signing - The signing configuration from serverless.yml
61
+ * @returns {Object|null} CloudFormation BrowserSigning or null
62
+ */
63
+ function buildBrowserSigning(signing) {
64
+ if (!signing) {
65
+ return null;
66
+ }
67
+
68
+ return { Enabled: signing.enabled || false };
69
+ }
70
+
71
+ /**
72
+ * Compile a BrowserCustom resource to CloudFormation
73
+ *
74
+ * @param {string} name - The browser name
75
+ * @param {Object} config - The browser configuration
76
+ * @param {Object} context - The compilation context
77
+ * @param {Object} tags - The merged tags
78
+ * @returns {Object} CloudFormation resource definition
79
+ */
80
+ function compileBrowser(name, config, context, tags) {
81
+ const { serviceName, stage } = context;
82
+ const resourceName = getResourceName(serviceName, name, stage);
83
+ const roleLogicalId = `${getLogicalId(name, 'Browser')}Role`;
84
+
85
+ const networkConfig = buildBrowserNetworkConfiguration(config.network);
86
+ const recordingConfig = buildRecordingConfig(config.recording);
87
+ const signingConfig = buildBrowserSigning(config.signing);
88
+
89
+ return {
90
+ Type: 'AWS::BedrockAgentCore::BrowserCustom',
91
+ Properties: {
92
+ Name: resourceName,
93
+ NetworkConfiguration: networkConfig,
94
+ ...(config.roleArn
95
+ ? { ExecutionRoleArn: config.roleArn }
96
+ : { ExecutionRoleArn: { 'Fn::GetAtt': [roleLogicalId, 'Arn'] } }),
97
+ ...(config.description && { Description: config.description }),
98
+ ...(signingConfig && { BrowserSigning: signingConfig }),
99
+ ...(recordingConfig && { RecordingConfig: recordingConfig }),
100
+ ...(Object.keys(tags).length > 0 && { Tags: tags }),
101
+ },
102
+ };
103
+ }
104
+
105
+ module.exports = {
106
+ compileBrowser,
107
+ buildBrowserNetworkConfiguration,
108
+ buildRecordingConfig,
109
+ buildBrowserSigning,
110
+ };
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ const { getResourceName, getLogicalId } = require('../utils/naming');
4
+
5
+ /**
6
+ * Build network configuration for CodeInterpreterCustom
7
+ * Supports PUBLIC, SANDBOX, and VPC modes
8
+ *
9
+ * @param {Object} network - The network configuration from serverless.yml
10
+ * @returns {Object} CloudFormation NetworkConfiguration
11
+ */
12
+ function buildCodeInterpreterNetworkConfiguration(network = {}) {
13
+ const networkMode = network.networkMode || 'SANDBOX';
14
+
15
+ const config = {
16
+ NetworkMode: networkMode, // PUBLIC, SANDBOX, or VPC
17
+ };
18
+
19
+ if (networkMode === 'VPC' && network.vpcConfig) {
20
+ config.VpcConfig = {
21
+ ...(network.vpcConfig.subnetIds && { SubnetIds: network.vpcConfig.subnetIds }),
22
+ ...(network.vpcConfig.securityGroupIds && {
23
+ SecurityGroupIds: network.vpcConfig.securityGroupIds,
24
+ }),
25
+ };
26
+ }
27
+
28
+ return config;
29
+ }
30
+
31
+ /**
32
+ * Compile a CodeInterpreterCustom resource to CloudFormation
33
+ *
34
+ * @param {string} name - The code interpreter name
35
+ * @param {Object} config - The code interpreter configuration
36
+ * @param {Object} context - The compilation context
37
+ * @param {Object} tags - The merged tags
38
+ * @returns {Object} CloudFormation resource definition
39
+ */
40
+ function compileCodeInterpreter(name, config, context, tags) {
41
+ const { serviceName, stage } = context;
42
+ const resourceName = getResourceName(serviceName, name, stage);
43
+ const roleLogicalId = `${getLogicalId(name, 'CodeInterpreter')}Role`;
44
+
45
+ const networkConfig = buildCodeInterpreterNetworkConfiguration(config.network);
46
+
47
+ return {
48
+ Type: 'AWS::BedrockAgentCore::CodeInterpreterCustom',
49
+ Properties: {
50
+ Name: resourceName,
51
+ NetworkConfiguration: networkConfig,
52
+ ...(config.roleArn
53
+ ? { ExecutionRoleArn: config.roleArn }
54
+ : { ExecutionRoleArn: { 'Fn::GetAtt': [roleLogicalId, 'Arn'] } }),
55
+ ...(config.description && { Description: config.description }),
56
+ ...(Object.keys(tags).length > 0 && { Tags: tags }),
57
+ },
58
+ };
59
+ }
60
+
61
+ module.exports = {
62
+ compileCodeInterpreter,
63
+ buildCodeInterpreterNetworkConfiguration,
64
+ };
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ const { getLogicalId } = require('../utils/naming');
4
+
5
+ /**
6
+ * Build authorizer configuration for the gateway
7
+ *
8
+ * @param {Object} authConfig - The authorizer configuration from serverless.yml
9
+ * @returns {Object|null} CloudFormation authorizer configuration or null
10
+ */
11
+ function buildGatewayAuthorizerConfiguration(authConfig) {
12
+ if (!authConfig) {
13
+ return null;
14
+ }
15
+
16
+ return {
17
+ ...(authConfig.allowedAudiences && { AllowedAudiences: authConfig.allowedAudiences }),
18
+ ...(authConfig.allowedClients && { AllowedClients: authConfig.allowedClients }),
19
+ ...(authConfig.allowedIssuers && { AllowedIssuers: authConfig.allowedIssuers }),
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Build protocol configuration for the gateway
25
+ *
26
+ * @param {Object} protocolConfig - The protocol configuration from serverless.yml
27
+ * @returns {Object|null} CloudFormation protocol configuration or null
28
+ */
29
+ function buildGatewayProtocolConfiguration(protocolConfig) {
30
+ if (!protocolConfig) {
31
+ return null;
32
+ }
33
+
34
+ return {
35
+ ...(protocolConfig.mcpConfiguration && {
36
+ McpConfiguration: {
37
+ ...(protocolConfig.mcpConfiguration.instructions && {
38
+ Instructions: protocolConfig.mcpConfiguration.instructions,
39
+ }),
40
+ ...(protocolConfig.mcpConfiguration.supportedVersions && {
41
+ SupportedVersions: protocolConfig.mcpConfiguration.supportedVersions,
42
+ }),
43
+ },
44
+ }),
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Compile a Gateway resource to CloudFormation
50
+ *
51
+ * @param {string} name - The agent name
52
+ * @param {Object} config - The gateway configuration
53
+ * @param {Object} context - The compilation context
54
+ * @param {Object} tags - The merged tags
55
+ * @returns {Object} CloudFormation resource definition
56
+ */
57
+ function compileGateway(name, config, context, tags) {
58
+ const { serviceName, stage } = context;
59
+
60
+ // Gateway name pattern: ^([0-9a-zA-Z][-]?){1,100}$
61
+ const resourceName = `${serviceName}-${name}-${stage}`
62
+ .replace(/[^0-9a-zA-Z-]/g, '-')
63
+ .replace(/--+/g, '-')
64
+ .substring(0, 100);
65
+
66
+ const roleLogicalId = `${getLogicalId(name, 'Gateway')}Role`;
67
+
68
+ // Default to AWS_IAM if not specified
69
+ const authorizerType = config.authorizerType || 'AWS_IAM';
70
+
71
+ // Default to MCP if not specified
72
+ const protocolType = config.protocolType || 'MCP';
73
+
74
+ const authConfig = buildGatewayAuthorizerConfiguration(config.authorizerConfiguration);
75
+ const protocolConfig = buildGatewayProtocolConfiguration(config.protocolConfiguration);
76
+
77
+ return {
78
+ Type: 'AWS::BedrockAgentCore::Gateway',
79
+ Properties: {
80
+ Name: resourceName,
81
+ AuthorizerType: authorizerType,
82
+ ProtocolType: protocolType,
83
+ RoleArn: config.roleArn || { 'Fn::GetAtt': [roleLogicalId, 'Arn'] },
84
+ ...(config.description && { Description: config.description }),
85
+ ...(authConfig && { AuthorizerConfiguration: authConfig }),
86
+ ...(protocolConfig && { ProtocolConfiguration: protocolConfig }),
87
+ ...(config.kmsKeyArn && { KmsKeyArn: config.kmsKeyArn }),
88
+ ...(config.exceptionLevel && { ExceptionLevel: config.exceptionLevel }),
89
+ ...(Object.keys(tags).length > 0 && { Tags: tags }),
90
+ },
91
+ };
92
+ }
93
+
94
+ module.exports = {
95
+ compileGateway,
96
+ buildGatewayAuthorizerConfiguration,
97
+ buildGatewayProtocolConfiguration,
98
+ };
@@ -0,0 +1,285 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Build credential provider configuration for the gateway target
5
+ *
6
+ * @param {Object} credProvider - The credential provider configuration
7
+ * @returns {Array} CloudFormation credential provider configurations array
8
+ */
9
+ function buildCredentialProviderConfigurations(credProvider) {
10
+ if (!credProvider) {
11
+ // Default to GATEWAY_IAM_ROLE
12
+ return [
13
+ {
14
+ CredentialProviderType: 'GATEWAY_IAM_ROLE',
15
+ },
16
+ ];
17
+ }
18
+
19
+ const config = {
20
+ CredentialProviderType: credProvider.type || 'GATEWAY_IAM_ROLE',
21
+ };
22
+
23
+ // Add OAuth configuration
24
+ if (credProvider.type === 'OAUTH' && credProvider.oauthConfig) {
25
+ config.OauthCredentialProviderConfiguration = {
26
+ ...(credProvider.oauthConfig.secretArn && {
27
+ CredentialsSecretArn: credProvider.oauthConfig.secretArn,
28
+ }),
29
+ ...(credProvider.oauthConfig.tokenUrl && {
30
+ OauthTokenEndpoint: credProvider.oauthConfig.tokenUrl,
31
+ }),
32
+ ...(credProvider.oauthConfig.scopes && { Scopes: credProvider.oauthConfig.scopes }),
33
+ };
34
+ }
35
+
36
+ // Add API Key configuration
37
+ if (credProvider.type === 'API_KEY' && credProvider.apiKeyConfig) {
38
+ config.ApiKeyCredentialProviderConfiguration = {
39
+ ...(credProvider.apiKeyConfig.secretArn && {
40
+ ApiKeySecretArn: credProvider.apiKeyConfig.secretArn,
41
+ }),
42
+ };
43
+ }
44
+
45
+ return [config];
46
+ }
47
+
48
+ /**
49
+ * Build target configuration based on target type
50
+ *
51
+ * @param {Object} target - The target configuration from serverless.yml
52
+ * @param {Object} context - The compilation context
53
+ * @returns {Object} CloudFormation target configuration
54
+ */
55
+ function buildTargetConfiguration(target, context) {
56
+ const targetType = target.type || 'lambda';
57
+
58
+ switch (targetType.toLowerCase()) {
59
+ case 'lambda':
60
+ return buildLambdaTargetConfiguration(target, context);
61
+ case 'openapi':
62
+ return buildOpenApiTargetConfiguration(target);
63
+ case 'smithy':
64
+ return buildSmithyTargetConfiguration(target);
65
+ default:
66
+ throw new Error(`Unknown gateway target type: ${targetType}`);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Transform JSON Schema to CloudFormation SchemaDefinition (PascalCase)
72
+ *
73
+ * @param {Object} schema - The JSON schema object
74
+ * @returns {Object} CloudFormation SchemaDefinition
75
+ */
76
+ function transformSchemaToCloudFormation(schema) {
77
+ if (!schema || typeof schema !== 'object') {
78
+ return schema;
79
+ }
80
+
81
+ const cfSchema = {};
82
+
83
+ // Transform type (required)
84
+ if (schema.type || schema.Type) {
85
+ cfSchema.Type = schema.Type || schema.type;
86
+ }
87
+
88
+ // Transform description (optional)
89
+ if (schema.description || schema.Description) {
90
+ cfSchema.Description = schema.Description || schema.description;
91
+ }
92
+
93
+ // Transform properties (optional, for object types)
94
+ if (schema.properties || schema.Properties) {
95
+ const props = schema.Properties || schema.properties;
96
+ cfSchema.Properties = {};
97
+ for (const [key, value] of Object.entries(props)) {
98
+ cfSchema.Properties[key] = transformSchemaToCloudFormation(value);
99
+ }
100
+ }
101
+
102
+ // Transform required (optional, for object types)
103
+ if (schema.required || schema.Required) {
104
+ cfSchema.Required = schema.Required || schema.required;
105
+ }
106
+
107
+ // Transform items (optional, for array types)
108
+ if (schema.items || schema.Items) {
109
+ cfSchema.Items = transformSchemaToCloudFormation(schema.Items || schema.items);
110
+ }
111
+
112
+ // Transform enum (optional)
113
+ if (schema.enum || schema.Enum) {
114
+ cfSchema.Enum = schema.Enum || schema.enum;
115
+ }
116
+
117
+ return cfSchema;
118
+ }
119
+
120
+ /**
121
+ * Build Lambda target configuration
122
+ *
123
+ * @param {Object} target - The target configuration
124
+ * @param {Object} context - The compilation context
125
+ * @returns {Object} CloudFormation Lambda target configuration
126
+ */
127
+ function buildLambdaTargetConfiguration(target, _context) {
128
+ let functionArn = target.functionArn;
129
+
130
+ // If functionName is provided instead of functionArn, build the ARN reference
131
+ if (target.functionName && !functionArn) {
132
+ // Reference the function from the same stack
133
+ const functionLogicalId =
134
+ target.functionName
135
+ .split(/[-_]/)
136
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
137
+ .join('') + 'LambdaFunction';
138
+
139
+ functionArn = { 'Fn::GetAtt': [functionLogicalId, 'Arn'] };
140
+ }
141
+
142
+ const lambdaConfig = {
143
+ LambdaArn: functionArn,
144
+ };
145
+
146
+ // Add ToolSchema if provided
147
+ if (target.toolSchema) {
148
+ const toolSchema = {};
149
+
150
+ // Handle InlinePayload (CloudFormation uses PascalCase)
151
+ if (target.toolSchema.inlinePayload) {
152
+ toolSchema.InlinePayload = target.toolSchema.inlinePayload.map((tool) => ({
153
+ Name: tool.name || tool.Name,
154
+ Description: tool.description || tool.Description,
155
+ InputSchema: transformSchemaToCloudFormation(tool.inputSchema || tool.InputSchema),
156
+ ...(tool.outputSchema || tool.OutputSchema
157
+ ? {
158
+ OutputSchema: transformSchemaToCloudFormation(tool.outputSchema || tool.OutputSchema),
159
+ }
160
+ : {}),
161
+ }));
162
+ }
163
+
164
+ // Handle S3 location (CloudFormation uses PascalCase)
165
+ if (target.toolSchema.s3) {
166
+ toolSchema.S3 = {
167
+ Uri:
168
+ target.toolSchema.s3.uri ||
169
+ `s3://${target.toolSchema.s3.bucket}/${target.toolSchema.s3.key}`,
170
+ ...(target.toolSchema.s3.bucketOwnerAccountId && {
171
+ BucketOwnerAccountId: target.toolSchema.s3.bucketOwnerAccountId,
172
+ }),
173
+ };
174
+ }
175
+
176
+ lambdaConfig.ToolSchema = toolSchema;
177
+ }
178
+
179
+ return {
180
+ Mcp: {
181
+ Lambda: lambdaConfig,
182
+ },
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Build OpenAPI target configuration
188
+ *
189
+ * @param {Object} target - The target configuration
190
+ * @returns {Object} CloudFormation OpenAPI target configuration
191
+ */
192
+ function buildOpenApiTargetConfiguration(target) {
193
+ const openApiConfig = {};
194
+
195
+ if (target.s3) {
196
+ openApiConfig.S3 = {
197
+ Uri: target.s3.uri || `s3://${target.s3.bucket}/${target.s3.key}`,
198
+ ...(target.s3.bucketOwnerAccountId && {
199
+ BucketOwnerAccountId: target.s3.bucketOwnerAccountId,
200
+ }),
201
+ };
202
+ }
203
+
204
+ if (target.inline || target.inlinePayload) {
205
+ openApiConfig.InlinePayload = target.inline || target.inlinePayload;
206
+ }
207
+
208
+ return {
209
+ Mcp: {
210
+ OpenApiSchema: openApiConfig,
211
+ },
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Build Smithy target configuration
217
+ *
218
+ * @param {Object} target - The target configuration
219
+ * @returns {Object} CloudFormation Smithy target configuration
220
+ */
221
+ function buildSmithyTargetConfiguration(target) {
222
+ const smithyConfig = {};
223
+
224
+ if (target.s3) {
225
+ smithyConfig.S3 = {
226
+ Uri: target.s3.uri || `s3://${target.s3.bucket}/${target.s3.key}`,
227
+ ...(target.s3.bucketOwnerAccountId && {
228
+ BucketOwnerAccountId: target.s3.bucketOwnerAccountId,
229
+ }),
230
+ };
231
+ }
232
+
233
+ if (target.inline || target.inlinePayload) {
234
+ smithyConfig.InlinePayload = target.inline || target.inlinePayload;
235
+ }
236
+
237
+ return {
238
+ Mcp: {
239
+ SmithyModel: smithyConfig,
240
+ },
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Compile a GatewayTarget resource to CloudFormation
246
+ *
247
+ * @param {string} gatewayName - The parent gateway name
248
+ * @param {string} targetName - The target name
249
+ * @param {Object} config - The target configuration
250
+ * @param {string} gatewayLogicalId - The logical ID of the parent Gateway resource
251
+ * @param {Object} context - The compilation context
252
+ * @returns {Object} CloudFormation resource definition
253
+ */
254
+ function compileGatewayTarget(gatewayName, targetName, config, gatewayLogicalId, context) {
255
+ // Target name pattern: ^([0-9a-zA-Z][-]?){1,100}$
256
+ const resourceName = targetName
257
+ .replace(/[^0-9a-zA-Z-]/g, '-')
258
+ .replace(/--+/g, '-')
259
+ .substring(0, 100);
260
+
261
+ const credentialConfigs = buildCredentialProviderConfigurations(config.credentialProvider);
262
+ const targetConfig = buildTargetConfiguration(config, context);
263
+
264
+ return {
265
+ Type: 'AWS::BedrockAgentCore::GatewayTarget',
266
+ DependsOn: [gatewayLogicalId],
267
+ Properties: {
268
+ Name: resourceName,
269
+ GatewayIdentifier: { 'Fn::GetAtt': [gatewayLogicalId, 'GatewayIdentifier'] },
270
+ CredentialProviderConfigurations: credentialConfigs,
271
+ TargetConfiguration: targetConfig,
272
+ ...(config.description && { Description: config.description }),
273
+ },
274
+ };
275
+ }
276
+
277
+ module.exports = {
278
+ compileGatewayTarget,
279
+ buildCredentialProviderConfigurations,
280
+ buildTargetConfiguration,
281
+ buildLambdaTargetConfiguration,
282
+ buildOpenApiTargetConfiguration,
283
+ buildSmithyTargetConfiguration,
284
+ transformSchemaToCloudFormation,
285
+ };
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ const { compileRuntime } = require('./runtime');
4
+ const { compileRuntimeEndpoint } = require('./runtimeEndpoint');
5
+ const { compileMemory } = require('./memory');
6
+ const { compileGateway } = require('./gateway');
7
+ const { compileGatewayTarget } = require('./gatewayTarget');
8
+
9
+ module.exports = {
10
+ compileRuntime,
11
+ compileRuntimeEndpoint,
12
+ compileMemory,
13
+ compileGateway,
14
+ compileGatewayTarget,
15
+ };