node-plume 1.0.1

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.
@@ -0,0 +1,3087 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-T7M5RXO7.mjs";
4
+
5
+ // src/start/app.ts
6
+ import { existsSync as existsSync3 } from "fs";
7
+ import { join as join3 } from "path";
8
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "url";
9
+ import { logger as logger2 } from "@plume/logger";
10
+
11
+ // src/app.ts
12
+ import { createLogger as createLogger2 } from "@plume/logger";
13
+
14
+ // src/context.ts
15
+ var Context = class {
16
+ constructor(app, event) {
17
+ this.app = app;
18
+ this.event = event;
19
+ }
20
+ app;
21
+ event;
22
+ state = /* @__PURE__ */ new Map();
23
+ get messageId() {
24
+ return this.event.messageId;
25
+ }
26
+ get text() {
27
+ return this.event.text;
28
+ }
29
+ get content() {
30
+ return this.event.content;
31
+ }
32
+ get sender() {
33
+ return this.event.sender;
34
+ }
35
+ get userId() {
36
+ return this.event.userId;
37
+ }
38
+ get groupId() {
39
+ return this.event.groupId;
40
+ }
41
+ get isPrivate() {
42
+ return this.event.isPrivate;
43
+ }
44
+ get isGroup() {
45
+ return this.event.isGroup;
46
+ }
47
+ get target() {
48
+ return this.event.target;
49
+ }
50
+ get selfId() {
51
+ return this.event.selfId;
52
+ }
53
+ get timestamp() {
54
+ return this.event.timestamp;
55
+ }
56
+ get raw() {
57
+ return this.event.raw;
58
+ }
59
+ get services() {
60
+ return this.app.services;
61
+ }
62
+ setState(key, value) {
63
+ this.state.set(key, value);
64
+ return this;
65
+ }
66
+ getState(key) {
67
+ return this.state.get(key);
68
+ }
69
+ async reply(content, options = {}) {
70
+ return this.app.sendMessage(this.target, normalizeReplyInput(content), {
71
+ ...options,
72
+ replyTo: options.reply === false ? void 0 : this.messageId
73
+ });
74
+ }
75
+ };
76
+ function normalizeReplyInput(content) {
77
+ if (typeof content === "string") {
78
+ return content;
79
+ }
80
+ if (Array.isArray(content)) {
81
+ return content.map(
82
+ (item) => typeof item === "string" ? { type: "text", data: { text: item } } : item
83
+ );
84
+ }
85
+ return [content];
86
+ }
87
+
88
+ // src/service/container.ts
89
+ var ServiceContainer = class {
90
+ services = /* @__PURE__ */ new Map();
91
+ factories = /* @__PURE__ */ new Map();
92
+ register(name, service) {
93
+ this.services.set(name, service);
94
+ return this;
95
+ }
96
+ registerFactory(name, factory) {
97
+ this.factories.set(name, factory);
98
+ return this;
99
+ }
100
+ resolve(name) {
101
+ if (this.services.has(name)) {
102
+ return this.services.get(name);
103
+ }
104
+ if (this.factories.has(name)) {
105
+ const service = this.factories.get(name)();
106
+ this.services.set(name, service);
107
+ return service;
108
+ }
109
+ throw new Error(`Service '${name}' not found`);
110
+ }
111
+ has(name) {
112
+ return this.services.has(name) || this.factories.has(name);
113
+ }
114
+ delete(name) {
115
+ return this.services.delete(name) || this.factories.delete(name);
116
+ }
117
+ clear() {
118
+ this.services.clear();
119
+ this.factories.clear();
120
+ }
121
+ get(name) {
122
+ if (this.services.has(name)) {
123
+ return this.services.get(name);
124
+ }
125
+ return void 0;
126
+ }
127
+ set(name, service) {
128
+ return this.register(name, service);
129
+ }
130
+ keys() {
131
+ return [.../* @__PURE__ */ new Set([...this.services.keys(), ...this.factories.keys()])];
132
+ }
133
+ entries() {
134
+ const resolvedEntries = [...this.services.entries()];
135
+ for (const [name] of this.factories.entries()) {
136
+ if (!this.services.has(name)) {
137
+ resolvedEntries.push([name, this.resolve(name)]);
138
+ }
139
+ }
140
+ return resolvedEntries;
141
+ }
142
+ size() {
143
+ return this.keys().length;
144
+ }
145
+ };
146
+
147
+ // src/service/message.ts
148
+ var MessageService = class {
149
+ sender = null;
150
+ registerSender(sender) {
151
+ this.sender = sender;
152
+ return this;
153
+ }
154
+ clearSender() {
155
+ this.sender = null;
156
+ return this;
157
+ }
158
+ hasSender() {
159
+ return this.sender !== null;
160
+ }
161
+ async sendMessage(params) {
162
+ if (!this.sender) {
163
+ throw new Error("\u6D88\u606F\u53D1\u9001\u5668\u672A\u6CE8\u518C");
164
+ }
165
+ return this.sender.sendMessage(params);
166
+ }
167
+ };
168
+
169
+ // src/service/config/index.ts
170
+ import { extname as extname3, resolve as resolve3 } from "path";
171
+
172
+ // src/service/config/default.ts
173
+ var plumeDefaultConfig = Object.freeze({
174
+ app: {
175
+ name: "Plume Bot",
176
+ version: "1.0.0"
177
+ },
178
+ log: {
179
+ level: "info",
180
+ timestamp: true
181
+ },
182
+ transport: {
183
+ host: "127.0.0.1",
184
+ port: 23333,
185
+ path: "/api",
186
+ wsPath: "/ws",
187
+ cors: true,
188
+ enableHttp: true,
189
+ enableWebSocket: true,
190
+ maxBodySize: 1024 * 1024
191
+ }
192
+ });
193
+ var plumeConfigSections = Object.freeze(
194
+ Object.keys(plumeDefaultConfig)
195
+ );
196
+
197
+ // src/service/config/init.ts
198
+ import { dirname, extname as extname2, resolve as resolve2 } from "path";
199
+
200
+ // src/service/config/file/config.ts
201
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
202
+ import { basename, extname, join, resolve } from "path";
203
+
204
+ // src/service/config/tools.ts
205
+ function parsePrimitiveValue(value) {
206
+ if (value === "true") return true;
207
+ if (value === "false") return false;
208
+ if (value === "null" || value === "~") return null;
209
+ if (/^-?\d+$/.test(value)) return parseInt(value, 10);
210
+ if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
211
+ if (value.startsWith('"') && value.endsWith('"')) return value.slice(1, -1);
212
+ if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
213
+ return value;
214
+ }
215
+ function flattenObject(obj, prefix = "", entries = []) {
216
+ for (const [key, value] of Object.entries(obj)) {
217
+ const fullKey = prefix ? `${prefix}.${key}` : key;
218
+ if (value && typeof value === "object" && !Array.isArray(value)) {
219
+ flattenObject(value, fullKey, entries);
220
+ } else {
221
+ entries.push([fullKey, value]);
222
+ }
223
+ }
224
+ return entries;
225
+ }
226
+ function parseYAML(content) {
227
+ const result = {};
228
+ const lines = content.split(/\r?\n/);
229
+ const stack = [
230
+ { obj: result, indent: -1 }
231
+ ];
232
+ for (const rawLine of lines) {
233
+ const line = rawLine.replace(/\t/g, " ");
234
+ if (!line.trim() || line.trim().startsWith("#")) {
235
+ continue;
236
+ }
237
+ const indent = line.search(/\S/);
238
+ const [key, ...valueParts] = line.trim().split(":");
239
+ const value = valueParts.join(":").trim();
240
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
241
+ stack.pop();
242
+ }
243
+ const current = stack[stack.length - 1].obj;
244
+ if (value) {
245
+ current[key.trim()] = parsePrimitiveValue(value);
246
+ } else {
247
+ const child = {};
248
+ current[key.trim()] = child;
249
+ stack.push({ obj: child, indent });
250
+ }
251
+ }
252
+ return result;
253
+ }
254
+ function stringifyJSON(data) {
255
+ return `${JSON.stringify(data, null, 2)}
256
+ `;
257
+ }
258
+
259
+ // src/service/config/file/config.ts
260
+ var CONFIG_EXTENSIONS = [".json", ".yaml", ".yml"];
261
+ function resolveConfigDirectory(path = "config") {
262
+ return resolve(path);
263
+ }
264
+ function ensureConfigDirectory(path = "config") {
265
+ const directory = resolveConfigDirectory(path);
266
+ mkdirSync(directory, { recursive: true });
267
+ return directory;
268
+ }
269
+ function readConfigDirectory(path = "config") {
270
+ const directory = resolveConfigDirectory(path);
271
+ const result = {};
272
+ for (const section of plumeConfigSections) {
273
+ const filePath = findSectionConfigFile(directory, section);
274
+ if (!filePath) {
275
+ continue;
276
+ }
277
+ result[section] = readConfigObject(filePath);
278
+ }
279
+ return result;
280
+ }
281
+ function ensureDefaultConfigFiles(path = "config") {
282
+ const directory = ensureConfigDirectory(path);
283
+ const createdFiles = [];
284
+ for (const section of plumeConfigSections) {
285
+ const filePath = join(directory, `${section}.json`);
286
+ if (existsSync(filePath)) {
287
+ continue;
288
+ }
289
+ writeConfigObject(filePath, plumeDefaultConfig[section]);
290
+ createdFiles.push(filePath);
291
+ }
292
+ return createdFiles;
293
+ }
294
+ function readConfigObject(path) {
295
+ const content = readFileSync(path, "utf-8");
296
+ const extension = extname(path).toLowerCase();
297
+ if (extension === ".json") {
298
+ return JSON.parse(content);
299
+ }
300
+ if (extension === ".yaml" || extension === ".yml") {
301
+ return parseYAML(content);
302
+ }
303
+ throw new Error(`Unsupported config file format: ${basename(path)}`);
304
+ }
305
+ function writeConfigObject(path, data) {
306
+ writeFileSync(path, stringifyJSON(data), "utf-8");
307
+ }
308
+ function findSectionConfigFile(directory, section) {
309
+ for (const extension of CONFIG_EXTENSIONS) {
310
+ const filePath = join(directory, `${section}${extension}`);
311
+ if (existsSync(filePath)) {
312
+ return filePath;
313
+ }
314
+ }
315
+ return null;
316
+ }
317
+
318
+ // src/service/config/init.ts
319
+ function initConfig(options = {}) {
320
+ const basePath = options.path ?? "config";
321
+ const useConfigDirectory = extname2(basePath) === "";
322
+ const configDirectory = useConfigDirectory ? ensureConfigDirectory(basePath) : ensureConfigDirectory(dirname(resolve2(basePath)));
323
+ const createdConfigFiles = useConfigDirectory ? ensureDefaultConfigFiles(basePath) : [];
324
+ return {
325
+ configDirectory,
326
+ createdConfigFiles
327
+ };
328
+ }
329
+
330
+ // src/service/config/index.ts
331
+ var ConfigService = class {
332
+ config = /* @__PURE__ */ new Map();
333
+ path;
334
+ constructor(options = {}) {
335
+ this.path = options.path ?? "config";
336
+ this.loadDefaults();
337
+ }
338
+ initialize(options = {}) {
339
+ return initConfig({
340
+ path: options.path ?? this.path
341
+ });
342
+ }
343
+ loadDefaults() {
344
+ const entries = flattenObject(plumeDefaultConfig);
345
+ for (const [key, value] of entries) {
346
+ this.set(key, value, "default");
347
+ }
348
+ return this;
349
+ }
350
+ loadFile(path) {
351
+ const targetPath = path ?? this.path;
352
+ const absolutePath = resolve3(targetPath);
353
+ const extension = extname3(absolutePath).toLowerCase();
354
+ if (!extension) {
355
+ return this.loadDirectory(targetPath);
356
+ }
357
+ return this.loadObject(readConfigObject(absolutePath), "file");
358
+ }
359
+ loadDirectory(path = this.path) {
360
+ return this.loadObject(readConfigDirectory(path), "file");
361
+ }
362
+ loadObject(data, source = "file") {
363
+ for (const [key, value] of flattenObject(data)) {
364
+ this.set(key, value, source);
365
+ }
366
+ return this;
367
+ }
368
+ reload() {
369
+ this.clear();
370
+ this.loadFile();
371
+ return this;
372
+ }
373
+ get(key, defaultValue) {
374
+ const entry = this.config.get(key);
375
+ if (entry) {
376
+ return entry.value;
377
+ }
378
+ return defaultValue;
379
+ }
380
+ set(key, value, source = "default") {
381
+ this.config.set(key, { value, source });
382
+ return this;
383
+ }
384
+ has(key) {
385
+ return this.config.has(key);
386
+ }
387
+ delete(key) {
388
+ return this.config.delete(key);
389
+ }
390
+ getSource(key) {
391
+ return this.config.get(key)?.source;
392
+ }
393
+ getAll() {
394
+ const result = {};
395
+ for (const [key, entry] of this.config) {
396
+ result[key] = entry.value;
397
+ }
398
+ return result;
399
+ }
400
+ entries() {
401
+ return [...this.config.entries()];
402
+ }
403
+ clear() {
404
+ this.config.clear();
405
+ this.loadDefaults();
406
+ }
407
+ };
408
+
409
+ // src/service/log.ts
410
+ import { createLogger } from "@plume/logger";
411
+ var LogService = class _LogService {
412
+ logger;
413
+ options;
414
+ outputs = [];
415
+ buffer = [];
416
+ maxBufferSize = 1e3;
417
+ suspended = false;
418
+ pending = [];
419
+ constructor(options = {}) {
420
+ this.options = {
421
+ level: "info",
422
+ timestamp: true,
423
+ ...options
424
+ };
425
+ this.logger = createLogger({
426
+ level: this.options.level,
427
+ prefix: this.options.prefix,
428
+ timestamp: this.options.timestamp
429
+ });
430
+ if (this.options.outputs) {
431
+ this.outputs = this.options.outputs;
432
+ }
433
+ }
434
+ get level() {
435
+ return this.options.level ?? "info";
436
+ }
437
+ setLevel(level) {
438
+ this.options.level = level;
439
+ this.logger.setLevel(level);
440
+ }
441
+ suspend() {
442
+ this.suspended = true;
443
+ return this;
444
+ }
445
+ resume() {
446
+ if (!this.suspended) {
447
+ return this;
448
+ }
449
+ this.suspended = false;
450
+ for (const entry of this.pending) {
451
+ this.writeEntry(entry);
452
+ }
453
+ this.pending = [];
454
+ return this;
455
+ }
456
+ debug(message, ...args) {
457
+ this.log("debug", message, ...args);
458
+ }
459
+ info(message, ...args) {
460
+ this.log("info", message, ...args);
461
+ }
462
+ warn(message, ...args) {
463
+ this.log("warn", message, ...args);
464
+ }
465
+ error(message, ...args) {
466
+ this.log("error", message, ...args);
467
+ }
468
+ log(level, message, ...args) {
469
+ if (level === "silent") return;
470
+ const entry = {
471
+ level,
472
+ message,
473
+ timestamp: /* @__PURE__ */ new Date(),
474
+ args,
475
+ prefix: this.options.prefix
476
+ };
477
+ this.buffer.push(entry);
478
+ if (this.buffer.length > this.maxBufferSize) {
479
+ this.buffer.shift();
480
+ }
481
+ if (this.suspended) {
482
+ this.pending.push(entry);
483
+ return;
484
+ }
485
+ this.writeEntry(entry);
486
+ }
487
+ writeEntry(entry) {
488
+ switch (entry.level) {
489
+ case "debug":
490
+ this.logger.debug(entry.message, ...entry.args);
491
+ break;
492
+ case "info":
493
+ this.logger.info(entry.message, ...entry.args);
494
+ break;
495
+ case "warn":
496
+ this.logger.warn(entry.message, ...entry.args);
497
+ break;
498
+ case "error":
499
+ this.logger.error(entry.message, ...entry.args);
500
+ break;
501
+ case "silent":
502
+ return;
503
+ }
504
+ for (const output2 of this.outputs) {
505
+ this.writeToOutput(output2, entry);
506
+ }
507
+ }
508
+ writeToOutput(output2, entry) {
509
+ switch (output2.type) {
510
+ case "console":
511
+ break;
512
+ case "file":
513
+ break;
514
+ case "custom":
515
+ if (output2.handler) {
516
+ output2.handler(entry.level, entry.message, ...entry.args);
517
+ }
518
+ break;
519
+ }
520
+ }
521
+ addOutput(output2) {
522
+ this.outputs.push(output2);
523
+ return this;
524
+ }
525
+ removeOutput(output2) {
526
+ const index = this.outputs.indexOf(output2);
527
+ if (index > -1) {
528
+ this.outputs.splice(index, 1);
529
+ return true;
530
+ }
531
+ return false;
532
+ }
533
+ child(prefix) {
534
+ return new _LogService({
535
+ ...this.options,
536
+ prefix: this.options.prefix ? `${this.options.prefix}:${prefix}` : prefix
537
+ });
538
+ }
539
+ getBuffer() {
540
+ return [...this.buffer];
541
+ }
542
+ clearBuffer() {
543
+ this.buffer = [];
544
+ }
545
+ flush() {
546
+ for (const output2 of this.outputs) {
547
+ if (output2.type === "file" && output2.path) {
548
+ const fs = __require("fs");
549
+ const content = this.buffer.map((e) => `[${e.timestamp.toISOString()}] [${e.level.toUpperCase()}] ${e.prefix ? `[${e.prefix}] ` : ""}${e.message}`).join("\n");
550
+ fs.appendFileSync(output2.path, content + "\n");
551
+ }
552
+ }
553
+ }
554
+ };
555
+ var logService = new LogService();
556
+
557
+ // src/event-emitter.ts
558
+ var EventEmitter = class {
559
+ handlers = /* @__PURE__ */ new Map();
560
+ on(event, handler) {
561
+ if (!this.handlers.has(event)) {
562
+ this.handlers.set(event, /* @__PURE__ */ new Set());
563
+ }
564
+ this.handlers.get(event).add(handler);
565
+ return this;
566
+ }
567
+ once(event, handler) {
568
+ const wrappedHandler = (data) => {
569
+ this.off(event, wrappedHandler);
570
+ return handler(data);
571
+ };
572
+ return this.on(event, wrappedHandler);
573
+ }
574
+ off(event, handler) {
575
+ const handlers = this.handlers.get(event);
576
+ if (handlers) {
577
+ handlers.delete(handler);
578
+ }
579
+ return this;
580
+ }
581
+ async emit(event, data) {
582
+ const handlers = this.handlers.get(event);
583
+ if (!handlers) return;
584
+ const promises = [];
585
+ for (const handler of handlers) {
586
+ try {
587
+ const result = handler(data);
588
+ if (result instanceof Promise) {
589
+ promises.push(result);
590
+ }
591
+ } catch (error) {
592
+ console.error(`Error in event handler for "${String(event)}":`, error);
593
+ }
594
+ }
595
+ await Promise.all(promises);
596
+ }
597
+ listenerCount(event) {
598
+ return this.handlers.get(event)?.size ?? 0;
599
+ }
600
+ removeAllListeners(event) {
601
+ if (event) {
602
+ this.handlers.delete(event);
603
+ } else {
604
+ this.handlers.clear();
605
+ }
606
+ return this;
607
+ }
608
+ };
609
+
610
+ // src/event/base.ts
611
+ var BaseEvent = class {
612
+ constructor(payload) {
613
+ this.payload = payload;
614
+ this.timestamp = payload.timestamp;
615
+ this.raw = payload.raw;
616
+ }
617
+ payload;
618
+ timestamp;
619
+ raw;
620
+ get selfId() {
621
+ return this.payload.selfId;
622
+ }
623
+ };
624
+
625
+ // src/event/message.ts
626
+ var MessageEvent = class extends BaseEvent {
627
+ type = "message";
628
+ get messageId() {
629
+ return this.payload.messageId;
630
+ }
631
+ get content() {
632
+ return this.payload.content;
633
+ }
634
+ get text() {
635
+ return this.payload.text;
636
+ }
637
+ get sender() {
638
+ return this.payload.sender;
639
+ }
640
+ get target() {
641
+ return this.payload.target;
642
+ }
643
+ get isPrivate() {
644
+ return this.payload.target.type === "private";
645
+ }
646
+ get isGroup() {
647
+ return this.payload.target.type === "group";
648
+ }
649
+ get userId() {
650
+ return this.payload.sender.id;
651
+ }
652
+ get groupId() {
653
+ return this.payload.target.type === "group" ? this.payload.target.id : void 0;
654
+ }
655
+ };
656
+
657
+ // src/event/notice.ts
658
+ var NoticeEvent = class extends BaseEvent {
659
+ type = "notice";
660
+ get subtype() {
661
+ return this.payload.subtype;
662
+ }
663
+ get operator() {
664
+ return this.payload.operator;
665
+ }
666
+ get targetUser() {
667
+ return this.payload.targetUser;
668
+ }
669
+ get group() {
670
+ return this.payload.group;
671
+ }
672
+ get data() {
673
+ return this.payload.data;
674
+ }
675
+ isGroupIncrease() {
676
+ return this.subtype === "group_increase";
677
+ }
678
+ isGroupDecrease() {
679
+ return this.subtype === "group_decrease";
680
+ }
681
+ isGroupAdmin() {
682
+ return this.subtype === "group_admin";
683
+ }
684
+ isGroupUpload() {
685
+ return this.subtype === "group_upload";
686
+ }
687
+ isFriendAdd() {
688
+ return this.subtype === "friend_add";
689
+ }
690
+ isFriendRecall() {
691
+ return this.subtype === "friend_recall";
692
+ }
693
+ isGroupRecall() {
694
+ return this.subtype === "group_recall";
695
+ }
696
+ };
697
+
698
+ // src/event/request.ts
699
+ var RequestEvent = class extends BaseEvent {
700
+ type = "request";
701
+ get subtype() {
702
+ return this.payload.subtype;
703
+ }
704
+ get requestId() {
705
+ return this.payload.requestId;
706
+ }
707
+ get sender() {
708
+ return this.payload.sender;
709
+ }
710
+ get group() {
711
+ return this.payload.group;
712
+ }
713
+ get comment() {
714
+ return this.payload.comment;
715
+ }
716
+ isFriendRequest() {
717
+ return this.subtype === "friend";
718
+ }
719
+ isGroupInvite() {
720
+ return this.subtype === "group_invite";
721
+ }
722
+ };
723
+
724
+ // src/segment.ts
725
+ function text(value) {
726
+ return {
727
+ type: "text",
728
+ data: { text: value }
729
+ };
730
+ }
731
+ function image(source) {
732
+ return {
733
+ type: "image",
734
+ data: normalizeBinaryData(source)
735
+ };
736
+ }
737
+ function audio(source) {
738
+ return {
739
+ type: "audio",
740
+ data: normalizeBinaryData(source)
741
+ };
742
+ }
743
+ function video(source) {
744
+ return {
745
+ type: "video",
746
+ data: normalizeBinaryData(source)
747
+ };
748
+ }
749
+ function file(source, name) {
750
+ if (typeof source === "string") {
751
+ return {
752
+ type: "file",
753
+ data: {
754
+ file: source.trim(),
755
+ name: trimOptionalString(name)
756
+ }
757
+ };
758
+ }
759
+ return {
760
+ type: "file",
761
+ data: {
762
+ name: trimOptionalString(source.name),
763
+ ...normalizeBinaryData(source)
764
+ }
765
+ };
766
+ }
767
+ function at(userId, name) {
768
+ return {
769
+ type: "at",
770
+ data: {
771
+ userId,
772
+ name: trimOptionalString(name)
773
+ }
774
+ };
775
+ }
776
+ function face(id, name) {
777
+ return {
778
+ type: "face",
779
+ data: {
780
+ id,
781
+ name: trimOptionalString(name)
782
+ }
783
+ };
784
+ }
785
+ function reply(messageId) {
786
+ return {
787
+ type: "reply",
788
+ data: { messageId }
789
+ };
790
+ }
791
+ function forward(input2) {
792
+ if (typeof input2 === "string") {
793
+ return {
794
+ type: "forward",
795
+ data: { id: input2.trim() }
796
+ };
797
+ }
798
+ if (Array.isArray(input2)) {
799
+ return {
800
+ type: "forward",
801
+ data: { messages: input2.map(normalizeForwardNode) }
802
+ };
803
+ }
804
+ return {
805
+ type: "forward",
806
+ data: {
807
+ id: trimOptionalString(input2.id),
808
+ messages: input2.messages?.map(normalizeForwardNode)
809
+ }
810
+ };
811
+ }
812
+ function location(latitudeOrData, longitude, title, content) {
813
+ if (typeof latitudeOrData === "number") {
814
+ return {
815
+ type: "location",
816
+ data: {
817
+ latitude: latitudeOrData,
818
+ longitude: longitude ?? 0,
819
+ title: trimOptionalString(title),
820
+ content: trimOptionalString(content)
821
+ }
822
+ };
823
+ }
824
+ return {
825
+ type: "location",
826
+ data: {
827
+ latitude: latitudeOrData.latitude,
828
+ longitude: latitudeOrData.longitude,
829
+ title: trimOptionalString(latitudeOrData.title),
830
+ content: trimOptionalString(latitudeOrData.content)
831
+ }
832
+ };
833
+ }
834
+ function json(data) {
835
+ return {
836
+ type: "json",
837
+ data: {
838
+ data: typeof data === "string" ? data.trim() : { ...data }
839
+ }
840
+ };
841
+ }
842
+ function xml(data) {
843
+ return {
844
+ type: "xml",
845
+ data: {
846
+ data: data.trim()
847
+ }
848
+ };
849
+ }
850
+ function custom(typeOrData, data = {}) {
851
+ if (typeof typeOrData === "string") {
852
+ return {
853
+ type: trimOptionalString(typeOrData) ?? "custom",
854
+ data: { ...data }
855
+ };
856
+ }
857
+ return {
858
+ type: "custom",
859
+ data: { ...typeOrData }
860
+ };
861
+ }
862
+ function normalizeMessageContent(content) {
863
+ if (typeof content === "string") {
864
+ return content;
865
+ }
866
+ return content.map((item) => normalizeMessageSegment(item));
867
+ }
868
+ function normalizeMessageSegment(segment2) {
869
+ switch (segment2.type) {
870
+ case "forward":
871
+ return forward(segment2.data);
872
+ case "location":
873
+ return location(segment2.data);
874
+ case "json":
875
+ return json(segment2.data.data);
876
+ case "xml":
877
+ return xml(typeof segment2.data.data === "string" ? segment2.data.data : "");
878
+ case "image":
879
+ return image(segment2.data);
880
+ case "audio":
881
+ return audio(segment2.data);
882
+ case "video":
883
+ return video(segment2.data);
884
+ case "file":
885
+ return file(segment2.data);
886
+ case "at":
887
+ return at(
888
+ typeof segment2.data.userId === "string" ? segment2.data.userId : "",
889
+ trimOptionalString(segment2.data.name)
890
+ );
891
+ case "face":
892
+ return face(
893
+ typeof segment2.data.id === "number" ? segment2.data.id : 0,
894
+ trimOptionalString(segment2.data.name)
895
+ );
896
+ case "reply":
897
+ return reply(typeof segment2.data.messageId === "string" ? segment2.data.messageId : "");
898
+ case "text":
899
+ return text(typeof segment2.data.text === "string" ? segment2.data.text : "");
900
+ case "custom":
901
+ return custom(segment2.data);
902
+ default:
903
+ return {
904
+ type: segment2.type,
905
+ data: { ...segment2.data }
906
+ };
907
+ }
908
+ }
909
+ function extractMessageText(content) {
910
+ if (typeof content === "string") {
911
+ return content;
912
+ }
913
+ return content.map((item) => renderMessageSegment(item)).filter(Boolean).join("");
914
+ }
915
+ function formatMessageContent(content) {
916
+ if (typeof content === "string") {
917
+ return content;
918
+ }
919
+ return content.map((item) => formatMessageSegment(item)).filter(Boolean).join(" ");
920
+ }
921
+ function renderMessageSegment(segment2) {
922
+ switch (segment2.type) {
923
+ case "text":
924
+ return typeof segment2.data.text === "string" ? segment2.data.text : "";
925
+ case "image":
926
+ return "[image]";
927
+ case "audio":
928
+ return "[audio]";
929
+ case "video":
930
+ return "[video]";
931
+ case "file":
932
+ return typeof segment2.data.name === "string" && segment2.data.name.trim() ? `[file:${segment2.data.name.trim()}]` : "[file]";
933
+ case "at":
934
+ return typeof segment2.data.userId === "string" ? `@${segment2.data.userId}` : "@unknown";
935
+ case "face":
936
+ return typeof segment2.data.name === "string" && segment2.data.name.trim() ? `[face:${segment2.data.name.trim()}]` : typeof segment2.data.id === "number" ? `[face:${segment2.data.id}]` : "[face]";
937
+ case "reply":
938
+ return typeof segment2.data.messageId === "string" ? `[reply:${segment2.data.messageId}]` : "[reply]";
939
+ case "forward":
940
+ return "[forward]";
941
+ case "location":
942
+ return typeof segment2.data.title === "string" && segment2.data.title.trim() ? `[location:${segment2.data.title.trim()}]` : "[location]";
943
+ case "json":
944
+ return "[json]";
945
+ case "xml":
946
+ return "[xml]";
947
+ case "custom":
948
+ return "[custom]";
949
+ default:
950
+ return `[${segment2.type}]`;
951
+ }
952
+ }
953
+ function formatMessageSegment(segment2) {
954
+ switch (segment2.type) {
955
+ case "text":
956
+ return typeof segment2.data.text === "string" ? segment2.data.text : "[text]";
957
+ case "image":
958
+ return formatCompactTypedSegment("image", segment2.data, ["url", "file", "path", "base64"]);
959
+ case "audio":
960
+ return formatCompactTypedSegment("audio", segment2.data, ["url", "file", "path", "base64"]);
961
+ case "video":
962
+ return formatCompactTypedSegment("video", segment2.data, ["url", "file", "path", "base64"]);
963
+ case "file":
964
+ return formatCompactTypedSegment("file", segment2.data, ["name", "url", "file", "path", "base64"]);
965
+ case "at":
966
+ return typeof segment2.data.userId === "string" ? `@${segment2.data.userId}` : "[at]";
967
+ case "face":
968
+ return formatCompactTypedSegment("face", segment2.data, ["name", "id"]);
969
+ case "reply":
970
+ return typeof segment2.data.messageId === "string" ? `[reply:${segment2.data.messageId}]` : "[reply]";
971
+ case "forward":
972
+ return formatCompactTypedSegment("forward", segment2.data, ["id"], formatForwardCount(segment2.data));
973
+ case "location":
974
+ return formatCompactTypedSegment("location", segment2.data, ["title", "latitude", "longitude", "content"]);
975
+ case "json":
976
+ return formatCompactTypedValue("json", segment2.data.data);
977
+ case "xml":
978
+ return formatCompactTypedValue("xml", segment2.data.data);
979
+ case "custom":
980
+ return formatCompactTypedValue("custom", segment2.data);
981
+ default:
982
+ return formatCompactTypedValue(segment2.type, segment2.data);
983
+ }
984
+ }
985
+ function normalizeForwardNode(node) {
986
+ return {
987
+ userId: trimOptionalString(node.userId),
988
+ nickname: trimOptionalString(node.nickname),
989
+ content: normalizeMessageContent(node.content)
990
+ };
991
+ }
992
+ function normalizeBinaryData(source) {
993
+ if (typeof source === "string") {
994
+ return { url: source.trim() };
995
+ }
996
+ return {
997
+ url: trimOptionalString(source.url),
998
+ file: trimOptionalString(source.file),
999
+ path: trimOptionalString(source.path),
1000
+ base64: trimOptionalString(source.base64)
1001
+ };
1002
+ }
1003
+ function formatCompactTypedSegment(type, data, fields, suffix = "") {
1004
+ const primaryValue = pickFirstDefinedValue(data, fields);
1005
+ if (!primaryValue) {
1006
+ return suffix ? `[${type}${suffix}]` : `[${type}]`;
1007
+ }
1008
+ return `[${type} ${primaryValue}${suffix}]`;
1009
+ }
1010
+ function formatForwardCount(data) {
1011
+ const messages = Array.isArray(data.messages) ? data.messages.length : 0;
1012
+ return messages > 0 ? ` messages=${messages}` : "";
1013
+ }
1014
+ function formatCompactTypedValue(type, value) {
1015
+ const formatted = formatDataValue(value);
1016
+ return formatted === "..." ? `[${type}]` : `[${type} ${formatted}]`;
1017
+ }
1018
+ function pickFirstDefinedValue(data, fields) {
1019
+ for (const field of fields) {
1020
+ const value = data[field];
1021
+ if (value === void 0 || value === null || value === "") {
1022
+ continue;
1023
+ }
1024
+ return formatDataValue(value);
1025
+ }
1026
+ return void 0;
1027
+ }
1028
+ function formatDataValue(value) {
1029
+ if (typeof value === "string") {
1030
+ return truncateValue(value);
1031
+ }
1032
+ if (typeof value === "number" || typeof value === "boolean") {
1033
+ return String(value);
1034
+ }
1035
+ try {
1036
+ return truncateValue(JSON.stringify(value));
1037
+ } catch {
1038
+ return String(value);
1039
+ }
1040
+ }
1041
+ function truncateValue(value, maxLength = 48) {
1042
+ const normalized = value.replace(/\s+/g, " ").trim();
1043
+ if (!normalized) {
1044
+ return "...";
1045
+ }
1046
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 3)}...` : normalized;
1047
+ }
1048
+ function trimOptionalString(value) {
1049
+ if (typeof value !== "string") {
1050
+ return void 0;
1051
+ }
1052
+ const trimmed = value.trim();
1053
+ return trimmed ? trimmed : void 0;
1054
+ }
1055
+
1056
+ // src/app.ts
1057
+ var App = class {
1058
+ services = new ServiceContainer();
1059
+ events = new EventEmitter();
1060
+ logger;
1061
+ name;
1062
+ running = false;
1063
+ plugins = /* @__PURE__ */ new Map();
1064
+ constructor(config = {}) {
1065
+ this.name = config.name ?? "app";
1066
+ this.logger = createLogger2({
1067
+ ...config.logger,
1068
+ prefix: config.logger?.prefix ?? this.name
1069
+ });
1070
+ }
1071
+ use(plugin) {
1072
+ if (this.plugins.has(plugin.name)) {
1073
+ throw new Error(`Plugin ${plugin.name} is already registered`);
1074
+ }
1075
+ this.plugins.set(plugin.name, plugin);
1076
+ this.logger.debug(
1077
+ `Registered plugin | name: ${plugin.name} | version: ${plugin.version ?? "unknown"} | priority: ${plugin.priority ?? 0}`
1078
+ );
1079
+ return this;
1080
+ }
1081
+ plugin(plugin) {
1082
+ return this.use(plugin);
1083
+ }
1084
+ registerService(name, service) {
1085
+ this.services.register(name, service);
1086
+ return this;
1087
+ }
1088
+ on(event, handler) {
1089
+ this.events.on(event, handler);
1090
+ return this;
1091
+ }
1092
+ ready(handler) {
1093
+ return this.on("ready", handler);
1094
+ }
1095
+ message(handler) {
1096
+ return this.on("message", handler);
1097
+ }
1098
+ command(name, handler, options = {}) {
1099
+ this.message(({ ctx, event }) => {
1100
+ const matched = matchCommand(event.text, name, options);
1101
+ if (!matched) {
1102
+ return;
1103
+ }
1104
+ return Promise.resolve(handler({
1105
+ app: this,
1106
+ command: name,
1107
+ args: matched.args,
1108
+ rawArgs: matched.rawArgs,
1109
+ event,
1110
+ text: event.text,
1111
+ sender: event.sender,
1112
+ userId: event.userId,
1113
+ groupId: event.groupId,
1114
+ isPrivate: event.isPrivate,
1115
+ isGroup: event.isGroup,
1116
+ reply: (content) => ctx.reply(content)
1117
+ })).then(() => void 0);
1118
+ });
1119
+ return this;
1120
+ }
1121
+ commands(commandMap) {
1122
+ for (const [name, definition] of Object.entries(commandMap)) {
1123
+ if (typeof definition === "function") {
1124
+ this.command(name, definition);
1125
+ continue;
1126
+ }
1127
+ this.command(name, definition.handler, definition);
1128
+ }
1129
+ return this;
1130
+ }
1131
+ notice(handler) {
1132
+ return this.on("notice", handler);
1133
+ }
1134
+ request(handler) {
1135
+ return this.on("request", handler);
1136
+ }
1137
+ error(handler) {
1138
+ return this.on("error", handler);
1139
+ }
1140
+ stopped(handler) {
1141
+ return this.on("stop", handler);
1142
+ }
1143
+ once(event, handler) {
1144
+ this.events.once(event, handler);
1145
+ return this;
1146
+ }
1147
+ off(event, handler) {
1148
+ this.events.off(event, handler);
1149
+ return this;
1150
+ }
1151
+ async start() {
1152
+ if (this.running) {
1153
+ return;
1154
+ }
1155
+ const plugins = this.getPluginsInOrder();
1156
+ for (const plugin of plugins) {
1157
+ const pluginStartTime = Date.now();
1158
+ try {
1159
+ this.logger.debug(`\u52A0\u8F7D\u63D2\u4EF6 ${plugin.name} v${plugin.version ?? "unknown"} priority=${plugin.priority ?? 0}`);
1160
+ await plugin.setup(this);
1161
+ this.logger.debug(`\u63D2\u4EF6\u5C31\u7EEA ${plugin.name} ${Date.now() - pluginStartTime}ms`);
1162
+ } catch (error) {
1163
+ const message = error instanceof Error ? error.message : String(error);
1164
+ this.logger.error(`\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25 ${plugin.name}: ${message}`);
1165
+ if (error instanceof Error && error.stack) {
1166
+ this.logger.debug(error.stack);
1167
+ }
1168
+ throw error;
1169
+ }
1170
+ }
1171
+ this.running = true;
1172
+ await this.events.emit("ready", void 0);
1173
+ }
1174
+ async stop() {
1175
+ if (!this.running) {
1176
+ return;
1177
+ }
1178
+ const plugins = this.getPluginsInOrder().reverse();
1179
+ for (const plugin of plugins) {
1180
+ if (!plugin.dispose) continue;
1181
+ const pluginStartTime = Date.now();
1182
+ try {
1183
+ this.logger.debug(`\u5378\u8F7D\u63D2\u4EF6 ${plugin.name}`);
1184
+ await plugin.dispose(this);
1185
+ this.logger.debug(`\u63D2\u4EF6\u5DF2\u5378\u8F7D ${plugin.name} ${Date.now() - pluginStartTime}ms`);
1186
+ } catch (error) {
1187
+ await this.handleError(error);
1188
+ }
1189
+ }
1190
+ this.running = false;
1191
+ await this.events.emit("stop", void 0);
1192
+ }
1193
+ isRunning() {
1194
+ return this.running;
1195
+ }
1196
+ get api() {
1197
+ return this.services.resolve("apiClient");
1198
+ }
1199
+ get transport() {
1200
+ return this.services.get("apiTransport");
1201
+ }
1202
+ setMessageSender(sender) {
1203
+ const service = this.getService("message");
1204
+ if (service) {
1205
+ service.registerSender(sender);
1206
+ return this;
1207
+ }
1208
+ this.registerService("message", new MessageService().registerSender(sender));
1209
+ return this;
1210
+ }
1211
+ getMessageService() {
1212
+ return this.getService("message");
1213
+ }
1214
+ action(name, handler) {
1215
+ this.api.on(name, handler);
1216
+ return this;
1217
+ }
1218
+ actions(handlers) {
1219
+ this.api.register(handlers);
1220
+ return this;
1221
+ }
1222
+ service(name) {
1223
+ return this.services.resolve(name);
1224
+ }
1225
+ getService(name) {
1226
+ return this.services.get(name);
1227
+ }
1228
+ listPlugins() {
1229
+ return this.getPluginsInOrder();
1230
+ }
1231
+ pluginCount() {
1232
+ return this.plugins.size;
1233
+ }
1234
+ async call(request) {
1235
+ return this.api.call(request);
1236
+ }
1237
+ async sendMessage(target, content, options = {}) {
1238
+ return this.call({
1239
+ id: `send:${Date.now()}`,
1240
+ version: "1.0.0",
1241
+ action: "send_message",
1242
+ params: {
1243
+ target,
1244
+ content,
1245
+ replyTo: options.replyTo
1246
+ },
1247
+ timestamp: Date.now()
1248
+ });
1249
+ }
1250
+ async dispatch(payload) {
1251
+ switch (payload.type) {
1252
+ case "message": {
1253
+ const event = new MessageEvent(payload);
1254
+ const ctx = new Context(this, event);
1255
+ await this.events.emit("message", { ctx, event });
1256
+ return;
1257
+ }
1258
+ case "notice": {
1259
+ const event = new NoticeEvent(payload);
1260
+ await this.events.emit("notice", { event });
1261
+ return;
1262
+ }
1263
+ case "request": {
1264
+ const event = new RequestEvent(payload);
1265
+ await this.events.emit("request", { event });
1266
+ return;
1267
+ }
1268
+ default:
1269
+ this.logger.warn(`Unknown event type: ${payload.type}`);
1270
+ }
1271
+ }
1272
+ async handleError(error) {
1273
+ if (this.events.listenerCount("error") === 0) {
1274
+ const message = error instanceof Error ? error.message : String(error);
1275
+ this.logger.error(`\u6846\u67B6\u672A\u5904\u7406\u9519\u8BEF: ${message}`);
1276
+ if (error instanceof Error && error.stack) {
1277
+ this.logger.debug(error.stack);
1278
+ }
1279
+ return;
1280
+ }
1281
+ await this.events.emit("error", { error });
1282
+ }
1283
+ getPluginsInOrder() {
1284
+ return [...this.plugins.values()].sort((a, b) => {
1285
+ const aPriority = a.priority ?? 0;
1286
+ const bPriority = b.priority ?? 0;
1287
+ return bPriority - aPriority;
1288
+ });
1289
+ }
1290
+ };
1291
+ function matchCommand(input2, name, options) {
1292
+ const text2 = input2.trim();
1293
+ if (!text2) {
1294
+ return null;
1295
+ }
1296
+ const segments = text2.split(/\s+/);
1297
+ const head = segments[0];
1298
+ const candidates = [name, ...options.aliases ?? []];
1299
+ const prefixes = options.prefix === void 0 ? [""] : Array.isArray(options.prefix) ? options.prefix : [options.prefix];
1300
+ const caseSensitive = options.caseSensitive ?? false;
1301
+ for (const candidate of candidates) {
1302
+ for (const prefix of prefixes) {
1303
+ const expected = `${prefix}${candidate}`;
1304
+ const matched = caseSensitive ? head === expected : head.toLowerCase() === expected.toLowerCase();
1305
+ if (matched) {
1306
+ return {
1307
+ args: segments.slice(1),
1308
+ rawArgs: segments.slice(1).join(" ")
1309
+ };
1310
+ }
1311
+ }
1312
+ }
1313
+ return null;
1314
+ }
1315
+
1316
+ // src/index.ts
1317
+ import { existsSync as existsSync2, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
1318
+ import { join as join2, resolve as resolve4 } from "path";
1319
+ import { fileURLToPath, pathToFileURL } from "url";
1320
+
1321
+ // src/api/router.ts
1322
+ var APIRouter = class {
1323
+ routes = /* @__PURE__ */ new Map();
1324
+ route(action, handler) {
1325
+ this.routes.set(action, handler);
1326
+ }
1327
+ match(action) {
1328
+ return this.routes.get(action);
1329
+ }
1330
+ has(action) {
1331
+ return this.routes.has(action);
1332
+ }
1333
+ remove(action) {
1334
+ return this.routes.delete(action);
1335
+ }
1336
+ clear() {
1337
+ this.routes.clear();
1338
+ }
1339
+ getActions() {
1340
+ return Array.from(this.routes.keys());
1341
+ }
1342
+ };
1343
+
1344
+ // src/api/client.ts
1345
+ var APIClient = class {
1346
+ router;
1347
+ options;
1348
+ constructor(options = {}) {
1349
+ this.router = new APIRouter();
1350
+ this.options = {
1351
+ timeout: 3e4,
1352
+ ...options
1353
+ };
1354
+ }
1355
+ async call(request) {
1356
+ const startTime = Date.now();
1357
+ try {
1358
+ const handler = this.router.match(request.action);
1359
+ if (!handler) {
1360
+ return this.createErrorResponse(
1361
+ 4e3,
1362
+ `Unknown action: ${request.action}`,
1363
+ startTime
1364
+ );
1365
+ }
1366
+ const result = await Promise.race([
1367
+ handler(request),
1368
+ this.createTimeoutPromise(request.timeout ?? this.options.timeout)
1369
+ ]);
1370
+ return result;
1371
+ } catch (error) {
1372
+ return this.createErrorResponse(
1373
+ 5e3,
1374
+ error instanceof Error ? error.message : "Internal error",
1375
+ startTime,
1376
+ error
1377
+ );
1378
+ }
1379
+ }
1380
+ on(action, handler) {
1381
+ this.router.route(action, handler);
1382
+ return this;
1383
+ }
1384
+ register(handlers) {
1385
+ for (const [action, handler] of Object.entries(handlers)) {
1386
+ this.router.route(action, handler);
1387
+ }
1388
+ return this;
1389
+ }
1390
+ getRouter() {
1391
+ return this.router;
1392
+ }
1393
+ getActions() {
1394
+ return this.router.getActions();
1395
+ }
1396
+ getActionCount() {
1397
+ return this.router.getActions().length;
1398
+ }
1399
+ createErrorResponse(code, message, startTime, error) {
1400
+ const apiError = {
1401
+ code,
1402
+ message,
1403
+ details: error instanceof Error ? { stack: error.stack } : void 0
1404
+ };
1405
+ return {
1406
+ status: "error",
1407
+ error: apiError,
1408
+ timestamp: Date.now(),
1409
+ duration: Date.now() - startTime
1410
+ };
1411
+ }
1412
+ createTimeoutPromise(timeout) {
1413
+ return new Promise((_, reject) => {
1414
+ setTimeout(() => {
1415
+ reject(new Error(`Request timeout after ${timeout}ms`));
1416
+ }, timeout);
1417
+ });
1418
+ }
1419
+ };
1420
+
1421
+ // src/api/error.ts
1422
+ import { ErrorCode } from "@plume/types";
1423
+ function createSuccessResponse(data, startTime) {
1424
+ return {
1425
+ status: "success",
1426
+ data,
1427
+ timestamp: Date.now(),
1428
+ duration: Date.now() - startTime
1429
+ };
1430
+ }
1431
+ function createErrorResponse(code, message, startTime, details) {
1432
+ return {
1433
+ status: "error",
1434
+ error: {
1435
+ code,
1436
+ message,
1437
+ details
1438
+ },
1439
+ timestamp: Date.now(),
1440
+ duration: Date.now() - startTime
1441
+ };
1442
+ }
1443
+
1444
+ // src/api/middleware.ts
1445
+ var MiddlewareChain = class {
1446
+ middlewares = [];
1447
+ use(middleware) {
1448
+ this.middlewares.push(middleware);
1449
+ return this;
1450
+ }
1451
+ async execute(request, handler) {
1452
+ let index = 0;
1453
+ const next = async () => {
1454
+ if (index < this.middlewares.length) {
1455
+ const middleware = this.middlewares[index++];
1456
+ const ctx = {
1457
+ request,
1458
+ next
1459
+ };
1460
+ const result = await middleware(ctx);
1461
+ if (result) {
1462
+ ctx.response = result;
1463
+ return result;
1464
+ }
1465
+ return ctx.response ?? next();
1466
+ }
1467
+ return handler();
1468
+ };
1469
+ return next();
1470
+ }
1471
+ clear() {
1472
+ this.middlewares = [];
1473
+ }
1474
+ };
1475
+
1476
+ // src/api/auth.ts
1477
+ function extractToken(request) {
1478
+ return request.token ?? null;
1479
+ }
1480
+ function createAuthMiddleware(authProvider) {
1481
+ return async (ctx) => {
1482
+ const token = extractToken(ctx.request);
1483
+ if (!token) {
1484
+ return {
1485
+ status: "error",
1486
+ error: { code: 2e3, message: "Unauthorized: No token provided" },
1487
+ timestamp: Date.now(),
1488
+ duration: 0
1489
+ };
1490
+ }
1491
+ const payload = await authProvider.verify(token);
1492
+ if (!payload) {
1493
+ return {
1494
+ status: "error",
1495
+ error: { code: 2002, message: "Invalid token" },
1496
+ timestamp: Date.now(),
1497
+ duration: 0
1498
+ };
1499
+ }
1500
+ ctx.request.auth = payload;
1501
+ return ctx.next();
1502
+ };
1503
+ }
1504
+
1505
+ // src/api/transport.ts
1506
+ import { createHash, randomUUID } from "crypto";
1507
+ import { createServer } from "http";
1508
+ var DEFAULT_TRANSPORT_OPTIONS = {
1509
+ host: "127.0.0.1",
1510
+ port: 23333,
1511
+ path: "/api",
1512
+ wsPath: "/ws",
1513
+ cors: true,
1514
+ enableHttp: true,
1515
+ enableWebSocket: true,
1516
+ maxBodySize: 1024 * 1024
1517
+ };
1518
+ var APITransportServer = class {
1519
+ apiClient;
1520
+ logger;
1521
+ middlewareChain = new MiddlewareChain();
1522
+ clients = /* @__PURE__ */ new Map();
1523
+ connections = /* @__PURE__ */ new Set();
1524
+ options;
1525
+ server = null;
1526
+ address = null;
1527
+ constructor(apiClient, options = {}, logger3) {
1528
+ this.apiClient = apiClient;
1529
+ this.logger = logger3;
1530
+ this.options = {
1531
+ ...DEFAULT_TRANSPORT_OPTIONS,
1532
+ ...options
1533
+ };
1534
+ if (this.options.authProvider) {
1535
+ this.middlewareChain.use(createAuthMiddleware(this.options.authProvider));
1536
+ }
1537
+ for (const middleware of this.options.middlewares ?? []) {
1538
+ this.middlewareChain.use(middleware);
1539
+ }
1540
+ }
1541
+ async start() {
1542
+ if (this.server && this.address) {
1543
+ return this.address;
1544
+ }
1545
+ this.server = createServer((request, response) => {
1546
+ void this.handleHTTPRequest(request, response);
1547
+ });
1548
+ this.server.on("connection", (socket) => {
1549
+ this.connections.add(socket);
1550
+ socket.on("close", () => {
1551
+ this.connections.delete(socket);
1552
+ });
1553
+ socket.on("error", () => {
1554
+ this.connections.delete(socket);
1555
+ });
1556
+ });
1557
+ if (this.options.enableWebSocket) {
1558
+ this.server.on("upgrade", (request, socket, head) => {
1559
+ this.handleUpgrade(request, socket, head);
1560
+ });
1561
+ }
1562
+ await new Promise((resolve5, reject) => {
1563
+ this.server.once("error", reject);
1564
+ this.server.listen(this.options.port, this.options.host, () => {
1565
+ this.server.off("error", reject);
1566
+ resolve5();
1567
+ });
1568
+ });
1569
+ this.address = {
1570
+ host: this.options.host,
1571
+ port: this.options.port,
1572
+ httpUrl: this.options.enableHttp ? `http://${this.options.host}:${this.options.port}${this.options.path}` : void 0,
1573
+ wsUrl: this.options.enableWebSocket ? `ws://${this.options.host}:${this.options.port}${this.options.wsPath}` : void 0
1574
+ };
1575
+ return this.address;
1576
+ }
1577
+ async stop() {
1578
+ for (const client of this.clients.values()) {
1579
+ client.socket.end();
1580
+ client.socket.destroy();
1581
+ }
1582
+ this.clients.clear();
1583
+ if (!this.server) {
1584
+ this.connections.clear();
1585
+ this.address = null;
1586
+ return;
1587
+ }
1588
+ const server = this.server;
1589
+ const connections = [...this.connections];
1590
+ this.server = null;
1591
+ this.address = null;
1592
+ server.closeAllConnections?.();
1593
+ for (const connection of connections) {
1594
+ connection.destroy();
1595
+ }
1596
+ this.connections.clear();
1597
+ await new Promise((resolve5, reject) => {
1598
+ server.close((error) => {
1599
+ if (error) {
1600
+ reject(error);
1601
+ return;
1602
+ }
1603
+ resolve5();
1604
+ });
1605
+ });
1606
+ }
1607
+ isRunning() {
1608
+ return this.server !== null && this.address !== null;
1609
+ }
1610
+ isEnabled() {
1611
+ return this.options.enableHttp || this.options.enableWebSocket;
1612
+ }
1613
+ getAddress() {
1614
+ return this.address;
1615
+ }
1616
+ getConnectedClientCount() {
1617
+ return this.clients.size;
1618
+ }
1619
+ async handleHTTPRequest(request, response) {
1620
+ this.applyCORSHeaders(response);
1621
+ const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "127.0.0.1"}`);
1622
+ const pathname = url.pathname;
1623
+ const method = request.method ?? "GET";
1624
+ if (method === "OPTIONS") {
1625
+ response.writeHead(204);
1626
+ response.end();
1627
+ return;
1628
+ }
1629
+ if (pathname === "/health") {
1630
+ this.writeJSON(response, 200, {
1631
+ status: "ok",
1632
+ timestamp: Date.now(),
1633
+ transport: this.address
1634
+ });
1635
+ return;
1636
+ }
1637
+ if (pathname === "/actions") {
1638
+ this.writeJSON(response, 200, {
1639
+ status: "success",
1640
+ data: this.apiClient.getActions(),
1641
+ timestamp: Date.now()
1642
+ });
1643
+ return;
1644
+ }
1645
+ if (!this.options.enableHttp || pathname !== this.options.path) {
1646
+ this.writeJSON(response, 404, {
1647
+ status: "error",
1648
+ error: { code: 4e3, message: `Not found: ${pathname}` },
1649
+ timestamp: Date.now()
1650
+ });
1651
+ return;
1652
+ }
1653
+ if (method !== "POST") {
1654
+ this.writeJSON(response, 405, {
1655
+ status: "error",
1656
+ error: { code: 1e3, message: `Method not allowed: ${method}` },
1657
+ timestamp: Date.now()
1658
+ });
1659
+ return;
1660
+ }
1661
+ try {
1662
+ const rawBody = await this.readRequestBody(request);
1663
+ const payload = rawBody.length > 0 ? JSON.parse(rawBody) : {};
1664
+ const apiRequest = this.normalizeRequest(payload, this.extractBearerToken(request.headers.authorization));
1665
+ const apiResponse = await this.executeRequest(apiRequest);
1666
+ this.writeJSON(response, 200, apiResponse);
1667
+ this.logger?.info(`HTTP API \u8BF7\u6C42\u5B8C\u6210 | action: ${apiRequest.action} | status: ${apiResponse.status}`);
1668
+ } catch (error) {
1669
+ const apiResponse = createErrorResponse(
1670
+ 5e3,
1671
+ error instanceof Error ? error.message : "Invalid request",
1672
+ Date.now()
1673
+ );
1674
+ this.writeJSON(response, 400, apiResponse);
1675
+ this.logger?.error("HTTP API \u8BF7\u6C42\u5931\u8D25", error);
1676
+ }
1677
+ }
1678
+ handleUpgrade(request, socket, _head) {
1679
+ const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "127.0.0.1"}`);
1680
+ if (!this.options.enableWebSocket || url.pathname !== this.options.wsPath) {
1681
+ socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
1682
+ socket.destroy();
1683
+ return;
1684
+ }
1685
+ const key = request.headers["sec-websocket-key"];
1686
+ if (typeof key !== "string") {
1687
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
1688
+ socket.destroy();
1689
+ return;
1690
+ }
1691
+ const accept = this.createWebSocketAccept(key);
1692
+ socket.write(
1693
+ [
1694
+ "HTTP/1.1 101 Switching Protocols",
1695
+ "Upgrade: websocket",
1696
+ "Connection: Upgrade",
1697
+ `Sec-WebSocket-Accept: ${accept}`,
1698
+ "\r\n"
1699
+ ].join("\r\n")
1700
+ );
1701
+ const client = {
1702
+ id: randomUUID(),
1703
+ socket,
1704
+ buffer: Buffer.alloc(0)
1705
+ };
1706
+ this.clients.set(client.id, client);
1707
+ this.sendWebSocketJSON(client, {
1708
+ type: "hello",
1709
+ data: {
1710
+ clientId: client.id,
1711
+ actions: this.apiClient.getActions(),
1712
+ timestamp: Date.now()
1713
+ }
1714
+ });
1715
+ socket.on("data", (chunk) => {
1716
+ this.handleWebSocketData(client, chunk);
1717
+ });
1718
+ socket.on("close", () => {
1719
+ this.clients.delete(client.id);
1720
+ });
1721
+ socket.on("error", () => {
1722
+ this.clients.delete(client.id);
1723
+ });
1724
+ this.logger?.info(`WebSocket \u5BA2\u6237\u7AEF\u5DF2\u8FDE\u63A5 | id: ${client.id} | total: ${this.clients.size}`);
1725
+ }
1726
+ handleWebSocketData(client, chunk) {
1727
+ client.buffer = Buffer.concat([client.buffer, chunk]);
1728
+ while (true) {
1729
+ const parsed = this.tryParseFrame(client.buffer);
1730
+ if (!parsed) {
1731
+ return;
1732
+ }
1733
+ client.buffer = parsed.remaining;
1734
+ const frame = parsed.frame;
1735
+ if (frame.opcode === 8) {
1736
+ client.socket.end();
1737
+ this.clients.delete(client.id);
1738
+ return;
1739
+ }
1740
+ if (frame.opcode === 9) {
1741
+ this.sendWebSocketFrame(client.socket, 10, frame.payload);
1742
+ continue;
1743
+ }
1744
+ if (frame.opcode !== 1) {
1745
+ continue;
1746
+ }
1747
+ void this.handleWebSocketMessage(client, frame.payload.toString("utf-8"));
1748
+ }
1749
+ }
1750
+ async handleWebSocketMessage(client, text2) {
1751
+ try {
1752
+ const payload = JSON.parse(text2);
1753
+ const apiRequest = this.normalizeRequest(payload, typeof payload?.token === "string" ? payload.token : null);
1754
+ const apiResponse = await this.executeRequest(apiRequest);
1755
+ this.sendWebSocketJSON(client, {
1756
+ type: "response",
1757
+ id: apiRequest.id,
1758
+ action: apiRequest.action,
1759
+ response: apiResponse
1760
+ });
1761
+ this.logger?.info(`WebSocket API \u8BF7\u6C42\u5B8C\u6210 | client: ${client.id} | action: ${apiRequest.action} | status: ${apiResponse.status}`);
1762
+ } catch (error) {
1763
+ this.sendWebSocketJSON(client, {
1764
+ type: "error",
1765
+ data: {
1766
+ message: error instanceof Error ? error.message : "Invalid websocket payload"
1767
+ }
1768
+ });
1769
+ this.logger?.error(`WebSocket API \u8BF7\u6C42\u5931\u8D25 | client: ${client.id}`, error);
1770
+ }
1771
+ }
1772
+ async executeRequest(request) {
1773
+ return this.middlewareChain.execute(request, () => this.apiClient.call(request));
1774
+ }
1775
+ normalizeRequest(payload, token) {
1776
+ const request = typeof payload === "object" && payload !== null ? payload : {};
1777
+ if (typeof request.action !== "string" || request.action.trim() === "") {
1778
+ throw new Error("Missing action in API request");
1779
+ }
1780
+ return {
1781
+ id: typeof request.id === "string" && request.id.length > 0 ? request.id : randomUUID(),
1782
+ version: typeof request.version === "string" && request.version.length > 0 ? request.version : "1.0.0",
1783
+ action: request.action,
1784
+ params: request.params ?? {},
1785
+ timestamp: typeof request.timestamp === "number" ? request.timestamp : Date.now(),
1786
+ timeout: typeof request.timeout === "number" ? request.timeout : void 0,
1787
+ token: request.token ?? token ?? void 0
1788
+ };
1789
+ }
1790
+ async readRequestBody(request) {
1791
+ return new Promise((resolve5, reject) => {
1792
+ let size = 0;
1793
+ const chunks = [];
1794
+ request.on("data", (chunk) => {
1795
+ size += chunk.length;
1796
+ if (size > this.options.maxBodySize) {
1797
+ reject(new Error(`Request body exceeds limit: ${this.options.maxBodySize}`));
1798
+ request.destroy();
1799
+ return;
1800
+ }
1801
+ chunks.push(chunk);
1802
+ });
1803
+ request.on("end", () => {
1804
+ resolve5(Buffer.concat(chunks).toString("utf-8"));
1805
+ });
1806
+ request.on("error", reject);
1807
+ });
1808
+ }
1809
+ applyCORSHeaders(response) {
1810
+ if (!this.options.cors) {
1811
+ return;
1812
+ }
1813
+ response.setHeader("Access-Control-Allow-Origin", "*");
1814
+ response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
1815
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1816
+ }
1817
+ extractBearerToken(header) {
1818
+ if (!header) {
1819
+ return null;
1820
+ }
1821
+ const match = /^Bearer\s+(.+)$/i.exec(header);
1822
+ return match?.[1] ?? null;
1823
+ }
1824
+ writeJSON(response, statusCode, payload) {
1825
+ response.writeHead(statusCode, {
1826
+ "Content-Type": "application/json; charset=utf-8"
1827
+ });
1828
+ response.end(JSON.stringify(payload));
1829
+ }
1830
+ createWebSocketAccept(key) {
1831
+ return createHash("sha1").update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest("base64");
1832
+ }
1833
+ sendWebSocketJSON(client, payload) {
1834
+ this.sendWebSocketFrame(client.socket, 1, Buffer.from(JSON.stringify(payload), "utf-8"));
1835
+ }
1836
+ sendWebSocketFrame(socket, opcode, payload) {
1837
+ const length = payload.length;
1838
+ let header;
1839
+ if (length < 126) {
1840
+ header = Buffer.from([128 | opcode, length]);
1841
+ } else if (length <= 65535) {
1842
+ header = Buffer.alloc(4);
1843
+ header[0] = 128 | opcode;
1844
+ header[1] = 126;
1845
+ header.writeUInt16BE(length, 2);
1846
+ } else {
1847
+ header = Buffer.alloc(10);
1848
+ header[0] = 128 | opcode;
1849
+ header[1] = 127;
1850
+ header.writeBigUInt64BE(BigInt(length), 2);
1851
+ }
1852
+ socket.write(Buffer.concat([header, payload]));
1853
+ }
1854
+ tryParseFrame(buffer) {
1855
+ if (buffer.length < 2) {
1856
+ return null;
1857
+ }
1858
+ const firstByte = buffer[0];
1859
+ const secondByte = buffer[1];
1860
+ const fin = (firstByte & 128) !== 0;
1861
+ const opcode = firstByte & 15;
1862
+ const masked = (secondByte & 128) !== 0;
1863
+ let payloadLength = secondByte & 127;
1864
+ let offset = 2;
1865
+ if (payloadLength === 126) {
1866
+ if (buffer.length < offset + 2) {
1867
+ return null;
1868
+ }
1869
+ payloadLength = buffer.readUInt16BE(offset);
1870
+ offset += 2;
1871
+ } else if (payloadLength === 127) {
1872
+ if (buffer.length < offset + 8) {
1873
+ return null;
1874
+ }
1875
+ payloadLength = Number(buffer.readBigUInt64BE(offset));
1876
+ offset += 8;
1877
+ }
1878
+ const maskLength = masked ? 4 : 0;
1879
+ if (buffer.length < offset + maskLength + payloadLength) {
1880
+ return null;
1881
+ }
1882
+ const maskingKey = masked ? buffer.subarray(offset, offset + 4) : void 0;
1883
+ offset += maskLength;
1884
+ const payload = Buffer.from(buffer.subarray(offset, offset + payloadLength));
1885
+ if (masked && maskingKey) {
1886
+ for (let index = 0; index < payload.length; index += 1) {
1887
+ payload[index] ^= maskingKey[index % 4];
1888
+ }
1889
+ }
1890
+ return {
1891
+ frame: { fin, opcode, payload },
1892
+ remaining: buffer.subarray(offset + payloadLength)
1893
+ };
1894
+ }
1895
+ };
1896
+
1897
+ // src/adapter.ts
1898
+ var Bot = class {
1899
+ constructor(app, options) {
1900
+ this.app = app;
1901
+ this.id = options.id;
1902
+ this.adapter = options.adapter;
1903
+ this.logger = options.logger ?? new LogService({
1904
+ prefix: options.loggerPrefix ?? `bot:${options.adapter}:${options.id}`
1905
+ });
1906
+ }
1907
+ app;
1908
+ logger;
1909
+ id;
1910
+ adapter;
1911
+ async dispatchEvent(event) {
1912
+ return this.app.call({
1913
+ id: `dispatch:${Date.now()}`,
1914
+ version: "1.0.0",
1915
+ action: "dispatch_event",
1916
+ params: { event },
1917
+ timestamp: Date.now()
1918
+ });
1919
+ }
1920
+ async sendMessage(target, content, options = {}) {
1921
+ return this.app.sendMessage(target, content, options);
1922
+ }
1923
+ };
1924
+ function normalizeEventPayload(event) {
1925
+ switch (event.type) {
1926
+ case "message":
1927
+ return isMessagePayload(event) ? event : normalizeMessageEvent(event);
1928
+ case "notice":
1929
+ return isNoticePayload(event) ? event : normalizeNoticeEvent(event);
1930
+ case "request":
1931
+ return isRequestPayload(event) ? event : normalizeRequestEvent(event);
1932
+ }
1933
+ throw new TypeError(`Unsupported event type: ${event.type}`);
1934
+ }
1935
+ function buildEventLog(event, _options = {}) {
1936
+ const payload = normalizeEventPayload(event);
1937
+ switch (payload.type) {
1938
+ case "message":
1939
+ return buildMessageEventLog(payload);
1940
+ case "notice":
1941
+ return buildNoticeEventLog(payload);
1942
+ case "request":
1943
+ return buildRequestEventLog(payload);
1944
+ }
1945
+ }
1946
+ function buildSendMessageLog(input2, _options = {}) {
1947
+ return formatLogLine(
1948
+ describeSendMessageTitle(input2.target.type),
1949
+ formatOutboundTarget(input2.target),
1950
+ input2.replyTo ? `[reply:${input2.replyTo}]` : void 0,
1951
+ formatPreview(extractMessageText(input2.content))
1952
+ );
1953
+ }
1954
+ function normalizeMessageEvent(event) {
1955
+ const content = normalizeMessageContent(event.content);
1956
+ return {
1957
+ type: "message",
1958
+ selfId: event.selfId,
1959
+ timestamp: event.timestamp ?? Date.now(),
1960
+ raw: event.raw ?? event,
1961
+ messageId: event.messageId ?? createGeneratedId("msg", event.selfId),
1962
+ content,
1963
+ text: normalizeText(event.text, content),
1964
+ sender: event.sender,
1965
+ target: event.target
1966
+ };
1967
+ }
1968
+ function normalizeNoticeEvent(event) {
1969
+ return {
1970
+ type: "notice",
1971
+ selfId: event.selfId,
1972
+ timestamp: event.timestamp ?? Date.now(),
1973
+ raw: event.raw ?? event,
1974
+ subtype: event.subtype,
1975
+ operator: event.operator,
1976
+ targetUser: event.targetUser,
1977
+ group: event.group,
1978
+ data: event.data
1979
+ };
1980
+ }
1981
+ function normalizeRequestEvent(event) {
1982
+ return {
1983
+ type: "request",
1984
+ selfId: event.selfId,
1985
+ timestamp: event.timestamp ?? Date.now(),
1986
+ raw: event.raw ?? event,
1987
+ subtype: event.subtype,
1988
+ requestId: event.requestId ?? createGeneratedId("req", event.selfId),
1989
+ sender: event.sender,
1990
+ group: event.group,
1991
+ comment: event.comment
1992
+ };
1993
+ }
1994
+ function isMessagePayload(event) {
1995
+ return event.type === "message" && typeof event.messageId === "string" && typeof event.text === "string" && typeof event.timestamp === "number" && "raw" in event;
1996
+ }
1997
+ function isNoticePayload(event) {
1998
+ return event.type === "notice" && typeof event.timestamp === "number" && "raw" in event;
1999
+ }
2000
+ function isRequestPayload(event) {
2001
+ return event.type === "request" && typeof event.requestId === "string" && typeof event.timestamp === "number" && "raw" in event;
2002
+ }
2003
+ function normalizeText(text2, content) {
2004
+ const value = text2?.trim();
2005
+ if (value) {
2006
+ return value;
2007
+ }
2008
+ return extractMessageText(content);
2009
+ }
2010
+ function createGeneratedId(prefix, selfId) {
2011
+ return `${prefix}:${selfId}:${Date.now()}`;
2012
+ }
2013
+ function buildMessageEventLog(payload) {
2014
+ if (payload.target.type === "group") {
2015
+ return formatLogLine(
2016
+ "Group message",
2017
+ `[${payload.target.id}-${payload.sender.id}]`,
2018
+ formatPreview(payload.text)
2019
+ );
2020
+ }
2021
+ return formatLogLine(
2022
+ "Private message",
2023
+ `[${payload.sender.id}]`,
2024
+ formatPreview(payload.text)
2025
+ );
2026
+ }
2027
+ function buildNoticeEventLog(payload) {
2028
+ if (payload.group?.id) {
2029
+ return formatLogLine(
2030
+ "Group notice",
2031
+ formatGroupNoticeIdentity(payload),
2032
+ describeNoticeSubtype(payload.subtype)
2033
+ );
2034
+ }
2035
+ if (payload.targetUser?.id) {
2036
+ return formatLogLine(
2037
+ "Friend notice",
2038
+ `[${payload.targetUser.id}]`,
2039
+ describeNoticeSubtype(payload.subtype)
2040
+ );
2041
+ }
2042
+ return formatLogLine(
2043
+ "Notice",
2044
+ describeNoticeSubtype(payload.subtype)
2045
+ );
2046
+ }
2047
+ function buildRequestEventLog(payload) {
2048
+ if (payload.group?.id) {
2049
+ return formatLogLine(
2050
+ "Group request",
2051
+ `[${payload.group.id}-${payload.sender.id}]`,
2052
+ formatRequestComment(payload.comment)
2053
+ );
2054
+ }
2055
+ return formatLogLine(
2056
+ "Friend request",
2057
+ `[${payload.sender.id}]`,
2058
+ formatRequestComment(payload.comment)
2059
+ );
2060
+ }
2061
+ function formatPreview(text2) {
2062
+ const normalized = text2.replace(/\s+/g, " ").trim();
2063
+ if (!normalized) {
2064
+ return "[\u7A7A\u6D88\u606F]";
2065
+ }
2066
+ return normalized.length > 48 ? `${normalized.slice(0, 45)}...` : normalized;
2067
+ }
2068
+ function formatLogLine(title, ...parts) {
2069
+ return [title, ...parts.filter((part) => Boolean(part))].join(" ");
2070
+ }
2071
+ function describeSendMessageTitle(type) {
2072
+ return type === "group" ? "Send group message" : "Send private message";
2073
+ }
2074
+ function formatOutboundTarget(target) {
2075
+ return target.type === "group" ? `[${target.id}]` : `[${target.id}]`;
2076
+ }
2077
+ function formatGroupNoticeIdentity(payload) {
2078
+ const userId = payload.targetUser?.id ?? payload.operator?.id;
2079
+ return userId ? `[${payload.group.id}-${userId}]` : `[${payload.group.id}]`;
2080
+ }
2081
+ function formatRequestComment(comment) {
2082
+ const value = comment?.trim();
2083
+ return value ? formatPreview(value) : void 0;
2084
+ }
2085
+ function describeNoticeSubtype(subtype) {
2086
+ switch (subtype) {
2087
+ case "group_increase":
2088
+ return "\u7FA4\u6210\u5458\u589E\u52A0";
2089
+ case "group_decrease":
2090
+ return "\u7FA4\u6210\u5458\u51CF\u5C11";
2091
+ case "group_admin":
2092
+ return "\u7FA4\u7BA1\u7406\u53D8\u66F4";
2093
+ case "group_upload":
2094
+ return "\u7FA4\u6587\u4EF6\u4E0A\u4F20";
2095
+ case "friend_add":
2096
+ return "\u65B0\u589E\u597D\u53CB";
2097
+ case "friend_recall":
2098
+ return "\u597D\u53CB\u64A4\u56DE";
2099
+ case "group_recall":
2100
+ return "\u7FA4\u6D88\u606F\u64A4\u56DE";
2101
+ case "group_ban":
2102
+ return "\u7FA4\u7981\u8A00";
2103
+ case "group_lift_ban":
2104
+ return "\u89E3\u9664\u7981\u8A00";
2105
+ }
2106
+ }
2107
+
2108
+ // src/api/standard.ts
2109
+ var STANDARD_ACTION_NAMES = [
2110
+ "get_framework_info",
2111
+ "get_plugin_list",
2112
+ "get_service_list",
2113
+ "get_action_list",
2114
+ "get_runtime_snapshot",
2115
+ "dispatch_event",
2116
+ "send_message"
2117
+ ];
2118
+ function getServiceKind(service) {
2119
+ if (service === null) return "null";
2120
+ if (service === void 0) return "undefined";
2121
+ if (typeof service === "object" && service.constructor?.name) {
2122
+ return service.constructor.name;
2123
+ }
2124
+ return typeof service;
2125
+ }
2126
+ function createTypedErrorResponse(code, message, startTime) {
2127
+ return createErrorResponse(code, message, startTime);
2128
+ }
2129
+ function createStandardActions(app, apiClient, startedAt) {
2130
+ return {
2131
+ get_framework_info: () => {
2132
+ const startTime = Date.now();
2133
+ return createSuccessResponse(
2134
+ {
2135
+ name: app.name,
2136
+ running: app.isRunning(),
2137
+ uptime: app.isRunning() ? Date.now() - startedAt : 0,
2138
+ startedAt,
2139
+ pluginCount: app.pluginCount(),
2140
+ serviceCount: app.services.size(),
2141
+ actionCount: apiClient.getActionCount()
2142
+ },
2143
+ startTime
2144
+ );
2145
+ },
2146
+ get_plugin_list: () => {
2147
+ const startTime = Date.now();
2148
+ return createSuccessResponse(
2149
+ app.listPlugins().map((plugin) => ({
2150
+ name: plugin.name,
2151
+ version: plugin.version,
2152
+ priority: plugin.priority ?? 0
2153
+ })),
2154
+ startTime
2155
+ );
2156
+ },
2157
+ get_service_list: () => {
2158
+ const startTime = Date.now();
2159
+ return createSuccessResponse(
2160
+ app.services.entries().map(([name, service]) => ({
2161
+ name,
2162
+ kind: getServiceKind(service)
2163
+ })),
2164
+ startTime
2165
+ );
2166
+ },
2167
+ get_action_list: () => {
2168
+ const startTime = Date.now();
2169
+ return createSuccessResponse(
2170
+ apiClient.getActions(),
2171
+ startTime
2172
+ );
2173
+ },
2174
+ get_runtime_snapshot: () => {
2175
+ const startTime = Date.now();
2176
+ return createSuccessResponse(
2177
+ {
2178
+ framework: {
2179
+ name: app.name,
2180
+ running: app.isRunning(),
2181
+ uptime: app.isRunning() ? Date.now() - startedAt : 0
2182
+ },
2183
+ plugins: app.listPlugins().map((plugin) => plugin.name),
2184
+ services: app.services.keys(),
2185
+ actions: apiClient.getActions()
2186
+ },
2187
+ startTime
2188
+ );
2189
+ },
2190
+ dispatch_event: async (request) => {
2191
+ const startTime = Date.now();
2192
+ const params = request.params;
2193
+ if (!params || typeof params !== "object" || !("event" in params)) {
2194
+ return createTypedErrorResponse(1e3, "Invalid dispatch_event params", startTime);
2195
+ }
2196
+ const rawEvent = params.event;
2197
+ if (!rawEvent || typeof rawEvent !== "object" || !("type" in rawEvent) || !("selfId" in rawEvent)) {
2198
+ return createTypedErrorResponse(1e3, "Invalid dispatch_event event payload", startTime);
2199
+ }
2200
+ const payload = normalizeEventPayload(rawEvent);
2201
+ app.logger.info(buildEventLog(payload));
2202
+ await app.dispatch(payload);
2203
+ return createSuccessResponse({
2204
+ type: payload.type,
2205
+ timestamp: payload.timestamp,
2206
+ selfId: payload.selfId
2207
+ }, startTime);
2208
+ },
2209
+ send_message: async (request) => {
2210
+ const startTime = Date.now();
2211
+ const messageService = app.services.get("message");
2212
+ const params = request.params;
2213
+ if (!params || typeof params !== "object" || !("target" in params) || !("content" in params)) {
2214
+ return createTypedErrorResponse(1e3, "Invalid send_message params", startTime);
2215
+ }
2216
+ if (!messageService?.hasSender()) {
2217
+ return createTypedErrorResponse(5001, "\u6D88\u606F\u53D1\u9001\u5668\u672A\u6CE8\u518C", startTime);
2218
+ }
2219
+ app.logger.info(buildSendMessageLog(params));
2220
+ const result = await messageService.sendMessage(params);
2221
+ return createSuccessResponse(result, startTime);
2222
+ }
2223
+ };
2224
+ }
2225
+ function registerStandardActions(app, apiClient, startedAt) {
2226
+ const actions = createStandardActions(app, apiClient, startedAt);
2227
+ const actionNames = [];
2228
+ for (const [name, handler] of Object.entries(actions)) {
2229
+ apiClient.on(name, handler);
2230
+ actionNames.push(name);
2231
+ }
2232
+ return actionNames;
2233
+ }
2234
+
2235
+ // src/plugin/base.ts
2236
+ function definePlugin(options) {
2237
+ return {
2238
+ name: options.name,
2239
+ version: options.version,
2240
+ priority: options.priority,
2241
+ setup: options.setup,
2242
+ dispose: options.dispose
2243
+ };
2244
+ }
2245
+
2246
+ // src/plugin/hooks/lifecycle.ts
2247
+ var LifecycleHookManager = class {
2248
+ hooks = /* @__PURE__ */ new Map();
2249
+ on(name, handler) {
2250
+ if (!this.hooks.has(name)) {
2251
+ this.hooks.set(name, /* @__PURE__ */ new Set());
2252
+ }
2253
+ this.hooks.get(name).add(handler);
2254
+ return this;
2255
+ }
2256
+ off(name, handler) {
2257
+ const handlers = this.hooks.get(name);
2258
+ if (handlers) {
2259
+ return handlers.delete(handler);
2260
+ }
2261
+ return false;
2262
+ }
2263
+ async emit(name, app) {
2264
+ const handlers = this.hooks.get(name);
2265
+ if (!handlers || handlers.size === 0) return;
2266
+ for (const handler of handlers) {
2267
+ try {
2268
+ await handler(app);
2269
+ } catch (error) {
2270
+ app.logger.error(`Lifecycle hook '${name}' error:`, error);
2271
+ }
2272
+ }
2273
+ }
2274
+ has(name) {
2275
+ const handlers = this.hooks.get(name);
2276
+ return handlers !== void 0 && handlers.size > 0;
2277
+ }
2278
+ count(name) {
2279
+ return this.hooks.get(name)?.size ?? 0;
2280
+ }
2281
+ clear(name) {
2282
+ if (name) {
2283
+ this.hooks.delete(name);
2284
+ } else {
2285
+ this.hooks.clear();
2286
+ }
2287
+ }
2288
+ };
2289
+
2290
+ // src/plugin/hooks/error.ts
2291
+ var ErrorHooks = class {
2292
+ hooks = /* @__PURE__ */ new Map();
2293
+ app;
2294
+ handleUnhandledRejection = (reason) => {
2295
+ const error = reason instanceof Error ? reason : new Error(String(reason));
2296
+ void this.emit("onUnhandledRejection", error);
2297
+ };
2298
+ handleUncaughtException = (error) => {
2299
+ void this.emit("onUncaughtException", error);
2300
+ };
2301
+ globalHandlersAttached = false;
2302
+ constructor(app) {
2303
+ this.app = app;
2304
+ this.setupGlobalHandlers();
2305
+ }
2306
+ on(name, handler) {
2307
+ if (!this.hooks.has(name)) {
2308
+ this.hooks.set(name, /* @__PURE__ */ new Set());
2309
+ }
2310
+ this.hooks.get(name).add(handler);
2311
+ return this;
2312
+ }
2313
+ off(name, handler) {
2314
+ return this.hooks.get(name)?.delete(handler) ?? false;
2315
+ }
2316
+ async emit(name, error, context) {
2317
+ const handlers = this.hooks.get(name);
2318
+ if (!handlers || handlers.size === 0) {
2319
+ if (name === "onError") {
2320
+ return false;
2321
+ }
2322
+ this.app.logger.error(`[${name}] ${error.message}`);
2323
+ if (error.stack) {
2324
+ this.app.logger.debug(error.stack);
2325
+ }
2326
+ return false;
2327
+ }
2328
+ for (const handler of handlers) {
2329
+ try {
2330
+ const result = await handler(error, context);
2331
+ if (result === true) {
2332
+ return true;
2333
+ }
2334
+ } catch (handlerError) {
2335
+ const message = handlerError instanceof Error ? handlerError.message : String(handlerError);
2336
+ this.app.logger.error(`Error in error hook '${name}': ${message}`);
2337
+ if (handlerError instanceof Error && handlerError.stack) {
2338
+ this.app.logger.debug(handlerError.stack);
2339
+ }
2340
+ }
2341
+ }
2342
+ return false;
2343
+ }
2344
+ setupGlobalHandlers() {
2345
+ if (this.globalHandlersAttached) {
2346
+ return;
2347
+ }
2348
+ process.on("unhandledRejection", this.handleUnhandledRejection);
2349
+ process.on("uncaughtException", this.handleUncaughtException);
2350
+ this.globalHandlersAttached = true;
2351
+ }
2352
+ has(name) {
2353
+ const handlers = this.hooks.get(name);
2354
+ return handlers !== void 0 && handlers.size > 0;
2355
+ }
2356
+ count(name) {
2357
+ return this.hooks.get(name)?.size ?? 0;
2358
+ }
2359
+ clear(name) {
2360
+ if (name) {
2361
+ this.hooks.delete(name);
2362
+ } else {
2363
+ this.hooks.clear();
2364
+ }
2365
+ }
2366
+ destroy() {
2367
+ if (this.globalHandlersAttached) {
2368
+ process.off("unhandledRejection", this.handleUnhandledRejection);
2369
+ process.off("uncaughtException", this.handleUncaughtException);
2370
+ this.globalHandlersAttached = false;
2371
+ }
2372
+ this.clear();
2373
+ }
2374
+ };
2375
+
2376
+ // src/console-adapter.ts
2377
+ import { createInterface } from "readline";
2378
+ import { stdin as input, stdout as output } from "process";
2379
+ var REQUIRED_STANDARD_ACTIONS = ["get_action_list", "dispatch_event", "send_message"];
2380
+ function createConsoleAdapter(options = {}) {
2381
+ const runtime = {
2382
+ rl: null,
2383
+ sequence: 0
2384
+ };
2385
+ return definePlugin({
2386
+ name: "adapter-console",
2387
+ version: "1.0.0",
2388
+ async setup(app) {
2389
+ const bot = new Bot(app, {
2390
+ id: options.selfId ?? "console-bot",
2391
+ adapter: options.adapter ?? "console",
2392
+ loggerPrefix: options.loggerPrefix ?? "console"
2393
+ });
2394
+ await ensureStandardActions(app);
2395
+ app.registerService("bot", bot);
2396
+ app.registerService("consoleBot", bot);
2397
+ app.setMessageSender(createConsoleSender(bot, runtime));
2398
+ runtime.rl = createConsoleInterface();
2399
+ runtime.rl.on("line", (line) => {
2400
+ void handleConsoleLine(line, bot, options, runtime);
2401
+ });
2402
+ bot.logger.info("\u63A7\u5236\u53F0\u9002\u914D\u5668\u5DF2\u542F\u52A8");
2403
+ },
2404
+ async dispose(app) {
2405
+ runtime.rl?.close();
2406
+ runtime.rl = null;
2407
+ app.getMessageService()?.clearSender();
2408
+ }
2409
+ });
2410
+ }
2411
+ function createConsoleSender(bot, runtime) {
2412
+ return {
2413
+ async sendMessage(params) {
2414
+ const timestamp = Date.now();
2415
+ const messageId = `console:out:${++runtime.sequence}`;
2416
+ output.write(`${formatOutgoingMessage(params.content)}
2417
+ `);
2418
+ bot.logger.debug(`\u63A7\u5236\u53F0\u6D88\u606F\u5DF2\u8F93\u51FA id=${messageId}`);
2419
+ return {
2420
+ messageId,
2421
+ timestamp
2422
+ };
2423
+ }
2424
+ };
2425
+ }
2426
+ async function ensureStandardActions(app) {
2427
+ const response = await app.call(createRequest("get_action_list", {}));
2428
+ if (response.status !== "success" || !response.data) {
2429
+ throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u5E94\u7528\u6807\u51C6\u63A5\u53E3\u52A8\u4F5C\u5217\u8868");
2430
+ }
2431
+ for (const action of REQUIRED_STANDARD_ACTIONS) {
2432
+ if (!response.data.includes(action)) {
2433
+ }
2434
+ }
2435
+ }
2436
+ async function handleConsoleLine(line, bot, options, runtime) {
2437
+ try {
2438
+ const trimmed = line.trim();
2439
+ if (!trimmed) {
2440
+ return;
2441
+ }
2442
+ const event = createDefaultMessageEvent(trimmed, bot, options, runtime);
2443
+ const response = await bot.dispatchEvent(event);
2444
+ if (response.status === "error") {
2445
+ const message = response.error?.message ?? "unknown error";
2446
+ bot.logger.error(`\u63A7\u5236\u53F0\u4E8B\u4EF6\u5206\u53D1\u5931\u8D25: ${message}`);
2447
+ }
2448
+ } catch (error) {
2449
+ const message = error instanceof Error ? error.message : String(error);
2450
+ bot.logger.error(`\u63A7\u5236\u53F0\u8F93\u5165\u65E0\u6548: ${message}`);
2451
+ }
2452
+ }
2453
+ function createDefaultMessageEvent(text2, bot, options, runtime) {
2454
+ const userId = options.userId ?? "1145114";
2455
+ const target = options.targetType === "group" ? { type: "group", id: options.groupId ?? "123456" } : { type: "private", id: userId };
2456
+ return createMessageEvent(bot, runtime, {
2457
+ content: text2,
2458
+ sender: createUser(userId, options.nickname),
2459
+ target
2460
+ });
2461
+ }
2462
+ function createMessageEvent(bot, runtime, input2) {
2463
+ const content = normalizeMessageContent(input2.content);
2464
+ return {
2465
+ type: "message",
2466
+ selfId: bot.id,
2467
+ adapter: bot.adapter,
2468
+ timestamp: Date.now(),
2469
+ raw: input2,
2470
+ messageId: `console:in:${++runtime.sequence}`,
2471
+ text: extractMessageText(content),
2472
+ content,
2473
+ sender: input2.sender,
2474
+ target: input2.target
2475
+ };
2476
+ }
2477
+ function createUser(userId, nickname) {
2478
+ return {
2479
+ id: userId,
2480
+ nickname: nickname ?? userId
2481
+ };
2482
+ }
2483
+ function createConsoleInterface() {
2484
+ const rl = createInterface({ input, output, terminal: true });
2485
+ return rl;
2486
+ }
2487
+ function formatOutgoingMessage(content) {
2488
+ return `[console] ${formatMessageContent(content)}`;
2489
+ }
2490
+ function createRequest(action, params) {
2491
+ return {
2492
+ id: `console:${action}:${Date.now()}`,
2493
+ version: "1.0.0",
2494
+ action,
2495
+ params,
2496
+ timestamp: Date.now()
2497
+ };
2498
+ }
2499
+
2500
+ // src/index.ts
2501
+ var START_SIGNALS = ["SIGINT", "SIGTERM"];
2502
+ var currentApp = null;
2503
+ var startPromise = null;
2504
+ var stopPromise = null;
2505
+ var disposeSignalHandlers = null;
2506
+ var logger = globalThis.logger ?? createPackageLogger();
2507
+ globalThis.logger = logger;
2508
+ function createApp(config = {}) {
2509
+ return new App(config);
2510
+ }
2511
+ function getFrameworkServices(app) {
2512
+ const logger3 = app.services.get("logger") ?? app.services.get("log") ?? registerLoggerService(app, {
2513
+ prefix: app.name,
2514
+ timestamp: true
2515
+ });
2516
+ const lifecycleHooks = app.services.get("lifecycleHooks") ?? app.services.get("lifecycle") ?? registerLifecycleHooks(app);
2517
+ const errorHooks = app.services.get("errorHooks") ?? app.services.get("errors") ?? registerErrorHooks(app);
2518
+ const apiClient = app.services.get("apiClient") ?? registerAPIService(app);
2519
+ const builtInActions = app.services.get("builtInActions") ?? [...STANDARD_ACTION_NAMES];
2520
+ const transportServer = app.services.get("apiTransport") ?? registerAPITransport(app, apiClient, logger3, void 0);
2521
+ const config = app.services.get("config") ?? registerConfigService(app);
2522
+ return {
2523
+ logger: logger3,
2524
+ lifecycleHooks,
2525
+ errorHooks,
2526
+ apiClient,
2527
+ builtInActions,
2528
+ transportServer,
2529
+ config
2530
+ };
2531
+ }
2532
+ async function start(optionsOrSetup = {}) {
2533
+ const options = typeof optionsOrSetup === "function" ? { setup: optionsOrSetup } : optionsOrSetup;
2534
+ const plugins = resolveStartPlugins(options.plugins);
2535
+ if (startPromise) {
2536
+ return startPromise;
2537
+ }
2538
+ startPromise = (async () => {
2539
+ if (currentApp?.isRunning()) {
2540
+ return currentApp;
2541
+ }
2542
+ const app = options.app ?? createApp(options);
2543
+ currentApp = app;
2544
+ const startTime = Date.now();
2545
+ const services = setupFramework(app, options);
2546
+ logStartupBanner(services.logger, app);
2547
+ try {
2548
+ logRuntimeConfig(services.logger, services.config);
2549
+ await services.lifecycleHooks.emit("beforeStart", app);
2550
+ for (const plugin of plugins) {
2551
+ app.use(plugin);
2552
+ }
2553
+ if (options.services) {
2554
+ for (const [name, service] of Object.entries(options.services)) {
2555
+ app.registerService(name, service);
2556
+ }
2557
+ }
2558
+ if (options.setup) {
2559
+ await options.setup(app);
2560
+ }
2561
+ await loadPackageApps(app, services.logger);
2562
+ logPluginSummary(services.logger, app);
2563
+ logStandardInterface(services.logger, services.apiClient);
2564
+ await app.start();
2565
+ await services.lifecycleHooks.emit("afterStart", app);
2566
+ const transportAddress = await startTransport(services.transportServer, services.logger);
2567
+ const duration = Date.now() - startTime;
2568
+ logStartupSummary(
2569
+ services.logger,
2570
+ app,
2571
+ services.apiClient,
2572
+ services.builtInActions,
2573
+ transportAddress,
2574
+ duration
2575
+ );
2576
+ if (options.onStarted) {
2577
+ await options.onStarted(app);
2578
+ }
2579
+ return app;
2580
+ } catch (error) {
2581
+ const startupError = error instanceof Error ? error : new Error(String(error));
2582
+ await services.errorHooks.emit("onError", startupError, { phase: "start", appName: app.name });
2583
+ await cleanupRuntime(app);
2584
+ throw startupError;
2585
+ }
2586
+ })().finally(() => {
2587
+ startPromise = null;
2588
+ });
2589
+ return startPromise;
2590
+ }
2591
+ async function stop(app = currentApp) {
2592
+ if (!app) {
2593
+ return;
2594
+ }
2595
+ if (stopPromise) {
2596
+ return stopPromise;
2597
+ }
2598
+ stopPromise = (async () => {
2599
+ const { logger: logger3, lifecycleHooks, errorHooks, transportServer } = getFrameworkServices(app);
2600
+ try {
2601
+ await lifecycleHooks.emit("beforeStop", app);
2602
+ if (transportServer.isRunning()) {
2603
+ await transportServer.stop();
2604
+ logger3.info("\u5BF9\u5916\u4F20\u8F93\u5165\u53E3\u5DF2\u5173\u95ED");
2605
+ }
2606
+ if (app.isRunning()) {
2607
+ await app.stop();
2608
+ }
2609
+ await lifecycleHooks.emit("afterStop", app);
2610
+ logger3.info(`Plume \u5E94\u7528\u5DF2\u505C\u6B62 | name: ${app.name}`);
2611
+ } catch (error) {
2612
+ const stopError = error instanceof Error ? error : new Error(String(error));
2613
+ await errorHooks.emit("onError", stopError, { phase: "stop", appName: app.name });
2614
+ throw stopError;
2615
+ } finally {
2616
+ await cleanupRuntime(app);
2617
+ }
2618
+ })().finally(() => {
2619
+ stopPromise = null;
2620
+ });
2621
+ return stopPromise;
2622
+ }
2623
+ function setupFramework(app, options) {
2624
+ const config = registerConfigService(app, options.config);
2625
+ if (options.config?.initialize !== false) {
2626
+ config.initialize({
2627
+ path: options.config?.path
2628
+ });
2629
+ }
2630
+ config.reload();
2631
+ const logger3 = registerLoggerService(app, {
2632
+ level: options.logger?.level ?? config.get("log.level", "info"),
2633
+ prefix: options.logger?.prefix ?? app.name,
2634
+ timestamp: config.get("log.timestamp", true)
2635
+ });
2636
+ app.logger.setLevel(options.logger?.level ?? config.get("log.level", "info"));
2637
+ const lifecycleHooks = registerLifecycleHooks(app);
2638
+ const errorHooks = registerErrorHooks(app);
2639
+ registerMessageService(app);
2640
+ const apiClient = registerAPIService(app);
2641
+ const builtInActions = registerStandardActions(app, apiClient, Date.now());
2642
+ app.registerService("builtInActions", builtInActions);
2643
+ app.registerService("apiStandard", createStandardInterfaceDescriptor(apiClient));
2644
+ const transportOptions = resolveTransportOptions(config, options.transport);
2645
+ const transportServer = registerAPITransport(app, apiClient, logger3, transportOptions);
2646
+ registerSignalHandlers(app, logger3);
2647
+ return {
2648
+ logger: logger3,
2649
+ lifecycleHooks,
2650
+ errorHooks,
2651
+ apiClient,
2652
+ builtInActions,
2653
+ transportServer,
2654
+ config
2655
+ };
2656
+ }
2657
+ function registerConfigService(app, options) {
2658
+ const existingConfig = app.services.get("config");
2659
+ if (existingConfig) {
2660
+ return existingConfig;
2661
+ }
2662
+ const config = new ConfigService(options);
2663
+ app.registerService("config", config);
2664
+ return config;
2665
+ }
2666
+ function registerLoggerService(app, options) {
2667
+ const existingLogger = app.services.get("logger") ?? app.services.get("log");
2668
+ if (existingLogger) {
2669
+ return existingLogger;
2670
+ }
2671
+ const logger3 = new LogService(options);
2672
+ app.registerService("logger", logger3);
2673
+ app.registerService("log", logger3);
2674
+ return logger3;
2675
+ }
2676
+ function registerLifecycleHooks(app) {
2677
+ const existingHooks = app.services.get("lifecycleHooks") ?? app.services.get("lifecycle");
2678
+ if (existingHooks) {
2679
+ return existingHooks;
2680
+ }
2681
+ const lifecycleHooks = new LifecycleHookManager();
2682
+ app.registerService("lifecycleHooks", lifecycleHooks);
2683
+ app.registerService("lifecycle", lifecycleHooks);
2684
+ return lifecycleHooks;
2685
+ }
2686
+ function registerErrorHooks(app) {
2687
+ const existingHooks = app.services.get("errorHooks") ?? app.services.get("errors");
2688
+ if (existingHooks) {
2689
+ return existingHooks;
2690
+ }
2691
+ const errorHooks = new ErrorHooks(app);
2692
+ app.registerService("errorHooks", errorHooks);
2693
+ app.registerService("errors", errorHooks);
2694
+ return errorHooks;
2695
+ }
2696
+ function registerAPIService(app) {
2697
+ const existingClient = app.services.get("apiClient");
2698
+ if (existingClient) {
2699
+ return existingClient;
2700
+ }
2701
+ const apiClient = new APIClient();
2702
+ app.registerService("apiClient", apiClient);
2703
+ app.registerService("apiRouter", apiClient.getRouter());
2704
+ app.registerService("apiStandard", createStandardInterfaceDescriptor(apiClient));
2705
+ app.registerService("builtInActions", [...STANDARD_ACTION_NAMES]);
2706
+ return apiClient;
2707
+ }
2708
+ function registerMessageService(app) {
2709
+ const existingService = app.services.get("message");
2710
+ if (existingService) {
2711
+ return existingService;
2712
+ }
2713
+ const messageService = new MessageService();
2714
+ app.registerService("message", messageService);
2715
+ return messageService;
2716
+ }
2717
+ function registerAPITransport(app, apiClient, logger3, options) {
2718
+ const existingTransport = app.services.get("apiTransport");
2719
+ if (existingTransport) {
2720
+ return existingTransport;
2721
+ }
2722
+ const transportServer = new APITransportServer(apiClient, options, logger3);
2723
+ app.registerService("apiTransport", transportServer);
2724
+ return transportServer;
2725
+ }
2726
+ function registerSignalHandlers(app, logger3) {
2727
+ disposeSignalHandlers?.();
2728
+ let shuttingDown = false;
2729
+ const listeners = START_SIGNALS.map((signal) => {
2730
+ const listener = () => {
2731
+ if (shuttingDown) {
2732
+ return;
2733
+ }
2734
+ shuttingDown = true;
2735
+ logger3.warn(`\u6536\u5230\u4FE1\u53F7 ${signal}\uFF0C\u51C6\u5907\u505C\u6B62\u5E94\u7528...`);
2736
+ void stop(app).finally(() => {
2737
+ process.exit(0);
2738
+ });
2739
+ };
2740
+ process.on(signal, listener);
2741
+ return { signal, listener };
2742
+ });
2743
+ disposeSignalHandlers = () => {
2744
+ for (const { signal, listener } of listeners) {
2745
+ process.off(signal, listener);
2746
+ }
2747
+ disposeSignalHandlers = null;
2748
+ };
2749
+ }
2750
+ async function cleanupRuntime(app) {
2751
+ if (currentApp === app) {
2752
+ currentApp = null;
2753
+ }
2754
+ const transportServer = app.services.get("apiTransport");
2755
+ if (transportServer?.isRunning()) {
2756
+ await transportServer.stop().catch(() => void 0);
2757
+ }
2758
+ app.services.get("errorHooks")?.destroy();
2759
+ disposeSignalHandlers?.();
2760
+ }
2761
+ function logStartupBanner(logger3, app) {
2762
+ logger3.info(`Plume \u542F\u52A8\u4E2D app=${app.name} cwd=${process.cwd()}`);
2763
+ }
2764
+ function logStartupSummary(logger3, app, apiClient, builtInActions, transportAddress, duration) {
2765
+ logger3.info(`\u542F\u52A8\u5B8C\u6210 app=${app.name} duration=${duration}ms plugins=${app.pluginCount()} actions=${apiClient.getActionCount()}`);
2766
+ if (transportAddress) {
2767
+ logger3.info(`HTTP ${transportAddress.httpUrl ?? "disabled"}`);
2768
+ logger3.info(`WS ${transportAddress.wsUrl ?? "disabled"}`);
2769
+ }
2770
+ if (builtInActions.length > 0) {
2771
+ logger3.debug(`\u5185\u7F6E\u52A8\u4F5C\u6570\u91CF ${builtInActions.length}`);
2772
+ }
2773
+ }
2774
+ function logStandardInterface(logger3, apiClient) {
2775
+ logger3.info(`\u63A5\u53E3\u5DF2\u5C31\u7EEA actions=${apiClient.getActionCount()}`);
2776
+ }
2777
+ function logRuntimeConfig(logger3, config) {
2778
+ logger3.info(
2779
+ `\u914D\u7F6E host=${config.get("transport.host")} port=${config.get("transport.port")} level=${config.get("log.level")} source=${config.getSource("transport.port") ?? "default"}`
2780
+ );
2781
+ }
2782
+ function logPluginSummary(logger3, app) {
2783
+ logger3.info(`\u63D2\u4EF6 count=${app.pluginCount()} names=${formatList(app.listPlugins().map((plugin) => plugin.name))}`);
2784
+ }
2785
+ function createStandardInterfaceDescriptor(apiClient) {
2786
+ return {
2787
+ requestFormat: "APIRequest",
2788
+ responseFormat: "APIResponse",
2789
+ authProvider: "TokenAuth",
2790
+ middleware: "MiddlewareChain",
2791
+ actions: apiClient.getActions()
2792
+ };
2793
+ }
2794
+ async function loadPackageApps(app, logger3) {
2795
+ const packageMetadata = readCurrentPackageMetadata();
2796
+ const manifest = packageMetadata?.plume ?? null;
2797
+ if (!manifest) {
2798
+ resumePackageLogger();
2799
+ return;
2800
+ }
2801
+ const directories = resolveManifestAppDirectories(manifest);
2802
+ if (directories.length === 0) {
2803
+ resumePackageLogger();
2804
+ return;
2805
+ }
2806
+ const loadedFiles = /* @__PURE__ */ new Set();
2807
+ for (const directory of directories) {
2808
+ if (!existsSync2(directory) || !statSync(directory).isDirectory()) {
2809
+ continue;
2810
+ }
2811
+ for (const filePath of collectAppModuleFiles(directory)) {
2812
+ if (loadedFiles.has(filePath)) {
2813
+ continue;
2814
+ }
2815
+ loadedFiles.add(filePath);
2816
+ }
2817
+ }
2818
+ if (isPluginPackage(packageMetadata) && loadedFiles.size > 0) {
2819
+ logger3.info(`\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6 count=${loadedFiles.size}`);
2820
+ }
2821
+ resumePackageLogger();
2822
+ for (const filePath of loadedFiles) {
2823
+ const module = await importAppModule(filePath);
2824
+ await registerAppModule(app, module, filePath);
2825
+ }
2826
+ }
2827
+ function formatList(items) {
2828
+ return items.length > 0 ? items.join(", ") : "none";
2829
+ }
2830
+ function createPackageLogger() {
2831
+ const packageLogger = new LogService();
2832
+ packageLogger.suspend();
2833
+ return packageLogger;
2834
+ }
2835
+ function resumePackageLogger() {
2836
+ logger.resume();
2837
+ }
2838
+ function readCurrentPackageMetadata() {
2839
+ const packageJsonPath = resolve4(process.cwd(), "package.json");
2840
+ if (!existsSync2(packageJsonPath)) {
2841
+ return null;
2842
+ }
2843
+ try {
2844
+ return JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
2845
+ } catch {
2846
+ return null;
2847
+ }
2848
+ }
2849
+ function isPluginPackage(metadata) {
2850
+ const packageName = metadata?.name?.trim();
2851
+ if (!packageName) {
2852
+ return false;
2853
+ }
2854
+ return packageName.includes("plugin");
2855
+ }
2856
+ function resolveManifestAppDirectories(manifest) {
2857
+ const isTypeScriptEntry = /\.(?:ts|tsx|mts|cts)$/i.test(process.argv[1] ?? "");
2858
+ const preferredDirs = isTypeScriptEntry ? manifest.sourceAppDirs ?? manifest.appDirs ?? [] : manifest.appDirs ?? manifest.sourceAppDirs ?? [];
2859
+ const fallbackDirs = isTypeScriptEntry ? manifest.appDirs ?? [] : manifest.sourceAppDirs ?? [];
2860
+ return [...preferredDirs, ...fallbackDirs].map((path) => resolve4(process.cwd(), path));
2861
+ }
2862
+ function collectAppModuleFiles(directory) {
2863
+ const files = [];
2864
+ for (const entry of readdirSync(directory)) {
2865
+ const fullPath = join2(directory, entry);
2866
+ const stats = statSync(fullPath);
2867
+ if (stats.isDirectory()) {
2868
+ files.push(...collectAppModuleFiles(fullPath));
2869
+ continue;
2870
+ }
2871
+ if (isAppModuleFile(fullPath)) {
2872
+ files.push(fullPath);
2873
+ }
2874
+ }
2875
+ return files.sort();
2876
+ }
2877
+ function isAppModuleFile(filePath) {
2878
+ if (filePath.endsWith(".d.ts") || filePath.endsWith(".map")) {
2879
+ return false;
2880
+ }
2881
+ return /\.(?:js|mjs|cjs|ts|mts|cts)$/i.test(filePath);
2882
+ }
2883
+ async function importAppModule(filePath) {
2884
+ return import(pathToFileURL(filePath).href);
2885
+ }
2886
+ async function registerAppModule(app, module, filePath) {
2887
+ if (isAppModuleDefinition(module.default)) {
2888
+ await applyAppModuleDefinition(app, module.default);
2889
+ return;
2890
+ }
2891
+ if (typeof module.default === "function") {
2892
+ await module.default(app);
2893
+ return;
2894
+ }
2895
+ if (module.default && typeof module.default === "object" && "setup" in module.default) {
2896
+ app.use(module.default);
2897
+ return;
2898
+ }
2899
+ if (typeof module.setup === "function" || isCommandMap(module.commands) || typeof module.name === "string" || typeof module.log === "boolean") {
2900
+ const setup = typeof module.setup === "function" ? module.setup : void 0;
2901
+ await applyAppModuleDefinition(app, {
2902
+ name: typeof module.name === "string" ? module.name : void 0,
2903
+ log: typeof module.log === "boolean" ? module.log : void 0,
2904
+ setup,
2905
+ commands: isCommandMap(module.commands) ? module.commands : void 0
2906
+ });
2907
+ return;
2908
+ }
2909
+ if (module.plugin && typeof module.plugin === "object" && "setup" in module.plugin) {
2910
+ app.use(module.plugin);
2911
+ return;
2912
+ }
2913
+ const namedDefinitions = Object.entries(module).filter(([name]) => !RESERVED_APP_EXPORT_NAMES.has(name)).map(([, value]) => value).filter(isAppModuleDefinition);
2914
+ if (namedDefinitions.length > 0) {
2915
+ for (const definition of namedDefinitions) {
2916
+ await applyAppModuleDefinition(app, definition);
2917
+ }
2918
+ return;
2919
+ }
2920
+ throw new TypeError(`\u5E94\u7528\u6A21\u5757\u65E0\u53EF\u6CE8\u518C\u5BFC\u51FA: ${filePath}`);
2921
+ }
2922
+ async function applyAppModuleDefinition(app, definition) {
2923
+ const moduleLogger = createAppModuleLogger(definition);
2924
+ if (definition.setup) {
2925
+ await definition.setup(app, moduleLogger);
2926
+ }
2927
+ if (definition.commands) {
2928
+ app.commands(definition.commands);
2929
+ }
2930
+ }
2931
+ function createAppModuleLogger(definition) {
2932
+ if (definition.log === true && !definition.name?.trim()) {
2933
+ throw new TypeError("\u5E94\u7528\u6A21\u5757\u542F\u7528\u65E5\u5FD7\u65F6\u5FC5\u987B\u63D0\u4F9B name");
2934
+ }
2935
+ const scopedLogger = logger;
2936
+ if (definition.log === false) {
2937
+ scopedLogger.setLevel("silent");
2938
+ }
2939
+ return scopedLogger;
2940
+ }
2941
+ function isAppModuleDefinition(value) {
2942
+ return typeof value === "object" && value !== null && "__plumeAppModule" in value && value.__plumeAppModule === true;
2943
+ }
2944
+ function isCommandMap(value) {
2945
+ return typeof value === "object" && value !== null;
2946
+ }
2947
+ var RESERVED_APP_EXPORT_NAMES = /* @__PURE__ */ new Set(["default", "setup", "commands", "name", "log", "plugin"]);
2948
+ async function startTransport(transportServer, logger3) {
2949
+ if (!transportServer || !transportServer.isEnabled()) {
2950
+ logger3.warn("\u4F20\u8F93\u5165\u53E3\u5DF2\u7981\u7528");
2951
+ return null;
2952
+ }
2953
+ try {
2954
+ const address = await transportServer.start();
2955
+ return address;
2956
+ } catch (error) {
2957
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "EADDRINUSE") {
2958
+ throw new Error("\u4F20\u8F93\u7AEF\u53E3\u5DF2\u88AB\u5360\u7528\uFF0C\u8BF7\u4FEE\u6539 config/transport.json \u4E2D\u7684 port");
2959
+ }
2960
+ throw error;
2961
+ }
2962
+ }
2963
+ function isDirectExecution() {
2964
+ const entry = process.argv[1];
2965
+ if (!entry) {
2966
+ return false;
2967
+ }
2968
+ try {
2969
+ return fileURLToPath(import.meta.url) === entry;
2970
+ } catch {
2971
+ return false;
2972
+ }
2973
+ }
2974
+ if (isDirectExecution()) {
2975
+ start().catch((error) => {
2976
+ const message = error instanceof Error ? error.message : String(error);
2977
+ console.error(`[Plume] \u542F\u52A8\u5931\u8D25: ${message}`);
2978
+ process.exit(1);
2979
+ });
2980
+ }
2981
+ function resolveStartPlugins(plugins) {
2982
+ const resolvedPlugins = [...plugins ?? []];
2983
+ if (!resolvedPlugins.some((plugin) => plugin.name === "adapter-console")) {
2984
+ resolvedPlugins.push(createConsoleAdapter());
2985
+ }
2986
+ return resolvedPlugins;
2987
+ }
2988
+ function resolveTransportOptions(config, transport) {
2989
+ if (transport === false) {
2990
+ return {
2991
+ enableHttp: false,
2992
+ enableWebSocket: false
2993
+ };
2994
+ }
2995
+ return {
2996
+ host: config.get("transport.host", "127.0.0.1"),
2997
+ port: config.get("transport.port", 23333),
2998
+ path: config.get("transport.path", "/api"),
2999
+ wsPath: config.get("transport.wsPath", "/ws"),
3000
+ cors: config.get("transport.cors", true),
3001
+ enableHttp: config.get("transport.enableHttp", true),
3002
+ enableWebSocket: config.get("transport.enableWebSocket", true),
3003
+ maxBodySize: config.get("transport.maxBodySize", 1024 * 1024),
3004
+ ...transport
3005
+ };
3006
+ }
3007
+
3008
+ // src/start/app.ts
3009
+ var isStarted = false;
3010
+ async function bootstrap() {
3011
+ if (isStarted) {
3012
+ logger2.warn("\u5E94\u7528\u5DF2\u542F\u52A8\uFF0C\u8DF3\u8FC7\u91CD\u590D\u542F\u52A8");
3013
+ return;
3014
+ }
3015
+ isStarted = true;
3016
+ const currentDir = fileURLToPath2(new URL(".", import.meta.url));
3017
+ const isDev = import.meta.url.includes(".ts");
3018
+ const possibleEntries = isDev ? [
3019
+ join3(currentDir, "..", "index.ts"),
3020
+ join3(process.cwd(), "src", "main.ts"),
3021
+ join3(process.cwd(), "src", "index.ts")
3022
+ ] : [
3023
+ join3(currentDir, "..", "index.mjs"),
3024
+ join3(process.cwd(), "dist", "main.js"),
3025
+ join3(process.cwd(), "dist", "index.js")
3026
+ ];
3027
+ let entryPath = null;
3028
+ for (const entry of possibleEntries) {
3029
+ if (existsSync3(entry)) {
3030
+ entryPath = entry;
3031
+ break;
3032
+ }
3033
+ }
3034
+ if (!entryPath) {
3035
+ logger2.error("\u672A\u627E\u5230\u6709\u6548\u7684\u5165\u53E3\u6587\u4EF6");
3036
+ logger2.info("\u8BF7\u786E\u4FDD\u4EE5\u4E0B\u6587\u4EF6\u4E4B\u4E00\u5B58\u5728:");
3037
+ possibleEntries.forEach((p) => logger2.info(` - ${p}`));
3038
+ process.exit(1);
3039
+ }
3040
+ try {
3041
+ logger2.info(`\u6B63\u5728\u52A0\u8F7D\u5165\u53E3: ${entryPath}`);
3042
+ const module = await import(pathToFileURL2(entryPath).href);
3043
+ await runEntryModule(module);
3044
+ logger2.info("\u5E94\u7528\u542F\u52A8\u6210\u529F");
3045
+ } catch (error) {
3046
+ logger2.error("\u5E94\u7528\u542F\u52A8\u5931\u8D25:", error);
3047
+ process.exit(1);
3048
+ }
3049
+ }
3050
+ async function runEntryModule(module) {
3051
+ if (module.app instanceof App) {
3052
+ await start({ app: module.app });
3053
+ return;
3054
+ }
3055
+ if (module.default instanceof App) {
3056
+ await start({ app: module.default });
3057
+ return;
3058
+ }
3059
+ if (typeof module.createApp === "function") {
3060
+ const app = await module.createApp();
3061
+ if (!(app instanceof App)) {
3062
+ throw new TypeError("createApp \u5FC5\u987B\u8FD4\u56DE App \u5B9E\u4F8B");
3063
+ }
3064
+ await start({ app });
3065
+ return;
3066
+ }
3067
+ if (typeof module.start === "function") {
3068
+ await module.start();
3069
+ return;
3070
+ }
3071
+ if (typeof module.default === "function") {
3072
+ await module.default();
3073
+ return;
3074
+ }
3075
+ if (module.default && typeof module.default === "object" && "start" in module.default) {
3076
+ const defaultExport = module.default;
3077
+ if (typeof defaultExport.start === "function") {
3078
+ await defaultExport.start();
3079
+ return;
3080
+ }
3081
+ }
3082
+ logger2.warn("\u5165\u53E3\u6A21\u5757\u5DF2\u52A0\u8F7D\uFF0C\u4F46\u672A\u53D1\u73B0\u53EF\u6267\u884C\u7684\u542F\u52A8\u5BFC\u51FA");
3083
+ }
3084
+ bootstrap().catch((error) => {
3085
+ logger2.error("\u5F15\u5BFC\u7A0B\u5E8F\u9519\u8BEF:", error);
3086
+ process.exit(1);
3087
+ });