@clawlabz/clawnetwork 0.1.16 → 0.1.18

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/index.ts CHANGED
@@ -7,7 +7,7 @@ declare function setInterval(fn: () => void, ms: number): unknown
7
7
  declare function clearInterval(id: unknown): void
8
8
  declare function fetch(url: string, init?: Record<string, unknown>): Promise<{ status: number; ok: boolean; text: () => Promise<string>; json: () => Promise<unknown> }>
9
9
 
10
- const VERSION = '0.1.0'
10
+ const VERSION = '0.1.18'
11
11
  const PLUGIN_ID = 'clawnetwork'
12
12
  const GITHUB_REPO = 'clawlabz/claw-network'
13
13
  const DEFAULT_RPC_PORT = 9710
@@ -123,16 +123,23 @@ const path = require('path')
123
123
  const fs = require('fs')
124
124
  const { execFileSync, spawn: nodeSpawn, fork } = require('child_process')
125
125
 
126
+ function getBaseDir(): string {
127
+ // OPENCLAW_DIR env var takes precedence (supports named profiles like ~/.openclaw-myprofile)
128
+ const envDir = process.env.OPENCLAW_DIR
129
+ if (envDir) return envDir
130
+ return path.join(os.homedir(), '.openclaw')
131
+ }
132
+
126
133
  function homePath(...segments: string[]): string {
127
134
  return path.join(os.homedir(), ...segments)
128
135
  }
129
136
 
130
- const WORKSPACE_DIR = homePath('.openclaw', 'workspace', 'clawnetwork')
131
- const BIN_DIR = homePath('.openclaw', 'bin')
137
+ const WORKSPACE_DIR = path.join(getBaseDir(), 'workspace', 'clawnetwork')
138
+ const BIN_DIR = path.join(getBaseDir(), 'bin')
132
139
  const DATA_DIR = homePath('.clawnetwork')
133
140
  const WALLET_PATH = path.join(WORKSPACE_DIR, 'wallet.json')
134
141
  const LOG_PATH = path.join(WORKSPACE_DIR, 'node.log')
135
- const UI_PORT_FILE = homePath('.openclaw', 'clawnetwork-ui-port')
142
+ const UI_PORT_FILE = path.join(getBaseDir(), 'clawnetwork-ui-port')
136
143
 
137
144
  function ensureDir(dir: string): void {
138
145
  fs.mkdirSync(dir, { recursive: true })
@@ -475,7 +482,7 @@ let nodeStartedAt: number | null = null
475
482
  let lastHealth: { blockHeight: number | null; peerCount: number | null; syncing: boolean } = { blockHeight: null, peerCount: null, syncing: false }
476
483
  let cachedBinaryVersion: string | null = null
477
484
 
478
- function isNodeRunning(): { running: boolean; pid: number | null } {
485
+ function isNodeRunning(rpcPort: number = DEFAULT_RPC_PORT): { running: boolean; pid: number | null } {
479
486
  // Check in-memory process first
480
487
  if (nodeProcess && !nodeProcess.killed) return { running: true, pid: nodeProcess.pid }
481
488
  // Check PID file (for detached processes from previous CLI invocations)
@@ -483,17 +490,24 @@ function isNodeRunning(): { running: boolean; pid: number | null } {
483
490
  try {
484
491
  const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10)
485
492
  if (pid > 0) {
486
- try { execFileSync('kill', ['-0', String(pid)], { timeout: 2000 }); return { running: true, pid } } catch { /* dead */ }
493
+ try { execFileSync('kill', ['-0', String(pid)], { timeout: 2000 }); return { running: true, pid } } catch {
494
+ // PID file exists but process is dead — clean up stale PID file
495
+ try { fs.unlinkSync(pidFile) } catch { /* ok */ }
496
+ }
487
497
  }
488
498
  } catch { /* no file */ }
489
499
  // Last resort: check if RPC port is responding (covers orphaned processes)
490
500
  try {
491
- execFileSync('curl', ['-sf', '--max-time', '1', 'http://localhost:9710/health'], { timeout: 3000, encoding: 'utf8' })
501
+ execFileSync('curl', ['-sf', '--max-time', '1', `http://localhost:${rpcPort}/health`], { timeout: 3000, encoding: 'utf8' })
492
502
  // Port is responding — find PID by port
493
503
  try {
494
- const lsof = execFileSync('lsof', ['-ti', ':9710'], { timeout: 3000, encoding: 'utf8' }).trim()
504
+ const lsof = execFileSync('lsof', ['-ti', `:${rpcPort}`], { timeout: 3000, encoding: 'utf8' }).trim()
495
505
  const pid = parseInt(lsof.split('\n')[0], 10)
496
- if (pid > 0) return { running: true, pid }
506
+ if (pid > 0) {
507
+ // Write recovered PID to file for future management
508
+ try { fs.writeFileSync(pidFile, String(pid)) } catch { /* ok */ }
509
+ return { running: true, pid }
510
+ }
497
511
  } catch { /* ok */ }
498
512
  return { running: true, pid: null }
499
513
  } catch { /* not responding */ }
@@ -502,7 +516,7 @@ function isNodeRunning(): { running: boolean; pid: number | null } {
502
516
 
503
517
  function buildStatus(cfg: PluginConfig): NodeStatus {
504
518
  const wallet = loadWallet()
505
- const nodeState = isNodeRunning()
519
+ const nodeState = isNodeRunning(cfg.rpcPort)
506
520
  const uptime = nodeStartedAt ? Math.floor((Date.now() - nodeStartedAt) / 1000) : null
507
521
  return {
508
522
  running: nodeState.running,
@@ -557,7 +571,7 @@ function startNodeProcess(binaryPath: string, cfg: PluginConfig, api: OpenClawAp
557
571
  api.logger?.warn?.('[clawnetwork] node already running (in-memory)')
558
572
  return
559
573
  }
560
- const existingState = isNodeRunning()
574
+ const existingState = isNodeRunning(cfg.rpcPort)
561
575
  if (existingState.running) {
562
576
  api.logger?.info?.(`[clawnetwork] node already running (pid=${existingState.pid}), skipping start`)
563
577
  return
@@ -678,8 +692,10 @@ function stopNode(api: OpenClawApi): void {
678
692
  const stopFile = path.join(WORKSPACE_DIR, 'stop.signal')
679
693
  try { fs.writeFileSync(stopFile, String(Date.now())) } catch { /* ok */ }
680
694
 
681
- // Also kill any claw-node processes by name (covers orphans from restart loops)
682
- try { execFileSync('pkill', ['-f', 'claw-node start'], { timeout: 3000 }) } catch { /* ok */ }
695
+ // Fallback: only use pkill if we had no PID to target (avoids killing other profiles' nodes)
696
+ if (!pid) {
697
+ try { execFileSync('pkill', ['-f', 'claw-node start'], { timeout: 3000 }) } catch { /* ok */ }
698
+ }
683
699
 
684
700
  nodeProcess = null
685
701
  nodeStartedAt = null
@@ -1291,7 +1307,7 @@ function buildUiHtml(cfg: PluginConfig): string {
1291
1307
  document.getElementById('lastUpdate').textContent = 'Updated: ' + new Date().toLocaleTimeString();
1292
1308
  } catch (e) {
1293
1309
  console.error(e);
1294
- renderStatus({ running: false, blockHeight: null, peerCount: null, walletAddress: '', network: 'mainnet', syncMode: 'light', rpcUrl: 'http://localhost:19877', pluginVersion: '0.1.1', restartCount: 0, dataDir: '', balance: '', syncing: false, uptimeFormatted: '—', pid: null });
1310
+ renderStatus({ running: false, blockHeight: null, peerCount: null, walletAddress: '', network: 'mainnet', syncMode: 'light', rpcUrl: 'http://localhost:19877', pluginVersion: '${VERSION}', restartCount: 0, dataDir: '', balance: '', syncing: false, uptimeFormatted: '—', pid: null });
1295
1311
  }
1296
1312
  }
1297
1313
 
@@ -1450,10 +1466,22 @@ const fs = require('fs');
1450
1466
  const os = require('os');
1451
1467
  const path = require('path');
1452
1468
 
1469
+ // OPENCLAW_BASE_DIR and PLUGIN_VERSION are injected as const by startUiServer() prepend.
1470
+ // Use global lookup to avoid const/var redeclaration conflict.
1471
+ const _BASE = (typeof OPENCLAW_BASE_DIR !== 'undefined') ? OPENCLAW_BASE_DIR : path.join(os.homedir(), '.openclaw');
1472
+ const _PVER = (typeof PLUGIN_VERSION !== 'undefined') ? PLUGIN_VERSION : 'unknown';
1473
+ const OC_WORKSPACE = path.join(_BASE, 'workspace', 'clawnetwork');
1474
+ const OC_BIN_DIR = path.join(_BASE, 'bin');
1475
+ const OC_WALLET_PATH = path.join(OC_WORKSPACE, 'wallet.json');
1476
+ const OC_PID_FILE = path.join(OC_WORKSPACE, 'node.pid');
1477
+ const OC_CONFIG_PATH = path.join(OC_WORKSPACE, 'config.json');
1478
+ const OC_STOP_SIGNAL = path.join(OC_WORKSPACE, 'stop.signal');
1479
+ const OC_LOG_PATH_DEFAULT = path.join(OC_WORKSPACE, 'node.log');
1480
+
1453
1481
  const PORT = parseInt(process.argv[2] || '19877', 10);
1454
1482
  const RPC_PORT = parseInt(process.argv[3] || '9710', 10);
1455
- const LOG_PATH = process.argv[4] || path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.log');
1456
- const PORT_FILE = path.join(os.homedir(), '.openclaw/clawnetwork-ui-port');
1483
+ const LOG_PATH = process.argv[4] || OC_LOG_PATH_DEFAULT;
1484
+ const PORT_FILE = path.join(_BASE, 'clawnetwork-ui-port');
1457
1485
  const MAX_RETRIES = 10;
1458
1486
 
1459
1487
  async function fetchJson(url) {
@@ -1492,12 +1520,10 @@ function readBody(req) {
1492
1520
  }
1493
1521
 
1494
1522
  function findNodeBinary() {
1495
- const binDir = path.join(os.homedir(), '.openclaw/bin');
1496
- const dataDir = path.join(os.homedir(), '.clawnetwork');
1497
1523
  const binName = process.platform === 'win32' ? 'claw-node.exe' : 'claw-node';
1498
- let binary = path.join(binDir, binName);
1524
+ let binary = path.join(OC_BIN_DIR, binName);
1499
1525
  if (fs.existsSync(binary)) return binary;
1500
- binary = path.join(dataDir, 'bin', 'claw-node');
1526
+ binary = path.join(os.homedir(), '.clawnetwork', 'bin', 'claw-node');
1501
1527
  if (fs.existsSync(binary)) return binary;
1502
1528
  return null;
1503
1529
  }
@@ -1540,7 +1566,7 @@ async function handle(req, res) {
1540
1566
  }
1541
1567
  } catch {}
1542
1568
  try {
1543
- const walletPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json');
1569
+ const walletPath = OC_WALLET_PATH;
1544
1570
  const w = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
1545
1571
  walletAddress = w.address || '';
1546
1572
  if (w.address) {
@@ -1557,15 +1583,15 @@ async function handle(req, res) {
1557
1583
  rpcUrl: 'http://localhost:' + RPC_PORT,
1558
1584
  walletAddress,
1559
1585
  binaryVersion: h.version,
1560
- pluginVersion: '0.1.1',
1586
+ pluginVersion: _PVER,
1561
1587
  uptime: h.uptime_secs,
1562
1588
  uptimeFormatted: h.uptime_secs < 60 ? h.uptime_secs + 's' : h.uptime_secs < 3600 ? Math.floor(h.uptime_secs/60) + 'm' : Math.floor(h.uptime_secs/3600) + 'h ' + Math.floor((h.uptime_secs%3600)/60) + 'm',
1563
1589
  restartCount: 0, dataDir: path.join(os.homedir(), '.clawnetwork'), balance, agentName, syncing: h.status === 'degraded', peerless: h.peer_count === 0, lastBlockAgeSecs: h.last_block_age_secs,
1564
1590
  upgradeLevel, latestVersion, releaseUrl, changelog, announcement,
1565
1591
  });
1566
1592
  } catch {
1567
- const walletAddr = (() => { try { return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json'), 'utf8')).address; } catch { return ''; } })();
1568
- json(200, { running: false, blockHeight: null, peerCount: null, walletAddress: walletAddr, network: 'mainnet', syncMode: 'light', rpcUrl: 'http://localhost:' + RPC_PORT, pluginVersion: '0.1.1', restartCount: 0, dataDir: path.join(os.homedir(), '.clawnetwork'), balance: '', agentName: '', syncing: false, uptimeFormatted: '—', pid: null, upgradeLevel: 'unknown', latestVersion: '', releaseUrl: '', changelog: '', announcement: null });
1593
+ const walletAddr = (() => { try { return JSON.parse(fs.readFileSync(OC_WALLET_PATH, 'utf8')).address; } catch { return ''; } })();
1594
+ json(200, { running: false, blockHeight: null, peerCount: null, walletAddress: walletAddr, network: 'mainnet', syncMode: 'light', rpcUrl: 'http://localhost:' + RPC_PORT, pluginVersion: _PVER, restartCount: 0, dataDir: path.join(os.homedir(), '.clawnetwork'), balance: '', agentName: '', syncing: false, uptimeFormatted: '—', pid: null, upgradeLevel: 'unknown', latestVersion: '', releaseUrl: '', changelog: '', announcement: null });
1569
1595
  }
1570
1596
  return;
1571
1597
  }
@@ -1585,7 +1611,7 @@ async function handle(req, res) {
1585
1611
  return;
1586
1612
  }
1587
1613
  try {
1588
- const walletPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json');
1614
+ const walletPath = OC_WALLET_PATH;
1589
1615
  const w = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
1590
1616
  json(200, { address: w.address, secretKey: w.secret_key || w.secretKey || w.private_key || '' });
1591
1617
  } catch (e) { json(400, { error: 'No wallet found' }); }
@@ -1594,7 +1620,7 @@ async function handle(req, res) {
1594
1620
  // ── Business API endpoints (mirrors Gateway methods) ──
1595
1621
  if (p === '/api/wallet/balance') {
1596
1622
  try {
1597
- const walletPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json');
1623
+ const walletPath = OC_WALLET_PATH;
1598
1624
  const w = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
1599
1625
  const address = new URL(req.url, 'http://localhost').searchParams.get('address') || w.address;
1600
1626
  const b = await rpcCall('claw_getBalance', [address]);
@@ -1669,7 +1695,7 @@ async function handle(req, res) {
1669
1695
  }
1670
1696
  if (p === '/api/node/config') {
1671
1697
  try {
1672
- const cfgPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/config.json');
1698
+ const cfgPath = OC_CONFIG_PATH;
1673
1699
  const cfg = fs.existsSync(cfgPath) ? JSON.parse(fs.readFileSync(cfgPath, 'utf8')) : {};
1674
1700
  json(200, { ...cfg, rpcPort: RPC_PORT, uiPort: PORT });
1675
1701
  } catch (e) { json(200, { rpcPort: RPC_PORT, uiPort: PORT }); }
@@ -1679,7 +1705,7 @@ async function handle(req, res) {
1679
1705
  const a = p.split('/').pop();
1680
1706
  if (a === 'faucet') {
1681
1707
  try {
1682
- const w = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json'), 'utf8'));
1708
+ const w = JSON.parse(fs.readFileSync(OC_WALLET_PATH, 'utf8'));
1683
1709
  const r = await rpcCall('claw_faucet', [w.address]);
1684
1710
  json(200, { message: 'Faucet success', ...r });
1685
1711
  } catch (e) { json(400, { error: e.message }); }
@@ -1688,7 +1714,7 @@ async function handle(req, res) {
1688
1714
  if (a === 'start') {
1689
1715
  try {
1690
1716
  // Check if already running — try RPC health first (covers stale PID file)
1691
- const pidFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.pid');
1717
+ const pidFile = OC_PID_FILE;
1692
1718
  try {
1693
1719
  const h = await fetchJson('http://localhost:' + RPC_PORT + '/health');
1694
1720
  if (h && (h.status === 'ok' || h.status === 'degraded')) {
@@ -1707,14 +1733,14 @@ async function handle(req, res) {
1707
1733
  if (pid > 0) { try { process.kill(pid, 0); json(200, { message: 'Node already running', pid }); return; } catch {} }
1708
1734
  } catch {}
1709
1735
  // Find binary
1710
- const binDir = path.join(os.homedir(), '.openclaw/bin');
1736
+ const binDir = OC_BIN_DIR;
1711
1737
  const dataDir = path.join(os.homedir(), '.clawnetwork');
1712
1738
  const binName = process.platform === 'win32' ? 'claw-node.exe' : 'claw-node';
1713
1739
  let binary = path.join(binDir, binName);
1714
1740
  if (!fs.existsSync(binary)) { binary = path.join(dataDir, 'bin', 'claw-node'); }
1715
1741
  if (!fs.existsSync(binary)) { json(400, { error: 'claw-node binary not found. Run: openclaw clawnetwork:download' }); return; }
1716
1742
  // Read config for network/ports
1717
- const cfgPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/config.json');
1743
+ const cfgPath = OC_CONFIG_PATH;
1718
1744
  let network = 'mainnet', p2pPort = 9711, syncMode = 'light', extraPeers = [];
1719
1745
  try {
1720
1746
  const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
@@ -1728,7 +1754,7 @@ async function handle(req, res) {
1728
1754
  const args = ['start', '--network', network, '--rpc-port', String(RPC_PORT), '--p2p-port', String(p2pPort), '--sync-mode', syncMode, '--allow-genesis'];
1729
1755
  for (const peer of peers) { args.push('--bootstrap', peer); }
1730
1756
  // Spawn detached
1731
- const logPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.log');
1757
+ const logPath = OC_LOG_PATH_DEFAULT;
1732
1758
  const logFd = fs.openSync(logPath, 'a');
1733
1759
  const { spawn: nodeSpawn } = require('child_process');
1734
1760
  const child = nodeSpawn(binary, args, {
@@ -1740,7 +1766,7 @@ async function handle(req, res) {
1740
1766
  fs.closeSync(logFd);
1741
1767
  fs.writeFileSync(pidFile, String(child.pid));
1742
1768
  // Remove stop signal if exists
1743
- const stopFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/stop.signal');
1769
+ const stopFile = OC_STOP_SIGNAL;
1744
1770
  try { fs.unlinkSync(stopFile); } catch {}
1745
1771
  json(200, { message: 'Node started', pid: child.pid });
1746
1772
  } catch (e) { json(500, { error: e.message }); }
@@ -1748,14 +1774,14 @@ async function handle(req, res) {
1748
1774
  }
1749
1775
  if (a === 'stop') {
1750
1776
  try {
1751
- const pidFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.pid');
1777
+ const pidFile = OC_PID_FILE;
1752
1778
  let pid = null;
1753
1779
  try { pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10); } catch {}
1754
1780
  if (pid && pid > 0) {
1755
1781
  try { process.kill(pid, 'SIGTERM'); } catch {}
1756
1782
  }
1757
1783
  // Write stop signal for restart loop
1758
- const stopFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/stop.signal');
1784
+ const stopFile = OC_STOP_SIGNAL;
1759
1785
  try { fs.writeFileSync(stopFile, String(Date.now())); } catch {}
1760
1786
  // Also kill by name (covers orphans)
1761
1787
  try { require('child_process').execFileSync('pkill', ['-f', 'claw-node start'], { timeout: 3000 }); } catch {}
@@ -1772,12 +1798,12 @@ async function handle(req, res) {
1772
1798
  if (a === 'restart') {
1773
1799
  // Stop, wait, start — all server-side
1774
1800
  try {
1775
- const pidFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.pid');
1801
+ const pidFile = OC_PID_FILE;
1776
1802
  try {
1777
1803
  const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
1778
1804
  if (pid > 0) try { process.kill(pid, 'SIGTERM'); } catch {}
1779
1805
  } catch {}
1780
- const stopFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/stop.signal');
1806
+ const stopFile = OC_STOP_SIGNAL;
1781
1807
  try { fs.writeFileSync(stopFile, String(Date.now())); } catch {}
1782
1808
  try { require('child_process').execFileSync('pkill', ['-f', 'claw-node start'], { timeout: 3000 }); } catch {}
1783
1809
  try { fs.unlinkSync(pidFile); } catch {}
@@ -1788,12 +1814,12 @@ async function handle(req, res) {
1788
1814
  }
1789
1815
  try { fs.unlinkSync(stopFile); } catch {}
1790
1816
  // Now start (reuse start logic inline)
1791
- const binDir = path.join(os.homedir(), '.openclaw/bin');
1817
+ const binDir = OC_BIN_DIR;
1792
1818
  const binName = process.platform === 'win32' ? 'claw-node.exe' : 'claw-node';
1793
1819
  let binary = path.join(binDir, binName);
1794
1820
  if (!fs.existsSync(binary)) { binary = path.join(os.homedir(), '.clawnetwork/bin/claw-node'); }
1795
1821
  if (!fs.existsSync(binary)) { json(400, { error: 'claw-node binary not found' }); return; }
1796
- const cfgPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/config.json');
1822
+ const cfgPath = OC_CONFIG_PATH;
1797
1823
  let network = 'mainnet', p2pPort = 9711, syncMode = 'light', extraPeers = [];
1798
1824
  try {
1799
1825
  const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
@@ -1806,7 +1832,7 @@ async function handle(req, res) {
1806
1832
  const peers = [...(bootstrapPeers[network] || []), ...extraPeers];
1807
1833
  const args = ['start', '--network', network, '--rpc-port', String(RPC_PORT), '--p2p-port', String(p2pPort), '--sync-mode', syncMode, '--allow-genesis'];
1808
1834
  for (const peer of peers) { args.push('--bootstrap', peer); }
1809
- const logPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.log');
1835
+ const logPath = OC_LOG_PATH_DEFAULT;
1810
1836
  const logFd = fs.openSync(logPath, 'a');
1811
1837
  const { spawn: nodeSpawn } = require('child_process');
1812
1838
  const child = nodeSpawn(binary, args, {
@@ -1824,7 +1850,7 @@ async function handle(req, res) {
1824
1850
  if (a === 'upgrade') {
1825
1851
  try {
1826
1852
  // 1. Stop running node
1827
- const pidFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.pid');
1853
+ const pidFile = OC_PID_FILE;
1828
1854
  try {
1829
1855
  const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
1830
1856
  if (pid > 0) try { process.kill(pid, 'SIGTERM'); } catch {}
@@ -1832,7 +1858,7 @@ async function handle(req, res) {
1832
1858
  try { require('child_process').execFileSync('pkill', ['-f', 'claw-node start'], { timeout: 5000 }); } catch {}
1833
1859
 
1834
1860
  // 2. Download latest binary
1835
- const binDir = path.join(os.homedir(), '.openclaw/bin');
1861
+ const binDir = OC_BIN_DIR;
1836
1862
  const binName = process.platform === 'win32' ? 'claw-node.exe' : 'claw-node';
1837
1863
  const target = process.platform === 'darwin'
1838
1864
  ? (process.arch === 'arm64' ? 'macos-aarch64' : 'macos-x86_64')
@@ -1914,8 +1940,8 @@ function startUiServer(cfg: PluginConfig, api: OpenClawApi): string | null {
1914
1940
  const htmlPath = path.join(WORKSPACE_DIR, 'ui-dashboard.html')
1915
1941
  fs.writeFileSync(htmlPath, buildUiHtml(cfg))
1916
1942
 
1917
- // Inject HTML path into script (read from file, no template escaping issues)
1918
- const fullScript = `const HTML_PATH = ${JSON.stringify(htmlPath)};\nconst HTML = require('fs').readFileSync(HTML_PATH, 'utf8');\n${UI_SERVER_SCRIPT}`
1943
+ // Inject base dir, version, and HTML path into script (read from file, no template escaping issues)
1944
+ const fullScript = `const OPENCLAW_BASE_DIR = ${JSON.stringify(getBaseDir())};\nconst PLUGIN_VERSION = ${JSON.stringify(VERSION)};\nconst HTML_PATH = ${JSON.stringify(htmlPath)};\nconst HTML = require('fs').readFileSync(HTML_PATH, 'utf8');\n${UI_SERVER_SCRIPT}`
1919
1945
  fs.writeFileSync(scriptPath, fullScript)
1920
1946
 
1921
1947
  try {
@@ -2129,7 +2155,7 @@ export default function register(api: OpenClawApi) {
2129
2155
 
2130
2156
  const handleStart = async () => {
2131
2157
  // Check if already running (in-memory or detached via PID file)
2132
- const state = isNodeRunning()
2158
+ const state = isNodeRunning(cfg.rpcPort)
2133
2159
  if (state.running) {
2134
2160
  out({ message: 'Node already running', pid: state.pid })
2135
2161
  return
@@ -2392,7 +2418,7 @@ export default function register(api: OpenClawApi) {
2392
2418
  ;(async () => {
2393
2419
  try {
2394
2420
  // Check if already running (e.g. from a previous detached start)
2395
- const state = isNodeRunning()
2421
+ const state = isNodeRunning(cfg.rpcPort)
2396
2422
  if (state.running) {
2397
2423
  api.logger?.info?.(`[clawnetwork] node already running (pid=${state.pid})`)
2398
2424
 
@@ -2414,7 +2440,7 @@ export default function register(api: OpenClawApi) {
2414
2440
 
2415
2441
  if (latestVersion && isVersionOlder(runningVersion, latestVersion)) {
2416
2442
  api.logger?.info?.(`[clawnetwork] node ${runningVersion} → ${latestVersion} available, upgrading...`)
2417
- stopNodeProcess(api)
2443
+ stopNode(api)
2418
2444
  await sleep(3_000)
2419
2445
  try {
2420
2446
  const newBinary = await downloadBinary(api)
@@ -2,7 +2,7 @@
2
2
  "id": "clawnetwork",
3
3
  "name": "ClawNetwork Node",
4
4
  "description": "Run a ClawNetwork blockchain node inside OpenClaw. Every agent is a node.",
5
- "version": "0.1.16",
5
+ "version": "0.1.18",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawlabz/clawnetwork",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Run a ClawNetwork blockchain node inside OpenClaw. Every agent is a blockchain node.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -56,6 +56,6 @@
56
56
  "scripts": {
57
57
  "build": "echo 'no build step'",
58
58
  "lint": "node -e \"process.exit(0)\"",
59
- "test": "node -e \"process.exit(0)\""
59
+ "test": "node --test tests/unit.test.mjs"
60
60
  }
61
61
  }