traderclaw-cli 1.0.78 → 1.0.80
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 +58 -10
- package/bin/openclaw-trader.mjs +45 -3
- package/package.json +2 -2
|
@@ -400,6 +400,14 @@ function isOpenClawConfigSchemaFailure(text) {
|
|
|
400
400
|
function runCommandWithEvents(cmd, args = [], opts = {}) {
|
|
401
401
|
return new Promise((resolve, reject) => {
|
|
402
402
|
const { onEvent, ...spawnOpts } = opts;
|
|
403
|
+
const isNpm = /(?:^|[\\/])npm(?:\.cmd)?$/.test(cmd) || cmd === "npm";
|
|
404
|
+
if (isNpm && !spawnOpts.env?.NODE_OPTIONS?.includes("max-old-space-size")) {
|
|
405
|
+
spawnOpts.env = {
|
|
406
|
+
...process.env,
|
|
407
|
+
...spawnOpts.env,
|
|
408
|
+
NODE_OPTIONS: [spawnOpts.env?.NODE_OPTIONS || process.env.NODE_OPTIONS || "", "--max-old-space-size=512"].filter(Boolean).join(" "),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
403
411
|
const child = spawn(cmd, args, {
|
|
404
412
|
stdio: "pipe",
|
|
405
413
|
shell: true,
|
|
@@ -427,14 +435,19 @@ function runCommandWithEvents(cmd, args = [], opts = {}) {
|
|
|
427
435
|
const urls = [...new Set([...extractUrls(stdout), ...extractUrls(stderr)])];
|
|
428
436
|
if (code === 0) resolve({ stdout, stderr, code, urls });
|
|
429
437
|
else {
|
|
438
|
+
const isOom = code === 137 || (stderr || stdout || "").includes("Killed");
|
|
430
439
|
const raw = (stderr || "").trim();
|
|
431
440
|
const tailLines = raw.split("\n").filter((l) => l.length > 0).slice(-40).join("\n");
|
|
432
441
|
const stderrPreview = tailLines.length > 8000 ? tailLines.slice(-8000) : tailLines;
|
|
433
|
-
const
|
|
442
|
+
const prefix = isOom
|
|
443
|
+
? `Out of memory (exit 137 / SIGKILL): the host killed '${cmd}' — try a machine with ≥1 GB free RAM, or reduce concurrency with npm_config_maxsockets=2`
|
|
444
|
+
: `command failed with exit code ${code}`;
|
|
445
|
+
const err = new Error(stderrPreview ? `${prefix}: ${stderrPreview}` : prefix);
|
|
434
446
|
err.code = code;
|
|
435
447
|
err.stdout = stdout;
|
|
436
448
|
err.stderr = stderr;
|
|
437
449
|
err.urls = urls;
|
|
450
|
+
err.oom = isOom;
|
|
438
451
|
reject(err);
|
|
439
452
|
}
|
|
440
453
|
});
|
|
@@ -454,6 +467,14 @@ function getGlobalOpenClawPackageDir() {
|
|
|
454
467
|
* incomplete `node_modules` after `npm install -g` (hoisting, optional deps, or interrupted
|
|
455
468
|
* installs). OpenClaw then fails at runtime with `Cannot find module 'grammy'` while loading
|
|
456
469
|
* config. Installing from the package directory restores declared dependencies.
|
|
470
|
+
*
|
|
471
|
+
* `--ignore-scripts` avoids OpenClaw's postinstall (and nested installs) failing on hosts without
|
|
472
|
+
* a C toolchain: e.g. `@discordjs/opus` has no prebuild for Node 22 and falls back to `node-gyp`
|
|
473
|
+
* (`make` not found). Skipping scripts still installs declared JS deps (e.g. `grammy`). Users who
|
|
474
|
+
* need native/voice features can install build-essential and re-run `npm install` without
|
|
475
|
+
* `--ignore-scripts` in the global openclaw directory.
|
|
476
|
+
*
|
|
477
|
+
* We still run `npm install grammy @buape/carbon --no-save` with `--ignore-scripts` as a safety net.
|
|
457
478
|
*/
|
|
458
479
|
/** Runs `npm install` in the global `openclaw` package directory (fixes missing `grammy` etc.). */
|
|
459
480
|
export async function ensureOpenClawGlobalPackageDependencies() {
|
|
@@ -461,10 +482,23 @@ export async function ensureOpenClawGlobalPackageDependencies() {
|
|
|
461
482
|
if (!dir) {
|
|
462
483
|
return { skipped: true, reason: "global_openclaw_dir_not_found" };
|
|
463
484
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
485
|
+
const registry = "https://registry.npmjs.org/";
|
|
486
|
+
const installFlags = ["install", "--omit=dev", "--ignore-scripts", "--registry", registry];
|
|
487
|
+
await runCommandWithEvents("npm", installFlags, { cwd: dir, shell: false });
|
|
488
|
+
await runCommandWithEvents(
|
|
489
|
+
"npm",
|
|
490
|
+
[
|
|
491
|
+
"install",
|
|
492
|
+
"--omit=dev",
|
|
493
|
+
"--no-save",
|
|
494
|
+
"--ignore-scripts",
|
|
495
|
+
"--registry",
|
|
496
|
+
registry,
|
|
497
|
+
"grammy",
|
|
498
|
+
"@buape/carbon",
|
|
499
|
+
],
|
|
500
|
+
{ cwd: dir, shell: false },
|
|
501
|
+
);
|
|
468
502
|
return { repaired: true, dir };
|
|
469
503
|
}
|
|
470
504
|
|
|
@@ -477,7 +511,7 @@ export async function ensureOpenClawGlobalPackageDependencies() {
|
|
|
477
511
|
async function installOpenClawPlatform() {
|
|
478
512
|
const hadOpenclaw = commandExists("openclaw");
|
|
479
513
|
const previousVersion = hadOpenclaw ? getCommandOutput("openclaw --version") : null;
|
|
480
|
-
await runCommandWithEvents("npm", ["install", "-g", "--registry", "https://registry.npmjs.org/", "openclaw@latest"]);
|
|
514
|
+
await runCommandWithEvents("npm", ["install", "-g", "--ignore-scripts", "--registry", "https://registry.npmjs.org/", "openclaw@latest"]);
|
|
481
515
|
const available = commandExists("openclaw");
|
|
482
516
|
const version = available ? getCommandOutput("openclaw --version") : null;
|
|
483
517
|
if (!available) {
|
|
@@ -514,7 +548,7 @@ function isNpmFilesystemPackageSpec(spec) {
|
|
|
514
548
|
* IMPORTANT: run with `{ shell: false }` — `spawn(..., { shell: true })` can drop argv on Unix and npm then mis-resolves the package name.
|
|
515
549
|
*/
|
|
516
550
|
function npmGlobalInstallArgs(spec, { force = false } = {}) {
|
|
517
|
-
const args = ["install", "-g"];
|
|
551
|
+
const args = ["install", "-g", "--ignore-scripts"];
|
|
518
552
|
if (force) args.push("--force");
|
|
519
553
|
if (!isNpmFilesystemPackageSpec(spec)) {
|
|
520
554
|
args.push("--registry", "https://registry.npmjs.org/");
|
|
@@ -1604,14 +1638,14 @@ export function spawnOpenClawCodexAuthLoginChild() {
|
|
|
1604
1638
|
if (process.platform === "win32") {
|
|
1605
1639
|
return spawn("openclaw", argv, { stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
1606
1640
|
}
|
|
1607
|
-
// `unbuffer` (expect package) runs the CLI under a PTY and forwards stdin for the paste step reliably.
|
|
1608
|
-
// Plain `script` often does not forward Node's stdin to the inner openclaw process, which causes hangs until timeout.
|
|
1609
1641
|
if (commandExists("unbuffer")) {
|
|
1610
1642
|
return spawn("unbuffer", ["openclaw", ...argv], { stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
1611
1643
|
}
|
|
1612
1644
|
if (commandExists("script")) {
|
|
1613
1645
|
const cmdline = "openclaw models auth login --provider openai-codex";
|
|
1614
|
-
return
|
|
1646
|
+
// --return propagates the inner command's exit code (util-linux 2.38+).
|
|
1647
|
+
// Without it, script may exit 0 even if openclaw fails.
|
|
1648
|
+
return spawn("script", ["--return", "-q", "-c", cmdline, "/dev/null"], {
|
|
1615
1649
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1616
1650
|
shell: false,
|
|
1617
1651
|
});
|
|
@@ -2093,6 +2127,20 @@ export class InstallerStepEngine {
|
|
|
2093
2127
|
}
|
|
2094
2128
|
}
|
|
2095
2129
|
|
|
2130
|
+
const authFile = join(homedir(), ".openclaw", "agents", "main", "agent", "auth-profiles.json");
|
|
2131
|
+
let hasAuth = false;
|
|
2132
|
+
try {
|
|
2133
|
+
hasAuth = readFileSync(authFile, "utf-8").length > 20;
|
|
2134
|
+
} catch { /* file missing */ }
|
|
2135
|
+
if (!hasAuth) {
|
|
2136
|
+
throw new Error(
|
|
2137
|
+
"No OAuth credentials found at " + authFile + ". " +
|
|
2138
|
+
"The wizard OAuth flow did not save tokens (the callback may not have reached the OpenClaw CLI). " +
|
|
2139
|
+
"Run 'openclaw models auth login --provider openai-codex' in a terminal, " +
|
|
2140
|
+
"then re-run the wizard with the 'already logged in' option.",
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2096
2144
|
const selection = resolveLlmModelSelection(provider, requestedModel);
|
|
2097
2145
|
for (const msg of selection.warnings) {
|
|
2098
2146
|
this.emitLog("configure_llm", "warn", msg);
|
package/bin/openclaw-trader.mjs
CHANGED
|
@@ -3257,6 +3257,32 @@ async function cmdInstall(args) {
|
|
|
3257
3257
|
}
|
|
3258
3258
|
}
|
|
3259
3259
|
|
|
3260
|
+
/**
|
|
3261
|
+
* Release port 1455 so the OpenClaw CLI can bind its own callback server
|
|
3262
|
+
* during `openclaw models auth login`. Returns a promise that resolves
|
|
3263
|
+
* once the proxy is fully closed (or after a safety timeout).
|
|
3264
|
+
*/
|
|
3265
|
+
function stopCallbackProxy() {
|
|
3266
|
+
return new Promise((resolve) => {
|
|
3267
|
+
if (!oauthCallbackProxy) { resolve(); return; }
|
|
3268
|
+
const proxy = oauthCallbackProxy;
|
|
3269
|
+
oauthCallbackProxy = null;
|
|
3270
|
+
const safety = setTimeout(resolve, 2000);
|
|
3271
|
+
proxy.close(() => { clearTimeout(safety); setTimeout(resolve, 150); });
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
function hasOpenaiCodexAuthTokens() {
|
|
3276
|
+
try {
|
|
3277
|
+
const authFile = join(homedir(), ".openclaw", "agents", "main", "agent", "auth-profiles.json");
|
|
3278
|
+
const raw = readFileSync(authFile, "utf-8");
|
|
3279
|
+
const data = JSON.parse(raw);
|
|
3280
|
+
return data && typeof data === "object" && JSON.stringify(data).length > 20;
|
|
3281
|
+
} catch {
|
|
3282
|
+
return false;
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3260
3286
|
function killOauthSession(sessionId, signal = "SIGTERM") {
|
|
3261
3287
|
const s = oauthSessions.get(sessionId);
|
|
3262
3288
|
if (!s) return;
|
|
@@ -3388,6 +3414,13 @@ async function cmdInstall(args) {
|
|
|
3388
3414
|
killOauthSession(id);
|
|
3389
3415
|
}
|
|
3390
3416
|
|
|
3417
|
+
// Release port 1455 so the OpenClaw CLI can bind its own callback
|
|
3418
|
+
// server directly. The previous approach relied on the wizard proxy
|
|
3419
|
+
// catching the callback and forwarding the code via stdin, but
|
|
3420
|
+
// `script` (PTY wrapper) does not reliably forward stdin to the
|
|
3421
|
+
// inner process, causing tokens to never be saved.
|
|
3422
|
+
await stopCallbackProxy();
|
|
3423
|
+
|
|
3391
3424
|
const sessionId = randomUUID();
|
|
3392
3425
|
const child = spawnOpenClawCodexAuthLoginChild();
|
|
3393
3426
|
|
|
@@ -3402,6 +3435,7 @@ async function cmdInstall(args) {
|
|
|
3402
3435
|
/* ignore */
|
|
3403
3436
|
}
|
|
3404
3437
|
oauthSessions.delete(sessionId);
|
|
3438
|
+
startCallbackProxy();
|
|
3405
3439
|
respondJson(504, {
|
|
3406
3440
|
ok: false,
|
|
3407
3441
|
error: "oauth_url_timeout",
|
|
@@ -3440,6 +3474,7 @@ async function cmdInstall(args) {
|
|
|
3440
3474
|
});
|
|
3441
3475
|
child.on("error", (err) => {
|
|
3442
3476
|
clearTimeout(urlTimeout);
|
|
3477
|
+
startCallbackProxy();
|
|
3443
3478
|
if (responded) return;
|
|
3444
3479
|
responded = true;
|
|
3445
3480
|
oauthSessions.delete(sessionId);
|
|
@@ -3447,6 +3482,7 @@ async function cmdInstall(args) {
|
|
|
3447
3482
|
});
|
|
3448
3483
|
child.on("close", (code) => {
|
|
3449
3484
|
clearTimeout(urlTimeout);
|
|
3485
|
+
startCallbackProxy();
|
|
3450
3486
|
if (!responded) {
|
|
3451
3487
|
responded = true;
|
|
3452
3488
|
oauthSessions.delete(sessionId);
|
|
@@ -3466,9 +3502,15 @@ async function cmdInstall(args) {
|
|
|
3466
3502
|
pending.updatedAt = Date.now();
|
|
3467
3503
|
pending.exitCode = typeof code === "number" ? code : null;
|
|
3468
3504
|
pending.detail = stripAnsi(combined).slice(-4000);
|
|
3469
|
-
if (code === 0) {
|
|
3505
|
+
if (code === 0 && hasOpenaiCodexAuthTokens()) {
|
|
3470
3506
|
pending.status = "succeeded";
|
|
3471
3507
|
pending.message = "ChatGPT OAuth completed successfully.";
|
|
3508
|
+
} else if (code === 0) {
|
|
3509
|
+
pending.status = "failed";
|
|
3510
|
+
pending.message =
|
|
3511
|
+
"OpenClaw exited OK but no auth tokens were saved. " +
|
|
3512
|
+
"Run 'openclaw models auth login --provider openai-codex' in a terminal, " +
|
|
3513
|
+
"then re-run the wizard with the already-logged-in option.";
|
|
3472
3514
|
} else {
|
|
3473
3515
|
pending.status = "failed";
|
|
3474
3516
|
pending.message =
|
|
@@ -4036,7 +4078,7 @@ Commands:
|
|
|
4036
4078
|
signup Create a new account (alias for: setup --signup; run locally, not via the agent)
|
|
4037
4079
|
precheck Run environment checks (dry-run or allow-install)
|
|
4038
4080
|
install Launch installer flows (--wizard for localhost GUI)
|
|
4039
|
-
repair-openclaw Re-run npm install in the global openclaw package (fixes missing grammy
|
|
4081
|
+
repair-openclaw Re-run npm install in the global openclaw package (fixes missing grammy / @buape/carbon; uses --ignore-scripts so @discordjs/opus does not require build tools)
|
|
4040
4082
|
gateway Gateway helpers (see subcommands below)
|
|
4041
4083
|
login Re-authenticate (uses refresh token when valid; full challenge only if needed)
|
|
4042
4084
|
logout Revoke current session and clear tokens
|
|
@@ -4110,7 +4152,7 @@ Examples:
|
|
|
4110
4152
|
|
|
4111
4153
|
async function cmdRepairOpenclaw() {
|
|
4112
4154
|
const { ensureOpenClawGlobalPackageDependencies } = await import("./installer-step-engine.mjs");
|
|
4113
|
-
printInfo("Repairing global OpenClaw npm dependencies (fixes missing grammy /
|
|
4155
|
+
printInfo("Repairing global OpenClaw npm dependencies (fixes missing grammy, @buape/carbon, etc.)...");
|
|
4114
4156
|
const r = await ensureOpenClawGlobalPackageDependencies();
|
|
4115
4157
|
if (r.skipped) {
|
|
4116
4158
|
printError(`Could not find global OpenClaw package (${r.reason}). Install or upgrade: npm install -g openclaw@latest`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "traderclaw-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.80",
|
|
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.80"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"traderclaw",
|