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

tally-sh

v0.1.0

Published

Counters with history: curl a +1 and a live sparkline page exists one second later.

Readme

curl -d "+1" https://tally.legible.sh/my-agents-x7k2q/tasks-completed
# {"metric":"tasks-completed","value":1}

Now open https://tally.legible.sh/my-agents-x7k2q/tasks-completed in a browser. That's a live page — big number, smooth sparkline, updating over SSE as the curls land. https://tally.legible.sh/my-agents-x7k2q is the dashboard of everything in the topic. No signup, no API key, no dashboard configuration — the URL is the chart.

The hosted API at tally.legible.sh is in soft launch. To run the identical API yourself, npx tally-sh serve starts it on http://localhost:4189; every example below works with the hostname swapped.

Count what your agents actually do

Your hooks already curl ntfy for pings — now curl tally for numbers. Paste this into .claude/settings.json and every Claude Code session starts feeding a dashboard:

{
  "hooks": {
    "Stop": [
      { "hooks": [{ "type": "command",
        "command": "curl -sd +1 https://tally.legible.sh/acme-agents-x7k2q/tasks-completed >/dev/null" }] }
    ],
    "PostToolUse": [
      { "matcher": "Bash",
        "hooks": [{ "type": "command",
        "command": "jq -e '.tool_response | tostring | test(\"(?i)error\")' >/dev/null && curl -sd +1 https://tally.legible.sh/acme-agents-x7k2q/tool-errors >/dev/null; exit 0" }] }
    ]
  }
}

Add one line to the agent's own instructions (see Teach your agent) and it counts tests-fixed itself whenever it turns a red test green. Open https://tally.legible.sh/acme-agents-x7k2q on a second monitor: tasks completed, tool errors, tests fixed — every card a live sparkline. That's the fleet, at a glance, for zero infrastructure.

The pain this solves: agents produce numbers constantly — tasks done, tokens burned, retries, queue depths — and every existing home for those numbers wants a signup, an SDK, or a Grafana. The countapi clones will store a number but show you no history. tally keeps a time series behind every counter and puts a genuinely pretty chart at its URL.

The whole API

| Call | Does | |---|---| | POST /{topic}/{metric} | count. Body +1 (default when empty), +5, -2, bare 7, or =42 (gauge set) → {"metric","value"} | | GET /{topic}/{metric} | curl: {metric, value, updated, series} · browser: live sparkline page · Accept: text/plain: bare value | | GET /{topic}/{metric}?wait=30 | long-poll: responds on the next change, or after the timeout (max 60s) | | GET /{topic} | curl: {topic, metrics:[{name, value, updated}]} · browser: live dashboard | | GET /{topic}/sse | SSE: named update / delete events with JSON data, heartbeat comments every 25s | | DELETE /{topic}/{metric} | remove a metric (token-guarded if the server runs --token) | | GET /README.md · /llms.txt | these docs, as text, for agents. Never token-gated; GET / with Accept: text/markdown serves the README too |

series holds three dense windows of {t, sum, count, last} buckets (t = bucket start, epoch ms): minute (last 60), hour (last 48), day (last 90). sum adds up the payloads written in the bucket (a counter's rate; divide by count for a gauge's average), last is the running value the sparkline plots. Retention: minutes 24 h, hours 14 d, days 400 d. Topics match [a-zA-Z0-9_-]{1,64} and are created by first write; metrics match [a-z0-9_-]{1,64} (sse is reserved). Errors are {"error","code","hint"} with honest status codes — the hint names the correct next call (omitted where the message already is one). Every limit is an exported constant in src/limits.mjs.

curl -d "=42" https://tally.legible.sh/my-agents-x7k2q/queue-depth      # gauges: set, don't add
curl https://tally.legible.sh/my-agents-x7k2q                           # list the topic
curl -H "Accept: text/plain" https://tally.legible.sh/my-agents-x7k2q/queue-depth   # → 42
curl "https://tally.legible.sh/my-agents-x7k2q/queue-depth?wait=30"     # block until it changes
curl https://tally.legible.sh/my-agents-x7k2q/sse                       # live event stream
curl -X DELETE https://tally.legible.sh/my-agents-x7k2q/queue-depth

Teach your agent

Paste this into your CLAUDE.md / AGENTS.md and the agent knows the whole tool:

## tally — counters with history (https://tally.legible.sh)
- Count something:    curl -d "+1" https://tally.legible.sh/<topic>/<metric>     (also "+5", "-2", bare "7")
- Set a gauge:        curl -d "=42" https://tally.legible.sh/<topic>/<metric>
- Read (JSON):        curl https://tally.legible.sh/<topic>/<metric>    → value + minute/hour/day series
- List a topic:       curl https://tally.legible.sh/<topic>
- Wait for a change:  curl "https://tally.legible.sh/<topic>/<metric>?wait=30"
- Live events:        curl https://tally.legible.sh/<topic>/sse         (SSE: update/delete)
- Delete:             curl -X DELETE https://tally.legible.sh/<topic>/<metric>
- Topics are created by the first write. Pick an unguessable topic name (add ~5 random chars).
- Metric names: [a-z0-9_-]. Every metric URL is a live chart in a browser; /<topic> is a dashboard.
- When you fix a failing test, run: curl -sd +1 https://tally.legible.sh/<topic>/tests-fixed

Self-hosting

npx tally-sh serve                      # http://0.0.0.0:4189, in-memory
tally serve --port 4189 --host 127.0.0.1
tally serve --data-dir ./data           # append-only JSONL, replayed on boot
tally serve --token s3cret              # POST/DELETE require Authorization: Bearer s3cret
tally serve --base-url https://tally.example.com   # absolute URLs in pages

Or clone and run: git clone … && cd tally && npm start. Zero dependencies — npm install has nothing to install. State is in-memory by default (restart = clean slate); --data-dir makes writes durable. With --token, reads stay public — metric pages and JSON never require the token, only writes and deletes do. If your numbers are sensitive, that flag is not enough; put the whole server behind something, or wait for private topics (below).

CLI

The CLI is sugar over the same HTTP API — curl remains the contract.

tally bump ci-x7k2q deploys             # +1
tally bump ci-x7k2q deploys +5
tally bump ci-x7k2q queue-depth '=42'    # quoted: zsh expands bare =42
tally get  ci-x7k2q deploys             # prints the value; --json for the series
tally ls   ci-x7k2q                     # name, value, updated
tally watch ci-x7k2q                    # live tail over SSE
tally rm   ci-x7k2q deploys
tally serve

Server resolution: --url flag, else $TALLY_URL, else https://tally.legible.sh. Writes against a --token server: --token flag or $TALLY_TOKEN.

Pro

Planned for the hosted instance, never gating the core verbs (self-host stays complete):

  • Longer retention — minutes beyond 24 h, days beyond 400.
  • Private topics — read tokens; today reads are public-by-obscurity.
  • Threshold alertstool-errors > 10/hour → webhook or ntfy topic.
  • CSV / JSON export of full retained history.
  • Teams — reserved topic prefixes, shared dashboards.

Straight talk

  • The topic name is the password. Anyone who guesses it can read and write your numbers. my-agents will be squatted; my-agents-x7k2q won't. This is capability-by-obscurity, same trade as ntfy — real auth is --token (writes) today and private topics (reads) later.
  • Rollups, not raw events. Each bucket keeps sum, count, last — that's it. No percentiles, no histograms, no labels/dimensions. If you need p99{region="eu"}, you need Prometheus, and that's fine.
  • Arrival time is the timestamp. Writes land in the bucket for now; there is no backfill API. A counter bumped offline stays unbumped.
  • The JSONL grows forever. --data-dir appends one line per write and never compacts. At hobby scale that's megabytes per year; still, check on it.
  • One process, one box. State lives in memory (Maps), fsync'd to JSONL if asked. No clustering, no HA. A restart without --data-dir forgets everything — by design, and documented as such.

The family

tally is one of the legible primitives — same stack, same conventions, one curl each:

| | | |---|---| | gate (4180) | ntfy tells you things. gate asks you things. | | bigred (4181) | The big red button for your agent fleet. | | trail (4182) | The flight recorder for agent runs. | | slate (4183) | The blackboard from the multi-agent papers, as a URL. | | relay (4184) | The work queue your agents can provision themselves. | | mutex (4185) | flock(1) for agents that live on different machines. | | quorum (4186) | Coordination for agents that do not share a parent process. | | meter (4187) | The kill-brake for agent spend. 429-as-a-service. | | stash (4188) | ntfy moves signals. stash moves bytes. | | tally (4189) | StatHat reborn as ntfy. Three months too late — or right on time. |

License

MIT.