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 +102 -17
- package/dist/cli.mjs.map +1 -1
- package/dist/package-DtWy_-83.mjs +6 -0
- package/dist/package-DtWy_-83.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/package-DHFOdqDi.mjs +0 -6
- package/dist/package-DHFOdqDi.mjs.map +0 -1
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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 === "
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"package-DtWy_-83.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"package-DHFOdqDi.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|