@lobu/worker 2.8.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.
- package/dist/core/error-handler.d.ts +7 -0
- package/dist/core/error-handler.d.ts.map +1 -0
- package/dist/core/error-handler.js +58 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/core/project-scanner.d.ts +9 -0
- package/dist/core/project-scanner.d.ts.map +1 -0
- package/dist/core/project-scanner.js +64 -0
- package/dist/core/project-scanner.js.map +1 -0
- package/dist/core/types.d.ts +102 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +8 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/url-utils.d.ts +5 -0
- package/dist/core/url-utils.d.ts.map +1 -0
- package/dist/core/url-utils.js +13 -0
- package/dist/core/url-utils.js.map +1 -0
- package/dist/core/workspace.d.ts +29 -0
- package/dist/core/workspace.d.ts.map +1 -0
- package/dist/core/workspace.js +104 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/embedded/just-bash-bootstrap.d.ts +21 -0
- package/dist/embedded/just-bash-bootstrap.d.ts.map +1 -0
- package/dist/embedded/just-bash-bootstrap.js +215 -0
- package/dist/embedded/just-bash-bootstrap.js.map +1 -0
- package/dist/gateway/gateway-integration.d.ts +57 -0
- package/dist/gateway/gateway-integration.d.ts.map +1 -0
- package/dist/gateway/gateway-integration.js +209 -0
- package/dist/gateway/gateway-integration.js.map +1 -0
- package/dist/gateway/message-batcher.d.ts +27 -0
- package/dist/gateway/message-batcher.d.ts.map +1 -0
- package/dist/gateway/message-batcher.js +102 -0
- package/dist/gateway/message-batcher.js.map +1 -0
- package/dist/gateway/sse-client.d.ts +74 -0
- package/dist/gateway/sse-client.d.ts.map +1 -0
- package/dist/gateway/sse-client.js +748 -0
- package/dist/gateway/sse-client.js.map +1 -0
- package/dist/gateway/types.d.ts +60 -0
- package/dist/gateway/types.d.ts.map +1 -0
- package/dist/gateway/types.js +6 -0
- package/dist/gateway/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions/builder.d.ts +8 -0
- package/dist/instructions/builder.d.ts.map +1 -0
- package/dist/instructions/builder.js +53 -0
- package/dist/instructions/builder.js.map +1 -0
- package/dist/instructions/providers.d.ts +13 -0
- package/dist/instructions/providers.d.ts.map +1 -0
- package/dist/instructions/providers.js +26 -0
- package/dist/instructions/providers.js.map +1 -0
- package/dist/modules/lifecycle.d.ts +18 -0
- package/dist/modules/lifecycle.d.ts.map +1 -0
- package/dist/modules/lifecycle.js +56 -0
- package/dist/modules/lifecycle.js.map +1 -0
- package/dist/openclaw/custom-tools.d.ts +17 -0
- package/dist/openclaw/custom-tools.d.ts.map +1 -0
- package/dist/openclaw/custom-tools.js +195 -0
- package/dist/openclaw/custom-tools.js.map +1 -0
- package/dist/openclaw/instructions.d.ts +15 -0
- package/dist/openclaw/instructions.d.ts.map +1 -0
- package/dist/openclaw/instructions.js +32 -0
- package/dist/openclaw/instructions.js.map +1 -0
- package/dist/openclaw/model-resolver.d.ts +30 -0
- package/dist/openclaw/model-resolver.d.ts.map +1 -0
- package/dist/openclaw/model-resolver.js +147 -0
- package/dist/openclaw/model-resolver.js.map +1 -0
- package/dist/openclaw/plugin-loader.d.ts +39 -0
- package/dist/openclaw/plugin-loader.d.ts.map +1 -0
- package/dist/openclaw/plugin-loader.js +347 -0
- package/dist/openclaw/plugin-loader.js.map +1 -0
- package/dist/openclaw/processor.d.ts +38 -0
- package/dist/openclaw/processor.d.ts.map +1 -0
- package/dist/openclaw/processor.js +182 -0
- package/dist/openclaw/processor.js.map +1 -0
- package/dist/openclaw/session-context.d.ts +44 -0
- package/dist/openclaw/session-context.d.ts.map +1 -0
- package/dist/openclaw/session-context.js +151 -0
- package/dist/openclaw/session-context.js.map +1 -0
- package/dist/openclaw/tool-policy.d.ts +23 -0
- package/dist/openclaw/tool-policy.d.ts.map +1 -0
- package/dist/openclaw/tool-policy.js +151 -0
- package/dist/openclaw/tool-policy.js.map +1 -0
- package/dist/openclaw/tools.d.ts +6 -0
- package/dist/openclaw/tools.d.ts.map +1 -0
- package/dist/openclaw/tools.js +158 -0
- package/dist/openclaw/tools.js.map +1 -0
- package/dist/openclaw/worker.d.ts +39 -0
- package/dist/openclaw/worker.d.ts.map +1 -0
- package/dist/openclaw/worker.js +1340 -0
- package/dist/openclaw/worker.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +304 -0
- package/dist/server.js.map +1 -0
- package/dist/shared/audio-provider-suggestions.d.ts +13 -0
- package/dist/shared/audio-provider-suggestions.d.ts.map +1 -0
- package/dist/shared/audio-provider-suggestions.js +105 -0
- package/dist/shared/audio-provider-suggestions.js.map +1 -0
- package/dist/shared/processor-utils.d.ts +6 -0
- package/dist/shared/processor-utils.d.ts.map +1 -0
- package/dist/shared/processor-utils.js +30 -0
- package/dist/shared/processor-utils.js.map +1 -0
- package/dist/shared/provider-auth-hints.d.ts +6 -0
- package/dist/shared/provider-auth-hints.d.ts.map +1 -0
- package/dist/shared/provider-auth-hints.js +51 -0
- package/dist/shared/provider-auth-hints.js.map +1 -0
- package/dist/shared/tool-display-config.d.ts +16 -0
- package/dist/shared/tool-display-config.d.ts.map +1 -0
- package/dist/shared/tool-display-config.js +67 -0
- package/dist/shared/tool-display-config.js.map +1 -0
- package/dist/shared/tool-implementations.d.ts +55 -0
- package/dist/shared/tool-implementations.d.ts.map +1 -0
- package/dist/shared/tool-implementations.js +519 -0
- package/dist/shared/tool-implementations.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SSE client for receiving jobs from dispatcher
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.GatewayClient = void 0;
|
|
40
|
+
exports.consumePendingConfigNotifications = consumePendingConfigNotifications;
|
|
41
|
+
const node_child_process_1 = require("node:child_process");
|
|
42
|
+
const core_1 = require("@lobu/core");
|
|
43
|
+
const zod_1 = require("zod");
|
|
44
|
+
const gateway_integration_1 = require("./gateway-integration");
|
|
45
|
+
const message_batcher_1 = require("./message-batcher");
|
|
46
|
+
const logger = (0, core_1.createLogger)("sse-client");
|
|
47
|
+
const pendingConfigNotifications = [];
|
|
48
|
+
/**
|
|
49
|
+
* Returns and clears all pending config change notifications.
|
|
50
|
+
* Called by the worker before building the next prompt.
|
|
51
|
+
*/
|
|
52
|
+
function consumePendingConfigNotifications() {
|
|
53
|
+
if (pendingConfigNotifications.length === 0)
|
|
54
|
+
return [];
|
|
55
|
+
return pendingConfigNotifications.splice(0);
|
|
56
|
+
}
|
|
57
|
+
// Zod schemas for runtime validation of SSE event data
|
|
58
|
+
const ConnectedEventSchema = zod_1.z.object({
|
|
59
|
+
deploymentName: zod_1.z.string(),
|
|
60
|
+
});
|
|
61
|
+
// PlatformMetadata has known fields plus string index signature
|
|
62
|
+
const PlatformMetadataSchema = zod_1.z
|
|
63
|
+
.object({
|
|
64
|
+
team_id: zod_1.z.string().optional(),
|
|
65
|
+
channel: zod_1.z.string().optional(),
|
|
66
|
+
ts: zod_1.z.string().optional(),
|
|
67
|
+
thread_ts: zod_1.z.string().optional(),
|
|
68
|
+
files: zod_1.z.array(zod_1.z.any()).optional(),
|
|
69
|
+
})
|
|
70
|
+
.and(zod_1.z.record(zod_1.z.string(), zod_1.z.union([
|
|
71
|
+
zod_1.z.string(),
|
|
72
|
+
zod_1.z.number(),
|
|
73
|
+
zod_1.z.boolean(),
|
|
74
|
+
zod_1.z.array(zod_1.z.any()),
|
|
75
|
+
zod_1.z.undefined(),
|
|
76
|
+
])));
|
|
77
|
+
// AgentOptions has known fields plus arbitrary extra fields (including nested objects)
|
|
78
|
+
const AgentOptionsSchema = zod_1.z
|
|
79
|
+
.object({
|
|
80
|
+
runtime: zod_1.z.string().optional(),
|
|
81
|
+
model: zod_1.z.string().optional(),
|
|
82
|
+
maxTokens: zod_1.z.number().optional(),
|
|
83
|
+
temperature: zod_1.z.number().optional(),
|
|
84
|
+
allowedTools: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional(),
|
|
85
|
+
disallowedTools: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional(),
|
|
86
|
+
timeoutMinutes: zod_1.z.union([zod_1.z.number(), zod_1.z.string()]).optional(),
|
|
87
|
+
// Additional settings passed through from gateway
|
|
88
|
+
networkConfig: zod_1.z.any().optional(),
|
|
89
|
+
envVars: zod_1.z.any().optional(),
|
|
90
|
+
})
|
|
91
|
+
.passthrough();
|
|
92
|
+
const JobEventSchema = zod_1.z.object({
|
|
93
|
+
payload: zod_1.z.object({
|
|
94
|
+
botId: zod_1.z.string(),
|
|
95
|
+
userId: zod_1.z.string(),
|
|
96
|
+
agentId: zod_1.z.string(),
|
|
97
|
+
conversationId: zod_1.z.string(),
|
|
98
|
+
platform: zod_1.z.string(),
|
|
99
|
+
channelId: zod_1.z.string(),
|
|
100
|
+
messageId: zod_1.z.string(),
|
|
101
|
+
messageText: zod_1.z.string(),
|
|
102
|
+
platformMetadata: PlatformMetadataSchema,
|
|
103
|
+
agentOptions: AgentOptionsSchema,
|
|
104
|
+
jobId: zod_1.z.string().optional(),
|
|
105
|
+
teamId: zod_1.z.string().optional(), // Optional for WhatsApp (top-level) and Slack (in platformMetadata)
|
|
106
|
+
}),
|
|
107
|
+
processedIds: zod_1.z.array(zod_1.z.string()).optional(),
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* Gateway client for workers - connects to dispatcher via SSE
|
|
111
|
+
* Receives jobs via SSE stream, sends responses via HTTP POST
|
|
112
|
+
*/
|
|
113
|
+
class GatewayClient {
|
|
114
|
+
dispatcherUrl;
|
|
115
|
+
workerToken;
|
|
116
|
+
userId;
|
|
117
|
+
deploymentName;
|
|
118
|
+
isRunning = false;
|
|
119
|
+
currentWorker = null;
|
|
120
|
+
abortController;
|
|
121
|
+
currentJobId;
|
|
122
|
+
currentTraceId; // Trace ID for end-to-end observability
|
|
123
|
+
currentTraceparent; // W3C traceparent for distributed tracing
|
|
124
|
+
reconnectAttempts = 0;
|
|
125
|
+
maxReconnectAttempts = 10;
|
|
126
|
+
messageBatcher;
|
|
127
|
+
eventErrorCount = 0;
|
|
128
|
+
eventErrorThreshold = 10;
|
|
129
|
+
httpPort;
|
|
130
|
+
constructor(dispatcherUrl, workerToken, userId, deploymentName, httpPort) {
|
|
131
|
+
this.dispatcherUrl = dispatcherUrl;
|
|
132
|
+
this.workerToken = workerToken;
|
|
133
|
+
this.userId = userId;
|
|
134
|
+
this.deploymentName = deploymentName;
|
|
135
|
+
this.httpPort = httpPort;
|
|
136
|
+
// Get initial traceId from environment (set by deployment)
|
|
137
|
+
this.currentTraceId = process.env.TRACE_ID;
|
|
138
|
+
this.messageBatcher = new message_batcher_1.MessageBatcher({
|
|
139
|
+
onBatchReady: async (messages) => {
|
|
140
|
+
await this.processBatchedMessages(messages);
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
logger.info({ traceId: this.currentTraceId, deploymentName }, "Worker connected");
|
|
144
|
+
}
|
|
145
|
+
async start() {
|
|
146
|
+
this.isRunning = true;
|
|
147
|
+
while (this.isRunning) {
|
|
148
|
+
try {
|
|
149
|
+
await this.connectAndListen();
|
|
150
|
+
if (!this.isRunning)
|
|
151
|
+
break;
|
|
152
|
+
await this.handleReconnect();
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
156
|
+
logger.info("SSE connection aborted");
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
logger.error("SSE connection error:", error);
|
|
160
|
+
if (!this.isRunning)
|
|
161
|
+
break;
|
|
162
|
+
await this.handleReconnect();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async connectAndListen() {
|
|
167
|
+
// Abort previous controller before creating a new one
|
|
168
|
+
if (this.abortController) {
|
|
169
|
+
this.abortController.abort();
|
|
170
|
+
}
|
|
171
|
+
const abortController = new globalThis.AbortController();
|
|
172
|
+
this.abortController = abortController;
|
|
173
|
+
const streamUrl = this.httpPort
|
|
174
|
+
? `${this.dispatcherUrl}/worker/stream?httpPort=${this.httpPort}`
|
|
175
|
+
: `${this.dispatcherUrl}/worker/stream`;
|
|
176
|
+
logger.info(`Connecting to dispatcher at ${streamUrl} (attempt ${this.reconnectAttempts + 1})`);
|
|
177
|
+
const response = await fetch(streamUrl, {
|
|
178
|
+
method: "GET",
|
|
179
|
+
headers: {
|
|
180
|
+
Authorization: `Bearer ${this.workerToken}`,
|
|
181
|
+
Accept: "text/event-stream",
|
|
182
|
+
},
|
|
183
|
+
signal: abortController.signal,
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(`Failed to connect to dispatcher: ${response.status} ${response.statusText}`);
|
|
187
|
+
}
|
|
188
|
+
logger.info("✅ Connected to dispatcher via SSE");
|
|
189
|
+
this.reconnectAttempts = 0;
|
|
190
|
+
const reader = response.body?.getReader();
|
|
191
|
+
const decoder = new TextDecoder();
|
|
192
|
+
if (!reader) {
|
|
193
|
+
throw new Error("No response body");
|
|
194
|
+
}
|
|
195
|
+
let buffer = "";
|
|
196
|
+
logger.info("[SSE-CLIENT] 🔄 Starting SSE stream reading loop");
|
|
197
|
+
while (this.isRunning) {
|
|
198
|
+
const { done, value } = await reader.read();
|
|
199
|
+
if (done) {
|
|
200
|
+
logger.info("[SSE-CLIENT] SSE stream ended");
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
204
|
+
logger.debug(`[SSE-CLIENT] 📨 Received chunk: ${chunk.substring(0, 200)}`);
|
|
205
|
+
buffer += chunk;
|
|
206
|
+
const events = buffer.split("\n\n");
|
|
207
|
+
buffer = events.pop() || "";
|
|
208
|
+
logger.debug(`[SSE-CLIENT] 📊 Parsed ${events.length} events from buffer`);
|
|
209
|
+
for (const event of events) {
|
|
210
|
+
if (!event.trim())
|
|
211
|
+
continue;
|
|
212
|
+
const lines = event.split("\n");
|
|
213
|
+
let eventType = "message";
|
|
214
|
+
let eventData = "";
|
|
215
|
+
for (const line of lines) {
|
|
216
|
+
if (line.startsWith("event:")) {
|
|
217
|
+
eventType = line.substring(6).trim();
|
|
218
|
+
}
|
|
219
|
+
else if (line.startsWith("data:")) {
|
|
220
|
+
eventData = line.substring(5).trim();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (eventData) {
|
|
224
|
+
logger.info(`[SSE-CLIENT] 🎯 Processing event type: ${eventType}`);
|
|
225
|
+
// Don't await - fire async to avoid blocking SSE reading loop
|
|
226
|
+
this.handleEvent(eventType, eventData).catch((error) => {
|
|
227
|
+
this.eventErrorCount++;
|
|
228
|
+
logger.error(`[SSE-CLIENT] Error handling ${eventType} event (error ${this.eventErrorCount}/${this.eventErrorThreshold}):`, error);
|
|
229
|
+
// Trigger cleanup if too many errors
|
|
230
|
+
if (this.eventErrorCount >= this.eventErrorThreshold) {
|
|
231
|
+
logger.error(`❌ Event error threshold reached (${this.eventErrorCount} errors). Triggering cleanup...`);
|
|
232
|
+
this.cleanupOnEventError(eventType, error).catch((cleanupErr) => {
|
|
233
|
+
logger.error("Failed to cleanup after event errors:", cleanupErr);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Send a quick delivery receipt to the gateway confirming job was received.
|
|
243
|
+
* Fire-and-forget — don't block job processing on the receipt send.
|
|
244
|
+
*/
|
|
245
|
+
sendDeliveryReceipt(jobId) {
|
|
246
|
+
const url = `${this.dispatcherUrl}/worker/response`;
|
|
247
|
+
fetch(url, {
|
|
248
|
+
method: "POST",
|
|
249
|
+
headers: {
|
|
250
|
+
"Content-Type": "application/json",
|
|
251
|
+
Authorization: `Bearer ${this.workerToken}`,
|
|
252
|
+
},
|
|
253
|
+
body: JSON.stringify({ jobId, received: true }),
|
|
254
|
+
signal: AbortSignal.timeout(10_000),
|
|
255
|
+
}).catch((err) => {
|
|
256
|
+
logger.warn(`Failed to send delivery receipt for job ${jobId}:`, err);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Send a heartbeat ACK back to the gateway so stale cleanup is based on
|
|
261
|
+
* verified inbound worker activity rather than outbound SSE writes.
|
|
262
|
+
*/
|
|
263
|
+
sendHeartbeatAck() {
|
|
264
|
+
const url = `${this.dispatcherUrl}/worker/response`;
|
|
265
|
+
fetch(url, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: {
|
|
268
|
+
"Content-Type": "application/json",
|
|
269
|
+
Authorization: `Bearer ${this.workerToken}`,
|
|
270
|
+
},
|
|
271
|
+
body: JSON.stringify({ received: true, heartbeat: true }),
|
|
272
|
+
signal: AbortSignal.timeout(10_000),
|
|
273
|
+
}).catch((err) => {
|
|
274
|
+
logger.warn("Failed to send heartbeat ACK:", err);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async handleReconnect() {
|
|
278
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
279
|
+
logger.error("Max reconnection attempts reached, giving up");
|
|
280
|
+
this.isRunning = false;
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
this.reconnectAttempts++;
|
|
284
|
+
const delay = Math.min(1000 * 2 ** (this.reconnectAttempts - 1), 60000);
|
|
285
|
+
logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
|
286
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
287
|
+
}
|
|
288
|
+
async stop() {
|
|
289
|
+
try {
|
|
290
|
+
this.isRunning = false;
|
|
291
|
+
if (this.abortController) {
|
|
292
|
+
this.abortController.abort();
|
|
293
|
+
}
|
|
294
|
+
this.messageBatcher.stop();
|
|
295
|
+
if (this.currentWorker) {
|
|
296
|
+
await this.currentWorker.cleanup();
|
|
297
|
+
this.currentWorker = null;
|
|
298
|
+
}
|
|
299
|
+
logger.info("✅ Gateway client stopped");
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
logger.error("Error stopping gateway client:", error);
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async handleEvent(eventType, data) {
|
|
307
|
+
try {
|
|
308
|
+
if (eventType === "connected") {
|
|
309
|
+
const parsedData = JSON.parse(data);
|
|
310
|
+
const validationResult = ConnectedEventSchema.safeParse(parsedData);
|
|
311
|
+
if (!validationResult.success) {
|
|
312
|
+
logger.error("Invalid connected event data:", validationResult.error.format());
|
|
313
|
+
throw new Error(`Connected event validation failed: ${validationResult.error.message}`);
|
|
314
|
+
}
|
|
315
|
+
const connData = validationResult.data;
|
|
316
|
+
logger.info(`Connected to dispatcher for deployment ${connData.deploymentName}`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (eventType === "ping") {
|
|
320
|
+
logger.debug("Received heartbeat ping from dispatcher");
|
|
321
|
+
this.sendHeartbeatAck();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (eventType === "config_changed") {
|
|
325
|
+
logger.info("Received config_changed event from gateway, invalidating session context cache");
|
|
326
|
+
const { invalidateSessionContextCache } = await Promise.resolve().then(() => __importStar(require("../openclaw/session-context")));
|
|
327
|
+
invalidateSessionContextCache();
|
|
328
|
+
// Parse and queue config change notifications for the next prompt
|
|
329
|
+
try {
|
|
330
|
+
const parsed = JSON.parse(data);
|
|
331
|
+
const changes = Array.isArray(parsed?.changes)
|
|
332
|
+
? parsed.changes
|
|
333
|
+
: [];
|
|
334
|
+
if (changes.length > 0) {
|
|
335
|
+
pendingConfigNotifications.push(...changes);
|
|
336
|
+
logger.info(`Queued ${changes.length} config change notification(s)`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// Backward compat: old gateway may send empty or invalid payload
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (eventType === "job") {
|
|
345
|
+
try {
|
|
346
|
+
const parsedData = JSON.parse(data);
|
|
347
|
+
const validationResult = JobEventSchema.safeParse(parsedData);
|
|
348
|
+
if (!validationResult.success) {
|
|
349
|
+
logger.error("Invalid job event data:", validationResult.error.format());
|
|
350
|
+
logger.debug(`Raw job data: ${data}`);
|
|
351
|
+
throw new Error(`Job event validation failed: ${validationResult.error.message}`);
|
|
352
|
+
}
|
|
353
|
+
// Send delivery receipt immediately so the gateway knows
|
|
354
|
+
// the job was actually received (not lost to a stale SSE connection).
|
|
355
|
+
// jobId is at the top level of the SSE event (set by job-router),
|
|
356
|
+
// not inside the validated payload.
|
|
357
|
+
const jobId = parsedData.jobId;
|
|
358
|
+
if (jobId) {
|
|
359
|
+
this.sendDeliveryReceipt(jobId);
|
|
360
|
+
}
|
|
361
|
+
// Zod validates structure but passthrough allows extra fields
|
|
362
|
+
// The validated payload matches MessagePayload interface
|
|
363
|
+
await this.handleThreadMessage(validationResult.data.payload);
|
|
364
|
+
}
|
|
365
|
+
catch (parseError) {
|
|
366
|
+
logger.error(`Failed to parse or validate job event data:`, parseError);
|
|
367
|
+
logger.debug(`Raw job data: ${data}`);
|
|
368
|
+
}
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
logger.warn(`[DEBUG] Unknown SSE event type: ${eventType}, data: ${data}`);
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
logger.error(`Error handling event ${eventType}:`, error);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async handleThreadMessage(data) {
|
|
378
|
+
// Extract traceparent for distributed tracing
|
|
379
|
+
// Prefer platformMetadata.traceparent, fall back to TRACEPARENT env var
|
|
380
|
+
const traceparent = data.platformMetadata?.traceparent || process.env.TRACEPARENT;
|
|
381
|
+
this.currentTraceparent = traceparent;
|
|
382
|
+
// Extract traceId for logging (backwards compatible)
|
|
383
|
+
const traceId = (0, core_1.extractTraceId)(data) || this.currentTraceId || process.env.TRACE_ID;
|
|
384
|
+
this.currentTraceId = traceId;
|
|
385
|
+
const conversationId = data.conversationId;
|
|
386
|
+
if (data.jobId) {
|
|
387
|
+
this.currentJobId = data.jobId;
|
|
388
|
+
// Create child span for job received (linked to parent via traceparent)
|
|
389
|
+
const span = (0, core_1.createChildSpan)("job_received", traceparent, {
|
|
390
|
+
"lobu.job_id": data.jobId,
|
|
391
|
+
"lobu.message_id": data.messageId,
|
|
392
|
+
"lobu.conversation_id": conversationId,
|
|
393
|
+
"lobu.job_type": data.jobType || "message",
|
|
394
|
+
});
|
|
395
|
+
span?.setStatus({ code: core_1.SpanStatusCode.OK });
|
|
396
|
+
span?.end();
|
|
397
|
+
// Flush job_received span immediately
|
|
398
|
+
void (0, core_1.flushTracing)();
|
|
399
|
+
logger.info({
|
|
400
|
+
traceparent,
|
|
401
|
+
traceId,
|
|
402
|
+
jobId: data.jobId,
|
|
403
|
+
messageId: data.messageId,
|
|
404
|
+
jobType: data.jobType,
|
|
405
|
+
}, "Job received");
|
|
406
|
+
}
|
|
407
|
+
if (data.userId.toLowerCase() !== this.userId.toLowerCase()) {
|
|
408
|
+
logger.warn({ traceId, receivedUserId: data.userId, expectedUserId: this.userId }, "Received message for wrong user");
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// Check job type and dispatch accordingly
|
|
412
|
+
if (data.jobType === "exec") {
|
|
413
|
+
await this.handleExecJob(data);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
// Default: message job
|
|
417
|
+
const queuedMessage = {
|
|
418
|
+
payload: data,
|
|
419
|
+
timestamp: Date.now(),
|
|
420
|
+
};
|
|
421
|
+
await this.messageBatcher.addMessage(queuedMessage);
|
|
422
|
+
logger.info({ traceId, messageId: data.messageId, conversationId }, "Message queued for processing");
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Handle exec job - spawn command in sandbox and stream output back
|
|
426
|
+
*/
|
|
427
|
+
async handleExecJob(data) {
|
|
428
|
+
const { execId, execCommand, execCwd, execEnv, execTimeout } = data;
|
|
429
|
+
const conversationId = data.conversationId;
|
|
430
|
+
const traceId = this.currentTraceId;
|
|
431
|
+
const traceparent = this.currentTraceparent;
|
|
432
|
+
if (!execId || !execCommand) {
|
|
433
|
+
logger.error({ traceId, execId }, "Invalid exec job: missing execId or execCommand");
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
logger.info({ traceId, execId, command: execCommand.substring(0, 100) }, "Executing command in sandbox");
|
|
437
|
+
// Create span for exec execution
|
|
438
|
+
const span = (0, core_1.createChildSpan)("exec_execution", traceparent, {
|
|
439
|
+
"lobu.exec_id": execId,
|
|
440
|
+
"lobu.command": execCommand.substring(0, 100),
|
|
441
|
+
});
|
|
442
|
+
// Determine working directory
|
|
443
|
+
const workingDir = execCwd || process.env.WORKSPACE_DIR || "/workspace";
|
|
444
|
+
const timeout = execTimeout || 300000; // 5 minutes default
|
|
445
|
+
// Create transport for sending responses back to gateway
|
|
446
|
+
const transport = new gateway_integration_1.HttpWorkerTransport({
|
|
447
|
+
gatewayUrl: this.dispatcherUrl,
|
|
448
|
+
workerToken: this.workerToken,
|
|
449
|
+
userId: data.userId,
|
|
450
|
+
channelId: data.channelId,
|
|
451
|
+
conversationId,
|
|
452
|
+
originalMessageTs: execId,
|
|
453
|
+
teamId: data.teamId || "api",
|
|
454
|
+
platform: data.platform,
|
|
455
|
+
platformMetadata: data.platformMetadata,
|
|
456
|
+
});
|
|
457
|
+
let completed = false;
|
|
458
|
+
try {
|
|
459
|
+
// Spawn the command
|
|
460
|
+
const proc = (0, node_child_process_1.spawn)("sh", ["-c", execCommand], {
|
|
461
|
+
cwd: workingDir,
|
|
462
|
+
env: { ...process.env, ...execEnv },
|
|
463
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
464
|
+
});
|
|
465
|
+
// Setup timeout
|
|
466
|
+
const timeoutId = setTimeout(() => {
|
|
467
|
+
if (!completed) {
|
|
468
|
+
logger.warn({ traceId, execId }, "Exec timeout reached, killing process");
|
|
469
|
+
proc.kill("SIGTERM");
|
|
470
|
+
setTimeout(() => {
|
|
471
|
+
if (!completed) {
|
|
472
|
+
proc.kill("SIGKILL");
|
|
473
|
+
}
|
|
474
|
+
}, 5000);
|
|
475
|
+
}
|
|
476
|
+
}, timeout);
|
|
477
|
+
// Stream stdout
|
|
478
|
+
proc.stdout?.on("data", (chunk) => {
|
|
479
|
+
const content = chunk.toString();
|
|
480
|
+
transport.sendExecOutput(execId, "stdout", content).catch((err) => {
|
|
481
|
+
logger.error({ traceId, execId, error: err }, "Failed to send stdout");
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
// Stream stderr
|
|
485
|
+
proc.stderr?.on("data", (chunk) => {
|
|
486
|
+
const content = chunk.toString();
|
|
487
|
+
transport.sendExecOutput(execId, "stderr", content).catch((err) => {
|
|
488
|
+
logger.error({ traceId, execId, error: err }, "Failed to send stderr");
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
// Wait for process to complete
|
|
492
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
493
|
+
proc.on("close", (code) => {
|
|
494
|
+
completed = true;
|
|
495
|
+
clearTimeout(timeoutId);
|
|
496
|
+
resolve(code ?? 0);
|
|
497
|
+
});
|
|
498
|
+
proc.on("error", (error) => {
|
|
499
|
+
completed = true;
|
|
500
|
+
clearTimeout(timeoutId);
|
|
501
|
+
reject(error);
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
// Send completion
|
|
505
|
+
await transport.sendExecComplete(execId, exitCode);
|
|
506
|
+
span?.setAttribute("lobu.exit_code", exitCode);
|
|
507
|
+
span?.setStatus({ code: core_1.SpanStatusCode.OK });
|
|
508
|
+
span?.end();
|
|
509
|
+
await (0, core_1.flushTracing)();
|
|
510
|
+
logger.info({ traceId, execId, exitCode }, "Exec completed");
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
514
|
+
// Send error
|
|
515
|
+
await transport.sendExecError(execId, errorMessage).catch((err) => {
|
|
516
|
+
logger.error({ traceId, execId, error: err }, "Failed to send exec error");
|
|
517
|
+
});
|
|
518
|
+
span?.setStatus({ code: core_1.SpanStatusCode.ERROR, message: errorMessage });
|
|
519
|
+
span?.end();
|
|
520
|
+
await (0, core_1.flushTracing)();
|
|
521
|
+
logger.error({ traceId, execId, error: errorMessage }, "Exec failed");
|
|
522
|
+
}
|
|
523
|
+
finally {
|
|
524
|
+
this.currentJobId = undefined;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async processBatchedMessages(messages) {
|
|
528
|
+
if (messages.length === 0)
|
|
529
|
+
return;
|
|
530
|
+
if (messages.length === 1) {
|
|
531
|
+
const singleMessage = messages[0];
|
|
532
|
+
if (singleMessage) {
|
|
533
|
+
await this.processSingleMessage(singleMessage, [
|
|
534
|
+
singleMessage.payload.messageId,
|
|
535
|
+
]);
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
logger.info(`Batching ${messages.length} messages for combined processing`);
|
|
540
|
+
const firstMessage = messages[0];
|
|
541
|
+
if (!firstMessage)
|
|
542
|
+
return;
|
|
543
|
+
const combinedPrompt = messages
|
|
544
|
+
.map((msg, index) => `Message ${index + 1}: ${msg.payload.messageText}`)
|
|
545
|
+
.join("\n\n");
|
|
546
|
+
const batchedMessage = {
|
|
547
|
+
timestamp: firstMessage.timestamp,
|
|
548
|
+
payload: {
|
|
549
|
+
...firstMessage.payload,
|
|
550
|
+
messageText: combinedPrompt,
|
|
551
|
+
agentOptions: firstMessage.payload.agentOptions,
|
|
552
|
+
},
|
|
553
|
+
};
|
|
554
|
+
const processedIds = messages
|
|
555
|
+
.map((m) => m.payload.messageId)
|
|
556
|
+
.filter(Boolean);
|
|
557
|
+
await this.processSingleMessage(batchedMessage, processedIds);
|
|
558
|
+
}
|
|
559
|
+
async processSingleMessage(message, processedIds) {
|
|
560
|
+
// Get traceparent for distributed tracing
|
|
561
|
+
const traceparent = message.payload.platformMetadata?.traceparent ||
|
|
562
|
+
this.currentTraceparent ||
|
|
563
|
+
process.env.TRACEPARENT;
|
|
564
|
+
const traceId = (0, core_1.extractTraceId)(message.payload) ||
|
|
565
|
+
this.currentTraceId ||
|
|
566
|
+
process.env.TRACE_ID;
|
|
567
|
+
const conversationId = message.payload.conversationId;
|
|
568
|
+
// Create child span for agent execution (linked to parent via traceparent)
|
|
569
|
+
const span = (0, core_1.createChildSpan)("agent_execution", traceparent, {
|
|
570
|
+
"lobu.message_id": message.payload.messageId,
|
|
571
|
+
"lobu.conversation_id": conversationId,
|
|
572
|
+
"lobu.user_id": message.payload.userId,
|
|
573
|
+
"lobu.model": message.payload.agentOptions?.model || "default",
|
|
574
|
+
});
|
|
575
|
+
try {
|
|
576
|
+
if (!process.env.USER_ID) {
|
|
577
|
+
logger.warn(`USER_ID not set in environment, using userId from payload: ${message.payload.userId}`);
|
|
578
|
+
process.env.USER_ID = message.payload.userId;
|
|
579
|
+
}
|
|
580
|
+
const workerConfig = this.payloadToWorkerConfig(message.payload);
|
|
581
|
+
logger.info({
|
|
582
|
+
traceparent,
|
|
583
|
+
traceId,
|
|
584
|
+
messageId: message.payload.messageId,
|
|
585
|
+
model: message.payload.agentOptions?.model,
|
|
586
|
+
}, "Agent starting");
|
|
587
|
+
// Worker will decide whether to continue session based on workspace state
|
|
588
|
+
const { OpenClawWorker } = await Promise.resolve().then(() => __importStar(require("../openclaw/worker")));
|
|
589
|
+
this.currentWorker = new OpenClawWorker(workerConfig);
|
|
590
|
+
const workerTransport = this.currentWorker.getWorkerTransport();
|
|
591
|
+
if (workerTransport && workerTransport instanceof gateway_integration_1.HttpWorkerTransport) {
|
|
592
|
+
if (this.currentJobId) {
|
|
593
|
+
workerTransport.setJobId(this.currentJobId);
|
|
594
|
+
}
|
|
595
|
+
// Set processedMessageIds directly on the integration instance
|
|
596
|
+
const messageIds = processedIds && processedIds.length > 0
|
|
597
|
+
? processedIds
|
|
598
|
+
: message?.payload?.messageId
|
|
599
|
+
? [message.payload.messageId]
|
|
600
|
+
: [];
|
|
601
|
+
workerTransport.processedMessageIds = messageIds;
|
|
602
|
+
}
|
|
603
|
+
await this.currentWorker.execute();
|
|
604
|
+
this.currentJobId = undefined;
|
|
605
|
+
// Reset error count on successful message processing
|
|
606
|
+
this.eventErrorCount = 0;
|
|
607
|
+
// End span with success
|
|
608
|
+
span?.setStatus({ code: core_1.SpanStatusCode.OK });
|
|
609
|
+
span?.end();
|
|
610
|
+
// Flush traces immediately to ensure spans are exported before worker scales down
|
|
611
|
+
await (0, core_1.flushTracing)();
|
|
612
|
+
logger.info({
|
|
613
|
+
traceparent,
|
|
614
|
+
messageId: message.payload.messageId,
|
|
615
|
+
conversationId,
|
|
616
|
+
}, "Agent completed");
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
// End span with error
|
|
620
|
+
span?.setStatus({
|
|
621
|
+
code: core_1.SpanStatusCode.ERROR,
|
|
622
|
+
message: error instanceof Error ? error.message : String(error),
|
|
623
|
+
});
|
|
624
|
+
span?.end();
|
|
625
|
+
// Flush traces on error too
|
|
626
|
+
await (0, core_1.flushTracing)();
|
|
627
|
+
logger.error({
|
|
628
|
+
traceparent,
|
|
629
|
+
messageId: message.payload.messageId,
|
|
630
|
+
conversationId,
|
|
631
|
+
error: error instanceof Error ? error.message : String(error),
|
|
632
|
+
}, "Agent failed");
|
|
633
|
+
const workerTransport = this.currentWorker?.getWorkerTransport();
|
|
634
|
+
if (workerTransport) {
|
|
635
|
+
try {
|
|
636
|
+
const enhancedError = error instanceof Error ? error : new Error(String(error));
|
|
637
|
+
await workerTransport.signalError(enhancedError);
|
|
638
|
+
}
|
|
639
|
+
catch (errorSendError) {
|
|
640
|
+
logger.error({ traceId, error: errorSendError }, "Failed to send error to dispatcher");
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
throw error;
|
|
644
|
+
}
|
|
645
|
+
finally {
|
|
646
|
+
if (this.currentWorker) {
|
|
647
|
+
try {
|
|
648
|
+
await this.currentWorker.cleanup();
|
|
649
|
+
}
|
|
650
|
+
catch (cleanupError) {
|
|
651
|
+
logger.error({ traceId, error: cleanupError }, "Error during worker cleanup");
|
|
652
|
+
}
|
|
653
|
+
this.currentWorker = null;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
payloadToWorkerConfig(payload) {
|
|
658
|
+
const conversationId = payload.conversationId || "default";
|
|
659
|
+
const platformMetadata = payload.platformMetadata;
|
|
660
|
+
const agentOptions = {
|
|
661
|
+
...(payload.agentOptions || {}),
|
|
662
|
+
...(payload.agentOptions?.allowedTools
|
|
663
|
+
? { allowedTools: payload.agentOptions.allowedTools }
|
|
664
|
+
: {}),
|
|
665
|
+
...(payload.agentOptions?.disallowedTools
|
|
666
|
+
? { disallowedTools: payload.agentOptions.disallowedTools }
|
|
667
|
+
: {}),
|
|
668
|
+
...(payload.agentOptions?.timeoutMinutes
|
|
669
|
+
? { timeoutMinutes: payload.agentOptions.timeoutMinutes }
|
|
670
|
+
: {}),
|
|
671
|
+
};
|
|
672
|
+
return {
|
|
673
|
+
sessionKey: `session-${conversationId}`,
|
|
674
|
+
userId: payload.userId,
|
|
675
|
+
agentId: payload.agentId,
|
|
676
|
+
channelId: payload.channelId,
|
|
677
|
+
conversationId,
|
|
678
|
+
userPrompt: Buffer.from(payload.messageText).toString("base64"),
|
|
679
|
+
responseChannel: String(platformMetadata.responseChannel || payload.channelId),
|
|
680
|
+
responseId: String(platformMetadata.responseId || payload.messageId),
|
|
681
|
+
botResponseId: platformMetadata.botResponseId
|
|
682
|
+
? String(platformMetadata.botResponseId)
|
|
683
|
+
: undefined,
|
|
684
|
+
// Check both payload.teamId (WhatsApp) and platformMetadata.teamId (Slack)
|
|
685
|
+
teamId: (payload.teamId ?? platformMetadata.teamId)
|
|
686
|
+
? String(payload.teamId ?? platformMetadata.teamId)
|
|
687
|
+
: undefined,
|
|
688
|
+
platform: payload.platform,
|
|
689
|
+
platformMetadata: platformMetadata, // Include full platformMetadata for files and other metadata
|
|
690
|
+
agentOptions: JSON.stringify(agentOptions),
|
|
691
|
+
workspace: {
|
|
692
|
+
baseDirectory: process.env.WORKSPACE_DIR || "/workspace",
|
|
693
|
+
},
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Cleanup resources after event handling errors exceed threshold
|
|
698
|
+
*/
|
|
699
|
+
async cleanupOnEventError(eventType, _error) {
|
|
700
|
+
logger.warn(`Cleaning up after ${this.eventErrorCount} event handling errors (last: ${eventType})`);
|
|
701
|
+
try {
|
|
702
|
+
// Clean up current worker if it exists
|
|
703
|
+
if (this.currentWorker) {
|
|
704
|
+
logger.info("Cleaning up current worker due to event errors");
|
|
705
|
+
try {
|
|
706
|
+
await this.currentWorker.cleanup?.();
|
|
707
|
+
}
|
|
708
|
+
catch (cleanupError) {
|
|
709
|
+
logger.error("Worker cleanup failed:", cleanupError);
|
|
710
|
+
}
|
|
711
|
+
this.currentWorker = null;
|
|
712
|
+
}
|
|
713
|
+
// Reset current job
|
|
714
|
+
if (this.currentJobId) {
|
|
715
|
+
logger.info(`Clearing stuck job: ${this.currentJobId}`);
|
|
716
|
+
this.currentJobId = undefined;
|
|
717
|
+
}
|
|
718
|
+
// Abort SSE connection to trigger reconnect
|
|
719
|
+
if (this.abortController) {
|
|
720
|
+
logger.info("Aborting SSE connection to trigger reconnect");
|
|
721
|
+
this.abortController.abort();
|
|
722
|
+
this.abortController = undefined;
|
|
723
|
+
}
|
|
724
|
+
// Reset error count after cleanup
|
|
725
|
+
this.eventErrorCount = 0;
|
|
726
|
+
logger.info("Event error cleanup completed, will reconnect");
|
|
727
|
+
}
|
|
728
|
+
catch (cleanupError) {
|
|
729
|
+
logger.error("Fatal error during event error cleanup:", cleanupError);
|
|
730
|
+
// Last resort: stop the client entirely
|
|
731
|
+
this.isRunning = false;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
isHealthy() {
|
|
735
|
+
return this.isRunning && !this.messageBatcher.isCurrentlyProcessing();
|
|
736
|
+
}
|
|
737
|
+
getStatus() {
|
|
738
|
+
return {
|
|
739
|
+
isRunning: this.isRunning,
|
|
740
|
+
isProcessing: this.messageBatcher.isCurrentlyProcessing(),
|
|
741
|
+
userId: this.userId,
|
|
742
|
+
deploymentName: this.deploymentName,
|
|
743
|
+
pendingMessages: this.messageBatcher.getPendingCount(),
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
exports.GatewayClient = GatewayClient;
|
|
748
|
+
//# sourceMappingURL=sse-client.js.map
|