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

@miadi/tide

v0.6.0

Published

Typed client boundary for the ironsilk `tide` runtime. Daemon Unix-socket transport with CLI fallback.

Readme

@miadi/tide

Read and steer the agents working in your terminals, from TypeScript.

In the Miadi stack, @miadi/tide is how a program sees and directs agents at work. It lists the terminals on a machine, reads what the agent in a pane is doing, and sends that agent a message when the caller is allowed to. It works with tmux and with herdr. Every send passes a steer gate first, so a program cannot type into a pane unless a controller has been named for it. Miadi's operator cockpit is built on this package.

Works with:

  • @miadi/tide-contract: the schema this client and the tide runtime share.
  • @miadi/annotate-core: marks an agent's reply before a review is sent back to its pane.
  • the tide CLI from the ironsilk Python package: the runtime this client talks to.

Install

npm install @miadi/tide
pip install -U ironsilk      # provides the `tide` CLI (0.9.27+)
tide --help                  # verify

The package ships TypeScript source (src/index.ts), so compile it with your bundler. In Next.js, add @miadi/tide to transpilePackages.

Verbs

import { getContext, getTerminals, detect, peek, steerProposal, send, sendKeys, buildSendKeysPlan, pingDaemon, describeTransport, TideError } from "@miadi/tide"

const snapshot  = await getContext()          // live context of every terminal (daemon socket → CLI fallback)
const terminals = await getTerminals()        // operator terminals inventory (read-only)
const match     = await detect({ pane: "56" })// domain detection for a pane or cwd → match | null
const scroll    = await peek("56", 80)        // last 80 lines of pane %56
const proposal  = await steerProposal("56", "continue from checkpoint")  // reviewed proposal, nothing sent
const sent      = await send("56", "continue") // guarded send through the tide runtime

// Direct steer through `tmux send-keys` (no shell, no Python):
const keyed     = await sendKeys("56", "continue")                 // types text + Enter
const noEnter   = await sendKeys("56", "ls -la", { submit: false })// types text, leaves it for a person to submit
const plan      = buildSendKeysPlan("56", "ls -la", false)         // the tmux argv that would run, nothing executed

const up   = await pingDaemon()              // daemon liveness (ping → pong)
const how  = describeTransport()             // { transport: "socket"|"cli", socketPath, socketAvailable }

sendKeys is the primary steer path. It runs tmux send-keys through execFile with argument arrays, so nothing is interpreted by a shell. submit (default true) controls the trailing Enter. buildSendKeysPlan returns the exact command without running it, for a preview.

pingDaemon() and describeTransport() tell a caller which transport is live and whether the daemon answers, before it reads context or proposes a steer.

The steer gate

Every steer that executes (sendKeys on tmux or herdr, sendKeysViaHerdr, runInHerdrPane) passes three checks, the same ones the Python runtime applies to tide operator send --run:

  1. Live target. The pane is in the current multiplexer inventory (tmux list-panes -a or herdr pane list).
  2. Controller posture. ${MIADI_HOME:-~/.miadi/navigator}/config.yml says mode: controller, and its lease has not expired. An expired lease reads as observer.
  3. Interactive terminal. stdin is a TTY, or TIDE_ALLOW_NON_INTERACTIVE_RUN=1 is set by a service that has reviewed the request itself.

A refused steer throws TideError naming every failed check and never touches the multiplexer. Plan builders (buildSendKeysPlan, buildHerdrSendKeysPlan) are not gated.

evaluateSteerGate, requireSteerGate and readNavigatorPosture expose the gate for health checks. A server route that has already authenticated a person can pass humanConsentSteerGate("<route and gate name>") as the gate option: posture and terminal then count as satisfied, and the live-target check still applies. No other gate option is accepted from outside the package, so options built from JSON or a request cannot bypass the gate.

herdr

sendKeys(pane, message, { multiplexer: "herdr" }) types with herdr pane send-text and submits with herdr pane send-keys … Enter. peek(pane, lines, { multiplexer: "herdr" }) reads herdr scrollback.

herdr pane ids (w1:p2, alphanumeric past p9 as in w2:pE; legacy 1-2 accepted) reset when herdr restarts, and a stale id fails with pane_not_found. Name a pane with renameHerdrPane and pass paneLabel: the client resolves the label to the current id before every action. Labels are compared after NFC normalization, a duplicate label is refused rather than guessed, and { workspace: "w7" } limits the search to one workspace. Sending a herdr id down the tmux path, or the reverse, fails.

Other herdr verbs:

| verb | returns | |---|---| | runInHerdrPane, readHerdrPane, renameHerdrPane | run a command, read recent text, set a label | | waitForHerdrOutput, waitForHerdrAgentStatus | wait for output or an agent status change | | listHerdrPanes, listHerdrWorkspaces, listHerdrTabs | inventory lists | | getHerdrInventory({ includeTabs? }) | workspaces and panes in one call | | getHerdrSnapshot() | the whole inventory in one socket round trip: workspaces, tabs, panes, agents, focus, server version | | describeHerdrCapabilities() | what the running herdr advertises, derived from its schema. Never throws: a failed probe returns { available: false, reason } |

describeHerdrCapabilities() reports lifecycle_authorized: false even when herdr advertises workspace.close: this client does not close workspaces. Tested against herdr 0.7.4.

Transports

| transport | used by | |---|---| | daemon Unix socket (miadi.tide.daemon.v2) | getContext when the daemon is up | | tide CLI (tide … --format json, execFile, no shell) | getContext fallback, getTerminals, detect, peek, steerProposal, send | | tmux directly | sendKeys | | herdr CLI | the herdr verbs and multiplexer: "herdr" |

getContext({ transport }) accepts auto (default: socket, falling back to the CLI on absence or error), socket (throws TideDaemonError when unavailable) or cli. The socket answers in microseconds where a CLI spawn costs 200–500 ms. Verb signatures stay the same when a verb moves to another transport.

The daemon speaks newline-delimited JSON. Request {contract_version:"miadi.tide.daemon.v2", request_id, action, payload}, reply {contract_version, request_id, ok:true, data} or {…, ok:false, error:{code,message,retryable}}.

Annotating a reply

The web half of the plannotator review loop that tan <pane> runs in a terminal:

| verb | does | |---|---| | fetchReply(pane, { pick, host }) | copy the agent's last reply from a pane into a review document | | listBlocks(doc) | the document's blocks | | mark(doc, { block \| quote, text, kind, author, author_name }) | add a signed mark | | exportReview(doc) | the review as text | | deliver(pane, doc, { submit }) | send the review back to the pane it came from |

The package holds no review logic of its own. It drives the review script and a headless plannotator-tui, so a review begun in the browser can be finished in the terminal and the other way round. A document remembers the pane it was fetched from, and deliver refuses any other pane. A block mark is always a comment. looks_good and delete need a quote.

Configuration

| variable | default | used for | |---|---|---| | TIDE_BIN | which tide, then /home/jgi/anaconda3/bin/tide | the tide CLI | | TIDE_SOCKET | ${MIADI_HOME:-~/.miadi/navigator}/daemon.sock | the daemon socket | | MIADI_HOME | ~/.miadi/navigator | socket and config.yml (controller posture) | | TIDE_TMUX_BIN | tmux on PATH | sendKeys | | TIDE_HERDR_BIN | herdr on PATH | the herdr verbs | | TIDE_ALLOW_NON_INTERACTIVE_RUN | unset | 1 lets a reviewed service pass the terminal check | | PLANNOTATOR_TMUX_REVIEW_SCRIPT | /opt/gaia/linux_migration/11-plannotator-tmux-review.sh | the review script | | PLANNOTATOR_TUI_BIN | which plannotator-tui, then ~/.cargo/bin/plannotator-tui | the headless annotator | | PLANNOTATOR_TMUX_REVIEW_DIR | ${XDG_STATE_HOME:-~/.local/state}/plannotator-tmux-review | review documents (web runs under web/) | | PLANNOTATOR_STORE_DIR | ~/.plannotator/clients/plannotator-tui/annotations | plannotator's mark records |