@clawlabz/clawnetwork 0.1.2 → 0.1.3
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 +194 -12
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ const DEFAULT_P2P_PORT = 9711
|
|
|
15
15
|
const DEFAULT_NETWORK = 'mainnet'
|
|
16
16
|
const DEFAULT_SYNC_MODE = 'light'
|
|
17
17
|
const DEFAULT_HEALTH_CHECK_SECONDS = 30
|
|
18
|
+
const MIN_NODE_VERSION = '0.4.19'
|
|
18
19
|
const DEFAULT_UI_PORT = 19877
|
|
19
20
|
const MAX_RESTART_ATTEMPTS = 3
|
|
20
21
|
|
|
@@ -227,6 +228,16 @@ function getBinaryVersion(binaryPath: string): string | null {
|
|
|
227
228
|
} catch { return null }
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
function isVersionOlder(current: string, required: string): boolean {
|
|
232
|
+
const c = current.split('.').map(Number)
|
|
233
|
+
const r = required.split('.').map(Number)
|
|
234
|
+
for (let i = 0; i < 3; i++) {
|
|
235
|
+
if ((c[i] || 0) < (r[i] || 0)) return true
|
|
236
|
+
if ((c[i] || 0) > (r[i] || 0)) return false
|
|
237
|
+
}
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
|
|
230
241
|
function detectPlatformTarget(): string {
|
|
231
242
|
const platform = process.platform === 'darwin' ? 'macos' : process.platform === 'win32' ? 'windows' : 'linux'
|
|
232
243
|
const arch = process.arch === 'arm64' ? 'aarch64' : 'x86_64'
|
|
@@ -908,6 +919,21 @@ function buildUiHtml(cfg: PluginConfig): string {
|
|
|
908
919
|
.modal-input { width: 100%; box-sizing: border-box; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; font-size: 14px; color: var(--text); font-family: var(--font-mono); outline: none; margin-top: 4px; }
|
|
909
920
|
.modal-input:focus { border-color: var(--accent); }
|
|
910
921
|
.modal-hint { font-size: 12px; color: var(--text-dim); margin-top: 6px; line-height: 1.5; }
|
|
922
|
+
|
|
923
|
+
.upgrade-banner { padding: 14px 16px; border-radius: var(--radius); margin-bottom: 16px; font-size: 13px; line-height: 1.6; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
|
|
924
|
+
.upgrade-banner.recommended { background: rgba(255, 170, 0, 0.1); border: 1px solid rgba(255, 170, 0, 0.3); color: #ffaa00; }
|
|
925
|
+
.upgrade-banner.recommended .upgrade-text { flex: 1; }
|
|
926
|
+
.upgrade-banner.recommended .upgrade-actions { display: flex; gap: 8px; }
|
|
927
|
+
.upgrade-banner.required { background: rgba(255, 140, 0, 0.1); border: 1px solid rgba(255, 140, 0, 0.3); color: #ff8c3a; }
|
|
928
|
+
.upgrade-banner.required .upgrade-text { flex: 1; }
|
|
929
|
+
.upgrade-banner.required .upgrade-actions { display: flex; gap: 8px; }
|
|
930
|
+
.upgrade-banner.critical { background: rgba(239, 68, 68, 0.15); border: 1px solid rgba(239, 68, 68, 0.4); color: var(--danger); width: 100%; margin-left: calc(-20px - 1px); margin-right: calc(-20px - 1px); padding: 16px calc(20px + 1px); border-radius: 0; font-weight: 600; }
|
|
931
|
+
.upgrade-banner.critical .upgrade-text { flex: 1; }
|
|
932
|
+
.upgrade-banner.critical .upgrade-actions { display: flex; gap: 8px; }
|
|
933
|
+
.upgrade-btn { background: var(--accent); color: var(--bg); border: none; padding: 6px 12px; border-radius: 4px; font-size: 12px; cursor: pointer; font-weight: 600; transition: 0.2s; }
|
|
934
|
+
.upgrade-btn:hover { opacity: 0.85; }
|
|
935
|
+
.upgrade-dismiss { background: none; border: 1px solid currentColor; color: currentColor; padding: 4px 10px; border-radius: 4px; font-size: 12px; cursor: pointer; transition: 0.2s; }
|
|
936
|
+
.upgrade-dismiss:hover { opacity: 0.7; }
|
|
911
937
|
</style>
|
|
912
938
|
</head>
|
|
913
939
|
<body>
|
|
@@ -923,6 +949,8 @@ function buildUiHtml(cfg: PluginConfig): string {
|
|
|
923
949
|
|
|
924
950
|
<main class="container" style="padding-top:16px;padding-bottom:40px">
|
|
925
951
|
|
|
952
|
+
<div id="upgradeBanner" style="display:none" class="upgrade-banner"></div>
|
|
953
|
+
|
|
926
954
|
<div class="panel">
|
|
927
955
|
<div class="panel-title">Node</div>
|
|
928
956
|
<div class="stats-grid" style="margin:0 0 4px">
|
|
@@ -944,8 +972,8 @@ function buildUiHtml(cfg: PluginConfig): string {
|
|
|
944
972
|
</div>
|
|
945
973
|
</div>
|
|
946
974
|
<div class="node-controls">
|
|
947
|
-
<button class="btn primary" onclick="doAction('start')">▶ Start Node</button>
|
|
948
|
-
<button class="btn danger" onclick="doAction('stop')">■ Stop Node</button>
|
|
975
|
+
<button class="btn primary" id="startBtn" onclick="doAction('start')">▶ Start Node</button>
|
|
976
|
+
<button class="btn danger" id="stopBtn" onclick="doAction('stop')">■ Stop Node</button>
|
|
949
977
|
</div>
|
|
950
978
|
</div>
|
|
951
979
|
|
|
@@ -1267,10 +1295,11 @@ function buildUiHtml(cfg: PluginConfig): string {
|
|
|
1267
1295
|
function renderStatus(s) {
|
|
1268
1296
|
const statusEl = document.getElementById('statusValue');
|
|
1269
1297
|
if (s.running) {
|
|
1270
|
-
|
|
1271
|
-
|
|
1298
|
+
let dotClass = 'online', label = 'Online';
|
|
1299
|
+
if (s.syncing && s.peerless) { dotClass = 'syncing'; label = 'No Peers'; }
|
|
1300
|
+
else if (s.syncing) { dotClass = 'syncing'; label = 'Syncing'; }
|
|
1272
1301
|
statusEl.innerHTML = '<span class="status-dot ' + dotClass + '"></span>' + label;
|
|
1273
|
-
statusEl.className = 'stat-value green';
|
|
1302
|
+
statusEl.className = 'stat-value' + (s.syncing ? '' : ' green');
|
|
1274
1303
|
} else {
|
|
1275
1304
|
statusEl.innerHTML = '<span class="status-dot offline"></span>Offline';
|
|
1276
1305
|
statusEl.className = 'stat-value danger';
|
|
@@ -1279,6 +1308,37 @@ function buildUiHtml(cfg: PluginConfig): string {
|
|
|
1279
1308
|
document.getElementById('heightValue').textContent = s.blockHeight !== null ? s.blockHeight.toLocaleString() : '—';
|
|
1280
1309
|
document.getElementById('peersValue').textContent = s.peerCount !== null ? s.peerCount : '—';
|
|
1281
1310
|
document.getElementById('uptimeValue').textContent = s.uptimeFormatted || '—';
|
|
1311
|
+
document.getElementById('startBtn').disabled = s.running;
|
|
1312
|
+
document.getElementById('stopBtn').disabled = !s.running;
|
|
1313
|
+
document.getElementById('startBtn').style.opacity = s.running ? '0.4' : '1';
|
|
1314
|
+
document.getElementById('stopBtn').style.opacity = !s.running ? '0.4' : '1';
|
|
1315
|
+
|
|
1316
|
+
// Handle upgrade banner
|
|
1317
|
+
const bannerEl = document.getElementById('upgradeBanner');
|
|
1318
|
+
if (s.upgradeLevel && s.upgradeLevel !== 'up_to_date' && s.upgradeLevel !== 'unknown') {
|
|
1319
|
+
bannerEl.style.display = '';
|
|
1320
|
+
const recommended = s.upgradeLevel === 'recommended';
|
|
1321
|
+
const required = s.upgradeLevel === 'required';
|
|
1322
|
+
const critical = s.upgradeLevel === 'critical';
|
|
1323
|
+
bannerEl.className = 'upgrade-banner ' + s.upgradeLevel;
|
|
1324
|
+
let bannerHtml = '<div class="upgrade-text">';
|
|
1325
|
+
if (critical) {
|
|
1326
|
+
bannerHtml += '⚠ CRITICAL UPDATE REQUIRED — ' + (s.changelog || 'Security update required') + '. Node stopped for security.';
|
|
1327
|
+
} else if (required) {
|
|
1328
|
+
bannerHtml += 'Update recommended: v' + (s.latestVersion || '') + ' — ' + (s.changelog || 'Update available');
|
|
1329
|
+
} else if (recommended) {
|
|
1330
|
+
bannerHtml += 'Update available: v' + (s.latestVersion || '') + ' — ' + (s.changelog || 'New version available');
|
|
1331
|
+
}
|
|
1332
|
+
bannerHtml += '</div><div class="upgrade-actions">';
|
|
1333
|
+
bannerHtml += '<button class="upgrade-btn" onclick="doAction(\'upgrade\')">Update Now</button>';
|
|
1334
|
+
if (recommended) {
|
|
1335
|
+
bannerHtml += '<button class="upgrade-dismiss" onclick="document.getElementById(\'upgradeBanner\').style.display=\'none\'">Dismiss</button>';
|
|
1336
|
+
}
|
|
1337
|
+
bannerHtml += '</div>';
|
|
1338
|
+
bannerEl.innerHTML = bannerHtml;
|
|
1339
|
+
} else {
|
|
1340
|
+
bannerEl.style.display = 'none';
|
|
1341
|
+
}
|
|
1282
1342
|
|
|
1283
1343
|
// Wallet
|
|
1284
1344
|
cachedAddress = s.walletAddress || '';
|
|
@@ -1313,11 +1373,21 @@ function buildUiHtml(cfg: PluginConfig): string {
|
|
|
1313
1373
|
}
|
|
1314
1374
|
|
|
1315
1375
|
// Node info
|
|
1376
|
+
let versionStatusHtml = s.binaryVersion || '—';
|
|
1377
|
+
if (s.upgradeLevel === 'up_to_date') {
|
|
1378
|
+
versionStatusHtml = (s.binaryVersion || '—') + ' <span style="color:var(--green)">✓</span>';
|
|
1379
|
+
} else if (s.upgradeLevel === 'recommended') {
|
|
1380
|
+
versionStatusHtml = (s.binaryVersion || '—') + ' <span style="color:#ffaa00">→ ' + (s.latestVersion || '') + '</span>';
|
|
1381
|
+
} else if (s.upgradeLevel === 'required') {
|
|
1382
|
+
versionStatusHtml = (s.binaryVersion || '—') + ' <span style="color:#ff8c3a">⚠ Update recommended</span>';
|
|
1383
|
+
} else if (s.upgradeLevel === 'critical') {
|
|
1384
|
+
versionStatusHtml = (s.binaryVersion || '—') + ' <span style="color:var(--danger)">🔴 CRITICAL</span>';
|
|
1385
|
+
}
|
|
1316
1386
|
const rows = [
|
|
1317
1387
|
['Network', s.network],
|
|
1318
1388
|
['Sync Mode', s.syncMode],
|
|
1319
1389
|
['RPC URL', s.rpcUrl],
|
|
1320
|
-
['Binary Version',
|
|
1390
|
+
['Binary Version', versionStatusHtml],
|
|
1321
1391
|
['Plugin Version', s.pluginVersion],
|
|
1322
1392
|
['PID', s.pid || '—'],
|
|
1323
1393
|
['Restart Count', s.restartCount],
|
|
@@ -1436,6 +1506,22 @@ async function handle(req, res) {
|
|
|
1436
1506
|
let balance = '';
|
|
1437
1507
|
let walletAddress = '';
|
|
1438
1508
|
let agentName = '';
|
|
1509
|
+
let upgradeLevel = 'unknown';
|
|
1510
|
+
let latestVersion = '';
|
|
1511
|
+
let releaseUrl = '';
|
|
1512
|
+
let changelog = '';
|
|
1513
|
+
let announcement = null;
|
|
1514
|
+
// Fetch version info if available (Phase 1 endpoint)
|
|
1515
|
+
try {
|
|
1516
|
+
const v = await fetchJson('http://localhost:' + RPC_PORT + '/version');
|
|
1517
|
+
if (v && v.upgrade_level) {
|
|
1518
|
+
upgradeLevel = v.upgrade_level;
|
|
1519
|
+
latestVersion = v.latest_version || '';
|
|
1520
|
+
releaseUrl = v.release_url || '';
|
|
1521
|
+
changelog = v.changelog || '';
|
|
1522
|
+
announcement = v.announcement || null;
|
|
1523
|
+
}
|
|
1524
|
+
} catch {}
|
|
1439
1525
|
try {
|
|
1440
1526
|
const walletPath = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json');
|
|
1441
1527
|
const w = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
|
|
@@ -1446,7 +1532,7 @@ async function handle(req, res) {
|
|
|
1446
1532
|
}
|
|
1447
1533
|
} catch {}
|
|
1448
1534
|
json(200, {
|
|
1449
|
-
running: h.status === 'ok',
|
|
1535
|
+
running: h.status === 'ok' || h.status === 'degraded',
|
|
1450
1536
|
blockHeight: h.height,
|
|
1451
1537
|
peerCount: h.peer_count,
|
|
1452
1538
|
network: h.chain_id,
|
|
@@ -1457,11 +1543,12 @@ async function handle(req, res) {
|
|
|
1457
1543
|
pluginVersion: '0.1.1',
|
|
1458
1544
|
uptime: h.uptime_secs,
|
|
1459
1545
|
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',
|
|
1460
|
-
restartCount: 0, dataDir: path.join(os.homedir(), '.clawnetwork'), balance, agentName, syncing: h.status === 'degraded',
|
|
1546
|
+
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,
|
|
1547
|
+
upgradeLevel, latestVersion, releaseUrl, changelog, announcement,
|
|
1461
1548
|
});
|
|
1462
1549
|
} catch {
|
|
1463
1550
|
const walletAddr = (() => { try { return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.openclaw/workspace/clawnetwork/wallet.json'), 'utf8')).address; } catch { return ''; } })();
|
|
1464
|
-
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 });
|
|
1551
|
+
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 });
|
|
1465
1552
|
}
|
|
1466
1553
|
return;
|
|
1467
1554
|
}
|
|
@@ -1583,8 +1670,21 @@ async function handle(req, res) {
|
|
|
1583
1670
|
}
|
|
1584
1671
|
if (a === 'start') {
|
|
1585
1672
|
try {
|
|
1586
|
-
// Check if already running
|
|
1673
|
+
// Check if already running — try RPC health first (covers stale PID file)
|
|
1587
1674
|
const pidFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.pid');
|
|
1675
|
+
try {
|
|
1676
|
+
const h = await fetchJson('http://localhost:' + RPC_PORT + '/health');
|
|
1677
|
+
if (h && (h.status === 'ok' || h.status === 'degraded')) {
|
|
1678
|
+
try {
|
|
1679
|
+
const { execSync } = require('child_process');
|
|
1680
|
+
const pgrep = execSync("pgrep -f 'claw-node start'", { encoding: 'utf8', timeout: 3000 }).trim();
|
|
1681
|
+
const livePid = parseInt(pgrep.split('\\n')[0], 10);
|
|
1682
|
+
if (livePid > 0) fs.writeFileSync(pidFile, String(livePid));
|
|
1683
|
+
} catch {}
|
|
1684
|
+
json(200, { message: 'Node already running' }); return;
|
|
1685
|
+
}
|
|
1686
|
+
} catch {}
|
|
1687
|
+
// Fallback: check PID file
|
|
1588
1688
|
try {
|
|
1589
1689
|
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
|
|
1590
1690
|
if (pid > 0) { try { process.kill(pid, 0); json(200, { message: 'Node already running', pid }); return; } catch {} }
|
|
@@ -1651,6 +1751,64 @@ async function handle(req, res) {
|
|
|
1651
1751
|
json(200, { message: 'Use Stop then Start to restart the node' });
|
|
1652
1752
|
return;
|
|
1653
1753
|
}
|
|
1754
|
+
if (a === 'upgrade') {
|
|
1755
|
+
try {
|
|
1756
|
+
// 1. Stop running node
|
|
1757
|
+
const pidFile = path.join(os.homedir(), '.openclaw/workspace/clawnetwork/node.pid');
|
|
1758
|
+
try {
|
|
1759
|
+
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
|
|
1760
|
+
if (pid > 0) try { process.kill(pid, 'SIGTERM'); } catch {}
|
|
1761
|
+
} catch {}
|
|
1762
|
+
try { require('child_process').execFileSync('pkill', ['-f', 'claw-node start'], { timeout: 5000 }); } catch {}
|
|
1763
|
+
|
|
1764
|
+
// 2. Download latest binary
|
|
1765
|
+
const binDir = path.join(os.homedir(), '.openclaw/bin');
|
|
1766
|
+
const binName = process.platform === 'win32' ? 'claw-node.exe' : 'claw-node';
|
|
1767
|
+
const target = process.platform === 'darwin'
|
|
1768
|
+
? (process.arch === 'arm64' ? 'macos-aarch64' : 'macos-x86_64')
|
|
1769
|
+
: process.platform === 'win32' ? 'windows-x86_64' : 'linux-x86_64';
|
|
1770
|
+
const ext = process.platform === 'win32' ? 'zip' : 'tar.gz';
|
|
1771
|
+
|
|
1772
|
+
// Fetch latest release tag
|
|
1773
|
+
let version = 'latest';
|
|
1774
|
+
try {
|
|
1775
|
+
const res = await fetch('https://api.github.com/repos/clawlabz/claw-network/releases/latest');
|
|
1776
|
+
if (res.ok) { const d = await res.json(); if (d.tag_name) version = d.tag_name; }
|
|
1777
|
+
} catch {}
|
|
1778
|
+
|
|
1779
|
+
const baseUrl = version === 'latest'
|
|
1780
|
+
? 'https://github.com/clawlabz/claw-network/releases/latest/download'
|
|
1781
|
+
: 'https://github.com/clawlabz/claw-network/releases/download/' + version;
|
|
1782
|
+
const downloadUrl = baseUrl + '/claw-node-' + target + '.' + ext;
|
|
1783
|
+
|
|
1784
|
+
const tmpFile = path.join(os.tmpdir(), 'claw-node-upgrade-' + Date.now() + '.' + ext);
|
|
1785
|
+
require('child_process').execFileSync('curl', ['-sSfL', '-o', tmpFile, downloadUrl], { timeout: 120000 });
|
|
1786
|
+
|
|
1787
|
+
// Ensure bin directory exists
|
|
1788
|
+
if (!fs.existsSync(binDir)) { fs.mkdirSync(binDir, { recursive: true }); }
|
|
1789
|
+
|
|
1790
|
+
// Extract binary
|
|
1791
|
+
if (ext === 'tar.gz') {
|
|
1792
|
+
require('child_process').execFileSync('tar', ['xzf', tmpFile, '-C', binDir], { timeout: 30000 });
|
|
1793
|
+
} else {
|
|
1794
|
+
// Windows zip handling
|
|
1795
|
+
const AdmZip = require('adm-zip');
|
|
1796
|
+
const zip = new AdmZip(tmpFile);
|
|
1797
|
+
zip.extractAllTo(binDir, true);
|
|
1798
|
+
}
|
|
1799
|
+
fs.chmodSync(path.join(binDir, binName), 0o755);
|
|
1800
|
+
try { fs.unlinkSync(tmpFile); } catch {}
|
|
1801
|
+
|
|
1802
|
+
// 3. Get new version
|
|
1803
|
+
let newVersion = 'unknown';
|
|
1804
|
+
try {
|
|
1805
|
+
newVersion = require('child_process').execFileSync(path.join(binDir, binName), ['--version'], { encoding: 'utf8', timeout: 5000 }).trim();
|
|
1806
|
+
} catch {}
|
|
1807
|
+
|
|
1808
|
+
json(200, { message: 'Upgraded to ' + newVersion + '. Restart the node from Dashboard.', newVersion });
|
|
1809
|
+
} catch (e) { json(500, { error: e.message }); }
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1654
1812
|
json(400, { error: 'Unknown action: ' + a });
|
|
1655
1813
|
return;
|
|
1656
1814
|
}
|
|
@@ -1918,6 +2076,18 @@ export default function register(api: OpenClawApi) {
|
|
|
1918
2076
|
out({ error: 'claw-node not found. Run: curl -sSf https://raw.githubusercontent.com/clawlabz/claw-network/main/claw-node/scripts/install.sh | bash' })
|
|
1919
2077
|
return
|
|
1920
2078
|
}
|
|
2079
|
+
} else {
|
|
2080
|
+
// Auto-upgrade if binary is older than required minimum
|
|
2081
|
+
const currentVersion = getBinaryVersion(binary)
|
|
2082
|
+
if (currentVersion && isVersionOlder(currentVersion, MIN_NODE_VERSION)) {
|
|
2083
|
+
api.logger?.info?.(`[clawnetwork] claw-node ${currentVersion} is outdated (need >=${MIN_NODE_VERSION}), upgrading...`)
|
|
2084
|
+
process.stdout.write(`Upgrading claw-node ${currentVersion} → ${MIN_NODE_VERSION}+...\n`)
|
|
2085
|
+
try {
|
|
2086
|
+
binary = await downloadBinary(api)
|
|
2087
|
+
} catch (e: unknown) {
|
|
2088
|
+
api.logger?.warn?.(`[clawnetwork] auto-upgrade failed: ${(e as Error).message}, continuing with ${currentVersion}`)
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
1921
2091
|
}
|
|
1922
2092
|
initNode(binary, cfg.network, api)
|
|
1923
2093
|
startNodeProcess(binary, cfg, api)
|
|
@@ -2157,12 +2327,16 @@ export default function register(api: OpenClawApi) {
|
|
|
2157
2327
|
// Check if already running (e.g. from a previous detached start)
|
|
2158
2328
|
const state = isNodeRunning()
|
|
2159
2329
|
if (state.running) {
|
|
2160
|
-
api.logger?.info?.(`[clawnetwork] node already running (pid=${state.pid}), skipping
|
|
2330
|
+
api.logger?.info?.(`[clawnetwork] node already running (pid=${state.pid}), skipping node start`)
|
|
2161
2331
|
startHealthCheck(cfg, api)
|
|
2332
|
+
// Still need to ensure heartbeat loop is running (may have been lost on gateway restart)
|
|
2333
|
+
const wallet = ensureWallet(cfg.network, api)
|
|
2334
|
+
await sleep(5_000)
|
|
2335
|
+
await autoRegisterMiner(cfg, wallet, api)
|
|
2162
2336
|
return
|
|
2163
2337
|
}
|
|
2164
2338
|
|
|
2165
|
-
// Step 1: Ensure binary
|
|
2339
|
+
// Step 1: Ensure binary (auto-upgrade if outdated)
|
|
2166
2340
|
let binary = findBinary()
|
|
2167
2341
|
if (!binary) {
|
|
2168
2342
|
if (cfg.autoDownload) {
|
|
@@ -2172,6 +2346,14 @@ export default function register(api: OpenClawApi) {
|
|
|
2172
2346
|
api.logger?.error?.('[clawnetwork] claw-node not found and autoDownload is disabled')
|
|
2173
2347
|
return
|
|
2174
2348
|
}
|
|
2349
|
+
} else if (cfg.autoDownload) {
|
|
2350
|
+
const cv = getBinaryVersion(binary)
|
|
2351
|
+
if (cv && isVersionOlder(cv, MIN_NODE_VERSION)) {
|
|
2352
|
+
api.logger?.info?.(`[clawnetwork] claw-node ${cv} outdated (need >=${MIN_NODE_VERSION}), upgrading...`)
|
|
2353
|
+
try { binary = await downloadBinary(api) } catch (e: unknown) {
|
|
2354
|
+
api.logger?.warn?.(`[clawnetwork] auto-upgrade failed: ${(e as Error).message}`)
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2175
2357
|
}
|
|
2176
2358
|
|
|
2177
2359
|
// Step 2: Init
|