@fiber-pay/node 0.1.0-rc.1 → 0.1.0-rc.3
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/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -258,8 +258,20 @@ var BinaryManager = class {
|
|
|
258
258
|
await writeFile(archivePath, buffer);
|
|
259
259
|
try {
|
|
260
260
|
await execAsync(`tar -xzf "${archivePath}" -C "${tempDir}"`);
|
|
261
|
-
} catch (
|
|
262
|
-
|
|
261
|
+
} catch (primaryError) {
|
|
262
|
+
try {
|
|
263
|
+
const { gunzipSync } = await import("zlib");
|
|
264
|
+
const tarPath = `${tempDir}/archive.tar`;
|
|
265
|
+
const tarBuffer = gunzipSync(buffer);
|
|
266
|
+
await writeFile(tarPath, tarBuffer);
|
|
267
|
+
await execAsync(`tar -xf "${tarPath}" -C "${tempDir}"`);
|
|
268
|
+
} catch (fallbackError) {
|
|
269
|
+
const primaryMessage = primaryError instanceof Error ? primaryError.message : String(primaryError);
|
|
270
|
+
const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
271
|
+
throw new Error(
|
|
272
|
+
`Failed to extract tar.gz archive. Primary: ${primaryMessage}. Fallback: ${fallbackMessage}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
263
275
|
}
|
|
264
276
|
const files = await readdir(tempDir, { recursive: true });
|
|
265
277
|
const binaryFile = files.find((f) => {
|
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 (_error) {\n // Fallback: try with gunzip + tar separately\n await execAsync(`gunzip -c \"${archivePath}\" | tar -xf - -C \"${tempDir}\"`);\n }\n\n // Find the binary in extracted files\n const files = await readdir(tempDir, { recursive: true });\n const binaryFile = files.find((f) => {\n const name = String(f);\n return name.endsWith('fnn') || name.endsWith('fnn.exe');\n });\n\n if (binaryFile) {\n const sourcePath = join(tempDir, String(binaryFile));\n await rename(sourcePath, targetPath);\n } else {\n // If no fnn found, maybe the archive contains a single binary\n const extractedFiles = await readdir(tempDir);\n const possibleBinary = extractedFiles.find(\n (f) => f !== 'archive.tar.gz' && !f.startsWith('.'),\n );\n if (possibleBinary) {\n await rename(join(tempDir, possibleBinary), targetPath);\n }\n }\n\n // Cleanup temp directory\n await rm(tempDir, { recursive: true, force: true });\n }\n\n /**\n * Extract zip archive (primarily for Windows)\n */\n private async extractZip(buffer: Buffer, targetPath: string): Promise<void> {\n const { writeFile, readdir, rename, rm } = await import('node:fs/promises');\n const tempDir = `${targetPath}.extract`;\n\n if (!existsSync(tempDir)) {\n mkdirSync(tempDir, { recursive: true });\n }\n\n const archivePath = `${tempDir}/archive.zip`;\n await writeFile(archivePath, buffer);\n\n // Extract using unzip command\n const { platform } = this.getPlatformInfo();\n if (platform === 'win32') {\n await execAsync(\n `powershell -command \"Expand-Archive -Path '${archivePath}' -DestinationPath '${tempDir}'\"`,\n );\n } else {\n await execAsync(`unzip -o \"${archivePath}\" -d \"${tempDir}\"`);\n }\n\n // Find and move the binary\n const files = await readdir(tempDir, { recursive: true });\n const binaryFile = files.find((f) => {\n const name = String(f);\n return name.endsWith('fnn') || name.endsWith('fnn.exe');\n });\n\n if (binaryFile) {\n await rename(join(tempDir, String(binaryFile)), targetPath);\n }\n\n await rm(tempDir, { recursive: true, force: true });\n }\n\n /**\n * Remove the installed binary\n */\n async uninstall(): Promise<void> {\n const binaryPath = this.getBinaryPath();\n if (existsSync(binaryPath)) {\n unlinkSync(binaryPath);\n }\n }\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Download the Fiber binary to the default location\n */\nexport async function downloadFiberBinary(options: DownloadOptions = {}): Promise<BinaryInfo> {\n const manager = new BinaryManager(options.installDir);\n return manager.download(options);\n}\n\n/**\n * Get information about the installed binary\n */\nexport async function getFiberBinaryInfo(installDir?: string): Promise<BinaryInfo> {\n const manager = new BinaryManager(installDir);\n return manager.getBinaryInfo();\n}\n\n/**\n * Ensure the Fiber binary is available, downloading if necessary.\n * If the binary exists but its version does not match the requested\n * (or default) version, it will be re-downloaded.\n */\nexport async function ensureFiberBinary(options: DownloadOptions = {}): Promise<string> {\n const manager = new BinaryManager(options.installDir);\n const info = await manager.getBinaryInfo();\n let downloadOptions = options;\n\n if (info.ready) {\n const wantedTag = manager.normalizeTag(options.version || DEFAULT_FIBER_VERSION);\n const wantedVersion = wantedTag.startsWith('v') ? wantedTag.slice(1) : wantedTag;\n if (info.version === wantedVersion) {\n return info.path;\n }\n // Version mismatch — force re-download.\n downloadOptions = { ...options, force: true };\n }\n\n const downloaded = await manager.download(downloadOptions);\n return downloaded.path;\n}\n\n/**\n * Get the default binary path\n */\nexport function getDefaultBinaryPath(): string {\n const manager = new BinaryManager();\n return manager.getBinaryPath();\n}\n","/**\n * Process Manager\n * Manages the lifecycle of the Fiber Network Node (fnn) binary\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport * as yaml from './yaml.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface FiberNodeConfig {\n /** Path to the fnn binary */\n binaryPath: string;\n /** Base directory for data storage */\n dataDir: string;\n /** Path to the config file (optional - will use built-in testnet config if not provided) */\n configFilePath?: string;\n /** Fiber P2P listening address */\n fiberListeningAddr?: string;\n /** Fiber node name */\n nodeName?: string;\n /** Bootstrap node addresses */\n bootnodeAddrs?: string[];\n /** CKB RPC URL */\n ckbRpcUrl?: string;\n /** RPC listening address */\n rpcListeningAddr?: string;\n /** Chain configuration (mainnet, testnet, or file path) */\n chain?: 'mainnet' | 'testnet' | string;\n /** Key encryption password */\n keyPassword?: string;\n /** Log level */\n logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error';\n /** UDT whitelist */\n udtWhitelist?: Array<{\n name: string;\n script: {\n code_hash: string;\n hash_type: 'type' | 'data' | 'data1' | 'data2';\n args: string;\n };\n }>;\n}\n\nexport interface ProcessManagerEvents {\n started: () => void;\n stopped: (code: number | null, signal: NodeJS.Signals | null) => void;\n error: (error: Error) => void;\n stdout: (data: string) => void;\n stderr: (data: string) => void;\n ready: () => void;\n}\n\nexport type ProcessState = 'stopped' | 'starting' | 'running' | 'stopping';\n\nconst DEFAULT_TESTNET_FIBER_CONFIG = {\n listening_addr: '/ip4/0.0.0.0/tcp/8228',\n bootnode_addrs: [\n '/ip4/54.179.226.154/tcp/8228/p2p/Qmes1EBD4yNo9Ywkfe6eRw9tG1nVNGLDmMud1xJMsoYFKy',\n '/ip4/54.179.226.154/tcp/18228/p2p/QmdyQWjPtbK4NWWsvy8s69NGJaQULwgeQDT5ZpNDrTNaeV',\n ],\n announce_listening_addr: true,\n chain: 'testnet',\n scripts: [\n {\n name: 'FundingLock',\n script: {\n code_hash: '0x6c67887fe201ee0c7853f1682c0b77c0e6214044c156c7558269390a8afa6d7c',\n hash_type: 'type',\n args: '0x',\n },\n cell_deps: [\n {\n type_id: {\n code_hash: '0x00000000000000000000000000000000000000000000000000545950455f4944',\n hash_type: 'type',\n args: '0x3cb7c0304fe53f75bb5727e2484d0beae4bd99d979813c6fc97c3cca569f10f6',\n },\n },\n {\n cell_dep: {\n out_point: {\n tx_hash: '0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7',\n index: '0x0',\n },\n dep_type: 'code',\n },\n },\n ],\n },\n {\n name: 'CommitmentLock',\n script: {\n code_hash: '0x740dee83f87c6f309824d8fd3fbdd3c8380ee6fc9acc90b1a748438afcdf81d8',\n hash_type: 'type',\n args: '0x',\n },\n cell_deps: [\n {\n type_id: {\n code_hash: '0x00000000000000000000000000000000000000000000000000545950455f4944',\n hash_type: 'type',\n args: '0xf7e458887495cf70dd30d1543cad47dc1dfe9d874177bf19291e4db478d5751b',\n },\n },\n {\n cell_dep: {\n out_point: {\n tx_hash: '0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7',\n index: '0x0',\n },\n dep_type: 'code',\n },\n },\n ],\n },\n ],\n} as const;\n\n// =============================================================================\n// Process Manager\n// =============================================================================\n\nexport class ProcessManager extends EventEmitter {\n private config: FiberNodeConfig;\n private process: ChildProcess | null = null;\n private state: ProcessState = 'stopped';\n private configPath: string;\n private stdoutBuffer: string[] = [];\n private stderrBuffer: string[] = [];\n private maxBufferSize = 1000;\n\n constructor(config: FiberNodeConfig) {\n super();\n this.config = config;\n this.configPath = join(config.dataDir, 'config.yml');\n }\n\n /**\n * Get current process state\n */\n getState(): ProcessState {\n return this.state;\n }\n\n /**\n * Check if the process is running\n */\n isRunning(): boolean {\n return this.state === 'running' || this.state === 'starting';\n }\n\n /**\n * Start the Fiber node\n */\n async start(): Promise<void> {\n if (this.isRunning()) {\n throw new Error('Node is already running');\n }\n\n this.state = 'starting';\n\n // Ensure data directory exists\n if (!existsSync(this.config.dataDir)) {\n mkdirSync(this.config.dataDir, { recursive: true });\n }\n\n // Copy or generate config file\n await this.ensureConfigFile();\n\n // Build environment variables\n const env: Record<string, string> = {\n ...process.env,\n RUST_LOG: this.config.logLevel || 'info',\n };\n\n // FIBER_SECRET_KEY_PASSWORD is always required by the fiber node\n // Use the provided password or generate a default one\n env.FIBER_SECRET_KEY_PASSWORD = this.config.keyPassword || 'fiber-pay-default-key';\n\n // Spawn the process\n const args = ['-c', this.configPath, '-d', this.config.dataDir];\n\n this.process = spawn(this.config.binaryPath, args, {\n env,\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: false,\n });\n\n // Handle stdout\n this.process.stdout?.on('data', (data: Buffer) => {\n const text = data.toString();\n this.stdoutBuffer.push(text);\n if (this.stdoutBuffer.length > this.maxBufferSize) {\n this.stdoutBuffer.shift();\n }\n this.emit('stdout', text);\n\n // Check for ready signal\n if (text.includes('RPC server started') || text.includes('listening on')) {\n if (this.state === 'starting') {\n this.state = 'running';\n this.emit('ready');\n }\n }\n });\n\n // Handle stderr\n this.process.stderr?.on('data', (data: Buffer) => {\n const text = data.toString();\n this.stderrBuffer.push(text);\n if (this.stderrBuffer.length > this.maxBufferSize) {\n this.stderrBuffer.shift();\n }\n this.emit('stderr', text);\n });\n\n // Handle process exit\n this.process.on('exit', (code, signal) => {\n this.state = 'stopped';\n this.process = null;\n this.emit('stopped', code, signal);\n });\n\n // Handle process error\n this.process.on('error', (error) => {\n this.state = 'stopped';\n this.process = null;\n this.emit('error', error);\n });\n\n this.emit('started');\n\n // Wait a bit for the process to initialize\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n // If process died immediately, throw error\n // State may have changed due to async event handlers\n if ((this.state as ProcessState) === 'stopped') {\n throw new Error('Process exited immediately. Check logs.');\n }\n }\n\n /**\n * Stop the Fiber node\n */\n async stop(timeout = 10000): Promise<void> {\n if (!this.process || this.state === 'stopped') {\n return;\n }\n\n this.state = 'stopping';\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n // Force kill if graceful shutdown fails\n this.process?.kill('SIGKILL');\n }, timeout);\n\n this.once('stopped', () => {\n clearTimeout(timer);\n resolve();\n });\n\n this.once('error', (error) => {\n clearTimeout(timer);\n reject(error);\n });\n\n // Send SIGTERM for graceful shutdown\n this.process?.kill('SIGTERM');\n });\n }\n\n /**\n * Restart the Fiber node\n */\n async restart(): Promise<void> {\n await this.stop();\n await this.start();\n }\n\n /**\n * Get recent stdout output\n */\n getStdout(lines?: number): string[] {\n if (lines) {\n return this.stdoutBuffer.slice(-lines);\n }\n return [...this.stdoutBuffer];\n }\n\n /**\n * Get recent stderr output\n */\n getStderr(lines?: number): string[] {\n if (lines) {\n return this.stderrBuffer.slice(-lines);\n }\n return [...this.stderrBuffer];\n }\n\n /**\n * Get the RPC URL for this node\n */\n getRpcUrl(): string {\n const addr = this.config.rpcListeningAddr || '127.0.0.1:8227';\n return `http://${addr}`;\n }\n\n /**\n * Wait for the node to be ready\n */\n waitForReady(timeout = 60000): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.state === 'running') {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n this.off('ready', onReady);\n this.off('stopped', onStopped);\n reject(new Error('Timeout waiting for node to be ready'));\n }, timeout);\n\n const onReady = () => {\n clearTimeout(timer);\n this.off('stopped', onStopped);\n resolve();\n };\n\n const onStopped = () => {\n clearTimeout(timer);\n this.off('ready', onReady);\n reject(new Error('Node stopped while waiting for ready'));\n };\n\n this.once('ready', onReady);\n this.once('stopped', onStopped);\n });\n }\n\n /**\n * Ensure the config file exists (copy from source or generate)\n */\n private async ensureConfigFile(): Promise<void> {\n const configDir = dirname(this.configPath);\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n // If a config file path is provided, copy it\n if (this.config.configFilePath && existsSync(this.config.configFilePath)) {\n // Copy the config file to the data directory\n const sourceContent = readFileSync(this.config.configFilePath, 'utf-8');\n writeFileSync(this.configPath, sourceContent);\n return;\n }\n\n // Otherwise, generate a basic config file\n this.generateConfigFile();\n }\n\n /**\n * Generate the config file (fallback when no source config provided)\n */\n private generateConfigFile(): void {\n const chain = this.config.chain || 'testnet';\n const useDefaultTestnetConfig = chain === 'testnet';\n const config: Record<string, unknown> = {\n fiber: useDefaultTestnetConfig\n ? { ...DEFAULT_TESTNET_FIBER_CONFIG }\n : {\n listening_addr: this.config.fiberListeningAddr || '/ip4/127.0.0.1/tcp/8228',\n announce_listening_addr: true,\n chain,\n },\n rpc: {\n listening_addr: this.config.rpcListeningAddr || '127.0.0.1:8227',\n },\n ckb: {\n rpc_url: this.config.ckbRpcUrl || 'https://testnet.ckbapp.dev/',\n },\n services: ['fiber', 'rpc', 'ckb'],\n };\n\n if (this.config.nodeName) {\n (config.fiber as Record<string, unknown>).announced_node_name = this.config.nodeName;\n }\n\n if (this.config.bootnodeAddrs?.length) {\n (config.fiber as Record<string, unknown>).bootnode_addrs = this.config.bootnodeAddrs;\n }\n\n if (this.config.udtWhitelist?.length) {\n (config.ckb as Record<string, unknown>).udt_whitelist = this.config.udtWhitelist;\n }\n\n const configDir = dirname(this.configPath);\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(this.configPath, yaml.stringify(config));\n }\n}\n","/**\n * Simple YAML serializer\n * Minimal implementation for config file generation\n */\n\nexport function stringify(obj: unknown, indent = 0): string {\n const spaces = ' '.repeat(indent);\n\n if (obj === null || obj === undefined) {\n return 'null';\n }\n\n if (typeof obj === 'string') {\n // Quote strings that need it\n if (\n obj.includes(':') ||\n obj.includes('#') ||\n obj.includes('\\n') ||\n obj.startsWith(' ') ||\n obj.endsWith(' ') ||\n obj === '' ||\n obj === 'true' ||\n obj === 'false' ||\n !Number.isNaN(Number(obj))\n ) {\n return JSON.stringify(obj);\n }\n return obj;\n }\n\n if (typeof obj === 'number' || typeof obj === 'boolean') {\n return String(obj);\n }\n\n if (Array.isArray(obj)) {\n if (obj.length === 0) {\n return '[]';\n }\n return obj\n .map((item) => {\n const value = stringify(item, indent + 1);\n if (typeof item === 'object' && item !== null && !Array.isArray(item)) {\n // Multi-line object in array\n const lines = value.split('\\n');\n return `${spaces}- ${lines[0]}\\n${lines\n .slice(1)\n .map((l) => `${spaces} ${l}`)\n .join('\\n')}`;\n }\n return `${spaces}- ${value}`;\n })\n .join('\\n');\n }\n\n if (typeof obj === 'object') {\n const entries = Object.entries(obj as Record<string, unknown>);\n if (entries.length === 0) {\n return '{}';\n }\n return entries\n .map(([key, value]) => {\n const valueStr = stringify(value, indent + 1);\n if (\n typeof value === 'object' &&\n value !== null &&\n !(Array.isArray(value) && value.length === 0) &&\n !(typeof value === 'object' && Object.keys(value).length === 0)\n ) {\n return `${spaces}${key}:\\n${valueStr}`;\n }\n return `${spaces}${key}: ${valueStr}`;\n })\n .join('\\n');\n }\n\n return String(obj);\n}\n\nexport function parse(yaml: string): unknown {\n // Basic YAML parsing - for production use a proper library\n const lines = yaml.split('\\n');\n const result: Record<string, unknown> = {};\n const stack: Array<{ indent: number; obj: Record<string, unknown>; key?: string }> = [\n { indent: -1, obj: result },\n ];\n\n for (const line of lines) {\n // Skip empty lines and comments\n if (!line.trim() || line.trim().startsWith('#')) {\n continue;\n }\n\n const indent = line.search(/\\S/);\n const content = line.trim();\n\n // Handle key: value\n const colonIndex = content.indexOf(':');\n if (colonIndex > 0) {\n const key = content.slice(0, colonIndex).trim();\n const value = content.slice(colonIndex + 1).trim();\n\n // Pop stack to correct level\n while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {\n stack.pop();\n }\n\n const parent = stack[stack.length - 1].obj;\n\n if (value === '' || value === '|' || value === '>') {\n // Nested object\n const newObj: Record<string, unknown> = {};\n parent[key] = newObj;\n stack.push({ indent, obj: newObj, key });\n } else {\n // Primitive value\n parent[key] = parseValue(value);\n }\n }\n }\n\n return result;\n}\n\nfunction parseValue(value: string): unknown {\n // Remove quotes\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n return value.slice(1, -1);\n }\n\n // Boolean\n if (value === 'true') return true;\n if (value === 'false') return false;\n\n // Null\n if (value === 'null' || value === '~') return null;\n\n // Number\n const num = Number(value);\n if (!Number.isNaN(num)) return num;\n\n // Array (inline)\n if (value.startsWith('[') && value.endsWith(']')) {\n return JSON.parse(value);\n }\n\n return value;\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","join","join","existsSync","mkdirSync"]}
|
|
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"]}
|