clawrtc 1.2.1 → 1.5.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.
- package/.github/workflows/build-windows.yml +40 -0
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/SKILL.md +102 -0
- package/clawrtc/__init__.py +3 -0
- package/clawrtc/__pycache__/__init__.cpython-313.pyc +0 -0
- package/clawrtc/__pycache__/cli.cpython-313.pyc +0 -0
- package/clawrtc/__pycache__/coinbase_wallet.cpython-313.pyc +0 -0
- package/clawrtc/cli.py +851 -0
- package/clawrtc/coinbase_wallet.py +234 -0
- package/clawrtc/data/__init__.py +0 -0
- package/{data → clawrtc/data}/miner.py +73 -12
- package/clawrtc.egg-info/PKG-INFO +102 -0
- package/clawrtc.egg-info/SOURCES.txt +15 -0
- package/clawrtc.egg-info/dependency_links.txt +1 -0
- package/clawrtc.egg-info/entry_points.txt +2 -0
- package/clawrtc.egg-info/requires.txt +5 -0
- package/clawrtc.egg-info/top_level.txt +1 -0
- package/dist/clawrtc-1.5.0-py3-none-any.whl +0 -0
- package/dist/clawrtc-1.5.0.tar.gz +0 -0
- package/package.json +29 -13
- package/pyproject.toml +42 -0
- package/bin/clawrtc.js +0 -538
- /package/{data → clawrtc/data}/fingerprint_checks.py +0 -0
package/bin/clawrtc.js
DELETED
|
@@ -1,538 +0,0 @@
|
|
|
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
|
-
* All miner scripts are bundled with this package — no external downloads.
|
|
9
|
-
* Network endpoint uses CA-signed TLS certificate.
|
|
10
|
-
*
|
|
11
|
-
* Security:
|
|
12
|
-
* clawrtc install --dry-run Preview without installing
|
|
13
|
-
* clawrtc install --verify Show SHA256 hashes of bundled files
|
|
14
|
-
* clawrtc start --service Opt-in background service
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const { execSync, spawn } = require('child_process');
|
|
18
|
-
const crypto = require('crypto');
|
|
19
|
-
const fs = require('fs');
|
|
20
|
-
const https = require('https');
|
|
21
|
-
const os = require('os');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const readline = require('readline');
|
|
24
|
-
|
|
25
|
-
const VERSION = '1.2.0';
|
|
26
|
-
const INSTALL_DIR = path.join(os.homedir(), '.clawrtc');
|
|
27
|
-
const VENV_DIR = path.join(INSTALL_DIR, 'venv');
|
|
28
|
-
const NODE_URL = 'https://bulbous-bouffant.metalseed.net';
|
|
29
|
-
const DATA_DIR = path.join(__dirname, '..', 'data');
|
|
30
|
-
|
|
31
|
-
// ANSI colors
|
|
32
|
-
const C = '\x1b[36m', G = '\x1b[32m', R = '\x1b[31m', Y = '\x1b[33m';
|
|
33
|
-
const B = '\x1b[1m', D = '\x1b[2m', NC = '\x1b[0m';
|
|
34
|
-
|
|
35
|
-
const log = (m) => console.log(`${C}[clawrtc]${NC} ${m}`);
|
|
36
|
-
const ok = (m) => console.log(`${G}[OK]${NC} ${m}`);
|
|
37
|
-
const warn = (m) => console.log(`${Y}[WARN]${NC} ${m}`);
|
|
38
|
-
|
|
39
|
-
// Bundled files shipped with the package
|
|
40
|
-
const BUNDLED_FILES = [
|
|
41
|
-
['miner.py', 'miner.py'],
|
|
42
|
-
['fingerprint_checks.py', 'fingerprint_checks.py'],
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
function sha256File(filepath) {
|
|
46
|
-
const data = fs.readFileSync(filepath);
|
|
47
|
-
return crypto.createHash('sha256').update(data).digest('hex');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function ask(prompt) {
|
|
51
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
52
|
-
return new Promise((resolve) => {
|
|
53
|
-
rl.question(prompt, (ans) => { rl.close(); resolve(ans.trim()); });
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function detectVM() {
|
|
58
|
-
const hints = [];
|
|
59
|
-
if (os.platform() === 'linux') {
|
|
60
|
-
const dmiPaths = ['/sys/class/dmi/id/sys_vendor', '/sys/class/dmi/id/product_name'];
|
|
61
|
-
const vmVendors = ['qemu', 'vmware', 'virtualbox', 'xen', 'kvm', 'microsoft', 'parallels'];
|
|
62
|
-
for (const p of dmiPaths) {
|
|
63
|
-
try {
|
|
64
|
-
const val = fs.readFileSync(p, 'utf8').trim().toLowerCase();
|
|
65
|
-
if (vmVendors.some(v => val.includes(v))) hints.push(`${p}: ${val}`);
|
|
66
|
-
} catch (e) {}
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
const cpu = fs.readFileSync('/proc/cpuinfo', 'utf8').toLowerCase();
|
|
70
|
-
if (cpu.includes('hypervisor')) hints.push('cpuinfo: hypervisor flag');
|
|
71
|
-
} catch (e) {}
|
|
72
|
-
}
|
|
73
|
-
return hints;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function showConsentDisclosure() {
|
|
77
|
-
console.log(`
|
|
78
|
-
${B}What ClawRTC will do:${NC}
|
|
79
|
-
|
|
80
|
-
${C}1. Extract${NC} Two Python scripts bundled with this package:
|
|
81
|
-
- fingerprint_checks.py (hardware detection)
|
|
82
|
-
- miner.py (attestation client)
|
|
83
|
-
${D}No external downloads — all code ships with the package.${NC}
|
|
84
|
-
|
|
85
|
-
${C}2. Install${NC} A Python virtual environment in ~/.clawrtc/
|
|
86
|
-
with one dependency: 'requests' (HTTP library)
|
|
87
|
-
|
|
88
|
-
${C}3. Attest${NC} When started, the miner contacts the RustChain network
|
|
89
|
-
every few minutes to prove your hardware is real.
|
|
90
|
-
Endpoint: ${NODE_URL} (CA-signed TLS certificate)
|
|
91
|
-
|
|
92
|
-
${C}4. Collect${NC} Hardware fingerprint data sent during attestation:
|
|
93
|
-
- CPU model, architecture, vendor
|
|
94
|
-
- Clock timing variance (proves real oscillator)
|
|
95
|
-
- Cache latency profile (proves real cache hierarchy)
|
|
96
|
-
- VM detection flags (hypervisor, DMI vendor)
|
|
97
|
-
${D}No personal data, files, browsing history, or credentials are collected.
|
|
98
|
-
No data is sent to any third party — only to the RustChain node.${NC}
|
|
99
|
-
|
|
100
|
-
${C}5. Earn${NC} RTC tokens accumulate in your wallet each epoch (~10 min)
|
|
101
|
-
|
|
102
|
-
${D}Verify yourself:${NC}
|
|
103
|
-
clawrtc install --dry-run Preview without installing
|
|
104
|
-
clawrtc install --verify Show SHA256 hashes of bundled files
|
|
105
|
-
Source code: https://github.com/Scottcjn/Rustchain
|
|
106
|
-
Block explorer: ${NODE_URL}/explorer
|
|
107
|
-
`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function cmdInstall(flags) {
|
|
111
|
-
console.log(`
|
|
112
|
-
${C}${B}
|
|
113
|
-
██████╗██╗ █████╗ ██╗ ██╗██████╗ ████████╗ ██████╗
|
|
114
|
-
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗╚══██╔══╝██╔════╝
|
|
115
|
-
██║ ██║ ███████║██║ █╗ ██║██████╔╝ ██║ ██║
|
|
116
|
-
██║ ██║ ██╔══██║██║███╗██║██╔══██╗ ██║ ██║
|
|
117
|
-
╚██████╗███████╗██║ ██║╚███╔███╔╝██║ ██║ ██║ ╚██████╗
|
|
118
|
-
╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
|
119
|
-
${NC}
|
|
120
|
-
${D} Mine RTC tokens with your AI agent on real hardware${NC}
|
|
121
|
-
${D} Modern x86/ARM = 1x | Vintage PowerPC = up to 2.5x | VM = ~0x${NC}
|
|
122
|
-
${D} Version ${VERSION}${NC}
|
|
123
|
-
`);
|
|
124
|
-
|
|
125
|
-
const plat = os.platform();
|
|
126
|
-
const arch = os.arch();
|
|
127
|
-
log(`Platform: ${plat} | Arch: ${arch}`);
|
|
128
|
-
|
|
129
|
-
if (plat !== 'linux' && plat !== 'darwin') {
|
|
130
|
-
console.error(`${R}[ERROR]${NC} Unsupported platform: ${plat}. Use Linux or macOS.`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// --verify: show bundled file hashes and exit
|
|
135
|
-
if (flags.verify) {
|
|
136
|
-
log('Bundled file hashes (SHA256):');
|
|
137
|
-
for (const [srcName, destName] of BUNDLED_FILES) {
|
|
138
|
-
const src = path.join(DATA_DIR, srcName);
|
|
139
|
-
if (fs.existsSync(src)) {
|
|
140
|
-
console.log(` ${destName}: ${sha256File(src)}`);
|
|
141
|
-
} else {
|
|
142
|
-
console.log(` ${destName}: NOT FOUND in package`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// --dry-run: show what would happen
|
|
149
|
-
if (flags.dryRun) {
|
|
150
|
-
showConsentDisclosure();
|
|
151
|
-
log('DRY RUN — no files extracted, no services created.');
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Show disclosure and get consent (unless --yes)
|
|
156
|
-
if (!flags.yes) {
|
|
157
|
-
showConsentDisclosure();
|
|
158
|
-
const answer = await ask(`${C}[clawrtc]${NC} Proceed with installation? [y/N] `);
|
|
159
|
-
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
160
|
-
log('Installation cancelled.');
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// VM check
|
|
166
|
-
const vmHints = detectVM();
|
|
167
|
-
if (vmHints.length > 0) {
|
|
168
|
-
console.log(`
|
|
169
|
-
${R}${B} ╔══════════════════════════════════════════════════════════╗
|
|
170
|
-
║ VM DETECTED — READ THIS ║
|
|
171
|
-
╠══════════════════════════════════════════════════════════╣
|
|
172
|
-
║ This machine appears to be a virtual machine. ║
|
|
173
|
-
║ RustChain will detect VMs and assign near-zero weight. ║
|
|
174
|
-
║ Your miner will attest but earn effectively nothing. ║
|
|
175
|
-
║ To earn RTC, run on bare-metal hardware. ║
|
|
176
|
-
╚══════════════════════════════════════════════════════════╝${NC}`);
|
|
177
|
-
for (const h of vmHints.slice(0, 4)) console.log(` ${R} * ${h}${NC}`);
|
|
178
|
-
console.log();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Wallet
|
|
182
|
-
let wallet = flags.wallet;
|
|
183
|
-
if (!wallet) {
|
|
184
|
-
wallet = await ask(`${C}[clawrtc]${NC} Enter agent wallet name (e.g. my-claw-agent): `);
|
|
185
|
-
}
|
|
186
|
-
if (!wallet) {
|
|
187
|
-
const host = os.hostname().split('.')[0] || 'agent';
|
|
188
|
-
wallet = `claw-${host}-${Date.now() % 100000}`;
|
|
189
|
-
warn(`No wallet provided. Auto-generated: ${wallet}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Create install dir
|
|
193
|
-
log(`Installing to ${INSTALL_DIR}`);
|
|
194
|
-
fs.mkdirSync(INSTALL_DIR, { recursive: true });
|
|
195
|
-
fs.writeFileSync(path.join(INSTALL_DIR, '.wallet'), wallet);
|
|
196
|
-
|
|
197
|
-
// Check for python3
|
|
198
|
-
let pythonBin = 'python3';
|
|
199
|
-
try { execSync('python3 --version', { stdio: 'pipe' }); }
|
|
200
|
-
catch (e) {
|
|
201
|
-
try { execSync('python --version', { stdio: 'pipe' }); pythonBin = 'python'; }
|
|
202
|
-
catch (e2) {
|
|
203
|
-
console.error(`${R}[ERROR]${NC} Python 3 not found. Install Python 3.8+ first.`);
|
|
204
|
-
process.exit(1);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Create venv
|
|
209
|
-
if (!fs.existsSync(VENV_DIR)) {
|
|
210
|
-
log('Creating Python environment...');
|
|
211
|
-
execSync(`${pythonBin} -m venv "${VENV_DIR}"`, { stdio: 'inherit' });
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Install deps
|
|
215
|
-
log('Installing dependencies...');
|
|
216
|
-
const pip = path.join(VENV_DIR, 'bin', 'pip');
|
|
217
|
-
execSync(`"${pip}" install --upgrade pip -q`, { stdio: 'pipe' });
|
|
218
|
-
execSync(`"${pip}" install requests -q`, { stdio: 'pipe' });
|
|
219
|
-
ok('Dependencies ready');
|
|
220
|
-
|
|
221
|
-
// Extract bundled miner files (no download!)
|
|
222
|
-
log('Extracting bundled miner scripts...');
|
|
223
|
-
for (const [srcName, destName] of BUNDLED_FILES) {
|
|
224
|
-
const src = path.join(DATA_DIR, srcName);
|
|
225
|
-
const dest = path.join(INSTALL_DIR, destName);
|
|
226
|
-
if (!fs.existsSync(src)) {
|
|
227
|
-
console.error(`${R}[ERROR]${NC} Bundled file missing: ${srcName}. Package may be corrupted.`);
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
fs.copyFileSync(src, dest);
|
|
231
|
-
const hash = sha256File(dest);
|
|
232
|
-
const size = fs.statSync(dest).size;
|
|
233
|
-
log(` ${destName} (${(size / 1024).toFixed(1)} KB) SHA256: ${hash.slice(0, 16)}...`);
|
|
234
|
-
}
|
|
235
|
-
ok('Miner files extracted from package (no external downloads)');
|
|
236
|
-
|
|
237
|
-
// Setup service ONLY if --service flag is passed
|
|
238
|
-
if (flags.service) {
|
|
239
|
-
log('Setting up background service (--service flag)...');
|
|
240
|
-
if (plat === 'linux') {
|
|
241
|
-
setupSystemd(wallet);
|
|
242
|
-
} else if (plat === 'darwin') {
|
|
243
|
-
setupLaunchd(wallet);
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
log('No background service created. To enable auto-start, re-run with --service');
|
|
247
|
-
log('Or start manually: clawrtc start');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Network check (CA-signed, no rejectUnauthorized needed)
|
|
251
|
-
log('Checking RustChain network...');
|
|
252
|
-
try {
|
|
253
|
-
const data = await new Promise((resolve, reject) => {
|
|
254
|
-
https.get(`${NODE_URL}/api/miners`, (res) => {
|
|
255
|
-
let d = '';
|
|
256
|
-
res.on('data', c => d += c);
|
|
257
|
-
res.on('end', () => resolve(d));
|
|
258
|
-
}).on('error', reject);
|
|
259
|
-
});
|
|
260
|
-
const miners = JSON.parse(data);
|
|
261
|
-
log(`Active miners on network: ${miners.length}`);
|
|
262
|
-
} catch (e) {
|
|
263
|
-
warn('Could not reach network (node may be temporarily unavailable)');
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Anonymous install telemetry — fire-and-forget, no PII
|
|
267
|
-
try {
|
|
268
|
-
const payload = JSON.stringify({
|
|
269
|
-
package: 'clawrtc', version: VERSION,
|
|
270
|
-
platform: os.platform(), arch: os.arch(), source: 'npm'
|
|
271
|
-
});
|
|
272
|
-
const req = https.request('https://bottube.ai/api/telemetry/install', {
|
|
273
|
-
method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
274
|
-
timeout: 5000
|
|
275
|
-
});
|
|
276
|
-
req.on('error', () => {});
|
|
277
|
-
req.end(payload);
|
|
278
|
-
} catch (e) {}
|
|
279
|
-
|
|
280
|
-
console.log(`
|
|
281
|
-
${G}${B}═══════════════════════════════════════════════════════════
|
|
282
|
-
ClawRTC installed! Your agent is ready to mine RTC.
|
|
283
|
-
|
|
284
|
-
Wallet: ${wallet}
|
|
285
|
-
Location: ${INSTALL_DIR}
|
|
286
|
-
Reward: 1x multiplier (modern hardware)
|
|
287
|
-
Node: ${NODE_URL} (CA-signed TLS)
|
|
288
|
-
|
|
289
|
-
Next steps:
|
|
290
|
-
clawrtc start Start mining (foreground)
|
|
291
|
-
clawrtc start --service Start + enable auto-restart
|
|
292
|
-
clawrtc stop Stop mining
|
|
293
|
-
clawrtc status Check miner + network status
|
|
294
|
-
clawrtc logs View miner output
|
|
295
|
-
|
|
296
|
-
How it works:
|
|
297
|
-
* Your agent proves real hardware via 6 fingerprint checks
|
|
298
|
-
* Attestation happens automatically every few minutes
|
|
299
|
-
* RTC tokens accumulate in your wallet each epoch (~10 min)
|
|
300
|
-
* Check balance: clawrtc status
|
|
301
|
-
|
|
302
|
-
Verify & audit:
|
|
303
|
-
* Source: https://github.com/Scottcjn/Rustchain
|
|
304
|
-
* Explorer: ${NODE_URL}/explorer
|
|
305
|
-
* clawrtc uninstall Remove everything cleanly
|
|
306
|
-
═══════════════════════════════════════════════════════════${NC}
|
|
307
|
-
`);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function setupSystemd(wallet) {
|
|
311
|
-
const svcDir = path.join(os.homedir(), '.config', 'systemd', 'user');
|
|
312
|
-
fs.mkdirSync(svcDir, { recursive: true });
|
|
313
|
-
const svcFile = path.join(svcDir, 'clawrtc-miner.service');
|
|
314
|
-
const pythonBin = path.join(VENV_DIR, 'bin', 'python');
|
|
315
|
-
const minerPy = path.join(INSTALL_DIR, 'miner.py');
|
|
316
|
-
|
|
317
|
-
fs.writeFileSync(svcFile, `[Unit]
|
|
318
|
-
Description=ClawRTC RTC Miner — AI Agent Mining
|
|
319
|
-
After=network-online.target
|
|
320
|
-
Wants=network-online.target
|
|
321
|
-
|
|
322
|
-
[Service]
|
|
323
|
-
ExecStart=${pythonBin} ${minerPy} --wallet ${wallet}
|
|
324
|
-
Restart=always
|
|
325
|
-
RestartSec=30
|
|
326
|
-
WorkingDirectory=${INSTALL_DIR}
|
|
327
|
-
Environment=PYTHONUNBUFFERED=1
|
|
328
|
-
|
|
329
|
-
[Install]
|
|
330
|
-
WantedBy=default.target
|
|
331
|
-
`);
|
|
332
|
-
|
|
333
|
-
try {
|
|
334
|
-
execSync('systemctl --user daemon-reload', { stdio: 'pipe' });
|
|
335
|
-
execSync('systemctl --user enable clawrtc-miner', { stdio: 'pipe' });
|
|
336
|
-
execSync('systemctl --user start clawrtc-miner', { stdio: 'pipe' });
|
|
337
|
-
ok('Service installed and started (auto-restarts on reboot)');
|
|
338
|
-
} catch (e) {
|
|
339
|
-
warn('Systemd user services not available. Use: clawrtc start');
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function setupLaunchd(wallet) {
|
|
344
|
-
const plistDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
345
|
-
fs.mkdirSync(plistDir, { recursive: true });
|
|
346
|
-
const plistFile = path.join(plistDir, 'com.clawrtc.miner.plist');
|
|
347
|
-
const pythonBin = path.join(VENV_DIR, 'bin', 'python');
|
|
348
|
-
const minerPy = path.join(INSTALL_DIR, 'miner.py');
|
|
349
|
-
|
|
350
|
-
fs.writeFileSync(plistFile, `<?xml version="1.0" encoding="UTF-8"?>
|
|
351
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
352
|
-
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
353
|
-
<plist version="1.0">
|
|
354
|
-
<dict>
|
|
355
|
-
<key>Label</key>
|
|
356
|
-
<string>com.clawrtc.miner</string>
|
|
357
|
-
<key>ProgramArguments</key>
|
|
358
|
-
<array>
|
|
359
|
-
<string>${pythonBin}</string>
|
|
360
|
-
<string>${minerPy}</string>
|
|
361
|
-
<string>--wallet</string>
|
|
362
|
-
<string>${wallet}</string>
|
|
363
|
-
</array>
|
|
364
|
-
<key>WorkingDirectory</key>
|
|
365
|
-
<string>${INSTALL_DIR}</string>
|
|
366
|
-
<key>RunAtLoad</key>
|
|
367
|
-
<true/>
|
|
368
|
-
<key>KeepAlive</key>
|
|
369
|
-
<true/>
|
|
370
|
-
<key>StandardOutPath</key>
|
|
371
|
-
<string>${path.join(INSTALL_DIR, 'miner.log')}</string>
|
|
372
|
-
<key>StandardErrorPath</key>
|
|
373
|
-
<string>${path.join(INSTALL_DIR, 'miner.err')}</string>
|
|
374
|
-
</dict>
|
|
375
|
-
</plist>`);
|
|
376
|
-
|
|
377
|
-
try {
|
|
378
|
-
execSync(`launchctl unload "${plistFile}" 2>/dev/null`, { stdio: 'pipe' });
|
|
379
|
-
} catch (e) {}
|
|
380
|
-
try {
|
|
381
|
-
execSync(`launchctl load "${plistFile}"`, { stdio: 'pipe' });
|
|
382
|
-
ok('LaunchAgent installed and loaded (auto-restarts on login)');
|
|
383
|
-
} catch (e) {
|
|
384
|
-
warn('Could not load LaunchAgent. Use: clawrtc start');
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function cmdStart(flags) {
|
|
389
|
-
const plat = os.platform();
|
|
390
|
-
|
|
391
|
-
if (flags.service) {
|
|
392
|
-
const wf = path.join(INSTALL_DIR, '.wallet');
|
|
393
|
-
const wallet = fs.existsSync(wf) ? fs.readFileSync(wf, 'utf8').trim() : 'agent';
|
|
394
|
-
if (plat === 'linux') setupSystemd(wallet);
|
|
395
|
-
else if (plat === 'darwin') setupLaunchd(wallet);
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (plat === 'linux') {
|
|
400
|
-
const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
|
|
401
|
-
if (fs.existsSync(sf)) {
|
|
402
|
-
try { execSync('systemctl --user start clawrtc-miner', { stdio: 'inherit' }); ok('Miner started (systemd)'); return; } catch (e) {}
|
|
403
|
-
}
|
|
404
|
-
} else if (plat === 'darwin') {
|
|
405
|
-
const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
|
|
406
|
-
if (fs.existsSync(pf)) {
|
|
407
|
-
try { execSync(`launchctl load "${pf}"`, { stdio: 'inherit' }); ok('Miner started (launchd)'); return; } catch (e) {}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const minerPy = path.join(INSTALL_DIR, 'miner.py');
|
|
412
|
-
const pythonBin = path.join(VENV_DIR, 'bin', 'python');
|
|
413
|
-
const wf = path.join(INSTALL_DIR, '.wallet');
|
|
414
|
-
|
|
415
|
-
if (!fs.existsSync(minerPy)) { console.error(`${R}[ERROR]${NC} Miner not installed. Run: clawrtc install`); process.exit(1); }
|
|
416
|
-
|
|
417
|
-
const wallet = fs.existsSync(wf) ? fs.readFileSync(wf, 'utf8').trim() : '';
|
|
418
|
-
const walletArgs = wallet ? ['--wallet', wallet] : [];
|
|
419
|
-
log('Starting miner in foreground (Ctrl+C to stop)...');
|
|
420
|
-
log('Tip: Use "clawrtc start --service" for background auto-restart');
|
|
421
|
-
const child = spawn(pythonBin, [minerPy, ...walletArgs], { stdio: 'inherit' });
|
|
422
|
-
child.on('exit', (code) => process.exit(code || 0));
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function cmdStop() {
|
|
426
|
-
if (os.platform() === 'linux') {
|
|
427
|
-
try { execSync('systemctl --user stop clawrtc-miner', { stdio: 'pipe' }); } catch (e) {}
|
|
428
|
-
} else if (os.platform() === 'darwin') {
|
|
429
|
-
const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
|
|
430
|
-
try { execSync(`launchctl unload "${pf}"`, { stdio: 'pipe' }); } catch (e) {}
|
|
431
|
-
}
|
|
432
|
-
ok('Miner stopped');
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function cmdStatus() {
|
|
436
|
-
const plat = os.platform();
|
|
437
|
-
if (plat === 'linux') {
|
|
438
|
-
const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
|
|
439
|
-
if (fs.existsSync(sf)) { try { execSync('systemctl --user status clawrtc-miner', { stdio: 'inherit' }); } catch (e) {} }
|
|
440
|
-
else log('No background service configured. Use: clawrtc start --service');
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const wf = path.join(INSTALL_DIR, '.wallet');
|
|
444
|
-
if (fs.existsSync(wf)) log(`Wallet: ${fs.readFileSync(wf, 'utf8').trim()}`);
|
|
445
|
-
|
|
446
|
-
for (const filename of ['miner.py', 'fingerprint_checks.py']) {
|
|
447
|
-
const fp = path.join(INSTALL_DIR, filename);
|
|
448
|
-
if (fs.existsSync(fp)) log(`${filename} SHA256: ${sha256File(fp).slice(0, 16)}...`);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
https.get(`${NODE_URL}/health`, (res) => {
|
|
452
|
-
let d = '';
|
|
453
|
-
res.on('data', c => d += c);
|
|
454
|
-
res.on('end', () => {
|
|
455
|
-
try { const h = JSON.parse(d); log(`Network: ${h.ok ? 'online' : 'offline'} (v${h.version || '?'})`); }
|
|
456
|
-
catch (e) { warn('Could not parse network status'); }
|
|
457
|
-
});
|
|
458
|
-
}).on('error', () => warn('Could not reach network'));
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function cmdLogs() {
|
|
462
|
-
if (os.platform() === 'linux') {
|
|
463
|
-
const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
|
|
464
|
-
if (fs.existsSync(sf)) { spawn('journalctl', ['--user', '-u', 'clawrtc-miner', '-f', '--no-pager', '-n', '50'], { stdio: 'inherit' }); }
|
|
465
|
-
else { const lf = path.join(INSTALL_DIR, 'miner.log'); if (fs.existsSync(lf)) spawn('tail', ['-f', lf], { stdio: 'inherit' }); else warn('No logs found.'); }
|
|
466
|
-
} else {
|
|
467
|
-
const lf = path.join(INSTALL_DIR, 'miner.log');
|
|
468
|
-
if (fs.existsSync(lf)) spawn('tail', ['-f', lf], { stdio: 'inherit' }); else warn('No log file found');
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function cmdUninstall() {
|
|
473
|
-
log('Stopping miner...');
|
|
474
|
-
cmdStop();
|
|
475
|
-
if (os.platform() === 'linux') {
|
|
476
|
-
try { execSync('systemctl --user disable clawrtc-miner', { stdio: 'pipe' }); } catch (e) {}
|
|
477
|
-
const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
|
|
478
|
-
try { fs.unlinkSync(sf); } catch (e) {}
|
|
479
|
-
try { execSync('systemctl --user daemon-reload', { stdio: 'pipe' }); } catch (e) {}
|
|
480
|
-
} else if (os.platform() === 'darwin') {
|
|
481
|
-
const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
|
|
482
|
-
try { fs.unlinkSync(pf); } catch (e) {}
|
|
483
|
-
}
|
|
484
|
-
try { fs.rmSync(INSTALL_DIR, { recursive: true, force: true }); } catch (e) {}
|
|
485
|
-
ok('ClawRTC miner fully uninstalled — no files remain');
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function showHelp() {
|
|
489
|
-
console.log(`
|
|
490
|
-
ClawRTC v${VERSION} — Mine RTC tokens with your AI agent on real hardware
|
|
491
|
-
|
|
492
|
-
Commands:
|
|
493
|
-
clawrtc install [--wallet NAME] Install miner and configure wallet
|
|
494
|
-
clawrtc start Start mining (foreground)
|
|
495
|
-
clawrtc start --service Start + create background service
|
|
496
|
-
clawrtc stop Stop mining
|
|
497
|
-
clawrtc status Check miner + network status + file hashes
|
|
498
|
-
clawrtc logs View miner output
|
|
499
|
-
clawrtc uninstall Remove everything cleanly
|
|
500
|
-
|
|
501
|
-
Security & Verification:
|
|
502
|
-
clawrtc install --dry-run Preview without installing
|
|
503
|
-
clawrtc install --verify Show SHA256 hashes of bundled files
|
|
504
|
-
clawrtc install -y Skip consent prompt (for CI/automation)
|
|
505
|
-
|
|
506
|
-
All miner code is bundled in the package. No external downloads.
|
|
507
|
-
Network endpoint: ${NODE_URL} (CA-signed TLS certificate)
|
|
508
|
-
|
|
509
|
-
Source: https://github.com/Scottcjn/Rustchain
|
|
510
|
-
`);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Parse flags
|
|
514
|
-
const args = process.argv.slice(2);
|
|
515
|
-
const cmd = args[0];
|
|
516
|
-
const flags = {
|
|
517
|
-
wallet: null,
|
|
518
|
-
dryRun: args.includes('--dry-run'),
|
|
519
|
-
verify: args.includes('--verify'),
|
|
520
|
-
service: args.includes('--service'),
|
|
521
|
-
yes: args.includes('-y') || args.includes('--yes'),
|
|
522
|
-
};
|
|
523
|
-
const walletIdx = args.indexOf('--wallet');
|
|
524
|
-
if (walletIdx >= 0) flags.wallet = args[walletIdx + 1];
|
|
525
|
-
|
|
526
|
-
switch (cmd) {
|
|
527
|
-
case 'install': cmdInstall(flags); break;
|
|
528
|
-
case 'start': cmdStart(flags); break;
|
|
529
|
-
case 'stop': cmdStop(); break;
|
|
530
|
-
case 'status': cmdStatus(); break;
|
|
531
|
-
case 'logs': cmdLogs(); break;
|
|
532
|
-
case 'uninstall': cmdUninstall(); break;
|
|
533
|
-
case '--help': case '-h': showHelp(); break;
|
|
534
|
-
case undefined: showHelp(); break;
|
|
535
|
-
default:
|
|
536
|
-
console.error(`Unknown command: ${cmd}. Use --help for usage.`);
|
|
537
|
-
process.exit(1);
|
|
538
|
-
}
|
|
File without changes
|