@sider-ai/chrome-openclaw-sider 1.0.31
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/README.md +75 -0
- package/README.zh_CN.md +108 -0
- package/dist/account-cW9SLuNu.d.ts +75 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +87 -0
- package/dist/setup-entry.d.ts +6 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.d.ts +7 -0
- package/dist/setup-plugin-api.js +4 -0
- package/dist/src/account.d.ts +6 -0
- package/dist/src/account.js +260 -0
- package/dist/src/auth.d.ts +30 -0
- package/dist/src/auth.js +225 -0
- package/dist/src/channel-auto-title.d.ts +6 -0
- package/dist/src/channel-auto-title.js +102 -0
- package/dist/src/channel-builders.d.ts +105 -0
- package/dist/src/channel-builders.js +238 -0
- package/dist/src/channel-hooks.d.ts +30 -0
- package/dist/src/channel-hooks.js +380 -0
- package/dist/src/channel-monitor.d.ts +34 -0
- package/dist/src/channel-monitor.js +335 -0
- package/dist/src/channel-parts.d.ts +26 -0
- package/dist/src/channel-parts.js +32 -0
- package/dist/src/channel-relay.d.ts +117 -0
- package/dist/src/channel-relay.js +574 -0
- package/dist/src/channel-runtime.d.ts +33 -0
- package/dist/src/channel-runtime.js +138 -0
- package/dist/src/channel-send-result.d.ts +19 -0
- package/dist/src/channel-send-result.js +126 -0
- package/dist/src/channel-send.d.ts +73 -0
- package/dist/src/channel-send.js +291 -0
- package/dist/src/channel-session-model.d.ts +19 -0
- package/dist/src/channel-session-model.js +244 -0
- package/dist/src/channel-shared.d.ts +153 -0
- package/dist/src/channel-shared.js +96 -0
- package/dist/src/channel-state.d.ts +93 -0
- package/dist/src/channel-state.js +475 -0
- package/dist/src/channel-streaming.d.ts +117 -0
- package/dist/src/channel-streaming.js +681 -0
- package/dist/src/channel-types.d.ts +209 -0
- package/dist/src/channel-types.js +40 -0
- package/dist/src/channel-typing.d.ts +17 -0
- package/dist/src/channel-typing.js +79 -0
- package/dist/src/channel-util.d.ts +78 -0
- package/dist/src/channel-util.js +604 -0
- package/dist/src/channel.d.ts +14 -0
- package/dist/src/channel.js +834 -0
- package/dist/src/channel.setup.d.ts +10 -0
- package/dist/src/channel.setup.js +9 -0
- package/dist/src/config.d.ts +18 -0
- package/dist/src/config.js +38 -0
- package/dist/src/inbound-media.d.ts +37 -0
- package/dist/src/inbound-media.js +148 -0
- package/dist/src/media-upload.d.ts +87 -0
- package/dist/src/media-upload.js +1308 -0
- package/dist/src/setup-core.d.ts +72 -0
- package/dist/src/setup-core.js +343 -0
- package/dist/src/user-agent.d.ts +4 -0
- package/dist/src/user-agent.js +6 -0
- package/openclaw.plugin.json +96 -0
- package/package.json +47 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
import { createSiderWebSocket, logDebug, logWarn, siderRelaySockets } from "./channel-runtime.js";
|
|
3
|
+
import { appendTokenQuery } from "./auth.js";
|
|
4
|
+
function getSocketCloseInfo(closeInfo) {
|
|
5
|
+
if (!closeInfo || typeof closeInfo !== "object") {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
code: typeof closeInfo.code === "number" ? closeInfo.code : void 0,
|
|
10
|
+
reason: typeof closeInfo.reason === "string" ? closeInfo.reason : Buffer.isBuffer(closeInfo.reason) ? closeInfo.reason.toString("utf8") : void 0,
|
|
11
|
+
wasClean: typeof closeInfo.wasClean === "boolean" ? closeInfo.wasClean : void 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function describeSocketClose(closeInfo) {
|
|
15
|
+
const { code, reason, wasClean } = getSocketCloseInfo(closeInfo);
|
|
16
|
+
const parts = [];
|
|
17
|
+
if (typeof code === "number") {
|
|
18
|
+
parts.push(`code=${code}`);
|
|
19
|
+
}
|
|
20
|
+
if (typeof reason === "string" && reason) {
|
|
21
|
+
parts.push(`reason=${reason}`);
|
|
22
|
+
}
|
|
23
|
+
if (typeof wasClean === "boolean") {
|
|
24
|
+
parts.push(`clean=${wasClean}`);
|
|
25
|
+
}
|
|
26
|
+
return parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
27
|
+
}
|
|
28
|
+
function resolveRelayWsUrl(gatewayUrl) {
|
|
29
|
+
const url = new URL(gatewayUrl);
|
|
30
|
+
if (url.pathname === "" || url.pathname === "/") {
|
|
31
|
+
url.pathname = "/ws/relay";
|
|
32
|
+
} else if (!url.pathname.endsWith("/ws/relay")) {
|
|
33
|
+
url.pathname = `${url.pathname.replace(/\/+$/, "")}/ws/relay`;
|
|
34
|
+
}
|
|
35
|
+
if (url.protocol === "http:") {
|
|
36
|
+
url.protocol = "ws:";
|
|
37
|
+
} else if (url.protocol === "https:") {
|
|
38
|
+
url.protocol = "wss:";
|
|
39
|
+
} else if (url.protocol !== "ws:" && url.protocol !== "wss:") {
|
|
40
|
+
throw new Error(`Unsupported gateway URL protocol: ${url.protocol}`);
|
|
41
|
+
}
|
|
42
|
+
return url.toString();
|
|
43
|
+
}
|
|
44
|
+
async function wsDataToText(data) {
|
|
45
|
+
if (typeof data === "string") {
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
if (Buffer.isBuffer(data)) {
|
|
49
|
+
return data.toString("utf8");
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(data) && data.every((item) => Buffer.isBuffer(item))) {
|
|
52
|
+
return Buffer.concat(data).toString("utf8");
|
|
53
|
+
}
|
|
54
|
+
if (data instanceof ArrayBuffer) {
|
|
55
|
+
return new TextDecoder().decode(data);
|
|
56
|
+
}
|
|
57
|
+
if (ArrayBuffer.isView(data)) {
|
|
58
|
+
return new TextDecoder().decode(data);
|
|
59
|
+
}
|
|
60
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) {
|
|
61
|
+
return await data.text();
|
|
62
|
+
}
|
|
63
|
+
return String(data ?? "");
|
|
64
|
+
}
|
|
65
|
+
function parseInboundFrame(rawText) {
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(rawText);
|
|
68
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (typeof parsed.type !== "string" || !parsed.type.trim()) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return parsed;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function waitForSocketOpen(params) {
|
|
80
|
+
const { ws, timeoutMs, accountId } = params;
|
|
81
|
+
await new Promise((resolve, reject) => {
|
|
82
|
+
const timeout = setTimeout(() => {
|
|
83
|
+
cleanup();
|
|
84
|
+
reject(new Error(`[${accountId}] websocket open timeout (${timeoutMs}ms)`));
|
|
85
|
+
}, timeoutMs);
|
|
86
|
+
const cleanup = () => {
|
|
87
|
+
clearTimeout(timeout);
|
|
88
|
+
ws.off("open", onOpen);
|
|
89
|
+
ws.off("close", onClose);
|
|
90
|
+
ws.off("error", onError);
|
|
91
|
+
};
|
|
92
|
+
const onOpen = () => {
|
|
93
|
+
cleanup();
|
|
94
|
+
resolve();
|
|
95
|
+
};
|
|
96
|
+
const onClose = (code, reason) => {
|
|
97
|
+
cleanup();
|
|
98
|
+
reject(new Error(`[${accountId}] websocket closed before open${describeSocketClose({ code, reason })}`));
|
|
99
|
+
};
|
|
100
|
+
const onError = () => {
|
|
101
|
+
cleanup();
|
|
102
|
+
reject(new Error(`[${accountId}] websocket failed before open`));
|
|
103
|
+
};
|
|
104
|
+
ws.on("open", onOpen);
|
|
105
|
+
ws.on("close", onClose);
|
|
106
|
+
ws.on("error", onError);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function closeSocketQuietly(ws) {
|
|
110
|
+
try {
|
|
111
|
+
ws.close();
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function normalizeRelaySocketToken(token) {
|
|
116
|
+
const trimmed = token?.trim();
|
|
117
|
+
return trimmed ? trimmed : void 0;
|
|
118
|
+
}
|
|
119
|
+
function getOrCreateRelaySocketState(accountId) {
|
|
120
|
+
const existing = siderRelaySockets.get(accountId);
|
|
121
|
+
if (existing) {
|
|
122
|
+
return existing;
|
|
123
|
+
}
|
|
124
|
+
const next = {
|
|
125
|
+
ws: null,
|
|
126
|
+
accountSignature: void 0,
|
|
127
|
+
relayId: void 0,
|
|
128
|
+
pendingAcksBySession: /* @__PURE__ */ new Map(),
|
|
129
|
+
pendingAcksByClientReqId: /* @__PURE__ */ new Map(),
|
|
130
|
+
readyWaiters: /* @__PURE__ */ new Set()
|
|
131
|
+
};
|
|
132
|
+
siderRelaySockets.set(accountId, next);
|
|
133
|
+
return next;
|
|
134
|
+
}
|
|
135
|
+
function buildRelaySocketAccountSignature(account) {
|
|
136
|
+
return `${account.gatewayUrl}|${account.relayId}|${normalizeRelaySocketToken(account.token) ?? ""}`;
|
|
137
|
+
}
|
|
138
|
+
function buildRelayMonitorAccountSignature(account) {
|
|
139
|
+
return [
|
|
140
|
+
buildRelaySocketAccountSignature(account),
|
|
141
|
+
String(account.connectTimeoutMs),
|
|
142
|
+
String(account.reconnectDelayMs)
|
|
143
|
+
].join("|");
|
|
144
|
+
}
|
|
145
|
+
function waitForAbortSignal(signal) {
|
|
146
|
+
if (signal.aborted) {
|
|
147
|
+
return Promise.resolve();
|
|
148
|
+
}
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
const onAbort = () => {
|
|
151
|
+
signal.removeEventListener("abort", onAbort);
|
|
152
|
+
resolve();
|
|
153
|
+
};
|
|
154
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function clearRelaySocketState(params) {
|
|
158
|
+
const state = siderRelaySockets.get(params.accountId);
|
|
159
|
+
if (!state) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (params.expectedWs && state.ws && state.ws !== params.expectedWs) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (params.closeSocket && state.ws && state.ws.readyState !== WebSocket.CLOSED) {
|
|
166
|
+
closeSocketQuietly(state.ws);
|
|
167
|
+
}
|
|
168
|
+
state.ws = null;
|
|
169
|
+
state.relayId = void 0;
|
|
170
|
+
state.accountSignature = void 0;
|
|
171
|
+
const ackError = new Error(
|
|
172
|
+
params.reason ? `[${params.accountId}] ${params.reason}` : `[${params.accountId}] relay socket closed`
|
|
173
|
+
);
|
|
174
|
+
const rejected = /* @__PURE__ */ new Set();
|
|
175
|
+
for (const pendingAcks of state.pendingAcksBySession.values()) {
|
|
176
|
+
for (const pendingAck of pendingAcks) {
|
|
177
|
+
if (rejected.has(pendingAck)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
rejected.add(pendingAck);
|
|
181
|
+
clearTimeout(pendingAck.timeout);
|
|
182
|
+
pendingAck.reject(ackError);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
state.pendingAcksBySession.clear();
|
|
186
|
+
state.pendingAcksByClientReqId.clear();
|
|
187
|
+
}
|
|
188
|
+
function setRelaySocketConnected(params) {
|
|
189
|
+
const state = getOrCreateRelaySocketState(params.account.accountId);
|
|
190
|
+
state.ws = params.ws;
|
|
191
|
+
state.relayId = params.relayId;
|
|
192
|
+
state.accountSignature = buildRelaySocketAccountSignature(params.account);
|
|
193
|
+
for (const waiter of state.readyWaiters) {
|
|
194
|
+
clearTimeout(waiter.timeout);
|
|
195
|
+
waiter.resolve(params.ws);
|
|
196
|
+
}
|
|
197
|
+
state.readyWaiters.clear();
|
|
198
|
+
}
|
|
199
|
+
async function waitForRelaySocketReady(params) {
|
|
200
|
+
const state = getOrCreateRelaySocketState(params.account.accountId);
|
|
201
|
+
const expectedSignature = buildRelaySocketAccountSignature(params.account);
|
|
202
|
+
const liveSocket = state.ws;
|
|
203
|
+
if (liveSocket && liveSocket.readyState === WebSocket.OPEN && state.accountSignature === expectedSignature) {
|
|
204
|
+
return { ws: liveSocket, state };
|
|
205
|
+
}
|
|
206
|
+
if (liveSocket && state.accountSignature !== expectedSignature) {
|
|
207
|
+
closeSocketQuietly(liveSocket);
|
|
208
|
+
clearRelaySocketState({
|
|
209
|
+
accountId: params.account.accountId,
|
|
210
|
+
expectedWs: liveSocket,
|
|
211
|
+
closeSocket: false,
|
|
212
|
+
reason: "relay socket account settings changed"
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const ws = await new Promise((resolve, reject) => {
|
|
216
|
+
const timeout = setTimeout(() => {
|
|
217
|
+
state.readyWaiters.delete(waiter);
|
|
218
|
+
reject(new Error(`[${params.account.accountId}] timeout waiting managed relay socket`));
|
|
219
|
+
}, params.timeoutMs);
|
|
220
|
+
const waiter = {
|
|
221
|
+
timeout,
|
|
222
|
+
resolve,
|
|
223
|
+
reject
|
|
224
|
+
};
|
|
225
|
+
state.readyWaiters.add(waiter);
|
|
226
|
+
});
|
|
227
|
+
if (state.accountSignature !== expectedSignature) {
|
|
228
|
+
throw new Error(`[${params.account.accountId}] managed relay socket account signature mismatch`);
|
|
229
|
+
}
|
|
230
|
+
return { ws, state };
|
|
231
|
+
}
|
|
232
|
+
function waitForManagedAck(params) {
|
|
233
|
+
return new Promise((resolve, reject) => {
|
|
234
|
+
const pendingAck = {
|
|
235
|
+
sessionId: params.sessionId,
|
|
236
|
+
clientReqId: params.clientReqId,
|
|
237
|
+
timeout: setTimeout(() => {
|
|
238
|
+
const queue2 = params.state.pendingAcksBySession.get(params.sessionId);
|
|
239
|
+
if (!queue2) {
|
|
240
|
+
if (params.clientReqId) {
|
|
241
|
+
params.state.pendingAcksByClientReqId.delete(params.clientReqId);
|
|
242
|
+
}
|
|
243
|
+
reject(new Error(`[${params.accountId}] timeout waiting sider ack`));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const idx = queue2.indexOf(pendingAck);
|
|
247
|
+
if (idx >= 0) {
|
|
248
|
+
queue2.splice(idx, 1);
|
|
249
|
+
if (queue2.length === 0) {
|
|
250
|
+
params.state.pendingAcksBySession.delete(params.sessionId);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (params.clientReqId) {
|
|
254
|
+
params.state.pendingAcksByClientReqId.delete(params.clientReqId);
|
|
255
|
+
}
|
|
256
|
+
reject(new Error(`[${params.accountId}] timeout waiting sider ack`));
|
|
257
|
+
}, params.timeoutMs),
|
|
258
|
+
resolve,
|
|
259
|
+
reject
|
|
260
|
+
};
|
|
261
|
+
const queue = params.state.pendingAcksBySession.get(params.sessionId) ?? [];
|
|
262
|
+
queue.push(pendingAck);
|
|
263
|
+
params.state.pendingAcksBySession.set(params.sessionId, queue);
|
|
264
|
+
if (params.clientReqId) {
|
|
265
|
+
params.state.pendingAcksByClientReqId.set(params.clientReqId, pendingAck);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function readAckToken(value) {
|
|
270
|
+
if (typeof value === "string") {
|
|
271
|
+
const trimmed = value.trim();
|
|
272
|
+
return trimmed || void 0;
|
|
273
|
+
}
|
|
274
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
275
|
+
return String(value);
|
|
276
|
+
}
|
|
277
|
+
return void 0;
|
|
278
|
+
}
|
|
279
|
+
function resolveAckSessionId(frame) {
|
|
280
|
+
const record = frame;
|
|
281
|
+
return readAckToken(record.session_id) ?? readAckToken(record.sessionId) ?? readAckToken(record.conversation_id) ?? readAckToken(record.conversationId);
|
|
282
|
+
}
|
|
283
|
+
function resolveAckClientReqId(frame) {
|
|
284
|
+
const record = frame;
|
|
285
|
+
return readAckToken(record.client_req_id) ?? readAckToken(record.clientReqId) ?? readAckToken(record.request_id) ?? readAckToken(record.requestId);
|
|
286
|
+
}
|
|
287
|
+
function resolveAckId(frame) {
|
|
288
|
+
const record = frame;
|
|
289
|
+
return readAckToken(record.id) ?? readAckToken(record.message_id) ?? readAckToken(record.messageId) ?? readAckToken(record.event_id) ?? readAckToken(record.eventId) ?? readAckToken(record.ack_id) ?? readAckToken(record.ackId);
|
|
290
|
+
}
|
|
291
|
+
function countPendingAcks(state) {
|
|
292
|
+
let count = 0;
|
|
293
|
+
for (const queue of state.pendingAcksBySession.values()) {
|
|
294
|
+
count += queue.length;
|
|
295
|
+
}
|
|
296
|
+
return count;
|
|
297
|
+
}
|
|
298
|
+
function detachPendingAck(state, pendingAck) {
|
|
299
|
+
const queue = state.pendingAcksBySession.get(pendingAck.sessionId);
|
|
300
|
+
if (queue) {
|
|
301
|
+
const index = queue.indexOf(pendingAck);
|
|
302
|
+
if (index >= 0) {
|
|
303
|
+
queue.splice(index, 1);
|
|
304
|
+
if (queue.length === 0) {
|
|
305
|
+
state.pendingAcksBySession.delete(pendingAck.sessionId);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (pendingAck.clientReqId) {
|
|
310
|
+
const existing = state.pendingAcksByClientReqId.get(pendingAck.clientReqId);
|
|
311
|
+
if (existing === pendingAck) {
|
|
312
|
+
state.pendingAcksByClientReqId.delete(pendingAck.clientReqId);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function settlePendingAck(params) {
|
|
317
|
+
detachPendingAck(params.state, params.pendingAck);
|
|
318
|
+
clearTimeout(params.pendingAck.timeout);
|
|
319
|
+
const resolvedId = params.ackId ?? params.ackClientReqId ?? params.pendingAck.clientReqId;
|
|
320
|
+
if (!resolvedId) {
|
|
321
|
+
params.pendingAck.reject(
|
|
322
|
+
new Error(
|
|
323
|
+
`[${params.accountId}] ack missing id for session ${params.pendingAck.sessionId}`
|
|
324
|
+
)
|
|
325
|
+
);
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
params.pendingAck.resolve(resolvedId);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
function resolveManagedAck(params) {
|
|
332
|
+
const state = siderRelaySockets.get(params.accountId);
|
|
333
|
+
if (!state || countPendingAcks(state) === 0) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
const ackSession = resolveAckSessionId(params.frame);
|
|
337
|
+
const ackId = resolveAckId(params.frame);
|
|
338
|
+
const ackClientReqId = resolveAckClientReqId(params.frame);
|
|
339
|
+
if (ackClientReqId) {
|
|
340
|
+
const pendingByReq = state.pendingAcksByClientReqId.get(ackClientReqId);
|
|
341
|
+
if (pendingByReq) {
|
|
342
|
+
return settlePendingAck({
|
|
343
|
+
accountId: params.accountId,
|
|
344
|
+
state,
|
|
345
|
+
pendingAck: pendingByReq,
|
|
346
|
+
ackId,
|
|
347
|
+
ackClientReqId
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (ackSession) {
|
|
352
|
+
const queue = state.pendingAcksBySession.get(ackSession);
|
|
353
|
+
if (queue && queue.length > 0) {
|
|
354
|
+
const pendingAck = queue[0];
|
|
355
|
+
return settlePendingAck({
|
|
356
|
+
accountId: params.accountId,
|
|
357
|
+
state,
|
|
358
|
+
pendingAck,
|
|
359
|
+
ackId,
|
|
360
|
+
ackClientReqId
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (countPendingAcks(state) === 1) {
|
|
365
|
+
for (const queue of state.pendingAcksBySession.values()) {
|
|
366
|
+
const pendingAck = queue[0];
|
|
367
|
+
if (pendingAck) {
|
|
368
|
+
return settlePendingAck({
|
|
369
|
+
accountId: params.accountId,
|
|
370
|
+
state,
|
|
371
|
+
pendingAck,
|
|
372
|
+
ackId,
|
|
373
|
+
ackClientReqId
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
function resolveFrameClientReqId(frame) {
|
|
381
|
+
return readAckToken(frame.client_req_id) ?? readAckToken(frame.clientReqId);
|
|
382
|
+
}
|
|
383
|
+
function isAckTimeoutError(error) {
|
|
384
|
+
const text = String(error ?? "");
|
|
385
|
+
return text.includes("timeout waiting sider ack");
|
|
386
|
+
}
|
|
387
|
+
async function sendRelayFrameWithAck(params) {
|
|
388
|
+
const { ws, state } = await waitForRelaySocketReady({
|
|
389
|
+
account: params.account,
|
|
390
|
+
timeoutMs: params.account.connectTimeoutMs
|
|
391
|
+
});
|
|
392
|
+
const ackPromise = waitForManagedAck({
|
|
393
|
+
state,
|
|
394
|
+
accountId: params.account.accountId,
|
|
395
|
+
sessionId: params.sessionId,
|
|
396
|
+
timeoutMs: params.timeoutMs,
|
|
397
|
+
clientReqId: resolveFrameClientReqId(params.frame)
|
|
398
|
+
});
|
|
399
|
+
try {
|
|
400
|
+
ws.send(JSON.stringify(params.frame));
|
|
401
|
+
} catch (err) {
|
|
402
|
+
clearRelaySocketState({
|
|
403
|
+
accountId: params.account.accountId,
|
|
404
|
+
expectedWs: ws,
|
|
405
|
+
closeSocket: true,
|
|
406
|
+
reason: `relay send failed: ${String(err)}`
|
|
407
|
+
});
|
|
408
|
+
throw err;
|
|
409
|
+
}
|
|
410
|
+
return await ackPromise;
|
|
411
|
+
}
|
|
412
|
+
async function connectRelaySocket(params) {
|
|
413
|
+
const wsUrl = resolveRelayWsUrl(params.account.gatewayUrl);
|
|
414
|
+
const authorizedWsUrl = appendTokenQuery(wsUrl, params.account.token);
|
|
415
|
+
logDebug("opening sider relay websocket", {
|
|
416
|
+
accountId: params.account.accountId,
|
|
417
|
+
wsUrl,
|
|
418
|
+
relayId: params.relayId,
|
|
419
|
+
hasTokenQuery: Boolean(params.account.token?.trim())
|
|
420
|
+
});
|
|
421
|
+
const ws = createSiderWebSocket(authorizedWsUrl);
|
|
422
|
+
await waitForSocketOpen({
|
|
423
|
+
ws,
|
|
424
|
+
timeoutMs: params.account.connectTimeoutMs,
|
|
425
|
+
accountId: params.account.accountId
|
|
426
|
+
});
|
|
427
|
+
const registerPayload = {
|
|
428
|
+
type: "register",
|
|
429
|
+
relay_id: params.relayId
|
|
430
|
+
};
|
|
431
|
+
if (params.account.token) {
|
|
432
|
+
registerPayload.token = params.account.token;
|
|
433
|
+
}
|
|
434
|
+
ws.send(JSON.stringify(registerPayload));
|
|
435
|
+
return ws;
|
|
436
|
+
}
|
|
437
|
+
async function sendMessageToSider(params) {
|
|
438
|
+
if (params.parts.length === 0) {
|
|
439
|
+
throw new Error("Cannot send sider message with empty parts");
|
|
440
|
+
}
|
|
441
|
+
logDebug("-> sendMessageToSider.parts:", params.parts);
|
|
442
|
+
const clientReqId = crypto.randomUUID();
|
|
443
|
+
let messageId = clientReqId;
|
|
444
|
+
let acked = false;
|
|
445
|
+
const frame = {
|
|
446
|
+
type: "message",
|
|
447
|
+
session_id: params.sessionId,
|
|
448
|
+
client_req_id: clientReqId,
|
|
449
|
+
...params.aggregation ? { aggregation: params.aggregation } : {},
|
|
450
|
+
parts: params.parts,
|
|
451
|
+
meta: params.meta ?? {}
|
|
452
|
+
};
|
|
453
|
+
try {
|
|
454
|
+
messageId = await sendRelayFrameWithAck({
|
|
455
|
+
account: params.account,
|
|
456
|
+
sessionId: params.sessionId,
|
|
457
|
+
timeoutMs: params.account.sendTimeoutMs,
|
|
458
|
+
frame
|
|
459
|
+
});
|
|
460
|
+
acked = true;
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if (!isAckTimeoutError(err)) {
|
|
463
|
+
throw err;
|
|
464
|
+
}
|
|
465
|
+
logWarn("sider outbound ack timeout; treating message as sent", {
|
|
466
|
+
accountId: params.account.accountId,
|
|
467
|
+
sessionId: params.sessionId,
|
|
468
|
+
clientReqId,
|
|
469
|
+
timeoutMs: params.account.sendTimeoutMs
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
logDebug("sider outbound message dispatched", {
|
|
473
|
+
accountId: params.account.accountId,
|
|
474
|
+
sessionId: params.sessionId,
|
|
475
|
+
clientReqId,
|
|
476
|
+
messageId,
|
|
477
|
+
acked,
|
|
478
|
+
aggregationId: params.aggregation?.id,
|
|
479
|
+
aggregationIndex: params.aggregation?.index,
|
|
480
|
+
aggregationFinal: params.aggregation?.final
|
|
481
|
+
});
|
|
482
|
+
return { messageId, conversationId: params.sessionId };
|
|
483
|
+
}
|
|
484
|
+
async function sendSiderEventBestEffort(params) {
|
|
485
|
+
try {
|
|
486
|
+
const { ws } = await waitForRelaySocketReady({
|
|
487
|
+
account: params.account,
|
|
488
|
+
timeoutMs: params.account.connectTimeoutMs
|
|
489
|
+
});
|
|
490
|
+
try {
|
|
491
|
+
ws.send(
|
|
492
|
+
JSON.stringify({
|
|
493
|
+
type: "event",
|
|
494
|
+
session_id: params.sessionId,
|
|
495
|
+
client_req_id: crypto.randomUUID(),
|
|
496
|
+
event_type: params.event.eventType,
|
|
497
|
+
payload: params.event.payload,
|
|
498
|
+
meta: params.event.meta ?? {}
|
|
499
|
+
})
|
|
500
|
+
);
|
|
501
|
+
} catch (err) {
|
|
502
|
+
clearRelaySocketState({
|
|
503
|
+
accountId: params.account.accountId,
|
|
504
|
+
expectedWs: ws,
|
|
505
|
+
closeSocket: true,
|
|
506
|
+
reason: `relay send failed: ${String(err)}`
|
|
507
|
+
});
|
|
508
|
+
throw err;
|
|
509
|
+
}
|
|
510
|
+
} catch (err) {
|
|
511
|
+
logWarn("sider outbound event failed", {
|
|
512
|
+
accountId: params.account.accountId,
|
|
513
|
+
sessionId: params.sessionId,
|
|
514
|
+
eventType: params.event.eventType,
|
|
515
|
+
context: params.context,
|
|
516
|
+
error: String(err)
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function sendSiderMessageBestEffort(params) {
|
|
521
|
+
try {
|
|
522
|
+
await sendMessageToSider({
|
|
523
|
+
account: params.account,
|
|
524
|
+
sessionId: params.sessionId,
|
|
525
|
+
parts: params.parts,
|
|
526
|
+
aggregation: params.aggregation,
|
|
527
|
+
meta: params.meta
|
|
528
|
+
});
|
|
529
|
+
} catch (err) {
|
|
530
|
+
logWarn("sider outbound message failed", {
|
|
531
|
+
accountId: params.account.accountId,
|
|
532
|
+
sessionId: params.sessionId,
|
|
533
|
+
partTypes: params.parts.map((part) => part.type),
|
|
534
|
+
aggregationId: params.aggregation?.id,
|
|
535
|
+
aggregationIndex: params.aggregation?.index,
|
|
536
|
+
aggregationFinal: params.aggregation?.final,
|
|
537
|
+
context: params.context,
|
|
538
|
+
error: String(err)
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
export {
|
|
543
|
+
buildRelayMonitorAccountSignature,
|
|
544
|
+
buildRelaySocketAccountSignature,
|
|
545
|
+
clearRelaySocketState,
|
|
546
|
+
closeSocketQuietly,
|
|
547
|
+
connectRelaySocket,
|
|
548
|
+
countPendingAcks,
|
|
549
|
+
describeSocketClose,
|
|
550
|
+
detachPendingAck,
|
|
551
|
+
getOrCreateRelaySocketState,
|
|
552
|
+
getSocketCloseInfo,
|
|
553
|
+
isAckTimeoutError,
|
|
554
|
+
normalizeRelaySocketToken,
|
|
555
|
+
parseInboundFrame,
|
|
556
|
+
readAckToken,
|
|
557
|
+
resolveAckClientReqId,
|
|
558
|
+
resolveAckId,
|
|
559
|
+
resolveAckSessionId,
|
|
560
|
+
resolveFrameClientReqId,
|
|
561
|
+
resolveManagedAck,
|
|
562
|
+
resolveRelayWsUrl,
|
|
563
|
+
sendMessageToSider,
|
|
564
|
+
sendRelayFrameWithAck,
|
|
565
|
+
sendSiderEventBestEffort,
|
|
566
|
+
sendSiderMessageBestEffort,
|
|
567
|
+
setRelaySocketConnected,
|
|
568
|
+
settlePendingAck,
|
|
569
|
+
waitForAbortSignal,
|
|
570
|
+
waitForManagedAck,
|
|
571
|
+
waitForRelaySocketReady,
|
|
572
|
+
waitForSocketOpen,
|
|
573
|
+
wsDataToText
|
|
574
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PluginRuntime } from 'openclaw/plugin-sdk';
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
3
|
+
import { SiderMonitorLogger, SiderRelayMonitorRegistration, SiderRelaySocketState, SiderRunState, SiderSessionBinding } from './channel-types.js';
|
|
4
|
+
import '../account-cW9SLuNu.js';
|
|
5
|
+
import 'openclaw/plugin-sdk/setup';
|
|
6
|
+
import './auth.js';
|
|
7
|
+
import 'openclaw/plugin-sdk/plugin-entry';
|
|
8
|
+
import 'openclaw/plugin-sdk/config-runtime';
|
|
9
|
+
|
|
10
|
+
declare let runtimeRef: PluginRuntime | null;
|
|
11
|
+
declare const siderSessionBindings: Map<string, SiderSessionBinding>;
|
|
12
|
+
declare const siderRunStates: Map<string, SiderRunState>;
|
|
13
|
+
declare const siderRelaySockets: Map<string, SiderRelaySocketState>;
|
|
14
|
+
declare const siderRelayMonitors: Map<string, SiderRelayMonitorRegistration>;
|
|
15
|
+
declare function createSiderWebSocket(url: string): WebSocket;
|
|
16
|
+
declare function setSiderRuntime(runtime: PluginRuntime): void;
|
|
17
|
+
declare function getSiderRuntime(): PluginRuntime;
|
|
18
|
+
type LogArg = unknown;
|
|
19
|
+
declare function logDebug(...args: LogArg[]): void;
|
|
20
|
+
declare function logInfo(...args: LogArg[]): void;
|
|
21
|
+
declare function logWarn(...args: LogArg[]): void;
|
|
22
|
+
declare function logError(...args: LogArg[]): void;
|
|
23
|
+
declare function createMonitorLogger(log?: SiderMonitorLogger): {
|
|
24
|
+
info: (...args: LogArg[]) => void;
|
|
25
|
+
warn: (...args: LogArg[]) => void;
|
|
26
|
+
};
|
|
27
|
+
declare function sleep(ms: number): Promise<void>;
|
|
28
|
+
declare const siderOutboundLogger: {
|
|
29
|
+
debug: typeof logDebug;
|
|
30
|
+
warn: typeof logWarn;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { createMonitorLogger, createSiderWebSocket, getSiderRuntime, logDebug, logError, logInfo, logWarn, runtimeRef, setSiderRuntime, siderOutboundLogger, siderRelayMonitors, siderRelaySockets, siderRunStates, siderSessionBindings, sleep };
|