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

@kodel-ai/runner

v0.1.0

Published

Kodel self-hosted pull-runner daemon (block-7)

Readme

@kodel-ai/runner

Self-hosted pull-runner daemon for Kodel pipelines (block-7).

The runner is a passive executor: it declares labels (what is installed on this VM) and opens an outbound WebSocket to the Kodel server (works through NAT). The server matches a pipeline node to this runner and sends a runner.claim carrying a structured AgentTask (engine + prompt + env + optional git/shell). The daemon cleans work/<project>/, optionally git clones, then runs the task with the engine adapter for task.engine, streams stdout/stderr back, and reports the final exit code.

Formula: the server says WHAT (intent), the runner's adapter does HOW. Built-in adapters: agentapi (claude-code / opencode via coder/agentapi) and shell (escape hatch that runs task.shell.script). There are no server-side profiles/recipes.

Install

npm i -g @kodel-ai/runner

This installs the kodel-runner daemon.

Configure

Create a registration on the server first:

kodelctl runner create backend-1 --kind self-hosted --label claude --label agentapi --project PLT
# → prints the backing service-account PAT (token) — copy it once
# labels are assigned server-side (the daemon does not send them).

Then on the VM, ~/.kodel/runner.yaml:

server: https://kodel.example.com
token: pat_xxxxxxxxxxxxxxxx        # backing SA PAT (resolves the runner; never logged)
slug: backend-1                    # optional; server resolves by token
workdir: ./work                    # cleaned per claim under work/<project>/
# concurrency: 1                   # fixed at 1 in MVP (sequential)
# labels are NOT set here — they are assigned server-side at `runner create --label`.

Every field can be overridden by env (KODEL_RUNNER_SERVER, KODEL_RUNNER_TOKEN, KODEL_RUNNER_WORKDIR, KODEL_RUNNER_SLUG) or CLI flags. Precedence (later wins): defaults < file < env < flags.

Run

kodel-runner start --server https://kodel.example.com --token pat_xxx
# or, with the config file in place:
kodel-runner start

The daemon registers, heartbeats every 30s, and waits for claims. Reconnects with exponential backoff (1s → 30s cap). Ctrl-C to stop.

WebSocket transport

The runner uses the native ws client (NOT socket.io). This matches the Kodel server, whose gateways are native-ws (WebSocket.Server({ noServer: true }) + an HTTP upgrade handler on a fixed path — see tui.gateway.ts). The daemon connects to <server>/ws/runners (http→ws, https→wss).

Auth is register-first: immediately after the socket opens, the daemon sends a runner.register message carrying the PAT (token) + slug + labels as its first frame; the server resolves the runner by token+slug and replies with runner.registered. The token is sent only inside that frame (never as a query param / header) and is never logged.

Protocol messages are defined in @kodel-ai/shared ("Self-Hosted Runner Protocol"): server → runner runner.claim / runner.cancel; runner → server runner.register / runner.heartbeat / runner.step / runner.node.complete / runner.node.error.

Credentials

The daemon does not store any long-lived engine credential. Per-claim env (including e.g. ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN minted by the server under the runner's service account) arrives inside AgentTask.env and is used only for that task.

Engine adapters

The daemon picks an adapter by task.engine:

  • agentapi (engine: claude-code | opencode) — drives the agentapi HTTP wrapper: spawns agentapi server -- <agent> in the checkout dir, POSTs the prompt, polls /status until stable, streams the answer. agentapi (and the engine binary, e.g. claude) must be pre-installed on the VM; declare the matching labels (agentapi, claude) on the runner.
  • shell (engine: shell) — escape hatch: runs task.shell.script[] sequentially, stopping at the first non-zero exit.

Development

bun test        # unit tests (config + executor)
bun run start   # run the daemon locally