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.
- package/README.md +471 -79
- package/cli.cjs +106 -78
- package/package.json +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/postinstall.cjs +66 -58
- package/server/methods/program/get-token-accounts-by-owner.ts +7 -2
- package/server/ws-server.ts +4 -1
- package/src/api-server-entry.ts +91 -91
- package/src/cli/commands/rpc-start.ts +4 -1
- package/src/cli/main.ts +7 -3
- package/src/cli/run-solforge.ts +20 -6
- package/src/commands/add-program.ts +324 -328
- package/src/commands/init.ts +106 -106
- package/src/commands/list.ts +125 -125
- package/src/commands/mint.ts +246 -246
- package/src/commands/start.ts +834 -831
- package/src/commands/status.ts +80 -80
- package/src/commands/stop.ts +381 -382
- package/src/config/manager.ts +149 -149
- package/src/gui/public/app.css +1556 -1
- package/src/gui/public/build/main.css +1569 -1
- package/src/gui/server.ts +20 -21
- package/src/gui/src/app.tsx +56 -37
- package/src/gui/src/components/airdrop-mint-form.tsx +17 -11
- package/src/gui/src/components/clone-program-modal.tsx +6 -6
- package/src/gui/src/components/clone-token-modal.tsx +7 -7
- package/src/gui/src/components/modal.tsx +13 -11
- package/src/gui/src/components/programs-panel.tsx +27 -15
- package/src/gui/src/components/status-panel.tsx +31 -17
- package/src/gui/src/components/tokens-panel.tsx +25 -19
- package/src/gui/src/index.css +491 -463
- package/src/index.ts +161 -146
- package/src/rpc/start.ts +1 -1
- package/src/services/api-server.ts +470 -473
- package/src/services/port-manager.ts +167 -167
- package/src/services/process-registry.ts +143 -143
- package/src/services/program-cloner.ts +312 -312
- package/src/services/token-cloner.ts +799 -797
- package/src/services/validator.ts +288 -288
- package/src/types/config.ts +71 -71
- package/src/utils/shell.ts +75 -75
- 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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
const vp = vendorPath();
|
|
63
|
+
if (!vp) return null;
|
|
64
|
+
if (fs.existsSync(vp)) return vp;
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
// Respect opt-out
|
|
67
|
+
if (
|
|
68
|
+
String(process.env.SOLFORGE_SKIP_DOWNLOAD || "").toLowerCase() === "true"
|
|
69
|
+
) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
116
|
-
|
|
141
|
+
process.exit(0);
|
|
142
|
+
}
|
|
117
143
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
package/scripts/install.sh
CHANGED
|
@@ -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.
|
|
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"
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -9,61 +9,62 @@ const path = require("path");
|
|
|
9
9
|
const https = require("https");
|
|
10
10
|
|
|
11
11
|
function log(msg) {
|
|
12
|
-
|
|
12
|
+
console.log(`[solforge] ${msg}`);
|
|
13
13
|
}
|
|
14
14
|
function warn(msg) {
|
|
15
|
-
|
|
15
|
+
console.warn(`[solforge] ${msg}`);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (String(process.env.SOLFORGE_SKIP_DOWNLOAD || "").toLowerCase() === "true") {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
log("Skipping binary download due to SOLFORGE_SKIP_DOWNLOAD=true");
|
|
20
|
+
process.exit(0);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function assetName() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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>(
|
|
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 =
|
|
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
|
}
|
package/server/ws-server.ts
CHANGED
|
@@ -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
|
-
|
|
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: () => {
|
package/src/api-server-entry.ts
CHANGED
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// Load configuration
|
|
45
|
+
await configManager.load(configPath);
|
|
46
|
+
const config = configManager.getConfig();
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
+
const result = await apiServer.start();
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if (result.success) {
|
|
61
|
+
console.log(chalk.green(`🚀 API Server started on port ${port}`));
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
});
|