@ai-setting/roy-agent-core 1.3.10 → 1.3.14

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 (379) hide show
  1. package/dist/config/index.js +1647 -0
  2. package/dist/index.js +12579 -89691
  3. package/package.json +19 -56
  4. package/src/config/config-component.test.ts +0 -627
  5. package/src/config/config-component.ts +0 -906
  6. package/src/config/config-parser.test.ts +0 -319
  7. package/src/config/config-parser.ts +0 -203
  8. package/src/config/decentralized-config.test.ts +0 -740
  9. package/src/config/env-key.ts +0 -210
  10. package/src/config/env-source.test.ts +0 -252
  11. package/src/config/env-source.ts +0 -301
  12. package/src/config/file-source.test.ts +0 -357
  13. package/src/config/file-source.ts +0 -421
  14. package/src/config/index.ts +0 -24
  15. package/src/config/protocol-resolver.test.ts +0 -217
  16. package/src/config/protocol-resolver.ts +0 -228
  17. package/src/env/agent/agent-component.abort.test.ts +0 -511
  18. package/src/env/agent/agent-component.record-session.test.ts +0 -349
  19. package/src/env/agent/agent-component.test.ts +0 -1389
  20. package/src/env/agent/agent-component.tool-error.test.ts +0 -327
  21. package/src/env/agent/agent-component.ts +0 -1711
  22. package/src/env/agent/agent-config-registration.test.ts +0 -226
  23. package/src/env/agent/agent-config-registration.ts +0 -46
  24. package/src/env/agent/agent-reminder-plugin.integration.test.ts +0 -243
  25. package/src/env/agent/index.ts +0 -10
  26. package/src/env/agent/summary-agent.parse-hint.test.ts +0 -360
  27. package/src/env/agent/summary-agent.ts +0 -508
  28. package/src/env/agent/types.ts +0 -536
  29. package/src/env/commands/commands-component.test.ts +0 -364
  30. package/src/env/commands/commands-component.ts +0 -604
  31. package/src/env/commands/commands-config-registration.test.ts +0 -198
  32. package/src/env/commands/commands-config-registration.ts +0 -38
  33. package/src/env/commands/index.ts +0 -21
  34. package/src/env/commands/parser.test.ts +0 -203
  35. package/src/env/commands/parser.ts +0 -115
  36. package/src/env/commands/types.ts +0 -184
  37. package/src/env/commands-prompt-integration.test.ts +0 -243
  38. package/src/env/component-env.test.ts +0 -119
  39. package/src/env/component.ts +0 -335
  40. package/src/env/constants.test.ts +0 -72
  41. package/src/env/constants.ts +0 -123
  42. package/src/env/debug/debug-component.test.ts +0 -114
  43. package/src/env/debug/debug-component.ts +0 -547
  44. package/src/env/debug/formatters/index.ts +0 -9
  45. package/src/env/debug/formatters/repl-formatter.test.ts +0 -139
  46. package/src/env/debug/formatters/repl-formatter.ts +0 -358
  47. package/src/env/debug/formatters/trace-formatter.test.ts +0 -119
  48. package/src/env/debug/formatters/trace-formatter.ts +0 -191
  49. package/src/env/debug/formatters/tree-formatter.test.ts +0 -107
  50. package/src/env/debug/formatters/tree-formatter.ts +0 -325
  51. package/src/env/debug/index.ts +0 -38
  52. package/src/env/debug/parser/regex-parser.test.ts +0 -201
  53. package/src/env/debug/parser/regex-parser.ts +0 -196
  54. package/src/env/debug/parser/span-builder.test.ts +0 -241
  55. package/src/env/debug/parser/span-builder.ts +0 -386
  56. package/src/env/debug/reader/log-reader.test.ts +0 -170
  57. package/src/env/debug/reader/log-reader.ts +0 -186
  58. package/src/env/debug/reader/span-db-reader.test.ts +0 -118
  59. package/src/env/debug/reader/span-db-reader.ts +0 -201
  60. package/src/env/debug/types.test.ts +0 -187
  61. package/src/env/debug/types.ts +0 -171
  62. package/src/env/environment-init.test.ts +0 -183
  63. package/src/env/environment-lifecycle.test.ts +0 -516
  64. package/src/env/environment-service.test.ts +0 -332
  65. package/src/env/environment.handle-query.test.ts +0 -96
  66. package/src/env/environment.test.ts +0 -232
  67. package/src/env/environment.ts +0 -708
  68. package/src/env/errors.test.ts +0 -165
  69. package/src/env/errors.ts +0 -157
  70. package/src/env/event-source/event-source-agent-handler.test.ts +0 -193
  71. package/src/env/event-source/event-source-agent-handler.ts +0 -111
  72. package/src/env/event-source/event-source-component.process-cleanup.test.ts +0 -236
  73. package/src/env/event-source/event-source-component.stop.test.ts +0 -346
  74. package/src/env/event-source/event-source-component.test.ts +0 -1207
  75. package/src/env/event-source/event-source-component.ts +0 -1379
  76. package/src/env/event-source/event-source-config-registration.test.ts +0 -242
  77. package/src/env/event-source/event-source-config-registration.ts +0 -37
  78. package/src/env/event-source/event-source-integration.test.ts +0 -320
  79. package/src/env/event-source/event-source-platform.test.ts +0 -630
  80. package/src/env/event-source/types.ts +0 -298
  81. package/src/env/hook/global-hook-manager.ts +0 -162
  82. package/src/env/hook/hook-manager.test.ts +0 -374
  83. package/src/env/hook/hook-manager.ts +0 -309
  84. package/src/env/hook/index.ts +0 -38
  85. package/src/env/hook/types.ts +0 -138
  86. package/src/env/index.ts +0 -144
  87. package/src/env/interface.ts +0 -203
  88. package/src/env/llm/hooks.test.ts +0 -293
  89. package/src/env/llm/hooks.ts +0 -316
  90. package/src/env/llm/index.ts +0 -61
  91. package/src/env/llm/invoke-threshold-check.test.ts +0 -88
  92. package/src/env/llm/invoke-timeout.test.ts +0 -54
  93. package/src/env/llm/invoke.test.ts +0 -71
  94. package/src/env/llm/invoke.ts +0 -1039
  95. package/src/env/llm/llm-config.test.ts +0 -523
  96. package/src/env/llm/llm.test.ts +0 -233
  97. package/src/env/llm/llm.ts +0 -568
  98. package/src/env/llm/provider.test.ts +0 -182
  99. package/src/env/llm/provider.ts +0 -108
  100. package/src/env/llm/transform.test.ts +0 -251
  101. package/src/env/llm/transform.ts +0 -286
  102. package/src/env/llm/types.test.ts +0 -580
  103. package/src/env/llm/types.ts +0 -424
  104. package/src/env/log-trace/decorator-otel.test.ts +0 -182
  105. package/src/env/log-trace/decorator.ts +0 -230
  106. package/src/env/log-trace/index.ts +0 -79
  107. package/src/env/log-trace/log-trace-component.test.ts +0 -242
  108. package/src/env/log-trace/log-trace-component.ts +0 -497
  109. package/src/env/log-trace/log-trace-config-registration.test.ts +0 -348
  110. package/src/env/log-trace/log-trace-config-registration.ts +0 -45
  111. package/src/env/log-trace/logger.test.ts +0 -149
  112. package/src/env/log-trace/logger.ts +0 -522
  113. package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +0 -147
  114. package/src/env/log-trace/opentelemetry/cli-propagation.ts +0 -194
  115. package/src/env/log-trace/opentelemetry/integration.test.ts +0 -668
  116. package/src/env/log-trace/opentelemetry/mod.ts +0 -25
  117. package/src/env/log-trace/opentelemetry/propagation-env.test.ts +0 -181
  118. package/src/env/log-trace/opentelemetry/propagation-env.ts +0 -136
  119. package/src/env/log-trace/opentelemetry/propagation.test.ts +0 -259
  120. package/src/env/log-trace/opentelemetry/propagation.ts +0 -215
  121. package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +0 -166
  122. package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +0 -379
  123. package/src/env/log-trace/opentelemetry/tracer-provider.ts +0 -612
  124. package/src/env/log-trace/span-storage.test.ts +0 -145
  125. package/src/env/log-trace/span-storage.ts +0 -230
  126. package/src/env/log-trace/trace-context.test.ts +0 -187
  127. package/src/env/log-trace/trace-context.ts +0 -162
  128. package/src/env/log-trace/types.test.ts +0 -63
  129. package/src/env/log-trace/types.ts +0 -172
  130. package/src/env/mcp/README.md +0 -244
  131. package/src/env/mcp/__integration__/mcp-component.integration.test.ts +0 -373
  132. package/src/env/mcp/config.test.ts +0 -74
  133. package/src/env/mcp/config.ts +0 -116
  134. package/src/env/mcp/index.ts +0 -41
  135. package/src/env/mcp/loader.test.ts +0 -161
  136. package/src/env/mcp/loader.ts +0 -209
  137. package/src/env/mcp/mcp-component.test.ts +0 -111
  138. package/src/env/mcp/mcp-component.ts +0 -358
  139. package/src/env/mcp/mcp-config-registration.test.ts +0 -304
  140. package/src/env/mcp/mcp-config-registration.ts +0 -50
  141. package/src/env/mcp/scanner.test.ts +0 -170
  142. package/src/env/mcp/scanner.ts +0 -246
  143. package/src/env/mcp/tool/adapter.test.ts +0 -520
  144. package/src/env/mcp/tool/adapter.ts +0 -521
  145. package/src/env/mcp/tool/index.ts +0 -5
  146. package/src/env/mcp/types.test.ts +0 -171
  147. package/src/env/mcp/types.ts +0 -79
  148. package/src/env/memory/README.md +0 -177
  149. package/src/env/memory/built-in/index.ts +0 -59
  150. package/src/env/memory/built-in/recall-memory.ts +0 -103
  151. package/src/env/memory/built-in/record-memory.ts +0 -148
  152. package/src/env/memory/index.ts +0 -20
  153. package/src/env/memory/memory-component.test.ts +0 -239
  154. package/src/env/memory/memory-component.ts +0 -503
  155. package/src/env/memory/memory-config-registration.test.ts +0 -67
  156. package/src/env/memory/memory-config-registration.ts +0 -48
  157. package/src/env/memory/memory-config.ts +0 -45
  158. package/src/env/memory/memory-file.test.ts +0 -268
  159. package/src/env/memory/plugin/index.ts +0 -48
  160. package/src/env/memory/plugin/memory-agent.test.ts +0 -249
  161. package/src/env/memory/plugin/memory-agent.ts +0 -365
  162. package/src/env/memory/plugin/memory-manager.ts +0 -198
  163. package/src/env/memory/plugin/memory-plugin-agent.test.ts +0 -145
  164. package/src/env/memory/plugin/memory-plugin.ts +0 -210
  165. package/src/env/memory/plugin/plugin-simplified.test.ts +0 -51
  166. package/src/env/memory/plugin/recall-memory.test.ts +0 -106
  167. package/src/env/memory/plugin/recall-memory.ts +0 -53
  168. package/src/env/memory/plugin/types.ts +0 -101
  169. package/src/env/memory/tools/memory-agent-tools.ts +0 -228
  170. package/src/env/memory/types.ts +0 -85
  171. package/src/env/paths.ts +0 -118
  172. package/src/env/prompt/index.ts +0 -18
  173. package/src/env/prompt/memory-prompts.test.ts +0 -91
  174. package/src/env/prompt/prompt-component.test.ts +0 -491
  175. package/src/env/prompt/prompt-component.ts +0 -619
  176. package/src/env/prompt/prompt-config-registration.test.ts +0 -213
  177. package/src/env/prompt/prompt-config-registration.ts +0 -39
  178. package/src/env/prompt/prompts-index.ts +0 -504
  179. package/src/env/prompt/renderer.ts +0 -67
  180. package/src/env/prompt/types.ts +0 -136
  181. package/src/env/session/hooks.ts +0 -18
  182. package/src/env/session/index.ts +0 -37
  183. package/src/env/session/search-query-parser.test.ts +0 -425
  184. package/src/env/session/search-query-parser.ts +0 -171
  185. package/src/env/session/session-checkpoint.test.ts +0 -523
  186. package/src/env/session/session-component.extract-recent-messages.test.ts +0 -209
  187. package/src/env/session/session-component.test.ts +0 -132
  188. package/src/env/session/session-component.ts +0 -1249
  189. package/src/env/session/session-config-registration.test.ts +0 -138
  190. package/src/env/session/session-config-registration.ts +0 -52
  191. package/src/env/session/session-message-converter.test.ts +0 -763
  192. package/src/env/session/session-message-converter.ts +0 -415
  193. package/src/env/session/session-message-e2e.test.ts +0 -448
  194. package/src/env/session/session-search.test.ts +0 -391
  195. package/src/env/session/session-store.test.ts +0 -362
  196. package/src/env/session/session-store.ts +0 -141
  197. package/src/env/session/storage/index.ts +0 -6
  198. package/src/env/session/storage/memory.ts +0 -502
  199. package/src/env/session/storage/sqlite.ts +0 -794
  200. package/src/env/session/types.ts +0 -742
  201. package/src/env/skill/config.ts +0 -39
  202. package/src/env/skill/index.ts +0 -6
  203. package/src/env/skill/parser.test.ts +0 -116
  204. package/src/env/skill/parser.ts +0 -77
  205. package/src/env/skill/scanner.test.ts +0 -211
  206. package/src/env/skill/scanner.ts +0 -119
  207. package/src/env/skill/skill-component.test.ts +0 -234
  208. package/src/env/skill/skill-component.ts +0 -352
  209. package/src/env/skill/skill-config-registration.test.ts +0 -60
  210. package/src/env/skill/skill-config-registration.ts +0 -43
  211. package/src/env/skill/tool/index.ts +0 -1
  212. package/src/env/skill/tool/skill-tool.test.ts +0 -100
  213. package/src/env/skill/tool/skill-tool.ts +0 -72
  214. package/src/env/skill/types.ts +0 -64
  215. package/src/env/task/delegate/delegate-tool.test.ts +0 -498
  216. package/src/env/task/delegate/delegate-tool.ts +0 -1014
  217. package/src/env/task/delegate/index.ts +0 -18
  218. package/src/env/task/delegate/stop-tool.test.ts +0 -140
  219. package/src/env/task/delegate/stop-tool.ts +0 -119
  220. package/src/env/task/delegate/task-events.test.ts +0 -178
  221. package/src/env/task/delegate/task-events.ts +0 -143
  222. package/src/env/task/hooks/contexts.test.ts +0 -92
  223. package/src/env/task/hooks/contexts.ts +0 -192
  224. package/src/env/task/hooks/index.ts +0 -23
  225. package/src/env/task/hooks/task-hook-points.test.ts +0 -32
  226. package/src/env/task/hooks/task-hook-points.ts +0 -54
  227. package/src/env/task/index.ts +0 -7
  228. package/src/env/task/plugins/index.ts +0 -13
  229. package/src/env/task/plugins/task-plugin.test.ts +0 -74
  230. package/src/env/task/plugins/task-plugin.ts +0 -89
  231. package/src/env/task/plugins/task-tag-plugin.test.ts +0 -377
  232. package/src/env/task/plugins/task-tag-plugin.ts +0 -319
  233. package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +0 -226
  234. package/src/env/task/plugins/workflow-extractor-agent.test.ts +0 -107
  235. package/src/env/task/plugins/workflow-extractor-agent.ts +0 -225
  236. package/src/env/task/storage/index.ts +0 -6
  237. package/src/env/task/storage/sqlite-task-store.test.ts +0 -283
  238. package/src/env/task/storage/sqlite-task-store.ts +0 -903
  239. package/src/env/task/storage/task-search.test.ts +0 -291
  240. package/src/env/task/tag-service.test.ts +0 -198
  241. package/src/env/task/tag-service.ts +0 -264
  242. package/src/env/task/task-component.test.ts +0 -193
  243. package/src/env/task/task-component.ts +0 -658
  244. package/src/env/task/task-config-registration.test.ts +0 -57
  245. package/src/env/task/task-config-registration.ts +0 -37
  246. package/src/env/task/task-types.test.ts +0 -137
  247. package/src/env/task/tools/complete-tool.ts +0 -44
  248. package/src/env/task/tools/create-tool.ts +0 -49
  249. package/src/env/task/tools/delete-tool.ts +0 -43
  250. package/src/env/task/tools/get-tool.ts +0 -59
  251. package/src/env/task/tools/index.ts +0 -10
  252. package/src/env/task/tools/list-tool.ts +0 -40
  253. package/src/env/task/tools/operation/create-tool.ts +0 -48
  254. package/src/env/task/tools/operation/delete-tool.ts +0 -43
  255. package/src/env/task/tools/operation/get-tool.ts +0 -43
  256. package/src/env/task/tools/operation/index.ts +0 -9
  257. package/src/env/task/tools/operation/list-tool.ts +0 -40
  258. package/src/env/task/tools/operation/operation-tools.test.ts +0 -274
  259. package/src/env/task/tools/operation/operation-types.ts +0 -75
  260. package/src/env/task/tools/operation/update-tool.ts +0 -47
  261. package/src/env/task/tools/task-tools.test.ts +0 -203
  262. package/src/env/task/tools/task-types.test.ts +0 -75
  263. package/src/env/task/tools/task-types.ts +0 -68
  264. package/src/env/task/tools/update-tool.ts +0 -70
  265. package/src/env/task/types.ts +0 -160
  266. package/src/env/tool/built-in/bash.ts +0 -201
  267. package/src/env/tool/built-in/echo.ts +0 -29
  268. package/src/env/tool/built-in/edit-file.test.ts +0 -136
  269. package/src/env/tool/built-in/edit-file.ts +0 -92
  270. package/src/env/tool/built-in/glob.test.ts +0 -94
  271. package/src/env/tool/built-in/glob.ts +0 -65
  272. package/src/env/tool/built-in/grep.test.ts +0 -122
  273. package/src/env/tool/built-in/grep.ts +0 -108
  274. package/src/env/tool/built-in/index.ts +0 -44
  275. package/src/env/tool/built-in/read-file.test.ts +0 -84
  276. package/src/env/tool/built-in/read-file.ts +0 -75
  277. package/src/env/tool/built-in/write-file.test.ts +0 -119
  278. package/src/env/tool/built-in/write-file.ts +0 -68
  279. package/src/env/tool/index.ts +0 -24
  280. package/src/env/tool/registry.test.ts +0 -257
  281. package/src/env/tool/registry.ts +0 -167
  282. package/src/env/tool/tool-component.test.ts +0 -559
  283. package/src/env/tool/tool-component.ts +0 -563
  284. package/src/env/tool/tool-config-registration.test.ts +0 -249
  285. package/src/env/tool/tool-config-registration.ts +0 -46
  286. package/src/env/tool/types.ts +0 -267
  287. package/src/env/tool/validator.test.ts +0 -143
  288. package/src/env/tool/validator.ts +0 -44
  289. package/src/env/types.ts +0 -180
  290. package/src/env/workflow/ask-user-tool-registration.test.ts +0 -216
  291. package/src/env/workflow/complex-workflow.integration.test.ts +0 -1900
  292. package/src/env/workflow/decorators/decorator-node.ts +0 -229
  293. package/src/env/workflow/decorators/decorator.test.ts +0 -196
  294. package/src/env/workflow/decorators/edge.ts +0 -82
  295. package/src/env/workflow/decorators/index.ts +0 -31
  296. package/src/env/workflow/decorators/node-as.ts +0 -98
  297. package/src/env/workflow/decorators/workflow.ts +0 -54
  298. package/src/env/workflow/engine/dag-manager.test.ts +0 -570
  299. package/src/env/workflow/engine/dag-manager.ts +0 -594
  300. package/src/env/workflow/engine/engine.ts +0 -1422
  301. package/src/env/workflow/engine/event-bus.test.ts +0 -359
  302. package/src/env/workflow/engine/event-bus.ts +0 -156
  303. package/src/env/workflow/engine/executor-agent-session.test.ts +0 -84
  304. package/src/env/workflow/engine/executor.test.ts +0 -619
  305. package/src/env/workflow/engine/executor.ts +0 -593
  306. package/src/env/workflow/engine/index.ts +0 -24
  307. package/src/env/workflow/engine/node-registry.test.ts +0 -560
  308. package/src/env/workflow/engine/node-registry.ts +0 -289
  309. package/src/env/workflow/engine/resume-removed.test.ts +0 -22
  310. package/src/env/workflow/engine/scheduler.test.ts +0 -715
  311. package/src/env/workflow/engine/scheduler.ts +0 -318
  312. package/src/env/workflow/engine/workflow-engine.test.ts +0 -815
  313. package/src/env/workflow/extractor/workflow-converter.ts +0 -306
  314. package/src/env/workflow/fixtures.ts +0 -380
  315. package/src/env/workflow/index.ts +0 -38
  316. package/src/env/workflow/integration/run-resume-unified.test.ts +0 -186
  317. package/src/env/workflow/integration/service-integration.test.ts +0 -267
  318. package/src/env/workflow/metadata/keys.ts +0 -12
  319. package/src/env/workflow/nodes/agent-component-adapter.test.ts +0 -318
  320. package/src/env/workflow/nodes/agent-component-adapter.ts +0 -448
  321. package/src/env/workflow/nodes/agent-node.test.ts +0 -371
  322. package/src/env/workflow/nodes/agent-node.ts +0 -598
  323. package/src/env/workflow/nodes/ask-user-node.ts +0 -113
  324. package/src/env/workflow/nodes/condition-node.ts +0 -200
  325. package/src/env/workflow/nodes/index.ts +0 -9
  326. package/src/env/workflow/nodes/merge-node.ts +0 -141
  327. package/src/env/workflow/nodes/skill-node.test.ts +0 -253
  328. package/src/env/workflow/nodes/skill-node.ts +0 -393
  329. package/src/env/workflow/nodes/tool-node.test.ts +0 -251
  330. package/src/env/workflow/nodes/tool-node.ts +0 -493
  331. package/src/env/workflow/nodes/workflow-llm-history.test.ts +0 -455
  332. package/src/env/workflow/nodes/workflow-node.test.ts +0 -315
  333. package/src/env/workflow/nodes/workflow-node.ts +0 -311
  334. package/src/env/workflow/service/index.ts +0 -27
  335. package/src/env/workflow/service/registry.test.ts +0 -133
  336. package/src/env/workflow/service/registry.ts +0 -71
  337. package/src/env/workflow/service/workflow-service.test.ts +0 -310
  338. package/src/env/workflow/service/workflow-service.ts +0 -393
  339. package/src/env/workflow/storage/index.ts +0 -28
  340. package/src/env/workflow/storage/mock-repositories.ts +0 -385
  341. package/src/env/workflow/storage/sqlite.test.ts +0 -179
  342. package/src/env/workflow/storage/sqlite.ts +0 -163
  343. package/src/env/workflow/storage/workflow-repo.test.ts +0 -780
  344. package/src/env/workflow/storage/workflow-repo.ts +0 -342
  345. package/src/env/workflow/tools/ask-user-tool.ts +0 -82
  346. package/src/env/workflow/tools/index.ts +0 -26
  347. package/src/env/workflow/tools/run-workflow.test.ts +0 -352
  348. package/src/env/workflow/tools/run-workflow.ts +0 -214
  349. package/src/env/workflow/types/context.ts +0 -18
  350. package/src/env/workflow/types/decorators-types.ts +0 -198
  351. package/src/env/workflow/types/event.test.ts +0 -515
  352. package/src/env/workflow/types/event.ts +0 -193
  353. package/src/env/workflow/types/index.ts +0 -49
  354. package/src/env/workflow/types/run.test.ts +0 -437
  355. package/src/env/workflow/types/run.ts +0 -173
  356. package/src/env/workflow/types/workflow-hil.ts +0 -114
  357. package/src/env/workflow/types/workflow-message.test.ts +0 -138
  358. package/src/env/workflow/types/workflow-message.ts +0 -196
  359. package/src/env/workflow/types/workflow-session.test.ts +0 -95
  360. package/src/env/workflow/types/workflow-session.ts +0 -59
  361. package/src/env/workflow/types/workflow.test.ts +0 -495
  362. package/src/env/workflow/types/workflow.ts +0 -195
  363. package/src/env/workflow/types_compat.ts +0 -51
  364. package/src/env/workflow/utils/create-workflow.ts +0 -47
  365. package/src/env/workflow/utils/execution-state.ts +0 -245
  366. package/src/env/workflow/utils/index.ts +0 -18
  367. package/src/env/workflow/utils/node-registry-helper.ts +0 -58
  368. package/src/env/workflow/utils/recovery-validator.test.ts +0 -460
  369. package/src/env/workflow/utils/recovery-validator.ts +0 -377
  370. package/src/env/workflow/utils/session-parser.test.ts +0 -111
  371. package/src/env/workflow/utils/session-parser.ts +0 -94
  372. package/src/env/workflow/utils/session-recovery.test.ts +0 -334
  373. package/src/env/workflow/utils/session-recovery.ts +0 -188
  374. package/src/env/workflow/utils/template-resolver.test.ts +0 -258
  375. package/src/env/workflow/utils/template-resolver.ts +0 -436
  376. package/src/env/workflow/utils/validation-rules.ts +0 -149
  377. package/src/env/workflow/workflow-component.ts +0 -544
  378. package/src/index.ts +0 -422
  379. package/src/utils/id.ts +0 -21
@@ -1,1379 +0,0 @@
1
- /**
2
- * EventSourceComponent
3
- *
4
- * 事件源组件,管理所有事件源的注册、配置和生命周期
5
- */
6
-
7
- import { ChildProcess, spawn, exec } from "child_process";
8
- import { promisify } from "util";
9
-
10
- // execAsync 函数,在测试时会被 mock 替换
11
- let execAsyncImpl: (command: string) => Promise<{ stdout: string; stderr: string }> =
12
- promisify(exec);
13
-
14
- /**
15
- * 设置 execAsync 实现(用于测试)
16
- */
17
- export function setExecAsync(fn: (command: string) => Promise<{ stdout: string; stderr: string }>): void {
18
- execAsyncImpl = fn;
19
- }
20
-
21
- /**
22
- * 获取 execAsync 函数
23
- */
24
- function getExecAsync(): (command: string) => Promise<{ stdout: string; stderr: string }> {
25
- return execAsyncImpl;
26
- }
27
- import { join } from "path";
28
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
29
- import { BaseComponent, type ComponentConfig } from "../component";
30
- import type { ConfigComponent } from "../../config/config-component";
31
- import { toEnvKey, envKeyToConfigKey } from "../../config/env-key";
32
- import type {
33
- EventSourceConfig,
34
- EventSourceStatus,
35
- EventSourceEvent,
36
- EventSourceEventHandler,
37
- EventSourceType,
38
- EventSourcePersistenceConfig,
39
- ReplyChannel,
40
- EventMetadata,
41
- RecommendedAction
42
- } from "./types";
43
- import { createLogger } from "../log-trace/logger";
44
- import { EVENT_SOURCE_CONFIG_REGISTRATION, EVENT_SOURCE_DEFAULTS } from "./event-source-config-registration";
45
-
46
- const logger = createLogger("event-source");
47
-
48
- // ============================================================================
49
- // EventSourceComponentOptions
50
- // ============================================================================
51
-
52
- /**
53
- * EventSourceComponent 配置选项
54
- */
55
- export interface EventSourceComponentOptions {
56
- /** ConfigComponent 实例(必填) */
57
- configComponent: ConfigComponent;
58
-
59
- /** 配置文件相对路径(可选) */
60
- configPath?: string;
61
-
62
- /** 直接传入的配置对象(可选,优先级最高) */
63
- config?: Partial<EventSourceConfigOptions>;
64
- }
65
-
66
- /**
67
- * EventSourceComponent 内部配置
68
- */
69
- interface EventSourceConfigOptions {
70
- enabled: boolean;
71
- persistenceEnabled: boolean;
72
- configPath: string;
73
- }
74
-
75
- // ============================================================================
76
- // EventSourceComponent
77
- // ============================================================================
78
-
79
- /**
80
- * EventSourceComponent
81
- *
82
- * 事件源组件,管理所有事件源的注册、配置和生命周期
83
- */
84
- // ============================================================================
85
- // ProcessInfo
86
- // ============================================================================
87
-
88
- /**
89
- * 扩展的进程信息接口
90
- */
91
- interface ProcessInfo {
92
- /** 基础进程对象 */
93
- child: ChildProcess;
94
- /** 进程组 ID */
95
- pgid?: number;
96
- /** 相关进程列表 */
97
- pids: number[];
98
- /** 执行的命令 */
99
- command?: string;
100
- }
101
-
102
- // ============================================================================
103
- // EventSourceComponent
104
- // ============================================================================
105
-
106
- export class EventSourceComponent extends BaseComponent {
107
- readonly name = "event-source";
108
- readonly version = "1.0.0";
109
-
110
- // 内部状态
111
- private sources: Map<string, EventSourceConfig> = new Map();
112
- private statuses: Map<string, EventSourceStatus> = new Map();
113
- private processes: Map<string, ProcessInfo> = new Map();
114
- private handlers: Map<string, EventSourceEventHandler> = new Map();
115
- private timers: Map<string, NodeJS.Timeout> = new Map();
116
- private buffers: Map<string, string> = new Map();
117
-
118
- // 持久化配置
119
- private configPath: string = "";
120
- private persistenceEnabled: boolean = true;
121
-
122
- /** ConfigComponent 用于配置管理 */
123
- private configComponent?: ConfigComponent;
124
-
125
- /** 配置热更新 watcher 清理函数 */
126
- private configWatcher?: () => void;
127
-
128
- // ========================================================================
129
- // 组件生命周期
130
- // ========================================================================
131
-
132
- /**
133
- * 初始化
134
- */
135
- protected async onInit(): Promise<void> {
136
- // 配置注册已在 init() 方法中完成(通过 ComponentConfig.options 获取)
137
- // 加载已保存的事件源配置
138
- await this.loadConfig();
139
- }
140
-
141
- /**
142
- * 公开的初始化方法,用于子类覆盖
143
- *
144
- * 这个方法会被 BaseComponent.init() 调用,用于执行子类特有的初始化逻辑。
145
- * 它接收 ComponentConfig,并通过 ComponentConfig.options 获取 EventSourceComponentOptions。
146
- */
147
- async init(options?: ComponentConfig): Promise<void> {
148
- // 如果提供了 env,则注入
149
- if (options?.env) {
150
- this.env = options.env;
151
- }
152
-
153
- this.setStatus("initializing");
154
-
155
- if (options?.name) Object.defineProperty(this, "name", { value: options.name, writable: false });
156
- if (options?.version) Object.defineProperty(this, "version", { value: options.version, writable: false });
157
- if (options?.enabled !== undefined) this._enabled = options.enabled;
158
-
159
- // 从 options 获取 EventSourceComponentOptions
160
- const opts = options?.options as unknown as EventSourceComponentOptions | undefined;
161
- if (opts?.configComponent) {
162
- this.configComponent = opts.configComponent;
163
- await this.registerConfig(opts);
164
- } else {
165
- throw new Error("ConfigComponent is required for EventSourceComponent initialization");
166
- }
167
-
168
- await this.onInit();
169
- this.setStatus("running");
170
- }
171
-
172
- protected async onStart(): Promise<void> {
173
- // onStart 时不再重复加载(已在 onInit 中加载)
174
- }
175
-
176
- protected async onStop(): Promise<void> {
177
- // 清理配置 watcher
178
- this.configWatcher?.();
179
- this.configWatcher = undefined;
180
-
181
- // 停止所有运行中的事件源
182
- const runningSources = Array.from(this.statuses.entries())
183
- .filter(([_, status]) => status === "running")
184
- .map(([id]) => id);
185
-
186
- for (const id of runningSources) {
187
- await this.stopSource(id);
188
- }
189
-
190
- // 清空所有状态
191
- this.sources.clear();
192
- this.statuses.clear();
193
- this.processes.clear();
194
- this.handlers.clear();
195
- this.timers.clear();
196
- this.buffers.clear();
197
- }
198
-
199
- // ========================================================================
200
- // 配置注册
201
- // ========================================================================
202
-
203
- /**
204
- * 注册配置到 ConfigComponent
205
- *
206
- * 配置加载顺序(优先级从低到高):
207
- * 1. Defaults - 默认值(最低)
208
- * 2. FileSource - 从配置文件加载
209
- * 3. EnvSource - 从环境变量加载
210
- * 4. config 对象 - 直接传入的配置(最高)
211
- */
212
- private async registerConfig(options: EventSourceComponentOptions): Promise<void> {
213
- const configComponent = options.configComponent;
214
- if (!configComponent) return;
215
-
216
- const { configPath, config } = options;
217
- const envPrefix = "EVENT_SOURCE";
218
-
219
- // 1. 使用 registerComponent 注册配置结构(keys 和 sources,不含 defaults)
220
- configComponent.registerComponent(EVENT_SOURCE_CONFIG_REGISTRATION);
221
-
222
- // 2. 注册 FileSource(如果提供了 configPath)
223
- if (configPath) {
224
- configComponent.registerSource({
225
- type: "file",
226
- relativePath: configPath,
227
- optional: true,
228
- watch: false
229
- });
230
- }
231
-
232
- // 3. 注册 EnvSource
233
- configComponent.registerSource({
234
- type: "env",
235
- envPrefix: envPrefix,
236
- priority: 20,
237
- watch: false
238
- });
239
-
240
- // 4. 从 sources 加载配置(FileSource 和 EnvSource)
241
- await configComponent.load("event-source");
242
-
243
- // 5. 后备方案:直接读取环境变量(确保所有环境变量都被处理)
244
- for (const envKey of Object.keys(process.env)) {
245
- const configKey = envKeyToConfigKey(envKey, envPrefix, "event-source");
246
- if (!configKey) continue;
247
- const value = process.env[envKey];
248
- if (value !== undefined) {
249
- await configComponent.set(configKey, value);
250
- }
251
- }
252
-
253
- // 6. 设置默认值(只有当配置不存在时)
254
- for (const [key, value] of Object.entries(EVENT_SOURCE_DEFAULTS)) {
255
- if (configComponent.get(key) === undefined) {
256
- await configComponent.set(key, value);
257
- }
258
- }
259
-
260
- // 6.5 特殊处理 configPath 动态默认值
261
- if (configComponent.get("event-source.configPath") === undefined) {
262
- // 默认路径: ~/.roy-agent/event-sources.json
263
- const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
264
- const dataDir = join(home, ".roy-agent");
265
- await configComponent.set("event-source.configPath", join(dataDir, "event-sources.json"));
266
- }
267
-
268
- // 7. 直接配置对象(最高优先级)
269
- if (config) {
270
- const flatConfig = this.flattenConfig(config);
271
- for (const [key, value] of Object.entries(flatConfig)) {
272
- await configComponent.set(key, value);
273
- }
274
- }
275
-
276
- // 8. 注册配置热更新监听
277
- this.registerConfigWatcher(configComponent);
278
- }
279
-
280
- /**
281
- * 构建最终配置对象
282
- */
283
- private buildConfig(): EventSourceConfigOptions {
284
- const configComponent = this.configComponent;
285
- if (!configComponent) {
286
- return {
287
- enabled: true,
288
- persistenceEnabled: true,
289
- configPath: "",
290
- };
291
- }
292
-
293
- return {
294
- enabled: configComponent.get("event-source.enabled") as boolean ?? true,
295
- persistenceEnabled: configComponent.get("event-source.persistenceEnabled") as boolean ?? true,
296
- configPath: configComponent.get("event-source.configPath") as string ?? "",
297
- };
298
- }
299
-
300
- /**
301
- * 将配置对象展平为点号路径
302
- */
303
- private flattenConfig(
304
- obj: Record<string, unknown>,
305
- prefix = "event-source"
306
- ): Record<string, unknown> {
307
- const result: Record<string, unknown> = {};
308
- for (const [key, value] of Object.entries(obj)) {
309
- const fullKey = `${prefix}.${key}`;
310
- if (value && typeof value === "object" && !Array.isArray(value)) {
311
- Object.assign(
312
- result,
313
- this.flattenConfig(value as Record<string, unknown>, fullKey)
314
- );
315
- } else {
316
- result[fullKey] = value;
317
- }
318
- }
319
- return result;
320
- }
321
-
322
- /**
323
- * 注册配置热更新监听
324
- */
325
- private registerConfigWatcher(configComponent: ConfigComponent): void {
326
- if (typeof configComponent.watch !== "function") {
327
- return;
328
- }
329
-
330
- this.configWatcher = configComponent.watch("event-source.*", (event) => {
331
- this.onConfigChange(event);
332
- });
333
- }
334
-
335
- /**
336
- * 处理配置变更
337
- */
338
- protected onConfigChange(event: { key: string; oldValue?: unknown; newValue?: unknown }): void {
339
- logger.info(`EventSource config changed: ${event.key}`, {
340
- oldValue: event.oldValue,
341
- newValue: event.newValue
342
- });
343
- }
344
-
345
- // ========================================================================
346
- // 事件源注册与管理
347
- // ========================================================================
348
-
349
- /**
350
- * 注册事件源
351
- */
352
- register(config: EventSourceConfig): void {
353
- if (this.sources.has(config.id)) {
354
- throw new Error(`EventSource already exists: ${config.id}`);
355
- }
356
- this.sources.set(config.id, config);
357
- this.statuses.set(config.id, "created");
358
- logger.debug(`EventSource registered: ${config.id} (${config.type})`);
359
-
360
- // 自动保存配置
361
- this.saveConfig().catch((err) => {
362
- logger.error(`Failed to save config after registering ${config.id}:`, err);
363
- });
364
- }
365
-
366
- /**
367
- * 注销事件源
368
- */
369
- unregister(id: string): boolean {
370
- const status = this.statuses.get(id);
371
- if (status === "running") {
372
- this.stopSource(id); // 自动停止
373
- }
374
-
375
- const existed = this.sources.has(id);
376
- this.sources.delete(id);
377
- this.statuses.delete(id);
378
- this.handlers.delete(id);
379
- this.buffers.delete(id);
380
-
381
- if (existed) {
382
- logger.debug(`EventSource unregistered: ${id}`);
383
- // 自动保存配置
384
- this.saveConfig().catch((err) => {
385
- logger.error(`Failed to save config after unregistering ${id}:`, err);
386
- });
387
- }
388
- return existed;
389
- }
390
-
391
- /**
392
- * 获取事件源配置
393
- */
394
- get(id: string): EventSourceConfig | undefined {
395
- return this.sources.get(id);
396
- }
397
-
398
- /**
399
- * 列出所有事件源
400
- */
401
- list(): EventSourceConfig[] {
402
- return Array.from(this.sources.values());
403
- }
404
-
405
- /**
406
- * 获取事件源状态
407
- */
408
- getEventSourceStatus(id: string): EventSourceStatus | undefined {
409
- return this.statuses.get(id);
410
- }
411
-
412
- /**
413
- * 获取指定事件源的状态
414
- */
415
- getEventSourceStatusById(id: string): EventSourceStatus | undefined {
416
- return this.statuses.get(id);
417
- }
418
-
419
- /**
420
- * 设置事件处理器
421
- */
422
- onEvent(id: string, handler: EventSourceEventHandler): void {
423
- this.handlers.set(id, handler);
424
- }
425
-
426
- /**
427
- * 移除事件处理器
428
- */
429
- offEvent(id: string): void {
430
- this.handlers.delete(id);
431
- }
432
-
433
- // ========================================================================
434
- // 生命周期控制
435
- // ========================================================================
436
-
437
- /**
438
- * 启动事件源
439
- *
440
- * 注意:方法名为 startSource 而非 start,以避免覆盖 Component 接口的 start() 方法
441
- */
442
- async startSource(id: string): Promise<void> {
443
- const config = this.sources.get(id);
444
- if (!config) {
445
- throw new Error(`EventSource not found: ${id}`);
446
- }
447
-
448
- const currentStatus = this.statuses.get(id);
449
- if (currentStatus === "running") {
450
- return; // 已运行
451
- }
452
-
453
- this.statuses.set(id, "starting");
454
-
455
- try {
456
- switch (config.type) {
457
- case "lark-cli":
458
- await this.startLarkCli(id, config);
459
- break;
460
- case "timer":
461
- await this.startTimer(id, config);
462
- break;
463
- case "websocket":
464
- await this.startWebSocket(id, config);
465
- break;
466
- default:
467
- throw new Error(`Unsupported event source type: ${config.type}`);
468
- }
469
-
470
- this.statuses.set(id, "running");
471
-
472
- // 发布事件
473
- this.getEnv()?.pushEnvEvent({
474
- type: "event-source.started",
475
- payload: { sourceId: id, sourceType: config.type },
476
- });
477
-
478
- logger.info(`EventSource started: ${id} (${config.type})`);
479
-
480
- } catch (error) {
481
- this.statuses.set(id, "error");
482
- logger.error(`EventSource failed to start: ${id}`, error);
483
- throw error;
484
- }
485
- }
486
-
487
- /**
488
- * 停止事件源
489
- *
490
- * 注意:方法名为 stopSource 而非 stop,以避免覆盖 Component 接口的 stop() 方法
491
- */
492
- async stopSource(id: string): Promise<void> {
493
- const status = this.statuses.get(id);
494
- logger.info(`[EventSource ${id}] stopSource called, current status: ${status}`);
495
-
496
- // 停止任意非 stopped 状态的事件源
497
- if (status === "stopped" || status === "created") {
498
- logger.info(`[EventSource ${id}] already stopped/created, skipping`);
499
- return;
500
- }
501
-
502
- this.statuses.set(id, "stopping");
503
-
504
- // 停止子进程
505
- const processInfo = this.processes.get(id);
506
- const child = processInfo?.child;
507
- logger.info(`[EventSource ${id}] process info: ${processInfo ? `pid=${processInfo.child.pid}` : "none"}`);
508
-
509
- if (processInfo && child) {
510
- // 清理事件监听器,防止内存泄漏
511
- child.removeAllListeners("exit");
512
- child.removeAllListeners("error");
513
- child.stdout?.removeAllListeners("data");
514
- child.stderr?.removeAllListeners("data");
515
-
516
- // 杀死所有相关进程的辅助函数
517
- const killProcess = (pid: number, signal: string): boolean => {
518
- try {
519
- process.kill(pid, signal);
520
- return true;
521
- } catch (e) {
522
- return false;
523
- }
524
- };
525
-
526
- // 尝试 SIGTERM(优雅终止)给所有相关进程
527
- const pidsToKill = processInfo.pids.length > 0 ? processInfo.pids : (processInfo.child.pid ? [processInfo.child.pid] : []);
528
- logger.info(`[EventSource ${id}] sending SIGTERM to ${pidsToKill.length} processes: ${pidsToKill.join(", ")}`);
529
-
530
- for (const pid of pidsToKill) {
531
- killProcess(pid, "SIGTERM");
532
- }
533
-
534
- // 等待进程退出,最多 3 秒
535
- await new Promise<void>((resolve) => {
536
- const timeout = setTimeout(() => {
537
- // SIGTERM 超时,使用 SIGKILL 强制杀死
538
- logger.warn(`[EventSource ${id}] SIGTERM timeout, sending SIGKILL to ${pidsToKill.length} processes`);
539
- for (const pid of pidsToKill) {
540
- killProcess(pid, "SIGKILL");
541
- }
542
- resolve();
543
- }, 3000);
544
-
545
- // 安全地注册退出监听器
546
- try {
547
- const onceResult = child.once?.("exit", () => {
548
- clearTimeout(timeout);
549
- // 即使主进程退出,也确保其他相关进程被杀死
550
- for (const pid of pidsToKill) {
551
- if (pid !== processInfo.child.pid) {
552
- killProcess(pid, "SIGKILL");
553
- }
554
- }
555
- resolve();
556
- });
557
- // 如果 once 不返回支持链式调用的对象,也尝试使用 on
558
- if (!onceResult || typeof onceResult.once !== "function") {
559
- child.on("exit", () => {
560
- clearTimeout(timeout);
561
- for (const pid of pidsToKill) {
562
- if (pid !== processInfo.child.pid) {
563
- killProcess(pid, "SIGKILL");
564
- }
565
- }
566
- resolve();
567
- });
568
- }
569
- } catch (e) {
570
- logger.warn(`[EventSource ${id}] failed to register exit listener: ${e}`);
571
- // 继续执行,让超时处理清理
572
- }
573
- });
574
-
575
- this.processes.delete(id);
576
- } else if (child) {
577
- // 兼容旧格式:直接存储 ChildProcess
578
- // 清理事件监听器,防止内存泄漏
579
- child.removeAllListeners("exit");
580
- child.removeAllListeners("error");
581
- child.stdout?.removeAllListeners("data");
582
- child.stderr?.removeAllListeners("data");
583
-
584
- const pid = (child as ChildProcess).pid!;
585
- const pgid = (child as any).pgid;
586
-
587
- // 尝试 SIGTERM(优雅终止)
588
- try {
589
- // 1. 首先尝试杀死整个进程组
590
- if (pgid && pgid > 0 && pgid !== pid) {
591
- logger.info(`[EventSource ${id}] killing process group ${pgid}`);
592
- try {
593
- process.kill(-pgid, "SIGTERM");
594
- } catch (e) {
595
- logger.warn(`[EventSource ${id}] failed to kill process group: ${e}`);
596
- }
597
- }
598
-
599
- // 2. 同时杀死主进程
600
- logger.info(`[EventSource ${id}] killing main process ${pid}`);
601
- (child as ChildProcess).kill("SIGTERM");
602
-
603
- // 等待进程退出,最多 3 秒
604
- await new Promise<void>((resolve) => {
605
- const timeout = setTimeout(() => {
606
- // SIGTERM 超时,使用 SIGKILL 强制杀死
607
- logger.warn(`[EventSource ${id}] SIGTERM timeout, sending SIGKILL`);
608
- if (pgid && pgid > 0 && pgid !== pid) {
609
- try {
610
- process.kill(-pgid, "SIGKILL");
611
- } catch (e) {
612
- logger.warn(`[EventSource ${id}] failed to kill process group with SIGKILL: ${e}`);
613
- }
614
- }
615
- (child as ChildProcess).kill("SIGKILL");
616
- resolve();
617
- }, 3000);
618
-
619
- (child as ChildProcess).once("exit", () => {
620
- clearTimeout(timeout);
621
- resolve();
622
- });
623
- });
624
- } catch (error) {
625
- logger.error(`[EventSource ${id}] Failed to kill process:`, error);
626
- // 确保最终使用 SIGKILL
627
- try {
628
- (child as ChildProcess).kill("SIGKILL");
629
- } catch {
630
- // ignore
631
- }
632
- }
633
-
634
- this.processes.delete(id);
635
- } else {
636
- logger.warn(`[EventSource ${id}] no child process found in processes map`);
637
- }
638
-
639
- // 停止定时器
640
- const timer = this.timers.get(id);
641
- if (timer) {
642
- clearInterval(timer);
643
- this.timers.delete(id);
644
- }
645
-
646
- // 清理缓冲区
647
- this.buffers.delete(id);
648
-
649
- const config = this.sources.get(id);
650
- this.statuses.set(id, "stopped");
651
-
652
- // 发布事件
653
- this.getEnv()?.pushEnvEvent({
654
- type: "event-source.stopped",
655
- payload: { sourceId: id, sourceType: config?.type },
656
- });
657
-
658
- logger.info(`EventSource stopped: ${id}`);
659
- }
660
-
661
- // ========================================================================
662
- // 跨平台进程查找
663
- // ========================================================================
664
-
665
- /**
666
- * 根据命令模式查找所有相关进程 PID
667
- *
668
- * @param pattern 命令模式,用于匹配进程
669
- * @returns 匹配的 PID 列表
670
- */
671
- private async findRelatedProcesses(pattern: string): Promise<number[]> {
672
- const pids: number[] = [];
673
- const platform = process.platform;
674
-
675
- try {
676
- const exec = getExecAsync();
677
-
678
- if (platform === "win32") {
679
- // Windows: 使用 PowerShell
680
- // 使用 -Command 参数执行 PowerShell 命令
681
- // Get-Process 获取所有进程,然后用 Where-Object 过滤
682
- // 注意:需要处理命令注入,使用 -LiteralPath 或适当转义
683
- const escapedPattern = pattern.replace(/'/g, "''").replace(/"/g, '`"');
684
- const psCommand = `Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${escapedPattern}*' } | Select-Object -ExpandProperty ProcessId`;
685
- const { stdout } = await exec(`powershell -Command "${psCommand}"`);
686
- // 解析输出,提取 PID(处理 \r\n)
687
- const matches = stdout.replace(/\r\n/g, "\n").match(/\d+/g);
688
- if (matches) {
689
- for (const match of matches) {
690
- const pid = parseInt(match, 10);
691
- if (!isNaN(pid) && pid > 0) {
692
- pids.push(pid);
693
- }
694
- }
695
- }
696
- } else {
697
- // Unix (Linux/macOS): 使用 pgrep
698
- // 用引号包裹模式,避免注入
699
- const { stdout } = await exec(`pgrep -f "${pattern.replace(/"/g, '\\"')}"`);
700
- const lines = stdout.trim().split("\n");
701
- for (const line of lines) {
702
- const trimmed = line.trim();
703
- if (trimmed) {
704
- const pid = parseInt(trimmed, 10);
705
- if (!isNaN(pid) && pid > 0) {
706
- pids.push(pid);
707
- }
708
- }
709
- }
710
- }
711
- } catch (error) {
712
- // pgrep 在没有匹配进程时返回错误,这是正常的
713
- logger.debug(`[EventSource] findRelatedProcesses: no processes found for pattern "${pattern}"`);
714
- }
715
-
716
- return pids;
717
- }
718
-
719
- /**
720
- * 查找并记录所有相关进程 PID
721
- *
722
- * @param id 事件源 ID
723
- */
724
- private async trackRelatedProcesses(id: string): Promise<void> {
725
- const processInfo = this.processes.get(id);
726
- if (!processInfo) return;
727
-
728
- // 查找匹配命令模式的进程
729
- // 使用更宽松的模式来匹配所有相关进程
730
- const command = processInfo?.command || "";
731
-
732
- // 提取命令的关键部分用于匹配(如 "lark-cli")
733
- // 这样可以匹配 node lark-cli 和 lark-cli binary 两种进程
734
- const commandParts = command.split(" ");
735
- const baseCommand = commandParts[0]; // 如 "lark-cli"
736
- const eventKeyword = "event"; // 用于进一步匹配事件订阅相关的进程
737
-
738
- // 先用基础命令查找所有相关进程
739
- let relatedPids = await this.findRelatedProcesses(baseCommand);
740
-
741
- // 如果找到的进程太少,再用更完整的模式查找
742
- if (relatedPids.length < 2) {
743
- const morePids = await this.findRelatedProcesses(eventKeyword);
744
- for (const pid of morePids) {
745
- if (!relatedPids.includes(pid)) {
746
- relatedPids.push(pid);
747
- }
748
- }
749
- }
750
-
751
- // 过滤掉 roy-agent 自身的进程(通过进程名判断)
752
- const ownPid = process.pid;
753
- relatedPids = relatedPids.filter(pid => pid !== ownPid);
754
-
755
- // 添加到已有的 PID 列表中
756
- for (const pid of relatedPids) {
757
- if (!processInfo.pids.includes(pid)) {
758
- processInfo.pids.push(pid);
759
- }
760
- }
761
-
762
- // 确保主进程 PID 也在列表中
763
- const mainPid = processInfo.child.pid;
764
- if (mainPid && !processInfo.pids.includes(mainPid)) {
765
- processInfo.pids.unshift(mainPid);
766
- }
767
-
768
- logger.info(`[EventSource ${id}] tracked ${processInfo.pids.length} related processes: ${processInfo.pids.join(", ")}`);
769
- }
770
-
771
- // ========================================================================
772
- // 事件源启动实现
773
- // ========================================================================
774
-
775
- /**
776
- * 启动 lark-cli 事件源
777
- * 返回 Promise,只有在进程真正成功启动后才 resolve
778
- */
779
- private startLarkCli(id: string, config: EventSourceConfig): Promise<void> {
780
- return new Promise((resolve, reject) => {
781
- const command = config.command || "lark-cli event +subscribe";
782
-
783
- logger.info(`Starting lark-cli event source: ${id}`);
784
- logger.debug(`Executing command: ${command}`);
785
-
786
- // 启动子进程
787
- // 根据平台选择正确的 shell
788
- // - Unix (Linux/macOS): 使用 sh
789
- // - Windows: 使用 cmd.exe
790
- const isWindows = process.platform === "win32";
791
- const shell = isWindows ? "cmd.exe" : "sh";
792
- const shellArgs = isWindows
793
- ? ["/c", command] // Windows: /c 执行命令后退出
794
- : ["-c", `exec ${command}`]; // Unix: -c 执行命令
795
-
796
- const child = spawn(shell, shellArgs, {
797
- stdio: ["ignore", "pipe", "pipe"],
798
- detached: false,
799
- windowsHide: true,
800
- });
801
-
802
- // 存储进程信息,包括主 PID 和后续查找的相关 PID 列表
803
- this.processes.set(id, {
804
- child,
805
- pgid: (child as any).pgid,
806
- pids: [], // 存储所有相关进程 PID
807
- command,
808
- });
809
-
810
- let startupTimeout: NodeJS.Timeout | null = null;
811
- let settled = false;
812
- let startupConfirmed = false;
813
-
814
- // 启动成功时的处理:标记状态并追踪所有相关进程
815
- const handleStartupSuccess = async () => {
816
- if (settled) return;
817
- settled = true;
818
- startupConfirmed = true;
819
- clearTimeout(startupTimeout!);
820
- this.statuses.set(id, "running");
821
- logger.info(`[EventSource ${id}] confirmed running`);
822
-
823
- // 追踪所有相关进程(同步,确保启动后立即记录)
824
- try {
825
- await this.trackRelatedProcesses(id);
826
- } catch (e) {
827
- logger.warn(`[EventSource ${id}] failed to track related processes: ${e}`);
828
- }
829
-
830
- resolve();
831
- };
832
-
833
- // 启动成功确认:1秒内没有退出且没有错误输出,则认为成功
834
- startupTimeout = setTimeout(() => {
835
- if (!settled && !startupConfirmed) {
836
- handleStartupSuccess();
837
- }
838
- }, 1000);
839
-
840
- // 处理输出流
841
- child.stdout?.on("data", (data: Buffer) => {
842
- const output = data.toString();
843
-
844
- // 处理事件流
845
- this.processStream(id, output);
846
-
847
- // 检查是否成功订阅(根据 lark-cli 输出 ok: true)
848
- if (output.includes('"ok": true') || output.includes('"ok":true')) {
849
- if (!settled) {
850
- handleStartupSuccess();
851
- }
852
- }
853
- });
854
-
855
- // 处理错误流
856
- child.stderr?.on("data", (data: Buffer) => {
857
- const output = data.toString();
858
-
859
- // 检查是否是致命错误(如 "another instance is running")
860
- if (output.includes('"ok": false') || output.includes('"ok":false')) {
861
- if (!settled) {
862
- settled = true;
863
- clearTimeout(startupTimeout!);
864
- this.statuses.set(id, "error");
865
- child.kill();
866
- // 提取错误消息
867
- try {
868
- const errorObj = JSON.parse(output);
869
- reject(new Error(errorObj.error?.message || `lark-cli error: ${output}`));
870
- } catch {
871
- reject(new Error(`lark-cli error: ${output}`));
872
- }
873
- }
874
- }
875
- });
876
-
877
- // 进程退出处理(只有非正常退出才拒绝)
878
- child.on("exit", (code) => {
879
- if (!settled) {
880
- if (code !== 0 && code !== null) {
881
- settled = true;
882
- clearTimeout(startupTimeout!);
883
- logger.warn(`[EventSource ${id}] exited with code ${code}`);
884
- this.statuses.set(id, "error");
885
- reject(new Error(`lark-cli exited with code ${code}`));
886
- }
887
- // 如果 code === 0(正常退出但不应该发生),则由 startupTimeout 处理
888
- }
889
- });
890
-
891
- child.on("error", (error) => {
892
- if (!settled) {
893
- settled = true;
894
- clearTimeout(startupTimeout!);
895
- logger.error(`[EventSource ${id}] process error:`, error);
896
- this.statuses.set(id, "error");
897
- reject(error);
898
- }
899
- });
900
- });
901
- }
902
-
903
- /**
904
- * 启动定时器事件源
905
- */
906
- private async startTimer(id: string, config: EventSourceConfig): Promise<void> {
907
- const interval = config.interval || 60000; // 默认 1 分钟
908
-
909
- logger.info(`Starting timer event source: ${id}, interval: ${interval}ms`);
910
-
911
- // 创建定时器
912
- const timer = setInterval(() => {
913
- const message = config.options?.message as string || `Timer event from ${config.name}`;
914
- this.handleEvent(id, JSON.stringify({
915
- type: "timer.tick",
916
- payload: {
917
- message,
918
- timestamp: Date.now(),
919
- },
920
- }));
921
- }, interval);
922
-
923
- this.timers.set(id, timer);
924
-
925
- // 立即触发一次
926
- this.handleEvent(id, JSON.stringify({
927
- type: "timer.tick",
928
- payload: {
929
- message: config.options?.message as string || `Timer event from ${config.name}`,
930
- timestamp: Date.now(),
931
- },
932
- }));
933
- }
934
-
935
- /**
936
- * 启动 WebSocket 事件源
937
- */
938
- private async startWebSocket(id: string, config: EventSourceConfig): Promise<void> {
939
- const url = config.url;
940
- if (!url) {
941
- throw new Error("WebSocket URL is required");
942
- }
943
-
944
- logger.info(`Starting WebSocket event source: ${id}, url: ${url}`);
945
-
946
- // 动态导入 ws 模块
947
- try {
948
- const { WebSocket } = await import("ws");
949
-
950
- const ws = new WebSocket(url, {
951
- headers: config.headers,
952
- });
953
-
954
- // 将 WebSocket 包装为可关闭的进程
955
- const wrapper: ProcessInfo = {
956
- child: {
957
- kill: () => ws.close(),
958
- on: (event: string, cb: (...args: unknown[]) => void) => {
959
- ws.on(event as any, cb as any);
960
- },
961
- } as any,
962
- pids: [],
963
- };
964
-
965
- this.processes.set(id, wrapper);
966
-
967
- ws.on("message", (data: any) => {
968
- this.processStream(id, data.toString());
969
- });
970
-
971
- ws.on("error", (error) => {
972
- logger.error(`[EventSource ${id}] WebSocket error:`, error);
973
- });
974
-
975
- ws.on("close", () => {
976
- logger.info(`[EventSource ${id}] WebSocket closed`);
977
- this.statuses.set(id, "stopped");
978
- });
979
-
980
- } catch (error) {
981
- throw new Error(`Failed to import ws module: ${error}`);
982
- }
983
- }
984
-
985
- // ========================================================================
986
- // 事件处理
987
- // ========================================================================
988
-
989
- /**
990
- * 处理数据流
991
- */
992
- private processStream(sourceId: string, data: string): void {
993
- // 获取或创建缓冲区
994
- let buffer = this.buffers.get(sourceId) || "";
995
- buffer += data;
996
-
997
- // 按行处理 NDJSON
998
- const lines = buffer.split("\n");
999
- buffer = lines.pop() || ""; // 保留未完成行
1000
- this.buffers.set(sourceId, buffer);
1001
-
1002
- for (const line of lines) {
1003
- if (line.trim()) {
1004
- this.handleEvent(sourceId, line);
1005
- }
1006
- }
1007
- }
1008
-
1009
- /**
1010
- * 处理事件
1011
- */
1012
- handleEvent(sourceId: string, rawData: string): void {
1013
- const config = this.sources.get(sourceId);
1014
- if (!config) return;
1015
-
1016
- try {
1017
- // 解析事件
1018
- const rawEvent = JSON.parse(rawData);
1019
-
1020
- // 提取事件类型(新格式优先使用 header.event_type)
1021
- let eventType: string;
1022
- if (rawEvent.header?.event_type) {
1023
- // 新格式:lark-cli 实际输出格式
1024
- eventType = rawEvent.header.event_type;
1025
- } else if (rawEvent.schema) {
1026
- // 旧格式:直接使用 schema
1027
- eventType = `lark.${rawEvent.schema}`;
1028
- } else if (rawEvent.type) {
1029
- // 其他格式
1030
- eventType = rawEvent.type;
1031
- } else {
1032
- eventType = "unknown";
1033
- }
1034
-
1035
- // 检查事件类型过滤
1036
- if (config.eventTypes?.length && !this.matchEventType(eventType, config.eventTypes)) {
1037
- return;
1038
- }
1039
-
1040
- // 提取事件元信息、反馈通道和推荐动作
1041
- const { metadata, replyChannel, recommendedAction } = this.extractMetadata(config.type, rawEvent, eventType);
1042
-
1043
- // 格式化消息
1044
- const message = this.formatEventMessage(config.type, rawEvent, eventType);
1045
-
1046
- // 创建事件对象
1047
- const event: EventSourceEvent = {
1048
- sourceId,
1049
- type: eventType,
1050
- timestamp: Date.now(),
1051
- payload: {
1052
- sourceId,
1053
- sourceType: config.type,
1054
- rawEvent,
1055
- message,
1056
- metadata,
1057
- replyChannel,
1058
- recommendedAction,
1059
- timestamp: Date.now(),
1060
- },
1061
- };
1062
-
1063
- // 调用处理器
1064
- const handler = this.handlers.get(sourceId);
1065
- if (handler) {
1066
- try {
1067
- const result = handler(event);
1068
- if (result instanceof Promise) {
1069
- result.catch((error) => {
1070
- logger.error(`[EventSource ${sourceId}] Handler error:`, error);
1071
- });
1072
- }
1073
- } catch (error) {
1074
- logger.error(`[EventSource ${sourceId}] Handler error:`, error);
1075
- }
1076
- }
1077
-
1078
- // 发布到环境事件总线
1079
- this.getEnv()?.pushEnvEvent({
1080
- type: `event-source.event.${config.type}`,
1081
- payload: event,
1082
- });
1083
-
1084
- } catch (error) {
1085
- // JSON 解析失败,尝试作为普通文本处理
1086
- if (error instanceof SyntaxError) {
1087
- logger.debug(`[EventSource ${sourceId}] Non-JSON data: ${rawData.substring(0, 100)}`);
1088
-
1089
- // 作为普通消息处理
1090
- const config = this.sources.get(sourceId);
1091
- if (config) {
1092
- const event: EventSourceEvent = {
1093
- sourceId,
1094
- type: "raw",
1095
- timestamp: Date.now(),
1096
- payload: {
1097
- sourceId,
1098
- sourceType: config.type,
1099
- rawEvent: rawData,
1100
- message: rawData,
1101
- metadata: {},
1102
- timestamp: Date.now(),
1103
- },
1104
- };
1105
-
1106
- const handler = this.handlers.get(sourceId);
1107
- if (handler) {
1108
- handler(event);
1109
- }
1110
- }
1111
- } else {
1112
- logger.error(`[EventSource ${sourceId}] Failed to handle event:`, error);
1113
- }
1114
- }
1115
- }
1116
-
1117
- /**
1118
- * 事件类型匹配(支持通配符)
1119
- */
1120
- private matchEventType(eventType: string, patterns: string[]): boolean {
1121
- for (const pattern of patterns) {
1122
- if (pattern === "*") return true;
1123
- if (pattern.endsWith(".*")) {
1124
- const prefix = pattern.slice(0, -2);
1125
- if (eventType.startsWith(prefix + ".") || eventType === prefix) return true;
1126
- }
1127
- if (eventType === pattern) return true;
1128
- }
1129
- return false;
1130
- }
1131
-
1132
- /**
1133
- * 提取事件元信息
1134
- */
1135
- private extractMetadata(sourceType: EventSourceType, rawEvent: unknown, eventType?: string): {
1136
- metadata: EventMetadata;
1137
- replyChannel?: ReplyChannel;
1138
- recommendedAction?: RecommendedAction;
1139
- } {
1140
- const event = rawEvent as Record<string, unknown>;
1141
- const metadata: EventMetadata = {};
1142
- let replyChannel: ReplyChannel | undefined;
1143
- let recommendedAction: RecommendedAction | undefined;
1144
-
1145
- switch (sourceType) {
1146
- case "lark-cli":
1147
- // 飞书事件元信息
1148
- // 消息可能在 event.message(新版)或直接在 message(旧版)
1149
- const larkInnerEvent = event.event as Record<string, unknown>;
1150
- const header = event.header as Record<string, unknown>;
1151
- const message = (larkInnerEvent?.message || event.message) as Record<string, unknown> | undefined;
1152
- const sender = (larkInnerEvent?.sender || event.sender) as Record<string, unknown> | undefined;
1153
- const senderId = sender?.sender_id as Record<string, unknown>;
1154
-
1155
- if (header) {
1156
- metadata.eventType = header.event_type as string;
1157
- metadata.appId = header.app_id as string;
1158
- metadata.tenantKey = header.tenant_key as string;
1159
- }
1160
- if (message) {
1161
- metadata.chatId = message.chat_id as string;
1162
- metadata.chatType = message.chat_type as string;
1163
- metadata.messageId = message.message_id as string;
1164
- metadata.messageType = message.message_type as string;
1165
- }
1166
- if (senderId) {
1167
- metadata.senderId = senderId.open_id as string || senderId.user_id as string || senderId.union_id as string;
1168
- }
1169
-
1170
- // 飞书消息的反馈通道和推荐动作
1171
- if (eventType === "im.message.receive_v1") {
1172
- replyChannel = {
1173
- type: "lark-cli",
1174
- appId: metadata.appId,
1175
- chatId: metadata.chatId,
1176
- messageId: metadata.messageId,
1177
- };
1178
- recommendedAction = {
1179
- action: "处理飞书消息并回复",
1180
- replyTo: {
1181
- appId: metadata.appId,
1182
- chatId: metadata.chatId,
1183
- messageId: metadata.messageId,
1184
- },
1185
- };
1186
- }
1187
- break;
1188
-
1189
- case "timer":
1190
- // 定时器元信息
1191
- metadata.eventType = "timer.tick";
1192
- recommendedAction = {
1193
- action: "执行定时任务",
1194
- };
1195
- break;
1196
-
1197
- case "websocket":
1198
- // WebSocket 元信息
1199
- metadata.eventType = eventType;
1200
- replyChannel = {
1201
- type: "websocket",
1202
- };
1203
- recommendedAction = {
1204
- action: "处理 WebSocket 消息",
1205
- };
1206
- break;
1207
-
1208
- default:
1209
- metadata.eventType = eventType;
1210
- recommendedAction = {
1211
- action: "处理事件",
1212
- };
1213
- }
1214
-
1215
- return { metadata, replyChannel, recommendedAction };
1216
- }
1217
-
1218
- /**
1219
- * 格式化事件消息
1220
- */
1221
- private formatEventMessage(sourceType: EventSourceType, rawEvent: unknown, eventType?: string): string {
1222
- const event = rawEvent as Record<string, unknown>;
1223
-
1224
- switch (sourceType) {
1225
- case "lark-cli": {
1226
- // 飞书消息格式:message 和 sender 在 event 字段下(不是直接在顶层)
1227
- const larkEvent = event as Record<string, unknown>;
1228
- const larkInnerEvent = larkEvent.event as Record<string, unknown> | undefined;
1229
-
1230
- // 尝试从 event.message 获取消息
1231
- const larkMessage = (larkInnerEvent?.message || larkEvent.message) as Record<string, unknown> | undefined;
1232
- const larkSender = (larkInnerEvent?.sender || larkEvent.sender) as Record<string, unknown> | undefined;
1233
-
1234
- if (larkMessage) {
1235
- const senderId = larkSender?.sender_id as Record<string, unknown> | undefined;
1236
-
1237
- // 提取发送者 ID
1238
- const openId = senderId?.open_id as string ||
1239
- senderId?.user_id as string ||
1240
- senderId?.email as string ||
1241
- senderId?.union_id as string ||
1242
- "未知用户";
1243
-
1244
- // 提取消息内容(content 是 JSON 字符串)
1245
- let content = "无消息内容";
1246
- if (larkMessage.content) {
1247
- try {
1248
- // 尝试解析 JSON 字符串
1249
- const contentObj = JSON.parse(larkMessage.content as string);
1250
- content = contentObj.text || contentObj.content || larkMessage.content as string;
1251
- } catch {
1252
- // 解析失败,直接使用原始内容
1253
- content = larkMessage.content as string;
1254
- }
1255
- }
1256
-
1257
- return `[飞书消息] ${openId}: ${content}`;
1258
- }
1259
-
1260
- // 旧格式处理
1261
- const larkSenderRecord = larkEvent.sender as Record<string, unknown> | undefined;
1262
- const senderIdRecord = larkSenderRecord?.sender_id as Record<string, unknown> | undefined;
1263
- if (larkEvent.message_id || (larkEvent.schema as string)?.includes("message")) {
1264
- const senderId = senderIdRecord?.open_id as string ||
1265
- senderIdRecord?.user_id as string ||
1266
- senderIdRecord?.email as string ||
1267
- senderIdRecord?.union_id as string ||
1268
- "未知用户";
1269
- const content = larkEvent.text || larkEvent.content || JSON.stringify(larkEvent);
1270
- return `[飞书消息] ${senderId}: ${content}`;
1271
- }
1272
-
1273
- // 飞书其他事件
1274
- return `[飞书事件] ${eventType || larkEvent.schema || "unknown"}`;
1275
- }
1276
-
1277
- case "timer": {
1278
- const timerEvent = event as Record<string, unknown>;
1279
- const timerMessage = (timerEvent.payload as any)?.message || timerEvent.message || "tick";
1280
- return `[定时任务] ${timerMessage}`;
1281
- }
1282
-
1283
- case "websocket":
1284
- return `[WebSocket] ${JSON.stringify(rawEvent).substring(0, 200)}`;
1285
-
1286
- default:
1287
- return `[${sourceType}] ${JSON.stringify(rawEvent).substring(0, 200)}`;
1288
- }
1289
- }
1290
-
1291
- // ========================================================================
1292
- // 配置持久化
1293
- // ========================================================================
1294
-
1295
- /**
1296
- * 获取配置文件路径
1297
- */
1298
- private getConfigFilePath(): string {
1299
- if (this.configPath) {
1300
- return this.configPath;
1301
- }
1302
-
1303
- // 默认路径: ~/.roy-agent/event-sources.json
1304
- const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
1305
- const dataDir = join(home, ".roy-agent");
1306
-
1307
- if (!existsSync(dataDir)) {
1308
- mkdirSync(dataDir, { recursive: true });
1309
- }
1310
-
1311
- return join(dataDir, "event-sources.json");
1312
- }
1313
-
1314
- /**
1315
- * 持久化配置
1316
- */
1317
- async saveConfig(): Promise<void> {
1318
- if (!this.persistenceEnabled) {
1319
- return;
1320
- }
1321
-
1322
- try {
1323
- const configPath = this.getConfigFilePath();
1324
- const config: EventSourcePersistenceConfig = {
1325
- version: "1.0.0",
1326
- sources: Array.from(this.sources.values()),
1327
- };
1328
-
1329
- const content = JSON.stringify(config, null, 2);
1330
- writeFileSync(configPath, content, "utf-8");
1331
-
1332
- logger.debug(`Saved ${this.sources.size} event source configurations to ${configPath}`);
1333
- } catch (error) {
1334
- logger.error("Failed to save event source configurations:", error);
1335
- throw error;
1336
- }
1337
- }
1338
-
1339
- /**
1340
- * 加载配置
1341
- */
1342
- async loadConfig(): Promise<void> {
1343
- if (!this.persistenceEnabled) {
1344
- return;
1345
- }
1346
-
1347
- try {
1348
- const configPath = this.getConfigFilePath();
1349
-
1350
- if (!existsSync(configPath)) {
1351
- logger.debug(`No event source config file found at ${configPath}`);
1352
- return;
1353
- }
1354
-
1355
- const content = readFileSync(configPath, "utf-8");
1356
- const config: EventSourcePersistenceConfig = JSON.parse(content);
1357
-
1358
- if (!config.sources || !Array.isArray(config.sources)) {
1359
- logger.warn("Invalid event source config format, skipping load");
1360
- return;
1361
- }
1362
-
1363
- // 加载所有事件源配置(不覆盖已存在的)
1364
- let loadedCount = 0;
1365
- for (const source of config.sources) {
1366
- if (!this.sources.has(source.id)) {
1367
- this.sources.set(source.id, source);
1368
- this.statuses.set(source.id, "created");
1369
- loadedCount++;
1370
- }
1371
- }
1372
-
1373
- logger.info(`Loaded ${loadedCount} event source configurations from ${configPath}`);
1374
- } catch (error) {
1375
- logger.error("Failed to load event source configurations:", error);
1376
- // 不抛出错误,允许组件继续运行
1377
- }
1378
- }
1379
- }