@efengx/openclaw-channel-dragon 0.3.5 → 0.3.7
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/index.js +44 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ let cachedRuntime;
|
|
|
11
11
|
let globalDispatcher;
|
|
12
12
|
console.log("[Dragon] Plugin script is being evaluated by OpenClaw gateway...");
|
|
13
13
|
const channelId = "dragon";
|
|
14
|
+
const processedMessageIds = new Set();
|
|
14
15
|
async function ensureConnection(account, runtimeLogger) {
|
|
15
16
|
if (bridgeClient)
|
|
16
17
|
return;
|
|
@@ -69,10 +70,23 @@ async function startPolling(ctx) {
|
|
|
69
70
|
}
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
|
-
const deliverToOpenClaw = async (content, sessionId = 'default', modelId, attachments) => {
|
|
73
|
+
const deliverToOpenClaw = async (content, sessionId = 'default', modelId, attachments, messageId) => {
|
|
73
74
|
const msg = String(content || '').trim();
|
|
74
75
|
if (!msg && (!attachments || attachments.length === 0))
|
|
75
76
|
return;
|
|
77
|
+
// Deduplication check
|
|
78
|
+
if (messageId && processedMessageIds.has(messageId)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (messageId) {
|
|
82
|
+
processedMessageIds.add(messageId);
|
|
83
|
+
// Keep cache size manageable
|
|
84
|
+
if (processedMessageIds.size > 1000) {
|
|
85
|
+
const first = processedMessageIds.values().next().value;
|
|
86
|
+
if (first !== undefined)
|
|
87
|
+
processedMessageIds.delete(first);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
76
90
|
// sessionKey format: channel:agentId:direct:peerId
|
|
77
91
|
// peerId can be the sessionId for multi-session support
|
|
78
92
|
const sessionKey = `dragon:${account.agentId}:direct:${sessionId}`;
|
|
@@ -153,8 +167,30 @@ async function startPolling(ctx) {
|
|
|
153
167
|
}
|
|
154
168
|
});
|
|
155
169
|
};
|
|
170
|
+
const consumePendingMessages = async () => {
|
|
171
|
+
try {
|
|
172
|
+
const pollUrl = `${orchestratorUrl}/api/agents/${agentId}/messages/poll`;
|
|
173
|
+
const res = await fetch(pollUrl);
|
|
174
|
+
if (res.ok) {
|
|
175
|
+
const data = (await res.json());
|
|
176
|
+
const messages = data.messages || [];
|
|
177
|
+
if (messages.length > 0) {
|
|
178
|
+
logger?.info?.(`dragon channel: [RECOVERY] Consuming ${messages.length} pending messages via polling.`);
|
|
179
|
+
for (const m of messages) {
|
|
180
|
+
await deliverToOpenClaw(String(m.content || ''), String(m.sessionId || 'default'), m.modelId, m.attachments, m.id // Pass DB ID for deduplication
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
logger?.error?.(`dragon channel: [RECOVERY] Polling failed: ${e.message}`);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
156
190
|
const connectOnce = async () => {
|
|
157
191
|
const sseUrl = `${orchestratorUrl}/api/agents/events`;
|
|
192
|
+
// Recovery: Catch up on any messages missed during downtime
|
|
193
|
+
void consumePendingMessages();
|
|
158
194
|
const handleSseText = async (chunkText, bufRef) => {
|
|
159
195
|
// Diagnostic: log first chunk of SSE if it's not a heartbeat comment
|
|
160
196
|
const isHeartbeat = chunkText.trim().startsWith(':');
|
|
@@ -191,7 +227,8 @@ async function startPolling(ctx) {
|
|
|
191
227
|
logger?.info?.(`dragon channel: [DEBUG] Received WORKBENCH_MESSAGE via SSE. AgentID=${evt.agentId}, ContentLen=${evt.payload?.content?.length}, Latency=${latency}ms`);
|
|
192
228
|
logger?.info?.(`dragon channel: [DEBUG] Payload Content: "${evt.payload?.content?.substring(0, 500)}"`);
|
|
193
229
|
logger?.info?.(`dragon channel: [DEBUG] Current Account Config: ${JSON.stringify(account)}`);
|
|
194
|
-
await deliverToOpenClaw(String(evt?.payload?.content || ''), String(evt?.payload?.sessionId || 'default'), evt?.payload?.modelId, evt?.payload?.attachments
|
|
230
|
+
await deliverToOpenClaw(String(evt?.payload?.content || ''), String(evt?.payload?.sessionId || 'default'), evt?.payload?.modelId, evt?.payload?.attachments, evt?.payload?.id // Pass DB ID for deduplication
|
|
231
|
+
);
|
|
195
232
|
}
|
|
196
233
|
else if (evt.type === 'FETCH_HISTORY') {
|
|
197
234
|
const { sessionId } = evt.payload;
|
|
@@ -314,6 +351,10 @@ async function startPolling(ctx) {
|
|
|
314
351
|
}
|
|
315
352
|
};
|
|
316
353
|
let attempt = 0;
|
|
354
|
+
// Safety: Periodic polling fallback every 60 seconds
|
|
355
|
+
const pollInterval = setInterval(() => {
|
|
356
|
+
void consumePendingMessages();
|
|
357
|
+
}, 60_000);
|
|
317
358
|
while (!ctx.abortSignal.aborted) {
|
|
318
359
|
try {
|
|
319
360
|
attempt += 1;
|
|
@@ -329,6 +370,7 @@ async function startPolling(ctx) {
|
|
|
329
370
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
330
371
|
}
|
|
331
372
|
}
|
|
373
|
+
clearInterval(pollInterval);
|
|
332
374
|
logger?.info?.({ agentId }, "dragon channel: SSE loop terminated");
|
|
333
375
|
}
|
|
334
376
|
const base = createChannelPluginBase({
|