@jsbot-tellus/core 2.0.3 → 2.1.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 (78) hide show
  1. package/README.md +13 -17
  2. package/dist/app.js +12 -40
  3. package/dist/app.js.map +1 -1
  4. package/dist/core/config/schema.d.ts +0 -2
  5. package/dist/core/config/schema.d.ts.map +1 -1
  6. package/dist/core/config/schema.js +0 -1
  7. package/dist/core/config/schema.js.map +1 -1
  8. package/dist/core/dispatch/command.d.ts +24 -2
  9. package/dist/core/dispatch/command.d.ts.map +1 -1
  10. package/dist/core/dispatch/command.js +297 -60
  11. package/dist/core/dispatch/command.js.map +1 -1
  12. package/dist/core/dispatch/router.d.ts +17 -3
  13. package/dist/core/dispatch/router.d.ts.map +1 -1
  14. package/dist/core/dispatch/router.js +38 -2
  15. package/dist/core/dispatch/router.js.map +1 -1
  16. package/dist/core/index.d.ts +1 -1
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/plugin/command-params.d.ts +15 -0
  19. package/dist/core/plugin/command-params.d.ts.map +1 -0
  20. package/dist/core/plugin/command-params.js +427 -0
  21. package/dist/core/plugin/command-params.js.map +1 -0
  22. package/dist/core/plugin/context.d.ts.map +1 -1
  23. package/dist/core/plugin/context.js +27 -0
  24. package/dist/core/plugin/context.js.map +1 -1
  25. package/dist/core/plugin/install-dir.d.ts +6 -9
  26. package/dist/core/plugin/install-dir.d.ts.map +1 -1
  27. package/dist/core/plugin/install-dir.js +99 -79
  28. package/dist/core/plugin/install-dir.js.map +1 -1
  29. package/dist/core/plugin/install-validate.d.ts +2 -1
  30. package/dist/core/plugin/install-validate.d.ts.map +1 -1
  31. package/dist/core/plugin/install-validate.js +25 -17
  32. package/dist/core/plugin/install-validate.js.map +1 -1
  33. package/dist/core/plugin/installer.d.ts +3 -3
  34. package/dist/core/plugin/installer.d.ts.map +1 -1
  35. package/dist/core/plugin/installer.js +143 -140
  36. package/dist/core/plugin/installer.js.map +1 -1
  37. package/dist/core/plugin/loader.d.ts +1 -0
  38. package/dist/core/plugin/loader.d.ts.map +1 -1
  39. package/dist/core/plugin/loader.js +24 -2
  40. package/dist/core/plugin/loader.js.map +1 -1
  41. package/dist/core/plugin/types.d.ts +11 -3
  42. package/dist/core/plugin/types.d.ts.map +1 -1
  43. package/dist/core/plugin/types.js.map +1 -1
  44. package/dist/plugins/admin/index.d.ts.map +1 -1
  45. package/dist/plugins/admin/index.js +119 -86
  46. package/dist/plugins/admin/index.js.map +1 -1
  47. package/dist/plugins/core/index.d.ts.map +1 -1
  48. package/dist/plugins/core/index.js +4 -1
  49. package/dist/plugins/core/index.js.map +1 -1
  50. package/docs/architecture.md +22 -24
  51. package/docs/configuration.md +19 -23
  52. package/docs/feature-parity.md +4 -4
  53. package/docs/migration.md +13 -13
  54. package/docs/plugin-api.md +156 -26
  55. package/docs/plugin-development.md +390 -0
  56. package/docs/release.md +2 -2
  57. package/examples/plugins/hello/README.md +10 -5
  58. package/examples/plugins/hello/index.js +3 -6
  59. package/package.json +2 -1
  60. package/src/app.ts +16 -55
  61. package/src/core/config/schema.ts +0 -1
  62. package/src/core/dispatch/command.ts +474 -70
  63. package/src/core/dispatch/router.ts +77 -3
  64. package/src/core/index.ts +2 -0
  65. package/src/core/plugin/command-params.ts +595 -0
  66. package/src/core/plugin/context.ts +47 -2
  67. package/src/core/plugin/install-dir.ts +117 -111
  68. package/src/core/plugin/install-validate.ts +36 -34
  69. package/src/core/plugin/installer.ts +228 -184
  70. package/src/core/plugin/loader.ts +36 -2
  71. package/src/core/plugin/types.ts +23 -3
  72. package/src/plugins/admin/index.ts +302 -233
  73. package/src/plugins/core/index.ts +5 -1
  74. package/dist/core/plugin/installed.d.ts +0 -9
  75. package/dist/core/plugin/installed.d.ts.map +0 -1
  76. package/dist/core/plugin/installed.js +0 -113
  77. package/dist/core/plugin/installed.js.map +0 -1
  78. package/src/core/plugin/installed.ts +0 -165
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod'
2
2
  import { definePlugin } from '../../core/index.js'
3
3
 
4
+ const INSTALLER_DISABLED = '插件安装功能未启用(installer.enabled=false)。'
5
+
4
6
  const PLUGIN_USAGE = `用法:
5
7
  /plugin install <npm包[@版本|tag]>
6
8
  /plugin list
@@ -26,292 +28,359 @@ export const adminPlugin = definePlugin({
26
28
  },
27
29
 
28
30
  setup(ctx) {
29
- ctx.commands.register(
31
+ ctx.commands.group(
30
32
  {
31
33
  name: 'utter',
32
34
  permission: 'Admin',
33
35
  description: '向当前会话发送文本',
34
- },
35
- async (_event, context, match) => {
36
- if (!match.rest) {
37
- await context.send('用法:/utter <文本>')
38
- return
39
- }
40
-
41
- await context.send(match.rest, {
42
- quote: false,
43
- autoEscape: true,
44
- })
45
- }
46
- )
36
+ handler: async (_event, context, match) => {
37
+ if (!match.rest) {
38
+ await context.send('用法:/utter <文本>')
39
+ return
40
+ }
47
41
 
48
- ctx.commands.register(
49
- {
50
- name: 'utter.raw',
51
- permission: 'SuperAdmin',
52
- description: '发送原始 CQ 消息',
42
+ await context.send(match.rest, {
43
+ quote: false,
44
+ autoEscape: true,
45
+ })
46
+ },
53
47
  },
54
- async (_event, context, match) => {
55
- if (!match.rest) {
56
- await context.send('用法:/utter.raw <CQ 消息>')
57
- return
58
- }
59
-
60
- await context.send(match.rest, {
61
- quote: false,
62
- autoEscape: false,
63
- })
48
+ (utter) => {
49
+ utter.register(
50
+ {
51
+ name: 'raw',
52
+ permission: 'SuperAdmin',
53
+ description: '发送原始 CQ 消息',
54
+ },
55
+ async (_event, context, match) => {
56
+ if (!match.rest) {
57
+ await context.send('用法:/utter raw <CQ 消息>')
58
+ return
59
+ }
60
+
61
+ await context.send(match.rest, {
62
+ quote: false,
63
+ autoEscape: false,
64
+ })
65
+ }
66
+ )
64
67
  }
65
68
  )
66
69
 
67
- ctx.commands.register(
70
+ ctx.commands.group(
68
71
  {
69
72
  name: 'plugin',
70
73
  permission: 'SuperAdmin',
71
74
  description: '管理已安装的插件包',
72
- },
73
- async (_event, context, match) => {
74
- const sub = (match.args[0] ?? '').toLowerCase()
75
- const argument = match.rest
76
- .slice(match.args[0]?.length ?? 0)
77
- .trim()
78
- const installer = ctx.core.installer
79
- const sessionId = context.sessionId
80
-
81
- if (sub.length === 0 || sub === 'help') {
75
+ handler: async (_event, context) => {
82
76
  await context.send(PLUGIN_USAGE, { quote: false })
83
- return
84
- }
85
-
86
- if (sub === 'list') {
87
- try {
88
- const packages = await installer.list()
89
-
90
- if (packages.length === 0) {
91
- await context.send('没有已安装的插件包。', {
92
- quote: false,
93
- })
94
- return
95
- }
96
-
97
- const lines = packages.map((item) => {
98
- const status = item.loaded ? '已加载' : '重启后生效'
99
- return `- ${item.packageName}@${item.version} → ${formatIds(item.pluginIds)}(${status})`
100
- })
101
-
102
- await context.send(
103
- [
104
- `已安装 ${packages.length} 个插件包:`,
105
- ...lines,
106
- ].join('\n'),
107
- { quote: false }
108
- )
109
- } catch (error) {
110
- await context.send(
111
- `❌ 读取插件包失败:${errorMessage(error)}`,
112
- { quote: false }
113
- )
114
- }
115
- return
116
- }
117
-
118
- if (sub !== 'install' && sub !== 'remove' && sub !== 'update') {
119
- await context.send(PLUGIN_USAGE, { quote: false })
120
- return
121
- }
122
-
123
- if (!installer.enabled) {
124
- await context.send(
125
- '插件安装功能未启用(installer.enabled=false)。',
126
- { quote: false }
127
- )
128
- return
129
- }
130
-
131
- if (sub === 'install') {
132
- if (argument.length === 0) {
133
- await context.send(
134
- '用法:/plugin install <npm包[@版本|tag]>',
135
- { quote: false }
136
- )
137
- return
138
- }
139
-
140
- await context.send(`正在安装 ${argument} ...`, {
141
- quote: false,
142
- })
77
+ },
78
+ },
79
+ (plugin) => {
80
+ plugin.register(
81
+ {
82
+ name: 'list',
83
+ description: '列出已安装的插件包',
84
+ },
85
+ async (_event, context) => {
86
+ const installer = ctx.core.installer
143
87
 
144
- ctx.tasks.enqueue('plugin-installer', async (signal) => {
145
88
  try {
146
- const result = await installer.install(
147
- argument,
148
- signal
149
- )
150
- const lines = [
151
- `✅ 已安装 ${result.packageName}@${result.version}(插件 id: ${formatIds(result.pluginIds)})。`,
152
- ]
89
+ const packages = await installer.list()
153
90
 
154
- for (const warning of result.warnings) {
155
- lines.push(`⚠️ ${warning}`)
91
+ if (packages.length === 0) {
92
+ await context.send('没有已安装的插件包。', {
93
+ quote: false,
94
+ })
95
+ return
156
96
  }
157
97
 
158
- lines.push(
159
- result.warnings.length === 0
160
- ? '正在重启。'
161
- : '请处理以上问题后手动重启。'
162
- )
163
-
164
- await ctx.api.send(sessionId, lines.join('\n'), {
165
- quote: false,
98
+ const lines = packages.map((item) => {
99
+ const status = item.loaded
100
+ ? '已加载'
101
+ : '重启后生效'
102
+ return `- ${item.packageName}@${item.version} → ${formatIds(item.pluginIds)}(${status})`
166
103
  })
167
104
 
168
- if (result.warnings.length === 0) {
169
- ctx.core.restart()
170
- }
105
+ await context.send(
106
+ [
107
+ `已安装 ${packages.length} 个插件包:`,
108
+ ...lines,
109
+ ].join('\n'),
110
+ { quote: false }
111
+ )
171
112
  } catch (error) {
172
- await ctx.api.send(
173
- sessionId,
174
- `❌ 安装失败:${errorMessage(error)}`,
113
+ await context.send(
114
+ `❌ 读取插件包失败:${errorMessage(error)}`,
175
115
  { quote: false }
176
116
  )
177
117
  }
178
- })
179
- return
180
- }
181
-
182
- if (sub === 'remove') {
183
- if (argument.length === 0) {
184
- await context.send(
185
- '用法:/plugin remove <包名|插件id> [--force]',
186
- { quote: false }
187
- )
188
- return
189
118
  }
119
+ )
190
120
 
191
- const force = /(?:^|\s)--force(?:\s|$)/.test(argument)
192
- const target = argument
193
- .replace(/(?:^|\s)--force(?:\s|$)/, ' ')
194
- .trim()
195
-
196
- if (target.length === 0) {
197
- await context.send(
198
- '用法:/plugin remove <包名|插件id> [--force]',
199
- { quote: false }
200
- )
201
- return
202
- }
121
+ plugin.register(
122
+ {
123
+ name: 'install',
124
+ description: '安装 npm 插件包',
125
+ },
126
+ async (_event, context, match) => {
127
+ const installer = ctx.core.installer
128
+ const sessionId = context.sessionId
129
+
130
+ if (!installer.enabled) {
131
+ await context.send(INSTALLER_DISABLED, {
132
+ quote: false,
133
+ })
134
+ return
135
+ }
203
136
 
204
- await context.send(`正在卸载 ${target} ...`, {
205
- quote: false,
206
- })
137
+ const argument = match.rest.trim()
207
138
 
208
- ctx.tasks.enqueue('plugin-installer', async (signal) => {
209
- try {
210
- const result = await installer.remove(
211
- target,
212
- signal,
213
- { force }
139
+ if (argument.length === 0) {
140
+ await context.send(
141
+ '用法:/plugin install <npm包[@版本|tag]>',
142
+ { quote: false }
214
143
  )
215
- const lines = [`✅ 已卸载 ${result.packageName}。`]
216
-
217
- if (result.pluginIds.length > 0) {
218
- lines.push(
219
- `插件 id: ${formatIds(result.pluginIds)}`
220
- )
221
- }
144
+ return
145
+ }
222
146
 
223
- for (const warning of result.warnings) {
224
- lines.push(`⚠️ ${warning}`)
225
- }
147
+ await context.send(`正在安装 ${argument} ...`, {
148
+ quote: false,
149
+ })
226
150
 
227
- if (result.warnings.length === 0) {
228
- lines.push('正在重启。')
229
- } else {
230
- lines.push('请处理以上问题后手动重启。')
151
+ ctx.tasks.enqueue(
152
+ 'plugin-installer',
153
+ async (signal) => {
154
+ try {
155
+ const result = await installer.install(
156
+ argument,
157
+ signal
158
+ )
159
+ const lines = [
160
+ `✅ 已安装 ${result.packageName}@${result.version}(插件 id: ${formatIds(result.pluginIds)})。`,
161
+ ]
162
+
163
+ for (const warning of result.warnings) {
164
+ lines.push(`⚠️ ${warning}`)
165
+ }
166
+
167
+ lines.push(
168
+ result.warnings.length === 0
169
+ ? '正在重启。'
170
+ : '请处理以上问题后手动重启。'
171
+ )
172
+
173
+ await ctx.api.send(
174
+ sessionId,
175
+ lines.join('\n'),
176
+ {
177
+ quote: false,
178
+ }
179
+ )
180
+
181
+ if (result.warnings.length === 0) {
182
+ ctx.core.restart()
183
+ }
184
+ } catch (error) {
185
+ await ctx.api.send(
186
+ sessionId,
187
+ `❌ 安装失败:${errorMessage(error)}`,
188
+ { quote: false }
189
+ )
190
+ }
231
191
  }
192
+ )
193
+ }
194
+ )
232
195
 
233
- await ctx.api.send(sessionId, lines.join('\n'), {
196
+ plugin.register(
197
+ {
198
+ name: 'remove',
199
+ description: '卸载插件包',
200
+ },
201
+ async (_event, context, match) => {
202
+ const installer = ctx.core.installer
203
+ const sessionId = context.sessionId
204
+
205
+ if (!installer.enabled) {
206
+ await context.send(INSTALLER_DISABLED, {
234
207
  quote: false,
235
208
  })
236
-
237
- if (result.warnings.length === 0) {
238
- ctx.core.restart()
239
- }
240
- } catch (error) {
241
- await ctx.api.send(
242
- sessionId,
243
- `❌ 卸载失败:${errorMessage(error)}`,
244
- { quote: false }
245
- )
209
+ return
246
210
  }
247
- })
248
- return
249
- }
250
-
251
- await context.send(
252
- argument.length > 0
253
- ? `正在更新 ${argument} ...`
254
- : '正在更新全部插件包 ...',
255
- { quote: false }
256
- )
257
-
258
- ctx.tasks.enqueue('plugin-installer', async (signal) => {
259
- try {
260
- const result = await installer.update(
261
- argument.length > 0 ? argument : undefined,
262
- signal
263
- )
264
- const lines: string[] = []
265
- const warnings = result.updated.flatMap(
266
- (item) => item.warnings
267
- )
268
211
 
269
- for (const item of result.updated) {
270
- lines.push(
271
- `✅ ${item.packageName}@${item.version}(插件 id: ${formatIds(item.pluginIds)})`
272
- )
273
- }
212
+ const argument = match.rest.trim()
274
213
 
275
- for (const item of result.failed) {
276
- lines.push(
277
- `❌ ${item.packageName}:${item.message}`
214
+ if (argument.length === 0) {
215
+ await context.send(
216
+ '用法:/plugin remove <包名|插件id> [--force]',
217
+ { quote: false }
278
218
  )
219
+ return
279
220
  }
280
221
 
281
- for (const warning of warnings) {
282
- lines.push(`⚠️ ${warning}`)
283
- }
284
-
285
- if (lines.length === 0) {
286
- lines.push('没有可更新的插件包。')
287
- }
222
+ const force = /(?:^|\s)--force(?:\s|$)/.test(argument)
223
+ const target = argument
224
+ .replace(/(?:^|\s)--force(?:\s|$)/, ' ')
225
+ .trim()
288
226
 
289
- if (result.updated.length > 0) {
290
- lines.push(
291
- warnings.length === 0
292
- ? '正在重启。'
293
- : '请处理以上问题后手动重启。'
227
+ if (target.length === 0) {
228
+ await context.send(
229
+ '用法:/plugin remove <包名|插件id> [--force]',
230
+ { quote: false }
294
231
  )
232
+ return
295
233
  }
296
234
 
297
- await ctx.api.send(sessionId, lines.join('\n'), {
235
+ await context.send(`正在卸载 ${target} ...`, {
298
236
  quote: false,
299
237
  })
300
238
 
301
- if (
302
- result.updated.length > 0 &&
303
- warnings.length === 0
304
- ) {
305
- ctx.core.restart()
239
+ ctx.tasks.enqueue(
240
+ 'plugin-installer',
241
+ async (signal) => {
242
+ try {
243
+ const result = await installer.remove(
244
+ target,
245
+ signal,
246
+ { force }
247
+ )
248
+ const lines = [
249
+ `✅ 已卸载 ${result.packageName}。`,
250
+ ]
251
+
252
+ if (result.pluginIds.length > 0) {
253
+ lines.push(
254
+ `插件 id: ${formatIds(result.pluginIds)}`
255
+ )
256
+ }
257
+
258
+ for (const warning of result.warnings) {
259
+ lines.push(`⚠️ ${warning}`)
260
+ }
261
+
262
+ if (result.warnings.length === 0) {
263
+ lines.push('正在重启。')
264
+ } else {
265
+ lines.push('请处理以上问题后手动重启。')
266
+ }
267
+
268
+ await ctx.api.send(
269
+ sessionId,
270
+ lines.join('\n'),
271
+ {
272
+ quote: false,
273
+ }
274
+ )
275
+
276
+ if (result.warnings.length === 0) {
277
+ ctx.core.restart()
278
+ }
279
+ } catch (error) {
280
+ await ctx.api.send(
281
+ sessionId,
282
+ `❌ 卸载失败:${errorMessage(error)}`,
283
+ { quote: false }
284
+ )
285
+ }
286
+ }
287
+ )
288
+ }
289
+ )
290
+
291
+ plugin.register(
292
+ {
293
+ name: 'update',
294
+ description: '更新插件包',
295
+ },
296
+ async (_event, context, match) => {
297
+ const installer = ctx.core.installer
298
+ const sessionId = context.sessionId
299
+
300
+ if (!installer.enabled) {
301
+ await context.send(INSTALLER_DISABLED, {
302
+ quote: false,
303
+ })
304
+ return
306
305
  }
307
- } catch (error) {
308
- await ctx.api.send(
309
- sessionId,
310
- `❌ 更新失败:${errorMessage(error)}`,
306
+
307
+ const argument = match.rest.trim()
308
+
309
+ await context.send(
310
+ argument.length > 0
311
+ ? `正在更新 ${argument} ...`
312
+ : '正在更新全部插件包 ...',
311
313
  { quote: false }
312
314
  )
315
+
316
+ ctx.tasks.enqueue(
317
+ 'plugin-installer',
318
+ async (signal) => {
319
+ try {
320
+ const result = await installer.update(
321
+ argument.length > 0
322
+ ? argument
323
+ : undefined,
324
+ signal
325
+ )
326
+ const lines: string[] = []
327
+ const warnings = result.updated.flatMap(
328
+ (item) => item.warnings
329
+ )
330
+
331
+ for (const item of result.updated) {
332
+ lines.push(
333
+ `✅ ${item.packageName}@${item.version}(插件 id: ${formatIds(item.pluginIds)})`
334
+ )
335
+ }
336
+
337
+ for (const item of result.failed) {
338
+ lines.push(
339
+ `❌ ${item.packageName}:${item.message}`
340
+ )
341
+ }
342
+
343
+ for (const warning of warnings) {
344
+ lines.push(`⚠️ ${warning}`)
345
+ }
346
+
347
+ if (lines.length === 0) {
348
+ lines.push('没有可更新的插件包。')
349
+ }
350
+
351
+ if (result.updated.length > 0) {
352
+ lines.push(
353
+ warnings.length === 0
354
+ ? '正在重启。'
355
+ : '请处理以上问题后手动重启。'
356
+ )
357
+ }
358
+
359
+ await ctx.api.send(
360
+ sessionId,
361
+ lines.join('\n'),
362
+ {
363
+ quote: false,
364
+ }
365
+ )
366
+
367
+ if (
368
+ result.updated.length > 0 &&
369
+ warnings.length === 0
370
+ ) {
371
+ ctx.core.restart()
372
+ }
373
+ } catch (error) {
374
+ await ctx.api.send(
375
+ sessionId,
376
+ `❌ 更新失败:${errorMessage(error)}`,
377
+ { quote: false }
378
+ )
379
+ }
380
+ }
381
+ )
313
382
  }
314
- })
383
+ )
315
384
  }
316
385
  )
317
386
  },
@@ -43,6 +43,10 @@ export const corePlugin = definePlugin({
43
43
  ctx.core.roles.can(role, command.permission)
44
44
  )
45
45
  .map((command) => {
46
+ const usage =
47
+ command.usage !== undefined
48
+ ? ` ${command.usage}`
49
+ : ''
46
50
  const aliases =
47
51
  command.aliases.length > 0
48
52
  ? ` (${command.aliases
@@ -53,7 +57,7 @@ export const corePlugin = definePlugin({
53
57
  ? ` - ${command.description}`
54
58
  : ''
55
59
 
56
- return `/${command.name}${aliases}${description}`
60
+ return `/${command.name}${usage}${aliases}${description}`
57
61
  })
58
62
 
59
63
  await context.send(`可用命令:\n${commands.join('\n')}`)
@@ -1,9 +0,0 @@
1
- import type { Logger } from '../logging/logger.js';
2
- import type { AnyPluginDefinition } from './types.js';
3
- export interface LoadedInstalledPlugins {
4
- plugins: AnyPluginDefinition[];
5
- byPackage: Map<string, string[]>;
6
- }
7
- export declare function packageEntry(directory: string, manifest: Record<string, unknown>): string;
8
- export declare function loadInstalledPlugins(directory: string, logger: Logger): Promise<LoadedInstalledPlugins>;
9
- //# sourceMappingURL=installed.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"installed.d.ts","sourceRoot":"","sources":["../../../src/core/plugin/installed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAUlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE,mBAAmB,EAAE,CAAA;IAC9B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CACnC;AA4CD,wBAAgB,YAAY,CACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAiCR;AAED,wBAAsB,oBAAoB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,sBAAsB,CAAC,CA8DjC"}