@ai-setting/roy-agent-core 1.5.17-beta.1 → 1.5.23
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/event-source/index.js +41 -0
- package/dist/env/index.js +24 -10
- 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 +35 -8
- package/dist/shared/@ai-setting/roy-agent-core-2grcjaad.js +0 -0
- 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-5ex3za0m.js +817 -0
- 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-j1sr5pk9.js +424 -0
- 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-wrcy0h6z.js → roy-agent-core-ty94k28r.js} +1 -1
- package/package.json +8 -29
- 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
- package/dist/shared/@ai-setting/roy-agent-core-gbqcyegm.js +0 -1387
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
import {
|
|
2
|
+
envKeyToConfigKey
|
|
3
|
+
} from "./roy-agent-core-qxhq8ven.js";
|
|
4
|
+
import {
|
|
5
|
+
BaseComponent
|
|
6
|
+
} from "./roy-agent-core-kkbwepqb.js";
|
|
7
|
+
import {
|
|
8
|
+
createLogger,
|
|
9
|
+
init_logger
|
|
10
|
+
} from "./roy-agent-core-44hnfb02.js";
|
|
11
|
+
import {
|
|
12
|
+
__require
|
|
13
|
+
} from "./roy-agent-core-fs0mn2jk.js";
|
|
14
|
+
|
|
15
|
+
// src/env/event-source/event-source-handlers.ts
|
|
16
|
+
import { spawn } from "child_process";
|
|
17
|
+
function matchEventType(eventType, patterns) {
|
|
18
|
+
for (const pattern of patterns) {
|
|
19
|
+
if (pattern === "*")
|
|
20
|
+
return true;
|
|
21
|
+
if (pattern.endsWith(".*")) {
|
|
22
|
+
const prefix = pattern.slice(0, -2);
|
|
23
|
+
if (eventType.startsWith(prefix + ".") || eventType === prefix)
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (eventType === pattern)
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function formatEventMessage(sourceType, rawEvent, eventType) {
|
|
32
|
+
const event = rawEvent;
|
|
33
|
+
switch (sourceType) {
|
|
34
|
+
case "lark-cli": {
|
|
35
|
+
const larkEvent = event;
|
|
36
|
+
const larkInnerEvent = larkEvent.event;
|
|
37
|
+
const larkMessage = larkInnerEvent?.message || larkEvent.message;
|
|
38
|
+
const larkSender = larkInnerEvent?.sender || larkEvent.sender;
|
|
39
|
+
const senderId = larkSender?.sender_id;
|
|
40
|
+
if (larkMessage) {
|
|
41
|
+
const openId = senderId?.open_id || senderId?.user_id || senderId?.email || senderId?.union_id || "未知用户";
|
|
42
|
+
let content = "无消息内容";
|
|
43
|
+
if (larkMessage.content) {
|
|
44
|
+
try {
|
|
45
|
+
const contentObj = JSON.parse(larkMessage.content);
|
|
46
|
+
content = contentObj.text || contentObj.content || larkMessage.content;
|
|
47
|
+
} catch {
|
|
48
|
+
content = larkMessage.content;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return `[飞书消息] ${openId}: ${content}`;
|
|
52
|
+
}
|
|
53
|
+
return `[飞书事件] ${eventType || larkEvent.schema || "unknown"}`;
|
|
54
|
+
}
|
|
55
|
+
case "timer": {
|
|
56
|
+
const timerMessage = event.payload?.message || event.message || "tick";
|
|
57
|
+
return `[定时任务] ${timerMessage}`;
|
|
58
|
+
}
|
|
59
|
+
case "websocket":
|
|
60
|
+
return `[WebSocket] ${JSON.stringify(rawEvent).substring(0, 200)}`;
|
|
61
|
+
default:
|
|
62
|
+
return `[${sourceType}] ${JSON.stringify(rawEvent).substring(0, 200)}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function extractMetadata(sourceType, rawEvent, eventType) {
|
|
66
|
+
const event = rawEvent;
|
|
67
|
+
const metadata = {};
|
|
68
|
+
switch (sourceType) {
|
|
69
|
+
case "lark-cli": {
|
|
70
|
+
const larkInnerEvent = event.event;
|
|
71
|
+
const header = event.header;
|
|
72
|
+
const message = larkInnerEvent?.message || event.message;
|
|
73
|
+
const sender = larkInnerEvent?.sender || event.sender;
|
|
74
|
+
const senderId = sender?.sender_id;
|
|
75
|
+
if (header) {
|
|
76
|
+
metadata.eventType = header.event_type;
|
|
77
|
+
metadata.appId = header.app_id;
|
|
78
|
+
metadata.tenantKey = header.tenant_key;
|
|
79
|
+
}
|
|
80
|
+
if (message) {
|
|
81
|
+
metadata.chatId = message.chat_id;
|
|
82
|
+
metadata.chatType = message.chat_type;
|
|
83
|
+
metadata.messageId = message.message_id;
|
|
84
|
+
metadata.messageType = message.message_type;
|
|
85
|
+
}
|
|
86
|
+
if (senderId) {
|
|
87
|
+
metadata.senderId = senderId.open_id || senderId.user_id || senderId.union_id;
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "timer":
|
|
92
|
+
metadata.eventType = "timer.tick";
|
|
93
|
+
break;
|
|
94
|
+
case "websocket":
|
|
95
|
+
metadata.eventType = eventType;
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
metadata.eventType = eventType;
|
|
99
|
+
}
|
|
100
|
+
let recommendedAction;
|
|
101
|
+
if (sourceType === "lark-cli" && eventType === "im.message.receive_v1") {
|
|
102
|
+
recommendedAction = {
|
|
103
|
+
action: "处理飞书消息并回复",
|
|
104
|
+
replyTo: {
|
|
105
|
+
appId: metadata.appId,
|
|
106
|
+
chatId: metadata.chatId,
|
|
107
|
+
messageId: metadata.messageId
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
} else if (sourceType === "timer") {
|
|
111
|
+
recommendedAction = { action: "执行定时任务" };
|
|
112
|
+
} else if (sourceType === "websocket") {
|
|
113
|
+
recommendedAction = { action: "处理 WebSocket 消息" };
|
|
114
|
+
}
|
|
115
|
+
let replyChannel;
|
|
116
|
+
if (sourceType === "lark-cli") {
|
|
117
|
+
replyChannel = {
|
|
118
|
+
type: "lark-cli",
|
|
119
|
+
appId: metadata.appId,
|
|
120
|
+
chatId: metadata.chatId,
|
|
121
|
+
messageId: metadata.messageId
|
|
122
|
+
};
|
|
123
|
+
} else if (sourceType === "websocket") {
|
|
124
|
+
replyChannel = { type: "websocket" };
|
|
125
|
+
}
|
|
126
|
+
return { metadata, replyChannel, recommendedAction };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class LarkCliInstance {
|
|
130
|
+
config;
|
|
131
|
+
status = "created";
|
|
132
|
+
child;
|
|
133
|
+
buffer = "";
|
|
134
|
+
eventHandler;
|
|
135
|
+
constructor(config) {
|
|
136
|
+
this.config = config;
|
|
137
|
+
}
|
|
138
|
+
async start() {
|
|
139
|
+
if (this.status === "running")
|
|
140
|
+
return;
|
|
141
|
+
const command = this.config.command || "lark-cli event +subscribe";
|
|
142
|
+
const isWindows = process.platform === "win32";
|
|
143
|
+
const shell = isWindows ? "cmd.exe" : "sh";
|
|
144
|
+
const shellArgs = isWindows ? ["/c", command] : ["-c", `exec ${command}`];
|
|
145
|
+
this.child = spawn(shell, shellArgs, {
|
|
146
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
147
|
+
detached: false,
|
|
148
|
+
windowsHide: true
|
|
149
|
+
});
|
|
150
|
+
this.child.stdout?.on("data", (data) => {
|
|
151
|
+
this.processStream(data.toString());
|
|
152
|
+
});
|
|
153
|
+
this.child.stderr?.on("data", (data) => {
|
|
154
|
+
const output = data.toString();
|
|
155
|
+
if (output.includes('"ok": false')) {
|
|
156
|
+
this.status = "error";
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
this.child.on("exit", (code) => {
|
|
160
|
+
if (code !== 0 && code !== null) {
|
|
161
|
+
this.status = "error";
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
165
|
+
this.status = "running";
|
|
166
|
+
}
|
|
167
|
+
async stop() {
|
|
168
|
+
if (this.status === "stopped" || this.status === "created")
|
|
169
|
+
return;
|
|
170
|
+
this.child?.kill("SIGTERM");
|
|
171
|
+
this.child = undefined;
|
|
172
|
+
this.buffer = "";
|
|
173
|
+
this.status = "stopped";
|
|
174
|
+
}
|
|
175
|
+
getStatus() {
|
|
176
|
+
return this.status;
|
|
177
|
+
}
|
|
178
|
+
onEvent(handler) {
|
|
179
|
+
this.eventHandler = handler;
|
|
180
|
+
}
|
|
181
|
+
offEvent() {
|
|
182
|
+
this.eventHandler = undefined;
|
|
183
|
+
}
|
|
184
|
+
processStream(data) {
|
|
185
|
+
this.buffer += data;
|
|
186
|
+
const lines = this.buffer.split(`
|
|
187
|
+
`);
|
|
188
|
+
this.buffer = lines.pop() || "";
|
|
189
|
+
for (const line of lines) {
|
|
190
|
+
if (line.trim()) {
|
|
191
|
+
this.handleEvent(line);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
handleEvent(rawData) {
|
|
196
|
+
try {
|
|
197
|
+
let rawEvent;
|
|
198
|
+
try {
|
|
199
|
+
rawEvent = JSON.parse(rawData);
|
|
200
|
+
} catch {
|
|
201
|
+
rawEvent = { event: "message", data: { content: { type: "text", body: rawData } } };
|
|
202
|
+
}
|
|
203
|
+
const event = rawEvent;
|
|
204
|
+
let eventType;
|
|
205
|
+
if (event.header?.event_type) {
|
|
206
|
+
eventType = event.header.event_type;
|
|
207
|
+
} else if (event.schema) {
|
|
208
|
+
eventType = `lark.${event.schema}`;
|
|
209
|
+
} else if (event.type) {
|
|
210
|
+
eventType = event.type;
|
|
211
|
+
} else {
|
|
212
|
+
eventType = "unknown";
|
|
213
|
+
}
|
|
214
|
+
if (this.config.eventTypes?.length && !matchEventType(eventType, this.config.eventTypes)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const { metadata, replyChannel, recommendedAction } = extractMetadata("lark-cli", rawEvent, eventType);
|
|
218
|
+
const message = formatEventMessage("lark-cli", rawEvent, eventType);
|
|
219
|
+
const evt = {
|
|
220
|
+
sourceId: this.config.id,
|
|
221
|
+
type: eventType,
|
|
222
|
+
timestamp: Date.now(),
|
|
223
|
+
payload: {
|
|
224
|
+
sourceId: this.config.id,
|
|
225
|
+
sourceType: "lark-cli",
|
|
226
|
+
rawEvent,
|
|
227
|
+
message,
|
|
228
|
+
metadata,
|
|
229
|
+
replyChannel,
|
|
230
|
+
recommendedAction,
|
|
231
|
+
timestamp: Date.now()
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
this.eventHandler?.(evt);
|
|
235
|
+
} catch (error) {}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
var larkCliHandler = {
|
|
239
|
+
type: "lark-cli",
|
|
240
|
+
validateConfig(config) {
|
|
241
|
+
const errors = [];
|
|
242
|
+
if (!config.command) {
|
|
243
|
+
errors.push("lark-cli command is required");
|
|
244
|
+
}
|
|
245
|
+
return errors;
|
|
246
|
+
},
|
|
247
|
+
createInstance(config) {
|
|
248
|
+
return new LarkCliInstance(config);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
class TimerInstance {
|
|
253
|
+
config;
|
|
254
|
+
status = "created";
|
|
255
|
+
timer;
|
|
256
|
+
eventHandler;
|
|
257
|
+
constructor(config) {
|
|
258
|
+
this.config = config;
|
|
259
|
+
}
|
|
260
|
+
async start() {
|
|
261
|
+
if (this.status === "running")
|
|
262
|
+
return;
|
|
263
|
+
const interval = this.config.interval || 60000;
|
|
264
|
+
const message = this.config.options?.message || `Timer event from ${this.config.name}`;
|
|
265
|
+
this.timer = setInterval(() => {
|
|
266
|
+
this.emitEvent(message);
|
|
267
|
+
}, interval);
|
|
268
|
+
this.emitEvent(message);
|
|
269
|
+
this.status = "running";
|
|
270
|
+
}
|
|
271
|
+
async stop() {
|
|
272
|
+
if (this.timer) {
|
|
273
|
+
clearInterval(this.timer);
|
|
274
|
+
this.timer = undefined;
|
|
275
|
+
}
|
|
276
|
+
this.status = "stopped";
|
|
277
|
+
}
|
|
278
|
+
getStatus() {
|
|
279
|
+
return this.status;
|
|
280
|
+
}
|
|
281
|
+
onEvent(handler) {
|
|
282
|
+
this.eventHandler = handler;
|
|
283
|
+
}
|
|
284
|
+
offEvent() {
|
|
285
|
+
this.eventHandler = undefined;
|
|
286
|
+
}
|
|
287
|
+
emitEvent(message) {
|
|
288
|
+
const rawEvent = {
|
|
289
|
+
type: "timer.tick",
|
|
290
|
+
payload: {
|
|
291
|
+
message,
|
|
292
|
+
timestamp: Date.now()
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const { metadata, recommendedAction } = extractMetadata("timer", rawEvent, "timer.tick");
|
|
296
|
+
const evt = {
|
|
297
|
+
sourceId: this.config.id,
|
|
298
|
+
type: "timer.tick",
|
|
299
|
+
timestamp: Date.now(),
|
|
300
|
+
payload: {
|
|
301
|
+
sourceId: this.config.id,
|
|
302
|
+
sourceType: "timer",
|
|
303
|
+
rawEvent,
|
|
304
|
+
message: formatEventMessage("timer", rawEvent),
|
|
305
|
+
metadata,
|
|
306
|
+
recommendedAction,
|
|
307
|
+
timestamp: Date.now()
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
this.eventHandler?.(evt);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
var timerHandler = {
|
|
314
|
+
type: "timer",
|
|
315
|
+
validateConfig(config) {
|
|
316
|
+
const errors = [];
|
|
317
|
+
if (!config.interval || config.interval <= 0) {
|
|
318
|
+
errors.push("Timer interval must be a positive number");
|
|
319
|
+
}
|
|
320
|
+
return errors;
|
|
321
|
+
},
|
|
322
|
+
createInstance(config) {
|
|
323
|
+
return new TimerInstance(config);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
class WebSocketInstance {
|
|
328
|
+
config;
|
|
329
|
+
status = "created";
|
|
330
|
+
ws;
|
|
331
|
+
buffer = "";
|
|
332
|
+
eventHandler;
|
|
333
|
+
constructor(config) {
|
|
334
|
+
this.config = config;
|
|
335
|
+
}
|
|
336
|
+
async start() {
|
|
337
|
+
if (this.status === "running")
|
|
338
|
+
return;
|
|
339
|
+
const url = this.config.url;
|
|
340
|
+
if (!url) {
|
|
341
|
+
throw new Error("WebSocket URL is required");
|
|
342
|
+
}
|
|
343
|
+
const { WebSocket } = await import("ws");
|
|
344
|
+
this.ws = new WebSocket(url, { headers: this.config.headers });
|
|
345
|
+
this.ws.on("open", () => {
|
|
346
|
+
this.status = "running";
|
|
347
|
+
});
|
|
348
|
+
this.ws.on("message", (data) => {
|
|
349
|
+
this.processStream(data.toString());
|
|
350
|
+
});
|
|
351
|
+
this.ws.on("error", () => {
|
|
352
|
+
this.status = "error";
|
|
353
|
+
});
|
|
354
|
+
this.ws.on("close", () => {
|
|
355
|
+
this.status = "stopped";
|
|
356
|
+
});
|
|
357
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
358
|
+
}
|
|
359
|
+
async stop() {
|
|
360
|
+
if (this.ws) {
|
|
361
|
+
this.ws.close();
|
|
362
|
+
this.ws = undefined;
|
|
363
|
+
}
|
|
364
|
+
this.buffer = "";
|
|
365
|
+
this.status = "stopped";
|
|
366
|
+
}
|
|
367
|
+
getStatus() {
|
|
368
|
+
return this.status;
|
|
369
|
+
}
|
|
370
|
+
onEvent(handler) {
|
|
371
|
+
this.eventHandler = handler;
|
|
372
|
+
}
|
|
373
|
+
offEvent() {
|
|
374
|
+
this.eventHandler = undefined;
|
|
375
|
+
}
|
|
376
|
+
processStream(data) {
|
|
377
|
+
this.buffer += data;
|
|
378
|
+
const lines = this.buffer.split(`
|
|
379
|
+
`);
|
|
380
|
+
this.buffer = lines.pop() || "";
|
|
381
|
+
for (const line of lines) {
|
|
382
|
+
if (line.trim()) {
|
|
383
|
+
this.handleEvent(line);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
handleEvent(rawData) {
|
|
388
|
+
try {
|
|
389
|
+
let rawEvent;
|
|
390
|
+
try {
|
|
391
|
+
rawEvent = JSON.parse(rawData);
|
|
392
|
+
} catch {
|
|
393
|
+
rawEvent = rawData;
|
|
394
|
+
}
|
|
395
|
+
const { metadata, replyChannel, recommendedAction } = extractMetadata("websocket", rawEvent);
|
|
396
|
+
const evt = {
|
|
397
|
+
sourceId: this.config.id,
|
|
398
|
+
type: "websocket.message",
|
|
399
|
+
timestamp: Date.now(),
|
|
400
|
+
payload: {
|
|
401
|
+
sourceId: this.config.id,
|
|
402
|
+
sourceType: "websocket",
|
|
403
|
+
rawEvent,
|
|
404
|
+
message: formatEventMessage("websocket", rawEvent),
|
|
405
|
+
metadata,
|
|
406
|
+
replyChannel,
|
|
407
|
+
recommendedAction,
|
|
408
|
+
timestamp: Date.now()
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
this.eventHandler?.(evt);
|
|
412
|
+
} catch (error) {}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
var websocketHandler = {
|
|
416
|
+
type: "websocket",
|
|
417
|
+
validateConfig(config) {
|
|
418
|
+
const errors = [];
|
|
419
|
+
if (!config.url) {
|
|
420
|
+
errors.push("WebSocket URL is required");
|
|
421
|
+
}
|
|
422
|
+
return errors;
|
|
423
|
+
},
|
|
424
|
+
createInstance(config) {
|
|
425
|
+
return new WebSocketInstance(config);
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
var builtInHandlers = [
|
|
429
|
+
larkCliHandler,
|
|
430
|
+
timerHandler,
|
|
431
|
+
websocketHandler
|
|
432
|
+
];
|
|
433
|
+
function getBuiltInHandler(type) {
|
|
434
|
+
return builtInHandlers.find((h) => h.type === type);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/env/event-source/event-source-component.ts
|
|
438
|
+
import { join } from "path";
|
|
439
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
440
|
+
init_logger();
|
|
441
|
+
|
|
442
|
+
// src/env/event-source/event-source-config-registration.ts
|
|
443
|
+
var EVENT_SOURCE_DEFAULTS = {
|
|
444
|
+
"event-source.enabled": true,
|
|
445
|
+
"event-source.persistenceEnabled": true
|
|
446
|
+
};
|
|
447
|
+
var EVENT_SOURCE_CONFIG_REGISTRATION = {
|
|
448
|
+
name: "event-source",
|
|
449
|
+
sources: [
|
|
450
|
+
{ type: "env", envPrefix: "EVENT_SOURCE", priority: 20, watch: false }
|
|
451
|
+
],
|
|
452
|
+
keys: [
|
|
453
|
+
{ key: "event-source.enabled", sources: ["env", "file"] },
|
|
454
|
+
{ key: "event-source.persistenceEnabled", sources: ["env", "file"] },
|
|
455
|
+
{ key: "event-source.configPath", sources: ["env", "file"] }
|
|
456
|
+
]
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// src/env/event-source/event-source-component.ts
|
|
460
|
+
var logger = createLogger("event-source");
|
|
461
|
+
|
|
462
|
+
class EventSourceComponent extends BaseComponent {
|
|
463
|
+
name = "event-source";
|
|
464
|
+
version = "1.0.0";
|
|
465
|
+
sources = new Map;
|
|
466
|
+
statuses = new Map;
|
|
467
|
+
handlers = new Map;
|
|
468
|
+
instances = new Map;
|
|
469
|
+
eventHandlers = new Map;
|
|
470
|
+
buffers = new Map;
|
|
471
|
+
configPath = "";
|
|
472
|
+
persistenceEnabled = true;
|
|
473
|
+
configComponent;
|
|
474
|
+
configWatcher;
|
|
475
|
+
async onInit() {
|
|
476
|
+
await this.executeInitHooks();
|
|
477
|
+
await this.loadConfig();
|
|
478
|
+
}
|
|
479
|
+
async init(options) {
|
|
480
|
+
if (options?.env) {
|
|
481
|
+
this.env = options.env;
|
|
482
|
+
}
|
|
483
|
+
this.setStatus("initializing");
|
|
484
|
+
if (options?.name)
|
|
485
|
+
Object.defineProperty(this, "name", { value: options.name, writable: false });
|
|
486
|
+
if (options?.version)
|
|
487
|
+
Object.defineProperty(this, "version", { value: options.version, writable: false });
|
|
488
|
+
if (options?.enabled !== undefined)
|
|
489
|
+
this._enabled = options.enabled;
|
|
490
|
+
const opts = options?.options;
|
|
491
|
+
if (opts?.configComponent) {
|
|
492
|
+
this.configComponent = opts.configComponent;
|
|
493
|
+
await this.registerConfig(opts);
|
|
494
|
+
if (opts.registerBuiltInHandlers !== false) {
|
|
495
|
+
this.registerBuiltInHandlers();
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
throw new Error("ConfigComponent is required for EventSourceComponent initialization");
|
|
499
|
+
}
|
|
500
|
+
await this.onInit();
|
|
501
|
+
this.setStatus("running");
|
|
502
|
+
}
|
|
503
|
+
async onStart() {}
|
|
504
|
+
async onStop() {
|
|
505
|
+
this.configWatcher?.();
|
|
506
|
+
this.configWatcher = undefined;
|
|
507
|
+
const runningSources = Array.from(this.statuses.entries()).filter(([_, status]) => status === "running").map(([id]) => id);
|
|
508
|
+
for (const id of runningSources) {
|
|
509
|
+
await this.stopSource(id);
|
|
510
|
+
}
|
|
511
|
+
this.sources.clear();
|
|
512
|
+
this.statuses.clear();
|
|
513
|
+
this.instances.clear();
|
|
514
|
+
this.eventHandlers.clear();
|
|
515
|
+
this.handlers.clear();
|
|
516
|
+
this.buffers.clear();
|
|
517
|
+
}
|
|
518
|
+
registerBuiltInHandlers() {
|
|
519
|
+
for (const handler of builtInHandlers) {
|
|
520
|
+
if (!this.handlers.has(handler.type)) {
|
|
521
|
+
this.handlers.set(handler.type, handler);
|
|
522
|
+
logger.debug(`Registered built-in handler: ${handler.type}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async executeInitHooks() {
|
|
527
|
+
const { EventSourceInitHooks } = await import("./roy-agent-core-8jxva565.js");
|
|
528
|
+
await EventSourceInitHooks.execute(this);
|
|
529
|
+
}
|
|
530
|
+
registerHandler(handler) {
|
|
531
|
+
if (this.handlers.has(handler.type)) {
|
|
532
|
+
logger.warn(`Handler already registered for type: ${handler.type}, overwriting`);
|
|
533
|
+
}
|
|
534
|
+
this.handlers.set(handler.type, handler);
|
|
535
|
+
logger.debug(`Handler registered: ${handler.type}`);
|
|
536
|
+
}
|
|
537
|
+
unregisterHandler(type) {
|
|
538
|
+
const deleted = this.handlers.delete(type);
|
|
539
|
+
if (deleted) {
|
|
540
|
+
logger.debug(`Handler unregistered: ${type}`);
|
|
541
|
+
}
|
|
542
|
+
return deleted;
|
|
543
|
+
}
|
|
544
|
+
getHandler(type) {
|
|
545
|
+
return this.handlers.get(type);
|
|
546
|
+
}
|
|
547
|
+
listHandlers() {
|
|
548
|
+
return Array.from(this.handlers.values());
|
|
549
|
+
}
|
|
550
|
+
async registerConfig(options) {
|
|
551
|
+
const configComponent = options.configComponent;
|
|
552
|
+
if (!configComponent)
|
|
553
|
+
return;
|
|
554
|
+
const { configPath, config } = options;
|
|
555
|
+
const envPrefix = "EVENT_SOURCE";
|
|
556
|
+
configComponent.registerComponent(EVENT_SOURCE_CONFIG_REGISTRATION);
|
|
557
|
+
if (configPath) {
|
|
558
|
+
configComponent.registerSource({
|
|
559
|
+
type: "file",
|
|
560
|
+
relativePath: configPath,
|
|
561
|
+
optional: true,
|
|
562
|
+
watch: false
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
configComponent.registerSource({
|
|
566
|
+
type: "env",
|
|
567
|
+
envPrefix,
|
|
568
|
+
priority: 20,
|
|
569
|
+
watch: false
|
|
570
|
+
});
|
|
571
|
+
await configComponent.load("event-source");
|
|
572
|
+
for (const envKey of Object.keys(process.env)) {
|
|
573
|
+
const configKey = envKeyToConfigKey(envKey, envPrefix, "event-source");
|
|
574
|
+
if (!configKey)
|
|
575
|
+
continue;
|
|
576
|
+
const value = process.env[envKey];
|
|
577
|
+
if (value !== undefined) {
|
|
578
|
+
await configComponent.set(configKey, value);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
for (const [key, value] of Object.entries(EVENT_SOURCE_DEFAULTS)) {
|
|
582
|
+
if (configComponent.get(key) === undefined) {
|
|
583
|
+
await configComponent.set(key, value);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (configComponent.get("event-source.configPath") === undefined) {
|
|
587
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
588
|
+
const dataDir = join(home, ".roy-agent");
|
|
589
|
+
await configComponent.set("event-source.configPath", join(dataDir, "event-sources.json"));
|
|
590
|
+
}
|
|
591
|
+
if (config) {
|
|
592
|
+
const flatConfig = this.flattenConfig(config);
|
|
593
|
+
for (const [key, value] of Object.entries(flatConfig)) {
|
|
594
|
+
await configComponent.set(key, value);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
this.registerConfigWatcher(configComponent);
|
|
598
|
+
}
|
|
599
|
+
buildConfig() {
|
|
600
|
+
const configComponent = this.configComponent;
|
|
601
|
+
if (!configComponent) {
|
|
602
|
+
return { enabled: true, persistenceEnabled: true, configPath: "" };
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
enabled: configComponent.get("event-source.enabled") ?? true,
|
|
606
|
+
persistenceEnabled: configComponent.get("event-source.persistenceEnabled") ?? true,
|
|
607
|
+
configPath: configComponent.get("event-source.configPath") ?? ""
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
flattenConfig(obj, prefix = "event-source") {
|
|
611
|
+
const result = {};
|
|
612
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
613
|
+
const fullKey = `${prefix}.${key}`;
|
|
614
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
615
|
+
Object.assign(result, this.flattenConfig(value, fullKey));
|
|
616
|
+
} else {
|
|
617
|
+
result[fullKey] = value;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
registerConfigWatcher(configComponent) {
|
|
623
|
+
if (typeof configComponent.watch !== "function")
|
|
624
|
+
return;
|
|
625
|
+
this.configWatcher = configComponent.watch("event-source.*", (event) => {
|
|
626
|
+
this.onConfigChange(event);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
onConfigChange(event) {
|
|
630
|
+
logger.info(`EventSource config changed: ${event.key}`, {
|
|
631
|
+
oldValue: event.oldValue,
|
|
632
|
+
newValue: event.newValue
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
register(config) {
|
|
636
|
+
if (this.sources.has(config.id)) {
|
|
637
|
+
throw new Error(`EventSource already exists: ${config.id}`);
|
|
638
|
+
}
|
|
639
|
+
const handler = this.handlers.get(config.type);
|
|
640
|
+
if (!handler) {
|
|
641
|
+
throw new Error(`No handler registered for event source type: ${config.type}`);
|
|
642
|
+
}
|
|
643
|
+
const errors = handler.validateConfig(config);
|
|
644
|
+
if (errors.length > 0) {
|
|
645
|
+
throw new Error(`Invalid config: ${errors.join(", ")}`);
|
|
646
|
+
}
|
|
647
|
+
this.sources.set(config.id, config);
|
|
648
|
+
this.statuses.set(config.id, "created");
|
|
649
|
+
logger.debug(`EventSource registered: ${config.id} (${config.type})`);
|
|
650
|
+
this.saveConfig().catch((err) => {
|
|
651
|
+
logger.error(`Failed to save config after registering ${config.id}:`, err);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
unregister(id) {
|
|
655
|
+
const status = this.statuses.get(id);
|
|
656
|
+
if (status === "running") {
|
|
657
|
+
this.stopSource(id);
|
|
658
|
+
}
|
|
659
|
+
const existed = this.sources.has(id);
|
|
660
|
+
this.sources.delete(id);
|
|
661
|
+
this.statuses.delete(id);
|
|
662
|
+
this.instances.delete(id);
|
|
663
|
+
this.eventHandlers.delete(id);
|
|
664
|
+
this.buffers.delete(id);
|
|
665
|
+
if (existed) {
|
|
666
|
+
logger.debug(`EventSource unregistered: ${id}`);
|
|
667
|
+
this.saveConfig().catch((err) => {
|
|
668
|
+
logger.error(`Failed to save config after unregistering ${id}:`, err);
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
return existed;
|
|
672
|
+
}
|
|
673
|
+
get(id) {
|
|
674
|
+
return this.sources.get(id);
|
|
675
|
+
}
|
|
676
|
+
list() {
|
|
677
|
+
return Array.from(this.sources.values());
|
|
678
|
+
}
|
|
679
|
+
getStatus(id) {
|
|
680
|
+
return this.statuses.get(id);
|
|
681
|
+
}
|
|
682
|
+
getEventSourceStatus(id) {
|
|
683
|
+
return this.getStatus(id);
|
|
684
|
+
}
|
|
685
|
+
onEvent(id, handler) {
|
|
686
|
+
this.eventHandlers.set(id, handler);
|
|
687
|
+
const instance = this.instances.get(id);
|
|
688
|
+
if (instance) {
|
|
689
|
+
instance.onEvent(handler);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
offEvent(id) {
|
|
693
|
+
this.eventHandlers.delete(id);
|
|
694
|
+
const instance = this.instances.get(id);
|
|
695
|
+
if (instance) {
|
|
696
|
+
instance.offEvent();
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
async startSource(id) {
|
|
700
|
+
const config = this.sources.get(id);
|
|
701
|
+
if (!config) {
|
|
702
|
+
throw new Error(`EventSource not found: ${id}`);
|
|
703
|
+
}
|
|
704
|
+
const currentStatus = this.statuses.get(id);
|
|
705
|
+
if (currentStatus === "running") {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
this.statuses.set(id, "starting");
|
|
709
|
+
try {
|
|
710
|
+
const handler = this.handlers.get(config.type);
|
|
711
|
+
if (!handler) {
|
|
712
|
+
throw new Error(`No handler registered for event source type: ${config.type}`);
|
|
713
|
+
}
|
|
714
|
+
const instance = handler.createInstance(config);
|
|
715
|
+
this.instances.set(id, instance);
|
|
716
|
+
const eventHandler = this.eventHandlers.get(id);
|
|
717
|
+
if (eventHandler) {
|
|
718
|
+
instance.onEvent(eventHandler);
|
|
719
|
+
}
|
|
720
|
+
await instance.start();
|
|
721
|
+
this.statuses.set(id, "running");
|
|
722
|
+
this.getEnv()?.pushEnvEvent({
|
|
723
|
+
type: "event-source.started",
|
|
724
|
+
payload: { sourceId: id, sourceType: config.type }
|
|
725
|
+
});
|
|
726
|
+
logger.info(`EventSource started: ${id} (${config.type})`);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
this.statuses.set(id, "error");
|
|
729
|
+
logger.error(`EventSource failed to start: ${id}`, error);
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async stopSource(id) {
|
|
734
|
+
const status = this.statuses.get(id);
|
|
735
|
+
logger.info(`[EventSource ${id}] stopSource called, current status: ${status}`);
|
|
736
|
+
if (status === "stopped" || status === "created") {
|
|
737
|
+
logger.info(`[EventSource ${id}] already stopped/created, skipping`);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
this.statuses.set(id, "stopping");
|
|
741
|
+
try {
|
|
742
|
+
const instance = this.instances.get(id);
|
|
743
|
+
if (instance) {
|
|
744
|
+
await instance.stop();
|
|
745
|
+
this.instances.delete(id);
|
|
746
|
+
}
|
|
747
|
+
const config = this.sources.get(id);
|
|
748
|
+
this.statuses.set(id, "stopped");
|
|
749
|
+
this.getEnv()?.pushEnvEvent({
|
|
750
|
+
type: "event-source.stopped",
|
|
751
|
+
payload: { sourceId: id, sourceType: config?.type }
|
|
752
|
+
});
|
|
753
|
+
logger.info(`EventSource stopped: ${id}`);
|
|
754
|
+
} catch (error) {
|
|
755
|
+
this.statuses.set(id, "error");
|
|
756
|
+
logger.error(`EventSource failed to stop: ${id}`, error);
|
|
757
|
+
throw error;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
getConfigFilePath() {
|
|
761
|
+
if (this.configPath) {
|
|
762
|
+
return this.configPath;
|
|
763
|
+
}
|
|
764
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
765
|
+
const dataDir = join(home, ".roy-agent");
|
|
766
|
+
if (!existsSync(dataDir)) {
|
|
767
|
+
mkdirSync(dataDir, { recursive: true });
|
|
768
|
+
}
|
|
769
|
+
return join(dataDir, "event-sources.json");
|
|
770
|
+
}
|
|
771
|
+
async saveConfig() {
|
|
772
|
+
if (!this.persistenceEnabled)
|
|
773
|
+
return;
|
|
774
|
+
try {
|
|
775
|
+
const configPath = this.getConfigFilePath();
|
|
776
|
+
const config = {
|
|
777
|
+
version: "1.0.0",
|
|
778
|
+
sources: Array.from(this.sources.values())
|
|
779
|
+
};
|
|
780
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
781
|
+
logger.debug(`Saved ${this.sources.size} event source configurations`);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
logger.error("Failed to save event source configurations:", error);
|
|
784
|
+
throw error;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
async loadConfig() {
|
|
788
|
+
if (!this.persistenceEnabled)
|
|
789
|
+
return;
|
|
790
|
+
try {
|
|
791
|
+
const configPath = this.getConfigFilePath();
|
|
792
|
+
if (!existsSync(configPath)) {
|
|
793
|
+
logger.debug(`No event source config file found at ${configPath}`);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const content = readFileSync(configPath, "utf-8");
|
|
797
|
+
const config = JSON.parse(content);
|
|
798
|
+
if (!config.sources || !Array.isArray(config.sources)) {
|
|
799
|
+
logger.warn("Invalid event source config format, skipping load");
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
let loadedCount = 0;
|
|
803
|
+
for (const source of config.sources) {
|
|
804
|
+
if (!this.sources.has(source.id)) {
|
|
805
|
+
this.sources.set(source.id, source);
|
|
806
|
+
this.statuses.set(source.id, "created");
|
|
807
|
+
loadedCount++;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
logger.info(`Loaded ${loadedCount} event source configurations`);
|
|
811
|
+
} catch (error) {
|
|
812
|
+
logger.error("Failed to load event source configurations:", error);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
export { larkCliHandler, timerHandler, websocketHandler, builtInHandlers, getBuiltInHandler, EventSourceComponent };
|