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/cli/index.cjs +1 -1
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +213 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +44 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +220 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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/
|
|
386
|
-
function
|
|
387
|
-
return
|
|
388
|
-
}
|
|
389
|
-
function
|
|
390
|
-
|
|
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.
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
430
|
-
if (!
|
|
431
|
-
|
|
432
|
-
|
|
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 ${
|
|
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
|
|
534
|
-
|
|
535
|
-
|
|
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
|
|
538
|
-
|
|
539
|
-
Object.assign(store[pluginName], state);
|
|
540
|
-
return store[pluginName];
|
|
663
|
+
function resetPluginRuntimeState(name) {
|
|
664
|
+
delete store[name];
|
|
541
665
|
}
|
|
542
|
-
function
|
|
543
|
-
|
|
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;
|