solana-traderclaw 1.0.97 → 1.0.99

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.
@@ -1,5 +1,7 @@
1
1
  // src/alpha-ws.ts
2
2
  var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
3
+ var PING_INTERVAL_MS = 3e4;
4
+ var PONG_TIMEOUT_MS = 1e4;
3
5
  var AlphaStreamManager = class {
4
6
  config;
5
7
  ws = null;
@@ -110,13 +112,42 @@ var AlphaStreamManager = class {
110
112
  reject(new Error("WebSocket connection timed out"));
111
113
  }
112
114
  }, 1e4);
115
+ let pingInterval = null;
116
+ let pongTimer = null;
117
+ const clearKeepalive = () => {
118
+ if (pingInterval) {
119
+ clearInterval(pingInterval);
120
+ pingInterval = null;
121
+ }
122
+ if (pongTimer) {
123
+ clearTimeout(pongTimer);
124
+ pongTimer = null;
125
+ }
126
+ };
113
127
  this.ws.on("open", () => {
114
128
  clearTimeout(connectTimeout);
115
129
  this.connectedAt = Date.now();
116
130
  this.reconnectAttempt = 0;
117
131
  this.log("info", "WebSocket connected, waiting for server handshake...");
132
+ pingInterval = setInterval(() => {
133
+ if (!this.ws || this.ws.readyState !== 1) return;
134
+ pongTimer = setTimeout(() => {
135
+ this.log("warn", "Pong timeout \u2014 forcing reconnect");
136
+ this.ws?.terminate();
137
+ }, PONG_TIMEOUT_MS);
138
+ try {
139
+ this.ws.ping();
140
+ } catch {
141
+ }
142
+ }, PING_INTERVAL_MS);
118
143
  resolve();
119
144
  });
145
+ this.ws.on("pong", () => {
146
+ if (pongTimer) {
147
+ clearTimeout(pongTimer);
148
+ pongTimer = null;
149
+ }
150
+ });
120
151
  this.ws.on("message", (data) => {
121
152
  try {
122
153
  const msg = JSON.parse(data.toString());
@@ -127,6 +158,7 @@ var AlphaStreamManager = class {
127
158
  });
128
159
  this.ws.on("close", () => {
129
160
  clearTimeout(connectTimeout);
161
+ clearKeepalive();
130
162
  this.subscribed = false;
131
163
  this.authenticated = false;
132
164
  this.log("info", "WebSocket closed");
@@ -1,5 +1,7 @@
1
1
  // src/bitquery-ws.ts
2
2
  var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
3
+ var PING_INTERVAL_MS = 3e4;
4
+ var PONG_TIMEOUT_MS = 1e4;
3
5
  var BitqueryStreamManager = class {
4
6
  config;
5
7
  ws = null;
@@ -98,6 +100,27 @@ var BitqueryStreamManager = class {
98
100
  }
99
101
  this.authenticated = false;
100
102
  }
103
+ /**
104
+ * Close the socket and schedule a reconnect without marking the close as
105
+ * intentional. Used for auth errors where we want to reconnect with a fresh
106
+ * token rather than leaving the socket permanently dead.
107
+ */
108
+ forceReconnect(reason) {
109
+ this.log("warn", `Force reconnect: ${reason}`);
110
+ if (this.reconnectTimer) {
111
+ clearTimeout(this.reconnectTimer);
112
+ this.reconnectTimer = null;
113
+ }
114
+ try {
115
+ this.ws?.close();
116
+ } catch {
117
+ }
118
+ this.ws = null;
119
+ this.authenticated = false;
120
+ if (this.activeSubscriptions.size > 0) {
121
+ this.scheduleReconnect();
122
+ }
123
+ }
101
124
  async ensureConnected() {
102
125
  if (this.ws && this.ws.readyState === 1 && this.authenticated) return;
103
126
  if (this.connecting) {
@@ -152,12 +175,41 @@ var BitqueryStreamManager = class {
152
175
  reject(new Error("WS connection timed out"));
153
176
  }
154
177
  }, 1e4);
178
+ let pingInterval = null;
179
+ let pongTimer = null;
180
+ const clearKeepalive = () => {
181
+ if (pingInterval) {
182
+ clearInterval(pingInterval);
183
+ pingInterval = null;
184
+ }
185
+ if (pongTimer) {
186
+ clearTimeout(pongTimer);
187
+ pongTimer = null;
188
+ }
189
+ };
155
190
  ws.on("open", () => {
156
191
  clearTimeout(connectTimeout);
157
192
  this.reconnectAttempt = 0;
158
193
  this.log("info", "Connected");
194
+ pingInterval = setInterval(() => {
195
+ if (!this.ws || this.ws.readyState !== 1) return;
196
+ pongTimer = setTimeout(() => {
197
+ this.log("warn", "Pong timeout \u2014 forcing reconnect");
198
+ this.ws?.terminate();
199
+ }, PONG_TIMEOUT_MS);
200
+ try {
201
+ this.ws.ping();
202
+ } catch {
203
+ }
204
+ }, PING_INTERVAL_MS);
159
205
  resolve();
160
206
  });
207
+ ws.on("pong", () => {
208
+ if (pongTimer) {
209
+ clearTimeout(pongTimer);
210
+ pongTimer = null;
211
+ }
212
+ });
161
213
  ws.on("message", (data) => {
162
214
  try {
163
215
  const msg = JSON.parse(data.toString());
@@ -168,6 +220,7 @@ var BitqueryStreamManager = class {
168
220
  });
169
221
  ws.on("close", () => {
170
222
  clearTimeout(connectTimeout);
223
+ clearKeepalive();
171
224
  this.authenticated = false;
172
225
  this.log("info", "WS closed");
173
226
  this.drainPendingOnClose();
@@ -242,7 +295,7 @@ var BitqueryStreamManager = class {
242
295
  }
243
296
  }
244
297
  if (["WS_AUTH_REQUIRED", "WS_AUTH_INVALID", "ACCESS_TOKEN_EXPIRED"].includes(code)) {
245
- this.close();
298
+ this.forceReconnect(code);
246
299
  }
247
300
  break;
248
301
  }
@@ -263,9 +316,9 @@ var BitqueryStreamManager = class {
263
316
  async resubscribeAll() {
264
317
  if (this.activeSubscriptions.size === 0) return;
265
318
  const subs = [...this.activeSubscriptions.values()];
266
- this.activeSubscriptions.clear();
267
319
  this.log("info", `Re-subscribing ${subs.length} subscription(s) after reconnect`);
268
320
  for (const sub of subs) {
321
+ this.activeSubscriptions.delete(sub.subscriptionId);
269
322
  try {
270
323
  const result = await this.subscribe({
271
324
  templateKey: sub.templateKey,
@@ -276,6 +329,7 @@ var BitqueryStreamManager = class {
276
329
  this.log("info", `Re-subscribed ${sub.templateKey} \u2192 new id: ${result.subscriptionId}`);
277
330
  } catch (err) {
278
331
  this.log("error", `Re-subscribe failed for ${sub.templateKey}: ${err}`);
332
+ this.activeSubscriptions.set(sub.subscriptionId, sub);
279
333
  }
280
334
  }
281
335
  }
package/dist/index.js CHANGED
@@ -15,10 +15,10 @@ import {
15
15
  } from "./chunk-3UQIQJPQ.js";
16
16
  import {
17
17
  AlphaStreamManager
18
- } from "./chunk-3YPZOXWE.js";
18
+ } from "./chunk-ATWB7K63.js";
19
19
  import {
20
20
  BitqueryStreamManager
21
- } from "./chunk-VR5WP5S4.js";
21
+ } from "./chunk-S2DLZKMQ.js";
22
22
  import {
23
23
  orchestratorRequest
24
24
  } from "./chunk-6GSGHMUH.js";
@@ -4284,6 +4284,30 @@ Context compaction triggered. STATE.md synced from last persisted state. Decisio
4284
4284
  } catch (err) {
4285
4285
  api.logger.warn(`[solana-trader] Forward probe failed: ${err instanceof Error ? err.message : String(err)}`);
4286
4286
  }
4287
+ const WATCHDOG_INTERVAL_MS = 9e4;
4288
+ const watchdogTimer = setInterval(async () => {
4289
+ if (!alphaStreamManager.isSubscribed()) {
4290
+ api.logger.warn("[watchdog] Alpha stream not subscribed \u2014 resubscribing...");
4291
+ try {
4292
+ await alphaStreamManager.subscribe();
4293
+ api.logger.info("[watchdog] Alpha stream resubscribed successfully.");
4294
+ } catch (err) {
4295
+ api.logger.error(`[watchdog] Alpha resubscribe failed: ${err instanceof Error ? err.message : String(err)}`);
4296
+ }
4297
+ }
4298
+ try {
4299
+ const creds = await get("/api/agents/gateway-credentials");
4300
+ if (!getActiveCredential(creds)) {
4301
+ api.logger.warn("[watchdog] Gateway credentials inactive \u2014 re-registering...");
4302
+ await runStartupGate({ autoFixGateway: true, force: true });
4303
+ }
4304
+ } catch (err) {
4305
+ api.logger.warn(`[watchdog] Gateway credential check failed: ${err instanceof Error ? err.message : String(err)}`);
4306
+ }
4307
+ }, WATCHDOG_INTERVAL_MS);
4308
+ if (watchdogTimer && typeof watchdogTimer === "object" && "unref" in watchdogTimer) {
4309
+ watchdogTimer.unref();
4310
+ }
4287
4311
  }
4288
4312
  });
4289
4313
  if (typeof api.registerContextEngine === "function") {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AlphaStreamManager
3
- } from "../chunk-3YPZOXWE.js";
3
+ } from "../chunk-ATWB7K63.js";
4
4
  export {
5
5
  AlphaStreamManager
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BitqueryStreamManager
3
- } from "../chunk-VR5WP5S4.js";
3
+ } from "../chunk-S2DLZKMQ.js";
4
4
  export {
5
5
  BitqueryStreamManager
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-traderclaw",
3
- "version": "1.0.97",
3
+ "version": "1.0.99",
4
4
  "description": "TraderClaw V1-Upgraded — Solana trading for OpenClaw with intelligence lab, tool envelopes, prompt scrubbing, read-only X social intel, and split skill docs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",