traderclaw-cli 1.0.117 → 1.0.120

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.
@@ -284,6 +284,47 @@ function getCommandOutput(cmd, { timeoutMs = 0 } = {}) {
284
284
  }
285
285
  }
286
286
 
287
+ /**
288
+ * First existing `skills/solana-trader` directory: local package → OpenClaw extension → global npm.
289
+ * @param {{ pluginId: string, pluginPackage: string }} modeConfig
290
+ * @returns {string|null}
291
+ */
292
+ export function resolveSolanaTraderPackagedRoot(modeConfig) {
293
+ const candidates = [
294
+ join(PLUGIN_PACKAGE_ROOT, "skills", "solana-trader"),
295
+ join(homedir(), ".openclaw", "extensions", modeConfig.pluginId, "skills", "solana-trader"),
296
+ ];
297
+ const npmRoot = getCommandOutput("npm root -g");
298
+ if (npmRoot) {
299
+ candidates.push(join(npmRoot, modeConfig.pluginPackage, "skills", "solana-trader"));
300
+ }
301
+ for (const dir of candidates) {
302
+ if (existsSync(dir)) return dir;
303
+ }
304
+ return null;
305
+ }
306
+
307
+ /**
308
+ * First existing gateway template file under `config/{filename}`.
309
+ * @param {{ pluginId: string, pluginPackage: string }} modeConfig
310
+ * @param {string} gatewayConfigFilename
311
+ * @returns {string|null}
312
+ */
313
+ function resolveGatewayConfigSourcePath(modeConfig, gatewayConfigFilename) {
314
+ const candidates = [
315
+ join(PLUGIN_PACKAGE_ROOT, "config", gatewayConfigFilename),
316
+ join(homedir(), ".openclaw", "extensions", modeConfig.pluginId, "config", gatewayConfigFilename),
317
+ ];
318
+ const npmRoot = getCommandOutput("npm root -g");
319
+ if (npmRoot) {
320
+ candidates.push(join(npmRoot, modeConfig.pluginPackage, "config", gatewayConfigFilename));
321
+ }
322
+ for (const p of candidates) {
323
+ if (existsSync(p)) return p;
324
+ }
325
+ return null;
326
+ }
327
+
287
328
  function extractUrls(text = "") {
288
329
  const matches = text.match(/https?:\/\/[^\s"')]+/g);
289
330
  return matches ? [...new Set(matches)] : [];
@@ -757,6 +798,31 @@ async function installAndEnableOpenClawPlugin(modeConfig, onEvent, orchestratorU
757
798
  };
758
799
  }
759
800
 
801
+ /**
802
+ * Idempotent: ensure OpenClaw discovers skills under ~/.openclaw/extensions/<pluginId>/skills (extraDirs).
803
+ * See OpenClaw workspace skill loader: config.skills.load.extraDirs → openclaw-extra.
804
+ * @param {Record<string, unknown>} config
805
+ * @param {string} pluginId
806
+ */
807
+ function ensureTraderSkillsExtraDir(config, pluginId) {
808
+ const marker = `.openclaw/extensions/${pluginId}/skills`;
809
+ const tildeEntry = `~/.openclaw/extensions/${pluginId}/skills`;
810
+ if (!config.skills || typeof config.skills !== "object") config.skills = {};
811
+ if (!config.skills.load || typeof config.skills.load !== "object") config.skills.load = {};
812
+ const raw = config.skills.load.extraDirs;
813
+ const dirs = Array.isArray(raw) ? [...raw] : [];
814
+ const normalized = (d) => (typeof d === "string" ? d.replace(/\\/g, "/") : "");
815
+ const needle = normalized(tildeEntry);
816
+ const hasMarker = dirs.some((d) => {
817
+ const n = normalized(d);
818
+ return n.includes(marker) || n === needle;
819
+ });
820
+ if (!hasMarker) {
821
+ dirs.push(tildeEntry);
822
+ config.skills.load.extraDirs = dirs;
823
+ }
824
+ }
825
+
760
826
  function seedPluginConfig(modeConfig, orchestratorUrl, configPath = CONFIG_FILE) {
761
827
  const defaultUrl = orchestratorUrl || "https://api.traderclaw.ai";
762
828
 
@@ -794,6 +860,8 @@ function seedPluginConfig(modeConfig, orchestratorUrl, configPath = CONFIG_FILE)
794
860
 
795
861
  mergeOrchestratorForId(modeConfig.pluginId);
796
862
 
863
+ ensureTraderSkillsExtraDir(config, modeConfig.pluginId);
864
+
797
865
  // Do not set plugins.allow here: OpenClaw validates allow[] against the plugin registry, and
798
866
  // the id is not registered until after `openclaw plugins install`. Pre-seeding allow caused:
799
867
  // "plugins.allow: plugin not found: <id>".
@@ -1316,10 +1384,8 @@ function deployGatewayConfig(modeConfig) {
1316
1384
  const gatewayDir = join(CONFIG_DIR, "gateway");
1317
1385
  mkdirSync(gatewayDir, { recursive: true });
1318
1386
  const destFile = join(gatewayDir, modeConfig.gatewayConfig);
1319
- const npmRoot = getCommandOutput("npm root -g");
1320
- if (!npmRoot) return { deployed: false, dest: destFile };
1321
- const src = join(npmRoot, modeConfig.pluginPackage, "config", modeConfig.gatewayConfig);
1322
- if (!existsSync(src)) return { deployed: false, dest: destFile };
1387
+ const src = resolveGatewayConfigSourcePath(modeConfig, modeConfig.gatewayConfig);
1388
+ if (!src) return { deployed: false, dest: destFile };
1323
1389
  writeFileSync(destFile, readFileSync(src));
1324
1390
  return { deployed: true, source: src, dest: destFile };
1325
1391
  }
@@ -1356,13 +1422,13 @@ export function resolveAgentWorkspaceDir(configPath = CONFIG_FILE) {
1356
1422
  }
1357
1423
 
1358
1424
  /**
1359
- * Copy skills/solana-trader/HEARTBEAT.md from the globally installed npm package into the workspace root.
1425
+ * Copy skills/solana-trader/HEARTBEAT.md from the plugin package, OpenClaw extension, or global npm into the workspace root.
1360
1426
  * Skips overwrite if a non-empty file already exists (user may have customized it).
1361
1427
  */
1362
1428
  export function deployWorkspaceHeartbeat(modeConfig) {
1363
- const npmRoot = getCommandOutput("npm root -g");
1364
- if (!npmRoot) return { deployed: false, reason: "npm_root_g_failed" };
1365
- const src = join(npmRoot, modeConfig.pluginPackage, "skills", "solana-trader", "HEARTBEAT.md");
1429
+ const skillRoot = resolveSolanaTraderPackagedRoot(modeConfig);
1430
+ if (!skillRoot) return { deployed: false, reason: "source_missing" };
1431
+ const src = join(skillRoot, "HEARTBEAT.md");
1366
1432
  if (!existsSync(src)) return { deployed: false, reason: "source_missing", src };
1367
1433
 
1368
1434
  const workspaceDir = resolveAgentWorkspaceDir(CONFIG_FILE);
@@ -1389,10 +1455,10 @@ export function deployWorkspaceHeartbeat(modeConfig) {
1389
1455
  * Skips files that already exist and are non-empty so user customisations are preserved.
1390
1456
  */
1391
1457
  export function deployWorkspaceBootstrapFiles(modeConfig) {
1392
- const npmRoot = getCommandOutput("npm root -g");
1393
- if (!npmRoot) return { deployed: [], skipped: [], failed: [], reason: "npm_root_g_failed" };
1458
+ const skillRoot = resolveSolanaTraderPackagedRoot(modeConfig);
1459
+ if (!skillRoot) return { deployed: [], skipped: [], failed: [], reason: "source_dir_missing" };
1394
1460
 
1395
- const srcDir = join(npmRoot, modeConfig.pluginPackage, "skills", "solana-trader", "workspace");
1461
+ const srcDir = join(skillRoot, "workspace");
1396
1462
  if (!existsSync(srcDir)) return { deployed: [], skipped: [], failed: [], reason: "source_dir_missing", srcDir };
1397
1463
 
1398
1464
  const workspaceDir = resolveAgentWorkspaceDir(CONFIG_FILE);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traderclaw-cli",
3
- "version": "1.0.117",
3
+ "version": "1.0.120",
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.117"
20
+ "solana-traderclaw": "^1.0.120"
21
21
  },
22
22
  "keywords": [
23
23
  "traderclaw",