Gioco di Dungeon con IA Agentiva
Modulo 1: Configurazione del monorepo
Sezione intitolata “Modulo 1: Configurazione del monorepo”Iniziamo creando un nuovo monorepo. Esegui il seguente comando dalla directory desiderata:
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=pnpm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=yarn --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=npm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=bun --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
Questo configurerà un monorepo NX all’interno della directory dungeon-adventure
che potrai aprire in VSCode. Dovrebbe apparire così:
Directory.nx/
- …
Directory.vscode/
- …
Directorynode_modules/
- …
Directorypackages/ qui risiederanno i tuoi sotto-progetti
- …
- .gitignore
- .npmrc
- .prettierignore
- .prettierrc
- nx.json configura la CLI NX e le impostazioni predefinite del monorepo
- package.json tutte le dipendenze Node sono definite qui
- pnpm-lock.yaml o bun.lock, yarn.lock, package-lock.json in base al package manager
- pnpm-workspace.yaml se si utilizza pnpm
- README.md
- tsconfig.base.json esteso da tutti i sotto-progetti basati su Node
- tsconfig.json
- aws-nx-plugin.config.mts configurazione per il Plugin Nx per AWS
Ora siamo pronti per iniziare a creare i diversi sotto-progetti utilizzando @aws/nx-plugin
.
Game API
Sezione intitolata “Game API”Iniziamo creando la nostra Game API. Per farlo, creiamo un’API tRPC chiamata GameApi
seguendo questi passaggi:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#trpc-api
- Compila i parametri richiesti
- name: GameApi
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
yarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
npx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
bunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
Dovresti vedere nuovi file apparire nella struttura delle cartelle.
File aggiornati da ts#trpc-api
Di seguito l’elenco dei file generati dal generatore ts#trpc-api
. Esamineremo alcuni file chiave evidenziati:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ CDK constructs specifici per l’applicazione
Directoryapis/
- game-api.ts CDK construct per creare l’API tRPC
- index.ts
- …
- index.ts
Directorycore/ CDK constructs generici
Directoryapi/
- rest-api.ts CDK construct base per API Gateway Rest API
- trpc-utils.ts utility per CDK constructs di API tRPC
- utils.ts utility per i constructs API
- index.ts
- runtime-config.ts
- index.ts
- project.json
- …
Directorytypes/ tipi condivisi
Directorysrc/
- index.ts
- runtime-config.ts definizione dell’interfaccia usata sia da CDK che dal sito web
- project.json
- …
Directorygame-api/ API tRPC
Directorysrc/
Directoryclient/ client vanilla tipicamente usato per chiamate machine-to-machine in TS
- index.ts
- sigv4.ts
Directorymiddleware/ strumentazione Powertools
- error.ts
- index.ts
- logger.ts
- metrics.ts
- tracer.ts
Directoryschema/ definizioni di input e output per l’API
- echo.ts
Directoryprocedures/ implementazioni specifiche delle procedure/route dell’API
- echo.ts
- index.ts
- init.ts configura contesto e middleware
- local-server.ts usato per eseguire il server tRPC localmente
- router.ts entrypoint per il lambda handler che definisce tutte le procedure
- project.json
- …
- eslint.config.mjs
- vitest.workspace.ts
Esaminiamo alcuni file chiave:
import { awsLambdaRequestHandler, CreateAWSLambdaContextOptions,} from '@trpc/server/adapters/aws-lambda';import { echo } from './procedures/echo.js';import { t } from './init.js';import { APIGatewayProxyEvent } from 'aws-lambda';
export const router = t.router;
export const appRouter = router({ echo,});
export const handler = awsLambdaRequestHandler({ router: appRouter, createContext: ( ctx: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>, ) => ctx, responseMeta: () => ({ headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', }, }),});
export type AppRouter = typeof appRouter;
Il router definisce l’entrypoint per l’API tRPC ed è dove dichiarerai tutti i metodi dell’API. Come visibile sopra, abbiamo un metodo chiamato echo
con l’implementazione nel file ./procedures/echo.ts
.
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 }));
Questo file implementa il metodo echo
ed è fortemente tipizzato dichiarando le strutture dati di input e output.
import { z } from 'zod';
export const EchoInputSchema = z.object({ message: z.string(),});
export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
export const EchoOutputSchema = z.object({ result: z.string(),});
export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;
Tutte le definizioni degli schema tRPC sono create con Zod ed esportate come tipi TypeScript tramite la sintassi z.TypeOf
.
import { Construct } from 'constructs';import * as url from 'url';import { Code, Runtime, Function, FunctionProps, Tracing,} from 'aws-cdk-lib/aws-lambda';import { AuthorizationType, Cors, LambdaIntegration,} from 'aws-cdk-lib/aws-apigateway';import { Duration, Stack } from 'aws-cdk-lib';import { PolicyDocument, PolicyStatement, Effect, AccountPrincipal, AnyPrincipal,} from 'aws-cdk-lib/aws-iam';import { IntegrationBuilder, RestApiIntegration,} from '../../core/api/utils.js';import { 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 per tutti i nomi delle operazioni APItype Operations = Procedures<AppRouter>;
/** * Proprietà per creare un construct GameApi * * @template TIntegrations - Mappa dei nomi delle operazioni alle loro integrazioni */export interface GameApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { /** * Mappa dei nomi delle operazioni alle integrazioni API Gateway */ integrations: TIntegrations;}
/** * CDK construct che crea e configura un'API REST API Gateway AWS * specifica per GameApi. * @template TIntegrations - Mappa dei nomi delle operazioni alle loro integrazioni */export class GameApi< TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> { /** * Crea integrazioni predefinite per tutte le operazioni, implementando ciascuna operazione * come singola funzione lambda. * * @param scope - Scope del construct CDK * @returns IntegrationBuilder con integrazioni lambda predefinite */ public static defaultIntegrations = (scope: Construct) => { return IntegrationBuilder.rest({ operations: routerToOperations(appRouter), defaultIntegrationOptions: { 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, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, } satisfies FunctionProps, buildDefaultIntegration: (op, props: FunctionProps) => { const handler = new Function(scope, `GameApi${op}Handler`, props); return { handler, integration: new LambdaIntegration(handler) }; }, }); };
constructor( scope: Construct, id: string, props: GameApiProps<TIntegrations>, ) { super(scope, id, { apiName: 'GameApi', defaultMethodOptions: { authorizationType: AuthorizationType.IAM, }, defaultCorsPreflightOptions: { allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS, }, policy: new PolicyDocument({ statements: [ // Concediamo a qualsiasi credenziale AWS dell'account di deployment di chiamare l'API. // È possibile definire qui accessi granulari machine-to-machine usando principal specifici new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), // Apriamo OPTIONS per permettere ai browser richieste preflight non autenticate new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: routerToOperations(appRouter), ...props, }); }}
Questo è il CDK construct che definisce la nostra GameApi. Come visibile, fornisce un metodo defaultIntegrations
che crea automaticamente una funzione lambda per ogni procedura nell’API tRPC, puntando all’implementazione bundled. Ciò significa che al momento di cdk synth
non avviene il bundling (a differenza di NodeJsFunction) poiché è già stato eseguito come parte del target di build del progetto backend.
Story Agent: Progetto Python
Sezione intitolata “Story Agent: Progetto Python”Ora creiamo il nostro Story Agent. Iniziamo creando un progetto Python:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - py#project
- Compila i parametri richiesti
- name: story
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactive
yarn nx g @aws/nx-plugin:py#project --name=story --no-interactive
npx nx g @aws/nx-plugin:py#project --name=story --no-interactive
bunx nx g @aws/nx-plugin:py#project --name=story --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-run
yarn nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-run
npx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-run
bunx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-run
Dovresti vedere nuovi file apparire nella struttura delle cartelle.
File aggiornati da py#project
Di seguito l’elenco dei file generati dal generatore py#project
.
Directory.venv/ virtual env condiviso per il monorepo
- …
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ modulo Python
- hello.py file Python di esempio (ignorabile)
Directorytests/
- …
- .python-version
- pyproject.toml
- project.json
- .python-version versione Python bloccata per UV
- pyproject.toml
- uv.lock
Questo ha configurato un progetto Python e un UV Workspace con ambiente virtuale condiviso.
Story Agent: Agente Strands
Sezione intitolata “Story Agent: Agente Strands”Aggiungiamo un agente Strands al progetto con il generatore py#strands-agent
:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - py#strands-agent
- Compila i parametri richiesti
- project: story
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive
yarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive
npx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive
bunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-run
yarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-run
npx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-run
bunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-run
Dovresti vedere nuovi file apparire nella struttura delle cartelle.
File aggiornati da py#strands-agent
Di seguito l’elenco dei file generati dal generatore py#strands-agent
.
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ modulo Python
Directoryagent/
- main.py entrypoint per l’agente in Bedrock AgentCore Runtime
- agent.py definisce un agente e strumenti di esempio
- agentcore_mcp_client.py utility per client MCP
- Dockerfile definisce l’immagine Docker per il deployment in AgentCore Runtime
Directorycommon/constructs/
Directorysrc
Directorycore/agent-core/
- runtime.ts construct generico per deployment in AgentCore Runtime
Directoryapp/agents/story-agent/
- story-agent.ts construct per deployare lo Story agent in AgentCore Runtime
Esaminiamo alcuni file in dettaglio:
from contextlib import contextmanager
from strands import Agent, toolfrom strands_tools import current_time
# Definisci uno strumento personalizzato@tooldef add(a: int, b: int) -> int: return a + b
@contextmanagerdef get_agent(session_id: str): yield Agent( system_prompt="""Sei un mago delle addizioni.Usa lo strumento 'add' per le operazioni di addizione.Riferisciti agli strumenti come al tuo 'grimorio'.""", tools=[add, current_time], )
Questo crea un agente Strands di esempio con uno strumento di addizione.
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from .agent import get_agent
app = BedrockAgentCoreApp()
@app.entrypointasync def invoke(payload, context): """Handler per l'invocazione dell'agente""" prompt = payload.get( "prompt", "Nessun prompt trovato nell'input, guida l'utente " "a creare un payload JSON con chiave prompt" )
with get_agent(session_id=context.session_id) as agent: stream = agent.stream_async(prompt) async for event in stream: print(event) yield (event)
if __name__ == "__main__": app.run()
Entrypoint dell’agente configurato con Amazon Bedrock AgentCore SDK. Utilizza lo streaming di Strands per inviare eventi al client in tempo reale.
import { Lazy, Names } from 'aws-cdk-lib';import { DockerImageAsset, 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 { AgentCoreRuntime, AgentCoreRuntimeProps,} from '../../../core/agent-core/runtime.js';
export type StoryAgentProps = Omit< AgentCoreRuntimeProps, 'runtimeName' | 'serverProtocol' | 'containerUri'>;
export class StoryAgent extends Construct { public readonly dockerImage: DockerImageAsset; public readonly agentCoreRuntime: AgentCoreRuntime;
constructor(scope: Construct, id: string, props?: StoryAgentProps) { super(scope, id);
this.dockerImage = new DockerImageAsset(this, 'DockerImage', { platform: Platform.LINUX_ARM64, directory: path.dirname(url.fileURLToPath(new URL(import.meta.url))), extraHash: execSync( `docker inspect dungeon-adventure-story-agent:latest --format '{{.Id}}'`, { encoding: 'utf-8' }, ).trim(), });
this.agentCoreRuntime = new AgentCoreRuntime(this, 'StoryAgent', { runtimeName: Lazy.string({ produce: () => Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }), }), serverProtocol: 'HTTP', containerUri: this.dockerImage.imageUri, ...props, }); }}
Questo configura un DockerImageAsset
CDK che carica l’immagine Docker dell’agente su ECR e la ospita usando AgentCore Runtime.
Il Dockerfile
referenzia l’immagine Docker dal progetto story
, permettendo di collocare Dockerfile e codice sorgente insieme.
Inventory: Progetto TypeScript
Sezione intitolata “Inventory: Progetto TypeScript”Creiamo un server MCP che fornirà strumenti allo Story Agent per gestire l’inventario del giocatore.
Iniziamo creando un progetto TypeScript:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#project
- Compila i parametri richiesti
- name: inventory
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive
yarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive
npx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive
bunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-run
Questo creerà un progetto TypeScript vuoto.
File aggiornati da ts#project
Di seguito l’elenco dei file generati dal generatore ts#project
.
Directorypackages/
Directoryinventory/
Directorysrc/
- index.ts entry point con funzione di esempio
- project.json configurazione del progetto
- eslint.config.mjs configurazione lint
- vite.config.ts configurazione test
- tsconfig.json configurazione TypeScript base
- tsconfig.lib.json configurazione per compilazione e bundling
- tsconfig.spec.json configurazione per i test
- tsconfig.base.json aggiornato con alias per riferire questo progetto
Inventory: Server MCP
Sezione intitolata “Inventory: Server MCP”Aggiungiamo un server MCP al progetto TypeScript:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#mcp-server
- Compila i parametri richiesti
- project: inventory
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive
yarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive
npx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive
bunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-run
Questo aggiungerà
File aggiornati da ts#mcp-server
Di seguito l’elenco dei file generati dal generatore ts#mcp-server
.
Directorypackages/
Directoryinventory/
Directorysrc/mcp-server/
- server.ts crea il server MCP
Directorytools/
- add.ts strumento di esempio
Directoryresources/
- sample-guidance.ts risorsa di esempio
- stdio.ts entry point per MCP con trasporto STDIO
- http.ts entry point per MCP con trasporto HTTP streamable
- Dockerfile builda l’immagine per AgentCore Runtime
- rolldown.config.ts configurazione per il bundling del server MCP
Directorycommon/constructs/
Directorysrc
Directoryapp/mcp-servers/inventory-mcp-server/
- inventory-mcp-server.ts construct per deployare il server MCP in AgentCore Runtime
Game UI: Sito Web
Sezione intitolata “Game UI: Sito Web”Creiamo l’interfaccia utente per interagire con il gioco. Creiamo un sito web chiamato GameUI
:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#react-website
- Compila i parametri richiesti
- name: GameUI
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
yarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
npx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
bunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
Dovresti vedere nuovi file apparire nella struttura delle cartelle.
File aggiornati da ts#react-website
Di seguito l’elenco dei file generati dal generatore ts#react-website
:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ CDK constructs specifici per l’applicazione
Directorystatic-websites/
- game-ui.ts CDK construct per creare la Game UI
Directorycore/
- static-website.ts construct generico per siti statici
Directorygame-ui/
Directorypublic/
- …
Directorysrc/
Directorycomponents/
DirectoryAppLayout/
- index.ts layout generale della pagina: header, footer, sidebar, ecc
- navitems.ts elementi di navigazione sidebar
Directoryhooks/
- useAppLayout.tsx permette di impostare notifiche, stile pagina, ecc
Directoryroutes/ routing basato su file @tanstack/react-router
- index.tsx root ’/’ reindirizza a ‘/welcome’
- __root.tsx componente base per tutte le pagine
Directorywelcome/
- index.tsx
- config.ts
- main.tsx entrypoint React
- routeTree.gen.ts generato automaticamente da @tanstack/react-router
- styles.css
- index.html
- project.json
- vite.config.ts
- …
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, ), ), }); }}
Questo CDK construct definisce la GameUI, configurando il percorso del bundle generato da Vite.
import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';
import '@cloudscape-design/global-styles/index.css';
const router = createRouter({ routeTree });
// Registra l'istanza del router per type safetydeclare module '@tanstack/react-router' { interface Register { router: typeof router; }}
const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RouterProvider router={router} /> </I18nProvider> </React.StrictMode>, );
Entrypoint dove React viene montato, configurando il routing basato su file con @tanstack/react-router
.
import { ContentLayout, Header, SpaceBetween, Container,} from '@cloudscape-design/components';import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/welcome/')({ component: RouteComponent,});
function RouteComponent() { return ( <ContentLayout header={<Header>Welcome</Header>}> <SpaceBetween size="l"> <Container>Benvenuto nel tuo nuovo sito Cloudscape!</Container> </SpaceBetween> </ContentLayout> );}
Componente renderizzato sulla route /welcome
, gestito automaticamente dal router.
Game UI: Autenticazione
Sezione intitolata “Game UI: Autenticazione”Configuriamo la Game UI per richiedere autenticazione via Amazon Cognito:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#react-website#auth
- Compila i parametri richiesti
- cognitoDomain: game-ui
- project: @dungeon-adventure/game-ui
- allowSignup: true
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
yarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
npx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
bunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
Dovresti vedere nuovi file/modifiche nella struttura delle cartelle.
File aggiornati da ts#react-website#auth
Di seguito i file generati/aggiornati dal generatore ts#react-website#auth
:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
- user-identity.ts CDK construct per pool utenti/identità
Directorytypes/
Directorysrc/
- runtime-config.ts aggiornato con cognitoProps
Directorygame-ui/
Directorysrc/
Directorycomponents/
DirectoryAppLayout/
- index.tsx aggiunge utente/logout nell’header
DirectoryCognitoAuth/
- index.ts gestisce il login a Cognito
DirectoryRuntimeConfig/
- index.tsx recupera
runtime-config.json
e lo fornisce via context
- index.tsx recupera
Directoryhooks/
- useRuntimeConfig.tsx
- main.tsx Aggiornato con Cognito
import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// Registra l'istanza del router per type safetydeclare module '@tanstack/react-router' { interface Register { router: typeof router; }}const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RuntimeConfigProvider> <CognitoAuth> <RouterProvider router={router} /> </CognitoAuth> </RuntimeConfigProvider> </I18nProvider> </React.StrictMode>, );
I componenti RuntimeConfigProvider
e CognitoAuth
sono stati aggiunti per gestire l’autenticazione tramite runtime-config.json
.
Game UI: Connessione a Game API
Sezione intitolata “Game UI: Connessione a Game API”Colleghiamo la Game UI alla Game API creata precedentemente:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - api-connection
- Compila i parametri richiesti
- sourceProject: @dungeon-adventure/game-ui
- targetProject: @dungeon-adventure/game-api
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
Dovresti vedere nuovi file/modifiche nella struttura delle cartelle.
File aggiornati da UI -> tRPC api-connection
Di seguito i file generati/aggiornati dal generatore api-connection
:
Directorypackages/
Directorygame-ui/
Directorysrc/
Directorycomponents/
- GameApiClientProvider.tsx configura il client GameAPI
Directoryhooks/
- useGameApi.tsx hook per chiamare la GameApi
- main.tsx inietta i provider trpc client
- package.json
import { GameApiTRCPContext } from '../components/GameApiClientProvider';
export const useGameApi = GameApiTRCPContext.useTRPC;
Questo hook utilizza l’integrazione di tRPC con React Query.
import GameApiClientProvider from './components/GameApiClientProvider';import QueryClientProvider from './components/QueryClientProvider';import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// Registra l'istanza del router per type safetydeclare module '@tanstack/react-router' { interface Register { router: typeof router; }}const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RuntimeConfigProvider> <CognitoAuth> <QueryClientProvider> <GameApiClientProvider> <RouterProvider router={router} /> </GameApiClientProvider> </QueryClientProvider> </CognitoAuth> </RuntimeConfigProvider> </I18nProvider> </React.StrictMode>, );
Aggiornamento del main.tsx
per iniettare i provider tRPC.
Game UI: Infrastruttura
Sezione intitolata “Game UI: Infrastruttura”Creiamo il progetto per l’infrastruttura CDK:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#infra
- Compila i parametri richiesti
- name: infra
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
Dovresti vedere nuovi file/modifiche nella struttura delle cartelle.
File aggiornati da ts#infra
Di seguito i file generati/aggiornati dal generatore ts#infra
:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
- checkov.ts
- index.ts
Directoryinfra
Directorysrc/
Directorystages/
- application-stage.ts stack CDK definiti qui
Directorystacks/
- application-stack.ts risorse CDK definite qui
- index.ts
- main.ts entrypoint che definisce tutti gli stage
- cdk.json
- project.json
- …
- package.json
- tsconfig.json aggiunti riferimenti
- tsconfig.base.json aggiunto alias
import { ApplicationStage } from './stacks/application-stage.js';import { App } from ':dungeon-adventure/common-constructs';
const app = new App();
// Deploya un ambiente sandbox personale (richiede credenziali CLI)new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, },});
app.synth();
Entrypoint dell’applicazione CDK.
import * as cdk from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props);
// Il codice che definisce lo stack va qui }}
Qui istanzieremo i CDK constructs per costruire il gioco.
Aggiornare l’infrastruttura
Sezione intitolata “Aggiornare l’infrastruttura”Aggiorniamo packages/infra/src/stacks/application-stack.ts
per istanziare alcuni constructs:
import { Stack, StackProps } from 'aws-cdk-lib';import { GameApi, GameUI, InventoryMcpServer, RuntimeConfig, 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 { userPool, userPoolClient } = userIdentity;
const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
// Use Cognito for user authentication with the agent const storyAgent = new StoryAgent(this, 'StoryAgent', { authorizerConfiguration: { customJwtAuthorizer: { discoveryUrl: `https://cognito-idp.${Stack.of(userPool).region}.amazonaws.com/${userPool.userPoolId}/.well-known/openid-configuration`, allowedAudience: [userPoolClient.userPoolClientId], }, }, environment: { INVENTORY_MCP_ARN: mcpServer.agentCoreRuntime.arn, }, }); // Add the Story Agent ARN to runtime-config.json so it can be referenced by the website RuntimeConfig.ensure(this).config.agentArn = storyAgent.agentCoreRuntime.arn;
new CfnOutput(this, 'StoryAgentArn', { value: storyAgent.agentCoreRuntime.arn, }); new CfnOutput(this, 'InventoryMcpArn', { value: mcpServer.agentCoreRuntime.arn, });
// Grant the agent permissions to invoke our mcp server mcpServer.agentCoreRuntime.grantInvoke(storyAgent.agentCoreRuntime);
// Grant the authenticated role access to invoke the api gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
// Ensure this is instantiated last so our runtime-config.json can be automatically configured new GameUI(this, 'GameUI'); }}
Nota che forniamo integrazioni predefinite per la Game API, mappando ogni operazione a una lambda separata.
Compilare il codice
Sezione intitolata “Compilare il codice”Comandi Nx
Target singoli vs multipli
Sezione intitolata “Target singoli vs multipli”Il comando run-many
esegue un target su più progetti (--all
li seleziona tutti). Garantisce l’ordine corretto delle dipendenze.
Per eseguire un target su un singolo progetto:
pnpm nx run @dungeon-adventure/infra:build
yarn nx run @dungeon-adventure/infra:build
npx nx run @dungeon-adventure/infra:build
bunx nx run @dungeon-adventure/infra:build
Forma abbreviata:
pnpm nx build infra
yarn nx build infra
npx nx build infra
bunx nx build infra
Visualizzare le dipendenze
Sezione intitolata “Visualizzare le dipendenze”Visualizza le dipendenze con:
pnpm nx graph
yarn nx graph
npx nx graph
bunx nx graph

Caching
Sezione intitolata “Caching”Nx utilizza il caching per riutilizzare artefatti. Per build senza cache usa --skip-nx-cache
:
pnpm nx run @dungeon-adventure/infra:build --skip-nx-cache
yarn nx run @dungeon-adventure/infra:build --skip-nx-cache
npx nx run @dungeon-adventure/infra:build --skip-nx-cache
bunx nx run @dungeon-adventure/infra:build --skip-nx-cache
Pulisci la cache con:
pnpm nx reset
yarn nx reset
npx nx reset
bunx nx reset
pnpm nx run-many --target build --all
yarn nx run-many --target build --all
npx nx run-many --target build --all
bunx nx run-many --target build --all
Dovresti vedere:
NX The workspace is out of sync
[@nx/js:typescript-sync]: Alcuni file di configurazione TypeScript mancano di riferimenti ai progetti dipendenti.
? Vuoi sincronizzare le modifiche per aggiornare il workspace? …Sì, sincronizza le modifiche ed esegui i taskNo, esegui i task senza sincronizzare
Seleziona Sì per risolvere automaticamente i riferimenti TypeScript.
Gli artefatti compilati sono disponibili in dist/
. Per pulire, elimina la cartella.
Complimenti! Hai creato tutti i sotto-progetti necessari per implementare il core del gioco Dungeon Adventure. 🎉🎉🎉