@fiber-pay/node 0.1.0-rc.3 → 0.1.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,4 +17,4 @@ import { createFiberNodeManager } from '@fiber-pay/node';
17
17
  ## Compatibility
18
18
 
19
19
  - Node.js `>=20`
20
- - Fiber target: `v0.6.1`
20
+ - Fiber target: `v0.7.1`
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { EventEmitter } from 'node:events';
2
+ import { KeyConfig, KeyInfo } from '@fiber-pay/sdk';
2
3
 
3
4
  /**
4
5
  * Shared constants for the @fiber-pay/node package
5
6
  */
6
7
  /** Default Fiber Network Node binary version used for downloads when no version is specified. */
7
- declare const DEFAULT_FIBER_VERSION = "v0.6.1";
8
+ declare const DEFAULT_FIBER_VERSION = "v0.7.1";
8
9
 
9
10
  /**
10
11
  * Binary Manager
@@ -58,6 +59,10 @@ declare class BinaryManager {
58
59
  * Get the path where the binary should be installed
59
60
  */
60
61
  getBinaryPath(): string;
62
+ /**
63
+ * Get the path where the fnn-migrate binary should be installed
64
+ */
65
+ getMigrateBinaryPath(): string;
61
66
  /**
62
67
  * Check if the binary is installed and get its info
63
68
  */
@@ -94,6 +99,10 @@ declare class BinaryManager {
94
99
  * Remove the installed binary
95
100
  */
96
101
  uninstall(): Promise<void>;
102
+ /**
103
+ * Find a named binary in a list of extracted file paths.
104
+ */
105
+ private findBinaryInExtractedFiles;
97
106
  }
98
107
  /**
99
108
  * Download the Fiber binary to the default location
@@ -114,6 +123,88 @@ declare function ensureFiberBinary(options?: DownloadOptions): Promise<string>;
114
123
  */
115
124
  declare function getDefaultBinaryPath(): string;
116
125
 
126
+ /**
127
+ * Migration Manager
128
+ * Handles Fiber node database migration when upgrading between versions.
129
+ *
130
+ * Uses the `fnn-migrate` binary shipped in Fiber release archives to migrate
131
+ * the on-disk store format so that it is compatible with a newer `fnn` binary.
132
+ */
133
+ interface MigrationCheckResult {
134
+ /** Whether migration is needed */
135
+ needed: boolean;
136
+ /** Whether the store is valid (parseable) */
137
+ valid: boolean;
138
+ /** Human-readable status message */
139
+ message: string;
140
+ /** Path to the store that was checked */
141
+ storePath: string;
142
+ }
143
+ interface MigrationResult {
144
+ /** Whether migration succeeded */
145
+ success: boolean;
146
+ /** Path to backup directory (if created) */
147
+ backupPath?: string;
148
+ /** Human-readable message */
149
+ message: string;
150
+ /** Detailed output from fnn-migrate */
151
+ output?: string;
152
+ }
153
+ interface MigrationOptions {
154
+ /** Path to the fiber store directory (typically `<dataDir>/fiber/store`) */
155
+ storePath: string;
156
+ /** Create a backup before migrating (default: true) */
157
+ backup?: boolean;
158
+ /** Directory to place backups in (default: sibling of storePath) */
159
+ backupDir?: string;
160
+ /**
161
+ * Force migration attempt even when pre-check reports incompatible data.
162
+ * Use carefully: migration command may still fail and store may require
163
+ * manual recovery; backup is strongly recommended.
164
+ */
165
+ force?: boolean;
166
+ }
167
+ declare class MigrationManager {
168
+ private migrateBinaryPath;
169
+ constructor(migrateBinaryPath: string);
170
+ /**
171
+ * Check whether the store needs migration or is incompatible.
172
+ *
173
+ * Runs `fnn-migrate -p <storePath> --check-validate` which exits 0 on
174
+ * success (no migration needed) and exits 1 with a message otherwise.
175
+ */
176
+ check(storePath: string): Promise<MigrationCheckResult>;
177
+ /**
178
+ * Create a timestamped backup of the store directory.
179
+ *
180
+ * @returns The path to the created backup directory.
181
+ */
182
+ backup(storePath: string, backupDir?: string): string;
183
+ /**
184
+ * Run the database migration.
185
+ *
186
+ * Optionally creates a backup first. Uses `--skip-confirm` to avoid
187
+ * interactive prompts.
188
+ */
189
+ migrate(options: MigrationOptions): Promise<MigrationResult>;
190
+ /**
191
+ * Rollback a migration by restoring a backup.
192
+ */
193
+ rollback(storePath: string, backupPath: string): void;
194
+ /**
195
+ * Resolve the store path from a data directory.
196
+ *
197
+ * The fiber node stores its database at `<dataDir>/fiber/store`.
198
+ */
199
+ static resolveStorePath(dataDir: string): string;
200
+ /**
201
+ * Check if the store directory exists and is a directory.
202
+ */
203
+ static storeExists(dataDir: string): boolean;
204
+ private ensureBinaryExists;
205
+ private extractStderr;
206
+ }
207
+
117
208
  /**
118
209
  * Process Manager
119
210
  * Manages the lifecycle of the Fiber Network Node (fnn) binary
@@ -208,4 +299,48 @@ declare class ProcessManager extends EventEmitter {
208
299
  private generateConfigFile;
209
300
  }
210
301
 
211
- export { type BinaryInfo, BinaryManager, DEFAULT_FIBER_VERSION, type DownloadOptions, type DownloadProgress, type FiberNodeConfig, ProcessManager, downloadFiberBinary, ensureFiberBinary, getDefaultBinaryPath, getFiberBinaryInfo };
302
+ /**
303
+ * Key Manager
304
+ * Handles generation, storage, and encryption of Fiber node keys
305
+ * Keys are isolated from LLM context for security
306
+ */
307
+
308
+ declare class KeyManager {
309
+ private config;
310
+ private fiberKeyPath;
311
+ private ckbKeyPath;
312
+ constructor(config: KeyConfig);
313
+ /**
314
+ * Initialize keys - generate if they don't exist and autoGenerate is true
315
+ */
316
+ initialize(): Promise<{
317
+ fiber: KeyInfo;
318
+ ckb: KeyInfo;
319
+ }>;
320
+ /**
321
+ * Generate a new key
322
+ */
323
+ generateKey(type: 'fiber' | 'ckb'): Promise<KeyInfo>;
324
+ /**
325
+ * Get information about a key (without exposing the private key)
326
+ */
327
+ getKeyInfo(type: 'fiber' | 'ckb'): Promise<KeyInfo>;
328
+ /**
329
+ * Load and decrypt a private key (for internal use only)
330
+ * This should NEVER be exposed to the LLM context
331
+ */
332
+ private loadPrivateKey;
333
+ /**
334
+ * Export keys for use with the Fiber node process
335
+ * Returns the password to use for FIBER_SECRET_KEY_PASSWORD env var
336
+ */
337
+ getNodeKeyConfig(): {
338
+ password?: string;
339
+ };
340
+ }
341
+ /**
342
+ * Create a key manager with environment-based configuration
343
+ */
344
+ declare function createKeyManager(baseDir: string, options?: Partial<KeyConfig>): KeyManager;
345
+
346
+ export { type BinaryInfo, BinaryManager, DEFAULT_FIBER_VERSION, type DownloadOptions, type DownloadProgress, type FiberNodeConfig, KeyManager, type MigrationCheckResult, MigrationManager, type MigrationOptions, type MigrationResult, ProcessManager, createKeyManager, downloadFiberBinary, ensureFiberBinary, getDefaultBinaryPath, getFiberBinaryInfo };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/constants.ts
2
- var DEFAULT_FIBER_VERSION = "v0.6.1";
2
+ var DEFAULT_FIBER_VERSION = "v0.7.1";
3
3
 
4
4
  // src/binary/manager.ts
5
5
  import { exec } from "child_process";
@@ -10,6 +10,7 @@ var execAsync = promisify(exec);
10
10
  var GITHUB_REPO = "nervosnetwork/fiber";
11
11
  var GITHUB_RELEASES_URL = `https://github.com/${GITHUB_REPO}/releases`;
12
12
  var DEFAULT_INSTALL_DIR = join(process.env.HOME || "~", ".fiber-pay", "bin");
13
+ var RELEASE_TAG_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
13
14
  var BINARY_PATTERNS = {
14
15
  darwin: {
15
16
  x64: "x86_64-darwin",
@@ -60,6 +61,14 @@ var BinaryManager = class {
60
61
  const binaryName = platform === "win32" ? "fnn.exe" : "fnn";
61
62
  return join(this.installDir, binaryName);
62
63
  }
64
+ /**
65
+ * Get the path where the fnn-migrate binary should be installed
66
+ */
67
+ getMigrateBinaryPath() {
68
+ const { platform } = this.getPlatformInfo();
69
+ const binaryName = platform === "win32" ? "fnn-migrate.exe" : "fnn-migrate";
70
+ return join(this.installDir, binaryName);
71
+ }
63
72
  /**
64
73
  * Check if the binary is installed and get its info
65
74
  */
@@ -104,7 +113,17 @@ var BinaryManager = class {
104
113
  * Normalize a version into a release tag
105
114
  */
106
115
  normalizeTag(version) {
107
- return version.startsWith("v") ? version : `v${version}`;
116
+ const input = version.trim();
117
+ if (!input) {
118
+ throw new Error("Version cannot be empty");
119
+ }
120
+ const tag = input.startsWith("v") ? input : `v${input}`;
121
+ if (!RELEASE_TAG_PATTERN.test(tag)) {
122
+ throw new Error(
123
+ `Invalid version format: ${version}. Expected semver-like tag, e.g. v0.7.1 or v0.7.1-rc.1`
124
+ );
125
+ }
126
+ return tag;
108
127
  }
109
128
  /**
110
129
  * Build download candidates for the current platform
@@ -274,10 +293,7 @@ var BinaryManager = class {
274
293
  }
275
294
  }
276
295
  const files = await readdir(tempDir, { recursive: true });
277
- const binaryFile = files.find((f) => {
278
- const name = String(f);
279
- return name.endsWith("fnn") || name.endsWith("fnn.exe");
280
- });
296
+ const binaryFile = this.findBinaryInExtractedFiles(files, "fnn");
281
297
  if (binaryFile) {
282
298
  const sourcePath = join(tempDir, String(binaryFile));
283
299
  await rename(sourcePath, targetPath);
@@ -290,6 +306,29 @@ var BinaryManager = class {
290
306
  await rename(join(tempDir, possibleBinary), targetPath);
291
307
  }
292
308
  }
309
+ const migrateFile = this.findBinaryInExtractedFiles(files, "fnn-migrate");
310
+ if (migrateFile) {
311
+ const migrateSourcePath = join(tempDir, String(migrateFile));
312
+ const migrateTargetPath = this.getMigrateBinaryPath();
313
+ try {
314
+ if (existsSync(migrateTargetPath)) {
315
+ try {
316
+ unlinkSync(migrateTargetPath);
317
+ } catch {
318
+ }
319
+ }
320
+ await rename(migrateSourcePath, migrateTargetPath);
321
+ const { platform } = this.getPlatformInfo();
322
+ if (platform !== "win32") {
323
+ chmodSync(migrateTargetPath, 493);
324
+ }
325
+ } catch (error) {
326
+ const message = error instanceof Error ? error.message : String(error);
327
+ console.warn(
328
+ `Warning: failed to install fnn-migrate helper. Migrations may be unavailable or stale. Error: ${message}`
329
+ );
330
+ }
331
+ }
293
332
  await rm(tempDir, { recursive: true, force: true });
294
333
  }
295
334
  /**
@@ -312,13 +351,32 @@ var BinaryManager = class {
312
351
  await execAsync(`unzip -o "${archivePath}" -d "${tempDir}"`);
313
352
  }
314
353
  const files = await readdir(tempDir, { recursive: true });
315
- const binaryFile = files.find((f) => {
316
- const name = String(f);
317
- return name.endsWith("fnn") || name.endsWith("fnn.exe");
318
- });
354
+ const binaryFile = this.findBinaryInExtractedFiles(files, "fnn");
319
355
  if (binaryFile) {
320
356
  await rename(join(tempDir, String(binaryFile)), targetPath);
321
357
  }
358
+ const migrateFile = this.findBinaryInExtractedFiles(files, "fnn-migrate");
359
+ if (migrateFile) {
360
+ const migrateTargetPath = this.getMigrateBinaryPath();
361
+ try {
362
+ if (existsSync(migrateTargetPath)) {
363
+ try {
364
+ unlinkSync(migrateTargetPath);
365
+ } catch {
366
+ }
367
+ }
368
+ await rename(join(tempDir, String(migrateFile)), migrateTargetPath);
369
+ const { platform: platform2 } = this.getPlatformInfo();
370
+ if (platform2 !== "win32") {
371
+ chmodSync(migrateTargetPath, 493);
372
+ }
373
+ } catch (error) {
374
+ const message = error instanceof Error ? error.message : String(error);
375
+ console.warn(
376
+ `Warning: failed to install fnn-migrate helper. Migrations may be unavailable or stale. Error: ${message}`
377
+ );
378
+ }
379
+ }
322
380
  await rm(tempDir, { recursive: true, force: true });
323
381
  }
324
382
  /**
@@ -330,6 +388,15 @@ var BinaryManager = class {
330
388
  unlinkSync(binaryPath);
331
389
  }
332
390
  }
391
+ /**
392
+ * Find a named binary in a list of extracted file paths.
393
+ */
394
+ findBinaryInExtractedFiles(files, binaryName) {
395
+ return files.find((f) => {
396
+ const name = String(f);
397
+ return name.endsWith(`/${binaryName}`) || name === binaryName || name.endsWith(`\\${binaryName}`) || name.endsWith(`${binaryName}.exe`);
398
+ });
399
+ }
333
400
  };
334
401
  async function downloadFiberBinary(options = {}) {
335
402
  const manager = new BinaryManager(options.installDir);
@@ -359,11 +426,256 @@ function getDefaultBinaryPath() {
359
426
  return manager.getBinaryPath();
360
427
  }
361
428
 
429
+ // src/migration/manager.ts
430
+ import { execFile } from "child_process";
431
+ import { cpSync, existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync, rmSync, statSync } from "fs";
432
+ import { basename, dirname, join as join2 } from "path";
433
+ import { promisify as promisify2 } from "util";
434
+ var execFileAsync = promisify2(execFile);
435
+ var MigrationManager = class _MigrationManager {
436
+ migrateBinaryPath;
437
+ constructor(migrateBinaryPath) {
438
+ this.migrateBinaryPath = migrateBinaryPath;
439
+ }
440
+ // ---------------------------------------------------------------------------
441
+ // Public API
442
+ // ---------------------------------------------------------------------------
443
+ /**
444
+ * Check whether the store needs migration or is incompatible.
445
+ *
446
+ * Runs `fnn-migrate -p <storePath> --check-validate` which exits 0 on
447
+ * success (no migration needed) and exits 1 with a message otherwise.
448
+ */
449
+ async check(storePath) {
450
+ this.ensureBinaryExists();
451
+ if (!existsSync2(storePath)) {
452
+ return {
453
+ needed: false,
454
+ valid: true,
455
+ message: "Store does not exist yet \u2014 no migration needed.",
456
+ storePath
457
+ };
458
+ }
459
+ try {
460
+ const { stdout } = await execFileAsync(this.migrateBinaryPath, [
461
+ "-p",
462
+ storePath,
463
+ "--check-validate"
464
+ ]);
465
+ const output = stdout.trim();
466
+ if (output.includes("validate success")) {
467
+ return {
468
+ needed: false,
469
+ valid: true,
470
+ message: "Store is up-to-date, no migration needed.",
471
+ storePath
472
+ };
473
+ }
474
+ return {
475
+ needed: false,
476
+ valid: true,
477
+ message: output || "Store validation passed.",
478
+ storePath
479
+ };
480
+ } catch (error) {
481
+ const stderr = this.extractStderr(error);
482
+ const isIncompatible = stderr.includes("incompatible database") || stderr.includes("need to upgrade");
483
+ const needsMigration = stderr.includes("need to run database migration") || stderr.includes("need to migrate");
484
+ const needsCleanStart = stderr.includes("shutdown all channels") || stderr.includes("shutdown all old channels");
485
+ if (needsCleanStart) {
486
+ return {
487
+ needed: true,
488
+ valid: false,
489
+ message: "Store requires a breaking migration that cannot be auto-migrated. You need to:\n 1. Start the OLD version of fnn\n 2. Close all channels (cooperative or forced)\n 3. Stop the old node\n 4. Remove the store directory\n 5. Start the new fnn version with a fresh database\n\nSee: https://github.com/nervosnetwork/fiber/wiki/Fiber-Breaking-Change-Migration-Guide",
490
+ storePath
491
+ };
492
+ }
493
+ if (needsMigration) {
494
+ return {
495
+ needed: true,
496
+ valid: true,
497
+ message: "Store needs migration. Run `fiber-pay node upgrade` to migrate.",
498
+ storePath
499
+ };
500
+ }
501
+ if (isIncompatible) {
502
+ return {
503
+ needed: true,
504
+ valid: false,
505
+ message: `Store is incompatible: ${stderr}`,
506
+ storePath
507
+ };
508
+ }
509
+ return {
510
+ needed: true,
511
+ valid: false,
512
+ message: `Store validation failed: ${stderr}`,
513
+ storePath
514
+ };
515
+ }
516
+ }
517
+ /**
518
+ * Create a timestamped backup of the store directory.
519
+ *
520
+ * @returns The path to the created backup directory.
521
+ */
522
+ backup(storePath, backupDir) {
523
+ if (!existsSync2(storePath)) {
524
+ throw new Error(`Store path does not exist: ${storePath}`);
525
+ }
526
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
527
+ const storeName = basename(storePath);
528
+ const targetDir = backupDir || dirname(storePath);
529
+ const backupPath = join2(targetDir, `${storeName}.bak-${timestamp}`);
530
+ mkdirSync2(backupPath, { recursive: true });
531
+ cpSync(storePath, backupPath, { recursive: true });
532
+ return backupPath;
533
+ }
534
+ /**
535
+ * Run the database migration.
536
+ *
537
+ * Optionally creates a backup first. Uses `--skip-confirm` to avoid
538
+ * interactive prompts.
539
+ */
540
+ async migrate(options) {
541
+ const { storePath, backup: doBackup = true, backupDir, force = false } = options;
542
+ this.ensureBinaryExists();
543
+ if (!existsSync2(storePath)) {
544
+ return {
545
+ success: true,
546
+ message: "Store does not exist \u2014 nothing to migrate."
547
+ };
548
+ }
549
+ const checkResult = await this.check(storePath);
550
+ if (!checkResult.needed) {
551
+ return {
552
+ success: true,
553
+ message: checkResult.message
554
+ };
555
+ }
556
+ if (!checkResult.valid && !force) {
557
+ return {
558
+ success: false,
559
+ message: checkResult.message
560
+ };
561
+ }
562
+ let backupPath;
563
+ if (doBackup) {
564
+ try {
565
+ backupPath = this.backup(storePath, backupDir);
566
+ } catch (backupError) {
567
+ const msg = backupError instanceof Error ? backupError.message : String(backupError);
568
+ return {
569
+ success: false,
570
+ message: `Failed to create backup before migration: ${msg}`
571
+ };
572
+ }
573
+ }
574
+ try {
575
+ const { stdout, stderr } = await execFileAsync(this.migrateBinaryPath, [
576
+ "-p",
577
+ storePath,
578
+ "--skip-confirm"
579
+ ]);
580
+ const output = `${stdout}
581
+ ${stderr}`.trim();
582
+ if (output.includes("migrated successfully") || output.includes("db migrated")) {
583
+ return {
584
+ success: true,
585
+ backupPath,
586
+ message: "Migration completed successfully.",
587
+ output
588
+ };
589
+ }
590
+ let ambiguousMessage = `Migration command finished without errors, but the expected success message was not found. Output: ${output}`;
591
+ if (backupPath) {
592
+ ambiguousMessage += `
593
+
594
+ A backup was created at: ${backupPath}
595
+ To roll back, delete the current store at "${storePath}" and restore the backup from that path.`;
596
+ }
597
+ return {
598
+ success: false,
599
+ backupPath,
600
+ message: ambiguousMessage,
601
+ output
602
+ };
603
+ } catch (error) {
604
+ const stderr = this.extractStderr(error);
605
+ let message = `Migration failed: ${stderr}`;
606
+ if (backupPath) {
607
+ message += `
608
+
609
+ A backup was created at: ${backupPath}
610
+ To roll back, delete the current store at "${storePath}" and restore the backup from that path.`;
611
+ }
612
+ return {
613
+ success: false,
614
+ backupPath,
615
+ message,
616
+ output: stderr
617
+ };
618
+ }
619
+ }
620
+ /**
621
+ * Rollback a migration by restoring a backup.
622
+ */
623
+ rollback(storePath, backupPath) {
624
+ if (!existsSync2(backupPath)) {
625
+ throw new Error(`Backup path does not exist: ${backupPath}`);
626
+ }
627
+ if (existsSync2(storePath)) {
628
+ rmSync(storePath, { recursive: true, force: true });
629
+ }
630
+ renameSync(backupPath, storePath);
631
+ }
632
+ // ---------------------------------------------------------------------------
633
+ // Helpers
634
+ // ---------------------------------------------------------------------------
635
+ /**
636
+ * Resolve the store path from a data directory.
637
+ *
638
+ * The fiber node stores its database at `<dataDir>/fiber/store`.
639
+ */
640
+ static resolveStorePath(dataDir) {
641
+ return join2(dataDir, "fiber", "store");
642
+ }
643
+ /**
644
+ * Check if the store directory exists and is a directory.
645
+ */
646
+ static storeExists(dataDir) {
647
+ const storePath = _MigrationManager.resolveStorePath(dataDir);
648
+ if (!existsSync2(storePath)) return false;
649
+ try {
650
+ const stats = statSync(storePath);
651
+ return stats.isDirectory();
652
+ } catch {
653
+ return false;
654
+ }
655
+ }
656
+ ensureBinaryExists() {
657
+ if (!existsSync2(this.migrateBinaryPath)) {
658
+ throw new Error(
659
+ `fnn-migrate binary not found at: ${this.migrateBinaryPath}
660
+ This binary is required for database migration.
661
+ Re-download the Fiber binary with: fiber-pay binary download --force`
662
+ );
663
+ }
664
+ }
665
+ extractStderr(error) {
666
+ if (error && typeof error === "object") {
667
+ const e = error;
668
+ return (e.stderr || e.stdout || e.message || String(error)).trim();
669
+ }
670
+ return String(error);
671
+ }
672
+ };
673
+
362
674
  // src/process/manager.ts
363
675
  import { spawn } from "child_process";
364
676
  import { EventEmitter } from "events";
365
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
366
- import { dirname, join as join2 } from "path";
677
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync, writeFileSync } from "fs";
678
+ import { dirname as dirname2, join as join3 } from "path";
367
679
 
368
680
  // src/process/yaml.ts
369
681
  function stringify(obj, indent = 0) {
@@ -416,7 +728,7 @@ var DEFAULT_TESTNET_FIBER_CONFIG = {
416
728
  listening_addr: "/ip4/0.0.0.0/tcp/8228",
417
729
  bootnode_addrs: [
418
730
  "/ip4/54.179.226.154/tcp/8228/p2p/Qmes1EBD4yNo9Ywkfe6eRw9tG1nVNGLDmMud1xJMsoYFKy",
419
- "/ip4/54.179.226.154/tcp/18228/p2p/QmdyQWjPtbK4NWWsvy8s69NGJaQULwgeQDT5ZpNDrTNaeV"
731
+ "/ip4/16.163.7.105/tcp/8228/p2p/QmdyQWjPtbK4NWWsvy8s69NGJaQULwgeQDT5ZpNDrTNaeV"
420
732
  ],
421
733
  announce_listening_addr: true,
422
734
  chain: "testnet",
@@ -486,7 +798,7 @@ var ProcessManager = class extends EventEmitter {
486
798
  constructor(config) {
487
799
  super();
488
800
  this.config = config;
489
- this.configPath = join2(config.dataDir, "config.yml");
801
+ this.configPath = join3(config.dataDir, "config.yml");
490
802
  }
491
803
  /**
492
804
  * Get current process state
@@ -508,8 +820,8 @@ var ProcessManager = class extends EventEmitter {
508
820
  throw new Error("Node is already running");
509
821
  }
510
822
  this.state = "starting";
511
- if (!existsSync2(this.config.dataDir)) {
512
- mkdirSync2(this.config.dataDir, { recursive: true });
823
+ if (!existsSync3(this.config.dataDir)) {
824
+ mkdirSync3(this.config.dataDir, { recursive: true });
513
825
  }
514
826
  await this.ensureConfigFile();
515
827
  const env = {
@@ -648,11 +960,11 @@ var ProcessManager = class extends EventEmitter {
648
960
  * Ensure the config file exists (copy from source or generate)
649
961
  */
650
962
  async ensureConfigFile() {
651
- const configDir = dirname(this.configPath);
652
- if (!existsSync2(configDir)) {
653
- mkdirSync2(configDir, { recursive: true });
963
+ const configDir = dirname2(this.configPath);
964
+ if (!existsSync3(configDir)) {
965
+ mkdirSync3(configDir, { recursive: true });
654
966
  }
655
- if (this.config.configFilePath && existsSync2(this.config.configFilePath)) {
967
+ if (this.config.configFilePath && existsSync3(this.config.configFilePath)) {
656
968
  const sourceContent = readFileSync(this.config.configFilePath, "utf-8");
657
969
  writeFileSync(this.configPath, sourceContent);
658
970
  return;
@@ -688,17 +1000,136 @@ var ProcessManager = class extends EventEmitter {
688
1000
  if (this.config.udtWhitelist?.length) {
689
1001
  config.ckb.udt_whitelist = this.config.udtWhitelist;
690
1002
  }
691
- const configDir = dirname(this.configPath);
692
- if (!existsSync2(configDir)) {
693
- mkdirSync2(configDir, { recursive: true });
1003
+ const configDir = dirname2(this.configPath);
1004
+ if (!existsSync3(configDir)) {
1005
+ mkdirSync3(configDir, { recursive: true });
694
1006
  }
695
1007
  writeFileSync(this.configPath, stringify(config));
696
1008
  }
697
1009
  };
1010
+
1011
+ // src/security/key-manager.ts
1012
+ import { chmodSync as chmodSync2, existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1013
+ import { dirname as dirname3, join as join4 } from "path";
1014
+ import { decryptKey, derivePublicKey, generatePrivateKey, isEncryptedKey } from "@fiber-pay/sdk";
1015
+ var KeyManager = class {
1016
+ config;
1017
+ fiberKeyPath;
1018
+ ckbKeyPath;
1019
+ constructor(config) {
1020
+ this.config = config;
1021
+ this.fiberKeyPath = join4(config.baseDir, "fiber", "sk");
1022
+ this.ckbKeyPath = join4(config.baseDir, "ckb", "key");
1023
+ }
1024
+ /**
1025
+ * Initialize keys - generate if they don't exist and autoGenerate is true
1026
+ */
1027
+ async initialize() {
1028
+ const fiberExists = existsSync4(this.fiberKeyPath);
1029
+ const ckbExists = existsSync4(this.ckbKeyPath);
1030
+ if (!fiberExists || !ckbExists) {
1031
+ if (!this.config.autoGenerate) {
1032
+ throw new Error(
1033
+ `Keys not found and autoGenerate is disabled. Missing: ${[!fiberExists && "fiber", !ckbExists && "ckb"].filter(Boolean).join(", ")}`
1034
+ );
1035
+ }
1036
+ }
1037
+ if (!fiberExists) {
1038
+ await this.generateKey("fiber");
1039
+ }
1040
+ if (!ckbExists) {
1041
+ await this.generateKey("ckb");
1042
+ }
1043
+ return {
1044
+ fiber: await this.getKeyInfo("fiber"),
1045
+ ckb: await this.getKeyInfo("ckb")
1046
+ };
1047
+ }
1048
+ /**
1049
+ * Generate a new key
1050
+ */
1051
+ async generateKey(type) {
1052
+ const keyPath = type === "fiber" ? this.fiberKeyPath : this.ckbKeyPath;
1053
+ const keyDir = dirname3(keyPath);
1054
+ if (!existsSync4(keyDir)) {
1055
+ mkdirSync4(keyDir, { recursive: true });
1056
+ }
1057
+ const privateKey = generatePrivateKey();
1058
+ let keyData;
1059
+ if (type === "fiber") {
1060
+ keyData = privateKey;
1061
+ } else {
1062
+ keyData = Buffer.from(privateKey).toString("hex");
1063
+ }
1064
+ writeFileSync2(keyPath, keyData);
1065
+ chmodSync2(keyPath, 384);
1066
+ return this.getKeyInfo(type);
1067
+ }
1068
+ /**
1069
+ * Get information about a key (without exposing the private key)
1070
+ */
1071
+ async getKeyInfo(type) {
1072
+ const keyPath = type === "fiber" ? this.fiberKeyPath : this.ckbKeyPath;
1073
+ if (!existsSync4(keyPath)) {
1074
+ throw new Error(`Key not found: ${keyPath}`);
1075
+ }
1076
+ const keyData = readFileSync2(keyPath);
1077
+ const encrypted = isEncryptedKey(keyData);
1078
+ const privateKey = await this.loadPrivateKey(type);
1079
+ const publicKey = await derivePublicKey(privateKey);
1080
+ return {
1081
+ publicKey,
1082
+ encrypted,
1083
+ path: keyPath,
1084
+ createdAt: Date.now()
1085
+ // TODO: get actual file creation time
1086
+ };
1087
+ }
1088
+ /**
1089
+ * Load and decrypt a private key (for internal use only)
1090
+ * This should NEVER be exposed to the LLM context
1091
+ */
1092
+ async loadPrivateKey(type) {
1093
+ const keyPath = type === "fiber" ? this.fiberKeyPath : this.ckbKeyPath;
1094
+ const keyData = readFileSync2(keyPath);
1095
+ if (isEncryptedKey(keyData)) {
1096
+ if (!this.config.encryptionPassword) {
1097
+ throw new Error("Key is encrypted but no password provided");
1098
+ }
1099
+ return await decryptKey(keyData, this.config.encryptionPassword);
1100
+ }
1101
+ if (type === "fiber") {
1102
+ return new Uint8Array(keyData);
1103
+ } else {
1104
+ const hexString = keyData.toString("utf-8").trim();
1105
+ return new Uint8Array(Buffer.from(hexString, "hex"));
1106
+ }
1107
+ }
1108
+ /**
1109
+ * Export keys for use with the Fiber node process
1110
+ * Returns the password to use for FIBER_SECRET_KEY_PASSWORD env var
1111
+ */
1112
+ getNodeKeyConfig() {
1113
+ return {
1114
+ password: this.config.encryptionPassword
1115
+ };
1116
+ }
1117
+ };
1118
+ function createKeyManager(baseDir, options) {
1119
+ const password = process.env.FIBER_KEY_PASSWORD || options?.encryptionPassword;
1120
+ return new KeyManager({
1121
+ baseDir,
1122
+ encryptionPassword: password,
1123
+ autoGenerate: options?.autoGenerate ?? true
1124
+ });
1125
+ }
698
1126
  export {
699
1127
  BinaryManager,
700
1128
  DEFAULT_FIBER_VERSION,
1129
+ KeyManager,
1130
+ MigrationManager,
701
1131
  ProcessManager,
1132
+ createKeyManager,
702
1133
  downloadFiberBinary,
703
1134
  ensureFiberBinary,
704
1135
  getDefaultBinaryPath,
package/dist/index.js.map CHANGED
@@ -1 +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 (primaryError) {\n // Fallback: use Node's built-in zlib to avoid external `gunzip` dependency\n try {\n const { gunzipSync } = await import('node:zlib');\n const tarPath = `${tempDir}/archive.tar`;\n const tarBuffer = gunzipSync(buffer);\n await writeFile(tarPath, tarBuffer);\n await execAsync(`tar -xf \"${tarPath}\" -C \"${tempDir}\"`);\n } catch (fallbackError) {\n const primaryMessage =\n primaryError instanceof Error ? primaryError.message : String(primaryError);\n const fallbackMessage =\n fallbackError instanceof Error ? fallbackError.message : String(fallbackError);\n throw new Error(\n `Failed to extract tar.gz archive. Primary: ${primaryMessage}. Fallback: ${fallbackMessage}`,\n );\n }\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"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/binary/manager.ts","../src/migration/manager.ts","../src/process/manager.ts","../src/process/yaml.ts","../src/security/key-manager.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.7.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');\nconst RELEASE_TAG_PATTERN = /^v\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/;\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 * Get the path where the fnn-migrate binary should be installed\n */\n getMigrateBinaryPath(): string {\n const { platform } = this.getPlatformInfo();\n const binaryName = platform === 'win32' ? 'fnn-migrate.exe' : 'fnn-migrate';\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.7.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 const input = version.trim();\n if (!input) {\n throw new Error('Version cannot be empty');\n }\n\n const tag = input.startsWith('v') ? input : `v${input}`;\n if (!RELEASE_TAG_PATTERN.test(tag)) {\n throw new Error(\n `Invalid version format: ${version}. Expected semver-like tag, e.g. v0.7.1 or v0.7.1-rc.1`,\n );\n }\n\n return tag;\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 (primaryError) {\n // Fallback: use Node's built-in zlib to avoid external `gunzip` dependency\n try {\n const { gunzipSync } = await import('node:zlib');\n const tarPath = `${tempDir}/archive.tar`;\n const tarBuffer = gunzipSync(buffer);\n await writeFile(tarPath, tarBuffer);\n await execAsync(`tar -xf \"${tarPath}\" -C \"${tempDir}\"`);\n } catch (fallbackError) {\n const primaryMessage =\n primaryError instanceof Error ? primaryError.message : String(primaryError);\n const fallbackMessage =\n fallbackError instanceof Error ? fallbackError.message : String(fallbackError);\n throw new Error(\n `Failed to extract tar.gz archive. Primary: ${primaryMessage}. Fallback: ${fallbackMessage}`,\n );\n }\n }\n\n // Find the binary in extracted files\n const files = await readdir(tempDir, { recursive: true });\n const binaryFile = this.findBinaryInExtractedFiles(files, 'fnn');\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 // Also extract fnn-migrate if present in the archive\n const migrateFile = this.findBinaryInExtractedFiles(files, 'fnn-migrate');\n\n if (migrateFile) {\n const migrateSourcePath = join(tempDir, String(migrateFile));\n const migrateTargetPath = this.getMigrateBinaryPath();\n try {\n // Proactively remove existing fnn-migrate so rename doesn't fail\n if (existsSync(migrateTargetPath)) {\n try {\n unlinkSync(migrateTargetPath);\n } catch {\n // If we can't remove the existing file, the rename will likely fail below\n }\n }\n await rename(migrateSourcePath, migrateTargetPath);\n const { platform } = this.getPlatformInfo();\n if (platform !== 'win32') {\n chmodSync(migrateTargetPath, 0o755);\n }\n } catch (error) {\n // fnn-migrate is optional; don't fail the main install, but warn\n const message = error instanceof Error ? error.message : String(error);\n console.warn(\n `Warning: failed to install fnn-migrate helper. Migrations may be unavailable or stale. Error: ${message}`,\n );\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 = this.findBinaryInExtractedFiles(files, 'fnn');\n\n if (binaryFile) {\n await rename(join(tempDir, String(binaryFile)), targetPath);\n }\n\n // Also extract fnn-migrate if present\n const migrateFile = this.findBinaryInExtractedFiles(files, 'fnn-migrate');\n\n if (migrateFile) {\n const migrateTargetPath = this.getMigrateBinaryPath();\n try {\n // Proactively remove existing fnn-migrate so rename doesn't fail\n if (existsSync(migrateTargetPath)) {\n try {\n unlinkSync(migrateTargetPath);\n } catch {\n // If we can't remove the existing file, the rename will likely fail below\n }\n }\n await rename(join(tempDir, String(migrateFile)), migrateTargetPath);\n const { platform } = this.getPlatformInfo();\n if (platform !== 'win32') {\n chmodSync(migrateTargetPath, 0o755);\n }\n } catch (error) {\n // fnn-migrate is optional; don't fail the main install, but warn\n const message = error instanceof Error ? error.message : String(error);\n console.warn(\n `Warning: failed to install fnn-migrate helper. Migrations may be unavailable or stale. Error: ${message}`,\n );\n }\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 * Find a named binary in a list of extracted file paths.\n */\n private findBinaryInExtractedFiles(\n files: (string | Buffer)[],\n binaryName: 'fnn' | 'fnn-migrate',\n ): string | Buffer | undefined {\n return files.find((f) => {\n const name = String(f);\n return (\n name.endsWith(`/${binaryName}`) ||\n name === binaryName ||\n name.endsWith(`\\\\${binaryName}`) ||\n name.endsWith(`${binaryName}.exe`)\n );\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 * Migration Manager\n * Handles Fiber node database migration when upgrading between versions.\n *\n * Uses the `fnn-migrate` binary shipped in Fiber release archives to migrate\n * the on-disk store format so that it is compatible with a newer `fnn` binary.\n */\n\nimport { execFile } from 'node:child_process';\nimport { cpSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface MigrationCheckResult {\n /** Whether migration is needed */\n needed: boolean;\n /** Whether the store is valid (parseable) */\n valid: boolean;\n /** Human-readable status message */\n message: string;\n /** Path to the store that was checked */\n storePath: string;\n}\n\nexport interface MigrationResult {\n /** Whether migration succeeded */\n success: boolean;\n /** Path to backup directory (if created) */\n backupPath?: string;\n /** Human-readable message */\n message: string;\n /** Detailed output from fnn-migrate */\n output?: string;\n}\n\nexport interface MigrationOptions {\n /** Path to the fiber store directory (typically `<dataDir>/fiber/store`) */\n storePath: string;\n /** Create a backup before migrating (default: true) */\n backup?: boolean;\n /** Directory to place backups in (default: sibling of storePath) */\n backupDir?: string;\n /**\n * Force migration attempt even when pre-check reports incompatible data.\n * Use carefully: migration command may still fail and store may require\n * manual recovery; backup is strongly recommended.\n */\n force?: boolean;\n}\n\n// =============================================================================\n// Migration Manager\n// =============================================================================\n\nexport class MigrationManager {\n private migrateBinaryPath: string;\n\n constructor(migrateBinaryPath: string) {\n this.migrateBinaryPath = migrateBinaryPath;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /**\n * Check whether the store needs migration or is incompatible.\n *\n * Runs `fnn-migrate -p <storePath> --check-validate` which exits 0 on\n * success (no migration needed) and exits 1 with a message otherwise.\n */\n async check(storePath: string): Promise<MigrationCheckResult> {\n this.ensureBinaryExists();\n\n if (!existsSync(storePath)) {\n return {\n needed: false,\n valid: true,\n message: 'Store does not exist yet — no migration needed.',\n storePath,\n };\n }\n\n try {\n const { stdout } = await execFileAsync(this.migrateBinaryPath, [\n '-p',\n storePath,\n '--check-validate',\n ]);\n const output = stdout.trim();\n if (output.includes('validate success')) {\n return {\n needed: false,\n valid: true,\n message: 'Store is up-to-date, no migration needed.',\n storePath,\n };\n }\n return {\n needed: false,\n valid: true,\n message: output || 'Store validation passed.',\n storePath,\n };\n } catch (error) {\n const stderr = this.extractStderr(error);\n const isIncompatible =\n stderr.includes('incompatible database') || stderr.includes('need to upgrade');\n const needsMigration =\n stderr.includes('need to run database migration') || stderr.includes('need to migrate');\n const needsCleanStart =\n stderr.includes('shutdown all channels') || stderr.includes('shutdown all old channels');\n\n if (needsCleanStart) {\n return {\n needed: true,\n valid: false,\n message:\n 'Store requires a breaking migration that cannot be auto-migrated. ' +\n 'You need to:\\n' +\n ' 1. Start the OLD version of fnn\\n' +\n ' 2. Close all channels (cooperative or forced)\\n' +\n ' 3. Stop the old node\\n' +\n ' 4. Remove the store directory\\n' +\n ' 5. Start the new fnn version with a fresh database\\n\\n' +\n 'See: https://github.com/nervosnetwork/fiber/wiki/Fiber-Breaking-Change-Migration-Guide',\n storePath,\n };\n }\n\n if (needsMigration) {\n return {\n needed: true,\n valid: true,\n message: 'Store needs migration. Run `fiber-pay node upgrade` to migrate.',\n storePath,\n };\n }\n\n if (isIncompatible) {\n return {\n needed: true,\n valid: false,\n message: `Store is incompatible: ${stderr}`,\n storePath,\n };\n }\n\n return {\n needed: true,\n valid: false,\n message: `Store validation failed: ${stderr}`,\n storePath,\n };\n }\n }\n\n /**\n * Create a timestamped backup of the store directory.\n *\n * @returns The path to the created backup directory.\n */\n backup(storePath: string, backupDir?: string): string {\n if (!existsSync(storePath)) {\n throw new Error(`Store path does not exist: ${storePath}`);\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const storeName = basename(storePath);\n const targetDir = backupDir || dirname(storePath);\n const backupPath = join(targetDir, `${storeName}.bak-${timestamp}`);\n\n mkdirSync(backupPath, { recursive: true });\n cpSync(storePath, backupPath, { recursive: true });\n\n return backupPath;\n }\n\n /**\n * Run the database migration.\n *\n * Optionally creates a backup first. Uses `--skip-confirm` to avoid\n * interactive prompts.\n */\n async migrate(options: MigrationOptions): Promise<MigrationResult> {\n const { storePath, backup: doBackup = true, backupDir, force = false } = options;\n\n this.ensureBinaryExists();\n\n if (!existsSync(storePath)) {\n return {\n success: true,\n message: 'Store does not exist — nothing to migrate.',\n };\n }\n\n // Pre-flight check\n const checkResult = await this.check(storePath);\n\n if (!checkResult.needed) {\n return {\n success: true,\n message: checkResult.message,\n };\n }\n\n if (!checkResult.valid && !force) {\n return {\n success: false,\n message: checkResult.message,\n };\n }\n\n // Backup\n let backupPath: string | undefined;\n if (doBackup) {\n try {\n backupPath = this.backup(storePath, backupDir);\n } catch (backupError) {\n const msg = backupError instanceof Error ? backupError.message : String(backupError);\n return {\n success: false,\n message: `Failed to create backup before migration: ${msg}`,\n };\n }\n }\n\n // Run migration\n try {\n const { stdout, stderr } = await execFileAsync(this.migrateBinaryPath, [\n '-p',\n storePath,\n '--skip-confirm',\n ]);\n const output = `${stdout}\\n${stderr}`.trim();\n\n if (output.includes('migrated successfully') || output.includes('db migrated')) {\n return {\n success: true,\n backupPath,\n message: 'Migration completed successfully.',\n output,\n };\n }\n\n // Command exited 0 but no recognized success message — treat as failure to be safe\n let ambiguousMessage = `Migration command finished without errors, but the expected success message was not found. Output: ${output}`;\n if (backupPath) {\n ambiguousMessage += `\\n\\nA backup was created at: ${backupPath}\\nTo roll back, delete the current store at \"${storePath}\" and restore the backup from that path.`;\n }\n return {\n success: false,\n backupPath,\n message: ambiguousMessage,\n output,\n };\n } catch (error) {\n const stderr = this.extractStderr(error);\n\n // Offer rollback information\n let message = `Migration failed: ${stderr}`;\n if (backupPath) {\n message += `\\n\\nA backup was created at: ${backupPath}\\nTo roll back, delete the current store at \"${storePath}\" and restore the backup from that path.`;\n }\n\n return {\n success: false,\n backupPath,\n message,\n output: stderr,\n };\n }\n }\n\n /**\n * Rollback a migration by restoring a backup.\n */\n rollback(storePath: string, backupPath: string): void {\n if (!existsSync(backupPath)) {\n throw new Error(`Backup path does not exist: ${backupPath}`);\n }\n\n // Remove the (potentially corrupted) current store\n if (existsSync(storePath)) {\n rmSync(storePath, { recursive: true, force: true });\n }\n\n // Restore from backup\n renameSync(backupPath, storePath);\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the store path from a data directory.\n *\n * The fiber node stores its database at `<dataDir>/fiber/store`.\n */\n static resolveStorePath(dataDir: string): string {\n return join(dataDir, 'fiber', 'store');\n }\n\n /**\n * Check if the store directory exists and is a directory.\n */\n static storeExists(dataDir: string): boolean {\n const storePath = MigrationManager.resolveStorePath(dataDir);\n if (!existsSync(storePath)) return false;\n try {\n const stats = statSync(storePath);\n return stats.isDirectory();\n } catch {\n return false;\n }\n }\n\n private ensureBinaryExists(): void {\n if (!existsSync(this.migrateBinaryPath)) {\n throw new Error(\n `fnn-migrate binary not found at: ${this.migrateBinaryPath}\\n` +\n 'This binary is required for database migration.\\n' +\n 'Re-download the Fiber binary with: fiber-pay binary download --force',\n );\n }\n }\n\n private extractStderr(error: unknown): string {\n if (error && typeof error === 'object') {\n const e = error as { stderr?: string; stdout?: string; message?: string };\n return (e.stderr || e.stdout || e.message || String(error)).trim();\n }\n return String(error);\n }\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/16.163.7.105/tcp/8228/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","/**\n * Key Manager\n * Handles generation, storage, and encryption of Fiber node keys\n * Keys are isolated from LLM context for security\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { KeyConfig, KeyInfo } from '@fiber-pay/sdk';\nimport { decryptKey, derivePublicKey, generatePrivateKey, isEncryptedKey } from '@fiber-pay/sdk';\n\n// =============================================================================\n// Key Manager\n// =============================================================================\n\nexport class KeyManager {\n private config: KeyConfig;\n private fiberKeyPath: string;\n private ckbKeyPath: string;\n\n constructor(config: KeyConfig) {\n this.config = config;\n this.fiberKeyPath = join(config.baseDir, 'fiber', 'sk');\n this.ckbKeyPath = join(config.baseDir, 'ckb', 'key');\n }\n\n /**\n * Initialize keys - generate if they don't exist and autoGenerate is true\n */\n async initialize(): Promise<{ fiber: KeyInfo; ckb: KeyInfo }> {\n const fiberExists = existsSync(this.fiberKeyPath);\n const ckbExists = existsSync(this.ckbKeyPath);\n\n if (!fiberExists || !ckbExists) {\n if (!this.config.autoGenerate) {\n throw new Error(\n `Keys not found and autoGenerate is disabled. ` +\n `Missing: ${[!fiberExists && 'fiber', !ckbExists && 'ckb'].filter(Boolean).join(', ')}`,\n );\n }\n }\n\n // Generate missing keys\n if (!fiberExists) {\n await this.generateKey('fiber');\n }\n if (!ckbExists) {\n await this.generateKey('ckb');\n }\n\n return {\n fiber: await this.getKeyInfo('fiber'),\n ckb: await this.getKeyInfo('ckb'),\n };\n }\n\n /**\n * Generate a new key\n */\n async generateKey(type: 'fiber' | 'ckb'): Promise<KeyInfo> {\n const keyPath = type === 'fiber' ? this.fiberKeyPath : this.ckbKeyPath;\n const keyDir = dirname(keyPath);\n\n // Create directory if it doesn't exist\n if (!existsSync(keyDir)) {\n mkdirSync(keyDir, { recursive: true });\n }\n\n // Generate 32 random bytes\n const privateKey = generatePrivateKey();\n\n // The fiber node expects different formats:\n // - fiber/sk: raw 32 bytes\n // - ckb/key: hex string (64 characters)\n let keyData: string | Uint8Array;\n if (type === 'fiber') {\n keyData = privateKey;\n } else {\n keyData = Buffer.from(privateKey).toString('hex');\n }\n\n // Write key file with restricted permissions\n writeFileSync(keyPath, keyData);\n chmodSync(keyPath, 0o600);\n\n return this.getKeyInfo(type);\n }\n\n /**\n * Get information about a key (without exposing the private key)\n */\n async getKeyInfo(type: 'fiber' | 'ckb'): Promise<KeyInfo> {\n const keyPath = type === 'fiber' ? this.fiberKeyPath : this.ckbKeyPath;\n\n if (!existsSync(keyPath)) {\n throw new Error(`Key not found: ${keyPath}`);\n }\n\n const keyData = readFileSync(keyPath);\n const encrypted = isEncryptedKey(keyData);\n\n // Get private key to derive public key\n const privateKey = await this.loadPrivateKey(type);\n const publicKey = await derivePublicKey(privateKey);\n\n return {\n publicKey,\n encrypted,\n path: keyPath,\n createdAt: Date.now(), // TODO: get actual file creation time\n };\n }\n\n /**\n * Load and decrypt a private key (for internal use only)\n * This should NEVER be exposed to the LLM context\n */\n private async loadPrivateKey(type: 'fiber' | 'ckb'): Promise<Uint8Array> {\n const keyPath = type === 'fiber' ? this.fiberKeyPath : this.ckbKeyPath;\n const keyData = readFileSync(keyPath);\n\n if (isEncryptedKey(keyData)) {\n if (!this.config.encryptionPassword) {\n throw new Error('Key is encrypted but no password provided');\n }\n return await decryptKey(keyData, this.config.encryptionPassword);\n }\n\n // The fiber node stores keys in different formats:\n // - fiber/sk: raw 32 bytes\n // - ckb/key: hex string (64 characters)\n if (type === 'fiber') {\n return new Uint8Array(keyData);\n } else {\n const hexString = keyData.toString('utf-8').trim();\n return new Uint8Array(Buffer.from(hexString, 'hex'));\n }\n }\n\n /**\n * Export keys for use with the Fiber node process\n * Returns the password to use for FIBER_SECRET_KEY_PASSWORD env var\n */\n getNodeKeyConfig(): { password?: string } {\n return {\n password: this.config.encryptionPassword,\n };\n }\n}\n\n/**\n * Create a key manager with environment-based configuration\n */\nexport function createKeyManager(baseDir: string, options?: Partial<KeyConfig>): KeyManager {\n const password = process.env.FIBER_KEY_PASSWORD || options?.encryptionPassword;\n\n return new KeyManager({\n baseDir,\n encryptionPassword: password,\n autoGenerate: options?.autoGenerate ?? true,\n });\n}\n"],"mappings":"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","names":["platform","existsSync","mkdirSync","join","promisify","existsSync","mkdirSync","dirname","join","join","existsSync","mkdirSync","dirname","chmodSync","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiber-pay/node",
3
- "version": "0.1.0-rc.3",
3
+ "version": "0.1.0-rc.4",
4
4
  "description": "Fiber Network node binary management and process lifecycle",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,6 +29,9 @@
29
29
  "engines": {
30
30
  "node": ">=20"
31
31
  },
32
+ "dependencies": {
33
+ "@fiber-pay/sdk": "0.1.0-rc.4"
34
+ },
32
35
  "scripts": {
33
36
  "build": "tsup",
34
37
  "dev": "tsup --watch",