Skip to content

Releases: thebkht/cliplink

@thebkht/rtc-file-transfer 1.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 14:13
778a196

📦 https://www.npmjs.com/package/@thebkht/rtc-file-transfer

  • A device that has a file can pass it on. With keepReceived, a finished download stays in the resume store instead of being deleted, and seed(entries) offers what the store holds — after a reload too — under a fresh offer id and the original digest. Nothing is hashed again, and the bytes are read back only when a peer asks. Seeded items are seeded: true.
  • Offers of the same content are one item. Matched by digest, they collapse into one incoming item whose sources lists every peer offering it; peerId is the one a download uses. A source leaving only revokes the item when it was the last. Offers without a digest keep a row each, as before.
  • A download continues at another peer when its source goes away or runs out, keeping its sink and every verified block. That is a handoff, not a failure: nothing is reported. Sources that let the download down are tried last, and handoffs that make no progress are bounded.
  • The partial capability lets a sender offer a verified prefix. The offer's have says how much, and the sender ends with {"t":"part","b":N} instead of "done". With seedWhileDownloading (on by default) a stored download offers its prefix while it is still arriving. hello now carries caps, since offers are broadcast and that is where a sender learns which peers can take a partial one.
  • ResumeProvider gains optional list, read and keep, implemented by opfsResume, which also takes a tag. ResumeKey may carry mime and path. A file stored with a different segmentBytes now starts over rather than resuming into misaligned parts.
  • prefixRange in /sinks: the Range arithmetic for playing a file while it downloads.
  • A device no longer lists an incoming offer for content it is itself offering.
  • FileItem.verifiedBytes: how much of a download has been verified and written, which on a slow disk trails bytes. Cleared when the download ends.
  • A growing prefix is re-announced at most every 5 s, and a finished download is offered on even when no prefix was. A content item that is withdrawn stays withdrawn for the session.
  • A peer's capabilities are learned from its offers and requests as well as its hello, and a source that returns under the same offer is taken back.
  • A download whose finished file is shorter than its size, or fails its digest, fails and forgets what was stored of it.
  • New exports: ItemSource, StoredFile, StoredMeta, prefixRange, PrefixRange.

All additive. Every new field is optional and every new behaviour is negotiated, so a 1.2 peer — or a v1 one — interoperates exactly as before.

@thebkht/rtc-file-transfer 1.2.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 11:22
3962a28

📦 https://www.npmjs.com/package/@thebkht/rtc-file-transfer

  • A failed connection is restarted once before the transfer is given up on. connectionState === "failed" went straight to a nat failure, so the things that routinely break a candidate pair mid-file — moving from Wi-Fi to Ethernet, a phone changing cell, a NAT binding expiring — ended a transfer that the network could still have carried. The sender now re-offers with iceRestart, which gathers fresh candidates over the existing connection. Only the sender offers, along the path it already used, so the restart is answered by a peer running an older version with no new message type and no capability to negotiate. One attempt, not a retry loop.
  • limits.iceRestartMs (8 s) bounds how long a restarted connection has to come back. Shorter than stallMs by default, so a restart that does not take still reports nat rather than stalled.
  • iceServers was already resolved per connection, so a restart dials with refreshed TURN credentials without further work.

No wire changes. An ICE restart is an ordinary rtc-description offer.

@thebkht/cliplink 0.2.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 11:22
3962a28

📦 https://www.npmjs.com/package/@thebkht/cliplink

  • A saved room is now matched by its code and its server, not by the code alone. A room code is six characters and unique only to the server that issued it, so the same code names different rooms on a dev server and on a deployment. findSavedRoom consulted only the code, so joining a code against one origin could be handed the key --save had written for another — which fails to decrypt, and is the wrong room's key besides. The origin was already recorded in rooms.json; it is now read. Saving matches the same way, so the same code can be held for two servers at once rather than one evicting the other. Trailing slashes, host case and a default port do not make two origins different; a path does. rooms --forget still takes a code alone, and so now forgets it on every server it was saved against, rather than the first one found.

@thebkht/cliplink 0.2.0

Choose a tag to compare

@github-actions github-actions released this 23 Sep 23:44
64a8f43

📦 https://www.npmjs.com/package/@thebkht/cliplink

  • cliplink rooms. --save could write to the saved-rooms file but nothing could read it back, so seeing what a machine had kept, or removing one entry, meant opening rooms.json by hand. rooms lists code, origin and age; --forget takes one, --forget-all takes them all, and --prune drops what has expired. The listing withholds keys unless --show-keys asks — keeping keys scarce is the whole reason --save is opt-in per run, and a command run to remind yourself of a room code should not put key material into scrollback or a screen share. It says on stderr that it is withholding, so the omission cannot be read as the room having no key. Forgetting every room removes the file rather than leaving an empty one behind.
  • cliplink link. The share link and QR were printed only when send created a room, so getting a second device onto a room already in progress meant scrolling back to find them. link resolves a room exactly as send and recv do and prints what they print. It opens no socket and asks the server nothing — the code and the key are both in hand once the session resolves — so it answers for a room that has already expired. It never creates a room: minting one just to print its link would leave a room on the server nobody asked for.
  • recv --json. Every clip was decrypted and all of it but the text thrown away, so a script could read what was said but not who said it, when, or which clip it was. --json prints one object per line: id, text, senderId, ts. The fields are listed rather than the clip stringified whole, so a field added to the wire type does not appear in the output format without someone deciding it should.
  • recv --last <n> and --all. recv always started at the newest clip, so anything sent before it was running was unreachable from the terminal. The clips are in the connect response either way, so this is a choice of where the cursor starts rather than a new request. Replayed clips take the same path as one arriving later, so --one and --json mean the same thing for both, and the shared cursor still refuses to print a clip twice.
  • recv --timeout <secs>. recv --one waited forever, so a script that expected a clip and did not get one hung rather than failing. It exits 1 when nothing arrived and 0 when something did: nothing arriving writes nothing to stdout, and an empty clip and a clip that never came look alike to a caller that only reads the pipe.
  • NO_COLOR and --no-color. renderQr has taken a color option since it was written and no caller ever passed one. Colour stays the default because it is what makes the QR scannable — block characters are drawn in the foreground colour, so on a dark terminal an unstyled QR comes out inverted and many scanners refuse it.
  • Clustered short flags. -q1 was an unknown option. Only a run whose every letter is a known short flag is expanded, so -50 and -kSECRET still reach the unknown-option error rather than being taken apart into letters; a flag that takes a value has to come last.
  • A failing poll is reported once per run of failures rather than once per poll. A server that is down stays down, and a line every 1.5 seconds for as long as that lasts buried both the clips already received and the first line that said why. Reaching the room again is noted, so a failure that comes and goes is still visible.
  • A room saved by --save on creation now records when it expires, which is what --prune reads. A room saved on a join was never told its lifetime, so it falls back to the longest a room may be configured to live — a bound rather than a reading.

No wire changes. The protocol, the encryption and both transports are untouched.

@thebkht/rtc-file-transfer 1.1.0

Choose a tag to compare

@github-actions github-actions released this 22 Sep 09:32
02e48a9

📦 https://www.npmjs.com/package/@thebkht/rtc-file-transfer

  • Progress on files you are sending. FileItem.bytes has always been the receiver's count, so a sender could see that a file was being pulled and by how many devices, but not how far along any of them was — there was no way to draw an upload bar. Outgoing items now carry outgoingTransfers, one entry per device pulling the file, with bytes, bytesPerSecond and etaMs. A single number would have to pick one receiver and be wrong about the rest. Each entry's committed says what its count means: with the flow capability it is what the receiver has written, and without it what the sender has handed to the channel, which can run ahead. A device joins the list when its transfer opens, before any byte moves, and leaves when it ends.
  • getItems() and getItem(id) read the current state back, for a caller that would otherwise mirror every onItemsChange. Both return copies.
  • iceServers also takes a function, called for each peer connection. TURN credentials are usually short-lived, and an array is read once at construction — a transfer started an hour later dialled with credentials that had expired. Synchronous, because request returns a boolean and awaiting would make it async.
  • FileTransferManager is a declared type rather than ReturnType<typeof createFileTransferManager>. The published surface no longer widens as a side effect of adding a property, the doc comments survive into the type, and it can be implemented or stubbed.
  • Fixed: offerFiles told an { file, path } entry from a bare file with instanceof File, so a File from another realm — an iframe, a worker, a polyfill, Node's own — was destructured as an entry and its bytes went missing. The check is structural now.
  • New export: OutgoingTransfer.

Everything here is additive. No wire messages change, and a 1.0.x peer interoperates in both directions exactly as before.

@thebkht/rtc-file-transfer 1.0.1

Choose a tag to compare

@github-actions github-actions released this 20 Sep 01:17
fe0084a

📦 https://www.npmjs.com/package/@thebkht/rtc-file-transfer

  • Fixed: resume was implemented as this.request(…), so pulling it off the manager — const { resume } = createFileTransferManager(…), which is how every other method here is meant to be used — threw. No test had ever called resume, which is how it reached 1.0.0; there is now one that drives a pause and a resume entirely through destructured methods.
  • ResumeProvider, ResumeState and ResumeKey are exported. They were declared in manager.ts and re-exported by neither entrypoint, so the custom resume store the README describes could not be typed.

@thebkht/cliplink 0.1.1

Choose a tag to compare

@github-actions github-actions released this 20 Sep 01:17
fe0084a

📦 https://www.npmjs.com/package/@thebkht/cliplink

  • Room codes now come from the CSPRNG. generateRoomCode drew from Math.random, whose output is predictable from a handful of earlier draws. A room code is the room's only identifier, and for a room opened by its code alone it is the whole of the key material, since deriveOpenRoomKey takes nothing else. The format is unchanged, so this is not a wire change and existing codes still resolve.
  • --ttl is checked against the 3600–86400 the help text advertises before the request goes out, through the validateRoomTtl the server already uses. --ttl alongside a room named by CLIPLINK_ROOM now says so, rather than reporting that "this run joins one" when the command line shows no room at all.
  • resolveBaseUrl is exported, for anyone implementing TransportClient against a lazy origin.
  • The package ships its own LICENSE. There was none, so the tarball carried no licence text and both README.md and CLI.md linked above the package root to a file that was not there.

@thebkht/cliplink 0.1.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 18:26

📦 https://www.npmjs.com/package/@thebkht/cliplink

First release. The CLIPLINK room protocol, extracted from the web app so the
app and the CLI share one implementation of the encryption rather than one
each.

  • The protocol. End-to-end encryption (HKDF from the room key, AES-GCM per
    clip), the room wire types, room codes, QR encoding, and hand-written
    validation. Wire protocol v1 is fixed: a client on any version talks to a
    room served by any other, and the tests pin ciphertexts from earlier builds.
  • Both transports. createHttpClient polls, createWebSocketTransport
    holds a socket open, and both implement TransportClient, so a client writes
    its retry and backoff logic once. Each takes its origin as a parameter —
    nothing here assumes a browser, and window appears nowhere in it.
  • The cliplink CLI, shipped in this package as a bin. cliplink send
    creates a room and prints a link and QR code for the other device;
    cliplink recv prints clips as they arrive. Importing the library never
    loads it, so a browser bundle pays nothing for the CLI.
  • Requires Node 22 or newer, and has no third-party dependencies. The CLI talks
    to a room over Node's own WebSocket global rather than carrying ws, and
    the library uses Web Crypto, which is global from the same versions.

@thebkht/rtc-file-transfer 1.0.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 00:30
9d689c3

📦 https://www.npmjs.com/package/@thebkht/rtc-file-transfer

1.0.0 is the commitment that what is there now stays there:

  • The API follows semver. Exported functions and types, option names, FileItem fields, notice types and failure codes change only in a major. New failure codes and new FileItem fields can still appear in a minor.
  • Wire protocol v1 is permanent. A peer on any version interoperates with a peer on any other, in both directions, and the test suite now pins that with a pair of transfers against a manager created as capabilities: [] — a 0.1.0 peer, as far as the wire is concerned. Future features arrive as capabilities and optional fields, never as changes to existing messages.
  • README: a "Compared with" table checked against each package's own source, a browser support matrix, an honest list of what this doesn't solve (TURN, tab lifetime, mobile backgrounding, SCTP throughput, integrity vs. authenticity), quick-starts for simple-peer, PeerJS and Trystero, and a stability section spelling the above out.
  • Fixed: a manager disposed mid-block no longer checkpoints that block into a ResumeProvider. Its sink has already been aborted, so those bytes were never there to resume from, and a store that recorded them sent the next load back to an offset its file did not reach. This was reachable from 0.10.0 whenever a page tore a manager down mid-transfer.
  • New examples/two-tabs.html: a complete client — offer, download, pause, resume across a reload — in one static file, not published to npm.
  • CONTRIBUTING documents how a capability is added.

@thebkht/rtc-file-transfer 0.9.0

Choose a tag to compare

@github-actions github-actions released this 16 Sep 13:41
ee8aabd

📦 https://www.npmjs.com/package/@thebkht/rtc-file-transfer

  • New capability flow: the receiver credits the sender with what its sink has committed, and the sender stops once it is windowBytes (16 MB) ahead. Memory is now bounded at both ends, so a slow disk can't pile up in the receiver's. Requires blocks; peers without it fall back to bufferedAmount alone.
  • pause(id) and resume(id): pause an incoming download and keep every verified block. The item's status becomes paused, with no failure notice, and resuming continues into the same sink. Needs blocks and resume on both sides; pause returns false otherwise.
  • New limit windowBytes, new status paused, new failure code paused (sent to the sender as the reason; the paused item carries no error).