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

@brumbelow/viewport

v1.0.0

Published

Local browser monitor and control surface for CLI coding-agent runs.

Readme

Viewport

Viewport is a local browser monitor for CLI coding agents.

The idea: run a coding agent through a lightweight wrapper, stream its activity into a cleaner local browser view, and give the operator fast controls for pause, kill, steering prompts, evidence pinning, and after-action summaries.

What it does today

  • viewport starts a local server on 127.0.0.1 and prints the URL.
  • viewport run -- <command> starts the server and immediately launches a run.
  • Browser clients list runs, receive live output over Server-Sent Events, and can reconnect without losing the buffered event stream.
  • Run timelines are persisted in a local SQLite database and reloaded when the server starts again.
  • The UI exposes a run list, an xterm.js terminal view, status/runner/workspace metadata, launch presets for common agent/workspace combinations, a custom agent launcher with optional args, a literal start-folder field, pause/resume and kill buttons, a multiline steering input with send modes and prompt history, and quick buttons for Ctrl+C / Ctrl+D / Esc / arrow keys. Selected runs are deep-linkable. Transcripts can be exported as plain text during or after a run, an after-action summary dialog (Summary button) shows a local digest of the selected run with copy-to-clipboard, and a report dialog shows the Markdown run report in-app and can open the HTML or JSON versions. Pins, spans, and search results open a bounded evidence dialog around the relevant event range so useful transcript context can be copied without scrolling, and pinned moments can carry uploaded artifact attachments. The whole evidence set (report, transcript, pins, artifacts, manifest) can be exported as a ZIP bundle for offline sharing.

The server is plain Node.js with static browser assets. Execution prefers node-pty so wrapped agent CLIs get terminal behavior, with a child_process fallback if PTY loading fails. No Vite or frontend build step is involved — the browser frontend is plain ES modules under public/js/ (loaded with <script type="module">) and per-feature CSS modules under public/css/ imported by public/styles.css.

Supported agents

Viewport auto-discovers the following agent CLIs from your PATH:

  • claude
  • codex
  • gemini
  • grok

Each one is listed in /api/agents with an available flag derived at request time. To point at a non-PATH install, set the matching env var:

  • VIEWPORT_CLAUDE_BIN
  • VIEWPORT_CODEX_BIN
  • VIEWPORT_GEMINI_BIN
  • VIEWPORT_GROK_BIN

For Codex, CODEX_HOME defaults to $HOME/.codex so nested sessions share the same sign-in, model, approval, sandbox, and project trust settings as the normal CLI. Override with VIEWPORT_CODEX_HOME. No user-specific paths are baked into the defaults.

Install

Viewport ships as a single Node.js package and a small static frontend. Once published, a global install works the same way locally and on a workstation:

npm install -g @brumbelow/viewport
viewport init        # writes ~/.viewport/{config.json,presets.json}
viewport             # start the server in the background
viewport doctor      # diagnose Node, node-pty, sqlite, agent discovery
viewport version
viewport status      # show running daemon state (exit 1 if not running)
viewport stop        # ask the daemon to exit cleanly
viewport open        # open the running daemon's URL in your browser

Until the package is on a registry, the same commands work after a local npm install -g . from a clone.

Outside the repository, Viewport stores local data in ~/.viewport/. Set VIEWPORT_DATA_DIR=/some/path to override, or run from inside the cloned repo to keep data in the repo's .viewport/.

If you start the daemon with a non-default PORT, set the same PORT env when running viewport status / stop / open — they probe that port.

Viewport uses Node's built-in SQLite bindings and node-pty. Running on a recent Node release (current LTS or newer) is recommended; viewport doctor reports whether each piece loaded.

Run from source

npm install
npm start

This starts Viewport in the background and prints the local URL.

To wrap another command:

node server/index.js run -- npm test

Or start the server without immediately launching a command:

node server/index.js --foreground

API shape

  • GET /api/health returns runner type, server start time, server cwd, and run counts.
  • GET /api/agents lists the supported agents (claude, codex, gemini, grok), each with an available flag based on PATH lookup or the matching VIEWPORT_*_BIN override.
  • POST /api/agents/:id/runs starts a new interactive agent run for one of the supported agent ids. Optional cwd chooses the workspace, and optional args appends CLI arguments such as permission flags. Returns 404 if the id is not a supported agent and 409 if the agent binary cannot be found.
  • GET /api/launch-presets lists built-in and configured launch presets.
  • POST /api/launch-presets/:id/runs starts a named preset. Presets carry agent, cwd, args, and env overrides; API responses expose envKeys, not env values.
  • GET /api/workspaces lists local workspace/project path suggestions. Override roots with VIEWPORT_WORKSPACE_ROOTS=/path/a:/path/b.
  • GET /api/runs lists known runs.
  • POST /api/runs starts a shell command from a command string. Optional cwd must point at an existing directory, and optional env is merged into the child process environment. Responses expose envKeys, not env values.
  • GET /api/runs/:id returns a run snapshot and buffered events.
  • GET /api/runs/:id/transcript returns a plain-text terminal transcript with run metadata, status events, and output chunks.
  • GET /api/runs/:id/summary returns a deterministic after-action summary derived locally from the run's stored events. The JSON includes status, exit info, command, cwd, runner, timestamps, durationMs, event counts (output/input/status/resize), output bytes/lines/lastLine, and a compact text digest suitable for copy.
  • GET /api/runs/:id/report?format=html|md|json returns a local run report with summary signals, pins, artifacts, and a clipped transcript.
  • GET /api/runs/:id/context?eventId=N&toEventId=M returns a bounded JSON evidence window and copyable plain-text transcript context around one event or event span. Optional before/after control surrounding event count.
  • DELETE /api/runs/:id removes a terminal run and its persisted timeline.
  • GET /api/runs/:id/events streams live events with SSE. Honors the Last-Event-ID header so reconnecting clients do not see duplicates, and sends keep-alive comments every 15 seconds.
  • POST /api/runs/:id/kill requests termination (SIGTERM, escalating to SIGKILL after 1.5s).
  • POST /api/runs/:id/pause sends SIGSTOP and marks a running POSIX run paused.
  • POST /api/runs/:id/resume sends SIGCONT and returns a paused POSIX run to running.
  • POST /api/runs/:id/input writes the JSON data string to the run's PTY (or stdin under the spawn fallback).
  • POST /api/runs/:id/resize sets the PTY size from {cols, rows}.
  • POST /api/runs/:id/signal delivers a named POSIX signal (SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGKILL, SIGUSR1, SIGUSR2).
  • POST /api/shutdown asks the server to exit cleanly. Used by viewport stop and equivalent to SIGTERM. Returns 202 immediately, then drains active runs and exits.

Local data

Viewport stores run timelines in .viewport/viewport.sqlite, using Node's built-in SQLite bindings. Each run gets a metadata row and ordered event rows. If you pass env overrides, those values are stored in the local database so restored runs retain their launch metadata. The directory is ignored by git and can be deleted when old local history is no longer useful.

Launch presets can be created and edited from the Preferences dialog (Settings → Presets tab). The file at .viewport/presets.json (overridable with VIEWPORT_PRESETS_FILE) remains the source of truth and can also be edited by hand:

{
  "presets": [
    {
      "id": "claude-viewport-print",
      "name": "Claude: viewport print",
      "agent": "claude",
      "cwd": "/path/to/project",
      "args": ["--print"],
      "env": {}
    }
  ]
}

Preset env values are merged into the launched process and stored in the local SQLite run metadata. Keep secrets out of preset env unless you are comfortable with them living in local Viewport history.

Codex launches resolve the codex binary from PATH (or VIEWPORT_CODEX_BIN) and set CODEX_HOME to $HOME/.codex so nested sessions share the same sign-in, model, approval, sandbox, and project trust settings as the normal CLI. Override the data directory with VIEWPORT_CODEX_HOME.

Network binding and auth

By default the server binds to 127.0.0.1. Override the bind address with VIEWPORT_HOST. Whenever the bind address is loopback (127.0.0.1, localhost, or ::1) no auth token is required, and the server still rejects any request whose Host header or Origin is not loopback (the former with 421, the latter with 403).

If VIEWPORT_HOST is set to a non-loopback address, an auth token is required on every request. Provide one explicitly with the VIEWPORT_TOKEN environment variable; if you leave it unset, Viewport generates a random token at startup and prints it. Send the token with the Authorization Bearer scheme, the X-Viewport-Token header, or the ?token=<value> query parameter. Requests missing or with an unrecognized token receive 401.

Backup and restore

The local SQLite database at .viewport/viewport.sqlite (or $VIEWPORT_DATA_DIR/viewport.sqlite if overridden) is the single source of truth for run history, pins, and artifact metadata. Artifact blobs live alongside it under .viewport/artifacts/.

To back up, stop the daemon (viewport stop) and copy the entire .viewport/ directory. To restore, drop the directory back in place and start the daemon again — runs and their events replay from the database on startup.

Corruption recovery

If SQLite refuses to open the file (for example after an unclean shutdown on a flaky filesystem), Viewport renames the bad file to viewport.sqlite.corrupt-<timestamp> and starts a fresh database. The corrupt file is left in place; you can attempt manual recovery with sqlite3 viewport.sqlite.corrupt-… .recover or delete it once you no longer need the history.

The schema is keyed on SQLite's PRAGMA user_version and migrated forward at startup. A database created by an older Viewport release is upgraded in place; the migrations run in a single transaction per version and roll back on failure.

Troubleshooting agent discovery

Viewport resolves claude, codex, gemini, and grok from PATH at request time. If viewport doctor or the /api/agents payload shows an agent as unavailable:

  • Confirm the binary is in PATH for the shell that launched the daemon (which claude, command -v codex). The daemon inherits the launching shell's PATH; restart it after editing your shell profile.
  • If the binary lives outside PATH (for example a homebrew prefix not exported in your profile), set the matching VIEWPORT_*_BIN env var with the absolute path.
  • For Codex, CODEX_HOME defaults to $HOME/.codex. Override with VIEWPORT_CODEX_HOME if your sessions live elsewhere.
  • Symlinks resolve through the host shell, so a stale symlink will look available but fail at spawn time — run the agent's own --version flag to confirm it executes outside Viewport first.

Verification

npm run check   # node --check on the server and smoke test
npm test        # boots the server on a free port and exercises the API

The smoke test boots the server with a temporary PATH of stub agent binaries (no real Claude / Codex / Gemini / Grok install required) and exercises: health, agent/workspace/preset discovery for all supported agents, auto-discovery + availability flags via PATH, run creation, cwd/env launch metadata, xterm static assets, launch/steering/copy/export/summary, report-preview and evidence-context UI controls, transcript export, after-action summary endpoint (status, duration, output counts, last line, 404), event-context endpoint, SSE delivery, pause/resume, input/resize/signal endpoints, Last-Event-ID resume, delete + active-run guard, presets CRUD, concurrent-run cap, retention pruning, DB corruption recovery, evidence-bundle ZIP export, pin-attachment lifecycle, and SQLite replay after server restart.

Summary markers

Agents that opt in can emit small marker tags in their normal stdout to populate the Signals tab of a run's summary:

  • <viewport:milestone name="..."/> — a named milestone reached during the run.
  • <viewport:result status="ok|fail"/> — a final or intermediate result.
  • <viewport:note text="..."/> — a free-form note.

Markers are tolerant of extra whitespace inside the angle brackets and are extracted alongside the heuristic detectors (errors, file paths, idle gaps) that run on every captured run.

Verification

  • npm test runs the integration smoke suite in scripts/smoke.mjs.
  • npm run test:unit runs the per-module frontend unit tests in test/ (node:test + jsdom; the browser modules exercised in isolation).
  • npm run test:load runs the large-history regression suite in scripts/load.mjs (~20 s; seeds ~500 runs / ~100k events).
  • docs/QA.md is the manual browser checklist to walk before cutting a release.

Frontend layout

  • public/js/app.js is a thin orchestrator; feature logic lives in per-feature modules (launcher, runs, streaming, steering, pins, artifacts, reports, evidence, search, settings) plus a shared terminal.js, on the state.js / dom.js / util.js foundation.
  • public/styles.css mirrors that split: a small @import manifest over per-feature CSS modules under public/css/ (runs, launcher, terminal, steering, pins, search, reports, artifacts, settings) on a base / layout / dialogs foundation, with a responsive media-query layer and a theme token layer loaded last. No build step.
  • Every browser module — including the app.js orchestrator — has isolated unit tests under test/ (run with npm run test:unit); the smoke suite continues to cover the server and asset wiring.