clawrtc 1.0.0

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.
Files changed (2) hide show
  1. package/bin/clawrtc.js +410 -0
  2. package/package.json +19 -0
package/bin/clawrtc.js ADDED
@@ -0,0 +1,410 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ClawRTC — Mine RTC tokens with your AI agent on real hardware.
4
+ *
5
+ * Modern machines get 1x multiplier. Vintage hardware gets bonus.
6
+ * VMs are detected and penalized — real iron only.
7
+ *
8
+ * Usage:
9
+ * npm install -g clawrtc
10
+ * clawrtc install --wallet my-agent
11
+ * clawrtc start
12
+ */
13
+
14
+ const { execSync, spawn } = require('child_process');
15
+ const fs = require('fs');
16
+ const https = require('https');
17
+ const http = require('http');
18
+ const os = require('os');
19
+ const path = require('path');
20
+ const readline = require('readline');
21
+
22
+ const REPO_BASE = 'https://raw.githubusercontent.com/Scottcjn/Rustchain/main';
23
+ const INSTALL_DIR = path.join(os.homedir(), '.clawrtc');
24
+ const VENV_DIR = path.join(INSTALL_DIR, 'venv');
25
+ const NODE_URL = 'https://50.28.86.131';
26
+
27
+ // ANSI colors
28
+ const C = '\x1b[36m', G = '\x1b[32m', R = '\x1b[31m', Y = '\x1b[33m';
29
+ const B = '\x1b[1m', D = '\x1b[2m', NC = '\x1b[0m';
30
+
31
+ const log = (m) => console.log(`${C}[clawrtc]${NC} ${m}`);
32
+ const ok = (m) => console.log(`${G}[OK]${NC} ${m}`);
33
+ const warn = (m) => console.log(`${Y}[WARN]${NC} ${m}`);
34
+
35
+ function downloadFile(url, dest) {
36
+ return new Promise((resolve, reject) => {
37
+ const file = fs.createWriteStream(dest);
38
+ const mod = url.startsWith('https') ? https : http;
39
+ const opts = { rejectUnauthorized: false };
40
+ mod.get(url, opts, (res) => {
41
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
42
+ file.close();
43
+ fs.unlinkSync(dest);
44
+ return downloadFile(res.headers.location, dest).then(resolve).catch(reject);
45
+ }
46
+ res.pipe(file);
47
+ file.on('finish', () => {
48
+ file.close();
49
+ const size = fs.statSync(dest).size;
50
+ if (size < 100) return reject(new Error(`File too small (${size} bytes)`));
51
+ resolve(size);
52
+ });
53
+ }).on('error', (e) => { file.close(); reject(e); });
54
+ });
55
+ }
56
+
57
+ function ask(prompt) {
58
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
59
+ return new Promise((resolve) => {
60
+ rl.question(prompt, (ans) => { rl.close(); resolve(ans.trim()); });
61
+ });
62
+ }
63
+
64
+ function detectVM() {
65
+ const hints = [];
66
+ if (os.platform() === 'linux') {
67
+ const dmiPaths = ['/sys/class/dmi/id/sys_vendor', '/sys/class/dmi/id/product_name'];
68
+ const vmVendors = ['qemu', 'vmware', 'virtualbox', 'xen', 'kvm', 'microsoft', 'parallels'];
69
+ for (const p of dmiPaths) {
70
+ try {
71
+ const val = fs.readFileSync(p, 'utf8').trim().toLowerCase();
72
+ if (vmVendors.some(v => val.includes(v))) hints.push(`${p}: ${val}`);
73
+ } catch (e) {}
74
+ }
75
+ try {
76
+ const cpu = fs.readFileSync('/proc/cpuinfo', 'utf8').toLowerCase();
77
+ if (cpu.includes('hypervisor')) hints.push('cpuinfo: hypervisor flag');
78
+ } catch (e) {}
79
+ }
80
+ return hints;
81
+ }
82
+
83
+ async function cmdInstall(walletArg) {
84
+ console.log(`
85
+ ${C}${B}
86
+ ██████╗██╗ █████╗ ██╗ ██╗███████╗██╗ ██╗██╗██╗ ██╗
87
+ ██╔════╝██║ ██╔══██╗██║ ██║██╔════╝██║ ██╔╝██║██║ ██║
88
+ ██║ ██║ ███████║██║ █╗ ██║███████╗█████╔╝ ██║██║ ██║
89
+ ██║ ██║ ██╔══██║██║███╗██║╚════██║██╔═██╗ ██║██║ ██║
90
+ ╚██████╗███████╗██║ ██║╚███╔███╔╝███████║██║ ██╗██║███████╗███████╗
91
+ ╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝
92
+ ${NC}
93
+ ${D} Mine RTC tokens with your AI agent on real hardware${NC}
94
+ ${D} Modern x86/ARM = 1x | Vintage PowerPC = up to 2.5x | VM = ~0x${NC}
95
+ `);
96
+
97
+ const plat = os.platform();
98
+ const arch = os.arch();
99
+ log(`Platform: ${plat} | Arch: ${arch}`);
100
+
101
+ if (plat !== 'linux' && plat !== 'darwin') {
102
+ console.error(`${R}[ERROR]${NC} Unsupported platform: ${plat}. Use Linux or macOS.`);
103
+ process.exit(1);
104
+ }
105
+
106
+ // VM check
107
+ const vmHints = detectVM();
108
+ if (vmHints.length > 0) {
109
+ console.log(`
110
+ ${R}${B} ╔══════════════════════════════════════════════════════════╗
111
+ ║ ⚠ VM DETECTED — READ THIS ⚠ ║
112
+ ╠══════════════════════════════════════════════════════════╣
113
+ ║ This machine appears to be a virtual machine. ║
114
+ ║ RustChain will detect VMs and assign near-zero weight. ║
115
+ ║ Your miner will attest but earn effectively nothing. ║
116
+ ║ To earn RTC, run on bare-metal hardware. ║
117
+ ╚══════════════════════════════════════════════════════════╝${NC}`);
118
+ for (const h of vmHints.slice(0, 4)) console.log(` ${R} • ${h}${NC}`);
119
+ console.log();
120
+ }
121
+
122
+ // Wallet
123
+ let wallet = walletArg;
124
+ if (!wallet) {
125
+ wallet = await ask(`${C}[clawrtc]${NC} Enter agent wallet name (e.g. my-claw-agent): `);
126
+ }
127
+ if (!wallet) {
128
+ const host = os.hostname().split('.')[0] || 'agent';
129
+ wallet = `claw-${host}-${Date.now() % 100000}`;
130
+ warn(`No wallet provided. Auto-generated: ${wallet}`);
131
+ }
132
+
133
+ // Create install dir
134
+ log(`Installing to ${INSTALL_DIR}`);
135
+ fs.mkdirSync(INSTALL_DIR, { recursive: true });
136
+ fs.writeFileSync(path.join(INSTALL_DIR, '.wallet'), wallet);
137
+
138
+ // Check for python3
139
+ let pythonBin = 'python3';
140
+ try { execSync('python3 --version', { stdio: 'pipe' }); }
141
+ catch (e) {
142
+ try { execSync('python --version', { stdio: 'pipe' }); pythonBin = 'python'; }
143
+ catch (e2) {
144
+ console.error(`${R}[ERROR]${NC} Python 3 not found. Install Python 3.8+ first.`);
145
+ process.exit(1);
146
+ }
147
+ }
148
+
149
+ // Create venv
150
+ if (!fs.existsSync(VENV_DIR)) {
151
+ log('Creating Python environment...');
152
+ execSync(`${pythonBin} -m venv "${VENV_DIR}"`, { stdio: 'inherit' });
153
+ }
154
+
155
+ // Install deps
156
+ log('Installing dependencies...');
157
+ const pip = path.join(VENV_DIR, 'bin', 'pip');
158
+ execSync(`"${pip}" install --upgrade pip -q`, { stdio: 'pipe' });
159
+ execSync(`"${pip}" install requests -q`, { stdio: 'pipe' });
160
+ ok('Dependencies ready');
161
+
162
+ // Download miner files
163
+ log('Downloading miner from RustChain repo...');
164
+ const downloads = [
165
+ [`${REPO_BASE}/miners/linux/fingerprint_checks.py`, 'fingerprint_checks.py'],
166
+ ];
167
+
168
+ if (plat === 'linux') {
169
+ downloads.push([`${REPO_BASE}/miners/linux/rustchain_linux_miner.py`, 'miner.py']);
170
+ } else if (plat === 'darwin') {
171
+ downloads.push([`${REPO_BASE}/miners/macos/rustchain_mac_miner_v2.4.py`, 'miner.py']);
172
+ }
173
+
174
+ for (const [url, filename] of downloads) {
175
+ const dest = path.join(INSTALL_DIR, filename);
176
+ const size = await downloadFile(url, dest);
177
+ log(` ${filename} (${(size / 1024).toFixed(1)} KB)`);
178
+ }
179
+ ok('Miner files downloaded');
180
+
181
+ // Setup service
182
+ if (plat === 'linux') {
183
+ setupSystemd(wallet);
184
+ } else if (plat === 'darwin') {
185
+ setupLaunchd(wallet);
186
+ }
187
+
188
+ // Network check
189
+ log('Checking RustChain network...');
190
+ try {
191
+ const data = await new Promise((resolve, reject) => {
192
+ https.get(`${NODE_URL}/api/miners`, { rejectUnauthorized: false }, (res) => {
193
+ let d = '';
194
+ res.on('data', c => d += c);
195
+ res.on('end', () => resolve(d));
196
+ }).on('error', reject);
197
+ });
198
+ const miners = JSON.parse(data);
199
+ log(`Active miners on network: ${miners.length}`);
200
+ } catch (e) {
201
+ warn('Could not reach network (node may be temporarily unavailable)');
202
+ }
203
+
204
+ console.log(`
205
+ ${G}${B}═══════════════════════════════════════════════════════════
206
+ ClawRTC installed! Your agent is ready to mine RTC.
207
+
208
+ Wallet: ${wallet}
209
+ Location: ${INSTALL_DIR}
210
+ Reward: 1x multiplier (modern hardware)
211
+
212
+ Commands:
213
+ clawrtc start Start mining in background
214
+ clawrtc stop Stop mining
215
+ clawrtc status Check miner + network status
216
+ clawrtc logs View miner output
217
+
218
+ How it works:
219
+ • Your agent proves real hardware via 6 fingerprint checks
220
+ • Attestation happens automatically every few minutes
221
+ • RTC tokens accumulate in your wallet each epoch (~10 min)
222
+ • Check balance: clawrtc status
223
+
224
+ Multipliers:
225
+ Modern x86/ARM → 1.0x (you are here)
226
+ Apple Silicon → 1.2x
227
+ PowerPC G5 → 2.0x
228
+ PowerPC G4 → 2.5x
229
+ VM/Emulator → ~0x (detected & penalized)
230
+ ═══════════════════════════════════════════════════════════${NC}
231
+ `);
232
+ }
233
+
234
+ function setupSystemd(wallet) {
235
+ const svcDir = path.join(os.homedir(), '.config', 'systemd', 'user');
236
+ fs.mkdirSync(svcDir, { recursive: true });
237
+ const svcFile = path.join(svcDir, 'clawrtc-miner.service');
238
+ const pythonBin = path.join(VENV_DIR, 'bin', 'python');
239
+ const minerPy = path.join(INSTALL_DIR, 'miner.py');
240
+
241
+ fs.writeFileSync(svcFile, `[Unit]
242
+ Description=ClawRTC RTC Miner — AI Agent Mining
243
+ After=network-online.target
244
+ Wants=network-online.target
245
+
246
+ [Service]
247
+ ExecStart=${pythonBin} ${minerPy} --wallet ${wallet}
248
+ Restart=always
249
+ RestartSec=30
250
+ WorkingDirectory=${INSTALL_DIR}
251
+ Environment=PYTHONUNBUFFERED=1
252
+
253
+ [Install]
254
+ WantedBy=default.target
255
+ `);
256
+
257
+ try {
258
+ execSync('systemctl --user daemon-reload', { stdio: 'pipe' });
259
+ execSync('systemctl --user enable clawrtc-miner', { stdio: 'pipe' });
260
+ execSync('systemctl --user start clawrtc-miner', { stdio: 'pipe' });
261
+ ok('Service installed and started');
262
+ } catch (e) {
263
+ warn('Systemd user services not available. Use: clawrtc start');
264
+ }
265
+ }
266
+
267
+ function setupLaunchd(wallet) {
268
+ const plistDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
269
+ fs.mkdirSync(plistDir, { recursive: true });
270
+ const plistFile = path.join(plistDir, 'com.clawrtc.miner.plist');
271
+ const pythonBin = path.join(VENV_DIR, 'bin', 'python');
272
+ const minerPy = path.join(INSTALL_DIR, 'miner.py');
273
+
274
+ fs.writeFileSync(plistFile, `<?xml version="1.0" encoding="UTF-8"?>
275
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
276
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
277
+ <plist version="1.0">
278
+ <dict>
279
+ <key>Label</key>
280
+ <string>com.clawrtc.miner</string>
281
+ <key>ProgramArguments</key>
282
+ <array>
283
+ <string>${pythonBin}</string>
284
+ <string>${minerPy}</string>
285
+ <string>--wallet</string>
286
+ <string>${wallet}</string>
287
+ </array>
288
+ <key>WorkingDirectory</key>
289
+ <string>${INSTALL_DIR}</string>
290
+ <key>RunAtLoad</key>
291
+ <true/>
292
+ <key>KeepAlive</key>
293
+ <true/>
294
+ <key>StandardOutPath</key>
295
+ <string>${path.join(INSTALL_DIR, 'miner.log')}</string>
296
+ <key>StandardErrorPath</key>
297
+ <string>${path.join(INSTALL_DIR, 'miner.err')}</string>
298
+ </dict>
299
+ </plist>`);
300
+
301
+ try {
302
+ execSync(`launchctl unload "${plistFile}" 2>/dev/null`, { stdio: 'pipe' });
303
+ } catch (e) {}
304
+ try {
305
+ execSync(`launchctl load "${plistFile}"`, { stdio: 'pipe' });
306
+ ok('LaunchAgent installed and loaded');
307
+ } catch (e) {
308
+ warn('Could not load LaunchAgent. Use: clawrtc start');
309
+ }
310
+ }
311
+
312
+ function cmdStart() {
313
+ if (os.platform() === 'linux') {
314
+ execSync('systemctl --user start clawrtc-miner', { stdio: 'inherit' });
315
+ } else if (os.platform() === 'darwin') {
316
+ const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
317
+ try { execSync(`launchctl load "${pf}"`, { stdio: 'inherit' }); } catch (e) {}
318
+ }
319
+ ok('Miner started');
320
+ }
321
+
322
+ function cmdStop() {
323
+ if (os.platform() === 'linux') {
324
+ try { execSync('systemctl --user stop clawrtc-miner', { stdio: 'pipe' }); } catch (e) {}
325
+ } else if (os.platform() === 'darwin') {
326
+ const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
327
+ try { execSync(`launchctl unload "${pf}"`, { stdio: 'pipe' }); } catch (e) {}
328
+ }
329
+ ok('Miner stopped');
330
+ }
331
+
332
+ function cmdStatus() {
333
+ if (os.platform() === 'linux') {
334
+ try { execSync('systemctl --user status clawrtc-miner', { stdio: 'inherit' }); } catch (e) {}
335
+ } else if (os.platform() === 'darwin') {
336
+ try { execSync('launchctl list | grep clawrtc', { stdio: 'inherit' }); } catch (e) {}
337
+ }
338
+ const wf = path.join(INSTALL_DIR, '.wallet');
339
+ if (fs.existsSync(wf)) log(`Wallet: ${fs.readFileSync(wf, 'utf8').trim()}`);
340
+
341
+ https.get(`${NODE_URL}/health`, { rejectUnauthorized: false }, (res) => {
342
+ let d = '';
343
+ res.on('data', c => d += c);
344
+ res.on('end', () => {
345
+ try {
346
+ const h = JSON.parse(d);
347
+ log(`Network: ${h.ok ? 'online' : 'offline'} (v${h.version || '?'})`);
348
+ } catch (e) { warn('Could not parse network status'); }
349
+ });
350
+ }).on('error', () => warn('Could not reach network'));
351
+ }
352
+
353
+ function cmdLogs() {
354
+ if (os.platform() === 'linux') {
355
+ spawn('journalctl', ['--user', '-u', 'clawrtc-miner', '-f', '--no-pager', '-n', '50'], { stdio: 'inherit' });
356
+ } else {
357
+ const lf = path.join(INSTALL_DIR, 'miner.log');
358
+ if (fs.existsSync(lf)) spawn('tail', ['-f', lf], { stdio: 'inherit' });
359
+ else warn('No log file found');
360
+ }
361
+ }
362
+
363
+ function cmdUninstall() {
364
+ log('Stopping miner...');
365
+ cmdStop();
366
+ if (os.platform() === 'linux') {
367
+ try { execSync('systemctl --user disable clawrtc-miner', { stdio: 'pipe' }); } catch (e) {}
368
+ const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
369
+ try { fs.unlinkSync(sf); } catch (e) {}
370
+ } else if (os.platform() === 'darwin') {
371
+ const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
372
+ try { fs.unlinkSync(pf); } catch (e) {}
373
+ }
374
+ try { fs.rmSync(INSTALL_DIR, { recursive: true, force: true }); } catch (e) {}
375
+ ok('ClawRTC miner uninstalled');
376
+ }
377
+
378
+ // Main
379
+ const args = process.argv.slice(2);
380
+ const cmd = args[0] || 'install';
381
+ const walletIdx = args.indexOf('--wallet');
382
+ const walletArg = walletIdx >= 0 ? args[walletIdx + 1] : null;
383
+
384
+ switch (cmd) {
385
+ case 'install': cmdInstall(walletArg); break;
386
+ case 'start': cmdStart(); break;
387
+ case 'stop': cmdStop(); break;
388
+ case 'status': cmdStatus(); break;
389
+ case 'logs': cmdLogs(); break;
390
+ case 'uninstall': cmdUninstall(); break;
391
+ case '--help': case '-h':
392
+ console.log(`
393
+ ClawRTC — Mine RTC tokens with your AI agent on real hardware
394
+
395
+ Commands:
396
+ clawrtc install [--wallet NAME] Install miner and configure wallet
397
+ clawrtc start Start mining in background
398
+ clawrtc stop Stop mining
399
+ clawrtc status Check miner + network status
400
+ clawrtc logs View miner output
401
+ clawrtc uninstall Remove everything
402
+
403
+ Modern hardware gets 1x multiplier. VMs are detected and penalized.
404
+ Vintage hardware (PowerPC G4/G5) gets up to 2.5x bonus.
405
+ `);
406
+ break;
407
+ default:
408
+ console.error(`Unknown command: ${cmd}. Use --help for usage.`);
409
+ process.exit(1);
410
+ }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "clawrtc",
3
+ "version": "1.0.0",
4
+ "description": "ClawRTC — Let your AI agent mine RTC tokens on any modern hardware. 1x multiplier, built-in wallet, VM-penalized.",
5
+ "bin": {
6
+ "clawrtc": "./bin/clawrtc.js"
7
+ },
8
+ "keywords": ["clawrtc", "ai-agent", "miner", "rustchain", "rtc", "openclaw", "proof-of-antiquity", "blockchain"],
9
+ "author": "Elyan Labs <scott@elyanlabs.ai>",
10
+ "license": "MIT",
11
+ "homepage": "https://bottube.ai",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/Scottcjn/Rustchain"
15
+ },
16
+ "engines": {
17
+ "node": ">=14"
18
+ }
19
+ }