hbeam 0.1.7-alpha.13 → 0.1.7-alpha.15

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
@@ -257,10 +257,11 @@ function createSpinner(frames, intervalMs) {
257
257
  //#endregion
258
258
  //#region src/lib/file-protocol.ts
259
259
  const FILE_TYPE = "file";
260
+ const FILE_COMPLETE_TYPE = "file-complete";
260
261
  const NEWLINE = "\n";
261
262
  const BYTES_PER_KIB = 1024;
262
263
  const UNIT_PRECISION = 1;
263
- const FIRST_INDEX$1 = 0;
264
+ const FIRST_INDEX$2 = 0;
264
265
  const MIN_SIZE = 0;
265
266
  const LAST_INDEX_OFFSET = 1;
266
267
  const UNIT_LABELS = [
@@ -273,9 +274,12 @@ const UNIT_LABELS = [
273
274
  function encodeHeader(header) {
274
275
  return Buffer.from(`${JSON.stringify(header)}${NEWLINE}`, "utf8");
275
276
  }
277
+ function encodeCompletionAck(ack) {
278
+ return Buffer.from(`${JSON.stringify(ack)}${NEWLINE}`, "utf8");
279
+ }
276
280
  function isFileHeader(chunk) {
277
281
  const lineEnd = findHeaderLineEnd(chunk);
278
- const trimmed = (lineEnd >= MIN_SIZE ? chunk.subarray(FIRST_INDEX$1, lineEnd) : chunk).toString("utf8").trimStart();
282
+ const trimmed = (lineEnd >= MIN_SIZE ? chunk.subarray(FIRST_INDEX$2, lineEnd) : chunk).toString("utf8").trimStart();
279
283
  return trimmed.startsWith("{") && trimmed.includes(`"type":"${FILE_TYPE}"`);
280
284
  }
281
285
  function parseFileHeader(line) {
@@ -289,19 +293,33 @@ function parseFileHeader(line) {
289
293
  type: FILE_TYPE
290
294
  };
291
295
  }
296
+ function parseCompletionAck(line) {
297
+ try {
298
+ const parsed = JSON.parse(line.toString("utf8"));
299
+ if (parsed.type !== FILE_COMPLETE_TYPE || typeof parsed.ok !== "boolean") return;
300
+ if (parsed.reason !== void 0 && typeof parsed.reason !== "string") return;
301
+ return {
302
+ ok: parsed.ok,
303
+ reason: parsed.reason,
304
+ type: FILE_COMPLETE_TYPE
305
+ };
306
+ } catch {
307
+ return;
308
+ }
309
+ }
292
310
  function findHeaderLineEnd(chunk) {
293
311
  return chunk.indexOf(NEWLINE);
294
312
  }
295
313
  function formatFileSize(bytes) {
296
- if (!Number.isFinite(bytes) || bytes < MIN_SIZE) return `0 ${UNIT_LABELS[FIRST_INDEX$1]}`;
314
+ if (!Number.isFinite(bytes) || bytes < MIN_SIZE) return `0 ${UNIT_LABELS[FIRST_INDEX$2]}`;
297
315
  let size = bytes;
298
- let unitIndex = FIRST_INDEX$1;
316
+ let unitIndex = FIRST_INDEX$2;
299
317
  const lastUnitIndex = UNIT_LABELS.length - LAST_INDEX_OFFSET;
300
318
  while (size >= BYTES_PER_KIB && unitIndex < lastUnitIndex) {
301
319
  size /= BYTES_PER_KIB;
302
320
  unitIndex++;
303
321
  }
304
- if (unitIndex === FIRST_INDEX$1) return `${Math.round(size)} ${UNIT_LABELS[unitIndex]}`;
322
+ if (unitIndex === FIRST_INDEX$2) return `${Math.round(size)} ${UNIT_LABELS[unitIndex]}`;
305
323
  return `${size.toFixed(UNIT_PRECISION)} ${UNIT_LABELS[unitIndex]}`;
306
324
  }
307
325
 
@@ -463,8 +481,8 @@ function createPulseFrames(label) {
463
481
 
464
482
  //#endregion
465
483
  //#region src/lib/session.ts
466
- const FIRST_INDEX = 0;
467
- const NEXT_OFFSET = 1;
484
+ const FIRST_INDEX$1 = 0;
485
+ const NEXT_OFFSET$1 = 1;
468
486
  const NO_DATA = 0;
469
487
  const KEEPALIVE_MS = 6e4;
470
488
  const CONNECTION_RESET = "connection reset by peer";
@@ -502,6 +520,7 @@ function runBeamSession(beam, options) {
502
520
  let receivedPipeData = false;
503
521
  let awaitingPrompt = false;
504
522
  let streamDone = false;
523
+ let streamTerminationHandled = false;
505
524
  let keepAlive = void 0;
506
525
  let pendingChunks = [];
507
526
  let fileStream = void 0;
@@ -518,6 +537,11 @@ function runBeamSession(beam, options) {
518
537
  fileStream.end(() => {
519
538
  log(dim$1(`SAVED ${filePath ?? ""}`));
520
539
  blank();
540
+ beam.write(encodeCompletionAck({
541
+ ok: true,
542
+ type: "file-complete"
543
+ }));
544
+ beam.end();
521
545
  });
522
546
  fileStream = void 0;
523
547
  }
@@ -532,8 +556,16 @@ function runBeamSession(beam, options) {
532
556
  function onStreamDone() {
533
557
  streamDone = true;
534
558
  if (awaitingPrompt) return;
559
+ if (streamTerminationHandled) return;
560
+ streamTerminationHandled = true;
535
561
  if (receiveMode === "file") finalizeFile();
536
- else if (receiveMode === "pipe" && receivedPipeData) {
562
+ else if (receiveMode === "file-stdout") {
563
+ beam.write(encodeCompletionAck({
564
+ ok: true,
565
+ type: "file-complete"
566
+ }));
567
+ beam.end();
568
+ } else if (receiveMode === "pipe" && receivedPipeData) {
537
569
  blank();
538
570
  write(SEPARATOR);
539
571
  }
@@ -560,8 +592,8 @@ function runBeamSession(beam, options) {
560
592
  }
561
593
  async function startFileReceive(headerChunk) {
562
594
  const lineEnd = findHeaderLineEnd(headerChunk);
563
- const header = parseFileHeader(headerChunk.subarray(FIRST_INDEX, lineEnd));
564
- const remainder = headerChunk.subarray(lineEnd + NEXT_OFFSET);
595
+ const header = parseFileHeader(headerChunk.subarray(FIRST_INDEX$1, lineEnd));
596
+ const remainder = headerChunk.subarray(lineEnd + NEXT_OFFSET$1);
565
597
  log(dim$1(`INCOMING FILE ${header.name} (${formatFileSize(header.size)})`));
566
598
  process.stdin.unpipe(beam);
567
599
  keepAlive = globalThis.setInterval(() => {}, KEEPALIVE_MS);
@@ -572,6 +604,12 @@ function runBeamSession(beam, options) {
572
604
  clearKeepAlive();
573
605
  log(dim$1("RECEIVE CANCELLED"));
574
606
  blank();
607
+ beam.write(encodeCompletionAck({
608
+ ok: false,
609
+ reason: "cancelled",
610
+ type: "file-complete"
611
+ }));
612
+ beam.end();
575
613
  return;
576
614
  }
577
615
  if (outputPath === void 0) {
@@ -592,6 +630,10 @@ function runBeamSession(beam, options) {
592
630
  else if (receiveMode === "file") fileStream?.write(chunk);
593
631
  else if (receiveMode === "file-stdout") process.stdout.write(chunk);
594
632
  }
633
+ function flushPendingChunks() {
634
+ for (const queued of pendingChunks) routeChunk(queued);
635
+ pendingChunks = [];
636
+ }
595
637
  beam.on("data", (chunk) => {
596
638
  if (awaitingPrompt) {
597
639
  pendingChunks.push(chunk);
@@ -604,13 +646,10 @@ function runBeamSession(beam, options) {
604
646
  pendingChunks.push(chunk);
605
647
  const pending = Buffer.concat(pendingChunks);
606
648
  if (isFileHeader(pending)) {
607
- if (findHeaderLineEnd(pending) < FIRST_INDEX) return;
649
+ if (findHeaderLineEnd(pending) < FIRST_INDEX$1) return;
608
650
  pendingChunks = [];
609
- awaitingPrompt = true;
610
651
  startFileReceive(pending).finally(() => {
611
- awaitingPrompt = false;
612
- for (const queued of pendingChunks) routeChunk(queued);
613
- pendingChunks = [];
652
+ flushPendingChunks();
614
653
  if (streamDone) onStreamDone();
615
654
  });
616
655
  return;
@@ -1064,6 +1103,10 @@ async function runPeersCommand(argv) {
1064
1103
  //#region src/commands/serve.ts
1065
1104
  const EXIT_FAILURE = 1;
1066
1105
  const MIN_FILE_SIZE = 0;
1106
+ const ACK_TIMEOUT_MS = 15e3;
1107
+ const EMPTY_BUFFER = Buffer.alloc(0);
1108
+ const FIRST_INDEX = 0;
1109
+ const NEXT_OFFSET = 1;
1067
1110
  function showUsageError(message) {
1068
1111
  blank();
1069
1112
  logError(message);
@@ -1101,6 +1144,16 @@ async function runServeCommand(argv, options = {}) {
1101
1144
  const { frames, intervalMs } = createPulseFrames("HBEAM");
1102
1145
  const spinner = createSpinner(frames, intervalMs);
1103
1146
  const lifecycle = createLifecycle(beam, spinner);
1147
+ let awaitingAck = false;
1148
+ let ackBuffer = EMPTY_BUFFER;
1149
+ let ackTimeout = void 0;
1150
+ function closeTransfer() {
1151
+ if (ackTimeout) {
1152
+ globalThis.clearTimeout(ackTimeout);
1153
+ ackTimeout = void 0;
1154
+ }
1155
+ beam.destroy();
1156
+ }
1104
1157
  blank();
1105
1158
  spinner.start();
1106
1159
  spinner.blank();
@@ -1135,17 +1188,49 @@ async function runServeCommand(argv, options = {}) {
1135
1188
  beam.on("error", (error) => {
1136
1189
  spinner.stop();
1137
1190
  const isPeerNotFound = error.message.includes("PEER_NOT_FOUND");
1191
+ if (awaitingAck && error.message.includes("connection reset by peer")) {
1192
+ logError("Receiver closed before completion acknowledgement.");
1193
+ blank();
1194
+ closeTransfer();
1195
+ return;
1196
+ }
1138
1197
  if (isPeerNotFound) log(red$1(dim$1("PEER NOT FOUND")));
1139
1198
  else if (error.message.includes("connection reset by peer")) log(dim$1("PEER DISCONNECTED"));
1140
1199
  else logError(error.message);
1141
1200
  blank();
1142
1201
  if (!isPeerNotFound) lifecycle.shutdown();
1143
1202
  });
1203
+ beam.on("data", (chunk) => {
1204
+ if (!awaitingAck) return;
1205
+ ackBuffer = Buffer.concat([ackBuffer, chunk]);
1206
+ while (true) {
1207
+ const lineEnd = findHeaderLineEnd(ackBuffer);
1208
+ if (lineEnd < FIRST_INDEX) return;
1209
+ const line = ackBuffer.subarray(FIRST_INDEX, lineEnd);
1210
+ ackBuffer = ackBuffer.subarray(lineEnd + NEXT_OFFSET);
1211
+ const ack = parseCompletionAck(line);
1212
+ if (ack) {
1213
+ awaitingAck = false;
1214
+ if (ack.ok) log(dim$1("RECEIVER CONFIRMED"));
1215
+ else logError(`Receiver declined file${ack.reason ? `: ${ack.reason}` : "."}`);
1216
+ blank();
1217
+ closeTransfer();
1218
+ return;
1219
+ }
1220
+ }
1221
+ });
1144
1222
  beam.on("end", () => beam.end());
1145
1223
  beam.on("finish", () => {
1146
1224
  log(dim$1("FILE SENT"));
1225
+ write(dim$1("WAITING FOR RECEIVER ACK"));
1147
1226
  blank();
1148
- beam.destroy();
1227
+ awaitingAck = true;
1228
+ ackTimeout = globalThis.setTimeout(() => {
1229
+ awaitingAck = false;
1230
+ logError("Timed out waiting for receiver acknowledgement.");
1231
+ blank();
1232
+ closeTransfer();
1233
+ }, ACK_TIMEOUT_MS);
1149
1234
  });
1150
1235
  beam.resume();
1151
1236
  }
@@ -1225,7 +1310,7 @@ if (argv.help) {
1225
1310
  process.exit(EXIT_SUCCESS);
1226
1311
  }
1227
1312
  if (argv.version) {
1228
- write((await import("./package-DHFOdqDi.mjs")).version ?? "0.0.0", NO_INDENT);
1313
+ write((await import("./package-DtWy_-83.mjs")).version ?? "0.0.0", NO_INDENT);
1229
1314
  process.exit(EXIT_SUCCESS);
1230
1315
  }
1231
1316
  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","dim","bold","red","KEY_SEED_BYTES","PUBLIC_KEY_BYTES","EXIT_FAILURE","dim","EXIT_SUCCESS","EXIT_FAILURE","dim","bold","dim","bold","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/dht.ts","../src/beam.ts","../src/lib/addressbook.ts","../src/commands/connect.ts","../src/commands/peers.ts","../src/commands/serve.ts","../src/commands/whoami.ts","../src/cli.ts"],"sourcesContent":["import { 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\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\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/** Copy text to the system clipboard using common platform commands. */\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","import { 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/** Absolute path to hbeam config directory. */\nexport function getConfigDir(): string {\n\treturn process.env[CONFIG_DIR_ENV] ?? join(homedir(), CONFIG_ROOT_DIR, APP_CONFIG_DIR)\n}\n\nfunction resolveConfigPath(filename: string): string {\n\treturn join(getConfigDir(), filename)\n}\n\n/** Ensure `~/.config/hbeam` exists with private directory permissions. */\nexport async function ensureConfigDir(): Promise<void> {\n\tawait mkdir(getConfigDir(), { mode: DIR_MODE, recursive: true })\n}\n\n/** Read and parse a JSON file from the hbeam config directory. */\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 */\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","import * as b4a from 'b4a'\nimport b32 from 'hi-base32'\nimport sodium from 'sodium-universal'\n\n/** Encode a buffer as a lowercase base32 string without padding. */\nexport function toBase32(buf: Buffer): string {\n\treturn b32.encode(buf).replace(/=/g, '').toLowerCase()\n}\n\n/** Decode a base32 string back into a raw Buffer. */\nexport function fromBase32(str: string): Buffer {\n\treturn b4a.from(b32.decode.asBytes(str.toUpperCase()))\n}\n\n/** Generate cryptographically secure random bytes. */\nexport function randomBytes(length: number): Buffer {\n\tconst buffer = b4a.alloc(length)\n\tsodium.randombytes_buf(buffer)\n\treturn buffer\n}\n","import 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\nfunction asHex(buffer: Buffer): string {\n\treturn buffer.toString('hex')\n}\n\nfunction fromHex(hex: string): Buffer {\n\treturn Buffer.from(hex, 'hex')\n}\n\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\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\nfunction serializeIdentity(keyPair: KeyPair): Identity {\n\treturn {\n\t\tpublicKey: asHex(keyPair.publicKey),\n\t\tsecretKey: asHex(keyPair.secretKey),\n\t}\n}\n\nfunction createIdentity(): KeyPair {\n\treturn DHT.keyPair(randomBytes(KEY_SEED_BYTES))\n}\n\n/** Load a persisted identity if present; otherwise create and persist one. */\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/** Load or create the local hbeam identity keypair. */\nexport async function loadOrCreateIdentity(): Promise<KeyPair> {\n\tconst { keyPair } = await loadOrCreateIdentityWithMeta()\n\treturn keyPair\n}\n\n/** Return the public key hex string for the local identity. */\nexport async function getPublicKeyHex(): Promise<string> {\n\tconst keyPair = await loadOrCreateIdentity()\n\treturn asHex(keyPair.publicKey)\n}\n","import { 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'\nconst NO_OFFSET = 0\n\nexport const INDENT = ' '\nexport const SEPARATOR = dim('╌'.repeat(SEPARATOR_WIDTH))\n\n/** Write a line to stderr at the standard indent level. */\nexport function write(message: string, indent: string = INDENT): void {\n\tprocess.stderr.write(`${indent}${message}\\n`)\n}\n\n/** Write a blank line to stderr. */\nexport function blank(): void {\n\tprocess.stderr.write('\\n')\n}\n\n/** Write a pre-formatted block (multiple lines) to stderr. */\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/** Write a status message to stderr at the standard indent level. */\nexport function log(message: string): void {\n\twrite(message)\n}\n\n/** Write an error message to stderr at the standard indent level. */\nexport function logError(message: string): void {\n\tprocess.stderr.write(`${INDENT}${red('ERROR')} ${message}\\n`)\n}\n\n/** Write a warning/notice message to stderr with a yellow prefix. */\nexport function logWarn(message: string): void {\n\tprocess.stderr.write(`${yellow('!')} ${message}\\n`)\n}\n\n/** Clear the current line (wipe terminal-echoed ^C, etc.). Falls back to a newline on non-TTY. */\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/** ANSI escape: move cursor up N lines. */\nfunction cursorUp(n: number): string {\n\treturn `\\u001B[${n}A`\n}\n\n/** ANSI escape: move cursor down N lines. */\nfunction cursorDown(n: number): string {\n\treturn `\\u001B[${n}B`\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/** Animate a single line in-place while content continues to print below it. */\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\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++\n\t\t},\n\t}\n}\n","const FILE_TYPE = 'file'\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 function encodeHeader(header: FileHeader): Buffer {\n\treturn Buffer.from(`${JSON.stringify(header)}${NEWLINE}`, 'utf8')\n}\n\nexport function isFileHeader(chunk: Buffer): boolean {\n\tconst lineEnd = findHeaderLineEnd(chunk)\n\tconst candidate = (lineEnd >= MIN_SIZE ? chunk.subarray(FIRST_INDEX, lineEnd) : chunk).toString('utf8')\n\tconst trimmed = candidate.trimStart()\n\treturn trimmed.startsWith('{') && trimmed.includes(`\"type\":\"${FILE_TYPE}\"`)\n}\n\nexport function parseFileHeader(line: Buffer): FileHeader {\n\tconst parsed = JSON.parse(line.toString('utf8')) as Partial<FileHeader>\n\tif (parsed.type !== FILE_TYPE) {\n\t\tthrow new Error('Invalid file header type')\n\t}\n\tif (!parsed.name || typeof parsed.name !== 'string') {\n\t\tthrow new Error('Invalid file header name')\n\t}\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\treturn { name: parsed.name, size: parsed.size, type: FILE_TYPE }\n}\n\nexport function findHeaderLineEnd(chunk: Buffer): number {\n\treturn chunk.indexOf(NEWLINE)\n}\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\tlet size = bytes\n\tlet unitIndex = FIRST_INDEX\n\tconst lastUnitIndex = UNIT_LABELS.length - LAST_INDEX_OFFSET\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\treturn `${size.toFixed(UNIT_PRECISION)} ${UNIT_LABELS[unitIndex]}`\n}\n","import { 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 */\nexport function createLifecycle(beam: Beam, spinner?: { stop(): void }): Lifecycle {\n\tlet isShuttingDown = false\n\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","import { 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\nfunction firstChar(data: Buffer): string {\n\treturn data.toString('utf8').toLowerCase().charAt(FIRST_CHAR_INDEX)\n}\n\n/** Minimal single-keypress confirm prompt with `y/N` default. */\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\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\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/** Minimal line-input prompt with an editable pre-filled default value. */\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\t\treturn trimmed === EMPTY_INPUT ? placeholder : trimmed\n\t} finally {\n\t\trl.close()\n\t}\n}\n","import { 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","import { createWriteStream } from 'node:fs'\nimport { mkdir } from 'node:fs/promises'\nimport { dirname, resolve } from 'node:path'\n\nimport {\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\tgray,\n\tINDENT,\n\tlog,\n\tlogError,\n\tred,\n\tSEPARATOR,\n\twrite,\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// Session\n// ---------------------------------------------------------------------------\n\n/** Run the standard hbeam CLI session UI and stdin/stdout piping. */\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\tspinner.start()\n\tspinner.blank()\n\n\tif (options.mode === 'announce') {\n\t\tspinner.write(dim(options.announceLabel ?? 'PUBLIC KEY'))\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\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\t\tspinner.stop()\n\t\tlog(bold('PIPE ACTIVE'))\n\t\twrite(gray('CTRL+C TO TERMINATE'))\n\t\tblank()\n\t})\n\n\t// -- Receive state -------------------------------------------------------\n\n\tlet receiveMode: ReceiveMode = 'unknown'\n\tlet receivedPipeData = false\n\tlet awaitingPrompt = false\n\tlet streamDone = 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// -- Helpers --------------------------------------------------------------\n\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\tfunction finalizeFile(): void {\n\t\tclearKeepAlive()\n\t\tif (!fileStream) {\n\t\t\treturn\n\t\t}\n\t\tfileStream.end(() => {\n\t\t\tlog(dim(`SAVED ${filePath ?? ''}`))\n\t\t\tblank()\n\t\t})\n\t\tfileStream = undefined\n\t}\n\n\tfunction writePipeChunk(chunk: Buffer): void {\n\t\tif (!receivedPipeData) {\n\t\t\treceivedPipeData = true\n\t\t\twrite(SEPARATOR)\n\t\t\tblank()\n\t\t}\n\t\tprocess.stdout.write(chunk.toString().replace(/^(?!$)/gm, INDENT))\n\t}\n\n\t// -- Stream lifecycle -----------------------------------------------------\n\n\tfunction onStreamDone(): void {\n\t\tstreamDone = true\n\n\t\t// Prompt still active — the .finally() handler will call us again.\n\t\tif (awaitingPrompt) {\n\t\t\treturn\n\t\t}\n\n\t\tif (receiveMode === 'file') {\n\t\t\tfinalizeFile()\n\t\t} else if (receiveMode === 'pipe' && receivedPipeData) {\n\t\t\tblank()\n\t\t\twrite(SEPARATOR)\n\t\t}\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\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\t\tblank()\n\n\t\tif (!isPeerNotFound) {\n\t\t\tlifecycle.shutdown()\n\t\t}\n\t})\n\n\t// -- File receive flow ----------------------------------------------------\n\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\t\tif (!process.stdout.isTTY) {\n\t\t\treturn undefined\n\t\t}\n\n\t\tconst suggestedPath = resolve(process.cwd(), fileName)\n\t\tconst shouldSave = await confirm('Save incoming file?')\n\t\tif (!shouldSave) {\n\t\t\treturn ''\n\t\t}\n\t\treturn await input('Save to:', suggestedPath)\n\t}\n\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(dim(`INCOMING FILE ${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('RECEIVE CANCELLED'))\n\t\t\tblank()\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// -- Data routing ---------------------------------------------------------\n\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\tbeam.on('data', (chunk: Buffer) => {\n\t\t// While the prompt is active, buffer incoming data.\n\t\tif (awaitingPrompt) {\n\t\t\tpendingChunks.push(chunk)\n\t\t\treturn\n\t\t}\n\n\t\t// Already resolved — route directly.\n\t\tif (receiveMode !== 'unknown') {\n\t\t\trouteChunk(chunk)\n\t\t\treturn\n\t\t}\n\n\t\t// First data: decide mode.\n\t\tpendingChunks.push(chunk)\n\t\tconst pending = Buffer.concat(pendingChunks)\n\n\t\tif (isFileHeader(pending)) {\n\t\t\tif (findHeaderLineEnd(pending) < FIRST_INDEX) {\n\t\t\t\treturn // Wait for more data to complete the header line.\n\t\t\t}\n\t\t\tpendingChunks = []\n\t\t\tawaitingPrompt = true\n\t\t\tvoid startFileReceive(pending).finally(() => {\n\t\t\t\tawaitingPrompt = false\n\t\t\t\tfor (const queued of pendingChunks) {\n\t\t\t\t\trouteChunk(queued)\n\t\t\t\t}\n\t\t\t\tpendingChunks = []\n\n\t\t\t\tif (streamDone) {\n\t\t\t\t\tonStreamDone()\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Not a file header — switch to pipe mode.\n\t\treceiveMode = 'pipe'\n\t\tpendingChunks = []\n\t\twritePipeChunk(pending)\n\t})\n\n\t// -- Kick off -------------------------------------------------------------\n\n\tprocess.stdin.pipe(beam)\n\n\tif (typeof process.stdin.unref === 'function') {\n\t\tprocess.stdin.unref()\n\t}\n}\n","import { createHash } from 'node:crypto'\n\nimport * as b4a from 'b4a'\nimport DHT from 'hyperdht'\n\nimport { fromBase32 } from './encoding.ts'\n\nimport type { EncryptedSocket, HyperDHTNode, KeyPair } from '../types.ts'\n\nconst KEY_SEED_BYTES = 32\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 */\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/** Derive a Noise keypair from a base32-encoded passphrase. */\nexport function deriveKeyPair(passphrase: string): KeyPair {\n\tconst seed = fromBase32(passphrase)\n\treturn DHT.keyPair(normalizeSeed(seed))\n}\n\n/** Create an ephemeral HyperDHT node that is destroyed with the beam. */\nexport function createNode(): HyperDHTNode {\n\treturn new DHT({ ephemeral: true }) as unknown as HyperDHTNode\n}\n\n/** Wait for an encrypted socket to complete its Noise handshake. */\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/** Create a firewall that rejects any connection not matching the keypair. */\nexport function createFirewall(keyPair: KeyPair): (remotePublicKey: Buffer) => boolean {\n\treturn (remotePublicKey: Buffer) => !b4a.equals(remotePublicKey, keyPair.publicKey)\n}\n","import 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\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/** Whether a peer connection has been established. */\n\tget connected(): boolean {\n\t\treturn this.outbound !== undefined\n\t}\n\n\t// Streamx lifecycle\n\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\toverride _read(cb: StreamCallback): void {\n\t\tthis.readCallback = cb\n\t\tthis.inbound?.resume()\n\t}\n\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\toverride _final(cb: StreamCallback): void {\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\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\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\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\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\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\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\tprivate pushEndOfStream(): void {\n\t\t// oxlint-disable-next-line unicorn/no-null\n\t\tthis.pushData(null)\n\t}\n\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\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\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\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","import { 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\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\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\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\nasync function readAddressBook(): Promise<AddressBook> {\n\treturn (await readJsonFile<AddressBook>(PEERS_FILE)) ?? {}\n}\n\nasync function writeAddressBook(addressBook: AddressBook): Promise<void> {\n\tawait writeJsonFile(PEERS_FILE, addressBook)\n}\n\n/** Add or update a named peer in the local address book. */\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/** Remove a peer from the local address book. */\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/** Return all peers sorted by name. */\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/** Lookup a single peer by name. */\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","import { 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/** Execute `hbeam connect <name>`. Exits on error; stays alive for the session. */\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","import { 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\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\nfunction shortenKey(publicKey: string): string {\n\treturn `${publicKey.slice(START_INDEX, PUBLIC_KEY_PREFIX_LENGTH)}...`\n}\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\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\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\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/** Execute `hbeam peers` subcommands. */\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","import { 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 { encodeHeader, formatFileSize } from '@/lib/file-protocol.ts'\nimport { loadOrCreateIdentityWithMeta } from '@/lib/identity.ts'\nimport { createLifecycle } from '@/lib/lifecycle.ts'\nimport {\n\tblank,\n\tbold,\n\tcreateSpinner,\n\tcyan,\n\tdim,\n\tgray,\n\tlog,\n\tlogError,\n\tred,\n\twrite,\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\n\ninterface ServeCommandOptions {\n\tlisten?: boolean\n}\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\nasync function resolveServeIdentity(listen: boolean | undefined): Promise<{\n\tannounceLabel: string\n\tkeyPair?: KeyPair\n}> {\n\tif (!listen) {\n\t\treturn { announceLabel: 'PASSPHRASE' }\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: 'PUBLIC KEY',\n\t\tkeyPair: identity.keyPair,\n\t}\n}\n\n/** Execute `hbeam serve <file>` to transfer one file to the first peer. */\nexport async function runServeCommand(\n\targv: string[],\n\toptions: ServeCommandOptions = {},\n): Promise<void> {\n\tconst [targetFile] = argv\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\tif (!fileStat || !fileStat.isFile()) {\n\t\tshowUsageError(`Not a readable file: ${targetFile}`)\n\t}\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\tblank()\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim(identity.announceLabel))\n\tspinner.write(cyan(beam.key))\n\tspinner.write(dim(`FILE ${fileName} (${formatFileSize(fileStat.size)})`))\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}\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\tlog(bold('PIPE ACTIVE'))\n\t\twrite(gray('SENDING FILE'))\n\t\tblank()\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 (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('end', () => beam.end())\n\tbeam.on('finish', () => {\n\t\tlog(dim('FILE SENT'))\n\t\tblank()\n\t\tbeam.destroy()\n\t})\n\n\t// streamx opens lazily; force it to start announcing now.\n\t;(beam as unknown as { resume(): void }).resume()\n}\n","import { 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/** Execute `hbeam whoami`. */\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\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 { 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', v: 'version' },\n\tboolean: ['help', 'listen', 'version'],\n\tstring: ['output'],\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 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('-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('# 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 === '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: 'PUBLIC KEY',\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: 'PASSPHRASE',\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","KEY_SEED_BYTES","PUBLIC_KEY_BYTES","EXIT_FAILURE","dim","EXIT_SUCCESS","EXIT_FAILURE","dim","bold","dim","bold","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/dht.ts","../src/beam.ts","../src/lib/addressbook.ts","../src/commands/connect.ts","../src/commands/peers.ts","../src/commands/serve.ts","../src/commands/whoami.ts","../src/cli.ts"],"sourcesContent":["import { 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\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\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/** Copy text to the system clipboard using common platform commands. */\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","import { 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/** Absolute path to hbeam config directory. */\nexport function getConfigDir(): string {\n\treturn process.env[CONFIG_DIR_ENV] ?? join(homedir(), CONFIG_ROOT_DIR, APP_CONFIG_DIR)\n}\n\nfunction resolveConfigPath(filename: string): string {\n\treturn join(getConfigDir(), filename)\n}\n\n/** Ensure `~/.config/hbeam` exists with private directory permissions. */\nexport async function ensureConfigDir(): Promise<void> {\n\tawait mkdir(getConfigDir(), { mode: DIR_MODE, recursive: true })\n}\n\n/** Read and parse a JSON file from the hbeam config directory. */\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 */\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","import * as b4a from 'b4a'\nimport b32 from 'hi-base32'\nimport sodium from 'sodium-universal'\n\n/** Encode a buffer as a lowercase base32 string without padding. */\nexport function toBase32(buf: Buffer): string {\n\treturn b32.encode(buf).replace(/=/g, '').toLowerCase()\n}\n\n/** Decode a base32 string back into a raw Buffer. */\nexport function fromBase32(str: string): Buffer {\n\treturn b4a.from(b32.decode.asBytes(str.toUpperCase()))\n}\n\n/** Generate cryptographically secure random bytes. */\nexport function randomBytes(length: number): Buffer {\n\tconst buffer = b4a.alloc(length)\n\tsodium.randombytes_buf(buffer)\n\treturn buffer\n}\n","import 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\nfunction asHex(buffer: Buffer): string {\n\treturn buffer.toString('hex')\n}\n\nfunction fromHex(hex: string): Buffer {\n\treturn Buffer.from(hex, 'hex')\n}\n\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\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\nfunction serializeIdentity(keyPair: KeyPair): Identity {\n\treturn {\n\t\tpublicKey: asHex(keyPair.publicKey),\n\t\tsecretKey: asHex(keyPair.secretKey),\n\t}\n}\n\nfunction createIdentity(): KeyPair {\n\treturn DHT.keyPair(randomBytes(KEY_SEED_BYTES))\n}\n\n/** Load a persisted identity if present; otherwise create and persist one. */\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/** Load or create the local hbeam identity keypair. */\nexport async function loadOrCreateIdentity(): Promise<KeyPair> {\n\tconst { keyPair } = await loadOrCreateIdentityWithMeta()\n\treturn keyPair\n}\n\n/** Return the public key hex string for the local identity. */\nexport async function getPublicKeyHex(): Promise<string> {\n\tconst keyPair = await loadOrCreateIdentity()\n\treturn asHex(keyPair.publicKey)\n}\n","import { 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'\nconst NO_OFFSET = 0\n\nexport const INDENT = ' '\nexport const SEPARATOR = dim('╌'.repeat(SEPARATOR_WIDTH))\n\n/** Write a line to stderr at the standard indent level. */\nexport function write(message: string, indent: string = INDENT): void {\n\tprocess.stderr.write(`${indent}${message}\\n`)\n}\n\n/** Write a blank line to stderr. */\nexport function blank(): void {\n\tprocess.stderr.write('\\n')\n}\n\n/** Write a pre-formatted block (multiple lines) to stderr. */\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/** Write a status message to stderr at the standard indent level. */\nexport function log(message: string): void {\n\twrite(message)\n}\n\n/** Write an error message to stderr at the standard indent level. */\nexport function logError(message: string): void {\n\tprocess.stderr.write(`${INDENT}${red('ERROR')} ${message}\\n`)\n}\n\n/** Write a warning/notice message to stderr with a yellow prefix. */\nexport function logWarn(message: string): void {\n\tprocess.stderr.write(`${yellow('!')} ${message}\\n`)\n}\n\n/** Clear the current line (wipe terminal-echoed ^C, etc.). Falls back to a newline on non-TTY. */\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/** ANSI escape: move cursor up N lines. */\nfunction cursorUp(n: number): string {\n\treturn `\\u001B[${n}A`\n}\n\n/** ANSI escape: move cursor down N lines. */\nfunction cursorDown(n: number): string {\n\treturn `\\u001B[${n}B`\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/** Animate a single line in-place while content continues to print below it. */\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\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++\n\t\t},\n\t}\n}\n","const 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\nexport function encodeHeader(header: FileHeader): Buffer {\n\treturn Buffer.from(`${JSON.stringify(header)}${NEWLINE}`, 'utf8')\n}\n\nexport function encodeCompletionAck(ack: FileCompletionAck): Buffer {\n\treturn Buffer.from(`${JSON.stringify(ack)}${NEWLINE}`, 'utf8')\n}\n\nexport function isFileHeader(chunk: Buffer): boolean {\n\tconst lineEnd = findHeaderLineEnd(chunk)\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\treturn trimmed.startsWith('{') && trimmed.includes(`\"type\":\"${FILE_TYPE}\"`)\n}\n\nexport function parseFileHeader(line: Buffer): FileHeader {\n\tconst parsed = JSON.parse(line.toString('utf8')) as Partial<FileHeader>\n\tif (parsed.type !== FILE_TYPE) {\n\t\tthrow new Error('Invalid file header type')\n\t}\n\tif (!parsed.name || typeof parsed.name !== 'string') {\n\t\tthrow new Error('Invalid file header name')\n\t}\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\treturn { name: parsed.name, size: parsed.size, type: FILE_TYPE }\n}\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\nexport function findHeaderLineEnd(chunk: Buffer): number {\n\treturn chunk.indexOf(NEWLINE)\n}\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\tlet size = bytes\n\tlet unitIndex = FIRST_INDEX\n\tconst lastUnitIndex = UNIT_LABELS.length - LAST_INDEX_OFFSET\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\treturn `${size.toFixed(UNIT_PRECISION)} ${UNIT_LABELS[unitIndex]}`\n}\n","import { 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 */\nexport function createLifecycle(beam: Beam, spinner?: { stop(): void }): Lifecycle {\n\tlet isShuttingDown = false\n\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","import { 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\nfunction firstChar(data: Buffer): string {\n\treturn data.toString('utf8').toLowerCase().charAt(FIRST_CHAR_INDEX)\n}\n\n/** Minimal single-keypress confirm prompt with `y/N` default. */\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\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\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/** Minimal line-input prompt with an editable pre-filled default value. */\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\t\treturn trimmed === EMPTY_INPUT ? placeholder : trimmed\n\t} finally {\n\t\trl.close()\n\t}\n}\n","import { 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","import { createWriteStream } from 'node:fs'\nimport { mkdir } from 'node:fs/promises'\nimport { dirname, 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\tgray,\n\tINDENT,\n\tlog,\n\tlogError,\n\tred,\n\tSEPARATOR,\n\twrite,\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// Session\n// ---------------------------------------------------------------------------\n\n/** Run the standard hbeam CLI session UI and stdin/stdout piping. */\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\tspinner.start()\n\tspinner.blank()\n\n\tif (options.mode === 'announce') {\n\t\tspinner.write(dim(options.announceLabel ?? 'PUBLIC KEY'))\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\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\t\tspinner.stop()\n\t\tlog(bold('PIPE ACTIVE'))\n\t\twrite(gray('CTRL+C TO TERMINATE'))\n\t\tblank()\n\t})\n\n\t// -- Receive state -------------------------------------------------------\n\n\tlet receiveMode: ReceiveMode = 'unknown'\n\tlet receivedPipeData = 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// -- Helpers --------------------------------------------------------------\n\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\tfunction finalizeFile(): void {\n\t\tclearKeepAlive()\n\t\tif (!fileStream) {\n\t\t\treturn\n\t\t}\n\t\tfileStream.end(() => {\n\t\t\tlog(dim(`SAVED ${filePath ?? ''}`))\n\t\t\tblank()\n\t\t\tbeam.write(encodeCompletionAck({ ok: true, type: 'file-complete' }))\n\t\t\tbeam.end()\n\t\t})\n\t\tfileStream = undefined\n\t}\n\n\tfunction writePipeChunk(chunk: Buffer): void {\n\t\tif (!receivedPipeData) {\n\t\t\treceivedPipeData = true\n\t\t\twrite(SEPARATOR)\n\t\t\tblank()\n\t\t}\n\t\tprocess.stdout.write(chunk.toString().replace(/^(?!$)/gm, INDENT))\n\t}\n\n\t// -- Stream lifecycle -----------------------------------------------------\n\n\tfunction onStreamDone(): void {\n\t\tstreamDone = true\n\n\t\t// Prompt still active — the .finally() handler will call us again.\n\t\tif (awaitingPrompt) {\n\t\t\treturn\n\t\t}\n\t\tif (streamTerminationHandled) {\n\t\t\treturn\n\t\t}\n\t\tstreamTerminationHandled = true\n\n\t\tif (receiveMode === 'file') {\n\t\t\tfinalizeFile()\n\t\t} else if (receiveMode === 'file-stdout') {\n\t\t\tbeam.write(encodeCompletionAck({ ok: true, type: 'file-complete' }))\n\t\t\tbeam.end()\n\t\t} else if (receiveMode === 'pipe' && receivedPipeData) {\n\t\t\tblank()\n\t\t\twrite(SEPARATOR)\n\t\t}\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\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\t\tblank()\n\n\t\tif (!isPeerNotFound) {\n\t\t\tlifecycle.shutdown()\n\t\t}\n\t})\n\n\t// -- File receive flow ----------------------------------------------------\n\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\t\tif (!process.stdout.isTTY) {\n\t\t\treturn undefined\n\t\t}\n\n\t\tconst suggestedPath = resolve(process.cwd(), fileName)\n\t\tconst shouldSave = await confirm('Save incoming file?')\n\t\tif (!shouldSave) {\n\t\t\treturn ''\n\t\t}\n\t\treturn await input('Save to:', suggestedPath)\n\t}\n\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(dim(`INCOMING FILE ${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('RECEIVE 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// -- Data routing ---------------------------------------------------------\n\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\tfunction flushPendingChunks(): void {\n\t\tfor (const queued of pendingChunks) {\n\t\t\trouteChunk(queued)\n\t\t}\n\t\tpendingChunks = []\n\t}\n\n\tbeam.on('data', (chunk: Buffer) => {\n\t\t// While the prompt is active, buffer incoming data.\n\t\tif (awaitingPrompt) {\n\t\t\tpendingChunks.push(chunk)\n\t\t\treturn\n\t\t}\n\n\t\t// Already resolved — route directly.\n\t\tif (receiveMode !== 'unknown') {\n\t\t\trouteChunk(chunk)\n\t\t\treturn\n\t\t}\n\n\t\t// First data: decide mode.\n\t\tpendingChunks.push(chunk)\n\t\tconst pending = Buffer.concat(pendingChunks)\n\n\t\tif (isFileHeader(pending)) {\n\t\t\tif (findHeaderLineEnd(pending) < FIRST_INDEX) {\n\t\t\t\treturn // Wait for more data to complete the header line.\n\t\t\t}\n\t\t\tpendingChunks = []\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\t\t\treturn\n\t\t}\n\n\t\t// Not a file header — switch to pipe mode.\n\t\treceiveMode = 'pipe'\n\t\tpendingChunks = []\n\t\twritePipeChunk(pending)\n\t})\n\n\t// -- Kick off -------------------------------------------------------------\n\n\tprocess.stdin.pipe(beam)\n\n\tif (typeof process.stdin.unref === 'function') {\n\t\tprocess.stdin.unref()\n\t}\n}\n","import { createHash } from 'node:crypto'\n\nimport * as b4a from 'b4a'\nimport DHT from 'hyperdht'\n\nimport { fromBase32 } from './encoding.ts'\n\nimport type { EncryptedSocket, HyperDHTNode, KeyPair } from '../types.ts'\n\nconst KEY_SEED_BYTES = 32\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 */\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/** Derive a Noise keypair from a base32-encoded passphrase. */\nexport function deriveKeyPair(passphrase: string): KeyPair {\n\tconst seed = fromBase32(passphrase)\n\treturn DHT.keyPair(normalizeSeed(seed))\n}\n\n/** Create an ephemeral HyperDHT node that is destroyed with the beam. */\nexport function createNode(): HyperDHTNode {\n\treturn new DHT({ ephemeral: true }) as unknown as HyperDHTNode\n}\n\n/** Wait for an encrypted socket to complete its Noise handshake. */\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/** Create a firewall that rejects any connection not matching the keypair. */\nexport function createFirewall(keyPair: KeyPair): (remotePublicKey: Buffer) => boolean {\n\treturn (remotePublicKey: Buffer) => !b4a.equals(remotePublicKey, keyPair.publicKey)\n}\n","import 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\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/** Whether a peer connection has been established. */\n\tget connected(): boolean {\n\t\treturn this.outbound !== undefined\n\t}\n\n\t// Streamx lifecycle\n\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\toverride _read(cb: StreamCallback): void {\n\t\tthis.readCallback = cb\n\t\tthis.inbound?.resume()\n\t}\n\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\toverride _final(cb: StreamCallback): void {\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\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\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\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\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\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\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\tprivate pushEndOfStream(): void {\n\t\t// oxlint-disable-next-line unicorn/no-null\n\t\tthis.pushData(null)\n\t}\n\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\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\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\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","import { 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\nfunction isHex(value: string): boolean {\n\treturn /^[0-9a-f]+$/i.test(value) && value.length % MODULUS_EVEN === REMAINDER_ZERO\n}\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\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\nasync function readAddressBook(): Promise<AddressBook> {\n\treturn (await readJsonFile<AddressBook>(PEERS_FILE)) ?? {}\n}\n\nasync function writeAddressBook(addressBook: AddressBook): Promise<void> {\n\tawait writeJsonFile(PEERS_FILE, addressBook)\n}\n\n/** Add or update a named peer in the local address book. */\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/** Remove a peer from the local address book. */\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/** Return all peers sorted by name. */\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/** Lookup a single peer by name. */\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","import { 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/** Execute `hbeam connect <name>`. Exits on error; stays alive for the session. */\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","import { 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\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\nfunction shortenKey(publicKey: string): string {\n\treturn `${publicKey.slice(START_INDEX, PUBLIC_KEY_PREFIX_LENGTH)}...`\n}\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\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\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\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/** Execute `hbeam peers` subcommands. */\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","import { 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\tbold,\n\tcreateSpinner,\n\tcyan,\n\tdim,\n\tgray,\n\tlog,\n\tlogError,\n\tred,\n\twrite,\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\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\nasync function resolveServeIdentity(listen: boolean | undefined): Promise<{\n\tannounceLabel: string\n\tkeyPair?: KeyPair\n}> {\n\tif (!listen) {\n\t\treturn { announceLabel: 'PASSPHRASE' }\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: 'PUBLIC KEY',\n\t\tkeyPair: identity.keyPair,\n\t}\n}\n\n/** Execute `hbeam serve <file>` to transfer one file to the first peer. */\nexport async function runServeCommand(\n\targv: string[],\n\toptions: ServeCommandOptions = {},\n): Promise<void> {\n\tconst [targetFile] = argv\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\tif (!fileStat || !fileStat.isFile()) {\n\t\tshowUsageError(`Not a readable file: ${targetFile}`)\n\t}\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\tlet awaitingAck = false\n\tlet ackBuffer = EMPTY_BUFFER\n\tlet ackTimeout: ReturnType<typeof globalThis.setTimeout> | undefined = undefined\n\n\tfunction closeTransfer(): void {\n\t\tif (ackTimeout) {\n\t\t\tglobalThis.clearTimeout(ackTimeout)\n\t\t\tackTimeout = undefined\n\t\t}\n\t\tbeam.destroy()\n\t}\n\n\tblank()\n\tspinner.start()\n\tspinner.blank()\n\tspinner.write(dim(identity.announceLabel))\n\tspinner.write(cyan(beam.key))\n\tspinner.write(dim(`FILE ${fileName} (${formatFileSize(fileStat.size)})`))\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}\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\tlog(bold('PIPE ACTIVE'))\n\t\twrite(gray('SENDING FILE'))\n\t\tblank()\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\tlogError('Receiver closed before completion acknowledgement.')\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\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\tlog(dim('RECEIVER CONFIRMED'))\n\t\t\t\t} else {\n\t\t\t\t\tlogError(`Receiver declined file${ack.reason ? `: ${ack.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\tlog(dim('FILE SENT'))\n\t\twrite(dim('WAITING FOR RECEIVER ACK'))\n\t\tblank()\n\t\tawaitingAck = true\n\t\tackTimeout = globalThis.setTimeout(() => {\n\t\t\tawaitingAck = false\n\t\t\tlogError('Timed out waiting for receiver acknowledgement.')\n\t\t\tblank()\n\t\t\tcloseTransfer()\n\t\t}, ACK_TIMEOUT_MS)\n\t})\n\n\t// streamx opens lazily; force it to start announcing now.\n\t;(beam as unknown as { resume(): void }).resume()\n}\n","import { 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/** Execute `hbeam whoami`. */\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\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 { 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', v: 'version' },\n\tboolean: ['help', 'listen', 'version'],\n\tstring: ['output'],\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 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('-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('# 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 === '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: 'PUBLIC KEY',\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: 'PASSPHRASE',\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.7-alpha.15";
3
+
4
+ //#endregion
5
+ export { version };
6
+ //# sourceMappingURL=package-DtWy_-83.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-DtWy_-83.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.7-alpha.13",
3
+ "version": "0.1.7-alpha.15",
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.7-alpha.13";
3
-
4
- //#endregion
5
- export { version };
6
- //# sourceMappingURL=package-DHFOdqDi.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"package-DHFOdqDi.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}