mioku 0.9.6 → 0.9.7

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/index.cjs CHANGED
@@ -178,6 +178,125 @@ function getOrCreate(key, factory) {
178
178
  return s[key];
179
179
  }
180
180
 
181
+ //#endregion
182
+ //#region src/core/plugin-manifest.ts
183
+ const PLUGIN_CONFIG_KEYS = new Set([
184
+ "services",
185
+ "help",
186
+ "accessHooks"
187
+ ]);
188
+ const ACCESS_HOOK_KEYS = new Set([
189
+ "id",
190
+ "match",
191
+ "event",
192
+ "description"
193
+ ]);
194
+ const HELP_COMMAND_KEYS = new Set([
195
+ "cmd",
196
+ "desc",
197
+ "usage",
198
+ "role"
199
+ ]);
200
+ function warnUnknownFields(prefix, obj, allowed) {
201
+ for (const key of Object.keys(obj)) if (!allowed.has(key)) logger.warn(`[plugin-manifest] ${prefix} 含未知字段 "${key}"(将被忽略)`);
202
+ }
203
+ function isPlainObject(value) {
204
+ return value !== null && typeof value === "object" && !Array.isArray(value);
205
+ }
206
+ function validateHelp(value, pluginName) {
207
+ if (value === void 0) return void 0;
208
+ if (!isPlainObject(value)) {
209
+ logger.warn(`[plugin-manifest] ${pluginName}.help 必须是对象,已忽略`);
210
+ return void 0;
211
+ }
212
+ warnUnknownFields(`${pluginName}.help`, value, HELP_COMMAND_KEYS);
213
+ const title = String(value.title ?? "").trim();
214
+ const description = String(value.description ?? "").trim();
215
+ const rawCommands = value.commands;
216
+ if (!title) logger.warn(`[plugin-manifest] ${pluginName}.help.title 缺失或为空`);
217
+ if (!description) logger.warn(`[plugin-manifest] ${pluginName}.help.description 缺失或为空`);
218
+ if (!Array.isArray(rawCommands)) {
219
+ logger.warn(`[plugin-manifest] ${pluginName}.help.commands 必须是数组`);
220
+ return {
221
+ title,
222
+ description,
223
+ commands: []
224
+ };
225
+ }
226
+ const commands = [];
227
+ for (const cmd of rawCommands) {
228
+ if (!isPlainObject(cmd)) {
229
+ logger.warn(`[plugin-manifest] ${pluginName}.help.commands 项不是对象`);
230
+ continue;
231
+ }
232
+ warnUnknownFields(`${pluginName}.help.commands[]`, cmd, HELP_COMMAND_KEYS);
233
+ const cmdName = String(cmd.cmd ?? "").trim();
234
+ const desc = String(cmd.desc ?? "").trim();
235
+ if (!cmdName || !desc) {
236
+ logger.warn(`[plugin-manifest] ${pluginName}.help.commands 项缺少 cmd 或 desc`);
237
+ continue;
238
+ }
239
+ commands.push({
240
+ cmd: cmdName,
241
+ desc,
242
+ usage: typeof cmd.usage === "string" ? cmd.usage : void 0,
243
+ role: typeof cmd.role === "string" ? cmd.role : void 0
244
+ });
245
+ }
246
+ return {
247
+ title,
248
+ description,
249
+ commands
250
+ };
251
+ }
252
+ function validateAccessHooks(value, pluginName) {
253
+ if (value === void 0) return void 0;
254
+ if (!Array.isArray(value)) {
255
+ logger.warn(`[plugin-manifest] ${pluginName}.accessHooks 必须是数组`);
256
+ return void 0;
257
+ }
258
+ const hooks = [];
259
+ for (const hook of value) {
260
+ if (!isPlainObject(hook)) {
261
+ logger.warn(`[plugin-manifest] ${pluginName}.accessHooks 项不是对象`);
262
+ continue;
263
+ }
264
+ warnUnknownFields(`${pluginName}.accessHooks[]`, hook, ACCESS_HOOK_KEYS);
265
+ const id = String(hook.id ?? "").trim();
266
+ if (!id) {
267
+ logger.warn(`[plugin-manifest] ${pluginName}.accessHooks 项缺少 id,已忽略`);
268
+ continue;
269
+ }
270
+ hooks.push({
271
+ id,
272
+ match: typeof hook.match === "string" ? hook.match : void 0,
273
+ event: typeof hook.event === "string" ? hook.event : void 0,
274
+ description: typeof hook.description === "string" ? hook.description : void 0
275
+ });
276
+ }
277
+ return hooks;
278
+ }
279
+ function validatePluginPackageConfig(raw, pluginName) {
280
+ const config = {};
281
+ if (raw === void 0 || raw === null) return config;
282
+ if (!isPlainObject(raw)) {
283
+ logger.warn(`[plugin-manifest] ${pluginName} 的 mioku 字段必须是对象,已使用空配置`);
284
+ return config;
285
+ }
286
+ warnUnknownFields(`${pluginName}.mioku`, raw, PLUGIN_CONFIG_KEYS);
287
+ if (raw.services !== void 0) if (Array.isArray(raw.services)) {
288
+ const services = raw.services.filter((s) => typeof s === "string" && s.length > 0);
289
+ const dropped = raw.services.length - services.length;
290
+ if (dropped > 0) logger.warn(`[plugin-manifest] ${pluginName}.services 丢弃 ${dropped} 个非字符串项`);
291
+ config.services = services;
292
+ } else logger.warn(`[plugin-manifest] ${pluginName}.services 必须是字符串数组`);
293
+ const help = validateHelp(raw.help, pluginName);
294
+ if (help) config.help = help;
295
+ const hooks = validateAccessHooks(raw.accessHooks, pluginName);
296
+ if (hooks) config.accessHooks = hooks;
297
+ return config;
298
+ }
299
+
181
300
  //#endregion
182
301
  //#region src/core/plugin-manager.ts
183
302
  const PLUGIN_PREFIX = "mioku-plugin-";
@@ -212,7 +331,7 @@ var PluginManager = class PluginManager {
212
331
  } catch (error) {
213
332
  logger.warn(`[plugin-manager] 读取 ${name} 的 package.json 失败: ${error}`);
214
333
  }
215
- const config = packageJson?.mioku ?? {};
334
+ const config = validatePluginPackageConfig(packageJson?.mioku, name);
216
335
  return {
217
336
  name,
218
337
  version: packageJson?.version ?? "0.0.0",
@@ -382,68 +501,49 @@ async function bootstrapMioku(deps) {
382
501
  }
383
502
 
384
503
  //#endregion
385
- //#region src/core/plugin-artifact-registry.ts
386
- function isAISkill(value) {
387
- return !!value && typeof value === "object" && typeof value.name === "string" && Array.isArray(value.tools);
388
- }
389
- function extractSkills(moduleExports) {
390
- const candidates = [
391
- moduleExports?.default,
392
- moduleExports?.skills,
393
- moduleExports
394
- ];
395
- for (const candidate of candidates) {
396
- if (Array.isArray(candidate)) return candidate.filter(isAISkill);
397
- if (isAISkill(candidate)) return [candidate];
398
- }
399
- return [];
400
- }
401
- async function resolveSkillsEntry(pluginPath) {
402
- const tsPath = path.join(pluginPath, "skills.ts");
403
- if (await pathExists(tsPath)) return tsPath;
404
- const jsPath = path.join(pluginPath, "skills.js");
405
- if (await pathExists(jsPath)) return jsPath;
406
- return null;
504
+ //#region src/core/service.ts
505
+ function defineService(id) {
506
+ return { id };
507
+ }
508
+ function getService(ctx, ref) {
509
+ return ctx.services[ref.id];
407
510
  }
511
+ function requireService(ctx, ref) {
512
+ const svc = ctx.services[ref.id];
513
+ if (svc === void 0) throw new Error(`[mioku] required service "${ref.id}" is not available`);
514
+ return svc;
515
+ }
516
+ function hasService(ctx, ref) {
517
+ return ctx.services[ref.id] !== void 0;
518
+ }
519
+
520
+ //#endregion
521
+ //#region src/core/services.ts
522
+ const Services = {
523
+ AI: defineService("ai"),
524
+ Config: defineService("config"),
525
+ Screenshot: defineService("screenshot"),
526
+ Help: defineService("help")
527
+ };
528
+
529
+ //#endregion
530
+ //#region src/core/plugin-artifact-registry.ts
408
531
  function hasPublicKeyword(keywords) {
409
532
  return Array.isArray(keywords) && keywords.includes("mioku");
410
533
  }
411
534
  async function registerPluginArtifacts(ctx) {
412
535
  const enabledPlugins = new Set(mioki.botConfig.plugins ?? []);
413
536
  const pluginMetadata = plugin_manager_default.getAllMetadata().filter((metadata) => enabledPlugins.size > 0 ? enabledPlugins.has(metadata.name) : true);
414
- const helpService = ctx.services.help;
415
- const aiService = ctx.services.ai;
416
- if (helpService) {
417
- let helpCount = 0;
418
- for (const metadata of pluginMetadata) {
419
- if (!metadata.config.help) continue;
420
- if (!hasPublicKeyword(metadata.packageJson?.keywords)) continue;
421
- helpService.registerHelp(metadata.name, metadata.config.help);
422
- helpCount += 1;
423
- }
424
- logger.info(`[plugin-artifacts] Registered ${helpCount} help manifest(s)`);
425
- }
426
- if (!aiService) return;
427
- let skillCount = 0;
537
+ const helpService = getService(ctx, Services.Help);
538
+ if (!helpService) return;
539
+ let helpCount = 0;
428
540
  for (const metadata of pluginMetadata) {
429
- const skillsEntry = await resolveSkillsEntry(metadata.path);
430
- if (!skillsEntry) continue;
431
- try {
432
- const moduleExports = await import(toImportPath(skillsEntry));
433
- const skills = extractSkills(moduleExports);
434
- if (skills.length === 0) {
435
- logger.warn(`[plugin-artifacts] Plugin ${metadata.name} has ${path.basename(skillsEntry)} but exported no valid skill`);
436
- continue;
437
- }
438
- for (const skill of skills) {
439
- aiService.registerSkill(skill);
440
- skillCount += 1;
441
- }
442
- } catch (error) {
443
- logger.error(`[plugin-artifacts] Failed to load skills for plugin ${metadata.name}: ${error}`);
444
- }
541
+ if (!metadata.config.help) continue;
542
+ if (!hasPublicKeyword(metadata.packageJson?.keywords)) continue;
543
+ helpService.registerHelp(metadata.name, metadata.config.help);
544
+ helpCount += 1;
445
545
  }
446
- logger.info(`[plugin-artifacts] Registered ${skillCount} skill(s)`);
546
+ logger.info(`[plugin-artifacts] Registered ${helpCount} help manifest(s)`);
447
547
  }
448
548
 
449
549
  //#endregion
@@ -499,6 +599,11 @@ async function deleteServiceConfig(serviceName, configName) {
499
599
 
500
600
  //#endregion
501
601
  //#region src/types.ts
602
+ function normalizeSkillPermissionRole(role) {
603
+ const normalized = String(role ?? "").trim().toLowerCase();
604
+ if (normalized === "master" || normalized === "owner" || normalized === "admin" || normalized === "member") return normalized;
605
+ return "member";
606
+ }
502
607
  const TOOL_RESULT_FOLLOWUP_KEY = "__miokuFollowup";
503
608
 
504
609
  //#endregion
@@ -530,17 +635,36 @@ function ensureDataDir(pluginName) {
530
635
  //#endregion
531
636
  //#region src/core/plugin-runtime-state.ts
532
637
  const store = getOrCreate("plugin-runtime-state", () => ({}));
533
- function getPluginRuntimeState(pluginName) {
534
- if (!store[pluginName]) store[pluginName] = {};
535
- return store[pluginName];
638
+ function defineState(name, initial) {
639
+ return {
640
+ name,
641
+ get() {
642
+ const current = store[name];
643
+ return current === void 0 ? initial : current;
644
+ },
645
+ set(next) {
646
+ store[name] = next;
647
+ },
648
+ reset() {
649
+ delete store[name];
650
+ }
651
+ };
652
+ }
653
+ function hasPluginState(name) {
654
+ return store[name] !== void 0;
655
+ }
656
+ function getPluginRuntimeState(name) {
657
+ if (!store[name]) store[name] = {};
658
+ return store[name];
659
+ }
660
+ function setPluginRuntimeState(name, state) {
661
+ store[name] = state;
536
662
  }
537
- function setPluginRuntimeState(pluginName, state) {
538
- if (!store[pluginName]) store[pluginName] = {};
539
- Object.assign(store[pluginName], state);
540
- return store[pluginName];
663
+ function resetPluginRuntimeState(name) {
664
+ delete store[name];
541
665
  }
542
- function resetPluginRuntimeState(pluginName) {
543
- delete store[pluginName];
666
+ function getAllPluginRuntimeStates() {
667
+ return { ...store };
544
668
  }
545
669
 
546
670
  //#endregion
@@ -553,6 +677,23 @@ async function start(options = {}) {
553
677
  if (cwd) process.chdir(cwd);
554
678
  const { start: startMioki, logger: logger$1, botConfig: botConfig$1 } = await import("mioki");
555
679
  setMiokuLogger(logger$1);
680
+ let shuttingDown = false;
681
+ const shutdown = async (signal) => {
682
+ if (shuttingDown) return;
683
+ shuttingDown = true;
684
+ logger$1.info(`收到 ${signal} 信号,正在关闭服务...`);
685
+ const timer = setTimeout(() => {
686
+ logger$1.error("服务关闭超时,强制退出");
687
+ process.exit(1);
688
+ }, 15e3);
689
+ timer.unref();
690
+ await service_manager_default.disposeAll();
691
+ clearTimeout(timer);
692
+ logger$1.info("Mioku 服务已完全关闭");
693
+ process.exit(0);
694
+ };
695
+ process.on("SIGINT", () => shutdown("SIGINT"));
696
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
556
697
  logger$1.info("こんにちは..");
557
698
  logger$1.info("---------------------------------------");
558
699
  logger$1.info("---------- Mioku 正在启动 ------------");
@@ -575,24 +716,33 @@ function readVersion() {
575
716
  const version = readVersion();
576
717
 
577
718
  //#endregion
719
+ exports.Services = Services;
578
720
  exports.TOOL_RESULT_FOLLOWUP_KEY = TOOL_RESULT_FOLLOWUP_KEY;
579
721
  exports.buildSpawnPlan = require_exec.buildSpawnPlan;
580
722
  exports.commandExists = require_exec.commandExists;
581
723
  exports.definePlugin = definePlugin;
724
+ exports.defineService = defineService;
725
+ exports.defineState = defineState;
582
726
  exports.deleteServiceConfig = deleteServiceConfig;
583
727
  exports.ensureDataDir = ensureDataDir;
728
+ exports.getAllPluginRuntimeStates = getAllPluginRuntimeStates;
584
729
  exports.getConfigDir = getConfigDir;
585
730
  exports.getDataDir = getDataDir;
586
731
  exports.getPluginConfigDir = getPluginConfigDir;
587
732
  exports.getPluginDataDir = getPluginDataDir;
588
733
  exports.getPluginRuntimeState = getPluginRuntimeState;
734
+ exports.getService = getService;
589
735
  exports.getServiceConfig = getServiceConfig;
590
736
  exports.getServiceConfigDir = getServiceConfigDir;
591
737
  exports.getServiceConfigs = getServiceConfigs;
592
738
  exports.getServiceDataDir = getServiceDataDir;
739
+ exports.hasPluginState = hasPluginState;
740
+ exports.hasService = hasService;
741
+ exports.normalizeSkillPermissionRole = normalizeSkillPermissionRole;
593
742
  exports.pluginManager = plugin_manager_default;
594
743
  exports.registerPluginArtifacts = registerPluginArtifacts;
595
744
  exports.registerServiceConfig = registerServiceConfig;
745
+ exports.requireService = requireService;
596
746
  exports.resetPluginRuntimeState = resetPluginRuntimeState;
597
747
  exports.resolveCommand = require_exec.resolveCommand;
598
748
  exports.runCommand = require_exec.runCommand;