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

@todoforai/tfa-wait

v0.2.6

Published

TODO for AI waitpoint engine — schedule future wake-ups or wait for external events (webhooks); results arrive as messages in the conversation.

Readme

@todoforai/tfa-wait

Wait for an expression of triggers — the result always arrives as a message in the conversation that created the wait, waking the agent with full context.

One primitive: a trigger is something that can fire (a moment in time, or a webhook). A wait is a boolean expression over triggers. That's the whole model — AND, OR, timeouts, schedules and streams are all just shapes of the same expression.

CLI (agent-facing)

tfa-wait '<expr>' [--repeat] [--until <time>] "<message>"

tfa-wait '3h' "Post the 20 remaining comments"
tfa-wait 'signup || 7d' "Signup: {{payload.email}} — verify domain, send welcome"
tfa-wait 'deploy && tests || 1h' "Ship it: deploy done AND tests green (or 1h passed)"
tfa-wait 'weekdays@9:00' --repeat --until 2w "Morning check-in"
tfa-wait 'deploys' --repeat --until 1d "Deploy: {{payload.status}}"   # every fire delivers
tfa-wait list
tfa-wait cancel w_abc123
tfa-wait emit deploys '{"status":"ok"}'          # agent→agent / testing (per-todo name or h_ key)

The expression

Leaves joined by && (all must fire), || (any fires), () for nesting. A leaf is one word — its shape says what it is:

| leaf | meaning | |---|---| | 3h 30m 2d | duration — fires that long from (re-)arming | | 9:00 | clock time — next occurrence | | mon@9:00 | day@time — mon..sun, daily, weekdays, weekends, 1st | | 2025-07-01T09:00 | ISO date — that instant | | deploy | webhook — per-todo name; mints a stable unguessable h_… URL | | h_a1b2c3 | existing webhook by id (cross-todo / agent→agent reuse) |

Names are scoped to the todo: deploy in another todo is a different hook — no collisions. The returned URL is the secret; wire it into CI/Zapier/anything.

The two flags

  • --repeat — when the expression satisfies: deliver, reset, re-anchor time leaves, wait for the next round. (Replaces both cron-style schedules and event streams.)
  • --until <duration> — hard stop; the wait always finishes here at the latest (default and max: 30 days — beyond that use a calendar).

Message templates: {{payload.x}}, {{from.kind}}, {{won}}, {{at}} — filled from the winning event. Delivery metadata is readable text: reason, source, time, whether the wait is still active, and an indented payload.

Engine (tfa-wait serve)

One Bun process: HTTP API + 1s tick. Store is the backend's Redis/Dragonfly — keys namespaced tfawait:*, with atomic winner selection via Redis GETDEL. Delivery goes through the public TODOforAI API (addMessage), so no backend changes or deploys are ever needed.

# production — same Redis as the backend (picks up $DRAGONFLY_URL):
TFA_WAIT_PUBLIC_URL=https://wait.todofor.ai tfa-wait serve --port 8787
# or explicit:
tfa-wait serve --port 8787 --redis redis://localhost:6379

Routes:

  • POST /hooks/:key — public webhook fire; the unguessable key is the auth
  • POST /wait · GET /wait · DELETE /wait/:id · POST /emit — authed via x-api-key (validated against the TODOforAI API)

Resolution rule (the entire semantics): the expression is flattened to DNF — OR-groups of AND-leaves. Each fired trigger counts once per round. The wait resolves when some group is fully fired; the until trigger ends it regardless. --repeat delivers and resets instead of finishing.

Env

| Var | Meaning | |---|---| | TODOFORAI_API_URL, TODOFORAI_API_TOKEN | TODOforAI API (auto-injected in agent shells) | | TODOFORAI_TODO_ID, TODOFORAI_AGENT_SETTINGS_ID | current conversation (auto-injected) | | TFA_WAIT_URL | engine URL override (default derived: api.todofor.aiwait.todofor.ai, localhost APIs → localhost:8787) | | TFA_WAIT_PORT, TFA_WAIT_PUBLIC_URL, TFA_WAIT_REDIS | serve-mode config | | DRAGONFLY_URL | store (backend's Redis URL; --redis overrides) |

Layout

src/parse.ts    expression → DNF groups · time spelling → next fire (nextTime)
src/types.ts    Waitpoint · Trigger · Envelope (2 tables + wire format)
src/store.ts    Store contract + RedisStore — tfawait:* keys, GETDEL claim, per-todo names
src/engine.ts   emit → mark fired → satisfied? → deliver (+tick, repeat re-arm, cancel)
src/create-wait.ts  parse + resolve names + arm triggers; guarantees eventual completion
src/deliver.ts  addMessage via public API — the only product touchpoint
src/server.ts   metal adapter (Bun.serve + setInterval)
src/cli.ts      '<expr>' / list / cancel / emit / serve

Delivery reliability: resolutions are written to a durable outbox before delivery, then retried with backoff (5s → 30s → 2m → 10m → hourly) until the API accepts them.

Security note: waitpoint rows store user API tokens (needed to deliver as the owner) — they live in the backend Redis, same trust domain as the rest of user data.