npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@patimweb/pi-localsend

v1.3.1

Published

LocalSend extension for the pi coding agent. Send and receive files over the local network with any LocalSend device, without a cloud service.

Readme

pi-localsend

pi.dev sending files over LocalSend

LocalSend extension for the pi coding agent.

Send and receive files between pi and any device running LocalSend — phone, tablet, or another computer — directly over the local network. No account, no cloud, nothing leaves the LAN.

Implements the LocalSend protocol v2.1 natively, with no runtime dependency beyond typebox.

Installation

# Install from npm
pi install npm:@patimweb/pi-localsend

# Install from local path during development
pi install /path/to/pi-localsend

Quick Start

Nothing to configure — every setting has a working default.

# See what is on the network (LocalSend must be open on the other device)
localsend_devices

# Send
localsend_send:
  to: Pats iPhone
  files: [/Users/pat/report.pdf]

# Receive one transfer
localsend_receive:
  timeoutSeconds: 300

localsend_receive prints a PIN and the address to use, then waits. Enter the PIN on the sending device.

No background service

This is the design constraint the extension is built around: there is no daemon. Sockets are open only while a tool is running, and each tool closes what it opened before it returns.

| Tool | What it opens | When it closes | |------|---------------|----------------| | localsend_devices | UDP socket + short-lived HTTP server | when the scan ends (seconds) | | localsend_send | outbound connections only | when the transfer ends | | localsend_receive | HTTP(S) server + announcement socket | after one transfer, or at the timeout |

localsend_receive accepts exactly one session and then shuts down. Nothing survives the tool call, and nothing survives the pi session.

Tools

| Tool | Description | |------|-------------| | localsend_setup | Change alias, download directory, PIN policy, transport, port. Optional. | | localsend_status | Show current settings and the addresses other devices can reach this machine at. Opens no ports. | | localsend_devices | Scan the network for LocalSend devices and list their aliases and addresses. | | localsend_send | Send files or a text snippet to a device, by alias or IP address. | | localsend_receive | Wait for exactly one incoming transfer, save the files, shut down. |

Sending

localsend_send:
  to: Pats iPhone            # alias from localsend_devices
  files:
    - /Users/pat/report.pdf
    - /Users/pat/screenshots  # a directory sends the files directly inside it
  # pin: "123456"             # if the receiving device asks for one

Text goes straight across without touching disk first:

localsend_send:
  to: 192.168.1.42
  text: "The staging URL is https://staging.example.com"
  # textFileName: staging.txt

When discovery does not work (guest Wi-Fi, VPN, firewall), give an address instead of an alias. Direct addresses default to port 53317 and https, which is what the LocalSend app uses.

Receiving

localsend_receive:
  timeoutSeconds: 300         # default 5 minutes, max 1 hour
  # downloadDir: ~/Desktop    # just for this transfer
  # noPin: true               # accept without a PIN

The call blocks and returns the listening port, this machine's addresses, and a generated six-digit PIN. One window accepts one transfer; several files in a single send are fine.

Incoming file names are attacker-controlled, so they are sanitised before anything is written: path separators and traversal are stripped, and a name that collides gets a (2) suffix rather than overwriting. A sender that pushes more bytes than it declared is cut off.

Commands

| Command | Description | |---------|-------------| | /localsend | Scan the network for LocalSend devices. | | /localsend-receive [minutes] | Open a receive window (default 5 minutes). |

Skills

| Skill | Purpose | |-------|---------| | localsend-transfer | Finding devices, confirming the right target before sending, surfacing the PIN while the receiver is still waiting, and what to check when a device does not appear. |

Configuration

Settings are saved to ~/.pi/localsend-config.json (mode 0600) by localsend_setup. Only overrides are stored; anything unset falls back to a default.

| Setting | Default | |---------|---------| | alias | pi on <hostname> | | downloadDir | ~/Downloads, or the home directory if that does not exist | | deviceType | desktop | | protocol | http | | port | 0 — a free port is picked per transfer | | requirePin | true |

Encryption

LocalSend normally runs over HTTPS with a self-signed certificate, where the device fingerprint is the hash of that certificate. Node cannot generate certificates on its own, and a crypto library is a lot of dependency for one optional feature, so:

  • protocol: http (default) is what this machine listens with. The protocol supports it; the fingerprint is then a stable random id.
  • protocol: https listens with TLS instead.

Either way a certificate is generated on first use and cached in ~/.pi/localsend/, because sending to a LocalSend app always requires one regardless of this setting — see below.

The certificate is built in process (src/x509.ts) rather than by shelling out to openssl: a sender that cannot present a certificate cannot talk to a LocalSend app at all, and depending on a binary that is routinely absent on Windows would make the extension unusable there. Nothing in this package spawns an external process, so Windows, Linux and macOS behave identically.

It is an end-entity certificate (CA:FALSE) with a subjectAltName, clientAuth and serverAuth in its extended key usage, and a 365-day lifetime. None of that is cosmetic: LocalSend verifies the certificate a sender presents, and a webpki-based verifier (rustls) rejects a CA certificate, one without a SAN, or one without clientAuth — the handshake then ends in a certificate unknown alert (46). Apple platforms separately refuse server certificates valid for more than 398 days. A cached certificate that fails these checks, including one written by an earlier version, is detected and replaced automatically.

When connecting to a peer over https, certificate chain validation is off by design: LocalSend peers are self-signed, and the protocol's trust anchor is the fingerprint, not a CA.

Sending to an encrypted peer also needs a certificate on this side. The LocalSend app identifies the sender by the fingerprint of the certificate it presents, so its TLS server requests one; a sender that presents nothing never gets past the handshake and sees a tlsv13 alert certificate required (alert 116). This extension therefore presents its own certificate when talking to an https peer, and reports that certificate's SHA-256 as its fingerprint rather than the random id used in http mode — including when the local protocol setting is http.

Running alongside the LocalSend app

The LocalSend desktop app owns UDP/TCP 53317 while it runs. This extension listens on a free port instead and advertises it in its announcement, so both can coexist — the test suite covers receiving a transfer while 53317 is occupied. The one consequence is that scans may miss devices that answer by multicast rather than HTTP, because the multicast listening port is taken; the tools report that as a note rather than an error. Your phone will simply list both the app and this extension as separate devices.

Development

npm install
npm test
npm run test:coverage

The test suite includes a real loopback transfer: it starts the receiver, sends files to it through the send client, and asserts on what landed on disk — including path traversal, duplicate names, PIN rejection and the concurrent-session case.

License

MIT