エージェント型AIダンジョンゲーム
モジュール1: モノレポ設定
Section titled “モジュール1: モノレポ設定”まず新しいモノレポを作成します。任意のディレクトリ内で次のコマンドを実行してください:
npx create-nx-workspace@21.6.4 dungeon-adventure --pm=pnpm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.4 dungeon-adventure --pm=yarn --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.4 dungeon-adventure --pm=npm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.4 dungeon-adventure --pm=bun --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsこれによりdungeon-adventureディレクトリ内にNXモノレポがセットアップされ、vscodeで開くことができます。以下のような構造になります:
Directory.nx/
- …
Directory.vscode/
- …
Directorynode_modules/
- …
Directorypackages/ サブプロジェクトが配置される場所
- …
- .gitignore
- .npmrc
- .prettierignore
- .prettierrc
- nx.json Nx CLIとモノレポのデフォルト設定
- package.json 全Node依存関係の定義
- pnpm-lock.yaml または bun.lock, yarn.lock, package-lock.json(パッケージマネージャー依存)
- pnpm-workspace.yaml(pnpm使用時)
- README.md
- tsconfig.base.json 全Nodeベースサブプロジェクトが継承
- tsconfig.json
- aws-nx-plugin.config.mts Nx Plugin for AWSの設定
これで@aws/nx-pluginを使用して様々なサブプロジェクトを作成する準備が整いました。
ゲームAPI
Section titled “ゲームAPI”最初にGame APIを作成します。以下の手順でGameApiというtRPC APIを作成します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#trpc-api - 必須パラメータを入力
- name: GameApi
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactiveyarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactivenpx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactivebunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runファイルツリーに新しいファイルが生成されているはずです。
ts#trpc-apiで更新されたファイル
ts#trpc-apiジェネレーターで生成された全ファイルのリストです。ファイルツリー内の主要ファイルを確認します:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ アプリ固有CDKコンストラクト
Directoryapis/
- game-api.ts tRPC API作成用CDKコンストラクト
- index.ts
- …
- index.ts
Directorycore/ 汎用CDKコンストラクト
Directoryapi/
- rest-api.ts API Gateway Rest API用ベースコンストラクト
- trpc-utils.ts trpc API CDKコンストラクト用ユーティリティ
- utils.ts APIコンストラクト用ユーティリティ
- index.ts
- runtime-config.ts
- index.ts
- project.json
- …
Directorytypes/ 共有型定義
Directorysrc/
- index.ts
- runtime-config.ts CDKとウェブサイト間で使用されるインターフェース定義
- project.json
- …
Directorygame-api/ tRPC API
Directorysrc/
Directoryclient/ 機械間TS呼び出し用バニラクライアント
- index.ts
- sigv4.ts
Directorymiddleware/ Powertools計装
- error.ts
- index.ts
- logger.ts
- metrics.ts
- tracer.ts
Directoryschema/ APIの入出力定義
- echo.ts
Directoryprocedures/ APIプロシージャ/ルート実装
- echo.ts
- index.ts
- init.ts コンテキストとミドルウェア設定
- local-server.ts ローカルtRPCサーバー実行用
- router.ts 全プロシージャを定義するLambdaハンドラーエントリーポイント
- project.json
- …
- eslint.config.mjs
- vitest.workspace.ts
主要ファイルの解説:
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;ルーターはtRPC APIのエントリーポイントで、すべてのAPIメソッドを宣言する場所です。echoメソッドの実装は./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 }));echoメソッドの実装で、入力/出力データ構造を厳密に型定義しています。
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>;tRPCスキーマ定義はZodを使用し、z.TypeOf構文でTypeScript型としてエクスポートされます。
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';
type Operations = Procedures<AppRouter>;
export interface GameApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { integrations: TIntegrations;}
export class GameApi< TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> { 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: [ new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: routerToOperations(appRouter), ...props, }); }}GameApiのCDKコンストラクトです。defaultIntegrationsメソッドにより、tRPC APIの各プロシージャ用Lambda関数が自動生成されます。バンドリングはバックエンドプロジェクトのビルドターゲット時に実行されます。
ストーリーエージェント: Pythonプロジェクト
Section titled “ストーリーエージェント: Pythonプロジェクト”次にストーリーエージェントを作成します。まずPythonプロジェクトを作成:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - py#project - 必須パラメータを入力
- name: story
- クリック
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-interactive変更されるファイルを確認するためにドライランを実行することもできます
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-runファイルツリーに新しいファイルが生成されます。
py#projectで更新されたファイル
py#projectジェネレーターで生成されたファイルリスト:
Directory.venv/ モノレポ用仮想環境
- …
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ Pythonモジュール
- hello.py サンプルファイル(無視可)
Directorytests/
- …
- .python-version
- pyproject.toml
- project.json
- .python-version 固定UV Pythonバージョン
- pyproject.toml
- uv.lock
共有仮想環境を持つUV Workspaceが設定されます。
ストーリーエージェント: Strandsエージェント
Section titled “ストーリーエージェント: Strandsエージェント”py#strands-agentジェネレーターでStrandsエージェントを追加:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - py#strands-agent - 必須パラメータを入力
- project: story
- クリック
Generate
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactiveyarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactivenpx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactivebunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runファイルツリーに新しいファイルが生成されます。
py#strands-agentで更新されたファイル
py#strands-agentジェネレーターで生成されたファイルリスト:
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/
Directoryagent/
- main.py Bedrock AgentCore Runtime用エージェントエントリーポイント
- agent.py サンプルエージェントとツール定義
- agentcore_mcp_client.py MCPサーバー連携用ユーティリティ
- Dockerfile AgentCore Runtime用Dockerイメージ定義
Directorycommon/constructs/
Directorysrc
Directorycore/agent-core/
- runtime.ts AgentCore Runtimeデプロイ用汎用コンストラクト
Directoryapp/agents/story-agent/
- story-agent.ts Storyエージェントデプロイ用コンストラクト
主要ファイルの解説:
from contextlib import contextmanager
from strands import Agent, toolfrom strands_tools import current_time
@tooldef add(a: int, b: int) -> int: return a + b
@contextmanagerdef get_agent(session_id: str): yield Agent( system_prompt="""You are an addition wizard.Use the 'add' tool for addition tasks.Refer to tools as your 'spellbook'.""", tools=[add, current_time], )加算ツールを持つStrandsエージェントの例。
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from .agent import get_agent
app = BedrockAgentCoreApp()
@app.entrypointasync def invoke(payload, context): prompt = payload.get( "prompt", "No prompt found in input, please guide the user " "to create a json payload with prompt key" )
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()Amazon Bedrock AgentCore SDKを使用したエージェントのエントリーポイント。ストリーミングイベントをクライアントに返します。
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, }); }}ECRへのDockerイメージアップロードとAgentCore Runtimeでのホスティングを設定します。
インベントリ: TypeScriptプロジェクト
Section titled “インベントリ: TypeScriptプロジェクト”プレイヤーのインベントリ管理用MCPサーバーを作成します。まずTypeScriptプロジェクトを作成:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#project - 必須パラメータを入力
- name: inventory
- クリック
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-interactive変更されるファイルを確認するためにドライランを実行することもできます
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-run空のTypeScriptプロジェクトが生成されます。
ts#projectで更新されたファイル
ts#projectジェネレーターで生成されたファイルリスト:
Directorypackages/
Directoryinventory/
Directorysrc/
- index.ts エントリーポイント(サンプル関数)
- project.json プロジェクト設定
- eslint.config.mjs リンター設定
- vite.config.ts テスト設定
- tsconfig.json ベースTypeScript設定
- tsconfig.lib.json コンパイル/バンドル用設定
- tsconfig.spec.json テスト用設定
- tsconfig.base.json プロジェクト参照用エイリアス更新
インベントリ: MCPサーバー
Section titled “インベントリ: MCPサーバー”TypeScriptプロジェクトにMCPサーバーを追加:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#mcp-server - 必須パラメータを入力
- project: inventory
- クリック
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-interactive変更されるファイルを確認するためにドライランを実行することもできます
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-runts#mcp-serverで更新されたファイル
ts#mcp-serverジェネレーターで生成されたファイルリスト:
Directorypackages/
Directoryinventory/
Directorysrc/mcp-server/
- server.ts MCPサーバー作成
Directorytools/
- add.ts サンプルツール
Directoryresources/
- sample-guidance.ts サンプルリソース
- stdio.ts STDIOトランスポート用エントリーポイント
- http.ts ストリーミングHTTPトランスポート用エントリーポイント
- Dockerfile AgentCore Runtime用Dockerイメージ
- rolldown.config.ts AgentCoreデプロイ用バンドル設定
Directorycommon/constructs/
Directorysrc
Directoryapp/mcp-servers/inventory-mcp-server/
- inventory-mcp-server.ts インベントリMCPサーバーデプロイ用コンストラクト
ゲームUI: ウェブサイト
Section titled “ゲームUI: ウェブサイト”ゲーム操作用UIを作成します。以下の手順でGameUIウェブサイトを作成:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#react-website - 必須パラメータを入力
- name: GameUI
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactiveyarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactivenpx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactivebunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runファイルツリーに新しいファイルが生成されます。
ts#react-websiteで更新されたファイル
ts#react-websiteジェネレーターで生成された主要ファイル:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/static-websites/
- game-ui.ts Game UI用CDKコンストラクト
Directorycore/
- static-website.ts 静的ウェブサイト汎用コンストラクト
Directorygame-ui/
Directorypublic/
- …
Directorysrc/
Directorycomponents/
DirectoryAppLayout/ ページレイアウトコンポーネント
- index.ts ヘッダー/フッター/サイドバー
- navitems.ts ナビゲーションアイテム
Directoryhooks/
- useAppLayout.tsx 通知/ページスタイル動的設定
Directoryroutes/ @tanstack/react-routerファイルベースルーティング
- index.tsx ルート’/’ → ‘/welcome’リダイレクト
- __root.tsx 全ページベースコンポーネント
Directorywelcome/
- index.tsx
- config.ts
- main.tsx Reactエントリーポイント
- routeTree.gen.ts 自動生成ルートツリー
- 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, ), ), }); }}ViteベースUIのバンドルパスを設定したCDKコンストラクト。
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 });
declare 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>, );ファイルベースルーティングを使用したReactエントリーポイント。
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>Welcome to your new Cloudscape website!</Container> </SpaceBetween> </ContentLayout> );}/welcomeルート用コンポーネント。
ゲームUI: 認証
Section titled “ゲームUI: 認証”Amazon Cognitoによる認証を設定:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#react-website#auth - 必須パラメータを入力
- cognitoDomain: game-ui
- project: @dungeon-adventure/game-ui
- allowSignup: true
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactiveyarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivenpx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivebunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runファイルツリーが更新されます。
ts#react-website#authで更新されたファイル
認証関連ファイルの更新内容:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/core/
- user-identity.ts Cognitoユーザープールコンストラクト
Directorytypes/
- src/runtime-config.ts Cognito設定追加
Directorygame-ui/
Directorysrc/
Directorycomponents/
- AppLayout/index.tsx ログイン状態表示
DirectoryCognitoAuth/ Cognito認証管理
- index.ts
DirectoryRuntimeConfig/ ランタイム設定取得
- index.tsx
Directoryhooks/
- useRuntimeConfig.tsx
- main.tsx 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 });declare 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>, );runtime-config.jsonからCognito設定を取得するプロバイダーを追加。
ゲームUI: Game API接続
Section titled “ゲームUI: Game API接続”Game APIへの接続を設定:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - api-connection - 必須パラメータを入力
- sourceProject: @dungeon-adventure/game-ui
- targetProject: @dungeon-adventure/game-api
- クリック
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactiveyarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivenpx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivebunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runyarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runnpx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runbunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runファイルツリーが更新されます。
UI → tRPC api-connection更新ファイル
接続関連ファイルの更新内容:
Directorypackages/
Directorygame-ui/
Directorysrc/
Directorycomponents/
- GameApiClientProvider.tsx GameAPIクライアント設定
Directoryhooks/
- useGameApi.tsx GameAPI呼び出し用フック
- main.tsx trpcプロバイダー追加
- package.json
import { GameApiTRCPContext } from '../components/GameApiClientProvider';
export const useGameApi = GameApiTRCPContext.useTRPC;tRPCのReact Query統合を使用したAPIフック。
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 });declare 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>, );tRPCプロバイダーを追加。
ゲームUI: インフラストラクチャー
Section titled “ゲームUI: インフラストラクチャー”最後にCDKインフラストラクチャープロジェクトを作成:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#infra - 必須パラメータを入力
- name: infra
- クリック
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-interactive変更されるファイルを確認するためにドライランを実行することもできます
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-runファイルツリーが更新されます。
ts#infraで更新されたファイル
インフラ関連ファイルの更新内容:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/core/
- checkov.ts セキュリティチェック
- index.ts
Directoryinfra
Directorysrc/
Directorystages/
- application-stage.ts CDKステージ定義
Directorystacks/
- application-stack.ts CDKリソース定義
- index.ts
- main.ts 全ステージエントリーポイント
- cdk.json
- project.json
- …
- package.json
- tsconfig.json 参照更新
- tsconfig.base.json エイリアス更新
import { ApplicationStage } from './stacks/application-stage.js';import { App } from ':dungeon-adventure/common-constructs';
const app = new App();
new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, },});
app.synth();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); // スタックリソース定義 }}ゲームリソース構築用CDKスタック。
インフラストラクチャー更新
Section titled “インフラストラクチャー更新”packages/infra/src/stacks/application-stack.tsを更新して生成済みコンストラクトをインスタンス化:
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);
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'); }}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'); }}Game APIのデフォルト統合を設定。各API操作に個別のLambda関数がマッピングされます。
コードビルド
Section titled “コードビルド”Nxコマンド
単一 vs 複数ターゲット
Section titled “単一 vs 複数ターゲット”run-manyコマンドは複数サブプロジェクトでターゲットを実行します。依存関係順に実行されます。
単一プロジェクトのビルド:
pnpm nx run @dungeon-adventure/infra:buildyarn nx run @dungeon-adventure/infra:buildnpx nx run @dungeon-adventure/infra:buildbunx nx run @dungeon-adventure/infra:build省略形:
pnpm nx build infrayarn nx build infranpx nx build infrabunx nx build infra依存関係可視化
Section titled “依存関係可視化”依存関係グラフ表示:
pnpm nx graphyarn nx graphnpx nx graphbunx nx graph
Nxはキャッシュを使用してビルドを高速化します。キャッシュ無効化:
pnpm nx run @dungeon-adventure/infra:build --skip-nx-cacheyarn nx run @dungeon-adventure/infra:build --skip-nx-cachenpx nx run @dungeon-adventure/infra:build --skip-nx-cachebunx nx run @dungeon-adventure/infra:build --skip-nx-cacheキャッシュクリア:
pnpm nx resetyarn nx resetnpx nx resetbunx nx resetpnpm nx run-many --target build --allyarn nx run-many --target build --allnpx nx run-many --target build --allbunx nx run-many --target build --all以下のプロンプトが表示されます:
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 changesYes, sync the changes and run the tasksを選択。IDEのインポートエラーが解消されます。
ビルド成果物はモノレポルートのdist/フォルダに生成されます。クリーン時はdist/を削除。
おめでとうございます!ダンジョンアドベンチャーゲームのコア実装に必要な全サブプロジェクトの作成が完了しました。🎉🎉🎉