Configurar um monorepo
Tarefa 1: Criar um monorepo
Seção intitulada “Tarefa 1: Criar um monorepo”Para criar um novo monorepo, dentro do diretório desejado, execute o seguinte comando:
pnpm create @aws/nx-workspace dungeon-adventure --iac=cdkyarn create @aws/nx-workspace dungeon-adventure --iac=cdknpm create @aws/nx-workspace -- dungeon-adventure --iac=cdkbun create @aws/nx-workspace dungeon-adventure --iac=cdkIsso configurará um monorepo NX dentro do diretório dungeon-adventure. Quando você abrir o diretório no VSCode, verá esta estrutura de arquivos:
Directory.nx/
- …
Directory.vscode/
- …
Directorynode_modules/
- …
Directorypackages/ aqui é onde seus sub-projetos residirão
- …
- .gitignore
- biome.json configura o Biome para linting e formatação
- nx.json configura o Nx CLI e padrões do monorepo
- package.json todas as dependências node são definidas aqui
- pnpm-lock.yaml ou bun.lock, yarn.lock, package-lock.json dependendo do gerenciador de pacotes
- pnpm-workspace.yaml se usar pnpm
- README.md
- tsconfig.base.json todos os sub-projetos baseados em node estendem este
- tsconfig.json
- aws-nx-plugin.config.mts configuração para o Nx Plugin para AWS
Agora podemos começar a criar nossos diferentes sub-projetos usando o @aws/nx-plugin.
Tarefa 2: Criar uma Game API
Seção intitulada “Tarefa 2: Criar uma Game API”Primeiro, vamos criar nossa Game API. Para isso, crie uma API tRPC chamada GameApi usando estas etapas:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#api - Preencha os parâmetros obrigatórios
- name: GameApi
- framework: trpc
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactiveyarn nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactivenpx nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactivebunx nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#api --name=GameApi --framework=trpc --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem na sua árvore de arquivos.
Arquivos atualizados do ts#api
Abaixo está uma lista de todos os arquivos gerados pelo gerador ts#api. Vamos examinar alguns dos arquivos-chave destacados na árvore de arquivos:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ constructs cdk específicos do app
Directoryapis/
- game-api.ts construct cdk para criar sua API tRPC
- index.ts
- …
- index.ts
Directorycore/ constructs cdk genéricos
Directoryapi/
- rest-api.ts construct cdk base para uma API Gateway Rest API
- trpc-utils.ts utilitários para constructs de API tRPC cdk
- utils.ts utilitários para constructs de API
- index.ts
- runtime-config.ts
- index.ts
- project.json
- …
Directorygame-api/ API tRPC
Directorysrc/
Directoryclient/ cliente vanilla tipicamente usado para chamadas ts máquina a máquina
- index.ts
Directorymiddleware/ instrumentação powertools
- error.ts
- index.ts
- logger.ts
- metrics.ts
- tracer.ts
Directoryschema/ definições de entradas e saídas para sua API
- index.ts
- echo.ts schema de entrada e saída de exemplo
- z-async-iterable.ts schema Zod wrapper para saída de subscription tRPC
Directoryprocedures/ implementações específicas para seus procedimentos/rotas de API
- echo.ts implementação de procedimento de exemplo
- index.ts
- init.ts configura contexto e middleware
- handler.ts ponto de entrada do handler Lambda (usa response streaming para REST APIs)
- local-server.ts usado ao executar o servidor tRPC localmente
- router.ts define o router tRPC e todos os procedimentos
- project.json
- …
- vitest.workspace.ts
Vamos analisar estes arquivos-chave:
import { echo } from './procedures/echo.js';import { t } from './init.js';
export const router = t.router;
export const appRouter = router({ echo,});
export type AppRouter = typeof appRouter;O router define o router tRPC para sua API e é o lugar onde você declarará todos os seus métodos de API. Como você pode ver acima, temos um método chamado echo com sua implementação no arquivo ./procedures/echo.ts. O ponto de entrada do handler Lambda está em handler.ts, que é configurado automaticamente pelo gerador.
import { publicProcedure } from '../init.js';import { EchoInputSchema, EchoOutputSchema,} from '../schema/echo.js';
export const echo = publicProcedure .input(EchoInputSchema) .output(EchoOutputSchema) .query((opts) => ({ result: opts.input.message }));Este arquivo é a implementação do método echo e como você pode ver é fortemente tipado ao declarar suas estruturas de dados de entrada e saída.
import { z } from 'zod';
export const EchoInputSchema = z.object({ message: z.string().max(1024),});
export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
export const EchoOutputSchema = z.object({ message: z.string().max(1024),});
export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;Todas as definições de schema tRPC são definidas usando Zod e são exportadas como tipos typescript via sintaxe z.TypeOf.
import { Construct } from 'constructs';import * as url from 'url';import { Distribution } from 'aws-cdk-lib/aws-cloudfront';import { Code, Runtime, Function, FunctionProps, Tracing,} from 'aws-cdk-lib/aws-lambda';import { AuthorizationType, LambdaIntegration, ResponseTransferMode,} from 'aws-cdk-lib/aws-apigateway';import { Aspects, Duration } from 'aws-cdk-lib';import { PolicyDocument, PolicyStatement, Effect, AnyPrincipal, IGrantable, Grant,} from 'aws-cdk-lib/aws-iam';import { IntegrationBuilder, RestApiIntegration,} from '../../core/api/utils.js';import { AddCorsPreflightAspect, RestApi } from '../../core/api/rest-api.js';import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';import { AppRouter, appRouter } from ':dungeon-adventure/game-api';
// Tipo union de string para todos os nomes de operação da APItype Operations = Procedures<AppRouter>;
/** * Propriedades para criar um construct GameApi * * @template TIntegrations - Mapa de nomes de operação para suas integrações */export interface GameApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { /** * Mapa de nomes de operação para suas integrações API Gateway */ integrations: TIntegrations;}
/** * Um construct CDK que cria e configura uma AWS API Gateway REST API * especificamente para GameApi. * @template TIntegrations - Mapa de nomes de operação para suas integrações */export class GameApi< TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> { private allowedOrigins: readonly string[] = ['*'];
/** * Cria integrações padrão para todas as operações, que implementam cada operação como * sua própria função lambda individual. * * @param scope - O escopo do construct CDK * @returns Um IntegrationBuilder com integrações lambda padrão */ public static defaultIntegrations = (scope: Construct) => { return IntegrationBuilder.rest({ operations: routerToOperations(appRouter), defaultIntegrationOptions: <FunctionProps>{ runtime: Runtime.NODEJS_LATEST, handler: 'index.handler', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/game-api/bundle', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, }, buildDefaultIntegration: (op, props: FunctionProps) => { const handler = new Function(scope, `GameApi${op}Handler`, props); return { handler, integration: new LambdaIntegration(handler, { responseTransferMode: ResponseTransferMode.STREAM, }), }; }, }); };
constructor( scope: Construct, id: string, props: GameApiProps<TIntegrations>, ) { super(scope, id, { apiName: 'GameApi', defaultMethodOptions: { authorizationType: AuthorizationType.IAM, }, deployOptions: { tracingEnabled: true, }, policy: new PolicyDocument({ statements: [ // Abre OPTIONS para permitir que navegadores façam requisições preflight não autenticadas new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: routerToOperations(appRouter), ...props, }); Aspects.of(this).add(new AddCorsPreflightAspect(() => this.allowedOrigins)); }
/** * Restringe CORS às origens fornecidas * * Configura os domínios de distribuição CloudFront ou strings de origem * como as únicas origens CORS permitidas nas respostas preflight do API Gateway e nas * integrações AWS Lambda. * * @param origins - As strings de origem, distribuições CloudFront, ou objetos contendo uma distribuição CloudFront para conceder CORS de */ public restrictCorsTo( ...origins: (string | Distribution | { cloudFrontDistribution: Distribution })[] ) { const allowedOrigins = origins.map((origin) => typeof origin === 'string' ? origin : 'cloudFrontDistribution' in origin ? `https://${origin.cloudFrontDistribution.distributionDomainName}` : `https://${origin.distributionDomainName}`, );
this.allowedOrigins = allowedOrigins;
// Define a variável de ambiente ALLOWED_ORIGINS para todas as integrações Lambda Object.values(this.integrations).forEach((integration) => { if ('handler' in integration && integration.handler instanceof Function) { integration.handler.addEnvironment( 'ALLOWED_ORIGINS', allowedOrigins.join(','), ); } }); }
/** * Concede permissões IAM para invocar qualquer método nesta API. * * @param grantee - O principal IAM para conceder permissões a */ public grantInvokeAccess(grantee: IGrantable) { // Aqui concedemos permissão ao grantee para chamar a api. // Acesso refinado máquina a máquina pode ser definido aqui usando principals mais específicos (ex: roles ou // usuários) e recursos (ex: quais caminhos de api podem ser invocados por qual principal) se necessário. this.api.addToResourcePolicy( new PolicyStatement({ effect: Effect.ALLOW, principals: [grantee.grantPrincipal], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), );
Grant.addToPrincipal({ grantee, actions: ['execute-api:Invoke'], resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')], }); }}Este é o construct CDK que define nossa GameApi. Ele fornece um método defaultIntegrations que cria automaticamente uma função Lambda para cada procedimento em nossa API tRPC, apontando para a implementação da API empacotada. Isso significa que no tempo de cdk synth, o empacotamento não ocorre (ao contrário de usar NodeJsFunction) pois já empacotamos como parte do target de build do projeto backend.
Tarefa 3: Criar agentes de história
Seção intitulada “Tarefa 3: Criar agentes de história”Agora vamos criar nossos Agentes de História.
Agente de história: Projeto Python
Seção intitulada “Agente de história: Projeto Python”Para criar um projeto Python:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - py#project - Preencha os parâmetros obrigatórios
- name: story
- Clique em
Generate
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactiveyarn nx g @aws/nx-plugin:py#project --name=story --no-interactivenpx nx g @aws/nx-plugin:py#project --name=story --no-interactivebunx nx g @aws/nx-plugin:py#project --name=story --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem na sua árvore de arquivos.
Arquivos atualizados do py#project
O py#project gera estes arquivos:
Directory.venv/ ambiente virtual único para o monorepo
- …
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ módulo python
- …
Directorytests/
- …
- .python-version
- pyproject.toml
- project.json
- .python-version versão python fixada do uv
- pyproject.toml
- uv.lock
Isso configurou um projeto Python e UV Workspace com ambiente virtual compartilhado.
Agente de história
Seção intitulada “Agente de história”Para adicionar um agente Strands ao projeto com o gerador py#agent:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - py#agent - Preencha os parâmetros obrigatórios
- project: story
- auth: cognito
- protocol: ag-ui
- Clique em
Generate
pnpm nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactiveyarn nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactivenpx nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactivebunx nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#agent --project=story --auth=cognito --protocol=ag-ui --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem na sua árvore de arquivos.
Arquivos atualizados do py#agent
O py#agent gera estes arquivos:
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ módulo python
Directoryagent/
- init.py configura o app FastAPI e middleware
- main.py ponto de entrada para seu agente no Bedrock AgentCore Runtime
- agent.py define um agente e ferramentas de exemplo
- Dockerfile define a imagem docker para implantação no AgentCore Runtime
Directorycommon/constructs/
Directorysrc
Directoryapp/agents/story-agent/
- story-agent.ts construct para implantar seu agente Story no AgentCore Runtime
Vamos analisar alguns dos arquivos em detalhes:
from contextlib import contextmanager
from strands import Agent, toolfrom strands_tools import current_time
@tooldef subtract(a: int, b: int) -> int: return a - b
@contextmanagerdef get_agent(): yield Agent( name="StoryAgent", description="StoryAgent Agent", system_prompt="""You are a mathematical wizard.Use your tools for mathematical tasks.Refer to tools as your 'spellbook'.""", tools=[subtract, current_time], )Isso cria um agente Strands de exemplo e define uma ferramenta de subtração.
import loggingimport uuid
from ag_ui_strands import StrandsAgent, create_strands_appfrom dungeon_adventure_agent_connection import session_id_contextfrom fastapi import Requestfrom starlette.middleware.base import BaseHTTPMiddleware
from .agent import get_agent
logging.basicConfig(level=logging.INFO)
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
# Create AG-UI agent wrapper_agent_ctx = get_agent()_agent = _agent_ctx.__enter__()
agui_agent = StrandsAgent( agent=_agent, name="StoryAgent", description="Um Agente exposto via protocolo AG-UI.",)
class _SessionIdMiddleware(BaseHTTPMiddleware): """Vincula o session ID para esta requisição para que clientes MCP / A2A downstream o encaminhem em chamadas de saída."""
async def dispatch(self, request: Request, call_next): session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4()) with session_id_context(session_id): return await call_next(request)
# Create FastAPI app with AG-UI endpoint and health checkapp = create_strands_app(agui_agent, path="/invocations")app.add_middleware(_SessionIdMiddleware)Este é o ponto de entrada para o agente. Como selecionamos --protocol=AG-UI, o gerador envolve nosso Agent Strands com StrandsAgent de ag_ui_strands e o monta em um app FastAPI que fala o protocolo AG-UI — é isso que o CopilotKit conversará a partir do website React. O _SessionIdMiddleware vincula o session ID de runtime do AgentCore de entrada a um ContextVar para que qualquer cliente MCP/A2A downstream que conectarmos mais tarde (por exemplo, o servidor MCP de Inventário no Módulo 2) o encaminhe automaticamente em suas chamadas de saída. No Módulo 3 também adicionaremos um session_manager_provider para que cada thread id obtenha seu próprio S3SessionManager e o histórico de conversação persista entre turnos.
import { Lazy, Names } from 'aws-cdk-lib';import { Platform } from 'aws-cdk-lib/aws-ecr-assets';import { Construct } from 'constructs';import { execSync } from 'child_process';import * as path from 'path';import * as url from 'url';import { AgentRuntimeArtifact, ProtocolType, Runtime, RuntimeProps,} from 'aws-cdk-lib/aws-bedrockagentcore';import { IGrantable, IPrincipal } from 'aws-cdk-lib/aws-iam';
export type StoryAgentProps = Omit< RuntimeProps, 'runtimeName' | 'protocolConfiguration' | 'agentRuntimeArtifact'>;
export class StoryAgent extends Construct implements IGrantable { public readonly dockerImage: AgentRuntimeArtifact; public readonly agentCoreRuntime: Runtime;
constructor(scope: Construct, id: string, props?: StoryAgentProps) { super(scope, id);
this.dockerImage = AgentRuntimeArtifact.fromAsset( path.dirname(url.fileURLToPath(new URL(import.meta.url))), { platform: Platform.LINUX_ARM64, extraHash: execSync( `docker inspect dungeon-adventure-story-agent:latest --format '{{.Id}}'`, { encoding: 'utf-8' }, ).trim(), }, );
this.agentCoreRuntime = new Runtime(this, 'StoryAgent', { runtimeName: Lazy.string({ produce: () => Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }), }), protocolConfiguration: ProtocolType.HTTP, agentRuntimeArtifact: this.dockerImage, ...props, }); }
public get grantPrincipal(): IPrincipal { return this.agentCoreRuntime.grantPrincipal; }}Isso configura um AgentRuntimeArtifact CDK que faz upload da sua imagem Docker do agente para o ECR e a hospeda usando AgentCore Runtime.
Você pode notar um Dockerfile extra, que referencia a imagem Docker do projeto story, permitindo que co-localizemos o Dockerfile e o código-fonte do agente.
Tarefa 4: Configurar ferramentas de inventário
Seção intitulada “Tarefa 4: Configurar ferramentas de inventário”Inventário: Projeto TypeScript
Seção intitulada “Inventário: Projeto TypeScript”Vamos criar um servidor MCP para fornecer ferramentas para nosso Story Agent gerenciar o inventário de um jogador.
Primeiro, criamos um projeto TypeScript:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#project - Preencha os parâmetros obrigatórios
- name: inventory
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactiveyarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactivenpx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactivebunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runIsso criará um projeto TypeScript vazio.
Arquivos atualizados do ts#project
O gerador ts#project gera estes arquivos.
Directorypackages/
Directoryinventory/
Directorysrc/
- index.ts ponto de entrada com função de exemplo
- project.json configuração do projeto
- vitest.config.mts configuração de teste
- tsconfig.json configuração typescript base para o projeto
- tsconfig.lib.json configuração typescript para o projeto direcionada para compilação e empacotamento
- tsconfig.spec.json configuração typescript para testes
- tsconfig.base.json atualizado para configurar um alias para outros projetos referenciarem este
Inventário: Servidor MCP
Seção intitulada “Inventário: Servidor MCP”Em seguida, adicionaremos um servidor MCP ao nosso projeto TypeScript:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#mcp-server - Preencha os parâmetros obrigatórios
- project: inventory
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactiveyarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactivenpx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactivebunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runIsso adicionará um servidor MCP.
Arquivos atualizados do ts#mcp-server
O gerador ts#mcp-server gera estes arquivos.
Directorypackages/
Directoryinventory/
Directorysrc/mcp-server/
- index.ts barrel export
- server.ts cria o servidor MCP
Directorytools/
- divide.ts ferramenta de exemplo
Directoryresources/
- sample-guidance.ts recurso de exemplo
- stdio.ts ponto de entrada para MCP com transporte STDIO
- http.ts ponto de entrada para MCP com transporte HTTP streamable
- Dockerfile constrói a imagem para AgentCore Runtime
- rolldown.config.ts configuração para empacotar o servidor MCP para implantação no AgentCore
Directorycommon/constructs/
Directorysrc
Directoryapp/mcp-servers/inventory-mcp-server/
- inventory-mcp-server.ts construct para implantar seu servidor MCP de inventário no AgentCore Runtime
Tarefa 5: Criar o banco de dados do jogo
Seção intitulada “Tarefa 5: Criar o banco de dados do jogo”O estado do nosso jogo — jogos salvos e o inventário de cada jogador — reside no Amazon DynamoDB. Crie um projeto DynamoDB chamado DungeonDb com o gerador ts#dynamodb:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#dynamodb - Preencha os parâmetros obrigatórios
- name: DungeonDb
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactiveyarn nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactivenpx nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactivebunx nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#dynamodb --name=DungeonDb --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem na sua árvore de arquivos.
Arquivos atualizados do ts#dynamodb
O gerador ts#dynamodb gera estes arquivos.
Directorypackages/
Directorydungeon-db/
- config.json configuração do DynamoDB incluindo porta, nome da tabela, configurações do contêiner e Global Secondary Indexes
Directorysrc/
- index.ts ponto de entrada e exports
- client.ts singleton do cliente DynamoDB e resolução do nome da tabela
Directoryentities/
- example.ts entidade ElectroDB de exemplo (vamos substituir isso)
- index.ts exports de entidades
- project.json adiciona os targets
devepull-image
Directorycommon/
Directoryscripts/
Directorysrc/
Directorydynamodb/
- create-local-table.ts cria a tabela no DynamoDB Local
- pull-image.ts puxa a imagem do DynamoDB Local
- start-container.ts inicia o contêiner do DynamoDB Local
Directoryconstructs/
Directorysrc/
Directoryapp/dynamodb/
- dungeon-db.ts construct para provisionar sua tabela
Directorycore/
- dynamodb.ts construct de tabela DynamoDB genérico
O src/client.ts gerado exporta getDynamoDBClient() e resolveTableName(). Quando SERVE_LOCAL=true (definido automaticamente pelos targets serve-local) estes se conectam ao DynamoDB Local; caso contrário, eles se conectam à AWS e resolvem o nome da tabela implantada a partir da Configuração de Runtime. Vamos modelar nossas entidades Game e Inventory neste projeto no Módulo 2.
Para mais detalhes, consulte o guia do gerador ts#dynamodb.
Tarefa 6: Criar a Interface do Usuário (UI)
Seção intitulada “Tarefa 6: Criar a Interface do Usuário (UI)”Nesta tarefa, criaremos a UI que permitirá interagir com o jogo.
UI do Jogo: Website
Seção intitulada “UI do Jogo: Website”Para criar a UI, crie um website chamado GameUI usando estas etapas:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#website - Preencha os parâmetros obrigatórios
- name: GameUI
- ux: shadcn
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactiveyarn nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactivenpx nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactivebunx nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#website --name=GameUI --ux=shadcn --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem na sua árvore de arquivos.
Arquivos atualizados do ts#website
O ts#website gera estes arquivos. Vamos examinar alguns dos arquivos-chave destacados na árvore de arquivos:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ constructs cdk específicos do app
Directorystatic-websites/
- game-ui.ts construct cdk para criar sua Game UI
Directorycore/
- static-website.ts construct de website estático genérico
Directorygame-ui/
Directorypublic/
- …
Directorysrc/
Directorycomponents/
DirectoryAppLayout/
- index.tsx layout geral da página usando shadcn
SidebarProvider+ cabeçalho
- index.tsx layout geral da página usando shadcn
- app-sidebar.tsx sidebar shadcn padrão com itens de navegação
- alert.tsx, spinner.tsx primitivos de feedback envolvidos em shadcn
Directoryroutes/ rotas baseadas em arquivo @tanstack/react-router
- index.tsx página raiz ’/’
- __root.tsx todas as páginas usam este componente como base
- config.ts
- main.tsx ponto de entrada React
- routeTree.gen.ts atualizado automaticamente pelo @tanstack/react-router
- styles.css importa globais shadcn compartilhados (Tailwind v4)
- index.html
- project.json
- vite.config.mts
- …
Directorycommon/
Directoryshadcn/ biblioteca shadcn/ui compartilhada (tokens de tema,
Button,Card,Input,Sidebar, …) importada por todo websiteuxProvider=Shadcn- src/components/ui/*
- src/styles/globals.css tokens de design Tailwind + shadcn
- …
import * as url from 'url';import { Construct } from 'constructs';import { StaticWebsite } from '../../core/index.js';
export class GameUI extends StaticWebsite { constructor(scope: Construct, id: string) { super(scope, id, { websiteName: 'GameUI', websiteFilePath: url.fileURLToPath( new URL( '../../../../../../dist/packages/game-ui/bundle', import.meta.url, ), ), }); }}Este é o construct CDK que define nossa GameUI. Ele já configurou o caminho do arquivo para o bundle gerado para nossa UI baseada em Vite. Isso significa que no tempo de build, o empacotamento ocorre dentro do target de build do projeto game-ui e a saída é usada aqui.
import React from 'react';import { createRoot } from 'react-dom/client';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import './styles.css';
export type RouterProviderContext = {};
const router = createRouter({ routeTree, context: {} });
declare module '@tanstack/react-router' { interface Register { router: typeof router; }}
const App = () => <RouterProvider router={router} context={{}} />;
const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <App /> </React.StrictMode>, );Este é o ponto de entrada onde o React é montado. A estilização vem de tokens Tailwind v4 importados via styles.css. O @tanstack/react-router é configurado no modo de roteamento baseado em arquivo: enquanto o servidor de desenvolvimento estiver rodando, qualquer arquivo que você criar em routes/ é detectado automaticamente e a árvore de rotas é regenerada. Geradores posteriores (auth, connection) farão patch AST neste arquivo para envolver <App /> em providers adicionais.
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/')({ component: RouteComponent,});
function RouteComponent() { return ( <div className="text-center"> <header> <h1>Welcome</h1> <p>Welcome to your new React website!</p> </header> </div> );}Um componente será renderizado ao navegar para a rota /. O @tanstack/react-router gerenciará a Route para você sempre que você criar/mover este arquivo (desde que o servidor de desenvolvimento esteja rodando).
UI do Jogo: Autenticação
Seção intitulada “UI do Jogo: Autenticação”Vamos configurar nossa Game UI para exigir acesso autenticado via Amazon Cognito usando estas etapas:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#website#auth - Preencha os parâmetros obrigatórios
- cognitoDomain: game-ui
- project: @dungeon-adventure/game-ui
- allowSignup: true
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactiveyarn nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivenpx nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivebunx nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem/mudarem na sua árvore de arquivos.
Arquivos atualizados do ts#website#auth
O gerador ts#website#auth atualiza/gera estes arquivos. Vamos examinar alguns dos arquivos-chave destacados na árvore de arquivos:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
- user-identity.ts construct cdk para criar pools de usuário/identidade
Directorygame-ui/
Directorysrc/
Directorycomponents/
DirectoryAppLayout/
- index.tsx adiciona o usuário logado/logout ao cabeçalho
DirectoryCognitoAuth/
- index.tsx gerencia login no Cognito
DirectoryRuntimeConfig/
- index.tsx busca o
runtime-config.jsone o fornece aos filhos via contexto
- index.tsx busca o
Directoryhooks/
- useRuntimeConfig.tsx
- main.tsx Atualizado para adicionar Cognito
import { useAuth } from 'react-oidc-context';import CognitoAuth from './components/CognitoAuth';import { useRuntimeConfig } from './hooks/useRuntimeConfig';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import './styles.css';export type RouterProviderContext = {};export type RouterProviderContext = { runtimeConfig?: ReturnType<typeof useRuntimeConfig>; auth?: ReturnType<typeof useAuth>;};const router = createRouter({ routeTree, context: {} });const router = createRouter({ routeTree, context: { runtimeConfig: undefined, auth: undefined },});// Registra a instância do router para segurança de tipodeclare module '@tanstack/react-router' { interface Register { router: typeof router; }}const App = () => <RouterProvider router={router} context={{}} />;const App = () => { const auth = useAuth(); const runtimeConfig = useRuntimeConfig(); return <RouterProvider router={router} context={{ runtimeConfig, auth }} />;};const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <RuntimeConfigProvider> <CognitoAuth> <App /> </CognitoAuth> </RuntimeConfigProvider> </React.StrictMode>, );Os componentes RuntimeConfigProvider e CognitoAuth foram adicionados ao arquivo main.tsx via uma transformação AST. Isso permite que o componente CognitoAuth autentique com o Amazon Cognito buscando o runtime-config.json que contém a configuração de conexão cognito necessária para fazer as chamadas backend para o destino correto.
UI do Jogo: Conectar à Game API
Seção intitulada “UI do Jogo: Conectar à Game API”Vamos configurar nossa Game UI para se conectar à nossa Game API criada anteriormente.
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - connection - Preencha os parâmetros obrigatórios
- sourceProject: @dungeon-adventure/game-ui
- targetProject: @dungeon-adventure/game-api
- Clique em
Generate
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactiveyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivenpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivebunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runnpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runbunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem/mudarem na sua árvore de arquivos.
Arquivos atualizados da conexão UI -> tRPC
O gerador connection gera/atualiza estes arquivos. Vamos examinar alguns dos arquivos-chave destacados na árvore de arquivos:
Directorypackages/
Directorygame-ui/
Directorysrc/
Directorycomponents/
- GameApiClientProvider.tsx configura o cliente GameAPI
Directoryhooks/
- useGameApi.tsx hooks para chamar a GameApi
- main.tsx injeta os providers do cliente trpc
- package.json
import { useContext } from 'react';import { GameApiTRPCContext } from '../components/GameApiClientProvider';
export const useGameApi = () => { const container = useContext(GameApiTRPCContext); if (!container) { throw new Error('useGameApi must be used within GameApiClientProvider'); } return container.optionsProxy;};
export const useGameApiClient = () => { const container = useContext(GameApiTRPCContext); if (!container) { throw new Error( 'useGameApiClient must be used within GameApiClientProvider', ); } return container.client;};Este hook fornece acesso ao cliente tRPC para chamar a GameApi. Para exemplos de como chamar APIs tRPC, consulte o guia de uso do hook tRPC.
import GameApiClientProvider from './components/GameApiClientProvider';import QueryClientProvider from './components/QueryClientProvider';import { useAuth } from 'react-oidc-context';import CognitoAuth from './components/CognitoAuth';import { useRuntimeConfig } from './hooks/useRuntimeConfig';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import './styles.css';...const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <RuntimeConfigProvider> <CognitoAuth> <QueryClientProvider> <GameApiClientProvider> <App /> </GameApiClientProvider> </QueryClientProvider> </CognitoAuth> </RuntimeConfigProvider> </React.StrictMode>, );O arquivo main.tsx foi atualizado via uma transformação AST para injetar os providers tRPC.
Agente de história: Conectar ao servidor MCP de inventário
Seção intitulada “Agente de história: Conectar ao servidor MCP de inventário”Vamos conectar nosso Agente de História ao servidor MCP de inventário para que o agente possa descobrir e invocar as ferramentas do servidor MCP.
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - connection - Preencha os parâmetros obrigatórios
- sourceProject: story
- targetProject: inventory
- Clique em
Generate
pnpm nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactiveyarn nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactivenpx nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactivebunx nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:connection --sourceProject=story --targetProject=inventory --no-interactive --dry-runArquivos atualizados da conexão Story Agent -> Inventory MCP
O gerador connection gera/atualiza estes arquivos:
Directorypackages/
Directorycommon/
Directoryagent_connection/
Directorydungeon_adventure_agent_connection/
Directorycore/
- agentcore_endpoints.py Resolução de ARN/URL independente de framework
- agentcore_mcp_transport.py Transporte MCP independente de framework
- agentcore_mcp_client_strands.py Cliente MCP Strands envolvendo o transporte
Directoryauth/
httpx.Authindependente de framework para SigV4 / encaminhamento de sessão- …
Directoryapp/
- inventory_mcp_server_client_strands.py Cliente Strands para conectar ao servidor MCP de inventário
- __init__.py Re-exporta clientes por conexão
Directorystory/
Directorydungeon_adventure_story/agent/
- agent.py Modificado para importar e usar o cliente MCP
O gerador:
- Cria um projeto Python compartilhado
agent_connection(se ainda não existir) com oAgentCoreMCPClientStrandsprincipal - Gera uma classe
InventoryMcpServerClientStrandsque lida com a conexão ao servidor MCP tanto localmente (HTTP direto) quanto quando implantado (via AgentCore com autenticação IAM) - Transforma
agent.pypara importar o cliente, criar uma instância e conectar as ferramentas do servidor MCP ao agente - Adiciona o projeto
agent_connectioncomo uma dependência de workspace do projeto story - Atualiza o target
devpara iniciar automaticamente o servidor MCP ao rodar localmente
Para mais detalhes, consulte o guia de conexão Python Agent para MCP.
UI do Jogo: Conectar ao Agente de História
Seção intitulada “UI do Jogo: Conectar ao Agente de História”Vamos conectar nossa Game UI ao Agente de História. Como o agente fala AG-UI, o gerador connection conecta o CopilotKit: um componente de chat com tema e um HttpAgent @ag-ui/client pronto para renderizar.
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - connection - Preencha os parâmetros obrigatórios
- sourceProject: @dungeon-adventure/game-ui
- targetProject: story
- Clique em
Generate
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactiveyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactivenpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactivebunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-ui --targetProject=story --no-interactive --dry-runArquivos atualizados da conexão UI -> Story Agent
O gerador connection gera/atualiza estes arquivos:
Directorypackages/
Directorygame-ui/
Directorysrc/
Directorycomponents/
- AguiProvider.tsx
CopilotKitProvidercom todos os agentes AG-UI conectados registrados Directorycopilot/
- index.tsx
CopilotChat/CopilotSidebar/CopilotPopupcom tema Shadcn - ShadcnAssistantMessage.tsx, ShadcnUserMessage.tsx, ShadcnChatInput.tsx, ShadcnCursor.tsx, copilot.css
- index.tsx
- AguiProvider.tsx
Directoryhooks/
- useAguiStoryAgent.tsx Constrói um
HttpAgent, injeta o token bearer Cognito e preenchethreadIdpara o session id de 33 caracteres do AgentCore
- useAguiStoryAgent.tsx Constrói um
- main.tsx Envolve
<App />em<AguiProvider>
O gerador:
- Detecta o
uxProviderdo website React (Shadcn aqui) e fornece componentes de chat correspondentes. - Registra todos os agentes conectados em um único
CopilotKitProvider— executar novamente para outro agente apenas adiciona outro hook. - Lê o ARN de runtime do agente da Configuração de Runtime, constrói a URL de invocação do AgentCore e anexa o token bearer Cognito mais o cabeçalho de session id do AgentCore.
Para mais detalhes, consulte o guia de conexão React para AG-UI.
Conectar a Game API e o servidor MCP de inventário ao banco de dados
Seção intitulada “Conectar a Game API e o servidor MCP de inventário ao banco de dados”Tanto a Game API quanto o servidor MCP de inventário leem e escrevem em nossa tabela DynamoDB, então vamos conectá-los ao projeto DungeonDb. O gerador connection detecta que o alvo é um projeto ts#dynamodb e configura o target serve-local de cada projeto de origem para iniciar o DynamoDB Local automaticamente.
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - connection - Preencha os parâmetros obrigatórios
- sourceProject: @dungeon-adventure/game-api
- targetProject: @dungeon-adventure/dungeon-db
- Clique em
Generate
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactiveyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactivenpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactivebunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runnpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runbunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/game-api --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-run- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - connection - Preencha os parâmetros obrigatórios
- sourceProject: @dungeon-adventure/inventory
- targetProject: @dungeon-adventure/dungeon-db
- Clique em
Generate
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactiveyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactivenpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactivebunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runyarn nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runnpx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runbunx nx g @aws/nx-plugin:connection --sourceProject=@dungeon-adventure/inventory --targetProject=@dungeon-adventure/dungeon-db --no-interactive --dry-runUI do Jogo: Infraestrutura
Seção intitulada “UI do Jogo: Infraestrutura”Vamos criar o sub-projeto final para a infraestrutura CDK.
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#infra - Preencha os parâmetros obrigatórios
- name: infra
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactiveyarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactivenpx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactivebunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactiveVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runVocê verá alguns novos arquivos aparecerem/mudarem na sua árvore de arquivos.
Arquivos atualizados do ts#infra
O gerador ts#infra gera/atualiza estes. Vamos examinar alguns dos arquivos-chave destacados na árvore de arquivos:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
- checkov.ts
- index.ts
Directoryinfra
Directorysrc/
Directorystages/
- application-stage.ts stacks cdk definidas aqui
Directorystacks/
- application-stack.ts recursos cdk definidos aqui
- main.ts ponto de entrada que define todos os stages
- cdk.json
- checkov.yml
- project.json
- …
- package.json
- tsconfig.json adiciona referências
- tsconfig.base.json adiciona alias
import { ApplicationStage } from './stages/application-stage.js';import { App } from ':dungeon-adventure/common-constructs';
const app = new App();
// Use isto para implantar seu próprio ambiente sandbox (assume suas credenciais CLI)new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, },});
app.synth();Este é o ponto de entrada para sua aplicação CDK.
import { Stack, StackProps } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
// O código que define sua stack vai aqui }}Vamos instanciar nossos constructs CDK para construir nosso jogo de aventura de dungeon.
Tarefa 7: Atualizar nossa infraestrutura
Seção intitulada “Tarefa 7: Atualizar nossa infraestrutura”Vamos atualizar packages/infra/src/stacks/application-stack.ts para instanciar alguns de nossos constructs gerados:
import { GameApi, GameUI, InventoryMcpServer, StoryAgent, UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib';import { Construct } from 'constructs';export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
const userIdentity = new UserIdentity(this, 'UserIdentity');
const gameApi = new GameApi(this, 'GameApi', { integrations: GameApi.defaultIntegrations(this).build(), });
const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
// Use Cognito for user authentication with the agent const storyAgent = new StoryAgent(this, 'StoryAgent', { identity: userIdentity, });
new CfnOutput(this, 'StoryAgentArn', { value: storyAgent.agentCoreRuntime.agentRuntimeArn, }); new CfnOutput(this, 'InventoryMcpArn', { value: mcpServer.agentCoreRuntime.agentRuntimeArn, });
// Grant the agent permissions to invoke our mcp server mcpServer.grantInvokeAccess(storyAgent);
// Grant the authenticated role access to invoke the api gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
new GameUI(this, 'GameUI'); }}import { Stack, StackProps } from 'aws-cdk-lib';import { GameApi, GameUI, InventoryMcpServer, StoryAgent, UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
// The code that defines your stack goes here const userIdentity = new UserIdentity(this, 'UserIdentity');
const gameApi = new GameApi(this, 'GameApi', { integrations: GameApi.defaultIntegrations(this).build(), });
const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
// Use Cognito for user authentication with the agent const storyAgent = new StoryAgent(this, 'StoryAgent', { identity: userIdentity, });
new CfnOutput(this, 'StoryAgentArn', { value: storyAgent.agentCoreRuntime.agentRuntimeArn, }); new CfnOutput(this, 'InventoryMcpArn', { value: mcpServer.agentCoreRuntime.agentRuntimeArn, });
// Grant the agent permissions to invoke our mcp server mcpServer.grantInvokeAccess(storyAgent);
// Grant the authenticated role access to invoke the api gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
new GameUI(this, 'GameUI'); }}Tarefa 8: Construir o código
Seção intitulada “Tarefa 8: Construir o código”Comandos Nx
Targets únicos vs múltiplos
Seção intitulada “Targets únicos vs múltiplos”O comando run-many executará um target em múltiplos sub-projetos listados (--all os seleciona todos). Isso garante que as dependências sejam executadas na ordem correta.
Você também pode disparar um build (ou qualquer outra tarefa) para um target de projeto único executando o target diretamente no projeto. Por exemplo, para fazer build do projeto @dungeon-adventure/infra, execute o seguinte comando:
pnpm nx build infrayarn nx build infranpx nx build infrabunx nx build infraVocê também pode omitir o escopo e usar a sintaxe abreviada do Nx se preferir:
pnpm nx build infrayarn nx build infranpx nx build infrabunx nx build infraVisualizando suas dependências
Seção intitulada “Visualizando suas dependências”Para visualizar suas dependências, execute:
pnpm nx graphyarn nx graphnpx nx graphbunx nx graph
Caching
Seção intitulada “Caching”O Nx depende de caching para que você possa reutilizar artefatos de builds anteriores e acelerar o desenvolvimento. É necessária alguma configuração para que isso funcione corretamente e pode haver casos onde você queira executar um build sem usar o cache. Para fazer isso, simplesmente adicione o argumento --skip-nx-cache ao seu comando. Por exemplo:
pnpm nx build infra --skip-nx-cacheyarn nx build infra --skip-nx-cachenpx nx build infra --skip-nx-cachebunx nx build infra --skip-nx-cacheSe por qualquer motivo você quiser limpar seu cache (armazenado na pasta .nx), você pode executar o seguinte comando:
pnpm nx resetyarn nx resetnpx nx resetbunx nx resetUsando a linha de comando, execute o seguinte comando para corrigir quaisquer problemas de lint primeiro:
pnpm lintyarn lintnpm run lintbun lintEm seguida, execute o seguinte comando para um build completo:
pnpm buildyarn buildnpm run buildbun buildVocê será questionado com o seguinte:
NX The workspace is out of sync
[@nx/js:typescript-sync]: Some TypeScript configuration files are missing project references to the projects they depend on or contain outdated project references.
This will result in an error in CI.
? Would you like to sync the identified changes to get your workspace up to date? …Yes, sync the changes and run the tasksNo, run the tasks without syncing the changesEsta mensagem indica que o NX detectou alguns arquivos que podem ser atualizados automaticamente para você. Neste caso, está se referindo aos arquivos tsconfig.json que não têm referências Typescript configuradas em projetos referenciados.
Selecione a opção Yes, sync the changes and run the tasks para prosseguir. Você deve notar que todos os seus erros de importação relacionados à IDE são resolvidos automaticamente, pois o gerador de sincronização adicionará as referências typescript faltantes automaticamente!
Todos os artefatos construídos estão agora disponíveis dentro da pasta dist/ localizada na raiz do monorepo. Esta é uma prática padrão ao usar projetos gerados pelo @aws/nx-plugin, pois não polui sua árvore de arquivos com arquivos gerados. Caso você queira limpar seus arquivos, delete a pasta dist/ sem se preocupar com artefatos de build espalhados pela árvore de arquivos.
Parabéns! Você criou todos os sub-projetos necessários para começar a implementar o núcleo do nosso jogo AI Dungeon Adventure. 🎉🎉🎉Adventure. 🎉🎉🎉