xinyu-pro 0.21.0

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.
Files changed (96) hide show
  1. package/.env.example +21 -0
  2. package/README.md +36 -0
  3. package/app/api/chat/route.ts +84 -0
  4. package/app/api/generate-svg/route.ts +171 -0
  5. package/app/api/generate-theme/route.ts +137 -0
  6. package/app/api/plugins/bindings/route.ts +173 -0
  7. package/app/api/plugins/export/route.ts +122 -0
  8. package/app/api/plugins/export-xye/route.ts +156 -0
  9. package/app/api/plugins/files/route.ts +146 -0
  10. package/app/api/plugins/files-list/route.ts +168 -0
  11. package/app/api/plugins/files-upload/route.ts +101 -0
  12. package/app/api/plugins/files-write/route.ts +272 -0
  13. package/app/api/plugins/import/route.ts +140 -0
  14. package/app/api/plugins/import-package/route.ts +231 -0
  15. package/app/api/plugins/resources/route.ts +109 -0
  16. package/app/api/plugins/route.ts +308 -0
  17. package/app/api/plugins/scan/route.ts +280 -0
  18. package/app/api/plugins/storage/route.ts +146 -0
  19. package/app/api/sessions/route.ts +165 -0
  20. package/app/api/settings/route.ts +40 -0
  21. package/app/api/suggest-fields/route.ts +129 -0
  22. package/app/api/templates/route.ts +159 -0
  23. package/app/api/test-api/route.ts +63 -0
  24. package/app/editor/page.tsx +1466 -0
  25. package/app/extensions/create/page.tsx +1422 -0
  26. package/app/extensions/edit/[id]/page.tsx +2342 -0
  27. package/app/extensions/page.tsx +1572 -0
  28. package/app/extensions/tutorial/page.tsx +4258 -0
  29. package/app/favicon.ico +0 -0
  30. package/app/fonts/GeistMonoVF.woff +0 -0
  31. package/app/fonts/GeistVF.woff +0 -0
  32. package/app/game/[id]/page.tsx +996 -0
  33. package/app/globals.css +3 -0
  34. package/app/layout.tsx +26 -0
  35. package/app/loading.tsx +26 -0
  36. package/app/page.tsx +345 -0
  37. package/app/settings/page.tsx +1490 -0
  38. package/bin/cli.js +262 -0
  39. package/components/ChatInput.tsx +106 -0
  40. package/components/ChatWindow.tsx +52 -0
  41. package/components/FullPageLoader.tsx +107 -0
  42. package/components/LoadingDots.tsx +20 -0
  43. package/components/MathCurveLoader.tsx +173 -0
  44. package/components/MessageBubble.tsx +147 -0
  45. package/components/WorldCardPreview.tsx +98 -0
  46. package/components/WorldCardUploader.tsx +58 -0
  47. package/components/ui/ConfirmDialog.tsx +135 -0
  48. package/components/ui/PageHeader.tsx +99 -0
  49. package/components/ui/PermissionConflictDialog.tsx +206 -0
  50. package/components/ui/PluginConfigForm.tsx +192 -0
  51. package/components/ui/PluginFloatingLayer.tsx +52 -0
  52. package/components/ui/PluginIcon.tsx +53 -0
  53. package/components/ui/PluginModalRenderer.tsx +185 -0
  54. package/components/ui/PluginProvider.tsx +1038 -0
  55. package/components/ui/PluginSlotRenderer.tsx +76 -0
  56. package/components/ui/ThemeCustomizer.tsx +174 -0
  57. package/components/ui/ThemeProvider.tsx +125 -0
  58. package/components/ui/ThemeSwitcher.tsx +140 -0
  59. package/components/ui/ToastProvider.tsx +141 -0
  60. package/lib/builtin-plugins.ts +11 -0
  61. package/lib/db-init.ts +35 -0
  62. package/lib/db.ts +244 -0
  63. package/lib/manifest-parser.ts +185 -0
  64. package/lib/parseWorldCard.ts +110 -0
  65. package/lib/plugin-dom-sandbox.ts +327 -0
  66. package/lib/plugin-events.ts +88 -0
  67. package/lib/plugin-files.ts +186 -0
  68. package/lib/plugin-html-sanitizer.ts +79 -0
  69. package/lib/plugin-resource-tracker.ts +175 -0
  70. package/lib/plugin-runtime.ts +2287 -0
  71. package/lib/plugin-security.ts +151 -0
  72. package/lib/plugin-types.ts +416 -0
  73. package/lib/prompt-builder.ts +55 -0
  74. package/lib/router-history.ts +119 -0
  75. package/lib/storage.ts +381 -0
  76. package/lib/themes.ts +129 -0
  77. package/lib/types.ts +117 -0
  78. package/lib/version.ts +55 -0
  79. package/next.config.mjs +43 -0
  80. package/package.json +56 -0
  81. package/plugins/xinyu.bag-system.xye +0 -0
  82. package/plugins/xinyu.cache-optimizer.xye +0 -0
  83. package/plugins/xinyu.dice-arbiter.xye +0 -0
  84. package/plugins/xinyu.game-auto-start-choices.xye +0 -0
  85. package/plugins/xinyu.markdown-render.xye +0 -0
  86. package/plugins/xinyu.slot-ui-beautify.xye +0 -0
  87. package/plugins/xinyu.world-info.xye +0 -0
  88. package/postcss.config.mjs +8 -0
  89. package/public/templates/atlantis.svg +63 -0
  90. package/public/templates/cyber-city.svg +68 -0
  91. package/public/templates/jianghu.svg +69 -0
  92. package/public/templates//351/255/224/346/263/225/344/270/226/347/225/214.svg +137 -0
  93. package/styles/themes.css +111 -0
  94. package/tailwind.config.ts +18 -0
  95. package/tsconfig.json +26 -0
  96. package/version.json +6 -0
@@ -0,0 +1,151 @@
1
+ // lib/plugin-security.ts - 插件安全与权限检查工具
2
+
3
+ import { UIPermission, PluginManifest, UISlotId } from './plugin-types';
4
+ import { checkAppVersion } from './version';
5
+
6
+ /** 默认共享权限集(插件未声明 commonPermissions 时使用) */
7
+ const DEFAULT_COMMON_PERMISSIONS: UIPermission[] = [
8
+ 'slot:input-toolbar',
9
+ 'style:inject',
10
+ 'modal:confirm',
11
+ ];
12
+
13
+ /**
14
+ * 获取插件的有效共享权限列表(commonPermissions + exclusivePermissions 或默认权限集)
15
+ */
16
+ export function getEffectivePermissions(manifest: PluginManifest): UIPermission[] {
17
+ const common = manifest.commonPermissions || [];
18
+ const exclusive = manifest.exclusivePermissions || [];
19
+ if (common.length > 0 || exclusive.length > 0) {
20
+ return [...common, ...exclusive];
21
+ }
22
+ return DEFAULT_COMMON_PERMISSIONS;
23
+ }
24
+
25
+ /**
26
+ * 检查插件是否具有指定共享权限
27
+ */
28
+ export function checkUIPermission(
29
+ manifest: PluginManifest,
30
+ permission: UIPermission
31
+ ): boolean {
32
+ const permissions = getEffectivePermissions(manifest);
33
+ return permissions.includes(permission);
34
+ }
35
+
36
+ /**
37
+ * 检查插件是否具有指定插槽的注入权限
38
+ */
39
+ export function checkSlotPermission(
40
+ manifest: PluginManifest,
41
+ slotId: UISlotId
42
+ ): boolean {
43
+ return checkUIPermission(manifest, `slot:${slotId}` as UIPermission);
44
+ }
45
+
46
+ /**
47
+ * 检查插件是否具有自由 DOM 操作权限
48
+ */
49
+ export function checkDOMPermission(manifest: PluginManifest): boolean {
50
+ return checkUIPermission(manifest, 'dom:free');
51
+ }
52
+
53
+ /**
54
+ * 检查插件是否具有样式注入权限
55
+ */
56
+ export function checkStylePermission(manifest: PluginManifest): boolean {
57
+ return checkUIPermission(manifest, 'style:inject');
58
+ }
59
+
60
+ /**
61
+ * 检查插件是否具有模态框权限
62
+ */
63
+ export function checkModalPermission(manifest: PluginManifest): boolean {
64
+ return (
65
+ checkUIPermission(manifest, 'modal:confirm') ||
66
+ checkUIPermission(manifest, 'modal:custom')
67
+ );
68
+ }
69
+
70
+ /**
71
+ * 检查插件是否具有宿主事件监听权限
72
+ */
73
+ export function checkHostEventPermission(manifest: PluginManifest): boolean {
74
+ return checkUIPermission(manifest, 'event:host');
75
+ }
76
+
77
+ /**
78
+ * 验证权限声明是否有效
79
+ */
80
+ export function validatePermissions(permissions: UIPermission[]): {
81
+ valid: boolean;
82
+ invalid: string[];
83
+ } {
84
+ const validPermissionPattern = /^(slot:[\w-]+|slot:style:[\w-]+|dom:free|dom:query|style:inject|modal:confirm|modal:custom|event:host|storage:plugin|file:read|file:write|game:info:read|game:info:write|input:control)$/;
85
+ const invalid = permissions.filter((p) => !validPermissionPattern.test(p));
86
+ return { valid: invalid.length === 0, invalid };
87
+ }
88
+
89
+ /**
90
+ * 检查插件的 minAppVersion 是否满足当前应用版本
91
+ * @returns { satisfied, current, required } — satisfied 为 false 时表示版本不兼容
92
+ */
93
+ export function checkPluginVersionCompat(manifest: PluginManifest): {
94
+ satisfied: boolean;
95
+ current: string;
96
+ required: string;
97
+ warning?: string;
98
+ } {
99
+ if (!manifest.minAppVersion) {
100
+ return { satisfied: true, current: '', required: '' };
101
+ }
102
+ const result = checkAppVersion(manifest.minAppVersion);
103
+ return {
104
+ ...result,
105
+ warning: result.satisfied
106
+ ? undefined
107
+ : `插件「${manifest.name}」要求星语 Pro v${result.required} 或更高版本,当前版本为 v${result.current},部分功能可能不可用`,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * 检查插件是否具有持久化存储权限
113
+ */
114
+ export function checkStoragePermission(manifest: PluginManifest): boolean {
115
+ return checkUIPermission(manifest, 'storage:plugin');
116
+ }
117
+
118
+ /**
119
+ * 检查插件是否具有文件读取权限
120
+ */
121
+ export function checkFileReadPermission(manifest: PluginManifest): boolean {
122
+ return checkUIPermission(manifest, 'file:read');
123
+ }
124
+
125
+ /**
126
+ * 检查插件是否具有文件写入权限
127
+ */
128
+ export function checkFileWritePermission(manifest: PluginManifest): boolean {
129
+ return checkUIPermission(manifest, 'file:write');
130
+ }
131
+
132
+ /**
133
+ * 检查插件是否具有输入框控制权限
134
+ */
135
+ export function checkInputControlPermission(manifest: PluginManifest): boolean {
136
+ return checkUIPermission(manifest, 'input:control');
137
+ }
138
+
139
+ /**
140
+ * 检查插件是否具有游戏信息读取权限
141
+ */
142
+ export function checkGameInfoPermission(manifest: PluginManifest): boolean {
143
+ return checkUIPermission(manifest, 'game:info:read');
144
+ }
145
+
146
+ /**
147
+ * 检查插件是否具有游戏信息写入权限
148
+ */
149
+ export function checkGameInfoWritePermission(manifest: PluginManifest): boolean {
150
+ return checkUIPermission(manifest, 'game:info:write');
151
+ }
@@ -0,0 +1,416 @@
1
+ // lib/plugin-types.ts - 插件系统 TypeScript 类型定义
2
+
3
+ /** 插件类型枚举 */
4
+ export type PluginType = 'game-mechanics' | 'message-render' | 'ai-prompt' | 'input-enhance';
5
+
6
+ /** 插件类型标签映射 */
7
+ export const PLUGIN_TYPE_LABELS: Record<PluginType, { label: string; icon: string }> = {
8
+ 'game-mechanics': { label: '游戏机制', icon: '🎮' },
9
+ 'message-render': { label: '消息渲染', icon: '🎨' },
10
+ 'ai-prompt': { label: 'AI Prompt', icon: '🤖' },
11
+ 'input-enhance': { label: '输入增强', icon: '⌨️' },
12
+ };
13
+
14
+ /** 插件生命周期钩子 */
15
+ export type PluginHook =
16
+ | 'onLoad' // 插件自身加载完成时触发(参数:pluginId)
17
+ | 'onPluginLoad' // 任意插件加载完成时触发(参数:被加载插件的 pluginId)
18
+ | 'onUnload' // 插件卸载时触发(无参数)
19
+ | 'onGameInit' // 游戏初始化:进入游戏且无聊天记录时触发(无参数)
20
+ | 'onPluginBindingWorld' // 插件被绑定到世界时触发(参数:worldId)
21
+ | 'onConfigChange' // 插件自身配置变更时触发(参数:config)
22
+ | 'onGlobalConfigChange' // 任意插件配置变更时触发(参数:pluginId, config)
23
+ | 'onRenderMessage'; // 消息渲染前触发(参数:{ role, content }),返回修改后的 content
24
+
25
+ /** 插件配置项定义 */
26
+ export interface PluginConfigField {
27
+ key: string;
28
+ label: string;
29
+ type: 'text' | 'number' | 'boolean' | 'select' | 'textarea';
30
+ defaultValue: string | number | boolean;
31
+ description?: string;
32
+ options?: { label: string; value: string }[];
33
+ }
34
+
35
+ /** UI 插槽类型(旧版,保留兼容) */
36
+ export type UISlotType = 'game-sidebar' | 'game-header' | 'game-input-toolbar' | 'message-bubble' | 'settings-panel';
37
+
38
+ /** UI 插槽注册(旧版,保留兼容) */
39
+ export interface UISlotRegistration {
40
+ slot: UISlotType;
41
+ component: string;
42
+ priority?: number;
43
+ }
44
+
45
+ // ==================== 全局 UI API 类型 ====================
46
+
47
+ /** UI 插槽 ID(新版,游戏页面全区域) */
48
+ export type UISlotId =
49
+ | 'header-left'
50
+ | 'header-center'
51
+ | 'header-right'
52
+ | 'sidebar-left'
53
+ | 'sidebar-right'
54
+ | 'message-top'
55
+ | 'message-bottom'
56
+ | 'input-toolbar'
57
+ | 'input-above'
58
+ | 'status-bar'
59
+ | 'floating'
60
+ | 'overlay';
61
+
62
+ /** 所有可用插槽 ID 常量 */
63
+ export const UI_SLOT_IDS: UISlotId[] = [
64
+ 'header-left', 'header-center', 'header-right',
65
+ 'sidebar-left', 'sidebar-right',
66
+ 'message-top', 'message-bottom',
67
+ 'input-toolbar', 'input-above',
68
+ 'status-bar', 'floating', 'overlay',
69
+ ];
70
+
71
+ /** 插槽 ID 的中文标签映射 */
72
+ export const UI_SLOT_LABELS: Record<UISlotId, string> = {
73
+ 'header-left': '顶部导航栏-左侧',
74
+ 'header-center': '顶部导航栏-中间',
75
+ 'header-right': '顶部导航栏-右侧',
76
+ 'sidebar-left': '左侧边栏',
77
+ 'sidebar-right': '右侧边栏',
78
+ 'message-top': '消息区域-上方',
79
+ 'message-bottom': '消息区域-下方',
80
+ 'input-toolbar': '输入框工具栏',
81
+ 'input-above': '输入框-正上方',
82
+ 'status-bar': '底部状态栏',
83
+ 'floating': '浮动层',
84
+ 'overlay': '全屏遮罩层',
85
+ };
86
+
87
+ /** 插槽注册项 */
88
+ export interface SlotRegistration {
89
+ id: string;
90
+ pluginId: string;
91
+ slotId: UISlotId;
92
+ content: string;
93
+ priority?: number;
94
+ persistent?: boolean;
95
+ }
96
+
97
+ /** 模态框操作按钮 */
98
+ export interface ModalAction {
99
+ text: string;
100
+ primary?: boolean;
101
+ danger?: boolean;
102
+ onClick: () => void;
103
+ }
104
+
105
+ /** 模态框操作(插件声明时,onClick 接收上下文参数) */
106
+ export interface ModalActionInput {
107
+ text: string;
108
+ primary?: boolean;
109
+ danger?: boolean;
110
+ onClick: (ctx: { modalId: string; close: () => void }) => void;
111
+ }
112
+
113
+ /** 模态框注册项 */
114
+ export interface ModalRegistration {
115
+ id: string;
116
+ pluginId: string;
117
+ title: string;
118
+ content: string;
119
+ width?: string;
120
+ style?: React.CSSProperties;
121
+ closable?: boolean;
122
+ backdrop?: boolean;
123
+ onClose?: () => void;
124
+ actions?: ModalAction[];
125
+ }
126
+
127
+ /** 确认对话框选项 */
128
+ export interface ConfirmOptions {
129
+ confirmText?: string;
130
+ cancelText?: string;
131
+ danger?: boolean;
132
+ }
133
+
134
+ /** 宿主事件映射 */
135
+ export interface HostEventMap {
136
+ 'message:received': { message: { role: string; content: string } };
137
+ 'message:sent': { content: string };
138
+ 'game:stateChange': { changedKeys: string[] };
139
+ 'sidebar:open': undefined;
140
+ 'sidebar:close': undefined;
141
+ 'input:focus': undefined;
142
+ 'input:blur': undefined;
143
+ 'theme:change': { themeId: string };
144
+ 'plugin:enabled': { pluginId: string };
145
+ 'plugin:disabled': { pluginId: string };
146
+ }
147
+
148
+ /** UI 权限类型(通用权限标识符,可用于 common/exclusive/required 三种模式) */
149
+ export type UIPermission =
150
+ | 'slot:header-left'
151
+ | 'slot:header-center'
152
+ | 'slot:header-right'
153
+ | 'slot:sidebar-left'
154
+ | 'slot:sidebar-right'
155
+ | 'slot:message-top'
156
+ | 'slot:message-bottom'
157
+ | 'slot:input-toolbar'
158
+ | 'slot:input-above'
159
+ | 'slot:status-bar'
160
+ | 'slot:floating'
161
+ | 'slot:overlay'
162
+ | 'dom:free'
163
+ | 'style:inject'
164
+ | 'modal:confirm'
165
+ | 'modal:custom'
166
+ | 'event:host'
167
+ | 'dom:query'
168
+ // 数据与文件权限
169
+ | 'storage:plugin' // 插件持久化存储(key-value,页面刷新后保留)
170
+ | 'file:read' // 读取插件私有目录下的文件
171
+ | 'file:write' // 写入/删除插件私有目录下的文件
172
+ // 游戏数据权限
173
+ | 'game:info:read' // 读取游戏世界信息(世界设定、会话ID、消息列表、游戏属性)
174
+ | 'game:info:write' // 写入游戏世界设定(修改世界信息,实时生效并持久化)
175
+ // 输入框控制权限
176
+ | 'input:control' // 控制聊天输入框(内容、样式、状态、事件)
177
+ /** 排他权限专用:独占插槽样式 */
178
+ | 'slot:style:header-left'
179
+ | 'slot:style:header-center'
180
+ | 'slot:style:header-right'
181
+ | 'slot:style:sidebar-left'
182
+ | 'slot:style:sidebar-right'
183
+ | 'slot:style:message-top'
184
+ | 'slot:style:message-bottom'
185
+ | 'slot:style:input-toolbar'
186
+ | 'slot:style:input-above'
187
+ | 'slot:style:status-bar'
188
+ | 'slot:style:floating'
189
+ | 'slot:style:overlay';
190
+
191
+ /** UI 权限标签映射 */
192
+ export const UI_PERMISSION_LABELS: Record<UIPermission, string> = {
193
+ 'slot:header-left': '顶部导航栏-左侧',
194
+ 'slot:header-center': '顶部导航栏-中间',
195
+ 'slot:header-right': '顶部导航栏-右侧',
196
+ 'slot:sidebar-left': '左侧边栏',
197
+ 'slot:sidebar-right': '右侧边栏',
198
+ 'slot:message-top': '消息区域-上方',
199
+ 'slot:message-bottom': '消息区域-下方',
200
+ 'slot:input-toolbar': '输入框工具栏',
201
+ 'slot:input-above': '输入框-正上方',
202
+ 'slot:status-bar': '底部状态栏',
203
+ 'slot:floating': '浮动层',
204
+ 'slot:overlay': '全屏遮罩层',
205
+ 'dom:free': '自由 DOM 操作',
206
+ 'style:inject': '样式注入',
207
+ 'modal:confirm': '确认对话框',
208
+ 'modal:custom': '自定义模态框',
209
+ 'event:host': '宿主事件监听',
210
+ 'dom:query': 'DOM 查询',
211
+ 'storage:plugin': '持久化存储',
212
+ 'file:read': '文件读取',
213
+ 'file:write': '文件写入',
214
+ 'game:info:read': '游戏信息读取',
215
+ 'game:info:write': '游戏信息写入',
216
+ 'input:control': '输入框控制',
217
+ 'slot:style:header-left': '独占-顶部导航栏左侧样式',
218
+ 'slot:style:header-center': '独占-顶部导航栏中间样式',
219
+ 'slot:style:header-right': '独占-顶部导航栏右侧样式',
220
+ 'slot:style:sidebar-left': '独占-左侧边栏样式',
221
+ 'slot:style:sidebar-right': '独占-右侧边栏样式',
222
+ 'slot:style:message-top': '独占-消息区域上方样式',
223
+ 'slot:style:message-bottom': '独占-消息区域下方样式',
224
+ 'slot:style:input-toolbar': '独占-输入框工具栏样式',
225
+ 'slot:style:input-above': '独占-输入框正上方样式',
226
+ 'slot:style:status-bar': '独占-底部状态栏样式',
227
+ 'slot:style:floating': '独占-浮动层样式',
228
+ 'slot:style:overlay': '独占-全屏遮罩层样式',
229
+ };
230
+
231
+ // ==================== 原有类型(保留) ====================
232
+
233
+ /** 插件清单 */
234
+ export interface PluginManifest {
235
+ id: string;
236
+ name: string;
237
+ version: string;
238
+ description: string;
239
+ author: string;
240
+ type: PluginType;
241
+ icon?: string;
242
+ minAppVersion?: string;
243
+ code: string;
244
+ configSchema?: PluginConfigField[];
245
+ uiSlots?: UISlotRegistration[];
246
+ /** 共享权限(可被多个插件同时声明,默认权限集:slot:input-toolbar, style:inject, modal:confirm) */
247
+ commonPermissions?: UIPermission[];
248
+ /** 排他权限(同时只能被一个插件声明,冲突时后加载的插件启动失败) */
249
+ exclusivePermissions?: UIPermission[];
250
+ /** 必要权限(没有此权限则插件不允许启动,必须在 commonPermissions 或 exclusivePermissions 中声明) */
251
+ requiredPermissions?: UIPermission[];
252
+ /** 插件公开导出的 API(供其他插件调用) */
253
+ publicExports?: Record<string, {
254
+ description: string;
255
+ params?: string;
256
+ returns?: string;
257
+ }>;
258
+ /** 插件依赖列表 */
259
+ dependencies?: PluginDependency[];
260
+ /** 入口代码文件路径(如 data/plugins/x.y/index.js),用于判断插件格式 */
261
+ codePath?: string;
262
+ createdAt: string;
263
+ updatedAt: string;
264
+ }
265
+
266
+ /** 插件依赖定义 */
267
+ export interface PluginDependency {
268
+ pluginId: string;
269
+ versionRange?: string;
270
+ optional?: boolean;
271
+ }
272
+
273
+ /** 依赖状态 */
274
+ export interface DependencyStatus {
275
+ pluginId: string;
276
+ status: 'satisfied' | 'missing' | 'version_mismatch';
277
+ required: boolean;
278
+ installedVersion?: string;
279
+ }
280
+
281
+ /** 插件运行时实例 */
282
+ export interface PluginInstance {
283
+ manifest: PluginManifest;
284
+ enabled: boolean;
285
+ config: Record<string, unknown>;
286
+ scope: 'global' | 'world';
287
+ worldId?: string;
288
+ exports?: Record<string, (...args: unknown[]) => unknown>;
289
+ }
290
+
291
+ /** 插件绑定(数据库存储) */
292
+ export interface PluginBinding {
293
+ id?: number;
294
+ extensionId: string;
295
+ scope: 'global' | 'world';
296
+ worldId?: string;
297
+ enabled: boolean;
298
+ config: Record<string, unknown>;
299
+ sortOrder: number;
300
+ createdAt?: string;
301
+ updatedAt?: string;
302
+ }
303
+
304
+ /** 游戏属性定义 */
305
+ export interface GameAttribute {
306
+ pluginId?: string;
307
+ key: string;
308
+ label: string;
309
+ type: 'number' | 'string' | 'boolean';
310
+ value: number | string | boolean;
311
+ min?: number;
312
+ max?: number;
313
+ icon?: string;
314
+ color?: string;
315
+ group?: string;
316
+ }
317
+
318
+ /** 快捷指令 */
319
+ export interface ChatCommand {
320
+ pluginId?: string;
321
+ name: string;
322
+ description: string;
323
+ icon?: string;
324
+ handler: (args: string) => string | void | Promise<string | void>;
325
+ }
326
+
327
+ /** 侧边栏面板注册(扩展 pluginId) */
328
+ export interface SidebarPanelRegistration {
329
+ id: string;
330
+ pluginId?: string;
331
+ title: string;
332
+ icon?: string;
333
+ render: () => string;
334
+ priority?: number;
335
+ }
336
+
337
+ /** 工具栏按钮注册(扩展 pluginId) */
338
+ export interface ToolbarButtonRegistration {
339
+ id: string;
340
+ pluginId?: string;
341
+ label: string;
342
+ icon?: string;
343
+ onClick: () => void;
344
+ order?: number;
345
+ }
346
+
347
+ /** 骰子结果 */
348
+ export interface DiceResult {
349
+ notation: string;
350
+ rolls: number[];
351
+ modifier: number;
352
+ total: number;
353
+ }
354
+
355
+ /** AI 请求配置 */
356
+ export interface AIRequestConfig {
357
+ temperature: number;
358
+ maxTokens: number;
359
+ model: string;
360
+ }
361
+
362
+ /** 消息渲染器(扩展 pluginId) */
363
+ export type MessageRenderer = {
364
+ pluginId?: string;
365
+ matcher: (msg: { role: string; content: string }) => boolean;
366
+ renderer: (msg: { role: string; content: string }) => string;
367
+ };
368
+
369
+ /** 安全检测报告 */
370
+ export interface SecurityScanResult {
371
+ riskLevel: 'low' | 'medium' | 'high' | 'critical';
372
+ score: number;
373
+ summary: string;
374
+ findings: SecurityFinding[];
375
+ recommendation: string;
376
+ }
377
+
378
+ /** 安全检测发现项 */
379
+ export interface SecurityFinding {
380
+ level: 'info' | 'warning' | 'danger';
381
+ category: string;
382
+ description: string;
383
+ code?: string;
384
+ recommendation: string;
385
+ }
386
+
387
+ /** 插件导出/导入格式 */
388
+ export interface PluginExportFormat {
389
+ format: 'xinyu-extension-v1';
390
+ exportedAt: string;
391
+ plugins: PluginManifest[];
392
+ }
393
+
394
+ /** .xye 插件包中的 manifest.json 结构 */
395
+ export interface XyeManifest {
396
+ /** 插件入口 JS 文件路径(相对于包根目录) */
397
+ pluginEntry: string;
398
+ id: string;
399
+ name: string;
400
+ version: string;
401
+ description?: string;
402
+ author?: string;
403
+ type: PluginType;
404
+ icon?: string;
405
+ minAppVersion?: string;
406
+ configSchema?: PluginConfigField[];
407
+ uiSlots?: UISlotRegistration[];
408
+ commonPermissions?: UIPermission[];
409
+ exclusivePermissions?: UIPermission[];
410
+ requiredPermissions?: UIPermission[];
411
+ publicExports?: Record<string, { description: string; params?: string; returns?: string }>;
412
+ dependencies?: PluginDependency[];
413
+ }
414
+
415
+ /** Hook 处理函数类型 */
416
+ export type HookHandler = (...args: unknown[]) => unknown;
@@ -0,0 +1,55 @@
1
+ // lib/prompt-builder.ts - 系统提示词构建器(客户端/服务端共享)
2
+
3
+ /** 从动态字段构建 system prompt */
4
+ export function buildSystemPrompt(worldData: Record<string, unknown>, extraPrompt?: string): string {
5
+ // 内部字段(不展示给 AI)
6
+ const internalKeys = new Set(['_plugins', '_labels', '_fieldMeta', '_fieldOrder']);
7
+
8
+ // 收集所有可展示字段
9
+ const entries = Object.entries(worldData).filter(([key, value]) =>
10
+ !internalKeys.has(key) && value !== null && value !== undefined && value !== ''
11
+ );
12
+
13
+ if (entries.length === 0) {
14
+ // 无世界设定,使用基础提示词
15
+ const base = `你是一位沉浸式角色扮演游戏的世界主持人。你的职责是推动剧情发展、扮演所有 NPC、描述场景氛围,以及回应玩家的每一次行动。
16
+
17
+ ## 你必须遵守的规则
18
+ 1. 始终使用第二人称"你"称呼玩家。
19
+ 2. 不能替玩家做决定或替玩家说话。
20
+ 3. 保持世界观的一致性和沉浸感。
21
+ 4. 扮演 NPC 时要符合该角色的性格和立场。
22
+ 5. 每次回复应包含:场景描写、NPC 反应、剧情推进,以及可探索的方向暗示(但不强制)。
23
+ 6. 回复应当生动、有画面感,让玩家有身临其境的感觉。
24
+ 7. 用中文回复。`;
25
+ if (extraPrompt && extraPrompt.trim()) {
26
+ return `${base}\n\n## 额外指令\n${extraPrompt.trim()}`;
27
+ }
28
+ return base;
29
+ }
30
+
31
+ // 构建 worldSection
32
+ let worldSection = '## 世界设定';
33
+ for (const [key, value] of entries) {
34
+ const displayValue = Array.isArray(value) ? value.join('、') : String(value);
35
+ worldSection += `\n- **${key}**: ${displayValue}`;
36
+ }
37
+
38
+ const base = `你是一位沉浸式角色扮演游戏的世界主持人。你的职责是推动剧情发展、扮演所有 NPC、描述场景氛围,以及回应玩家的每一次行动。
39
+
40
+ ${worldSection}
41
+
42
+ ## 你必须遵守的规则
43
+ 1. 始终使用第二人称"你"称呼玩家。
44
+ 2. 不能替玩家做决定或替玩家说话。
45
+ 3. 保持世界观的一致性和沉浸感。
46
+ 4. 扮演 NPC 时要符合该角色的性格和立场。
47
+ 5. 每次回复应包含:场景描写、NPC 反应、剧情推进,以及可探索的方向暗示(但不强制)。
48
+ 6. 回复应当生动、有画面感,让玩家有身临其境的感觉。
49
+ 7. 用中文回复。`;
50
+
51
+ if (extraPrompt && extraPrompt.trim()) {
52
+ return `${base}\n\n## 额外指令\n${extraPrompt.trim()}`;
53
+ }
54
+ return base;
55
+ }