clawaxis 1.0.3 → 1.2.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/index.ts +8 -15
- package/openclaw.plugin.json +10 -10
- package/package.json +3 -4
- package/src/channel.ts +14 -30
- package/src/config.ts +32 -23
- package/src/governance/hooks.ts +2 -2
- package/src/message-handler.ts +26 -56
- package/src/runtime.ts +9 -38
- package/src/socket.ts +271 -0
- package/src/sync/documents.ts +35 -24
- package/src/sync/routines.ts +5 -9
- package/src/tools/content.ts +14 -19
- package/src/tools/helpers.ts +5 -17
- package/src/tools/index.ts +2 -5
- package/src/tools/memory.ts +15 -45
- package/src/tools/routines.ts +5 -13
- package/src/tools/sync.ts +5 -11
- package/src/types.ts +2 -2
- package/src/utils.ts +24 -97
- package/src/tools/media.ts +0 -21
- package/src/websocket.ts +0 -294
package/src/types.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -1,71 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { LOG_PREFIX, ANON_KEY } from "./config.js";
|
|
1
|
+
import { LOG_PREFIX } from "./config.js";
|
|
3
2
|
import {
|
|
4
3
|
getRuntime,
|
|
5
|
-
getWs,
|
|
6
4
|
getIsAuthenticated,
|
|
7
5
|
getLogQueue,
|
|
8
6
|
getFlushTimeout,
|
|
9
7
|
setFlushTimeout,
|
|
10
8
|
} from "./runtime.js";
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
let _config: ClawAxisConfig | null = null;
|
|
14
|
-
|
|
15
|
-
export function setUtilsConfig(cfg: ClawAxisConfig) {
|
|
16
|
-
_config = cfg;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function getRelayUrl(): string {
|
|
20
|
-
return _config?.relayUrl ?? "";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getAgentToken(): string {
|
|
24
|
-
return _config?.agentToken ?? "";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getGovernance() {
|
|
28
|
-
return _config?.governance;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function relayFetch(method: string, path: string, body?: any) {
|
|
32
|
-
const base = getRelayUrl();
|
|
33
|
-
const url = path ? `${base}/${path}` : base;
|
|
34
|
-
const opts: RequestInit = {
|
|
35
|
-
method,
|
|
36
|
-
headers: {
|
|
37
|
-
"Authorization": `Bearer ${ANON_KEY}`,
|
|
38
|
-
"apikey": ANON_KEY,
|
|
39
|
-
"x-agent-token": getAgentToken(),
|
|
40
|
-
"Content-Type": "application/json",
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
if (body) opts.body = JSON.stringify(body);
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const res = await fetch(url, opts);
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
const text = await res.text().catch(() => "");
|
|
49
|
-
|
|
50
|
-
if (res.status === 401) {
|
|
51
|
-
console.error(`${LOG_PREFIX} Relay ${method} ${path || ""}: 401 Unauthorized — check agentToken`);
|
|
52
|
-
} else if (res.status === 429) {
|
|
53
|
-
const retryAfter = res.headers.get("retry-after");
|
|
54
|
-
console.warn(`${LOG_PREFIX} Relay ${method} ${path || ""}: 429 Rate limited${retryAfter ? ` (retry-after: ${retryAfter}s)` : ""}`);
|
|
55
|
-
} else if (res.status >= 500) {
|
|
56
|
-
console.error(`${LOG_PREFIX} Relay ${method} ${path || ""}: ${res.status} Server error — ${text.substring(0, 200)}`);
|
|
57
|
-
} else {
|
|
58
|
-
console.error(`${LOG_PREFIX} Relay ${method} ${path || ""} failed: ${res.status}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
return await res.json();
|
|
64
|
-
} catch (err: any) {
|
|
65
|
-
console.error(`${LOG_PREFIX} Relay ${method} ${path || ""} error:`, err.message);
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
9
|
+
import { relaySend } from "./socket.js";
|
|
69
10
|
|
|
70
11
|
export async function relayLog(
|
|
71
12
|
action: string,
|
|
@@ -73,56 +14,42 @@ export async function relayLog(
|
|
|
73
14
|
level: "info" | "success" | "warning" | "error" = "info",
|
|
74
15
|
metadata?: Record<string, any>
|
|
75
16
|
): Promise<void> {
|
|
76
|
-
const governance = getGovernance();
|
|
77
|
-
if (!governance?.logging.enabled) return;
|
|
78
|
-
|
|
79
17
|
const logQueue = getLogQueue();
|
|
80
18
|
logQueue.push({ action, detail, level, metadata: metadata || {} });
|
|
81
19
|
|
|
82
20
|
const existing = getFlushTimeout();
|
|
83
21
|
if (existing) clearTimeout(existing);
|
|
84
22
|
|
|
85
|
-
setFlushTimeout(setTimeout(
|
|
86
|
-
|
|
87
|
-
},
|
|
23
|
+
setFlushTimeout(setTimeout(() => {
|
|
24
|
+
flushLogQueue();
|
|
25
|
+
}, 100));
|
|
88
26
|
}
|
|
89
27
|
|
|
90
|
-
export
|
|
28
|
+
export function flushLogQueue(): void {
|
|
91
29
|
const logQueue = getLogQueue();
|
|
92
30
|
if (logQueue.length === 0) return;
|
|
93
31
|
|
|
94
32
|
const batch = [...logQueue];
|
|
95
33
|
logQueue.length = 0;
|
|
96
34
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
(
|
|
114
|
-
|
|
115
|
-
entries: entries.slice(0, 10)
|
|
116
|
-
}));
|
|
117
|
-
} else {
|
|
118
|
-
for (const log of batch) {
|
|
119
|
-
try {
|
|
120
|
-
await relayFetch("POST", "log", log);
|
|
121
|
-
} catch (err) {
|
|
122
|
-
console.error(`${LOG_PREFIX} Failed to log:`, log.action, err);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
35
|
+
const entries = batch.map(log => {
|
|
36
|
+
const { tokens_used, cost, duration_ms, ...extraMetadata } = log.metadata || {};
|
|
37
|
+
return {
|
|
38
|
+
action: log.action,
|
|
39
|
+
detail: log.detail,
|
|
40
|
+
level: log.level || "info",
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
tokens_used: tokens_used || 0,
|
|
43
|
+
cost: cost || 0,
|
|
44
|
+
duration_ms: duration_ms || null,
|
|
45
|
+
metadata: Object.keys(extraMetadata).length > 0 ? extraMetadata : undefined,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
relaySend({
|
|
50
|
+
type: "log",
|
|
51
|
+
entries: entries.slice(0, 10),
|
|
52
|
+
});
|
|
126
53
|
}
|
|
127
54
|
|
|
128
55
|
export function getModelName(): string {
|
package/src/tools/media.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { relayFetch } from "../utils.js";
|
|
3
|
-
import { toolOk, toolFail } from "./helpers.js";
|
|
4
|
-
|
|
5
|
-
export function registerMediaTools(api: any) {
|
|
6
|
-
api.registerTool({
|
|
7
|
-
name: "clawaxis_upload_media",
|
|
8
|
-
description: "Upload an image to permanent R2 storage from a temporary URL. Returns a permanent public URL for use in content.",
|
|
9
|
-
parameters: Type.Object({
|
|
10
|
-
image_url: Type.String({ description: "The temporary image URL to upload to permanent storage" }),
|
|
11
|
-
filename: Type.Optional(Type.String({ description: "Custom filename without extension. Defaults to timestamp." })),
|
|
12
|
-
folder: Type.Optional(Type.String({ description: "Subfolder in bucket. Defaults to agent ID." })),
|
|
13
|
-
}),
|
|
14
|
-
async execute(_id: string, params: any) {
|
|
15
|
-
const result = await relayFetch("POST", "media-upload", params);
|
|
16
|
-
return result?.ok
|
|
17
|
-
? toolOk(`Image uploaded successfully: ${result.url}`, result)
|
|
18
|
-
: toolFail("Failed to upload image", { result });
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
}
|
package/src/websocket.ts
DELETED
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
-
import WS from "ws";
|
|
3
|
-
import {
|
|
4
|
-
LOG_PREFIX,
|
|
5
|
-
PLUGIN_VERSION,
|
|
6
|
-
HEARTBEAT_MS,
|
|
7
|
-
PING_MS,
|
|
8
|
-
REAUTH_BUFFER_MS,
|
|
9
|
-
MAX_RECONNECT_DELAY,
|
|
10
|
-
} from "./config.js";
|
|
11
|
-
import {
|
|
12
|
-
getRuntime,
|
|
13
|
-
getWs,
|
|
14
|
-
setWs,
|
|
15
|
-
getIsAuthenticated,
|
|
16
|
-
setIsAuthenticated,
|
|
17
|
-
getAuthenticatedAgentId,
|
|
18
|
-
setAuthenticatedAgentId,
|
|
19
|
-
getReconnectAttempts,
|
|
20
|
-
setReconnectAttempts,
|
|
21
|
-
incrementReconnectAttempts,
|
|
22
|
-
getPingInterval,
|
|
23
|
-
setPingInterval,
|
|
24
|
-
getHeartbeatInterval,
|
|
25
|
-
setHeartbeatInterval,
|
|
26
|
-
isRunning,
|
|
27
|
-
setRunning,
|
|
28
|
-
getSessionTokensAccumulated,
|
|
29
|
-
getSessionCostAccumulated,
|
|
30
|
-
getLastHeartbeatTokens,
|
|
31
|
-
setLastHeartbeatTokens,
|
|
32
|
-
getFlushTimeout,
|
|
33
|
-
setFlushTimeout,
|
|
34
|
-
} from "./runtime.js";
|
|
35
|
-
import { relayLog, flushLogQueue, getModelName } from "./utils.js";
|
|
36
|
-
import { handleMessage, handleCommand } from "./message-handler.js";
|
|
37
|
-
import { processPendingDocs, processDocDeletion } from "./sync/documents.js";
|
|
38
|
-
import { processPendingRoutines } from "./sync/routines.js";
|
|
39
|
-
import type { ClawAxisConfig } from "./types.js";
|
|
40
|
-
|
|
41
|
-
let _config: ClawAxisConfig | null = null;
|
|
42
|
-
|
|
43
|
-
function getAgentToken(): string {
|
|
44
|
-
return _config?.agentToken ?? "";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getRelayUrl(): string {
|
|
48
|
-
return _config?.relayUrl ?? "";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function sendAuth() {
|
|
52
|
-
const ws = getWs();
|
|
53
|
-
if (!ws || ws.readyState !== WS.OPEN) return;
|
|
54
|
-
|
|
55
|
-
const authMsg = {
|
|
56
|
-
type: "auth",
|
|
57
|
-
token: getAgentToken(),
|
|
58
|
-
plugin_version: PLUGIN_VERSION,
|
|
59
|
-
openclaw_version: getRuntime()?.version || "unknown",
|
|
60
|
-
host_os: process.platform
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
console.log(`${LOG_PREFIX} Sending auth...`);
|
|
64
|
-
ws.send(JSON.stringify(authMsg));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function cleanup() {
|
|
68
|
-
setIsAuthenticated(false);
|
|
69
|
-
setAuthenticatedAgentId(null);
|
|
70
|
-
|
|
71
|
-
const ping = getPingInterval();
|
|
72
|
-
if (ping) {
|
|
73
|
-
clearInterval(ping);
|
|
74
|
-
setPingInterval(null);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function startKeepalive() {
|
|
79
|
-
const existing = getPingInterval();
|
|
80
|
-
if (existing) clearInterval(existing);
|
|
81
|
-
|
|
82
|
-
setPingInterval(setInterval(() => {
|
|
83
|
-
const ws = getWs();
|
|
84
|
-
if (ws?.readyState === WS.OPEN && getIsAuthenticated()) {
|
|
85
|
-
ws.send(JSON.stringify({ type: "ping" }));
|
|
86
|
-
}
|
|
87
|
-
}, PING_MS));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function startHeartbeat() {
|
|
91
|
-
const existing = getHeartbeatInterval();
|
|
92
|
-
if (existing) clearInterval(existing);
|
|
93
|
-
|
|
94
|
-
setHeartbeatInterval(setInterval(async () => {
|
|
95
|
-
const ws = getWs();
|
|
96
|
-
if (ws?.readyState === WS.OPEN && getIsAuthenticated()) {
|
|
97
|
-
const uptime = process.uptime();
|
|
98
|
-
const sessionTokens = getSessionTokensAccumulated();
|
|
99
|
-
const tokensDelta = sessionTokens - getLastHeartbeatTokens();
|
|
100
|
-
|
|
101
|
-
ws.send(JSON.stringify({
|
|
102
|
-
type: "heartbeat",
|
|
103
|
-
status: "online",
|
|
104
|
-
model: getModelName(),
|
|
105
|
-
openclaw_version: getRuntime()?.version || "unknown",
|
|
106
|
-
uptime_seconds: Math.floor(uptime),
|
|
107
|
-
tokens_used_session: sessionTokens,
|
|
108
|
-
cost_session: getSessionCostAccumulated(),
|
|
109
|
-
tokens_delta: tokensDelta > 0 ? tokensDelta : 0,
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
setLastHeartbeatTokens(sessionTokens);
|
|
113
|
-
}
|
|
114
|
-
}, HEARTBEAT_MS));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function scheduleReconnect() {
|
|
118
|
-
if (!isRunning()) return;
|
|
119
|
-
|
|
120
|
-
const base = Math.min(1000 * Math.pow(2, getReconnectAttempts()), MAX_RECONNECT_DELAY);
|
|
121
|
-
const jitter = base * 0.3 * (Math.random() * 2 - 1);
|
|
122
|
-
const delay = Math.max(0, Math.round(base + jitter));
|
|
123
|
-
incrementReconnectAttempts();
|
|
124
|
-
|
|
125
|
-
console.log(`${LOG_PREFIX} Reconnecting in ${delay}ms (attempt ${getReconnectAttempts()})...`);
|
|
126
|
-
|
|
127
|
-
setTimeout(() => {
|
|
128
|
-
if (isRunning()) connectWebSocket();
|
|
129
|
-
}, delay);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ─── WebSocket message router ────────────────────────────
|
|
133
|
-
|
|
134
|
-
async function handleWebSocketMessage(msg: any) {
|
|
135
|
-
console.log(`${LOG_PREFIX} [WS] Received: ${msg.type}`);
|
|
136
|
-
|
|
137
|
-
switch (msg.type) {
|
|
138
|
-
case "auth_ok":
|
|
139
|
-
await handleAuthOk(msg);
|
|
140
|
-
break;
|
|
141
|
-
case "auth_error":
|
|
142
|
-
await handleAuthError(msg);
|
|
143
|
-
break;
|
|
144
|
-
case "message":
|
|
145
|
-
await handleMessage(msg);
|
|
146
|
-
break;
|
|
147
|
-
case "command":
|
|
148
|
-
await handleCommand(msg);
|
|
149
|
-
break;
|
|
150
|
-
case "sync":
|
|
151
|
-
case "heartbeat_ack":
|
|
152
|
-
await handleSync(msg);
|
|
153
|
-
break;
|
|
154
|
-
case "content_ack":
|
|
155
|
-
console.log(`${LOG_PREFIX} Content ${msg.action}: ${msg.content_id}`);
|
|
156
|
-
break;
|
|
157
|
-
case "doc_deleted":
|
|
158
|
-
await processDocDeletion(msg);
|
|
159
|
-
break;
|
|
160
|
-
case "routine_ack":
|
|
161
|
-
console.log(`${LOG_PREFIX} Routine ${msg.action}: ${msg.routine_id}`);
|
|
162
|
-
break;
|
|
163
|
-
case "error":
|
|
164
|
-
console.warn(`${LOG_PREFIX} Server error: ${msg.reason} \u2014 ${msg.detail || ""}`);
|
|
165
|
-
break;
|
|
166
|
-
case "reauth":
|
|
167
|
-
await handleReauth(msg);
|
|
168
|
-
break;
|
|
169
|
-
case "pong":
|
|
170
|
-
break;
|
|
171
|
-
default:
|
|
172
|
-
console.warn(`${LOG_PREFIX} Unknown message type: ${msg.type}`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async function handleAuthOk(msg: any) {
|
|
177
|
-
console.log(`${LOG_PREFIX} \u2713 Authenticated`);
|
|
178
|
-
setIsAuthenticated(true);
|
|
179
|
-
setAuthenticatedAgentId(msg.agent_id || null);
|
|
180
|
-
if (msg.agent_id) console.log(`${LOG_PREFIX} Agent ID: ${msg.agent_id}`);
|
|
181
|
-
startKeepalive();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async function handleAuthError(msg: any) {
|
|
185
|
-
console.error(`${LOG_PREFIX} \u274C Auth failed: ${msg.reason}`);
|
|
186
|
-
setIsAuthenticated(false);
|
|
187
|
-
setRunning(false);
|
|
188
|
-
getWs()?.close();
|
|
189
|
-
await relayLog("error", `Authentication failed: ${msg.reason}`, "error", { reason: msg.reason });
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
async function handleSync(msg: any) {
|
|
193
|
-
console.log(`${LOG_PREFIX} Sync received: ${msg.pending_docs?.length || 0} docs, ${msg.pending_routines?.length || 0} routines`);
|
|
194
|
-
if (msg.pending_docs?.length > 0) await processPendingDocs(msg.pending_docs);
|
|
195
|
-
if (msg.pending_routines?.length > 0) await processPendingRoutines(msg.pending_routines);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async function handleReauth(msg: any) {
|
|
199
|
-
console.log(`${LOG_PREFIX} Re-auth requested (deadline: ${msg.deadline_ms}ms)`);
|
|
200
|
-
setTimeout(() => {
|
|
201
|
-
const ws = getWs();
|
|
202
|
-
if (ws?.readyState === WS.OPEN) sendAuth();
|
|
203
|
-
}, Math.max(100, msg.deadline_ms - REAUTH_BUFFER_MS));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ─── Connection ──────────────────────────────────────────
|
|
207
|
-
|
|
208
|
-
function connectWebSocket() {
|
|
209
|
-
const wsUrl = getRelayUrl().replace(/^https?:/, "wss:").replace(/\/agent-relay$/, "/ws-relay");
|
|
210
|
-
|
|
211
|
-
console.log(`${LOG_PREFIX} [WS] Connecting to: ${wsUrl}`);
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
const socket = new WS(wsUrl);
|
|
215
|
-
setWs(socket as any);
|
|
216
|
-
|
|
217
|
-
socket.on("open", () => {
|
|
218
|
-
console.log(`${LOG_PREFIX} [WS] \u2713 Connection opened`);
|
|
219
|
-
setReconnectAttempts(0);
|
|
220
|
-
sendAuth();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
socket.on("message", async (data: WS.RawData) => {
|
|
224
|
-
try {
|
|
225
|
-
const msg = JSON.parse(data.toString());
|
|
226
|
-
await handleWebSocketMessage(msg);
|
|
227
|
-
} catch (err) {
|
|
228
|
-
console.error(`${LOG_PREFIX} [WS] Failed to parse message:`, err);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
socket.on("close", (code: number, reason: Buffer) => {
|
|
233
|
-
console.log(`${LOG_PREFIX} [WS] Closed (code ${code}, reason: ${reason.toString() || "none"}), reconnecting...`);
|
|
234
|
-
cleanup();
|
|
235
|
-
scheduleReconnect();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
socket.on("error", (err: Error) => {
|
|
239
|
-
console.error(`${LOG_PREFIX} [WS] Socket error:`, err.message);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
} catch (err: any) {
|
|
243
|
-
console.error(`${LOG_PREFIX} [WS] Failed to create WebSocket:`, err.message);
|
|
244
|
-
scheduleReconnect();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// ─── Service factory ─────────────────────────────────────
|
|
249
|
-
|
|
250
|
-
export function createWebSocketService(cfg: ClawAxisConfig) {
|
|
251
|
-
_config = cfg;
|
|
252
|
-
let started = false;
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
id: "clawaxis-ws",
|
|
256
|
-
|
|
257
|
-
async start() {
|
|
258
|
-
if (started) return;
|
|
259
|
-
started = true;
|
|
260
|
-
console.log(`${LOG_PREFIX} \uD83C\uDF10 Starting WebSocket connection...`);
|
|
261
|
-
setRunning(true);
|
|
262
|
-
connectWebSocket();
|
|
263
|
-
startHeartbeat();
|
|
264
|
-
},
|
|
265
|
-
|
|
266
|
-
async stop() {
|
|
267
|
-
console.log(`${LOG_PREFIX} Shutting down WebSocket...`);
|
|
268
|
-
started = false;
|
|
269
|
-
setRunning(false);
|
|
270
|
-
|
|
271
|
-
const ws = getWs();
|
|
272
|
-
if (ws?.readyState === WS.OPEN && getIsAuthenticated()) {
|
|
273
|
-
(ws as any).send(JSON.stringify({ type: "heartbeat", status: "offline" }));
|
|
274
|
-
await sleep(100);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const ft = getFlushTimeout();
|
|
278
|
-
if (ft) {
|
|
279
|
-
clearTimeout(ft);
|
|
280
|
-
setFlushTimeout(null);
|
|
281
|
-
await flushLogQueue();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const hb = getHeartbeatInterval();
|
|
285
|
-
if (hb) {
|
|
286
|
-
clearInterval(hb);
|
|
287
|
-
setHeartbeatInterval(null);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
cleanup();
|
|
291
|
-
(ws as any)?.close();
|
|
292
|
-
},
|
|
293
|
-
};
|
|
294
|
-
}
|