idlebench 1.0.0 → 1.0.2
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/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +107 -100
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +12 -2
- package/dist/index.js +3 -1
- package/dist/lib/docker.d.ts.map +1 -1
- package/dist/lib/docker.js +29 -29
- package/dist/lib/gpu.js +2 -2
- package/package.json +4 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AA8IA,wBAAsB,mBAAmB,kBAgMxC"}
|
package/dist/commands/run.js
CHANGED
|
@@ -9,23 +9,25 @@ import { getNetworkInfo } from '../lib/network.js';
|
|
|
9
9
|
import { runBenchmark } from '../lib/benchmark.js';
|
|
10
10
|
import { calculateGpuScore, calculateRuntimeScore, calculateNetworkScore, calculateInferenceScore, calculateStabilityScore, calculateFinalScore, getGrade, } from '../lib/scoring.js';
|
|
11
11
|
import { buildReport, saveReportLocally } from '../lib/report.js';
|
|
12
|
-
|
|
13
|
-
const
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const { version: CLI_VERSION } = require('../../package.json');
|
|
15
|
+
const DEFAULT_API_URL = 'https://idlebench.vercel.app';
|
|
14
16
|
function line() {
|
|
15
17
|
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
16
18
|
}
|
|
17
19
|
function header() {
|
|
18
20
|
console.log();
|
|
19
|
-
console.log(chalk.bold.white(' IdleBench'));
|
|
21
|
+
console.log(chalk.bold.white(' IdleBench') + chalk.dim(` v${CLI_VERSION}`));
|
|
20
22
|
console.log(chalk.dim(' The verification layer for idle compute.'));
|
|
21
23
|
console.log();
|
|
22
|
-
console.log(chalk.bgYellow.black(' SECURITY
|
|
24
|
+
console.log(chalk.bgYellow.black(' SECURITY ') + chalk.yellow(' IdleBench never asks for private keys or seed phrases.'));
|
|
23
25
|
console.log();
|
|
24
26
|
line();
|
|
25
27
|
console.log();
|
|
26
28
|
}
|
|
27
29
|
function checkRow(label, value, ok) {
|
|
28
|
-
const icon = ok === true ? chalk.green('✓') : ok === false ? chalk.red('✗') : chalk.
|
|
30
|
+
const icon = ok === true ? chalk.green('✓') : ok === false ? chalk.red('✗') : chalk.dim('—');
|
|
29
31
|
console.log(` ${icon} ${chalk.dim(label.padEnd(26))} ${chalk.white(value)}`);
|
|
30
32
|
}
|
|
31
33
|
function scoreBar(label, score, width = 20) {
|
|
@@ -34,6 +36,31 @@ function scoreBar(label, score, width = 20) {
|
|
|
34
36
|
const color = score >= 85 ? chalk.green : score >= 70 ? chalk.yellow : chalk.red;
|
|
35
37
|
console.log(` ${chalk.dim(label.padEnd(28))} ${color(bar)} ${chalk.bold(score)}`);
|
|
36
38
|
}
|
|
39
|
+
async function safeJsonFetch(url, options) {
|
|
40
|
+
let rawText = '';
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(url, options);
|
|
43
|
+
rawText = await res.text();
|
|
44
|
+
let json;
|
|
45
|
+
try {
|
|
46
|
+
json = JSON.parse(rawText);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
throw new Error(`Server returned non-JSON (HTTP ${res.status}). ` +
|
|
50
|
+
(rawText.length > 0 ? rawText.slice(0, 120) : 'Empty response.'));
|
|
51
|
+
}
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const msg = typeof json.error === 'string' ? json.error : `HTTP ${res.status}`;
|
|
54
|
+
throw new Error(msg);
|
|
55
|
+
}
|
|
56
|
+
return json;
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (err instanceof Error)
|
|
60
|
+
throw err;
|
|
61
|
+
throw new Error(`Network error: ${String(err)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
37
64
|
async function promptSetup() {
|
|
38
65
|
console.log(chalk.dim(' Answer a few questions to configure your benchmark.\n'));
|
|
39
66
|
const answers = await inquirer.prompt([
|
|
@@ -42,11 +69,11 @@ async function promptSetup() {
|
|
|
42
69
|
name: 'providerWallet',
|
|
43
70
|
message: 'Solana wallet address (public key):',
|
|
44
71
|
validate: (input) => {
|
|
45
|
-
|
|
72
|
+
const t = input.trim();
|
|
73
|
+
if (!t)
|
|
46
74
|
return 'Wallet address is required';
|
|
47
|
-
if (
|
|
48
|
-
return '
|
|
49
|
-
}
|
|
75
|
+
if (t.length < 32 || t.length > 44)
|
|
76
|
+
return 'Solana wallet address must be 32–44 characters';
|
|
50
77
|
return true;
|
|
51
78
|
},
|
|
52
79
|
},
|
|
@@ -74,7 +101,7 @@ async function promptSetup() {
|
|
|
74
101
|
{
|
|
75
102
|
type: 'input',
|
|
76
103
|
name: 'idleGatewayUrl',
|
|
77
|
-
message: 'IDLE
|
|
104
|
+
message: 'IDLE endpoint URL (from earnidle.com, optional — press Enter to skip):',
|
|
78
105
|
default: '',
|
|
79
106
|
validate: (input) => {
|
|
80
107
|
if (!input.trim())
|
|
@@ -84,22 +111,7 @@ async function promptSetup() {
|
|
|
84
111
|
return true;
|
|
85
112
|
}
|
|
86
113
|
catch {
|
|
87
|
-
return 'Enter a valid URL or leave blank';
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: 'input',
|
|
93
|
-
name: 'apiUrl',
|
|
94
|
-
message: `IdleBench API URL:`,
|
|
95
|
-
default: DEFAULT_API_URL,
|
|
96
|
-
validate: (input) => {
|
|
97
|
-
try {
|
|
98
|
-
new URL(input.trim());
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return 'Enter a valid URL';
|
|
114
|
+
return 'Enter a valid URL (e.g. https://gateway.earnidle.com/ep/...) or leave blank';
|
|
103
115
|
}
|
|
104
116
|
},
|
|
105
117
|
},
|
|
@@ -109,7 +121,7 @@ async function promptSetup() {
|
|
|
109
121
|
resourceName: answers.resourceName.trim(),
|
|
110
122
|
resourceType: answers.resourceType,
|
|
111
123
|
idleGatewayUrl: answers.idleGatewayUrl.trim() || null,
|
|
112
|
-
apiUrl:
|
|
124
|
+
apiUrl: process.env.IDLEBENCH_API_URL?.trim() ?? DEFAULT_API_URL,
|
|
113
125
|
};
|
|
114
126
|
}
|
|
115
127
|
export async function runBenchmarkCommand() {
|
|
@@ -135,7 +147,7 @@ export async function runBenchmarkCommand() {
|
|
|
135
147
|
}
|
|
136
148
|
catch (err) {
|
|
137
149
|
spinner.fail('Failed to read system info');
|
|
138
|
-
throw err;
|
|
150
|
+
throw new Error(`System check failed: ${err instanceof Error ? err.message : err}`);
|
|
139
151
|
}
|
|
140
152
|
}
|
|
141
153
|
console.log();
|
|
@@ -143,19 +155,25 @@ export async function runBenchmarkCommand() {
|
|
|
143
155
|
let gpu;
|
|
144
156
|
{
|
|
145
157
|
const spinner = ora({ text: 'Detecting GPU', color: 'green' }).start();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
try {
|
|
159
|
+
gpu = await getGpuInfo();
|
|
160
|
+
spinner.succeed(chalk.dim('GPU detection'));
|
|
161
|
+
if (gpu.name) {
|
|
162
|
+
checkRow('GPU Model', gpu.name, true);
|
|
163
|
+
if (gpu.vramGb)
|
|
164
|
+
checkRow('VRAM', `${gpu.vramGb} GB`);
|
|
165
|
+
checkRow('CUDA', gpu.cudaAvailable ? `${gpu.cudaVersion ?? 'Available'}` : 'Not available', gpu.cudaAvailable);
|
|
166
|
+
if (gpu.driverVersion)
|
|
167
|
+
checkRow('NVIDIA Driver', gpu.driverVersion, true);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
checkRow('GPU', 'Not detected', null);
|
|
171
|
+
console.log(chalk.dim(' → No GPU found. Running CPU-only checks.'));
|
|
172
|
+
}
|
|
155
173
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
174
|
+
catch (err) {
|
|
175
|
+
spinner.warn('GPU detection failed — continuing');
|
|
176
|
+
gpu = { name: null, vendor: null, vramGb: null, cudaAvailable: false, cudaVersion: null, driverVersion: null, temperature: null, utilizationPercent: null, detectionMethod: 'failed' };
|
|
159
177
|
}
|
|
160
178
|
}
|
|
161
179
|
console.log();
|
|
@@ -163,72 +181,62 @@ export async function runBenchmarkCommand() {
|
|
|
163
181
|
let docker;
|
|
164
182
|
{
|
|
165
183
|
const spinner = ora({ text: 'Checking Docker', color: 'green' }).start();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (docker.
|
|
171
|
-
checkRow('Docker GPU Runtime', 'Available',
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
checkRow('Docker GPU Runtime', docker.gpuRuntimeError ?? 'Not available', false);
|
|
184
|
+
try {
|
|
185
|
+
docker = await getDockerInfo();
|
|
186
|
+
spinner.succeed(chalk.dim('Docker check'));
|
|
187
|
+
checkRow('Docker', docker.available ? `v${docker.version}` : 'Not installed', docker.available);
|
|
188
|
+
if (docker.available) {
|
|
189
|
+
checkRow('Docker GPU Runtime', docker.gpuRuntime ? 'Available' : (docker.gpuRuntimeError ?? 'Not available'), docker.gpuRuntime);
|
|
175
190
|
}
|
|
176
191
|
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
spinner.warn('Docker check failed — continuing');
|
|
194
|
+
docker = { available: false, version: null, gpuRuntime: false, gpuRuntimeError: 'Check failed' };
|
|
195
|
+
}
|
|
177
196
|
}
|
|
178
197
|
console.log();
|
|
179
198
|
// Benchmark
|
|
180
199
|
let benchmarkResult;
|
|
181
200
|
{
|
|
182
201
|
const spinner = ora({ text: 'Running benchmark', color: 'green' }).start();
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
202
|
+
try {
|
|
203
|
+
benchmarkResult = await runBenchmark();
|
|
204
|
+
spinner.succeed(chalk.dim('Benchmark complete'));
|
|
205
|
+
checkRow('CPU hash workload', `${benchmarkResult.cpuHashMs}ms`);
|
|
206
|
+
checkRow('Matrix calculation', `${benchmarkResult.matrixMs}ms`);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
spinner.warn('Benchmark failed — using defaults');
|
|
210
|
+
benchmarkResult = { cpuHashMs: 500, matrixMs: 500, totalMs: 1000 };
|
|
211
|
+
}
|
|
187
212
|
}
|
|
188
213
|
console.log();
|
|
189
214
|
// Network
|
|
190
215
|
let network;
|
|
191
216
|
{
|
|
192
217
|
const spinner = ora({ text: 'Testing network', color: 'green' }).start();
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
218
|
+
try {
|
|
219
|
+
network = await getNetworkInfo(apiUrl);
|
|
220
|
+
spinner.succeed(chalk.dim('Network check'));
|
|
221
|
+
checkRow('Latency', network.latencyMs !== null ? `${network.latencyMs}ms` : 'Could not measure', network.latencyMs !== null && network.latencyMs < 500);
|
|
222
|
+
if (network.downloadMbps !== null)
|
|
223
|
+
checkRow('Download estimate', `${network.downloadMbps} Mbps`);
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
spinner.warn('Network check failed — continuing');
|
|
227
|
+
network = { latencyMs: null, downloadMbps: null, uploadMbps: null };
|
|
198
228
|
}
|
|
199
229
|
}
|
|
200
230
|
console.log();
|
|
201
231
|
line();
|
|
202
232
|
console.log();
|
|
203
233
|
// Calculate scores
|
|
204
|
-
const gpuScore = calculateGpuScore({
|
|
205
|
-
|
|
206
|
-
vramGb: gpu.vramGb,
|
|
207
|
-
cpuCores: system.cpuCores,
|
|
208
|
-
ramGb: system.ramGb,
|
|
209
|
-
cudaAvailable: gpu.cudaAvailable,
|
|
210
|
-
dockerAvailable: docker.available,
|
|
211
|
-
dockerGpuRuntime: docker.gpuRuntime,
|
|
212
|
-
});
|
|
213
|
-
const runtimeScore = calculateRuntimeScore({
|
|
214
|
-
gpuName: gpu.name,
|
|
215
|
-
vramGb: gpu.vramGb,
|
|
216
|
-
cpuCores: system.cpuCores,
|
|
217
|
-
ramGb: system.ramGb,
|
|
218
|
-
cudaAvailable: gpu.cudaAvailable,
|
|
219
|
-
dockerAvailable: docker.available,
|
|
220
|
-
dockerGpuRuntime: docker.gpuRuntime,
|
|
221
|
-
});
|
|
234
|
+
const gpuScore = calculateGpuScore({ gpuName: gpu.name, vramGb: gpu.vramGb, cpuCores: system.cpuCores, ramGb: system.ramGb, cudaAvailable: gpu.cudaAvailable, dockerAvailable: docker.available, dockerGpuRuntime: docker.gpuRuntime });
|
|
235
|
+
const runtimeScore = calculateRuntimeScore({ gpuName: gpu.name, vramGb: gpu.vramGb, cpuCores: system.cpuCores, ramGb: system.ramGb, cudaAvailable: gpu.cudaAvailable, dockerAvailable: docker.available, dockerGpuRuntime: docker.gpuRuntime });
|
|
222
236
|
const networkScore = calculateNetworkScore(network.latencyMs);
|
|
223
237
|
const inferenceScore = calculateInferenceScore(system.cpuCores, system.ramGb, benchmarkResult.totalMs, gpu.name);
|
|
224
238
|
const stabilityScore = calculateStabilityScore(system.cpuCores, system.ramGb);
|
|
225
|
-
const finalScore = calculateFinalScore({
|
|
226
|
-
gpuScore,
|
|
227
|
-
runtimeScore,
|
|
228
|
-
networkScore,
|
|
229
|
-
inferenceScore,
|
|
230
|
-
stabilityScore,
|
|
231
|
-
});
|
|
239
|
+
const finalScore = calculateFinalScore({ gpuScore, runtimeScore, networkScore, inferenceScore, stabilityScore });
|
|
232
240
|
const grade = getGrade(finalScore);
|
|
233
241
|
console.log(chalk.bold(' Score breakdown'));
|
|
234
242
|
console.log();
|
|
@@ -243,7 +251,7 @@ export async function runBenchmarkCommand() {
|
|
|
243
251
|
console.log();
|
|
244
252
|
line();
|
|
245
253
|
console.log();
|
|
246
|
-
// Build report
|
|
254
|
+
// Build + save report
|
|
247
255
|
const report = buildReport({
|
|
248
256
|
reportVersion: '1.0.0',
|
|
249
257
|
providerWallet,
|
|
@@ -258,27 +266,25 @@ export async function runBenchmarkCommand() {
|
|
|
258
266
|
benchmarkMs: benchmarkResult.totalMs,
|
|
259
267
|
cliVersion: CLI_VERSION,
|
|
260
268
|
});
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
let localPath;
|
|
270
|
+
try {
|
|
271
|
+
localPath = saveReportLocally(report);
|
|
272
|
+
console.log(chalk.dim(` Report saved locally: ${localPath}`));
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
localPath = 'unknown';
|
|
276
|
+
console.log(chalk.dim(' Could not save report locally.'));
|
|
277
|
+
}
|
|
264
278
|
console.log();
|
|
265
279
|
// Upload
|
|
266
280
|
const uploadSpinner = ora({ text: `Uploading to ${apiUrl}`, color: 'green' }).start();
|
|
267
281
|
try {
|
|
268
|
-
const
|
|
282
|
+
const json = await safeJsonFetch(`${apiUrl}/api/reports/upload`, {
|
|
269
283
|
method: 'POST',
|
|
270
284
|
headers: { 'Content-Type': 'application/json' },
|
|
271
285
|
body: JSON.stringify(report),
|
|
272
286
|
signal: AbortSignal.timeout(30000),
|
|
273
287
|
});
|
|
274
|
-
const json = await res.json();
|
|
275
|
-
if (!res.ok) {
|
|
276
|
-
uploadSpinner.fail('Upload failed');
|
|
277
|
-
console.error(chalk.red(` ✗ Server error: ${JSON.stringify(json)}`));
|
|
278
|
-
console.log(chalk.dim(` Your report was saved locally: ${localPath}`));
|
|
279
|
-
console.log(chalk.dim(` You can upload it later with: idlebench upload ${localPath}`));
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
288
|
uploadSpinner.succeed('Report uploaded');
|
|
283
289
|
console.log();
|
|
284
290
|
console.log(chalk.bold(' Benchmark complete.'));
|
|
@@ -288,15 +294,16 @@ export async function runBenchmarkCommand() {
|
|
|
288
294
|
console.log(chalk.dim(' Public URL: '), chalk.cyan(String(json.publicUrl)));
|
|
289
295
|
console.log(chalk.dim(' Local report: '), chalk.dim(localPath));
|
|
290
296
|
console.log();
|
|
291
|
-
console.log(chalk.dim(
|
|
292
|
-
console.log(chalk.dim(` Badge URL: ${apiUrl}/api/badge/${json.resourceId}`));
|
|
297
|
+
console.log(chalk.dim(` Badge: ${apiUrl}/api/badge/${json.resourceId}`));
|
|
293
298
|
console.log();
|
|
294
299
|
}
|
|
295
300
|
catch (err) {
|
|
296
|
-
uploadSpinner.fail('Upload failed
|
|
301
|
+
uploadSpinner.fail('Upload failed');
|
|
302
|
+
console.error();
|
|
297
303
|
console.error(chalk.red(` ✗ ${err instanceof Error ? err.message : err}`));
|
|
298
|
-
console.
|
|
304
|
+
console.error();
|
|
299
305
|
console.log(chalk.dim(` Your report was saved locally: ${localPath}`));
|
|
300
306
|
console.log(chalk.dim(` Upload later with: idlebench upload ${localPath}`));
|
|
307
|
+
console.log();
|
|
301
308
|
}
|
|
302
309
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAqCA,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAqCA,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,iBAoE7E"}
|
package/dist/commands/upload.js
CHANGED
|
@@ -65,10 +65,20 @@ export async function runUpload(filePath, options) {
|
|
|
65
65
|
body: rawJson,
|
|
66
66
|
signal: AbortSignal.timeout(30000),
|
|
67
67
|
});
|
|
68
|
-
const
|
|
68
|
+
const rawText = await res.text();
|
|
69
|
+
let json;
|
|
70
|
+
try {
|
|
71
|
+
json = JSON.parse(rawText);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
spinner.fail('Upload failed');
|
|
75
|
+
console.error(chalk.red(` ✗ Server returned non-JSON (HTTP ${res.status}):`));
|
|
76
|
+
console.error(chalk.dim(` ${rawText.slice(0, 200)}`));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
69
79
|
if (!res.ok) {
|
|
70
80
|
spinner.fail('Upload failed');
|
|
71
|
-
console.error(chalk.red(` ✗ ${JSON.stringify(json)}`));
|
|
81
|
+
console.error(chalk.red(` ✗ ${typeof json.error === 'string' ? json.error : JSON.stringify(json)}`));
|
|
72
82
|
process.exit(1);
|
|
73
83
|
}
|
|
74
84
|
spinner.succeed('Report uploaded');
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
+
import { createRequire } from 'module';
|
|
4
5
|
import { runBenchmarkCommand } from './commands/run.js';
|
|
5
6
|
import { runDoctor } from './commands/doctor.js';
|
|
6
7
|
import { runUpload } from './commands/upload.js';
|
|
7
|
-
const
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const { version: VERSION } = require('../package.json');
|
|
8
10
|
const program = new Command();
|
|
9
11
|
program
|
|
10
12
|
.name('idlebench')
|
package/dist/lib/docker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/lib/docker.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,EAAE,OAAO,CAAA;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/lib/docker.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,EAAE,OAAO,CAAA;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAeD,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAuCzD"}
|
package/dist/lib/docker.js
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
import { execa } from 'execa';
|
|
2
|
-
|
|
3
|
-
// Check if docker is installed
|
|
4
|
-
let version = null;
|
|
2
|
+
async function runCmd(cmd, args, timeoutMs = 8000) {
|
|
5
3
|
try {
|
|
6
|
-
const { stdout } = await execa(
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
const { stdout } = await execa(cmd, args, {
|
|
5
|
+
timeout: timeoutMs,
|
|
6
|
+
shell: true, // use system shell — resolves PATH on Windows
|
|
7
|
+
reject: true,
|
|
8
|
+
});
|
|
9
|
+
return stdout.trim();
|
|
9
10
|
}
|
|
10
11
|
catch {
|
|
11
|
-
return
|
|
12
|
+
return null;
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
}
|
|
15
|
+
export async function getDockerInfo() {
|
|
16
|
+
// Check version
|
|
17
|
+
const versionOut = await runCmd('docker', ['--version']);
|
|
18
|
+
if (!versionOut) {
|
|
19
|
+
return { available: false, version: null, gpuRuntime: false, gpuRuntimeError: 'Docker not found in PATH' };
|
|
16
20
|
}
|
|
17
|
-
|
|
21
|
+
const match = versionOut.match(/Docker version ([\d.]+)/);
|
|
22
|
+
const version = match ? match[1] : versionOut.split('\n')[0];
|
|
23
|
+
// Check daemon is running
|
|
24
|
+
const infoOut = await runCmd('docker', ['info', '--format', '{{.ServerVersion}}'], 10000);
|
|
25
|
+
if (!infoOut) {
|
|
18
26
|
return { available: false, version, gpuRuntime: false, gpuRuntimeError: 'Docker daemon not running' };
|
|
19
27
|
}
|
|
20
|
-
//
|
|
28
|
+
// Test GPU passthrough
|
|
21
29
|
let gpuRuntime = false;
|
|
22
30
|
let gpuRuntimeError = null;
|
|
23
31
|
try {
|
|
24
|
-
await execa('docker', [
|
|
25
|
-
|
|
26
|
-
'--gpus', 'all',
|
|
27
|
-
'nvidia/cuda:12.3.0-base-ubuntu22.04',
|
|
28
|
-
'nvidia-smi', '-L',
|
|
29
|
-
], { timeout: 60000 });
|
|
30
|
-
gpuRuntime = true;
|
|
32
|
+
const { stdout } = await execa('docker', ['run', '--rm', '--gpus', 'all', 'nvidia/cuda:12.3.0-base-ubuntu22.04', 'nvidia-smi', '-L'], { timeout: 60000, shell: true });
|
|
33
|
+
gpuRuntime = stdout.length > 0;
|
|
31
34
|
}
|
|
32
35
|
catch (err) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (gpuRuntimeError.includes('could not select device driver')) {
|
|
36
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
37
|
+
if (msg.includes('could not select device driver') || msg.includes('nvidia')) {
|
|
36
38
|
gpuRuntimeError = 'NVIDIA container toolkit not installed';
|
|
37
39
|
}
|
|
38
|
-
else if (
|
|
40
|
+
else if (msg.includes('Unable to find image')) {
|
|
39
41
|
gpuRuntimeError = 'Could not pull test image';
|
|
40
42
|
}
|
|
43
|
+
else {
|
|
44
|
+
gpuRuntimeError = 'GPU runtime not available';
|
|
45
|
+
}
|
|
41
46
|
}
|
|
42
|
-
return {
|
|
43
|
-
available: true,
|
|
44
|
-
version,
|
|
45
|
-
gpuRuntime,
|
|
46
|
-
gpuRuntimeError,
|
|
47
|
-
};
|
|
47
|
+
return { available: true, version, gpuRuntime, gpuRuntimeError };
|
|
48
48
|
}
|
package/dist/lib/gpu.js
CHANGED
|
@@ -5,7 +5,7 @@ async function tryNvidiaSmi() {
|
|
|
5
5
|
const { stdout } = await execa('nvidia-smi', [
|
|
6
6
|
'--query-gpu=name,memory.total,driver_version,utilization.gpu,temperature.gpu',
|
|
7
7
|
'--format=csv,noheader,nounits',
|
|
8
|
-
], { timeout: 8000 });
|
|
8
|
+
], { timeout: 8000, shell: true });
|
|
9
9
|
const lines = stdout.trim().split('\n');
|
|
10
10
|
if (lines.length === 0)
|
|
11
11
|
return null;
|
|
@@ -15,7 +15,7 @@ async function tryNvidiaSmi() {
|
|
|
15
15
|
let cudaVersion = null;
|
|
16
16
|
let cudaAvailable = false;
|
|
17
17
|
try {
|
|
18
|
-
const { stdout: smiOut } = await execa('nvidia-smi', [], { timeout: 5000 });
|
|
18
|
+
const { stdout: smiOut } = await execa('nvidia-smi', [], { timeout: 5000, shell: true });
|
|
19
19
|
const cudaMatch = smiOut.match(/CUDA Version:\s*([\d.]+)/);
|
|
20
20
|
if (cudaMatch) {
|
|
21
21
|
cudaVersion = cudaMatch[1];
|
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "idlebench",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Benchmark and verify idle compute before it earns. The CLI for IDLE compute providers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "IdleBench",
|
|
7
|
-
"
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/idlebench/idlebench-cli"
|
|
10
|
-
},
|
|
7
|
+
"homepage": "https://idlebench.com",
|
|
11
8
|
"keywords": [
|
|
12
9
|
"benchmark",
|
|
13
10
|
"gpu",
|
|
@@ -24,7 +21,8 @@
|
|
|
24
21
|
"files": [
|
|
25
22
|
"dist",
|
|
26
23
|
"README.md",
|
|
27
|
-
"LICENSE"
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"package.json"
|
|
28
26
|
],
|
|
29
27
|
"scripts": {
|
|
30
28
|
"build": "tsc",
|