@elefunc/send 0.1.12 → 0.1.14
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/package.json +1 -1
- package/src/core/files.ts +44 -11
- package/src/core/session.ts +175 -10
- package/src/index.ts +10 -8
package/package.json
CHANGED
package/src/core/files.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, mkdir, stat } from "node:fs/promises"
|
|
1
|
+
import { access, mkdir, open, rm, stat, type FileHandle } from "node:fs/promises"
|
|
2
2
|
import { basename, extname, join, resolve } from "node:path"
|
|
3
3
|
import { resolveUserPath } from "./paths"
|
|
4
4
|
|
|
@@ -8,16 +8,16 @@ export interface LocalFile {
|
|
|
8
8
|
size: number
|
|
9
9
|
type: string
|
|
10
10
|
lastModified: number
|
|
11
|
-
|
|
11
|
+
reader?: FileHandle
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export type LocalFileInfo = Omit<LocalFile, "
|
|
14
|
+
export type LocalFileInfo = Omit<LocalFile, "reader">
|
|
15
15
|
export interface LocalPathIssue {
|
|
16
16
|
path: string
|
|
17
17
|
error: string
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
export const pathExists = async (path: string) => access(path).then(() => true, () => false)
|
|
21
21
|
|
|
22
22
|
export const inspectLocalFile = async (path: string): Promise<LocalFileInfo> => {
|
|
23
23
|
const absolute = resolveUserPath(path)
|
|
@@ -52,23 +52,52 @@ export const inspectLocalPaths = async (paths: string[]) => {
|
|
|
52
52
|
|
|
53
53
|
export const loadLocalFile = async (path: string): Promise<LocalFile> => {
|
|
54
54
|
const info = await inspectLocalFile(path)
|
|
55
|
-
return {
|
|
56
|
-
...info,
|
|
57
|
-
blob: Bun.file(info.path),
|
|
58
|
-
}
|
|
55
|
+
return { ...info }
|
|
59
56
|
}
|
|
60
57
|
|
|
61
58
|
export const loadLocalFiles = (paths: string[]) => Promise.all(paths.map(loadLocalFile))
|
|
62
59
|
|
|
63
|
-
|
|
60
|
+
const readHandleChunk = async (handle: FileHandle, offset: number, size: number) => {
|
|
61
|
+
const chunk = Buffer.allocUnsafe(size)
|
|
62
|
+
let bytesReadTotal = 0
|
|
63
|
+
while (bytesReadTotal < size) {
|
|
64
|
+
const { bytesRead } = await handle.read(chunk, bytesReadTotal, size - bytesReadTotal, offset + bytesReadTotal)
|
|
65
|
+
if (!bytesRead) break
|
|
66
|
+
bytesReadTotal += bytesRead
|
|
67
|
+
}
|
|
68
|
+
return bytesReadTotal === size ? chunk : chunk.subarray(0, bytesReadTotal)
|
|
69
|
+
}
|
|
64
70
|
|
|
65
|
-
export const
|
|
71
|
+
export const readFileChunk = async (file: LocalFile, offset: number, size: number) => {
|
|
72
|
+
file.reader ||= await open(file.path, "r")
|
|
73
|
+
return readHandleChunk(file.reader, offset, size)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const closeLocalFile = async (file?: LocalFile) => {
|
|
77
|
+
if (!file?.reader) return
|
|
78
|
+
const reader = file.reader
|
|
79
|
+
file.reader = undefined
|
|
80
|
+
await reader.close()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const writeFileChunk = async (handle: FileHandle, data: Buffer, offset: number) => {
|
|
84
|
+
let bytesWrittenTotal = 0
|
|
85
|
+
while (bytesWrittenTotal < data.byteLength) {
|
|
86
|
+
const { bytesWritten } = await handle.write(data, bytesWrittenTotal, data.byteLength - bytesWrittenTotal, offset + bytesWrittenTotal)
|
|
87
|
+
if (!bytesWritten) throw new Error("short write")
|
|
88
|
+
bytesWrittenTotal += bytesWritten
|
|
89
|
+
}
|
|
90
|
+
return bytesWrittenTotal
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const uniqueOutputPath = async (directory: string, fileName: string, reservedPaths: ReadonlySet<string> = new Set()) => {
|
|
66
94
|
await mkdir(directory, { recursive: true })
|
|
67
95
|
const extension = extname(fileName)
|
|
68
96
|
const stem = extension ? fileName.slice(0, -extension.length) : fileName
|
|
69
97
|
for (let index = 0; ; index += 1) {
|
|
70
98
|
const candidate = join(directory, index ? `${stem} (${index})${extension}` : fileName)
|
|
71
|
-
if (
|
|
99
|
+
if (reservedPaths.has(candidate)) continue
|
|
100
|
+
if (!await pathExists(candidate)) return candidate
|
|
72
101
|
}
|
|
73
102
|
}
|
|
74
103
|
|
|
@@ -77,3 +106,7 @@ export const saveIncomingFile = async (directory: string, fileName: string, data
|
|
|
77
106
|
await Bun.write(path, data)
|
|
78
107
|
return path
|
|
79
108
|
}
|
|
109
|
+
|
|
110
|
+
export const removePath = async (path: string) => {
|
|
111
|
+
await rm(path, { force: true }).catch(() => {})
|
|
112
|
+
}
|
package/src/core/session.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { open, rename, type FileHandle } from "node:fs/promises"
|
|
1
2
|
import { resolve } from "node:path"
|
|
2
3
|
import type { RTCDataChannel, RTCIceCandidateInit, RTCIceServer } from "werift"
|
|
3
4
|
import { RTCPeerConnection } from "werift"
|
|
4
|
-
import { loadLocalFiles, readFileChunk, saveIncomingFile, type LocalFile } from "./files"
|
|
5
|
+
import { closeLocalFile, loadLocalFiles, pathExists, readFileChunk, removePath, saveIncomingFile, uniqueOutputPath, writeFileChunk, type LocalFile } from "./files"
|
|
5
6
|
import {
|
|
6
7
|
BASE_ICE_SERVERS,
|
|
7
8
|
BUFFER_HIGH,
|
|
@@ -87,12 +88,23 @@ interface TransferState {
|
|
|
87
88
|
file?: LocalFile
|
|
88
89
|
buffers?: Buffer[]
|
|
89
90
|
data?: Buffer
|
|
91
|
+
incomingDisk?: IncomingDiskState
|
|
90
92
|
inFlight: boolean
|
|
91
93
|
cancel: boolean
|
|
92
94
|
cancelReason?: string
|
|
93
95
|
cancelSource?: "local" | "remote"
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
interface IncomingDiskState {
|
|
99
|
+
finalPath: string
|
|
100
|
+
tempPath: string
|
|
101
|
+
handle: FileHandle
|
|
102
|
+
queue: Promise<void>
|
|
103
|
+
offset: number
|
|
104
|
+
error: string
|
|
105
|
+
closed: boolean
|
|
106
|
+
}
|
|
107
|
+
|
|
96
108
|
export interface PeerSnapshot {
|
|
97
109
|
id: string
|
|
98
110
|
name: string
|
|
@@ -384,6 +396,7 @@ export class SendSession {
|
|
|
384
396
|
private readonly logs: LogEntry[] = []
|
|
385
397
|
private readonly subscribers = new Set<() => void>()
|
|
386
398
|
private readonly eventSubscribers = new Set<(event: SessionEvent) => void>()
|
|
399
|
+
private readonly reservedSavePaths = new Set<string>()
|
|
387
400
|
|
|
388
401
|
private rtcEpochCounter = 0
|
|
389
402
|
private socket: WebSocket | null = null
|
|
@@ -674,7 +687,14 @@ export class SendSession {
|
|
|
674
687
|
if (!transfer || transfer.direction !== "in" || isFinal(transfer)) return false
|
|
675
688
|
const peer = this.peers.get(transfer.peerId)
|
|
676
689
|
if (!peer || !this.isPeerReady(peer)) return false
|
|
677
|
-
|
|
690
|
+
const streamingToDisk = await this.startIncomingDiskTransfer(transfer)
|
|
691
|
+
transfer.data = undefined
|
|
692
|
+
transfer.buffers = streamingToDisk ? undefined : []
|
|
693
|
+
if (!this.sendDataControl(peer, { kind: "file-accept", transferId })) {
|
|
694
|
+
if (streamingToDisk) this.discardIncomingDiskTransfer(transfer)
|
|
695
|
+
transfer.buffers = []
|
|
696
|
+
return false
|
|
697
|
+
}
|
|
678
698
|
transfer.status = "accepted"
|
|
679
699
|
this.noteTransfer(transfer)
|
|
680
700
|
this.emit({ type: "transfer", transfer: this.transferSnapshot(transfer) })
|
|
@@ -738,13 +758,12 @@ export class SendSession {
|
|
|
738
758
|
async saveTransfer(transferId: string) {
|
|
739
759
|
const transfer = this.transfers.get(transferId)
|
|
740
760
|
if (!transfer || transfer.direction !== "in" || transfer.status !== "complete") return null
|
|
761
|
+
if (transfer.savedPath) return transfer.savedPath
|
|
741
762
|
if (!transfer.data && transfer.buffers?.length) transfer.data = Buffer.concat(transfer.buffers)
|
|
742
763
|
if (!transfer.data) return null
|
|
743
|
-
transfer.savedPath
|
|
764
|
+
transfer.savedPath = await saveIncomingFile(this.saveDir, transfer.name, transfer.data)
|
|
744
765
|
transfer.savedAt ||= Date.now()
|
|
745
|
-
|
|
746
|
-
this.pushLog("transfer:saved", { transferId: transfer.id, path: transfer.savedPath })
|
|
747
|
-
this.emit({ type: "saved", transfer: snapshot })
|
|
766
|
+
this.emitTransferSaved(transfer)
|
|
748
767
|
this.notify()
|
|
749
768
|
return transfer.savedPath
|
|
750
769
|
}
|
|
@@ -753,6 +772,124 @@ export class SendSession {
|
|
|
753
772
|
return this.transfers.get(transferId)
|
|
754
773
|
}
|
|
755
774
|
|
|
775
|
+
private emitTransferSaved(transfer: TransferState) {
|
|
776
|
+
const snapshot = this.transferSnapshot(transfer)
|
|
777
|
+
this.pushLog("transfer:saved", { transferId: transfer.id, path: transfer.savedPath })
|
|
778
|
+
this.emit({ type: "saved", transfer: snapshot })
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
private autoSaveTransfer(transferId: string) {
|
|
782
|
+
void this.saveTransfer(transferId).catch(error => {
|
|
783
|
+
this.pushLog("transfer:save-error", { transferId, error: `${error}` }, "error")
|
|
784
|
+
this.notify()
|
|
785
|
+
})
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
private async createIncomingDiskState(fileName: string): Promise<IncomingDiskState> {
|
|
789
|
+
const finalPath = await uniqueOutputPath(this.saveDir, fileName || "download", this.reservedSavePaths)
|
|
790
|
+
this.reservedSavePaths.add(finalPath)
|
|
791
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
792
|
+
const tempPath = `${finalPath}.part.${uid(6)}${attempt ? `.${attempt}` : ""}`
|
|
793
|
+
try {
|
|
794
|
+
const handle = await open(tempPath, "wx")
|
|
795
|
+
return {
|
|
796
|
+
finalPath,
|
|
797
|
+
tempPath,
|
|
798
|
+
handle,
|
|
799
|
+
queue: Promise.resolve(),
|
|
800
|
+
offset: 0,
|
|
801
|
+
error: "",
|
|
802
|
+
closed: false,
|
|
803
|
+
}
|
|
804
|
+
} catch (error) {
|
|
805
|
+
if ((error as NodeJS.ErrnoException | undefined)?.code === "EEXIST") continue
|
|
806
|
+
this.reservedSavePaths.delete(finalPath)
|
|
807
|
+
throw error
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
private takeIncomingDisk(transfer: TransferState) {
|
|
813
|
+
const disk = transfer.incomingDisk
|
|
814
|
+
transfer.incomingDisk = undefined
|
|
815
|
+
return disk
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
private async closeIncomingDiskState(disk: IncomingDiskState, removeTemp: boolean) {
|
|
819
|
+
if (!disk.closed) {
|
|
820
|
+
disk.closed = true
|
|
821
|
+
try { await disk.handle.close() } catch {}
|
|
822
|
+
}
|
|
823
|
+
if (removeTemp) await removePath(disk.tempPath)
|
|
824
|
+
this.reservedSavePaths.delete(disk.finalPath)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
private discardIncomingDiskTransfer(transfer: TransferState) {
|
|
828
|
+
const disk = this.takeIncomingDisk(transfer)
|
|
829
|
+
if (!disk) return
|
|
830
|
+
void this.closeIncomingDiskState(disk, true)
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
private async startIncomingDiskTransfer(transfer: TransferState) {
|
|
834
|
+
if (!this.autoSaveIncoming || transfer.incomingDisk) return false
|
|
835
|
+
try {
|
|
836
|
+
transfer.incomingDisk = await this.createIncomingDiskState(transfer.name)
|
|
837
|
+
return true
|
|
838
|
+
} catch (error) {
|
|
839
|
+
this.pushLog("transfer:save-error", { transferId: transfer.id, error: `${error}` }, "error")
|
|
840
|
+
return false
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
private queueIncomingDiskWrite(peer: PeerState, transfer: TransferState, data: Buffer) {
|
|
845
|
+
const disk = transfer.incomingDisk
|
|
846
|
+
if (!disk) return
|
|
847
|
+
disk.queue = disk.queue.then(async () => {
|
|
848
|
+
if (disk.closed || disk.error || transfer.cancel || transfer.status !== "receiving") return
|
|
849
|
+
await writeFileChunk(disk.handle, data, disk.offset)
|
|
850
|
+
disk.offset += data.byteLength
|
|
851
|
+
}).catch(error => {
|
|
852
|
+
if (disk.error || isFinal(transfer)) return
|
|
853
|
+
disk.error = `${error}`
|
|
854
|
+
this.pushLog("transfer:save-error", { transferId: transfer.id, error: disk.error }, "error")
|
|
855
|
+
this.sendDataControl(peer, { kind: "file-error", transferId: transfer.id, reason: disk.error })
|
|
856
|
+
this.completeTransfer(transfer, "error", disk.error)
|
|
857
|
+
})
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
private async finalizeIncomingDiskTransfer(transfer: TransferState, expectedSize: number) {
|
|
861
|
+
const disk = this.takeIncomingDisk(transfer)
|
|
862
|
+
if (!disk) return null
|
|
863
|
+
await disk.queue
|
|
864
|
+
if (disk.error) {
|
|
865
|
+
await this.closeIncomingDiskState(disk, true)
|
|
866
|
+
return null
|
|
867
|
+
}
|
|
868
|
+
if (disk.offset !== expectedSize) throw new Error(`saved size mismatch: ${disk.offset} vs ${expectedSize}`)
|
|
869
|
+
|
|
870
|
+
let finalPath = disk.finalPath
|
|
871
|
+
try {
|
|
872
|
+
if (!disk.closed) {
|
|
873
|
+
disk.closed = true
|
|
874
|
+
await disk.handle.close()
|
|
875
|
+
}
|
|
876
|
+
if (await pathExists(finalPath)) {
|
|
877
|
+
this.reservedSavePaths.delete(finalPath)
|
|
878
|
+
finalPath = await uniqueOutputPath(this.saveDir, transfer.name || "download", this.reservedSavePaths)
|
|
879
|
+
this.reservedSavePaths.add(finalPath)
|
|
880
|
+
}
|
|
881
|
+
await rename(disk.tempPath, finalPath)
|
|
882
|
+
transfer.savedPath = finalPath
|
|
883
|
+
transfer.savedAt ||= Date.now()
|
|
884
|
+
return finalPath
|
|
885
|
+
} catch (error) {
|
|
886
|
+
await removePath(disk.tempPath)
|
|
887
|
+
throw error
|
|
888
|
+
} finally {
|
|
889
|
+
this.reservedSavePaths.delete(finalPath)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
756
893
|
async waitFor(predicate: () => boolean, timeoutMs: number, signal?: AbortSignal | null) {
|
|
757
894
|
if (predicate()) return
|
|
758
895
|
if (signal?.aborted) throw new SessionAbortedError()
|
|
@@ -930,9 +1067,11 @@ export class SendSession {
|
|
|
930
1067
|
transfer.inFlight = false
|
|
931
1068
|
transfer.endedAt = Date.now()
|
|
932
1069
|
this.noteTransfer(transfer)
|
|
1070
|
+
if (transfer.direction === "out") void closeLocalFile(transfer.file).catch(() => {})
|
|
933
1071
|
if (status !== "complete" && transfer.direction === "in") {
|
|
934
1072
|
transfer.buffers = []
|
|
935
1073
|
transfer.data = undefined
|
|
1074
|
+
this.discardIncomingDiskTransfer(transfer)
|
|
936
1075
|
}
|
|
937
1076
|
|
|
938
1077
|
const peer = this.peers.get(transfer.peerId)
|
|
@@ -945,7 +1084,7 @@ export class SendSession {
|
|
|
945
1084
|
|
|
946
1085
|
const snapshot = this.transferSnapshot(transfer)
|
|
947
1086
|
this.emit({ type: "transfer", transfer: snapshot })
|
|
948
|
-
if (status === "complete" && transfer.direction === "in" && this.autoSaveIncoming)
|
|
1087
|
+
if (status === "complete" && transfer.direction === "in" && this.autoSaveIncoming && transfer.savedAt === 0) this.autoSaveTransfer(transfer.id)
|
|
949
1088
|
this.notify()
|
|
950
1089
|
}
|
|
951
1090
|
|
|
@@ -1517,8 +1656,11 @@ export class SendSession {
|
|
|
1517
1656
|
private onBinary(peer: PeerState, data: Buffer) {
|
|
1518
1657
|
const transfer = this.transfers.get(peer.activeIncoming)
|
|
1519
1658
|
if (!transfer || transfer.status !== "receiving") return
|
|
1520
|
-
transfer.
|
|
1521
|
-
|
|
1659
|
+
if (transfer.incomingDisk) this.queueIncomingDiskWrite(peer, transfer, data)
|
|
1660
|
+
else {
|
|
1661
|
+
transfer.buffers ||= []
|
|
1662
|
+
transfer.buffers.push(data)
|
|
1663
|
+
}
|
|
1522
1664
|
transfer.bytes += data.byteLength
|
|
1523
1665
|
transfer.chunks += 1
|
|
1524
1666
|
this.noteTransfer(transfer)
|
|
@@ -1579,7 +1721,8 @@ export class SendSession {
|
|
|
1579
1721
|
peer.activeIncoming = transfer.id
|
|
1580
1722
|
transfer.status = "receiving"
|
|
1581
1723
|
transfer.startedAt ||= Date.now()
|
|
1582
|
-
transfer.
|
|
1724
|
+
transfer.data = undefined
|
|
1725
|
+
transfer.buffers = transfer.incomingDisk ? undefined : []
|
|
1583
1726
|
this.noteTransfer(transfer)
|
|
1584
1727
|
this.emit({ type: "transfer", transfer: this.transferSnapshot(transfer) })
|
|
1585
1728
|
}
|
|
@@ -1593,6 +1736,7 @@ export class SendSession {
|
|
|
1593
1736
|
case "file-end": {
|
|
1594
1737
|
const transfer = this.transfers.get(message.transferId)
|
|
1595
1738
|
if (!transfer || transfer.direction !== "in") break
|
|
1739
|
+
if (isFinal(transfer)) break
|
|
1596
1740
|
if (transfer.status === "cancelling") {
|
|
1597
1741
|
this.completeTransfer(transfer, "cancelled", transfer.cancelReason || "cancelled")
|
|
1598
1742
|
break
|
|
@@ -1601,6 +1745,27 @@ export class SendSession {
|
|
|
1601
1745
|
this.completeTransfer(transfer, "error", `unexpected end while ${transfer.status}`)
|
|
1602
1746
|
break
|
|
1603
1747
|
}
|
|
1748
|
+
if (transfer.bytes !== message.size) {
|
|
1749
|
+
const reason = `size mismatch: ${transfer.bytes} vs ${message.size}`
|
|
1750
|
+
this.sendDataControl(peer, { kind: "file-error", transferId: transfer.id, reason })
|
|
1751
|
+
this.completeTransfer(transfer, "error", reason)
|
|
1752
|
+
break
|
|
1753
|
+
}
|
|
1754
|
+
if (transfer.incomingDisk) {
|
|
1755
|
+
try {
|
|
1756
|
+
const savedPath = await this.finalizeIncomingDiskTransfer(transfer, message.size)
|
|
1757
|
+
if (!savedPath) break
|
|
1758
|
+
this.sendDataControl(peer, { kind: "file-done", transferId: transfer.id, size: transfer.bytes, totalChunks: transfer.chunks })
|
|
1759
|
+
this.completeTransfer(transfer, "complete")
|
|
1760
|
+
this.emitTransferSaved(transfer)
|
|
1761
|
+
} catch (error) {
|
|
1762
|
+
const reason = `${error}`
|
|
1763
|
+
this.pushLog("transfer:save-error", { transferId: transfer.id, error: reason }, "error")
|
|
1764
|
+
this.sendDataControl(peer, { kind: "file-error", transferId: transfer.id, reason })
|
|
1765
|
+
this.completeTransfer(transfer, "error", reason)
|
|
1766
|
+
}
|
|
1767
|
+
break
|
|
1768
|
+
}
|
|
1604
1769
|
const data = Buffer.concat(transfer.buffers || [])
|
|
1605
1770
|
if (data.byteLength !== message.size) {
|
|
1606
1771
|
const reason = `size mismatch: ${data.byteLength} vs ${message.size}`
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { resolve } from "node:path"
|
|
3
3
|
import { cac, type CAC } from "cac"
|
|
4
|
-
import { cleanRoom } from "./core/protocol"
|
|
4
|
+
import { cleanRoom, displayPeerName } from "./core/protocol"
|
|
5
5
|
import type { SendSession, SessionConfig, SessionEvent } from "./core/session"
|
|
6
6
|
import { resolvePeerTargets } from "./core/targeting"
|
|
7
7
|
import { ensureSessionRuntimePatches, ensureTuiRuntimePatches } from "../runtime/install"
|
|
@@ -74,6 +74,7 @@ const TUI_TOGGLE_OPTIONS = [
|
|
|
74
74
|
["--offer <0|1>", "auto-offer drafts to matching ready peers: 1 on, 0 off"],
|
|
75
75
|
["--save <0|1>", "auto-save completed incoming files: 1 on, 0 off"],
|
|
76
76
|
] as const
|
|
77
|
+
export const ACCEPT_SESSION_DEFAULTS = { autoAcceptIncoming: true, autoSaveIncoming: true } as const
|
|
77
78
|
const addOptions = (command: CliCommand, definitions: readonly (readonly [string, string])[]) =>
|
|
78
79
|
definitions.reduce((next, [flag, description]) => next.option(flag, description), command)
|
|
79
80
|
|
|
@@ -108,9 +109,10 @@ export const sessionConfigFrom = (options: Record<string, unknown>, defaults: {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
export const roomAnnouncement = (room: string, json = false) =>
|
|
112
|
+
export const roomAnnouncement = (room: string, self: string, json = false) =>
|
|
113
|
+
json ? JSON.stringify({ type: "room", room, self }) : `room ${room}\nself ${self}`
|
|
112
114
|
|
|
113
|
-
const printRoomAnnouncement = (room: string, json = false) => console.log(roomAnnouncement(room, json))
|
|
115
|
+
const printRoomAnnouncement = (room: string, self: string, json = false) => console.log(roomAnnouncement(room, self, json))
|
|
114
116
|
|
|
115
117
|
const printEvent = (event: SessionEvent) => console.log(JSON.stringify(event))
|
|
116
118
|
|
|
@@ -191,7 +193,7 @@ const peersCommand = async (options: Record<string, unknown>) => {
|
|
|
191
193
|
const { SendSession } = await loadSessionRuntime()
|
|
192
194
|
const session = new SendSession(sessionConfigFrom(options, {}))
|
|
193
195
|
handleSignals(session)
|
|
194
|
-
printRoomAnnouncement(session.room, !!options.json)
|
|
196
|
+
printRoomAnnouncement(session.room, displayPeerName(session.name, session.localId), !!options.json)
|
|
195
197
|
await session.connect()
|
|
196
198
|
await Bun.sleep(numberOption(options.wait, 3000))
|
|
197
199
|
const snapshot = session.snapshot()
|
|
@@ -218,7 +220,7 @@ const offerCommand = async (files: string[], options: Record<string, unknown>) =
|
|
|
218
220
|
const { SendSession } = await loadSessionRuntime()
|
|
219
221
|
const session = new SendSession(sessionConfigFrom(options, {}))
|
|
220
222
|
handleSignals(session)
|
|
221
|
-
printRoomAnnouncement(session.room, !!options.json)
|
|
223
|
+
printRoomAnnouncement(session.room, displayPeerName(session.name, session.localId), !!options.json)
|
|
222
224
|
const detachReporter = attachReporter(session, !!options.json)
|
|
223
225
|
await session.connect()
|
|
224
226
|
const targets = await waitForTargets(session, selectors, timeoutMs)
|
|
@@ -232,9 +234,9 @@ const offerCommand = async (files: string[], options: Record<string, unknown>) =
|
|
|
232
234
|
|
|
233
235
|
const acceptCommand = async (options: Record<string, unknown>) => {
|
|
234
236
|
const { SendSession } = await loadSessionRuntime()
|
|
235
|
-
const session = new SendSession(sessionConfigFrom(options,
|
|
237
|
+
const session = new SendSession(sessionConfigFrom(options, ACCEPT_SESSION_DEFAULTS))
|
|
236
238
|
handleSignals(session)
|
|
237
|
-
printRoomAnnouncement(session.room, !!options.json)
|
|
239
|
+
printRoomAnnouncement(session.room, displayPeerName(session.name, session.localId), !!options.json)
|
|
238
240
|
const detachReporter = attachReporter(session, !!options.json)
|
|
239
241
|
await session.connect()
|
|
240
242
|
if (!options.json) console.log(`listening in ${session.room}`)
|
|
@@ -252,7 +254,7 @@ const acceptCommand = async (options: Record<string, unknown>) => {
|
|
|
252
254
|
}
|
|
253
255
|
|
|
254
256
|
const tuiCommand = async (options: Record<string, unknown>) => {
|
|
255
|
-
const initialConfig = sessionConfigFrom(options,
|
|
257
|
+
const initialConfig = sessionConfigFrom(options, ACCEPT_SESSION_DEFAULTS)
|
|
256
258
|
const { clean, offer } = parseBinaryOptions(options, ["clean", "offer"] as const)
|
|
257
259
|
const { startTui } = await loadTuiRuntime()
|
|
258
260
|
await startTui(initialConfig, {
|