traderclaw-cli 1.0.93-beta.0 → 1.0.93
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 +127 -36
- package/package.json +2 -2
|
@@ -530,6 +530,42 @@ async function installOpenClawPlatform() {
|
|
|
530
530
|
};
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Check whether the OpenClaw CLI has any devices stuck in a pending-approval or
|
|
535
|
+
* repair state. This can happen when the gateway version >= 1.0.93-beta.0 starts
|
|
536
|
+
* treating every CLI invocation as a "device" that must be explicitly approved
|
|
537
|
+
* before it gets operator-write scope. Without that scope all trading RPCs fail
|
|
538
|
+
* silently (read-only).
|
|
539
|
+
*
|
|
540
|
+
* Returns:
|
|
541
|
+
* { ran: false } – openclaw not on PATH or devices subcommand not supported
|
|
542
|
+
* { ran: true, pendingIds: string[], – list ran OK; ids needing approval
|
|
543
|
+
* repairDetected: boolean, – current device is in repair/read-only state
|
|
544
|
+
* envTokenSet: boolean } – OPENCLAW_GATEWAY_TOKEN env var already present (fallback)
|
|
545
|
+
*/
|
|
546
|
+
function checkOpenClawDeviceApproval() {
|
|
547
|
+
if (!commandExists("openclaw")) return { ran: false };
|
|
548
|
+
const raw = getCommandOutput("openclaw devices list");
|
|
549
|
+
if (!raw) return { ran: false };
|
|
550
|
+
|
|
551
|
+
const lower = raw.toLowerCase();
|
|
552
|
+
const envTokenSet = !!process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
553
|
+
|
|
554
|
+
// Detect devices that are waiting for approval ("pending" requestId lines).
|
|
555
|
+
const pendingIds = [];
|
|
556
|
+
for (const line of raw.split("\n")) {
|
|
557
|
+
// Lines typically look like: d4fcdbe8-5176-422b-... pending
|
|
558
|
+
const m = line.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
|
|
559
|
+
if (m && (line.toLowerCase().includes("pending") || line.toLowerCase().includes("repair"))) {
|
|
560
|
+
pendingIds.push(m[1]);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const repairDetected = lower.includes("repair");
|
|
565
|
+
|
|
566
|
+
return { ran: true, pendingIds, repairDetected, envTokenSet, raw };
|
|
567
|
+
}
|
|
568
|
+
|
|
533
569
|
function isNpmGlobalBinConflict(err, cliName) {
|
|
534
570
|
const text = `${err?.message || ""}\n${err?.stderr || ""}\n${err?.stdout || ""}`.toLowerCase();
|
|
535
571
|
return (
|
|
@@ -1135,39 +1171,6 @@ function configureGatewayScheduling(modeConfig, configPath = CONFIG_FILE) {
|
|
|
1135
1171
|
}
|
|
1136
1172
|
config.agents.defaults.heartbeat = { ...defaultHeartbeat };
|
|
1137
1173
|
|
|
1138
|
-
if (!config.agents.defaults.memorySearch || typeof config.agents.defaults.memorySearch !== "object") {
|
|
1139
|
-
config.agents.defaults.memorySearch = {
|
|
1140
|
-
provider: "openai",
|
|
1141
|
-
model: "text-embedding-3-small",
|
|
1142
|
-
query: {
|
|
1143
|
-
hybrid: true,
|
|
1144
|
-
mmr: { enabled: true, lambda: 0.5 },
|
|
1145
|
-
temporalDecay: { enabled: true, halfLifeDays: 14 },
|
|
1146
|
-
},
|
|
1147
|
-
};
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
if (!config.agents.defaults.contextPruning || typeof config.agents.defaults.contextPruning !== "object") {
|
|
1151
|
-
config.agents.defaults.contextPruning = { cacheTtl: "1h" };
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
if (!config.env || typeof config.env !== "object") config.env = {};
|
|
1155
|
-
if (process.env.OPENAI_API_KEY && !config.env.OPENAI_API_KEY) {
|
|
1156
|
-
// Literal env reference (not the value). The gateway expands ${OPENAI_API_KEY}
|
|
1157
|
-
// at runtime from its process environment. Keeps the secret out of the
|
|
1158
|
-
// config file on disk.
|
|
1159
|
-
config.env.OPENAI_API_KEY = "${OPENAI_API_KEY}";
|
|
1160
|
-
} else if (!process.env.OPENAI_API_KEY && !config.env.OPENAI_API_KEY && typeof console !== "undefined") {
|
|
1161
|
-
console.warn(
|
|
1162
|
-
"[traderclaw] OPENAI_API_KEY not detected in the installer shell. " +
|
|
1163
|
-
"Memory embeddings require it for vector search (text-embedding-3-small, ~$0.02/M tokens).\n" +
|
|
1164
|
-
"Set it and re-run the installer, or add the env ref manually:\n" +
|
|
1165
|
-
" 1) export OPENAI_API_KEY=sk-...\n" +
|
|
1166
|
-
" 2) edit ~/.openclaw/openclaw.json and set env.OPENAI_API_KEY to \"${OPENAI_API_KEY}\"\n" +
|
|
1167
|
-
" 3) openclaw gateway restart"
|
|
1168
|
-
);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
1174
|
ensureAgentsDefaultsSchemaCompat(config);
|
|
1172
1175
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1173
1176
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
@@ -1175,6 +1178,21 @@ function configureGatewayScheduling(modeConfig, configPath = CONFIG_FILE) {
|
|
|
1175
1178
|
const cronStorePath = resolveCronJobsStorePath(config);
|
|
1176
1179
|
const cronMerge = mergeTraderCronJobsIntoStore(cronStorePath, targetJobs);
|
|
1177
1180
|
|
|
1181
|
+
let qmdAvailable = false;
|
|
1182
|
+
let qmdVersion = null;
|
|
1183
|
+
try { qmdAvailable = commandExists("qmd"); } catch {}
|
|
1184
|
+
if (qmdAvailable) {
|
|
1185
|
+
qmdVersion = getCommandOutput("qmd --version");
|
|
1186
|
+
} else {
|
|
1187
|
+
if (typeof console !== "undefined") {
|
|
1188
|
+
console.warn(
|
|
1189
|
+
"[traderclaw] QMD binary not found. Memory engine will fall back to SQLite (no vector search, no temporal decay, no MMR).\n" +
|
|
1190
|
+
"Install QMD: npm install -g @tobilu/qmd\n" +
|
|
1191
|
+
"Then restart the gateway: openclaw gateway restart"
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1178
1196
|
return {
|
|
1179
1197
|
configPath,
|
|
1180
1198
|
agentsConfigured: targetAgents.length,
|
|
@@ -1186,6 +1204,8 @@ function configureGatewayScheduling(modeConfig, configPath = CONFIG_FILE) {
|
|
|
1186
1204
|
cronJobsStoreError: cronMerge.error,
|
|
1187
1205
|
removedLegacyCronJobs,
|
|
1188
1206
|
hooksConfigured: config.hooks.mappings.length,
|
|
1207
|
+
qmdAvailable,
|
|
1208
|
+
qmdVersion,
|
|
1189
1209
|
isV2,
|
|
1190
1210
|
};
|
|
1191
1211
|
}
|
|
@@ -1877,9 +1897,9 @@ function verifyInstallation(modeConfig, apiKey) {
|
|
|
1877
1897
|
note: heartbeatInWorkspace ? workspaceRoot : `expected ${join(workspaceRoot, "HEARTBEAT.md")}`,
|
|
1878
1898
|
},
|
|
1879
1899
|
{
|
|
1880
|
-
label: "
|
|
1881
|
-
ok:
|
|
1882
|
-
note: "
|
|
1900
|
+
label: "QMD memory engine (vector search)",
|
|
1901
|
+
ok: commandExists("qmd"),
|
|
1902
|
+
note: "not installed — memory uses keyword search only. Install: npm install -g @tobilu/qmd",
|
|
1883
1903
|
},
|
|
1884
1904
|
];
|
|
1885
1905
|
}
|
|
@@ -2363,6 +2383,40 @@ export class InstallerStepEngine {
|
|
|
2363
2383
|
if (!this.options.skipInstallOpenClaw) {
|
|
2364
2384
|
await this.runStep("install_openclaw", "Installing or upgrading OpenClaw platform", async () => installOpenClawPlatform());
|
|
2365
2385
|
}
|
|
2386
|
+
|
|
2387
|
+
// Non-fatal: warn when the CLI has devices in pending-approval or repair state.
|
|
2388
|
+
// Gateway >= 1.0.93-beta.0 requires explicit device approval for operator-write scope;
|
|
2389
|
+
// without it, agent trading RPCs silently fail (device gets read-only "repair" state).
|
|
2390
|
+
await this.runStep("device_approval_check", "Checking OpenClaw device approval status", async () => {
|
|
2391
|
+
const check = checkOpenClawDeviceApproval();
|
|
2392
|
+
if (!check.ran) {
|
|
2393
|
+
this.emitLog("device_approval_check", "info", "Device approval check skipped (openclaw CLI not available or devices subcommand not supported).");
|
|
2394
|
+
return { ran: false };
|
|
2395
|
+
}
|
|
2396
|
+
const needsAction = check.pendingIds.length > 0 || check.repairDetected;
|
|
2397
|
+
if (!needsAction) {
|
|
2398
|
+
this.emitLog("device_approval_check", "info", "No pending or repair-state devices found. Device approval OK.");
|
|
2399
|
+
return { ran: true, ok: true };
|
|
2400
|
+
}
|
|
2401
|
+
const lines = [
|
|
2402
|
+
"ACTION REQUIRED — OpenClaw device approval needed.",
|
|
2403
|
+
"The gateway requires explicit device approval for operator-write scope.",
|
|
2404
|
+
"Without it, trading RPCs will fail silently (read-only / repair state).",
|
|
2405
|
+
"",
|
|
2406
|
+
"Run in your VPS shell:",
|
|
2407
|
+
" openclaw devices list",
|
|
2408
|
+
...(check.pendingIds.length > 0
|
|
2409
|
+
? check.pendingIds.map((id) => ` openclaw devices approve ${id}`)
|
|
2410
|
+
: [" openclaw devices approve <requestId> # use the id shown above"]),
|
|
2411
|
+
"",
|
|
2412
|
+
check.envTokenSet
|
|
2413
|
+
? "OPENCLAW_GATEWAY_TOKEN env var is already set — env-first auth will work as a fallback."
|
|
2414
|
+
: "Optionally set: export OPENCLAW_GATEWAY_TOKEN=\"<token>\" # bypasses device auth entirely",
|
|
2415
|
+
];
|
|
2416
|
+
this.emitLog("device_approval_check", "warn", lines.join("\n"));
|
|
2417
|
+
return { ran: true, ok: false, pendingIds: check.pendingIds, repairDetected: check.repairDetected, envTokenSet: check.envTokenSet };
|
|
2418
|
+
});
|
|
2419
|
+
|
|
2366
2420
|
await this.runStep("configure_llm", "Configuring required OpenClaw LLM provider", async () => this.configureLlmStep());
|
|
2367
2421
|
if (!this.options.skipInstallPlugin) {
|
|
2368
2422
|
await this.runStep("install_plugin_package", "Installing TraderClaw CLI package", async () =>
|
|
@@ -2373,6 +2427,32 @@ export class InstallerStepEngine {
|
|
|
2373
2427
|
await this.runStep("openclaw_global_deps", "Ensuring OpenClaw global package dependencies", async () =>
|
|
2374
2428
|
ensureOpenClawGlobalPackageDependencies(),
|
|
2375
2429
|
);
|
|
2430
|
+
await this.runStep("install_qmd", "Installing QMD memory engine (vector search)", async () => {
|
|
2431
|
+
if (commandExists("qmd")) {
|
|
2432
|
+
const ver = getCommandOutput("qmd --version");
|
|
2433
|
+
this.emitLog("install_qmd", "info", `QMD already installed: ${ver}`);
|
|
2434
|
+
return { alreadyInstalled: true, version: ver };
|
|
2435
|
+
}
|
|
2436
|
+
this.emitLog("install_qmd", "info", "Installing @tobilu/qmd globally for vector search memory...");
|
|
2437
|
+
try {
|
|
2438
|
+
await runCommandWithEvents("npm", ["install", "-g", "--ignore-scripts", "--registry", "https://registry.npmjs.org/", "@tobilu/qmd"], {
|
|
2439
|
+
onEvent: (evt) => this.emitLog("install_qmd", evt.type === "stderr" ? "warn" : "info", evt.text, evt.urls || []),
|
|
2440
|
+
});
|
|
2441
|
+
} catch (err) {
|
|
2442
|
+
this.emitLog(
|
|
2443
|
+
"install_qmd",
|
|
2444
|
+
"warn",
|
|
2445
|
+
`QMD install failed (non-fatal): ${err?.message || err}. Memory will use keyword search only. You can install manually later: npm install -g @tobilu/qmd`,
|
|
2446
|
+
);
|
|
2447
|
+
return { installed: false, error: err?.message || String(err) };
|
|
2448
|
+
}
|
|
2449
|
+
const available = commandExists("qmd");
|
|
2450
|
+
const ver = available ? getCommandOutput("qmd --version") : null;
|
|
2451
|
+
if (!available) {
|
|
2452
|
+
this.emitLog("install_qmd", "warn", "QMD installed but not on PATH. Memory will use keyword search only.");
|
|
2453
|
+
}
|
|
2454
|
+
return { installed: available, version: ver };
|
|
2455
|
+
});
|
|
2376
2456
|
await this.runStep(
|
|
2377
2457
|
"activate_openclaw_plugin",
|
|
2378
2458
|
"Installing and enabling TraderClaw inside OpenClaw",
|
|
@@ -2480,6 +2560,17 @@ export class InstallerStepEngine {
|
|
|
2480
2560
|
this.emitLog("gateway_scheduling", "warn", "Removed legacy 'cron.jobs' from openclaw.json to keep config validation compatible.");
|
|
2481
2561
|
}
|
|
2482
2562
|
this.emitLog("gateway_scheduling", "info", `Webhook hooks: ${result.hooksConfigured}`);
|
|
2563
|
+
if (!result.qmdAvailable) {
|
|
2564
|
+
this.emitLog(
|
|
2565
|
+
"gateway_scheduling",
|
|
2566
|
+
"warn",
|
|
2567
|
+
"QMD binary not found — memory will use SQLite keyword search only (no vector search, no temporal decay, no MMR). " +
|
|
2568
|
+
"Vector search makes the agent's memory significantly more effective. " +
|
|
2569
|
+
"Install: npm install -g @tobilu/qmd — then restart the gateway: openclaw gateway restart",
|
|
2570
|
+
);
|
|
2571
|
+
} else {
|
|
2572
|
+
this.emitLog("gateway_scheduling", "info", `QMD memory engine: ${result.qmdVersion || "installed"}`);
|
|
2573
|
+
}
|
|
2483
2574
|
const restart = await restartGateway();
|
|
2484
2575
|
return { ...result, restart };
|
|
2485
2576
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "traderclaw-cli",
|
|
3
|
-
"version": "1.0.93
|
|
3
|
+
"version": "1.0.93",
|
|
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.93
|
|
20
|
+
"solana-traderclaw": "^1.0.93"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"traderclaw",
|