@ai-setting/roy-agent-core 1.5.16-test → 1.5.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env/index.js +20 -8
- package/dist/env/log-trace/index.js +1 -1
- package/dist/env/prompt/index.js +1 -1
- package/dist/env/workflow/engine/index.js +1 -1
- package/dist/env/workflow/index.js +2 -2
- package/dist/index.js +6 -7
- package/dist/shared/@ai-setting/{roy-agent-core-xq8hhqb8.js → roy-agent-core-4wjywp3c.js} +4 -2
- package/dist/shared/@ai-setting/roy-agent-core-8jxva565.js +19 -0
- package/dist/shared/@ai-setting/roy-agent-core-avq1x4t7.js +84 -0
- package/dist/shared/@ai-setting/{roy-agent-core-gq20wsgv.js → roy-agent-core-ffb9fq4v.js} +23 -2
- package/dist/shared/@ai-setting/{roy-agent-core-93zfb3r1.js → roy-agent-core-mrcxzpbg.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-rhmtwnw1.js → roy-agent-core-pw7cv1px.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-gbqcyegm.js → roy-agent-core-rccptwv0.js} +512 -673
- package/dist/shared/@ai-setting/{roy-agent-core-wrcy0h6z.js → roy-agent-core-ty94k28r.js} +1 -1
- package/package.json +1 -1
- package/dist/config/index.d.ts +0 -1250
- package/dist/env/agent/index.d.ts +0 -2279
- package/dist/env/commands/index.d.ts +0 -1131
- package/dist/env/debug/formatters/index.d.ts +0 -236
- package/dist/env/debug/index.d.ts +0 -1652
- package/dist/env/hook/index.d.ts +0 -279
- package/dist/env/index.d.ts +0 -3481
- package/dist/env/llm/index.d.ts +0 -1760
- package/dist/env/log-trace/index.d.ts +0 -1574
- package/dist/env/mcp/index.d.ts +0 -1331
- package/dist/env/mcp/tool/index.d.ts +0 -183
- package/dist/env/memory/built-in/index.d.ts +0 -232
- package/dist/env/memory/index.d.ts +0 -1799
- package/dist/env/memory/plugin/index.d.ts +0 -747
- package/dist/env/prompt/index.d.ts +0 -1164
- package/dist/env/session/index.d.ts +0 -1908
- package/dist/env/session/storage/index.d.ts +0 -564
- package/dist/env/skill/index.d.ts +0 -1266
- package/dist/env/skill/tool/index.d.ts +0 -193
- package/dist/env/task/delegate/index.d.ts +0 -1612
- package/dist/env/task/events/index.d.ts +0 -171
- package/dist/env/task/hooks/index.d.ts +0 -624
- package/dist/env/task/index.d.ts +0 -1553
- package/dist/env/task/plugins/index.d.ts +0 -466
- package/dist/env/task/storage/index.d.ts +0 -241
- package/dist/env/task/tools/index.d.ts +0 -1485
- package/dist/env/task/tools/operation/index.d.ts +0 -1484
- package/dist/env/tool/built-in/index.d.ts +0 -218
- package/dist/env/tool/index.d.ts +0 -1396
- package/dist/env/workflow/decorators/index.d.ts +0 -2161
- package/dist/env/workflow/engine/index.d.ts +0 -3453
- package/dist/env/workflow/index.d.ts +0 -3546
- package/dist/env/workflow/nodes/index.d.ts +0 -2092
- package/dist/env/workflow/service/index.d.ts +0 -227
- package/dist/env/workflow/storage/index.d.ts +0 -165
- package/dist/env/workflow/tools/index.d.ts +0 -416
- package/dist/env/workflow/types/index.d.ts +0 -2255
- package/dist/env/workflow/utils/index.d.ts +0 -2031
- package/dist/index.d.ts +0 -7858
|
@@ -426,8 +426,6 @@ __legacyDecorateClassTS([
|
|
|
426
426
|
TracedAs("env.handle_query", { recordParams: true, recordResult: true, log: true })
|
|
427
427
|
], BaseEnvironment.prototype, "handle_query", null);
|
|
428
428
|
// src/env/event-source/event-source-component.ts
|
|
429
|
-
import { spawn, exec } from "child_process";
|
|
430
|
-
import { promisify } from "util";
|
|
431
429
|
import { join } from "path";
|
|
432
430
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
433
431
|
init_logger();
|
|
@@ -449,11 +447,426 @@ var EVENT_SOURCE_CONFIG_REGISTRATION = {
|
|
|
449
447
|
]
|
|
450
448
|
};
|
|
451
449
|
|
|
452
|
-
// src/env/event-source/event-source-
|
|
453
|
-
|
|
454
|
-
function
|
|
455
|
-
|
|
450
|
+
// src/env/event-source/event-source-handlers.ts
|
|
451
|
+
import { spawn } from "child_process";
|
|
452
|
+
function matchEventType(eventType, patterns) {
|
|
453
|
+
for (const pattern of patterns) {
|
|
454
|
+
if (pattern === "*")
|
|
455
|
+
return true;
|
|
456
|
+
if (pattern.endsWith(".*")) {
|
|
457
|
+
const prefix = pattern.slice(0, -2);
|
|
458
|
+
if (eventType.startsWith(prefix + ".") || eventType === prefix)
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
if (eventType === pattern)
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
function formatEventMessage(sourceType, rawEvent, eventType) {
|
|
467
|
+
const event = rawEvent;
|
|
468
|
+
switch (sourceType) {
|
|
469
|
+
case "lark-cli": {
|
|
470
|
+
const larkEvent = event;
|
|
471
|
+
const larkInnerEvent = larkEvent.event;
|
|
472
|
+
const larkMessage = larkInnerEvent?.message || larkEvent.message;
|
|
473
|
+
const larkSender = larkInnerEvent?.sender || larkEvent.sender;
|
|
474
|
+
const senderId = larkSender?.sender_id;
|
|
475
|
+
if (larkMessage) {
|
|
476
|
+
const openId = senderId?.open_id || senderId?.user_id || senderId?.email || senderId?.union_id || "未知用户";
|
|
477
|
+
let content = "无消息内容";
|
|
478
|
+
if (larkMessage.content) {
|
|
479
|
+
try {
|
|
480
|
+
const contentObj = JSON.parse(larkMessage.content);
|
|
481
|
+
content = contentObj.text || contentObj.content || larkMessage.content;
|
|
482
|
+
} catch {
|
|
483
|
+
content = larkMessage.content;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return `[飞书消息] ${openId}: ${content}`;
|
|
487
|
+
}
|
|
488
|
+
return `[飞书事件] ${eventType || larkEvent.schema || "unknown"}`;
|
|
489
|
+
}
|
|
490
|
+
case "timer": {
|
|
491
|
+
const timerMessage = event.payload?.message || event.message || "tick";
|
|
492
|
+
return `[定时任务] ${timerMessage}`;
|
|
493
|
+
}
|
|
494
|
+
case "websocket":
|
|
495
|
+
return `[WebSocket] ${JSON.stringify(rawEvent).substring(0, 200)}`;
|
|
496
|
+
default:
|
|
497
|
+
return `[${sourceType}] ${JSON.stringify(rawEvent).substring(0, 200)}`;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function extractMetadata(sourceType, rawEvent, eventType) {
|
|
501
|
+
const event = rawEvent;
|
|
502
|
+
const metadata = {};
|
|
503
|
+
switch (sourceType) {
|
|
504
|
+
case "lark-cli": {
|
|
505
|
+
const larkInnerEvent = event.event;
|
|
506
|
+
const header = event.header;
|
|
507
|
+
const message = larkInnerEvent?.message || event.message;
|
|
508
|
+
const sender = larkInnerEvent?.sender || event.sender;
|
|
509
|
+
const senderId = sender?.sender_id;
|
|
510
|
+
if (header) {
|
|
511
|
+
metadata.eventType = header.event_type;
|
|
512
|
+
metadata.appId = header.app_id;
|
|
513
|
+
metadata.tenantKey = header.tenant_key;
|
|
514
|
+
}
|
|
515
|
+
if (message) {
|
|
516
|
+
metadata.chatId = message.chat_id;
|
|
517
|
+
metadata.chatType = message.chat_type;
|
|
518
|
+
metadata.messageId = message.message_id;
|
|
519
|
+
metadata.messageType = message.message_type;
|
|
520
|
+
}
|
|
521
|
+
if (senderId) {
|
|
522
|
+
metadata.senderId = senderId.open_id || senderId.user_id || senderId.union_id;
|
|
523
|
+
}
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
case "timer":
|
|
527
|
+
metadata.eventType = "timer.tick";
|
|
528
|
+
break;
|
|
529
|
+
case "websocket":
|
|
530
|
+
metadata.eventType = eventType;
|
|
531
|
+
break;
|
|
532
|
+
default:
|
|
533
|
+
metadata.eventType = eventType;
|
|
534
|
+
}
|
|
535
|
+
let recommendedAction;
|
|
536
|
+
if (sourceType === "lark-cli" && eventType === "im.message.receive_v1") {
|
|
537
|
+
recommendedAction = {
|
|
538
|
+
action: "处理飞书消息并回复",
|
|
539
|
+
replyTo: {
|
|
540
|
+
appId: metadata.appId,
|
|
541
|
+
chatId: metadata.chatId,
|
|
542
|
+
messageId: metadata.messageId
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
} else if (sourceType === "timer") {
|
|
546
|
+
recommendedAction = { action: "执行定时任务" };
|
|
547
|
+
} else if (sourceType === "websocket") {
|
|
548
|
+
recommendedAction = { action: "处理 WebSocket 消息" };
|
|
549
|
+
}
|
|
550
|
+
let replyChannel;
|
|
551
|
+
if (sourceType === "lark-cli") {
|
|
552
|
+
replyChannel = {
|
|
553
|
+
type: "lark-cli",
|
|
554
|
+
appId: metadata.appId,
|
|
555
|
+
chatId: metadata.chatId,
|
|
556
|
+
messageId: metadata.messageId
|
|
557
|
+
};
|
|
558
|
+
} else if (sourceType === "websocket") {
|
|
559
|
+
replyChannel = { type: "websocket" };
|
|
560
|
+
}
|
|
561
|
+
return { metadata, replyChannel, recommendedAction };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
class LarkCliInstance {
|
|
565
|
+
config;
|
|
566
|
+
status = "created";
|
|
567
|
+
child;
|
|
568
|
+
buffer = "";
|
|
569
|
+
eventHandler;
|
|
570
|
+
constructor(config) {
|
|
571
|
+
this.config = config;
|
|
572
|
+
}
|
|
573
|
+
async start() {
|
|
574
|
+
if (this.status === "running")
|
|
575
|
+
return;
|
|
576
|
+
const command = this.config.command || "lark-cli event +subscribe";
|
|
577
|
+
const isWindows = process.platform === "win32";
|
|
578
|
+
const shell = isWindows ? "cmd.exe" : "sh";
|
|
579
|
+
const shellArgs = isWindows ? ["/c", command] : ["-c", `exec ${command}`];
|
|
580
|
+
this.child = spawn(shell, shellArgs, {
|
|
581
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
582
|
+
detached: false,
|
|
583
|
+
windowsHide: true
|
|
584
|
+
});
|
|
585
|
+
this.child.stdout?.on("data", (data) => {
|
|
586
|
+
this.processStream(data.toString());
|
|
587
|
+
});
|
|
588
|
+
this.child.stderr?.on("data", (data) => {
|
|
589
|
+
const output = data.toString();
|
|
590
|
+
if (output.includes('"ok": false')) {
|
|
591
|
+
this.status = "error";
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
this.child.on("exit", (code) => {
|
|
595
|
+
if (code !== 0 && code !== null) {
|
|
596
|
+
this.status = "error";
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1000));
|
|
600
|
+
this.status = "running";
|
|
601
|
+
}
|
|
602
|
+
async stop() {
|
|
603
|
+
if (this.status === "stopped" || this.status === "created")
|
|
604
|
+
return;
|
|
605
|
+
this.child?.kill("SIGTERM");
|
|
606
|
+
this.child = undefined;
|
|
607
|
+
this.buffer = "";
|
|
608
|
+
this.status = "stopped";
|
|
609
|
+
}
|
|
610
|
+
getStatus() {
|
|
611
|
+
return this.status;
|
|
612
|
+
}
|
|
613
|
+
onEvent(handler) {
|
|
614
|
+
this.eventHandler = handler;
|
|
615
|
+
}
|
|
616
|
+
offEvent() {
|
|
617
|
+
this.eventHandler = undefined;
|
|
618
|
+
}
|
|
619
|
+
processStream(data) {
|
|
620
|
+
this.buffer += data;
|
|
621
|
+
const lines = this.buffer.split(`
|
|
622
|
+
`);
|
|
623
|
+
this.buffer = lines.pop() || "";
|
|
624
|
+
for (const line of lines) {
|
|
625
|
+
if (line.trim()) {
|
|
626
|
+
this.handleEvent(line);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
handleEvent(rawData) {
|
|
631
|
+
try {
|
|
632
|
+
let rawEvent;
|
|
633
|
+
try {
|
|
634
|
+
rawEvent = JSON.parse(rawData);
|
|
635
|
+
} catch {
|
|
636
|
+
rawEvent = { event: "message", data: { content: { type: "text", body: rawData } } };
|
|
637
|
+
}
|
|
638
|
+
const event = rawEvent;
|
|
639
|
+
let eventType;
|
|
640
|
+
if (event.header?.event_type) {
|
|
641
|
+
eventType = event.header.event_type;
|
|
642
|
+
} else if (event.schema) {
|
|
643
|
+
eventType = `lark.${event.schema}`;
|
|
644
|
+
} else if (event.type) {
|
|
645
|
+
eventType = event.type;
|
|
646
|
+
} else {
|
|
647
|
+
eventType = "unknown";
|
|
648
|
+
}
|
|
649
|
+
if (this.config.eventTypes?.length && !matchEventType(eventType, this.config.eventTypes)) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
const { metadata, replyChannel, recommendedAction } = extractMetadata("lark-cli", rawEvent, eventType);
|
|
653
|
+
const message = formatEventMessage("lark-cli", rawEvent, eventType);
|
|
654
|
+
const evt = {
|
|
655
|
+
sourceId: this.config.id,
|
|
656
|
+
type: eventType,
|
|
657
|
+
timestamp: Date.now(),
|
|
658
|
+
payload: {
|
|
659
|
+
sourceId: this.config.id,
|
|
660
|
+
sourceType: "lark-cli",
|
|
661
|
+
rawEvent,
|
|
662
|
+
message,
|
|
663
|
+
metadata,
|
|
664
|
+
replyChannel,
|
|
665
|
+
recommendedAction,
|
|
666
|
+
timestamp: Date.now()
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
this.eventHandler?.(evt);
|
|
670
|
+
} catch (error) {}
|
|
671
|
+
}
|
|
456
672
|
}
|
|
673
|
+
var larkCliHandler = {
|
|
674
|
+
type: "lark-cli",
|
|
675
|
+
validateConfig(config) {
|
|
676
|
+
const errors = [];
|
|
677
|
+
if (!config.command) {
|
|
678
|
+
errors.push("lark-cli command is required");
|
|
679
|
+
}
|
|
680
|
+
return errors;
|
|
681
|
+
},
|
|
682
|
+
createInstance(config) {
|
|
683
|
+
return new LarkCliInstance(config);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
class TimerInstance {
|
|
688
|
+
config;
|
|
689
|
+
status = "created";
|
|
690
|
+
timer;
|
|
691
|
+
eventHandler;
|
|
692
|
+
constructor(config) {
|
|
693
|
+
this.config = config;
|
|
694
|
+
}
|
|
695
|
+
async start() {
|
|
696
|
+
if (this.status === "running")
|
|
697
|
+
return;
|
|
698
|
+
const interval = this.config.interval || 60000;
|
|
699
|
+
const message = this.config.options?.message || `Timer event from ${this.config.name}`;
|
|
700
|
+
this.timer = setInterval(() => {
|
|
701
|
+
this.emitEvent(message);
|
|
702
|
+
}, interval);
|
|
703
|
+
this.emitEvent(message);
|
|
704
|
+
this.status = "running";
|
|
705
|
+
}
|
|
706
|
+
async stop() {
|
|
707
|
+
if (this.timer) {
|
|
708
|
+
clearInterval(this.timer);
|
|
709
|
+
this.timer = undefined;
|
|
710
|
+
}
|
|
711
|
+
this.status = "stopped";
|
|
712
|
+
}
|
|
713
|
+
getStatus() {
|
|
714
|
+
return this.status;
|
|
715
|
+
}
|
|
716
|
+
onEvent(handler) {
|
|
717
|
+
this.eventHandler = handler;
|
|
718
|
+
}
|
|
719
|
+
offEvent() {
|
|
720
|
+
this.eventHandler = undefined;
|
|
721
|
+
}
|
|
722
|
+
emitEvent(message) {
|
|
723
|
+
const rawEvent = {
|
|
724
|
+
type: "timer.tick",
|
|
725
|
+
payload: {
|
|
726
|
+
message,
|
|
727
|
+
timestamp: Date.now()
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
const { metadata, recommendedAction } = extractMetadata("timer", rawEvent, "timer.tick");
|
|
731
|
+
const evt = {
|
|
732
|
+
sourceId: this.config.id,
|
|
733
|
+
type: "timer.tick",
|
|
734
|
+
timestamp: Date.now(),
|
|
735
|
+
payload: {
|
|
736
|
+
sourceId: this.config.id,
|
|
737
|
+
sourceType: "timer",
|
|
738
|
+
rawEvent,
|
|
739
|
+
message: formatEventMessage("timer", rawEvent),
|
|
740
|
+
metadata,
|
|
741
|
+
recommendedAction,
|
|
742
|
+
timestamp: Date.now()
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
this.eventHandler?.(evt);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
var timerHandler = {
|
|
749
|
+
type: "timer",
|
|
750
|
+
validateConfig(config) {
|
|
751
|
+
const errors = [];
|
|
752
|
+
if (!config.interval || config.interval <= 0) {
|
|
753
|
+
errors.push("Timer interval must be a positive number");
|
|
754
|
+
}
|
|
755
|
+
return errors;
|
|
756
|
+
},
|
|
757
|
+
createInstance(config) {
|
|
758
|
+
return new TimerInstance(config);
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
class WebSocketInstance {
|
|
763
|
+
config;
|
|
764
|
+
status = "created";
|
|
765
|
+
ws;
|
|
766
|
+
buffer = "";
|
|
767
|
+
eventHandler;
|
|
768
|
+
constructor(config) {
|
|
769
|
+
this.config = config;
|
|
770
|
+
}
|
|
771
|
+
async start() {
|
|
772
|
+
if (this.status === "running")
|
|
773
|
+
return;
|
|
774
|
+
const url = this.config.url;
|
|
775
|
+
if (!url) {
|
|
776
|
+
throw new Error("WebSocket URL is required");
|
|
777
|
+
}
|
|
778
|
+
const { WebSocket } = await import("ws");
|
|
779
|
+
this.ws = new WebSocket(url, { headers: this.config.headers });
|
|
780
|
+
this.ws.on("open", () => {
|
|
781
|
+
this.status = "running";
|
|
782
|
+
});
|
|
783
|
+
this.ws.on("message", (data) => {
|
|
784
|
+
this.processStream(data.toString());
|
|
785
|
+
});
|
|
786
|
+
this.ws.on("error", () => {
|
|
787
|
+
this.status = "error";
|
|
788
|
+
});
|
|
789
|
+
this.ws.on("close", () => {
|
|
790
|
+
this.status = "stopped";
|
|
791
|
+
});
|
|
792
|
+
await new Promise((resolve2) => setTimeout(resolve2, 5000));
|
|
793
|
+
}
|
|
794
|
+
async stop() {
|
|
795
|
+
if (this.ws) {
|
|
796
|
+
this.ws.close();
|
|
797
|
+
this.ws = undefined;
|
|
798
|
+
}
|
|
799
|
+
this.buffer = "";
|
|
800
|
+
this.status = "stopped";
|
|
801
|
+
}
|
|
802
|
+
getStatus() {
|
|
803
|
+
return this.status;
|
|
804
|
+
}
|
|
805
|
+
onEvent(handler) {
|
|
806
|
+
this.eventHandler = handler;
|
|
807
|
+
}
|
|
808
|
+
offEvent() {
|
|
809
|
+
this.eventHandler = undefined;
|
|
810
|
+
}
|
|
811
|
+
processStream(data) {
|
|
812
|
+
this.buffer += data;
|
|
813
|
+
const lines = this.buffer.split(`
|
|
814
|
+
`);
|
|
815
|
+
this.buffer = lines.pop() || "";
|
|
816
|
+
for (const line of lines) {
|
|
817
|
+
if (line.trim()) {
|
|
818
|
+
this.handleEvent(line);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
handleEvent(rawData) {
|
|
823
|
+
try {
|
|
824
|
+
let rawEvent;
|
|
825
|
+
try {
|
|
826
|
+
rawEvent = JSON.parse(rawData);
|
|
827
|
+
} catch {
|
|
828
|
+
rawEvent = rawData;
|
|
829
|
+
}
|
|
830
|
+
const { metadata, replyChannel, recommendedAction } = extractMetadata("websocket", rawEvent);
|
|
831
|
+
const evt = {
|
|
832
|
+
sourceId: this.config.id,
|
|
833
|
+
type: "websocket.message",
|
|
834
|
+
timestamp: Date.now(),
|
|
835
|
+
payload: {
|
|
836
|
+
sourceId: this.config.id,
|
|
837
|
+
sourceType: "websocket",
|
|
838
|
+
rawEvent,
|
|
839
|
+
message: formatEventMessage("websocket", rawEvent),
|
|
840
|
+
metadata,
|
|
841
|
+
replyChannel,
|
|
842
|
+
recommendedAction,
|
|
843
|
+
timestamp: Date.now()
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
this.eventHandler?.(evt);
|
|
847
|
+
} catch (error) {}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
var websocketHandler = {
|
|
851
|
+
type: "websocket",
|
|
852
|
+
validateConfig(config) {
|
|
853
|
+
const errors = [];
|
|
854
|
+
if (!config.url) {
|
|
855
|
+
errors.push("WebSocket URL is required");
|
|
856
|
+
}
|
|
857
|
+
return errors;
|
|
858
|
+
},
|
|
859
|
+
createInstance(config) {
|
|
860
|
+
return new WebSocketInstance(config);
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
var builtInHandlers = [
|
|
864
|
+
larkCliHandler,
|
|
865
|
+
timerHandler,
|
|
866
|
+
websocketHandler
|
|
867
|
+
];
|
|
868
|
+
|
|
869
|
+
// src/env/event-source/event-source-component.ts
|
|
457
870
|
var logger2 = createLogger("event-source");
|
|
458
871
|
|
|
459
872
|
class EventSourceComponent extends BaseComponent {
|
|
@@ -461,15 +874,16 @@ class EventSourceComponent extends BaseComponent {
|
|
|
461
874
|
version = "1.0.0";
|
|
462
875
|
sources = new Map;
|
|
463
876
|
statuses = new Map;
|
|
464
|
-
processes = new Map;
|
|
465
877
|
handlers = new Map;
|
|
466
|
-
|
|
878
|
+
instances = new Map;
|
|
879
|
+
eventHandlers = new Map;
|
|
467
880
|
buffers = new Map;
|
|
468
881
|
configPath = "";
|
|
469
882
|
persistenceEnabled = true;
|
|
470
883
|
configComponent;
|
|
471
884
|
configWatcher;
|
|
472
885
|
async onInit() {
|
|
886
|
+
await this.executeInitHooks();
|
|
473
887
|
await this.loadConfig();
|
|
474
888
|
}
|
|
475
889
|
async init(options) {
|
|
@@ -487,6 +901,9 @@ class EventSourceComponent extends BaseComponent {
|
|
|
487
901
|
if (opts?.configComponent) {
|
|
488
902
|
this.configComponent = opts.configComponent;
|
|
489
903
|
await this.registerConfig(opts);
|
|
904
|
+
if (opts.registerBuiltInHandlers !== false) {
|
|
905
|
+
this.registerBuiltInHandlers();
|
|
906
|
+
}
|
|
490
907
|
} else {
|
|
491
908
|
throw new Error("ConfigComponent is required for EventSourceComponent initialization");
|
|
492
909
|
}
|
|
@@ -503,11 +920,43 @@ class EventSourceComponent extends BaseComponent {
|
|
|
503
920
|
}
|
|
504
921
|
this.sources.clear();
|
|
505
922
|
this.statuses.clear();
|
|
506
|
-
this.
|
|
923
|
+
this.instances.clear();
|
|
924
|
+
this.eventHandlers.clear();
|
|
507
925
|
this.handlers.clear();
|
|
508
|
-
this.timers.clear();
|
|
509
926
|
this.buffers.clear();
|
|
510
927
|
}
|
|
928
|
+
registerBuiltInHandlers() {
|
|
929
|
+
for (const handler of builtInHandlers) {
|
|
930
|
+
if (!this.handlers.has(handler.type)) {
|
|
931
|
+
this.handlers.set(handler.type, handler);
|
|
932
|
+
logger2.debug(`Registered built-in handler: ${handler.type}`);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
async executeInitHooks() {
|
|
937
|
+
const { EventSourceInitHooks } = await import("./roy-agent-core-8jxva565.js");
|
|
938
|
+
await EventSourceInitHooks.execute(this);
|
|
939
|
+
}
|
|
940
|
+
registerHandler(handler) {
|
|
941
|
+
if (this.handlers.has(handler.type)) {
|
|
942
|
+
logger2.warn(`Handler already registered for type: ${handler.type}, overwriting`);
|
|
943
|
+
}
|
|
944
|
+
this.handlers.set(handler.type, handler);
|
|
945
|
+
logger2.debug(`Handler registered: ${handler.type}`);
|
|
946
|
+
}
|
|
947
|
+
unregisterHandler(type) {
|
|
948
|
+
const deleted = this.handlers.delete(type);
|
|
949
|
+
if (deleted) {
|
|
950
|
+
logger2.debug(`Handler unregistered: ${type}`);
|
|
951
|
+
}
|
|
952
|
+
return deleted;
|
|
953
|
+
}
|
|
954
|
+
getHandler(type) {
|
|
955
|
+
return this.handlers.get(type);
|
|
956
|
+
}
|
|
957
|
+
listHandlers() {
|
|
958
|
+
return Array.from(this.handlers.values());
|
|
959
|
+
}
|
|
511
960
|
async registerConfig(options) {
|
|
512
961
|
const configComponent = options.configComponent;
|
|
513
962
|
if (!configComponent)
|
|
@@ -560,11 +1009,7 @@ class EventSourceComponent extends BaseComponent {
|
|
|
560
1009
|
buildConfig() {
|
|
561
1010
|
const configComponent = this.configComponent;
|
|
562
1011
|
if (!configComponent) {
|
|
563
|
-
return {
|
|
564
|
-
enabled: true,
|
|
565
|
-
persistenceEnabled: true,
|
|
566
|
-
configPath: ""
|
|
567
|
-
};
|
|
1012
|
+
return { enabled: true, persistenceEnabled: true, configPath: "" };
|
|
568
1013
|
}
|
|
569
1014
|
return {
|
|
570
1015
|
enabled: configComponent.get("event-source.enabled") ?? true,
|
|
@@ -585,9 +1030,8 @@ class EventSourceComponent extends BaseComponent {
|
|
|
585
1030
|
return result;
|
|
586
1031
|
}
|
|
587
1032
|
registerConfigWatcher(configComponent) {
|
|
588
|
-
if (typeof configComponent.watch !== "function")
|
|
1033
|
+
if (typeof configComponent.watch !== "function")
|
|
589
1034
|
return;
|
|
590
|
-
}
|
|
591
1035
|
this.configWatcher = configComponent.watch("event-source.*", (event) => {
|
|
592
1036
|
this.onConfigChange(event);
|
|
593
1037
|
});
|
|
@@ -602,6 +1046,14 @@ class EventSourceComponent extends BaseComponent {
|
|
|
602
1046
|
if (this.sources.has(config.id)) {
|
|
603
1047
|
throw new Error(`EventSource already exists: ${config.id}`);
|
|
604
1048
|
}
|
|
1049
|
+
const handler = this.handlers.get(config.type);
|
|
1050
|
+
if (!handler) {
|
|
1051
|
+
throw new Error(`No handler registered for event source type: ${config.type}`);
|
|
1052
|
+
}
|
|
1053
|
+
const errors = handler.validateConfig(config);
|
|
1054
|
+
if (errors.length > 0) {
|
|
1055
|
+
throw new Error(`Invalid config: ${errors.join(", ")}`);
|
|
1056
|
+
}
|
|
605
1057
|
this.sources.set(config.id, config);
|
|
606
1058
|
this.statuses.set(config.id, "created");
|
|
607
1059
|
logger2.debug(`EventSource registered: ${config.id} (${config.type})`);
|
|
@@ -617,7 +1069,8 @@ class EventSourceComponent extends BaseComponent {
|
|
|
617
1069
|
const existed = this.sources.has(id);
|
|
618
1070
|
this.sources.delete(id);
|
|
619
1071
|
this.statuses.delete(id);
|
|
620
|
-
this.
|
|
1072
|
+
this.instances.delete(id);
|
|
1073
|
+
this.eventHandlers.delete(id);
|
|
621
1074
|
this.buffers.delete(id);
|
|
622
1075
|
if (existed) {
|
|
623
1076
|
logger2.debug(`EventSource unregistered: ${id}`);
|
|
@@ -633,17 +1086,25 @@ class EventSourceComponent extends BaseComponent {
|
|
|
633
1086
|
list() {
|
|
634
1087
|
return Array.from(this.sources.values());
|
|
635
1088
|
}
|
|
636
|
-
|
|
1089
|
+
getStatus(id) {
|
|
637
1090
|
return this.statuses.get(id);
|
|
638
1091
|
}
|
|
639
|
-
|
|
640
|
-
return this.
|
|
1092
|
+
getEventSourceStatus(id) {
|
|
1093
|
+
return this.getStatus(id);
|
|
641
1094
|
}
|
|
642
1095
|
onEvent(id, handler) {
|
|
643
|
-
this.
|
|
1096
|
+
this.eventHandlers.set(id, handler);
|
|
1097
|
+
const instance = this.instances.get(id);
|
|
1098
|
+
if (instance) {
|
|
1099
|
+
instance.onEvent(handler);
|
|
1100
|
+
}
|
|
644
1101
|
}
|
|
645
1102
|
offEvent(id) {
|
|
646
|
-
this.
|
|
1103
|
+
this.eventHandlers.delete(id);
|
|
1104
|
+
const instance = this.instances.get(id);
|
|
1105
|
+
if (instance) {
|
|
1106
|
+
instance.offEvent();
|
|
1107
|
+
}
|
|
647
1108
|
}
|
|
648
1109
|
async startSource(id) {
|
|
649
1110
|
const config = this.sources.get(id);
|
|
@@ -656,19 +1117,17 @@ class EventSourceComponent extends BaseComponent {
|
|
|
656
1117
|
}
|
|
657
1118
|
this.statuses.set(id, "starting");
|
|
658
1119
|
try {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
break;
|
|
663
|
-
case "timer":
|
|
664
|
-
await this.startTimer(id, config);
|
|
665
|
-
break;
|
|
666
|
-
case "websocket":
|
|
667
|
-
await this.startWebSocket(id, config);
|
|
668
|
-
break;
|
|
669
|
-
default:
|
|
670
|
-
throw new Error(`Unsupported event source type: ${config.type}`);
|
|
1120
|
+
const handler = this.handlers.get(config.type);
|
|
1121
|
+
if (!handler) {
|
|
1122
|
+
throw new Error(`No handler registered for event source type: ${config.type}`);
|
|
671
1123
|
}
|
|
1124
|
+
const instance = handler.createInstance(config);
|
|
1125
|
+
this.instances.set(id, instance);
|
|
1126
|
+
const eventHandler = this.eventHandlers.get(id);
|
|
1127
|
+
if (eventHandler) {
|
|
1128
|
+
instance.onEvent(eventHandler);
|
|
1129
|
+
}
|
|
1130
|
+
await instance.start();
|
|
672
1131
|
this.statuses.set(id, "running");
|
|
673
1132
|
this.getEnv()?.pushEnvEvent({
|
|
674
1133
|
type: "event-source.started",
|
|
@@ -689,550 +1148,23 @@ class EventSourceComponent extends BaseComponent {
|
|
|
689
1148
|
return;
|
|
690
1149
|
}
|
|
691
1150
|
this.statuses.set(id, "stopping");
|
|
692
|
-
const processInfo = this.processes.get(id);
|
|
693
|
-
const child = processInfo?.child;
|
|
694
|
-
logger2.info(`[EventSource ${id}] process info: ${processInfo ? `pid=${processInfo.child.pid}` : "none"}`);
|
|
695
|
-
if (processInfo && child) {
|
|
696
|
-
child.removeAllListeners("exit");
|
|
697
|
-
child.removeAllListeners("error");
|
|
698
|
-
child.stdout?.removeAllListeners("data");
|
|
699
|
-
child.stderr?.removeAllListeners("data");
|
|
700
|
-
const killProcess = (pid, signal) => {
|
|
701
|
-
try {
|
|
702
|
-
process.kill(pid, signal);
|
|
703
|
-
return true;
|
|
704
|
-
} catch (e) {
|
|
705
|
-
return false;
|
|
706
|
-
}
|
|
707
|
-
};
|
|
708
|
-
const pidsToKill = processInfo.pids.length > 0 ? processInfo.pids : processInfo.child.pid ? [processInfo.child.pid] : [];
|
|
709
|
-
logger2.info(`[EventSource ${id}] sending SIGTERM to ${pidsToKill.length} processes: ${pidsToKill.join(", ")}`);
|
|
710
|
-
for (const pid of pidsToKill) {
|
|
711
|
-
killProcess(pid, "SIGTERM");
|
|
712
|
-
}
|
|
713
|
-
await new Promise((resolve2) => {
|
|
714
|
-
const timeout = setTimeout(() => {
|
|
715
|
-
logger2.warn(`[EventSource ${id}] SIGTERM timeout, sending SIGKILL to ${pidsToKill.length} processes`);
|
|
716
|
-
for (const pid of pidsToKill) {
|
|
717
|
-
killProcess(pid, "SIGKILL");
|
|
718
|
-
}
|
|
719
|
-
resolve2();
|
|
720
|
-
}, 3000);
|
|
721
|
-
try {
|
|
722
|
-
const onceResult = child.once?.("exit", () => {
|
|
723
|
-
clearTimeout(timeout);
|
|
724
|
-
for (const pid of pidsToKill) {
|
|
725
|
-
if (pid !== processInfo.child.pid) {
|
|
726
|
-
killProcess(pid, "SIGKILL");
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
resolve2();
|
|
730
|
-
});
|
|
731
|
-
if (!onceResult || typeof onceResult.once !== "function") {
|
|
732
|
-
child.on("exit", () => {
|
|
733
|
-
clearTimeout(timeout);
|
|
734
|
-
for (const pid of pidsToKill) {
|
|
735
|
-
if (pid !== processInfo.child.pid) {
|
|
736
|
-
killProcess(pid, "SIGKILL");
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
resolve2();
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
} catch (e) {
|
|
743
|
-
logger2.warn(`[EventSource ${id}] failed to register exit listener: ${e}`);
|
|
744
|
-
}
|
|
745
|
-
});
|
|
746
|
-
this.processes.delete(id);
|
|
747
|
-
} else if (child) {
|
|
748
|
-
child.removeAllListeners("exit");
|
|
749
|
-
child.removeAllListeners("error");
|
|
750
|
-
child.stdout?.removeAllListeners("data");
|
|
751
|
-
child.stderr?.removeAllListeners("data");
|
|
752
|
-
const pid = child.pid;
|
|
753
|
-
const pgid = child.pgid;
|
|
754
|
-
try {
|
|
755
|
-
if (pgid && pgid > 0 && pgid !== pid) {
|
|
756
|
-
logger2.info(`[EventSource ${id}] killing process group ${pgid}`);
|
|
757
|
-
try {
|
|
758
|
-
process.kill(-pgid, "SIGTERM");
|
|
759
|
-
} catch (e) {
|
|
760
|
-
logger2.warn(`[EventSource ${id}] failed to kill process group: ${e}`);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
logger2.info(`[EventSource ${id}] killing main process ${pid}`);
|
|
764
|
-
child.kill("SIGTERM");
|
|
765
|
-
await new Promise((resolve2) => {
|
|
766
|
-
const timeout = setTimeout(() => {
|
|
767
|
-
logger2.warn(`[EventSource ${id}] SIGTERM timeout, sending SIGKILL`);
|
|
768
|
-
if (pgid && pgid > 0 && pgid !== pid) {
|
|
769
|
-
try {
|
|
770
|
-
process.kill(-pgid, "SIGKILL");
|
|
771
|
-
} catch (e) {
|
|
772
|
-
logger2.warn(`[EventSource ${id}] failed to kill process group with SIGKILL: ${e}`);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
child.kill("SIGKILL");
|
|
776
|
-
resolve2();
|
|
777
|
-
}, 3000);
|
|
778
|
-
child.once("exit", () => {
|
|
779
|
-
clearTimeout(timeout);
|
|
780
|
-
resolve2();
|
|
781
|
-
});
|
|
782
|
-
});
|
|
783
|
-
} catch (error) {
|
|
784
|
-
logger2.error(`[EventSource ${id}] Failed to kill process:`, error);
|
|
785
|
-
try {
|
|
786
|
-
child.kill("SIGKILL");
|
|
787
|
-
} catch {}
|
|
788
|
-
}
|
|
789
|
-
this.processes.delete(id);
|
|
790
|
-
} else {
|
|
791
|
-
logger2.warn(`[EventSource ${id}] no child process found in processes map`);
|
|
792
|
-
}
|
|
793
|
-
const timer = this.timers.get(id);
|
|
794
|
-
if (timer) {
|
|
795
|
-
clearInterval(timer);
|
|
796
|
-
this.timers.delete(id);
|
|
797
|
-
}
|
|
798
|
-
this.buffers.delete(id);
|
|
799
|
-
const config = this.sources.get(id);
|
|
800
|
-
this.statuses.set(id, "stopped");
|
|
801
|
-
this.getEnv()?.pushEnvEvent({
|
|
802
|
-
type: "event-source.stopped",
|
|
803
|
-
payload: { sourceId: id, sourceType: config?.type }
|
|
804
|
-
});
|
|
805
|
-
logger2.info(`EventSource stopped: ${id}`);
|
|
806
|
-
}
|
|
807
|
-
async findRelatedProcesses(pattern) {
|
|
808
|
-
const pids = [];
|
|
809
|
-
const platform = process.platform;
|
|
810
1151
|
try {
|
|
811
|
-
const
|
|
812
|
-
if (
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const { stdout } = await exec2(`powershell -Command "${psCommand}"`);
|
|
816
|
-
const matches = stdout.replace(/\r\n/g, `
|
|
817
|
-
`).match(/\d+/g);
|
|
818
|
-
if (matches) {
|
|
819
|
-
for (const match of matches) {
|
|
820
|
-
const pid = parseInt(match, 10);
|
|
821
|
-
if (!isNaN(pid) && pid > 0) {
|
|
822
|
-
pids.push(pid);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
} else {
|
|
827
|
-
const { stdout } = await exec2(`pgrep -f "${pattern.replace(/"/g, "\\\"")}"`);
|
|
828
|
-
const lines = stdout.trim().split(`
|
|
829
|
-
`);
|
|
830
|
-
for (const line of lines) {
|
|
831
|
-
const trimmed = line.trim();
|
|
832
|
-
if (trimmed) {
|
|
833
|
-
const pid = parseInt(trimmed, 10);
|
|
834
|
-
if (!isNaN(pid) && pid > 0) {
|
|
835
|
-
pids.push(pid);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
} catch (error) {
|
|
841
|
-
logger2.debug(`[EventSource] findRelatedProcesses: no processes found for pattern "${pattern}"`);
|
|
842
|
-
}
|
|
843
|
-
return pids;
|
|
844
|
-
}
|
|
845
|
-
async trackRelatedProcesses(id) {
|
|
846
|
-
const processInfo = this.processes.get(id);
|
|
847
|
-
if (!processInfo)
|
|
848
|
-
return;
|
|
849
|
-
const command = processInfo?.command || "";
|
|
850
|
-
const commandParts = command.split(" ");
|
|
851
|
-
const baseCommand = commandParts[0];
|
|
852
|
-
const eventKeyword = "event";
|
|
853
|
-
let relatedPids = await this.findRelatedProcesses(baseCommand);
|
|
854
|
-
if (relatedPids.length < 2) {
|
|
855
|
-
const morePids = await this.findRelatedProcesses(eventKeyword);
|
|
856
|
-
for (const pid of morePids) {
|
|
857
|
-
if (!relatedPids.includes(pid)) {
|
|
858
|
-
relatedPids.push(pid);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
const ownPid = process.pid;
|
|
863
|
-
relatedPids = relatedPids.filter((pid) => pid !== ownPid);
|
|
864
|
-
for (const pid of relatedPids) {
|
|
865
|
-
if (!processInfo.pids.includes(pid)) {
|
|
866
|
-
processInfo.pids.push(pid);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
const mainPid = processInfo.child.pid;
|
|
870
|
-
if (mainPid && !processInfo.pids.includes(mainPid)) {
|
|
871
|
-
processInfo.pids.unshift(mainPid);
|
|
872
|
-
}
|
|
873
|
-
logger2.info(`[EventSource ${id}] tracked ${processInfo.pids.length} related processes: ${processInfo.pids.join(", ")}`);
|
|
874
|
-
}
|
|
875
|
-
startLarkCli(id, config) {
|
|
876
|
-
return new Promise((resolve2, reject) => {
|
|
877
|
-
const command = config.command || "lark-cli event +subscribe";
|
|
878
|
-
logger2.info(`Starting lark-cli event source: ${id}`);
|
|
879
|
-
logger2.debug(`Executing command: ${command}`);
|
|
880
|
-
const isWindows = process.platform === "win32";
|
|
881
|
-
const shell = isWindows ? "cmd.exe" : "sh";
|
|
882
|
-
const shellArgs = isWindows ? ["/c", command] : ["-c", `exec ${command}`];
|
|
883
|
-
const child = spawn(shell, shellArgs, {
|
|
884
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
885
|
-
detached: false,
|
|
886
|
-
windowsHide: true
|
|
887
|
-
});
|
|
888
|
-
this.processes.set(id, {
|
|
889
|
-
child,
|
|
890
|
-
pgid: child.pgid,
|
|
891
|
-
pids: [],
|
|
892
|
-
command
|
|
893
|
-
});
|
|
894
|
-
let startupTimeout = null;
|
|
895
|
-
let settled = false;
|
|
896
|
-
let startupConfirmed = false;
|
|
897
|
-
const handleStartupSuccess = async () => {
|
|
898
|
-
if (settled)
|
|
899
|
-
return;
|
|
900
|
-
settled = true;
|
|
901
|
-
startupConfirmed = true;
|
|
902
|
-
clearTimeout(startupTimeout);
|
|
903
|
-
this.statuses.set(id, "running");
|
|
904
|
-
logger2.info(`[EventSource ${id}] confirmed running`);
|
|
905
|
-
try {
|
|
906
|
-
await this.trackRelatedProcesses(id);
|
|
907
|
-
} catch (e) {
|
|
908
|
-
logger2.warn(`[EventSource ${id}] failed to track related processes: ${e}`);
|
|
909
|
-
}
|
|
910
|
-
resolve2();
|
|
911
|
-
};
|
|
912
|
-
startupTimeout = setTimeout(() => {
|
|
913
|
-
if (!settled && !startupConfirmed) {
|
|
914
|
-
handleStartupSuccess();
|
|
915
|
-
}
|
|
916
|
-
}, 1000);
|
|
917
|
-
child.stdout?.on("data", (data) => {
|
|
918
|
-
const output = data.toString();
|
|
919
|
-
this.processStream(id, output);
|
|
920
|
-
if (output.includes('"ok": true') || output.includes('"ok":true')) {
|
|
921
|
-
if (!settled) {
|
|
922
|
-
handleStartupSuccess();
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
});
|
|
926
|
-
child.stderr?.on("data", (data) => {
|
|
927
|
-
const output = data.toString();
|
|
928
|
-
if (output.includes('"ok": false') || output.includes('"ok":false')) {
|
|
929
|
-
if (!settled) {
|
|
930
|
-
settled = true;
|
|
931
|
-
clearTimeout(startupTimeout);
|
|
932
|
-
this.statuses.set(id, "error");
|
|
933
|
-
child.kill();
|
|
934
|
-
try {
|
|
935
|
-
const errorObj = JSON.parse(output);
|
|
936
|
-
reject(new Error(errorObj.error?.message || `lark-cli error: ${output}`));
|
|
937
|
-
} catch {
|
|
938
|
-
reject(new Error(`lark-cli error: ${output}`));
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
});
|
|
943
|
-
child.on("exit", (code) => {
|
|
944
|
-
if (!settled) {
|
|
945
|
-
if (code !== 0 && code !== null) {
|
|
946
|
-
settled = true;
|
|
947
|
-
clearTimeout(startupTimeout);
|
|
948
|
-
logger2.warn(`[EventSource ${id}] exited with code ${code}`);
|
|
949
|
-
this.statuses.set(id, "error");
|
|
950
|
-
reject(new Error(`lark-cli exited with code ${code}`));
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
});
|
|
954
|
-
child.on("error", (error) => {
|
|
955
|
-
if (!settled) {
|
|
956
|
-
settled = true;
|
|
957
|
-
clearTimeout(startupTimeout);
|
|
958
|
-
logger2.error(`[EventSource ${id}] process error:`, error);
|
|
959
|
-
this.statuses.set(id, "error");
|
|
960
|
-
reject(error);
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
async startTimer(id, config) {
|
|
966
|
-
const interval = config.interval || 60000;
|
|
967
|
-
logger2.info(`Starting timer event source: ${id}, interval: ${interval}ms`);
|
|
968
|
-
const timer = setInterval(() => {
|
|
969
|
-
const message = config.options?.message || `Timer event from ${config.name}`;
|
|
970
|
-
this.handleEvent(id, JSON.stringify({
|
|
971
|
-
type: "timer.tick",
|
|
972
|
-
payload: {
|
|
973
|
-
message,
|
|
974
|
-
timestamp: Date.now()
|
|
975
|
-
}
|
|
976
|
-
}));
|
|
977
|
-
}, interval);
|
|
978
|
-
this.timers.set(id, timer);
|
|
979
|
-
this.handleEvent(id, JSON.stringify({
|
|
980
|
-
type: "timer.tick",
|
|
981
|
-
payload: {
|
|
982
|
-
message: config.options?.message || `Timer event from ${config.name}`,
|
|
983
|
-
timestamp: Date.now()
|
|
984
|
-
}
|
|
985
|
-
}));
|
|
986
|
-
}
|
|
987
|
-
async startWebSocket(id, config) {
|
|
988
|
-
const url = config.url;
|
|
989
|
-
if (!url) {
|
|
990
|
-
throw new Error("WebSocket URL is required");
|
|
991
|
-
}
|
|
992
|
-
logger2.info(`Starting WebSocket event source: ${id}, url: ${url}`);
|
|
993
|
-
try {
|
|
994
|
-
const { WebSocket } = await import("ws");
|
|
995
|
-
const ws = new WebSocket(url, {
|
|
996
|
-
headers: config.headers
|
|
997
|
-
});
|
|
998
|
-
const wrapper = {
|
|
999
|
-
child: {
|
|
1000
|
-
kill: () => ws.close(),
|
|
1001
|
-
on: (event, cb) => {
|
|
1002
|
-
ws.on(event, cb);
|
|
1003
|
-
}
|
|
1004
|
-
},
|
|
1005
|
-
pids: []
|
|
1006
|
-
};
|
|
1007
|
-
this.processes.set(id, wrapper);
|
|
1008
|
-
ws.on("message", (data) => {
|
|
1009
|
-
this.processStream(id, data.toString());
|
|
1010
|
-
});
|
|
1011
|
-
ws.on("error", (error) => {
|
|
1012
|
-
logger2.error(`[EventSource ${id}] WebSocket error:`, error);
|
|
1013
|
-
});
|
|
1014
|
-
ws.on("close", () => {
|
|
1015
|
-
logger2.info(`[EventSource ${id}] WebSocket closed`);
|
|
1016
|
-
this.statuses.set(id, "stopped");
|
|
1017
|
-
});
|
|
1018
|
-
} catch (error) {
|
|
1019
|
-
throw new Error(`Failed to import ws module: ${error}`);
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
processStream(sourceId, data) {
|
|
1023
|
-
let buffer = this.buffers.get(sourceId) || "";
|
|
1024
|
-
buffer += data;
|
|
1025
|
-
const lines = buffer.split(`
|
|
1026
|
-
`);
|
|
1027
|
-
buffer = lines.pop() || "";
|
|
1028
|
-
this.buffers.set(sourceId, buffer);
|
|
1029
|
-
for (const line of lines) {
|
|
1030
|
-
if (line.trim()) {
|
|
1031
|
-
this.handleEvent(sourceId, line);
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
handleEvent(sourceId, rawData) {
|
|
1036
|
-
const config = this.sources.get(sourceId);
|
|
1037
|
-
if (!config)
|
|
1038
|
-
return;
|
|
1039
|
-
try {
|
|
1040
|
-
const rawEvent = JSON.parse(rawData);
|
|
1041
|
-
let eventType;
|
|
1042
|
-
if (rawEvent.header?.event_type) {
|
|
1043
|
-
eventType = rawEvent.header.event_type;
|
|
1044
|
-
} else if (rawEvent.schema) {
|
|
1045
|
-
eventType = `lark.${rawEvent.schema}`;
|
|
1046
|
-
} else if (rawEvent.type) {
|
|
1047
|
-
eventType = rawEvent.type;
|
|
1048
|
-
} else {
|
|
1049
|
-
eventType = "unknown";
|
|
1050
|
-
}
|
|
1051
|
-
if (config.eventTypes?.length && !this.matchEventType(eventType, config.eventTypes)) {
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
const { metadata, replyChannel, recommendedAction } = this.extractMetadata(config.type, rawEvent, eventType);
|
|
1055
|
-
const message = this.formatEventMessage(config.type, rawEvent, eventType);
|
|
1056
|
-
const event = {
|
|
1057
|
-
sourceId,
|
|
1058
|
-
type: eventType,
|
|
1059
|
-
timestamp: Date.now(),
|
|
1060
|
-
payload: {
|
|
1061
|
-
sourceId,
|
|
1062
|
-
sourceType: config.type,
|
|
1063
|
-
rawEvent,
|
|
1064
|
-
message,
|
|
1065
|
-
metadata,
|
|
1066
|
-
replyChannel,
|
|
1067
|
-
recommendedAction,
|
|
1068
|
-
timestamp: Date.now()
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
const handler = this.handlers.get(sourceId);
|
|
1072
|
-
if (handler) {
|
|
1073
|
-
try {
|
|
1074
|
-
const result = handler(event);
|
|
1075
|
-
if (result instanceof Promise) {
|
|
1076
|
-
result.catch((error) => {
|
|
1077
|
-
logger2.error(`[EventSource ${sourceId}] Handler error:`, error);
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
} catch (error) {
|
|
1081
|
-
logger2.error(`[EventSource ${sourceId}] Handler error:`, error);
|
|
1082
|
-
}
|
|
1152
|
+
const instance = this.instances.get(id);
|
|
1153
|
+
if (instance) {
|
|
1154
|
+
await instance.stop();
|
|
1155
|
+
this.instances.delete(id);
|
|
1083
1156
|
}
|
|
1157
|
+
const config = this.sources.get(id);
|
|
1158
|
+
this.statuses.set(id, "stopped");
|
|
1084
1159
|
this.getEnv()?.pushEnvEvent({
|
|
1085
|
-
type:
|
|
1086
|
-
payload:
|
|
1160
|
+
type: "event-source.stopped",
|
|
1161
|
+
payload: { sourceId: id, sourceType: config?.type }
|
|
1087
1162
|
});
|
|
1163
|
+
logger2.info(`EventSource stopped: ${id}`);
|
|
1088
1164
|
} catch (error) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
if (config2) {
|
|
1093
|
-
const event = {
|
|
1094
|
-
sourceId,
|
|
1095
|
-
type: "raw",
|
|
1096
|
-
timestamp: Date.now(),
|
|
1097
|
-
payload: {
|
|
1098
|
-
sourceId,
|
|
1099
|
-
sourceType: config2.type,
|
|
1100
|
-
rawEvent: rawData,
|
|
1101
|
-
message: rawData,
|
|
1102
|
-
metadata: {},
|
|
1103
|
-
timestamp: Date.now()
|
|
1104
|
-
}
|
|
1105
|
-
};
|
|
1106
|
-
const handler = this.handlers.get(sourceId);
|
|
1107
|
-
if (handler) {
|
|
1108
|
-
handler(event);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
} else {
|
|
1112
|
-
logger2.error(`[EventSource ${sourceId}] Failed to handle event:`, error);
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
matchEventType(eventType, patterns) {
|
|
1117
|
-
for (const pattern of patterns) {
|
|
1118
|
-
if (pattern === "*")
|
|
1119
|
-
return true;
|
|
1120
|
-
if (pattern.endsWith(".*")) {
|
|
1121
|
-
const prefix = pattern.slice(0, -2);
|
|
1122
|
-
if (eventType.startsWith(prefix + ".") || eventType === prefix)
|
|
1123
|
-
return true;
|
|
1124
|
-
}
|
|
1125
|
-
if (eventType === pattern)
|
|
1126
|
-
return true;
|
|
1127
|
-
}
|
|
1128
|
-
return false;
|
|
1129
|
-
}
|
|
1130
|
-
extractMetadata(sourceType, rawEvent, eventType) {
|
|
1131
|
-
const event = rawEvent;
|
|
1132
|
-
const metadata = {};
|
|
1133
|
-
let replyChannel;
|
|
1134
|
-
let recommendedAction;
|
|
1135
|
-
switch (sourceType) {
|
|
1136
|
-
case "lark-cli":
|
|
1137
|
-
const larkInnerEvent = event.event;
|
|
1138
|
-
const header = event.header;
|
|
1139
|
-
const message = larkInnerEvent?.message || event.message;
|
|
1140
|
-
const sender = larkInnerEvent?.sender || event.sender;
|
|
1141
|
-
const senderId = sender?.sender_id;
|
|
1142
|
-
if (header) {
|
|
1143
|
-
metadata.eventType = header.event_type;
|
|
1144
|
-
metadata.appId = header.app_id;
|
|
1145
|
-
metadata.tenantKey = header.tenant_key;
|
|
1146
|
-
}
|
|
1147
|
-
if (message) {
|
|
1148
|
-
metadata.chatId = message.chat_id;
|
|
1149
|
-
metadata.chatType = message.chat_type;
|
|
1150
|
-
metadata.messageId = message.message_id;
|
|
1151
|
-
metadata.messageType = message.message_type;
|
|
1152
|
-
}
|
|
1153
|
-
if (senderId) {
|
|
1154
|
-
metadata.senderId = senderId.open_id || senderId.user_id || senderId.union_id;
|
|
1155
|
-
}
|
|
1156
|
-
if (eventType === "im.message.receive_v1") {
|
|
1157
|
-
replyChannel = {
|
|
1158
|
-
type: "lark-cli",
|
|
1159
|
-
appId: metadata.appId,
|
|
1160
|
-
chatId: metadata.chatId,
|
|
1161
|
-
messageId: metadata.messageId
|
|
1162
|
-
};
|
|
1163
|
-
recommendedAction = {
|
|
1164
|
-
action: "处理飞书消息并回复",
|
|
1165
|
-
replyTo: {
|
|
1166
|
-
appId: metadata.appId,
|
|
1167
|
-
chatId: metadata.chatId,
|
|
1168
|
-
messageId: metadata.messageId
|
|
1169
|
-
}
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
break;
|
|
1173
|
-
case "timer":
|
|
1174
|
-
metadata.eventType = "timer.tick";
|
|
1175
|
-
recommendedAction = {
|
|
1176
|
-
action: "执行定时任务"
|
|
1177
|
-
};
|
|
1178
|
-
break;
|
|
1179
|
-
case "websocket":
|
|
1180
|
-
metadata.eventType = eventType;
|
|
1181
|
-
replyChannel = {
|
|
1182
|
-
type: "websocket"
|
|
1183
|
-
};
|
|
1184
|
-
recommendedAction = {
|
|
1185
|
-
action: "处理 WebSocket 消息"
|
|
1186
|
-
};
|
|
1187
|
-
break;
|
|
1188
|
-
default:
|
|
1189
|
-
metadata.eventType = eventType;
|
|
1190
|
-
recommendedAction = {
|
|
1191
|
-
action: "处理事件"
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
return { metadata, replyChannel, recommendedAction };
|
|
1195
|
-
}
|
|
1196
|
-
formatEventMessage(sourceType, rawEvent, eventType) {
|
|
1197
|
-
const event = rawEvent;
|
|
1198
|
-
switch (sourceType) {
|
|
1199
|
-
case "lark-cli": {
|
|
1200
|
-
const larkEvent = event;
|
|
1201
|
-
const larkInnerEvent = larkEvent.event;
|
|
1202
|
-
const larkMessage = larkInnerEvent?.message || larkEvent.message;
|
|
1203
|
-
const larkSender = larkInnerEvent?.sender || larkEvent.sender;
|
|
1204
|
-
if (larkMessage) {
|
|
1205
|
-
const senderId = larkSender?.sender_id;
|
|
1206
|
-
const openId = senderId?.open_id || senderId?.user_id || senderId?.email || senderId?.union_id || "未知用户";
|
|
1207
|
-
let content = "无消息内容";
|
|
1208
|
-
if (larkMessage.content) {
|
|
1209
|
-
try {
|
|
1210
|
-
const contentObj = JSON.parse(larkMessage.content);
|
|
1211
|
-
content = contentObj.text || contentObj.content || larkMessage.content;
|
|
1212
|
-
} catch {
|
|
1213
|
-
content = larkMessage.content;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
return `[飞书消息] ${openId}: ${content}`;
|
|
1217
|
-
}
|
|
1218
|
-
const larkSenderRecord = larkEvent.sender;
|
|
1219
|
-
const senderIdRecord = larkSenderRecord?.sender_id;
|
|
1220
|
-
if (larkEvent.message_id || larkEvent.schema?.includes("message")) {
|
|
1221
|
-
const senderId = senderIdRecord?.open_id || senderIdRecord?.user_id || senderIdRecord?.email || senderIdRecord?.union_id || "未知用户";
|
|
1222
|
-
const content = larkEvent.text || larkEvent.content || JSON.stringify(larkEvent);
|
|
1223
|
-
return `[飞书消息] ${senderId}: ${content}`;
|
|
1224
|
-
}
|
|
1225
|
-
return `[飞书事件] ${eventType || larkEvent.schema || "unknown"}`;
|
|
1226
|
-
}
|
|
1227
|
-
case "timer": {
|
|
1228
|
-
const timerEvent = event;
|
|
1229
|
-
const timerMessage = timerEvent.payload?.message || timerEvent.message || "tick";
|
|
1230
|
-
return `[定时任务] ${timerMessage}`;
|
|
1231
|
-
}
|
|
1232
|
-
case "websocket":
|
|
1233
|
-
return `[WebSocket] ${JSON.stringify(rawEvent).substring(0, 200)}`;
|
|
1234
|
-
default:
|
|
1235
|
-
return `[${sourceType}] ${JSON.stringify(rawEvent).substring(0, 200)}`;
|
|
1165
|
+
this.statuses.set(id, "error");
|
|
1166
|
+
logger2.error(`EventSource failed to stop: ${id}`, error);
|
|
1167
|
+
throw error;
|
|
1236
1168
|
}
|
|
1237
1169
|
}
|
|
1238
1170
|
getConfigFilePath() {
|
|
@@ -1247,27 +1179,24 @@ class EventSourceComponent extends BaseComponent {
|
|
|
1247
1179
|
return join(dataDir, "event-sources.json");
|
|
1248
1180
|
}
|
|
1249
1181
|
async saveConfig() {
|
|
1250
|
-
if (!this.persistenceEnabled)
|
|
1182
|
+
if (!this.persistenceEnabled)
|
|
1251
1183
|
return;
|
|
1252
|
-
}
|
|
1253
1184
|
try {
|
|
1254
1185
|
const configPath = this.getConfigFilePath();
|
|
1255
1186
|
const config = {
|
|
1256
1187
|
version: "1.0.0",
|
|
1257
1188
|
sources: Array.from(this.sources.values())
|
|
1258
1189
|
};
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
logger2.debug(`Saved ${this.sources.size} event source configurations to ${configPath}`);
|
|
1190
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1191
|
+
logger2.debug(`Saved ${this.sources.size} event source configurations`);
|
|
1262
1192
|
} catch (error) {
|
|
1263
1193
|
logger2.error("Failed to save event source configurations:", error);
|
|
1264
1194
|
throw error;
|
|
1265
1195
|
}
|
|
1266
1196
|
}
|
|
1267
1197
|
async loadConfig() {
|
|
1268
|
-
if (!this.persistenceEnabled)
|
|
1198
|
+
if (!this.persistenceEnabled)
|
|
1269
1199
|
return;
|
|
1270
|
-
}
|
|
1271
1200
|
try {
|
|
1272
1201
|
const configPath = this.getConfigFilePath();
|
|
1273
1202
|
if (!existsSync2(configPath)) {
|
|
@@ -1288,100 +1217,10 @@ class EventSourceComponent extends BaseComponent {
|
|
|
1288
1217
|
loadedCount++;
|
|
1289
1218
|
}
|
|
1290
1219
|
}
|
|
1291
|
-
logger2.info(`Loaded ${loadedCount} event source configurations
|
|
1220
|
+
logger2.info(`Loaded ${loadedCount} event source configurations`);
|
|
1292
1221
|
} catch (error) {
|
|
1293
1222
|
logger2.error("Failed to load event source configurations:", error);
|
|
1294
1223
|
}
|
|
1295
1224
|
}
|
|
1296
1225
|
}
|
|
1297
|
-
|
|
1298
|
-
init_logger();
|
|
1299
|
-
var logger3 = createLogger("event-source-agent-handler");
|
|
1300
|
-
|
|
1301
|
-
class EventSourceAgentHandler {
|
|
1302
|
-
config;
|
|
1303
|
-
constructor(config = {}) {
|
|
1304
|
-
this.config = {
|
|
1305
|
-
enabled: config.enabled ?? true,
|
|
1306
|
-
prefix: config.prefix ?? "[事件源]",
|
|
1307
|
-
includeRawEvent: config.includeRawEvent ?? false,
|
|
1308
|
-
includeTimestamp: config.includeTimestamp ?? true
|
|
1309
|
-
};
|
|
1310
|
-
}
|
|
1311
|
-
createHandler() {
|
|
1312
|
-
return (event) => {
|
|
1313
|
-
if (!this.config.enabled) {
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
const message = this.formatEventForAgent(event);
|
|
1317
|
-
logger3.debug(`[AgentHandler] Formatted event: ${message.substring(0, 100)}`);
|
|
1318
|
-
return message;
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
formatEventForAgent(event) {
|
|
1322
|
-
const parts = [];
|
|
1323
|
-
parts.push(this.config.prefix);
|
|
1324
|
-
if (this.config.includeTimestamp) {
|
|
1325
|
-
const time = new Date(event.timestamp).toLocaleString("zh-CN");
|
|
1326
|
-
parts.push(`[${time}]`);
|
|
1327
|
-
}
|
|
1328
|
-
parts.push(`来源: ${event.payload.sourceType}`);
|
|
1329
|
-
parts.push(`类型: ${event.type}`);
|
|
1330
|
-
parts.push(`内容: ${event.payload.message}`);
|
|
1331
|
-
if (this.config.includeRawEvent) {
|
|
1332
|
-
const rawStr = typeof event.payload.rawEvent === "string" ? event.payload.rawEvent : JSON.stringify(event.payload.rawEvent);
|
|
1333
|
-
parts.push(`原始数据: ${rawStr}`);
|
|
1334
|
-
}
|
|
1335
|
-
return parts.join(" ");
|
|
1336
|
-
}
|
|
1337
|
-
updateConfig(config) {
|
|
1338
|
-
this.config = {
|
|
1339
|
-
...this.config,
|
|
1340
|
-
...config
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
getConfig() {
|
|
1344
|
-
return { ...this.config };
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
// src/env/event-source/types.ts
|
|
1348
|
-
function isValidEventSourceType(type) {
|
|
1349
|
-
return ["lark-cli", "websocket", "timer", "http-webhook", "file-watcher"].includes(type);
|
|
1350
|
-
}
|
|
1351
|
-
function getDefaultConfigForType(type) {
|
|
1352
|
-
switch (type) {
|
|
1353
|
-
case "timer":
|
|
1354
|
-
return { interval: 60000 };
|
|
1355
|
-
case "lark-cli":
|
|
1356
|
-
return { command: "lark-cli event +subscribe" };
|
|
1357
|
-
case "websocket":
|
|
1358
|
-
return {};
|
|
1359
|
-
default:
|
|
1360
|
-
return {};
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
function validateEventSourceConfig(config) {
|
|
1364
|
-
const errors = [];
|
|
1365
|
-
if (!config.id) {
|
|
1366
|
-
errors.push("EventSource ID is required");
|
|
1367
|
-
}
|
|
1368
|
-
if (!config.name) {
|
|
1369
|
-
errors.push("EventSource name is required");
|
|
1370
|
-
}
|
|
1371
|
-
if (!isValidEventSourceType(config.type)) {
|
|
1372
|
-
errors.push(`Invalid EventSource type: ${config.type}`);
|
|
1373
|
-
}
|
|
1374
|
-
if (config.type === "timer") {
|
|
1375
|
-
if (!config.interval || config.interval <= 0) {
|
|
1376
|
-
errors.push("Timer interval must be a positive number");
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
if (config.type === "lark-cli" && !config.command) {
|
|
1380
|
-
errors.push("lark-cli command is required");
|
|
1381
|
-
}
|
|
1382
|
-
if (config.type === "websocket" && !config.url) {
|
|
1383
|
-
errors.push("WebSocket URL is required");
|
|
1384
|
-
}
|
|
1385
|
-
return errors;
|
|
1386
|
-
}
|
|
1387
|
-
export { generateId, generateDescendingId, BaseEnvironment, EventSourceComponent, EventSourceAgentHandler, isValidEventSourceType, getDefaultConfigForType, validateEventSourceConfig };
|
|
1226
|
+
export { generateId, generateDescendingId, BaseEnvironment, larkCliHandler, timerHandler, websocketHandler, builtInHandlers, EventSourceComponent };
|