@jellylegsai/aether-cli 1.8.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 +110 -0
- package/aether-cli-1.0.0.tgz +0 -0
- package/aether-cli-1.8.0.tgz +0 -0
- package/aether-hub-1.0.5.tgz +0 -0
- package/aether-hub-1.1.8.tgz +0 -0
- package/aether-hub-1.2.1.tgz +0 -0
- package/commands/account.js +280 -0
- package/commands/apy.js +499 -0
- package/commands/balance.js +241 -0
- package/commands/blockhash.js +181 -0
- package/commands/broadcast.js +387 -0
- package/commands/claim.js +490 -0
- package/commands/config.js +851 -0
- package/commands/delegations.js +582 -0
- package/commands/doctor.js +769 -0
- package/commands/emergency.js +667 -0
- package/commands/epoch.js +275 -0
- package/commands/fees.js +276 -0
- package/commands/index.js +78 -0
- package/commands/info.js +495 -0
- package/commands/init.js +816 -0
- package/commands/install.js +666 -0
- package/commands/kyc.js +272 -0
- package/commands/logs.js +315 -0
- package/commands/monitor.js +431 -0
- package/commands/multisig.js +701 -0
- package/commands/network.js +429 -0
- package/commands/nft.js +857 -0
- package/commands/ping.js +266 -0
- package/commands/price.js +253 -0
- package/commands/rewards.js +931 -0
- package/commands/sdk-test.js +477 -0
- package/commands/sdk.js +656 -0
- package/commands/slot.js +155 -0
- package/commands/snapshot.js +470 -0
- package/commands/stake-info.js +139 -0
- package/commands/stake-positions.js +205 -0
- package/commands/stake.js +516 -0
- package/commands/stats.js +396 -0
- package/commands/status.js +327 -0
- package/commands/supply.js +391 -0
- package/commands/tps.js +238 -0
- package/commands/transfer.js +495 -0
- package/commands/tx-history.js +346 -0
- package/commands/unstake.js +597 -0
- package/commands/validator-info.js +657 -0
- package/commands/validator-register.js +593 -0
- package/commands/validator-start.js +323 -0
- package/commands/validator-status.js +227 -0
- package/commands/validators.js +626 -0
- package/commands/wallet.js +1570 -0
- package/index.js +593 -0
- package/lib/errors.js +398 -0
- package/package.json +76 -0
- package/sdk/README.md +210 -0
- package/sdk/index.js +1639 -0
- package/sdk/package.json +34 -0
- package/sdk/rpc.js +254 -0
- package/sdk/test.js +85 -0
- package/test/doctor.test.js +76 -0
- package/validator-identity.json +4 -0
package/sdk/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jellylegsai/aether-sdk",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Official Aether Blockchain SDK - Real HTTP RPC calls to Aether chain",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"rpc.js",
|
|
10
|
+
"test.js",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node test.js",
|
|
15
|
+
"build": "node -e \"require('./index.js'); console.log('SDK build OK')\""
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"aether",
|
|
19
|
+
"blockchain",
|
|
20
|
+
"sdk",
|
|
21
|
+
"crypto",
|
|
22
|
+
"rpc",
|
|
23
|
+
"web3"
|
|
24
|
+
],
|
|
25
|
+
"author": "Jelly-legs AI Team",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/jelly-legs-ai/Jelly-legs-unsteady-workshop"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=14.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/sdk/rpc.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @jellylegsai/aether-sdk - RPC Client
|
|
4
|
+
*
|
|
5
|
+
* Low-level HTTP RPC client for Aether blockchain with retry logic.
|
|
6
|
+
* All functions make REAL HTTP calls to the blockchain RPC endpoint.
|
|
7
|
+
* No stubs, no mocks.
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Retry logic with exponential backoff
|
|
11
|
+
* - Enhanced error handling for network timeouts
|
|
12
|
+
* - Rate limiting support
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const http = require('http');
|
|
16
|
+
const https = require('https');
|
|
17
|
+
|
|
18
|
+
const DEFAULT_RPC = process.env.AETHER_RPC || 'http://127.0.0.1:8899';
|
|
19
|
+
|
|
20
|
+
// Retry configuration
|
|
21
|
+
const DEFAULT_RETRIES = 3;
|
|
22
|
+
const DEFAULT_RETRY_DELAY_MS = 1000;
|
|
23
|
+
const DEFAULT_BACKOFF_MULTIPLIER = 2;
|
|
24
|
+
const DEFAULT_MAX_RETRY_DELAY_MS = 30000;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if error is retryable
|
|
28
|
+
*/
|
|
29
|
+
function isRetryableError(error) {
|
|
30
|
+
if (!error) return false;
|
|
31
|
+
|
|
32
|
+
// Network errors
|
|
33
|
+
if (error.code === 'ECONNREFUSED') return true;
|
|
34
|
+
if (error.code === 'ENOTFOUND') return true;
|
|
35
|
+
if (error.code === 'ETIMEDOUT') return true;
|
|
36
|
+
if (error.code === 'ECONNRESET') return true;
|
|
37
|
+
if (error.code === 'EPIPE') return true;
|
|
38
|
+
|
|
39
|
+
// Timeout errors
|
|
40
|
+
if (error.message && error.message.includes('timeout')) return true;
|
|
41
|
+
|
|
42
|
+
// HTTP errors
|
|
43
|
+
if (error.statusCode >= 500) return true;
|
|
44
|
+
if (error.statusCode === 429) return true; // Rate limit
|
|
45
|
+
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Calculate delay for exponential backoff with jitter
|
|
51
|
+
*/
|
|
52
|
+
function calculateDelay(attempt, baseDelay = DEFAULT_RETRY_DELAY_MS, maxDelay = DEFAULT_MAX_RETRY_DELAY_MS) {
|
|
53
|
+
const delay = baseDelay * Math.pow(DEFAULT_BACKOFF_MULTIPLIER, attempt);
|
|
54
|
+
const jitter = Math.random() * 100; // Add up to 100ms jitter
|
|
55
|
+
return Math.min(delay + jitter, maxDelay);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Make a GET request to the RPC endpoint with retry logic
|
|
60
|
+
* @param {string} rpcUrl - RPC endpoint URL
|
|
61
|
+
* @param {string} path - API path (e.g., /v1/slot)
|
|
62
|
+
* @param {number} timeout - Request timeout in ms
|
|
63
|
+
* @param {number} retries - Number of retry attempts
|
|
64
|
+
* @returns {Promise<object>} Parsed JSON response
|
|
65
|
+
*/
|
|
66
|
+
async function rpcGet(rpcUrl, path, timeout = 8000, retries = DEFAULT_RETRIES) {
|
|
67
|
+
let lastError = null;
|
|
68
|
+
|
|
69
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
70
|
+
try {
|
|
71
|
+
const result = await _rpcGetInternal(rpcUrl, path, timeout);
|
|
72
|
+
return result;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
lastError = error;
|
|
75
|
+
|
|
76
|
+
// Don't retry if not retryable or on last attempt
|
|
77
|
+
if (attempt === retries || !isRetryableError(error)) {
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Calculate and wait for backoff
|
|
82
|
+
const delay = calculateDelay(attempt);
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw lastError;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Internal: Make HTTP GET request
|
|
92
|
+
*/
|
|
93
|
+
function _rpcGetInternal(rpcUrl, path, timeout) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
const url = new URL(path, rpcUrl);
|
|
96
|
+
const isHttps = url.protocol === 'https:';
|
|
97
|
+
const lib = isHttps ? https : http;
|
|
98
|
+
|
|
99
|
+
const req = lib.request({
|
|
100
|
+
hostname: url.hostname,
|
|
101
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
102
|
+
path: url.pathname + url.search,
|
|
103
|
+
method: 'GET',
|
|
104
|
+
timeout,
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
'Accept': 'application/json',
|
|
108
|
+
'User-Agent': 'aether-sdk/1.0.0',
|
|
109
|
+
},
|
|
110
|
+
}, (res) => {
|
|
111
|
+
let data = '';
|
|
112
|
+
res.on('data', (chunk) => (data += chunk));
|
|
113
|
+
res.on('end', () => {
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(data);
|
|
116
|
+
if (parsed.error) {
|
|
117
|
+
const err = new Error(parsed.error.message || JSON.stringify(parsed.error));
|
|
118
|
+
err.statusCode = res.statusCode;
|
|
119
|
+
err.responseData = parsed;
|
|
120
|
+
reject(err);
|
|
121
|
+
} else {
|
|
122
|
+
resolve(parsed);
|
|
123
|
+
}
|
|
124
|
+
} catch (e) {
|
|
125
|
+
resolve({ raw: data });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
req.on('error', (err) => {
|
|
131
|
+
err.rpcUrl = rpcUrl;
|
|
132
|
+
err.path = path;
|
|
133
|
+
reject(err);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
req.on('timeout', () => {
|
|
137
|
+
req.destroy();
|
|
138
|
+
const err = new Error(`RPC request timeout after ${timeout}ms`);
|
|
139
|
+
err.code = 'ETIMEDOUT';
|
|
140
|
+
err.rpcUrl = rpcUrl;
|
|
141
|
+
err.path = path;
|
|
142
|
+
reject(err);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
req.end();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Make a POST request to the RPC endpoint with retry logic
|
|
151
|
+
* @param {string} rpcUrl - RPC endpoint URL
|
|
152
|
+
* @param {string} path - API path
|
|
153
|
+
* @param {object} body - Request body (will be JSON.stringify'd)
|
|
154
|
+
* @param {number} timeout - Request timeout in ms
|
|
155
|
+
* @param {number} retries - Number of retry attempts
|
|
156
|
+
* @returns {Promise<object>} Parsed JSON response
|
|
157
|
+
*/
|
|
158
|
+
async function rpcPost(rpcUrl, path, body, timeout = 8000, retries = DEFAULT_RETRIES) {
|
|
159
|
+
let lastError = null;
|
|
160
|
+
|
|
161
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
162
|
+
try {
|
|
163
|
+
const result = await _rpcPostInternal(rpcUrl, path, body, timeout);
|
|
164
|
+
return result;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
lastError = error;
|
|
167
|
+
|
|
168
|
+
// Don't retry if not retryable or on last attempt
|
|
169
|
+
if (attempt === retries || !isRetryableError(error)) {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Calculate and wait for backoff
|
|
174
|
+
const delay = calculateDelay(attempt);
|
|
175
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
throw lastError;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Internal: Make HTTP POST request
|
|
184
|
+
*/
|
|
185
|
+
function _rpcPostInternal(rpcUrl, path, body, timeout) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const url = new URL(path, rpcUrl);
|
|
188
|
+
const isHttps = url.protocol === 'https:';
|
|
189
|
+
const lib = isHttps ? https : http;
|
|
190
|
+
const bodyStr = JSON.stringify(body);
|
|
191
|
+
|
|
192
|
+
const req = lib.request({
|
|
193
|
+
hostname: url.hostname,
|
|
194
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
195
|
+
path: url.pathname + url.search,
|
|
196
|
+
method: 'POST',
|
|
197
|
+
timeout,
|
|
198
|
+
headers: {
|
|
199
|
+
'Content-Type': 'application/json',
|
|
200
|
+
'Accept': 'application/json',
|
|
201
|
+
'Content-Length': Buffer.byteLength(bodyStr),
|
|
202
|
+
'User-Agent': 'aether-sdk/1.0.0',
|
|
203
|
+
},
|
|
204
|
+
}, (res) => {
|
|
205
|
+
let data = '';
|
|
206
|
+
res.on('data', (chunk) => (data += chunk));
|
|
207
|
+
res.on('end', () => {
|
|
208
|
+
try {
|
|
209
|
+
const parsed = JSON.parse(data);
|
|
210
|
+
if (parsed.error) {
|
|
211
|
+
const err = new Error(parsed.error.message || JSON.stringify(parsed.error));
|
|
212
|
+
err.statusCode = res.statusCode;
|
|
213
|
+
err.responseData = parsed;
|
|
214
|
+
reject(err);
|
|
215
|
+
} else {
|
|
216
|
+
resolve(parsed);
|
|
217
|
+
}
|
|
218
|
+
} catch (e) {
|
|
219
|
+
resolve(data);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
req.on('error', (err) => {
|
|
225
|
+
err.rpcUrl = rpcUrl;
|
|
226
|
+
err.path = path;
|
|
227
|
+
reject(err);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
req.on('timeout', () => {
|
|
231
|
+
req.destroy();
|
|
232
|
+
const err = new Error(`RPC request timeout after ${timeout}ms`);
|
|
233
|
+
err.code = 'ETIMEDOUT';
|
|
234
|
+
err.rpcUrl = rpcUrl;
|
|
235
|
+
err.path = path;
|
|
236
|
+
reject(err);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
req.write(bodyStr);
|
|
240
|
+
req.end();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = {
|
|
245
|
+
rpcGet,
|
|
246
|
+
rpcPost,
|
|
247
|
+
DEFAULT_RPC,
|
|
248
|
+
DEFAULT_RETRIES,
|
|
249
|
+
DEFAULT_RETRY_DELAY_MS,
|
|
250
|
+
DEFAULT_BACKOFF_MULTIPLIER,
|
|
251
|
+
DEFAULT_MAX_RETRY_DELAY_MS,
|
|
252
|
+
isRetryableError,
|
|
253
|
+
calculateDelay,
|
|
254
|
+
};
|
package/sdk/test.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @jellylegsai/aether-sdk - Test Script
|
|
4
|
+
*
|
|
5
|
+
* Tests all SDK functions with REAL RPC calls to http://127.0.0.1:8899
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const aether = require('./index');
|
|
9
|
+
|
|
10
|
+
const C = {
|
|
11
|
+
reset: '\x1b[0m',
|
|
12
|
+
green: '\x1b[32m',
|
|
13
|
+
red: '\x1b[31m',
|
|
14
|
+
yellow: '\x1b[33m',
|
|
15
|
+
cyan: '\x1b[36m',
|
|
16
|
+
dim: '\x1b[2m',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function test(name, fn) {
|
|
20
|
+
try {
|
|
21
|
+
const result = await fn();
|
|
22
|
+
console.log(` ${C.green}✓${C.reset} ${name}`);
|
|
23
|
+
return { ok: true, result };
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.log(` ${C.red}✗${C.reset} ${name}: ${err.message}`);
|
|
26
|
+
return { ok: false, error: err.message };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function main() {
|
|
31
|
+
console.log(`\n${C.cyan}══ @jellylegsai/aether-sdk Test Suite ══${C.reset}\n`);
|
|
32
|
+
console.log(` ${C.dim}RPC Endpoint: ${aether.DEFAULT_RPC_URL}${C.reset}\n`);
|
|
33
|
+
|
|
34
|
+
const results = [];
|
|
35
|
+
|
|
36
|
+
// Test ping first
|
|
37
|
+
results.push(await test('ping()', async () => {
|
|
38
|
+
const ping = await aether.ping();
|
|
39
|
+
if (!ping.ok) throw new Error(ping.error);
|
|
40
|
+
return ping;
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
// Test chain queries
|
|
44
|
+
results.push(await test('getSlot()', () => aether.getSlot()));
|
|
45
|
+
results.push(await test('getBlockHeight()', () => aether.getBlockHeight()));
|
|
46
|
+
results.push(await test('getEpoch()', () => aether.getEpoch()));
|
|
47
|
+
results.push(await test('getTPS()', () => aether.getTPS()));
|
|
48
|
+
results.push(await test('getSupply()', () => aether.getSupply()));
|
|
49
|
+
results.push(await test('getFees()', () => aether.getFees()));
|
|
50
|
+
results.push(await test('getValidators()', () => aether.getValidators()));
|
|
51
|
+
results.push(await test('getPeers()', () => aether.getPeers()));
|
|
52
|
+
results.push(await test('getHealth()', () => aether.getHealth()));
|
|
53
|
+
results.push(await test('getSlotProduction()', () => aether.getSlotProduction()));
|
|
54
|
+
|
|
55
|
+
// Test with a sample address (may not exist, but tests the call)
|
|
56
|
+
const testAddress = 'ATH111111111111111111111111111111111';
|
|
57
|
+
results.push(await test(`getAccount('${testAddress.substring(0, 12)}...')`, () =>
|
|
58
|
+
aether.getAccount(testAddress)
|
|
59
|
+
));
|
|
60
|
+
results.push(await test(`getBalance('${testAddress.substring(0, 12)}...')`, () =>
|
|
61
|
+
aether.getBalance(testAddress)
|
|
62
|
+
));
|
|
63
|
+
results.push(await test(`getStakePositions('${testAddress.substring(0, 12)}...')`, () =>
|
|
64
|
+
aether.getStakePositions(testAddress)
|
|
65
|
+
));
|
|
66
|
+
results.push(await test(`getRewards('${testAddress.substring(0, 12)}...')`, () =>
|
|
67
|
+
aether.getRewards(testAddress)
|
|
68
|
+
));
|
|
69
|
+
|
|
70
|
+
// Summary
|
|
71
|
+
const passed = results.filter(r => r.ok).length;
|
|
72
|
+
const failed = results.filter(r => !r.ok).length;
|
|
73
|
+
|
|
74
|
+
console.log();
|
|
75
|
+
if (failed === 0) {
|
|
76
|
+
console.log(` ${C.green}══ All ${passed} tests passed! ══${C.reset}\n`);
|
|
77
|
+
} else {
|
|
78
|
+
console.log(` ${C.yellow}══ ${passed} passed, ${failed} failed ══${C.reset}`);
|
|
79
|
+
console.log(` ${C.dim} (Failures may be due to RPC not running or test data not existing)${C.reset}\n`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test suite for aether-cli doctor command
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { checkCPU, checkMemory, checkDisk, checkNetwork, checkFirewall } = require('../commands/doctor');
|
|
7
|
+
|
|
8
|
+
console.log('Running aether-cli doctor tests...\n');
|
|
9
|
+
|
|
10
|
+
let passed = 0;
|
|
11
|
+
let failed = 0;
|
|
12
|
+
|
|
13
|
+
function test(name, fn) {
|
|
14
|
+
try {
|
|
15
|
+
const result = fn();
|
|
16
|
+
console.log(`✅ ${name}`);
|
|
17
|
+
passed++;
|
|
18
|
+
return result;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.log(`❌ ${name}: ${error.message}`);
|
|
21
|
+
failed++;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Test CPU check
|
|
27
|
+
test('checkCPU returns valid structure', () => {
|
|
28
|
+
const cpu = checkCPU();
|
|
29
|
+
if (!cpu.section) throw new Error('Missing section');
|
|
30
|
+
if (!('passed' in cpu)) throw new Error('Missing passed');
|
|
31
|
+
if (!cpu.message) throw new Error('Missing message');
|
|
32
|
+
console.log(` CPU: ${cpu.physicalCores} cores, ${cpu.model}`);
|
|
33
|
+
return cpu;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Test Memory check
|
|
37
|
+
test('checkMemory returns valid structure', () => {
|
|
38
|
+
const mem = checkMemory();
|
|
39
|
+
if (!mem.section) throw new Error('Missing section');
|
|
40
|
+
if (!('passed' in mem)) throw new Error('Missing passed');
|
|
41
|
+
if (!mem.message) throw new Error('Missing message');
|
|
42
|
+
console.log(` Memory: ${mem.total} total, ${mem.available} available`);
|
|
43
|
+
return mem;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Test Disk check
|
|
47
|
+
test('checkDisk returns valid structure', () => {
|
|
48
|
+
const disk = checkDisk();
|
|
49
|
+
if (!disk.section) throw new Error('Missing section');
|
|
50
|
+
if (!('passed' in disk)) throw new Error('Missing passed');
|
|
51
|
+
if (!disk.message) throw new Error('Missing message');
|
|
52
|
+
console.log(` Disk: ${disk.total} total, ${disk.free} free`);
|
|
53
|
+
return disk;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Test Network check
|
|
57
|
+
test('checkNetwork returns valid structure', () => {
|
|
58
|
+
const net = checkNetwork();
|
|
59
|
+
if (!net.section) throw new Error('Missing section');
|
|
60
|
+
if (!('passed' in net)) throw new Error('Missing passed');
|
|
61
|
+
console.log(` Network: ${net.publicIP}`);
|
|
62
|
+
return net;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Test Firewall check
|
|
66
|
+
test('checkFirewall returns valid structure', () => {
|
|
67
|
+
const fw = checkFirewall();
|
|
68
|
+
if (!fw.section) throw new Error('Missing section');
|
|
69
|
+
if (!('passed' in fw)) throw new Error('Missing passed');
|
|
70
|
+
console.log(` Firewall: P2P=${fw.p2p}, RPC=${fw.rpc}, SSH=${fw.ssh}`);
|
|
71
|
+
return fw;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Summary
|
|
75
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
76
|
+
process.exit(failed > 0 ? 1 : 0);
|