@fiber-pay/node 0.1.0-rc.1

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 ADDED
@@ -0,0 +1,20 @@
1
+ # @fiber-pay/node
2
+
3
+ Fiber node binary management and process lifecycle utilities.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @fiber-pay/node
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { createFiberNodeManager } from '@fiber-pay/node';
15
+ ```
16
+
17
+ ## Compatibility
18
+
19
+ - Node.js `>=20`
20
+ - Fiber target: `v0.6.1`
@@ -0,0 +1,211 @@
1
+ import { EventEmitter } from 'node:events';
2
+
3
+ /**
4
+ * Shared constants for the @fiber-pay/node package
5
+ */
6
+ /** Default Fiber Network Node binary version used for downloads when no version is specified. */
7
+ declare const DEFAULT_FIBER_VERSION = "v0.6.1";
8
+
9
+ /**
10
+ * Binary Manager
11
+ * Handles downloading, installing, and managing the Fiber Network Node (fnn) binary
12
+ */
13
+ type Platform = 'darwin' | 'linux' | 'win32';
14
+ type Arch = 'x64' | 'arm64';
15
+ interface BinaryInfo {
16
+ /** Path to the binary */
17
+ path: string;
18
+ /** Version of the binary */
19
+ version: string;
20
+ /** Whether the binary exists and is executable */
21
+ ready: boolean;
22
+ }
23
+ interface DownloadOptions {
24
+ /** Target directory for the binary */
25
+ installDir?: string;
26
+ /** Specific version to download (default: latest) */
27
+ version?: string;
28
+ /** Force re-download even if binary exists */
29
+ force?: boolean;
30
+ /** Progress callback */
31
+ onProgress?: (progress: DownloadProgress) => void;
32
+ }
33
+ interface DownloadProgress {
34
+ phase: 'fetching' | 'downloading' | 'extracting' | 'installing';
35
+ percent?: number;
36
+ message: string;
37
+ }
38
+ interface AssetCandidate {
39
+ name: string;
40
+ url: string;
41
+ usesRosetta: boolean;
42
+ }
43
+ declare class BinaryManager {
44
+ private installDir;
45
+ constructor(installDir?: string);
46
+ /**
47
+ * Get the current platform and architecture
48
+ */
49
+ getPlatformInfo(): {
50
+ platform: Platform;
51
+ arch: Arch;
52
+ };
53
+ /**
54
+ * Get the pattern to match for the current platform
55
+ */
56
+ getAssetPattern(): string;
57
+ /**
58
+ * Get the path where the binary should be installed
59
+ */
60
+ getBinaryPath(): string;
61
+ /**
62
+ * Check if the binary is installed and get its info
63
+ */
64
+ getBinaryInfo(): Promise<BinaryInfo>;
65
+ /**
66
+ * Fetch the latest release tag from GitHub (no API, follows redirect)
67
+ */
68
+ getLatestTag(): Promise<string>;
69
+ /**
70
+ * Normalize a version into a release tag
71
+ */
72
+ normalizeTag(version: string): string;
73
+ /**
74
+ * Build download candidates for the current platform
75
+ */
76
+ buildAssetCandidates(tag: string): AssetCandidate[];
77
+ /**
78
+ * Validate Rosetta support when falling back to x86_64 binary on Apple Silicon.
79
+ */
80
+ private ensureRosettaAvailable;
81
+ /**
82
+ * Download and install the Fiber binary
83
+ */
84
+ download(options?: DownloadOptions): Promise<BinaryInfo>;
85
+ /**
86
+ * Extract tar.gz archive
87
+ */
88
+ private extractTarGz;
89
+ /**
90
+ * Extract zip archive (primarily for Windows)
91
+ */
92
+ private extractZip;
93
+ /**
94
+ * Remove the installed binary
95
+ */
96
+ uninstall(): Promise<void>;
97
+ }
98
+ /**
99
+ * Download the Fiber binary to the default location
100
+ */
101
+ declare function downloadFiberBinary(options?: DownloadOptions): Promise<BinaryInfo>;
102
+ /**
103
+ * Get information about the installed binary
104
+ */
105
+ declare function getFiberBinaryInfo(installDir?: string): Promise<BinaryInfo>;
106
+ /**
107
+ * Ensure the Fiber binary is available, downloading if necessary.
108
+ * If the binary exists but its version does not match the requested
109
+ * (or default) version, it will be re-downloaded.
110
+ */
111
+ declare function ensureFiberBinary(options?: DownloadOptions): Promise<string>;
112
+ /**
113
+ * Get the default binary path
114
+ */
115
+ declare function getDefaultBinaryPath(): string;
116
+
117
+ /**
118
+ * Process Manager
119
+ * Manages the lifecycle of the Fiber Network Node (fnn) binary
120
+ */
121
+
122
+ interface FiberNodeConfig {
123
+ /** Path to the fnn binary */
124
+ binaryPath: string;
125
+ /** Base directory for data storage */
126
+ dataDir: string;
127
+ /** Path to the config file (optional - will use built-in testnet config if not provided) */
128
+ configFilePath?: string;
129
+ /** Fiber P2P listening address */
130
+ fiberListeningAddr?: string;
131
+ /** Fiber node name */
132
+ nodeName?: string;
133
+ /** Bootstrap node addresses */
134
+ bootnodeAddrs?: string[];
135
+ /** CKB RPC URL */
136
+ ckbRpcUrl?: string;
137
+ /** RPC listening address */
138
+ rpcListeningAddr?: string;
139
+ /** Chain configuration (mainnet, testnet, or file path) */
140
+ chain?: 'mainnet' | 'testnet' | string;
141
+ /** Key encryption password */
142
+ keyPassword?: string;
143
+ /** Log level */
144
+ logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error';
145
+ /** UDT whitelist */
146
+ udtWhitelist?: Array<{
147
+ name: string;
148
+ script: {
149
+ code_hash: string;
150
+ hash_type: 'type' | 'data' | 'data1' | 'data2';
151
+ args: string;
152
+ };
153
+ }>;
154
+ }
155
+ type ProcessState = 'stopped' | 'starting' | 'running' | 'stopping';
156
+ declare class ProcessManager extends EventEmitter {
157
+ private config;
158
+ private process;
159
+ private state;
160
+ private configPath;
161
+ private stdoutBuffer;
162
+ private stderrBuffer;
163
+ private maxBufferSize;
164
+ constructor(config: FiberNodeConfig);
165
+ /**
166
+ * Get current process state
167
+ */
168
+ getState(): ProcessState;
169
+ /**
170
+ * Check if the process is running
171
+ */
172
+ isRunning(): boolean;
173
+ /**
174
+ * Start the Fiber node
175
+ */
176
+ start(): Promise<void>;
177
+ /**
178
+ * Stop the Fiber node
179
+ */
180
+ stop(timeout?: number): Promise<void>;
181
+ /**
182
+ * Restart the Fiber node
183
+ */
184
+ restart(): Promise<void>;
185
+ /**
186
+ * Get recent stdout output
187
+ */
188
+ getStdout(lines?: number): string[];
189
+ /**
190
+ * Get recent stderr output
191
+ */
192
+ getStderr(lines?: number): string[];
193
+ /**
194
+ * Get the RPC URL for this node
195
+ */
196
+ getRpcUrl(): string;
197
+ /**
198
+ * Wait for the node to be ready
199
+ */
200
+ waitForReady(timeout?: number): Promise<void>;
201
+ /**
202
+ * Ensure the config file exists (copy from source or generate)
203
+ */
204
+ private ensureConfigFile;
205
+ /**
206
+ * Generate the config file (fallback when no source config provided)
207
+ */
208
+ private generateConfigFile;
209
+ }
210
+
211
+ export { type BinaryInfo, BinaryManager, DEFAULT_FIBER_VERSION, type DownloadOptions, type DownloadProgress, type FiberNodeConfig, ProcessManager, downloadFiberBinary, ensureFiberBinary, getDefaultBinaryPath, getFiberBinaryInfo };
package/dist/index.js ADDED
@@ -0,0 +1,695 @@
1
+ // src/constants.ts
2
+ var DEFAULT_FIBER_VERSION = "v0.6.1";
3
+
4
+ // src/binary/manager.ts
5
+ import { exec } from "child_process";
6
+ import { chmodSync, existsSync, mkdirSync, unlinkSync } from "fs";
7
+ import { join } from "path";
8
+ import { promisify } from "util";
9
+ var execAsync = promisify(exec);
10
+ var GITHUB_REPO = "nervosnetwork/fiber";
11
+ var GITHUB_RELEASES_URL = `https://github.com/${GITHUB_REPO}/releases`;
12
+ var DEFAULT_INSTALL_DIR = join(process.env.HOME || "~", ".fiber-pay", "bin");
13
+ var BINARY_PATTERNS = {
14
+ darwin: {
15
+ x64: "x86_64-darwin",
16
+ arm64: "aarch64-darwin"
17
+ // May not exist yet, will fallback to x64
18
+ },
19
+ linux: {
20
+ x64: "x86_64-linux",
21
+ arm64: "aarch64-linux"
22
+ },
23
+ win32: {
24
+ x64: "x86_64-windows",
25
+ arm64: "aarch64-windows"
26
+ }
27
+ };
28
+ var BinaryManager = class {
29
+ installDir;
30
+ constructor(installDir) {
31
+ this.installDir = installDir || DEFAULT_INSTALL_DIR;
32
+ }
33
+ /**
34
+ * Get the current platform and architecture
35
+ */
36
+ getPlatformInfo() {
37
+ const platform = process.platform;
38
+ const arch = process.arch === "arm64" ? "arm64" : "x64";
39
+ if (!["darwin", "linux", "win32"].includes(platform)) {
40
+ throw new Error(`Unsupported platform: ${platform}`);
41
+ }
42
+ return { platform, arch };
43
+ }
44
+ /**
45
+ * Get the pattern to match for the current platform
46
+ */
47
+ getAssetPattern() {
48
+ const { platform, arch } = this.getPlatformInfo();
49
+ const pattern = BINARY_PATTERNS[platform]?.[arch];
50
+ if (!pattern) {
51
+ throw new Error(`No binary pattern for ${platform}/${arch}`);
52
+ }
53
+ return pattern;
54
+ }
55
+ /**
56
+ * Get the path where the binary should be installed
57
+ */
58
+ getBinaryPath() {
59
+ const { platform } = this.getPlatformInfo();
60
+ const binaryName = platform === "win32" ? "fnn.exe" : "fnn";
61
+ return join(this.installDir, binaryName);
62
+ }
63
+ /**
64
+ * Check if the binary is installed and get its info
65
+ */
66
+ async getBinaryInfo() {
67
+ const binaryPath = this.getBinaryPath();
68
+ const exists = existsSync(binaryPath);
69
+ let version = "unknown";
70
+ let ready = false;
71
+ if (exists) {
72
+ try {
73
+ const { stdout } = await execAsync(`"${binaryPath}" --version`);
74
+ const versionMatch = stdout.match(/v(\d+\.\d+\.\d+)/);
75
+ version = versionMatch ? versionMatch[1] : stdout.trim();
76
+ ready = true;
77
+ } catch {
78
+ ready = false;
79
+ }
80
+ }
81
+ return { path: binaryPath, version, ready };
82
+ }
83
+ /**
84
+ * Fetch the latest release tag from GitHub (no API, follows redirect)
85
+ */
86
+ async getLatestTag() {
87
+ const response = await fetch(`${GITHUB_RELEASES_URL}/latest`, {
88
+ redirect: "manual",
89
+ headers: {
90
+ "User-Agent": "fiber-pay"
91
+ }
92
+ });
93
+ const location = response.headers.get("location") || response.url;
94
+ if (!location) {
95
+ throw new Error(`Failed to resolve latest release tag (status: ${response.status})`);
96
+ }
97
+ const match = location.match(/\/tag\/([^/?#]+)/);
98
+ if (!match) {
99
+ throw new Error(`Failed to parse release tag from redirect: ${location}`);
100
+ }
101
+ return match[1];
102
+ }
103
+ /**
104
+ * Normalize a version into a release tag
105
+ */
106
+ normalizeTag(version) {
107
+ return version.startsWith("v") ? version : `v${version}`;
108
+ }
109
+ /**
110
+ * Build download candidates for the current platform
111
+ */
112
+ buildAssetCandidates(tag) {
113
+ const { platform, arch } = this.getPlatformInfo();
114
+ const extensions = platform === "win32" ? ["zip", "tar.gz"] : ["tar.gz"];
115
+ const variants = platform === "win32" ? ["", "-portable"] : ["-portable", ""];
116
+ const patterns = [
117
+ { pattern: BINARY_PATTERNS[platform][arch], usesRosetta: false }
118
+ ];
119
+ if (platform === "darwin" && arch === "arm64") {
120
+ patterns.push({ pattern: BINARY_PATTERNS.darwin.x64, usesRosetta: true });
121
+ }
122
+ const candidates = [];
123
+ for (const { pattern, usesRosetta } of patterns) {
124
+ for (const variant of variants) {
125
+ for (const ext of extensions) {
126
+ const name = `fnn_${tag}-${pattern}${variant}.${ext}`;
127
+ const url = `${GITHUB_RELEASES_URL}/download/${tag}/${name}`;
128
+ candidates.push({ name, url, usesRosetta });
129
+ }
130
+ }
131
+ }
132
+ return candidates;
133
+ }
134
+ /**
135
+ * Validate Rosetta support when falling back to x86_64 binary on Apple Silicon.
136
+ */
137
+ async ensureRosettaAvailable() {
138
+ const { platform, arch } = this.getPlatformInfo();
139
+ if (platform !== "darwin" || arch !== "arm64") {
140
+ return;
141
+ }
142
+ try {
143
+ await execAsync("arch -x86_64 /usr/bin/true");
144
+ } catch {
145
+ throw new Error(
146
+ "Apple Silicon fallback selected x86_64 binary, but Rosetta 2 is not available. Install Rosetta with: softwareupdate --install-rosetta --agree-to-license"
147
+ );
148
+ }
149
+ }
150
+ /**
151
+ * Download and install the Fiber binary
152
+ */
153
+ async download(options = {}) {
154
+ const { version, force = false, onProgress = () => {
155
+ } } = options;
156
+ const binaryPath = this.getBinaryPath();
157
+ if (!force && existsSync(binaryPath)) {
158
+ const info = await this.getBinaryInfo();
159
+ if (info.ready) {
160
+ onProgress({ phase: "installing", message: `Binary already installed at ${binaryPath}` });
161
+ return info;
162
+ }
163
+ }
164
+ if (!existsSync(this.installDir)) {
165
+ mkdirSync(this.installDir, { recursive: true });
166
+ }
167
+ onProgress({ phase: "fetching", message: "Resolving release tag..." });
168
+ const tag = this.normalizeTag(version || DEFAULT_FIBER_VERSION);
169
+ onProgress({ phase: "fetching", message: `Found release: ${tag}` });
170
+ const candidates = this.buildAssetCandidates(tag);
171
+ let response;
172
+ let selected;
173
+ const attempted = [];
174
+ for (const candidate of candidates) {
175
+ onProgress({
176
+ phase: "downloading",
177
+ message: `Downloading ${candidate.name} from ${candidate.url}...`,
178
+ percent: 0
179
+ });
180
+ attempted.push(candidate.name);
181
+ const candidateResponse = await fetch(candidate.url, {
182
+ headers: { "User-Agent": "fiber-pay" }
183
+ });
184
+ if (candidateResponse.ok) {
185
+ response = candidateResponse;
186
+ selected = candidate;
187
+ break;
188
+ }
189
+ }
190
+ if (!response || !selected) {
191
+ const attemptedUrls = candidates.map((candidate) => candidate.url).join(", ");
192
+ throw new Error(`Download failed. Tried: ${attempted.join(", ")}. URLs: ${attemptedUrls}`);
193
+ }
194
+ onProgress({
195
+ phase: "downloading",
196
+ message: `Using ${selected.name} (${selected.url})`
197
+ });
198
+ if (selected.usesRosetta) {
199
+ onProgress({
200
+ phase: "downloading",
201
+ message: `No ARM64 binary available, using x86_64 version with Rosetta 2...`
202
+ });
203
+ await this.ensureRosettaAvailable();
204
+ onProgress({
205
+ phase: "downloading",
206
+ message: `Rosetta 2 available, continuing with x86_64 fallback binary...`
207
+ });
208
+ }
209
+ const contentLength = parseInt(response.headers.get("content-length") || "0", 10);
210
+ const body = response.body;
211
+ if (!body) {
212
+ throw new Error("No response body");
213
+ }
214
+ let downloaded = 0;
215
+ const reader = body.getReader();
216
+ const chunks = [];
217
+ while (true) {
218
+ const { done, value } = await reader.read();
219
+ if (done) break;
220
+ chunks.push(value);
221
+ downloaded += value.length;
222
+ if (contentLength > 0) {
223
+ const percent = Math.round(downloaded / contentLength * 100);
224
+ onProgress({
225
+ phase: "downloading",
226
+ message: `Downloading... ${percent}%`,
227
+ percent
228
+ });
229
+ }
230
+ }
231
+ const buffer = Buffer.concat(chunks);
232
+ onProgress({ phase: "extracting", message: "Extracting binary..." });
233
+ if (selected.name.endsWith(".tar.gz") || selected.name.endsWith(".tgz")) {
234
+ await this.extractTarGz(buffer, binaryPath);
235
+ } else if (selected.name.endsWith(".zip")) {
236
+ await this.extractZip(buffer, binaryPath);
237
+ } else {
238
+ const { writeFile } = await import("fs/promises");
239
+ await writeFile(binaryPath, buffer);
240
+ }
241
+ const { platform } = this.getPlatformInfo();
242
+ if (platform !== "win32") {
243
+ chmodSync(binaryPath, 493);
244
+ }
245
+ onProgress({ phase: "installing", message: `Installed to ${binaryPath}` });
246
+ return this.getBinaryInfo();
247
+ }
248
+ /**
249
+ * Extract tar.gz archive
250
+ */
251
+ async extractTarGz(buffer, targetPath) {
252
+ const { writeFile, readdir, rename, rm } = await import("fs/promises");
253
+ const tempDir = `${targetPath}.extract`;
254
+ if (!existsSync(tempDir)) {
255
+ mkdirSync(tempDir, { recursive: true });
256
+ }
257
+ const archivePath = `${tempDir}/archive.tar.gz`;
258
+ await writeFile(archivePath, buffer);
259
+ try {
260
+ await execAsync(`tar -xzf "${archivePath}" -C "${tempDir}"`);
261
+ } catch (_error) {
262
+ await execAsync(`gunzip -c "${archivePath}" | tar -xf - -C "${tempDir}"`);
263
+ }
264
+ const files = await readdir(tempDir, { recursive: true });
265
+ const binaryFile = files.find((f) => {
266
+ const name = String(f);
267
+ return name.endsWith("fnn") || name.endsWith("fnn.exe");
268
+ });
269
+ if (binaryFile) {
270
+ const sourcePath = join(tempDir, String(binaryFile));
271
+ await rename(sourcePath, targetPath);
272
+ } else {
273
+ const extractedFiles = await readdir(tempDir);
274
+ const possibleBinary = extractedFiles.find(
275
+ (f) => f !== "archive.tar.gz" && !f.startsWith(".")
276
+ );
277
+ if (possibleBinary) {
278
+ await rename(join(tempDir, possibleBinary), targetPath);
279
+ }
280
+ }
281
+ await rm(tempDir, { recursive: true, force: true });
282
+ }
283
+ /**
284
+ * Extract zip archive (primarily for Windows)
285
+ */
286
+ async extractZip(buffer, targetPath) {
287
+ const { writeFile, readdir, rename, rm } = await import("fs/promises");
288
+ const tempDir = `${targetPath}.extract`;
289
+ if (!existsSync(tempDir)) {
290
+ mkdirSync(tempDir, { recursive: true });
291
+ }
292
+ const archivePath = `${tempDir}/archive.zip`;
293
+ await writeFile(archivePath, buffer);
294
+ const { platform } = this.getPlatformInfo();
295
+ if (platform === "win32") {
296
+ await execAsync(
297
+ `powershell -command "Expand-Archive -Path '${archivePath}' -DestinationPath '${tempDir}'"`
298
+ );
299
+ } else {
300
+ await execAsync(`unzip -o "${archivePath}" -d "${tempDir}"`);
301
+ }
302
+ const files = await readdir(tempDir, { recursive: true });
303
+ const binaryFile = files.find((f) => {
304
+ const name = String(f);
305
+ return name.endsWith("fnn") || name.endsWith("fnn.exe");
306
+ });
307
+ if (binaryFile) {
308
+ await rename(join(tempDir, String(binaryFile)), targetPath);
309
+ }
310
+ await rm(tempDir, { recursive: true, force: true });
311
+ }
312
+ /**
313
+ * Remove the installed binary
314
+ */
315
+ async uninstall() {
316
+ const binaryPath = this.getBinaryPath();
317
+ if (existsSync(binaryPath)) {
318
+ unlinkSync(binaryPath);
319
+ }
320
+ }
321
+ };
322
+ async function downloadFiberBinary(options = {}) {
323
+ const manager = new BinaryManager(options.installDir);
324
+ return manager.download(options);
325
+ }
326
+ async function getFiberBinaryInfo(installDir) {
327
+ const manager = new BinaryManager(installDir);
328
+ return manager.getBinaryInfo();
329
+ }
330
+ async function ensureFiberBinary(options = {}) {
331
+ const manager = new BinaryManager(options.installDir);
332
+ const info = await manager.getBinaryInfo();
333
+ let downloadOptions = options;
334
+ if (info.ready) {
335
+ const wantedTag = manager.normalizeTag(options.version || DEFAULT_FIBER_VERSION);
336
+ const wantedVersion = wantedTag.startsWith("v") ? wantedTag.slice(1) : wantedTag;
337
+ if (info.version === wantedVersion) {
338
+ return info.path;
339
+ }
340
+ downloadOptions = { ...options, force: true };
341
+ }
342
+ const downloaded = await manager.download(downloadOptions);
343
+ return downloaded.path;
344
+ }
345
+ function getDefaultBinaryPath() {
346
+ const manager = new BinaryManager();
347
+ return manager.getBinaryPath();
348
+ }
349
+
350
+ // src/process/manager.ts
351
+ import { spawn } from "child_process";
352
+ import { EventEmitter } from "events";
353
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
354
+ import { dirname, join as join2 } from "path";
355
+
356
+ // src/process/yaml.ts
357
+ function stringify(obj, indent = 0) {
358
+ const spaces = " ".repeat(indent);
359
+ if (obj === null || obj === void 0) {
360
+ return "null";
361
+ }
362
+ if (typeof obj === "string") {
363
+ if (obj.includes(":") || obj.includes("#") || obj.includes("\n") || obj.startsWith(" ") || obj.endsWith(" ") || obj === "" || obj === "true" || obj === "false" || !Number.isNaN(Number(obj))) {
364
+ return JSON.stringify(obj);
365
+ }
366
+ return obj;
367
+ }
368
+ if (typeof obj === "number" || typeof obj === "boolean") {
369
+ return String(obj);
370
+ }
371
+ if (Array.isArray(obj)) {
372
+ if (obj.length === 0) {
373
+ return "[]";
374
+ }
375
+ return obj.map((item) => {
376
+ const value = stringify(item, indent + 1);
377
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
378
+ const lines = value.split("\n");
379
+ return `${spaces}- ${lines[0]}
380
+ ${lines.slice(1).map((l) => `${spaces} ${l}`).join("\n")}`;
381
+ }
382
+ return `${spaces}- ${value}`;
383
+ }).join("\n");
384
+ }
385
+ if (typeof obj === "object") {
386
+ const entries = Object.entries(obj);
387
+ if (entries.length === 0) {
388
+ return "{}";
389
+ }
390
+ return entries.map(([key, value]) => {
391
+ const valueStr = stringify(value, indent + 1);
392
+ if (typeof value === "object" && value !== null && !(Array.isArray(value) && value.length === 0) && !(typeof value === "object" && Object.keys(value).length === 0)) {
393
+ return `${spaces}${key}:
394
+ ${valueStr}`;
395
+ }
396
+ return `${spaces}${key}: ${valueStr}`;
397
+ }).join("\n");
398
+ }
399
+ return String(obj);
400
+ }
401
+
402
+ // src/process/manager.ts
403
+ var DEFAULT_TESTNET_FIBER_CONFIG = {
404
+ listening_addr: "/ip4/0.0.0.0/tcp/8228",
405
+ bootnode_addrs: [
406
+ "/ip4/54.179.226.154/tcp/8228/p2p/Qmes1EBD4yNo9Ywkfe6eRw9tG1nVNGLDmMud1xJMsoYFKy",
407
+ "/ip4/54.179.226.154/tcp/18228/p2p/QmdyQWjPtbK4NWWsvy8s69NGJaQULwgeQDT5ZpNDrTNaeV"
408
+ ],
409
+ announce_listening_addr: true,
410
+ chain: "testnet",
411
+ scripts: [
412
+ {
413
+ name: "FundingLock",
414
+ script: {
415
+ code_hash: "0x6c67887fe201ee0c7853f1682c0b77c0e6214044c156c7558269390a8afa6d7c",
416
+ hash_type: "type",
417
+ args: "0x"
418
+ },
419
+ cell_deps: [
420
+ {
421
+ type_id: {
422
+ code_hash: "0x00000000000000000000000000000000000000000000000000545950455f4944",
423
+ hash_type: "type",
424
+ args: "0x3cb7c0304fe53f75bb5727e2484d0beae4bd99d979813c6fc97c3cca569f10f6"
425
+ }
426
+ },
427
+ {
428
+ cell_dep: {
429
+ out_point: {
430
+ tx_hash: "0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7",
431
+ index: "0x0"
432
+ },
433
+ dep_type: "code"
434
+ }
435
+ }
436
+ ]
437
+ },
438
+ {
439
+ name: "CommitmentLock",
440
+ script: {
441
+ code_hash: "0x740dee83f87c6f309824d8fd3fbdd3c8380ee6fc9acc90b1a748438afcdf81d8",
442
+ hash_type: "type",
443
+ args: "0x"
444
+ },
445
+ cell_deps: [
446
+ {
447
+ type_id: {
448
+ code_hash: "0x00000000000000000000000000000000000000000000000000545950455f4944",
449
+ hash_type: "type",
450
+ args: "0xf7e458887495cf70dd30d1543cad47dc1dfe9d874177bf19291e4db478d5751b"
451
+ }
452
+ },
453
+ {
454
+ cell_dep: {
455
+ out_point: {
456
+ tx_hash: "0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7",
457
+ index: "0x0"
458
+ },
459
+ dep_type: "code"
460
+ }
461
+ }
462
+ ]
463
+ }
464
+ ]
465
+ };
466
+ var ProcessManager = class extends EventEmitter {
467
+ config;
468
+ process = null;
469
+ state = "stopped";
470
+ configPath;
471
+ stdoutBuffer = [];
472
+ stderrBuffer = [];
473
+ maxBufferSize = 1e3;
474
+ constructor(config) {
475
+ super();
476
+ this.config = config;
477
+ this.configPath = join2(config.dataDir, "config.yml");
478
+ }
479
+ /**
480
+ * Get current process state
481
+ */
482
+ getState() {
483
+ return this.state;
484
+ }
485
+ /**
486
+ * Check if the process is running
487
+ */
488
+ isRunning() {
489
+ return this.state === "running" || this.state === "starting";
490
+ }
491
+ /**
492
+ * Start the Fiber node
493
+ */
494
+ async start() {
495
+ if (this.isRunning()) {
496
+ throw new Error("Node is already running");
497
+ }
498
+ this.state = "starting";
499
+ if (!existsSync2(this.config.dataDir)) {
500
+ mkdirSync2(this.config.dataDir, { recursive: true });
501
+ }
502
+ await this.ensureConfigFile();
503
+ const env = {
504
+ ...process.env,
505
+ RUST_LOG: this.config.logLevel || "info"
506
+ };
507
+ env.FIBER_SECRET_KEY_PASSWORD = this.config.keyPassword || "fiber-pay-default-key";
508
+ const args = ["-c", this.configPath, "-d", this.config.dataDir];
509
+ this.process = spawn(this.config.binaryPath, args, {
510
+ env,
511
+ stdio: ["ignore", "pipe", "pipe"],
512
+ detached: false
513
+ });
514
+ this.process.stdout?.on("data", (data) => {
515
+ const text = data.toString();
516
+ this.stdoutBuffer.push(text);
517
+ if (this.stdoutBuffer.length > this.maxBufferSize) {
518
+ this.stdoutBuffer.shift();
519
+ }
520
+ this.emit("stdout", text);
521
+ if (text.includes("RPC server started") || text.includes("listening on")) {
522
+ if (this.state === "starting") {
523
+ this.state = "running";
524
+ this.emit("ready");
525
+ }
526
+ }
527
+ });
528
+ this.process.stderr?.on("data", (data) => {
529
+ const text = data.toString();
530
+ this.stderrBuffer.push(text);
531
+ if (this.stderrBuffer.length > this.maxBufferSize) {
532
+ this.stderrBuffer.shift();
533
+ }
534
+ this.emit("stderr", text);
535
+ });
536
+ this.process.on("exit", (code, signal) => {
537
+ this.state = "stopped";
538
+ this.process = null;
539
+ this.emit("stopped", code, signal);
540
+ });
541
+ this.process.on("error", (error) => {
542
+ this.state = "stopped";
543
+ this.process = null;
544
+ this.emit("error", error);
545
+ });
546
+ this.emit("started");
547
+ await new Promise((resolve) => setTimeout(resolve, 500));
548
+ if (this.state === "stopped") {
549
+ throw new Error("Process exited immediately. Check logs.");
550
+ }
551
+ }
552
+ /**
553
+ * Stop the Fiber node
554
+ */
555
+ async stop(timeout = 1e4) {
556
+ if (!this.process || this.state === "stopped") {
557
+ return;
558
+ }
559
+ this.state = "stopping";
560
+ return new Promise((resolve, reject) => {
561
+ const timer = setTimeout(() => {
562
+ this.process?.kill("SIGKILL");
563
+ }, timeout);
564
+ this.once("stopped", () => {
565
+ clearTimeout(timer);
566
+ resolve();
567
+ });
568
+ this.once("error", (error) => {
569
+ clearTimeout(timer);
570
+ reject(error);
571
+ });
572
+ this.process?.kill("SIGTERM");
573
+ });
574
+ }
575
+ /**
576
+ * Restart the Fiber node
577
+ */
578
+ async restart() {
579
+ await this.stop();
580
+ await this.start();
581
+ }
582
+ /**
583
+ * Get recent stdout output
584
+ */
585
+ getStdout(lines) {
586
+ if (lines) {
587
+ return this.stdoutBuffer.slice(-lines);
588
+ }
589
+ return [...this.stdoutBuffer];
590
+ }
591
+ /**
592
+ * Get recent stderr output
593
+ */
594
+ getStderr(lines) {
595
+ if (lines) {
596
+ return this.stderrBuffer.slice(-lines);
597
+ }
598
+ return [...this.stderrBuffer];
599
+ }
600
+ /**
601
+ * Get the RPC URL for this node
602
+ */
603
+ getRpcUrl() {
604
+ const addr = this.config.rpcListeningAddr || "127.0.0.1:8227";
605
+ return `http://${addr}`;
606
+ }
607
+ /**
608
+ * Wait for the node to be ready
609
+ */
610
+ waitForReady(timeout = 6e4) {
611
+ return new Promise((resolve, reject) => {
612
+ if (this.state === "running") {
613
+ resolve();
614
+ return;
615
+ }
616
+ const timer = setTimeout(() => {
617
+ this.off("ready", onReady);
618
+ this.off("stopped", onStopped);
619
+ reject(new Error("Timeout waiting for node to be ready"));
620
+ }, timeout);
621
+ const onReady = () => {
622
+ clearTimeout(timer);
623
+ this.off("stopped", onStopped);
624
+ resolve();
625
+ };
626
+ const onStopped = () => {
627
+ clearTimeout(timer);
628
+ this.off("ready", onReady);
629
+ reject(new Error("Node stopped while waiting for ready"));
630
+ };
631
+ this.once("ready", onReady);
632
+ this.once("stopped", onStopped);
633
+ });
634
+ }
635
+ /**
636
+ * Ensure the config file exists (copy from source or generate)
637
+ */
638
+ async ensureConfigFile() {
639
+ const configDir = dirname(this.configPath);
640
+ if (!existsSync2(configDir)) {
641
+ mkdirSync2(configDir, { recursive: true });
642
+ }
643
+ if (this.config.configFilePath && existsSync2(this.config.configFilePath)) {
644
+ const sourceContent = readFileSync(this.config.configFilePath, "utf-8");
645
+ writeFileSync(this.configPath, sourceContent);
646
+ return;
647
+ }
648
+ this.generateConfigFile();
649
+ }
650
+ /**
651
+ * Generate the config file (fallback when no source config provided)
652
+ */
653
+ generateConfigFile() {
654
+ const chain = this.config.chain || "testnet";
655
+ const useDefaultTestnetConfig = chain === "testnet";
656
+ const config = {
657
+ fiber: useDefaultTestnetConfig ? { ...DEFAULT_TESTNET_FIBER_CONFIG } : {
658
+ listening_addr: this.config.fiberListeningAddr || "/ip4/127.0.0.1/tcp/8228",
659
+ announce_listening_addr: true,
660
+ chain
661
+ },
662
+ rpc: {
663
+ listening_addr: this.config.rpcListeningAddr || "127.0.0.1:8227"
664
+ },
665
+ ckb: {
666
+ rpc_url: this.config.ckbRpcUrl || "https://testnet.ckbapp.dev/"
667
+ },
668
+ services: ["fiber", "rpc", "ckb"]
669
+ };
670
+ if (this.config.nodeName) {
671
+ config.fiber.announced_node_name = this.config.nodeName;
672
+ }
673
+ if (this.config.bootnodeAddrs?.length) {
674
+ config.fiber.bootnode_addrs = this.config.bootnodeAddrs;
675
+ }
676
+ if (this.config.udtWhitelist?.length) {
677
+ config.ckb.udt_whitelist = this.config.udtWhitelist;
678
+ }
679
+ const configDir = dirname(this.configPath);
680
+ if (!existsSync2(configDir)) {
681
+ mkdirSync2(configDir, { recursive: true });
682
+ }
683
+ writeFileSync(this.configPath, stringify(config));
684
+ }
685
+ };
686
+ export {
687
+ BinaryManager,
688
+ DEFAULT_FIBER_VERSION,
689
+ ProcessManager,
690
+ downloadFiberBinary,
691
+ ensureFiberBinary,
692
+ getDefaultBinaryPath,
693
+ getFiberBinaryInfo
694
+ };
695
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/binary/manager.ts","../src/process/manager.ts","../src/process/yaml.ts"],"sourcesContent":["/**\n * Shared constants for the @fiber-pay/node package\n */\n\n/** Default Fiber Network Node binary version used for downloads when no version is specified. */\nexport const DEFAULT_FIBER_VERSION = 'v0.6.1';\n","/**\n * Binary Manager\n * Handles downloading, installing, and managing the Fiber Network Node (fnn) binary\n */\n\nimport { exec } from 'node:child_process';\nimport { chmodSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { DEFAULT_FIBER_VERSION } from '../constants.js';\n\nconst execAsync = promisify(exec);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type Platform = 'darwin' | 'linux' | 'win32';\nexport type Arch = 'x64' | 'arm64';\n\nexport interface BinaryInfo {\n /** Path to the binary */\n path: string;\n /** Version of the binary */\n version: string;\n /** Whether the binary exists and is executable */\n ready: boolean;\n}\n\nexport interface DownloadOptions {\n /** Target directory for the binary */\n installDir?: string;\n /** Specific version to download (default: latest) */\n version?: string;\n /** Force re-download even if binary exists */\n force?: boolean;\n /** Progress callback */\n onProgress?: (progress: DownloadProgress) => void;\n}\n\nexport interface DownloadProgress {\n phase: 'fetching' | 'downloading' | 'extracting' | 'installing';\n percent?: number;\n message: string;\n}\n\ninterface AssetCandidate {\n name: string;\n url: string;\n usesRosetta: boolean;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst GITHUB_REPO = 'nervosnetwork/fiber';\nconst GITHUB_RELEASES_URL = `https://github.com/${GITHUB_REPO}/releases`;\nconst DEFAULT_INSTALL_DIR = join(process.env.HOME || '~', '.fiber-pay', 'bin');\n\n// Binary naming patterns for different platforms\n// Pattern used to match assets: fnn_vX.X.X-{pattern}.tar.gz\nconst BINARY_PATTERNS: Record<Platform, Record<Arch, string>> = {\n darwin: {\n x64: 'x86_64-darwin',\n arm64: 'aarch64-darwin', // May not exist yet, will fallback to x64\n },\n linux: {\n x64: 'x86_64-linux',\n arm64: 'aarch64-linux',\n },\n win32: {\n x64: 'x86_64-windows',\n arm64: 'aarch64-windows',\n },\n};\n\n// =============================================================================\n// Binary Manager\n// =============================================================================\n\nexport class BinaryManager {\n private installDir: string;\n\n constructor(installDir?: string) {\n this.installDir = installDir || DEFAULT_INSTALL_DIR;\n }\n\n /**\n * Get the current platform and architecture\n */\n getPlatformInfo(): { platform: Platform; arch: Arch } {\n const platform = process.platform as Platform;\n const arch = process.arch === 'arm64' ? 'arm64' : 'x64';\n\n if (!['darwin', 'linux', 'win32'].includes(platform)) {\n throw new Error(`Unsupported platform: ${platform}`);\n }\n\n return { platform, arch };\n }\n\n /**\n * Get the pattern to match for the current platform\n */\n getAssetPattern(): string {\n const { platform, arch } = this.getPlatformInfo();\n const pattern = BINARY_PATTERNS[platform]?.[arch];\n\n if (!pattern) {\n throw new Error(`No binary pattern for ${platform}/${arch}`);\n }\n\n return pattern;\n }\n\n /**\n * Get the path where the binary should be installed\n */\n getBinaryPath(): string {\n const { platform } = this.getPlatformInfo();\n const binaryName = platform === 'win32' ? 'fnn.exe' : 'fnn';\n return join(this.installDir, binaryName);\n }\n\n /**\n * Check if the binary is installed and get its info\n */\n async getBinaryInfo(): Promise<BinaryInfo> {\n const binaryPath = this.getBinaryPath();\n const exists = existsSync(binaryPath);\n\n let version = 'unknown';\n let ready = false;\n\n if (exists) {\n try {\n const { stdout } = await execAsync(`\"${binaryPath}\" --version`);\n // Output format: \"fnn Fiber v0.6.1 (f761b6d 2026-01-14)\"\n // Extract the version number\n const versionMatch = stdout.match(/v(\\d+\\.\\d+\\.\\d+)/);\n version = versionMatch ? versionMatch[1] : stdout.trim();\n ready = true;\n } catch {\n // Binary exists but may not be executable\n ready = false;\n }\n }\n\n return { path: binaryPath, version, ready };\n }\n\n /**\n * Fetch the latest release tag from GitHub (no API, follows redirect)\n */\n async getLatestTag(): Promise<string> {\n const response = await fetch(`${GITHUB_RELEASES_URL}/latest`, {\n redirect: 'manual',\n headers: {\n 'User-Agent': 'fiber-pay',\n },\n });\n\n const location = response.headers.get('location') || response.url;\n if (!location) {\n throw new Error(`Failed to resolve latest release tag (status: ${response.status})`);\n }\n\n const match = location.match(/\\/tag\\/([^/?#]+)/);\n if (!match) {\n throw new Error(`Failed to parse release tag from redirect: ${location}`);\n }\n\n return match[1];\n }\n\n /**\n * Normalize a version into a release tag\n */\n normalizeTag(version: string): string {\n return version.startsWith('v') ? version : `v${version}`;\n }\n\n /**\n * Build download candidates for the current platform\n */\n buildAssetCandidates(tag: string): AssetCandidate[] {\n const { platform, arch } = this.getPlatformInfo();\n const extensions = platform === 'win32' ? ['zip', 'tar.gz'] : ['tar.gz'];\n const variants = platform === 'win32' ? ['', '-portable'] : ['-portable', ''];\n const patterns: Array<{ pattern: string; usesRosetta: boolean }> = [\n { pattern: BINARY_PATTERNS[platform][arch], usesRosetta: false },\n ];\n\n if (platform === 'darwin' && arch === 'arm64') {\n patterns.push({ pattern: BINARY_PATTERNS.darwin.x64, usesRosetta: true });\n }\n\n const candidates: AssetCandidate[] = [];\n for (const { pattern, usesRosetta } of patterns) {\n for (const variant of variants) {\n for (const ext of extensions) {\n const name = `fnn_${tag}-${pattern}${variant}.${ext}`;\n const url = `${GITHUB_RELEASES_URL}/download/${tag}/${name}`;\n candidates.push({ name, url, usesRosetta });\n }\n }\n }\n\n return candidates;\n }\n\n /**\n * Validate Rosetta support when falling back to x86_64 binary on Apple Silicon.\n */\n private async ensureRosettaAvailable(): Promise<void> {\n const { platform, arch } = this.getPlatformInfo();\n if (platform !== 'darwin' || arch !== 'arm64') {\n return;\n }\n\n try {\n await execAsync('arch -x86_64 /usr/bin/true');\n } catch {\n throw new Error(\n 'Apple Silicon fallback selected x86_64 binary, but Rosetta 2 is not available. ' +\n 'Install Rosetta with: softwareupdate --install-rosetta --agree-to-license',\n );\n }\n }\n\n /**\n * Download and install the Fiber binary\n */\n async download(options: DownloadOptions = {}): Promise<BinaryInfo> {\n const { version, force = false, onProgress = () => {} } = options;\n\n const binaryPath = this.getBinaryPath();\n\n // Check if already installed\n if (!force && existsSync(binaryPath)) {\n const info = await this.getBinaryInfo();\n if (info.ready) {\n onProgress({ phase: 'installing', message: `Binary already installed at ${binaryPath}` });\n return info;\n }\n }\n\n // Ensure install directory exists\n if (!existsSync(this.installDir)) {\n mkdirSync(this.installDir, { recursive: true });\n }\n\n // Resolve release tag\n onProgress({ phase: 'fetching', message: 'Resolving release tag...' });\n const tag = this.normalizeTag(version || DEFAULT_FIBER_VERSION);\n\n onProgress({ phase: 'fetching', message: `Found release: ${tag}` });\n\n // Build asset candidates\n const candidates = this.buildAssetCandidates(tag);\n\n let response: Response | undefined;\n let selected: AssetCandidate | undefined;\n const attempted: string[] = [];\n\n for (const candidate of candidates) {\n onProgress({\n phase: 'downloading',\n message: `Downloading ${candidate.name} from ${candidate.url}...`,\n percent: 0,\n });\n attempted.push(candidate.name);\n const candidateResponse = await fetch(candidate.url, {\n headers: { 'User-Agent': 'fiber-pay' },\n });\n\n if (candidateResponse.ok) {\n response = candidateResponse;\n selected = candidate;\n break;\n }\n }\n\n if (!response || !selected) {\n const attemptedUrls = candidates.map((candidate) => candidate.url).join(', ');\n throw new Error(`Download failed. Tried: ${attempted.join(', ')}. URLs: ${attemptedUrls}`);\n }\n\n onProgress({\n phase: 'downloading',\n message: `Using ${selected.name} (${selected.url})`,\n });\n\n if (selected.usesRosetta) {\n onProgress({\n phase: 'downloading',\n message: `No ARM64 binary available, using x86_64 version with Rosetta 2...`,\n });\n\n await this.ensureRosettaAvailable();\n\n onProgress({\n phase: 'downloading',\n message: `Rosetta 2 available, continuing with x86_64 fallback binary...`,\n });\n }\n\n const contentLength = parseInt(response.headers.get('content-length') || '0', 10);\n\n // Stream download with progress\n const body = response.body;\n if (!body) {\n throw new Error('No response body');\n }\n\n let downloaded = 0;\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n chunks.push(value);\n downloaded += value.length;\n\n if (contentLength > 0) {\n const percent = Math.round((downloaded / contentLength) * 100);\n onProgress({\n phase: 'downloading',\n message: `Downloading... ${percent}%`,\n percent,\n });\n }\n }\n\n const buffer = Buffer.concat(chunks);\n\n // Handle different archive formats\n onProgress({ phase: 'extracting', message: 'Extracting binary...' });\n\n if (selected.name.endsWith('.tar.gz') || selected.name.endsWith('.tgz')) {\n await this.extractTarGz(buffer, binaryPath);\n } else if (selected.name.endsWith('.zip')) {\n await this.extractZip(buffer, binaryPath);\n } else {\n // Direct binary\n const { writeFile } = await import('node:fs/promises');\n await writeFile(binaryPath, buffer);\n }\n\n // Make executable (Unix)\n const { platform } = this.getPlatformInfo();\n if (platform !== 'win32') {\n chmodSync(binaryPath, 0o755);\n }\n\n onProgress({ phase: 'installing', message: `Installed to ${binaryPath}` });\n\n return this.getBinaryInfo();\n }\n\n /**\n * Extract tar.gz archive\n */\n private async extractTarGz(buffer: Buffer, targetPath: string): Promise<void> {\n const { writeFile, readdir, rename, rm } = await import('node:fs/promises');\n const tempDir = `${targetPath}.extract`;\n\n // Create temp directory\n if (!existsSync(tempDir)) {\n mkdirSync(tempDir, { recursive: true });\n }\n\n // Write archive to temp file\n const archivePath = `${tempDir}/archive.tar.gz`;\n await writeFile(archivePath, buffer);\n\n // Extract using tar command\n try {\n await execAsync(`tar -xzf \"${archivePath}\" -C \"${tempDir}\"`);\n } catch (_error) {\n // Fallback: try with gunzip + tar separately\n await execAsync(`gunzip -c \"${archivePath}\" | tar -xf - -C \"${tempDir}\"`);\n }\n\n // Find the binary in extracted files\n const files = await readdir(tempDir, { recursive: true });\n const binaryFile = files.find((f) => {\n const name = String(f);\n return name.endsWith('fnn') || name.endsWith('fnn.exe');\n });\n\n if (binaryFile) {\n const sourcePath = join(tempDir, String(binaryFile));\n await rename(sourcePath, targetPath);\n } else {\n // If no fnn found, maybe the archive contains a single binary\n const extractedFiles = await readdir(tempDir);\n const possibleBinary = extractedFiles.find(\n (f) => f !== 'archive.tar.gz' && !f.startsWith('.'),\n );\n if (possibleBinary) {\n await rename(join(tempDir, possibleBinary), targetPath);\n }\n }\n\n // Cleanup temp directory\n await rm(tempDir, { recursive: true, force: true });\n }\n\n /**\n * Extract zip archive (primarily for Windows)\n */\n private async extractZip(buffer: Buffer, targetPath: string): Promise<void> {\n const { writeFile, readdir, rename, rm } = await import('node:fs/promises');\n const tempDir = `${targetPath}.extract`;\n\n if (!existsSync(tempDir)) {\n mkdirSync(tempDir, { recursive: true });\n }\n\n const archivePath = `${tempDir}/archive.zip`;\n await writeFile(archivePath, buffer);\n\n // Extract using unzip command\n const { platform } = this.getPlatformInfo();\n if (platform === 'win32') {\n await execAsync(\n `powershell -command \"Expand-Archive -Path '${archivePath}' -DestinationPath '${tempDir}'\"`,\n );\n } else {\n await execAsync(`unzip -o \"${archivePath}\" -d \"${tempDir}\"`);\n }\n\n // Find and move the binary\n const files = await readdir(tempDir, { recursive: true });\n const binaryFile = files.find((f) => {\n const name = String(f);\n return name.endsWith('fnn') || name.endsWith('fnn.exe');\n });\n\n if (binaryFile) {\n await rename(join(tempDir, String(binaryFile)), targetPath);\n }\n\n await rm(tempDir, { recursive: true, force: true });\n }\n\n /**\n * Remove the installed binary\n */\n async uninstall(): Promise<void> {\n const binaryPath = this.getBinaryPath();\n if (existsSync(binaryPath)) {\n unlinkSync(binaryPath);\n }\n }\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Download the Fiber binary to the default location\n */\nexport async function downloadFiberBinary(options: DownloadOptions = {}): Promise<BinaryInfo> {\n const manager = new BinaryManager(options.installDir);\n return manager.download(options);\n}\n\n/**\n * Get information about the installed binary\n */\nexport async function getFiberBinaryInfo(installDir?: string): Promise<BinaryInfo> {\n const manager = new BinaryManager(installDir);\n return manager.getBinaryInfo();\n}\n\n/**\n * Ensure the Fiber binary is available, downloading if necessary.\n * If the binary exists but its version does not match the requested\n * (or default) version, it will be re-downloaded.\n */\nexport async function ensureFiberBinary(options: DownloadOptions = {}): Promise<string> {\n const manager = new BinaryManager(options.installDir);\n const info = await manager.getBinaryInfo();\n let downloadOptions = options;\n\n if (info.ready) {\n const wantedTag = manager.normalizeTag(options.version || DEFAULT_FIBER_VERSION);\n const wantedVersion = wantedTag.startsWith('v') ? wantedTag.slice(1) : wantedTag;\n if (info.version === wantedVersion) {\n return info.path;\n }\n // Version mismatch — force re-download.\n downloadOptions = { ...options, force: true };\n }\n\n const downloaded = await manager.download(downloadOptions);\n return downloaded.path;\n}\n\n/**\n * Get the default binary path\n */\nexport function getDefaultBinaryPath(): string {\n const manager = new BinaryManager();\n return manager.getBinaryPath();\n}\n","/**\n * Process Manager\n * Manages the lifecycle of the Fiber Network Node (fnn) binary\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport * as yaml from './yaml.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface FiberNodeConfig {\n /** Path to the fnn binary */\n binaryPath: string;\n /** Base directory for data storage */\n dataDir: string;\n /** Path to the config file (optional - will use built-in testnet config if not provided) */\n configFilePath?: string;\n /** Fiber P2P listening address */\n fiberListeningAddr?: string;\n /** Fiber node name */\n nodeName?: string;\n /** Bootstrap node addresses */\n bootnodeAddrs?: string[];\n /** CKB RPC URL */\n ckbRpcUrl?: string;\n /** RPC listening address */\n rpcListeningAddr?: string;\n /** Chain configuration (mainnet, testnet, or file path) */\n chain?: 'mainnet' | 'testnet' | string;\n /** Key encryption password */\n keyPassword?: string;\n /** Log level */\n logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error';\n /** UDT whitelist */\n udtWhitelist?: Array<{\n name: string;\n script: {\n code_hash: string;\n hash_type: 'type' | 'data' | 'data1' | 'data2';\n args: string;\n };\n }>;\n}\n\nexport interface ProcessManagerEvents {\n started: () => void;\n stopped: (code: number | null, signal: NodeJS.Signals | null) => void;\n error: (error: Error) => void;\n stdout: (data: string) => void;\n stderr: (data: string) => void;\n ready: () => void;\n}\n\nexport type ProcessState = 'stopped' | 'starting' | 'running' | 'stopping';\n\nconst DEFAULT_TESTNET_FIBER_CONFIG = {\n listening_addr: '/ip4/0.0.0.0/tcp/8228',\n bootnode_addrs: [\n '/ip4/54.179.226.154/tcp/8228/p2p/Qmes1EBD4yNo9Ywkfe6eRw9tG1nVNGLDmMud1xJMsoYFKy',\n '/ip4/54.179.226.154/tcp/18228/p2p/QmdyQWjPtbK4NWWsvy8s69NGJaQULwgeQDT5ZpNDrTNaeV',\n ],\n announce_listening_addr: true,\n chain: 'testnet',\n scripts: [\n {\n name: 'FundingLock',\n script: {\n code_hash: '0x6c67887fe201ee0c7853f1682c0b77c0e6214044c156c7558269390a8afa6d7c',\n hash_type: 'type',\n args: '0x',\n },\n cell_deps: [\n {\n type_id: {\n code_hash: '0x00000000000000000000000000000000000000000000000000545950455f4944',\n hash_type: 'type',\n args: '0x3cb7c0304fe53f75bb5727e2484d0beae4bd99d979813c6fc97c3cca569f10f6',\n },\n },\n {\n cell_dep: {\n out_point: {\n tx_hash: '0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7',\n index: '0x0',\n },\n dep_type: 'code',\n },\n },\n ],\n },\n {\n name: 'CommitmentLock',\n script: {\n code_hash: '0x740dee83f87c6f309824d8fd3fbdd3c8380ee6fc9acc90b1a748438afcdf81d8',\n hash_type: 'type',\n args: '0x',\n },\n cell_deps: [\n {\n type_id: {\n code_hash: '0x00000000000000000000000000000000000000000000000000545950455f4944',\n hash_type: 'type',\n args: '0xf7e458887495cf70dd30d1543cad47dc1dfe9d874177bf19291e4db478d5751b',\n },\n },\n {\n cell_dep: {\n out_point: {\n tx_hash: '0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7',\n index: '0x0',\n },\n dep_type: 'code',\n },\n },\n ],\n },\n ],\n} as const;\n\n// =============================================================================\n// Process Manager\n// =============================================================================\n\nexport class ProcessManager extends EventEmitter {\n private config: FiberNodeConfig;\n private process: ChildProcess | null = null;\n private state: ProcessState = 'stopped';\n private configPath: string;\n private stdoutBuffer: string[] = [];\n private stderrBuffer: string[] = [];\n private maxBufferSize = 1000;\n\n constructor(config: FiberNodeConfig) {\n super();\n this.config = config;\n this.configPath = join(config.dataDir, 'config.yml');\n }\n\n /**\n * Get current process state\n */\n getState(): ProcessState {\n return this.state;\n }\n\n /**\n * Check if the process is running\n */\n isRunning(): boolean {\n return this.state === 'running' || this.state === 'starting';\n }\n\n /**\n * Start the Fiber node\n */\n async start(): Promise<void> {\n if (this.isRunning()) {\n throw new Error('Node is already running');\n }\n\n this.state = 'starting';\n\n // Ensure data directory exists\n if (!existsSync(this.config.dataDir)) {\n mkdirSync(this.config.dataDir, { recursive: true });\n }\n\n // Copy or generate config file\n await this.ensureConfigFile();\n\n // Build environment variables\n const env: Record<string, string> = {\n ...process.env,\n RUST_LOG: this.config.logLevel || 'info',\n };\n\n // FIBER_SECRET_KEY_PASSWORD is always required by the fiber node\n // Use the provided password or generate a default one\n env.FIBER_SECRET_KEY_PASSWORD = this.config.keyPassword || 'fiber-pay-default-key';\n\n // Spawn the process\n const args = ['-c', this.configPath, '-d', this.config.dataDir];\n\n this.process = spawn(this.config.binaryPath, args, {\n env,\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: false,\n });\n\n // Handle stdout\n this.process.stdout?.on('data', (data: Buffer) => {\n const text = data.toString();\n this.stdoutBuffer.push(text);\n if (this.stdoutBuffer.length > this.maxBufferSize) {\n this.stdoutBuffer.shift();\n }\n this.emit('stdout', text);\n\n // Check for ready signal\n if (text.includes('RPC server started') || text.includes('listening on')) {\n if (this.state === 'starting') {\n this.state = 'running';\n this.emit('ready');\n }\n }\n });\n\n // Handle stderr\n this.process.stderr?.on('data', (data: Buffer) => {\n const text = data.toString();\n this.stderrBuffer.push(text);\n if (this.stderrBuffer.length > this.maxBufferSize) {\n this.stderrBuffer.shift();\n }\n this.emit('stderr', text);\n });\n\n // Handle process exit\n this.process.on('exit', (code, signal) => {\n this.state = 'stopped';\n this.process = null;\n this.emit('stopped', code, signal);\n });\n\n // Handle process error\n this.process.on('error', (error) => {\n this.state = 'stopped';\n this.process = null;\n this.emit('error', error);\n });\n\n this.emit('started');\n\n // Wait a bit for the process to initialize\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n // If process died immediately, throw error\n // State may have changed due to async event handlers\n if ((this.state as ProcessState) === 'stopped') {\n throw new Error('Process exited immediately. Check logs.');\n }\n }\n\n /**\n * Stop the Fiber node\n */\n async stop(timeout = 10000): Promise<void> {\n if (!this.process || this.state === 'stopped') {\n return;\n }\n\n this.state = 'stopping';\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n // Force kill if graceful shutdown fails\n this.process?.kill('SIGKILL');\n }, timeout);\n\n this.once('stopped', () => {\n clearTimeout(timer);\n resolve();\n });\n\n this.once('error', (error) => {\n clearTimeout(timer);\n reject(error);\n });\n\n // Send SIGTERM for graceful shutdown\n this.process?.kill('SIGTERM');\n });\n }\n\n /**\n * Restart the Fiber node\n */\n async restart(): Promise<void> {\n await this.stop();\n await this.start();\n }\n\n /**\n * Get recent stdout output\n */\n getStdout(lines?: number): string[] {\n if (lines) {\n return this.stdoutBuffer.slice(-lines);\n }\n return [...this.stdoutBuffer];\n }\n\n /**\n * Get recent stderr output\n */\n getStderr(lines?: number): string[] {\n if (lines) {\n return this.stderrBuffer.slice(-lines);\n }\n return [...this.stderrBuffer];\n }\n\n /**\n * Get the RPC URL for this node\n */\n getRpcUrl(): string {\n const addr = this.config.rpcListeningAddr || '127.0.0.1:8227';\n return `http://${addr}`;\n }\n\n /**\n * Wait for the node to be ready\n */\n waitForReady(timeout = 60000): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.state === 'running') {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n this.off('ready', onReady);\n this.off('stopped', onStopped);\n reject(new Error('Timeout waiting for node to be ready'));\n }, timeout);\n\n const onReady = () => {\n clearTimeout(timer);\n this.off('stopped', onStopped);\n resolve();\n };\n\n const onStopped = () => {\n clearTimeout(timer);\n this.off('ready', onReady);\n reject(new Error('Node stopped while waiting for ready'));\n };\n\n this.once('ready', onReady);\n this.once('stopped', onStopped);\n });\n }\n\n /**\n * Ensure the config file exists (copy from source or generate)\n */\n private async ensureConfigFile(): Promise<void> {\n const configDir = dirname(this.configPath);\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n // If a config file path is provided, copy it\n if (this.config.configFilePath && existsSync(this.config.configFilePath)) {\n // Copy the config file to the data directory\n const sourceContent = readFileSync(this.config.configFilePath, 'utf-8');\n writeFileSync(this.configPath, sourceContent);\n return;\n }\n\n // Otherwise, generate a basic config file\n this.generateConfigFile();\n }\n\n /**\n * Generate the config file (fallback when no source config provided)\n */\n private generateConfigFile(): void {\n const chain = this.config.chain || 'testnet';\n const useDefaultTestnetConfig = chain === 'testnet';\n const config: Record<string, unknown> = {\n fiber: useDefaultTestnetConfig\n ? { ...DEFAULT_TESTNET_FIBER_CONFIG }\n : {\n listening_addr: this.config.fiberListeningAddr || '/ip4/127.0.0.1/tcp/8228',\n announce_listening_addr: true,\n chain,\n },\n rpc: {\n listening_addr: this.config.rpcListeningAddr || '127.0.0.1:8227',\n },\n ckb: {\n rpc_url: this.config.ckbRpcUrl || 'https://testnet.ckbapp.dev/',\n },\n services: ['fiber', 'rpc', 'ckb'],\n };\n\n if (this.config.nodeName) {\n (config.fiber as Record<string, unknown>).announced_node_name = this.config.nodeName;\n }\n\n if (this.config.bootnodeAddrs?.length) {\n (config.fiber as Record<string, unknown>).bootnode_addrs = this.config.bootnodeAddrs;\n }\n\n if (this.config.udtWhitelist?.length) {\n (config.ckb as Record<string, unknown>).udt_whitelist = this.config.udtWhitelist;\n }\n\n const configDir = dirname(this.configPath);\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(this.configPath, yaml.stringify(config));\n }\n}\n","/**\n * Simple YAML serializer\n * Minimal implementation for config file generation\n */\n\nexport function stringify(obj: unknown, indent = 0): string {\n const spaces = ' '.repeat(indent);\n\n if (obj === null || obj === undefined) {\n return 'null';\n }\n\n if (typeof obj === 'string') {\n // Quote strings that need it\n if (\n obj.includes(':') ||\n obj.includes('#') ||\n obj.includes('\\n') ||\n obj.startsWith(' ') ||\n obj.endsWith(' ') ||\n obj === '' ||\n obj === 'true' ||\n obj === 'false' ||\n !Number.isNaN(Number(obj))\n ) {\n return JSON.stringify(obj);\n }\n return obj;\n }\n\n if (typeof obj === 'number' || typeof obj === 'boolean') {\n return String(obj);\n }\n\n if (Array.isArray(obj)) {\n if (obj.length === 0) {\n return '[]';\n }\n return obj\n .map((item) => {\n const value = stringify(item, indent + 1);\n if (typeof item === 'object' && item !== null && !Array.isArray(item)) {\n // Multi-line object in array\n const lines = value.split('\\n');\n return `${spaces}- ${lines[0]}\\n${lines\n .slice(1)\n .map((l) => `${spaces} ${l}`)\n .join('\\n')}`;\n }\n return `${spaces}- ${value}`;\n })\n .join('\\n');\n }\n\n if (typeof obj === 'object') {\n const entries = Object.entries(obj as Record<string, unknown>);\n if (entries.length === 0) {\n return '{}';\n }\n return entries\n .map(([key, value]) => {\n const valueStr = stringify(value, indent + 1);\n if (\n typeof value === 'object' &&\n value !== null &&\n !(Array.isArray(value) && value.length === 0) &&\n !(typeof value === 'object' && Object.keys(value).length === 0)\n ) {\n return `${spaces}${key}:\\n${valueStr}`;\n }\n return `${spaces}${key}: ${valueStr}`;\n })\n .join('\\n');\n }\n\n return String(obj);\n}\n\nexport function parse(yaml: string): unknown {\n // Basic YAML parsing - for production use a proper library\n const lines = yaml.split('\\n');\n const result: Record<string, unknown> = {};\n const stack: Array<{ indent: number; obj: Record<string, unknown>; key?: string }> = [\n { indent: -1, obj: result },\n ];\n\n for (const line of lines) {\n // Skip empty lines and comments\n if (!line.trim() || line.trim().startsWith('#')) {\n continue;\n }\n\n const indent = line.search(/\\S/);\n const content = line.trim();\n\n // Handle key: value\n const colonIndex = content.indexOf(':');\n if (colonIndex > 0) {\n const key = content.slice(0, colonIndex).trim();\n const value = content.slice(colonIndex + 1).trim();\n\n // Pop stack to correct level\n while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {\n stack.pop();\n }\n\n const parent = stack[stack.length - 1].obj;\n\n if (value === '' || value === '|' || value === '>') {\n // Nested object\n const newObj: Record<string, unknown> = {};\n parent[key] = newObj;\n stack.push({ indent, obj: newObj, key });\n } else {\n // Primitive value\n parent[key] = parseValue(value);\n }\n }\n }\n\n return result;\n}\n\nfunction parseValue(value: string): unknown {\n // Remove quotes\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n return value.slice(1, -1);\n }\n\n // Boolean\n if (value === 'true') return true;\n if (value === 'false') return false;\n\n // Null\n if (value === 'null' || value === '~') return null;\n\n // Number\n const num = Number(value);\n if (!Number.isNaN(num)) return num;\n\n // Array (inline)\n if (value.startsWith('[') && value.endsWith(']')) {\n return JSON.parse(value);\n }\n\n return value;\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","join","join","existsSync","mkdirSync"]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@fiber-pay/node",
3
+ "version": "0.1.0-rc.1",
4
+ "description": "Fiber Network node binary management and process lifecycle",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "keywords": [
19
+ "ckb",
20
+ "lightning",
21
+ "fiber",
22
+ "node-management",
23
+ "nervos"
24
+ ],
25
+ "license": "MIT",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup",
34
+ "dev": "tsup --watch",
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest --config vitest.config.ts",
37
+ "test:run": "vitest run --config vitest.config.ts"
38
+ }
39
+ }