@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.
Files changed (58) hide show
  1. package/dist/env/event-source/index.js +41 -0
  2. package/dist/env/index.js +24 -10
  3. package/dist/env/log-trace/index.js +1 -1
  4. package/dist/env/prompt/index.js +1 -1
  5. package/dist/env/workflow/engine/index.js +1 -1
  6. package/dist/env/workflow/index.js +2 -2
  7. package/dist/index.js +35 -8
  8. package/dist/shared/@ai-setting/roy-agent-core-2grcjaad.js +0 -0
  9. package/dist/shared/@ai-setting/{roy-agent-core-xq8hhqb8.js → roy-agent-core-4wjywp3c.js} +4 -2
  10. package/dist/shared/@ai-setting/roy-agent-core-5ex3za0m.js +817 -0
  11. package/dist/shared/@ai-setting/roy-agent-core-8jxva565.js +19 -0
  12. package/dist/shared/@ai-setting/roy-agent-core-avq1x4t7.js +84 -0
  13. package/dist/shared/@ai-setting/{roy-agent-core-gq20wsgv.js → roy-agent-core-ffb9fq4v.js} +23 -2
  14. package/dist/shared/@ai-setting/roy-agent-core-j1sr5pk9.js +424 -0
  15. package/dist/shared/@ai-setting/{roy-agent-core-93zfb3r1.js → roy-agent-core-mrcxzpbg.js} +1 -1
  16. package/dist/shared/@ai-setting/{roy-agent-core-rhmtwnw1.js → roy-agent-core-pw7cv1px.js} +1 -1
  17. package/dist/shared/@ai-setting/{roy-agent-core-wrcy0h6z.js → roy-agent-core-ty94k28r.js} +1 -1
  18. package/package.json +8 -29
  19. package/dist/config/index.d.ts +0 -1250
  20. package/dist/env/agent/index.d.ts +0 -2279
  21. package/dist/env/commands/index.d.ts +0 -1131
  22. package/dist/env/debug/formatters/index.d.ts +0 -236
  23. package/dist/env/debug/index.d.ts +0 -1652
  24. package/dist/env/hook/index.d.ts +0 -279
  25. package/dist/env/index.d.ts +0 -3481
  26. package/dist/env/llm/index.d.ts +0 -1760
  27. package/dist/env/log-trace/index.d.ts +0 -1574
  28. package/dist/env/mcp/index.d.ts +0 -1331
  29. package/dist/env/mcp/tool/index.d.ts +0 -183
  30. package/dist/env/memory/built-in/index.d.ts +0 -232
  31. package/dist/env/memory/index.d.ts +0 -1799
  32. package/dist/env/memory/plugin/index.d.ts +0 -747
  33. package/dist/env/prompt/index.d.ts +0 -1164
  34. package/dist/env/session/index.d.ts +0 -1908
  35. package/dist/env/session/storage/index.d.ts +0 -564
  36. package/dist/env/skill/index.d.ts +0 -1266
  37. package/dist/env/skill/tool/index.d.ts +0 -193
  38. package/dist/env/task/delegate/index.d.ts +0 -1612
  39. package/dist/env/task/events/index.d.ts +0 -171
  40. package/dist/env/task/hooks/index.d.ts +0 -624
  41. package/dist/env/task/index.d.ts +0 -1553
  42. package/dist/env/task/plugins/index.d.ts +0 -466
  43. package/dist/env/task/storage/index.d.ts +0 -241
  44. package/dist/env/task/tools/index.d.ts +0 -1485
  45. package/dist/env/task/tools/operation/index.d.ts +0 -1484
  46. package/dist/env/tool/built-in/index.d.ts +0 -218
  47. package/dist/env/tool/index.d.ts +0 -1396
  48. package/dist/env/workflow/decorators/index.d.ts +0 -2161
  49. package/dist/env/workflow/engine/index.d.ts +0 -3453
  50. package/dist/env/workflow/index.d.ts +0 -3546
  51. package/dist/env/workflow/nodes/index.d.ts +0 -2092
  52. package/dist/env/workflow/service/index.d.ts +0 -227
  53. package/dist/env/workflow/storage/index.d.ts +0 -165
  54. package/dist/env/workflow/tools/index.d.ts +0 -416
  55. package/dist/env/workflow/types/index.d.ts +0 -2255
  56. package/dist/env/workflow/utils/index.d.ts +0 -2031
  57. package/dist/index.d.ts +0 -7858
  58. 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 };