@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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/aether-cli-1.0.0.tgz +0 -0
  4. package/aether-cli-1.8.0.tgz +0 -0
  5. package/aether-hub-1.0.5.tgz +0 -0
  6. package/aether-hub-1.1.8.tgz +0 -0
  7. package/aether-hub-1.2.1.tgz +0 -0
  8. package/commands/account.js +280 -0
  9. package/commands/apy.js +499 -0
  10. package/commands/balance.js +241 -0
  11. package/commands/blockhash.js +181 -0
  12. package/commands/broadcast.js +387 -0
  13. package/commands/claim.js +490 -0
  14. package/commands/config.js +851 -0
  15. package/commands/delegations.js +582 -0
  16. package/commands/doctor.js +769 -0
  17. package/commands/emergency.js +667 -0
  18. package/commands/epoch.js +275 -0
  19. package/commands/fees.js +276 -0
  20. package/commands/index.js +78 -0
  21. package/commands/info.js +495 -0
  22. package/commands/init.js +816 -0
  23. package/commands/install.js +666 -0
  24. package/commands/kyc.js +272 -0
  25. package/commands/logs.js +315 -0
  26. package/commands/monitor.js +431 -0
  27. package/commands/multisig.js +701 -0
  28. package/commands/network.js +429 -0
  29. package/commands/nft.js +857 -0
  30. package/commands/ping.js +266 -0
  31. package/commands/price.js +253 -0
  32. package/commands/rewards.js +931 -0
  33. package/commands/sdk-test.js +477 -0
  34. package/commands/sdk.js +656 -0
  35. package/commands/slot.js +155 -0
  36. package/commands/snapshot.js +470 -0
  37. package/commands/stake-info.js +139 -0
  38. package/commands/stake-positions.js +205 -0
  39. package/commands/stake.js +516 -0
  40. package/commands/stats.js +396 -0
  41. package/commands/status.js +327 -0
  42. package/commands/supply.js +391 -0
  43. package/commands/tps.js +238 -0
  44. package/commands/transfer.js +495 -0
  45. package/commands/tx-history.js +346 -0
  46. package/commands/unstake.js +597 -0
  47. package/commands/validator-info.js +657 -0
  48. package/commands/validator-register.js +593 -0
  49. package/commands/validator-start.js +323 -0
  50. package/commands/validator-status.js +227 -0
  51. package/commands/validators.js +626 -0
  52. package/commands/wallet.js +1570 -0
  53. package/index.js +593 -0
  54. package/lib/errors.js +398 -0
  55. package/package.json +76 -0
  56. package/sdk/README.md +210 -0
  57. package/sdk/index.js +1639 -0
  58. package/sdk/package.json +34 -0
  59. package/sdk/rpc.js +254 -0
  60. package/sdk/test.js +85 -0
  61. package/test/doctor.test.js +76 -0
  62. package/validator-identity.json +4 -0
@@ -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);
@@ -0,0 +1,4 @@
1
+ {
2
+ "pubkey": "2LKAbw8SykbpqU4VeHuhKKW97HWMzp6TB2sGb1UqsZFQ",
3
+ "secret": "8ataEJXSePVGf922cCZxspPb3FCErgqVzwPyXPfTkPSg"
4
+ }