traderclaw-cli 1.0.77 → 1.0.79
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 +17 -3
- package/bin/openclaw-trader.mjs +154 -2
- package/package.json +2 -2
|
@@ -1604,14 +1604,14 @@ export function spawnOpenClawCodexAuthLoginChild() {
|
|
|
1604
1604
|
if (process.platform === "win32") {
|
|
1605
1605
|
return spawn("openclaw", argv, { stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
1606
1606
|
}
|
|
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
1607
|
if (commandExists("unbuffer")) {
|
|
1610
1608
|
return spawn("unbuffer", ["openclaw", ...argv], { stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
1611
1609
|
}
|
|
1612
1610
|
if (commandExists("script")) {
|
|
1613
1611
|
const cmdline = "openclaw models auth login --provider openai-codex";
|
|
1614
|
-
return
|
|
1612
|
+
// --return propagates the inner command's exit code (util-linux 2.38+).
|
|
1613
|
+
// Without it, script may exit 0 even if openclaw fails.
|
|
1614
|
+
return spawn("script", ["--return", "-q", "-c", cmdline, "/dev/null"], {
|
|
1615
1615
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1616
1616
|
shell: false,
|
|
1617
1617
|
});
|
|
@@ -2093,6 +2093,20 @@ export class InstallerStepEngine {
|
|
|
2093
2093
|
}
|
|
2094
2094
|
}
|
|
2095
2095
|
|
|
2096
|
+
const authFile = join(homedir(), ".openclaw", "agents", "main", "agent", "auth-profiles.json");
|
|
2097
|
+
let hasAuth = false;
|
|
2098
|
+
try {
|
|
2099
|
+
hasAuth = readFileSync(authFile, "utf-8").length > 20;
|
|
2100
|
+
} catch { /* file missing */ }
|
|
2101
|
+
if (!hasAuth) {
|
|
2102
|
+
throw new Error(
|
|
2103
|
+
"No OAuth credentials found at " + authFile + ". " +
|
|
2104
|
+
"The wizard OAuth flow did not save tokens (the callback may not have reached the OpenClaw CLI). " +
|
|
2105
|
+
"Run 'openclaw models auth login --provider openai-codex' in a terminal, " +
|
|
2106
|
+
"then re-run the wizard with the 'already logged in' option.",
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2096
2110
|
const selection = resolveLlmModelSelection(provider, requestedModel);
|
|
2097
2111
|
for (const msg of selection.warnings) {
|
|
2098
2112
|
this.emitLog("configure_llm", "warn", msg);
|
package/bin/openclaw-trader.mjs
CHANGED
|
@@ -2158,6 +2158,16 @@ function wizardHtml(defaults) {
|
|
|
2158
2158
|
<button type="button" id="oauthRetryBtn" class="secondary hidden">Try sign-in again</button>
|
|
2159
2159
|
</div>
|
|
2160
2160
|
<p id="oauthFlowStatus" class="muted" style="margin-top:8px;" aria-live="polite">Choose OAuth and wait a moment. We will prepare your sign-in automatically.</p>
|
|
2161
|
+
<div id="oauthFallbackPaste" class="hidden" style="margin-top:12px;padding:12px;background:#111827;border:1px solid #334a87;border-radius:8px;">
|
|
2162
|
+
<p class="muted" style="margin:0 0 8px;font-size:13px;color:#ffcc70;">
|
|
2163
|
+
<strong>Redirect didn't reach us?</strong> Copy the full URL from the error page in your browser (it starts with <code>http://localhost:1455/auth/callback?code=…</code>) and paste it below.
|
|
2164
|
+
</p>
|
|
2165
|
+
<div style="display:flex;gap:8px;">
|
|
2166
|
+
<input id="oauthFallbackUrlInput" type="text" placeholder="Paste the full localhost:1455/auth/callback?code=… URL here" style="flex:1;font-size:12px;padding:8px 10px;border-radius:6px;border:1px solid #334a87;background:#0a1224;color:#e0e8ff;">
|
|
2167
|
+
<button type="button" id="oauthFallbackSubmitBtn" class="secondary" style="padding:8px 14px;white-space:nowrap;">Submit URL</button>
|
|
2168
|
+
</div>
|
|
2169
|
+
<p id="oauthFallbackError" class="muted hidden" style="margin:6px 0 0;font-size:12px;color:#ff6b6b;"></p>
|
|
2170
|
+
</div>
|
|
2161
2171
|
</div>
|
|
2162
2172
|
</div>
|
|
2163
2173
|
<p class="muted" id="llmLoadState" aria-live="polite">Loading LLM provider catalog...</p>
|
|
@@ -2344,6 +2354,11 @@ function wizardHtml(defaults) {
|
|
|
2344
2354
|
const oauthStepOpen = document.getElementById("oauthStepOpen");
|
|
2345
2355
|
const oauthStepComplete = document.getElementById("oauthStepComplete");
|
|
2346
2356
|
const oauthStepVerify = document.getElementById("oauthStepVerify");
|
|
2357
|
+
const oauthFallbackPaste = document.getElementById("oauthFallbackPaste");
|
|
2358
|
+
const oauthFallbackUrlInput = document.getElementById("oauthFallbackUrlInput");
|
|
2359
|
+
const oauthFallbackSubmitBtn = document.getElementById("oauthFallbackSubmitBtn");
|
|
2360
|
+
const oauthFallbackError = document.getElementById("oauthFallbackError");
|
|
2361
|
+
let oauthFallbackTimer = null;
|
|
2347
2362
|
|
|
2348
2363
|
function setOauthStep(stepEl, mode) {
|
|
2349
2364
|
if (!stepEl) return;
|
|
@@ -2402,8 +2417,25 @@ function wizardHtml(defaults) {
|
|
|
2402
2417
|
oauthSessionId = null;
|
|
2403
2418
|
}
|
|
2404
2419
|
|
|
2420
|
+
function hideFallbackPaste() {
|
|
2421
|
+
if (oauthFallbackPaste) oauthFallbackPaste.classList.add("hidden");
|
|
2422
|
+
if (oauthFallbackUrlInput) oauthFallbackUrlInput.value = "";
|
|
2423
|
+
if (oauthFallbackError) { oauthFallbackError.textContent = ""; oauthFallbackError.classList.add("hidden"); }
|
|
2424
|
+
if (oauthFallbackTimer) { clearTimeout(oauthFallbackTimer); oauthFallbackTimer = null; }
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
function showFallbackPasteAfterDelay(ms) {
|
|
2428
|
+
hideFallbackPaste();
|
|
2429
|
+
oauthFallbackTimer = setTimeout(() => {
|
|
2430
|
+
if (oauthFallbackPaste && oauthSessionId && oauthOpenedInBrowser && !oauthWizardLoginDone) {
|
|
2431
|
+
oauthFallbackPaste.classList.remove("hidden");
|
|
2432
|
+
}
|
|
2433
|
+
}, ms);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2405
2436
|
function resetOauthWizardState() {
|
|
2406
2437
|
stopOauthPolling();
|
|
2438
|
+
hideFallbackPaste();
|
|
2407
2439
|
oauthSessionId = null;
|
|
2408
2440
|
oauthWizardLoginDone = false;
|
|
2409
2441
|
oauthStartInFlight = false;
|
|
@@ -2526,6 +2558,7 @@ function wizardHtml(defaults) {
|
|
|
2526
2558
|
if (state === "succeeded") {
|
|
2527
2559
|
oauthWizardLoginDone = true;
|
|
2528
2560
|
oauthSessionId = null;
|
|
2561
|
+
hideFallbackPaste();
|
|
2529
2562
|
setOauthStep(oauthStepPrepare, "done");
|
|
2530
2563
|
setOauthStep(oauthStepOpen, "done");
|
|
2531
2564
|
setOauthStep(oauthStepComplete, "done");
|
|
@@ -3068,7 +3101,49 @@ function wizardHtml(defaults) {
|
|
|
3068
3101
|
setOauthStep(oauthStepOpen, "done");
|
|
3069
3102
|
setOauthStep(oauthStepComplete, "active");
|
|
3070
3103
|
setOauthStep(oauthStepVerify, "active");
|
|
3071
|
-
setOauthStatus("Complete ChatGPT approval in this
|
|
3104
|
+
setOauthStatus("Complete ChatGPT approval in this browser, then return here. We detect completion automatically.");
|
|
3105
|
+
showFallbackPasteAfterDelay(15_000);
|
|
3106
|
+
});
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
if (oauthFallbackSubmitBtn) {
|
|
3110
|
+
oauthFallbackSubmitBtn.addEventListener("click", async () => {
|
|
3111
|
+
const raw = (oauthFallbackUrlInput && oauthFallbackUrlInput.value || "").trim();
|
|
3112
|
+
if (!raw || !raw.includes("code=")) {
|
|
3113
|
+
if (oauthFallbackError) {
|
|
3114
|
+
oauthFallbackError.textContent = "Paste the full URL from the browser address bar. It must contain code=…";
|
|
3115
|
+
oauthFallbackError.classList.remove("hidden");
|
|
3116
|
+
}
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
if (oauthFallbackError) oauthFallbackError.classList.add("hidden");
|
|
3120
|
+
oauthFallbackSubmitBtn.disabled = true;
|
|
3121
|
+
oauthFallbackSubmitBtn.textContent = "Submitting…";
|
|
3122
|
+
try {
|
|
3123
|
+
const res = await fetch("/api/llm/oauth/submit-callback-url", {
|
|
3124
|
+
method: "POST",
|
|
3125
|
+
headers: { "content-type": "application/json" },
|
|
3126
|
+
body: JSON.stringify({ sessionId: oauthSessionId, callbackUrl: raw }),
|
|
3127
|
+
});
|
|
3128
|
+
const data = await res.json().catch(() => ({}));
|
|
3129
|
+
if (res.ok && data.ok) {
|
|
3130
|
+
hideFallbackPaste();
|
|
3131
|
+
setOauthStatus("Callback received! Waiting for OpenClaw to finish…", false);
|
|
3132
|
+
} else {
|
|
3133
|
+
if (oauthFallbackError) {
|
|
3134
|
+
oauthFallbackError.textContent = data.message || data.error || "Could not submit the URL. Try again.";
|
|
3135
|
+
oauthFallbackError.classList.remove("hidden");
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
} catch (err) {
|
|
3139
|
+
if (oauthFallbackError) {
|
|
3140
|
+
oauthFallbackError.textContent = err.message || "Request failed.";
|
|
3141
|
+
oauthFallbackError.classList.remove("hidden");
|
|
3142
|
+
}
|
|
3143
|
+
} finally {
|
|
3144
|
+
oauthFallbackSubmitBtn.disabled = false;
|
|
3145
|
+
oauthFallbackSubmitBtn.textContent = "Submit URL";
|
|
3146
|
+
}
|
|
3072
3147
|
});
|
|
3073
3148
|
}
|
|
3074
3149
|
|
|
@@ -3182,6 +3257,32 @@ async function cmdInstall(args) {
|
|
|
3182
3257
|
}
|
|
3183
3258
|
}
|
|
3184
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
|
+
|
|
3185
3286
|
function killOauthSession(sessionId, signal = "SIGTERM") {
|
|
3186
3287
|
const s = oauthSessions.get(sessionId);
|
|
3187
3288
|
if (!s) return;
|
|
@@ -3313,6 +3414,13 @@ async function cmdInstall(args) {
|
|
|
3313
3414
|
killOauthSession(id);
|
|
3314
3415
|
}
|
|
3315
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
|
+
|
|
3316
3424
|
const sessionId = randomUUID();
|
|
3317
3425
|
const child = spawnOpenClawCodexAuthLoginChild();
|
|
3318
3426
|
|
|
@@ -3327,6 +3435,7 @@ async function cmdInstall(args) {
|
|
|
3327
3435
|
/* ignore */
|
|
3328
3436
|
}
|
|
3329
3437
|
oauthSessions.delete(sessionId);
|
|
3438
|
+
startCallbackProxy();
|
|
3330
3439
|
respondJson(504, {
|
|
3331
3440
|
ok: false,
|
|
3332
3441
|
error: "oauth_url_timeout",
|
|
@@ -3365,6 +3474,7 @@ async function cmdInstall(args) {
|
|
|
3365
3474
|
});
|
|
3366
3475
|
child.on("error", (err) => {
|
|
3367
3476
|
clearTimeout(urlTimeout);
|
|
3477
|
+
startCallbackProxy();
|
|
3368
3478
|
if (responded) return;
|
|
3369
3479
|
responded = true;
|
|
3370
3480
|
oauthSessions.delete(sessionId);
|
|
@@ -3372,6 +3482,7 @@ async function cmdInstall(args) {
|
|
|
3372
3482
|
});
|
|
3373
3483
|
child.on("close", (code) => {
|
|
3374
3484
|
clearTimeout(urlTimeout);
|
|
3485
|
+
startCallbackProxy();
|
|
3375
3486
|
if (!responded) {
|
|
3376
3487
|
responded = true;
|
|
3377
3488
|
oauthSessions.delete(sessionId);
|
|
@@ -3391,9 +3502,15 @@ async function cmdInstall(args) {
|
|
|
3391
3502
|
pending.updatedAt = Date.now();
|
|
3392
3503
|
pending.exitCode = typeof code === "number" ? code : null;
|
|
3393
3504
|
pending.detail = stripAnsi(combined).slice(-4000);
|
|
3394
|
-
if (code === 0) {
|
|
3505
|
+
if (code === 0 && hasOpenaiCodexAuthTokens()) {
|
|
3395
3506
|
pending.status = "succeeded";
|
|
3396
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.";
|
|
3397
3514
|
} else {
|
|
3398
3515
|
pending.status = "failed";
|
|
3399
3516
|
pending.message =
|
|
@@ -3486,6 +3603,41 @@ async function cmdInstall(args) {
|
|
|
3486
3603
|
return;
|
|
3487
3604
|
}
|
|
3488
3605
|
|
|
3606
|
+
if (req.method === "POST" && req.url === "/api/llm/oauth/submit-callback-url") {
|
|
3607
|
+
const body = await parseJsonBody(req).catch(() => ({}));
|
|
3608
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
|
|
3609
|
+
const callbackUrl = typeof body.callbackUrl === "string" ? body.callbackUrl.trim() : "";
|
|
3610
|
+
if (!callbackUrl || !callbackUrl.includes("code=")) {
|
|
3611
|
+
respondJson(400, { ok: false, error: "invalid_url", message: "URL must contain a code= parameter." });
|
|
3612
|
+
return;
|
|
3613
|
+
}
|
|
3614
|
+
const s = sessionId ? oauthSessions.get(sessionId) : findActiveOauthSession();
|
|
3615
|
+
if (!s || !s.child) {
|
|
3616
|
+
respondJson(404, { ok: false, error: "no_active_session", message: "No active OAuth session. Click Try sign-in again." });
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
try {
|
|
3620
|
+
if (s.child.stdin && !s.child.stdin.writableEnded && !s.child.stdin.destroyed) {
|
|
3621
|
+
s.child.stdin.write(`${callbackUrl}\n`, () => {
|
|
3622
|
+
setTimeout(() => {
|
|
3623
|
+
try {
|
|
3624
|
+
if (s.child && s.child.stdin && !s.child.stdin.destroyed && !s.child.stdin.writableEnded) {
|
|
3625
|
+
s.child.stdin.end();
|
|
3626
|
+
}
|
|
3627
|
+
} catch { /* ignore */ }
|
|
3628
|
+
}, 100);
|
|
3629
|
+
});
|
|
3630
|
+
s.updatedAt = Date.now();
|
|
3631
|
+
respondJson(200, { ok: true, message: "Callback URL submitted. Waiting for OpenClaw to complete…" });
|
|
3632
|
+
} else {
|
|
3633
|
+
respondJson(500, { ok: false, error: "stdin_closed", message: "OpenClaw process stdin is closed. Click Try sign-in again." });
|
|
3634
|
+
}
|
|
3635
|
+
} catch (err) {
|
|
3636
|
+
respondJson(500, { ok: false, error: "write_error", message: err.message || "Failed to write to OpenClaw." });
|
|
3637
|
+
}
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3489
3641
|
if (req.method === "POST" && req.url === "/api/llm/oauth/cancel") {
|
|
3490
3642
|
const body = await parseJsonBody(req).catch(() => ({}));
|
|
3491
3643
|
const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "traderclaw-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
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.79"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"traderclaw",
|