aether-hub 1.0.3

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.
@@ -0,0 +1,381 @@
1
+ /**
2
+ * aether-cli sdk
3
+ *
4
+ * Provides download links and install instructions for the Aether SDK,
5
+ * Aether JS client, and FLUX/ATH token libraries.
6
+ *
7
+ * Usage:
8
+ * aether-cli sdk # Show all SDK options
9
+ * aether-cli sdk js # Aether JS client
10
+ * aether-cli sdk rust # Aether Rust SDK
11
+ * aether-cli sdk tokens # FLUX/ATH token libraries
12
+ */
13
+
14
+ const os = require('os');
15
+
16
+ // ANSI colors
17
+ const colors = {
18
+ reset: '\x1b[0m',
19
+ bright: '\x1b[1m',
20
+ green: '\x1b[32m',
21
+ yellow: '\x1b[33m',
22
+ cyan: '\x1b[36m',
23
+ red: '\x1b[31m',
24
+ dim: '\x1b[2m',
25
+ magenta: '\x1b[35m',
26
+ blue: '\x1b[34m',
27
+ };
28
+
29
+ /**
30
+ * Print the SDK banner
31
+ */
32
+ function printBanner() {
33
+ console.log(`
34
+ ${colors.cyan}╔═══════════════════════════════════════════════════════════════╗
35
+ ${colors.cyan}║ ║
36
+ ${colors.cyan}║ ${colors.bright}AETHER SDK${colors.reset}${colors.cyan} ║
37
+ ${colors.cyan}║ ${colors.bright}Developer Tools & Libraries${colors.reset}${colors.cyan} ║
38
+ ${colors.cyan}║ ║
39
+ ${colors.cyan}╚═══════════════════════════════════════════════════════════════╝${colors.reset}
40
+ `);
41
+ }
42
+
43
+ /**
44
+ * Print a section header
45
+ */
46
+ function printSection(title, icon = '📦') {
47
+ console.log();
48
+ console.log(`${colors.bright}${colors.cyan}${'═'.repeat(60)}${colors.reset}`);
49
+ console.log(`${colors.bright} ${icon} ${title}${colors.reset}`);
50
+ console.log(`${colors.bright}${colors.cyan}${'═'.repeat(60)}${colors.reset}`);
51
+ console.log();
52
+ }
53
+
54
+ /**
55
+ * Print a code block
56
+ */
57
+ function printCode(code, lang = 'bash') {
58
+ console.log(` ${colors.dim}[ ${lang} ]${colors.reset}`);
59
+ console.log(` ${colors.bright}${code}${colors.reset}`);
60
+ console.log();
61
+ }
62
+
63
+ /**
64
+ * Print a link
65
+ */
66
+ function printLink(label, url) {
67
+ console.log(` ${colors.cyan}🔗 ${label}:${colors.reset}`);
68
+ console.log(` ${colors.blue}${url}${colors.reset}`);
69
+ console.log();
70
+ }
71
+
72
+ /**
73
+ * Show all SDK options
74
+ */
75
+ function showAllSdks() {
76
+ printBanner();
77
+
78
+ console.log(` ${colors.bright}Available SDKs and Libraries:${colors.reset}\n`);
79
+
80
+ console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk js - JavaScript/TypeScript client`);
81
+ console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk rust - Rust SDK for native development`);
82
+ console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk tokens - FLUX/ATH token libraries`);
83
+ console.log(` ${colors.yellow}npm${colors.reset} aether-cli sdk docs - Documentation portal`);
84
+ console.log();
85
+
86
+ // Quick start
87
+ printSection('⚡ Quick Start', '🚀');
88
+ console.log(' Get started with Aether development in 3 steps:\n');
89
+ console.log(` 1. ${colors.bright}Install the JS client:${colors.reset}`);
90
+ printCode('npm install @aether-network/client');
91
+ console.log(` 2. ${colors.bright}Initialize your connection:${colors.reset}`);
92
+ printCode('const aether = require(\'@aether-network/client\');\nconst client = new aether.Client({ rpcUrl: \'http://localhost:8899\' });');
93
+ console.log(` 3. ${colors.bright}Start building!${colors.reset}`);
94
+ console.log(` ${colors.dim}See docs for full API reference${colors.reset}`);
95
+ console.log();
96
+
97
+ printLink('Documentation', 'https://docs.aether.network');
98
+ printLink('GitHub Organization', 'https://github.com/aether-network');
99
+ printLink('Discord Community', 'https://discord.gg/aether');
100
+ }
101
+
102
+ /**
103
+ * Show JavaScript SDK info
104
+ */
105
+ function showJsSdk() {
106
+ printSection('Aether JavaScript Client', '📜');
107
+
108
+ console.log(` ${colors.bright}The official Aether JavaScript/TypeScript client library.${colors.reset}`);
109
+ console.log(` Provides a simple API for interacting with the Aether blockchain.${colors.reset}\n`);
110
+
111
+ console.log(` ${colors.green}✓ Stable Release${colors.reset}`);
112
+ console.log(` ${colors.dim}Version: 1.2.0${colors.reset}`);
113
+ console.log();
114
+
115
+ printSection('Installation');
116
+ printCode('npm install @aether-network/client');
117
+ console.log(' or');
118
+ printCode('yarn add @aether-network/client');
119
+ console.log(' or');
120
+ printCode('pnpm add @aether-network/client');
121
+
122
+ printSection('Usage Example');
123
+ const example = `const aether = require('@aether-network/client');
124
+
125
+ // Initialize client
126
+ const client = new aether.Client({
127
+ rpcUrl: 'http://localhost:8899',
128
+ wsUrl: 'ws://localhost:8900',
129
+ });
130
+
131
+ // Get slot info
132
+ const slot = await client.getSlot();
133
+ console.log('Current slot:', slot);
134
+
135
+ // Get balance
136
+ const balance = await client.getBalance('pubkey...');
137
+ console.log('Balance:', balance);
138
+
139
+ // Send transaction
140
+ const tx = await client.sendTransaction({
141
+ from: 'sender-pubkey',
142
+ to: 'recipient-pubkey',
143
+ amount: 1000,
144
+ });
145
+ console.log('Transaction signature:', tx.signature);`;
146
+
147
+ console.log(` ${colors.dim}[ javascript ]${colors.reset}`);
148
+ example.split('\n').forEach(line => {
149
+ console.log(` ${colors.bright}${line}${colors.reset}`);
150
+ });
151
+ console.log();
152
+
153
+ printSection('API Reference');
154
+ console.log(` ${colors.cyan}Core Methods:${colors.reset}`);
155
+ console.log(` • getSlot() - Get current slot number`);
156
+ console.log(` • getBalance(pubkey) - Get account balance in lamports`);
157
+ console.log(` • getAccountInfo(pubkey) - Get full account information`);
158
+ console.log(` • sendTransaction(tx) - Send a signed transaction`);
159
+ console.log(` • confirmTransaction(sig) - Wait for transaction confirmation`);
160
+ console.log(` • getProgramAccounts(...) - Query accounts by program`);
161
+ console.log();
162
+
163
+ printLink('NPM Package', 'https://www.npmjs.com/package/@aether-network/client');
164
+ printLink('TypeScript Docs', 'https://docs.aether.network/sdk/js');
165
+ printLink('GitHub Repo', 'https://github.com/aether-network/aether-js');
166
+ }
167
+
168
+ /**
169
+ * Show Rust SDK info
170
+ */
171
+ function showRustSdk() {
172
+ printSection('Aether Rust SDK', '🦀');
173
+
174
+ console.log(` ${colors.bright}Native Rust SDK for building Aether programs and clients.${colors.reset}`);
175
+ console.log(` Use this for validator plugins, custom programs, and high-performance tools.${colors.reset}\n`);
176
+
177
+ console.log(` ${colors.green}✓ Stable Release${colors.reset}`);
178
+ console.log(` ${colors.dim}Version: 1.2.0${colors.reset}`);
179
+ console.log();
180
+
181
+ printSection('Installation');
182
+ printCode('cargo add aether-sdk');
183
+ console.log(' or add to your Cargo.toml:');
184
+ console.log();
185
+ console.log(` ${colors.dim}${colors.bgRed}toml${colors.reset}`);
186
+ console.log(` ${colors.bright}[dependencies]${colors.reset}`);
187
+ console.log(` ${colors.bright}aether-sdk = "1.2"${colors.reset}`);
188
+ console.log();
189
+
190
+ printSection('Usage Example');
191
+ const rustExample = `use aether_sdk::{client::Client, pubkey::Pubkey};
192
+
193
+ #[tokio::main]
194
+ async fn main() -> Result<(), Box<dyn std::error::Error>> {
195
+ // Initialize client
196
+ let client = Client::new("http://localhost:8899");
197
+
198
+ // Get slot
199
+ let slot = client.get_slot().await?;
200
+ println!("Current slot: {}", slot);
201
+
202
+ // Get balance
203
+ let pubkey = Pubkey::from_str("...")?;
204
+ let balance = client.get_balance(&pubkey).await?;
205
+ println!("Balance: {} lamports", balance);
206
+
207
+ Ok(())
208
+ }`;
209
+
210
+ console.log(` ${colors.dim}${colors.bgRed}rust${colors.reset}`);
211
+ rustExample.split('\n').forEach(line => {
212
+ console.log(` ${colors.bright}${line}${colors.reset}`);
213
+ });
214
+ console.log();
215
+
216
+ printSection('Features');
217
+ console.log(` ${colors.cyan}• Full RPC client${colors.reset}`);
218
+ console.log(` ${colors.cyan}• Program development framework${colors.reset}`);
219
+ console.log(` ${colors.cyan}• Account serialization/deserialization${colors.reset}`);
220
+ console.log(` ${colors.cyan}• Transaction building and signing${colors.reset}`);
221
+ console.log(` ${colors.cyan}• Async runtime support (tokio)${colors.reset}`);
222
+ console.log();
223
+
224
+ printLink('Crates.io', 'https://crates.io/crates/aether-sdk');
225
+ printLink('API Docs', 'https://docs.rs/aether-sdk');
226
+ printLink('GitHub Repo', 'https://github.com/aether-network/aether-rust');
227
+ }
228
+
229
+ /**
230
+ * Show token libraries info
231
+ */
232
+ function showTokensSdk() {
233
+ printSection('FLUX / ATH Token Libraries', '🪙');
234
+
235
+ console.log(` ${colors.bright}Token libraries for FLUX (utility) and ATH (governance) tokens.${colors.reset}`);
236
+ console.log(` Use these to integrate Aether tokens into your applications.${colors.reset}\n`);
237
+
238
+ console.log(` ${colors.yellow}⚠ Beta Release${colors.reset}`);
239
+ console.log(` ${colors.dim}Version: 0.9.0 (testnet only)${colors.reset}`);
240
+ console.log();
241
+
242
+ printSection('Installation');
243
+ console.log(` ${colors.bright}JavaScript:${colors.reset}`);
244
+ printCode('npm install @aether-network/tokens');
245
+ console.log();
246
+ console.log(` ${colors.bright}Rust:${colors.reset}`);
247
+ printCode('cargo add aether-tokens');
248
+
249
+ printSection('Supported Tokens');
250
+ console.log();
251
+ console.log(` ${colors.magenta}FLUX${colors.reset} - Utility Token`);
252
+ console.log(` • Purpose: Transaction fees, staking rewards`);
253
+ console.log(` • Decimals: 9`);
254
+ console.log(` • Mint: ${colors.dim}flux7x... (testnet)${colors.reset}`);
255
+ console.log();
256
+ console.log(` ${colors.blue}ATH${colors.reset} - Governance Token`);
257
+ console.log(` • Purpose: Voting, protocol upgrades`);
258
+ console.log(` • Decimals: 6`);
259
+ console.log(` • Mint: ${colors.dim}athgov... (testnet)${colors.reset}`);
260
+ console.log();
261
+
262
+ printSection('Usage Example (JavaScript)');
263
+ const tokenExample = `const { TokenClient, TOKENS } = require('@aether-network/tokens');
264
+
265
+ const client = new TokenClient(rpcUrl);
266
+
267
+ // Get FLUX balance
268
+ const fluxBalance = await client.getTokenBalance(pubkey, TOKENS.FLUX);
269
+ console.log('FLUX:', fluxBalance);
270
+
271
+ // Transfer FLUX
272
+ const tx = await client.transfer({
273
+ mint: TOKENS.FLUX,
274
+ from: senderPubkey,
275
+ to: recipientPubkey,
276
+ amount: 1000,
277
+ });`;
278
+
279
+ console.log(` ${colors.dim}${colors.bgRed}javascript${colors.reset}`);
280
+ tokenExample.split('\n').forEach(line => {
281
+ console.log(` ${colors.bright}${line}${colors.reset}`);
282
+ });
283
+ console.log();
284
+
285
+ printLink('Token Documentation', 'https://docs.aether.network/tokens');
286
+ printLink('Token Registry', 'https://github.com/aether-network/token-registry');
287
+ printLink('Testnet Faucet', 'https://faucet.aether.network');
288
+ }
289
+
290
+ /**
291
+ * Show documentation portal info
292
+ */
293
+ function showDocs() {
294
+ printSection('Aether Documentation', '📚');
295
+
296
+ console.log(` ${colors.bright}Comprehensive documentation for Aether developers.${colors.reset}\n`);
297
+
298
+ console.log(` ${colors.cyan}📖 Documentation Portal:${colors.reset}`);
299
+ console.log(` ${colors.blue}https://docs.aether.network${colors.reset}`);
300
+ console.log();
301
+
302
+ console.log(` ${colors.cyan}Sections:${colors.reset}`);
303
+ console.log(` • Getting Started - Quick start guides`);
304
+ console.log(` • Core Concepts - Accounts, programs, transactions`);
305
+ console.log(` • SDK Reference - Full API docs for JS and Rust`);
306
+ console.log(` • Tutorials - Step-by-step projects`);
307
+ console.log(` • Validator Guide - Running and maintaining validators`);
308
+ console.log(` • Economics - Staking, rewards, fees`);
309
+ console.log();
310
+
311
+ printLink('Main Docs', 'https://docs.aether.network');
312
+ printLink('API Reference', 'https://docs.aether.network/api');
313
+ printLink('Tutorials', 'https://docs.aether.network/tutorials');
314
+ printLink('Validator Docs', 'https://docs.aether.network/validators');
315
+ }
316
+
317
+ /**
318
+ * Parse command line args
319
+ */
320
+ function parseArgs() {
321
+ const args = process.argv.slice(3); // Skip 'aether-cli sdk'
322
+
323
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
324
+ return 'all';
325
+ }
326
+
327
+ const subcmd = args[0].toLowerCase();
328
+
329
+ switch (subcmd) {
330
+ case 'js':
331
+ case 'javascript':
332
+ case 'node':
333
+ return 'js';
334
+ case 'rust':
335
+ case 'rs':
336
+ return 'rust';
337
+ case 'tokens':
338
+ case 'token':
339
+ case 'flux':
340
+ case 'ath':
341
+ return 'tokens';
342
+ case 'docs':
343
+ case 'doc':
344
+ case 'documentation':
345
+ return 'docs';
346
+ default:
347
+ return 'all';
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Main SDK command
353
+ */
354
+ function sdkCommand() {
355
+ const subcmd = parseArgs();
356
+
357
+ switch (subcmd) {
358
+ case 'js':
359
+ showJsSdk();
360
+ break;
361
+ case 'rust':
362
+ showRustSdk();
363
+ break;
364
+ case 'tokens':
365
+ showTokensSdk();
366
+ break;
367
+ case 'docs':
368
+ showDocs();
369
+ break;
370
+ default:
371
+ showAllSdks();
372
+ }
373
+ }
374
+
375
+ // Export for use as module
376
+ module.exports = { sdkCommand };
377
+
378
+ // Run if called directly
379
+ if (require.main === module) {
380
+ sdkCommand();
381
+ }
@@ -0,0 +1,290 @@
1
+ /**
2
+ * aether-cli validator-start
3
+ *
4
+ * Spawns the aether-validator binary as a child process.
5
+ * Handles startup, logging, and graceful shutdown.
6
+ */
7
+
8
+ const { spawn } = require('child_process');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const os = require('os');
12
+
13
+ // ANSI colors
14
+ const colors = {
15
+ reset: '\x1b[0m',
16
+ bright: '\x1b[1m',
17
+ green: '\x1b[32m',
18
+ yellow: '\x1b[33m',
19
+ cyan: '\x1b[36m',
20
+ red: '\x1b[31m',
21
+ };
22
+
23
+ /**
24
+ * Find the aether-validator binary
25
+ * Searches common locations based on OS and repo layout
26
+ */
27
+ function findValidatorBinary() {
28
+ const platform = os.platform();
29
+ const isWindows = platform === 'win32';
30
+ const binaryName = isWindows ? 'aether-validator.exe' : 'aether-validator';
31
+
32
+ // Check common locations
33
+ const locations = [
34
+ // Sibling repo: Jelly-legs-unsteady-workshop/target/debug/
35
+ path.join(__dirname, '..', '..', 'Jelly-legs-unsteady-workshop', 'target', 'debug', binaryName),
36
+ path.join(__dirname, '..', '..', 'Jelly-legs-unsteady-workshop', 'target', 'release', binaryName),
37
+ // Local build in aether-cli (if someone built here)
38
+ path.join(__dirname, '..', 'target', 'debug', binaryName),
39
+ path.join(__dirname, '..', 'target', 'release', binaryName),
40
+ // System PATH
41
+ 'aether-validator' + (isWindows ? '.exe' : ''),
42
+ ];
43
+
44
+ for (const loc of locations) {
45
+ if (loc.startsWith('aether-validator')) {
46
+ // Check if it's in PATH
47
+ try {
48
+ const { execSync } = require('child_process');
49
+ const checkCmd = isWindows ? 'where' : 'which';
50
+ execSync(`${checkCmd} ${loc}`, { stdio: 'pipe' });
51
+ return { type: 'binary', path: loc, inPath: true };
52
+ } catch {
53
+ // Not in PATH, continue
54
+ }
55
+ }
56
+ if (fs.existsSync(loc)) {
57
+ return { type: 'binary', path: loc };
58
+ }
59
+ }
60
+
61
+ // Binary not found - offer to build it
62
+ return { type: 'missing', path: null };
63
+ }
64
+
65
+ /**
66
+ * Parse command line args for validator-start
67
+ */
68
+ function parseArgs() {
69
+ const args = process.argv.slice(3); // Skip 'aether-cli validator start'
70
+
71
+ const options = {
72
+ testnet: false,
73
+ rpcAddr: '127.0.0.1:8899',
74
+ p2pAddr: '0.0.0.0:8001',
75
+ identity: null,
76
+ verbose: false,
77
+ tier: 'full',
78
+ };
79
+
80
+ for (let i = 0; i < args.length; i++) {
81
+ switch (args[i]) {
82
+ case '--testnet':
83
+ options.testnet = true;
84
+ break;
85
+ case '--rpc-addr':
86
+ options.rpcAddr = args[++i];
87
+ break;
88
+ case '--p2p-addr':
89
+ options.p2pAddr = args[++i];
90
+ break;
91
+ case '--identity':
92
+ options.identity = args[++i];
93
+ break;
94
+ case '--tier':
95
+ options.tier = args[++i];
96
+ break;
97
+ case '-v':
98
+ case '--verbose':
99
+ options.verbose = true;
100
+ break;
101
+ }
102
+ }
103
+
104
+ return options;
105
+ }
106
+
107
+ /**
108
+ * Print startup banner
109
+ */
110
+ function printBanner(options) {
111
+ const tierBadge = options.tier.toUpperCase();
112
+ const tierLabel = `[${tierBadge}]`;
113
+
114
+ console.log(`
115
+ ${colors.cyan}╔═══════════════════════════════════════════════════════════════╗
116
+ ${colors.cyan}║ ║
117
+ ${colors.cyan}║ ${colors.bright}AETHER VALIDATOR${colors.reset}${colors.cyan} ║
118
+ ${colors.cyan}║ ${colors.bright}Starting Validator Node${colors.reset}${colors.cyan} ║
119
+ ${colors.cyan}║ ${colors.bright}${tierLabel}${colors.reset}${colors.cyan} ║
120
+ ${colors.cyan}╚═══════════════════════════════════════════════════════════════╝${colors.reset}
121
+ `);
122
+
123
+ console.log(` ${colors.bright}Network:${colors.reset}`);
124
+ console.log(` Mode: ${options.testnet ? colors.yellow + 'TESTNET' : colors.green + 'MAINNET (not implemented)'}`);
125
+ console.log(` Tier: ${tierLabel}`);
126
+ console.log(` RPC: http://${options.rpcAddr}`);
127
+ console.log(` P2P: ${options.p2pAddr}`);
128
+ if (options.identity) {
129
+ console.log(` Identity: ${options.identity}`);
130
+ }
131
+ console.log();
132
+ }
133
+
134
+ /**
135
+ * Build the validator binary if missing
136
+ */
137
+ function buildValidator() {
138
+ const { execSync } = require('child_process');
139
+ const workspaceRoot = path.join(__dirname, '..', '..');
140
+ const repoPath = path.join(workspaceRoot, 'Jelly-legs-unsteady-workshop');
141
+
142
+ console.log(` ${colors.cyan}Building aether-validator...${colors.reset}`);
143
+
144
+ try {
145
+ execSync('cargo build --bin aether-validator', {
146
+ cwd: repoPath,
147
+ stdio: 'inherit',
148
+ shell: true,
149
+ });
150
+
151
+ // Re-check for binary
152
+ const result = findValidatorBinary();
153
+ if (result.type === 'binary') {
154
+ console.log(` ${colors.green}✓ Build successful!${colors.reset}`);
155
+ return result;
156
+ }
157
+
158
+ console.error(` ${colors.red}✗ Build completed but binary not found${colors.reset}`);
159
+ return null;
160
+ } catch (err) {
161
+ console.error(` ${colors.red}✗ Build failed: ${err.message}${colors.reset}`);
162
+ return null;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Main validator start command
168
+ */
169
+ function validatorStart(overrideTier = null) {
170
+ const options = parseArgs();
171
+
172
+ // Allow tier override from init.js
173
+ if (overrideTier) {
174
+ options.tier = overrideTier;
175
+ }
176
+
177
+ let result = findValidatorBinary();
178
+
179
+ printBanner(options);
180
+
181
+ // Handle missing binary
182
+ if (result.type === 'missing') {
183
+ console.log(` ${colors.yellow}⚠ Validator binary not found${colors.reset}`);
184
+ console.log(` ${colors.cyan}Would you like to build it now? (cargo build --bin aether-validator)${colors.reset}`);
185
+ console.log();
186
+
187
+ const readline = require('readline');
188
+ const rl = readline.createInterface({
189
+ input: process.stdin,
190
+ output: process.stdout,
191
+ });
192
+
193
+ rl.question(' Build now? [Y/n] ', (answer) => {
194
+ rl.close();
195
+
196
+ if (answer.toLowerCase() === 'n' || answer.toLowerCase() === 'no') {
197
+ console.log(` ${colors.red}Aborted. Build the validator first, then try again.${colors.reset}`);
198
+ process.exit(1);
199
+ }
200
+
201
+ const built = buildValidator();
202
+ if (!built) {
203
+ process.exit(1);
204
+ }
205
+ result = built;
206
+ startValidatorProcess(result, options);
207
+ });
208
+ return;
209
+ }
210
+
211
+ startValidatorProcess(result, options);
212
+ }
213
+
214
+ /**
215
+ * Spawn the validator process
216
+ */
217
+ function startValidatorProcess({ type, path: binaryPath, inPath }, options) {
218
+ // Build command args
219
+ const validatorArgs = ['start'];
220
+
221
+ if (options.testnet) {
222
+ validatorArgs.push('--testnet');
223
+ }
224
+ validatorArgs.push('--tier', options.tier);
225
+ validatorArgs.push('--rpc-addr', options.rpcAddr);
226
+ validatorArgs.push('--p2p-addr', options.p2pAddr);
227
+ if (options.identity) {
228
+ validatorArgs.push('--identity', options.identity);
229
+ }
230
+ if (options.verbose) {
231
+ validatorArgs.push('-vvv');
232
+ }
233
+
234
+ const commandDisplay = inPath ? binaryPath : binaryPath || 'cargo run --bin aether-validator';
235
+ console.log(` ${colors.bright}Command:${colors.reset} ${commandDisplay} ${validatorArgs.join(' ')}`);
236
+ console.log();
237
+ console.log(` ${colors.yellow}Starting validator (press Ctrl+C to stop)...${colors.reset}`);
238
+ console.log();
239
+
240
+ // Determine working directory
241
+ const workspaceRoot = path.join(__dirname, '..', '..');
242
+ const repoPath = path.join(workspaceRoot, 'Jelly-legs-unsteady-workshop');
243
+
244
+ // Spawn the validator process
245
+ const child = inPath || binaryPath === 'aether-validator' || binaryPath === 'aether-validator.exe'
246
+ ? spawn(binaryPath, validatorArgs, {
247
+ stdio: ['inherit', 'pipe', 'pipe'],
248
+ })
249
+ : spawn(binaryPath, validatorArgs, {
250
+ stdio: ['inherit', 'pipe', 'pipe'],
251
+ cwd: repoPath,
252
+ });
253
+
254
+ // Colorize output
255
+ const outputColorizer = (data, isError = false) => {
256
+ const str = data.toString();
257
+ const color = isError ? colors.red : colors.reset;
258
+ process.stdout.write(`${color}${str}${colors.reset}`);
259
+ };
260
+
261
+ child.stdout.on('data', (data) => outputColorizer(data));
262
+ child.stderr.on('data', (data) => outputColorizer(data, true));
263
+
264
+ child.on('error', (err) => {
265
+ console.error(`${colors.red}Failed to start validator: ${err.message}${colors.reset}`);
266
+ process.exit(1);
267
+ });
268
+
269
+ child.on('close', (code) => {
270
+ console.log(`\n${colors.yellow}Validator exited with code ${code}${colors.reset}`);
271
+ process.exit(code);
272
+ });
273
+
274
+ // Handle Ctrl+C
275
+ process.on('SIGINT', () => {
276
+ console.log(`\n${colors.yellow}Shutting down validator...${colors.reset}`);
277
+ child.kill('SIGINT');
278
+ setTimeout(() => {
279
+ child.kill('SIGTERM');
280
+ }, 1000);
281
+ });
282
+ }
283
+
284
+ // Export for use as module
285
+ module.exports = { validatorStart };
286
+
287
+ // Run if called directly
288
+ if (require.main === module) {
289
+ validatorStart();
290
+ }