traderclaw-cli 1.0.127 → 1.0.129
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/bin/installer-step-engine.mjs +19 -0
- package/bin/openclaw-trader.mjs +90 -4
- package/package.json +2 -2
|
@@ -369,6 +369,21 @@ function canUseSudoWithoutPrompt() {
|
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
/**
|
|
373
|
+
* NodeSource/deb npm defaults to a root-owned global tree (/usr/lib/node_modules). Non-root
|
|
374
|
+
* `npm install -g` must target a user prefix; `NPM_CONFIG_PREFIX` is applied to every npm child.
|
|
375
|
+
*/
|
|
376
|
+
function ensureNpmUserGlobalPrefixForNonRoot() {
|
|
377
|
+
if (typeof process.getuid !== "function" || process.getuid() === 0) return;
|
|
378
|
+
const prefix = join(homedir(), ".npm-global");
|
|
379
|
+
try {
|
|
380
|
+
mkdirSync(prefix, { recursive: true });
|
|
381
|
+
} catch {
|
|
382
|
+
/* ignore */
|
|
383
|
+
}
|
|
384
|
+
process.env.NPM_CONFIG_PREFIX = prefix;
|
|
385
|
+
}
|
|
386
|
+
|
|
372
387
|
/** User systemd (openclaw gateway) expects XDG_RUNTIME_DIR under /run/user/<uid> once linger is on. */
|
|
373
388
|
function primeLinuxUserRuntimeEnv() {
|
|
374
389
|
if (process.platform !== "linux" || typeof process.getuid !== "function") return;
|
|
@@ -599,6 +614,7 @@ function getGlobalOpenClawPackageDir() {
|
|
|
599
614
|
*/
|
|
600
615
|
/** Runs `npm install` in the global `openclaw` package directory (fixes missing `grammy` etc.). */
|
|
601
616
|
export async function ensureOpenClawGlobalPackageDependencies() {
|
|
617
|
+
ensureNpmUserGlobalPrefixForNonRoot();
|
|
602
618
|
const dir = getGlobalOpenClawPackageDir();
|
|
603
619
|
if (!dir) {
|
|
604
620
|
return { skipped: true, reason: "global_openclaw_dir_not_found" };
|
|
@@ -2770,6 +2786,7 @@ export class InstallerStepEngine {
|
|
|
2770
2786
|
this.state.status = "running";
|
|
2771
2787
|
this.state.startedAt = nowIso();
|
|
2772
2788
|
try {
|
|
2789
|
+
ensureNpmUserGlobalPrefixForNonRoot();
|
|
2773
2790
|
if (!this.options.skipPreflight) {
|
|
2774
2791
|
await this.runStep("preflight", "Checking prerequisites", async () => {
|
|
2775
2792
|
if (!commandExists("node") || !commandExists("npm")) throw new Error("node and npm are required");
|
|
@@ -3190,6 +3207,8 @@ export async function runTraderClawDeepUpdate(options = {}) {
|
|
|
3190
3207
|
onLog({ at: nowIso(), stepId, level, text: clean, urls: [] });
|
|
3191
3208
|
};
|
|
3192
3209
|
|
|
3210
|
+
ensureNpmUserGlobalPrefixForNonRoot();
|
|
3211
|
+
|
|
3193
3212
|
async function runDeepStep(stepId, title, handler) {
|
|
3194
3213
|
onStep({ at: nowIso(), stepId, status: "in_progress", detail: title });
|
|
3195
3214
|
try {
|
package/bin/openclaw-trader.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { createInterface } from "readline";
|
|
4
|
-
import { readFileSync, writeFileSync, mkdirSync, appendFileSync, existsSync } from "fs";
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, appendFileSync, existsSync, renameSync } from "fs";
|
|
5
5
|
import { dirname, join } from "path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
7
7
|
import { homedir } from "os";
|
|
@@ -74,6 +74,86 @@ const CONFIG_DIR = join(homedir(), ".openclaw");
|
|
|
74
74
|
const CONFIG_FILE = join(CONFIG_DIR, "openclaw.json");
|
|
75
75
|
const WALLET_PRIVATE_KEY_ENV = "TRADERCLAW_WALLET_PRIVATE_KEY";
|
|
76
76
|
|
|
77
|
+
/** Session sidecar directory: explicit plugins.entries.solana-trader.config.dataDir or ~/.traderclaw-v1-data */
|
|
78
|
+
function resolveTraderClawDataDir(pluginConfig) {
|
|
79
|
+
const raw =
|
|
80
|
+
pluginConfig && typeof pluginConfig.dataDir === "string" ? pluginConfig.dataDir.trim() : "";
|
|
81
|
+
if (raw.length > 0) {
|
|
82
|
+
return raw.replace(/^~(?=$|[/\\])/, homedir());
|
|
83
|
+
}
|
|
84
|
+
return join(homedir(), ".traderclaw-v1-data");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function ensureExplicitPluginDataDir(pluginConfig) {
|
|
88
|
+
if (!pluginConfig || typeof pluginConfig !== "object") return;
|
|
89
|
+
const raw = typeof pluginConfig.dataDir === "string" ? pluginConfig.dataDir.trim() : "";
|
|
90
|
+
if (raw.length > 0) {
|
|
91
|
+
pluginConfig.dataDir = raw.replace(/^~(?=$|[/\\])/, homedir());
|
|
92
|
+
} else {
|
|
93
|
+
pluginConfig.dataDir = join(homedir(), ".traderclaw-v1-data");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function persistSessionSidecar(pluginConfig, tokens) {
|
|
98
|
+
if (!tokens || typeof tokens.refreshToken !== "string" || tokens.refreshToken.length === 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const dataDir = resolveTraderClawDataDir(pluginConfig);
|
|
102
|
+
const sessionTokensPath = join(dataDir, "session-tokens.json");
|
|
103
|
+
mkdirSync(dataDir, { recursive: true });
|
|
104
|
+
let existingSidecar = {};
|
|
105
|
+
try {
|
|
106
|
+
if (existsSync(sessionTokensPath)) {
|
|
107
|
+
existingSidecar = JSON.parse(readFileSync(sessionTokensPath, "utf-8")) || {};
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
/* ignore */
|
|
111
|
+
}
|
|
112
|
+
const ttlSec =
|
|
113
|
+
typeof tokens.accessTokenTtlSeconds === "number" && Number.isFinite(tokens.accessTokenTtlSeconds)
|
|
114
|
+
? tokens.accessTokenTtlSeconds
|
|
115
|
+
: 900;
|
|
116
|
+
const accessTokenExpiresAt = Date.now() + ttlSec * 1000;
|
|
117
|
+
const walletEntry =
|
|
118
|
+
pluginConfig.walletPublicKey && String(pluginConfig.walletPublicKey).trim()
|
|
119
|
+
? { walletPublicKey: String(pluginConfig.walletPublicKey).trim() }
|
|
120
|
+
: {};
|
|
121
|
+
const recoveryEntry =
|
|
122
|
+
typeof pluginConfig.recoverySecret === "string" && pluginConfig.recoverySecret.trim().length > 0
|
|
123
|
+
? { recoverySecret: pluginConfig.recoverySecret.trim() }
|
|
124
|
+
: {};
|
|
125
|
+
const payload = {
|
|
126
|
+
...existingSidecar,
|
|
127
|
+
refreshToken: tokens.refreshToken,
|
|
128
|
+
accessToken: tokens.accessToken,
|
|
129
|
+
accessTokenExpiresAt,
|
|
130
|
+
...walletEntry,
|
|
131
|
+
...recoveryEntry,
|
|
132
|
+
};
|
|
133
|
+
const tmp = `${sessionTokensPath}.${process.pid}.${Date.now()}.tmp`;
|
|
134
|
+
writeFileSync(tmp, JSON.stringify(payload, null, 2) + "\n", "utf-8");
|
|
135
|
+
renameSync(tmp, sessionTokensPath);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function clearSessionSidecarSessionFields(pluginConfig) {
|
|
139
|
+
const dataDir = resolveTraderClawDataDir(pluginConfig);
|
|
140
|
+
const sessionTokensPath = join(dataDir, "session-tokens.json");
|
|
141
|
+
if (!existsSync(sessionTokensPath)) return;
|
|
142
|
+
try {
|
|
143
|
+
const raw = JSON.parse(readFileSync(sessionTokensPath, "utf-8"));
|
|
144
|
+
if (!raw || typeof raw !== "object") return;
|
|
145
|
+
const next = { ...raw };
|
|
146
|
+
delete next.refreshToken;
|
|
147
|
+
delete next.accessToken;
|
|
148
|
+
delete next.accessTokenExpiresAt;
|
|
149
|
+
const tmp = `${sessionTokensPath}.${process.pid}.${Date.now()}.tmp`;
|
|
150
|
+
writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
151
|
+
renameSync(tmp, sessionTokensPath);
|
|
152
|
+
} catch {
|
|
153
|
+
/* ignore */
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
77
157
|
/** Linked from CLI errors and setup — keep in sync with SKILL / README. */
|
|
78
158
|
const TRADERCLAW_SESSION_TROUBLESHOOTING_URL =
|
|
79
159
|
"https://docs.traderclaw.ai/docs/installation#troubleshooting-session-expired-auth-errors-or-the-agent-logged-out";
|
|
@@ -1290,6 +1370,8 @@ async function cmdSetup(args) {
|
|
|
1290
1370
|
if (!pluginConfig.kaybaFolder) pluginConfig.kaybaFolder = "traderclaw";
|
|
1291
1371
|
}
|
|
1292
1372
|
|
|
1373
|
+
ensureExplicitPluginDataDir(pluginConfig);
|
|
1374
|
+
|
|
1293
1375
|
print("\nWriting configuration...\n");
|
|
1294
1376
|
|
|
1295
1377
|
const existingConfig = readConfig();
|
|
@@ -1327,6 +1409,8 @@ async function cmdSetup(args) {
|
|
|
1327
1409
|
|
|
1328
1410
|
writeConfig(existingConfig);
|
|
1329
1411
|
|
|
1412
|
+
persistSessionSidecar(pluginConfig, sessionTokens);
|
|
1413
|
+
|
|
1330
1414
|
printSuccess(` Config written to ${CONFIG_FILE}`);
|
|
1331
1415
|
|
|
1332
1416
|
if (kaybaApiKey) {
|
|
@@ -1657,7 +1741,9 @@ async function cmdLogin(args) {
|
|
|
1657
1741
|
const removedLegacyKey = removeLegacyWalletPrivateKey(pluginConfig);
|
|
1658
1742
|
|
|
1659
1743
|
try {
|
|
1660
|
-
await establishSession(orchestratorUrl, pluginConfig, walletPrivateKeyArg);
|
|
1744
|
+
const tokens = await establishSession(orchestratorUrl, pluginConfig, walletPrivateKeyArg);
|
|
1745
|
+
ensureExplicitPluginDataDir(pluginConfig);
|
|
1746
|
+
persistSessionSidecar(pluginConfig, tokens);
|
|
1661
1747
|
setPluginConfig(config, pluginConfig);
|
|
1662
1748
|
writeConfig(config);
|
|
1663
1749
|
printSuccess("\n Session established and saved.");
|
|
@@ -1706,6 +1792,7 @@ async function cmdLogout() {
|
|
|
1706
1792
|
const removedLegacyKey = removeLegacyWalletPrivateKey(pluginConfig);
|
|
1707
1793
|
setPluginConfig(config, pluginConfig);
|
|
1708
1794
|
writeConfig(config);
|
|
1795
|
+
clearSessionSidecarSessionFields(pluginConfig);
|
|
1709
1796
|
|
|
1710
1797
|
printSuccess(" Local session cleared.");
|
|
1711
1798
|
if (removedLegacyKey) {
|
|
@@ -4481,7 +4568,7 @@ async function cmdTestSession(args) {
|
|
|
4481
4568
|
process.exit(1);
|
|
4482
4569
|
}
|
|
4483
4570
|
|
|
4484
|
-
const dataDir = pluginConfig
|
|
4571
|
+
const dataDir = resolveTraderClawDataDir(pluginConfig);
|
|
4485
4572
|
const sessionTokensPath = join(dataDir, "session-tokens.json");
|
|
4486
4573
|
|
|
4487
4574
|
let sidecar = null;
|
|
@@ -4666,7 +4753,6 @@ async function cmdTestSession(args) {
|
|
|
4666
4753
|
};
|
|
4667
4754
|
const tmp = `${sessionTokensPath}.${process.pid}.${Date.now()}.tmp`;
|
|
4668
4755
|
writeFileSync(tmp, JSON.stringify(payload, null, 2) + "\n", "utf-8");
|
|
4669
|
-
const { renameSync } = await import("fs");
|
|
4670
4756
|
renameSync(tmp, sessionTokensPath);
|
|
4671
4757
|
printSuccess(` OK — written to ${sessionTokensPath}`);
|
|
4672
4758
|
results.push({ test: "persist_sidecar", status: "ok" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "traderclaw-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.129",
|
|
4
4
|
"description": "Global TraderClaw CLI (install --wizard, setup, precheck). Installs solana-traderclaw as a dependency for OpenClaw plugin files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"node": ">=22"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"solana-traderclaw": "^1.0.
|
|
20
|
+
"solana-traderclaw": "^1.0.129"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"traderclaw",
|