idlebench 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.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +107 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +302 -0
- package/dist/commands/upload.d.ts +4 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +88 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/lib/benchmark.d.ts +7 -0
- package/dist/lib/benchmark.d.ts.map +1 -0
- package/dist/lib/benchmark.js +34 -0
- package/dist/lib/docker.d.ts +8 -0
- package/dist/lib/docker.d.ts.map +1 -0
- package/dist/lib/docker.js +48 -0
- package/dist/lib/gpu.d.ts +13 -0
- package/dist/lib/gpu.d.ts.map +1 -0
- package/dist/lib/gpu.js +86 -0
- package/dist/lib/network.d.ts +9 -0
- package/dist/lib/network.d.ts.map +1 -0
- package/dist/lib/network.js +62 -0
- package/dist/lib/report.d.ts +76 -0
- package/dist/lib/report.d.ts.map +1 -0
- package/dist/lib/report.js +53 -0
- package/dist/lib/scoring.d.ts +24 -0
- package/dist/lib/scoring.d.ts.map +1 -0
- package/dist/lib/scoring.js +125 -0
- package/dist/lib/system.d.ts +10 -0
- package/dist/lib/system.d.ts.map +1 -0
- package/dist/lib/system.js +19 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { runBenchmarkCommand } from './commands/run.js';
|
|
5
|
+
import { runDoctor } from './commands/doctor.js';
|
|
6
|
+
import { runUpload } from './commands/upload.js';
|
|
7
|
+
const VERSION = '1.0.0';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('idlebench')
|
|
11
|
+
.description('Benchmark and verify idle compute before it earns.')
|
|
12
|
+
.version(VERSION, '-v, --version', 'Show CLI version');
|
|
13
|
+
program
|
|
14
|
+
.command('run')
|
|
15
|
+
.description('Run a local benchmark and upload the report to IdleBench')
|
|
16
|
+
.option('--api-url <url>', 'IdleBench API URL (default: https://idlebench.com)')
|
|
17
|
+
.action(async (_options) => {
|
|
18
|
+
try {
|
|
19
|
+
await runBenchmarkCommand();
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.error();
|
|
23
|
+
console.error(chalk.red(' ✗ Benchmark failed:'), err instanceof Error ? err.message : err);
|
|
24
|
+
console.error();
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
program
|
|
29
|
+
.command('doctor')
|
|
30
|
+
.description('Check local environment for benchmark readiness')
|
|
31
|
+
.option('--api-url <url>', 'IdleBench API URL to test connectivity')
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
try {
|
|
34
|
+
await runDoctor(options);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error(chalk.red(' ✗ Doctor failed:'), err instanceof Error ? err.message : err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
program
|
|
42
|
+
.command('upload <path>')
|
|
43
|
+
.description('Upload an existing benchmark report JSON')
|
|
44
|
+
.option('--api-url <url>', 'IdleBench API URL (default: https://idlebench.com)')
|
|
45
|
+
.action(async (filePath, options) => {
|
|
46
|
+
try {
|
|
47
|
+
await runUpload(filePath, options);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error(chalk.red(' ✗ Upload failed:'), err instanceof Error ? err.message : err);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
program
|
|
55
|
+
.command('version')
|
|
56
|
+
.description('Show CLI version')
|
|
57
|
+
.action(() => {
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(chalk.bold('idlebench'), chalk.dim(`v${VERSION}`));
|
|
60
|
+
console.log(chalk.dim('The verification layer for idle compute.'));
|
|
61
|
+
console.log();
|
|
62
|
+
});
|
|
63
|
+
// Default: show help if no args
|
|
64
|
+
if (process.argv.length <= 2) {
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(chalk.bold(' IdleBench') + chalk.dim(` v${VERSION}`));
|
|
67
|
+
console.log(chalk.dim(' The verification layer for idle compute.'));
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(chalk.dim(' Usage:'));
|
|
70
|
+
console.log(` ${chalk.white('npx idlebench run')} ${chalk.dim('Run benchmark and publish score')}`);
|
|
71
|
+
console.log(` ${chalk.white('npx idlebench doctor')} ${chalk.dim('Check environment readiness')}`);
|
|
72
|
+
console.log(` ${chalk.white('npx idlebench upload')} ${chalk.dim('<path> Upload saved report')}`);
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(chalk.dim(' Run ') + chalk.white('idlebench --help') + chalk.dim(' for full options.'));
|
|
75
|
+
console.log();
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark.d.ts","sourceRoot":"","sources":["../../src/lib/benchmark.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAgCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC,CAY7D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
function hashWorkload(iterations) {
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
let hash = '';
|
|
5
|
+
for (let i = 0; i < iterations; i++) {
|
|
6
|
+
hash = createHash('sha256').update(hash + i.toString()).digest('hex');
|
|
7
|
+
}
|
|
8
|
+
return Date.now() - start;
|
|
9
|
+
}
|
|
10
|
+
function matrixMultiply(size) {
|
|
11
|
+
const start = Date.now();
|
|
12
|
+
const a = Array.from({ length: size }, (_, i) => Array.from({ length: size }, (_, j) => i * size + j));
|
|
13
|
+
const b = Array.from({ length: size }, (_, i) => Array.from({ length: size }, (_, j) => j * size + i));
|
|
14
|
+
const c = Array.from({ length: size }, () => new Array(size).fill(0));
|
|
15
|
+
for (let i = 0; i < size; i++) {
|
|
16
|
+
for (let k = 0; k < size; k++) {
|
|
17
|
+
for (let j = 0; j < size; j++) {
|
|
18
|
+
c[i][j] += a[i][k] * b[k][j];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Date.now() - start;
|
|
23
|
+
}
|
|
24
|
+
export async function runBenchmark() {
|
|
25
|
+
// CPU hash workload (2048 iterations of SHA-256)
|
|
26
|
+
const cpuHashMs = hashWorkload(2048);
|
|
27
|
+
// Matrix multiplication 128x128
|
|
28
|
+
const matrixMs = matrixMultiply(128);
|
|
29
|
+
return {
|
|
30
|
+
cpuHashMs,
|
|
31
|
+
matrixMs,
|
|
32
|
+
totalMs: cpuHashMs + matrixMs,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +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;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAkDzD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
export async function getDockerInfo() {
|
|
3
|
+
// Check if docker is installed
|
|
4
|
+
let version = null;
|
|
5
|
+
try {
|
|
6
|
+
const { stdout } = await execa('docker', ['--version'], { timeout: 6000 });
|
|
7
|
+
const match = stdout.match(/Docker version ([\d.]+)/);
|
|
8
|
+
version = match ? match[1] : stdout.trim().split('\n')[0];
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { available: false, version: null, gpuRuntime: false, gpuRuntimeError: 'Docker not installed' };
|
|
12
|
+
}
|
|
13
|
+
// Check docker daemon is running
|
|
14
|
+
try {
|
|
15
|
+
await execa('docker', ['info'], { timeout: 8000 });
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { available: false, version, gpuRuntime: false, gpuRuntimeError: 'Docker daemon not running' };
|
|
19
|
+
}
|
|
20
|
+
// Try GPU runtime
|
|
21
|
+
let gpuRuntime = false;
|
|
22
|
+
let gpuRuntimeError = null;
|
|
23
|
+
try {
|
|
24
|
+
await execa('docker', [
|
|
25
|
+
'run', '--rm',
|
|
26
|
+
'--gpus', 'all',
|
|
27
|
+
'nvidia/cuda:12.3.0-base-ubuntu22.04',
|
|
28
|
+
'nvidia-smi', '-L',
|
|
29
|
+
], { timeout: 60000 });
|
|
30
|
+
gpuRuntime = true;
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
gpuRuntimeError = err instanceof Error ? err.message : 'GPU runtime test failed';
|
|
34
|
+
// Check if it's a known non-GPU error vs docker error
|
|
35
|
+
if (gpuRuntimeError.includes('could not select device driver')) {
|
|
36
|
+
gpuRuntimeError = 'NVIDIA container toolkit not installed';
|
|
37
|
+
}
|
|
38
|
+
else if (gpuRuntimeError.includes('Unable to find image')) {
|
|
39
|
+
gpuRuntimeError = 'Could not pull test image';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
available: true,
|
|
44
|
+
version,
|
|
45
|
+
gpuRuntime,
|
|
46
|
+
gpuRuntimeError,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface GpuInfo {
|
|
2
|
+
name: string | null;
|
|
3
|
+
vendor: string | null;
|
|
4
|
+
vramGb: number | null;
|
|
5
|
+
cudaAvailable: boolean;
|
|
6
|
+
cudaVersion: string | null;
|
|
7
|
+
driverVersion: string | null;
|
|
8
|
+
temperature: number | null;
|
|
9
|
+
utilizationPercent: number | null;
|
|
10
|
+
detectionMethod: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function getGpuInfo(): Promise<GpuInfo>;
|
|
13
|
+
//# sourceMappingURL=gpu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu.d.ts","sourceRoot":"","sources":["../../src/lib/gpu.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,aAAa,EAAE,OAAO,CAAA;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,eAAe,EAAE,MAAM,CAAA;CACxB;AAiED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CA0BnD"}
|
package/dist/lib/gpu.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import si from 'systeminformation';
|
|
3
|
+
async function tryNvidiaSmi() {
|
|
4
|
+
try {
|
|
5
|
+
const { stdout } = await execa('nvidia-smi', [
|
|
6
|
+
'--query-gpu=name,memory.total,driver_version,utilization.gpu,temperature.gpu',
|
|
7
|
+
'--format=csv,noheader,nounits',
|
|
8
|
+
], { timeout: 8000 });
|
|
9
|
+
const lines = stdout.trim().split('\n');
|
|
10
|
+
if (lines.length === 0)
|
|
11
|
+
return null;
|
|
12
|
+
const parts = lines[0].split(',').map(s => s.trim());
|
|
13
|
+
const [name, vramMb, driver, utilization, temperature] = parts;
|
|
14
|
+
// Get CUDA version from nvcc or nvidia-smi
|
|
15
|
+
let cudaVersion = null;
|
|
16
|
+
let cudaAvailable = false;
|
|
17
|
+
try {
|
|
18
|
+
const { stdout: smiOut } = await execa('nvidia-smi', [], { timeout: 5000 });
|
|
19
|
+
const cudaMatch = smiOut.match(/CUDA Version:\s*([\d.]+)/);
|
|
20
|
+
if (cudaMatch) {
|
|
21
|
+
cudaVersion = cudaMatch[1];
|
|
22
|
+
cudaAvailable = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch { }
|
|
26
|
+
return {
|
|
27
|
+
name: name || null,
|
|
28
|
+
vendor: 'NVIDIA',
|
|
29
|
+
vramGb: vramMb ? Math.round((parseFloat(vramMb) / 1024) * 10) / 10 : null,
|
|
30
|
+
driverVersion: driver || null,
|
|
31
|
+
cudaAvailable,
|
|
32
|
+
cudaVersion,
|
|
33
|
+
temperature: temperature ? parseFloat(temperature) : null,
|
|
34
|
+
utilizationPercent: utilization ? parseFloat(utilization) : null,
|
|
35
|
+
detectionMethod: 'nvidia-smi',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function trySysinformation() {
|
|
43
|
+
try {
|
|
44
|
+
const graphics = await si.graphics();
|
|
45
|
+
if (!graphics.controllers || graphics.controllers.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
const gpu = graphics.controllers[0];
|
|
48
|
+
if (!gpu.model)
|
|
49
|
+
return null;
|
|
50
|
+
const isNvidia = gpu.vendor?.toLowerCase().includes('nvidia') || gpu.model?.toLowerCase().includes('nvidia');
|
|
51
|
+
return {
|
|
52
|
+
name: gpu.model,
|
|
53
|
+
vendor: gpu.vendor || (isNvidia ? 'NVIDIA' : null),
|
|
54
|
+
vramGb: gpu.vram ? Math.round((gpu.vram / 1024) * 10) / 10 : null,
|
|
55
|
+
cudaAvailable: isNvidia ? undefined : false,
|
|
56
|
+
detectionMethod: 'systeminformation',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function getGpuInfo() {
|
|
64
|
+
const defaults = {
|
|
65
|
+
name: null,
|
|
66
|
+
vendor: null,
|
|
67
|
+
vramGb: null,
|
|
68
|
+
cudaAvailable: false,
|
|
69
|
+
cudaVersion: null,
|
|
70
|
+
driverVersion: null,
|
|
71
|
+
temperature: null,
|
|
72
|
+
utilizationPercent: null,
|
|
73
|
+
detectionMethod: 'none',
|
|
74
|
+
};
|
|
75
|
+
// Try nvidia-smi first (most accurate for NVIDIA)
|
|
76
|
+
const nvidiaSmi = await tryNvidiaSmi();
|
|
77
|
+
if (nvidiaSmi && nvidiaSmi.name) {
|
|
78
|
+
return { ...defaults, ...nvidiaSmi };
|
|
79
|
+
}
|
|
80
|
+
// Fallback to systeminformation
|
|
81
|
+
const siGpu = await trySysinformation();
|
|
82
|
+
if (siGpu && siGpu.name) {
|
|
83
|
+
return { ...defaults, ...siGpu };
|
|
84
|
+
}
|
|
85
|
+
return defaults;
|
|
86
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface NetworkInfo {
|
|
2
|
+
latencyMs: number | null;
|
|
3
|
+
downloadMbps: number | null;
|
|
4
|
+
uploadMbps: number | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function measureLatency(url: string): Promise<number | null>;
|
|
7
|
+
export declare function estimateDownload(): Promise<number | null>;
|
|
8
|
+
export declare function getNetworkInfo(apiUrl: string): Promise<NetworkInfo>;
|
|
9
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/lib/network.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BxE;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc/D;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAWzE"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { fetch } from 'undici';
|
|
2
|
+
export async function measureLatency(url) {
|
|
3
|
+
const samples = [];
|
|
4
|
+
const attempts = 3;
|
|
5
|
+
for (let i = 0; i < attempts; i++) {
|
|
6
|
+
const start = Date.now();
|
|
7
|
+
try {
|
|
8
|
+
await fetch(`${url}/api/health/check`, {
|
|
9
|
+
method: 'GET',
|
|
10
|
+
signal: AbortSignal.timeout(5000),
|
|
11
|
+
});
|
|
12
|
+
samples.push(Date.now() - start);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Try a fallback HEAD request
|
|
16
|
+
try {
|
|
17
|
+
const fallbackStart = Date.now();
|
|
18
|
+
await fetch(url, {
|
|
19
|
+
method: 'HEAD',
|
|
20
|
+
signal: AbortSignal.timeout(5000),
|
|
21
|
+
});
|
|
22
|
+
samples.push(Date.now() - fallbackStart);
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
}
|
|
26
|
+
// Small delay between samples
|
|
27
|
+
if (i < attempts - 1)
|
|
28
|
+
await new Promise(r => setTimeout(r, 200));
|
|
29
|
+
}
|
|
30
|
+
if (samples.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
// Use median
|
|
33
|
+
samples.sort((a, b) => a - b);
|
|
34
|
+
return samples[Math.floor(samples.length / 2)];
|
|
35
|
+
}
|
|
36
|
+
export async function estimateDownload() {
|
|
37
|
+
// Download a small fixed payload and estimate throughput
|
|
38
|
+
const url = 'https://httpbin.org/bytes/524288'; // 512 KB
|
|
39
|
+
const start = Date.now();
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
|
|
42
|
+
const buffer = await res.arrayBuffer();
|
|
43
|
+
const elapsed = (Date.now() - start) / 1000;
|
|
44
|
+
const bytes = buffer.byteLength;
|
|
45
|
+
const mbps = (bytes * 8) / (elapsed * 1_000_000);
|
|
46
|
+
return Math.round(mbps * 10) / 10;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function getNetworkInfo(apiUrl) {
|
|
53
|
+
const [latencyMs, downloadMbps] = await Promise.all([
|
|
54
|
+
measureLatency(apiUrl),
|
|
55
|
+
estimateDownload(),
|
|
56
|
+
]);
|
|
57
|
+
return {
|
|
58
|
+
latencyMs,
|
|
59
|
+
downloadMbps,
|
|
60
|
+
uploadMbps: null, // upload test not implemented in v1 to avoid large payloads
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { SystemInfo } from './system.js';
|
|
2
|
+
import type { GpuInfo } from './gpu.js';
|
|
3
|
+
import type { DockerInfo } from './docker.js';
|
|
4
|
+
import type { NetworkInfo } from './network.js';
|
|
5
|
+
export interface ReportData {
|
|
6
|
+
reportVersion: string;
|
|
7
|
+
providerWallet: string;
|
|
8
|
+
resourceName: string;
|
|
9
|
+
resourceType: string;
|
|
10
|
+
idleGatewayUrl: string | null;
|
|
11
|
+
system: SystemInfo;
|
|
12
|
+
gpu: GpuInfo;
|
|
13
|
+
docker: DockerInfo;
|
|
14
|
+
network: NetworkInfo;
|
|
15
|
+
scores: {
|
|
16
|
+
gpuScore: number;
|
|
17
|
+
runtimeScore: number;
|
|
18
|
+
networkScore: number;
|
|
19
|
+
inferenceScore: number;
|
|
20
|
+
stabilityScore: number;
|
|
21
|
+
finalScore: number;
|
|
22
|
+
grade: string;
|
|
23
|
+
};
|
|
24
|
+
benchmarkMs: number;
|
|
25
|
+
cliVersion: string;
|
|
26
|
+
}
|
|
27
|
+
export interface FullReport {
|
|
28
|
+
reportVersion: string;
|
|
29
|
+
providerWallet: string;
|
|
30
|
+
resourceName: string;
|
|
31
|
+
resourceType: string;
|
|
32
|
+
idleGatewayUrl: string | null;
|
|
33
|
+
system: {
|
|
34
|
+
os: string;
|
|
35
|
+
arch: string;
|
|
36
|
+
cpuModel: string;
|
|
37
|
+
cpuCores: number;
|
|
38
|
+
ramGb: number;
|
|
39
|
+
};
|
|
40
|
+
gpu: {
|
|
41
|
+
name: string | null;
|
|
42
|
+
vendor: string | null;
|
|
43
|
+
vramGb: number | null;
|
|
44
|
+
cudaAvailable: boolean;
|
|
45
|
+
cudaVersion: string | null;
|
|
46
|
+
driverVersion: string | null;
|
|
47
|
+
};
|
|
48
|
+
runtime: {
|
|
49
|
+
dockerAvailable: boolean;
|
|
50
|
+
dockerVersion: string | null;
|
|
51
|
+
dockerGpuRuntime: boolean;
|
|
52
|
+
};
|
|
53
|
+
network: {
|
|
54
|
+
latencyMs: number | null;
|
|
55
|
+
downloadMbps: number | null;
|
|
56
|
+
uploadMbps: number | null;
|
|
57
|
+
};
|
|
58
|
+
scores: {
|
|
59
|
+
gpuScore: number;
|
|
60
|
+
runtimeScore: number;
|
|
61
|
+
networkScore: number;
|
|
62
|
+
inferenceScore: number;
|
|
63
|
+
stabilityScore: number;
|
|
64
|
+
finalScore: number;
|
|
65
|
+
grade: string;
|
|
66
|
+
};
|
|
67
|
+
metadata: {
|
|
68
|
+
createdAt: string;
|
|
69
|
+
cliVersion: string;
|
|
70
|
+
benchmarkMs: number;
|
|
71
|
+
};
|
|
72
|
+
reportHash: string;
|
|
73
|
+
}
|
|
74
|
+
export declare function buildReport(data: ReportData): FullReport;
|
|
75
|
+
export declare function saveReportLocally(report: FullReport): string;
|
|
76
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/lib/report.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,MAAM,EAAE,UAAU,CAAA;IAClB,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAA;QAChB,YAAY,EAAE,MAAM,CAAA;QACpB,YAAY,EAAE,MAAM,CAAA;QACpB,cAAc,EAAE,MAAM,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,UAAU,EAAE,MAAM,CAAA;QAClB,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;IACD,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;IACD,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;QACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,aAAa,EAAE,OAAO,CAAA;QACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;KAC7B,CAAA;IACD,OAAO,EAAE;QACP,eAAe,EAAE,OAAO,CAAA;QACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,gBAAgB,EAAE,OAAO,CAAA;KAC1B,CAAA;IACD,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAC1B,CAAA;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAA;QAChB,YAAY,EAAE,MAAM,CAAA;QACpB,YAAY,EAAE,MAAM,CAAA;QACpB,cAAc,EAAE,MAAM,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,UAAU,EAAE,MAAM,CAAA;QAClB,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;IACD,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;QAClB,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CA6CxD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAK5D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
export function buildReport(data) {
|
|
5
|
+
const reportBody = {
|
|
6
|
+
reportVersion: data.reportVersion,
|
|
7
|
+
providerWallet: data.providerWallet,
|
|
8
|
+
resourceName: data.resourceName,
|
|
9
|
+
resourceType: data.resourceType,
|
|
10
|
+
idleGatewayUrl: data.idleGatewayUrl,
|
|
11
|
+
system: {
|
|
12
|
+
os: data.system.os,
|
|
13
|
+
arch: data.system.arch,
|
|
14
|
+
cpuModel: data.system.cpuModel,
|
|
15
|
+
cpuCores: data.system.cpuCores,
|
|
16
|
+
ramGb: data.system.ramGb,
|
|
17
|
+
},
|
|
18
|
+
gpu: {
|
|
19
|
+
name: data.gpu.name,
|
|
20
|
+
vendor: data.gpu.vendor,
|
|
21
|
+
vramGb: data.gpu.vramGb,
|
|
22
|
+
cudaAvailable: data.gpu.cudaAvailable,
|
|
23
|
+
cudaVersion: data.gpu.cudaVersion,
|
|
24
|
+
driverVersion: data.gpu.driverVersion,
|
|
25
|
+
},
|
|
26
|
+
runtime: {
|
|
27
|
+
dockerAvailable: data.docker.available,
|
|
28
|
+
dockerVersion: data.docker.version,
|
|
29
|
+
dockerGpuRuntime: data.docker.gpuRuntime,
|
|
30
|
+
},
|
|
31
|
+
network: {
|
|
32
|
+
latencyMs: data.network.latencyMs,
|
|
33
|
+
downloadMbps: data.network.downloadMbps,
|
|
34
|
+
uploadMbps: data.network.uploadMbps,
|
|
35
|
+
},
|
|
36
|
+
scores: data.scores,
|
|
37
|
+
metadata: {
|
|
38
|
+
createdAt: new Date().toISOString(),
|
|
39
|
+
cliVersion: data.cliVersion,
|
|
40
|
+
benchmarkMs: data.benchmarkMs,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const reportHash = createHash('sha256')
|
|
44
|
+
.update(JSON.stringify(reportBody))
|
|
45
|
+
.digest('hex');
|
|
46
|
+
return { ...reportBody, reportHash };
|
|
47
|
+
}
|
|
48
|
+
export function saveReportLocally(report) {
|
|
49
|
+
const filename = `idlebench-report-${Date.now()}.json`;
|
|
50
|
+
const filepath = join(process.cwd(), filename);
|
|
51
|
+
writeFileSync(filepath, JSON.stringify(report, null, 2));
|
|
52
|
+
return filepath;
|
|
53
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ScoreInputs {
|
|
2
|
+
gpuName?: string | null;
|
|
3
|
+
vramGb?: number | null;
|
|
4
|
+
cpuCores: number;
|
|
5
|
+
ramGb: number;
|
|
6
|
+
cudaAvailable: boolean;
|
|
7
|
+
dockerAvailable: boolean;
|
|
8
|
+
dockerGpuRuntime: boolean;
|
|
9
|
+
networkLatencyMs?: number | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function calculateGpuScore(inputs: ScoreInputs): number;
|
|
12
|
+
export declare function calculateRuntimeScore(inputs: ScoreInputs): number;
|
|
13
|
+
export declare function calculateNetworkScore(latencyMs?: number | null): number;
|
|
14
|
+
export declare function calculateInferenceScore(cpuCores: number, ramGb: number, benchmarkMs: number, gpuName?: string | null): number;
|
|
15
|
+
export declare function calculateStabilityScore(cpuCores: number, ramGb: number): number;
|
|
16
|
+
export declare function calculateFinalScore(scores: {
|
|
17
|
+
gpuScore: number;
|
|
18
|
+
runtimeScore: number;
|
|
19
|
+
networkScore: number;
|
|
20
|
+
inferenceScore: number;
|
|
21
|
+
stabilityScore: number;
|
|
22
|
+
}): number;
|
|
23
|
+
export declare function getGrade(score: number): string;
|
|
24
|
+
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/lib/scoring.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,OAAO,CAAA;IACtB,eAAe,EAAE,OAAO,CAAA;IACxB,gBAAgB,EAAE,OAAO,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAiC7D;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAMjE;AAED,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAUvE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAS7H;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ/E;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;CACvB,GAAG,MAAM,CAST;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM9C"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export function calculateGpuScore(inputs) {
|
|
2
|
+
let score = 30;
|
|
3
|
+
if (inputs.gpuName) {
|
|
4
|
+
const g = inputs.gpuName.toLowerCase();
|
|
5
|
+
if (g.includes('h100'))
|
|
6
|
+
score = 100;
|
|
7
|
+
else if (g.includes('a100'))
|
|
8
|
+
score = 99;
|
|
9
|
+
else if (g.includes('4090'))
|
|
10
|
+
score = 97;
|
|
11
|
+
else if (g.includes('4080'))
|
|
12
|
+
score = 92;
|
|
13
|
+
else if (g.includes('3090'))
|
|
14
|
+
score = 88;
|
|
15
|
+
else if (g.includes('4070'))
|
|
16
|
+
score = 82;
|
|
17
|
+
else if (g.includes('3080'))
|
|
18
|
+
score = 80;
|
|
19
|
+
else if (g.includes('a6000'))
|
|
20
|
+
score = 86;
|
|
21
|
+
else if (g.includes('v100'))
|
|
22
|
+
score = 82;
|
|
23
|
+
else if (g.includes('3070'))
|
|
24
|
+
score = 72;
|
|
25
|
+
else if (g.includes('t4'))
|
|
26
|
+
score = 68;
|
|
27
|
+
else if (g.includes('2080'))
|
|
28
|
+
score = 68;
|
|
29
|
+
else if (g.includes('1080'))
|
|
30
|
+
score = 55;
|
|
31
|
+
else if (g.includes('amd') || g.includes('radeon') || g.includes('rx'))
|
|
32
|
+
score = 52;
|
|
33
|
+
else
|
|
34
|
+
score = 50;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const coreScore = Math.min(40, (inputs.cpuCores / 64) * 40);
|
|
38
|
+
const ramScore = Math.min(15, (inputs.ramGb / 256) * 15);
|
|
39
|
+
score = Math.round(coreScore + ramScore);
|
|
40
|
+
}
|
|
41
|
+
if (inputs.vramGb) {
|
|
42
|
+
if (inputs.vramGb >= 40)
|
|
43
|
+
score = Math.min(100, score + 3);
|
|
44
|
+
else if (inputs.vramGb >= 24)
|
|
45
|
+
score = Math.min(100, score + 2);
|
|
46
|
+
else if (inputs.vramGb >= 16)
|
|
47
|
+
score = Math.min(100, score + 1);
|
|
48
|
+
}
|
|
49
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
50
|
+
}
|
|
51
|
+
export function calculateRuntimeScore(inputs) {
|
|
52
|
+
let score = 20;
|
|
53
|
+
if (inputs.dockerAvailable)
|
|
54
|
+
score += 40;
|
|
55
|
+
if (inputs.cudaAvailable)
|
|
56
|
+
score += 25;
|
|
57
|
+
if (inputs.dockerGpuRuntime)
|
|
58
|
+
score += 15;
|
|
59
|
+
return Math.min(100, score);
|
|
60
|
+
}
|
|
61
|
+
export function calculateNetworkScore(latencyMs) {
|
|
62
|
+
if (!latencyMs || latencyMs <= 0)
|
|
63
|
+
return 40;
|
|
64
|
+
if (latencyMs < 20)
|
|
65
|
+
return 100;
|
|
66
|
+
if (latencyMs < 50)
|
|
67
|
+
return 92;
|
|
68
|
+
if (latencyMs < 100)
|
|
69
|
+
return 82;
|
|
70
|
+
if (latencyMs < 150)
|
|
71
|
+
return 72;
|
|
72
|
+
if (latencyMs < 200)
|
|
73
|
+
return 60;
|
|
74
|
+
if (latencyMs < 400)
|
|
75
|
+
return 45;
|
|
76
|
+
if (latencyMs < 600)
|
|
77
|
+
return 30;
|
|
78
|
+
return 15;
|
|
79
|
+
}
|
|
80
|
+
export function calculateInferenceScore(cpuCores, ramGb, benchmarkMs, gpuName) {
|
|
81
|
+
let base = 50;
|
|
82
|
+
if (gpuName)
|
|
83
|
+
base += 20;
|
|
84
|
+
base += Math.min(15, cpuCores / 3);
|
|
85
|
+
base += Math.min(10, ramGb / 32);
|
|
86
|
+
// Faster benchmark = better score
|
|
87
|
+
if (benchmarkMs < 200)
|
|
88
|
+
base += 5;
|
|
89
|
+
else if (benchmarkMs > 2000)
|
|
90
|
+
base -= 10;
|
|
91
|
+
return Math.min(100, Math.max(0, Math.round(base)));
|
|
92
|
+
}
|
|
93
|
+
export function calculateStabilityScore(cpuCores, ramGb) {
|
|
94
|
+
let score = 75;
|
|
95
|
+
if (cpuCores >= 8)
|
|
96
|
+
score += 5;
|
|
97
|
+
if (cpuCores >= 16)
|
|
98
|
+
score += 5;
|
|
99
|
+
if (ramGb >= 16)
|
|
100
|
+
score += 5;
|
|
101
|
+
if (ramGb >= 32)
|
|
102
|
+
score += 5;
|
|
103
|
+
if (ramGb >= 64)
|
|
104
|
+
score += 5;
|
|
105
|
+
return Math.min(100, score);
|
|
106
|
+
}
|
|
107
|
+
export function calculateFinalScore(scores) {
|
|
108
|
+
const final = scores.gpuScore * 0.3 +
|
|
109
|
+
scores.runtimeScore * 0.2 +
|
|
110
|
+
scores.inferenceScore * 0.2 +
|
|
111
|
+
scores.networkScore * 0.15 +
|
|
112
|
+
scores.stabilityScore * 0.15;
|
|
113
|
+
return Math.max(0, Math.min(100, Math.round(final)));
|
|
114
|
+
}
|
|
115
|
+
export function getGrade(score) {
|
|
116
|
+
if (score >= 95)
|
|
117
|
+
return 'Elite';
|
|
118
|
+
if (score >= 85)
|
|
119
|
+
return 'Verified A';
|
|
120
|
+
if (score >= 70)
|
|
121
|
+
return 'Verified B';
|
|
122
|
+
if (score >= 55)
|
|
123
|
+
return 'Risky';
|
|
124
|
+
return 'Not Recommended';
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../src/lib/system.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAmBzD"}
|