solforge 0.2.4 → 0.2.5

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 (42) hide show
  1. package/README.md +471 -79
  2. package/cli.cjs +106 -78
  3. package/package.json +1 -1
  4. package/scripts/install.sh +1 -1
  5. package/scripts/postinstall.cjs +66 -58
  6. package/server/methods/program/get-token-accounts-by-owner.ts +7 -2
  7. package/server/ws-server.ts +4 -1
  8. package/src/api-server-entry.ts +91 -91
  9. package/src/cli/commands/rpc-start.ts +4 -1
  10. package/src/cli/main.ts +7 -3
  11. package/src/cli/run-solforge.ts +20 -6
  12. package/src/commands/add-program.ts +324 -328
  13. package/src/commands/init.ts +106 -106
  14. package/src/commands/list.ts +125 -125
  15. package/src/commands/mint.ts +246 -246
  16. package/src/commands/start.ts +834 -831
  17. package/src/commands/status.ts +80 -80
  18. package/src/commands/stop.ts +381 -382
  19. package/src/config/manager.ts +149 -149
  20. package/src/gui/public/app.css +1556 -1
  21. package/src/gui/public/build/main.css +1569 -1
  22. package/src/gui/server.ts +20 -21
  23. package/src/gui/src/app.tsx +56 -37
  24. package/src/gui/src/components/airdrop-mint-form.tsx +17 -11
  25. package/src/gui/src/components/clone-program-modal.tsx +6 -6
  26. package/src/gui/src/components/clone-token-modal.tsx +7 -7
  27. package/src/gui/src/components/modal.tsx +13 -11
  28. package/src/gui/src/components/programs-panel.tsx +27 -15
  29. package/src/gui/src/components/status-panel.tsx +31 -17
  30. package/src/gui/src/components/tokens-panel.tsx +25 -19
  31. package/src/gui/src/index.css +491 -463
  32. package/src/index.ts +161 -146
  33. package/src/rpc/start.ts +1 -1
  34. package/src/services/api-server.ts +470 -473
  35. package/src/services/port-manager.ts +167 -167
  36. package/src/services/process-registry.ts +143 -143
  37. package/src/services/program-cloner.ts +312 -312
  38. package/src/services/token-cloner.ts +799 -797
  39. package/src/services/validator.ts +288 -288
  40. package/src/types/config.ts +71 -71
  41. package/src/utils/shell.ts +75 -75
  42. package/src/utils/token-loader.ts +77 -77
package/cli.cjs CHANGED
@@ -9,90 +9,114 @@ const https = require("node:https");
9
9
  const { spawn } = require("node:child_process");
10
10
 
11
11
  function pkg() {
12
- // Resolve package.json next to this file regardless of install location
13
- const p = path.join(__dirname, "package.json");
14
- try { return require(p); } catch { return { version: "" }; }
12
+ // Resolve package.json next to this file regardless of install location
13
+ const p = path.join(__dirname, "package.json");
14
+ try {
15
+ return require(p);
16
+ } catch {
17
+ return { version: "" };
18
+ }
15
19
  }
16
20
 
17
21
  function assetName() {
18
- const p = process.platform;
19
- const a = process.arch;
20
- if (p === "darwin" && a === "arm64") return "solforge-darwin-arm64";
21
- if (p === "darwin" && a === "x64") return "solforge-darwin-x64";
22
- if (p === "linux" && a === "x64") return "solforge-linux-x64";
23
- if (p === "linux" && a === "arm64") return "solforge-linux-arm64";
24
- if (p === "win32" && a === "x64") return "solforge-windows-x64.exe";
25
- return null;
22
+ const p = process.platform;
23
+ const a = process.arch;
24
+ if (p === "darwin" && a === "arm64") return "solforge-darwin-arm64";
25
+ if (p === "darwin" && a === "x64") return "solforge-darwin-x64";
26
+ if (p === "linux" && a === "x64") return "solforge-linux-x64";
27
+ if (p === "linux" && a === "arm64") return "solforge-linux-arm64";
28
+ if (p === "win32" && a === "x64") return "solforge-windows-x64.exe";
29
+ return null;
26
30
  }
27
31
 
28
32
  function vendorPath() {
29
- const name = assetName();
30
- if (!name) return null;
31
- return path.join(__dirname, "vendor", name);
33
+ const name = assetName();
34
+ if (!name) return null;
35
+ return path.join(__dirname, "vendor", name);
32
36
  }
33
37
 
34
38
  function download(url, outPath) {
35
- return new Promise((resolve, reject) => {
36
- const req = https.get(url, (res) => {
37
- if ([301, 302, 307, 308].includes(res.statusCode) && res.headers.location) {
38
- return resolve(download(res.headers.location, outPath));
39
- }
40
- if (res.statusCode !== 200) {
41
- return reject(new Error(`HTTP ${res.statusCode}`));
42
- }
43
- const file = fs.createWriteStream(outPath, { mode: process.platform === "win32" ? undefined : 0o755 });
44
- res.pipe(file);
45
- file.on("finish", () => file.close(() => resolve()));
46
- file.on("error", reject);
47
- });
48
- req.on("error", reject);
49
- });
39
+ return new Promise((resolve, reject) => {
40
+ const req = https.get(url, (res) => {
41
+ if (
42
+ [301, 302, 307, 308].includes(res.statusCode) &&
43
+ res.headers.location
44
+ ) {
45
+ return resolve(download(res.headers.location, outPath));
46
+ }
47
+ if (res.statusCode !== 200) {
48
+ return reject(new Error(`HTTP ${res.statusCode}`));
49
+ }
50
+ const file = fs.createWriteStream(outPath, {
51
+ mode: process.platform === "win32" ? undefined : 0o755,
52
+ });
53
+ res.pipe(file);
54
+ file.on("finish", () => file.close(() => resolve()));
55
+ file.on("error", reject);
56
+ });
57
+ req.on("error", reject);
58
+ });
50
59
  }
51
60
 
52
61
  async function ensureBinary() {
53
- const vp = vendorPath();
54
- if (!vp) return null;
55
- if (fs.existsSync(vp)) return vp;
62
+ const vp = vendorPath();
63
+ if (!vp) return null;
64
+ if (fs.existsSync(vp)) return vp;
56
65
 
57
- // Respect opt-out
58
- if (String(process.env.SOLFORGE_SKIP_DOWNLOAD || "").toLowerCase() === "true") {
59
- return null;
60
- }
66
+ // Respect opt-out
67
+ if (
68
+ String(process.env.SOLFORGE_SKIP_DOWNLOAD || "").toLowerCase() === "true"
69
+ ) {
70
+ return null;
71
+ }
61
72
 
62
- const { version, repository } = pkg();
63
- const repo = process.env.SOLFORGE_REPO || (repository && (typeof repository === "string" ? repository.replace(/^github:/, "") : (repository.url && (repository.url.match(/github\.com[:/](.+?)\.git$/) || [])[1]))) || "nitishxyz/solforge";
64
- if (!version) return null;
65
- const asset = path.basename(vp);
66
- const url = `https://github.com/${repo}/releases/download/v${version}/${asset}`;
73
+ const { version, repository } = pkg();
74
+ const repo =
75
+ process.env.SOLFORGE_REPO ||
76
+ (repository &&
77
+ (typeof repository === "string"
78
+ ? repository.replace(/^github:/, "")
79
+ : repository.url &&
80
+ (repository.url.match(/github\.com[:/](.+?)\.git$/) || [])[1])) ||
81
+ "nitishxyz/solforge";
82
+ if (!version) return null;
83
+ const asset = path.basename(vp);
84
+ const url = `https://github.com/${repo}/releases/download/v${version}/${asset}`;
67
85
 
68
- try {
69
- fs.mkdirSync(path.dirname(vp), { recursive: true });
70
- await download(url, vp);
71
- if (process.platform !== "win32") {
72
- try { fs.chmodSync(vp, 0o755); } catch {}
73
- }
74
- return fs.existsSync(vp) ? vp : null;
75
- } catch {
76
- return null;
77
- }
86
+ try {
87
+ fs.mkdirSync(path.dirname(vp), { recursive: true });
88
+ await download(url, vp);
89
+ if (process.platform !== "win32") {
90
+ try {
91
+ fs.chmodSync(vp, 0o755);
92
+ } catch {}
93
+ }
94
+ return fs.existsSync(vp) ? vp : null;
95
+ } catch {
96
+ return null;
97
+ }
78
98
  }
79
99
 
80
100
  function run(cmd, args) {
81
- return new Promise((resolve) => {
82
- const child = spawn(cmd, args, { stdio: "inherit" });
83
- child.on("exit", (code) => resolve(typeof code === "number" ? code : 0));
84
- });
101
+ return new Promise((resolve) => {
102
+ const child = spawn(cmd, args, { stdio: "inherit" });
103
+ child.on("exit", (code) => resolve(typeof code === "number" ? code : 0));
104
+ });
85
105
  }
86
106
 
87
107
  (async () => {
88
- // Fast path for --version/--help without booting the app
89
- const args = process.argv.slice(2);
90
- if (args.includes("-v") || args.includes("--version") || args[0] === "version") {
91
- console.log(pkg().version || "");
92
- process.exit(0);
93
- }
94
- if (args.includes("-h") || args.includes("--help") || args[0] === "help") {
95
- console.log(`
108
+ // Fast path for --version/--help without booting the app
109
+ const args = process.argv.slice(2);
110
+ if (
111
+ args.includes("-v") ||
112
+ args.includes("--version") ||
113
+ args[0] === "version"
114
+ ) {
115
+ console.log(pkg().version || "");
116
+ process.exit(0);
117
+ }
118
+ if (args.includes("-h") || args.includes("--help") || args[0] === "help") {
119
+ console.log(`
96
120
  solforge <command>
97
121
 
98
122
  Commands:
@@ -111,21 +135,25 @@ Commands:
111
135
  Options:
112
136
  -h, --help Show help
113
137
  -v, --version Show version
138
+ --network Bind servers to 0.0.0.0 (LAN access)
139
+ -y, --ci Non-interactive; auto-accept prompts (use existing config)
114
140
  `);
115
- process.exit(0);
116
- }
141
+ process.exit(0);
142
+ }
117
143
 
118
- const vp = await ensureBinary();
119
- if (vp) {
120
- const code = await run(vp, process.argv.slice(2));
121
- process.exit(code);
122
- }
123
- // Fallback: try to run TS entry via Bun
124
- const bun = process.env.SOLFORGE_BUN || "bun";
125
- const entry = path.join(__dirname, "src", "cli", "main.ts");
126
- const code = await run(bun, [entry, ...process.argv.slice(2)]);
127
- if (code !== 0) {
128
- console.error("solforge: failed to run binary and Bun fallback. Install Bun or ensure a release asset exists.");
129
- }
130
- process.exit(code);
144
+ const vp = await ensureBinary();
145
+ if (vp) {
146
+ const code = await run(vp, process.argv.slice(2));
147
+ process.exit(code);
148
+ }
149
+ // Fallback: try to run TS entry via Bun
150
+ const bun = process.env.SOLFORGE_BUN || "bun";
151
+ const entry = path.join(__dirname, "src", "cli", "main.ts");
152
+ const code = await run(bun, [entry, ...process.argv.slice(2)]);
153
+ if (code !== 0) {
154
+ console.error(
155
+ "solforge: failed to run binary and Bun fallback. Install Bun or ensure a release asset exists.",
156
+ );
157
+ }
158
+ process.exit(code);
131
159
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solforge",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "private": false,
@@ -3,7 +3,7 @@ set -e
3
3
 
4
4
  # SolForge installer (downloads GitHub release binary)
5
5
  # Usage: curl -fsSL https://sh.solforge.sh | sh
6
- # Optional: SOLFORGE_VERSION=v0.2.3 curl -fsSL https://sh.solforge.sh | sh
6
+ # Optional: SOLFORGE_VERSION=v0.2.5 curl -fsSL https://sh.solforge.sh | sh
7
7
 
8
8
  REPO="nitishxyz/solforge"
9
9
  BIN_NAME="solforge"
@@ -9,61 +9,62 @@ const path = require("path");
9
9
  const https = require("https");
10
10
 
11
11
  function log(msg) {
12
- console.log(`[solforge] ${msg}`);
12
+ console.log(`[solforge] ${msg}`);
13
13
  }
14
14
  function warn(msg) {
15
- console.warn(`[solforge] ${msg}`);
15
+ console.warn(`[solforge] ${msg}`);
16
16
  }
17
17
 
18
18
  if (String(process.env.SOLFORGE_SKIP_DOWNLOAD || "").toLowerCase() === "true") {
19
- log("Skipping binary download due to SOLFORGE_SKIP_DOWNLOAD=true");
20
- process.exit(0);
19
+ log("Skipping binary download due to SOLFORGE_SKIP_DOWNLOAD=true");
20
+ process.exit(0);
21
21
  }
22
22
 
23
23
  function assetName() {
24
- const p = process.platform;
25
- const a = process.arch;
26
- if (p === "darwin" && a === "arm64") return "solforge-darwin-arm64";
27
- if (p === "darwin" && a === "x64") return "solforge-darwin-x64";
28
- if (p === "linux" && a === "x64") return "solforge-linux-x64";
29
- if (p === "linux" && a === "arm64") return "solforge-linux-arm64";
30
- if (p === "win32" && a === "x64") return "solforge-windows-x64.exe";
31
- return null;
24
+ const p = process.platform;
25
+ const a = process.arch;
26
+ if (p === "darwin" && a === "arm64") return "solforge-darwin-arm64";
27
+ if (p === "darwin" && a === "x64") return "solforge-darwin-x64";
28
+ if (p === "linux" && a === "x64") return "solforge-linux-x64";
29
+ if (p === "linux" && a === "arm64") return "solforge-linux-arm64";
30
+ if (p === "win32" && a === "x64") return "solforge-windows-x64.exe";
31
+ return null;
32
32
  }
33
33
 
34
34
  function getRepo() {
35
- try {
36
- const pkg = require(path.join(__dirname, "..", "package.json"));
37
- if (pkg.repository) {
38
- if (typeof pkg.repository === "string") return pkg.repository.replace(/^github:/, "");
39
- if (pkg.repository.url) {
40
- const m = pkg.repository.url.match(/github\.com[:/](.+?)\.git$/);
41
- if (m) return m[1];
42
- }
43
- }
44
- } catch {}
45
- return process.env.SOLFORGE_REPO || "nitishxyz/solforge";
35
+ try {
36
+ const pkg = require(path.join(__dirname, "..", "package.json"));
37
+ if (pkg.repository) {
38
+ if (typeof pkg.repository === "string")
39
+ return pkg.repository.replace(/^github:/, "");
40
+ if (pkg.repository.url) {
41
+ const m = pkg.repository.url.match(/github\.com[:/](.+?)\.git$/);
42
+ if (m) return m[1];
43
+ }
44
+ }
45
+ } catch {}
46
+ return process.env.SOLFORGE_REPO || "nitishxyz/solforge";
46
47
  }
47
48
 
48
49
  function getVersion() {
49
- try {
50
- const pkg = require(path.join(__dirname, "..", "package.json"));
51
- return pkg.version;
52
- } catch {
53
- return process.env.npm_package_version || "";
54
- }
50
+ try {
51
+ const pkg = require(path.join(__dirname, "..", "package.json"));
52
+ return pkg.version;
53
+ } catch {
54
+ return process.env.npm_package_version || "";
55
+ }
55
56
  }
56
57
 
57
58
  const name = assetName();
58
59
  if (!name) {
59
- warn(`No prebuilt binary for ${process.platform}/${process.arch}; skipping`);
60
- process.exit(0);
60
+ warn(`No prebuilt binary for ${process.platform}/${process.arch}; skipping`);
61
+ process.exit(0);
61
62
  }
62
63
 
63
64
  const version = getVersion();
64
65
  if (!version) {
65
- warn("Unable to determine package version; skipping binary download");
66
- process.exit(0);
66
+ warn("Unable to determine package version; skipping binary download");
67
+ process.exit(0);
67
68
  }
68
69
 
69
70
  const repo = getRepo();
@@ -73,38 +74,45 @@ const vendorDir = path.join(__dirname, "..", "vendor");
73
74
  const outPath = path.join(vendorDir, name);
74
75
 
75
76
  if (fs.existsSync(outPath)) {
76
- log(`Binary already present at vendor/${name}`);
77
- process.exit(0);
77
+ log(`Binary already present at vendor/${name}`);
78
+ process.exit(0);
78
79
  }
79
80
 
80
81
  fs.mkdirSync(vendorDir, { recursive: true });
81
82
 
82
83
  function download(to, from, cb, redirects = 0) {
83
- const req = https.get(from, (res) => {
84
- if ([301, 302, 307, 308].includes(res.statusCode) && res.headers.location && redirects < 5) {
85
- return download(to, res.headers.location, cb, redirects + 1);
86
- }
87
- if (res.statusCode !== 200) {
88
- return cb(new Error(`HTTP ${res.statusCode} for ${from}`));
89
- }
90
- const file = fs.createWriteStream(to, { mode: 0o755 });
91
- res.pipe(file);
92
- file.on("finish", () => file.close(cb));
93
- });
94
- req.on("error", (err) => cb(err));
84
+ const req = https.get(from, (res) => {
85
+ if (
86
+ [301, 302, 307, 308].includes(res.statusCode) &&
87
+ res.headers.location &&
88
+ redirects < 5
89
+ ) {
90
+ return download(to, res.headers.location, cb, redirects + 1);
91
+ }
92
+ if (res.statusCode !== 200) {
93
+ return cb(new Error(`HTTP ${res.statusCode} for ${from}`));
94
+ }
95
+ const file = fs.createWriteStream(to, { mode: 0o755 });
96
+ res.pipe(file);
97
+ file.on("finish", () => file.close(cb));
98
+ });
99
+ req.on("error", (err) => cb(err));
95
100
  }
96
101
 
97
102
  log(`Fetching ${name} for v${version}...`);
98
103
  download(outPath, url, (err) => {
99
- if (err) {
100
- warn(`Could not download prebuilt binary: ${err.message}`);
101
- warn("CLI will fall back to running via Bun if available.");
102
- try { fs.unlinkSync(outPath); } catch {}
103
- process.exit(0);
104
- }
105
- if (process.platform !== "win32") {
106
- try { fs.chmodSync(outPath, 0o755); } catch {}
107
- }
108
- log(`Installed vendor/${name}`);
104
+ if (err) {
105
+ warn(`Could not download prebuilt binary: ${err.message}`);
106
+ warn("CLI will fall back to running via Bun if available.");
107
+ try {
108
+ fs.unlinkSync(outPath);
109
+ } catch {}
110
+ process.exit(0);
111
+ }
112
+ if (process.platform !== "win32") {
113
+ try {
114
+ fs.chmodSync(outPath, 0o755);
115
+ } catch {}
116
+ }
117
+ log(`Installed vendor/${name}`);
109
118
  });
110
-
@@ -359,12 +359,17 @@ export const getTokenAccountsByOwner: RpcMethodHandler = async (
359
359
  }
360
360
 
361
361
  // Build filtered list keeping only chosen indices
362
- const keep = new Set<number>(Array.from(pick.values()).filter((v) => typeof v === "number" && v >= 0));
362
+ const keep = new Set<number>(
363
+ Array.from(pick.values()).filter(
364
+ (v) => typeof v === "number" && v >= 0,
365
+ ),
366
+ );
363
367
  const filtered: any[] = [];
364
368
  for (let i = 0; i < out.length; i++) {
365
369
  const e = out[i];
366
370
  const info = e.account?.data?.parsed?.info;
367
- const key = info?.owner && info?.mint ? `${info.owner}:${info.mint}` : null;
371
+ const key =
372
+ info?.owner && info?.mint ? `${info.owner}:${info.mint}` : null;
368
373
  if (key && keep.has(i)) filtered.push(e);
369
374
  else if (!key) filtered.push(e); // non-parsed or unexpected shape
370
375
  }
@@ -6,6 +6,7 @@ type Sub = { id: number; type: "signature"; signature: string };
6
6
  export function createLiteSVMWebSocketServer(
7
7
  rpcServer: LiteSVMRpcServer,
8
8
  port: number = 8900,
9
+ host?: string,
9
10
  ) {
10
11
  let nextSubId = 1;
11
12
  const subs = new Map<number, Sub>();
@@ -67,6 +68,7 @@ export function createLiteSVMWebSocketServer(
67
68
 
68
69
  const server: Server = Bun.serve({
69
70
  port,
71
+ hostname: host || process.env.RPC_HOST || "127.0.0.1",
70
72
  fetch(req, srv) {
71
73
  if (srv.upgrade(req)) return undefined as any;
72
74
  return new Response("Not a websocket", { status: 400 });
@@ -160,7 +162,8 @@ export function createLiteSVMWebSocketServer(
160
162
  },
161
163
  });
162
164
 
163
- console.log(`📣 LiteSVM RPC PubSub running on ws://localhost:${port}`);
165
+ const hostname = (host || process.env.RPC_HOST || "127.0.0.1").toString();
166
+ console.log(`📣 LiteSVM RPC PubSub running on ws://${hostname}:${port}`);
164
167
  return {
165
168
  wsServer: server,
166
169
  stop: () => {
@@ -1,109 +1,109 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { APIServer } from "./services/api-server.js";
4
- import { configManager } from "./config/manager.js";
5
3
  import chalk from "chalk";
4
+ import { configManager } from "./config/manager.js";
5
+ import { APIServer } from "./services/api-server.js";
6
6
 
7
7
  async function main() {
8
- try {
9
- // Parse command line arguments
10
- const args = process.argv.slice(2);
11
- const portIndex = args.indexOf("--port");
12
- const hostIndex = args.indexOf("--host");
13
- const configIndex = args.indexOf("--config");
14
- const rpcIndex = args.indexOf("--rpc-url");
15
- const faucetIndex = args.indexOf("--faucet-url");
16
- const workDirIndex = args.indexOf("--work-dir");
8
+ try {
9
+ // Parse command line arguments
10
+ const args = process.argv.slice(2);
11
+ const portIndex = args.indexOf("--port");
12
+ const hostIndex = args.indexOf("--host");
13
+ const configIndex = args.indexOf("--config");
14
+ const rpcIndex = args.indexOf("--rpc-url");
15
+ const faucetIndex = args.indexOf("--faucet-url");
16
+ const workDirIndex = args.indexOf("--work-dir");
17
17
 
18
- if (
19
- portIndex === -1 ||
20
- configIndex === -1 ||
21
- rpcIndex === -1 ||
22
- faucetIndex === -1 ||
23
- workDirIndex === -1 ||
24
- !args[portIndex + 1] ||
25
- !args[configIndex + 1] ||
26
- !args[rpcIndex + 1] ||
27
- !args[faucetIndex + 1] ||
28
- !args[workDirIndex + 1]
29
- ) {
30
- console.error(
31
- "Usage: api-server-entry --port <port> --config <config-path> --rpc-url <url> --faucet-url <url> --work-dir <dir> [--host <host>]"
32
- );
33
- process.exit(1);
34
- }
18
+ if (
19
+ portIndex === -1 ||
20
+ configIndex === -1 ||
21
+ rpcIndex === -1 ||
22
+ faucetIndex === -1 ||
23
+ workDirIndex === -1 ||
24
+ !args[portIndex + 1] ||
25
+ !args[configIndex + 1] ||
26
+ !args[rpcIndex + 1] ||
27
+ !args[faucetIndex + 1] ||
28
+ !args[workDirIndex + 1]
29
+ ) {
30
+ console.error(
31
+ "Usage: api-server-entry --port <port> --config <config-path> --rpc-url <url> --faucet-url <url> --work-dir <dir> [--host <host>]",
32
+ );
33
+ process.exit(1);
34
+ }
35
35
 
36
- const port = parseInt(args[portIndex + 1]!);
37
- const host =
38
- hostIndex !== -1 && args[hostIndex + 1] ? args[hostIndex + 1] : undefined;
39
- const configPath = args[configIndex + 1]!;
40
- const rpcUrl = args[rpcIndex + 1]!;
41
- const faucetUrl = args[faucetIndex + 1]!;
42
- const workDir = args[workDirIndex + 1]!;
36
+ const port = parseInt(args[portIndex + 1]!);
37
+ const host =
38
+ hostIndex !== -1 && args[hostIndex + 1] ? args[hostIndex + 1] : undefined;
39
+ const configPath = args[configIndex + 1]!;
40
+ const rpcUrl = args[rpcIndex + 1]!;
41
+ const faucetUrl = args[faucetIndex + 1]!;
42
+ const workDir = args[workDirIndex + 1]!;
43
43
 
44
- // Load configuration
45
- await configManager.load(configPath);
46
- const config = configManager.getConfig();
44
+ // Load configuration
45
+ await configManager.load(configPath);
46
+ const config = configManager.getConfig();
47
47
 
48
- // Create and start API server
49
- const apiServer = new APIServer({
50
- port,
51
- host,
52
- validatorRpcUrl: rpcUrl,
53
- validatorFaucetUrl: faucetUrl,
54
- config,
55
- workDir,
56
- });
48
+ // Create and start API server
49
+ const apiServer = new APIServer({
50
+ port,
51
+ host,
52
+ validatorRpcUrl: rpcUrl,
53
+ validatorFaucetUrl: faucetUrl,
54
+ config,
55
+ workDir,
56
+ });
57
57
 
58
- const result = await apiServer.start();
58
+ const result = await apiServer.start();
59
59
 
60
- if (result.success) {
61
- console.log(chalk.green(`🚀 API Server started on port ${port}`));
60
+ if (result.success) {
61
+ console.log(chalk.green(`🚀 API Server started on port ${port}`));
62
62
 
63
- // Keep the process alive
64
- process.on("SIGTERM", async () => {
65
- console.log(
66
- chalk.yellow("📡 API Server received SIGTERM, shutting down...")
67
- );
68
- await apiServer.stop();
69
- process.exit(0);
70
- });
63
+ // Keep the process alive
64
+ process.on("SIGTERM", async () => {
65
+ console.log(
66
+ chalk.yellow("📡 API Server received SIGTERM, shutting down..."),
67
+ );
68
+ await apiServer.stop();
69
+ process.exit(0);
70
+ });
71
71
 
72
- process.on("SIGINT", async () => {
73
- console.log(
74
- chalk.yellow("📡 API Server received SIGINT, shutting down...")
75
- );
76
- await apiServer.stop();
77
- process.exit(0);
78
- });
72
+ process.on("SIGINT", async () => {
73
+ console.log(
74
+ chalk.yellow("📡 API Server received SIGINT, shutting down..."),
75
+ );
76
+ await apiServer.stop();
77
+ process.exit(0);
78
+ });
79
79
 
80
- // Keep process alive
81
- setInterval(() => {}, 1000);
82
- } else {
83
- console.error(
84
- chalk.red(`❌ Failed to start API server: ${result.error}`)
85
- );
86
- process.exit(1);
87
- }
88
- } catch (error) {
89
- console.error(
90
- chalk.red(
91
- `❌ API Server error: ${
92
- error instanceof Error ? error.message : String(error)
93
- }`
94
- )
95
- );
96
- process.exit(1);
97
- }
80
+ // Keep process alive
81
+ setInterval(() => {}, 1000);
82
+ } else {
83
+ console.error(
84
+ chalk.red(`❌ Failed to start API server: ${result.error}`),
85
+ );
86
+ process.exit(1);
87
+ }
88
+ } catch (error) {
89
+ console.error(
90
+ chalk.red(
91
+ `❌ API Server error: ${
92
+ error instanceof Error ? error.message : String(error)
93
+ }`,
94
+ ),
95
+ );
96
+ process.exit(1);
97
+ }
98
98
  }
99
99
 
100
100
  main().catch((error) => {
101
- console.error(
102
- chalk.red(
103
- `❌ Fatal error: ${
104
- error instanceof Error ? error.message : String(error)
105
- }`
106
- )
107
- );
108
- process.exit(1);
101
+ console.error(
102
+ chalk.red(
103
+ `❌ Fatal error: ${
104
+ error instanceof Error ? error.message : String(error)
105
+ }`,
106
+ ),
107
+ );
108
+ process.exit(1);
109
109
  });