solana-traderclaw 1.0.139 → 1.0.141

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,38 @@
1
1
  // src/http-client.ts
2
2
  import kayba, { SpanType } from "@kayba_ai/tracing";
3
+ var OrchestratorRateLimitError = class extends Error {
4
+ code = "RATE_LIMIT_EXCEEDED";
5
+ retryAfterMs;
6
+ status;
7
+ constructor(message, retryAfterMs, status) {
8
+ super(message);
9
+ this.name = "OrchestratorRateLimitError";
10
+ this.retryAfterMs = retryAfterMs;
11
+ this.status = status;
12
+ }
13
+ };
14
+ function parseRetryAfterMs(headerValue, maxMs) {
15
+ if (!headerValue) return null;
16
+ const trimmed = headerValue.trim();
17
+ if (trimmed === "") return null;
18
+ const asSeconds = Number(trimmed);
19
+ if (Number.isFinite(asSeconds) && asSeconds >= 0) {
20
+ return Math.min(Math.round(asSeconds * 1e3), maxMs);
21
+ }
22
+ const asDate = Date.parse(trimmed);
23
+ if (Number.isFinite(asDate)) {
24
+ return Math.max(0, Math.min(asDate - Date.now(), maxMs));
25
+ }
26
+ return null;
27
+ }
28
+ function sleep(ms) {
29
+ return new Promise((resolve) => {
30
+ const t = setTimeout(resolve, ms);
31
+ if (t && typeof t === "object" && "unref" in t) {
32
+ t.unref();
33
+ }
34
+ });
35
+ }
3
36
  async function orchestratorRequest(opts) {
4
37
  if (!kayba.isConfigured()) {
5
38
  return doRequest(opts);
@@ -21,7 +54,7 @@ async function orchestratorRequest(opts) {
21
54
  throw err;
22
55
  }
23
56
  }
24
- async function doRequest(opts, isRetry = false) {
57
+ async function doRequest(opts, isRetry = false, rateLimitAttempt = 0) {
25
58
  const url = `${opts.baseUrl.replace(/\/$/, "")}${opts.path}`;
26
59
  const controller = new AbortController();
27
60
  const timeoutId = setTimeout(
@@ -64,7 +97,25 @@ async function doRequest(opts, isRetry = false) {
64
97
  if ((res.status === 401 || res.status === 403) && !isRetry && opts.onUnauthorized) {
65
98
  clearTimeout(timeoutId);
66
99
  const newToken = await opts.onUnauthorized();
67
- return doRequest({ ...opts, accessToken: newToken }, true);
100
+ return doRequest({ ...opts, accessToken: newToken }, true, rateLimitAttempt);
101
+ }
102
+ const bodyCodeIsRateLimit = typeof dataObj?.code === "string" && /\bRATE_LIMIT\b/i.test(dataObj.code);
103
+ const isRateLimited = res.status === 429 || !res.ok && bodyCodeIsRateLimit;
104
+ if (isRateLimited) {
105
+ clearTimeout(timeoutId);
106
+ const maxWait = opts.rateLimitMaxRetryWaitMs ?? 3e4;
107
+ const maxRetries = opts.rateLimitMaxRetries ?? 1;
108
+ const retryAfter = parseRetryAfterMs(res.headers.get("Retry-After"), maxWait) ?? Math.min(maxWait, 1e3 * Math.pow(2, rateLimitAttempt));
109
+ const bodyMessage = typeof dataObj?.message === "string" ? dataObj.message : typeof dataObj?.error === "string" ? dataObj.error : `HTTP ${res.status}`;
110
+ if (rateLimitAttempt < maxRetries) {
111
+ await sleep(retryAfter);
112
+ return doRequest(opts, isRetry, rateLimitAttempt + 1);
113
+ }
114
+ throw new OrchestratorRateLimitError(
115
+ `RATE_LIMIT_EXCEEDED: ${bodyMessage}`,
116
+ retryAfter,
117
+ res.status
118
+ );
68
119
  }
69
120
  if (!res.ok) {
70
121
  const errBody = data && typeof data === "object" && !Array.isArray(data) ? data : null;
@@ -87,5 +138,6 @@ async function doRequest(opts, isRetry = false) {
87
138
  }
88
139
 
89
140
  export {
141
+ OrchestratorRateLimitError,
90
142
  orchestratorRequest
91
143
  };
@@ -1,5 +1,7 @@
1
1
  // src/session-manager.ts
2
2
  var TRADERCLAW_SESSION_TROUBLESHOOTING = "https://docs.traderclaw.ai/docs/installation#troubleshooting-session-expired-auth-errors-or-the-agent-logged-out";
3
+ var TRADERCLAW_MANAGED_INSTALL_CONSOLE_PANEL_URL = "https://install.traderclaw.ai/panel";
4
+ var TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT = `If you installed via install.traderclaw.ai's managed VPS console, re-authenticate in Panel \u2192 TraderClaw login (${TRADERCLAW_MANAGED_INSTALL_CONSOLE_PANEL_URL}), then openclaw gateway restart when convenient.`;
3
5
  var BS58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
4
6
  function b58Decode(str) {
5
7
  let num = BigInt(0);
@@ -357,7 +359,7 @@ var SessionManager = class _SessionManager {
357
359
  const walletPrivateKey = (await this.walletPrivateKeyProvider?.())?.trim();
358
360
  if (!walletPrivateKey) {
359
361
  throw new Error(
360
- `Wallet proof required but the gateway cannot sign interactively \u2014 no wallet key is wired into this process. This account already has a wallet. On the host that runs OpenClaw (with a normal terminal / TTY), run: traderclaw login \u2014 complete wallet proof when prompted \u2014 then openclaw gateway restart. That persists new session tokens without putting a private key in the gateway configuration. Do not paste private keys into openclaw.json. Troubleshooting: ${TRADERCLAW_SESSION_TROUBLESHOOTING}`
362
+ `Wallet proof required but the gateway cannot sign interactively \u2014 no wallet key is wired into this process. This account already has a wallet. On the host that runs OpenClaw (with a normal terminal / TTY), run: traderclaw login \u2014 complete wallet proof when prompted \u2014 then openclaw gateway restart. That persists new session tokens without putting a private key in the gateway configuration. Do not paste private keys into openclaw.json. Troubleshooting: ${TRADERCLAW_SESSION_TROUBLESHOOTING} ${TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT}`
361
363
  );
362
364
  }
363
365
  walletPubKey = challenge.walletPublicKey || this.walletPublicKey || void 0;
@@ -387,7 +389,7 @@ var SessionManager = class _SessionManager {
387
389
  await this.unifiedRefresh();
388
390
  if (!this.accessToken) {
389
391
  throw new Error(
390
- `Session expired and could not be refreshed. Re-authentication required. On the gateway host try: traderclaw login \u2014 then openclaw gateway restart. Troubleshooting: ${TRADERCLAW_SESSION_TROUBLESHOOTING}`
392
+ `Session expired and could not be refreshed. Re-authentication required. On the gateway host try: traderclaw login \u2014 then openclaw gateway restart. Troubleshooting: ${TRADERCLAW_SESSION_TROUBLESHOOTING} ${TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT}`
391
393
  );
392
394
  }
393
395
  return this.accessToken;
@@ -398,7 +400,7 @@ var SessionManager = class _SessionManager {
398
400
  await this.unifiedRefresh();
399
401
  if (!this.accessToken) {
400
402
  throw new Error(
401
- `Session expired and could not be refreshed. Re-authentication required. On the gateway host try: traderclaw login \u2014 then openclaw gateway restart. Troubleshooting: ${TRADERCLAW_SESSION_TROUBLESHOOTING}`
403
+ `Session expired and could not be refreshed. Re-authentication required. On the gateway host try: traderclaw login \u2014 then openclaw gateway restart. Troubleshooting: ${TRADERCLAW_SESSION_TROUBLESHOOTING} ${TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT}`
402
404
  );
403
405
  }
404
406
  return this.accessToken;
@@ -541,5 +543,7 @@ var SessionManager = class _SessionManager {
541
543
  };
542
544
 
543
545
  export {
546
+ TRADERCLAW_MANAGED_INSTALL_CONSOLE_PANEL_URL,
547
+ TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT,
544
548
  SessionManager
545
549
  };
package/dist/index.js CHANGED
@@ -4,8 +4,9 @@ import {
4
4
  writeRefreshTokenToOpenclawAtomic
5
5
  } from "./chunk-IAQC34O7.js";
6
6
  import {
7
- SessionManager
8
- } from "./chunk-NEMH5KB5.js";
7
+ SessionManager,
8
+ TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT
9
+ } from "./chunk-OKBHBUZ7.js";
9
10
  import {
10
11
  looksLikeTelegramChatId,
11
12
  resolveTelegramRecipientToChatId
@@ -29,7 +30,7 @@ import {
29
30
  } from "./chunk-R24UDHQG.js";
30
31
  import {
31
32
  orchestratorRequest
32
- } from "./chunk-6GSGHMUH.js";
33
+ } from "./chunk-E7QOPXSU.js";
33
34
  import {
34
35
  IntelligenceLab
35
36
  } from "./chunk-FBS5FGW2.js";
@@ -774,6 +775,26 @@ function registerWebFetchTool(api, Type2, logPrefix, options) {
774
775
  }
775
776
 
776
777
  // index.ts
778
+ var SOLANA_TRADER_LIFECYCLE_SINGLETON_KEY = Symbol.for(
779
+ "openclaw.solana-trader.lifecycle.v1"
780
+ );
781
+ var __solanaTraderGlobalSingletonHolder = globalThis;
782
+ function __solanaTraderDisposePreviousLifecycle(logger) {
783
+ const prev = __solanaTraderGlobalSingletonHolder[SOLANA_TRADER_LIFECYCLE_SINGLETON_KEY];
784
+ if (!prev) return;
785
+ __solanaTraderGlobalSingletonHolder[SOLANA_TRADER_LIFECYCLE_SINGLETON_KEY] = void 0;
786
+ try {
787
+ logger?.info("[solana-trader] Disposing previous plugin lifecycle before re-register");
788
+ prev.dispose();
789
+ } catch (err) {
790
+ logger?.warn(
791
+ `[solana-trader] Previous lifecycle dispose error: ${err instanceof Error ? err.message : String(err)}`
792
+ );
793
+ }
794
+ }
795
+ function __solanaTraderSetCurrentLifecycle(lc) {
796
+ __solanaTraderGlobalSingletonHolder[SOLANA_TRADER_LIFECYCLE_SINGLETON_KEY] = lc;
797
+ }
777
798
  var TRADERCLAW_WALLET_PRIVATE_KEY_ENV = "TRADERCLAW_WALLET_PRIVATE_KEY";
778
799
  function walletPrivateKeyFromPluginConfigRecord(obj) {
779
800
  const w = typeof obj.walletPrivateKey === "string" ? obj.walletPrivateKey.trim() : "";
@@ -922,6 +943,8 @@ var solanaTraderPlugin = {
922
943
  name: "Solana Trader",
923
944
  description: "Autonomous Solana memecoin trading agent \u2014 V1-Upgraded with intelligence lab, tool envelopes, prompt scrubbing, and split skill architecture",
924
945
  register(api) {
946
+ __solanaTraderDisposePreviousLifecycle(api.logger);
947
+ const __solanaTraderDisposers = [];
925
948
  const pluginConfigRaw = api.pluginConfig && typeof api.pluginConfig === "object" && !Array.isArray(api.pluginConfig) ? api.pluginConfig : {};
926
949
  const walletPrivateKeyFromPluginJsonOnly = walletPrivateKeyFromPluginConfigRecord(pluginConfigRaw);
927
950
  const config = parseConfig(api.pluginConfig);
@@ -932,7 +955,7 @@ var solanaTraderPlugin = {
932
955
  }
933
956
  if (!apiKey && !config.refreshToken) {
934
957
  api.logger.error(
935
- "[solana-trader] apiKey or refreshToken is required. Tell the user to run on their machine: traderclaw setup --signup (or traderclaw signup) for a new account, or traderclaw setup / traderclaw login if they already have an API key. The agent cannot sign up or edit credentials."
958
+ `[solana-trader] apiKey or refreshToken is required. Tell the user to run on their machine: traderclaw setup --signup (or traderclaw signup) for a new account, or traderclaw setup / traderclaw login if they already have an API key. The agent cannot sign up or edit credentials. ${TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT}`
936
959
  );
937
960
  return;
938
961
  }
@@ -1049,6 +1072,12 @@ var solanaTraderPlugin = {
1049
1072
  error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
1050
1073
  }
1051
1074
  });
1075
+ __solanaTraderDisposers.push(() => {
1076
+ try {
1077
+ sessionManager.destroy();
1078
+ } catch {
1079
+ }
1080
+ });
1052
1081
  const onUnauthorized = async () => {
1053
1082
  api.logger.warn("[solana-trader] Received 401 \u2014 refreshing session...");
1054
1083
  return sessionManager.handleUnauthorized();
@@ -2434,6 +2463,12 @@ ${notes}
2434
2463
  error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
2435
2464
  }
2436
2465
  });
2466
+ __solanaTraderDisposers.push(() => {
2467
+ try {
2468
+ bitqueryStreamManager.close();
2469
+ } catch {
2470
+ }
2471
+ });
2437
2472
  api.registerTool({
2438
2473
  name: "solana_bitquery_subscribe",
2439
2474
  description: "Subscribe to a managed real-time Bitquery data stream. The orchestrator manages the WebSocket connection and broadcasts events. Available templates: realtimeTokenPricesSolana, ohlc1s, dexPoolLiquidityChanges, pumpFunTokenCreation, pumpFunTrades, pumpSwapTrades, raydiumNewPools. Returns a subscriptionId for tracking. Pass agentId to enable event-to-agent forwarding \u2014 orchestrator delivers each event to your Gateway via /v1/responses in addition to normal WS delivery. Subscriptions expire after 24h and emit subscription_expiring/subscription_expired events. See websocket-streaming.md in the solana-trader skill for the full message contract and usage patterns.",
@@ -2567,6 +2602,12 @@ ${notes}
2567
2602
  error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
2568
2603
  }
2569
2604
  });
2605
+ __solanaTraderDisposers.push(() => {
2606
+ try {
2607
+ void alphaStreamManager.unsubscribe().catch(() => void 0);
2608
+ } catch {
2609
+ }
2610
+ });
2570
2611
  let startupGateRunning = null;
2571
2612
  let startupGateState = { ok: false, ts: 0, steps: [] };
2572
2613
  let lastForwardProbeState = null;
@@ -4296,6 +4337,12 @@ Context compaction triggered. STATE.md synced from last persisted state. Decisio
4296
4337
  }
4297
4338
  );
4298
4339
  let solanaTraderSessionWatchdogTimer = null;
4340
+ __solanaTraderDisposers.push(() => {
4341
+ if (solanaTraderSessionWatchdogTimer !== null) {
4342
+ clearInterval(solanaTraderSessionWatchdogTimer);
4343
+ solanaTraderSessionWatchdogTimer = null;
4344
+ }
4345
+ });
4299
4346
  api.registerService({
4300
4347
  id: "solana-trader-session",
4301
4348
  start: async () => {
@@ -4310,7 +4357,7 @@ Context compaction triggered. STATE.md synced from last persisted state. Decisio
4310
4357
  `[solana-trader] Session initialization failed: ${err instanceof Error ? err.message : String(err)}`
4311
4358
  );
4312
4359
  api.logger.error(
4313
- "[solana-trader] Trading tools will fail until session is established. User should run on this machine: traderclaw login (after logout) or traderclaw setup / traderclaw signup for a new account. Wallet proof uses local signing only \u2014 private key never leaves this system."
4360
+ `[solana-trader] Trading tools will fail until session is established. User should run on this machine: traderclaw login (after logout) or traderclaw setup / traderclaw signup for a new account. Wallet proof uses local signing only \u2014 private key never leaves this system. ${TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT}`
4314
4361
  );
4315
4362
  return;
4316
4363
  }
@@ -4546,6 +4593,16 @@ Context compaction triggered. STATE.md synced from last persisted state. Decisio
4546
4593
  api.logger.info(
4547
4594
  `[solana-trader] V1-Upgraded-Public: Registered ${totalToolCount} tools (${baseToolCount} base + ${intelligenceToolCount} intelligence + ${webFetchCount} web_fetch = ${totalRegistered} Solana + ${xToolCount} X/Twitter read-only) for walletId ${walletId} (session auth mode)`
4548
4595
  );
4596
+ __solanaTraderSetCurrentLifecycle({
4597
+ dispose: () => {
4598
+ for (let i = __solanaTraderDisposers.length - 1; i >= 0; i--) {
4599
+ try {
4600
+ __solanaTraderDisposers[i]();
4601
+ } catch {
4602
+ }
4603
+ }
4604
+ }
4605
+ });
4549
4606
  }
4550
4607
  };
4551
4608
  var index_default = solanaTraderPlugin;
@@ -1,6 +1,8 @@
1
1
  import {
2
+ OrchestratorRateLimitError,
2
3
  orchestratorRequest
3
- } from "../chunk-6GSGHMUH.js";
4
+ } from "../chunk-E7QOPXSU.js";
4
5
  export {
6
+ OrchestratorRateLimitError,
5
7
  orchestratorRequest
6
8
  };
@@ -1,6 +1,10 @@
1
1
  import {
2
- SessionManager
3
- } from "../chunk-NEMH5KB5.js";
2
+ SessionManager,
3
+ TRADERCLAW_MANAGED_INSTALL_CONSOLE_PANEL_URL,
4
+ TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT
5
+ } from "../chunk-OKBHBUZ7.js";
4
6
  export {
5
- SessionManager
7
+ SessionManager,
8
+ TRADERCLAW_MANAGED_INSTALL_CONSOLE_PANEL_URL,
9
+ TRADERCLAW_MANAGED_INSTALL_LOGIN_HINT
6
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-traderclaw",
3
- "version": "1.0.139",
3
+ "version": "1.0.141",
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",