solana-traderclaw 1.0.140 → 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
|
};
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "./chunk-R24UDHQG.js";
|
|
31
31
|
import {
|
|
32
32
|
orchestratorRequest
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-E7QOPXSU.js";
|
|
34
34
|
import {
|
|
35
35
|
IntelligenceLab
|
|
36
36
|
} from "./chunk-FBS5FGW2.js";
|
|
@@ -775,6 +775,26 @@ function registerWebFetchTool(api, Type2, logPrefix, options) {
|
|
|
775
775
|
}
|
|
776
776
|
|
|
777
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
|
+
}
|
|
778
798
|
var TRADERCLAW_WALLET_PRIVATE_KEY_ENV = "TRADERCLAW_WALLET_PRIVATE_KEY";
|
|
779
799
|
function walletPrivateKeyFromPluginConfigRecord(obj) {
|
|
780
800
|
const w = typeof obj.walletPrivateKey === "string" ? obj.walletPrivateKey.trim() : "";
|
|
@@ -923,6 +943,8 @@ var solanaTraderPlugin = {
|
|
|
923
943
|
name: "Solana Trader",
|
|
924
944
|
description: "Autonomous Solana memecoin trading agent \u2014 V1-Upgraded with intelligence lab, tool envelopes, prompt scrubbing, and split skill architecture",
|
|
925
945
|
register(api) {
|
|
946
|
+
__solanaTraderDisposePreviousLifecycle(api.logger);
|
|
947
|
+
const __solanaTraderDisposers = [];
|
|
926
948
|
const pluginConfigRaw = api.pluginConfig && typeof api.pluginConfig === "object" && !Array.isArray(api.pluginConfig) ? api.pluginConfig : {};
|
|
927
949
|
const walletPrivateKeyFromPluginJsonOnly = walletPrivateKeyFromPluginConfigRecord(pluginConfigRaw);
|
|
928
950
|
const config = parseConfig(api.pluginConfig);
|
|
@@ -1050,6 +1072,12 @@ var solanaTraderPlugin = {
|
|
|
1050
1072
|
error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
|
|
1051
1073
|
}
|
|
1052
1074
|
});
|
|
1075
|
+
__solanaTraderDisposers.push(() => {
|
|
1076
|
+
try {
|
|
1077
|
+
sessionManager.destroy();
|
|
1078
|
+
} catch {
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1053
1081
|
const onUnauthorized = async () => {
|
|
1054
1082
|
api.logger.warn("[solana-trader] Received 401 \u2014 refreshing session...");
|
|
1055
1083
|
return sessionManager.handleUnauthorized();
|
|
@@ -2435,6 +2463,12 @@ ${notes}
|
|
|
2435
2463
|
error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
|
|
2436
2464
|
}
|
|
2437
2465
|
});
|
|
2466
|
+
__solanaTraderDisposers.push(() => {
|
|
2467
|
+
try {
|
|
2468
|
+
bitqueryStreamManager.close();
|
|
2469
|
+
} catch {
|
|
2470
|
+
}
|
|
2471
|
+
});
|
|
2438
2472
|
api.registerTool({
|
|
2439
2473
|
name: "solana_bitquery_subscribe",
|
|
2440
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.",
|
|
@@ -2568,6 +2602,12 @@ ${notes}
|
|
|
2568
2602
|
error: (msg) => api.logger.error(`[solana-trader] ${msg}`)
|
|
2569
2603
|
}
|
|
2570
2604
|
});
|
|
2605
|
+
__solanaTraderDisposers.push(() => {
|
|
2606
|
+
try {
|
|
2607
|
+
void alphaStreamManager.unsubscribe().catch(() => void 0);
|
|
2608
|
+
} catch {
|
|
2609
|
+
}
|
|
2610
|
+
});
|
|
2571
2611
|
let startupGateRunning = null;
|
|
2572
2612
|
let startupGateState = { ok: false, ts: 0, steps: [] };
|
|
2573
2613
|
let lastForwardProbeState = null;
|
|
@@ -4297,6 +4337,12 @@ Context compaction triggered. STATE.md synced from last persisted state. Decisio
|
|
|
4297
4337
|
}
|
|
4298
4338
|
);
|
|
4299
4339
|
let solanaTraderSessionWatchdogTimer = null;
|
|
4340
|
+
__solanaTraderDisposers.push(() => {
|
|
4341
|
+
if (solanaTraderSessionWatchdogTimer !== null) {
|
|
4342
|
+
clearInterval(solanaTraderSessionWatchdogTimer);
|
|
4343
|
+
solanaTraderSessionWatchdogTimer = null;
|
|
4344
|
+
}
|
|
4345
|
+
});
|
|
4300
4346
|
api.registerService({
|
|
4301
4347
|
id: "solana-trader-session",
|
|
4302
4348
|
start: async () => {
|
|
@@ -4547,6 +4593,16 @@ Context compaction triggered. STATE.md synced from last persisted state. Decisio
|
|
|
4547
4593
|
api.logger.info(
|
|
4548
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)`
|
|
4549
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
|
+
});
|
|
4550
4606
|
}
|
|
4551
4607
|
};
|
|
4552
4608
|
var index_default = solanaTraderPlugin;
|
package/dist/src/http-client.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solana-traderclaw",
|
|
3
|
-
"version": "1.0.
|
|
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",
|