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

@ahpd/tunnel-devtunnel

v0.14.0

Published

A Dev Tunnel plugin for ahpd: forwards the bound port through a Microsoft Dev Tunnel so VS Code can find and reach the daemon from anywhere

Readme

@ahpd/tunnel-devtunnel

npm CI license MIT node >=22 Agent Host Protocol 0.9.0

A Dev Tunnel plugin for the @ahpd/server daemon. It forwards the daemon's port through a Microsoft Dev Tunnel, labelled the way VS Code expects, so VS Code can find and reach the daemon from a machine on another network.

Part of ahpd. The source is in packages/tunnel-devtunnel.

Install

ahpd plugin install @ahpd/tunnel-devtunnel
ahpd restart

Update, remove and one-run flags such as --plugin are in Plugins.

What it needs

  • The devtunnel CLI, installed and logged in:

    devtunnel user login       # a Microsoft account
    devtunnel user login -g    # or a GitHub one

Options

In the configuration file, with every option set:

{ "plugins": [{ "name": "@ahpd/tunnel-devtunnel", "options": { "name": "dev03", "anonymous": true } }] }

| Option | Default | What it does | | --- | --- | --- | | name | this machine's hostname | What the tunnel is called in a client's list | | keep | true | Whether the tunnel outlives the daemon | | anonymous | false | Whether somebody not signed in to this account may reach it |

Keep keep on: a client derives its connection token from the tunnel's id, so a new tunnel on every start is a new token on every start.

No option holds a credential, so the plugin takes nothing from the vault. The tunnel signs in through the devtunnel CLI's own login.

In your own host

The package's apply is the plugin entry. Hand it a plugin host from @ahpd/sdk and fold what it registered into the host's options. The plugin does its work on the listening and stopping events, which the daemon raises and an embedding host raises itself:

import { createHost, foldHostOptions, listen, pluginHost, raise } from '@ahpd/sdk';
import { claude } from '@ahpd/agent-claude';
import { apply, name } from '@ahpd/tunnel-devtunnel';

const path = process.cwd();
const context = { path, paths: [path], version: '0.10.0', hostName: 'my-host', configDir: '/var/lib/my-host', log: console.log, say: console.log };
const { host: plugin, contribution, seal } = pluginHost(name, context);
await apply(plugin, { name: 'dev03', keep: true });
seal();

const { options } = foldHostOptions({ path, agents: [claude({ paths: [path] })] }, [contribution]);
const host = createHost(options);
const listener = await listen({ port: 9187 }, (peer) => host.accept(peer));
const { runtime, host: address, port, guarded } = listener;
await raise(options.events, { type: 'listening', runtime, host: address, port, guarded });

// On the way down, before the listener closes:
await raise(options.events, { type: 'stopping' });
await listener.close();

In VS Code

The daemon prints the tunnel beside its own address, and ahpd status shows it:

ahpd on ws://127.0.0.1:9187 (node), sessions in /work
tunnel sunny-otter-9k3 (dev03), port 31546

In VS Code, sign in to the same Dev Tunnels account and run Agents: Connect to Remote Agent Host via Dev Tunnel. Finding a tunnel needs that sign-in. A direct WebSocket address does not, and is what Agents: Add Remote Agent Host... takes.

The connection token

A client connecting over a tunnel computes its token from the tunnel's id, as the unpadded base64url of its SHA-256, and presents it as ?tkn=. Only people who pass the Dev Tunnels access check can reach the tunnel, so the token does not need to be secret.

A daemon on loopback with no connection token of its own accepts it, and that needs no configuration. A daemon started with --connection-token refuses it, because the client presents the derived token and not the daemon's. The plugin prints the derived token on its own line so you can make the two agree:

tunnel token 4d8f...  - what a client presents over the tunnel; this daemon requires its own

What it does at startup

  1. Finds the tunnel it made before, by its own label, or makes one.
  2. Labels it vscode-server-launcher and protocolv5, which is what makes a client list it.
  3. Puts port 31546 on it, the fixed port VS Code looks for.
  4. Forwards 31546 on loopback to the port the daemon bound, unless the daemon is already on 31546.
  5. Hosts the tunnel and prints its address.

If any step fails, the daemon keeps running without the tunnel.

It claims protocolv5 and not 6, because protocol 6 clients also expect a selection gateway at /agent-host/select, which a plain port forward does not serve.

Documentation

| | | | --- | --- | | REMOTE.md | Connection tokens and remote access | | PLUGINS.md | Loading a plugin into the daemon |

License

MIT © Softov