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

@justfortytwo/telegram

v0.1.5

Published

Telegram channel adapter for fortytwo: a long-polling bridge that drives a headless `claude` turn loop per chat, surfaces gate approvals as inline keyboards, and gates inbound access behind a login/pairing binding flow.

Downloads

889

Readme

@justfortytwo/telegram

The Telegram channel adapter for fortytwo — a standalone, always-on bridge that connects a Telegram bot to a headless claude turn loop.

It does three jobs:

  1. Turn loop. Long-poll Telegram, and for each authorized inbound message drive a headless claude -p --output-format json turn (resuming the per-chat session), then transport the assistant's reply back.
  2. Approval UX. When the safety gate defers an external/irreversible tool call, the turn ends with stop_reason: tool_deferred. The bridge surfaces an inline-keyboard approval card (Approve / Deny, plus "Allow _N_h" for exact Bash commands). The tap records the decision and resumes the turn so the one-shot allow fires.
  3. Proactive wakes. A scheduler invokes claude -p at fixed cron-like slots (daily briefing, open-thread sweeps, weekly learn-review).

Pairing / login binding model

The bot is locked down: an unbound sender that does not present a valid /login gets no response at all. Access is earned through a channel-agnostic challenge/verify pairing flow:

owner side:    adapter.issueChallenge(owner)  ->  { code, ttl }
channel side:  /login <code>                  ->  verify(chatId, code) -> binding
  • issueChallenge(owner) mints a single-use, short-TTL code (default 6 digits, 5 minutes). The owner relays it out-of-band to the chat they want to bind.
  • /login <code> verifies the code and, on success, persists a binding (channelType, channelUserId) -> owner.
  • /logout removes the binding.
  • Every inbound message is authorized against persisted bindings ∪ the optional ALLOWED_CHAT_IDS bootstrap set. The bootstrap set lets the first owner chat reach the bridge before any binding exists; it is not required once bindings exist.

Pending challenges are persisted in the binding store (not in process memory), so a code minted by one process — e.g. the fortytwo pair CLI — is redeemable by the separately-running bridge. They are single-use and short-TTL.

The contract is intentionally channel-agnostic (ChannelAdapter in src/adapter.ts) so a future Slack / email / SMS adapter implements the same issueChallenge / verify shape.

Binding store

Authorization must work even when the memory package is absent, so the binding store is self-owned and does not assume the @justfortytwo/memory sqlite db exists:

  • SqliteBindingStore — default; a tiny better-sqlite3 db in its own file (state/telegram-bindings.db by default) holding bindings + pending challenges, so a CLI-issued pairing code reaches the bridge.
  • MemoryBindingStore — in-process Map, for tests / ephemeral use.
  • BindingStore — the injectable interface. Supply a memory-backed implementation later without touching the login flow.

Cross-repo reconciliation: if fortytwo standardizes a shared identity table, swap the default store for a memory-backed BindingStore. The login flow is unaffected.

Peer dependencies

This package orchestrates two peer packages — they are referenced, not bundled:

| Peer | Used for | Contract | | --- | --- | --- | | @justfortytwo/memory | Memory store, jobs, pending-decision records, embeddings | MEMORY_TOOL_CONTRACT_VERSION (tools mcp__fortytwo-memory__*) | | @justfortytwo/gate | Bash exact-command allowlist behind "Allow _N_h"; provenance envelopes | POLICY_SCHEMA_VERSION |

Install them alongside this package:

npm install @justfortytwo/telegram @justfortytwo/memory @justfortytwo/gate

The memory data path is wired: the bridge opens + migrates the memory DB at startup and persists each channel event to @justfortytwo/memory; recall happens inside the headless claude turn via the Memory MCP tools. If the memory peer is absent, the bridge degrades to channel-only (messages relay but are not persisted) rather than refusing to start. The gate "Allow _N_h" path still uses a local bash-allowlist fallback; wiring it to @justfortytwo/gate's exact-command allowlist is the remaining seam.

Environment

| Var | Required | Meaning | | --- | --- | --- | | TELEGRAM_BOT_TOKEN | yes | Bot token. The bridge is the only long-poller on this bot. | | ALLOWED_CHAT_IDS | bootstrap | Comma-separated chat IDs allowed before any binding exists. Optional once bindings exist. | | CLAUDE_BIN | no | Path to the claude binary (default claude). | | FORTYTWO_TURN_TIMEOUT | no | Hard cap (seconds) on a single turn so a stalled model can't wedge the bridge (default 300). | | FORTYTWO_BASH_ALLOW_TTL_HOURS | no | Default TTL (hours) for "Allow _N_h" bash approvals (default 8). | | TELEGRAM_BINDINGS_DB | no | Path to the self-owned bindings db (default state/telegram-bindings.db). | | DB_PATH | no | Path to the memory DB the bridge opens/migrates at startup (default db/fortytwo.db, relative to FORTYTWO_ROOT). | | EMBED_MODEL | no | When set, the bridge uses the Ollama embedder for memory (otherwise a deterministic fallback). | | OLLAMA_BASE_URL | no | Base URL for the Ollama embedder (default http://localhost:11434; may be remote). | | ASSISTANT_NAME / ASSISTANT_ACTOR / OWNER_ACTOR | no | Display name + journal actor labels. | | FORTYTWO_ROOT | no | Working root for the headless claude process + state files. |

The historical FORD_* names (FORD_ROOT, FORD_TURN_TIMEOUT, FORD_BASH_ALLOW_TTL_HOURS) are still honored as deprecated aliases — set the FORTYTWO_* equivalents in new deployments.

Proxy auth for the headless claude session is not read here — the claude CLI reads ANTHROPIC_* from its own config.

Usage

Prerequisites / first run

  • The bridge spawns the claude binary, so Claude Code's claude must be on your PATH (or point CLAUDE_BIN at it).
  • It loads config/adapters.toml from $FORTYTWO_ROOT/config/.
  • On first run it requires TELEGRAM_BOT_TOKEN and either ALLOWED_CHAT_IDS or a pre-existing binding — with no authorized chat to reach, the bridge exits immediately rather than long-polling a bot nobody can talk to.
npm run build
node dist/bridge.js

Run it under a restart loop (tmux/systemd) so it survives transient errors and crashes. Example launcher (adapted from the monolith's wakeup.sh):

set -a; source .env; set +a
while true; do
  node node_modules/@justfortytwo/telegram/dist/bridge.js
  echo "[telegram] bridge exited (rc=$?) — restarting in 3s"; sleep 3
done

License

MIT © 2026 Enrico Deleo


Created and maintained by Enrico Deleo.