palz-connector 1.5.0 → 1.5.2
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/config.js +3 -0
- package/src/monitor.js +56 -5
- package/src/send.js +26 -6
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import fs from "fs";
|
|
11
11
|
import path from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
12
15
|
const DEFAULT_ACCOUNT_ID = "__default__";
|
|
13
16
|
const DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
14
17
|
const DEFAULT_CONFIG_FILENAME = "palz-connector.config.json";
|
package/src/monitor.js
CHANGED
|
@@ -8,6 +8,9 @@ import WebSocket from "ws";
|
|
|
8
8
|
import { handlePalzMessage } from "./bot.js";
|
|
9
9
|
import { reportPalzActivity } from "./activity.js";
|
|
10
10
|
import { tracer, extractTraceparentContext, SpanStatusCode } from "./tracing.js";
|
|
11
|
+
const WS_CONNECT_TIMEOUT_MS = 15_000;
|
|
12
|
+
const WS_PING_INTERVAL_MS = 30_000;
|
|
13
|
+
const WS_PONG_TIMEOUT_MS = 70_000;
|
|
11
14
|
export async function monitorPalzProvider(params) {
|
|
12
15
|
const { cfg, config, runtime, abortSignal, accountId } = params;
|
|
13
16
|
const log = typeof runtime?.log === "function" ? runtime.log : console.log;
|
|
@@ -24,6 +27,7 @@ export async function monitorPalzProvider(params) {
|
|
|
24
27
|
let reconnectDelay = 1000;
|
|
25
28
|
let reconnectTimer = null;
|
|
26
29
|
let currentWs = null;
|
|
30
|
+
let disposeCurrentSocket = null;
|
|
27
31
|
let consecutive4002 = 0;
|
|
28
32
|
const MAX_CONSECUTIVE_4002 = 10;
|
|
29
33
|
const MAX_RECONNECT_DELAY_MS = 16_000;
|
|
@@ -33,6 +37,10 @@ export async function monitorPalzProvider(params) {
|
|
|
33
37
|
clearTimeout(reconnectTimer);
|
|
34
38
|
reconnectTimer = null;
|
|
35
39
|
}
|
|
40
|
+
if (disposeCurrentSocket) {
|
|
41
|
+
disposeCurrentSocket();
|
|
42
|
+
disposeCurrentSocket = null;
|
|
43
|
+
}
|
|
36
44
|
if (currentWs) {
|
|
37
45
|
currentWs.removeAllListeners();
|
|
38
46
|
if (currentWs.readyState === WebSocket.OPEN || currentWs.readyState === WebSocket.CLOSING) {
|
|
@@ -91,15 +99,29 @@ export async function monitorPalzProvider(params) {
|
|
|
91
99
|
currentWs = ws;
|
|
92
100
|
let connectedAt = 0;
|
|
93
101
|
let pingInterval = null;
|
|
102
|
+
let connectTimeout = null;
|
|
103
|
+
let lastPingAt = 0;
|
|
104
|
+
let lastPongAt = Date.now();
|
|
94
105
|
let messageCount = 0;
|
|
95
106
|
let ended = false;
|
|
107
|
+
const disposeSocketTimers = () => {
|
|
108
|
+
if (connectTimeout) {
|
|
109
|
+
clearTimeout(connectTimeout);
|
|
110
|
+
connectTimeout = null;
|
|
111
|
+
}
|
|
112
|
+
if (pingInterval) {
|
|
113
|
+
clearInterval(pingInterval);
|
|
114
|
+
pingInterval = null;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
disposeCurrentSocket = disposeSocketTimers;
|
|
96
118
|
const handleSocketEnd = (code, reasonStr) => {
|
|
97
119
|
if (ended)
|
|
98
120
|
return;
|
|
99
121
|
ended = true;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
disposeSocketTimers();
|
|
123
|
+
if (disposeCurrentSocket === disposeSocketTimers) {
|
|
124
|
+
disposeCurrentSocket = null;
|
|
103
125
|
}
|
|
104
126
|
if (closed)
|
|
105
127
|
return;
|
|
@@ -133,18 +155,47 @@ export async function monitorPalzProvider(params) {
|
|
|
133
155
|
log(`palz[${accountId}]: disconnected (code=${code}, reason=${reasonStr}, uptime=${Math.round(stableMs / 1000)}s, msgs=${messageCount}), reconnecting in ${reconnectDelay}ms`);
|
|
134
156
|
scheduleReconnect();
|
|
135
157
|
};
|
|
158
|
+
connectTimeout = setTimeout(() => {
|
|
159
|
+
if (ws.readyState !== WebSocket.CONNECTING)
|
|
160
|
+
return;
|
|
161
|
+
error(`palz[${accountId}]: WebSocket connect timeout after ${WS_CONNECT_TIMEOUT_MS}ms`);
|
|
162
|
+
ws.terminate();
|
|
163
|
+
handleSocketEnd(1006, `connect timeout ${WS_CONNECT_TIMEOUT_MS}ms`);
|
|
164
|
+
}, WS_CONNECT_TIMEOUT_MS);
|
|
136
165
|
ws.on("open", () => {
|
|
166
|
+
if (connectTimeout) {
|
|
167
|
+
clearTimeout(connectTimeout);
|
|
168
|
+
connectTimeout = null;
|
|
169
|
+
}
|
|
137
170
|
connectedAt = Date.now();
|
|
171
|
+
lastPongAt = connectedAt;
|
|
138
172
|
consecutive4002 = 0;
|
|
139
173
|
log(`palz[${accountId}]: WebSocket connected, bot_id=${config.botId}`);
|
|
140
174
|
pingInterval = setInterval(() => {
|
|
141
175
|
if (ws.readyState === WebSocket.OPEN) {
|
|
176
|
+
const timeSinceLastPong = Date.now() - lastPongAt;
|
|
177
|
+
if (timeSinceLastPong > WS_PONG_TIMEOUT_MS) {
|
|
178
|
+
error(`palz[${accountId}]: WebSocket pong timeout after ${timeSinceLastPong}ms, terminating`);
|
|
179
|
+
ws.terminate();
|
|
180
|
+
handleSocketEnd(1006, `pong timeout ${timeSinceLastPong}ms`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
142
183
|
try {
|
|
184
|
+
lastPingAt = Date.now();
|
|
143
185
|
ws.ping();
|
|
186
|
+
log(`palz[${accountId}]: [WS_HEARTBEAT] ping sent, last_pong_ago=${timeSinceLastPong}ms`);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
error(`palz[${accountId}]: [WS_HEARTBEAT] ping failed: ${err.message ?? err}`);
|
|
144
190
|
}
|
|
145
|
-
catch { }
|
|
146
191
|
}
|
|
147
|
-
},
|
|
192
|
+
}, WS_PING_INTERVAL_MS);
|
|
193
|
+
});
|
|
194
|
+
ws.on("pong", () => {
|
|
195
|
+
const now = Date.now();
|
|
196
|
+
const rttMs = lastPingAt > 0 ? now - lastPingAt : null;
|
|
197
|
+
lastPongAt = now;
|
|
198
|
+
log(`palz[${accountId}]: [WS_HEARTBEAT] pong received${rttMs === null ? "" : `, rtt=${rttMs}ms`}`);
|
|
148
199
|
});
|
|
149
200
|
ws.on("message", (data) => {
|
|
150
201
|
const receivedAt = new Date();
|
package/src/send.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { tracer, trace, SpanStatusCode, buildTraceparentHeader } from "./tracing.js";
|
|
8
8
|
import { contentPartsToOpenAIContent } from "./content.js";
|
|
9
|
+
const SEND_TIMEOUT_MS = 10_000;
|
|
9
10
|
let msgSeq = 0;
|
|
10
11
|
function nextMsgId() {
|
|
11
12
|
return `bot_reply_${Date.now()}_${++msgSeq}`;
|
|
@@ -83,13 +84,32 @@ async function _sendToPalzIMInner(params) {
|
|
|
83
84
|
headers["Traceparent"] = traceparent;
|
|
84
85
|
}
|
|
85
86
|
const startMs = Date.now();
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
const controller = new AbortController();
|
|
88
|
+
const timeout = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);
|
|
89
|
+
let response;
|
|
90
|
+
try {
|
|
91
|
+
response = await fetch(url, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers,
|
|
94
|
+
body: reqBodyStr,
|
|
95
|
+
signal: controller.signal,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
const elapsedMs = Date.now() - startMs;
|
|
100
|
+
const reason = err?.name === "AbortError" ? `timeout ${SEND_TIMEOUT_MS}ms` : (err?.message ?? String(err));
|
|
101
|
+
const failLog = `[HTTP_RES] ERROR elapsed=${elapsedMs}ms reason=${reason}`;
|
|
102
|
+
console.error(`palz-send: ${failLog}`);
|
|
103
|
+
span?.addEvent(failLog);
|
|
104
|
+
throw err?.name === "AbortError"
|
|
105
|
+
? new Error(`Palz send timeout after ${SEND_TIMEOUT_MS}ms`)
|
|
106
|
+
: err;
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
}
|
|
92
111
|
const rawText = await response.text().catch(() => "");
|
|
112
|
+
const elapsedMs = Date.now() - startMs;
|
|
93
113
|
let body = null;
|
|
94
114
|
try {
|
|
95
115
|
body = JSON.parse(rawText);
|