@aaronbassett/midnight-local-devnet 0.1.2 → 0.3.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/README.md CHANGED
@@ -13,16 +13,16 @@ A CLI and MCP (Model Context Protocol) server for managing a local Docker-based
13
13
 
14
14
  ### CLI
15
15
 
16
- Run with no arguments to enter interactive mode:
16
+ Start the network:
17
17
 
18
18
  ```bash
19
- npx @aaronbassett/midnight-local-devnet
19
+ npx @aaronbassett/midnight-local-devnet start
20
20
  ```
21
21
 
22
- Or start the network directly:
22
+ Open the dashboard in your browser to monitor your devnet:
23
23
 
24
24
  ```bash
25
- npx @aaronbassett/midnight-local-devnet start
25
+ npx @aaronbassett/midnight-local-devnet dashboard
26
26
  ```
27
27
 
28
28
  ### MCP Server
@@ -58,9 +58,38 @@ All commands can be run via `npx @aaronbassett/midnight-local-devnet <command>`.
58
58
  | `fund <address>` | Fund a Bech32 address with NIGHT tokens | `--amount <n>` Amount in NIGHT (default: 50,000) |
59
59
  | `fund-file <path>` | Fund all accounts from an accounts.json file | |
60
60
  | `generate-accounts` | Generate random test accounts | `--count <n>`, `--format <mnemonic\|privateKey>`, `--output <path>`, `--fund`, `--register-dust` |
61
+ | `dashboard` | Open browser dashboard | `--port <n>` (default: 31780), `--no-open` Suppress auto-open |
61
62
  | `interactive` | Start interactive menu mode | |
62
63
 
63
- Running with no arguments automatically enters interactive mode.
64
+ Running with no arguments displays help.
65
+
66
+ ## Dashboard
67
+
68
+ The `dashboard` command starts a local server and opens a browser-based dashboard that displays the state of all local devnet services in real time.
69
+
70
+ ```bash
71
+ npx @aaronbassett/midnight-local-devnet dashboard
72
+ ```
73
+
74
+ This starts a server on `http://localhost:31780` and opens your default browser. Press Ctrl+C in the terminal to stop.
75
+
76
+ Options:
77
+
78
+ - `--port <n>` -- Use a specific port (default: 31780, auto-increments if in use)
79
+ - `--no-open` -- Start the server without opening the browser
80
+
81
+ The dashboard shows:
82
+
83
+ - **Node** -- Block height, average block time, chain, peers, sync status, version
84
+ - **Indexer** -- Ready status, response time
85
+ - **Proof Server** -- Version, proof versions, job processing/pending/capacity gauge
86
+ - **Wallet** -- Master wallet NIGHT (unshielded + shielded) and DUST balances
87
+ - **Response Times** -- SVG sparkline charts of per-service response times
88
+ - **Logs** -- Combined color-coded log stream from all services, filterable by service, level, or text search
89
+
90
+ The dashboard connects to the server via WebSocket for live updates. If the connection drops, it auto-reconnects. You can start and stop the network directly from the dashboard using the action buttons.
91
+
92
+ The layout is responsive and adapts to your browser window width.
64
93
 
65
94
  ## MCP Tool Reference
66
95
 
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ import type { NetworkManager } from '../../core/network-manager.js';
3
+ export declare function registerDashboardCommand(program: Command, manager: NetworkManager): void;
@@ -0,0 +1,59 @@
1
+ import { createServer } from 'node:net';
2
+ async function findOpenPort(start, maxAttempts = 10) {
3
+ for (let i = 0; i < maxAttempts; i++) {
4
+ const port = start + i;
5
+ const available = await new Promise((resolve) => {
6
+ const server = createServer();
7
+ server.once('error', () => resolve(false));
8
+ server.once('listening', () => {
9
+ server.close(() => resolve(true));
10
+ });
11
+ server.listen(port, '127.0.0.1');
12
+ });
13
+ if (available)
14
+ return port;
15
+ }
16
+ throw new Error(`No open port found in range ${start}-${start + maxAttempts - 1}`);
17
+ }
18
+ export function registerDashboardCommand(program, manager) {
19
+ program
20
+ .command('dashboard')
21
+ .description('Open browser dashboard for the local devnet')
22
+ .option('--port <port>', 'Port to serve dashboard on', '31780')
23
+ .option('--no-open', 'Do not auto-open the browser')
24
+ .action(async (opts) => {
25
+ const { createDashboardApp } = await import('../dashboard/server.js');
26
+ const { serve } = await import('@hono/node-server');
27
+ const open = (await import('open')).default;
28
+ const preferredPort = parseInt(opts.port, 10);
29
+ const port = await findOpenPort(preferredPort);
30
+ const { app, setupWebSocket, startPolling, shutdown } = createDashboardApp({
31
+ config: manager.config,
32
+ manager,
33
+ port,
34
+ });
35
+ const server = serve({ fetch: app.fetch, port, hostname: '127.0.0.1' }, (info) => {
36
+ const url = `http://localhost:${info.port}`;
37
+ console.log(`Dashboard running at ${url}`);
38
+ console.log('Press Ctrl+C to stop\n');
39
+ if (opts.open !== false) {
40
+ open(url).catch(() => {
41
+ // Ignore browser open errors
42
+ });
43
+ }
44
+ });
45
+ const wss = setupWebSocket(server);
46
+ startPolling();
47
+ const gracefulShutdown = async () => {
48
+ console.log('\nShutting down dashboard...');
49
+ shutdown();
50
+ wss.close();
51
+ await new Promise((resolve) => server.close(() => resolve()));
52
+ await manager.shutdown();
53
+ process.exit(0);
54
+ };
55
+ process.on('SIGINT', gracefulShutdown);
56
+ process.on('SIGTERM', gracefulShutdown);
57
+ });
58
+ }
59
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../src/cli/commands/dashboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ import type { NetworkManager } from '../../core/network-manager.js';
3
+ export declare function registerInteractiveCommand(program: Command, manager: NetworkManager): void;
@@ -0,0 +1,16 @@
1
+ import { startInteractiveMode } from '../interactive.js';
2
+ export function registerInteractiveCommand(program, manager) {
3
+ program
4
+ .command('interactive')
5
+ .description('Start interactive menu mode')
6
+ .action(async () => {
7
+ try {
8
+ await startInteractiveMode(manager);
9
+ }
10
+ catch (error) {
11
+ console.error('Interactive mode error:', error instanceof Error ? error.message : error);
12
+ process.exit(1);
13
+ }
14
+ });
15
+ }
16
+ //# sourceMappingURL=interactive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../../src/cli/commands/interactive.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,UAAU,0BAA0B,CAAC,OAAgB,EAAE,OAAuB;IAClF,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,6BAA6B,CAAC;SAC1C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function generateDashboardHtml({ wsUrl }: {
2
+ wsUrl: string;
3
+ }): string;