@aws/nx-plugin 0.64.1 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-THIRD-PARTY +198 -132
- package/README.md +6 -3
- package/generators.json +6 -0
- package/package.json +13 -13
- package/sdk/ts.d.ts +2 -0
- package/sdk/ts.js +20 -17
- package/sdk/ts.js.map +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +31 -31
- package/src/preset/__snapshots__/generator.spec.ts.snap +5 -3
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +8 -8
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +93 -49
- package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +1035 -0
- package/src/ts/strands-agent/files/app/agent-core-mcp-client.ts.template +132 -0
- package/src/ts/strands-agent/files/app/agent-core-trpc-client.ts.template +124 -0
- package/src/ts/strands-agent/files/app/agent.ts.template +20 -0
- package/src/ts/strands-agent/files/app/client.ts.template +28 -0
- package/src/ts/strands-agent/files/app/index.ts.template +54 -0
- package/src/ts/strands-agent/files/app/init.ts.template +8 -0
- package/src/ts/strands-agent/files/app/router.ts.template +34 -0
- package/src/ts/strands-agent/files/app/schema/z-async-iterable.ts.template +77 -0
- package/src/ts/strands-agent/files/deploy/Dockerfile.template +16 -0
- package/src/ts/strands-agent/generator.d.ts +10 -0
- package/src/ts/strands-agent/generator.js +115 -0
- package/src/ts/strands-agent/generator.js.map +1 -0
- package/src/ts/strands-agent/schema.d.ts +15 -0
- package/src/ts/strands-agent/schema.json +41 -0
- package/src/utils/versions.d.ts +58 -55
- package/src/utils/versions.js +57 -54
- package/src/utils/versions.js.map +1 -1
|
@@ -0,0 +1,1035 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`ts#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-Dockerfile 1`] = `
|
|
4
|
+
"FROM public.ecr.aws/docker/library/node:lts-jod
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Add AWS Distro for OpenTelemetry for observability
|
|
9
|
+
# https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html
|
|
10
|
+
RUN npm install @aws/aws-distro-opentelemetry-node-autoinstrumentation@^0.7.0
|
|
11
|
+
|
|
12
|
+
# Copy bundled agent
|
|
13
|
+
COPY --from=workspace dist/apps/test-project/bundle/agent/snapshot-bedrock-agent/index.js /app
|
|
14
|
+
|
|
15
|
+
EXPOSE 8080
|
|
16
|
+
|
|
17
|
+
# Auto-instrument with AWS Distro for OpenTelemetry
|
|
18
|
+
# https://aws-otel.github.io/docs/getting-started/js-sdk/trace-metric-auto-instr
|
|
19
|
+
CMD [ "node", "--require", "@aws/aws-distro-opentelemetry-node-autoinstrumentation/register", "index.js" ]
|
|
20
|
+
"
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
exports[`ts#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-construct.ts 1`] = `
|
|
24
|
+
"import { Lazy, Names } from 'aws-cdk-lib';
|
|
25
|
+
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
|
|
26
|
+
import { Construct } from 'constructs';
|
|
27
|
+
import { execSync } from 'child_process';
|
|
28
|
+
import * as path from 'path';
|
|
29
|
+
import * as url from 'url';
|
|
30
|
+
import {
|
|
31
|
+
AgentRuntimeArtifact,
|
|
32
|
+
ProtocolType,
|
|
33
|
+
Runtime,
|
|
34
|
+
RuntimeProps,
|
|
35
|
+
} from '@aws-cdk/aws-bedrock-agentcore-alpha';
|
|
36
|
+
|
|
37
|
+
export type SnapshotBedrockAgentProps = Omit<
|
|
38
|
+
RuntimeProps,
|
|
39
|
+
'runtimeName' | 'protocolConfiguration' | 'agentRuntimeArtifact'
|
|
40
|
+
>;
|
|
41
|
+
|
|
42
|
+
export class SnapshotBedrockAgent extends Construct {
|
|
43
|
+
public readonly dockerImage: AgentRuntimeArtifact;
|
|
44
|
+
public readonly agentCoreRuntime: Runtime;
|
|
45
|
+
|
|
46
|
+
constructor(scope: Construct, id: string, props?: SnapshotBedrockAgentProps) {
|
|
47
|
+
super(scope, id);
|
|
48
|
+
|
|
49
|
+
this.dockerImage = AgentRuntimeArtifact.fromAsset(
|
|
50
|
+
path.dirname(url.fileURLToPath(new URL(import.meta.url))),
|
|
51
|
+
{
|
|
52
|
+
platform: Platform.LINUX_ARM64,
|
|
53
|
+
extraHash: execSync(
|
|
54
|
+
\`docker inspect proj-snapshot-bedrock-agent:latest --format '{{.Id}}'\`,
|
|
55
|
+
{ encoding: 'utf-8' },
|
|
56
|
+
).trim(),
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
this.agentCoreRuntime = new Runtime(this, 'SnapshotBedrockAgent', {
|
|
61
|
+
runtimeName: Lazy.string({
|
|
62
|
+
produce: () =>
|
|
63
|
+
Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }),
|
|
64
|
+
}),
|
|
65
|
+
protocolConfiguration: ProtocolType.HTTP,
|
|
66
|
+
agentRuntimeArtifact: this.dockerImage,
|
|
67
|
+
...props,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
"
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
exports[`ts#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agents-index.ts 1`] = `
|
|
75
|
+
"export * from './snapshot-bedrock-agent/snapshot-bedrock-agent.js';
|
|
76
|
+
"
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
exports[`ts#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > app-index.ts 1`] = `
|
|
80
|
+
"export * from './agents/index.js';
|
|
81
|
+
"
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
exports[`ts#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > core-index.ts 1`] = `
|
|
85
|
+
"export * from './app.js';
|
|
86
|
+
export * from './checkov.js';
|
|
87
|
+
export * from './runtime-config.js';
|
|
88
|
+
"
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
exports[`ts#strands-agent generator > should match snapshot for Terraform generated files > terraform-agent.tf 1`] = `
|
|
92
|
+
"variable "env" {
|
|
93
|
+
description = "Environment variables to pass to the agent core runtime"
|
|
94
|
+
type = map(string)
|
|
95
|
+
default = {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
variable "additional_iam_policy_statements" {
|
|
99
|
+
description = "Additional IAM policy statements to attach to the agent core runtime role"
|
|
100
|
+
type = list(object({
|
|
101
|
+
Effect = string
|
|
102
|
+
Action = list(string)
|
|
103
|
+
Resource = list(string)
|
|
104
|
+
}))
|
|
105
|
+
default = []
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
variable "tags" {
|
|
109
|
+
description = "Tags to apply to resources"
|
|
110
|
+
type = map(string)
|
|
111
|
+
default = {}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module "agent_core_runtime" {
|
|
115
|
+
source = "../../../core/agent-core"
|
|
116
|
+
agent_runtime_name = "TerraformSnapshotAgent"
|
|
117
|
+
docker_image_tag = "proj-terraform-snapshot-agent:latest"
|
|
118
|
+
server_protocol = "HTTP"
|
|
119
|
+
# authorizer_configuration = {
|
|
120
|
+
# custom_jwt_authorizer = {
|
|
121
|
+
# discovery_url = "https://xxx/.well-known/openid-configuration"
|
|
122
|
+
# allowed_clients = [ "xxx" ]
|
|
123
|
+
# }
|
|
124
|
+
# }
|
|
125
|
+
|
|
126
|
+
env = var.env
|
|
127
|
+
additional_iam_policy_statements = var.additional_iam_policy_statements
|
|
128
|
+
tags = var.tags
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
output "agent_core_runtime_role_arn" {
|
|
132
|
+
description = "ARN of the agent core runtime role"
|
|
133
|
+
value = module.agent_core_runtime.agent_core_runtime_role_arn
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
output "agent_core_runtime_arn" {
|
|
137
|
+
description = "ARN of the Bedrock Agent Core runtime"
|
|
138
|
+
value = module.agent_core_runtime.agent_core_runtime_arn
|
|
139
|
+
}
|
|
140
|
+
"
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
exports[`ts#strands-agent generator > should match snapshot for Terraform generated files > terraform-agent-core-runtime.tf 1`] = `
|
|
144
|
+
"terraform {
|
|
145
|
+
required_version = ">= 1.0"
|
|
146
|
+
|
|
147
|
+
required_providers {
|
|
148
|
+
aws = {
|
|
149
|
+
source = "hashicorp/aws"
|
|
150
|
+
version = ">= 6.23"
|
|
151
|
+
}
|
|
152
|
+
null = {
|
|
153
|
+
source = "hashicorp/null"
|
|
154
|
+
version = ">= 3.0"
|
|
155
|
+
}
|
|
156
|
+
random = {
|
|
157
|
+
source = "hashicorp/random"
|
|
158
|
+
version = ">= 3.0"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Variables
|
|
164
|
+
variable "agent_runtime_name" {
|
|
165
|
+
description = "Name of the agent runtime"
|
|
166
|
+
type = string
|
|
167
|
+
validation {
|
|
168
|
+
condition = can(regex("^[a-zA-Z][a-zA-Z0-9_]{0,42}$", var.agent_runtime_name))
|
|
169
|
+
error_message = "Value must start with a letter and contain only letters, numbers, and underscores (1-43 characters)."
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
variable "server_protocol" {
|
|
174
|
+
description = "Server protocol for the agent runtime (HTTP, MCP, or A2A)"
|
|
175
|
+
type = string
|
|
176
|
+
default = "HTTP"
|
|
177
|
+
validation {
|
|
178
|
+
condition = contains(["MCP", "HTTP", "A2A"], var.server_protocol)
|
|
179
|
+
error_message = "Protocol type must be either 'MCP', 'HTTP', or 'A2A'."
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
variable "authorizer_configuration" {
|
|
184
|
+
description = "Authorization configuration for authenticating incoming requests"
|
|
185
|
+
type = object({
|
|
186
|
+
custom_jwt_authorizer = optional(object({
|
|
187
|
+
discovery_url = string
|
|
188
|
+
allowed_audience = optional(list(string))
|
|
189
|
+
allowed_clients = optional(list(string))
|
|
190
|
+
}))
|
|
191
|
+
})
|
|
192
|
+
default = null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
variable "docker_image_tag" {
|
|
196
|
+
description = "Name of the docker image tag to use as the agent core runtime"
|
|
197
|
+
type = string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
variable "env" {
|
|
201
|
+
description = "Environment variables to pass to the agent core runtime"
|
|
202
|
+
type = map(string)
|
|
203
|
+
default = {}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
variable "additional_iam_policy_statements" {
|
|
207
|
+
description = "Additional IAM policy statements to attach to the agent core runtime role"
|
|
208
|
+
type = list(object({
|
|
209
|
+
Effect = string
|
|
210
|
+
Action = list(string)
|
|
211
|
+
Resource = list(string)
|
|
212
|
+
}))
|
|
213
|
+
default = []
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
variable "tags" {
|
|
217
|
+
description = "Tags to apply to resources"
|
|
218
|
+
type = map(string)
|
|
219
|
+
default = {}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# Data sources
|
|
223
|
+
data "aws_caller_identity" "current" {}
|
|
224
|
+
data "aws_region" "current" {}
|
|
225
|
+
|
|
226
|
+
locals {
|
|
227
|
+
aws_account_id = data.aws_caller_identity.current.account_id
|
|
228
|
+
aws_region = data.aws_region.current.id
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# Random ID for bucket suffix to ensure uniqueness
|
|
232
|
+
resource "random_id" "unique_suffix" {
|
|
233
|
+
byte_length = 4
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# ECR Repository
|
|
237
|
+
resource "aws_ecr_repository" "agent_core_repository" {
|
|
238
|
+
#checkov:skip=CKV_AWS_136:AES256 encryption is sufficient for ECR repositories
|
|
239
|
+
name = "\${lower(var.agent_runtime_name)}_repository_\${random_id.unique_suffix.hex}"
|
|
240
|
+
|
|
241
|
+
#checkov:skip=CKV_AWS_51:Image tag is reused for latest deployments
|
|
242
|
+
image_tag_mutability = "MUTABLE"
|
|
243
|
+
force_delete = true
|
|
244
|
+
|
|
245
|
+
image_scanning_configuration {
|
|
246
|
+
scan_on_push = true
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
tags = var.tags
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# ECR Repository Policy
|
|
253
|
+
resource "aws_ecr_repository_policy" "agent_core_ecr_policy" {
|
|
254
|
+
repository = aws_ecr_repository.agent_core_repository.name
|
|
255
|
+
|
|
256
|
+
policy = jsonencode({
|
|
257
|
+
Version = "2012-10-17"
|
|
258
|
+
Statement = [
|
|
259
|
+
{
|
|
260
|
+
Sid = "AllowPushPull"
|
|
261
|
+
Effect = "Allow"
|
|
262
|
+
Principal = {
|
|
263
|
+
AWS = "arn:aws:iam::\${local.aws_account_id}:root"
|
|
264
|
+
}
|
|
265
|
+
Action = [
|
|
266
|
+
"ecr:GetDownloadUrlForLayer",
|
|
267
|
+
"ecr:BatchGetImage",
|
|
268
|
+
"ecr:BatchCheckLayerAvailability",
|
|
269
|
+
"ecr:PutImage",
|
|
270
|
+
"ecr:InitiateLayerUpload",
|
|
271
|
+
"ecr:UploadLayerPart",
|
|
272
|
+
"ecr:CompleteLayerUpload"
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
# IAM Role for Agent Core Runtime
|
|
280
|
+
resource "aws_iam_role" "agent_core_runtime_role" {
|
|
281
|
+
name = "\${var.agent_runtime_name}-AgentCoreRuntimeRole-\${random_id.unique_suffix.hex}"
|
|
282
|
+
|
|
283
|
+
assume_role_policy = jsonencode({
|
|
284
|
+
Version = "2012-10-17"
|
|
285
|
+
Statement = [
|
|
286
|
+
{
|
|
287
|
+
Sid = "AgentCoreAssumeRolePolicy"
|
|
288
|
+
Effect = "Allow"
|
|
289
|
+
Principal = {
|
|
290
|
+
Service = "bedrock-agentcore.amazonaws.com"
|
|
291
|
+
}
|
|
292
|
+
Action = "sts:AssumeRole"
|
|
293
|
+
Condition = {
|
|
294
|
+
StringEquals = {
|
|
295
|
+
"aws:SourceAccount" = local.aws_account_id
|
|
296
|
+
}
|
|
297
|
+
ArnLike = {
|
|
298
|
+
"aws:SourceArn" = "arn:aws:bedrock-agentcore:\${local.aws_region}:\${local.aws_account_id}:*"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
tags = var.tags
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# IAM Policy for Agent Core Runtime
|
|
309
|
+
resource "aws_iam_policy" "agent_core_runtime_policy" {
|
|
310
|
+
name = "\${var.agent_runtime_name}-QueryAgentPolicy-\${random_id.unique_suffix.hex}"
|
|
311
|
+
description = "Restricted policy for Agent"
|
|
312
|
+
|
|
313
|
+
policy = jsonencode({
|
|
314
|
+
Version = "2012-10-17"
|
|
315
|
+
Statement = concat([
|
|
316
|
+
{
|
|
317
|
+
Sid = "ECRImageAccess"
|
|
318
|
+
Effect = "Allow"
|
|
319
|
+
Action = [
|
|
320
|
+
"ecr:BatchGetImage",
|
|
321
|
+
"ecr:GetDownloadUrlForLayer"
|
|
322
|
+
]
|
|
323
|
+
Resource = [
|
|
324
|
+
aws_ecr_repository.agent_core_repository.arn
|
|
325
|
+
]
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
Sid = "ECRTokenAccess"
|
|
329
|
+
Effect = "Allow"
|
|
330
|
+
Action = [
|
|
331
|
+
"ecr:GetAuthorizationToken"
|
|
332
|
+
]
|
|
333
|
+
Resource = [
|
|
334
|
+
"*"
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"Effect" : "Allow",
|
|
339
|
+
"Action" : [
|
|
340
|
+
"logs:DescribeLogStreams",
|
|
341
|
+
"logs:CreateLogGroup"
|
|
342
|
+
],
|
|
343
|
+
"Resource" : [
|
|
344
|
+
"arn:aws:logs:\${local.aws_region}:\${local.aws_account_id}:log-group:/aws/bedrock-agentcore/runtimes/*"
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
"Effect" : "Allow",
|
|
349
|
+
"Action" : [
|
|
350
|
+
"logs:DescribeLogGroups"
|
|
351
|
+
],
|
|
352
|
+
"Resource" : [
|
|
353
|
+
"arn:aws:logs:\${local.aws_region}:\${local.aws_account_id}:log-group:*"
|
|
354
|
+
]
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"Effect" : "Allow",
|
|
358
|
+
"Action" : [
|
|
359
|
+
"logs:CreateLogStream",
|
|
360
|
+
"logs:PutLogEvents"
|
|
361
|
+
],
|
|
362
|
+
"Resource" : [
|
|
363
|
+
"arn:aws:logs:\${local.aws_region}:\${local.aws_account_id}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*"
|
|
364
|
+
]
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"Effect" : "Allow",
|
|
368
|
+
"Action" : [
|
|
369
|
+
"xray:PutTraceSegments",
|
|
370
|
+
"xray:PutTelemetryRecords",
|
|
371
|
+
"xray:GetSamplingRules",
|
|
372
|
+
"xray:GetSamplingTargets"
|
|
373
|
+
],
|
|
374
|
+
"Resource" : ["*"]
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"Effect" : "Allow",
|
|
378
|
+
"Resource" : "*",
|
|
379
|
+
"Action" : "cloudwatch:PutMetricData",
|
|
380
|
+
"Condition" : {
|
|
381
|
+
"StringEquals" : {
|
|
382
|
+
"cloudwatch:namespace" : "bedrock-agentcore"
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"Sid" : "GetAgentAccessToken",
|
|
388
|
+
"Effect" : "Allow",
|
|
389
|
+
"Action" : [
|
|
390
|
+
"bedrock-agentcore:GetWorkloadAccessToken",
|
|
391
|
+
"bedrock-agentcore:GetWorkloadAccessTokenForJWT",
|
|
392
|
+
"bedrock-agentcore:GetWorkloadAccessTokenForUserId"
|
|
393
|
+
],
|
|
394
|
+
"Resource" : [
|
|
395
|
+
"arn:aws:bedrock-agentcore:\${local.aws_region}:\${local.aws_account_id}:workload-identity-directory/default",
|
|
396
|
+
"arn:aws:bedrock-agentcore:\${local.aws_region}:\${local.aws_account_id}:workload-identity-directory/default/workload-identity/*"
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
], var.additional_iam_policy_statements)
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
tags = var.tags
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
# Attach the restricted policy to the role
|
|
406
|
+
resource "aws_iam_role_policy_attachment" "agent_core_policy" {
|
|
407
|
+
role = aws_iam_role.agent_core_runtime_role.name
|
|
408
|
+
policy_arn = aws_iam_policy.agent_core_runtime_policy.arn
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
# Data source to get Docker image digest
|
|
412
|
+
data "external" "docker_digest" {
|
|
413
|
+
program = ["sh", "-c", "echo '{\\"digest\\":\\"'$(docker inspect \${var.docker_image_tag} --format '{{.Id}}')'\\"}' "]
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# Null resource for Docker publish
|
|
417
|
+
resource "null_resource" "docker_publish" {
|
|
418
|
+
triggers = {
|
|
419
|
+
docker_digest = data.external.docker_digest.result.digest
|
|
420
|
+
repository_url = aws_ecr_repository.agent_core_repository.repository_url
|
|
421
|
+
docker_image_tag = var.docker_image_tag
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
provisioner "local-exec" {
|
|
425
|
+
command = <<-EOT
|
|
426
|
+
# Get ECR login token
|
|
427
|
+
aws ecr get-login-password --region \${local.aws_region} | docker login --username AWS --password-stdin \${self.triggers.repository_url}
|
|
428
|
+
|
|
429
|
+
# Tag the image
|
|
430
|
+
docker tag \${self.triggers.docker_image_tag} \${self.triggers.repository_url}:latest
|
|
431
|
+
|
|
432
|
+
# Push the image
|
|
433
|
+
docker push \${self.triggers.repository_url}:latest
|
|
434
|
+
EOT
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
depends_on = [aws_ecr_repository_policy.agent_core_ecr_policy]
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
# Bedrock AgentCore Agent Runtime
|
|
441
|
+
resource "aws_bedrockagentcore_agent_runtime" "agent_runtime" {
|
|
442
|
+
agent_runtime_name = "\${var.agent_runtime_name}_\${random_id.unique_suffix.hex}"
|
|
443
|
+
description = "Agent Runtime for \${var.agent_runtime_name}"
|
|
444
|
+
role_arn = aws_iam_role.agent_core_runtime_role.arn
|
|
445
|
+
|
|
446
|
+
agent_runtime_artifact {
|
|
447
|
+
container_configuration {
|
|
448
|
+
container_uri = "\${aws_ecr_repository.agent_core_repository.repository_url}:latest"
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
environment_variables = length(var.env) > 0 ? var.env : null
|
|
453
|
+
|
|
454
|
+
dynamic "authorizer_configuration" {
|
|
455
|
+
for_each = var.authorizer_configuration != null && var.authorizer_configuration.custom_jwt_authorizer != null ? [var.authorizer_configuration.custom_jwt_authorizer] : []
|
|
456
|
+
content {
|
|
457
|
+
custom_jwt_authorizer {
|
|
458
|
+
discovery_url = authorizer_configuration.value.discovery_url
|
|
459
|
+
allowed_audience = authorizer_configuration.value.allowed_audience
|
|
460
|
+
allowed_clients = authorizer_configuration.value.allowed_clients
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
network_configuration {
|
|
466
|
+
network_mode = "PUBLIC"
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
protocol_configuration {
|
|
470
|
+
server_protocol = var.server_protocol
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
tags = var.tags
|
|
474
|
+
|
|
475
|
+
depends_on = [
|
|
476
|
+
null_resource.docker_publish,
|
|
477
|
+
aws_iam_role_policy.agent_core_runtime_policy
|
|
478
|
+
]
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
# Outputs
|
|
482
|
+
output "agent_core_runtime_role_arn" {
|
|
483
|
+
description = "ARN of the Agent Core Runtime IAM role"
|
|
484
|
+
value = aws_iam_role.agent_core_runtime_role.arn
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
output "agent_core_runtime_role_name" {
|
|
488
|
+
description = "Name of the Agent Core Runtime IAM role"
|
|
489
|
+
value = aws_iam_role.agent_core_runtime_role.name
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
output "agent_runtime_name" {
|
|
493
|
+
description = "Name of the deployed agent runtime"
|
|
494
|
+
value = aws_bedrockagentcore_agent_runtime.agent_runtime.agent_runtime_name
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
output "agent_core_runtime_arn" {
|
|
498
|
+
description = "ARN of the Bedrock Agent Core runtime"
|
|
499
|
+
value = aws_bedrockagentcore_agent_runtime.agent_runtime.agent_runtime_arn
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
output "agent_runtime_id" {
|
|
503
|
+
description = "ID of the Bedrock Agent Core runtime"
|
|
504
|
+
value = aws_bedrockagentcore_agent_runtime.agent_runtime.agent_runtime_id
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
output "agent_runtime_version" {
|
|
508
|
+
description = "Version of the Bedrock Agent Core runtime"
|
|
509
|
+
value = aws_bedrockagentcore_agent_runtime.agent_runtime.agent_runtime_version
|
|
510
|
+
}
|
|
511
|
+
"
|
|
512
|
+
`;
|
|
513
|
+
|
|
514
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-agent.ts 1`] = `
|
|
515
|
+
"import { Agent, tool } from '@strands-agents/sdk';
|
|
516
|
+
import { z } from 'zod';
|
|
517
|
+
|
|
518
|
+
const add = tool({
|
|
519
|
+
name: 'Add',
|
|
520
|
+
description: 'Add two numbers',
|
|
521
|
+
inputSchema: z.object({
|
|
522
|
+
a: z.number(),
|
|
523
|
+
b: z.number(),
|
|
524
|
+
}),
|
|
525
|
+
callback: ({ a, b }) => a + b,
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
export const getAgent = () =>
|
|
529
|
+
new Agent({
|
|
530
|
+
systemPrompt: \`You are an addition wizard.
|
|
531
|
+
Use the add tool for addition tasks.
|
|
532
|
+
Refer to tools as your 'spellbook'.\`,
|
|
533
|
+
tools: [add],
|
|
534
|
+
});
|
|
535
|
+
"
|
|
536
|
+
`;
|
|
537
|
+
|
|
538
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-agent-core-mcp-client.ts 1`] = `
|
|
539
|
+
"import { McpClient } from '@strands-agents/sdk';
|
|
540
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
541
|
+
import { AwsClient } from 'aws4fetch';
|
|
542
|
+
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Options for creating an AgentCore MCP client with IAM authentication
|
|
546
|
+
*/
|
|
547
|
+
export interface AgentCoreMcpClientIamOptions {
|
|
548
|
+
agentRuntimeArn: string;
|
|
549
|
+
region: string;
|
|
550
|
+
sessionId: string;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Options for creating an AgentCore MCP client with JWT authentication
|
|
555
|
+
*/
|
|
556
|
+
export interface AgentCoreMcpClientJwtOptions {
|
|
557
|
+
agentRuntimeArn: string;
|
|
558
|
+
accessToken: string;
|
|
559
|
+
region: string;
|
|
560
|
+
sessionId: string;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Internal options for creating an AgentCore MCP client
|
|
565
|
+
*/
|
|
566
|
+
interface CreateOptions {
|
|
567
|
+
agentRuntimeArn: string;
|
|
568
|
+
region: string;
|
|
569
|
+
sessionId: string;
|
|
570
|
+
additionalHeaders?: Record<string, string>;
|
|
571
|
+
fetch?: typeof fetch;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Factory for clients to call MCP servers hosted on Bedrock AgentCore Runtime
|
|
576
|
+
*/
|
|
577
|
+
export class AgentCoreMcpClient {
|
|
578
|
+
/**
|
|
579
|
+
* Internal method to create an MCP client
|
|
580
|
+
*/
|
|
581
|
+
private static create(options: CreateOptions): McpClient {
|
|
582
|
+
const {
|
|
583
|
+
agentRuntimeArn,
|
|
584
|
+
region,
|
|
585
|
+
sessionId,
|
|
586
|
+
additionalHeaders = {},
|
|
587
|
+
fetch: customFetch,
|
|
588
|
+
} = options;
|
|
589
|
+
|
|
590
|
+
// Encode the ARN for URL
|
|
591
|
+
const encodedArn = agentRuntimeArn
|
|
592
|
+
.replace(/:/g, '%3A')
|
|
593
|
+
.replace(/\\//g, '%2F');
|
|
594
|
+
const url = \`https://bedrock-agentcore.\${region}.amazonaws.com/runtimes/\${encodedArn}/invocations?qualifier=DEFAULT\`;
|
|
595
|
+
|
|
596
|
+
// Create a custom fetch that adds required headers
|
|
597
|
+
const fetchWithHeaders: typeof fetch = async (input, init) => {
|
|
598
|
+
const headers = {
|
|
599
|
+
'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id': sessionId,
|
|
600
|
+
...additionalHeaders,
|
|
601
|
+
};
|
|
602
|
+
const existingHeaders = init?.headers;
|
|
603
|
+
|
|
604
|
+
const mergedInit = {
|
|
605
|
+
...init,
|
|
606
|
+
headers: !existingHeaders
|
|
607
|
+
? headers
|
|
608
|
+
: existingHeaders instanceof Headers
|
|
609
|
+
? (() => {
|
|
610
|
+
const h = new Headers(existingHeaders);
|
|
611
|
+
Object.entries(headers).forEach(([k, v]) => h.append(k, v));
|
|
612
|
+
return h;
|
|
613
|
+
})()
|
|
614
|
+
: Array.isArray(existingHeaders)
|
|
615
|
+
? [...existingHeaders, ...Object.entries(headers)]
|
|
616
|
+
: { ...existingHeaders, ...headers },
|
|
617
|
+
};
|
|
618
|
+
return (customFetch || fetch)(input, mergedInit);
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// Create the transport
|
|
622
|
+
const transport = new StreamableHTTPClientTransport(new URL(url), {
|
|
623
|
+
fetch: fetchWithHeaders,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// Create and return the MCP client
|
|
627
|
+
return new McpClient({ transport });
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Create an MCP client with IAM (SigV4) authentication
|
|
632
|
+
*/
|
|
633
|
+
static withIamAuth(options: AgentCoreMcpClientIamOptions): McpClient {
|
|
634
|
+
const { region, ...rest } = options;
|
|
635
|
+
|
|
636
|
+
// Create credential provider
|
|
637
|
+
const credentialProvider = fromNodeProviderChain();
|
|
638
|
+
|
|
639
|
+
// Create a SigV4 signing fetch function
|
|
640
|
+
const sigv4Fetch: typeof fetch = async (...args) => {
|
|
641
|
+
const credentials = await credentialProvider();
|
|
642
|
+
const client = new AwsClient({
|
|
643
|
+
...credentials,
|
|
644
|
+
service: 'bedrock-agentcore',
|
|
645
|
+
region,
|
|
646
|
+
});
|
|
647
|
+
return client.fetch(...args);
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
return AgentCoreMcpClient.create({
|
|
651
|
+
...rest,
|
|
652
|
+
region,
|
|
653
|
+
fetch: sigv4Fetch,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Create an MCP client with JWT authentication
|
|
659
|
+
*/
|
|
660
|
+
static withJwtAuth(options: AgentCoreMcpClientJwtOptions): McpClient {
|
|
661
|
+
const { accessToken, ...rest } = options;
|
|
662
|
+
|
|
663
|
+
return AgentCoreMcpClient.create({
|
|
664
|
+
...rest,
|
|
665
|
+
additionalHeaders: {
|
|
666
|
+
Authorization: \`Bearer \${accessToken}\`,
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
"
|
|
672
|
+
`;
|
|
673
|
+
|
|
674
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-agent-core-trpc-client.ts 1`] = `
|
|
675
|
+
"import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
676
|
+
import {
|
|
677
|
+
createTRPCClient,
|
|
678
|
+
createWSClient,
|
|
679
|
+
WebSocketLinkOptions,
|
|
680
|
+
wsLink,
|
|
681
|
+
} from '@trpc/client';
|
|
682
|
+
import { AwsClient } from 'aws4fetch';
|
|
683
|
+
import { AnyTRPCRouter } from '@trpc/server';
|
|
684
|
+
import { WebSocket } from 'node:http';
|
|
685
|
+
|
|
686
|
+
export interface WebSocketTrpcClientOptions {
|
|
687
|
+
/**
|
|
688
|
+
* URL of the websocket server, eg http://localhost:8080/ws
|
|
689
|
+
*/
|
|
690
|
+
url: string;
|
|
691
|
+
/**
|
|
692
|
+
* Factory for creating headers to include in the WebSocket handshake
|
|
693
|
+
*/
|
|
694
|
+
buildHeaders?: () => Promise<{ [key: string]: string }>;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Client for connecting to tRPC APIs via WebSocket
|
|
699
|
+
*/
|
|
700
|
+
export class WebSocketTrpcClient {
|
|
701
|
+
/**
|
|
702
|
+
* Utility for creating a tRPC client with a WebSocket link and custom headers
|
|
703
|
+
*/
|
|
704
|
+
public static create = <TRouter extends AnyTRPCRouter>({
|
|
705
|
+
url,
|
|
706
|
+
buildHeaders = async () => ({}),
|
|
707
|
+
}: WebSocketTrpcClientOptions) => {
|
|
708
|
+
let headers = {};
|
|
709
|
+
const client = createWSClient({
|
|
710
|
+
url: async () => {
|
|
711
|
+
headers = await buildHeaders();
|
|
712
|
+
return url;
|
|
713
|
+
},
|
|
714
|
+
WebSocket: class extends WebSocket {
|
|
715
|
+
constructor(wsUrl: string | URL) {
|
|
716
|
+
super(wsUrl, { headers });
|
|
717
|
+
}
|
|
718
|
+
} as any,
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
return createTRPCClient<TRouter>({
|
|
722
|
+
links: [wsLink({ client } as WebSocketLinkOptions<TRouter>)],
|
|
723
|
+
});
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
export interface AgentCoreTrpcClientOptions {
|
|
728
|
+
/**
|
|
729
|
+
* The ARN of the Bedrock AgentCore Runtime to connect to
|
|
730
|
+
*/
|
|
731
|
+
agentRuntimeArn: string;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export interface AgentCoreTrpcClientIamOptions extends AgentCoreTrpcClientOptions {
|
|
735
|
+
/**
|
|
736
|
+
* Optional AWS credential provider. If not provided, uses the default
|
|
737
|
+
* credential provider chain from the AWS SDK.
|
|
738
|
+
*/
|
|
739
|
+
credentialProvider?: ReturnType<typeof fromNodeProviderChain>;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
export interface AgentCoreTrpcClientJwtOptions extends AgentCoreTrpcClientOptions {
|
|
743
|
+
/**
|
|
744
|
+
* A function which returns the JWT access token used to authenticate
|
|
745
|
+
*/
|
|
746
|
+
accessTokenProvider: () => Promise<string>;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Client for connecting to a tRPC API on Bedrock AgentCore Runtime via WebSocket.
|
|
751
|
+
*/
|
|
752
|
+
export class AgentCoreTrpcClient {
|
|
753
|
+
/**
|
|
754
|
+
* Construct the websocket url for connecting to Bedrock AgentCore Runtime
|
|
755
|
+
*/
|
|
756
|
+
private static buildUrl = (agentRuntimeArn: string) => {
|
|
757
|
+
const region = agentRuntimeArn.split(':')[3];
|
|
758
|
+
const url = \`wss://bedrock-agentcore.\${region}.amazonaws.com/runtimes/\${agentRuntimeArn.replace(/:/g, '%3A').replace(/\\//g, '%2F')}/ws\`;
|
|
759
|
+
return { region, url };
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Creates a tRPC client with IAM authentication using AWS credentials.
|
|
764
|
+
*
|
|
765
|
+
* The WebSocket connection is authenticated using AWS Signature Version 4 (SigV4) signed headers.
|
|
766
|
+
* This method signs the WebSocket upgrade request headers directly, rather than using a presigned URL.
|
|
767
|
+
*/
|
|
768
|
+
public static withIamAuth = <TRouter extends AnyTRPCRouter>(
|
|
769
|
+
options: AgentCoreTrpcClientIamOptions,
|
|
770
|
+
) => {
|
|
771
|
+
const credentialProvider =
|
|
772
|
+
options.credentialProvider ?? fromNodeProviderChain();
|
|
773
|
+
const { region, url } = AgentCoreTrpcClient.buildUrl(
|
|
774
|
+
options.agentRuntimeArn,
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
return WebSocketTrpcClient.create<TRouter>({
|
|
778
|
+
url,
|
|
779
|
+
buildHeaders: async () =>
|
|
780
|
+
Object.fromEntries(
|
|
781
|
+
(
|
|
782
|
+
await new AwsClient({
|
|
783
|
+
...(await credentialProvider()),
|
|
784
|
+
region,
|
|
785
|
+
service: 'bedrock-agentcore',
|
|
786
|
+
}).sign(url)
|
|
787
|
+
).headers,
|
|
788
|
+
),
|
|
789
|
+
});
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Creates a tRPC client with JWT (JSON Web Token) authentication.
|
|
794
|
+
*
|
|
795
|
+
* The WebSocket connection includes the JWT as a Bearer token in the Authorization header.
|
|
796
|
+
*/
|
|
797
|
+
public static withJwtAuth = <TRouter extends AnyTRPCRouter>(
|
|
798
|
+
options: AgentCoreTrpcClientJwtOptions,
|
|
799
|
+
) => {
|
|
800
|
+
const { url } = AgentCoreTrpcClient.buildUrl(options.agentRuntimeArn);
|
|
801
|
+
|
|
802
|
+
return WebSocketTrpcClient.create<TRouter>({
|
|
803
|
+
url,
|
|
804
|
+
buildHeaders: async () => ({
|
|
805
|
+
Authorization: \`Bearer \${await options.accessTokenProvider()}\`,
|
|
806
|
+
}),
|
|
807
|
+
});
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
"
|
|
811
|
+
`;
|
|
812
|
+
|
|
813
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-client.ts 1`] = `
|
|
814
|
+
"import { AppRouter } from './router.js';
|
|
815
|
+
import {
|
|
816
|
+
AgentCoreTrpcClient,
|
|
817
|
+
WebSocketTrpcClient,
|
|
818
|
+
} from './agent-core-trpc-client.js';
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Client for connecting to SnapshotAgent on Bedrock AgentCore Runtime via WebSocket from a NodeJS environment
|
|
822
|
+
*
|
|
823
|
+
* Refer to the Nx Plugin for AWS documentation for instructions to connect to this agent in a browser.
|
|
824
|
+
*/
|
|
825
|
+
export class SnapshotAgentClient {
|
|
826
|
+
/**
|
|
827
|
+
* Creates a tRPC client with IAM authentication using AWS credentials.
|
|
828
|
+
*
|
|
829
|
+
* The WebSocket connection is authenticated using AWS Signature Version 4 (SigV4)
|
|
830
|
+
*/
|
|
831
|
+
public static withIamAuth = AgentCoreTrpcClient.withIamAuth<AppRouter>;
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Creates a tRPC client with JWT (JSON Web Token) authentication.
|
|
835
|
+
*
|
|
836
|
+
* The WebSocket connection includes the JWT as a Bearer token in the Authorization header.
|
|
837
|
+
*/
|
|
838
|
+
public static withJwtAuth = AgentCoreTrpcClient.withJwtAuth<AppRouter>;
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Creates a tRPC client for use with a locally running SnapshotAgent.
|
|
842
|
+
*/
|
|
843
|
+
public static local = WebSocketTrpcClient.create<AppRouter>;
|
|
844
|
+
}
|
|
845
|
+
"
|
|
846
|
+
`;
|
|
847
|
+
|
|
848
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-index.ts 1`] = `
|
|
849
|
+
"import { createServer } from 'http';
|
|
850
|
+
import {
|
|
851
|
+
CreateHTTPContextOptions,
|
|
852
|
+
createHTTPHandler,
|
|
853
|
+
} from '@trpc/server/adapters/standalone';
|
|
854
|
+
import { appRouter, AppRouter } from './router.js';
|
|
855
|
+
import { WebSocketServer } from 'ws';
|
|
856
|
+
import cors from 'cors';
|
|
857
|
+
import {
|
|
858
|
+
CreateWSSContextFnOptions,
|
|
859
|
+
applyWSSHandler,
|
|
860
|
+
} from '@trpc/server/adapters/ws';
|
|
861
|
+
import { Context } from './init.js';
|
|
862
|
+
|
|
863
|
+
const PORT = parseInt(process.env.PORT || '8080');
|
|
864
|
+
|
|
865
|
+
const createContext = (
|
|
866
|
+
opts: CreateHTTPContextOptions | CreateWSSContextFnOptions,
|
|
867
|
+
): Context => ({});
|
|
868
|
+
|
|
869
|
+
const handler = createHTTPHandler({
|
|
870
|
+
router: appRouter,
|
|
871
|
+
middleware: cors(),
|
|
872
|
+
createContext,
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
const server = createServer((req, res) => {
|
|
876
|
+
const url = new URL(req.url || '', \`https://\${req.headers.host}\`);
|
|
877
|
+
|
|
878
|
+
// Handle bedrock agentcore health check
|
|
879
|
+
if (url.pathname === '/ping') {
|
|
880
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
881
|
+
res.end('Healthy');
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Handle other requests with tRPC
|
|
886
|
+
handler(req, res);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
const wss = new WebSocketServer({
|
|
890
|
+
server,
|
|
891
|
+
path: '/ws',
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
applyWSSHandler<AppRouter>({
|
|
895
|
+
wss,
|
|
896
|
+
router: appRouter,
|
|
897
|
+
createContext,
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
server.listen(PORT);
|
|
901
|
+
|
|
902
|
+
console.log(\`TRPC server listening on port \${PORT}\`);
|
|
903
|
+
"
|
|
904
|
+
`;
|
|
905
|
+
|
|
906
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-init.ts 1`] = `
|
|
907
|
+
"import { initTRPC } from '@trpc/server';
|
|
908
|
+
|
|
909
|
+
// eslint-disable-next-line
|
|
910
|
+
export interface Context {}
|
|
911
|
+
|
|
912
|
+
export const t = initTRPC.context<Context>().create();
|
|
913
|
+
|
|
914
|
+
export const publicProcedure = t.procedure;
|
|
915
|
+
"
|
|
916
|
+
`;
|
|
917
|
+
|
|
918
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-router.ts 1`] = `
|
|
919
|
+
"import { publicProcedure, t } from './init.js';
|
|
920
|
+
import { z } from 'zod';
|
|
921
|
+
import { zAsyncIterable } from './schema/z-async-iterable.js';
|
|
922
|
+
import { getAgent } from './agent.js';
|
|
923
|
+
|
|
924
|
+
export const router = t.router;
|
|
925
|
+
|
|
926
|
+
export const appRouter = router({
|
|
927
|
+
invoke: publicProcedure
|
|
928
|
+
.input(z.object({ message: z.string() }))
|
|
929
|
+
.output(
|
|
930
|
+
zAsyncIterable({
|
|
931
|
+
yield: z.string(),
|
|
932
|
+
tracked: false,
|
|
933
|
+
}),
|
|
934
|
+
)
|
|
935
|
+
.subscription(async function* (opts) {
|
|
936
|
+
const agent = getAgent();
|
|
937
|
+
|
|
938
|
+
for await (const event of agent.stream(opts.input.message)) {
|
|
939
|
+
if (
|
|
940
|
+
event.type === 'modelContentBlockDeltaEvent' &&
|
|
941
|
+
event.delta.type === 'textDelta'
|
|
942
|
+
) {
|
|
943
|
+
yield event.delta.text;
|
|
944
|
+
} else if (event.type === 'modelMessageStopEvent') {
|
|
945
|
+
yield '\\n';
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return;
|
|
949
|
+
}),
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
export type AppRouter = typeof appRouter;
|
|
953
|
+
"
|
|
954
|
+
`;
|
|
955
|
+
|
|
956
|
+
exports[`ts#strands-agent generator > should match snapshot for generated files > strands-agent-z-async-iterable.ts 1`] = `
|
|
957
|
+
"import { isTrackedEnvelope, tracked, TrackedEnvelope } from '@trpc/server';
|
|
958
|
+
import { z } from 'zod';
|
|
959
|
+
|
|
960
|
+
function isAsyncIterable<TValue, TReturn = unknown>(
|
|
961
|
+
value: unknown,
|
|
962
|
+
): value is AsyncIterable<TValue, TReturn> {
|
|
963
|
+
return !!value && typeof value === 'object' && Symbol.asyncIterator in value;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const trackedEnvelopeSchema =
|
|
967
|
+
z.custom<TrackedEnvelope<unknown>>(isTrackedEnvelope);
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* A Zod schema helper designed specifically for validating async iterables. This schema ensures that:
|
|
971
|
+
* 1. The value being validated is an async iterable.
|
|
972
|
+
* 2. Each item yielded by the async iterable conforms to a specified type.
|
|
973
|
+
* 3. The return value of the async iterable, if any, also conforms to a specified type.
|
|
974
|
+
*/
|
|
975
|
+
export function zAsyncIterable<
|
|
976
|
+
TYieldIn,
|
|
977
|
+
TYieldOut,
|
|
978
|
+
TReturnIn = void,
|
|
979
|
+
TReturnOut = void,
|
|
980
|
+
Tracked extends boolean = false,
|
|
981
|
+
>(opts: {
|
|
982
|
+
/**
|
|
983
|
+
* Validate the value yielded by the async generator
|
|
984
|
+
*/
|
|
985
|
+
yield: z.ZodType<TYieldOut, TYieldIn>;
|
|
986
|
+
/**
|
|
987
|
+
* Validate the return value of the async generator
|
|
988
|
+
* @remark not applicable for subscriptions
|
|
989
|
+
*/
|
|
990
|
+
return?: z.ZodType<TReturnOut, TReturnIn>;
|
|
991
|
+
/**
|
|
992
|
+
* Whether if the yielded values are tracked
|
|
993
|
+
* @remark only applicable for subscriptions
|
|
994
|
+
*/
|
|
995
|
+
tracked?: Tracked;
|
|
996
|
+
}) {
|
|
997
|
+
return z
|
|
998
|
+
.custom<
|
|
999
|
+
AsyncIterable<
|
|
1000
|
+
Tracked extends true ? TrackedEnvelope<TYieldIn> : TYieldIn,
|
|
1001
|
+
TReturnIn
|
|
1002
|
+
>
|
|
1003
|
+
>((val) => isAsyncIterable(val))
|
|
1004
|
+
.transform(async function* (iter) {
|
|
1005
|
+
const iterator = iter[Symbol.asyncIterator]();
|
|
1006
|
+
try {
|
|
1007
|
+
let next;
|
|
1008
|
+
while ((next = await iterator.next()) && !next.done) {
|
|
1009
|
+
if (opts.tracked) {
|
|
1010
|
+
const [id, data] = trackedEnvelopeSchema.parse(next.value);
|
|
1011
|
+
yield tracked(id, await opts.yield.parseAsync(data));
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
yield opts.yield.parseAsync(next.value);
|
|
1015
|
+
}
|
|
1016
|
+
if (opts.return) {
|
|
1017
|
+
return await opts.return.parseAsync(next.value);
|
|
1018
|
+
}
|
|
1019
|
+
return;
|
|
1020
|
+
} finally {
|
|
1021
|
+
await iterator.return?.();
|
|
1022
|
+
}
|
|
1023
|
+
}) as z.ZodType<
|
|
1024
|
+
AsyncIterable<
|
|
1025
|
+
Tracked extends true ? TrackedEnvelope<TYieldIn> : TYieldIn,
|
|
1026
|
+
TReturnIn
|
|
1027
|
+
>,
|
|
1028
|
+
AsyncIterable<
|
|
1029
|
+
Tracked extends true ? TrackedEnvelope<TYieldOut> : TYieldOut,
|
|
1030
|
+
TReturnOut
|
|
1031
|
+
>
|
|
1032
|
+
>;
|
|
1033
|
+
}
|
|
1034
|
+
"
|
|
1035
|
+
`;
|