blockmine 1.22.0 → 1.23.1

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 (108) hide show
  1. package/.claude/agents/code-architect.md +34 -0
  2. package/.claude/agents/code-explorer.md +51 -0
  3. package/.claude/agents/code-reviewer.md +46 -0
  4. package/.claude/commands/feature-dev.md +125 -0
  5. package/.claude/settings.json +5 -1
  6. package/.claude/settings.local.json +12 -1
  7. package/.claude/skills/frontend-design/SKILL.md +42 -0
  8. package/CHANGELOG.md +32 -1
  9. package/README.md +302 -152
  10. package/backend/package-lock.json +681 -9
  11. package/backend/package.json +8 -0
  12. package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
  13. package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
  14. package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
  15. package/backend/prisma/schema.prisma +70 -1
  16. package/backend/src/__tests__/services/BotLifecycleService.test.js +9 -4
  17. package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
  18. package/backend/src/api/middleware/auth.js +27 -0
  19. package/backend/src/api/middleware/botAccess.js +7 -3
  20. package/backend/src/api/middleware/panelApiAuth.js +135 -0
  21. package/backend/src/api/routes/aiAssistant.js +995 -0
  22. package/backend/src/api/routes/auth.js +90 -54
  23. package/backend/src/api/routes/botCommands.js +107 -0
  24. package/backend/src/api/routes/botGroups.js +165 -0
  25. package/backend/src/api/routes/botHistory.js +108 -0
  26. package/backend/src/api/routes/botPermissions.js +99 -0
  27. package/backend/src/api/routes/botStatus.js +36 -0
  28. package/backend/src/api/routes/botUsers.js +162 -0
  29. package/backend/src/api/routes/bots.js +108 -59
  30. package/backend/src/api/routes/eventGraphs.js +4 -1
  31. package/backend/src/api/routes/logs.js +13 -3
  32. package/backend/src/api/routes/panel.js +3 -3
  33. package/backend/src/api/routes/panelApiKeys.js +179 -0
  34. package/backend/src/api/routes/pluginIde.js +1715 -135
  35. package/backend/src/api/routes/plugins.js +170 -13
  36. package/backend/src/api/routes/proxies.js +130 -0
  37. package/backend/src/api/routes/search.js +4 -0
  38. package/backend/src/api/routes/servers.js +20 -3
  39. package/backend/src/api/routes/settings.js +5 -0
  40. package/backend/src/api/routes/system.js +3 -3
  41. package/backend/src/api/routes/traces.js +131 -0
  42. package/backend/src/config/debug.config.js +36 -0
  43. package/backend/src/core/BotHistoryStore.js +180 -0
  44. package/backend/src/core/BotManager.js +14 -4
  45. package/backend/src/core/BotProcess.js +1517 -1092
  46. package/backend/src/core/EventGraphManager.js +194 -280
  47. package/backend/src/core/GraphExecutionEngine.js +1004 -321
  48. package/backend/src/core/MessageQueue.js +12 -6
  49. package/backend/src/core/PluginLoader.js +99 -5
  50. package/backend/src/core/PluginManager.js +74 -13
  51. package/backend/src/core/TaskScheduler.js +1 -1
  52. package/backend/src/core/commands/whois.js +1 -1
  53. package/backend/src/core/node-registries/actions.js +72 -2
  54. package/backend/src/core/node-registries/arrays.js +18 -0
  55. package/backend/src/core/node-registries/data.js +1 -1
  56. package/backend/src/core/node-registries/events.js +14 -0
  57. package/backend/src/core/node-registries/logic.js +17 -0
  58. package/backend/src/core/node-registries/strings.js +34 -0
  59. package/backend/src/core/node-registries/type.js +25 -0
  60. package/backend/src/core/nodes/actions/bot_look_at.js +1 -1
  61. package/backend/src/core/nodes/actions/create_command.js +189 -0
  62. package/backend/src/core/nodes/actions/delete_command.js +92 -0
  63. package/backend/src/core/nodes/actions/http_request.js +23 -4
  64. package/backend/src/core/nodes/actions/send_message.js +2 -12
  65. package/backend/src/core/nodes/actions/update_command.js +133 -0
  66. package/backend/src/core/nodes/arrays/join.js +28 -0
  67. package/backend/src/core/nodes/data/cast.js +2 -1
  68. package/backend/src/core/nodes/data/string_literal.js +2 -13
  69. package/backend/src/core/nodes/logic/not.js +22 -0
  70. package/backend/src/core/nodes/strings/starts_with.js +1 -1
  71. package/backend/src/core/nodes/strings/to_lower.js +22 -0
  72. package/backend/src/core/nodes/strings/to_upper.js +22 -0
  73. package/backend/src/core/nodes/type/to_string.js +32 -0
  74. package/backend/src/core/services/BotLifecycleService.js +835 -596
  75. package/backend/src/core/services/CommandExecutionService.js +430 -351
  76. package/backend/src/core/services/DebugSessionManager.js +347 -0
  77. package/backend/src/core/services/GraphCollaborationManager.js +501 -0
  78. package/backend/src/core/services/MinecraftBotManager.js +259 -0
  79. package/backend/src/core/services/MinecraftViewerService.js +216 -0
  80. package/backend/src/core/services/TraceCollectorService.js +545 -0
  81. package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
  82. package/backend/src/core/system/Transport.js +0 -4
  83. package/backend/src/core/validation/nodeSchemas.js +6 -6
  84. package/backend/src/real-time/botApi/handlers/graphHandlers.js +2 -2
  85. package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +1 -1
  86. package/backend/src/real-time/botApi/utils.js +11 -0
  87. package/backend/src/real-time/panelNamespace.js +387 -0
  88. package/backend/src/real-time/presence.js +7 -2
  89. package/backend/src/real-time/socketHandler.js +395 -4
  90. package/backend/src/server.js +18 -0
  91. package/frontend/dist/assets/index-DqzDkFsP.js +11210 -0
  92. package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
  93. package/frontend/dist/index.html +2 -2
  94. package/frontend/package-lock.json +9437 -0
  95. package/frontend/package.json +8 -0
  96. package/package.json +2 -2
  97. package/screen/console.png +0 -0
  98. package/screen/dashboard.png +0 -0
  99. package/screen/graph_collabe.png +0 -0
  100. package/screen/graph_live_debug.png +0 -0
  101. package/screen/management_command.png +0 -0
  102. package/screen/node_debug_trace.png +0 -0
  103. package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
  104. package/screen/websocket.png +0 -0
  105. 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
  106. 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
  107. package/frontend/dist/assets/index-CfTo92bP.css +0 -1
  108. package/frontend/dist/assets/index-CiFD5X9Z.js +0 -8344
@@ -1,281 +1,195 @@
1
- const GraphExecutionEngine = require('./GraphExecutionEngine');
2
- const nodeRegistry = require('./NodeRegistry');
3
- const prismaService = require('./PrismaService');
4
- const { safeJsonParse } = require('./utils/jsonParser');
5
- const { parseVariables, parseVariableValue } = require('./utils/variableParser');
6
- const validationService = require('./services/ValidationService');
7
-
8
- const prisma = prismaService.getClient();
9
-
10
- class EventGraphManager {
11
- constructor(botManager = null) {
12
- this.botManager = botManager;
13
- this.graphEngine = botManager ? new GraphExecutionEngine(nodeRegistry, botManager) : null;
14
- this.activeGraphs = new Map();
15
- this.graphStates = new Map();
16
- }
17
-
18
- setBotManager(botManager) {
19
- this.botManager = botManager;
20
- if (!this.graphEngine) {
21
- this.graphEngine = new GraphExecutionEngine(nodeRegistry, botManager);
22
- }
23
- }
24
-
25
- async loadGraphsForBot(botId) {
26
- console.log(`[EventGraphs] Загрузка графов для бота ${botId}...`);
27
- const botGraphs = await prisma.eventGraph.findMany({
28
- where: { botId, isEnabled: true },
29
- include: { triggers: true },
30
- });
31
-
32
- const graphsByEvent = new Map();
33
- for (const graph of botGraphs) {
34
- if (!graph.triggers || graph.triggers.length === 0 || !graph.graphJson) continue;
35
-
36
- try {
37
- const parsedGraph = validationService.parseGraph(graph.graphJson, `EventGraph ID ${graph.id}`);
38
- if (!validationService.hasValidBasicStructure(parsedGraph)) continue;
39
-
40
- const initialState = {};
41
- if (graph.variables) {
42
- const parsedVars = safeJsonParse(graph.variables, [], `EventGraph ID ${graph.id} variables`);
43
- Object.assign(initialState, parseVariables(parsedVars, `EventGraph ID ${graph.id}`));
44
- }
45
- this.graphStates.set(`${botId}-${graph.id}`, initialState);
46
-
47
- for (const trigger of graph.triggers) {
48
- if (!graphsByEvent.has(trigger.eventType)) {
49
- graphsByEvent.set(trigger.eventType, []);
50
- }
51
- graphsByEvent.get(trigger.eventType).push({
52
- id: graph.id,
53
- name: graph.name,
54
- nodes: parsedGraph.nodes,
55
- connections: parsedGraph.connections,
56
- variables: parsedGraph.variables || [],
57
- });
58
- }
59
- } catch (e) {
60
- console.error(`[EventGraphs] Ошибка парсинга JSON для графа ID ${graph.id}:`, e);
61
- }
62
- }
63
-
64
- this.activeGraphs.set(botId, graphsByEvent);
65
- console.log(`[EventGraphs] Загружено ${botGraphs.length} графов, сгруппировано по ${graphsByEvent.size} событиям для бота ${botId}.`);
66
- }
67
-
68
- unloadGraphsForBot(botId) {
69
- this.activeGraphs.delete(botId);
70
- for (const key of this.graphStates.keys()) {
71
- if (key.startsWith(`${botId}-`)) {
72
- this.graphStates.delete(key);
73
- }
74
- }
75
- console.log(`[EventGraphs] Графы и их состояния для бота ${botId} выгружены.`);
76
- }
77
-
78
- async handleEvent(botId, eventType, args) {
79
- this.broadcastEventToApi(botId, eventType, args);
80
-
81
- const graphsForBot = this.activeGraphs.get(botId);
82
- if (!graphsForBot) return;
83
-
84
- const graphsToRun = graphsForBot.get(eventType);
85
- if (!graphsToRun || graphsToRun.length === 0) return;
86
-
87
- for (const graph of graphsToRun) {
88
- try {
89
- await this.executeGraph(botId, eventType, graph, args);
90
- } catch (error) {
91
- console.error(`[EventGraphManager] Uncaught error during graph execution for event '${eventType}':`, error);
92
- this.botManager.appendLog(botId, `[ERROR] Uncaught error in graph execution: ${error.message}`);
93
- }
94
- }
95
- }
96
-
97
- async executeGraph(botId, eventType, graph, eventArgs) {
98
- if (!graph || !graph.nodes || graph.nodes.length === 0) return;
99
-
100
- const players = await this.botManager.getPlayerList(botId);
101
-
102
- const botApi = {
103
- sendMessage: (chatType, message, recipient) => {
104
- this.botManager.sendMessageToBot(botId, message, chatType, recipient);
105
- },
106
- executeCommand: (command) => {
107
- this.botManager.sendMessageToBot(botId, command, 'command');
108
- },
109
- lookAt: (position) => {
110
- this.botManager.lookAt(botId, position);
111
- },
112
- getPlayerList: () => players,
113
- getNearbyEntities: (position = null, radius = 32) => {
114
- return this.botManager.getNearbyEntities(botId, position, radius);
115
- },
116
- sendLog: (message) => {
117
- this.botManager.appendLog(botId, message);
118
- },
119
- entity: eventArgs.botEntity || null,
120
- api: {
121
- emitApiEvent: (eventName, payload) => {
122
- this.emitCustomApiEvent(botId, eventName, payload);
123
- },
124
- },
125
- };
126
-
127
- const stateKey = `${botId}-${graph.id}`;
128
-
129
- const initialContext = this.getInitialContextForEvent(eventType, eventArgs);
130
- initialContext.bot = botApi;
131
- initialContext.botId = botId;
132
- initialContext.players = players;
133
- initialContext.botState = eventArgs.botState || {};
134
-
135
- const savedVariables = { ...(this.graphStates.get(stateKey) || {}) };
136
-
137
- if (graph.variables && Array.isArray(graph.variables)) {
138
- for (const v of graph.variables) {
139
- if (!savedVariables.hasOwnProperty(v.name)) {
140
- savedVariables[v.name] = parseVariableValue(v, `EventGraph ID ${graph.id}`);
141
- }
142
- }
143
- }
144
-
145
- initialContext.variables = savedVariables;
146
-
147
- try {
148
- const finalContext = await this.graphEngine.execute(graph, initialContext, eventType);
149
-
150
- if (finalContext && finalContext.variables) {
151
- this.graphStates.set(stateKey, finalContext.variables);
152
- }
153
- } catch (error) {
154
- console.error(`[EventGraphManager] Error during execution or saving state for graph '${graph.name}'`, error);
155
- }
156
- }
157
-
158
- getInitialContextForEvent(eventType, args) {
159
- const context = {};
160
- switch (eventType) {
161
- case 'chat':
162
- case 'private':
163
- case 'global':
164
- case 'clan':
165
- context.user = { username: args.username };
166
- context.username = args.username;
167
- context.message = args.message;
168
- context.chat_type = args.chatType;
169
- break;
170
- case 'raw_message':
171
- context.rawText = args.rawText;
172
- break;
173
- case 'playerJoined':
174
- case 'playerLeft':
175
- context.user = args.user;
176
- break;
177
- case 'botDied':
178
- context.user = args.user;
179
- break;
180
- case 'health':
181
- context.health = args.health;
182
- context.food = args.food;
183
- context.saturation = args.saturation;
184
- break;
185
- case 'tick':
186
- break;
187
- case 'entitySpawn':
188
- case 'entityMoved':
189
- case 'entityGone':
190
- context.entity = args.entity;
191
- break;
192
- case 'command':
193
- context.command_name = args.commandName;
194
- context.user = args.user;
195
- context.args = args.args;
196
- context.chat_type = args.typeChat;
197
- context.success = args.success !== undefined ? args.success : true;
198
- break;
199
- case 'websocket_call':
200
- context.graphName = args.graphName;
201
- context.data = args.data || {};
202
- context.socketId = args.socketId;
203
- context.keyPrefix = args.keyPrefix;
204
- context.sendResponse = args.sendResponse;
205
- break;
206
- }
207
- return context;
208
- }
209
-
210
- /**
211
- * Отправляет события в WebSocket API
212
- */
213
- broadcastEventToApi(botId, eventType, args) {
214
- try {
215
- // Динамический импорт для избежания циклической зависимости
216
- const { getIO } = require('../real-time/socketHandler');
217
- const { broadcastToApiClients } = require('../real-time/botApi');
218
-
219
- const io = getIO();
220
-
221
- switch (eventType) {
222
- case 'chat':
223
- case 'private':
224
- case 'global':
225
- case 'clan':
226
- broadcastToApiClients(io, botId, 'chat:message', {
227
- type: eventType,
228
- username: args.username,
229
- message: args.message,
230
- raw_message: args.rawText || args.raw_message,
231
- });
232
- break;
233
-
234
- case 'playerJoined':
235
- broadcastToApiClients(io, botId, 'player:join', {
236
- username: args.user?.username || args.username,
237
- });
238
- break;
239
-
240
- case 'playerLeft':
241
- broadcastToApiClients(io, botId, 'player:leave', {
242
- username: args.user?.username || args.username,
243
- });
244
- break;
245
-
246
- case 'health':
247
- broadcastToApiClients(io, botId, 'bot:health', {
248
- health: args.health,
249
- food: args.food,
250
- });
251
- break;
252
-
253
- case 'death':
254
- broadcastToApiClients(io, botId, 'bot:death', {});
255
- break;
256
- }
257
- } catch (error) {
258
- }
259
- }
260
-
261
- /**
262
- * Отправляет кастомное событие от плагина в WebSocket API
263
- */
264
- emitCustomApiEvent(botId, eventName, payload = {}) {
265
- try {
266
- const { getIO } = require('../real-time/socketHandler');
267
- const { broadcastToApiClients } = require('../real-time/botApi');
268
-
269
- const io = getIO();
270
- broadcastToApiClients(io, botId, 'plugin:custom_event', {
271
- eventName,
272
- payload,
273
- timestamp: new Date().toISOString(),
274
- });
275
- } catch (error) {
276
- // Игнорируем ошибки - Socket.IO может быть еще не инициализирован
277
- }
278
- }
279
- }
280
-
1
+ const prismaService = require('./PrismaService');
2
+ const { safeJsonParse } = require('./utils/jsonParser');
3
+ const { parseVariables } = require('./utils/variableParser');
4
+ const validationService = require('./services/ValidationService');
5
+ const botHistoryStore = require('./BotHistoryStore');
6
+
7
+ const prisma = prismaService.getClient();
8
+
9
+ class EventGraphManager {
10
+ constructor(botManager = null) {
11
+ this.botManager = botManager;
12
+ this.activeGraphs = new Map();
13
+ this.graphStates = new Map();
14
+ }
15
+
16
+ setBotManager(botManager) {
17
+ this.botManager = botManager;
18
+ }
19
+
20
+ async loadGraphsForBot(botId) {
21
+ console.log(`[EventGraphs] Загрузка графов для бота ${botId}...`);
22
+ const botGraphs = await prisma.eventGraph.findMany({
23
+ where: { botId, isEnabled: true },
24
+ include: { triggers: true },
25
+ });
26
+
27
+ const graphsByEvent = new Map();
28
+ for (const graph of botGraphs) {
29
+ if (!graph.triggers || graph.triggers.length === 0 || !graph.graphJson) continue;
30
+
31
+ try {
32
+ const parsedGraph = validationService.parseGraph(graph.graphJson, `EventGraph ID ${graph.id}`);
33
+ if (!validationService.hasValidBasicStructure(parsedGraph)) continue;
34
+
35
+ const initialState = {};
36
+ if (graph.variables) {
37
+ const parsedVars = safeJsonParse(graph.variables, [], `EventGraph ID ${graph.id} variables`);
38
+ Object.assign(initialState, parseVariables(parsedVars, `EventGraph ID ${graph.id}`));
39
+ }
40
+ this.graphStates.set(`${botId}-${graph.id}`, initialState);
41
+
42
+ for (const trigger of graph.triggers) {
43
+ if (!graphsByEvent.has(trigger.eventType)) {
44
+ graphsByEvent.set(trigger.eventType, []);
45
+ }
46
+ graphsByEvent.get(trigger.eventType).push({
47
+ id: graph.id,
48
+ name: graph.name,
49
+ nodes: parsedGraph.nodes,
50
+ connections: parsedGraph.connections,
51
+ variables: parsedGraph.variables || [],
52
+ });
53
+ }
54
+ } catch (e) {
55
+ console.error(`[EventGraphs] Ошибка парсинга JSON для графа ID ${graph.id}:`, e);
56
+ }
57
+ }
58
+
59
+ this.activeGraphs.set(botId, graphsByEvent);
60
+ console.log(`[EventGraphs] Загружено ${botGraphs.length} графов, сгруппировано по ${graphsByEvent.size} событиям для бота ${botId}.`);
61
+ }
62
+
63
+ unloadGraphsForBot(botId) {
64
+ this.activeGraphs.delete(botId);
65
+ for (const key of this.graphStates.keys()) {
66
+ if (key.startsWith(`${botId}-`)) {
67
+ this.graphStates.delete(key);
68
+ }
69
+ }
70
+ console.log(`[EventGraphs] Графы и их состояния для бота ${botId} выгружены.`);
71
+ }
72
+
73
+ async handleEvent(botId, eventType, args) {
74
+ this.broadcastEventToApi(botId, eventType, args);
75
+
76
+ const graphsForBot = this.activeGraphs.get(botId);
77
+ if (!graphsForBot) return;
78
+
79
+ const graphsToRun = graphsForBot.get(eventType);
80
+ if (!graphsToRun || graphsToRun.length === 0) return;
81
+
82
+ for (const graph of graphsToRun) {
83
+ try {
84
+ await this.executeGraphInChildProcess(botId, eventType, graph, args);
85
+ } catch (error) {
86
+ console.error(`[EventGraphManager] Error sending event to child process for '${eventType}':`, error);
87
+ this.botManager.appendLog(botId, `[ERROR] Error in event graph: ${error.message}`);
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Отправляет граф в child process для выполнения
94
+ */
95
+ async executeGraphInChildProcess(botId, eventType, graph, eventArgs) {
96
+ if (!graph || !graph.nodes || graph.nodes.length === 0) return;
97
+
98
+ const childProcess = this.botManager.getChildProcess(botId);
99
+ if (!childProcess || !childProcess.send) {
100
+ console.error(`[EventGraphManager] No child process found for bot ${botId}`);
101
+ return;
102
+ }
103
+
104
+ childProcess.send({
105
+ type: 'execute_event_graph',
106
+ botId: botId,
107
+ graph: graph,
108
+ eventType: eventType,
109
+ eventArgs: eventArgs
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Отправляет события в WebSocket API
115
+ */
116
+ broadcastEventToApi(botId, eventType, args) {
117
+ try {
118
+ // Динамический импорт для избежания циклической зависимости
119
+ const { getIOSafe } = require('../real-time/socketHandler');
120
+ const { broadcastToApiClients } = require('../real-time/botApi');
121
+
122
+ const io = getIOSafe();
123
+ if (!io) return;
124
+
125
+ switch (eventType) {
126
+ case 'chat':
127
+ case 'private':
128
+ case 'global':
129
+ case 'clan':
130
+ const chatData = {
131
+ type: eventType,
132
+ username: args.username,
133
+ message: args.message,
134
+ raw_message: args.rawText || args.raw_message,
135
+ };
136
+
137
+ broadcastToApiClients(io, botId, 'chat:message', chatData);
138
+
139
+ botHistoryStore.addChatMessage(botId, {
140
+ type: eventType,
141
+ username: args.username,
142
+ message: args.message
143
+ });
144
+ break;
145
+
146
+ case 'playerJoined':
147
+ broadcastToApiClients(io, botId, 'player:join', {
148
+ username: args.user?.username || args.username,
149
+ });
150
+ break;
151
+
152
+ case 'playerLeft':
153
+ broadcastToApiClients(io, botId, 'player:leave', {
154
+ username: args.user?.username || args.username,
155
+ });
156
+ break;
157
+
158
+ case 'health':
159
+ broadcastToApiClients(io, botId, 'bot:health', {
160
+ health: args.health,
161
+ food: args.food,
162
+ });
163
+ break;
164
+
165
+ case 'death':
166
+ broadcastToApiClients(io, botId, 'bot:death', {});
167
+ break;
168
+ }
169
+ } catch (error) {
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Отправляет кастомное событие от плагина в WebSocket API
175
+ */
176
+ emitCustomApiEvent(botId, eventName, payload = {}) {
177
+ try {
178
+ const { getIOSafe } = require('../real-time/socketHandler');
179
+ const { broadcastToApiClients } = require('../real-time/botApi');
180
+
181
+ const io = getIOSafe();
182
+ if (!io) return;
183
+
184
+ broadcastToApiClients(io, botId, 'plugin:custom_event', {
185
+ eventName,
186
+ payload,
187
+ timestamp: new Date().toISOString(),
188
+ });
189
+ } catch (error) {
190
+ // Игнорируем другие ошибки
191
+ }
192
+ }
193
+ }
194
+
281
195
  module.exports = EventGraphManager;