hbeam 0.1.8-alpha.2 → 0.1.8-alpha.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/cli.mjs CHANGED
@@ -1831,6 +1831,8 @@ const DEFAULT_FORWARD_HOST = "127.0.0.1";
1831
1831
  const MIN_PORT = 1;
1832
1832
  const MAX_PORT = 65535;
1833
1833
  const RANDOM_PORT = 0;
1834
+ const FIRST_ACTIVE_CONNECTION = 1;
1835
+ const NO_ACTIVE_CONNECTIONS = 0;
1834
1836
  /**
1835
1837
  * Determine whether a tunnel error is a non-fatal per-connection close.
1836
1838
  *
@@ -1903,6 +1905,18 @@ function registerShutdown(tunnel) {
1903
1905
  logError(error.message);
1904
1906
  shutdown(EXIT_FAILURE$3);
1905
1907
  });
1908
+ /**
1909
+ * Keep bind runtime UX aligned with other commands.
1910
+ * Only log edge transitions to avoid noisy per-connection spam.
1911
+ */
1912
+ tunnel.on("connect", (activeConnections) => {
1913
+ if (shuttingDown || activeConnections !== FIRST_ACTIVE_CONNECTION) return;
1914
+ log(bold$1("CONNECTED"));
1915
+ });
1916
+ tunnel.on("disconnect", (activeConnections) => {
1917
+ if (shuttingDown || activeConnections !== NO_ACTIVE_CONNECTIONS) return;
1918
+ log(dim$1("PEER DISCONNECTED"));
1919
+ });
1906
1920
  }
1907
1921
  /**
1908
1922
  * Execute reverse-proxy bind mode (P2P -> local TCP service).
@@ -2488,7 +2502,7 @@ if (argv.help) {
2488
2502
  process.exit(EXIT_SUCCESS);
2489
2503
  }
2490
2504
  if (argv.version) {
2491
- write((await import("./package-B-S49qcw.mjs")).version ?? "0.0.0", NO_INDENT);
2505
+ write((await import("./package-uWr_S1qI.mjs")).version ?? "0.0.0", NO_INDENT);
2492
2506
  process.exit(EXIT_SUCCESS);
2493
2507
  }
2494
2508
  const [firstArg, ...restArgs] = argv._;
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["EXIT_SUCCESS","MODULUS_EVEN","REMAINDER_ZERO","PUBLIC_KEY_BYTES","KEY_SEED_BYTES","isHex","FIRST_INDEX","EXIT_FAILURE","dim","dim","FIRST_INDEX","NEXT_OFFSET","dim","bold","red","PUBLIC_KEY_BYTES","KEY_SEED_BYTES","PUBLIC_KEY_BYTES","DEFAULT_FORWARD_HOST","EXIT_FAILURE","EXIT_SUCCESS","showUsageError","dim","EXIT_FAILURE","dim","EXIT_SUCCESS","EXIT_FAILURE","dim","bold","dim","red","EXIT_SUCCESS","dim","bold","bold","dim"],"sources":["../src/lib/clipboard.ts","../src/lib/config.ts","../src/lib/encoding.ts","../src/lib/identity.ts","../src/lib/log.ts","../src/lib/file-protocol.ts","../src/lib/lifecycle.ts","../src/lib/prompt.ts","../src/lib/pulse.ts","../src/lib/session.ts","../src/lib/addressbook.ts","../src/lib/dht.ts","../src/beam.ts","../src/lib/tunnel.ts","../src/commands/bind.ts","../src/commands/connect.ts","../src/commands/peers.ts","../src/commands/serve.ts","../src/commands/whoami.ts","../src/cli.ts"],"sourcesContent":["/**\n * Cross-platform clipboard integration for terminal workflows.\n *\n * Tries common platform-native clipboard commands in priority order.\n *\n * @module\n */\nimport { spawnSync } from 'node:child_process'\n\ntype ClipboardCommand = Readonly<{\n\targs: readonly string[]\n\tcommand: string\n}>\n\nconst EXIT_SUCCESS = 0\nconst EMPTY_ARGS: readonly string[] = []\n\nconst DARWIN_COMMANDS: readonly ClipboardCommand[] = [{ args: EMPTY_ARGS, command: 'pbcopy' }]\nconst WINDOWS_COMMANDS: readonly ClipboardCommand[] = [{ args: EMPTY_ARGS, command: 'clip' }]\n\nconst LINUX_COMMANDS: readonly ClipboardCommand[] = [\n\t{ args: EMPTY_ARGS, command: 'wl-copy' },\n\t{ args: ['-selection', 'clipboard'], command: 'xclip' },\n\t{ args: ['--clipboard', '--input'], command: 'xsel' },\n]\n\n/**\n * Resolve the clipboard command list for the current platform.\n *\n * @returns Ordered command list to try for clipboard writes.\n */\nfunction getClipboardCommands(): readonly ClipboardCommand[] {\n\tif (process.platform === 'darwin') {\n\t\treturn DARWIN_COMMANDS\n\t}\n\tif (process.platform === 'win32') {\n\t\treturn WINDOWS_COMMANDS\n\t}\n\treturn LINUX_COMMANDS\n}\n\n/**\n * Attempt to write text to clipboard with a specific command.\n *\n * @param text - Text to place in clipboard.\n * @param item - Clipboard command and argument tuple.\n * @returns True when command succeeds.\n */\nfunction tryClipboardCommand(text: string, item: ClipboardCommand): boolean {\n\tconst result = spawnSync(item.command, item.args, {\n\t\tinput: text,\n\t\tstdio: ['pipe', 'ignore', 'ignore'],\n\t})\n\treturn !result.error && result.status === EXIT_SUCCESS\n}\n\n/**\n * Copy text to the system clipboard using common platform commands.\n *\n * @param text - Text to copy.\n * @returns True when at least one command succeeds.\n */\nexport function copyToClipboard(text: string): boolean {\n\tfor (const item of getClipboardCommands()) {\n\t\tif (tryClipboardCommand(text, item)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n","/**\n * Config-directory utilities for hbeam local state.\n *\n * Provides path resolution and JSON read/write helpers under `~/.config/hbeam`\n * (or an override directory via environment variable).\n *\n * @module\n */\nimport { mkdir, readFile, writeFile, chmod } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\n\nconst CONFIG_ROOT_DIR = '.config'\nconst APP_CONFIG_DIR = 'hbeam'\nconst CONFIG_DIR_ENV = 'HBEAM_CONFIG_DIR'\n\nconst DIR_MODE = 0o700\nconst FILE_MODE_SECURE = 0o600\n\n/**\n * Resolve the absolute path to the hbeam config directory.\n *\n * @returns Config directory path, honoring `HBEAM_CONFIG_DIR` when set.\n */\nexport function getConfigDir(): string {\n\treturn process.env[CONFIG_DIR_ENV] ?? join(homedir(), CONFIG_ROOT_DIR, APP_CONFIG_DIR)\n}\n\n/**\n * Resolve a filename under the hbeam config directory.\n *\n * @param filename - Relative config filename.\n * @returns Absolute path to the config file.\n */\nfunction resolveConfigPath(filename: string): string {\n\treturn join(getConfigDir(), filename)\n}\n\n/**\n * Ensure the hbeam config directory exists with private directory permissions.\n *\n * @returns Promise that resolves once the directory exists.\n */\nexport async function ensureConfigDir(): Promise<void> {\n\tawait mkdir(getConfigDir(), { mode: DIR_MODE, recursive: true })\n}\n\n/**\n * Read and parse a JSON file from the hbeam config directory.\n *\n * @param filename - Relative config filename.\n * @returns Parsed JSON object, or `undefined` when file does not exist.\n */\nexport async function readJsonFile<T>(filename: string): Promise<T | undefined> {\n\tconst path = resolveConfigPath(filename)\n\ttry {\n\t\tconst raw = await readFile(path, 'utf8')\n\t\treturn JSON.parse(raw) as T\n\t} catch (error) {\n\t\tconst err = error as NodeJS.ErrnoException\n\t\tif (err.code === 'ENOENT') {\n\t\t\treturn undefined\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/**\n * Write a JSON file into the hbeam config directory.\n *\n * Set `secure` for files containing private key material.\n *\n * @param filename - Relative config filename.\n * @param data - Serializable payload.\n * @param options - Write options.\n * @returns Promise that resolves when file write is complete.\n */\nexport async function writeJsonFile(\n\tfilename: string,\n\tdata: unknown,\n\toptions?: { secure?: boolean },\n): Promise<void> {\n\tconst path = resolveConfigPath(filename)\n\tawait ensureConfigDir()\n\tawait mkdir(dirname(path), { mode: DIR_MODE, recursive: true })\n\tawait writeFile(path, `${JSON.stringify(data, null, '\\t')}\\n`, 'utf8')\n\tif (options?.secure) {\n\t\tawait chmod(path, FILE_MODE_SECURE)\n\t}\n}\n","/**\n * Byte/string encoding helpers for hbeam key material.\n *\n * Provides base32 conversion helpers and secure random byte generation.\n *\n * @module\n */\nimport * as b4a from 'b4a'\nimport b32 from 'hi-base32'\nimport sodium from 'sodium-universal'\n\n/**\n * Encode a buffer as a lowercase base32 string without padding.\n *\n * @param buf - Raw bytes to encode.\n * @returns Base32-encoded string without trailing `=` padding.\n */\nexport function toBase32(buf: Buffer): string {\n\treturn b32.encode(buf).replace(/=/g, '').toLowerCase()\n}\n\n/**\n * Decode a base32 string back into a raw buffer.\n *\n * @param str - Base32-encoded input string.\n * @returns Decoded raw bytes.\n */\nexport function fromBase32(str: string): Buffer {\n\treturn b4a.from(b32.decode.asBytes(str.toUpperCase()))\n}\n\n/**\n * Generate cryptographically secure random bytes.\n *\n * @param length - Number of bytes to generate.\n * @returns Random byte buffer with the requested length.\n */\nexport function randomBytes(length: number): Buffer {\n\tconst buffer = b4a.alloc(length)\n\tsodium.randombytes_buf(buffer)\n\treturn buffer\n}\n","/**\n * Persistent identity keypair management for hbeam.\n *\n * Loads, validates, creates, and serializes identity key material stored\n * under the local config directory.\n *\n * @module\n */\nimport DHT from 'hyperdht'\n\nimport { readJsonFile, writeJsonFile } from './config.ts'\nimport { randomBytes } from './encoding.ts'\n\nimport type { Identity, KeyPair } from '../types.ts'\n\nconst IDENTITY_FILE = 'identity.json'\n\nconst MODULUS_EVEN = 2\nconst REMAINDER_ZERO = 0\nconst PUBLIC_KEY_BYTES = 32\nconst KEY_SEED_BYTES = 32\nconst SECRET_KEY_BYTES = 64\n\n/**\n * Convert a key buffer to a hex string.\n *\n * @param buffer - Key bytes.\n * @returns Lowercase hex representation.\n */\nfunction asHex(buffer: Buffer): string {\n\treturn buffer.toString('hex')\n}\n\n/**\n * Parse a hex string into a key buffer.\n *\n * @param hex - Hex-encoded bytes.\n * @returns Buffer decoded from hex.\n */\nfunction fromHex(hex: string): Buffer {\n\treturn Buffer.from(hex, 'hex')\n}\n\n/**\n * Check whether a string is valid even-length hex.\n *\n * @param value - Candidate hex string.\n * @returns True when string is valid hex.\n */\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\n\n/**\n * Validate and parse serialized identity data.\n *\n * @param value - Serialized identity object from disk.\n * @returns Keypair parsed from identity object.\n */\nfunction parseIdentity(value: Identity): KeyPair {\n\tif (!isHex(value.publicKey) || !isHex(value.secretKey)) {\n\t\tthrow new Error('Invalid identity file: keys must be hex-encoded')\n\t}\n\n\tconst publicKey = fromHex(value.publicKey)\n\tconst secretKey = fromHex(value.secretKey)\n\n\tif (publicKey.length !== PUBLIC_KEY_BYTES || secretKey.length !== SECRET_KEY_BYTES) {\n\t\tthrow new Error('Invalid identity file: unexpected key lengths')\n\t}\n\n\treturn { publicKey, secretKey }\n}\n\n/**\n * Serialize a keypair for disk storage.\n *\n * @param keyPair - Keypair to serialize.\n * @returns Identity object with hex-encoded keys.\n */\nfunction serializeIdentity(keyPair: KeyPair): Identity {\n\treturn {\n\t\tpublicKey: asHex(keyPair.publicKey),\n\t\tsecretKey: asHex(keyPair.secretKey),\n\t}\n}\n\n/**\n * Create a brand new identity keypair.\n *\n * @returns Freshly generated keypair.\n */\nfunction createIdentity(): KeyPair {\n\treturn DHT.keyPair(randomBytes(KEY_SEED_BYTES))\n}\n\n/**\n * Load a persisted identity if present; otherwise create and persist one.\n *\n * @returns Keypair and metadata indicating whether it was newly created.\n */\nexport async function loadOrCreateIdentityWithMeta(): Promise<{\n\tcreated: boolean\n\tkeyPair: KeyPair\n}> {\n\tconst existing = await readJsonFile<Identity>(IDENTITY_FILE)\n\tif (existing) {\n\t\treturn { created: false, keyPair: parseIdentity(existing) }\n\t}\n\n\tconst keyPair = createIdentity()\n\tawait writeJsonFile(IDENTITY_FILE, serializeIdentity(keyPair), { secure: true })\n\treturn { created: true, keyPair }\n}\n\n/**\n * Load or create the local hbeam identity keypair.\n *\n * @returns Local identity keypair.\n */\nexport async function loadOrCreateIdentity(): Promise<KeyPair> {\n\tconst { keyPair } = await loadOrCreateIdentityWithMeta()\n\treturn keyPair\n}\n\n/**\n * Return the public key hex string for the local identity.\n *\n * @returns Hex-encoded public key.\n */\nexport async function getPublicKeyHex(): Promise<string> {\n\tconst keyPair = await loadOrCreateIdentity()\n\treturn asHex(keyPair.publicKey)\n}\n","/**\n * Terminal output primitives and animated spinner utilities.\n *\n * Centralizes formatted stderr logging and in-place spinner rendering.\n *\n * @module\n */\nimport { dim, red, yellow } from 'colorette'\n\nexport { bold, cyan, dim, gray, green, italic, red, yellow } from 'colorette'\n\nconst SEPARATOR_WIDTH = 36\nconst CLEAR_LINE = '\\r\\u001B[2K'\n// eslint-disable-next-line no-control-regex\nconst ANSI_ESCAPE = /\\u001B\\[[0-9;]*m/g\nconst NO_OFFSET = 0\nconst DEFAULT_COLUMNS = 80\nconst MIN_LINES = 1\n\nexport const INDENT = ' '\nexport const SEPARATOR = dim('╌'.repeat(SEPARATOR_WIDTH))\n\n/**\n * Write a line to stderr at the standard indent level.\n *\n * @param message - Text to print.\n * @param indent - Prefix to prepend before the message.\n * @returns Nothing.\n */\nexport function write(message: string, indent: string = INDENT): void {\n\tprocess.stderr.write(`${indent}${message}\\n`)\n}\n\n/**\n * Write to stderr at the standard indent level without a trailing newline.\n *\n * Useful for partial lines that will be completed later (e.g. status updates).\n *\n * @param message - Text to print.\n * @returns Nothing.\n */\nexport function writeInline(message: string): void {\n\tprocess.stderr.write(`${INDENT}${message}`)\n}\n\n/**\n * Complete a partial line previously started by {@link writeInline}.\n *\n * @param message - Suffix text to append (a trailing newline is added).\n * @returns Nothing.\n */\nexport function endInline(message: string): void {\n\tprocess.stderr.write(`${message}\\n`)\n}\n\n/**\n * Write a blank line to stderr.\n *\n * @returns Nothing.\n */\nexport function blank(): void {\n\tprocess.stderr.write('\\n')\n}\n\n/**\n * Write a pre-formatted block (multiple lines) to stderr.\n *\n * @param lines - Lines to print.\n * @returns Nothing.\n */\nexport function writeBlock(lines: string[]): void {\n\tfor (const line of lines) {\n\t\tprocess.stderr.write(`${INDENT}${line}\\n`)\n\t}\n}\n\n/**\n * Write a status message to stderr at the standard indent level.\n *\n * @param message - Status text.\n * @returns Nothing.\n */\nexport function log(message: string): void {\n\twrite(message)\n}\n\n/**\n * Write an error message to stderr at the standard indent level.\n *\n * @param message - Error text.\n * @returns Nothing.\n */\nexport function logError(message: string): void {\n\tprocess.stderr.write(`${INDENT}${red('ERROR')} ${message}\\n`)\n}\n\n/**\n * Write a warning/notice message to stderr with a yellow prefix.\n *\n * @param message - Warning text.\n * @returns Nothing.\n */\nexport function logWarn(message: string): void {\n\tprocess.stderr.write(`${yellow('!')} ${message}\\n`)\n}\n\n/**\n * Clear the current terminal line, falling back to newline on non-TTY.\n *\n * @returns Nothing.\n */\nexport function clearLine(): void {\n\tif (process.stderr.isTTY) {\n\t\tprocess.stderr.write(CLEAR_LINE)\n\t} else {\n\t\tprocess.stderr.write('\\n')\n\t}\n}\n\n// -- Spinner ----------------------------------------------------------------\n\n/**\n * Build ANSI sequence to move cursor up N lines.\n *\n * @param n - Number of lines.\n * @returns ANSI escape sequence.\n */\nfunction cursorUp(n: number): string {\n\treturn `\\u001B[${n}A`\n}\n\n/**\n * Build ANSI sequence to move cursor down N lines.\n *\n * @param n - Number of lines.\n * @returns ANSI escape sequence.\n */\nfunction cursorDown(n: number): string {\n\treturn `\\u001B[${n}B`\n}\n\n/**\n * Count visual terminal lines an indented message occupies, accounting\n * for ANSI escape codes and terminal width. Falls back to 1 on non-TTY.\n *\n * @param message - Possibly ANSI-styled text (without indent prefix).\n * @returns Number of visual lines.\n */\nfunction visualLines(message: string): number {\n\tconst columns = process.stderr.columns || DEFAULT_COLUMNS\n\tconst width = INDENT.length + message.replace(ANSI_ESCAPE, '').length\n\treturn Math.max(MIN_LINES, Math.ceil(width / columns))\n}\n\n/** Handle for a line that animates in-place while content prints below. */\nexport interface Spinner {\n\t/** Write a blank line below the spinner and track the cursor offset. */\n\tblank(): void\n\t/** Render the first frame and begin the animation loop. */\n\tstart(): void\n\t/** Stop the animation loop. */\n\tstop(): void\n\t/** Write an indented line below the spinner and track the cursor offset. */\n\twrite(message: string): void\n}\n\n/**\n * Animate a single line in-place while content continues to print below it.\n *\n * @param frames - Spinner frame strings.\n * @param intervalMs - Frame interval in milliseconds.\n * @returns Spinner controller for writing/stopping.\n */\nexport function createSpinner(frames: readonly string[], intervalMs: number): Spinner {\n\tlet offset = NO_OFFSET\n\tlet frameIndex = NO_OFFSET\n\tlet timer: ReturnType<typeof globalThis.setInterval> | undefined = undefined\n\n\t/**\n\t * Render the current frame in-place at the spinner cursor location.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction render(): void {\n\t\tif (offset > NO_OFFSET) {\n\t\t\tprocess.stderr.write(cursorUp(offset))\n\t\t}\n\t\tprocess.stderr.write(`${CLEAR_LINE}${INDENT}${frames[frameIndex]}`)\n\t\tif (offset > NO_OFFSET) {\n\t\t\tprocess.stderr.write(`${cursorDown(offset)}\\r`)\n\t\t}\n\t\tframeIndex++\n\t\tif (frameIndex >= frames.length) {\n\t\t\tframeIndex = NO_OFFSET\n\t\t}\n\t}\n\n\treturn {\n\t\tblank(): void {\n\t\t\tblank()\n\t\t\toffset++\n\t\t},\n\t\tstart(): void {\n\t\t\trender()\n\t\t\tprocess.stderr.write('\\n')\n\t\t\toffset++\n\t\t\ttimer = globalThis.setInterval(render, intervalMs)\n\t\t},\n\t\tstop(): void {\n\t\t\tif (timer) {\n\t\t\t\tglobalThis.clearInterval(timer)\n\t\t\t\ttimer = undefined\n\t\t\t}\n\t\t},\n\t\twrite(message: string): void {\n\t\t\twrite(message)\n\t\t\toffset += visualLines(message)\n\t\t},\n\t}\n}\n","/**\n * Line-delimited control-frame protocol for single-file transfers.\n *\n * Defines and parses file header frames and completion acknowledgement frames\n * exchanged between sender and receiver during `hbeam serve` sessions.\n *\n * @module\n */\nconst FILE_TYPE = 'file'\nconst FILE_COMPLETE_TYPE = 'file-complete'\nconst NEWLINE = '\\n'\nconst BYTES_PER_KIB = 1024\nconst UNIT_PRECISION = 1\nconst FIRST_INDEX = 0\nconst MIN_SIZE = 0\nconst LAST_INDEX_OFFSET = 1\nconst UNIT_LABELS = ['B', 'KB', 'MB', 'GB', 'TB'] as const\n\nexport interface FileHeader {\n\tname: string\n\tsize: number\n\ttype: typeof FILE_TYPE\n}\n\nexport interface FileCompletionAck {\n\tok: boolean\n\treason?: string\n\ttype: typeof FILE_COMPLETE_TYPE\n}\n\n/**\n * Encode a file header as a newline-delimited JSON frame.\n *\n * @param header - File header payload.\n * @returns Encoded frame bytes.\n */\nexport function encodeHeader(header: FileHeader): Buffer {\n\treturn Buffer.from(`${JSON.stringify(header)}${NEWLINE}`, 'utf8')\n}\n\n/**\n * Encode a completion acknowledgement as a newline-delimited JSON frame.\n *\n * @param ack - Completion acknowledgement payload.\n * @returns Encoded frame bytes.\n */\nexport function encodeCompletionAck(ack: FileCompletionAck): Buffer {\n\treturn Buffer.from(`${JSON.stringify(ack)}${NEWLINE}`, 'utf8')\n}\n\n/**\n * Check whether buffered bytes begin with a file-header control frame.\n *\n * @param chunk - Buffered inbound bytes.\n * @returns True when the first line appears to be a file header.\n */\nexport function isFileHeader(chunk: Buffer): boolean {\n\tconst lineEnd = findHeaderLineEnd(chunk)\n\n\t/**\n\t * Header key order is not guaranteed by JSON serializers, so detection is\n\t * intentionally permissive as long as the first line is JSON with `type:file`.\n\t */\n\tconst candidate = (lineEnd >= MIN_SIZE ? chunk.subarray(FIRST_INDEX, lineEnd) : chunk).toString(\n\t\t'utf8',\n\t)\n\tconst trimmed = candidate.trimStart()\n\n\treturn trimmed.startsWith('{') && trimmed.includes(`\"type\":\"${FILE_TYPE}\"`)\n}\n\n/**\n * Parse and validate a file-header control frame.\n *\n * @param line - Header frame bytes (without trailing newline).\n * @returns Validated file header payload.\n */\nexport function parseFileHeader(line: Buffer): FileHeader {\n\tconst parsed = JSON.parse(line.toString('utf8')) as Partial<FileHeader>\n\n\tif (parsed.type !== FILE_TYPE) {\n\t\tthrow new Error('Invalid file header type')\n\t}\n\n\tif (!parsed.name || typeof parsed.name !== 'string') {\n\t\tthrow new Error('Invalid file header name')\n\t}\n\n\tif (\n\t\ttypeof parsed.size !== 'number' ||\n\t\t!Number.isSafeInteger(parsed.size) ||\n\t\tparsed.size < MIN_SIZE\n\t) {\n\t\tthrow new Error('Invalid file header size')\n\t}\n\n\treturn { name: parsed.name, size: parsed.size, type: FILE_TYPE }\n}\n\n/**\n * Parse and validate a completion-ack control frame.\n *\n * @param line - Ack frame bytes (without trailing newline).\n * @returns Parsed ack payload, or `undefined` if frame is unrelated/invalid.\n */\nexport function parseCompletionAck(line: Buffer): FileCompletionAck | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(line.toString('utf8')) as Partial<FileCompletionAck>\n\n\t\tif (parsed.type !== FILE_COMPLETE_TYPE || typeof parsed.ok !== 'boolean') {\n\t\t\treturn undefined\n\t\t}\n\n\t\tif (parsed.reason !== undefined && typeof parsed.reason !== 'string') {\n\t\t\treturn undefined\n\t\t}\n\n\t\treturn {\n\t\t\tok: parsed.ok,\n\t\t\treason: parsed.reason,\n\t\t\ttype: FILE_COMPLETE_TYPE,\n\t\t}\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Find the end offset of the first newline-delimited frame.\n *\n * @param chunk - Buffered bytes to scan.\n * @returns Index of first newline, or `-1` when no full line exists yet.\n */\nexport function findHeaderLineEnd(chunk: Buffer): number {\n\treturn chunk.indexOf(NEWLINE)\n}\n\n/**\n * Format byte size into a human-readable unit string.\n *\n * @param bytes - Size in bytes.\n * @returns Human-readable size string (for example, `4 B`, `2.4 MB`).\n */\nexport function formatFileSize(bytes: number): string {\n\tif (!Number.isFinite(bytes) || bytes < MIN_SIZE) {\n\t\treturn `0 ${UNIT_LABELS[FIRST_INDEX]}`\n\t}\n\n\tconst lastUnitIndex = UNIT_LABELS.length - LAST_INDEX_OFFSET\n\n\tlet size = bytes\n\tlet unitIndex = FIRST_INDEX\n\n\twhile (size >= BYTES_PER_KIB && unitIndex < lastUnitIndex) {\n\t\tsize /= BYTES_PER_KIB\n\t\tunitIndex++\n\t}\n\n\tif (unitIndex === FIRST_INDEX) {\n\t\treturn `${Math.round(size)} ${UNIT_LABELS[unitIndex]}`\n\t}\n\n\treturn `${size.toFixed(UNIT_PRECISION)} ${UNIT_LABELS[unitIndex]}`\n}\n","/**\n * Session lifecycle controller for graceful shutdown behavior.\n *\n * Encapsulates SIGINT handling, spinner teardown, and timed beam destruction.\n *\n * @module\n */\nimport { blank, clearLine, dim, log } from './log.ts'\n\nimport type { Beam } from '../beam.ts'\n\nconst EXIT_FAILURE = 1\nconst SHUTDOWN_TIMEOUT_MS = 2000\n\n/** Controller for graceful shutdown of a beam session. */\nexport interface Lifecycle {\n\t/** Returns true if shutdown is in progress (use as an early-return guard). */\n\tdone(): boolean\n\t/** Tear down the beam, stop the spinner, and exit after a grace period. */\n\tshutdown(): void\n}\n\n/**\n * Create a lifecycle controller that manages SIGINT handling and graceful shutdown.\n *\n * Registers a one-shot SIGINT handler on creation. All shutdown state is\n * encapsulated — callers just check `done()` and call `shutdown()`.\n *\n * @param beam - Active beam instance to destroy during shutdown.\n * @param spinner - Optional spinner handle to stop during shutdown.\n * @returns Lifecycle controller with `done` and `shutdown`.\n */\nexport function createLifecycle(beam: Beam, spinner?: { stop(): void }): Lifecycle {\n\tlet isShuttingDown = false\n\n\t/**\n\t * Start graceful teardown and register a forced-exit timeout.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction shutdown(): void {\n\t\tif (isShuttingDown) {\n\t\t\treturn\n\t\t}\n\t\tisShuttingDown = true\n\t\tspinner?.stop()\n\n\t\tlog(dim('SHUTTING DOWN'))\n\t\tblank()\n\n\t\tconst timeout = globalThis.setTimeout(() => {\n\t\t\tprocess.exit(EXIT_FAILURE)\n\t\t}, SHUTDOWN_TIMEOUT_MS)\n\n\t\tbeam.destroy()\n\t\tbeam.on('close', () => {\n\t\t\tglobalThis.clearTimeout(timeout)\n\t\t})\n\t}\n\n\tprocess.once('SIGINT', () => {\n\t\tclearLine()\n\t\tshutdown()\n\t})\n\n\treturn {\n\t\tdone(): boolean {\n\t\t\treturn isShuttingDown\n\t\t},\n\t\tshutdown,\n\t}\n}\n","/**\n * Minimal interactive prompt helpers for CLI sessions.\n *\n * Includes raw-key confirmation prompts and editable line-input prompts.\n *\n * @module\n */\nimport { createInterface } from 'node:readline/promises'\n\nimport { INDENT, dim } from './log.ts'\n\nconst YES = 'y'\nconst NO = 'n'\nconst CTRL_C = '\\u0003'\nconst ENTER = '\\r'\nconst FIRST_CHAR_INDEX = 0\nconst EMPTY_INPUT = ''\n\n/**\n * Extract the normalized first character from a stdin buffer.\n *\n * @param data - Raw stdin chunk.\n * @returns Lowercased first character, or empty string.\n */\nfunction firstChar(data: Buffer): string {\n\treturn data.toString('utf8').toLowerCase().charAt(FIRST_CHAR_INDEX)\n}\n\n/**\n * Prompt for a yes/no confirmation with `y/N` semantics.\n *\n * @param message - Prompt message body.\n * @returns True when user confirms with `y`.\n */\nexport async function confirm(message: string): Promise<boolean> {\n\tprocess.stderr.write(`${INDENT}${message} ${dim('(y/N)')} `)\n\n\tconst stdin = process.stdin\n\tif (!stdin.isTTY || typeof stdin.setRawMode !== 'function') {\n\t\tprocess.stderr.write('\\n')\n\t\treturn false\n\t}\n\n\tconst originalRaw = stdin.isRaw\n\n\treturn await new Promise<boolean>(resolve => {\n\t\t/**\n\t\t * Restore terminal state and resolve prompt result.\n\t\t *\n\t\t * @param answer - Final confirmation result.\n\t\t * @returns Nothing.\n\t\t */\n\t\tfunction cleanup(answer: boolean): void {\n\t\t\tstdin.setRawMode(Boolean(originalRaw))\n\t\t\tstdin.pause()\n\t\t\tstdin.removeListener('data', onData)\n\t\t\tprocess.stderr.write(`${answer ? YES : NO}\\n`)\n\t\t\tresolve(answer)\n\t\t}\n\n\t\t/**\n\t\t * Handle raw keypress bytes for the confirmation prompt.\n\t\t *\n\t\t * @param data - Raw stdin bytes.\n\t\t * @returns Nothing.\n\t\t */\n\t\tfunction onData(data: Buffer): void {\n\t\t\tconst key = firstChar(data)\n\t\t\tif (key === CTRL_C) {\n\t\t\t\tprocess.stderr.write('\\n')\n\t\t\t\tprocess.kill(process.pid, 'SIGINT')\n\t\t\t\tcleanup(false)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (key === YES) {\n\t\t\t\tcleanup(true)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (key === NO || key === '' || key === ENTER) {\n\t\t\t\tcleanup(false)\n\t\t\t}\n\t\t}\n\n\t\tstdin.setRawMode(true)\n\t\tstdin.resume()\n\t\tstdin.on('data', onData)\n\t})\n}\n\n/**\n * Prompt for line input with an editable pre-filled placeholder.\n *\n * @param message - Prompt message body.\n * @param placeholder - Default value shown to the user.\n * @returns User-entered value, or placeholder when input is empty/non-interactive.\n */\nexport async function input(message: string, placeholder: string): Promise<string> {\n\tif (!process.stdin.isTTY || !process.stderr.isTTY) {\n\t\treturn placeholder\n\t}\n\n\tconst rl = createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stderr,\n\t\tterminal: true,\n\t})\n\n\ttry {\n\t\tconst answerPromise = rl.question(`${INDENT}${message} `)\n\t\trl.write(placeholder)\n\t\tconst answer = await answerPromise\n\t\tconst trimmed = answer.trim()\n\n\t\treturn trimmed === EMPTY_INPUT ? placeholder : trimmed\n\t} finally {\n\t\trl.close()\n\t}\n}\n","/**\n * Spinner frame generation for the hbeam pulse animation.\n *\n * Produces styled frame sequences with brightness gradients for terminal UI.\n *\n * @module\n */\nimport { bold, dim } from 'colorette'\n\nconst INTERVAL_MS = 125\nconst PEAK_A = 5\nconst PEAK_B = 15\nconst PEAK_WRAP = 25\nconst DIST_PEAK = 0\nconst DIST_NEAR = 1\n\nconst RAW_FRAMES: readonly string[] = [\n\t' ',\n\t'· ',\n\t'·· ',\n\t'··· ',\n\t'···· ',\n\t'·····',\n\t' ····',\n\t' ···',\n\t' ··',\n\t' ·',\n\t' ',\n\t' ·',\n\t' ··',\n\t' ···',\n\t' ····',\n\t'·····',\n\t'···· ',\n\t'··· ',\n\t'·· ',\n\t'· ',\n\t' ',\n]\n\n/**\n * Generate the styled spinner frames for the HBEAM pulse animation.\n *\n * Each frame is rendered with a brightness gradient: bold at peak,\n * normal near peak, and dim everywhere else.\n *\n * @param label - The text label to prefix each frame (e.g. \"HBEAM\").\n * @returns An object with the styled `frames` array and `intervalMs` timing.\n */\nexport function createPulseFrames(label: string): { frames: string[]; intervalMs: number } {\n\tconst frames = RAW_FRAMES.map((s, i) => {\n\t\tconst distanceToPeak = Math.min(\n\t\t\tMath.abs(i - PEAK_A),\n\t\t\tMath.abs(i - PEAK_B),\n\t\t\tMath.abs(i - PEAK_WRAP),\n\t\t)\n\t\tlet glyph = dim(s)\n\t\tif (distanceToPeak === DIST_PEAK) {\n\t\t\tglyph = bold(s)\n\t\t} else if (distanceToPeak === DIST_NEAR) {\n\t\t\tglyph = s\n\t\t}\n\t\treturn `${bold(label)} ${glyph}`\n\t})\n\n\treturn { frames, intervalMs: INTERVAL_MS }\n}\n","/**\n * Shared interactive session runtime for announce/connect flows.\n *\n * Handles spinner/lifecycle output, stream piping behavior, and protocol-aware\n * file-receive mode with completion acknowledgements.\n *\n * @module\n */\nimport { createWriteStream } from 'node:fs'\nimport { mkdir } from 'node:fs/promises'\nimport { dirname, relative, resolve } from 'node:path'\n\nimport {\n\tencodeCompletionAck,\n\tfindHeaderLineEnd,\n\tformatFileSize,\n\tisFileHeader,\n\tparseFileHeader,\n} from './file-protocol.ts'\nimport { createLifecycle } from './lifecycle.ts'\nimport {\n\tblank,\n\tbold,\n\tcreateSpinner,\n\tcyan,\n\tdim,\n\tendInline,\n\tgray,\n\tgreen,\n\tINDENT,\n\tlog,\n\tlogError,\n\tred,\n\tSEPARATOR,\n\twrite,\n\twriteInline,\n} from './log.ts'\nimport { confirm, input } from './prompt.ts'\nimport { createPulseFrames } from './pulse.ts'\n\nimport type { Beam } from '../beam.ts'\nimport type { ConnectionInfo } from '../types.ts'\n\ntype ReceiveMode = 'unknown' | 'pipe' | 'file' | 'file-stdout'\n\nconst FIRST_INDEX = 0\nconst NEXT_OFFSET = 1\nconst NO_DATA = 0\nconst KEEPALIVE_MS = 60_000\nconst CONNECTION_RESET = 'connection reset by peer'\n\nexport interface SessionOptions {\n\tannounceLabel?: string\n\tcopyValue?: (text: string) => void\n\tmode: 'announce' | 'connect'\n\toutputPath?: string\n\tvalue: string\n}\n\n/**\n * Run the standard hbeam CLI session UI and stdin/stdout piping.\n *\n * @param beam - Active beam instance for this session.\n * @param options - Session rendering and behavior options.\n * @returns Nothing.\n */\nexport function runBeamSession(beam: Beam, options: SessionOptions): void {\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst lifecycle = createLifecycle(beam, spinner)\n\n\tblank()\n\n\tspinner.start()\n\tspinner.blank()\n\n\tif (options.mode === 'announce') {\n\t\tspinner.write(dim(options.announceLabel ?? 'ANNOUNCING'))\n\t\tspinner.write(cyan(options.value))\n\t\toptions.copyValue?.(options.value)\n\t} else {\n\t\tspinner.write(dim('CONNECTING'))\n\t\tspinner.write(cyan(options.value))\n\t}\n\n\tbeam.on('remote-address', ({ host, port }: ConnectionInfo) => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\n\t\tif (host) {\n\t\t\tspinner.write(dim(`ONLINE ${gray(`[${host}:${port}]`)}`))\n\t\t\tspinner.blank()\n\t\t}\n\t})\n\n\tbeam.on('connected', () => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\n\t\tspinner.stop()\n\n\t\tif (!process.stdin.isTTY) {\n\t\t\twriteInline(bold('CONNECTED...'))\n\t\t} else {\n\t\t\tlog(bold('CONNECTED'))\n\t\t}\n\t})\n\n\t/**\n\t * Receive-side state machine:\n\t * - unknown: we have not yet decided if stream is plain pipe data or file transfer\n\t * - pipe: classic stdin/stdout hbeam mode\n\t * - file: file transfer writing to disk\n\t * - file-stdout: file transfer streaming bytes to stdout (non-interactive mode)\n\t */\n\tlet receiveMode: ReceiveMode = 'unknown'\n\tlet receivedPipeData = false\n\tlet pipeFrameClosed = false\n\tlet awaitingPrompt = false\n\tlet streamDone = false\n\tlet streamTerminationHandled = false\n\tlet keepAlive: ReturnType<typeof globalThis.setInterval> | undefined = undefined\n\tlet pendingChunks: Buffer[] = []\n\tlet fileStream: ReturnType<typeof createWriteStream> | undefined = undefined\n\tlet filePath: string | undefined = undefined\n\n\t/**\n\t * Stop and clear the temporary keepalive timer.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction clearKeepAlive(): void {\n\t\tif (keepAlive) {\n\t\t\tglobalThis.clearInterval(keepAlive)\n\t\t\tkeepAlive = undefined\n\t\t}\n\t}\n\n\t/**\n\t * Flush and close the destination file stream, then send completion ack.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction finalizeFile(): void {\n\t\tclearKeepAlive()\n\n\t\tif (!fileStream) {\n\t\t\treturn\n\t\t}\n\n\t\tfileStream.end(() => {\n\t\t\tconst absolutePath = filePath ?? ''\n\t\t\tconst displayPath = relative(process.cwd(), absolutePath) || absolutePath\n\t\t\tlog(`SAVED ${dim(displayPath)}`)\n\n\t\t\tblank()\n\t\t\tbeam.write(encodeCompletionAck({ ok: true, type: 'file-complete' }))\n\t\t\tbeam.end()\n\t\t})\n\n\t\tfileStream = undefined\n\t}\n\n\t/**\n\t * Close the pipe-mode content frame with a trailing separator.\n\t * No-op if no pipe data was received or the frame is already closed.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction closePipeFrame(): void {\n\t\tif (!receivedPipeData || pipeFrameClosed) {\n\t\t\treturn\n\t\t}\n\t\tpipeFrameClosed = true\n\t\twrite(SEPARATOR)\n\t}\n\n\t/**\n\t * Render normal pipe-mode output with visual framing/indentation.\n\t *\n\t * @param chunk - Raw inbound data chunk.\n\t * @returns Nothing.\n\t */\n\tfunction writePipeChunk(chunk: Buffer): void {\n\t\tif (!receivedPipeData) {\n\t\t\treceivedPipeData = true\n\t\t\twrite(SEPARATOR)\n\t\t}\n\n\t\tprocess.stdout.write(chunk.toString().replace(/^(?!$)/gm, INDENT))\n\t}\n\n\t/**\n\t * Finalize receive-side session behavior once stream termination is observed.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction onStreamDone(): void {\n\t\tstreamDone = true\n\n\t\t/**\n\t\t * Defer finalization until the async prompt flow finishes.\n\t\t */\n\t\tif (awaitingPrompt) {\n\t\t\treturn\n\t\t}\n\n\t\tif (streamTerminationHandled) {\n\t\t\treturn\n\t\t}\n\n\t\tstreamTerminationHandled = true\n\n\t\tif (receiveMode === 'file') {\n\t\t\tfinalizeFile()\n\t\t\treturn\n\t\t}\n\n\t\tif (receiveMode === 'file-stdout') {\n\t\t\tbeam.write(encodeCompletionAck({ ok: true, type: 'file-complete' }))\n\t\t} else {\n\t\t\tclosePipeFrame()\n\n\t\t\t/** Confirm clean delivery unless shutdown is already in progress. */\n\t\t\tif (!lifecycle.done()) {\n\t\t\t\tif (!process.stdin.isTTY && !receivedPipeData) {\n\t\t\t\t\tendInline(` ${green('DONE')}`)\n\t\t\t\t} else {\n\t\t\t\t\tlog(dim('DONE'))\n\t\t\t\t}\n\t\t\t\tblank()\n\t\t\t}\n\t\t}\n\n\t\tbeam.end()\n\t}\n\n\tbeam.on('end', onStreamDone)\n\tbeam.on('close', onStreamDone)\n\n\tbeam.on('error', (error: Error) => {\n\t\tspinner.stop()\n\t\tconst isPeerNotFound = error.message.includes('PEER_NOT_FOUND')\n\t\tconst isReset = error.message.includes(CONNECTION_RESET)\n\n\t\tif (\n\t\t\tisReset &&\n\t\t\t(awaitingPrompt || receiveMode === 'file' || receiveMode === 'file-stdout')\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tclosePipeFrame()\n\n\t\tif (isPeerNotFound) {\n\t\t\tlog(red(dim('PEER NOT FOUND')))\n\t\t} else if (isReset) {\n\t\t\tlog(dim('PEER DISCONNECTED'))\n\t\t} else {\n\t\t\tlogError(error.message)\n\t\t}\n\n\t\tblank()\n\n\t\tif (!isPeerNotFound) {\n\t\t\tlifecycle.shutdown()\n\t\t}\n\t})\n\n\t/**\n\t * Resolve where an incoming file should be written.\n\t *\n\t * @param fileName - Suggested source filename from header.\n\t * @returns Output path, empty string when cancelled, or undefined for stdout mode.\n\t */\n\tasync function promptForOutputPath(fileName: string): Promise<string | undefined> {\n\t\tif (options.outputPath) {\n\t\t\treturn resolve(options.outputPath)\n\t\t}\n\n\t\tif (!process.stdout.isTTY) {\n\t\t\treturn undefined\n\t\t}\n\n\t\tconst shouldSave = await confirm('Save file?')\n\n\t\tif (!shouldSave) {\n\t\t\treturn ''\n\t\t}\n\n\t\tconst chosen = await input('Save to:', `./${fileName}`)\n\n\t\treturn resolve(process.cwd(), chosen)\n\t}\n\n\t/**\n\t * Initialize file-receive mode from the first header-bearing data chunk.\n\t *\n\t * @param headerChunk - Initial chunk containing the file header line.\n\t * @returns Promise that resolves after file mode setup completes.\n\t */\n\tasync function startFileReceive(headerChunk: Buffer): Promise<void> {\n\t\tconst lineEnd = findHeaderLineEnd(headerChunk)\n\t\tconst header = parseFileHeader(headerChunk.subarray(FIRST_INDEX, lineEnd))\n\t\tconst remainder = headerChunk.subarray(lineEnd + NEXT_OFFSET)\n\n\t\tlog(`RECEIVED ${dim(`${header.name} (${formatFileSize(header.size)})`)}`)\n\n\t\tprocess.stdin.unpipe(beam)\n\t\tkeepAlive = globalThis.setInterval(() => {}, KEEPALIVE_MS)\n\n\t\tawaitingPrompt = true\n\t\tconst outputPath = await promptForOutputPath(header.name)\n\t\tawaitingPrompt = false\n\n\t\tif (outputPath === '') {\n\t\t\tclearKeepAlive()\n\t\t\tlog(dim('CANCELLED'))\n\t\t\tblank()\n\t\t\tbeam.write(\n\t\t\t\tencodeCompletionAck({\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: 'cancelled',\n\t\t\t\t\ttype: 'file-complete',\n\t\t\t\t}),\n\t\t\t)\n\t\t\tbeam.end()\n\t\t\treturn\n\t\t}\n\n\t\tif (outputPath === undefined) {\n\t\t\tclearKeepAlive()\n\t\t\treceiveMode = 'file-stdout'\n\t\t\tif (remainder.length > NO_DATA) {\n\t\t\t\tprocess.stdout.write(remainder)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\treceiveMode = 'file'\n\t\tfilePath = outputPath\n\t\tawait mkdir(dirname(outputPath), { recursive: true })\n\t\tfileStream = createWriteStream(outputPath)\n\t\tfileStream.on('error', error => beam.destroy(error))\n\n\t\tif (remainder.length > NO_DATA) {\n\t\t\tfileStream.write(remainder)\n\t\t}\n\t}\n\n\t/**\n\t * Route a chunk to the active receive-mode sink.\n\t *\n\t * @param chunk - Inbound data chunk.\n\t * @returns Nothing.\n\t */\n\tfunction routeChunk(chunk: Buffer): void {\n\t\tif (receiveMode === 'pipe') {\n\t\t\twritePipeChunk(chunk)\n\t\t} else if (receiveMode === 'file') {\n\t\t\tfileStream?.write(chunk)\n\t\t} else if (receiveMode === 'file-stdout') {\n\t\t\tprocess.stdout.write(chunk)\n\t\t}\n\t}\n\n\t/**\n\t * Replay chunks buffered while interactive prompts were active.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction flushPendingChunks(): void {\n\t\tfor (const queued of pendingChunks) {\n\t\t\trouteChunk(queued)\n\t\t}\n\n\t\tpendingChunks = []\n\t}\n\n\tbeam.on('data', (chunk: Buffer) => {\n\t\t/**\n\t\t * While prompting, stash inbound chunks and replay afterward.\n\t\t */\n\t\tif (awaitingPrompt) {\n\t\t\tpendingChunks.push(chunk)\n\t\t\treturn\n\t\t}\n\n\t\tif (receiveMode !== 'unknown') {\n\t\t\trouteChunk(chunk)\n\t\t\treturn\n\t\t}\n\n\t\tpendingChunks.push(chunk)\n\t\tconst pending = Buffer.concat(pendingChunks)\n\n\t\tif (isFileHeader(pending)) {\n\t\t\t/**\n\t\t\t * Wait until the full header line arrives before parsing.\n\t\t\t */\n\t\t\tif (findHeaderLineEnd(pending) < FIRST_INDEX) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingChunks = []\n\n\t\t\tvoid startFileReceive(pending).finally(() => {\n\t\t\t\tflushPendingChunks()\n\n\t\t\t\tif (streamDone) {\n\t\t\t\t\tonStreamDone()\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn\n\t\t}\n\n\t\treceiveMode = 'pipe'\n\t\tpendingChunks = []\n\t\twritePipeChunk(pending)\n\t})\n\n\tprocess.stdin.pipe(beam)\n\n\tif (typeof process.stdin.unref === 'function') {\n\t\tprocess.stdin.unref()\n\t}\n}\n","/**\n * Local address book data access and validation helpers.\n *\n * Handles peer name/public-key normalization plus CRUD operations\n * for the `peers.json` config file.\n *\n * @module\n */\nimport { readJsonFile, writeJsonFile } from './config.ts'\n\nimport type { AddressBook, Peer } from '../types.ts'\n\nconst PEERS_FILE = 'peers.json'\nconst MODULUS_EVEN = 2\nconst PUBLIC_KEY_BYTES = 32\nconst REMAINDER_ZERO = 0\n\n/**\n * Check whether a string is valid even-length hex.\n *\n * @param value - Candidate hex string.\n * @returns True when valid hex.\n */\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\n\n/**\n * Validate and normalize a peer public key.\n *\n * @param publicKeyHex - User-provided hex public key.\n * @returns Normalized lowercase 64-char hex public key.\n */\nfunction normalizePublicKeyHex(publicKeyHex: string): string {\n\tconst normalized = publicKeyHex.trim().toLowerCase()\n\tif (!isHex(normalized)) {\n\t\tthrow new Error('Public key must be a valid hex string')\n\t}\n\tconst key = Buffer.from(normalized, 'hex')\n\tif (key.length !== PUBLIC_KEY_BYTES) {\n\t\tthrow new Error('Public key must be 32 bytes (64 hex chars)')\n\t}\n\treturn normalized\n}\n\n/**\n * Validate and normalize a peer name.\n *\n * @param name - User-provided peer name.\n * @returns Normalized lowercase peer name.\n */\nfunction normalizePeerName(name: string): string {\n\tconst normalized = name.trim().toLowerCase()\n\tif (!/^[a-z0-9-]+$/.test(normalized)) {\n\t\tthrow new Error('Peer name must use only letters, numbers, and hyphens')\n\t}\n\treturn normalized\n}\n\n/**\n * Load the full address book from disk.\n *\n * @returns Name-keyed peer map.\n */\nasync function readAddressBook(): Promise<AddressBook> {\n\treturn (await readJsonFile<AddressBook>(PEERS_FILE)) ?? {}\n}\n\n/**\n * Persist the full address book to disk.\n *\n * @param addressBook - Address book object to persist.\n * @returns Promise that resolves when write is complete.\n */\nasync function writeAddressBook(addressBook: AddressBook): Promise<void> {\n\tawait writeJsonFile(PEERS_FILE, addressBook)\n}\n\n/**\n * Add or update a named peer in the local address book.\n *\n * @param name - Peer alias.\n * @param publicKeyHex - Hex-encoded public key.\n * @returns Saved peer record.\n */\nexport async function addPeer(name: string, publicKeyHex: string): Promise<Peer> {\n\tconst normalizedName = normalizePeerName(name)\n\tconst normalizedPublicKey = normalizePublicKeyHex(publicKeyHex)\n\tconst addressBook = await readAddressBook()\n\n\tconst peer: Peer = {\n\t\taddedAt: new Date().toISOString(),\n\t\tpublicKey: normalizedPublicKey,\n\t}\n\taddressBook[normalizedName] = peer\n\n\tawait writeAddressBook(addressBook)\n\treturn peer\n}\n\n/**\n * Remove a peer from the local address book.\n *\n * @param name - Peer alias to remove.\n * @returns True when a peer was removed.\n */\nexport async function removePeer(name: string): Promise<boolean> {\n\tconst normalizedName = normalizePeerName(name)\n\tconst addressBook = await readAddressBook()\n\tif (!addressBook[normalizedName]) {\n\t\treturn false\n\t}\n\tdelete addressBook[normalizedName]\n\tawait writeAddressBook(addressBook)\n\treturn true\n}\n\n/**\n * Return all peers sorted by name.\n *\n * @returns Array of peers including their names.\n */\nexport async function listPeers(): Promise<({ name: string } & Peer)[]> {\n\tconst addressBook = await readAddressBook()\n\treturn Object.entries(addressBook)\n\t\t.map(([name, peer]) => ({ name, ...peer }))\n\t\t.toSorted((a, b) => a.name.localeCompare(b.name))\n}\n\n/**\n * Look up a single peer by name.\n *\n * @param name - Peer alias.\n * @returns Peer record when found, otherwise `undefined`.\n */\nexport async function getPeer(name: string): Promise<Peer | undefined> {\n\tconst normalizedName = normalizePeerName(name)\n\tconst addressBook = await readAddressBook()\n\treturn addressBook[normalizedName]\n}\n","/**\n * HyperDHT integration helpers used by the Beam transport.\n *\n * Includes deterministic key derivation, ephemeral node creation,\n * socket open synchronization, and inbound firewall generation.\n *\n * @module\n */\nimport { createHash } from 'node:crypto'\n\nimport * as b4a from 'b4a'\nimport DHT from 'hyperdht'\n\nimport { getPeer } from './addressbook.ts'\nimport { fromBase32 } from './encoding.ts'\n\nimport type { EncryptedSocket, HyperDHTNode, KeyPair } from '../types.ts'\n\nconst KEY_SEED_BYTES = 32\nconst PUBLIC_KEY_BYTES = 32\n\n/**\n * Check whether a string is a lowercase/uppercase 64-char hex public key.\n *\n * @param value - Candidate public key string.\n * @returns True when valid hex public key text.\n */\nfunction isPublicKeyHex(value: string): boolean {\n\treturn /^[0-9a-f]{64}$/i.test(value.trim())\n}\n\n/**\n * Normalize an arbitrary decoded seed into exactly 32 bytes.\n *\n * HyperDHT's `keyPair()` behavior for non-32-byte seeds can differ across JS\n * runtimes (e.g. Node vs Bun). By hashing to 32 bytes we ensure both sides\n * derive the same keypair for a given passphrase.\n *\n * @param seed - Decoded passphrase seed bytes.\n * @returns A 32-byte normalized seed.\n */\nfunction normalizeSeed(seed: Buffer): Buffer {\n\tif (seed.length === KEY_SEED_BYTES) {\n\t\treturn seed\n\t}\n\tconst digest = createHash('sha256').update(seed).digest()\n\treturn b4a.from(digest)\n}\n\n/**\n * Derive a Noise keypair from a base32-encoded passphrase.\n *\n * @param passphrase - Shared base32 passphrase.\n * @returns Deterministic Noise keypair derived from the passphrase.\n */\nexport function deriveKeyPair(passphrase: string): KeyPair {\n\tconst seed = fromBase32(passphrase)\n\treturn DHT.keyPair(normalizeSeed(seed))\n}\n\n/**\n * Create an ephemeral HyperDHT node that is destroyed with the beam.\n *\n * @returns New ephemeral HyperDHT node instance.\n */\nexport function createNode(): HyperDHTNode {\n\treturn new DHT({ ephemeral: true }) as unknown as HyperDHTNode\n}\n\n/**\n * Wait for an encrypted socket to complete its Noise handshake.\n *\n * @param socket - Socket to await open/close/error on.\n * @returns Promise that resolves when the socket opens.\n */\nexport function awaitOpen(socket: EncryptedSocket): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\tsocket.once('open', resolve)\n\t\tsocket.once('close', reject)\n\t\tsocket.once('error', reject)\n\t})\n}\n\n/**\n * Create a firewall that rejects connections not matching the expected keypair.\n *\n * @param keyPair - Local keypair used to validate remote key.\n * @returns Predicate used by HyperDHT server firewall.\n */\nexport function createFirewall(keyPair: KeyPair): (remotePublicKey: Buffer) => boolean {\n\treturn (remotePublicKey: Buffer) => !b4a.equals(remotePublicKey, keyPair.publicKey)\n}\n\n/**\n * Resolve a peer target string to a remote public key.\n *\n * Resolution order:\n * 1) raw 64-char hex public key\n * 2) saved peer name in address book\n * 3) passphrase-derived keypair public key\n *\n * @param target - Hex key, address-book name, or passphrase.\n * @returns Resolved 32-byte remote public key.\n */\nexport async function resolveRemoteKey(target: string): Promise<Buffer> {\n\tconst normalized = target.trim()\n\n\tif (isPublicKeyHex(normalized)) {\n\t\tconst key = Buffer.from(normalized, 'hex')\n\t\tif (key.length === PUBLIC_KEY_BYTES) {\n\t\t\treturn key\n\t\t}\n\t}\n\n\tconst peer = await getPeer(normalized).catch(() => undefined)\n\n\tif (peer) {\n\t\tconst key = Buffer.from(peer.publicKey, 'hex')\n\t\tif (key.length === PUBLIC_KEY_BYTES) {\n\t\t\treturn key\n\t\t}\n\t}\n\n\treturn deriveKeyPair(normalized).publicKey\n}\n","/**\n * Core Beam duplex stream implementation built on HyperDHT sockets.\n *\n * Handles announce/connect setup, socket wiring, streamx lifecycle hooks,\n * and connection event emission for CLI and library consumers.\n *\n * @module\n */\nimport queueTick from 'queue-tick'\nimport { Duplex } from 'streamx'\n\nimport { awaitOpen, createFirewall, createNode, deriveKeyPair } from './lib/dht.ts'\nimport { randomBytes, toBase32 } from './lib/encoding.ts'\n\nimport type {\n\tBeamOptions,\n\tConnectionInfo,\n\tEncryptedSocket,\n\tHyperDHTNode,\n\tHyperDHTServer,\n\tKeyPair,\n\tStreamCallback,\n} from './types.ts'\n\n/** Number of random bytes used to generate a passphrase seed. */\nconst KEY_SEED_BYTES = 32\n\n/**\n * A 1-to-1 end-to-end encrypted duplex stream powered by HyperDHT.\n *\n * Creates an encrypted tunnel between two peers using a shared passphrase.\n * If no passphrase is provided, one is generated and the beam listens for\n * an incoming connection (server mode). When a passphrase is provided, the\n * beam connects to the listening peer (client mode).\n *\n * @example\n * ```ts\n * const server = new Beam()\n * console.log(server.key) // Share this with the other side\n *\n * const client = new Beam(server.key)\n * ```\n */\nexport class Beam extends Duplex {\n\t/** Base32-encoded passphrase for peer discovery and key derivation. */\n\treadonly key: string\n\n\t/** Whether this beam is announcing (server) or connecting (client). */\n\treadonly announce: boolean\n\n\tprivate node: HyperDHTNode | undefined\n\tprivate server: HyperDHTServer | undefined = undefined\n\tprivate inbound: EncryptedSocket | undefined = undefined\n\tprivate outbound: EncryptedSocket | undefined = undefined\n\tprivate readonly keyPairOverride: KeyPair | undefined\n\tprivate readonly remotePublicKeyOverride: Buffer | undefined\n\tprivate readonly openInboundFirewall: boolean\n\n\tprivate openCallback: StreamCallback | undefined = undefined\n\tprivate readCallback: StreamCallback | undefined = undefined\n\tprivate drainCallback: StreamCallback | undefined = undefined\n\n\t/**\n\t * Create a new beam instance in announce or connect mode.\n\t *\n\t * @param keyOrOptions - Passphrase or options object.\n\t * @param options - Options used when a passphrase is provided.\n\t */\n\tconstructor(keyOrOptions?: string | BeamOptions, options?: BeamOptions) {\n\t\tsuper()\n\n\t\tlet key: string | undefined = undefined\n\t\tlet opts: BeamOptions = {}\n\t\tconst passphraseWasProvided = typeof keyOrOptions === 'string'\n\n\t\tif (passphraseWasProvided) {\n\t\t\tkey = keyOrOptions\n\t\t\topts = options ?? {}\n\t\t} else {\n\t\t\topts = keyOrOptions ?? {}\n\t\t}\n\n\t\tlet shouldAnnounce = opts.announce ?? false\n\n\t\tif (!key && !opts.keyPair) {\n\t\t\tkey = toBase32(randomBytes(KEY_SEED_BYTES))\n\t\t\tshouldAnnounce = true\n\t\t} else if (!key && opts.keyPair) {\n\t\t\tkey = opts.keyPair.publicKey.toString('hex')\n\t\t}\n\t\tif (!key) {\n\t\t\tthrow new Error('Missing key material')\n\t\t}\n\n\t\tthis.key = key\n\t\tthis.announce = shouldAnnounce\n\t\tthis.node = (opts.dht as HyperDHTNode) ?? undefined\n\t\tthis.keyPairOverride = opts.keyPair\n\t\tthis.remotePublicKeyOverride = opts.remotePublicKey\n\t\tthis.openInboundFirewall = !passphraseWasProvided && opts.keyPair !== undefined\n\t}\n\n\t/**\n\t * Whether a peer connection has been established.\n\t *\n\t * @returns True when outbound socket has been created.\n\t */\n\tget connected(): boolean {\n\t\treturn this.outbound !== undefined\n\t}\n\n\t// Streamx lifecycle\n\n\t/**\n\t * Streamx open hook: initialize DHT node and start announce/connect flow.\n\t *\n\t * @param cb - Open callback from streamx.\n\t * @returns Promise that resolves when setup path completes.\n\t */\n\toverride async _open(cb: StreamCallback): Promise<void> {\n\t\tthis.openCallback = cb\n\t\tconst keyPair = this.keyPairOverride ?? deriveKeyPair(this.key)\n\t\tthis.node ??= createNode()\n\n\t\tif (this.announce) {\n\t\t\tawait this.listenAsServer(keyPair)\n\t\t} else {\n\t\t\tawait this.connectAsClient(keyPair)\n\t\t}\n\t}\n\n\t/**\n\t * Streamx read hook: resume inbound flow when consumer requests data.\n\t *\n\t * @param cb - Read callback from streamx.\n\t * @returns Nothing.\n\t */\n\toverride _read(cb: StreamCallback): void {\n\t\tthis.readCallback = cb\n\t\tthis.inbound?.resume()\n\t}\n\n\t/**\n\t * Streamx write hook: write outbound bytes, respecting backpressure.\n\t *\n\t * @param data - Data chunk to send.\n\t * @param cb - Write callback from streamx.\n\t * @returns Nothing.\n\t */\n\toverride _write(data: unknown, cb: StreamCallback): void {\n\t\tif (this.outbound!.write(data as Buffer) !== false) {\n\t\t\tcb()\n\t\t\treturn\n\t\t}\n\t\tthis.drainCallback = cb\n\t}\n\n\t/**\n\t * Streamx final hook: end outbound socket and resolve when flushed.\n\t *\n\t * @param cb - Final callback from streamx.\n\t * @returns Nothing.\n\t */\n\toverride _final(cb: StreamCallback): void {\n\t\t/**\n\t\t * Resolve the final callback once outbound finish/error fires.\n\t\t *\n\t\t * @returns Nothing.\n\t\t */\n\t\tconst done = (): void => {\n\t\t\tthis.outbound!.removeListener('finish', done)\n\t\t\tthis.outbound!.removeListener('error', done)\n\t\t\tcb()\n\t\t}\n\t\tthis.outbound!.end()\n\t\tthis.outbound!.on('finish', done)\n\t\tthis.outbound!.on('error', done)\n\t}\n\n\t/**\n\t * Streamx pre-destroy hook: tear down sockets and pending callbacks.\n\t *\n\t * @returns Nothing.\n\t */\n\toverride _predestroy(): void {\n\t\tthis.inbound?.destroy()\n\t\tthis.outbound?.destroy()\n\t\tconst error = new Error('Destroyed')\n\t\tthis.resolveOpen(error)\n\t\tthis.resolveRead(error)\n\t\tthis.resolveDrain(error)\n\t}\n\n\t/**\n\t * Streamx destroy hook: close DHT server/node resources.\n\t *\n\t * @param cb - Destroy callback from streamx.\n\t * @returns Promise that resolves after cleanup.\n\t */\n\toverride async _destroy(cb: StreamCallback): Promise<void> {\n\t\tif (!this.node) {\n\t\t\tcb()\n\t\t\treturn\n\t\t}\n\t\tif (this.server) {\n\t\t\tawait this.server.close().catch(() => {})\n\t\t}\n\t\tawait this.node.destroy().catch(() => {})\n\t\tcb()\n\t}\n\n\t// Connection setup\n\n\t/**\n\t * Start announce/listen mode with optional inbound firewall.\n\t *\n\t * @param keyPair - Local keypair used for server listen.\n\t * @returns Promise that resolves when listen path completes.\n\t */\n\tprivate async listenAsServer(keyPair: KeyPair): Promise<void> {\n\t\tconst serverOptions = this.openInboundFirewall\n\t\t\t? undefined\n\t\t\t: { firewall: createFirewall(keyPair) }\n\t\tthis.server = this.node!.createServer(serverOptions)\n\t\tthis.server.on('connection', (socket: EncryptedSocket) => this.handleConnection(socket))\n\n\t\ttry {\n\t\t\tawait this.server.listen(keyPair)\n\t\t} catch (error) {\n\t\t\tthis.resolveOpen(error as Error)\n\t\t\treturn\n\t\t}\n\t\tthis.emitRemoteAddress()\n\t}\n\n\t/**\n\t * Start client mode by dialing a remote public key.\n\t *\n\t * @param keyPair - Local keypair used for dial auth.\n\t * @returns Promise that resolves when connect path completes.\n\t */\n\tprivate async connectAsClient(keyPair: KeyPair): Promise<void> {\n\t\tconst remotePublicKey = this.remotePublicKeyOverride ?? keyPair.publicKey\n\t\tconst socket: EncryptedSocket = this.node!.connect(remotePublicKey, { keyPair })\n\n\t\ttry {\n\t\t\tawait awaitOpen(socket)\n\t\t} catch (error) {\n\t\t\tthis.resolveOpen(error as Error)\n\t\t\treturn\n\t\t}\n\t\tthis.emitRemoteAddress()\n\t\tthis.handleConnection(socket)\n\t}\n\n\t/**\n\t * Bind socket handlers for inbound/outbound stream integration.\n\t *\n\t * @param socket - Encrypted socket from HyperDHT.\n\t * @returns Nothing.\n\t */\n\tprivate handleConnection(socket: EncryptedSocket): void {\n\t\tsocket.on('data', (data: Buffer) => {\n\t\t\tif (!this.inbound) {\n\t\t\t\tthis.inbound = socket\n\t\t\t\tthis.inbound.on('error', (err: Error) => this.destroy(err))\n\t\t\t\tthis.inbound.on('end', () => this.pushEndOfStream())\n\t\t\t}\n\t\t\tif (socket !== this.inbound) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.pushData(data) === false) {\n\t\t\t\tsocket.pause()\n\t\t\t}\n\t\t})\n\n\t\tsocket.on('end', () => {\n\t\t\tif (this.inbound) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.pushEndOfStream()\n\t\t})\n\n\t\tif (!this.outbound) {\n\t\t\tthis.outbound = socket\n\t\t\tthis.outbound.on('error', (err: Error) => this.destroy(err))\n\t\t\tthis.outbound.on('drain', () => this.resolveDrain())\n\t\t\tthis.emit('connected')\n\t\t\tthis.resolveOpen()\n\t\t}\n\t}\n\n\t// Helpers\n\n\t/**\n\t * Push inbound data into streamx and schedule read callback resolution.\n\t *\n\t * @param data - Data chunk or end-of-stream marker.\n\t * @returns Push backpressure result from streamx.\n\t */\n\tprivate pushData(data: Buffer | null): boolean {\n\t\tconst result = this.push(data)\n\t\tqueueTick(() => this.resolveRead())\n\t\treturn result\n\t}\n\n\t/**\n\t * Push end-of-stream marker into streamx.\n\t *\n\t * @returns Nothing.\n\t */\n\tprivate pushEndOfStream(): void {\n\t\t// oxlint-disable-next-line unicorn/no-null\n\t\tthis.pushData(null)\n\t}\n\n\t/**\n\t * Emit current node host/port information.\n\t *\n\t * @returns Nothing.\n\t */\n\tprivate emitRemoteAddress(): void {\n\t\tthis.emit('remote-address', {\n\t\t\thost: this.node!.host,\n\t\t\tport: this.node!.port,\n\t\t} satisfies ConnectionInfo)\n\t}\n\n\t/**\n\t * Resolve the pending open callback, if any.\n\t *\n\t * @param error - Optional error to pass to callback.\n\t * @returns Nothing.\n\t */\n\tprivate resolveOpen(error?: Error): void {\n\t\tconst cb = this.openCallback\n\t\tif (cb) {\n\t\t\tthis.openCallback = undefined\n\t\t\tcb(error)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the pending read callback, if any.\n\t *\n\t * @param error - Optional error to pass to callback.\n\t * @returns Nothing.\n\t */\n\tprivate resolveRead(error?: Error): void {\n\t\tconst cb = this.readCallback\n\t\tif (cb) {\n\t\t\tthis.readCallback = undefined\n\t\t\tcb(error)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the pending drain callback, if any.\n\t *\n\t * @param error - Optional error to pass to callback.\n\t * @returns Nothing.\n\t */\n\tprivate resolveDrain(error?: Error): void {\n\t\tconst cb = this.drainCallback\n\t\tif (cb) {\n\t\t\tthis.drainCallback = undefined\n\t\t\tcb(error)\n\t\t}\n\t}\n}\n","/**\n * Reusable TCP-over-DHT tunnel primitives for bind-like commands.\n *\n * Provides reverse and forward proxy modes with connection lifecycle events.\n *\n * @module\n */\nimport { EventEmitter } from 'node:events'\nimport net from 'node:net'\n\nimport { awaitOpen, createNode } from './dht.ts'\n\nimport type {\n\tEncryptedSocket,\n\tForwardTunnelOptions,\n\tHyperDHTNode,\n\tReverseTunnelOptions,\n\tTunnelController,\n\tTunnelEventMap,\n} from '../types.ts'\n\nconst DEFAULT_FORWARD_HOST = '127.0.0.1'\n\n/**\n * Link two duplex sockets and destroy each side on counterpart errors.\n *\n * @param left - First socket.\n * @param right - Second socket.\n * @returns Nothing.\n */\nfunction pipeBothWays(left: net.Socket, right: EncryptedSocket): void {\n\tleft.pipe(right as unknown as NodeJS.WritableStream)\n\t;(right as unknown as NodeJS.ReadableStream).pipe(left)\n}\n\n/**\n * Create a reverse tunnel (P2P -> local TCP service).\n *\n * @param options - Reverse tunnel configuration.\n * @returns Controller for observing/closing the tunnel.\n */\nexport async function createReverseTunnel(\n\toptions: ReverseTunnelOptions,\n): Promise<TunnelController> {\n\tconst events = new EventEmitter()\n\tconst node = options.dht ?? createNode()\n\tconst ownsNode = options.dht === undefined\n\tconst server = node.createServer()\n\n\tlet closed = false\n\tlet connections = 0\n\n\tconst peers = new Set<EncryptedSocket>()\n\tconst tcpSockets = new Set<net.Socket>()\n\n\tfunction emitError(error: unknown): void {\n\t\tevents.emit('error', error instanceof Error ? error : new Error(String(error)))\n\t}\n\n\tfunction onDisconnect(): void {\n\t\tconnections--\n\t\tevents.emit('disconnect', connections)\n\t}\n\n\tserver.on('error', emitError)\n\n\tserver.on('connection', (peer: EncryptedSocket) => {\n\t\tif (closed) {\n\t\t\tpeer.destroy()\n\t\t\treturn\n\t\t}\n\n\t\tpeers.add(peer)\n\n\t\tconst local = net.connect({ host: options.host, port: options.port })\n\t\ttcpSockets.add(local)\n\n\t\tlet connected = false\n\t\tlet disconnected = false\n\n\t\tfunction cleanup(): void {\n\t\t\tpeers.delete(peer)\n\t\t\ttcpSockets.delete(local)\n\n\t\t\tif (connected && !disconnected) {\n\t\t\t\tdisconnected = true\n\t\t\t\tonDisconnect()\n\t\t\t}\n\t\t}\n\n\t\tlocal.on('connect', () => {\n\t\t\tif (closed) {\n\t\t\t\tlocal.destroy()\n\t\t\t\tpeer.destroy()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconnected = true\n\t\t\tconnections++\n\n\t\t\tevents.emit('connect', connections)\n\n\t\t\tpipeBothWays(local, peer)\n\t\t})\n\n\t\tlocal.on('error', error => {\n\t\t\temitError(error)\n\t\t\tpeer.destroy()\n\t\t})\n\n\t\tlocal.on('close', cleanup)\n\n\t\tpeer.on('error', error => {\n\t\t\temitError(error)\n\t\t\tlocal.destroy()\n\t\t})\n\n\t\tpeer.on('close', cleanup)\n\t})\n\n\tawait server.listen(options.keyPair)\n\n\tconst controller: TunnelController = {\n\t\tasync close(): Promise<void> {\n\t\t\tif (closed) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tclosed = true\n\n\t\t\tfor (const socket of tcpSockets) {\n\t\t\t\tsocket.destroy()\n\t\t\t}\n\n\t\t\tfor (const peer of peers) {\n\t\t\t\tpeer.destroy()\n\t\t\t}\n\n\t\t\tawait server.close().catch(() => {})\n\n\t\t\tif (ownsNode) {\n\t\t\t\tawait node.destroy().catch(() => {})\n\t\t\t}\n\t\t},\n\t\tget connections(): number {\n\t\t\treturn connections\n\t\t},\n\t\ton<K extends keyof TunnelEventMap>(event: K, handler: TunnelEventMap[K]): TunnelController {\n\t\t\tevents.on(event, handler)\n\t\t\treturn controller\n\t\t},\n\t}\n\n\treturn controller\n}\n\n/**\n * Create a forward tunnel (local TCP listener -> P2P peer).\n *\n * @param options - Forward tunnel configuration.\n * @returns Controller for observing/closing the tunnel.\n */\nexport async function createForwardTunnel(\n\toptions: ForwardTunnelOptions,\n): Promise<TunnelController> {\n\tconst events = new EventEmitter()\n\tconst node: HyperDHTNode = options.dht ?? createNode()\n\tconst ownsNode = options.dht === undefined\n\tconst host = options.host ?? DEFAULT_FORWARD_HOST\n\tconst tcpServer = net.createServer()\n\n\tlet closed = false\n\tlet connections = 0\n\n\tconst peers = new Set<EncryptedSocket>()\n\tconst tcpSockets = new Set<net.Socket>()\n\n\tfunction emitError(error: unknown): void {\n\t\tevents.emit('error', error instanceof Error ? error : new Error(String(error)))\n\t}\n\n\tfunction onDisconnect(): void {\n\t\tconnections--\n\t\tevents.emit('disconnect', connections)\n\t}\n\n\ttcpServer.on('error', emitError)\n\n\ttcpServer.on('connection', (local: net.Socket) => {\n\t\tif (closed) {\n\t\t\tlocal.destroy()\n\t\t\treturn\n\t\t}\n\n\t\ttcpSockets.add(local)\n\t\tlocal.pause()\n\n\t\tconst peer = node.connect(options.remotePublicKey, { keyPair: options.keyPair })\n\t\tpeers.add(peer)\n\n\t\tlet connected = false\n\t\tlet disconnected = false\n\n\t\tfunction cleanup(): void {\n\t\t\ttcpSockets.delete(local)\n\t\t\tpeers.delete(peer)\n\t\t\tif (connected && !disconnected) {\n\t\t\t\tdisconnected = true\n\t\t\t\tonDisconnect()\n\t\t\t}\n\t\t}\n\n\t\tvoid awaitOpen(peer)\n\t\t\t.then(() => {\n\t\t\t\tif (closed) {\n\t\t\t\t\tlocal.destroy()\n\t\t\t\t\tpeer.destroy()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconnected = true\n\t\t\t\tconnections++\n\n\t\t\t\tevents.emit('connect', connections)\n\t\t\t\tlocal.resume()\n\n\t\t\t\tpipeBothWays(local, peer)\n\t\t\t})\n\t\t\t.catch(error => {\n\t\t\t\temitError(error)\n\n\t\t\t\tlocal.destroy()\n\t\t\t\tpeer.destroy()\n\t\t\t})\n\n\t\tlocal.on('error', error => {\n\t\t\temitError(error)\n\t\t\tpeer.destroy()\n\t\t})\n\n\t\tlocal.on('close', cleanup)\n\n\t\tpeer.on('error', error => {\n\t\t\temitError(error)\n\t\t\tlocal.destroy()\n\t\t})\n\n\t\tpeer.on('close', cleanup)\n\t})\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\ttcpServer.listen(options.port, host, () => resolve())\n\t\ttcpServer.once('error', reject)\n\t})\n\n\tconst controller: TunnelController = {\n\t\tasync close(): Promise<void> {\n\t\t\tif (closed) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tclosed = true\n\n\t\t\tfor (const socket of tcpSockets) {\n\t\t\t\tsocket.destroy()\n\t\t\t}\n\n\t\t\tfor (const peer of peers) {\n\t\t\t\tpeer.destroy()\n\t\t\t}\n\n\t\t\tawait new Promise<void>(resolve => {\n\t\t\t\ttcpServer.close(() => resolve())\n\t\t\t})\n\n\t\t\tif (ownsNode) {\n\t\t\t\tawait node.destroy().catch(() => {})\n\t\t\t}\n\t\t},\n\t\tget connections(): number {\n\t\t\treturn connections\n\t\t},\n\t\tget listenHost(): string {\n\t\t\treturn host\n\t\t},\n\t\tget listenPort(): number | undefined {\n\t\t\tconst address = tcpServer.address()\n\n\t\t\treturn typeof address === 'object' && address ? address.port : undefined\n\t\t},\n\t\ton<K extends keyof TunnelEventMap>(event: K, handler: TunnelEventMap[K]): TunnelController {\n\t\t\tevents.on(event, handler)\n\n\t\t\treturn controller\n\t\t},\n\t}\n\n\treturn controller\n}\n","/**\n * `hbeam bind` command implementation.\n *\n * Creates encrypted TCP tunnels over HyperDHT in either reverse mode\n * (P2P -> local TCP service) or forward mode (local TCP -> P2P peer).\n *\n * @module\n */\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport { createNode, deriveKeyPair, resolveRemoteKey } from '@/lib/dht.ts'\nimport { randomBytes, toBase32 } from '@/lib/encoding.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { blank, createSpinner, cyan, dim, gray, log, logError, write } from '@/lib/log.ts'\nimport { createPulseFrames } from '@/lib/pulse.ts'\nimport { createForwardTunnel, createReverseTunnel } from '@/lib/tunnel.ts'\n\nimport type { KeyPair, TunnelController } from '@/types.ts'\n\nconst EXIT_FAILURE = 1\nconst EXIT_SUCCESS = 0\nconst EPHEMERAL_KEY_BYTES = 32\nconst DEFAULT_REVERSE_HOST = 'localhost'\nconst DEFAULT_FORWARD_HOST = '127.0.0.1'\nconst MIN_PORT = 1\nconst MAX_PORT = 65_535\nconst RANDOM_PORT = 0\n\ninterface BindCommandOptions {\n\thost?: string\n\tlisten?: boolean\n\tport?: number | string\n}\n\n/**\n * Determine whether a tunnel error is a non-fatal per-connection close.\n *\n * @param error - Error emitted by tunnel internals.\n * @returns True when bind should continue running.\n */\nfunction isBenignConnectionError(error: Error): boolean {\n\tconst message = error.message.toLowerCase()\n\tconst code = (error as NodeJS.ErrnoException).code?.toLowerCase() ?? ''\n\n\treturn (\n\t\tcode === 'econnreset' ||\n\t\tcode === 'epipe' ||\n\t\tmessage.includes('connection reset by peer') ||\n\t\tmessage.includes('writable stream closed prematurely') ||\n\t\tmessage.includes('premature close')\n\t)\n}\n\n/**\n * Print a bind usage error and terminate with failure.\n *\n * @param message - Error message to display.\n * @returns Never returns (process exits).\n */\nfunction showUsageError(message: string): never {\n\tblank()\n\tlogError(message)\n\twrite(\n\t\tdim(\n\t\t\t'Usage: hbeam bind <port|passphrase|name|public-key> [--host <host>] [-p <port>] [--listen]',\n\t\t),\n\t)\n\tblank()\n\tprocess.exit(EXIT_FAILURE)\n}\n\n/**\n * Parse and validate a TCP port number.\n *\n * @param value - Port text/number.\n * @param label - Human-readable label used in error messages.\n * @returns Parsed port number.\n */\nfunction parsePort(value: string | number, label: string): number {\n\tconst parsed = typeof value === 'number' ? value : Number(value)\n\tif (!Number.isInteger(parsed) || parsed < MIN_PORT || parsed > MAX_PORT) {\n\t\tshowUsageError(`Invalid ${label}: ${value}`)\n\t}\n\treturn parsed\n}\n\n/**\n * Build a throwaway keypair for ephemeral tunnel sessions.\n *\n * @returns Ephemeral keypair derived from random passphrase material.\n */\nfunction createEphemeralKeyPair(): KeyPair {\n\tconst passphrase = toBase32(randomBytes(EPHEMERAL_KEY_BYTES))\n\treturn deriveKeyPair(passphrase)\n}\n\n/**\n * Register SIGINT/SIGTERM handlers and close resources once.\n *\n * @param tunnel - Active tunnel controller.\n * @returns Nothing.\n */\nfunction registerShutdown(tunnel: TunnelController): void {\n\tlet shuttingDown = false\n\n\tasync function shutdown(exitCode: number): Promise<void> {\n\t\tif (shuttingDown) {\n\t\t\treturn\n\t\t}\n\t\tshuttingDown = true\n\t\tblank()\n\t\tlog(dim('SHUTTING DOWN'))\n\t\tblank()\n\t\tawait tunnel.close().catch(() => {})\n\t\tprocess.exit(exitCode)\n\t}\n\n\tprocess.once('SIGINT', () => {\n\t\tvoid shutdown(EXIT_SUCCESS)\n\t})\n\tprocess.once('SIGTERM', () => {\n\t\tvoid shutdown(EXIT_SUCCESS)\n\t})\n\n\ttunnel.on('error', error => {\n\t\tif (isBenignConnectionError(error)) {\n\t\t\treturn\n\t\t}\n\t\tlogError(error.message)\n\t\tvoid shutdown(EXIT_FAILURE)\n\t})\n}\n\n/**\n * Execute reverse-proxy bind mode (P2P -> local TCP service).\n *\n * @param targetPort - Local service port to expose.\n * @param options - Bind command options.\n * @returns Promise that resolves once tunnel is running.\n */\nasync function runReverseBind(targetPort: number, options: BindCommandOptions): Promise<void> {\n\tconst host = options.host ?? DEFAULT_REVERSE_HOST\n\tlet keyPair: KeyPair | undefined = undefined\n\tlet announceValue: string | undefined = undefined\n\n\tif (options.listen) {\n\t\tconst identity = await loadOrCreateIdentityWithMeta()\n\t\tkeyPair = identity.keyPair\n\t\tannounceValue = identity.keyPair.publicKey.toString('hex')\n\t\tif (identity.created) {\n\t\t\tblank()\n\t\t\tlog(dim('IDENTITY CREATED'))\n\t\t\twrite(cyan(announceValue))\n\t\t}\n\t} else {\n\t\tannounceValue = toBase32(randomBytes(EPHEMERAL_KEY_BYTES))\n\t\tkeyPair = deriveKeyPair(announceValue)\n\t\tcopyToClipboard(announceValue)\n\t}\n\n\tif (!keyPair || !announceValue) {\n\t\tshowUsageError('Failed to initialize bind identity.')\n\t}\n\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst node = createNode()\n\n\tblank()\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim('ANNOUNCING'))\n\tspinner.write(cyan(announceValue))\n\n\tconst tunnel = await createReverseTunnel({\n\t\tdht: node,\n\t\thost,\n\t\tkeyPair,\n\t\tport: targetPort,\n\t})\n\n\tspinner.write(dim(`ONLINE ${gray(`[${node.host}:${node.port}]`)}`))\n\tspinner.write(`FORWARDING ${dim(`${host}:${targetPort}`)}`)\n\tspinner.blank()\n\tspinner.stop()\n\n\tregisterShutdown(tunnel)\n}\n\n/**\n * Execute forward-proxy bind mode (local TCP -> P2P peer).\n *\n * @param target - Remote target (peer name, passphrase, or public key).\n * @param options - Bind command options.\n * @returns Promise that resolves once tunnel is running.\n */\nasync function runForwardBind(target: string, options: BindCommandOptions): Promise<void> {\n\tconst remotePublicKey = await resolveRemoteKey(target)\n\tlet keyPair: KeyPair | undefined = undefined\n\n\tif (options.listen) {\n\t\tconst identity = await loadOrCreateIdentityWithMeta()\n\t\tkeyPair = identity.keyPair\n\t} else {\n\t\tkeyPair = createEphemeralKeyPair()\n\t}\n\n\tif (!keyPair) {\n\t\tshowUsageError('Failed to initialize bind identity.')\n\t}\n\n\tconst listenHost = options.host ?? DEFAULT_FORWARD_HOST\n\tconst listenPort = options.port ? parsePort(options.port, 'listen port') : RANDOM_PORT\n\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst node = createNode()\n\n\tblank()\n\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim(`CONNECTING ${target}`))\n\n\tconst tunnel = await createForwardTunnel({\n\t\tdht: node,\n\t\thost: listenHost,\n\t\tkeyPair,\n\t\tport: listenPort,\n\t\tremotePublicKey,\n\t})\n\n\tconst boundPort = tunnel.listenPort ?? listenPort\n\n\tspinner.write(`LISTENING ${dim(`${listenHost}:${boundPort}`)}`)\n\tspinner.blank()\n\tspinner.stop()\n\n\tregisterShutdown(tunnel)\n}\n\n/**\n * Execute `hbeam bind` in reverse or forward proxy mode.\n *\n * @param argv - Positional command arguments after `bind`.\n * @param options - Optional command flags.\n * @returns Promise that resolves when command setup completes.\n */\nexport async function runBindCommand(\n\targv: string[],\n\toptions: BindCommandOptions = {},\n): Promise<void> {\n\tconst [target] = argv\n\n\tif (!target) {\n\t\tshowUsageError('Missing target argument.')\n\t}\n\n\tif (/^\\d+$/.test(target)) {\n\t\tawait runReverseBind(parsePort(target, 'target port'), options)\n\n\t\treturn\n\t}\n\n\tawait runForwardBind(target, options)\n}\n","/**\n * `hbeam connect` command implementation.\n *\n * Resolves named peers from the address book, ensures local identity exists,\n * and starts a connect-mode session against the peer public key.\n *\n * @module\n */\nimport { getPeer } from '@/lib/addressbook.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { blank, cyan, dim, log, logError, write } from '@/lib/log.ts'\nimport { runBeamSession } from '@/lib/session.ts'\n\nimport { Beam } from '../beam.ts'\n\nconst EXIT_FAILURE = 1\nconst PUBLIC_KEY_BYTES = 32\n\ninterface ConnectCommandOptions {\n\toutputPath?: string\n}\n\n/**\n * Execute `hbeam connect <name>`.\n *\n * @param argv - Positional command arguments after `connect`.\n * @param options - Optional command flags.\n * @returns Promise that resolves when the command has started the session.\n */\nexport async function runConnectCommand(\n\targv: string[],\n\toptions: ConnectCommandOptions = {},\n): Promise<void> {\n\tconst [name] = argv\n\tif (!name) {\n\t\tblank()\n\t\tlogError('Missing peer name.')\n\t\twrite(dim('Usage: hbeam connect <name>'))\n\t\tblank()\n\t\tprocess.exit(EXIT_FAILURE)\n\t}\n\n\tconst peer = await getPeer(name).catch(() => undefined)\n\tif (!peer) {\n\t\tblank()\n\t\tlogError(`Unknown peer: ${name}`)\n\t\tblank()\n\t\tprocess.exit(EXIT_FAILURE)\n\t}\n\n\tconst remotePublicKey = Buffer.from(peer.publicKey, 'hex')\n\tif (remotePublicKey.length !== PUBLIC_KEY_BYTES) {\n\t\tblank()\n\t\tlogError(`Invalid public key for peer: ${name}`)\n\t\tblank()\n\t\tprocess.exit(EXIT_FAILURE)\n\t}\n\n\tconst identity = await loadOrCreateIdentityWithMeta()\n\tif (identity.created) {\n\t\tblank()\n\t\tlog(dim('IDENTITY CREATED'))\n\t\twrite(cyan(identity.keyPair.publicKey.toString('hex')))\n\t}\n\n\tconst beam = new Beam({\n\t\tkeyPair: identity.keyPair,\n\t\tremotePublicKey,\n\t})\n\n\trunBeamSession(beam, {\n\t\tmode: 'connect',\n\t\toutputPath: options.outputPath,\n\t\tvalue: name,\n\t})\n}\n","/**\n * `hbeam peers` command implementation.\n *\n * Provides add/remove/list subcommands for managing the local peer address book.\n *\n * @module\n */\nimport { addPeer, getPeer, listPeers, removePeer } from '@/lib/addressbook.ts'\nimport { blank, bold, cyan, dim, log, logError, write } from '@/lib/log.ts'\nimport { confirm } from '@/lib/prompt.ts'\n\nconst EXIT_SUCCESS = 0\nconst EXIT_FAILURE = 1\nconst START_INDEX = 0\nconst PUBLIC_KEY_PREFIX_LENGTH = 8\nconst SECONDS_PER_MINUTE = 60\nconst MINUTES_PER_HOUR = 60\nconst HOURS_PER_DAY = 24\nconst DAYS_PER_WEEK = 7\nconst MILLISECONDS_PER_SECOND = 1000\nconst SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR\nconst SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY\nconst SECONDS_PER_WEEK = SECONDS_PER_DAY * DAYS_PER_WEEK\nconst EMPTY_PEERS = 0\n\n/**\n * Convert an ISO timestamp into a compact relative-age label.\n *\n * @param addedAt - ISO timestamp string.\n * @returns Relative age label (for example, `2h ago`).\n */\nfunction formatAge(addedAt: string): string {\n\tconst then = Date.parse(addedAt)\n\n\tif (Number.isNaN(then)) {\n\t\treturn 'unknown'\n\t}\n\n\tconst seconds = Math.floor((Date.now() - then) / MILLISECONDS_PER_SECOND)\n\n\tif (seconds < SECONDS_PER_MINUTE) {\n\t\treturn 'just now'\n\t}\n\n\tif (seconds < SECONDS_PER_HOUR) {\n\t\treturn `${Math.floor(seconds / SECONDS_PER_MINUTE)}m ago`\n\t}\n\n\tif (seconds < SECONDS_PER_DAY) {\n\t\treturn `${Math.floor(seconds / SECONDS_PER_HOUR)}h ago`\n\t}\n\n\tif (seconds < SECONDS_PER_WEEK) {\n\t\treturn `${Math.floor(seconds / SECONDS_PER_DAY)}d ago`\n\t}\n\n\treturn `${Math.floor(seconds / SECONDS_PER_WEEK)}w ago`\n}\n\n/**\n * Shorten a full public key for compact terminal display.\n *\n * @param publicKey - Full hex public key.\n * @returns Truncated key prefix with ellipsis.\n */\nfunction shortenKey(publicKey: string): string {\n\treturn `${publicKey.slice(START_INDEX, PUBLIC_KEY_PREFIX_LENGTH)}...`\n}\n\n/**\n * Print usage guidance for `hbeam peers`.\n *\n * @returns Nothing.\n */\nfunction usage(): void {\n\tlogError('Invalid peers command.')\n\twrite(dim('Usage: hbeam peers add <name> <public-key>'))\n\twrite(dim(' hbeam peers rm <name>'))\n\twrite(dim(' hbeam peers ls'))\n}\n\n/**\n * Handle `hbeam peers add <name> <public-key>`.\n *\n * @param name - Peer alias.\n * @param publicKey - Hex public key.\n * @returns Process exit code.\n */\nasync function handleAdd(name: string | undefined, publicKey: string | undefined): Promise<number> {\n\tif (!name || !publicKey) {\n\t\tblank()\n\t\tusage()\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\ttry {\n\t\tawait addPeer(name, publicKey)\n\t} catch (error) {\n\t\tblank()\n\t\tlogError((error as Error).message)\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\tblank()\n\tlog(bold('SAVED'))\n\twrite(cyan(name))\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n\n/**\n * Handle `hbeam peers rm <name>`.\n *\n * @param name - Peer alias.\n * @returns Process exit code.\n */\nasync function handleRemove(name: string | undefined): Promise<number> {\n\tif (!name) {\n\t\tblank()\n\t\tusage()\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\tconst peer = await getPeer(name).catch(() => undefined)\n\n\tif (!peer) {\n\t\tblank()\n\t\tlogError(`Unknown peer: ${name}`)\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\tblank()\n\tconst approved = await confirm(`REMOVE ${name}?`)\n\n\tif (!approved) {\n\t\tlog(dim('CANCELLED'))\n\t\tblank()\n\t\treturn EXIT_SUCCESS\n\t}\n\n\tawait removePeer(name)\n\tlog(bold('REMOVED'))\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n\n/**\n * Handle `hbeam peers ls`.\n *\n * @returns Process exit code.\n */\nasync function handleList(): Promise<number> {\n\tconst peers = await listPeers()\n\n\tblank()\n\tlog(bold('PEERS'))\n\n\tif (peers.length === EMPTY_PEERS) {\n\t\twrite(dim('No peers saved yet.'))\n\t\tblank()\n\t\treturn EXIT_SUCCESS\n\t}\n\n\tblank()\n\n\tfor (const peer of peers) {\n\t\twrite(`${peer.name} ${dim(shortenKey(peer.publicKey))} ${dim(formatAge(peer.addedAt))}`)\n\t}\n\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n\n/**\n * Execute `hbeam peers` subcommands.\n *\n * @param argv - Positional command arguments after `peers`.\n * @returns Process exit code.\n */\nexport async function runPeersCommand(argv: string[]): Promise<number> {\n\tconst [action, name, publicKey] = argv\n\n\tif (action === 'add') {\n\t\treturn handleAdd(name, publicKey)\n\t}\n\n\tif (action === 'rm') {\n\t\treturn handleRemove(name)\n\t}\n\n\tif (action === 'ls') {\n\t\treturn handleList()\n\t}\n\n\tblank()\n\tusage()\n\tblank()\n\n\treturn EXIT_FAILURE\n}\n","/**\n * `hbeam serve` command implementation.\n *\n * Announces a sender endpoint, streams one file with a control header,\n * and waits for protocol-level completion acknowledgement from receiver.\n *\n * @module\n */\nimport { createReadStream } from 'node:fs'\nimport { stat } from 'node:fs/promises'\nimport { basename, resolve } from 'node:path'\n\nimport { Beam } from '@/beam.ts'\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport {\n\tencodeHeader,\n\tfindHeaderLineEnd,\n\tformatFileSize,\n\tparseCompletionAck,\n} from '@/lib/file-protocol.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { createLifecycle } from '@/lib/lifecycle.ts'\nimport {\n\tblank,\n\tcreateSpinner,\n\tcyan,\n\tdim,\n\tgray,\n\tgreen,\n\tlog,\n\tlogError,\n\tred,\n\twrite,\n\tendInline,\n\twriteInline,\n} from '@/lib/log.ts'\nimport { createPulseFrames } from '@/lib/pulse.ts'\n\nimport type { ConnectionInfo, KeyPair } from '@/types.ts'\n\nconst EXIT_FAILURE = 1\nconst MIN_FILE_SIZE = 0\nconst ACK_TIMEOUT_MS = 15_000\nconst EMPTY_BUFFER_SIZE = 0\nconst EMPTY_BUFFER = Buffer.alloc(EMPTY_BUFFER_SIZE)\nconst FIRST_INDEX = 0\nconst NEXT_OFFSET = 1\n\ninterface ServeCommandOptions {\n\tlisten?: boolean\n}\n\n/**\n * Print a serve usage error and terminate with failure.\n *\n * @param message - Error message to display.\n * @returns Never returns (process exits).\n */\nfunction showUsageError(message: string): never {\n\tblank()\n\tlogError(message)\n\twrite(dim('Usage: hbeam serve <file> [--listen]'))\n\tblank()\n\tprocess.exit(EXIT_FAILURE)\n}\n\n/**\n * Resolve identity mode for the serve command.\n *\n * @param listen - Whether identity listen mode was requested.\n * @returns Announce label plus optional keypair override.\n */\nasync function resolveServeIdentity(listen: boolean | undefined): Promise<{\n\tannounceLabel: string\n\tkeyPair?: KeyPair\n}> {\n\tif (!listen) {\n\t\treturn { announceLabel: 'ANNOUNCING' }\n\t}\n\tconst identity = await loadOrCreateIdentityWithMeta()\n\tif (identity.created) {\n\t\tblank()\n\t\tlog(dim('IDENTITY CREATED'))\n\t\twrite(cyan(identity.keyPair.publicKey.toString('hex')))\n\t}\n\treturn {\n\t\tannounceLabel: 'ANNOUNCING',\n\t\tkeyPair: identity.keyPair,\n\t}\n}\n\n/**\n * Execute `hbeam serve <file>` to transfer one file to the first peer.\n *\n * @param argv - Positional command arguments after `serve`.\n * @param options - Optional command flags.\n * @returns Promise that resolves when command setup is complete.\n */\nexport async function runServeCommand(\n\targv: string[],\n\toptions: ServeCommandOptions = {},\n): Promise<void> {\n\tconst [targetFile] = argv\n\n\tif (!targetFile) {\n\t\tshowUsageError('Missing file path.')\n\t}\n\n\tconst filePath = resolve(targetFile)\n\tconst fileName = basename(filePath)\n\tconst fileStat = await stat(filePath).catch(() => undefined)\n\n\tif (!fileStat || !fileStat.isFile()) {\n\t\tshowUsageError(`Not a readable file: ${targetFile}`)\n\t}\n\n\tif (fileStat.size < MIN_FILE_SIZE) {\n\t\tshowUsageError(`Invalid file size: ${targetFile}`)\n\t}\n\n\tconst identity = await resolveServeIdentity(options.listen)\n\tconst beam = identity.keyPair\n\t\t? new Beam({ announce: true, keyPair: identity.keyPair })\n\t\t: new Beam(undefined, { announce: true })\n\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst lifecycle = createLifecycle(beam, spinner)\n\n\tlet awaitingAck = false\n\tlet ackBuffer = EMPTY_BUFFER\n\tlet ackTimeout: ReturnType<typeof globalThis.setTimeout> | undefined = undefined\n\n\t/**\n\t * Tear down transfer resources and destroy the beam.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction closeTransfer(): void {\n\t\tif (ackTimeout) {\n\t\t\tglobalThis.clearTimeout(ackTimeout)\n\t\t\tackTimeout = undefined\n\t\t}\n\n\t\tbeam.destroy()\n\t}\n\n\tblank()\n\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim(identity.announceLabel))\n\tspinner.write(cyan(beam.key))\n\n\tcopyToClipboard(beam.key)\n\n\tbeam.on('remote-address', ({ host, port }: ConnectionInfo) => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\t\tif (host) {\n\t\t\tspinner.write(dim(`ONLINE ${gray(`[${host}:${port}]`)}`))\n\t\t\tspinner.blank()\n\t\t\tspinner.write(`SERVING ${dim(`${fileName} (${formatFileSize(fileStat.size)})`)}`)\n\n\t\t\tspinner.blank()\n\t\t}\n\t})\n\n\tbeam.on('connected', () => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\t\tspinner.stop()\n\t\twriteInline(`SENDING ${dim(`${fileName}...`)}`)\n\n\t\tconst header = encodeHeader({ name: fileName, size: fileStat.size, type: 'file' })\n\t\tif (beam.write(header) === false) {\n\t\t\tbeam.once('drain', () => createReadStream(filePath).pipe(beam))\n\t\t\treturn\n\t\t}\n\t\tcreateReadStream(filePath).pipe(beam)\n\t})\n\n\tbeam.on('error', (error: Error) => {\n\t\tspinner.stop()\n\t\tconst isPeerNotFound = error.message.includes('PEER_NOT_FOUND')\n\t\tif (awaitingAck && error.message.includes('connection reset by peer')) {\n\t\t\tendInline(` ${dim('DISCONNECTED')}`)\n\t\t\tblank()\n\t\t\tcloseTransfer()\n\t\t\treturn\n\t\t}\n\t\tif (isPeerNotFound) {\n\t\t\tlog(red(dim('PEER NOT FOUND')))\n\t\t} else if (error.message.includes('connection reset by peer')) {\n\t\t\tlog(dim('PEER DISCONNECTED'))\n\t\t} else {\n\t\t\tlogError(error.message)\n\t\t}\n\t\tblank()\n\t\tif (!isPeerNotFound) {\n\t\t\tlifecycle.shutdown()\n\t\t}\n\t})\n\n\tbeam.on('data', (chunk: Buffer) => {\n\t\tif (!awaitingAck) {\n\t\t\treturn\n\t\t}\n\t\tackBuffer = Buffer.concat([ackBuffer, chunk])\n\t\t/**\n\t\t * Acks are newline-delimited JSON control frames, so we parse line-by-line\n\t\t * and ignore unrelated payload chunks until a valid ack appears.\n\t\t */\n\t\twhile (true) {\n\t\t\tconst lineEnd = findHeaderLineEnd(ackBuffer)\n\n\t\t\tif (lineEnd < FIRST_INDEX) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst line = ackBuffer.subarray(FIRST_INDEX, lineEnd)\n\t\t\tackBuffer = ackBuffer.subarray(lineEnd + NEXT_OFFSET)\n\t\t\tconst ack = parseCompletionAck(line)\n\n\t\t\tif (ack) {\n\t\t\t\tawaitingAck = false\n\t\t\t\tif (ack.ok) {\n\t\t\t\t\tendInline(` ${green('DONE')}`)\n\t\t\t\t} else {\n\t\t\t\t\tconst reason = ack.reason === 'cancelled' ? 'CANCELLED' : 'DECLINED'\n\t\t\t\t\tendInline(` ${dim(reason)}`)\n\t\t\t\t}\n\t\t\t\tblank()\n\t\t\t\tcloseTransfer()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t})\n\n\tbeam.on('end', () => beam.end())\n\tbeam.on('finish', () => {\n\t\tawaitingAck = true\n\t\tackTimeout = globalThis.setTimeout(() => {\n\t\t\tawaitingAck = false\n\t\t\tendInline(` ${dim('TIMED OUT')}`)\n\t\t\tblank()\n\t\t\tcloseTransfer()\n\t\t}, ACK_TIMEOUT_MS)\n\t})\n\n\t/**\n\t * Streamx opens lazily; force announce mode to start immediately.\n\t */\n\t;(beam as unknown as { resume(): void }).resume()\n}\n","/**\n * `hbeam whoami` command implementation.\n *\n * Displays the local persistent identity public key and copies it\n * to the system clipboard for easy sharing.\n *\n * @module\n */\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { blank, bold, cyan, dim, log, write } from '@/lib/log.ts'\n\nconst EXIT_SUCCESS = 0\n\n/**\n * Execute `hbeam whoami`.\n *\n * @returns Process exit code.\n */\nexport async function runWhoamiCommand(): Promise<number> {\n\tconst identity = await loadOrCreateIdentityWithMeta()\n\tconst publicKey = identity.keyPair.publicKey.toString('hex')\n\n\tblank()\n\n\tif (identity.created) {\n\t\tlog(dim('IDENTITY CREATED'))\n\t}\n\n\tlog(bold('IDENTITY'))\n\twrite(cyan(publicKey))\n\tcopyToClipboard(publicKey)\n\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n","#!/usr/bin/env node\n\n/**\n * Command-line entrypoint for the hbeam executable.\n *\n * Parses CLI flags/arguments, routes to subcommands, and starts\n * interactive announce/connect sessions for pipe and file-transfer flows.\n *\n * @module\n */\nimport mri from 'mri'\n\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { bold, cyan, dim, log, write, writeBlock } from '@/lib/log.ts'\nimport { runBeamSession } from '@/lib/session.ts'\n\nimport { Beam } from './beam.ts'\nimport { runBindCommand } from './commands/bind.ts'\nimport { runConnectCommand } from './commands/connect.ts'\nimport { runPeersCommand } from './commands/peers.ts'\nimport { runServeCommand } from './commands/serve.ts'\nimport { runWhoamiCommand } from './commands/whoami.ts'\n\nimport type { BeamOptions } from './types.ts'\n\nconst ARGV_OFFSET = 2\nconst EXIT_SUCCESS = 0\n\nconst NO_INDENT = ''\n\nconst argv = mri(process.argv.slice(ARGV_OFFSET), {\n\talias: { h: 'help', l: 'listen', o: 'output', p: 'port', v: 'version' },\n\tboolean: ['help', 'listen', 'version'],\n\tstring: ['host', 'output', 'port'],\n})\n\nif (argv.help) {\n\twriteBlock([\n\t\t`${bold('hbeam')} — end-to-end encrypted pipe over HyperDHT`,\n\t\t'',\n\t\t`${bold('Usage:')}`,\n\t\t` hbeam ${dim('[passphrase]')} ${dim('[options]')}`,\n\t\t` hbeam connect ${dim('<name>')}`,\n\t\t` hbeam bind ${dim('<port|passphrase|name|public-key>')} ${dim('[options]')}`,\n\t\t` hbeam peers ${dim('<add|rm|ls> ...')}`,\n\t\t` hbeam serve ${dim('<file>')} ${dim('[--listen]')}`,\n\t\t` hbeam whoami`,\n\t\t'',\n\t\t`${bold('Options:')}`,\n\t\t` ${dim('-l, --listen')} Listen using passphrase or identity`,\n\t\t` ${dim('-o, --output')} Save incoming file to a specific path`,\n\t\t` ${dim('-p, --port')} Local listen port (bind forward mode)`,\n\t\t` ${dim('--host')} Host target/listen host (bind mode)`,\n\t\t` ${dim('-h, --help')} Show this help`,\n\t\t` ${dim('-v, --version')} Show version`,\n\t\t'',\n\t\t`${bold('Examples:')}`,\n\t\t` ${dim('# Start a new pipe (generates a passphrase)')}`,\n\t\t\" echo 'hello' | hbeam\",\n\t\t'',\n\t\t` ${dim('# Connect to an existing pipe')}`,\n\t\t' hbeam <passphrase>',\n\t\t'',\n\t\t` ${dim('# Listen with a specific passphrase')}`,\n\t\t\" echo 'hello again' | hbeam <passphrase> --listen\",\n\t\t'',\n\t\t` ${dim('# Listen on your persistent identity')}`,\n\t\t' hbeam --listen',\n\t\t'',\n\t\t` ${dim('# Save and connect to peers by name')}`,\n\t\t' hbeam peers add workserver <public-key>',\n\t\t' hbeam connect workserver',\n\t\t'',\n\t\t` ${dim('# Expose local port 3000 over P2P')}`,\n\t\t' hbeam bind 3000 --listen',\n\t\t'',\n\t\t` ${dim('# Create local TCP tunnel to remote peer')}`,\n\t\t' hbeam bind workserver -p 8080',\n\t\t'',\n\t\t` ${dim('# Serve a single file')}`,\n\t\t' hbeam serve ./report.pdf',\n\t])\n\tprocess.exit(EXIT_SUCCESS)\n}\n\nif (argv.version) {\n\tconst pkg = (await import('../package.json')) as { version?: string }\n\twrite(pkg.version ?? '0.0.0', NO_INDENT)\n\tprocess.exit(EXIT_SUCCESS)\n}\n\nconst [firstArg, ...restArgs] = argv._ as string[]\nlet ranSubcommand = false\n\nif (firstArg === 'peers') {\n\tprocess.exit(await runPeersCommand(restArgs))\n}\nif (firstArg === 'connect') {\n\tawait runConnectCommand(restArgs, { outputPath: argv.output })\n\tranSubcommand = true\n}\nif (firstArg === 'bind') {\n\tawait runBindCommand(restArgs, { host: argv.host, listen: argv.listen, port: argv.port })\n\tranSubcommand = true\n}\nif (firstArg === 'serve') {\n\tawait runServeCommand(restArgs, { listen: argv.listen })\n\tranSubcommand = true\n}\nif (firstArg === 'whoami') {\n\tprocess.exit(await runWhoamiCommand())\n}\n\nif (!ranSubcommand) {\n\tconst passphrase = firstArg\n\n\tif (argv.listen && !passphrase) {\n\t\tconst identity = await loadOrCreateIdentityWithMeta()\n\t\tif (identity.created) {\n\t\t\tlog(dim('IDENTITY CREATED'))\n\t\t\twrite(cyan(identity.keyPair.publicKey.toString('hex')))\n\t\t}\n\n\t\tconst beam = new Beam({\n\t\t\tannounce: true,\n\t\t\tkeyPair: identity.keyPair,\n\t\t})\n\t\trunBeamSession(beam, {\n\t\t\tannounceLabel: 'ANNOUNCING',\n\t\t\tcopyValue: copyToClipboard,\n\t\t\tmode: 'announce',\n\t\t\tvalue: beam.key,\n\t\t})\n\t} else {\n\t\tconst beamOptions: BeamOptions | undefined = argv.listen ? { announce: true } : undefined\n\t\tconst beam = new Beam(passphrase, beamOptions)\n\t\trunBeamSession(beam, {\n\t\t\tannounceLabel: 'ANNOUNCING',\n\t\t\tcopyValue: copyToClipboard,\n\t\t\tmode: beam.announce ? 'announce' : 'connect',\n\t\t\toutputPath: argv.output,\n\t\t\tvalue: beam.announce ? beam.key : (passphrase ?? 'unknown'),\n\t\t})\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["EXIT_SUCCESS","MODULUS_EVEN","REMAINDER_ZERO","PUBLIC_KEY_BYTES","KEY_SEED_BYTES","isHex","FIRST_INDEX","EXIT_FAILURE","dim","dim","FIRST_INDEX","NEXT_OFFSET","dim","bold","red","PUBLIC_KEY_BYTES","KEY_SEED_BYTES","PUBLIC_KEY_BYTES","DEFAULT_FORWARD_HOST","EXIT_FAILURE","EXIT_SUCCESS","showUsageError","dim","bold","EXIT_FAILURE","dim","EXIT_SUCCESS","EXIT_FAILURE","dim","bold","dim","red","EXIT_SUCCESS","dim","bold","bold","dim"],"sources":["../src/lib/clipboard.ts","../src/lib/config.ts","../src/lib/encoding.ts","../src/lib/identity.ts","../src/lib/log.ts","../src/lib/file-protocol.ts","../src/lib/lifecycle.ts","../src/lib/prompt.ts","../src/lib/pulse.ts","../src/lib/session.ts","../src/lib/addressbook.ts","../src/lib/dht.ts","../src/beam.ts","../src/lib/tunnel.ts","../src/commands/bind.ts","../src/commands/connect.ts","../src/commands/peers.ts","../src/commands/serve.ts","../src/commands/whoami.ts","../src/cli.ts"],"sourcesContent":["/**\n * Cross-platform clipboard integration for terminal workflows.\n *\n * Tries common platform-native clipboard commands in priority order.\n *\n * @module\n */\nimport { spawnSync } from 'node:child_process'\n\ntype ClipboardCommand = Readonly<{\n\targs: readonly string[]\n\tcommand: string\n}>\n\nconst EXIT_SUCCESS = 0\nconst EMPTY_ARGS: readonly string[] = []\n\nconst DARWIN_COMMANDS: readonly ClipboardCommand[] = [{ args: EMPTY_ARGS, command: 'pbcopy' }]\nconst WINDOWS_COMMANDS: readonly ClipboardCommand[] = [{ args: EMPTY_ARGS, command: 'clip' }]\n\nconst LINUX_COMMANDS: readonly ClipboardCommand[] = [\n\t{ args: EMPTY_ARGS, command: 'wl-copy' },\n\t{ args: ['-selection', 'clipboard'], command: 'xclip' },\n\t{ args: ['--clipboard', '--input'], command: 'xsel' },\n]\n\n/**\n * Resolve the clipboard command list for the current platform.\n *\n * @returns Ordered command list to try for clipboard writes.\n */\nfunction getClipboardCommands(): readonly ClipboardCommand[] {\n\tif (process.platform === 'darwin') {\n\t\treturn DARWIN_COMMANDS\n\t}\n\tif (process.platform === 'win32') {\n\t\treturn WINDOWS_COMMANDS\n\t}\n\treturn LINUX_COMMANDS\n}\n\n/**\n * Attempt to write text to clipboard with a specific command.\n *\n * @param text - Text to place in clipboard.\n * @param item - Clipboard command and argument tuple.\n * @returns True when command succeeds.\n */\nfunction tryClipboardCommand(text: string, item: ClipboardCommand): boolean {\n\tconst result = spawnSync(item.command, item.args, {\n\t\tinput: text,\n\t\tstdio: ['pipe', 'ignore', 'ignore'],\n\t})\n\treturn !result.error && result.status === EXIT_SUCCESS\n}\n\n/**\n * Copy text to the system clipboard using common platform commands.\n *\n * @param text - Text to copy.\n * @returns True when at least one command succeeds.\n */\nexport function copyToClipboard(text: string): boolean {\n\tfor (const item of getClipboardCommands()) {\n\t\tif (tryClipboardCommand(text, item)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n","/**\n * Config-directory utilities for hbeam local state.\n *\n * Provides path resolution and JSON read/write helpers under `~/.config/hbeam`\n * (or an override directory via environment variable).\n *\n * @module\n */\nimport { mkdir, readFile, writeFile, chmod } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\n\nconst CONFIG_ROOT_DIR = '.config'\nconst APP_CONFIG_DIR = 'hbeam'\nconst CONFIG_DIR_ENV = 'HBEAM_CONFIG_DIR'\n\nconst DIR_MODE = 0o700\nconst FILE_MODE_SECURE = 0o600\n\n/**\n * Resolve the absolute path to the hbeam config directory.\n *\n * @returns Config directory path, honoring `HBEAM_CONFIG_DIR` when set.\n */\nexport function getConfigDir(): string {\n\treturn process.env[CONFIG_DIR_ENV] ?? join(homedir(), CONFIG_ROOT_DIR, APP_CONFIG_DIR)\n}\n\n/**\n * Resolve a filename under the hbeam config directory.\n *\n * @param filename - Relative config filename.\n * @returns Absolute path to the config file.\n */\nfunction resolveConfigPath(filename: string): string {\n\treturn join(getConfigDir(), filename)\n}\n\n/**\n * Ensure the hbeam config directory exists with private directory permissions.\n *\n * @returns Promise that resolves once the directory exists.\n */\nexport async function ensureConfigDir(): Promise<void> {\n\tawait mkdir(getConfigDir(), { mode: DIR_MODE, recursive: true })\n}\n\n/**\n * Read and parse a JSON file from the hbeam config directory.\n *\n * @param filename - Relative config filename.\n * @returns Parsed JSON object, or `undefined` when file does not exist.\n */\nexport async function readJsonFile<T>(filename: string): Promise<T | undefined> {\n\tconst path = resolveConfigPath(filename)\n\ttry {\n\t\tconst raw = await readFile(path, 'utf8')\n\t\treturn JSON.parse(raw) as T\n\t} catch (error) {\n\t\tconst err = error as NodeJS.ErrnoException\n\t\tif (err.code === 'ENOENT') {\n\t\t\treturn undefined\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/**\n * Write a JSON file into the hbeam config directory.\n *\n * Set `secure` for files containing private key material.\n *\n * @param filename - Relative config filename.\n * @param data - Serializable payload.\n * @param options - Write options.\n * @returns Promise that resolves when file write is complete.\n */\nexport async function writeJsonFile(\n\tfilename: string,\n\tdata: unknown,\n\toptions?: { secure?: boolean },\n): Promise<void> {\n\tconst path = resolveConfigPath(filename)\n\tawait ensureConfigDir()\n\tawait mkdir(dirname(path), { mode: DIR_MODE, recursive: true })\n\tawait writeFile(path, `${JSON.stringify(data, null, '\\t')}\\n`, 'utf8')\n\tif (options?.secure) {\n\t\tawait chmod(path, FILE_MODE_SECURE)\n\t}\n}\n","/**\n * Byte/string encoding helpers for hbeam key material.\n *\n * Provides base32 conversion helpers and secure random byte generation.\n *\n * @module\n */\nimport * as b4a from 'b4a'\nimport b32 from 'hi-base32'\nimport sodium from 'sodium-universal'\n\n/**\n * Encode a buffer as a lowercase base32 string without padding.\n *\n * @param buf - Raw bytes to encode.\n * @returns Base32-encoded string without trailing `=` padding.\n */\nexport function toBase32(buf: Buffer): string {\n\treturn b32.encode(buf).replace(/=/g, '').toLowerCase()\n}\n\n/**\n * Decode a base32 string back into a raw buffer.\n *\n * @param str - Base32-encoded input string.\n * @returns Decoded raw bytes.\n */\nexport function fromBase32(str: string): Buffer {\n\treturn b4a.from(b32.decode.asBytes(str.toUpperCase()))\n}\n\n/**\n * Generate cryptographically secure random bytes.\n *\n * @param length - Number of bytes to generate.\n * @returns Random byte buffer with the requested length.\n */\nexport function randomBytes(length: number): Buffer {\n\tconst buffer = b4a.alloc(length)\n\tsodium.randombytes_buf(buffer)\n\treturn buffer\n}\n","/**\n * Persistent identity keypair management for hbeam.\n *\n * Loads, validates, creates, and serializes identity key material stored\n * under the local config directory.\n *\n * @module\n */\nimport DHT from 'hyperdht'\n\nimport { readJsonFile, writeJsonFile } from './config.ts'\nimport { randomBytes } from './encoding.ts'\n\nimport type { Identity, KeyPair } from '../types.ts'\n\nconst IDENTITY_FILE = 'identity.json'\n\nconst MODULUS_EVEN = 2\nconst REMAINDER_ZERO = 0\nconst PUBLIC_KEY_BYTES = 32\nconst KEY_SEED_BYTES = 32\nconst SECRET_KEY_BYTES = 64\n\n/**\n * Convert a key buffer to a hex string.\n *\n * @param buffer - Key bytes.\n * @returns Lowercase hex representation.\n */\nfunction asHex(buffer: Buffer): string {\n\treturn buffer.toString('hex')\n}\n\n/**\n * Parse a hex string into a key buffer.\n *\n * @param hex - Hex-encoded bytes.\n * @returns Buffer decoded from hex.\n */\nfunction fromHex(hex: string): Buffer {\n\treturn Buffer.from(hex, 'hex')\n}\n\n/**\n * Check whether a string is valid even-length hex.\n *\n * @param value - Candidate hex string.\n * @returns True when string is valid hex.\n */\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\n\n/**\n * Validate and parse serialized identity data.\n *\n * @param value - Serialized identity object from disk.\n * @returns Keypair parsed from identity object.\n */\nfunction parseIdentity(value: Identity): KeyPair {\n\tif (!isHex(value.publicKey) || !isHex(value.secretKey)) {\n\t\tthrow new Error('Invalid identity file: keys must be hex-encoded')\n\t}\n\n\tconst publicKey = fromHex(value.publicKey)\n\tconst secretKey = fromHex(value.secretKey)\n\n\tif (publicKey.length !== PUBLIC_KEY_BYTES || secretKey.length !== SECRET_KEY_BYTES) {\n\t\tthrow new Error('Invalid identity file: unexpected key lengths')\n\t}\n\n\treturn { publicKey, secretKey }\n}\n\n/**\n * Serialize a keypair for disk storage.\n *\n * @param keyPair - Keypair to serialize.\n * @returns Identity object with hex-encoded keys.\n */\nfunction serializeIdentity(keyPair: KeyPair): Identity {\n\treturn {\n\t\tpublicKey: asHex(keyPair.publicKey),\n\t\tsecretKey: asHex(keyPair.secretKey),\n\t}\n}\n\n/**\n * Create a brand new identity keypair.\n *\n * @returns Freshly generated keypair.\n */\nfunction createIdentity(): KeyPair {\n\treturn DHT.keyPair(randomBytes(KEY_SEED_BYTES))\n}\n\n/**\n * Load a persisted identity if present; otherwise create and persist one.\n *\n * @returns Keypair and metadata indicating whether it was newly created.\n */\nexport async function loadOrCreateIdentityWithMeta(): Promise<{\n\tcreated: boolean\n\tkeyPair: KeyPair\n}> {\n\tconst existing = await readJsonFile<Identity>(IDENTITY_FILE)\n\tif (existing) {\n\t\treturn { created: false, keyPair: parseIdentity(existing) }\n\t}\n\n\tconst keyPair = createIdentity()\n\tawait writeJsonFile(IDENTITY_FILE, serializeIdentity(keyPair), { secure: true })\n\treturn { created: true, keyPair }\n}\n\n/**\n * Load or create the local hbeam identity keypair.\n *\n * @returns Local identity keypair.\n */\nexport async function loadOrCreateIdentity(): Promise<KeyPair> {\n\tconst { keyPair } = await loadOrCreateIdentityWithMeta()\n\treturn keyPair\n}\n\n/**\n * Return the public key hex string for the local identity.\n *\n * @returns Hex-encoded public key.\n */\nexport async function getPublicKeyHex(): Promise<string> {\n\tconst keyPair = await loadOrCreateIdentity()\n\treturn asHex(keyPair.publicKey)\n}\n","/**\n * Terminal output primitives and animated spinner utilities.\n *\n * Centralizes formatted stderr logging and in-place spinner rendering.\n *\n * @module\n */\nimport { dim, red, yellow } from 'colorette'\n\nexport { bold, cyan, dim, gray, green, italic, red, yellow } from 'colorette'\n\nconst SEPARATOR_WIDTH = 36\nconst CLEAR_LINE = '\\r\\u001B[2K'\n// eslint-disable-next-line no-control-regex\nconst ANSI_ESCAPE = /\\u001B\\[[0-9;]*m/g\nconst NO_OFFSET = 0\nconst DEFAULT_COLUMNS = 80\nconst MIN_LINES = 1\n\nexport const INDENT = ' '\nexport const SEPARATOR = dim('╌'.repeat(SEPARATOR_WIDTH))\n\n/**\n * Write a line to stderr at the standard indent level.\n *\n * @param message - Text to print.\n * @param indent - Prefix to prepend before the message.\n * @returns Nothing.\n */\nexport function write(message: string, indent: string = INDENT): void {\n\tprocess.stderr.write(`${indent}${message}\\n`)\n}\n\n/**\n * Write to stderr at the standard indent level without a trailing newline.\n *\n * Useful for partial lines that will be completed later (e.g. status updates).\n *\n * @param message - Text to print.\n * @returns Nothing.\n */\nexport function writeInline(message: string): void {\n\tprocess.stderr.write(`${INDENT}${message}`)\n}\n\n/**\n * Complete a partial line previously started by {@link writeInline}.\n *\n * @param message - Suffix text to append (a trailing newline is added).\n * @returns Nothing.\n */\nexport function endInline(message: string): void {\n\tprocess.stderr.write(`${message}\\n`)\n}\n\n/**\n * Write a blank line to stderr.\n *\n * @returns Nothing.\n */\nexport function blank(): void {\n\tprocess.stderr.write('\\n')\n}\n\n/**\n * Write a pre-formatted block (multiple lines) to stderr.\n *\n * @param lines - Lines to print.\n * @returns Nothing.\n */\nexport function writeBlock(lines: string[]): void {\n\tfor (const line of lines) {\n\t\tprocess.stderr.write(`${INDENT}${line}\\n`)\n\t}\n}\n\n/**\n * Write a status message to stderr at the standard indent level.\n *\n * @param message - Status text.\n * @returns Nothing.\n */\nexport function log(message: string): void {\n\twrite(message)\n}\n\n/**\n * Write an error message to stderr at the standard indent level.\n *\n * @param message - Error text.\n * @returns Nothing.\n */\nexport function logError(message: string): void {\n\tprocess.stderr.write(`${INDENT}${red('ERROR')} ${message}\\n`)\n}\n\n/**\n * Write a warning/notice message to stderr with a yellow prefix.\n *\n * @param message - Warning text.\n * @returns Nothing.\n */\nexport function logWarn(message: string): void {\n\tprocess.stderr.write(`${yellow('!')} ${message}\\n`)\n}\n\n/**\n * Clear the current terminal line, falling back to newline on non-TTY.\n *\n * @returns Nothing.\n */\nexport function clearLine(): void {\n\tif (process.stderr.isTTY) {\n\t\tprocess.stderr.write(CLEAR_LINE)\n\t} else {\n\t\tprocess.stderr.write('\\n')\n\t}\n}\n\n// -- Spinner ----------------------------------------------------------------\n\n/**\n * Build ANSI sequence to move cursor up N lines.\n *\n * @param n - Number of lines.\n * @returns ANSI escape sequence.\n */\nfunction cursorUp(n: number): string {\n\treturn `\\u001B[${n}A`\n}\n\n/**\n * Build ANSI sequence to move cursor down N lines.\n *\n * @param n - Number of lines.\n * @returns ANSI escape sequence.\n */\nfunction cursorDown(n: number): string {\n\treturn `\\u001B[${n}B`\n}\n\n/**\n * Count visual terminal lines an indented message occupies, accounting\n * for ANSI escape codes and terminal width. Falls back to 1 on non-TTY.\n *\n * @param message - Possibly ANSI-styled text (without indent prefix).\n * @returns Number of visual lines.\n */\nfunction visualLines(message: string): number {\n\tconst columns = process.stderr.columns || DEFAULT_COLUMNS\n\tconst width = INDENT.length + message.replace(ANSI_ESCAPE, '').length\n\treturn Math.max(MIN_LINES, Math.ceil(width / columns))\n}\n\n/** Handle for a line that animates in-place while content prints below. */\nexport interface Spinner {\n\t/** Write a blank line below the spinner and track the cursor offset. */\n\tblank(): void\n\t/** Render the first frame and begin the animation loop. */\n\tstart(): void\n\t/** Stop the animation loop. */\n\tstop(): void\n\t/** Write an indented line below the spinner and track the cursor offset. */\n\twrite(message: string): void\n}\n\n/**\n * Animate a single line in-place while content continues to print below it.\n *\n * @param frames - Spinner frame strings.\n * @param intervalMs - Frame interval in milliseconds.\n * @returns Spinner controller for writing/stopping.\n */\nexport function createSpinner(frames: readonly string[], intervalMs: number): Spinner {\n\tlet offset = NO_OFFSET\n\tlet frameIndex = NO_OFFSET\n\tlet timer: ReturnType<typeof globalThis.setInterval> | undefined = undefined\n\n\t/**\n\t * Render the current frame in-place at the spinner cursor location.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction render(): void {\n\t\tif (offset > NO_OFFSET) {\n\t\t\tprocess.stderr.write(cursorUp(offset))\n\t\t}\n\t\tprocess.stderr.write(`${CLEAR_LINE}${INDENT}${frames[frameIndex]}`)\n\t\tif (offset > NO_OFFSET) {\n\t\t\tprocess.stderr.write(`${cursorDown(offset)}\\r`)\n\t\t}\n\t\tframeIndex++\n\t\tif (frameIndex >= frames.length) {\n\t\t\tframeIndex = NO_OFFSET\n\t\t}\n\t}\n\n\treturn {\n\t\tblank(): void {\n\t\t\tblank()\n\t\t\toffset++\n\t\t},\n\t\tstart(): void {\n\t\t\trender()\n\t\t\tprocess.stderr.write('\\n')\n\t\t\toffset++\n\t\t\ttimer = globalThis.setInterval(render, intervalMs)\n\t\t},\n\t\tstop(): void {\n\t\t\tif (timer) {\n\t\t\t\tglobalThis.clearInterval(timer)\n\t\t\t\ttimer = undefined\n\t\t\t}\n\t\t},\n\t\twrite(message: string): void {\n\t\t\twrite(message)\n\t\t\toffset += visualLines(message)\n\t\t},\n\t}\n}\n","/**\n * Line-delimited control-frame protocol for single-file transfers.\n *\n * Defines and parses file header frames and completion acknowledgement frames\n * exchanged between sender and receiver during `hbeam serve` sessions.\n *\n * @module\n */\nconst FILE_TYPE = 'file'\nconst FILE_COMPLETE_TYPE = 'file-complete'\nconst NEWLINE = '\\n'\nconst BYTES_PER_KIB = 1024\nconst UNIT_PRECISION = 1\nconst FIRST_INDEX = 0\nconst MIN_SIZE = 0\nconst LAST_INDEX_OFFSET = 1\nconst UNIT_LABELS = ['B', 'KB', 'MB', 'GB', 'TB'] as const\n\nexport interface FileHeader {\n\tname: string\n\tsize: number\n\ttype: typeof FILE_TYPE\n}\n\nexport interface FileCompletionAck {\n\tok: boolean\n\treason?: string\n\ttype: typeof FILE_COMPLETE_TYPE\n}\n\n/**\n * Encode a file header as a newline-delimited JSON frame.\n *\n * @param header - File header payload.\n * @returns Encoded frame bytes.\n */\nexport function encodeHeader(header: FileHeader): Buffer {\n\treturn Buffer.from(`${JSON.stringify(header)}${NEWLINE}`, 'utf8')\n}\n\n/**\n * Encode a completion acknowledgement as a newline-delimited JSON frame.\n *\n * @param ack - Completion acknowledgement payload.\n * @returns Encoded frame bytes.\n */\nexport function encodeCompletionAck(ack: FileCompletionAck): Buffer {\n\treturn Buffer.from(`${JSON.stringify(ack)}${NEWLINE}`, 'utf8')\n}\n\n/**\n * Check whether buffered bytes begin with a file-header control frame.\n *\n * @param chunk - Buffered inbound bytes.\n * @returns True when the first line appears to be a file header.\n */\nexport function isFileHeader(chunk: Buffer): boolean {\n\tconst lineEnd = findHeaderLineEnd(chunk)\n\n\t/**\n\t * Header key order is not guaranteed by JSON serializers, so detection is\n\t * intentionally permissive as long as the first line is JSON with `type:file`.\n\t */\n\tconst candidate = (lineEnd >= MIN_SIZE ? chunk.subarray(FIRST_INDEX, lineEnd) : chunk).toString(\n\t\t'utf8',\n\t)\n\tconst trimmed = candidate.trimStart()\n\n\treturn trimmed.startsWith('{') && trimmed.includes(`\"type\":\"${FILE_TYPE}\"`)\n}\n\n/**\n * Parse and validate a file-header control frame.\n *\n * @param line - Header frame bytes (without trailing newline).\n * @returns Validated file header payload.\n */\nexport function parseFileHeader(line: Buffer): FileHeader {\n\tconst parsed = JSON.parse(line.toString('utf8')) as Partial<FileHeader>\n\n\tif (parsed.type !== FILE_TYPE) {\n\t\tthrow new Error('Invalid file header type')\n\t}\n\n\tif (!parsed.name || typeof parsed.name !== 'string') {\n\t\tthrow new Error('Invalid file header name')\n\t}\n\n\tif (\n\t\ttypeof parsed.size !== 'number' ||\n\t\t!Number.isSafeInteger(parsed.size) ||\n\t\tparsed.size < MIN_SIZE\n\t) {\n\t\tthrow new Error('Invalid file header size')\n\t}\n\n\treturn { name: parsed.name, size: parsed.size, type: FILE_TYPE }\n}\n\n/**\n * Parse and validate a completion-ack control frame.\n *\n * @param line - Ack frame bytes (without trailing newline).\n * @returns Parsed ack payload, or `undefined` if frame is unrelated/invalid.\n */\nexport function parseCompletionAck(line: Buffer): FileCompletionAck | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(line.toString('utf8')) as Partial<FileCompletionAck>\n\n\t\tif (parsed.type !== FILE_COMPLETE_TYPE || typeof parsed.ok !== 'boolean') {\n\t\t\treturn undefined\n\t\t}\n\n\t\tif (parsed.reason !== undefined && typeof parsed.reason !== 'string') {\n\t\t\treturn undefined\n\t\t}\n\n\t\treturn {\n\t\t\tok: parsed.ok,\n\t\t\treason: parsed.reason,\n\t\t\ttype: FILE_COMPLETE_TYPE,\n\t\t}\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Find the end offset of the first newline-delimited frame.\n *\n * @param chunk - Buffered bytes to scan.\n * @returns Index of first newline, or `-1` when no full line exists yet.\n */\nexport function findHeaderLineEnd(chunk: Buffer): number {\n\treturn chunk.indexOf(NEWLINE)\n}\n\n/**\n * Format byte size into a human-readable unit string.\n *\n * @param bytes - Size in bytes.\n * @returns Human-readable size string (for example, `4 B`, `2.4 MB`).\n */\nexport function formatFileSize(bytes: number): string {\n\tif (!Number.isFinite(bytes) || bytes < MIN_SIZE) {\n\t\treturn `0 ${UNIT_LABELS[FIRST_INDEX]}`\n\t}\n\n\tconst lastUnitIndex = UNIT_LABELS.length - LAST_INDEX_OFFSET\n\n\tlet size = bytes\n\tlet unitIndex = FIRST_INDEX\n\n\twhile (size >= BYTES_PER_KIB && unitIndex < lastUnitIndex) {\n\t\tsize /= BYTES_PER_KIB\n\t\tunitIndex++\n\t}\n\n\tif (unitIndex === FIRST_INDEX) {\n\t\treturn `${Math.round(size)} ${UNIT_LABELS[unitIndex]}`\n\t}\n\n\treturn `${size.toFixed(UNIT_PRECISION)} ${UNIT_LABELS[unitIndex]}`\n}\n","/**\n * Session lifecycle controller for graceful shutdown behavior.\n *\n * Encapsulates SIGINT handling, spinner teardown, and timed beam destruction.\n *\n * @module\n */\nimport { blank, clearLine, dim, log } from './log.ts'\n\nimport type { Beam } from '../beam.ts'\n\nconst EXIT_FAILURE = 1\nconst SHUTDOWN_TIMEOUT_MS = 2000\n\n/** Controller for graceful shutdown of a beam session. */\nexport interface Lifecycle {\n\t/** Returns true if shutdown is in progress (use as an early-return guard). */\n\tdone(): boolean\n\t/** Tear down the beam, stop the spinner, and exit after a grace period. */\n\tshutdown(): void\n}\n\n/**\n * Create a lifecycle controller that manages SIGINT handling and graceful shutdown.\n *\n * Registers a one-shot SIGINT handler on creation. All shutdown state is\n * encapsulated — callers just check `done()` and call `shutdown()`.\n *\n * @param beam - Active beam instance to destroy during shutdown.\n * @param spinner - Optional spinner handle to stop during shutdown.\n * @returns Lifecycle controller with `done` and `shutdown`.\n */\nexport function createLifecycle(beam: Beam, spinner?: { stop(): void }): Lifecycle {\n\tlet isShuttingDown = false\n\n\t/**\n\t * Start graceful teardown and register a forced-exit timeout.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction shutdown(): void {\n\t\tif (isShuttingDown) {\n\t\t\treturn\n\t\t}\n\t\tisShuttingDown = true\n\t\tspinner?.stop()\n\n\t\tlog(dim('SHUTTING DOWN'))\n\t\tblank()\n\n\t\tconst timeout = globalThis.setTimeout(() => {\n\t\t\tprocess.exit(EXIT_FAILURE)\n\t\t}, SHUTDOWN_TIMEOUT_MS)\n\n\t\tbeam.destroy()\n\t\tbeam.on('close', () => {\n\t\t\tglobalThis.clearTimeout(timeout)\n\t\t})\n\t}\n\n\tprocess.once('SIGINT', () => {\n\t\tclearLine()\n\t\tshutdown()\n\t})\n\n\treturn {\n\t\tdone(): boolean {\n\t\t\treturn isShuttingDown\n\t\t},\n\t\tshutdown,\n\t}\n}\n","/**\n * Minimal interactive prompt helpers for CLI sessions.\n *\n * Includes raw-key confirmation prompts and editable line-input prompts.\n *\n * @module\n */\nimport { createInterface } from 'node:readline/promises'\n\nimport { INDENT, dim } from './log.ts'\n\nconst YES = 'y'\nconst NO = 'n'\nconst CTRL_C = '\\u0003'\nconst ENTER = '\\r'\nconst FIRST_CHAR_INDEX = 0\nconst EMPTY_INPUT = ''\n\n/**\n * Extract the normalized first character from a stdin buffer.\n *\n * @param data - Raw stdin chunk.\n * @returns Lowercased first character, or empty string.\n */\nfunction firstChar(data: Buffer): string {\n\treturn data.toString('utf8').toLowerCase().charAt(FIRST_CHAR_INDEX)\n}\n\n/**\n * Prompt for a yes/no confirmation with `y/N` semantics.\n *\n * @param message - Prompt message body.\n * @returns True when user confirms with `y`.\n */\nexport async function confirm(message: string): Promise<boolean> {\n\tprocess.stderr.write(`${INDENT}${message} ${dim('(y/N)')} `)\n\n\tconst stdin = process.stdin\n\tif (!stdin.isTTY || typeof stdin.setRawMode !== 'function') {\n\t\tprocess.stderr.write('\\n')\n\t\treturn false\n\t}\n\n\tconst originalRaw = stdin.isRaw\n\n\treturn await new Promise<boolean>(resolve => {\n\t\t/**\n\t\t * Restore terminal state and resolve prompt result.\n\t\t *\n\t\t * @param answer - Final confirmation result.\n\t\t * @returns Nothing.\n\t\t */\n\t\tfunction cleanup(answer: boolean): void {\n\t\t\tstdin.setRawMode(Boolean(originalRaw))\n\t\t\tstdin.pause()\n\t\t\tstdin.removeListener('data', onData)\n\t\t\tprocess.stderr.write(`${answer ? YES : NO}\\n`)\n\t\t\tresolve(answer)\n\t\t}\n\n\t\t/**\n\t\t * Handle raw keypress bytes for the confirmation prompt.\n\t\t *\n\t\t * @param data - Raw stdin bytes.\n\t\t * @returns Nothing.\n\t\t */\n\t\tfunction onData(data: Buffer): void {\n\t\t\tconst key = firstChar(data)\n\t\t\tif (key === CTRL_C) {\n\t\t\t\tprocess.stderr.write('\\n')\n\t\t\t\tprocess.kill(process.pid, 'SIGINT')\n\t\t\t\tcleanup(false)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (key === YES) {\n\t\t\t\tcleanup(true)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (key === NO || key === '' || key === ENTER) {\n\t\t\t\tcleanup(false)\n\t\t\t}\n\t\t}\n\n\t\tstdin.setRawMode(true)\n\t\tstdin.resume()\n\t\tstdin.on('data', onData)\n\t})\n}\n\n/**\n * Prompt for line input with an editable pre-filled placeholder.\n *\n * @param message - Prompt message body.\n * @param placeholder - Default value shown to the user.\n * @returns User-entered value, or placeholder when input is empty/non-interactive.\n */\nexport async function input(message: string, placeholder: string): Promise<string> {\n\tif (!process.stdin.isTTY || !process.stderr.isTTY) {\n\t\treturn placeholder\n\t}\n\n\tconst rl = createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stderr,\n\t\tterminal: true,\n\t})\n\n\ttry {\n\t\tconst answerPromise = rl.question(`${INDENT}${message} `)\n\t\trl.write(placeholder)\n\t\tconst answer = await answerPromise\n\t\tconst trimmed = answer.trim()\n\n\t\treturn trimmed === EMPTY_INPUT ? placeholder : trimmed\n\t} finally {\n\t\trl.close()\n\t}\n}\n","/**\n * Spinner frame generation for the hbeam pulse animation.\n *\n * Produces styled frame sequences with brightness gradients for terminal UI.\n *\n * @module\n */\nimport { bold, dim } from 'colorette'\n\nconst INTERVAL_MS = 125\nconst PEAK_A = 5\nconst PEAK_B = 15\nconst PEAK_WRAP = 25\nconst DIST_PEAK = 0\nconst DIST_NEAR = 1\n\nconst RAW_FRAMES: readonly string[] = [\n\t' ',\n\t'· ',\n\t'·· ',\n\t'··· ',\n\t'···· ',\n\t'·····',\n\t' ····',\n\t' ···',\n\t' ··',\n\t' ·',\n\t' ',\n\t' ·',\n\t' ··',\n\t' ···',\n\t' ····',\n\t'·····',\n\t'···· ',\n\t'··· ',\n\t'·· ',\n\t'· ',\n\t' ',\n]\n\n/**\n * Generate the styled spinner frames for the HBEAM pulse animation.\n *\n * Each frame is rendered with a brightness gradient: bold at peak,\n * normal near peak, and dim everywhere else.\n *\n * @param label - The text label to prefix each frame (e.g. \"HBEAM\").\n * @returns An object with the styled `frames` array and `intervalMs` timing.\n */\nexport function createPulseFrames(label: string): { frames: string[]; intervalMs: number } {\n\tconst frames = RAW_FRAMES.map((s, i) => {\n\t\tconst distanceToPeak = Math.min(\n\t\t\tMath.abs(i - PEAK_A),\n\t\t\tMath.abs(i - PEAK_B),\n\t\t\tMath.abs(i - PEAK_WRAP),\n\t\t)\n\t\tlet glyph = dim(s)\n\t\tif (distanceToPeak === DIST_PEAK) {\n\t\t\tglyph = bold(s)\n\t\t} else if (distanceToPeak === DIST_NEAR) {\n\t\t\tglyph = s\n\t\t}\n\t\treturn `${bold(label)} ${glyph}`\n\t})\n\n\treturn { frames, intervalMs: INTERVAL_MS }\n}\n","/**\n * Shared interactive session runtime for announce/connect flows.\n *\n * Handles spinner/lifecycle output, stream piping behavior, and protocol-aware\n * file-receive mode with completion acknowledgements.\n *\n * @module\n */\nimport { createWriteStream } from 'node:fs'\nimport { mkdir } from 'node:fs/promises'\nimport { dirname, relative, resolve } from 'node:path'\n\nimport {\n\tencodeCompletionAck,\n\tfindHeaderLineEnd,\n\tformatFileSize,\n\tisFileHeader,\n\tparseFileHeader,\n} from './file-protocol.ts'\nimport { createLifecycle } from './lifecycle.ts'\nimport {\n\tblank,\n\tbold,\n\tcreateSpinner,\n\tcyan,\n\tdim,\n\tendInline,\n\tgray,\n\tgreen,\n\tINDENT,\n\tlog,\n\tlogError,\n\tred,\n\tSEPARATOR,\n\twrite,\n\twriteInline,\n} from './log.ts'\nimport { confirm, input } from './prompt.ts'\nimport { createPulseFrames } from './pulse.ts'\n\nimport type { Beam } from '../beam.ts'\nimport type { ConnectionInfo } from '../types.ts'\n\ntype ReceiveMode = 'unknown' | 'pipe' | 'file' | 'file-stdout'\n\nconst FIRST_INDEX = 0\nconst NEXT_OFFSET = 1\nconst NO_DATA = 0\nconst KEEPALIVE_MS = 60_000\nconst CONNECTION_RESET = 'connection reset by peer'\n\nexport interface SessionOptions {\n\tannounceLabel?: string\n\tcopyValue?: (text: string) => void\n\tmode: 'announce' | 'connect'\n\toutputPath?: string\n\tvalue: string\n}\n\n/**\n * Run the standard hbeam CLI session UI and stdin/stdout piping.\n *\n * @param beam - Active beam instance for this session.\n * @param options - Session rendering and behavior options.\n * @returns Nothing.\n */\nexport function runBeamSession(beam: Beam, options: SessionOptions): void {\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst lifecycle = createLifecycle(beam, spinner)\n\n\tblank()\n\n\tspinner.start()\n\tspinner.blank()\n\n\tif (options.mode === 'announce') {\n\t\tspinner.write(dim(options.announceLabel ?? 'ANNOUNCING'))\n\t\tspinner.write(cyan(options.value))\n\t\toptions.copyValue?.(options.value)\n\t} else {\n\t\tspinner.write(dim('CONNECTING'))\n\t\tspinner.write(cyan(options.value))\n\t}\n\n\tbeam.on('remote-address', ({ host, port }: ConnectionInfo) => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\n\t\tif (host) {\n\t\t\tspinner.write(dim(`ONLINE ${gray(`[${host}:${port}]`)}`))\n\t\t\tspinner.blank()\n\t\t}\n\t})\n\n\tbeam.on('connected', () => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\n\t\tspinner.stop()\n\n\t\tif (!process.stdin.isTTY) {\n\t\t\twriteInline(bold('CONNECTED...'))\n\t\t} else {\n\t\t\tlog(bold('CONNECTED'))\n\t\t}\n\t})\n\n\t/**\n\t * Receive-side state machine:\n\t * - unknown: we have not yet decided if stream is plain pipe data or file transfer\n\t * - pipe: classic stdin/stdout hbeam mode\n\t * - file: file transfer writing to disk\n\t * - file-stdout: file transfer streaming bytes to stdout (non-interactive mode)\n\t */\n\tlet receiveMode: ReceiveMode = 'unknown'\n\tlet receivedPipeData = false\n\tlet pipeFrameClosed = false\n\tlet awaitingPrompt = false\n\tlet streamDone = false\n\tlet streamTerminationHandled = false\n\tlet keepAlive: ReturnType<typeof globalThis.setInterval> | undefined = undefined\n\tlet pendingChunks: Buffer[] = []\n\tlet fileStream: ReturnType<typeof createWriteStream> | undefined = undefined\n\tlet filePath: string | undefined = undefined\n\n\t/**\n\t * Stop and clear the temporary keepalive timer.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction clearKeepAlive(): void {\n\t\tif (keepAlive) {\n\t\t\tglobalThis.clearInterval(keepAlive)\n\t\t\tkeepAlive = undefined\n\t\t}\n\t}\n\n\t/**\n\t * Flush and close the destination file stream, then send completion ack.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction finalizeFile(): void {\n\t\tclearKeepAlive()\n\n\t\tif (!fileStream) {\n\t\t\treturn\n\t\t}\n\n\t\tfileStream.end(() => {\n\t\t\tconst absolutePath = filePath ?? ''\n\t\t\tconst displayPath = relative(process.cwd(), absolutePath) || absolutePath\n\t\t\tlog(`SAVED ${dim(displayPath)}`)\n\n\t\t\tblank()\n\t\t\tbeam.write(encodeCompletionAck({ ok: true, type: 'file-complete' }))\n\t\t\tbeam.end()\n\t\t})\n\n\t\tfileStream = undefined\n\t}\n\n\t/**\n\t * Close the pipe-mode content frame with a trailing separator.\n\t * No-op if no pipe data was received or the frame is already closed.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction closePipeFrame(): void {\n\t\tif (!receivedPipeData || pipeFrameClosed) {\n\t\t\treturn\n\t\t}\n\t\tpipeFrameClosed = true\n\t\twrite(SEPARATOR)\n\t}\n\n\t/**\n\t * Render normal pipe-mode output with visual framing/indentation.\n\t *\n\t * @param chunk - Raw inbound data chunk.\n\t * @returns Nothing.\n\t */\n\tfunction writePipeChunk(chunk: Buffer): void {\n\t\tif (!receivedPipeData) {\n\t\t\treceivedPipeData = true\n\t\t\twrite(SEPARATOR)\n\t\t}\n\n\t\tprocess.stdout.write(chunk.toString().replace(/^(?!$)/gm, INDENT))\n\t}\n\n\t/**\n\t * Finalize receive-side session behavior once stream termination is observed.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction onStreamDone(): void {\n\t\tstreamDone = true\n\n\t\t/**\n\t\t * Defer finalization until the async prompt flow finishes.\n\t\t */\n\t\tif (awaitingPrompt) {\n\t\t\treturn\n\t\t}\n\n\t\tif (streamTerminationHandled) {\n\t\t\treturn\n\t\t}\n\n\t\tstreamTerminationHandled = true\n\n\t\tif (receiveMode === 'file') {\n\t\t\tfinalizeFile()\n\t\t\treturn\n\t\t}\n\n\t\tif (receiveMode === 'file-stdout') {\n\t\t\tbeam.write(encodeCompletionAck({ ok: true, type: 'file-complete' }))\n\t\t} else {\n\t\t\tclosePipeFrame()\n\n\t\t\t/** Confirm clean delivery unless shutdown is already in progress. */\n\t\t\tif (!lifecycle.done()) {\n\t\t\t\tif (!process.stdin.isTTY && !receivedPipeData) {\n\t\t\t\t\tendInline(` ${green('DONE')}`)\n\t\t\t\t} else {\n\t\t\t\t\tlog(dim('DONE'))\n\t\t\t\t}\n\t\t\t\tblank()\n\t\t\t}\n\t\t}\n\n\t\tbeam.end()\n\t}\n\n\tbeam.on('end', onStreamDone)\n\tbeam.on('close', onStreamDone)\n\n\tbeam.on('error', (error: Error) => {\n\t\tspinner.stop()\n\t\tconst isPeerNotFound = error.message.includes('PEER_NOT_FOUND')\n\t\tconst isReset = error.message.includes(CONNECTION_RESET)\n\n\t\tif (\n\t\t\tisReset &&\n\t\t\t(awaitingPrompt || receiveMode === 'file' || receiveMode === 'file-stdout')\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tclosePipeFrame()\n\n\t\tif (isPeerNotFound) {\n\t\t\tlog(red(dim('PEER NOT FOUND')))\n\t\t} else if (isReset) {\n\t\t\tlog(dim('PEER DISCONNECTED'))\n\t\t} else {\n\t\t\tlogError(error.message)\n\t\t}\n\n\t\tblank()\n\n\t\tif (!isPeerNotFound) {\n\t\t\tlifecycle.shutdown()\n\t\t}\n\t})\n\n\t/**\n\t * Resolve where an incoming file should be written.\n\t *\n\t * @param fileName - Suggested source filename from header.\n\t * @returns Output path, empty string when cancelled, or undefined for stdout mode.\n\t */\n\tasync function promptForOutputPath(fileName: string): Promise<string | undefined> {\n\t\tif (options.outputPath) {\n\t\t\treturn resolve(options.outputPath)\n\t\t}\n\n\t\tif (!process.stdout.isTTY) {\n\t\t\treturn undefined\n\t\t}\n\n\t\tconst shouldSave = await confirm('Save file?')\n\n\t\tif (!shouldSave) {\n\t\t\treturn ''\n\t\t}\n\n\t\tconst chosen = await input('Save to:', `./${fileName}`)\n\n\t\treturn resolve(process.cwd(), chosen)\n\t}\n\n\t/**\n\t * Initialize file-receive mode from the first header-bearing data chunk.\n\t *\n\t * @param headerChunk - Initial chunk containing the file header line.\n\t * @returns Promise that resolves after file mode setup completes.\n\t */\n\tasync function startFileReceive(headerChunk: Buffer): Promise<void> {\n\t\tconst lineEnd = findHeaderLineEnd(headerChunk)\n\t\tconst header = parseFileHeader(headerChunk.subarray(FIRST_INDEX, lineEnd))\n\t\tconst remainder = headerChunk.subarray(lineEnd + NEXT_OFFSET)\n\n\t\tlog(`RECEIVED ${dim(`${header.name} (${formatFileSize(header.size)})`)}`)\n\n\t\tprocess.stdin.unpipe(beam)\n\t\tkeepAlive = globalThis.setInterval(() => {}, KEEPALIVE_MS)\n\n\t\tawaitingPrompt = true\n\t\tconst outputPath = await promptForOutputPath(header.name)\n\t\tawaitingPrompt = false\n\n\t\tif (outputPath === '') {\n\t\t\tclearKeepAlive()\n\t\t\tlog(dim('CANCELLED'))\n\t\t\tblank()\n\t\t\tbeam.write(\n\t\t\t\tencodeCompletionAck({\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: 'cancelled',\n\t\t\t\t\ttype: 'file-complete',\n\t\t\t\t}),\n\t\t\t)\n\t\t\tbeam.end()\n\t\t\treturn\n\t\t}\n\n\t\tif (outputPath === undefined) {\n\t\t\tclearKeepAlive()\n\t\t\treceiveMode = 'file-stdout'\n\t\t\tif (remainder.length > NO_DATA) {\n\t\t\t\tprocess.stdout.write(remainder)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\treceiveMode = 'file'\n\t\tfilePath = outputPath\n\t\tawait mkdir(dirname(outputPath), { recursive: true })\n\t\tfileStream = createWriteStream(outputPath)\n\t\tfileStream.on('error', error => beam.destroy(error))\n\n\t\tif (remainder.length > NO_DATA) {\n\t\t\tfileStream.write(remainder)\n\t\t}\n\t}\n\n\t/**\n\t * Route a chunk to the active receive-mode sink.\n\t *\n\t * @param chunk - Inbound data chunk.\n\t * @returns Nothing.\n\t */\n\tfunction routeChunk(chunk: Buffer): void {\n\t\tif (receiveMode === 'pipe') {\n\t\t\twritePipeChunk(chunk)\n\t\t} else if (receiveMode === 'file') {\n\t\t\tfileStream?.write(chunk)\n\t\t} else if (receiveMode === 'file-stdout') {\n\t\t\tprocess.stdout.write(chunk)\n\t\t}\n\t}\n\n\t/**\n\t * Replay chunks buffered while interactive prompts were active.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction flushPendingChunks(): void {\n\t\tfor (const queued of pendingChunks) {\n\t\t\trouteChunk(queued)\n\t\t}\n\n\t\tpendingChunks = []\n\t}\n\n\tbeam.on('data', (chunk: Buffer) => {\n\t\t/**\n\t\t * While prompting, stash inbound chunks and replay afterward.\n\t\t */\n\t\tif (awaitingPrompt) {\n\t\t\tpendingChunks.push(chunk)\n\t\t\treturn\n\t\t}\n\n\t\tif (receiveMode !== 'unknown') {\n\t\t\trouteChunk(chunk)\n\t\t\treturn\n\t\t}\n\n\t\tpendingChunks.push(chunk)\n\t\tconst pending = Buffer.concat(pendingChunks)\n\n\t\tif (isFileHeader(pending)) {\n\t\t\t/**\n\t\t\t * Wait until the full header line arrives before parsing.\n\t\t\t */\n\t\t\tif (findHeaderLineEnd(pending) < FIRST_INDEX) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingChunks = []\n\n\t\t\tvoid startFileReceive(pending).finally(() => {\n\t\t\t\tflushPendingChunks()\n\n\t\t\t\tif (streamDone) {\n\t\t\t\t\tonStreamDone()\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn\n\t\t}\n\n\t\treceiveMode = 'pipe'\n\t\tpendingChunks = []\n\t\twritePipeChunk(pending)\n\t})\n\n\tprocess.stdin.pipe(beam)\n\n\tif (typeof process.stdin.unref === 'function') {\n\t\tprocess.stdin.unref()\n\t}\n}\n","/**\n * Local address book data access and validation helpers.\n *\n * Handles peer name/public-key normalization plus CRUD operations\n * for the `peers.json` config file.\n *\n * @module\n */\nimport { readJsonFile, writeJsonFile } from './config.ts'\n\nimport type { AddressBook, Peer } from '../types.ts'\n\nconst PEERS_FILE = 'peers.json'\nconst MODULUS_EVEN = 2\nconst PUBLIC_KEY_BYTES = 32\nconst REMAINDER_ZERO = 0\n\n/**\n * Check whether a string is valid even-length hex.\n *\n * @param value - Candidate hex string.\n * @returns True when valid hex.\n */\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\n\n/**\n * Validate and normalize a peer public key.\n *\n * @param publicKeyHex - User-provided hex public key.\n * @returns Normalized lowercase 64-char hex public key.\n */\nfunction normalizePublicKeyHex(publicKeyHex: string): string {\n\tconst normalized = publicKeyHex.trim().toLowerCase()\n\tif (!isHex(normalized)) {\n\t\tthrow new Error('Public key must be a valid hex string')\n\t}\n\tconst key = Buffer.from(normalized, 'hex')\n\tif (key.length !== PUBLIC_KEY_BYTES) {\n\t\tthrow new Error('Public key must be 32 bytes (64 hex chars)')\n\t}\n\treturn normalized\n}\n\n/**\n * Validate and normalize a peer name.\n *\n * @param name - User-provided peer name.\n * @returns Normalized lowercase peer name.\n */\nfunction normalizePeerName(name: string): string {\n\tconst normalized = name.trim().toLowerCase()\n\tif (!/^[a-z0-9-]+$/.test(normalized)) {\n\t\tthrow new Error('Peer name must use only letters, numbers, and hyphens')\n\t}\n\treturn normalized\n}\n\n/**\n * Load the full address book from disk.\n *\n * @returns Name-keyed peer map.\n */\nasync function readAddressBook(): Promise<AddressBook> {\n\treturn (await readJsonFile<AddressBook>(PEERS_FILE)) ?? {}\n}\n\n/**\n * Persist the full address book to disk.\n *\n * @param addressBook - Address book object to persist.\n * @returns Promise that resolves when write is complete.\n */\nasync function writeAddressBook(addressBook: AddressBook): Promise<void> {\n\tawait writeJsonFile(PEERS_FILE, addressBook)\n}\n\n/**\n * Add or update a named peer in the local address book.\n *\n * @param name - Peer alias.\n * @param publicKeyHex - Hex-encoded public key.\n * @returns Saved peer record.\n */\nexport async function addPeer(name: string, publicKeyHex: string): Promise<Peer> {\n\tconst normalizedName = normalizePeerName(name)\n\tconst normalizedPublicKey = normalizePublicKeyHex(publicKeyHex)\n\tconst addressBook = await readAddressBook()\n\n\tconst peer: Peer = {\n\t\taddedAt: new Date().toISOString(),\n\t\tpublicKey: normalizedPublicKey,\n\t}\n\taddressBook[normalizedName] = peer\n\n\tawait writeAddressBook(addressBook)\n\treturn peer\n}\n\n/**\n * Remove a peer from the local address book.\n *\n * @param name - Peer alias to remove.\n * @returns True when a peer was removed.\n */\nexport async function removePeer(name: string): Promise<boolean> {\n\tconst normalizedName = normalizePeerName(name)\n\tconst addressBook = await readAddressBook()\n\tif (!addressBook[normalizedName]) {\n\t\treturn false\n\t}\n\tdelete addressBook[normalizedName]\n\tawait writeAddressBook(addressBook)\n\treturn true\n}\n\n/**\n * Return all peers sorted by name.\n *\n * @returns Array of peers including their names.\n */\nexport async function listPeers(): Promise<({ name: string } & Peer)[]> {\n\tconst addressBook = await readAddressBook()\n\treturn Object.entries(addressBook)\n\t\t.map(([name, peer]) => ({ name, ...peer }))\n\t\t.toSorted((a, b) => a.name.localeCompare(b.name))\n}\n\n/**\n * Look up a single peer by name.\n *\n * @param name - Peer alias.\n * @returns Peer record when found, otherwise `undefined`.\n */\nexport async function getPeer(name: string): Promise<Peer | undefined> {\n\tconst normalizedName = normalizePeerName(name)\n\tconst addressBook = await readAddressBook()\n\treturn addressBook[normalizedName]\n}\n","/**\n * HyperDHT integration helpers used by the Beam transport.\n *\n * Includes deterministic key derivation, ephemeral node creation,\n * socket open synchronization, and inbound firewall generation.\n *\n * @module\n */\nimport { createHash } from 'node:crypto'\n\nimport * as b4a from 'b4a'\nimport DHT from 'hyperdht'\n\nimport { getPeer } from './addressbook.ts'\nimport { fromBase32 } from './encoding.ts'\n\nimport type { EncryptedSocket, HyperDHTNode, KeyPair } from '../types.ts'\n\nconst KEY_SEED_BYTES = 32\nconst PUBLIC_KEY_BYTES = 32\n\n/**\n * Check whether a string is a lowercase/uppercase 64-char hex public key.\n *\n * @param value - Candidate public key string.\n * @returns True when valid hex public key text.\n */\nfunction isPublicKeyHex(value: string): boolean {\n\treturn /^[0-9a-f]{64}$/i.test(value.trim())\n}\n\n/**\n * Normalize an arbitrary decoded seed into exactly 32 bytes.\n *\n * HyperDHT's `keyPair()` behavior for non-32-byte seeds can differ across JS\n * runtimes (e.g. Node vs Bun). By hashing to 32 bytes we ensure both sides\n * derive the same keypair for a given passphrase.\n *\n * @param seed - Decoded passphrase seed bytes.\n * @returns A 32-byte normalized seed.\n */\nfunction normalizeSeed(seed: Buffer): Buffer {\n\tif (seed.length === KEY_SEED_BYTES) {\n\t\treturn seed\n\t}\n\tconst digest = createHash('sha256').update(seed).digest()\n\treturn b4a.from(digest)\n}\n\n/**\n * Derive a Noise keypair from a base32-encoded passphrase.\n *\n * @param passphrase - Shared base32 passphrase.\n * @returns Deterministic Noise keypair derived from the passphrase.\n */\nexport function deriveKeyPair(passphrase: string): KeyPair {\n\tconst seed = fromBase32(passphrase)\n\treturn DHT.keyPair(normalizeSeed(seed))\n}\n\n/**\n * Create an ephemeral HyperDHT node that is destroyed with the beam.\n *\n * @returns New ephemeral HyperDHT node instance.\n */\nexport function createNode(): HyperDHTNode {\n\treturn new DHT({ ephemeral: true }) as unknown as HyperDHTNode\n}\n\n/**\n * Wait for an encrypted socket to complete its Noise handshake.\n *\n * @param socket - Socket to await open/close/error on.\n * @returns Promise that resolves when the socket opens.\n */\nexport function awaitOpen(socket: EncryptedSocket): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\tsocket.once('open', resolve)\n\t\tsocket.once('close', reject)\n\t\tsocket.once('error', reject)\n\t})\n}\n\n/**\n * Create a firewall that rejects connections not matching the expected keypair.\n *\n * @param keyPair - Local keypair used to validate remote key.\n * @returns Predicate used by HyperDHT server firewall.\n */\nexport function createFirewall(keyPair: KeyPair): (remotePublicKey: Buffer) => boolean {\n\treturn (remotePublicKey: Buffer) => !b4a.equals(remotePublicKey, keyPair.publicKey)\n}\n\n/**\n * Resolve a peer target string to a remote public key.\n *\n * Resolution order:\n * 1) raw 64-char hex public key\n * 2) saved peer name in address book\n * 3) passphrase-derived keypair public key\n *\n * @param target - Hex key, address-book name, or passphrase.\n * @returns Resolved 32-byte remote public key.\n */\nexport async function resolveRemoteKey(target: string): Promise<Buffer> {\n\tconst normalized = target.trim()\n\n\tif (isPublicKeyHex(normalized)) {\n\t\tconst key = Buffer.from(normalized, 'hex')\n\t\tif (key.length === PUBLIC_KEY_BYTES) {\n\t\t\treturn key\n\t\t}\n\t}\n\n\tconst peer = await getPeer(normalized).catch(() => undefined)\n\n\tif (peer) {\n\t\tconst key = Buffer.from(peer.publicKey, 'hex')\n\t\tif (key.length === PUBLIC_KEY_BYTES) {\n\t\t\treturn key\n\t\t}\n\t}\n\n\treturn deriveKeyPair(normalized).publicKey\n}\n","/**\n * Core Beam duplex stream implementation built on HyperDHT sockets.\n *\n * Handles announce/connect setup, socket wiring, streamx lifecycle hooks,\n * and connection event emission for CLI and library consumers.\n *\n * @module\n */\nimport queueTick from 'queue-tick'\nimport { Duplex } from 'streamx'\n\nimport { awaitOpen, createFirewall, createNode, deriveKeyPair } from './lib/dht.ts'\nimport { randomBytes, toBase32 } from './lib/encoding.ts'\n\nimport type {\n\tBeamOptions,\n\tConnectionInfo,\n\tEncryptedSocket,\n\tHyperDHTNode,\n\tHyperDHTServer,\n\tKeyPair,\n\tStreamCallback,\n} from './types.ts'\n\n/** Number of random bytes used to generate a passphrase seed. */\nconst KEY_SEED_BYTES = 32\n\n/**\n * A 1-to-1 end-to-end encrypted duplex stream powered by HyperDHT.\n *\n * Creates an encrypted tunnel between two peers using a shared passphrase.\n * If no passphrase is provided, one is generated and the beam listens for\n * an incoming connection (server mode). When a passphrase is provided, the\n * beam connects to the listening peer (client mode).\n *\n * @example\n * ```ts\n * const server = new Beam()\n * console.log(server.key) // Share this with the other side\n *\n * const client = new Beam(server.key)\n * ```\n */\nexport class Beam extends Duplex {\n\t/** Base32-encoded passphrase for peer discovery and key derivation. */\n\treadonly key: string\n\n\t/** Whether this beam is announcing (server) or connecting (client). */\n\treadonly announce: boolean\n\n\tprivate node: HyperDHTNode | undefined\n\tprivate server: HyperDHTServer | undefined = undefined\n\tprivate inbound: EncryptedSocket | undefined = undefined\n\tprivate outbound: EncryptedSocket | undefined = undefined\n\tprivate readonly keyPairOverride: KeyPair | undefined\n\tprivate readonly remotePublicKeyOverride: Buffer | undefined\n\tprivate readonly openInboundFirewall: boolean\n\n\tprivate openCallback: StreamCallback | undefined = undefined\n\tprivate readCallback: StreamCallback | undefined = undefined\n\tprivate drainCallback: StreamCallback | undefined = undefined\n\n\t/**\n\t * Create a new beam instance in announce or connect mode.\n\t *\n\t * @param keyOrOptions - Passphrase or options object.\n\t * @param options - Options used when a passphrase is provided.\n\t */\n\tconstructor(keyOrOptions?: string | BeamOptions, options?: BeamOptions) {\n\t\tsuper()\n\n\t\tlet key: string | undefined = undefined\n\t\tlet opts: BeamOptions = {}\n\t\tconst passphraseWasProvided = typeof keyOrOptions === 'string'\n\n\t\tif (passphraseWasProvided) {\n\t\t\tkey = keyOrOptions\n\t\t\topts = options ?? {}\n\t\t} else {\n\t\t\topts = keyOrOptions ?? {}\n\t\t}\n\n\t\tlet shouldAnnounce = opts.announce ?? false\n\n\t\tif (!key && !opts.keyPair) {\n\t\t\tkey = toBase32(randomBytes(KEY_SEED_BYTES))\n\t\t\tshouldAnnounce = true\n\t\t} else if (!key && opts.keyPair) {\n\t\t\tkey = opts.keyPair.publicKey.toString('hex')\n\t\t}\n\t\tif (!key) {\n\t\t\tthrow new Error('Missing key material')\n\t\t}\n\n\t\tthis.key = key\n\t\tthis.announce = shouldAnnounce\n\t\tthis.node = (opts.dht as HyperDHTNode) ?? undefined\n\t\tthis.keyPairOverride = opts.keyPair\n\t\tthis.remotePublicKeyOverride = opts.remotePublicKey\n\t\tthis.openInboundFirewall = !passphraseWasProvided && opts.keyPair !== undefined\n\t}\n\n\t/**\n\t * Whether a peer connection has been established.\n\t *\n\t * @returns True when outbound socket has been created.\n\t */\n\tget connected(): boolean {\n\t\treturn this.outbound !== undefined\n\t}\n\n\t// Streamx lifecycle\n\n\t/**\n\t * Streamx open hook: initialize DHT node and start announce/connect flow.\n\t *\n\t * @param cb - Open callback from streamx.\n\t * @returns Promise that resolves when setup path completes.\n\t */\n\toverride async _open(cb: StreamCallback): Promise<void> {\n\t\tthis.openCallback = cb\n\t\tconst keyPair = this.keyPairOverride ?? deriveKeyPair(this.key)\n\t\tthis.node ??= createNode()\n\n\t\tif (this.announce) {\n\t\t\tawait this.listenAsServer(keyPair)\n\t\t} else {\n\t\t\tawait this.connectAsClient(keyPair)\n\t\t}\n\t}\n\n\t/**\n\t * Streamx read hook: resume inbound flow when consumer requests data.\n\t *\n\t * @param cb - Read callback from streamx.\n\t * @returns Nothing.\n\t */\n\toverride _read(cb: StreamCallback): void {\n\t\tthis.readCallback = cb\n\t\tthis.inbound?.resume()\n\t}\n\n\t/**\n\t * Streamx write hook: write outbound bytes, respecting backpressure.\n\t *\n\t * @param data - Data chunk to send.\n\t * @param cb - Write callback from streamx.\n\t * @returns Nothing.\n\t */\n\toverride _write(data: unknown, cb: StreamCallback): void {\n\t\tif (this.outbound!.write(data as Buffer) !== false) {\n\t\t\tcb()\n\t\t\treturn\n\t\t}\n\t\tthis.drainCallback = cb\n\t}\n\n\t/**\n\t * Streamx final hook: end outbound socket and resolve when flushed.\n\t *\n\t * @param cb - Final callback from streamx.\n\t * @returns Nothing.\n\t */\n\toverride _final(cb: StreamCallback): void {\n\t\t/**\n\t\t * Resolve the final callback once outbound finish/error fires.\n\t\t *\n\t\t * @returns Nothing.\n\t\t */\n\t\tconst done = (): void => {\n\t\t\tthis.outbound!.removeListener('finish', done)\n\t\t\tthis.outbound!.removeListener('error', done)\n\t\t\tcb()\n\t\t}\n\t\tthis.outbound!.end()\n\t\tthis.outbound!.on('finish', done)\n\t\tthis.outbound!.on('error', done)\n\t}\n\n\t/**\n\t * Streamx pre-destroy hook: tear down sockets and pending callbacks.\n\t *\n\t * @returns Nothing.\n\t */\n\toverride _predestroy(): void {\n\t\tthis.inbound?.destroy()\n\t\tthis.outbound?.destroy()\n\t\tconst error = new Error('Destroyed')\n\t\tthis.resolveOpen(error)\n\t\tthis.resolveRead(error)\n\t\tthis.resolveDrain(error)\n\t}\n\n\t/**\n\t * Streamx destroy hook: close DHT server/node resources.\n\t *\n\t * @param cb - Destroy callback from streamx.\n\t * @returns Promise that resolves after cleanup.\n\t */\n\toverride async _destroy(cb: StreamCallback): Promise<void> {\n\t\tif (!this.node) {\n\t\t\tcb()\n\t\t\treturn\n\t\t}\n\t\tif (this.server) {\n\t\t\tawait this.server.close().catch(() => {})\n\t\t}\n\t\tawait this.node.destroy().catch(() => {})\n\t\tcb()\n\t}\n\n\t// Connection setup\n\n\t/**\n\t * Start announce/listen mode with optional inbound firewall.\n\t *\n\t * @param keyPair - Local keypair used for server listen.\n\t * @returns Promise that resolves when listen path completes.\n\t */\n\tprivate async listenAsServer(keyPair: KeyPair): Promise<void> {\n\t\tconst serverOptions = this.openInboundFirewall\n\t\t\t? undefined\n\t\t\t: { firewall: createFirewall(keyPair) }\n\t\tthis.server = this.node!.createServer(serverOptions)\n\t\tthis.server.on('connection', (socket: EncryptedSocket) => this.handleConnection(socket))\n\n\t\ttry {\n\t\t\tawait this.server.listen(keyPair)\n\t\t} catch (error) {\n\t\t\tthis.resolveOpen(error as Error)\n\t\t\treturn\n\t\t}\n\t\tthis.emitRemoteAddress()\n\t}\n\n\t/**\n\t * Start client mode by dialing a remote public key.\n\t *\n\t * @param keyPair - Local keypair used for dial auth.\n\t * @returns Promise that resolves when connect path completes.\n\t */\n\tprivate async connectAsClient(keyPair: KeyPair): Promise<void> {\n\t\tconst remotePublicKey = this.remotePublicKeyOverride ?? keyPair.publicKey\n\t\tconst socket: EncryptedSocket = this.node!.connect(remotePublicKey, { keyPair })\n\n\t\ttry {\n\t\t\tawait awaitOpen(socket)\n\t\t} catch (error) {\n\t\t\tthis.resolveOpen(error as Error)\n\t\t\treturn\n\t\t}\n\t\tthis.emitRemoteAddress()\n\t\tthis.handleConnection(socket)\n\t}\n\n\t/**\n\t * Bind socket handlers for inbound/outbound stream integration.\n\t *\n\t * @param socket - Encrypted socket from HyperDHT.\n\t * @returns Nothing.\n\t */\n\tprivate handleConnection(socket: EncryptedSocket): void {\n\t\tsocket.on('data', (data: Buffer) => {\n\t\t\tif (!this.inbound) {\n\t\t\t\tthis.inbound = socket\n\t\t\t\tthis.inbound.on('error', (err: Error) => this.destroy(err))\n\t\t\t\tthis.inbound.on('end', () => this.pushEndOfStream())\n\t\t\t}\n\t\t\tif (socket !== this.inbound) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.pushData(data) === false) {\n\t\t\t\tsocket.pause()\n\t\t\t}\n\t\t})\n\n\t\tsocket.on('end', () => {\n\t\t\tif (this.inbound) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.pushEndOfStream()\n\t\t})\n\n\t\tif (!this.outbound) {\n\t\t\tthis.outbound = socket\n\t\t\tthis.outbound.on('error', (err: Error) => this.destroy(err))\n\t\t\tthis.outbound.on('drain', () => this.resolveDrain())\n\t\t\tthis.emit('connected')\n\t\t\tthis.resolveOpen()\n\t\t}\n\t}\n\n\t// Helpers\n\n\t/**\n\t * Push inbound data into streamx and schedule read callback resolution.\n\t *\n\t * @param data - Data chunk or end-of-stream marker.\n\t * @returns Push backpressure result from streamx.\n\t */\n\tprivate pushData(data: Buffer | null): boolean {\n\t\tconst result = this.push(data)\n\t\tqueueTick(() => this.resolveRead())\n\t\treturn result\n\t}\n\n\t/**\n\t * Push end-of-stream marker into streamx.\n\t *\n\t * @returns Nothing.\n\t */\n\tprivate pushEndOfStream(): void {\n\t\t// oxlint-disable-next-line unicorn/no-null\n\t\tthis.pushData(null)\n\t}\n\n\t/**\n\t * Emit current node host/port information.\n\t *\n\t * @returns Nothing.\n\t */\n\tprivate emitRemoteAddress(): void {\n\t\tthis.emit('remote-address', {\n\t\t\thost: this.node!.host,\n\t\t\tport: this.node!.port,\n\t\t} satisfies ConnectionInfo)\n\t}\n\n\t/**\n\t * Resolve the pending open callback, if any.\n\t *\n\t * @param error - Optional error to pass to callback.\n\t * @returns Nothing.\n\t */\n\tprivate resolveOpen(error?: Error): void {\n\t\tconst cb = this.openCallback\n\t\tif (cb) {\n\t\t\tthis.openCallback = undefined\n\t\t\tcb(error)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the pending read callback, if any.\n\t *\n\t * @param error - Optional error to pass to callback.\n\t * @returns Nothing.\n\t */\n\tprivate resolveRead(error?: Error): void {\n\t\tconst cb = this.readCallback\n\t\tif (cb) {\n\t\t\tthis.readCallback = undefined\n\t\t\tcb(error)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the pending drain callback, if any.\n\t *\n\t * @param error - Optional error to pass to callback.\n\t * @returns Nothing.\n\t */\n\tprivate resolveDrain(error?: Error): void {\n\t\tconst cb = this.drainCallback\n\t\tif (cb) {\n\t\t\tthis.drainCallback = undefined\n\t\t\tcb(error)\n\t\t}\n\t}\n}\n","/**\n * Reusable TCP-over-DHT tunnel primitives for bind-like commands.\n *\n * Provides reverse and forward proxy modes with connection lifecycle events.\n *\n * @module\n */\nimport { EventEmitter } from 'node:events'\nimport net from 'node:net'\n\nimport { awaitOpen, createNode } from './dht.ts'\n\nimport type {\n\tEncryptedSocket,\n\tForwardTunnelOptions,\n\tHyperDHTNode,\n\tReverseTunnelOptions,\n\tTunnelController,\n\tTunnelEventMap,\n} from '../types.ts'\n\nconst DEFAULT_FORWARD_HOST = '127.0.0.1'\n\n/**\n * Link two duplex sockets and destroy each side on counterpart errors.\n *\n * @param left - First socket.\n * @param right - Second socket.\n * @returns Nothing.\n */\nfunction pipeBothWays(left: net.Socket, right: EncryptedSocket): void {\n\tleft.pipe(right as unknown as NodeJS.WritableStream)\n\t;(right as unknown as NodeJS.ReadableStream).pipe(left)\n}\n\n/**\n * Create a reverse tunnel (P2P -> local TCP service).\n *\n * @param options - Reverse tunnel configuration.\n * @returns Controller for observing/closing the tunnel.\n */\nexport async function createReverseTunnel(\n\toptions: ReverseTunnelOptions,\n): Promise<TunnelController> {\n\tconst events = new EventEmitter()\n\tconst node = options.dht ?? createNode()\n\tconst ownsNode = options.dht === undefined\n\tconst server = node.createServer()\n\n\tlet closed = false\n\tlet connections = 0\n\n\tconst peers = new Set<EncryptedSocket>()\n\tconst tcpSockets = new Set<net.Socket>()\n\n\tfunction emitError(error: unknown): void {\n\t\tevents.emit('error', error instanceof Error ? error : new Error(String(error)))\n\t}\n\n\tfunction onDisconnect(): void {\n\t\tconnections--\n\t\tevents.emit('disconnect', connections)\n\t}\n\n\tserver.on('error', emitError)\n\n\tserver.on('connection', (peer: EncryptedSocket) => {\n\t\tif (closed) {\n\t\t\tpeer.destroy()\n\t\t\treturn\n\t\t}\n\n\t\tpeers.add(peer)\n\n\t\tconst local = net.connect({ host: options.host, port: options.port })\n\t\ttcpSockets.add(local)\n\n\t\tlet connected = false\n\t\tlet disconnected = false\n\n\t\tfunction cleanup(): void {\n\t\t\tpeers.delete(peer)\n\t\t\ttcpSockets.delete(local)\n\n\t\t\tif (connected && !disconnected) {\n\t\t\t\tdisconnected = true\n\t\t\t\tonDisconnect()\n\t\t\t}\n\t\t}\n\n\t\tlocal.on('connect', () => {\n\t\t\tif (closed) {\n\t\t\t\tlocal.destroy()\n\t\t\t\tpeer.destroy()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconnected = true\n\t\t\tconnections++\n\n\t\t\tevents.emit('connect', connections)\n\n\t\t\tpipeBothWays(local, peer)\n\t\t})\n\n\t\tlocal.on('error', error => {\n\t\t\temitError(error)\n\t\t\tpeer.destroy()\n\t\t})\n\n\t\tlocal.on('close', cleanup)\n\n\t\tpeer.on('error', error => {\n\t\t\temitError(error)\n\t\t\tlocal.destroy()\n\t\t})\n\n\t\tpeer.on('close', cleanup)\n\t})\n\n\tawait server.listen(options.keyPair)\n\n\tconst controller: TunnelController = {\n\t\tasync close(): Promise<void> {\n\t\t\tif (closed) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tclosed = true\n\n\t\t\tfor (const socket of tcpSockets) {\n\t\t\t\tsocket.destroy()\n\t\t\t}\n\n\t\t\tfor (const peer of peers) {\n\t\t\t\tpeer.destroy()\n\t\t\t}\n\n\t\t\tawait server.close().catch(() => {})\n\n\t\t\tif (ownsNode) {\n\t\t\t\tawait node.destroy().catch(() => {})\n\t\t\t}\n\t\t},\n\t\tget connections(): number {\n\t\t\treturn connections\n\t\t},\n\t\ton<K extends keyof TunnelEventMap>(event: K, handler: TunnelEventMap[K]): TunnelController {\n\t\t\tevents.on(event, handler)\n\t\t\treturn controller\n\t\t},\n\t}\n\n\treturn controller\n}\n\n/**\n * Create a forward tunnel (local TCP listener -> P2P peer).\n *\n * @param options - Forward tunnel configuration.\n * @returns Controller for observing/closing the tunnel.\n */\nexport async function createForwardTunnel(\n\toptions: ForwardTunnelOptions,\n): Promise<TunnelController> {\n\tconst events = new EventEmitter()\n\tconst node: HyperDHTNode = options.dht ?? createNode()\n\tconst ownsNode = options.dht === undefined\n\tconst host = options.host ?? DEFAULT_FORWARD_HOST\n\tconst tcpServer = net.createServer()\n\n\tlet closed = false\n\tlet connections = 0\n\n\tconst peers = new Set<EncryptedSocket>()\n\tconst tcpSockets = new Set<net.Socket>()\n\n\tfunction emitError(error: unknown): void {\n\t\tevents.emit('error', error instanceof Error ? error : new Error(String(error)))\n\t}\n\n\tfunction onDisconnect(): void {\n\t\tconnections--\n\t\tevents.emit('disconnect', connections)\n\t}\n\n\ttcpServer.on('error', emitError)\n\n\ttcpServer.on('connection', (local: net.Socket) => {\n\t\tif (closed) {\n\t\t\tlocal.destroy()\n\t\t\treturn\n\t\t}\n\n\t\ttcpSockets.add(local)\n\t\tlocal.pause()\n\n\t\tconst peer = node.connect(options.remotePublicKey, { keyPair: options.keyPair })\n\t\tpeers.add(peer)\n\n\t\tlet connected = false\n\t\tlet disconnected = false\n\n\t\tfunction cleanup(): void {\n\t\t\ttcpSockets.delete(local)\n\t\t\tpeers.delete(peer)\n\t\t\tif (connected && !disconnected) {\n\t\t\t\tdisconnected = true\n\t\t\t\tonDisconnect()\n\t\t\t}\n\t\t}\n\n\t\tvoid awaitOpen(peer)\n\t\t\t.then(() => {\n\t\t\t\tif (closed) {\n\t\t\t\t\tlocal.destroy()\n\t\t\t\t\tpeer.destroy()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconnected = true\n\t\t\t\tconnections++\n\n\t\t\t\tevents.emit('connect', connections)\n\t\t\t\tlocal.resume()\n\n\t\t\t\tpipeBothWays(local, peer)\n\t\t\t})\n\t\t\t.catch(error => {\n\t\t\t\temitError(error)\n\n\t\t\t\tlocal.destroy()\n\t\t\t\tpeer.destroy()\n\t\t\t})\n\n\t\tlocal.on('error', error => {\n\t\t\temitError(error)\n\t\t\tpeer.destroy()\n\t\t})\n\n\t\tlocal.on('close', cleanup)\n\n\t\tpeer.on('error', error => {\n\t\t\temitError(error)\n\t\t\tlocal.destroy()\n\t\t})\n\n\t\tpeer.on('close', cleanup)\n\t})\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\ttcpServer.listen(options.port, host, () => resolve())\n\t\ttcpServer.once('error', reject)\n\t})\n\n\tconst controller: TunnelController = {\n\t\tasync close(): Promise<void> {\n\t\t\tif (closed) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tclosed = true\n\n\t\t\tfor (const socket of tcpSockets) {\n\t\t\t\tsocket.destroy()\n\t\t\t}\n\n\t\t\tfor (const peer of peers) {\n\t\t\t\tpeer.destroy()\n\t\t\t}\n\n\t\t\tawait new Promise<void>(resolve => {\n\t\t\t\ttcpServer.close(() => resolve())\n\t\t\t})\n\n\t\t\tif (ownsNode) {\n\t\t\t\tawait node.destroy().catch(() => {})\n\t\t\t}\n\t\t},\n\t\tget connections(): number {\n\t\t\treturn connections\n\t\t},\n\t\tget listenHost(): string {\n\t\t\treturn host\n\t\t},\n\t\tget listenPort(): number | undefined {\n\t\t\tconst address = tcpServer.address()\n\n\t\t\treturn typeof address === 'object' && address ? address.port : undefined\n\t\t},\n\t\ton<K extends keyof TunnelEventMap>(event: K, handler: TunnelEventMap[K]): TunnelController {\n\t\t\tevents.on(event, handler)\n\n\t\t\treturn controller\n\t\t},\n\t}\n\n\treturn controller\n}\n","/**\n * `hbeam bind` command implementation.\n *\n * Creates encrypted TCP tunnels over HyperDHT in either reverse mode\n * (P2P -> local TCP service) or forward mode (local TCP -> P2P peer).\n *\n * @module\n */\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport { createNode, deriveKeyPair, resolveRemoteKey } from '@/lib/dht.ts'\nimport { randomBytes, toBase32 } from '@/lib/encoding.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { blank, bold, createSpinner, cyan, dim, gray, log, logError, write } from '@/lib/log.ts'\nimport { createPulseFrames } from '@/lib/pulse.ts'\nimport { createForwardTunnel, createReverseTunnel } from '@/lib/tunnel.ts'\n\nimport type { KeyPair, TunnelController } from '@/types.ts'\n\nconst EXIT_FAILURE = 1\nconst EXIT_SUCCESS = 0\nconst EPHEMERAL_KEY_BYTES = 32\nconst DEFAULT_REVERSE_HOST = 'localhost'\nconst DEFAULT_FORWARD_HOST = '127.0.0.1'\nconst MIN_PORT = 1\nconst MAX_PORT = 65_535\nconst RANDOM_PORT = 0\nconst FIRST_ACTIVE_CONNECTION = 1\nconst NO_ACTIVE_CONNECTIONS = 0\n\ninterface BindCommandOptions {\n\thost?: string\n\tlisten?: boolean\n\tport?: number | string\n}\n\n/**\n * Determine whether a tunnel error is a non-fatal per-connection close.\n *\n * @param error - Error emitted by tunnel internals.\n * @returns True when bind should continue running.\n */\nfunction isBenignConnectionError(error: Error): boolean {\n\tconst message = error.message.toLowerCase()\n\tconst code = (error as NodeJS.ErrnoException).code?.toLowerCase() ?? ''\n\n\treturn (\n\t\tcode === 'econnreset' ||\n\t\tcode === 'epipe' ||\n\t\tmessage.includes('connection reset by peer') ||\n\t\tmessage.includes('writable stream closed prematurely') ||\n\t\tmessage.includes('premature close')\n\t)\n}\n\n/**\n * Print a bind usage error and terminate with failure.\n *\n * @param message - Error message to display.\n * @returns Never returns (process exits).\n */\nfunction showUsageError(message: string): never {\n\tblank()\n\tlogError(message)\n\twrite(\n\t\tdim(\n\t\t\t'Usage: hbeam bind <port|passphrase|name|public-key> [--host <host>] [-p <port>] [--listen]',\n\t\t),\n\t)\n\tblank()\n\tprocess.exit(EXIT_FAILURE)\n}\n\n/**\n * Parse and validate a TCP port number.\n *\n * @param value - Port text/number.\n * @param label - Human-readable label used in error messages.\n * @returns Parsed port number.\n */\nfunction parsePort(value: string | number, label: string): number {\n\tconst parsed = typeof value === 'number' ? value : Number(value)\n\tif (!Number.isInteger(parsed) || parsed < MIN_PORT || parsed > MAX_PORT) {\n\t\tshowUsageError(`Invalid ${label}: ${value}`)\n\t}\n\treturn parsed\n}\n\n/**\n * Build a throwaway keypair for ephemeral tunnel sessions.\n *\n * @returns Ephemeral keypair derived from random passphrase material.\n */\nfunction createEphemeralKeyPair(): KeyPair {\n\tconst passphrase = toBase32(randomBytes(EPHEMERAL_KEY_BYTES))\n\treturn deriveKeyPair(passphrase)\n}\n\n/**\n * Register SIGINT/SIGTERM handlers and close resources once.\n *\n * @param tunnel - Active tunnel controller.\n * @returns Nothing.\n */\nfunction registerShutdown(tunnel: TunnelController): void {\n\tlet shuttingDown = false\n\n\tasync function shutdown(exitCode: number): Promise<void> {\n\t\tif (shuttingDown) {\n\t\t\treturn\n\t\t}\n\t\tshuttingDown = true\n\t\tblank()\n\t\tlog(dim('SHUTTING DOWN'))\n\t\tblank()\n\t\tawait tunnel.close().catch(() => {})\n\t\tprocess.exit(exitCode)\n\t}\n\n\tprocess.once('SIGINT', () => {\n\t\tvoid shutdown(EXIT_SUCCESS)\n\t})\n\tprocess.once('SIGTERM', () => {\n\t\tvoid shutdown(EXIT_SUCCESS)\n\t})\n\n\ttunnel.on('error', error => {\n\t\tif (isBenignConnectionError(error)) {\n\t\t\treturn\n\t\t}\n\t\tlogError(error.message)\n\t\tvoid shutdown(EXIT_FAILURE)\n\t})\n\n\t/**\n\t * Keep bind runtime UX aligned with other commands.\n\t * Only log edge transitions to avoid noisy per-connection spam.\n\t */\n\ttunnel.on('connect', activeConnections => {\n\t\tif (shuttingDown || activeConnections !== FIRST_ACTIVE_CONNECTION) {\n\t\t\treturn\n\t\t}\n\t\tlog(bold('CONNECTED'))\n\t})\n\n\ttunnel.on('disconnect', activeConnections => {\n\t\tif (shuttingDown || activeConnections !== NO_ACTIVE_CONNECTIONS) {\n\t\t\treturn\n\t\t}\n\t\tlog(dim('PEER DISCONNECTED'))\n\t})\n}\n\n/**\n * Execute reverse-proxy bind mode (P2P -> local TCP service).\n *\n * @param targetPort - Local service port to expose.\n * @param options - Bind command options.\n * @returns Promise that resolves once tunnel is running.\n */\nasync function runReverseBind(targetPort: number, options: BindCommandOptions): Promise<void> {\n\tconst host = options.host ?? DEFAULT_REVERSE_HOST\n\tlet keyPair: KeyPair | undefined = undefined\n\tlet announceValue: string | undefined = undefined\n\n\tif (options.listen) {\n\t\tconst identity = await loadOrCreateIdentityWithMeta()\n\t\tkeyPair = identity.keyPair\n\t\tannounceValue = identity.keyPair.publicKey.toString('hex')\n\t\tif (identity.created) {\n\t\t\tblank()\n\t\t\tlog(dim('IDENTITY CREATED'))\n\t\t\twrite(cyan(announceValue))\n\t\t}\n\t} else {\n\t\tannounceValue = toBase32(randomBytes(EPHEMERAL_KEY_BYTES))\n\t\tkeyPair = deriveKeyPair(announceValue)\n\t\tcopyToClipboard(announceValue)\n\t}\n\n\tif (!keyPair || !announceValue) {\n\t\tshowUsageError('Failed to initialize bind identity.')\n\t}\n\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst node = createNode()\n\n\tblank()\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim('ANNOUNCING'))\n\tspinner.write(cyan(announceValue))\n\n\tconst tunnel = await createReverseTunnel({\n\t\tdht: node,\n\t\thost,\n\t\tkeyPair,\n\t\tport: targetPort,\n\t})\n\n\tspinner.write(dim(`ONLINE ${gray(`[${node.host}:${node.port}]`)}`))\n\tspinner.write(`FORWARDING ${dim(`${host}:${targetPort}`)}`)\n\tspinner.blank()\n\tspinner.stop()\n\n\tregisterShutdown(tunnel)\n}\n\n/**\n * Execute forward-proxy bind mode (local TCP -> P2P peer).\n *\n * @param target - Remote target (peer name, passphrase, or public key).\n * @param options - Bind command options.\n * @returns Promise that resolves once tunnel is running.\n */\nasync function runForwardBind(target: string, options: BindCommandOptions): Promise<void> {\n\tconst remotePublicKey = await resolveRemoteKey(target)\n\tlet keyPair: KeyPair | undefined = undefined\n\n\tif (options.listen) {\n\t\tconst identity = await loadOrCreateIdentityWithMeta()\n\t\tkeyPair = identity.keyPair\n\t} else {\n\t\tkeyPair = createEphemeralKeyPair()\n\t}\n\n\tif (!keyPair) {\n\t\tshowUsageError('Failed to initialize bind identity.')\n\t}\n\n\tconst listenHost = options.host ?? DEFAULT_FORWARD_HOST\n\tconst listenPort = options.port ? parsePort(options.port, 'listen port') : RANDOM_PORT\n\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst node = createNode()\n\n\tblank()\n\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim(`CONNECTING ${target}`))\n\n\tconst tunnel = await createForwardTunnel({\n\t\tdht: node,\n\t\thost: listenHost,\n\t\tkeyPair,\n\t\tport: listenPort,\n\t\tremotePublicKey,\n\t})\n\n\tconst boundPort = tunnel.listenPort ?? listenPort\n\n\tspinner.write(`LISTENING ${dim(`${listenHost}:${boundPort}`)}`)\n\tspinner.blank()\n\tspinner.stop()\n\n\tregisterShutdown(tunnel)\n}\n\n/**\n * Execute `hbeam bind` in reverse or forward proxy mode.\n *\n * @param argv - Positional command arguments after `bind`.\n * @param options - Optional command flags.\n * @returns Promise that resolves when command setup completes.\n */\nexport async function runBindCommand(\n\targv: string[],\n\toptions: BindCommandOptions = {},\n): Promise<void> {\n\tconst [target] = argv\n\n\tif (!target) {\n\t\tshowUsageError('Missing target argument.')\n\t}\n\n\tif (/^\\d+$/.test(target)) {\n\t\tawait runReverseBind(parsePort(target, 'target port'), options)\n\n\t\treturn\n\t}\n\n\tawait runForwardBind(target, options)\n}\n","/**\n * `hbeam connect` command implementation.\n *\n * Resolves named peers from the address book, ensures local identity exists,\n * and starts a connect-mode session against the peer public key.\n *\n * @module\n */\nimport { getPeer } from '@/lib/addressbook.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { blank, cyan, dim, log, logError, write } from '@/lib/log.ts'\nimport { runBeamSession } from '@/lib/session.ts'\n\nimport { Beam } from '../beam.ts'\n\nconst EXIT_FAILURE = 1\nconst PUBLIC_KEY_BYTES = 32\n\ninterface ConnectCommandOptions {\n\toutputPath?: string\n}\n\n/**\n * Execute `hbeam connect <name>`.\n *\n * @param argv - Positional command arguments after `connect`.\n * @param options - Optional command flags.\n * @returns Promise that resolves when the command has started the session.\n */\nexport async function runConnectCommand(\n\targv: string[],\n\toptions: ConnectCommandOptions = {},\n): Promise<void> {\n\tconst [name] = argv\n\tif (!name) {\n\t\tblank()\n\t\tlogError('Missing peer name.')\n\t\twrite(dim('Usage: hbeam connect <name>'))\n\t\tblank()\n\t\tprocess.exit(EXIT_FAILURE)\n\t}\n\n\tconst peer = await getPeer(name).catch(() => undefined)\n\tif (!peer) {\n\t\tblank()\n\t\tlogError(`Unknown peer: ${name}`)\n\t\tblank()\n\t\tprocess.exit(EXIT_FAILURE)\n\t}\n\n\tconst remotePublicKey = Buffer.from(peer.publicKey, 'hex')\n\tif (remotePublicKey.length !== PUBLIC_KEY_BYTES) {\n\t\tblank()\n\t\tlogError(`Invalid public key for peer: ${name}`)\n\t\tblank()\n\t\tprocess.exit(EXIT_FAILURE)\n\t}\n\n\tconst identity = await loadOrCreateIdentityWithMeta()\n\tif (identity.created) {\n\t\tblank()\n\t\tlog(dim('IDENTITY CREATED'))\n\t\twrite(cyan(identity.keyPair.publicKey.toString('hex')))\n\t}\n\n\tconst beam = new Beam({\n\t\tkeyPair: identity.keyPair,\n\t\tremotePublicKey,\n\t})\n\n\trunBeamSession(beam, {\n\t\tmode: 'connect',\n\t\toutputPath: options.outputPath,\n\t\tvalue: name,\n\t})\n}\n","/**\n * `hbeam peers` command implementation.\n *\n * Provides add/remove/list subcommands for managing the local peer address book.\n *\n * @module\n */\nimport { addPeer, getPeer, listPeers, removePeer } from '@/lib/addressbook.ts'\nimport { blank, bold, cyan, dim, log, logError, write } from '@/lib/log.ts'\nimport { confirm } from '@/lib/prompt.ts'\n\nconst EXIT_SUCCESS = 0\nconst EXIT_FAILURE = 1\nconst START_INDEX = 0\nconst PUBLIC_KEY_PREFIX_LENGTH = 8\nconst SECONDS_PER_MINUTE = 60\nconst MINUTES_PER_HOUR = 60\nconst HOURS_PER_DAY = 24\nconst DAYS_PER_WEEK = 7\nconst MILLISECONDS_PER_SECOND = 1000\nconst SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR\nconst SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY\nconst SECONDS_PER_WEEK = SECONDS_PER_DAY * DAYS_PER_WEEK\nconst EMPTY_PEERS = 0\n\n/**\n * Convert an ISO timestamp into a compact relative-age label.\n *\n * @param addedAt - ISO timestamp string.\n * @returns Relative age label (for example, `2h ago`).\n */\nfunction formatAge(addedAt: string): string {\n\tconst then = Date.parse(addedAt)\n\n\tif (Number.isNaN(then)) {\n\t\treturn 'unknown'\n\t}\n\n\tconst seconds = Math.floor((Date.now() - then) / MILLISECONDS_PER_SECOND)\n\n\tif (seconds < SECONDS_PER_MINUTE) {\n\t\treturn 'just now'\n\t}\n\n\tif (seconds < SECONDS_PER_HOUR) {\n\t\treturn `${Math.floor(seconds / SECONDS_PER_MINUTE)}m ago`\n\t}\n\n\tif (seconds < SECONDS_PER_DAY) {\n\t\treturn `${Math.floor(seconds / SECONDS_PER_HOUR)}h ago`\n\t}\n\n\tif (seconds < SECONDS_PER_WEEK) {\n\t\treturn `${Math.floor(seconds / SECONDS_PER_DAY)}d ago`\n\t}\n\n\treturn `${Math.floor(seconds / SECONDS_PER_WEEK)}w ago`\n}\n\n/**\n * Shorten a full public key for compact terminal display.\n *\n * @param publicKey - Full hex public key.\n * @returns Truncated key prefix with ellipsis.\n */\nfunction shortenKey(publicKey: string): string {\n\treturn `${publicKey.slice(START_INDEX, PUBLIC_KEY_PREFIX_LENGTH)}...`\n}\n\n/**\n * Print usage guidance for `hbeam peers`.\n *\n * @returns Nothing.\n */\nfunction usage(): void {\n\tlogError('Invalid peers command.')\n\twrite(dim('Usage: hbeam peers add <name> <public-key>'))\n\twrite(dim(' hbeam peers rm <name>'))\n\twrite(dim(' hbeam peers ls'))\n}\n\n/**\n * Handle `hbeam peers add <name> <public-key>`.\n *\n * @param name - Peer alias.\n * @param publicKey - Hex public key.\n * @returns Process exit code.\n */\nasync function handleAdd(name: string | undefined, publicKey: string | undefined): Promise<number> {\n\tif (!name || !publicKey) {\n\t\tblank()\n\t\tusage()\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\ttry {\n\t\tawait addPeer(name, publicKey)\n\t} catch (error) {\n\t\tblank()\n\t\tlogError((error as Error).message)\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\tblank()\n\tlog(bold('SAVED'))\n\twrite(cyan(name))\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n\n/**\n * Handle `hbeam peers rm <name>`.\n *\n * @param name - Peer alias.\n * @returns Process exit code.\n */\nasync function handleRemove(name: string | undefined): Promise<number> {\n\tif (!name) {\n\t\tblank()\n\t\tusage()\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\tconst peer = await getPeer(name).catch(() => undefined)\n\n\tif (!peer) {\n\t\tblank()\n\t\tlogError(`Unknown peer: ${name}`)\n\t\tblank()\n\t\treturn EXIT_FAILURE\n\t}\n\n\tblank()\n\tconst approved = await confirm(`REMOVE ${name}?`)\n\n\tif (!approved) {\n\t\tlog(dim('CANCELLED'))\n\t\tblank()\n\t\treturn EXIT_SUCCESS\n\t}\n\n\tawait removePeer(name)\n\tlog(bold('REMOVED'))\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n\n/**\n * Handle `hbeam peers ls`.\n *\n * @returns Process exit code.\n */\nasync function handleList(): Promise<number> {\n\tconst peers = await listPeers()\n\n\tblank()\n\tlog(bold('PEERS'))\n\n\tif (peers.length === EMPTY_PEERS) {\n\t\twrite(dim('No peers saved yet.'))\n\t\tblank()\n\t\treturn EXIT_SUCCESS\n\t}\n\n\tblank()\n\n\tfor (const peer of peers) {\n\t\twrite(`${peer.name} ${dim(shortenKey(peer.publicKey))} ${dim(formatAge(peer.addedAt))}`)\n\t}\n\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n\n/**\n * Execute `hbeam peers` subcommands.\n *\n * @param argv - Positional command arguments after `peers`.\n * @returns Process exit code.\n */\nexport async function runPeersCommand(argv: string[]): Promise<number> {\n\tconst [action, name, publicKey] = argv\n\n\tif (action === 'add') {\n\t\treturn handleAdd(name, publicKey)\n\t}\n\n\tif (action === 'rm') {\n\t\treturn handleRemove(name)\n\t}\n\n\tif (action === 'ls') {\n\t\treturn handleList()\n\t}\n\n\tblank()\n\tusage()\n\tblank()\n\n\treturn EXIT_FAILURE\n}\n","/**\n * `hbeam serve` command implementation.\n *\n * Announces a sender endpoint, streams one file with a control header,\n * and waits for protocol-level completion acknowledgement from receiver.\n *\n * @module\n */\nimport { createReadStream } from 'node:fs'\nimport { stat } from 'node:fs/promises'\nimport { basename, resolve } from 'node:path'\n\nimport { Beam } from '@/beam.ts'\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport {\n\tencodeHeader,\n\tfindHeaderLineEnd,\n\tformatFileSize,\n\tparseCompletionAck,\n} from '@/lib/file-protocol.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { createLifecycle } from '@/lib/lifecycle.ts'\nimport {\n\tblank,\n\tcreateSpinner,\n\tcyan,\n\tdim,\n\tgray,\n\tgreen,\n\tlog,\n\tlogError,\n\tred,\n\twrite,\n\tendInline,\n\twriteInline,\n} from '@/lib/log.ts'\nimport { createPulseFrames } from '@/lib/pulse.ts'\n\nimport type { ConnectionInfo, KeyPair } from '@/types.ts'\n\nconst EXIT_FAILURE = 1\nconst MIN_FILE_SIZE = 0\nconst ACK_TIMEOUT_MS = 15_000\nconst EMPTY_BUFFER_SIZE = 0\nconst EMPTY_BUFFER = Buffer.alloc(EMPTY_BUFFER_SIZE)\nconst FIRST_INDEX = 0\nconst NEXT_OFFSET = 1\n\ninterface ServeCommandOptions {\n\tlisten?: boolean\n}\n\n/**\n * Print a serve usage error and terminate with failure.\n *\n * @param message - Error message to display.\n * @returns Never returns (process exits).\n */\nfunction showUsageError(message: string): never {\n\tblank()\n\tlogError(message)\n\twrite(dim('Usage: hbeam serve <file> [--listen]'))\n\tblank()\n\tprocess.exit(EXIT_FAILURE)\n}\n\n/**\n * Resolve identity mode for the serve command.\n *\n * @param listen - Whether identity listen mode was requested.\n * @returns Announce label plus optional keypair override.\n */\nasync function resolveServeIdentity(listen: boolean | undefined): Promise<{\n\tannounceLabel: string\n\tkeyPair?: KeyPair\n}> {\n\tif (!listen) {\n\t\treturn { announceLabel: 'ANNOUNCING' }\n\t}\n\tconst identity = await loadOrCreateIdentityWithMeta()\n\tif (identity.created) {\n\t\tblank()\n\t\tlog(dim('IDENTITY CREATED'))\n\t\twrite(cyan(identity.keyPair.publicKey.toString('hex')))\n\t}\n\treturn {\n\t\tannounceLabel: 'ANNOUNCING',\n\t\tkeyPair: identity.keyPair,\n\t}\n}\n\n/**\n * Execute `hbeam serve <file>` to transfer one file to the first peer.\n *\n * @param argv - Positional command arguments after `serve`.\n * @param options - Optional command flags.\n * @returns Promise that resolves when command setup is complete.\n */\nexport async function runServeCommand(\n\targv: string[],\n\toptions: ServeCommandOptions = {},\n): Promise<void> {\n\tconst [targetFile] = argv\n\n\tif (!targetFile) {\n\t\tshowUsageError('Missing file path.')\n\t}\n\n\tconst filePath = resolve(targetFile)\n\tconst fileName = basename(filePath)\n\tconst fileStat = await stat(filePath).catch(() => undefined)\n\n\tif (!fileStat || !fileStat.isFile()) {\n\t\tshowUsageError(`Not a readable file: ${targetFile}`)\n\t}\n\n\tif (fileStat.size < MIN_FILE_SIZE) {\n\t\tshowUsageError(`Invalid file size: ${targetFile}`)\n\t}\n\n\tconst identity = await resolveServeIdentity(options.listen)\n\tconst beam = identity.keyPair\n\t\t? new Beam({ announce: true, keyPair: identity.keyPair })\n\t\t: new Beam(undefined, { announce: true })\n\n\tconst { frames, intervalMs } = createPulseFrames('HBEAM')\n\tconst spinner = createSpinner(frames, intervalMs)\n\tconst lifecycle = createLifecycle(beam, spinner)\n\n\tlet awaitingAck = false\n\tlet ackBuffer = EMPTY_BUFFER\n\tlet ackTimeout: ReturnType<typeof globalThis.setTimeout> | undefined = undefined\n\n\t/**\n\t * Tear down transfer resources and destroy the beam.\n\t *\n\t * @returns Nothing.\n\t */\n\tfunction closeTransfer(): void {\n\t\tif (ackTimeout) {\n\t\t\tglobalThis.clearTimeout(ackTimeout)\n\t\t\tackTimeout = undefined\n\t\t}\n\n\t\tbeam.destroy()\n\t}\n\n\tblank()\n\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim(identity.announceLabel))\n\tspinner.write(cyan(beam.key))\n\n\tcopyToClipboard(beam.key)\n\n\tbeam.on('remote-address', ({ host, port }: ConnectionInfo) => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\t\tif (host) {\n\t\t\tspinner.write(dim(`ONLINE ${gray(`[${host}:${port}]`)}`))\n\t\t\tspinner.blank()\n\t\t\tspinner.write(`SERVING ${dim(`${fileName} (${formatFileSize(fileStat.size)})`)}`)\n\n\t\t\tspinner.blank()\n\t\t}\n\t})\n\n\tbeam.on('connected', () => {\n\t\tif (lifecycle.done()) {\n\t\t\treturn\n\t\t}\n\t\tspinner.stop()\n\t\twriteInline(`SENDING ${dim(`${fileName}...`)}`)\n\n\t\tconst header = encodeHeader({ name: fileName, size: fileStat.size, type: 'file' })\n\t\tif (beam.write(header) === false) {\n\t\t\tbeam.once('drain', () => createReadStream(filePath).pipe(beam))\n\t\t\treturn\n\t\t}\n\t\tcreateReadStream(filePath).pipe(beam)\n\t})\n\n\tbeam.on('error', (error: Error) => {\n\t\tspinner.stop()\n\t\tconst isPeerNotFound = error.message.includes('PEER_NOT_FOUND')\n\t\tif (awaitingAck && error.message.includes('connection reset by peer')) {\n\t\t\tendInline(` ${dim('DISCONNECTED')}`)\n\t\t\tblank()\n\t\t\tcloseTransfer()\n\t\t\treturn\n\t\t}\n\t\tif (isPeerNotFound) {\n\t\t\tlog(red(dim('PEER NOT FOUND')))\n\t\t} else if (error.message.includes('connection reset by peer')) {\n\t\t\tlog(dim('PEER DISCONNECTED'))\n\t\t} else {\n\t\t\tlogError(error.message)\n\t\t}\n\t\tblank()\n\t\tif (!isPeerNotFound) {\n\t\t\tlifecycle.shutdown()\n\t\t}\n\t})\n\n\tbeam.on('data', (chunk: Buffer) => {\n\t\tif (!awaitingAck) {\n\t\t\treturn\n\t\t}\n\t\tackBuffer = Buffer.concat([ackBuffer, chunk])\n\t\t/**\n\t\t * Acks are newline-delimited JSON control frames, so we parse line-by-line\n\t\t * and ignore unrelated payload chunks until a valid ack appears.\n\t\t */\n\t\twhile (true) {\n\t\t\tconst lineEnd = findHeaderLineEnd(ackBuffer)\n\n\t\t\tif (lineEnd < FIRST_INDEX) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst line = ackBuffer.subarray(FIRST_INDEX, lineEnd)\n\t\t\tackBuffer = ackBuffer.subarray(lineEnd + NEXT_OFFSET)\n\t\t\tconst ack = parseCompletionAck(line)\n\n\t\t\tif (ack) {\n\t\t\t\tawaitingAck = false\n\t\t\t\tif (ack.ok) {\n\t\t\t\t\tendInline(` ${green('DONE')}`)\n\t\t\t\t} else {\n\t\t\t\t\tconst reason = ack.reason === 'cancelled' ? 'CANCELLED' : 'DECLINED'\n\t\t\t\t\tendInline(` ${dim(reason)}`)\n\t\t\t\t}\n\t\t\t\tblank()\n\t\t\t\tcloseTransfer()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t})\n\n\tbeam.on('end', () => beam.end())\n\tbeam.on('finish', () => {\n\t\tawaitingAck = true\n\t\tackTimeout = globalThis.setTimeout(() => {\n\t\t\tawaitingAck = false\n\t\t\tendInline(` ${dim('TIMED OUT')}`)\n\t\t\tblank()\n\t\t\tcloseTransfer()\n\t\t}, ACK_TIMEOUT_MS)\n\t})\n\n\t/**\n\t * Streamx opens lazily; force announce mode to start immediately.\n\t */\n\t;(beam as unknown as { resume(): void }).resume()\n}\n","/**\n * `hbeam whoami` command implementation.\n *\n * Displays the local persistent identity public key and copies it\n * to the system clipboard for easy sharing.\n *\n * @module\n */\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { blank, bold, cyan, dim, log, write } from '@/lib/log.ts'\n\nconst EXIT_SUCCESS = 0\n\n/**\n * Execute `hbeam whoami`.\n *\n * @returns Process exit code.\n */\nexport async function runWhoamiCommand(): Promise<number> {\n\tconst identity = await loadOrCreateIdentityWithMeta()\n\tconst publicKey = identity.keyPair.publicKey.toString('hex')\n\n\tblank()\n\n\tif (identity.created) {\n\t\tlog(dim('IDENTITY CREATED'))\n\t}\n\n\tlog(bold('IDENTITY'))\n\twrite(cyan(publicKey))\n\tcopyToClipboard(publicKey)\n\n\tblank()\n\n\treturn EXIT_SUCCESS\n}\n","#!/usr/bin/env node\n\n/**\n * Command-line entrypoint for the hbeam executable.\n *\n * Parses CLI flags/arguments, routes to subcommands, and starts\n * interactive announce/connect sessions for pipe and file-transfer flows.\n *\n * @module\n */\nimport mri from 'mri'\n\nimport { copyToClipboard } from '@/lib/clipboard.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { bold, cyan, dim, log, write, writeBlock } from '@/lib/log.ts'\nimport { runBeamSession } from '@/lib/session.ts'\n\nimport { Beam } from './beam.ts'\nimport { runBindCommand } from './commands/bind.ts'\nimport { runConnectCommand } from './commands/connect.ts'\nimport { runPeersCommand } from './commands/peers.ts'\nimport { runServeCommand } from './commands/serve.ts'\nimport { runWhoamiCommand } from './commands/whoami.ts'\n\nimport type { BeamOptions } from './types.ts'\n\nconst ARGV_OFFSET = 2\nconst EXIT_SUCCESS = 0\n\nconst NO_INDENT = ''\n\nconst argv = mri(process.argv.slice(ARGV_OFFSET), {\n\talias: { h: 'help', l: 'listen', o: 'output', p: 'port', v: 'version' },\n\tboolean: ['help', 'listen', 'version'],\n\tstring: ['host', 'output', 'port'],\n})\n\nif (argv.help) {\n\twriteBlock([\n\t\t`${bold('hbeam')} — end-to-end encrypted pipe over HyperDHT`,\n\t\t'',\n\t\t`${bold('Usage:')}`,\n\t\t` hbeam ${dim('[passphrase]')} ${dim('[options]')}`,\n\t\t` hbeam connect ${dim('<name>')}`,\n\t\t` hbeam bind ${dim('<port|passphrase|name|public-key>')} ${dim('[options]')}`,\n\t\t` hbeam peers ${dim('<add|rm|ls> ...')}`,\n\t\t` hbeam serve ${dim('<file>')} ${dim('[--listen]')}`,\n\t\t` hbeam whoami`,\n\t\t'',\n\t\t`${bold('Options:')}`,\n\t\t` ${dim('-l, --listen')} Listen using passphrase or identity`,\n\t\t` ${dim('-o, --output')} Save incoming file to a specific path`,\n\t\t` ${dim('-p, --port')} Local listen port (bind forward mode)`,\n\t\t` ${dim('--host')} Host target/listen host (bind mode)`,\n\t\t` ${dim('-h, --help')} Show this help`,\n\t\t` ${dim('-v, --version')} Show version`,\n\t\t'',\n\t\t`${bold('Examples:')}`,\n\t\t` ${dim('# Start a new pipe (generates a passphrase)')}`,\n\t\t\" echo 'hello' | hbeam\",\n\t\t'',\n\t\t` ${dim('# Connect to an existing pipe')}`,\n\t\t' hbeam <passphrase>',\n\t\t'',\n\t\t` ${dim('# Listen with a specific passphrase')}`,\n\t\t\" echo 'hello again' | hbeam <passphrase> --listen\",\n\t\t'',\n\t\t` ${dim('# Listen on your persistent identity')}`,\n\t\t' hbeam --listen',\n\t\t'',\n\t\t` ${dim('# Save and connect to peers by name')}`,\n\t\t' hbeam peers add workserver <public-key>',\n\t\t' hbeam connect workserver',\n\t\t'',\n\t\t` ${dim('# Expose local port 3000 over P2P')}`,\n\t\t' hbeam bind 3000 --listen',\n\t\t'',\n\t\t` ${dim('# Create local TCP tunnel to remote peer')}`,\n\t\t' hbeam bind workserver -p 8080',\n\t\t'',\n\t\t` ${dim('# Serve a single file')}`,\n\t\t' hbeam serve ./report.pdf',\n\t])\n\tprocess.exit(EXIT_SUCCESS)\n}\n\nif (argv.version) {\n\tconst pkg = (await import('../package.json')) as { version?: string }\n\twrite(pkg.version ?? '0.0.0', NO_INDENT)\n\tprocess.exit(EXIT_SUCCESS)\n}\n\nconst [firstArg, ...restArgs] = argv._ as string[]\nlet ranSubcommand = false\n\nif (firstArg === 'peers') {\n\tprocess.exit(await runPeersCommand(restArgs))\n}\nif (firstArg === 'connect') {\n\tawait runConnectCommand(restArgs, { outputPath: argv.output })\n\tranSubcommand = true\n}\nif (firstArg === 'bind') {\n\tawait runBindCommand(restArgs, { host: argv.host, listen: argv.listen, port: argv.port })\n\tranSubcommand = true\n}\nif (firstArg === 'serve') {\n\tawait runServeCommand(restArgs, { listen: argv.listen })\n\tranSubcommand = true\n}\nif (firstArg === 'whoami') {\n\tprocess.exit(await runWhoamiCommand())\n}\n\nif (!ranSubcommand) {\n\tconst passphrase = firstArg\n\n\tif (argv.listen && !passphrase) {\n\t\tconst identity = await loadOrCreateIdentityWithMeta()\n\t\tif (identity.created) {\n\t\t\tlog(dim('IDENTITY CREATED'))\n\t\t\twrite(cyan(identity.keyPair.publicKey.toString('hex')))\n\t\t}\n\n\t\tconst beam = new Beam({\n\t\t\tannounce: true,\n\t\t\tkeyPair: identity.keyPair,\n\t\t})\n\t\trunBeamSession(beam, {\n\t\t\tannounceLabel: 'ANNOUNCING',\n\t\t\tcopyValue: copyToClipboard,\n\t\t\tmode: 'announce',\n\t\t\tvalue: beam.key,\n\t\t})\n\t} else {\n\t\tconst beamOptions: BeamOptions | undefined = argv.listen ? { announce: true } : undefined\n\t\tconst beam = new Beam(passphrase, beamOptions)\n\t\trunBeamSession(beam, {\n\t\t\tannounceLabel: 'ANNOUNCING',\n\t\t\tcopyValue: copyToClipboard,\n\t\t\tmode: beam.announce ? 'announce' : 'connect',\n\t\t\toutputPath: argv.output,\n\t\t\tvalue: beam.announce ? beam.key : (passphrase ?? 'unknown'),\n\t\t})\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region package.json
2
+ var version = "0.1.8-alpha.3";
3
+
4
+ //#endregion
5
+ export { version };
6
+ //# sourceMappingURL=package-uWr_S1qI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-uWr_S1qI.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hbeam",
3
- "version": "0.1.8-alpha.2",
3
+ "version": "0.1.8-alpha.3",
4
4
  "description": "A 1-to-1 end-to-end encrypted pipe over HyperDHT.",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,6 +0,0 @@
1
- //#region package.json
2
- var version = "0.1.8-alpha.2";
3
-
4
- //#endregion
5
- export { version };
6
- //# sourceMappingURL=package-B-S49qcw.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"package-B-S49qcw.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}