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

dilaya-cli

v0.1.3

Published

Dilaya agent-loop CLI — a dumb (AI-free) poller that wakes Claude only when there's work.

Downloads

89

Readme

dilaya

A dumb (AI-free) poller that wakes a Claude agent only when there's work.

Each Dilaya app (a Postgres schema) can have one agent: a prompt that runs on demand instead of an AI session sitting idle 24/7. This CLI is the local half of that loop:

  1. It asks the app's MCP server one cheap question on a fixed interval — "is there work?" (a DynamoDB-backed read; Aurora stays scaled to zero, no model tokens spent).
  2. Only when the answer is yes does it launch an interactive claude inside a detached tmux session, running from the agent's project dir.
  3. The agent does all available work, optionally has a back-and-forth via the await-work tool, decides how it should next be woken (set-agent-mode), then marks itself idle. The poller reaps the idle session.

Because the agent runs in tmux (not headless claude -p), you can attach to the live session to watch it work or message it: dilaya agent attach.

You pay for Claude only while there's actual work. The poller itself is free.

Why a separate CLI

The long-lived poll token must never enter the LLM's context. So the agent (in Claude Code) only ever handles a single-use, 15-minute setup token; the CLI exchanges that for a long-lived, opaque poll token that it stores locally (chmod 600) and that only ever reveals "work or not". The user never has to touch the CLI by hand — Claude Code drives the whole install via get-agent-setup-instructions.

Install

npm i -g dilaya-cli   # or from source: npm i -g <path-to>/dilaya-cli
dilaya --version      # package is `dilaya-cli`; the command it installs is `dilaya`

Requires Node ≥ 18, plus claude and tmux on PATH (the agent runs inside tmux).

Usage

Normally you don't run these yourself — ask the app's agent to "install the agent loop on this machine" and Claude Code runs them for you. For reference:

# 1. Exchange a setup token (from the MCP tool get-agent-setup-token) for a poll token.
dilaya agent setup --schema myapp \
  --url   https://<host>/myapp/agent/token \
  --setup-token <single-use-token> \
  --project-dir ~/.dilaya-agents/myapp \
  --interval 30s

# 2. Install + start it as a background service (launchd on macOS, systemd-user on Linux).
dilaya agent install --schema myapp --interval 30s

# Inspect / control
dilaya agent status    --schema myapp    # service + poller + tmux session state
dilaya agent attach    --schema myapp    # attach to the live agent (type to talk; Ctrl-b then d to detach)
dilaya agent logs      --schema myapp    # tail the agent log
dilaya agent stop      --schema myapp    # stop poller (and kill any running session)
dilaya agent uninstall --schema myapp    # stop + remove the service

# What the service actually runs (foreground poll loop):
dilaya agent run --schema myapp

How a wake works

poll returns { shouldWake, mode, lifecycle }. When there's work and no session is live, the poller starts one interactive claude (no -p) inside tmux, from the project dir:

  • mode: "new" — start a fresh Claude session (claude --permission-mode auto --session-id <uuid> /dilaya-agent). Cheaper; the agent starts clean. Used when the agent last finished its task.
  • mode: "continue" — resume the previous session (--resume <id>), keeping context. Used when the agent was mid-task or expecting a reply.

The agent itself picks the next mode by calling set-agent-mode before it goes idle.

Interactive claude never self-exits, so the poller decides when a run is over from lifecycle:

  • running — the agent called begin-agent-run; the poller leaves it alone. It never time-kills a working agent (coding work can take hours) — but it does watch for a dead turn: while a turn is in flight, interactive claude repaints its spinner every second, so the pane snapshot (tmux capture-pane, read-only) changes on every poll. If the pane stays byte-identical for ~3 min while running (e.g. an API error killed the turn and the session fell back to its prompt), the run would otherwise hang forever: the poller reaps the session and re-wakes with --resume, so begin-agent-run returns recovered_from_crash and the agent reconciles. Same recovery (rate-limited to once per ~5 min) if the lifecycle says running but the tmux session is gone entirely.
  • idle — the agent called set-agent-mode; the poller reaps the tmux session after a short grace (~12s) — unless you're attached (attached sessions are never reaped; an attached session is also never treated as a dead turn).

Only one session per schema exists at a time: the loop checks for a live session before starting another, so a second agent can never run concurrently. A session that starts but never reports running (e.g. claude failed to launch) is reaped after a startup timeout (~90s) and retried.

Files it writes

Per schema, under ~/.config/dilaya/<schema>/:

| File | Purpose | | --- | --- | | config.json | schema, poll URL, poll token (chmod 600), project dir, interval, last session id | | poller.pid | liveness of the poll loop | | agent.log | poll + agent activity (what logs tails) |

The running agent itself lives in a detached tmux session (dilaya-<schema> on a dedicated tmux socket dilaya), not a file — dilaya agent attach connects to it.

Service definition: ~/Library/LaunchAgents/com.dilaya.agent.<schema>.plist (macOS) or ~/.config/systemd/user/dilaya-agent-<schema>.service (Linux).

Caveats

  • Login-scoped. Starts at login (not pre-login boot); laptop sleep pauses polling. On Linux run loginctl enable-linger to keep the service alive after logout.
  • Requires tmux — the agent runs inside it (install preflights it). The poller talks to tmux on a dedicated socket (-L dilaya), so it never touches your default tmux server.
  • The spawned claude uses your existing MCP auth; if that token expires, ticks fail until you re-login.
  • The poller runs claude --permission-mode auto (interactive, inside tmux), so the project dir's .claude/settings.local.json must allowlist every tool the agent uses — auto mode runs allowlisted tools without prompting, and deny rules still win. Override the mode with dilaya agent setup --permission-mode <mode>.
  • One agent per schema; run setup/install once per app you want a loop for.

Build (from source)

npm install
npm run typecheck
npm test             # node:test suite (via tsx), no network
npm run build        # esbuild → dist/index.js (with shebang)

Tests live in test/ and run on Node's built-in test runner through tsx (no extra runtime deps); they cover the pure logic — interval parsing, settings merging, and the poll/exchange HTTP client (with fetch stubbed). CI (.github/workflows/ci.yml) runs typecheck → test → build on every pull request and push to main, across Node 18/20/22.

Releasing

Publishing to npm is automated. To cut a release:

  1. Bump version in package.json, then commit and push to main.
  2. Create a GitHub Release whose tag matches that version (v0.1.0 or 0.1.0).

The Publish to npm workflow (.github/workflows/publish.yml) then verifies the tag matches package.json, type-checks, and runs npm publish. It needs an NPM_TOKEN repository secret — an npm automation token (it bypasses 2FA in CI):

gh secret set NPM_TOKEN --repo hereya/dilaya-cli   # paste the npm automation token

The GitHub repo is private; the npm package (dilaya-cli, unscoped) is published public so npm i -g dilaya-cli works for everyone. It installs the dilaya command.