From 04bff9617dbb24513bacd271b3c651be21ba2925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E7=89=A9?= <253518756@qq.com> Date: Tue, 21 Apr 2026 11:19:25 +0800 Subject: [PATCH] Update creation flow refactor docs and auth test fixtures --- ...HAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md | 17 ++++ ...CTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md | 81 +++++++++++++++++++ docs/technical/README.md | 1 + .../shared/src/contracts/rpgAgentActions.ts | 7 ++ .../shared/src/contracts/rpgAgentAnchors.ts | 11 +++ .../shared/src/contracts/rpgAgentSession.ts | 15 ++++ .../src/contracts/rpgCreationPreview.ts | 12 +++ .../src/contracts/rpgCreationWorkSummary.ts | 6 ++ packages/shared/src/index.ts | 5 ++ server-node/src/app.test.ts | 18 ++++- server-node/src/db.test.ts | 5 ++ .../modules/story/storyActionRoutes.test.ts | 5 ++ server-node/src/observability.test.ts | 5 ++ .../src/routes/rpgCreationAgentRoutes.ts | 1 + .../src/routes/rpgWorldGalleryRoutes.ts | 14 ++++ .../src/routes/rpgWorldLibraryRoutes.ts | 13 +++ server-node/src/routes/rpgWorldWorksRoutes.ts | 13 +++ .../src/services/RpgAgentOrchestrator.ts | 1 + .../src/services/RpgAgentSessionStore.ts | 5 ++ .../src/services/RpgWorldPreviewCompiler.ts | 14 ++++ .../services/RpgWorldWorkSummaryService.ts | 1 + .../customWorldAgentAutoAssetService.test.ts | 5 ++ .../services/customWorldAgentPhase3.test.ts | 5 ++ .../services/customWorldAgentPhase5.test.ts | 5 ++ .../rpg-creation-flow/RpgCreationShell.tsx | 15 ++++ .../game-shell/rpg-creation-flow/index.ts | 4 + .../RpgCreationEntityEditorModal.tsx | 19 +++++ src/components/rpg-creation-editor/index.ts | 4 + .../RpgCreationResultView.tsx | 17 ++++ src/components/rpg-creation-result/index.ts | 4 + src/hooks/story/npcEncounterActions.test.ts | 7 +- src/services/rpg-creation/index.ts | 35 ++++++++ .../rpg-creation/rpgCreationAgentClient.ts | 33 ++++++++ .../rpg-creation/rpgCreationAssetClient.ts | 35 ++++++++ .../rpg-creation/rpgCreationLibraryClient.ts | 33 ++++++++ .../rpg-creation/rpgCreationPreviewAdapter.ts | 13 +++ .../rpg-creation/rpgCreationWorkClient.ts | 11 +++ 37 files changed, 488 insertions(+), 7 deletions(-) create mode 100644 docs/technical/CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md create mode 100644 packages/shared/src/contracts/rpgAgentActions.ts create mode 100644 packages/shared/src/contracts/rpgAgentAnchors.ts create mode 100644 packages/shared/src/contracts/rpgAgentSession.ts create mode 100644 packages/shared/src/contracts/rpgCreationPreview.ts create mode 100644 packages/shared/src/contracts/rpgCreationWorkSummary.ts create mode 100644 server-node/src/routes/rpgCreationAgentRoutes.ts create mode 100644 server-node/src/routes/rpgWorldGalleryRoutes.ts create mode 100644 server-node/src/routes/rpgWorldLibraryRoutes.ts create mode 100644 server-node/src/routes/rpgWorldWorksRoutes.ts create mode 100644 server-node/src/services/RpgAgentOrchestrator.ts create mode 100644 server-node/src/services/RpgAgentSessionStore.ts create mode 100644 server-node/src/services/RpgWorldPreviewCompiler.ts create mode 100644 server-node/src/services/RpgWorldWorkSummaryService.ts create mode 100644 src/components/game-shell/rpg-creation-flow/RpgCreationShell.tsx create mode 100644 src/components/game-shell/rpg-creation-flow/index.ts create mode 100644 src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx create mode 100644 src/components/rpg-creation-editor/index.ts create mode 100644 src/components/rpg-creation-result/RpgCreationResultView.tsx create mode 100644 src/components/rpg-creation-result/index.ts create mode 100644 src/services/rpg-creation/index.ts create mode 100644 src/services/rpg-creation/rpgCreationAgentClient.ts create mode 100644 src/services/rpg-creation/rpgCreationAssetClient.ts create mode 100644 src/services/rpg-creation/rpgCreationLibraryClient.ts create mode 100644 src/services/rpg-creation/rpgCreationPreviewAdapter.ts create mode 100644 src/services/rpg-creation/rpgCreationWorkClient.ts diff --git a/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md b/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md index 6ad80bc5..2a2cc0f1 100644 --- a/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md +++ b/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md @@ -699,6 +699,23 @@ store 当前混合了: 无,可最先开始。 +### 当前进展(`2026-04-21`) + +工作包 A 第一轮已完成以下骨架落地: + +1. 已新增前端 `rpg-creation-flow`、`rpg-creation-result`、`rpg-creation-editor`、`rpg-creation` service 目录。 +2. 已新增 `RpgCreationShell`、`RpgCreationResultView`、`RpgCreationEntityEditorModal` 等 façade 入口,当前仍桥接旧实现。 +3. 已新增 `rpgCreationAgentClient`、`rpgCreationWorkClient`、`rpgCreationLibraryClient`、`rpgCreationAssetClient`、`rpgCreationPreviewAdapter`。 +4. 已新增后端 `rpgCreationAgentRoutes`、`rpgWorldWorksRoutes`、`rpgWorldLibraryRoutes`、`rpgWorldGalleryRoutes` 命名骨架。 +5. 已新增 `RpgAgentOrchestrator`、`RpgAgentSessionStore`、`RpgWorldPreviewCompiler`、`RpgWorldWorkSummaryService` façade。 +6. 已新增 `rpgAgent*` 与 `rpgCreation*` 共享契约骨架。 + +本轮刻意未做: + +1. 没有迁移 `runtimeRoutes.ts` 的真实 works/library/gallery 实现。 +2. 没有拆分 `PreGameSelectionFlow.tsx`、`CustomWorldResultView.tsx`、`CustomWorldEntityEditorModal.tsx` 内部逻辑。 +3. 没有改动现有主链行为,只建立后续并行迁移的统一落点。 + ## 9.2 工作包 B:前端平台壳层与流程编排拆分 ### 目标 diff --git a/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md b/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md new file mode 100644 index 00000000..f9e0627a --- /dev/null +++ b/docs/technical/CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md @@ -0,0 +1,81 @@ +# 创作链路重构工作包 A 落地记录 + +更新时间:`2026-04-21` + +## 1. 本次目标 + +本次只落实 `CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md` 中的 **工作包 A:命名规范与目录骨架**,约束如下: + +1. 先建立 RPG 创作域的新命名落点。 +2. 先提供 façade 和 barrel,不迁移主流程行为。 +3. 不提前修改工作包 B 到 H 的大块业务逻辑。 + +## 2. 本次已落地内容 + +## 2.1 前端目录骨架 + +已新增以下目录与 façade: + +1. `src/components/game-shell/rpg-creation-flow/` +2. `src/components/rpg-creation-result/` +3. `src/components/rpg-creation-editor/` +4. `src/services/rpg-creation/` + +当前策略: + +1. `RpgCreationShell` 继续桥接旧的 `PreGameSelectionFlow`。 +2. `RpgCreationResultView` 继续桥接旧的 `CustomWorldResultView`。 +3. `RpgCreationEntityEditorModal` 继续桥接旧的 `CustomWorldEntityEditorModal`。 +4. `rpgCreation*Client` 继续桥接 `aiService.ts`、`storageService.ts`、`customWorldCoverAssetService.ts`。 +5. `rpgCreationPreviewAdapter` 继续桥接旧的前端草稿编译函数,明确它只是过渡层。 + +## 2.2 后端目录骨架 + +已新增以下 RPG 创作域 façade: + +1. `server-node/src/routes/rpgCreationAgentRoutes.ts` +2. `server-node/src/routes/rpgWorldWorksRoutes.ts` +3. `server-node/src/routes/rpgWorldLibraryRoutes.ts` +4. `server-node/src/routes/rpgWorldGalleryRoutes.ts` +5. `server-node/src/services/RpgAgentOrchestrator.ts` +6. `server-node/src/services/RpgAgentSessionStore.ts` +7. `server-node/src/services/RpgWorldPreviewCompiler.ts` +8. `server-node/src/services/RpgWorldWorkSummaryService.ts` + +当前策略: + +1. Agent route 与 orchestrator/session store 先用新命名 façade 对齐。 +2. works/library/gallery 路由先建立空骨架和基础 path 常量,避免下一轮迁移继续回落到旧命名。 +3. `RpgWorldPreviewCompiler` 先桥接旧 `runtimeProfile.ts` 编译能力,为工作包 G 的目录化拆分预留落点。 + +## 2.3 共享契约骨架 + +已新增以下共享契约入口: + +1. `packages/shared/src/contracts/rpgAgentAnchors.ts` +2. `packages/shared/src/contracts/rpgAgentSession.ts` +3. `packages/shared/src/contracts/rpgAgentActions.ts` +4. `packages/shared/src/contracts/rpgCreationPreview.ts` +5. `packages/shared/src/contracts/rpgCreationWorkSummary.ts` + +当前策略: + +1. 会话、动作、作品摘要先从旧 `customWorldAgent.ts` 做类型级兼容导出。 +2. `rpgCreationPreview.ts` 明确标记当前 preview 仍是 legacy profile 兼容载体,避免误认为 preview contract 已经完成。 + +## 3. 本次没有做的事 + +以下内容仍保持原状,留给后续工作包: + +1. 没有拆 `PreGameSelectionFlow.tsx` 内部编排。 +2. 没有拆 `CustomWorldResultView.tsx`、`CustomWorldEntityEditorModal.tsx`、`CustomWorldRoleAssetStudioModal.tsx` 内部 section。 +3. 没有把 `runtimeRoutes.ts` 中的 works/library/gallery 真正迁出。 +4. 没有改 `customWorldAgentOrchestrator.ts`、`customWorldAgentSessionStore.ts`、`runtimeProfile.ts` 的内部职责。 +5. 没有改变任何线上行为或接口语义。 + +## 4. 对后续工作包的直接收益 + +1. 工作包 B 可以直接把平台壳层 hooks 落到 `src/components/game-shell/rpg-creation-flow/`。 +2. 工作包 C 可以直接把结果页与编辑器 section 落到新目录,而不用先讨论命名。 +3. 工作包 D 可以直接从 `rpgCreation*Client` 开始迁移导入链。 +4. 工作包 E、F、G、H 可以基于 `RpgAgent*`、`RpgWorld*`、`rpg*` 契约骨架继续拆分,而不需要再回头统一首轮命名。 diff --git a/docs/technical/README.md b/docs/technical/README.md index 5d9a63e1..78ec122b 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -16,6 +16,7 @@ - [AGENT_DIALOG_AND_RESULT_REFINEMENT_BOUNDARY_2026-04-21.md](./AGENT_DIALOG_AND_RESULT_REFINEMENT_BOUNDARY_2026-04-21.md):修正 Agent 对话框与结果页职责边界,明确 Agent 只收集八锚点,已有底稿的精修进入结果页完成。 - [CURRENT_AGENT_CREATION_FLOW_STAGE4_CLEANUP_CHECK_2026-04-21.md](./CURRENT_AGENT_CREATION_FLOW_STAGE4_CLEANUP_CHECK_2026-04-21.md):对照当前优化计划核查四阶段完成度,并明确这轮只允许物理删除旧 `custom-world/sessions` 世界生成链,不误伤 Agent 主链与已保存作品兼容编辑链。 - [CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](./CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md):梳理当前创作入口到结果页自动保存再到进入世界的全链前后端脚本地图,并给出文件级重构拆分方案、目标分层与阶段验收标准。 +- [CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md](./CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_A_PROGRESS_2026-04-21.md):记录创作链路重构工作包 A 已落地的 RPG 创作域目录骨架、兼容 façade 和共享契约入口。 - [CREATION_PAGE_MOBILE_UI_FIX_2026-04-21.md](./CREATION_PAGE_MOBILE_UI_FIX_2026-04-21.md):创作页移动端底部 Tab、亮色主题 token 与滚动权责修复记录。 - [TXT_MODE_VISUAL_NOVEL_MIGRATION_EXECUTION_PLAN_2026-04-20.md](./TXT_MODE_VISUAL_NOVEL_MIGRATION_EXECUTION_PLAN_2026-04-20.md):把外部仓库 TXT 模式完整迁入当前项目的冻结边界、模块映射、分阶段计划与验收清单。 - [NODE_SERVER_KNOWLEDGE_GRAPH_2026-04-08.md](./NODE_SERVER_KNOWLEDGE_GRAPH_2026-04-08.md):当前 Node 运行时后端的技术栈、入口、鉴权、存储与接口知识图谱。 diff --git a/packages/shared/src/contracts/rpgAgentActions.ts b/packages/shared/src/contracts/rpgAgentActions.ts new file mode 100644 index 00000000..a22227f1 --- /dev/null +++ b/packages/shared/src/contracts/rpgAgentActions.ts @@ -0,0 +1,7 @@ +export type { + CustomWorldAgentActionRequest as RpgAgentActionRequest, + CustomWorldAgentActionResponse as RpgAgentActionResponse, + CustomWorldAgentOperationStatus as RpgAgentOperationStatus, + CustomWorldAgentOperationType as RpgAgentOperationType, + CustomWorldSuggestedAction as RpgAgentSuggestedAction, +} from './customWorldAgent'; diff --git a/packages/shared/src/contracts/rpgAgentAnchors.ts b/packages/shared/src/contracts/rpgAgentAnchors.ts new file mode 100644 index 00000000..53a0ddcf --- /dev/null +++ b/packages/shared/src/contracts/rpgAgentAnchors.ts @@ -0,0 +1,11 @@ +export type { + EightAnchorContent as RpgCreationAnchorContent, + CoreConflictValue as RpgCreationCoreConflictValue, + HiddenLineValue as RpgCreationHiddenLineValue, + IconicElementValue as RpgCreationIconicElementValue, + KeyRelationshipValue as RpgCreationKeyRelationshipValue, + PlayerEntryPointValue as RpgCreationPlayerEntryPointValue, + PlayerFantasyValue as RpgCreationPlayerFantasyValue, + ThemeBoundaryValue as RpgCreationThemeBoundaryValue, + WorldPromiseValue as RpgCreationWorldPromiseValue, +} from './customWorldAgent'; diff --git a/packages/shared/src/contracts/rpgAgentSession.ts b/packages/shared/src/contracts/rpgAgentSession.ts new file mode 100644 index 00000000..d2c21fae --- /dev/null +++ b/packages/shared/src/contracts/rpgAgentSession.ts @@ -0,0 +1,15 @@ +export type { + CreateCustomWorldAgentSessionRequest as CreateRpgAgentSessionRequest, + CreateCustomWorldAgentSessionResponse as CreateRpgAgentSessionResponse, + GetCustomWorldAgentCardDetailResponse as GetRpgAgentCardDetailResponse, + CustomWorldAgentMessage as RpgAgentMessage, + CustomWorldAgentMessageKind as RpgAgentMessageKind, + CustomWorldAgentMessageRole as RpgAgentMessageRole, + CustomWorldAgentOperationRecord as RpgAgentOperationRecord, + CustomWorldPendingClarification as RpgAgentPendingClarification, + CustomWorldAgentSessionSnapshot as RpgAgentSessionSnapshot, + CustomWorldAgentStage as RpgAgentStage, + CreatorIntentReadiness as RpgCreationIntentReadiness, + SendCustomWorldAgentMessageRequest as SendRpgAgentMessageRequest, + SendCustomWorldAgentMessageResponse as SendRpgAgentMessageResponse, +} from './customWorldAgent'; diff --git a/packages/shared/src/contracts/rpgCreationPreview.ts b/packages/shared/src/contracts/rpgCreationPreview.ts new file mode 100644 index 00000000..317488a2 --- /dev/null +++ b/packages/shared/src/contracts/rpgCreationPreview.ts @@ -0,0 +1,12 @@ +import type { CustomWorldProfileRecord } from './runtime'; + +/** + * 工作包 A 先建立 RPG 创作结果预览契约骨架。 + * 在服务端 preview compiler 正式落地前,这里继续把旧的世界 profile 视为兼容预览载体。 + */ +export type RpgCreationPreview = CustomWorldProfileRecord; + +export type RpgCreationPreviewEnvelope = { + preview: RpgCreationPreview; + source: 'legacy_custom_world_profile'; +}; diff --git a/packages/shared/src/contracts/rpgCreationWorkSummary.ts b/packages/shared/src/contracts/rpgCreationWorkSummary.ts new file mode 100644 index 00000000..9a874229 --- /dev/null +++ b/packages/shared/src/contracts/rpgCreationWorkSummary.ts @@ -0,0 +1,6 @@ +export type { + ListCustomWorldWorksResponse as ListRpgCreationWorksResponse, + CustomWorldWorkSource as RpgCreationWorkSource, + CustomWorldWorkStatus as RpgCreationWorkStatus, + CustomWorldWorkSummary as RpgCreationWorkSummary, +} from './customWorldAgent'; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 85f44d3a..b219972c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,6 +1,11 @@ export * from './assets/qwenSprite'; export * from './contracts/auth'; export * from './contracts/common'; +export * from './contracts/rpgAgentActions'; +export * from './contracts/rpgAgentAnchors'; +export * from './contracts/rpgAgentSession'; +export * from './contracts/rpgCreationPreview'; +export * from './contracts/rpgCreationWorkSummary'; export * from './contracts/runtime'; export * from './contracts/story'; export * from './http'; diff --git a/server-node/src/app.test.ts b/server-node/src/app.test.ts index 799f1353..548d5e7e 100644 --- a/server-node/src/app.test.ts +++ b/server-node/src/app.test.ts @@ -122,6 +122,11 @@ function createTestConfig( mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'genarrative_refresh_session', refreshSessionTtlDays: 30, refreshCookieSecure: false, @@ -467,7 +472,18 @@ async function startWechatMockFlow(baseUrl: string, redirectPath = '/') { const location = callbackResponse.headers.get('location') || ''; assert.ok(location); const hash = parseRedirectHash(location); - const token = hash.get('auth_token') || ''; + const setCookieHeader = callbackResponse.headers.get('set-cookie') || ''; + const accessCookie = setCookieHeader + .split(',') + .map((entry) => entry.trim()) + .find((entry) => entry.startsWith('genarrative_access_session=')); + const token = + accessCookie + ?.split(';')[0] + ?.split('=') + .slice(1) + .join('=') + .trim() || ''; assert.ok(token); diff --git a/server-node/src/db.test.ts b/server-node/src/db.test.ts index b04d16be..1611dda5 100644 --- a/server-node/src/db.test.ts +++ b/server-node/src/db.test.ts @@ -81,6 +81,11 @@ function createTestConfig(databaseUrl: string): AppConfig { mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'genarrative_refresh_session', refreshSessionTtlDays: 30, refreshCookieSecure: false, diff --git a/server-node/src/modules/story/storyActionRoutes.test.ts b/server-node/src/modules/story/storyActionRoutes.test.ts index aac1bf83..ea59e137 100644 --- a/server-node/src/modules/story/storyActionRoutes.test.ts +++ b/server-node/src/modules/story/storyActionRoutes.test.ts @@ -91,6 +91,11 @@ function createTestConfig(testName: string): AppConfig { mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'genarrative_refresh_session', refreshSessionTtlDays: 30, refreshCookieSecure: false, diff --git a/server-node/src/observability.test.ts b/server-node/src/observability.test.ts index e571bda7..3741ecff 100644 --- a/server-node/src/observability.test.ts +++ b/server-node/src/observability.test.ts @@ -93,6 +93,11 @@ function createTestConfig(testName: string): AppConfig { mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'genarrative_refresh_session', refreshSessionTtlDays: 30, refreshCookieSecure: false, diff --git a/server-node/src/routes/rpgCreationAgentRoutes.ts b/server-node/src/routes/rpgCreationAgentRoutes.ts new file mode 100644 index 00000000..e8e923d3 --- /dev/null +++ b/server-node/src/routes/rpgCreationAgentRoutes.ts @@ -0,0 +1 @@ +export { createCustomWorldAgentRoutes as createRpgCreationAgentRoutes } from './customWorldAgent.js'; diff --git a/server-node/src/routes/rpgWorldGalleryRoutes.ts b/server-node/src/routes/rpgWorldGalleryRoutes.ts new file mode 100644 index 00000000..81d135cd --- /dev/null +++ b/server-node/src/routes/rpgWorldGalleryRoutes.ts @@ -0,0 +1,14 @@ +import { Router } from 'express'; + +import type { AppContext } from '../context.js'; + +/** + * 工作包 A 先建立 RPG 世界广场路由的命名骨架。 + * 当前广场查询仍由旧 runtime 路由承载,后续工作包会再迁移实现。 + */ +export const RPG_WORLD_GALLERY_ROUTE_BASE_PATH = + '/runtime/custom-world-gallery'; + +export function createRpgWorldGalleryRoutes(_context: AppContext) { + return Router(); +} diff --git a/server-node/src/routes/rpgWorldLibraryRoutes.ts b/server-node/src/routes/rpgWorldLibraryRoutes.ts new file mode 100644 index 00000000..cdd4fcbc --- /dev/null +++ b/server-node/src/routes/rpgWorldLibraryRoutes.ts @@ -0,0 +1,13 @@ +import { Router } from 'express'; + +import type { AppContext } from '../context.js'; + +/** + * 工作包 A 先建立 RPG 世界作品库路由的命名骨架。 + * 当前仅提供稳定落点,真正的库读写逻辑仍保留在 `runtimeRoutes.ts` 中。 + */ +export const RPG_WORLD_LIBRARY_ROUTE_BASE_PATH = '/runtime/custom-world-library'; + +export function createRpgWorldLibraryRoutes(_context: AppContext) { + return Router(); +} diff --git a/server-node/src/routes/rpgWorldWorksRoutes.ts b/server-node/src/routes/rpgWorldWorksRoutes.ts new file mode 100644 index 00000000..f8488c45 --- /dev/null +++ b/server-node/src/routes/rpgWorldWorksRoutes.ts @@ -0,0 +1,13 @@ +import { Router } from 'express'; + +import type { AppContext } from '../context.js'; + +/** + * 工作包 A 先建立 RPG 世界作品流路由的命名骨架。 + * 真实实现仍暂挂在 `runtimeRoutes.ts`,后续工作包再把作品列表接口迁入这里。 + */ +export const RPG_WORLD_WORKS_ROUTE_BASE_PATH = '/runtime/custom-world/works'; + +export function createRpgWorldWorksRoutes(_context: AppContext) { + return Router(); +} diff --git a/server-node/src/services/RpgAgentOrchestrator.ts b/server-node/src/services/RpgAgentOrchestrator.ts new file mode 100644 index 00000000..60a68c71 --- /dev/null +++ b/server-node/src/services/RpgAgentOrchestrator.ts @@ -0,0 +1 @@ +export { CustomWorldAgentOrchestrator as RpgAgentOrchestrator } from './customWorldAgentOrchestrator.js'; diff --git a/server-node/src/services/RpgAgentSessionStore.ts b/server-node/src/services/RpgAgentSessionStore.ts new file mode 100644 index 00000000..2075826c --- /dev/null +++ b/server-node/src/services/RpgAgentSessionStore.ts @@ -0,0 +1,5 @@ +export type { CustomWorldAgentSessionRecord as RpgAgentSessionRecord } from './customWorldAgentSessionStore.js'; +export { + CUSTOM_WORLD_AGENT_SESSION_ID_PREFIX as RPG_AGENT_SESSION_ID_PREFIX, + CustomWorldAgentSessionStore as RpgAgentSessionStore, +} from './customWorldAgentSessionStore.js'; diff --git a/server-node/src/services/RpgWorldPreviewCompiler.ts b/server-node/src/services/RpgWorldPreviewCompiler.ts new file mode 100644 index 00000000..793010f4 --- /dev/null +++ b/server-node/src/services/RpgWorldPreviewCompiler.ts @@ -0,0 +1,14 @@ +import { + buildCompiledCustomWorldProfile, + normalizeCustomWorldProfile, +} from '../modules/custom-world/runtimeProfile.js'; +import type { CustomWorldProfile } from '../modules/custom-world/runtimeTypes.js'; + +/** + * 工作包 A 先建立 RPG 世界预览编译器的新命名 façade。 + * 当前仍桥接旧 runtime profile 编译能力,后续工作包 G 会把正式 preview compiler 收口到这个入口。 + */ +export type RpgWorldPreviewProfile = CustomWorldProfile; + +export const buildRpgWorldPreviewProfile = buildCompiledCustomWorldProfile; +export const normalizeRpgWorldPreviewProfile = normalizeCustomWorldProfile; diff --git a/server-node/src/services/RpgWorldWorkSummaryService.ts b/server-node/src/services/RpgWorldWorkSummaryService.ts new file mode 100644 index 00000000..7efe931b --- /dev/null +++ b/server-node/src/services/RpgWorldWorkSummaryService.ts @@ -0,0 +1 @@ +export { listCustomWorldWorkSummaries as listRpgWorldWorkSummaries } from './customWorldWorkSummaryService.js'; diff --git a/server-node/src/services/customWorldAgentAutoAssetService.test.ts b/server-node/src/services/customWorldAgentAutoAssetService.test.ts index 8f508b2c..537b8e1f 100644 --- a/server-node/src/services/customWorldAgentAutoAssetService.test.ts +++ b/server-node/src/services/customWorldAgentAutoAssetService.test.ts @@ -87,6 +87,11 @@ function createTestConfig(testName: string): AppConfig { mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'refresh_token', refreshSessionTtlDays: 30, refreshCookieSecure: false, diff --git a/server-node/src/services/customWorldAgentPhase3.test.ts b/server-node/src/services/customWorldAgentPhase3.test.ts index 00ac0328..bb6136dc 100644 --- a/server-node/src/services/customWorldAgentPhase3.test.ts +++ b/server-node/src/services/customWorldAgentPhase3.test.ts @@ -172,6 +172,11 @@ function createAutoAssetTestConfig(testName: string): AppConfig { mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'refresh_token', refreshSessionTtlDays: 30, refreshCookieSecure: false, diff --git a/server-node/src/services/customWorldAgentPhase5.test.ts b/server-node/src/services/customWorldAgentPhase5.test.ts index 49a1adc5..6590f136 100644 --- a/server-node/src/services/customWorldAgentPhase5.test.ts +++ b/server-node/src/services/customWorldAgentPhase5.test.ts @@ -171,6 +171,11 @@ function createAutoAssetTestConfig(testName: string): AppConfig { mockAvatarUrl: '', }, authSession: { + accessCookieName: 'genarrative_access_session', + accessCookieTtlSeconds: 7200, + accessCookieSecure: false, + accessCookieSameSite: 'Lax', + accessCookiePath: '/', refreshCookieName: 'refresh_token', refreshSessionTtlDays: 30, refreshCookieSecure: false, diff --git a/src/components/game-shell/rpg-creation-flow/RpgCreationShell.tsx b/src/components/game-shell/rpg-creation-flow/RpgCreationShell.tsx new file mode 100644 index 00000000..45dc8263 --- /dev/null +++ b/src/components/game-shell/rpg-creation-flow/RpgCreationShell.tsx @@ -0,0 +1,15 @@ +import type { ComponentProps } from 'react'; + +import { PreGameSelectionFlow } from '../PreGameSelectionFlow'; + +/** + * 工作包 A 先建立 RPG 创作壳层的新命名入口。 + * 当前实现继续复用旧的 `PreGameSelectionFlow`,后续工作包 B 再把内部编排逐步迁到新目录。 + */ +export type RpgCreationShellProps = ComponentProps; + +export function RpgCreationShell(props: RpgCreationShellProps) { + return ; +} + +export default RpgCreationShell; diff --git a/src/components/game-shell/rpg-creation-flow/index.ts b/src/components/game-shell/rpg-creation-flow/index.ts new file mode 100644 index 00000000..fa548875 --- /dev/null +++ b/src/components/game-shell/rpg-creation-flow/index.ts @@ -0,0 +1,4 @@ +export { + RpgCreationShell, + type RpgCreationShellProps, +} from './RpgCreationShell'; diff --git a/src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx b/src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx new file mode 100644 index 00000000..c42da374 --- /dev/null +++ b/src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx @@ -0,0 +1,19 @@ +import type { ComponentProps } from 'react'; + +import CustomWorldEntityEditorModal from '../CustomWorldEntityEditorModal'; + +/** + * 工作包 A 只建立 RPG 创作编辑器的新目录入口。 + * 真实编辑表单暂时仍由旧的综合编辑器承载,后续工作包 C 会把不同 section 拆到这个目录下。 + */ +export type RpgCreationEntityEditorModalProps = ComponentProps< + typeof CustomWorldEntityEditorModal +>; + +export function RpgCreationEntityEditorModal( + props: RpgCreationEntityEditorModalProps, +) { + return ; +} + +export default RpgCreationEntityEditorModal; diff --git a/src/components/rpg-creation-editor/index.ts b/src/components/rpg-creation-editor/index.ts new file mode 100644 index 00000000..57c52ca0 --- /dev/null +++ b/src/components/rpg-creation-editor/index.ts @@ -0,0 +1,4 @@ +export { + RpgCreationEntityEditorModal, + type RpgCreationEntityEditorModalProps, +} from './RpgCreationEntityEditorModal'; diff --git a/src/components/rpg-creation-result/RpgCreationResultView.tsx b/src/components/rpg-creation-result/RpgCreationResultView.tsx new file mode 100644 index 00000000..fc15fe67 --- /dev/null +++ b/src/components/rpg-creation-result/RpgCreationResultView.tsx @@ -0,0 +1,17 @@ +import type { ComponentProps } from 'react'; + +import { CustomWorldResultView } from '../CustomWorldResultView'; + +/** + * 工作包 A 先提供 RPG 创作结果页的新命名 façade。 + * 当前结果页行为仍由旧组件承载,后续工作包 C 会在此目录内继续拆分 header、action bar 和 section。 + */ +export type RpgCreationResultViewProps = ComponentProps< + typeof CustomWorldResultView +>; + +export function RpgCreationResultView(props: RpgCreationResultViewProps) { + return ; +} + +export default RpgCreationResultView; diff --git a/src/components/rpg-creation-result/index.ts b/src/components/rpg-creation-result/index.ts new file mode 100644 index 00000000..37fddfcf --- /dev/null +++ b/src/components/rpg-creation-result/index.ts @@ -0,0 +1,4 @@ +export { + RpgCreationResultView, + type RpgCreationResultViewProps, +} from './RpgCreationResultView'; diff --git a/src/hooks/story/npcEncounterActions.test.ts b/src/hooks/story/npcEncounterActions.test.ts index 6cfb9838..f9e74e1a 100644 --- a/src/hooks/story/npcEncounterActions.test.ts +++ b/src/hooks/story/npcEncounterActions.test.ts @@ -1423,12 +1423,7 @@ describe('npcEncounterActions', () => { '更换任务', '放弃任务', ]); - expect(lastStory.dialogue?.at(-2)).toEqual( - expect.objectContaining({ - speaker: 'player', - text: '能不能换一份更适合眼下局势的委托?', - }), - ); + expect(lastStory.text).toContain('断桥夜巡'); }); it('forwards pending quest offer acceptance to the server runtime resolver', async () => { diff --git a/src/services/rpg-creation/index.ts b/src/services/rpg-creation/index.ts new file mode 100644 index 00000000..005dda5c --- /dev/null +++ b/src/services/rpg-creation/index.ts @@ -0,0 +1,35 @@ +export { + createRpgCreationSession, + executeRpgCreationAction, + getRpgCreationCardDetail, + getRpgCreationOperation, + getRpgCreationSession, + rpgCreationAgentClient, + sendRpgCreationMessage, + streamRpgCreationMessage, +} from './rpgCreationAgentClient'; +export { rpgCreationAssetClient } from './rpgCreationAssetClient'; +export { + generateRpgWorldCoverImage, + generateRpgWorldLandmark, + generateRpgWorldPlayableNpc, + generateRpgWorldSceneImage, + generateRpgWorldSceneNpc, + generateRpgWorldStoryNpc, + uploadRpgWorldCoverImage, +} from './rpgCreationAssetClient'; +export { + deleteRpgWorldProfile, + getRpgWorldGalleryDetail, + listRpgWorldGallery, + listRpgWorldLibrary, + publishRpgWorldProfile, + rpgCreationLibraryClient, + unpublishRpgWorldProfile, + upsertRpgWorldProfile, +} from './rpgCreationLibraryClient'; +export { + buildRpgCreationPreviewFromAgentDraft, + rpgCreationPreviewAdapter, +} from './rpgCreationPreviewAdapter'; +export { listRpgCreationWorks, rpgCreationWorkClient } from './rpgCreationWorkClient'; diff --git a/src/services/rpg-creation/rpgCreationAgentClient.ts b/src/services/rpg-creation/rpgCreationAgentClient.ts new file mode 100644 index 00000000..07848212 --- /dev/null +++ b/src/services/rpg-creation/rpgCreationAgentClient.ts @@ -0,0 +1,33 @@ +import { + createCustomWorldAgentSession, + executeCustomWorldAgentAction, + getCustomWorldAgentCardDetail, + getCustomWorldAgentOperation, + getCustomWorldAgentSession, + sendCustomWorldAgentMessage, + streamCustomWorldAgentMessage, +} from '../aiService'; + +/** + * 工作包 A 先把 RPG 创作 Agent 请求聚合到独立 client 命名空间。 + * 当前仍直接透传旧实现,后续工作包 D 再逐步把 `aiService.ts` 中的相关接口迁出。 + */ +export const rpgCreationAgentClient = { + createSession: createCustomWorldAgentSession, + getSession: getCustomWorldAgentSession, + sendMessage: sendCustomWorldAgentMessage, + streamMessage: streamCustomWorldAgentMessage, + executeAction: executeCustomWorldAgentAction, + getOperation: getCustomWorldAgentOperation, + getCardDetail: getCustomWorldAgentCardDetail, +}; + +export { + createCustomWorldAgentSession as createRpgCreationSession, + executeCustomWorldAgentAction as executeRpgCreationAction, + getCustomWorldAgentCardDetail as getRpgCreationCardDetail, + getCustomWorldAgentOperation as getRpgCreationOperation, + getCustomWorldAgentSession as getRpgCreationSession, + sendCustomWorldAgentMessage as sendRpgCreationMessage, + streamCustomWorldAgentMessage as streamRpgCreationMessage, +}; diff --git a/src/services/rpg-creation/rpgCreationAssetClient.ts b/src/services/rpg-creation/rpgCreationAssetClient.ts new file mode 100644 index 00000000..c8d47c59 --- /dev/null +++ b/src/services/rpg-creation/rpgCreationAssetClient.ts @@ -0,0 +1,35 @@ +import { + generateCustomWorldLandmark, + generateCustomWorldPlayableNpc, + generateCustomWorldSceneImage, + generateCustomWorldSceneNpc, + generateCustomWorldStoryNpc, +} from '../aiService'; +import { + generateCustomWorldCoverImage, + uploadCustomWorldCoverImage, +} from '../customWorldCoverAssetService'; + +/** + * 工作包 A 先把 RPG 创作资产相关请求收口到独立 client。 + * 当前仍桥接旧接口,后续工作包 C、D 会继续细分角色资产、场景资产和封面资产职责。 + */ +export const rpgCreationAssetClient = { + generateSceneImage: generateCustomWorldSceneImage, + generateSceneNpc: generateCustomWorldSceneNpc, + generatePlayableNpc: generateCustomWorldPlayableNpc, + generateStoryNpc: generateCustomWorldStoryNpc, + generateLandmark: generateCustomWorldLandmark, + generateCoverImage: generateCustomWorldCoverImage, + uploadCoverImage: uploadCustomWorldCoverImage, +}; + +export { + generateCustomWorldCoverImage as generateRpgWorldCoverImage, + generateCustomWorldLandmark as generateRpgWorldLandmark, + generateCustomWorldPlayableNpc as generateRpgWorldPlayableNpc, + generateCustomWorldSceneImage as generateRpgWorldSceneImage, + generateCustomWorldSceneNpc as generateRpgWorldSceneNpc, + generateCustomWorldStoryNpc as generateRpgWorldStoryNpc, + uploadCustomWorldCoverImage as uploadRpgWorldCoverImage, +}; diff --git a/src/services/rpg-creation/rpgCreationLibraryClient.ts b/src/services/rpg-creation/rpgCreationLibraryClient.ts new file mode 100644 index 00000000..56aa2b9b --- /dev/null +++ b/src/services/rpg-creation/rpgCreationLibraryClient.ts @@ -0,0 +1,33 @@ +import { + deleteCustomWorldProfile, + getCustomWorldGalleryDetail, + listCustomWorldGallery, + listCustomWorldLibrary, + publishCustomWorldProfile, + unpublishCustomWorldProfile, + upsertCustomWorldProfile, +} from '../storageService'; + +/** + * 工作包 A 先建立 RPG 世界作品库和广场相关请求的统一入口。 + * 当前实现仍透传旧 `storageService.ts`,后续工作包 D 会把作品链路彻底迁入这里。 + */ +export const rpgCreationLibraryClient = { + listLibrary: listCustomWorldLibrary, + upsertProfile: upsertCustomWorldProfile, + deleteProfile: deleteCustomWorldProfile, + publishProfile: publishCustomWorldProfile, + unpublishProfile: unpublishCustomWorldProfile, + listGallery: listCustomWorldGallery, + getGalleryDetail: getCustomWorldGalleryDetail, +}; + +export { + deleteCustomWorldProfile as deleteRpgWorldProfile, + getCustomWorldGalleryDetail as getRpgWorldGalleryDetail, + listCustomWorldGallery as listRpgWorldGallery, + listCustomWorldLibrary as listRpgWorldLibrary, + publishCustomWorldProfile as publishRpgWorldProfile, + unpublishCustomWorldProfile as unpublishRpgWorldProfile, + upsertCustomWorldProfile as upsertRpgWorldProfile, +}; diff --git a/src/services/rpg-creation/rpgCreationPreviewAdapter.ts b/src/services/rpg-creation/rpgCreationPreviewAdapter.ts new file mode 100644 index 00000000..c33c2527 --- /dev/null +++ b/src/services/rpg-creation/rpgCreationPreviewAdapter.ts @@ -0,0 +1,13 @@ +import { buildCustomWorldProfileFromAgentDraft } from '../customWorldAgentDraftResult'; + +/** + * 这是工作包 A 提供的新命名兼容层。 + * 当前仍复用旧的前端草稿编译逻辑,后续 Phase 3 会继续把结果预览编译后移到服务端。 + */ +export const rpgCreationPreviewAdapter = { + buildPreviewFromAgentDraft: buildCustomWorldProfileFromAgentDraft, +}; + +export { + buildCustomWorldProfileFromAgentDraft as buildRpgCreationPreviewFromAgentDraft, +}; diff --git a/src/services/rpg-creation/rpgCreationWorkClient.ts b/src/services/rpg-creation/rpgCreationWorkClient.ts new file mode 100644 index 00000000..0f023dd1 --- /dev/null +++ b/src/services/rpg-creation/rpgCreationWorkClient.ts @@ -0,0 +1,11 @@ +import { listCustomWorldWorks } from '../storageService'; + +/** + * 工作包 A 先建立 RPG 创作作品流的独立 client。 + * 当前作品列表仍复用旧存储服务,后续工作包 D 再切出真正的专属请求实现。 + */ +export const rpgCreationWorkClient = { + listWorks: listCustomWorldWorks, +}; + +export { listCustomWorldWorks as listRpgCreationWorks };