@musnows/scriverse 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.js CHANGED
@@ -40,10 +40,14 @@ import { S3BackupManager } from "./s3-backup.js";
40
40
  import { APP_VERSION } from "./version.js";
41
41
  import { ReleaseUpdateChecker } from "./release-update.js";
42
42
  import { DEFAULT_IMAGE_UPLOAD_LIMITS, formatUploadLimit } from "./upload-limits.js";
43
- import { canReadWorkModule, canWriteWorkModule, fullWorkModulePermissions, proseReplacementPermissionModules } from "./work-permissions.js";
43
+ import { canReadWorkModule, canWriteWorkModule, chapterAnnotationPermissionModule, fullWorkModulePermissions, proseReplacementPermissionModules } from "./work-permissions.js";
44
44
  import { CollaborationPresence, editorPageKey, entityEditorPageKey, modulePageKey, presencePageKinds } from "./collaboration-presence.js";
45
45
  import { PresenceStore } from "./presence-store.js";
46
46
  import { analysisTaskReadModules, clearSessionCookie, createCliApiScopeMiddleware, createUserSessionMiddleware, createWorkAuthorizationMiddleware, relationshipAnalysisReadModules, setSessionCookie, UserAuthService } from "./user-auth.js";
47
+ const chapterAnnotationKinds = ["note", "todo"];
48
+ function readableChapterAnnotationKinds(permissions) {
49
+ return chapterAnnotationKinds.filter((kind) => canReadWorkModule(permissions, chapterAnnotationPermissionModule(kind)));
50
+ }
47
51
  const nonEmpty = z.string().trim().min(1);
48
52
  const identifier = z.string().trim().min(1).max(200);
49
53
  const idempotencyKeySchema = z.string().trim().min(16).max(128)
@@ -110,6 +114,8 @@ const memberRoleValueSchema = z.enum(["editor", "settings-editor", "viewer"]);
110
114
  const moduleAccessSchema = z.enum(["none", "read", "write"]);
111
115
  const modulePermissionsSchema = z.object({
112
116
  prose: moduleAccessSchema,
117
+ comments: moduleAccessSchema.optional(),
118
+ todos: moduleAccessSchema.optional(),
113
119
  drafts: moduleAccessSchema.optional(),
114
120
  settings: moduleAccessSchema,
115
121
  characters: moduleAccessSchema,
@@ -124,6 +130,8 @@ const modulePermissionsSchema = z.object({
124
130
  "ai-settings": moduleAccessSchema
125
131
  }).strict().transform((permissions) => ({
126
132
  ...permissions,
133
+ comments: permissions.comments ?? permissions.prose,
134
+ todos: permissions.todos ?? permissions.prose,
127
135
  drafts: permissions.drafts ?? (permissions.prose === "write" && permissions.settings === "write"
128
136
  ? "write"
129
137
  : permissions.prose === "none" || permissions.settings === "none" ? "none" : "read")
@@ -932,12 +940,18 @@ function redactAiConversationMessage(item, permissions) {
932
940
  if (!message)
933
941
  return item;
934
942
  if (permissions.prose !== "none") {
935
- if (permissions.characters !== "none")
936
- return item;
937
943
  const metadata = recordValue(message.metadata);
938
- if (!metadata || !("mentionCharacterIds" in metadata))
944
+ if (!metadata)
945
+ return item;
946
+ const readableMetadata = { ...metadata };
947
+ if (permissions.characters === "none")
948
+ delete readableMetadata.mentionCharacterIds;
949
+ if (permissions.races === "none")
950
+ delete readableMetadata.mentionRaceIds;
951
+ if (permissions.organizations === "none")
952
+ delete readableMetadata.mentionOrganizationIds;
953
+ if (Object.keys(readableMetadata).length === Object.keys(metadata).length)
939
954
  return item;
940
- const { mentionCharacterIds: _mentionCharacterIds, ...readableMetadata } = metadata;
941
955
  return { ...message, metadata: readableMetadata };
942
956
  }
943
957
  return {
@@ -950,7 +964,9 @@ function redactAiConversationMessage(item, permissions) {
950
964
  }
951
965
  /** 无正文读取权限时隐藏对话预览与消息正文,避免历史对话泄露章节原文。 */
952
966
  function redactAiConversation(record, permissions) {
953
- const readableRecord = permissions.characters === "none" ? { ...record, roleplayCharacter: null } : record;
967
+ const readableRecord = permissions.characters === "none"
968
+ ? { ...record, roleplayCharacter: null, roleplayUserCharacter: null }
969
+ : record;
954
970
  const scopedRecord = redactAiCallContext(readableRecord, permissions);
955
971
  const result = {
956
972
  ...scopedRecord
@@ -1609,12 +1625,23 @@ export function createRuntime(options) {
1609
1625
  const pagination = parsePagination(request.query);
1610
1626
  data(response, pagination ? store.listChapterInsightsPage(request.params.chapterId, pagination) : store.listChapterInsights(request.params.chapterId));
1611
1627
  });
1612
- app.get("/api/chapters/:chapterId/annotations", (request, response) => data(response, store.listChapterAnnotations(request.params.chapterId)));
1628
+ app.get("/api/chapters/:chapterId/annotation-counts", (request, response) => {
1629
+ const permissions = requestPermissions(request);
1630
+ data(response, store.listChapterAnnotationCounts(request.params.chapterId, readableChapterAnnotationKinds(permissions)));
1631
+ });
1632
+ app.get("/api/chapters/:chapterId/annotations", (request, response) => {
1633
+ const line = request.query.line === undefined
1634
+ ? undefined
1635
+ : parse(z.coerce.number().int().positive().max(100_000), request.query.line);
1636
+ const permissions = requestPermissions(request);
1637
+ data(response, store.listChapterAnnotations(request.params.chapterId, readableChapterAnnotationKinds(permissions), line));
1638
+ });
1613
1639
  app.get("/api/works/:workId/chapter-annotations", (request, response) => {
1614
1640
  const pagination = parsePagination(request.query);
1641
+ const permissions = requestPermissions(request, request.params.workId);
1615
1642
  data(response, pagination
1616
- ? store.listWorkChapterAnnotationsPage(request.params.workId, pagination)
1617
- : store.listWorkChapterAnnotations(request.params.workId));
1643
+ ? store.listWorkChapterAnnotationsPage(request.params.workId, pagination, readableChapterAnnotationKinds(permissions))
1644
+ : store.listWorkChapterAnnotations(request.params.workId, readableChapterAnnotationKinds(permissions)));
1618
1645
  });
1619
1646
  app.post("/api/chapters/:chapterId/annotations", (request, response) => {
1620
1647
  const input = parse(z.object({
@@ -2436,7 +2463,7 @@ export function createRuntime(options) {
2436
2463
  }).strict(), request.body);
2437
2464
  const sourceConversation = store.getAiConversationSummary(request.params.conversationId);
2438
2465
  const permissions = requestPermissions(request, String(sourceConversation.workId));
2439
- if (sourceConversation.roleplayCharacter && !canReadWorkModule(permissions, "characters")) {
2466
+ if ((sourceConversation.roleplayCharacter || sourceConversation.roleplayUserCharacter) && !canReadWorkModule(permissions, "characters")) {
2440
2467
  throw new AppError(403, "WORK_MODULE_READ_DENIED", "你没有读取“角色”模块的权限");
2441
2468
  }
2442
2469
  const forked = store.forkAiConversation(request.params.conversationId, input.messageId, input.title, input.requestId);
@@ -2455,8 +2482,11 @@ export function createRuntime(options) {
2455
2482
  data(response, redactAiConversation(updated, permissions));
2456
2483
  });
2457
2484
  app.patch("/api/ai-conversations/:conversationId/roleplay", (request, response) => {
2458
- const input = parse(z.object({ characterId: identifier.nullable() }).strict(), request.body);
2459
- const updated = store.setAiConversationRoleplayCharacter(request.params.conversationId, input.characterId);
2485
+ const input = parse(z.object({
2486
+ characterId: identifier.nullable(),
2487
+ userCharacterId: identifier.nullable().optional()
2488
+ }).strict(), request.body);
2489
+ const updated = store.setAiConversationRoleplayCharacter(request.params.conversationId, input.characterId, input.userCharacterId);
2460
2490
  const permissions = requestPermissions(request, String(updated.workId));
2461
2491
  data(response, redactAiConversation(updated, permissions));
2462
2492
  });
@@ -2709,6 +2739,8 @@ export function createRuntime(options) {
2709
2739
  ...(resolvedScope.characterIds ?? []),
2710
2740
  ...(resolvedScope.mentionCharacterIds ?? [])
2711
2741
  ])];
2742
+ const mentionRaceIds = [...new Set(resolvedScope.raceIds ?? [])];
2743
+ const mentionOrganizationIds = [...new Set(resolvedScope.organizationIds ?? [])];
2712
2744
  const begun = store.beginAiConversationStreamRequest({
2713
2745
  workId: request.params.workId,
2714
2746
  conversationId,
@@ -2719,9 +2751,11 @@ export function createRuntime(options) {
2719
2751
  content: input.instruction,
2720
2752
  citations,
2721
2753
  ...(input.currentMessageId ? { existingMessageId: input.currentMessageId } : {}),
2722
- ...((modelId || mentionCharacterIds.length) ? { metadata: {
2754
+ ...((modelId || mentionCharacterIds.length || mentionRaceIds.length || mentionOrganizationIds.length) ? { metadata: {
2723
2755
  ...(modelId ? { modelId } : {}),
2724
- ...(mentionCharacterIds.length ? { mentionCharacterIds } : {})
2756
+ ...(mentionCharacterIds.length ? { mentionCharacterIds } : {}),
2757
+ ...(mentionRaceIds.length ? { mentionRaceIds } : {}),
2758
+ ...(mentionOrganizationIds.length ? { mentionOrganizationIds } : {})
2725
2759
  } } : {})
2726
2760
  }
2727
2761
  });