blockmine 1.24.0 → 1.27.0

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 (476) hide show
  1. package/CHANGELOG.md +76 -1
  2. package/README.en.md +427 -0
  3. package/README.md +40 -0
  4. package/backend/package.json +2 -2
  5. package/backend/prisma/migrations/20260328173000_add_plugin_source_ref/migration.sql +2 -0
  6. package/backend/prisma/migrations/migration_lock.toml +2 -2
  7. package/backend/prisma/schema.prisma +2 -0
  8. package/backend/src/ai/plugin-assistant-system-prompt.md +664 -5
  9. package/backend/src/api/routes/apiKeys.js +8 -0
  10. package/backend/src/api/routes/bots.js +271 -9
  11. package/backend/src/api/routes/eventGraphs.js +151 -1
  12. package/backend/src/api/routes/health.js +38 -0
  13. package/backend/src/api/routes/nodeRegistry.js +63 -0
  14. package/backend/src/api/routes/plugins.js +254 -29
  15. package/backend/src/api/routes/servers.js +14 -2
  16. package/backend/src/container.js +11 -8
  17. package/backend/src/core/BotCommandLoader.js +161 -0
  18. package/backend/src/core/BotConnection.js +125 -0
  19. package/backend/src/core/BotEventHandlers.js +234 -0
  20. package/backend/src/core/BotIPCHandler.js +445 -0
  21. package/backend/src/core/BotManager.js +15 -7
  22. package/backend/src/core/BotProcess.js +169 -140
  23. package/backend/src/core/EventGraphManager.js +7 -3
  24. package/backend/src/core/GraphDebugHandler.js +229 -0
  25. package/backend/src/core/GraphDebugIPC.js +117 -0
  26. package/backend/src/core/GraphExecutionEngine.js +545 -978
  27. package/backend/src/core/GraphTraversal.js +80 -0
  28. package/backend/src/core/GraphValidation.js +73 -0
  29. package/backend/src/core/NodeDefinition.js +138 -0
  30. package/backend/src/core/NodeRegistry.js +153 -141
  31. package/backend/src/core/PluginLoader.js +83 -3
  32. package/backend/src/core/PluginManager.js +346 -35
  33. package/backend/src/core/RewindSignal.js +9 -0
  34. package/backend/src/core/config/ConfigValidator.js +72 -0
  35. package/backend/src/core/config/FeatureFlags.js +52 -0
  36. package/backend/src/core/config/__tests__/ConfigValidator.test.js +232 -0
  37. package/backend/src/core/domain/entities/Bot.js +39 -0
  38. package/backend/src/core/domain/entities/Command.js +41 -0
  39. package/backend/src/core/domain/entities/EventGraph.js +39 -0
  40. package/backend/src/core/domain/entities/Plugin.js +45 -0
  41. package/backend/src/core/domain/entities/User.js +40 -0
  42. package/backend/src/core/domain/services/DependencyResolver.js +168 -0
  43. package/backend/src/core/domain/services/GraphValidator.js +117 -0
  44. package/backend/src/core/domain/services/PermissionChecker.js +34 -0
  45. package/backend/src/core/domain/services/__tests__/DependencyResolver.test.js +126 -0
  46. package/backend/src/core/domain/valueObjects/BotConfig.js +27 -0
  47. package/backend/src/core/domain/valueObjects/DependencyGraph.js +86 -0
  48. package/backend/src/core/domain/valueObjects/PluginManifest.js +36 -0
  49. package/backend/src/core/errors/BaseError.js +29 -0
  50. package/backend/src/core/errors/ErrorHandler.js +81 -0
  51. package/backend/src/core/errors/__tests__/ErrorHandler.test.js +188 -0
  52. package/backend/src/core/errors/index.js +68 -0
  53. package/backend/src/core/infrastructure/BatchingUtility.js +66 -0
  54. package/backend/src/core/infrastructure/CircuitBreaker.js +103 -0
  55. package/backend/src/core/infrastructure/ConnectionPool.js +81 -0
  56. package/backend/src/core/infrastructure/RateLimiter.js +64 -0
  57. package/backend/src/core/infrastructure/__tests__/BatchingUtility.test.js +86 -0
  58. package/backend/src/core/infrastructure/__tests__/CircuitBreaker.test.js +156 -0
  59. package/backend/src/core/infrastructure/__tests__/ConnectionPool.test.js +146 -0
  60. package/backend/src/core/infrastructure/__tests__/RateLimiter.test.js +171 -0
  61. package/backend/src/core/ipc/botApiFactory.js +72 -0
  62. package/backend/src/core/ipc/ipcMessageTypes.js +115 -0
  63. package/backend/src/core/logging/AuditLogger.js +61 -0
  64. package/backend/src/core/logging/StructuredLogger.js +80 -0
  65. package/backend/src/core/logging/__tests__/StructuredLogger.test.js +213 -0
  66. package/backend/src/core/logging/index.js +7 -0
  67. package/backend/src/core/metrics/MetricsCollector.js +104 -0
  68. package/backend/src/core/metrics/__tests__/MetricsCollector.test.js +131 -0
  69. package/backend/src/core/node-registries/actionsNodes.js +191 -0
  70. package/backend/src/core/node-registries/arraysNodes.js +152 -0
  71. package/backend/src/core/node-registries/botNodes.js +48 -0
  72. package/backend/src/core/node-registries/containerNodes.js +141 -0
  73. package/backend/src/core/node-registries/dataNodes.js +284 -0
  74. package/backend/src/core/node-registries/debugNodes.js +23 -0
  75. package/backend/src/core/node-registries/eventsNodes.js +223 -0
  76. package/backend/src/core/node-registries/flowNodes.js +151 -0
  77. package/backend/src/core/node-registries/furnaceNodes.js +123 -0
  78. package/backend/src/core/node-registries/index.js +108 -0
  79. package/backend/src/core/node-registries/inventory.js +102 -106
  80. package/backend/src/core/node-registries/logicNodes.js +54 -0
  81. package/backend/src/core/node-registries/mathNodes.js +38 -0
  82. package/backend/src/core/node-registries/navigationNodes.js +109 -0
  83. package/backend/src/core/node-registries/objectsNodes.js +90 -0
  84. package/backend/src/core/node-registries/stringsNodes.js +165 -0
  85. package/backend/src/core/node-registries/timeNodes.js +105 -0
  86. package/backend/src/core/node-registries/typeNodes.js +22 -0
  87. package/backend/src/core/node-registries/usersNodes.js +126 -0
  88. package/backend/src/core/nodes/arrays/shuffle.js +14 -0
  89. package/backend/src/core/nodes/bot/get_name.js +8 -0
  90. package/backend/src/core/nodes/bot/stop_bot.js +5 -0
  91. package/backend/src/core/nodes/container/open.js +101 -111
  92. package/backend/src/core/nodes/data/store_read.js +26 -0
  93. package/backend/src/core/nodes/data/store_write.js +23 -0
  94. package/backend/src/core/nodes/event/call_event.js +31 -0
  95. package/backend/src/core/nodes/event/custom_event.js +8 -0
  96. package/backend/src/core/nodes/flow/timer.js +35 -0
  97. package/backend/src/core/nodes/inventory/drop.js +73 -65
  98. package/backend/src/core/nodes/inventory/equip.js +54 -45
  99. package/backend/src/core/nodes/inventory/select_slot.js +48 -46
  100. package/backend/src/core/nodes/navigation/follow.js +54 -51
  101. package/backend/src/core/nodes/navigation/go_to.js +41 -53
  102. package/backend/src/core/nodes/navigation/go_to_entity.js +65 -69
  103. package/backend/src/core/nodes/navigation/go_to_player.js +65 -70
  104. package/backend/src/core/nodes/navigation/stop.js +17 -26
  105. package/backend/src/core/nodes/users/add_to_group.js +24 -0
  106. package/backend/src/core/nodes/users/check_permission.js +26 -0
  107. package/backend/src/core/nodes/users/remove_from_group.js +24 -0
  108. package/backend/src/core/services/BotIPCMessageRouter.js +337 -0
  109. package/backend/src/core/services/BotLifecycleService.js +43 -450
  110. package/backend/src/core/services/CacheManager.js +83 -23
  111. package/backend/src/core/services/CrashRestartManager.js +42 -0
  112. package/backend/src/core/services/DebugSessionManager.js +114 -12
  113. package/backend/src/core/services/EventGraphService.js +69 -0
  114. package/backend/src/core/services/MinecraftBotManager.js +9 -1
  115. package/backend/src/core/services/PluginManagementService.js +84 -0
  116. package/backend/src/core/services/TestModeContext.js +65 -0
  117. package/backend/src/core/services/__tests__/CacheManager.test.js +168 -0
  118. package/backend/src/core/services.js +1 -11
  119. package/backend/src/core/validation/InputValidator.js +167 -0
  120. package/backend/src/core/validation/__tests__/InputValidator.test.js +296 -0
  121. package/backend/src/real-time/botApi/index.js +1 -1
  122. package/backend/src/real-time/socketHandler.js +26 -0
  123. package/backend/src/server.js +21 -6
  124. package/frontend/dist/assets/browser-ponyfill-D8y0Ty7C.js +2 -0
  125. package/frontend/dist/assets/index-CFJLS0dk.css +32 -0
  126. package/frontend/dist/assets/index-D91UGNMG.js +11260 -0
  127. package/frontend/dist/flags/en.svg +32 -0
  128. package/frontend/dist/flags/ru.svg +5 -0
  129. package/frontend/dist/index.html +2 -2
  130. package/frontend/dist/locales/en/admin.json +100 -0
  131. package/frontend/dist/locales/en/api-keys.json +58 -0
  132. package/frontend/dist/locales/en/bots.json +113 -0
  133. package/frontend/dist/locales/en/common.json +53 -0
  134. package/frontend/dist/locales/en/configuration.json +22 -0
  135. package/frontend/dist/locales/en/console.json +10 -0
  136. package/frontend/dist/locales/en/dashboard.json +85 -0
  137. package/frontend/dist/locales/en/dialogs.json +70 -0
  138. package/frontend/dist/locales/en/event-graphs.json +50 -0
  139. package/frontend/dist/locales/en/graph-store.json +70 -0
  140. package/frontend/dist/locales/en/login.json +36 -0
  141. package/frontend/dist/locales/en/management.json +192 -0
  142. package/frontend/dist/locales/en/minecraft-viewer.json +27 -0
  143. package/frontend/dist/locales/en/nodes.json +1132 -0
  144. package/frontend/dist/locales/en/permissions.json +50 -0
  145. package/frontend/dist/locales/en/plugin-detail.json +69 -0
  146. package/frontend/dist/locales/en/plugins.json +329 -0
  147. package/frontend/dist/locales/en/proxies.json +81 -0
  148. package/frontend/dist/locales/en/servers.json +39 -0
  149. package/frontend/dist/locales/en/setup.json +19 -0
  150. package/frontend/dist/locales/en/sidebar.json +195 -0
  151. package/frontend/dist/locales/en/tasks.json +62 -0
  152. package/frontend/dist/locales/en/visual-editor.json +418 -0
  153. package/frontend/dist/locales/en/websocket.json +86 -0
  154. package/frontend/dist/locales/ru/admin.json +100 -0
  155. package/frontend/dist/locales/ru/api-keys.json +58 -0
  156. package/frontend/dist/locales/ru/bots.json +113 -0
  157. package/frontend/dist/locales/ru/common.json +49 -0
  158. package/frontend/dist/locales/ru/configuration.json +22 -0
  159. package/frontend/dist/locales/ru/console.json +10 -0
  160. package/frontend/dist/locales/ru/dashboard.json +85 -0
  161. package/frontend/dist/locales/ru/dialogs.json +70 -0
  162. package/frontend/dist/locales/ru/event-graphs.json +50 -0
  163. package/frontend/dist/locales/ru/graph-store.json +70 -0
  164. package/frontend/dist/locales/ru/login.json +36 -0
  165. package/frontend/dist/locales/ru/management.json +192 -0
  166. package/frontend/dist/locales/ru/minecraft-viewer.json +30 -0
  167. package/frontend/dist/locales/ru/nodes.json +1131 -0
  168. package/frontend/dist/locales/ru/permissions.json +50 -0
  169. package/frontend/dist/locales/ru/plugin-detail.json +49 -0
  170. package/frontend/dist/locales/ru/plugins.json +209 -0
  171. package/frontend/dist/locales/ru/proxies.json +81 -0
  172. package/frontend/dist/locales/ru/servers.json +39 -0
  173. package/frontend/dist/locales/ru/setup.json +19 -0
  174. package/frontend/dist/locales/ru/sidebar.json +195 -0
  175. package/frontend/dist/locales/ru/tasks.json +62 -0
  176. package/frontend/dist/locales/ru/visual-editor.json +420 -0
  177. package/frontend/dist/locales/ru/websocket.json +86 -0
  178. package/frontend/dist/monacoeditorwork/css.worker.bundle.js +7 -7
  179. package/frontend/dist/monacoeditorwork/html.worker.bundle.js +7 -7
  180. package/frontend/dist/monacoeditorwork/json.worker.bundle.js +7 -7
  181. package/frontend/dist/monacoeditorwork/ts.worker.bundle.js +3 -3
  182. package/frontend/package.json +6 -0
  183. package/nul +12 -0
  184. package/package.json +3 -3
  185. package/screen/3dviewer.png +0 -0
  186. package/screen/console.png +0 -0
  187. package/screen/dashboard.png +0 -0
  188. package/screen/graph_collabe.png +0 -0
  189. package/screen/graph_live_debug.png +0 -0
  190. package/screen/language_selector.png +0 -0
  191. package/screen/management_command.png +0 -0
  192. package/screen/node_debug_trace.png +0 -0
  193. package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
  194. package/screen/websocket.png +0 -0
  195. package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
  196. package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
  197. package/.claude/agents/README.md +0 -469
  198. package/.claude/agents/auth-route-debugger.md +0 -118
  199. package/.claude/agents/auth-route-tester.md +0 -93
  200. package/.claude/agents/auto-error-resolver.md +0 -97
  201. package/.claude/agents/build-optimizer.md +0 -236
  202. package/.claude/agents/code-architect.md +0 -34
  203. package/.claude/agents/code-architecture-reviewer.md +0 -83
  204. package/.claude/agents/code-explorer.md +0 -51
  205. package/.claude/agents/code-refactor-master.md +0 -94
  206. package/.claude/agents/code-reviewer.md +0 -46
  207. package/.claude/agents/cost-optimizer.md +0 -134
  208. package/.claude/agents/deployment-orchestrator.md +0 -113
  209. package/.claude/agents/documentation-architect.md +0 -82
  210. package/.claude/agents/frontend-error-fixer.md +0 -77
  211. package/.claude/agents/iac-code-generator.md +0 -71
  212. package/.claude/agents/incident-responder.md +0 -346
  213. package/.claude/agents/infrastructure-architect.md +0 -31
  214. package/.claude/agents/kubernetes-specialist.md +0 -56
  215. package/.claude/agents/migration-planner.md +0 -181
  216. package/.claude/agents/network-architect.md +0 -196
  217. package/.claude/agents/plan-reviewer.md +0 -52
  218. package/.claude/agents/refactor-planner.md +0 -63
  219. package/.claude/agents/security-scanner.md +0 -102
  220. package/.claude/agents/web-research-specialist.md +0 -78
  221. package/.claude/commands/cost-analysis.md +0 -315
  222. package/.claude/commands/dev-docs-update.md +0 -55
  223. package/.claude/commands/dev-docs.md +0 -51
  224. package/.claude/commands/feature-dev.md +0 -125
  225. package/.claude/commands/incident-debug.md +0 -247
  226. package/.claude/commands/infra-plan.md +0 -81
  227. package/.claude/commands/migration-plan.md +0 -478
  228. package/.claude/commands/route-research-for-testing.md +0 -37
  229. package/.claude/commands/security-review.md +0 -66
  230. package/.claude/hooks/CONFIG.md +0 -448
  231. package/.claude/hooks/README.md +0 -163
  232. package/.claude/hooks/SKILL_ACTIVATION_COMPLETE.md +0 -226
  233. package/.claude/hooks/WINDOWS_HOOKS_README.md +0 -151
  234. package/.claude/hooks/add-skill-activation-banners.ts +0 -132
  235. package/.claude/hooks/comprehensive-skill-test.ts +0 -1315
  236. package/.claude/hooks/error-handling-reminder.sh +0 -12
  237. package/.claude/hooks/error-handling-reminder.ts +0 -222
  238. package/.claude/hooks/k8s-manifest-validator.sh +0 -56
  239. package/.claude/hooks/package-lock.json +0 -556
  240. package/.claude/hooks/package.json +0 -16
  241. package/.claude/hooks/post-tool-use-tracker.ps1 +0 -174
  242. package/.claude/hooks/post-tool-use-tracker.sh +0 -183
  243. package/.claude/hooks/security-policy-check.sh +0 -247
  244. package/.claude/hooks/skill-activation-prompt.ps1 +0 -10
  245. package/.claude/hooks/skill-activation-prompt.sh +0 -10
  246. package/.claude/hooks/skill-activation-prompt.ts +0 -141
  247. package/.claude/hooks/stop-build-check-enhanced.sh +0 -130
  248. package/.claude/hooks/terraform-validator.sh +0 -53
  249. package/.claude/hooks/test-input.json +0 -7
  250. package/.claude/hooks/test-skill-activation.ts +0 -427
  251. package/.claude/hooks/trigger-build-resolver.sh +0 -79
  252. package/.claude/hooks/tsc-check.sh +0 -173
  253. package/.claude/hooks/tsconfig.json +0 -19
  254. package/.claude/settings.json +0 -59
  255. package/.claude/settings.local.json +0 -67
  256. package/.claude/skills/README.md +0 -507
  257. package/.claude/skills/api-engineering/SKILL.md +0 -63
  258. package/.claude/skills/api-engineering/resources/api-versioning.md +0 -88
  259. package/.claude/skills/api-engineering/resources/graphql-patterns.md +0 -106
  260. package/.claude/skills/api-engineering/resources/rate-limiting.md +0 -118
  261. package/.claude/skills/api-engineering/resources/rest-api-design.md +0 -105
  262. package/.claude/skills/backend-dev-guidelines/SKILL.md +0 -306
  263. package/.claude/skills/backend-dev-guidelines/resources/architecture-overview.md +0 -451
  264. package/.claude/skills/backend-dev-guidelines/resources/async-and-errors.md +0 -307
  265. package/.claude/skills/backend-dev-guidelines/resources/complete-examples.md +0 -638
  266. package/.claude/skills/backend-dev-guidelines/resources/configuration.md +0 -275
  267. package/.claude/skills/backend-dev-guidelines/resources/database-patterns.md +0 -224
  268. package/.claude/skills/backend-dev-guidelines/resources/middleware-guide.md +0 -213
  269. package/.claude/skills/backend-dev-guidelines/resources/routing-and-controllers.md +0 -756
  270. package/.claude/skills/backend-dev-guidelines/resources/sentry-and-monitoring.md +0 -336
  271. package/.claude/skills/backend-dev-guidelines/resources/services-and-repositories.md +0 -789
  272. package/.claude/skills/backend-dev-guidelines/resources/testing-guide.md +0 -235
  273. package/.claude/skills/backend-dev-guidelines/resources/validation-patterns.md +0 -754
  274. package/.claude/skills/budget-and-cost-management/SKILL.md +0 -850
  275. package/.claude/skills/build-engineering/SKILL.md +0 -431
  276. package/.claude/skills/build-engineering/resources/artifact-repositories.md +0 -72
  277. package/.claude/skills/build-engineering/resources/build-caching.md +0 -96
  278. package/.claude/skills/build-engineering/resources/build-pipelines.md +0 -105
  279. package/.claude/skills/build-engineering/resources/build-security.md +0 -95
  280. package/.claude/skills/build-engineering/resources/build-systems.md +0 -389
  281. package/.claude/skills/build-engineering/resources/compilation-optimization.md +0 -201
  282. package/.claude/skills/build-engineering/resources/dependency-management.md +0 -73
  283. package/.claude/skills/build-engineering/resources/monorepo-builds.md +0 -110
  284. package/.claude/skills/build-engineering/resources/performance-optimization.md +0 -113
  285. package/.claude/skills/build-engineering/resources/reproducible-builds.md +0 -82
  286. package/.claude/skills/cloud-engineering/SKILL.md +0 -675
  287. package/.claude/skills/cloud-engineering/resources/aws-patterns.md +0 -742
  288. package/.claude/skills/cloud-engineering/resources/azure-patterns.md +0 -714
  289. package/.claude/skills/cloud-engineering/resources/cleared-cloud-environments.md +0 -987
  290. package/.claude/skills/cloud-engineering/resources/cloud-cost-optimization.md +0 -757
  291. package/.claude/skills/cloud-engineering/resources/cloud-networking.md +0 -1058
  292. package/.claude/skills/cloud-engineering/resources/cloud-security-tools.md +0 -1530
  293. package/.claude/skills/cloud-engineering/resources/cloud-security.md +0 -990
  294. package/.claude/skills/cloud-engineering/resources/gcp-patterns.md +0 -758
  295. package/.claude/skills/cloud-engineering/resources/migration-strategies.md +0 -820
  296. package/.claude/skills/cloud-engineering/resources/multi-cloud-strategies.md +0 -670
  297. package/.claude/skills/cloud-engineering/resources/oci-patterns.md +0 -1198
  298. package/.claude/skills/cloud-engineering/resources/serverless-patterns.md +0 -795
  299. package/.claude/skills/cloud-engineering/resources/well-architected-frameworks.md +0 -966
  300. package/.claude/skills/cybersecurity/SKILL.md +0 -409
  301. package/.claude/skills/cybersecurity/resources/security-architecture.md +0 -266
  302. package/.claude/skills/database-engineering/SKILL.md +0 -61
  303. package/.claude/skills/database-engineering/resources/backup-and-recovery.md +0 -72
  304. package/.claude/skills/database-engineering/resources/database-replication.md +0 -63
  305. package/.claude/skills/database-engineering/resources/postgresql-fundamentals.md +0 -70
  306. package/.claude/skills/database-engineering/resources/query-optimization.md +0 -68
  307. package/.claude/skills/devsecops/SKILL.md +0 -374
  308. package/.claude/skills/devsecops/resources/ci-cd-security.md +0 -204
  309. package/.claude/skills/devsecops/resources/compliance-automation.md +0 -530
  310. package/.claude/skills/devsecops/resources/compliance-frameworks.md +0 -2322
  311. package/.claude/skills/devsecops/resources/container-security.md +0 -915
  312. package/.claude/skills/devsecops/resources/cspm-integration.md +0 -1440
  313. package/.claude/skills/devsecops/resources/policy-enforcement.md +0 -619
  314. package/.claude/skills/devsecops/resources/secrets-management.md +0 -755
  315. package/.claude/skills/devsecops/resources/security-monitoring.md +0 -146
  316. package/.claude/skills/devsecops/resources/security-scanning.md +0 -887
  317. package/.claude/skills/devsecops/resources/security-testing.md +0 -203
  318. package/.claude/skills/devsecops/resources/supply-chain-security.md +0 -518
  319. package/.claude/skills/devsecops/resources/vulnerability-management.md +0 -481
  320. package/.claude/skills/devsecops/resources/zero-trust-architecture.md +0 -177
  321. package/.claude/skills/documentation-as-code/SKILL.md +0 -323
  322. package/.claude/skills/documentation-as-code/resources/api-documentation.md +0 -90
  323. package/.claude/skills/documentation-as-code/resources/changelog-management.md +0 -79
  324. package/.claude/skills/documentation-as-code/resources/diagram-generation.md +0 -44
  325. package/.claude/skills/documentation-as-code/resources/docs-as-code-workflow.md +0 -99
  326. package/.claude/skills/documentation-as-code/resources/documentation-automation.md +0 -68
  327. package/.claude/skills/documentation-as-code/resources/documentation-sites.md +0 -79
  328. package/.claude/skills/documentation-as-code/resources/markdown-best-practices.md +0 -162
  329. package/.claude/skills/documentation-as-code/resources/openapi-specification.md +0 -77
  330. package/.claude/skills/documentation-as-code/resources/readme-engineering.md +0 -60
  331. package/.claude/skills/documentation-as-code/resources/technical-writing-guide.md +0 -202
  332. package/.claude/skills/engineering-management/SKILL.md +0 -356
  333. package/.claude/skills/engineering-management/resources/career-ladders.md +0 -609
  334. package/.claude/skills/engineering-management/resources/hiring-and-assessment.md +0 -555
  335. package/.claude/skills/engineering-management/resources/one-on-one-guides.md +0 -609
  336. package/.claude/skills/engineering-management/resources/resource-planning.md +0 -557
  337. package/.claude/skills/engineering-management/resources/team-organization-patterns.md +0 -491
  338. package/.claude/skills/engineering-management/resources/technical-interviews.md +0 -474
  339. package/.claude/skills/engineering-operations-management/SKILL.md +0 -817
  340. package/.claude/skills/error-tracking/SKILL.md +0 -379
  341. package/.claude/skills/frontend-design/SKILL.md +0 -42
  342. package/.claude/skills/frontend-dev-guidelines/SKILL.md +0 -403
  343. package/.claude/skills/frontend-dev-guidelines/resources/common-patterns.md +0 -331
  344. package/.claude/skills/frontend-dev-guidelines/resources/complete-examples.md +0 -872
  345. package/.claude/skills/frontend-dev-guidelines/resources/component-patterns.md +0 -502
  346. package/.claude/skills/frontend-dev-guidelines/resources/data-fetching.md +0 -767
  347. package/.claude/skills/frontend-dev-guidelines/resources/file-organization.md +0 -502
  348. package/.claude/skills/frontend-dev-guidelines/resources/loading-and-error-states.md +0 -501
  349. package/.claude/skills/frontend-dev-guidelines/resources/performance.md +0 -406
  350. package/.claude/skills/frontend-dev-guidelines/resources/routing-guide.md +0 -364
  351. package/.claude/skills/frontend-dev-guidelines/resources/styling-guide.md +0 -428
  352. package/.claude/skills/frontend-dev-guidelines/resources/typescript-standards.md +0 -418
  353. package/.claude/skills/general-it-engineering/SKILL.md +0 -393
  354. package/.claude/skills/general-it-engineering/resources/asset-management.md +0 -712
  355. package/.claude/skills/general-it-engineering/resources/automation-orchestration.md +0 -817
  356. package/.claude/skills/general-it-engineering/resources/business-continuity.md +0 -786
  357. package/.claude/skills/general-it-engineering/resources/change-management.md +0 -715
  358. package/.claude/skills/general-it-engineering/resources/enterprise-monitoring.md +0 -729
  359. package/.claude/skills/general-it-engineering/resources/help-desk-operations.md +0 -738
  360. package/.claude/skills/general-it-engineering/resources/incident-service-management.md +0 -834
  361. package/.claude/skills/general-it-engineering/resources/it-governance.md +0 -753
  362. package/.claude/skills/general-it-engineering/resources/itil-framework.md +0 -503
  363. package/.claude/skills/general-it-engineering/resources/service-management.md +0 -669
  364. package/.claude/skills/infrastructure-architecture/SKILL.md +0 -328
  365. package/.claude/skills/infrastructure-architecture/resources/architecture-decision-records.md +0 -505
  366. package/.claude/skills/infrastructure-architecture/resources/architecture-patterns.md +0 -528
  367. package/.claude/skills/infrastructure-architecture/resources/capacity-planning.md +0 -453
  368. package/.claude/skills/infrastructure-architecture/resources/cleared-environment-architecture.md +0 -773
  369. package/.claude/skills/infrastructure-architecture/resources/cost-architecture.md +0 -499
  370. package/.claude/skills/infrastructure-architecture/resources/data-architecture.md +0 -501
  371. package/.claude/skills/infrastructure-architecture/resources/disaster-recovery.md +0 -535
  372. package/.claude/skills/infrastructure-architecture/resources/migration-architecture.md +0 -512
  373. package/.claude/skills/infrastructure-architecture/resources/multi-region-design.md +0 -608
  374. package/.claude/skills/infrastructure-architecture/resources/reference-architectures.md +0 -562
  375. package/.claude/skills/infrastructure-architecture/resources/security-architecture.md +0 -538
  376. package/.claude/skills/infrastructure-architecture/resources/system-design-principles.md +0 -489
  377. package/.claude/skills/infrastructure-architecture/resources/workload-classification.md +0 -1000
  378. package/.claude/skills/infrastructure-strategy/SKILL.md +0 -924
  379. package/.claude/skills/network-engineering/SKILL.md +0 -385
  380. package/.claude/skills/network-engineering/resources/dns-management.md +0 -738
  381. package/.claude/skills/network-engineering/resources/load-balancing.md +0 -820
  382. package/.claude/skills/network-engineering/resources/network-architecture.md +0 -546
  383. package/.claude/skills/network-engineering/resources/network-security.md +0 -921
  384. package/.claude/skills/network-engineering/resources/network-troubleshooting.md +0 -749
  385. package/.claude/skills/network-engineering/resources/routing-switching.md +0 -373
  386. package/.claude/skills/network-engineering/resources/sdn-networking.md +0 -695
  387. package/.claude/skills/network-engineering/resources/service-mesh-networking.md +0 -777
  388. package/.claude/skills/network-engineering/resources/tcp-ip-protocols.md +0 -444
  389. package/.claude/skills/network-engineering/resources/vpn-connectivity.md +0 -672
  390. package/.claude/skills/node-development/SKILL.md +0 -317
  391. package/.claude/skills/observability-engineering/SKILL.md +0 -101
  392. package/.claude/skills/observability-engineering/resources/apm-tools.md +0 -97
  393. package/.claude/skills/observability-engineering/resources/correlation-strategies.md +0 -87
  394. package/.claude/skills/observability-engineering/resources/distributed-tracing.md +0 -98
  395. package/.claude/skills/observability-engineering/resources/logs-aggregation.md +0 -118
  396. package/.claude/skills/observability-engineering/resources/observability-cost-optimization.md +0 -141
  397. package/.claude/skills/observability-engineering/resources/opentelemetry.md +0 -110
  398. package/.claude/skills/platform-engineering/SKILL.md +0 -555
  399. package/.claude/skills/platform-engineering/resources/architecture-overview.md +0 -600
  400. package/.claude/skills/platform-engineering/resources/container-orchestration.md +0 -916
  401. package/.claude/skills/platform-engineering/resources/cost-optimization.md +0 -634
  402. package/.claude/skills/platform-engineering/resources/developer-platforms.md +0 -670
  403. package/.claude/skills/platform-engineering/resources/gitops-automation.md +0 -650
  404. package/.claude/skills/platform-engineering/resources/infrastructure-as-code.md +0 -778
  405. package/.claude/skills/platform-engineering/resources/infrastructure-standards.md +0 -708
  406. package/.claude/skills/platform-engineering/resources/multi-tenancy.md +0 -602
  407. package/.claude/skills/platform-engineering/resources/platform-security.md +0 -711
  408. package/.claude/skills/platform-engineering/resources/resource-management.md +0 -592
  409. package/.claude/skills/platform-engineering/resources/service-mesh.md +0 -628
  410. package/.claude/skills/release-engineering/SKILL.md +0 -393
  411. package/.claude/skills/release-engineering/resources/artifact-management.md +0 -108
  412. package/.claude/skills/release-engineering/resources/build-optimization.md +0 -84
  413. package/.claude/skills/release-engineering/resources/ci-cd-pipelines.md +0 -411
  414. package/.claude/skills/release-engineering/resources/deployment-strategies.md +0 -197
  415. package/.claude/skills/release-engineering/resources/pipeline-security.md +0 -62
  416. package/.claude/skills/release-engineering/resources/progressive-delivery.md +0 -83
  417. package/.claude/skills/release-engineering/resources/release-automation.md +0 -68
  418. package/.claude/skills/release-engineering/resources/release-orchestration.md +0 -77
  419. package/.claude/skills/release-engineering/resources/rollback-strategies.md +0 -66
  420. package/.claude/skills/release-engineering/resources/versioning-strategies.md +0 -59
  421. package/.claude/skills/route-tester/SKILL.md +0 -392
  422. package/.claude/skills/skill-developer/ADVANCED.md +0 -197
  423. package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +0 -306
  424. package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +0 -152
  425. package/.claude/skills/skill-developer/SKILL.md +0 -430
  426. package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +0 -315
  427. package/.claude/skills/skill-developer/TRIGGER_TYPES.md +0 -305
  428. package/.claude/skills/skill-developer/TROUBLESHOOTING.md +0 -514
  429. package/.claude/skills/skill-rules.json +0 -2989
  430. package/.claude/skills/sre/SKILL.md +0 -464
  431. package/.claude/skills/sre/resources/alerting-best-practices.md +0 -282
  432. package/.claude/skills/sre/resources/capacity-planning.md +0 -226
  433. package/.claude/skills/sre/resources/chaos-engineering.md +0 -193
  434. package/.claude/skills/sre/resources/disaster-recovery.md +0 -232
  435. package/.claude/skills/sre/resources/incident-management.md +0 -436
  436. package/.claude/skills/sre/resources/observability-stack.md +0 -240
  437. package/.claude/skills/sre/resources/on-call-runbooks.md +0 -167
  438. package/.claude/skills/sre/resources/performance-optimization.md +0 -108
  439. package/.claude/skills/sre/resources/reliability-patterns.md +0 -183
  440. package/.claude/skills/sre/resources/slo-sli-sla.md +0 -464
  441. package/.claude/skills/sre/resources/toil-reduction.md +0 -145
  442. package/.claude/skills/systems-engineering/SKILL.md +0 -648
  443. package/.claude/skills/systems-engineering/resources/automation-patterns.md +0 -771
  444. package/.claude/skills/systems-engineering/resources/configuration-management.md +0 -998
  445. package/.claude/skills/systems-engineering/resources/linux-administration.md +0 -672
  446. package/.claude/skills/systems-engineering/resources/networking-fundamentals.md +0 -982
  447. package/.claude/skills/systems-engineering/resources/performance-tuning.md +0 -871
  448. package/.claude/skills/systems-engineering/resources/powershell-scripting.md +0 -482
  449. package/.claude/skills/systems-engineering/resources/security-hardening.md +0 -739
  450. package/.claude/skills/systems-engineering/resources/shell-scripting.md +0 -915
  451. package/.claude/skills/systems-engineering/resources/storage-management.md +0 -628
  452. package/.claude/skills/systems-engineering/resources/system-monitoring.md +0 -787
  453. package/.claude/skills/systems-engineering/resources/troubleshooting-guide.md +0 -753
  454. package/.claude/skills/systems-engineering/resources/windows-administration.md +0 -738
  455. package/.claude/skills/technical-leadership/SKILL.md +0 -728
  456. package/backend/docs/SECRETS_DOCUMENTATION.md +0 -327
  457. package/backend/package-lock.json +0 -6801
  458. package/backend/src/core/node-registries/actions.js +0 -202
  459. package/backend/src/core/node-registries/arrays.js +0 -155
  460. package/backend/src/core/node-registries/bot.js +0 -23
  461. package/backend/src/core/node-registries/container.js +0 -162
  462. package/backend/src/core/node-registries/data.js +0 -290
  463. package/backend/src/core/node-registries/debug.js +0 -26
  464. package/backend/src/core/node-registries/events.js +0 -201
  465. package/backend/src/core/node-registries/flow.js +0 -139
  466. package/backend/src/core/node-registries/furnace.js +0 -143
  467. package/backend/src/core/node-registries/logic.js +0 -62
  468. package/backend/src/core/node-registries/math.js +0 -42
  469. package/backend/src/core/node-registries/navigation.js +0 -111
  470. package/backend/src/core/node-registries/objects.js +0 -98
  471. package/backend/src/core/node-registries/strings.js +0 -187
  472. package/backend/src/core/node-registries/time.js +0 -113
  473. package/backend/src/core/node-registries/type.js +0 -25
  474. package/backend/src/core/node-registries/users.js +0 -79
  475. package/frontend/dist/assets/index-BC-NbKXi.css +0 -32
  476. package/frontend/dist/assets/index-DqJXZMHY.js +0 -11266
@@ -1,1012 +1,579 @@
1
- const prismaService = require('./PrismaService');
2
1
  const { parseVariables } = require('./utils/variableParser');
3
- const validationService = require('./services/ValidationService');
4
- const { MAX_RECURSION_DEPTH } = require('./config/validation');
5
- const debugConfig = require('../config/debug.config');
6
- const prisma = prismaService.getClient();
7
-
8
- const BreakLoopSignal = require('./BreakLoopSignal');
2
+ const GraphValidation = require('./GraphValidation');
3
+ const GraphDebugHandler = require('./GraphDebugHandler');
4
+ const { attachDebugIpcHandler } = require('./GraphDebugIPC');
9
5
  const { getTraceCollector } = require('./services/TraceCollectorService');
10
6
  const { getGlobalDebugManager } = require('./services/DebugSessionManager');
7
+ const { MessageTypes } = require('./ipc/ipcMessageTypes');
8
+ const RewindSignal = require('./RewindSignal');
9
+ const BreakLoopSignal = require('./BreakLoopSignal');
11
10
 
12
11
  class GraphExecutionEngine {
13
- // Static флаг для предотвращения дублирования IPC handler
14
- static _ipcHandlerAttached = false;
15
- // Static Map для хранения всех pending requests из всех instances
16
- static _allPendingRequests = new Map();
17
-
18
- constructor(nodeRegistry, botManagerOrApi = null) {
19
- if (!nodeRegistry || typeof nodeRegistry.getNodeConfig !== 'function') {
20
- throw new Error('GraphExecutionEngine requires a valid NodeRegistry instance.');
21
- }
22
- this.nodeRegistry = nodeRegistry;
23
- this.botManager = botManagerOrApi;
24
- this.activeGraph = null;
25
- this.context = null;
26
- this.memo = new Map();
27
- this.traceCollector = getTraceCollector();
28
- this.currentTraceId = null;
29
-
30
- // Используем статическую Map для всех instance
31
- this.pendingDebugRequests = GraphExecutionEngine._allPendingRequests;
32
-
33
- if (process.on && !GraphExecutionEngine._ipcHandlerAttached) {
34
- process.on('message', (message) => {
35
- if (message.type === 'debug:breakpoint_response' || message.type === 'debug:step_response') {
36
- GraphExecutionEngine._handleGlobalDebugResponse(message);
37
- }
38
- });
39
- GraphExecutionEngine._ipcHandlerAttached = true;
40
- }
41
- }
42
-
43
- async execute(graph, context, eventType) {
44
- if (!graph || graph === 'null') return context;
45
-
46
- const parsedGraph = validationService.parseGraph(graph, 'GraphExecutionEngine.execute');
47
- if (!parsedGraph) {
48
- return context;
49
- }
50
-
51
- const validation = validationService.validateGraphStructure(parsedGraph, 'GraphExecutionEngine');
52
- if (validation.shouldSkip) {
53
- return context;
54
- }
55
-
56
- const graphId = context.graphId || null;
57
- const botId = context.botId || null;
58
-
59
- if (graphId && botId) {
60
- this.currentTraceId = await this.traceCollector.startTrace(
61
- botId,
62
- graphId,
63
- eventType || 'command',
64
- context.eventArgs || {}
65
- );
66
- }
67
-
68
- try {
69
- this.activeGraph = parsedGraph;
70
- this.context = context;
71
-
72
- if (!this.context.variables) {
73
- this.context.variables = parseVariables(
74
- this.activeGraph.variables,
75
- 'GraphExecutionEngine'
12
+ constructor(nodeRegistry, botManagerOrApi = null) {
13
+ if (!nodeRegistry || typeof nodeRegistry.getNodeConfig !== 'function') {
14
+ throw new Error('GraphExecutionEngine requires a valid NodeRegistry instance.');
15
+ }
16
+ this.nodeRegistry = nodeRegistry;
17
+ this.botManager = botManagerOrApi;
18
+ this.activeGraph = null;
19
+ this.context = null;
20
+ this.memo = new Map();
21
+ this.traceCollector = getTraceCollector();
22
+ this.currentTraceId = null;
23
+ this.debugHandler = null;
24
+ this._inputOverrides = new Map();
25
+
26
+ attachDebugIpcHandler();
27
+ }
28
+
29
+ async execute(graph, context, eventType) {
30
+ const validation = GraphValidation.validateGraphForExecution(graph, 'GraphExecutionEngine.execute');
31
+ if (validation.shouldSkip) {
32
+ return context;
33
+ }
34
+
35
+ const parsedGraph = validation.graph;
36
+ const graphId = context.graphId || null;
37
+ const botId = context.botId || null;
38
+
39
+ if (graphId && botId) {
40
+ this.currentTraceId = await this.traceCollector.startTrace(
41
+ botId, graphId, eventType || 'command', context.eventArgs || {}
76
42
  );
77
- }
78
-
79
- if (!this.context.persistenceIntent) this.context.persistenceIntent = new Map();
80
- this.memo.clear();
81
-
82
- const eventName = eventType || 'command';
83
- const startNode = this.activeGraph.nodes.find(n => n.type === `event:${eventName}`);
84
-
85
- if (startNode) {
86
- if (this.currentTraceId) {
87
- this.traceCollector.recordStep(this.currentTraceId, {
88
- nodeId: startNode.id,
89
- nodeType: startNode.type,
90
- status: 'executed',
91
- duration: 0,
92
- inputs: {},
93
- outputs: await this._captureNodeOutputs(startNode),
94
- });
95
- }
96
-
97
- await this.traverse(startNode, 'exec');
98
- } else if (!eventType) {
99
- throw new Error(`Не найдена стартовая нода события (event:command) в графе.`);
100
- }
101
-
102
- if (this.currentTraceId) {
103
- // Перед завершением trace, захватываем outputs для всех data-нод
104
- await this._captureAllDataNodeOutputs();
105
-
106
- const trace = await this.traceCollector.completeTrace(this.currentTraceId);
107
-
108
- // Если работаем в дочернем процессе (BotProcess), отправляем трассировку в главный процесс
109
- if (trace && process.send) {
110
- process.send({
111
- type: 'trace:completed',
112
- trace: {
113
- ...trace,
114
- steps: trace.steps,
115
- eventArgs: typeof trace.eventArgs === 'string' ? trace.eventArgs : JSON.stringify(trace.eventArgs)
116
- }
117
- });
118
- }
119
-
120
- // Отправляем debug событие только если DebugSessionManager инициализирован
121
- // (он может быть не инициализирован в дочерних процессах BotProcess)
122
- if (graphId) {
123
- try {
124
- const debugManager = getGlobalDebugManager();
125
- const debugState = debugManager.get(graphId);
126
- if (debugState && debugState.activeExecution) {
127
- debugState.broadcast('debug:completed', {
128
- trace: await this.traceCollector.getTrace(this.currentTraceId)
129
- });
130
- }
131
- } catch (error) {
132
- // DebugSessionManager не инициализирован - это нормально для дочерних процессов
133
- // Просто игнорируем
134
- }
135
- }
136
-
137
- this.currentTraceId = null;
138
- }
139
-
140
- } catch (error) {
141
- if (this.currentTraceId) {
142
- // Даже при ошибке захватываем outputs для data-нод
143
- try {
144
- await this._captureAllDataNodeOutputs();
145
- } catch (captureError) {
146
- console.error(`[GraphExecutor] Error capturing outputs on failure:`, captureError);
147
- }
148
-
149
- const trace = await this.traceCollector.failTrace(this.currentTraceId, error);
150
-
151
- // Если работаем в дочернем процессе (BotProcess), отправляем трассировку с ошибкой в главный процесс
152
- if (trace && process.send) {
153
- process.send({
154
- type: 'trace:completed',
155
- trace: {
156
- ...trace,
157
- steps: trace.steps,
158
- eventArgs: typeof trace.eventArgs === 'string' ? trace.eventArgs : JSON.stringify(trace.eventArgs)
159
- }
160
- });
161
- }
162
-
163
- this.currentTraceId = null;
164
- }
165
-
166
- if (!(error instanceof BreakLoopSignal)) {
43
+ }
44
+
45
+ try {
46
+ this.activeGraph = parsedGraph;
47
+ this.context = context;
48
+
49
+ if (!this.context.variables) {
50
+ this.context.variables = parseVariables(this.activeGraph.variables, 'GraphExecutionEngine');
51
+ }
52
+
53
+ if (!this.context.persistenceIntent) {
54
+ this.context.persistenceIntent = new Map();
55
+ }
56
+
57
+ this.debugHandler = new GraphDebugHandler(this.context);
58
+ this._inputOverrides = new Map();
59
+
60
+ let rewindAttempts = 0;
61
+ const MAX_REWINDS = 100;
62
+
63
+ while (true) {
64
+ this.memo.clear();
65
+
66
+ await this._applyReplayState(graphId);
67
+
68
+ const startNode = GraphValidation.findStartNode(this.activeGraph, eventType);
69
+ if (!startNode) {
70
+ if (!eventType) {
71
+ throw new Error(`Не найдена стартовая нода события (event:command) в графе.`);
72
+ }
73
+ break;
74
+ }
75
+
76
+ if (this.currentTraceId) {
77
+ this.traceCollector.recordStep(this.currentTraceId, {
78
+ nodeId: startNode.id,
79
+ nodeType: startNode.type,
80
+ status: 'executed',
81
+ duration: 0,
82
+ inputs: {},
83
+ outputs: await this._captureNodeOutputs(startNode),
84
+ });
85
+ }
86
+
87
+ try {
88
+ await this.traverse(startNode, 'exec');
89
+ break;
90
+ } catch (err) {
91
+ if (err instanceof RewindSignal) {
92
+ rewindAttempts++;
93
+ if (rewindAttempts > MAX_REWINDS) {
94
+ console.error('[Debug] Too many rewinds, aborting');
95
+ break;
96
+ }
97
+ console.log(`[Debug] Rewind requested, restarting graph (attempt ${rewindAttempts})`);
98
+
99
+ if (this.currentTraceId && graphId && botId) {
100
+ try {
101
+ await this.traceCollector.completeTrace(this.currentTraceId);
102
+ } catch (e) {}
103
+ this.currentTraceId = await this.traceCollector.startTrace(
104
+ botId, graphId, eventType || 'command', context.eventArgs || {}
105
+ );
106
+ }
107
+ continue;
108
+ }
109
+ throw err;
110
+ }
111
+ }
112
+
113
+ await this._finalizeTrace(graphId);
114
+
115
+ } catch (error) {
116
+ await this._handleExecutionError(error, graphId);
117
+ }
118
+
119
+ return this.context;
120
+ }
121
+
122
+ async _applyReplayState(graphId) {
123
+ if (!graphId) return;
124
+
125
+ try {
126
+ const debugManager = getGlobalDebugManager();
127
+ const debugState = debugManager.get(graphId);
128
+ if (debugState && debugState.replayState) {
129
+ if (debugState.replayState.variables) {
130
+ this.context.variables = JSON.parse(JSON.stringify(debugState.replayState.variables));
131
+ }
132
+ if (debugState.replayState.commandArguments) {
133
+ this.context.commandArguments = JSON.parse(JSON.stringify(debugState.replayState.commandArguments));
134
+ }
135
+ }
136
+ } catch (e) {}
137
+ }
138
+
139
+ async _finalizeTrace(graphId) {
140
+ if (!this.currentTraceId) return;
141
+
142
+ await this._captureAllDataNodeOutputs();
143
+
144
+ const trace = await this.traceCollector.completeTrace(this.currentTraceId);
145
+
146
+ if (trace && process.send) {
147
+ process.send({
148
+ type: MessageTypes.GRAPH.TRACE_COMPLETED,
149
+ trace: {
150
+ ...trace,
151
+ steps: trace.steps,
152
+ eventArgs: typeof trace.eventArgs === 'string' ? trace.eventArgs : JSON.stringify(trace.eventArgs)
153
+ }
154
+ });
155
+ }
156
+
157
+ if (graphId) {
158
+ try {
159
+ const debugManager = getGlobalDebugManager();
160
+ const debugState = debugManager.get(graphId);
161
+ if (debugState && debugState.activeExecution) {
162
+ debugState.broadcast('debug:completed', {
163
+ trace: await this.traceCollector.getTrace(this.currentTraceId)
164
+ });
165
+ }
166
+ } catch (error) {}
167
+ }
168
+
169
+ this.currentTraceId = null;
170
+ }
171
+
172
+ async _handleExecutionError(error, graphId) {
173
+ if (this.currentTraceId) {
174
+ try {
175
+ await this._captureAllDataNodeOutputs();
176
+ } catch (captureError) {
177
+ console.error(`[GraphExecutor] Error capturing outputs on failure:`, captureError);
178
+ }
179
+
180
+ const trace = await this.traceCollector.failTrace(this.currentTraceId, error);
181
+
182
+ if (trace && process.send) {
183
+ process.send({
184
+ type: MessageTypes.GRAPH.TRACE_COMPLETED,
185
+ trace: {
186
+ ...trace,
187
+ steps: trace.steps,
188
+ eventArgs: typeof trace.eventArgs === 'string' ? trace.eventArgs : JSON.stringify(trace.eventArgs)
189
+ }
190
+ });
191
+ }
192
+
193
+ this.currentTraceId = null;
194
+ }
195
+
196
+ const isStoppedByDebugger = error?.message === 'Execution stopped by debugger';
197
+ if (!(error instanceof BreakLoopSignal) && !isStoppedByDebugger) {
167
198
  console.error(`[GraphExecutionEngine] Критическая ошибка выполнения графа: ${error.stack}`);
168
- }
169
- }
170
-
171
- return this.context;
172
- }
173
-
174
- async traverse(node, fromPinId) {
175
- // Находим соединение к СУЩЕСТВУЮЩЕЙ ноде (пропускаем мусорные соединения к удалённым нодам)
176
- const connection = this.activeGraph.connections.find(c => {
177
- if (c.sourceNodeId !== node.id || c.sourcePinId !== fromPinId) return false;
178
- const targetExists = this.activeGraph.nodes.some(n => n.id === c.targetNodeId);
179
- return targetExists;
180
- });
181
- if (!connection) {
182
- return;
183
- }
184
-
185
- const nextNode = this.activeGraph.nodes.find(n => n.id === connection.targetNodeId);
186
- if (!nextNode) return;
187
-
188
- if (this.currentTraceId) {
189
- this.traceCollector.recordTraversal(
190
- this.currentTraceId,
191
- node.id,
192
- fromPinId,
193
- nextNode.id
194
- );
195
- }
196
-
197
- await this.executeNode(nextNode);
198
- }
199
-
200
- async executeNode(node) {
201
- const startTime = Date.now();
202
-
203
- // Записываем шаг ДО проверки брейкпоинта, чтобы в trace были данные о предыдущих нодах
204
- if (this.currentTraceId) {
205
- this.traceCollector.recordStep(this.currentTraceId, {
206
- nodeId: node.id,
207
- nodeType: node.type,
208
- status: 'executed',
209
- duration: null,
210
- inputs: await this._captureNodeInputs(node),
211
- outputs: {},
212
- });
213
- }
214
-
215
- // Проверка брейкпоинта ПЕРЕД выполнением (но ПОСЛЕ записи в trace)
216
- await this.checkBreakpoint(node);
217
-
218
- // Проверка step mode ПЕРЕД выполнением (важно делать ДО executor, чтобы поймать ноду до traverse)
219
- await this._checkStepMode(node);
220
-
221
- const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
222
- if (nodeConfig && typeof nodeConfig.executor === 'function') {
223
- const helpers = {
224
- resolvePinValue: this.resolvePinValue.bind(this),
225
- traverse: this.traverse.bind(this),
226
- memo: this.memo,
227
- clearLoopBodyMemo: this.clearLoopBodyMemo.bind(this),
228
- };
229
-
230
- try {
231
- await nodeConfig.executor.call(this, node, this.context, helpers);
232
-
233
- const executionTime = Date.now() - startTime;
234
-
235
- if (this.currentTraceId) {
236
- this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, await this._captureNodeOutputs(node));
237
- this.traceCollector.updateStepDuration(this.currentTraceId, node.id, executionTime);
238
- }
239
- } catch (error) {
240
- if (this.currentTraceId) {
241
- this.traceCollector.updateStepError(
242
- this.currentTraceId,
243
- node.id,
244
- error.message,
245
- Date.now() - startTime
246
- );
247
- }
248
- throw error;
249
- }
250
-
251
- return;
252
- }
253
-
254
- const execCacheKey = `${node.id}_executed`;
255
- if (this.memo.has(execCacheKey)) {
256
- return;
257
- }
258
- this.memo.set(execCacheKey, true);
259
-
260
- try {
261
- await this._executeLegacyNode(node);
262
-
263
-
264
- const executionTime = Date.now() - startTime;
265
-
266
- if (this.currentTraceId) {
267
- this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, await this._captureNodeOutputs(node));
268
- this.traceCollector.updateStepDuration(this.currentTraceId, node.id, executionTime);
269
- }
270
- } catch (error) {
271
- if (this.currentTraceId) {
272
- this.traceCollector.updateStepError(
273
- this.currentTraceId,
274
- node.id,
275
- error.message,
276
- Date.now() - startTime
277
- );
278
- }
279
- throw error;
280
- }
281
- }
282
-
283
- async _checkStepMode(node) {
284
- try {
285
- // Если работаем в дочернем процессе, используем IPC
286
- if (process.send) {
287
- return await this._checkStepModeViaIpc(node);
288
- }
289
-
290
- // Иначе используем прямой доступ к DebugManager
291
- const { getGlobalDebugManager } = require('./services/DebugSessionManager');
292
- const debugManager = getGlobalDebugManager();
293
- const graphId = this.activeGraph?.id;
294
-
295
- if (graphId) {
296
- const debugState = debugManager.get(graphId);
297
- if (debugState && debugState.shouldStepPause(node.id)) {
298
- console.log(`[Debug] Step mode: pausing after executing node ${node.id}`);
299
-
300
- // Получаем inputs для отправки в debug session
301
- const inputs = await this._captureNodeInputs(node);
302
-
303
- // Получаем текущую трассировку для отображения выполненных шагов
304
- const executedSteps = this.currentTraceId
305
- ? await this.traceCollector.getTrace(this.currentTraceId)
306
- : null;
307
-
308
- // Паузим выполнение
309
- await debugState.pause({
310
- nodeId: node.id,
311
- nodeType: node.type,
312
- inputs,
313
- executedSteps,
314
- context: {
315
- user: this.context.user,
316
- variables: this.context.variables,
317
- commandArguments: this.context.commandArguments,
318
- }
319
- });
320
- }
321
- }
322
- } catch (error) {
323
- if (error.message !== 'DebugSessionManager not initialized! Call initializeDebugManager(io) first.') {
324
- throw error;
325
- }
326
- }
327
- }
328
-
329
- async _checkStepModeViaIpc(node) {
330
- // Step mode работает аналогично breakpoint, просто отправляем тип 'step'
331
- const { randomUUID } = require('crypto');
332
- const requestId = randomUUID();
333
-
334
- const inputs = await this._captureNodeInputs(node);
335
- const executedSteps = this.currentTraceId
336
- ? await this.traceCollector.getTrace(this.currentTraceId)
337
- : null;
338
-
339
- process.send({
340
- type: 'debug:check_step_mode',
341
- requestId,
342
- payload: {
343
- graphId: this.context.graphId,
344
- nodeId: node.id,
345
- nodeType: node.type,
346
- inputs,
347
- executedSteps,
348
- context: {
349
- user: this.context.user,
350
- variables: this.context.variables,
351
- commandArguments: this.context.commandArguments,
352
- }
353
- }
354
- });
355
-
356
- // Ждём ответа (если step mode не активен, ответ придёт сразу)
357
- await new Promise((resolve) => {
358
- this.pendingDebugRequests.set(requestId, resolve);
359
-
360
- // Таймаут на случай, если ответ не придёт
361
- const timeoutId = setTimeout(() => {
362
- if (this.pendingDebugRequests.has(requestId)) {
363
- this.pendingDebugRequests.delete(requestId);
364
- resolve(null);
365
- }
366
- }, debugConfig.STEP_MODE_TIMEOUT);
367
-
368
- // Сохраняем timeoutId для возможной отмены
369
- this.pendingDebugRequests.set(`${requestId}_timeout`, timeoutId);
370
- });
371
- }
372
-
373
- async _executeLegacyNode(node) {
374
- switch (node.type) {
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
- case 'string:contains':
383
- case 'string:matches':
384
- case 'string:equals': {
385
- await this.traverse(node, 'exec');
386
- break;
387
- }
388
- case 'array:get_random_element': {
389
- await this.traverse(node, 'element');
390
- break;
391
- }
392
- case 'array:add_element':
393
- case 'array:remove_by_index':
394
- case 'array:get_by_index':
395
- case 'array:find_index':
396
- case 'array:contains': {
397
- await this.traverse(node, 'result');
398
- break;
399
- }
400
- case 'data:array_literal':
401
- case 'data:make_object':
402
- case 'data:get_variable':
403
- case 'data:get_argument':
404
- case 'data:length':
405
- case 'data:get_entity_field':
406
- case 'data:cast':
407
- case 'data:string_literal':
408
- case 'data:get_user_field':
409
- case 'data:get_server_players':
410
- case 'data:get_bot_look':
411
- case 'math:operation':
412
- case 'math:random_number':
413
- case 'logic:operation':
414
- case 'string:concat':
415
- case 'object:create':
416
- case 'object:get':
417
- case 'object:set':
418
- case 'object:delete':
419
- case 'object:has_key': {
420
- await this.traverse(node, 'value');
421
- break;
422
- }
423
- }
424
- }
425
-
426
- clearLoopBodyMemo(loopNode) {
427
- const nodesToClear = new Set();
428
- const queue = [];
429
-
430
- const initialConnection = this.activeGraph.connections.find(
431
- c => c.sourceNodeId === loopNode.id && c.sourcePinId === 'loop_body'
432
- );
433
- if (initialConnection) {
434
- const firstNode = this.activeGraph.nodes.find(n => n.id === initialConnection.targetNodeId);
435
- if (firstNode) {
436
- queue.push(firstNode);
199
+ } else if (isStoppedByDebugger) {
200
+ console.log('[GraphExecutionEngine] Execution stopped by user from debugger');
437
201
  }
438
202
  }
439
-
440
- const visited = new Set();
441
- while (queue.length > 0) {
442
- const currentNode = queue.shift();
443
- if (visited.has(currentNode.id)) continue;
444
- visited.add(currentNode.id);
445
-
446
- nodesToClear.add(currentNode.id);
447
-
448
- const connections = this.activeGraph.connections.filter(c => c.sourceNodeId === currentNode.id);
449
- for (const conn of connections) {
450
- const nextNode = this.activeGraph.nodes.find(n => n.id === conn.targetNodeId);
451
- if (nextNode) {
452
- queue.push(nextNode);
203
+
204
+ async traverse(node, fromPinId) {
205
+ const connection = this.activeGraph.connections.find(c => {
206
+ if (c.sourceNodeId !== node.id || c.sourcePinId !== fromPinId) return false;
207
+ const targetExists = this.activeGraph.nodes.some(n => n.id === c.targetNodeId);
208
+ return targetExists;
209
+ });
210
+ if (!connection) return;
211
+
212
+ const nextNode = this.activeGraph.nodes.find(n => n.id === connection.targetNodeId);
213
+ if (!nextNode) return;
214
+
215
+ if (this.currentTraceId) {
216
+ this.traceCollector.recordTraversal(this.currentTraceId, node.id, fromPinId, nextNode.id);
453
217
  }
454
- }
218
+
219
+ await this.executeNode(nextNode);
455
220
  }
456
221
 
457
- for (const nodeId of nodesToClear) {
458
- for (const key of this.memo.keys()) {
459
- if (key.startsWith(nodeId)) {
460
- this.memo.delete(key);
222
+ async executeNode(node) {
223
+ const startTime = Date.now();
224
+
225
+ if (this.currentTraceId) {
226
+ this.traceCollector.recordStep(this.currentTraceId, {
227
+ nodeId: node.id,
228
+ nodeType: node.type,
229
+ status: 'executed',
230
+ duration: null,
231
+ inputs: await this._captureNodeInputs(node),
232
+ outputs: {},
233
+ });
234
+ }
235
+
236
+ if (this.debugHandler) {
237
+ const bpResult = await this.debugHandler.checkBreakpoint(
238
+ node,
239
+ this._captureNodeInputs.bind(this),
240
+ this._captureNodeOutputs.bind(this),
241
+ this.currentTraceId, this.traceCollector
242
+ );
243
+
244
+ await this.debugHandler.checkStepMode(
245
+ node,
246
+ this._captureNodeInputs.bind(this),
247
+ this.currentTraceId, this.traceCollector
248
+ );
249
+ }
250
+
251
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
252
+ if (nodeConfig && typeof nodeConfig.executor === 'function') {
253
+ const helpers = {
254
+ resolvePinValue: this.resolvePinValue.bind(this),
255
+ traverse: this.traverse.bind(this),
256
+ memo: this.memo,
257
+ clearLoopBodyMemo: this.clearLoopBodyMemo.bind(this),
258
+ };
259
+
260
+ try {
261
+ await nodeConfig.executor.call(this, node, this.context, helpers);
262
+
263
+ const executionTime = Date.now() - startTime;
264
+ if (this.currentTraceId) {
265
+ this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, await this._captureNodeOutputs(node));
266
+ this.traceCollector.updateStepDuration(this.currentTraceId, node.id, executionTime);
267
+ }
268
+ } catch (error) {
269
+ if (this.currentTraceId) {
270
+ this.traceCollector.updateStepError(this.currentTraceId, node.id, error.message, Date.now() - startTime);
271
+ }
272
+ throw error;
273
+ }
274
+
275
+ return;
276
+ }
277
+
278
+ const execCacheKey = `${node.id}_executed`;
279
+ if (this.memo.has(execCacheKey)) return;
280
+ this.memo.set(execCacheKey, true);
281
+
282
+ try {
283
+ await this._executeLegacyNode(node);
284
+
285
+ const executionTime = Date.now() - startTime;
286
+ if (this.currentTraceId) {
287
+ this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, await this._captureNodeOutputs(node));
288
+ this.traceCollector.updateStepDuration(this.currentTraceId, node.id, executionTime);
289
+ }
290
+ } catch (error) {
291
+ if (this.currentTraceId) {
292
+ this.traceCollector.updateStepError(this.currentTraceId, node.id, error.message, Date.now() - startTime);
461
293
  }
294
+ throw error;
462
295
  }
463
296
  }
464
- }
465
-
466
- async resolvePinValue(node, pinId, defaultValue = null) {
467
- const connection = this.activeGraph.connections.find(c => c.targetNodeId === node.id && c.targetPinId === pinId);
468
- if (connection) {
469
- const sourceNode = this.activeGraph.nodes.find(n => n.id === connection.sourceNodeId);
470
- return await this.evaluateOutputPin(sourceNode, connection.sourcePinId, defaultValue);
471
- }
472
-
473
- let value = node.data && node.data[pinId] !== undefined ? node.data[pinId] : defaultValue;
474
-
475
- // Автоматически заменяем переменные {varName} в строковых значениях
476
- if (typeof value === 'string' && value.includes('{')) {
477
- value = await this._replaceVariablesInString(value, node);
478
- }
479
-
480
- return value;
481
- }
482
-
483
- /**
484
- * Заменяет переменные {varName} на значения из пинов
485
- * @private
486
- */
487
- async _replaceVariablesInString(text, node) {
488
- const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
489
- const matches = [...text.matchAll(variablePattern)];
490
-
491
- let result = text;
492
-
493
- for (const match of matches) {
494
- const varName = match[1];
495
- const varValue = await this.resolvePinValue(node, `var_${varName}`, '');
496
- result = result.replace(match[0], String(varValue));
497
- }
498
-
499
- return result;
500
- }
501
-
502
- async evaluateOutputPin(node, pinId, defaultValue = null) {
503
- if (!node) return defaultValue;
504
-
505
- const cacheKey = `${node.id}:${pinId}`;
506
- if (this.memo.has(cacheKey)) {
507
- return this.memo.get(cacheKey);
508
- }
509
-
510
- const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
511
- if (nodeConfig && typeof nodeConfig.evaluator === 'function') {
512
- const helpers = {
513
- resolvePinValue: this.resolvePinValue.bind(this),
514
- memo: this.memo,
515
- };
516
-
517
- // Записываем data ноду в трассировку перед вычислением (только один раз)
518
- const traceKey = `trace_recorded:${node.id}`;
519
- const isDataNode = !nodeConfig.pins.inputs.some(p => p.type === 'Exec');
520
-
521
- if (this.currentTraceId && isDataNode && !this.memo.has(traceKey)) {
522
- // Это data нода (нет exec пинов) и она ещё не записана - записываем её inputs
523
- const inputs = await this._captureNodeInputs(node);
524
-
525
- this.traceCollector.recordStep(this.currentTraceId, {
526
- nodeId: node.id,
527
- nodeType: node.type,
528
- status: 'executed',
529
- duration: 0,
530
- inputs,
531
- outputs: {},
532
- });
533
-
534
- // Помечаем, что нода уже записана в трассировку
535
- this.memo.set(traceKey, true);
536
- }
537
-
538
- const result = await nodeConfig.evaluator.call(this, node, pinId, this.context, helpers);
539
-
540
- const isVolatile = this.isNodeVolatile(node);
541
- if (!isVolatile) {
542
- this.memo.set(cacheKey, result);
543
- }
544
-
545
- // Обновляем outputs для data ноды после вычисления И после записи в memo
546
- // Обновляем только один раз для всей ноды (не для каждого пина отдельно)
547
- const traceOutputsKey = `trace_outputs_recorded:${node.id}`;
548
- if (this.currentTraceId && isDataNode && this.memo.has(traceKey) && !this.memo.has(traceOutputsKey)) {
549
- const outputs = {};
550
- for (const outputPin of nodeConfig.pins.outputs) {
551
- if (outputPin.type !== 'Exec') {
552
- const outKey = `${node.id}:${outputPin.id}`;
553
- if (this.memo.has(outKey)) {
554
- outputs[outputPin.id] = this.memo.get(outKey);
555
- }
556
- }
557
- }
558
- this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, outputs);
559
- this.memo.set(traceOutputsKey, true); // Помечаем, что outputs уже записаны
560
- }
561
-
562
- return result;
563
- }
564
-
565
- let result;
566
-
567
- switch (node.type) {
568
- case 'user:set_blacklist':
569
- result = this.memo.get(`${node.id}:updated_user`);
570
- break;
571
- case 'event:command':
572
- if (pinId === 'args') result = this.context.eventArgs?.args || {};
573
- else if (pinId === 'user') result = this.context.eventArgs?.user || {};
574
- else if (pinId === 'chat_type') result = this.context.eventArgs?.typeChat || 'chat';
575
- else if (pinId === 'command_name') result = this.context.eventArgs?.commandName;
576
- else if (pinId === 'success') result = this.context.success !== undefined ? this.context.success : true;
577
- else result = this.context.eventArgs?.[pinId];
578
- break;
579
- case 'event:chat':
580
- if (pinId === 'username') result = this.context.eventArgs?.username || this.context.username;
581
- else if (pinId === 'message') result = this.context.eventArgs?.message || this.context.message;
582
- else if (pinId === 'chatType') result = this.context.eventArgs?.chatType || this.context.chat_type;
583
- else result = this.context.eventArgs?.[pinId] || this.context[pinId];
584
- break;
585
- case 'event:raw_message':
586
- if (pinId === 'rawText') result = this.context.eventArgs?.rawText || this.context.rawText;
587
- else result = this.context.eventArgs?.[pinId] || this.context[pinId];
588
- break;
589
- case 'event:playerJoined':
590
- case 'event:playerLeft':
591
- if (pinId === 'user') result = this.context.eventArgs?.user || this.context.user;
592
- else result = this.context.eventArgs?.[pinId] || this.context[pinId];
593
- break;
594
- case 'event:entitySpawn':
595
- case 'event:entityMoved':
596
- case 'event:entityGone':
597
- if (pinId === 'entity') result = this.context.eventArgs?.entity || this.context.entity;
598
- else result = this.context.eventArgs?.[pinId] || this.context[pinId];
599
- break;
600
- case 'event:health':
601
- case 'event:botDied':
602
- case 'event:botStartup':
603
- result = this.context.eventArgs?.[pinId] || this.context[pinId];
604
- break;
605
- case 'event:websocket_call':
606
- if (pinId === 'graphName') result = this.context.eventArgs?.graphName || this.context.graphName;
607
- else if (pinId === 'data') result = this.context.eventArgs?.data || this.context.data;
608
- else if (pinId === 'socketId') result = this.context.eventArgs?.socketId || this.context.socketId;
609
- else if (pinId === 'keyPrefix') result = this.context.eventArgs?.keyPrefix || this.context.keyPrefix;
610
- else result = this.context.eventArgs?.[pinId] || this.context[pinId];
611
- break;
612
-
613
- case 'flow:for_each': {
614
- if (pinId === 'element') {
615
- result = this.memo.get(`${node.id}:element`);
616
- } else if (pinId === 'index') {
617
- result = this.memo.get(`${node.id}:index`);
297
+
298
+ async _executeLegacyNode(node) {
299
+ const legacyMap = {
300
+ 'string:contains': 'exec', 'string:matches': 'exec', 'string:equals': 'exec',
301
+ 'array:get_random_element': 'element',
302
+ 'array:add_element': 'result', 'array:remove_by_index': 'result',
303
+ 'array:get_by_index': 'result', 'array:find_index': 'result', 'array:contains': 'result',
304
+ 'data:array_literal': 'value', 'data:make_object': 'value', 'data:get_variable': 'value',
305
+ 'data:get_argument': 'value', 'data:length': 'value', 'data:get_entity_field': 'value',
306
+ 'data:cast': 'value', 'data:string_literal': 'value', 'data:get_user_field': 'value',
307
+ 'data:get_server_players': 'value', 'data:get_bot_look': 'value',
308
+ 'math:operation': 'value', 'math:random_number': 'value',
309
+ 'logic:operation': 'value', 'string:concat': 'value',
310
+ 'object:create': 'value', 'object:get': 'value', 'object:set': 'value',
311
+ 'object:delete': 'value', 'object:has_key': 'value',
312
+ };
313
+
314
+ const pin = legacyMap[node.type];
315
+ if (pin) {
316
+ await this.traverse(node, pin);
317
+ }
318
+ }
319
+
320
+ clearLoopBodyMemo(loopNode) {
321
+ const nodesToClear = new Set();
322
+ const queue = [];
323
+
324
+ const initialConnection = this.activeGraph.connections.find(
325
+ c => c.sourceNodeId === loopNode.id && c.sourcePinId === 'loop_body'
326
+ );
327
+ if (initialConnection) {
328
+ const firstNode = this.activeGraph.nodes.find(n => n.id === initialConnection.targetNodeId);
329
+ if (firstNode) queue.push(firstNode);
330
+ }
331
+
332
+ const visited = new Set();
333
+ while (queue.length > 0) {
334
+ const currentNode = queue.shift();
335
+ if (visited.has(currentNode.id)) continue;
336
+ visited.add(currentNode.id);
337
+ nodesToClear.add(currentNode.id);
338
+
339
+ const connections = this.activeGraph.connections.filter(c => c.sourceNodeId === currentNode.id);
340
+ for (const conn of connections) {
341
+ const nextNode = this.activeGraph.nodes.find(n => n.id === conn.targetNodeId);
342
+ if (nextNode) queue.push(nextNode);
618
343
  }
619
- break;
620
- }
344
+ }
621
345
 
622
- default:
623
- result = defaultValue;
624
- break;
625
- }
346
+ for (const nodeId of nodesToClear) {
347
+ for (const key of this.memo.keys()) {
348
+ if (key.startsWith(nodeId)) this.memo.delete(key);
349
+ }
350
+ }
351
+ }
626
352
 
627
- const isVolatile = this.isNodeVolatile(node);
353
+ async resolvePinValue(node, pinId, defaultValue = null) {
354
+ const overrideValue = this.debugHandler?.getInputOverride(node.id, pinId);
355
+ if (overrideValue !== undefined) return overrideValue;
628
356
 
629
- if (!isVolatile) {
630
- this.memo.set(cacheKey, result);
631
- }
357
+ const connection = this.activeGraph.connections.find(
358
+ c => c.targetNodeId === node.id && c.targetPinId === pinId
359
+ );
360
+ if (connection) {
361
+ const sourceNode = this.activeGraph.nodes.find(n => n.id === connection.sourceNodeId);
362
+ return await this.evaluateOutputPin(sourceNode, connection.sourcePinId, defaultValue);
363
+ }
632
364
 
633
- return result;
634
- }
365
+ let value = node.data && node.data[pinId] !== undefined ? node.data[pinId] : defaultValue;
635
366
 
636
- isNodeVolatile(node, visited = new Set(), depth = 0) {
637
- if (!node) return false;
367
+ if (typeof value === 'string' && value.includes('{')) {
368
+ value = await this._replaceVariablesInString(value, node);
369
+ }
638
370
 
639
- if (depth > MAX_RECURSION_DEPTH) {
640
- console.warn(`[GraphExecutionEngine] isNodeVolatile достиг максимальной глубины рекурсии (${MAX_RECURSION_DEPTH})`);
641
- return false;
371
+ return value;
642
372
  }
643
373
 
644
- if (visited.has(node.id)) {
645
- return false;
374
+ async _replaceVariablesInString(text, node) {
375
+ const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
376
+ const matches = [...text.matchAll(variablePattern)];
377
+ let result = text;
378
+
379
+ for (const match of matches) {
380
+ const varName = match[1];
381
+ const varValue = await this.resolvePinValue(node, `var_${varName}`, '');
382
+ result = result.replace(match[0], String(varValue));
383
+ }
384
+
385
+ return result;
646
386
  }
647
- visited.add(node.id);
648
387
 
649
- if (node.type === 'data:get_variable') {
650
- return true;
388
+ async evaluateOutputPin(node, pinId, defaultValue = null) {
389
+ if (!node) return defaultValue;
390
+
391
+ const cacheKey = `${node.id}:${pinId}`;
392
+ if (this.memo.has(cacheKey)) return this.memo.get(cacheKey);
393
+
394
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
395
+ if (nodeConfig && typeof nodeConfig.evaluator === 'function') {
396
+ return await this._evaluateWithConfig(node, pinId, nodeConfig, cacheKey, defaultValue);
397
+ }
398
+
399
+ const result = this._evaluateLegacyNode(node, pinId, defaultValue);
400
+
401
+ if (!GraphDebugHandler.checkVolatility(node, this.activeGraph)) {
402
+ this.memo.set(cacheKey, result);
403
+ }
404
+
405
+ return result;
651
406
  }
652
407
 
653
- const connections = this.activeGraph.connections.filter(c => c.targetNodeId === node.id);
654
- for (const conn of connections) {
655
- const sourceNode = this.activeGraph.nodes.find(n => n.id === conn.sourceNodeId);
656
- if (this.isNodeVolatile(sourceNode, visited, depth + 1)) {
657
- return true;
408
+ async _evaluateWithConfig(node, pinId, nodeConfig, cacheKey, defaultValue) {
409
+ const helpers = { resolvePinValue: this.resolvePinValue.bind(this), memo: this.memo };
410
+
411
+ const traceKey = `trace_recorded:${node.id}`;
412
+ const inputPins = GraphValidation.getNodeInputPins(nodeConfig, node.data);
413
+ const outputPins = GraphValidation.getNodeOutputPins(nodeConfig, node.data);
414
+ const isDataNode = !inputPins.some(p => p && p.type === 'Exec');
415
+
416
+ if (this.currentTraceId && isDataNode && !this.memo.has(traceKey)) {
417
+ this.traceCollector.recordStep(this.currentTraceId, {
418
+ nodeId: node.id, nodeType: node.type, status: 'executed', duration: 0,
419
+ inputs: await this._captureNodeInputs(node), outputs: {},
420
+ });
421
+ this.memo.set(traceKey, true);
658
422
  }
423
+
424
+ const result = await nodeConfig.evaluator.call(this, node, pinId, this.context, helpers);
425
+
426
+ if (!GraphDebugHandler.checkVolatility(node, this.activeGraph)) {
427
+ this.memo.set(cacheKey, result);
428
+ }
429
+
430
+ const traceOutputsKey = `trace_outputs_recorded:${node.id}`;
431
+ if (this.currentTraceId && isDataNode && this.memo.has(traceKey) && !this.memo.has(traceOutputsKey)) {
432
+ const outputs = {};
433
+ for (const outputPin of outputPins) {
434
+ if (outputPin && outputPin.type !== 'Exec') {
435
+ const outKey = `${node.id}:${outputPin.id}`;
436
+ if (this.memo.has(outKey)) outputs[outputPin.id] = this.memo.get(outKey);
437
+ }
438
+ }
439
+ this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, outputs);
440
+ this.memo.set(traceOutputsKey, true);
441
+ }
442
+
443
+ return result;
444
+ }
445
+
446
+ _evaluateLegacyNode(node, pinId, defaultValue) {
447
+ let result;
448
+
449
+ switch (node.type) {
450
+ case 'user:set_blacklist':
451
+ result = this.memo.get(`${node.id}:updated_user`);
452
+ break;
453
+ case 'event:command':
454
+ if (pinId === 'args') result = this.context.eventArgs?.args || {};
455
+ else if (pinId === 'user') result = this.context.eventArgs?.user || {};
456
+ else if (pinId === 'chat_type') result = this.context.eventArgs?.typeChat || 'chat';
457
+ else if (pinId === 'command_name') result = this.context.eventArgs?.commandName;
458
+ else if (pinId === 'success') result = this.context.success !== undefined ? this.context.success : true;
459
+ else result = this.context.eventArgs?.[pinId];
460
+ break;
461
+ case 'event:chat':
462
+ if (pinId === 'username') result = this.context.eventArgs?.username || this.context.username;
463
+ else if (pinId === 'message') result = this.context.eventArgs?.message || this.context.message;
464
+ else if (pinId === 'chatType') result = this.context.eventArgs?.chatType || this.context.chat_type;
465
+ else result = this.context.eventArgs?.[pinId] || this.context[pinId];
466
+ break;
467
+ case 'event:raw_message':
468
+ if (pinId === 'rawText') result = this.context.eventArgs?.rawText || this.context.rawText;
469
+ else result = this.context.eventArgs?.[pinId] || this.context[pinId];
470
+ break;
471
+ case 'event:playerJoined':
472
+ case 'event:playerLeft':
473
+ if (pinId === 'user') result = this.context.eventArgs?.user || this.context.user;
474
+ else result = this.context.eventArgs?.[pinId] || this.context[pinId];
475
+ break;
476
+ case 'event:entitySpawn':
477
+ case 'event:entityMoved':
478
+ case 'event:entityGone':
479
+ if (pinId === 'entity') result = this.context.eventArgs?.entity || this.context.entity;
480
+ else result = this.context.eventArgs?.[pinId] || this.context[pinId];
481
+ break;
482
+ case 'event:health':
483
+ case 'event:botDied':
484
+ case 'event:botStartup':
485
+ result = this.context.eventArgs?.[pinId] || this.context[pinId];
486
+ break;
487
+ case 'event:websocket_call':
488
+ if (pinId === 'graphName') result = this.context.eventArgs?.graphName || this.context.graphName;
489
+ else if (pinId === 'data') result = this.context.eventArgs?.data || this.context.data;
490
+ else if (pinId === 'socketId') result = this.context.eventArgs?.socketId || this.context.socketId;
491
+ else if (pinId === 'keyPrefix') result = this.context.eventArgs?.keyPrefix || this.context.keyPrefix;
492
+ else result = this.context.eventArgs?.[pinId] || this.context[pinId];
493
+ break;
494
+ case 'flow:for_each':
495
+ if (pinId === 'element') result = this.memo.get(`${node.id}:element`);
496
+ else if (pinId === 'index') result = this.memo.get(`${node.id}:index`);
497
+ break;
498
+ default:
499
+ result = defaultValue;
500
+ }
501
+
502
+ return result;
503
+ }
504
+
505
+ hasConnection(node, pinId) {
506
+ if (!this.activeGraph || !this.activeGraph.connections) return false;
507
+ return this.activeGraph.connections.some(conn =>
508
+ conn.targetNodeId === node.id && conn.targetPinId === pinId
509
+ );
659
510
  }
660
511
 
661
- return false;
662
- }
663
-
664
- hasConnection(node, pinId) {
665
- if (!this.activeGraph || !this.activeGraph.connections) return false;
666
- return this.activeGraph.connections.some(conn =>
667
- conn.targetNodeId === node.id && conn.targetPinId === pinId
668
- );
669
- }
670
-
671
- /**
672
- * Захватить outputs для всех data-нод в trace
673
- * Вызывается перед завершением trace, чтобы гарантировать,
674
- * что для всех data-нод записаны outputs (даже если их выходы не подключены)
675
- */
676
- async _captureAllDataNodeOutputs() {
677
- if (!this.currentTraceId) return;
678
-
679
- const trace = await this.traceCollector.getTrace(this.currentTraceId);
680
- if (!trace || !trace.steps) return;
681
-
682
- // Проходим по всем шагам и находим data-ноды
683
- for (const step of trace.steps) {
684
- if (step.type === 'traversal') continue;
685
-
686
- // Проверяем, есть ли уже outputs
687
- if (step.outputs && Object.keys(step.outputs).length > 0) continue;
688
-
689
- // Находим ноду в графе
690
- const node = this.activeGraph.nodes.find(n => n.id === step.nodeId);
691
- if (!node) continue;
692
-
693
- // Получаем конфигурацию ноды
694
- const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
695
- if (!nodeConfig) continue;
696
-
697
- // Проверяем, является ли это data-нодой (нет exec входов)
698
- const isDataNode = !nodeConfig.pins.inputs.some(p => p.type === 'Exec');
699
- if (!isDataNode) continue;
700
-
701
- // Захватываем outputs
702
- try {
703
- const outputs = await this._captureNodeOutputs(node);
704
- if (outputs && Object.keys(outputs).length > 0) {
705
- this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, outputs);
706
- }
707
- } catch (error) {
708
- console.error(`[GraphExecutor] Error capturing outputs for data node ${node.id}:`, error);
709
- }
710
- }
711
- }
712
-
713
- /**
714
- * Захватить значения входов ноды для трассировки
715
- */
716
- async _captureNodeInputs(node) {
717
- const inputs = {};
718
-
719
- // Получаем конфигурацию ноды
720
- const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
721
- if (!nodeConfig || !nodeConfig.pins || !nodeConfig.pins.inputs) {
722
- return inputs;
723
- }
724
-
725
- // Захватываем значения всех входов
726
- for (const inputPin of nodeConfig.pins.inputs) {
727
- if (inputPin.type === 'Exec') continue; // Пропускаем exec пины
728
-
729
- try {
730
- // Используем resolvePinValue для получения актуального значения
731
- const value = await this.resolvePinValue(node, inputPin.id);
732
- // Записываем все значения, включая undefined и null
733
- inputs[inputPin.id] = value;
734
- } catch (error) {
735
- inputs[inputPin.id] = '<error capturing>';
736
- }
737
- }
738
- return inputs;
739
- }
740
-
741
- /**
742
- * Захватить значения выходов ноды для трассировки
743
- */
744
- async _captureNodeOutputs(node) {
745
- const outputs = {};
746
-
747
- // Получаем конфигурацию ноды
748
- const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
749
- if (!nodeConfig || !nodeConfig.pins || !nodeConfig.pins.outputs) {
750
- return outputs;
751
- }
752
-
753
- // Захватываем значения всех выходов
754
- for (const outputPin of nodeConfig.pins.outputs) {
755
- if (outputPin.type === 'Exec') continue; // Пропускаем exec пины
756
-
757
- try {
758
- // Активно вызываем evaluateOutputPin вместо чтения из memo
759
- // Это необходимо для event нод, которые используют switch-case и не пишут в memo
760
- const value = await this.evaluateOutputPin(node, outputPin.id);
761
- // Записываем все значения, включая undefined и null
762
- outputs[outputPin.id] = value;
763
- } catch (error) {
764
- outputs[outputPin.id] = '<error capturing>';
765
- }
766
- }
767
- return outputs;
768
- }
769
-
770
- /**
771
- * Проверить брейкпоинт перед выполнением ноды
772
- */
773
- async checkBreakpoint(node) {
774
- try {
775
- // Если работаем в дочернем процессе, используем IPC
776
- if (process.send) {
777
- return await this._checkBreakpointViaIpc(node);
778
- }
779
-
780
- // Иначе используем прямой доступ к DebugManager
781
- const debugManager = getGlobalDebugManager();
782
- const graphId = this.context.graphId;
783
-
784
- if (!graphId) return;
785
-
786
- const debugState = debugManager.get(graphId);
787
- if (!debugState) return;
788
-
789
- const breakpoint = debugState.breakpoints.get(node.id);
790
- if (!breakpoint || !breakpoint.enabled) return;
791
-
792
- const shouldPause = await this.evaluateBreakpointCondition(breakpoint);
793
-
794
- if (shouldPause) {
795
- console.log(`[Debug] Hit breakpoint at node ${node.id}, pausing execution`);
796
-
797
- breakpoint.hitCount++;
798
-
799
- const inputs = await this._captureNodeInputs(node);
800
-
801
- const executedSteps = this.currentTraceId
802
- ? await this.traceCollector.getTrace(this.currentTraceId)
803
- : null;
804
-
805
- const overrides = await debugState.pause({
806
- nodeId: node.id,
807
- nodeType: node.type,
808
- inputs,
809
- executedSteps, // Добавляем выполненные шаги
810
- context: {
811
- user: this.context.user,
812
- variables: this.context.variables,
813
- commandArguments: this.context.commandArguments,
814
- },
815
- breakpoint: {
816
- condition: breakpoint.condition,
817
- hitCount: breakpoint.hitCount
818
- }
819
- });
820
-
821
- if (overrides && overrides.__stopped) {
822
- throw new Error('Execution stopped by debugger');
823
- }
824
-
825
- if (overrides) {
826
- this.applyWhatIfOverrides(node, overrides);
827
- }
828
- }
829
- } catch (error) {
830
- console.error(`[checkBreakpoint] ERROR:`, error.message, error.stack);
831
- if (error.message === 'DebugSessionManager not initialized! Call initializeDebugManager(io) first.') {
832
- return;
833
- }
834
- // НЕ пробрасываем ошибку дальше, чтобы не сломать выполнение
835
- console.error(`[checkBreakpoint] Ignoring error to continue execution`);
836
- }
837
- }
838
-
839
- /**
840
- * Оценить условие брейкпоинта
841
- */
842
- async evaluateBreakpointCondition(breakpoint) {
843
- // Если нет условия, всегда срабатывает
844
- if (!breakpoint.condition || breakpoint.condition.trim() === '') {
845
- return true;
846
- }
847
-
848
- try {
849
- // Создаем sandbox для безопасного выполнения условия
850
- const sandbox = {
851
- user: this.context.user || {},
852
- args: this.context.commandArguments || {},
853
- variables: this.context.variables || {},
854
- context: this.context
855
- };
856
-
857
- // Используем Function constructor для безопасного выполнения
858
- const fn = new Function(
859
- ...Object.keys(sandbox),
860
- `return (${breakpoint.condition})`
861
- );
862
-
863
- const result = fn(...Object.values(sandbox));
864
-
865
- return Boolean(result);
866
- } catch (error) {
867
- console.error(`[Debug] Error evaluating breakpoint condition: ${error.message}`);
868
- console.error(`[Debug] Condition was: ${breakpoint.condition}`);
869
- return false;
870
- }
871
- }
872
-
873
- /**
874
- * Применить what-if overrides к ноде
875
- */
876
- /**
877
- * Проверить брейкпоинт через IPC (для дочерних процессов)
878
- */
879
- async _checkBreakpointViaIpc(node) {
880
- try {
881
- const { randomUUID } = require('crypto');
882
- const requestId = randomUUID();
883
-
884
- const inputs = await this._captureNodeInputs(node);
885
- const executedSteps = this.currentTraceId
886
- ? await this.traceCollector.getTrace(this.currentTraceId)
887
- : null;
888
-
889
- // Отправляем запрос в главный процесс
890
- process.send({
891
- type: 'debug:check_breakpoint',
892
- requestId,
893
- payload: {
894
- graphId: this.context.graphId,
895
- nodeId: node.id,
896
- nodeType: node.type,
897
- inputs,
898
- executedSteps,
899
- context: {
900
- user: this.context.user,
901
- variables: this.context.variables,
902
- commandArguments: this.context.commandArguments,
903
- }
904
- }
905
- });
906
-
907
- // Ждём ответа
908
- const overrides = await new Promise((resolve) => {
909
- this.pendingDebugRequests.set(requestId, resolve);
910
-
911
- // Таймаут на случай, если ответ не придёт
912
- const timeoutId = setTimeout(() => {
913
- if (this.pendingDebugRequests.has(requestId)) {
914
- this.pendingDebugRequests.delete(requestId);
915
- resolve(null);
916
- }
917
- }, debugConfig.BREAKPOINT_TIMEOUT);
918
-
919
- // Сохраняем timeoutId для возможной отмены
920
- this.pendingDebugRequests.set(`${requestId}_timeout`, timeoutId);
921
- });
922
-
923
- if (overrides && overrides.__stopped) {
924
- throw new Error('Execution stopped by debugger');
925
- }
926
-
927
- if (overrides) {
928
- this.applyWhatIfOverrides(node, overrides);
929
- }
930
- } catch (error) {
931
- if (error.message === 'Execution stopped by debugger') {
932
- throw error;
933
- }
934
- }
935
- }
936
-
937
- /**
938
- * Статический обработчик IPC ответов (глобальный для всех instances)
939
- */
940
- static _handleGlobalDebugResponse(message) {
941
- const { requestId, overrides } = message;
942
- const resolve = GraphExecutionEngine._allPendingRequests.get(requestId);
943
-
944
- if (resolve) {
945
- // Отменяем таймаут
946
- const timeoutId = GraphExecutionEngine._allPendingRequests.get(`${requestId}_timeout`);
947
- if (timeoutId) {
948
- clearTimeout(timeoutId);
949
- GraphExecutionEngine._allPendingRequests.delete(`${requestId}_timeout`);
950
- }
951
-
952
- GraphExecutionEngine._allPendingRequests.delete(requestId);
953
- resolve(overrides);
954
- }
955
- }
956
-
957
- /**
958
- * Обработать IPC ответ от главного процесса (legacy wrapper)
959
- */
960
- _handleDebugResponse(message) {
961
- GraphExecutionEngine._handleGlobalDebugResponse(message);
962
- }
963
-
964
- applyWhatIfOverrides(node, overrides) {
965
- if (!overrides || typeof overrides !== 'object') return;
966
-
967
- console.log(`[Debug] Applying what-if overrides to node ${node.id}:`, overrides);
968
-
969
- for (const [key, value] of Object.entries(overrides)) {
970
- if (key === '__stopped') continue;
971
-
972
- // Парсим ключ для определения типа изменения
973
- // Форматы:
974
- // - "var.varName" - переменная графа
975
- // - "nodeId.out.pinName" - выходной пин ноды
976
- // - "nodeId.in.pinName" - входной пин ноды
977
- // - "pinName" - входной пин текущей ноды
978
-
979
- if (key.startsWith('var.')) {
980
- // Изменение переменной графа
981
- const varName = key.substring(4);
982
- if (!this.context.variables) {
983
- this.context.variables = {};
984
- }
985
- this.context.variables[varName] = value;
986
- console.log(`[Debug] Variable override: ${varName} = ${JSON.stringify(value)}`);
987
- }
988
- else if (key.includes('.out.')) {
989
- // Изменение выходного пина ноды
990
- const [nodeId, , pinName] = key.split('.');
991
- const memoKey = `${nodeId}:${pinName}`;
992
- this.memo.set(memoKey, value);
993
- console.log(`[Debug] Output override: ${memoKey} = ${JSON.stringify(value)}`);
994
- }
995
- else if (key.includes('.in.')) {
996
- // Изменение входного пина ноды (пока не применяется, но можно расширить)
997
- const [nodeId, , pinName] = key.split('.');
998
- console.log(`[Debug] Input override (informational): ${nodeId}.${pinName} = ${JSON.stringify(value)}`);
999
- // Входы можно изменить через изменение outputs предыдущих нод или переменных
1000
- }
1001
- else {
1002
- // Изменение входного пина текущей ноды
1003
- if (!node.data) {
1004
- node.data = {};
1005
- }
1006
- node.data[key] = value;
1007
- }
1008
- }
1009
- }
512
+ async _captureAllDataNodeOutputs() {
513
+ if (!this.currentTraceId) return;
514
+
515
+ const trace = await this.traceCollector.getTrace(this.currentTraceId);
516
+ if (!trace || !trace.steps) return;
517
+
518
+ for (const step of trace.steps) {
519
+ if (step.type === 'traversal') continue;
520
+ if (step.outputs && Object.keys(step.outputs).length > 0) continue;
521
+
522
+ const node = this.activeGraph.nodes.find(n => n.id === step.nodeId);
523
+ if (!node) continue;
524
+
525
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
526
+ if (!nodeConfig) continue;
527
+
528
+ const inputPins = GraphValidation.getNodeInputPins(nodeConfig, node.data);
529
+ if (inputPins.some(p => p && p.type === 'Exec')) continue;
530
+
531
+ try {
532
+ const outputs = await this._captureNodeOutputs(node);
533
+ if (outputs && Object.keys(outputs).length > 0) {
534
+ this.traceCollector.updateStepOutputs(this.currentTraceId, node.id, outputs);
535
+ }
536
+ } catch (error) {
537
+ console.error(`[GraphExecutor] Error capturing outputs for data node ${node.id}:`, error);
538
+ }
539
+ }
540
+ }
541
+
542
+ async _captureNodeInputs(node) {
543
+ const inputs = {};
544
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
545
+ if (!nodeConfig) return inputs;
546
+
547
+ const inputPins = GraphValidation.getNodeInputPins(nodeConfig, node.data);
548
+
549
+ for (const inputPin of inputPins) {
550
+ if (!inputPin || inputPin.type === 'Exec') continue;
551
+ try {
552
+ inputs[inputPin.id] = await this.resolvePinValue(node, inputPin.id);
553
+ } catch (error) {
554
+ inputs[inputPin.id] = '<error capturing>';
555
+ }
556
+ }
557
+ return inputs;
558
+ }
559
+
560
+ async _captureNodeOutputs(node) {
561
+ const outputs = {};
562
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
563
+ if (!nodeConfig) return outputs;
564
+
565
+ const outputPins = GraphValidation.getNodeOutputPins(nodeConfig, node.data);
566
+
567
+ for (const outputPin of outputPins) {
568
+ if (!outputPin || outputPin.type === 'Exec') continue;
569
+ try {
570
+ outputs[outputPin.id] = await this.evaluateOutputPin(node, outputPin.id);
571
+ } catch (error) {
572
+ outputs[outputPin.id] = '<error capturing>';
573
+ }
574
+ }
575
+ return outputs;
576
+ }
1010
577
  }
1011
578
 
1012
579
  module.exports = GraphExecutionEngine;