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.
@@ -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: "Memory engine (builtin, vector + FTS)",
1881
- ok: true,
1882
- note: "run `openclaw memory status --deep` to verify index",
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-beta.0",
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-beta.0"
20
+ "solana-traderclaw": "^1.0.93"
21
21
  },
22
22
  "keywords": [
23
23
  "traderclaw",