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

metwatch

v0.5.0

Published

Process monitor & manager with a terminal and a local web dashboard — like htop + PM2, works with your existing PM2 apps

Downloads

451

Readme

MetWatch

npm version License: MIT CI Bun >=1.3 Platform PRs Welcome

MetWatch is a process monitor and manager with two dashboards: one in your terminal and one in your browser. Think htop + PM2 in one tool: realtime CPU, memory, disk and network, a full process table, and managed processes with restarts, logs, history and alerts. Already using PM2? Your PM2 apps show up automatically.

bun add -g metwatch
mw web      # web dashboard in your browser
mw          # terminal dashboard

✨ Web dashboard — mw web

A free, modern dashboard for your processes and PM2 apps, running on your own machine. One command, no account, no cloud, no agent to install.

MetWatch web dashboard: process detail with CPU and memory charts, logs, details and events

  • Overview — live CPU, memory, disk and network with trends, your processes at a glance, recent events and system load
  • Processes — MetWatch and PM2 apps together (cluster apps as ×N), search and filters, restart / reload / stop with confirmation
  • Process detail — CPU and memory charts (live, 1h, 6h, 24h), heap and event-loop lag, restarts, uptime, live logs and events
  • Logs — every process in one live stream, with search, per-process and stdout/stderr filters, pause and resume
  • Events & alerts — starts, crashes, restarts, crash loops and memory alerts; edit alert rules and Slack / Discord / Telegram / webhook targets right in the dashboard, with a test button
  • Start processes — launch the apps from your config or saved with mw save; new commands too with --allow-exec
  • System — per-core load, disks, network interfaces, top processes
  • Always on — mw daemon start --web keeps the dashboard up with the daemon; history and events survive restarts
  • Light and dark — follows your system theme; works on mobile screens too
  • Private by default — listens on 127.0.0.1 only, with a private link; reach it on a server through an SSH tunnel

| | PM2 on its own | PM2 + MetWatch | |---|---|---| | Web dashboard | PM2 Plus: hosted service, account and pm2 link | mw web: local, free, no account | | Terminal dashboard | pm2 monit | Full system + processes dashboard with charts and a details view | | CPU / memory history | PM2 Plus | 24h per process, in both dashboards and mw history | | Crash & memory alerts | PM2 Plus | Slack, Discord, Telegram or JSON webhooks from your machine | | Logs | pm2 logs | Time and regex filters, web log viewer with search | | Start on boot | pm2 startup + pm2 save | mw startup + mw save for MetWatch processes, no admin rights | | Wait until ready | — | mw wait pm2:web --port 3000 | | AI agents | — | --json everywhere + MCP server |

PM2 keeps running your apps exactly as before: MetWatch only reads them and can restart, reload or stop them. It never creates, deletes or kills PM2 apps.


🖥️ Terminal dashboard — mw

For servers over SSH or when you live in the terminal: the whole machine and your processes in one screen, sized exactly to your terminal.

MetWatch terminal dashboard: CPU cores, memory, disk, network, managed and PM2 apps, process table and logs

Press Enter on any process for its details: CPU and memory charts, heap and event-loop lag, latest logs, and restart / stop / reload.

MetWatch terminal process details: PM2 cluster app with CPU and memory charts and live logs


Features

  • Web dashboard — mw web: overview, processes with actions, per-process charts, logs, events and system pages, light/dark, local only
  • Live terminal dashboard — CPU (per-core grid, history, busiest processes), memory (history, swap, top consumers), disks (usage, read/write rates), network (mirrored RX/TX chart, TCP connections)
  • Process table — sortable, filterable (/), All / Watched modes, managed processes highlighted with status
  • Process details — press Enter on any process for CPU/memory charts, status, logs and restart / stop / reload in one view
  • Managed processes — like PM2: run scripts with auto-restart and exponential back-off, in the dashboard or in a background daemon that survives closing the terminal
  • Managed panel — CPU, memory, uptime and restarts per process; heap and event-loop lag for Node/Bun with --inspect
  • Logs — buffered stdout/stderr, live in the dashboards or from the CLI with time and regex filters
  • Works with PM2 — apps of a running PM2 daemon show up automatically as pm2:<app>: both dashboards, logs with filters, wait, zero-downtime reload, alerts and MCP tools, without replacing PM2
  • Alerts & history — Slack, Discord, Telegram or JSON webhooks on crashes, crash loops and memory limits, editable from the web dashboard; per-process CPU/memory history for 24h that survives restarts
  • Runs on its own — mw save + mw startup bring your processes back when you sign in or the server boots (Windows, Linux systemd, macOS launchd)
  • Agent friendly — --json output, stable exit codes, mw wait for readiness, and a built-in MCP server for AI agents and editors
  • Lightweight — non-overlapping polling, one persistent PowerShell session on Windows instead of a process per poll; the web dashboard only collects data while a browser is open

Requirements

| Requirement | Version | |---|---| | Bun | ≥ 1.3 | | Terminal | Interactive terminal for the dashboard (CLI commands and MCP work anywhere) | | OS | macOS, Linux, Windows |

Install Bun if you haven't already:

curl -fsSL https://bun.sh/install | bash

Installation

Global install (recommended)

bun add -g metwatch

Then run from anywhere:

mw

Run without installing

bunx metwatch

From source

git clone https://github.com/victorhhh/metwatch.git
cd metwatch
bun install
bun run start

Quick Start

# Open the web dashboard (starts the background daemon if needed)
mw web

# Or the terminal dashboard
mw

# Run a server and watch it in the dashboard (stops when you quit)
mw start server.ts

# Run it in the background instead, then wait until it listens
mw start server.ts --name api --detach
mw wait api --port 3000

# Check on it from anywhere
mw list
mw logs api --since 5m --grep error

# One-shot system report (add --json for scripts and agents)
mw snapshot

Keep it running on a server

mw start server.ts --name api --detach      # your apps, in the background daemon
mw save                                     # remember them
mw startup --web                            # start the daemon (and web dashboard) at boot/sign-in
mw web                                      # get the dashboard link any time

# From your computer, open the dashboard of a remote server privately:
ssh -N -L 9615:127.0.0.1:9615 you@server    # then open the link printed by `mw web` on the server

Usage

Run mw help <command> for every flag. Commands that talk to managed processes (list, logs, stop, restart, wait) connect to the running MetWatch instance — the dashboard or the daemon — over a local named pipe (Windows) or unix socket (macOS/Linux). Only one instance per user owns managed processes.

Output conventions: most commands accept --json (one JSON document on stdout, errors as {"error": "..."}; logs --json streams one object per line). Human output has no colors. Exit codes: 0 success, 1 failure, 2 usage error.

mw / mw monitor

Open the dashboard. If a daemon is running, the dashboard attaches to it: you see and control its processes, and quitting leaves them running. Without an interactive terminal (pipes, CI, agents) it prints a snapshot instead of failing.

mw web — web dashboard

Opens the web dashboard in your browser: overview with live CPU, memory, disk and network; processes (MetWatch and PM2) with search, filters, restart / reload / stop; a detail page per process with CPU and memory charts (live, or 1h / 6h / 24h from the history), logs and events; a log viewer with search; the event log; and a system page with cores, disks, interfaces and top processes. It follows your system's light/dark theme, with a toggle.

It also lets you start processes (the ones in metwatch.config.json or saved with mw save) and edit alert settings: rules, Slack / Discord / Telegram / JSON targets and a Send test alert button. Changes apply immediately and are saved to the instance's metwatch.config.json.

It starts the daemon if needed (the dashboard reads everything from it), listens on 127.0.0.1:9615 (--port <n>; if that port is taken it picks a free one) and opens the browser (--no-open just prints the link). Ctrl+C stops the web server; the daemon and its processes keep running. If the daemon already serves the dashboard (mw daemon start --web), mw web just prints and opens that link.

| Flag | Description | Default | |---|---|---| | --port <n> | Port on 127.0.0.1 | 9615 | | --no-open | Print the link without opening a browser | off | | --allow-exec | Allow starting new commands from the dashboard (configured and saved processes can always be started) | off |

It is private by default: it binds to 127.0.0.1, and the printed link carries a session token that is swapped for an HttpOnly cookie. Requests from other sites or host names are rejected. Don't share the link.

On a remote server, keep it that way and use an SSH tunnel (any local port works):

# on the server
mw daemon start --web        # or: mw web --no-open
mw web --no-open             # prints the link

# on your computer
ssh -N -L 9615:127.0.0.1:9615 you@server
# open the printed link in your browser (use your local port if you picked another)

mw start <file>

Launch a script as a managed process. The runtime is inferred from the extension: .ts/.tsx → bun, .js/.mjs/.cjs → node, .py → python, anything else is executed directly.

| Flag | Description | Default | |---|---|---| | --name <label> | Process name | basename of <file> | | --runtime <cmd> | Override the inferred runtime | auto-detected | | --no-restart | Disable auto-restart on crash | auto-restart enabled | | --cwd <dir> | Working directory for the child process | current directory | | --env KEY=VALUE | Environment variable (repeatable) | — | | --inspect | Node/Bun: heap and event-loop metrics in the Managed panel. Opens a debugger port on localhost. | off | | --detach, -d | Run in the background daemon (started automatically) and return | off | | --json | With --detach: print the started process as JSON | off |

mw start server.ts                                   # dashboard hosts it
mw start worker.js --cwd ./workers --env PORT=4000 --detach
mw start main.ts --runtime tsx --inspect

mw daemon start | stop | status

The daemon keeps managed processes running without a dashboard. It loads metwatch.config.json from --cwd (default: current directory), starts its managedProcesses, and polls nothing on its own. Errors are written to metwatch-<user>-daemon.log in the temp directory.

| Flag | Description | |---|---| | --web | Also serve the web dashboard while the daemon runs (config: "web": { "enabled": true }) — get the link with mw web | | --port <n> | Web dashboard port (default 9615) | | --resurrect | Also start the processes saved with mw save |

mw daemon start
mw daemon status --json
mw daemon stop          # stops the daemon and its processes

mw save / mw startup

mw save remembers the MetWatch processes running now (command, arguments, directory and environment) in MetWatch's private data directory. mw daemon start --resurrect starts them again.

mw startup makes that automatic, without administrator rights:

| OS | What it installs | When it starts | |---|---|---| | Windows | MetWatch.cmd in your Startup folder | when you sign in | | Linux | systemd user service metwatch.service (enabled and started) | with your session; run loginctl enable-linger $USER once to start at boot without signing in | | macOS | LaunchAgent dev.metwatch.daemon.plist | when you sign in |

mw save
mw startup --web --cwd /srv/shop    # the daemon loads /srv/shop/metwatch.config.json
mw startup status
mw startup remove

PM2 apps are started by PM2 itself (pm2 startup + pm2 save); MetWatch picks them up automatically.

mw list

Managed processes of the running instance. With nothing running, lists the definitions from metwatch.config.json.

NAME  STATUS   PID    RESTARTS  UPTIME  COMMAND
api   running  12345         0  2m 34s  bun run api.ts
jobs  crashed      -         3  -       node worker.js

mw logs <name|all>

| Flag | Description | Default | |---|---|---| | --follow, -f | Keep streaming new lines | off | | --lines, -n <n> | Buffered lines to show | 50 | | --since <duration> | Only newer lines: 30s, 5m, 2h | — | | --grep <regex> | Only matching lines (case-insensitive) | — | | --json | NDJSON: {id, stream, line, timestamp} | off |

mw stop <name|all> / mw restart <name|all> / mw reload <name>

Stop (no auto-restart), restart or reload managed processes. reload is a zero-downtime rolling reload for PM2 cluster apps and a restart for everything else. all targets MetWatch's own processes; pm2:all targets every PM2 app.

mw history <name> / mw events [name]

Recorded by the running dashboard or daemon: history shows CPU and memory once per minute for the last 24 hours (--last 6h), events lists starts, stops, crashes, restarts and alerts (--since 1h, --limit). Both accept --json. They are saved in MetWatch's data directory, so restarting the daemon (or the machine) keeps them.

mw wait <name>

Block until a process is ready; exit 0 when ready, 1 if it crashes, stops or times out.

| Flag | Description | Default | |---|---|---| | --log <regex> | A log line of the current run matches | — | | --port <n> | localhost:<n> accepts TCP connections | — | | --timeout <duration> | Give up after | 60s | | --json | {ready, name, elapsedMs, reason?, matchedLine?, process} | off |

mw start server.ts --name api --detach && mw wait api --log "listening on" --port 3000

mw snapshot

CPU, memory, disks, network, top processes by CPU and memory, and managed processes — once. Uses the running instance if there is one, otherwise samples locally (about a second, since rates need two readings). --top <n> sets processes per ranking (default 10), --json prints everything.


PM2 integration

Already deploying with PM2? Keep it. When a PM2 daemon is running, its apps appear in MetWatch automatically as pm2:<app> — no configuration, and nothing to install in PM2.

pm2 start ecosystem.config.js      # deploy as usual

mw                                 # dashboard: pm2 apps in Managed, Processes and Logs
mw list                            # SOURCE column: metwatch | pm2 (cluster apps as "web ×4")
mw logs pm2:web --since 10m --grep error
mw wait pm2:web --port 3000
mw reload pm2:web                  # zero-downtime rolling reload (cluster mode)
mw daemon start                    # history, events and alerts for PM2 apps too
  • Works without a MetWatch instance: list, logs, wait, stop, restart and reload talk to PM2 directly. History, events and alerts need the dashboard or daemon running.
  • Metrics: CPU, memory, heap and event-loop latency come from PM2's own probes — no --inspect needed.
  • Light: MetWatch listens to PM2's event bus for logs and state changes and asks for the full process list only occasionally.
  • Safe by design: MetWatch never creates, deletes or kills PM2 apps (only restart / stop / reload), all never touches PM2 apps, and PM2's process environments — which often hold secrets — are never read into MetWatch's output.
  • Opt out with "integrations": { "pm2": false } in metwatch.config.json.
  • MetWatch talks to PM2 as a separate program over its local sockets and bundles no PM2 code. PM2 is a trademark of its respective owners.

AI agents & MCP

MetWatch is built to be driven by coding agents as well as people: an agent can start a dev server in the background, wait until it is ready, read its logs when something fails, and check system load — without holding a terminal open.

From a shell (Claude Code, Codex, Cursor agents…): use the CLI with --json and the exit codes above.

Over MCP: mw mcp runs a Model Context Protocol server over stdio.

| Tool | What it does | |---|---| | get_system_metrics | CPU, memory, disks, network, top processes, managed processes | | list_processes | OS processes filtered by name/command/PID, sorted and limited | | list_managed | Managed processes (or configured ones when nothing runs) | | get_logs | Recent lines with lines, since and grep | | wait_until_ready | Wait for a log pattern and/or port; fails fast on crash | | start_process | Start a process in the daemon (started if needed) | | stop_process / restart_process / reload_process | Lifecycle control (reload = zero-downtime for PM2 cluster apps) | | get_process_history | CPU/memory over time with averages and maxima | | get_events | Crashes, restarts and alerts | | daemon_status | Whether a dashboard or daemon is running |

Every tool accepts PM2 apps as pm2:<app>.

Add it to your client:

# Claude Code
claude mcp add metwatch -- mw mcp
{
  "mcpServers": {
    "metwatch": { "command": "mw", "args": ["mcp"] }
  }
}

If mw isn't on the client's PATH (or on Windows with an npm install), use "command": "bunx", "args": ["metwatch", "mcp"].

Safety: by default start_process only starts processes defined in metwatch.config.json, by name. Run mw mcp --allow-exec to let agents start arbitrary commands — they run as your user. Tools can only stop or restart processes MetWatch manages; there is no tool to kill arbitrary PIDs. Responses are size-limited (lines, line length, process counts) to keep agent context small.


Configuration

MetWatch reads metwatch.config.json from the current working directory at startup. If the file is not found, built-in defaults are used. Bad JSON or invalid fields fall back to defaults and print a warning, without crashing.

{
  "watchedProcesses": [
    { "name": "node",   "label": "Node Apps" },
    { "name": "bun",    "label": "Bun Apps"  },
    { "name": "python", "label": "Python"    }
  ],
  "refreshInterval": 1000,
  "maxProcesses": 50
}

Fields:

| Field | Type | Default | Description | |---|---|---|---| | watchedProcesses | array | [] | Processes to highlight in Watched mode | | watchedProcesses[].name | string | — | Substring matched against process name (case-insensitive) | | watchedProcesses[].label | string | same as name | Display label in the Watched view | | refreshInterval | number (ms) | 2000 | CPU/memory poll interval — minimum 500 ms. Network polls at least every 2 s, disk every 3 s, the process list at 2× this interval. | | maxProcesses | number | 50 | Top processes kept by CPU and by memory (so both sort orders are meaningful) | | managedProcesses | array | [] | Processes started by the dashboard or daemon, and startable by name from MCP: { "name", "command", "args"?, "autoRestart"?, "cwd"?, "env"?, "inspect"? } | | logScrollback | number | 500 | Log lines kept per managed process | | integrations.pm2 | boolean | true | Show and control PM2 apps as pm2:<app> | | alerts | object | see below | Webhook alerts for crashes, crash loops and memory limits (also editable from the web dashboard) | | web.enabled | boolean | false | Serve the web dashboard from the daemon (like mw daemon start --web) | | web.port | number | 9615 | Port of the daemon's web dashboard on 127.0.0.1 | | web.allowExec | boolean | false | Allow starting new commands from the daemon's web dashboard | | panels | object | all true | Initial panel visibility, e.g. { "disk": false } |

Data directory — history, events and saved processes live in %LOCALAPPDATA%\metwatch (Windows), ~/Library/Application Support/metwatch (macOS) or ~/.local/state/metwatch (Linux). Set METWATCH_HOME to use another folder.


Alerts

Alerts are evaluated by the running dashboard or daemon, for MetWatch processes and PM2 apps alike, and listed by mw events.

{
  "alerts": {
    "webhooks": [
      { "url": "https://hooks.slack.com/services/…", "format": "slack" },
      { "url": "https://discord.com/api/webhooks/…", "format": "discord" },
      { "url": "https://api.telegram.org/bot<token>/sendMessage", "format": "telegram", "chatId": "-1001234567890" },
      { "url": "https://example.com/metwatch", "format": "json" }
    ],
    "crash": true,
    "restartLoop": { "restarts": 5, "withinSec": 300 },
    "memoryMB": 1024,
    "cooldownSec": 300
  }
}

| Field | Default | Description | |---|---|---| | webhooks | [] | POST targets; format is json (full alert), slack ({text}), discord ({content}) or telegram (Bot API sendMessage, needs chatId) | | crash | true | Alert when a process exits with an error | | restartLoop | { "restarts": 5, "withinSec": 300 } | Alert when a process crashes this often in the window; null disables | | memoryMB | null | Alert when a process uses more memory (checked once per minute) | | cooldownSec | 300 | Minimum time between identical alerts for the same process |

Telegram: create a bot with @BotFather and copy its token. Send the bot a message (or add it to your group), then open https://api.telegram.org/bot<token>/getUpdates and copy chat.id — groups and channels start with -100; public channels can use @channelname (the bot must be an admin there). In the web dashboard, pick Telegram and paste the token and chat ID; the token is stored in the config like any webhook URL, so keep that file private.


Dashboard Panels

The layout adapts to the terminal size: every panel is sized to fit exactly, the Network/Managed row is hidden on short terminals so the process table stays usable, and Managed and Logs only appear when there are managed processes.

| Panel | Toggle key | What it shows | |---|---|---| | Header | — | Host, OS, uptime, thread count, RAM, process count, live CPU/memory %, clock | | CPU | always on | Total load meter + history; per-core grid (meters, compact cells or blocks depending on space); busiest processes; load average on macOS/Linux | | Memory | always on | RAM and swap/page-file meters, used/available, top memory consumers, usage history chart | | Disk | d | Per-mount usage meter, used/total, free space and filesystem; read/write rates in the title | | Network | n | Total ↓RX / ↑TX, TCP connections (Windows/Linux), active interfaces, mirrored history chart (download up, upload down, one scale) | | Managed | R | One row per managed process and PM2 app (tagged pm2, clusters as ×N): status, PID, CPU, memory, uptime, restarts; heap and event-loop lag from --inspect or PM2's probes | | Processes | p | Sortable, filterable table; managed processes show their name and status badge | | Logs | l / Tab (focus) | Interleaved stdout/stderr of managed processes, colored per process, with scroll-back | | Footer | — | Key hints for the focused panel, confirmations and status messages |

Colors: green / yellow / red always mean load level and are shown next to the number; RX/TX and per-process colors use cyan/magenta/blue so they never read as warnings.


Keyboard Shortcuts

Press ? inside MetWatch for the same list.

Global

| Key | Action | |---|---| | q / Ctrl+C | Quit MetWatch | | ? | Toggle help overlay | | Tab | Switch focus between Processes and Logs | | l / Esc | Focus Logs / back to Processes (Esc also clears the filter) | | d n R p | Toggle Disk / Network / Managed / Processes panel |

Process table

| Key | Action | |---|---| | ↑ ↓ / k j | Move selection (it follows the process when the list re-sorts) | | PgUp PgDn / g G | Page / jump to top or bottom | | / | Filter by name, command, managed name or PID prefix (Enter keeps, Esc clears) | | c / m / o | Sort by CPU / memory / cycle CPU → MEM → NAME → PID | | a / f | All processes / watched processes (config + managed) | | K (Shift+k) | Kill selected process (confirm with y) | | Enter | Open process details | | r / s / u | Restart / stop / reload the selected managed process or PM2 app |

Process details

Charts of CPU and memory (managed apps are sampled in the background, so their charts already have history when opened; other processes start sampling when the view opens), status, PIDs, uptime, restarts, heap and event-loop lag, and the latest logs.

| Key | Action | |---|---| | Esc / ← / Backspace | Back to the dashboard | | r / s / u | Restart / stop / reload (managed processes and PM2 apps) | | K | Kill (confirm with y) |

Logs (when focused)

| Key | Action | |---|---| | ↑ ↓ / PgUp PgDn | Scroll back (pauses following) | | G / End | Resume following new output | | ← → | Show all processes or one at a time |


Contributing

Contributions are welcome! Here is how to get started:

  1. Fork the repository on GitHub
  2. Clone your fork: git clone https://github.com/<your-username>/metwatch.git
  3. Create a branch: git checkout -b feat/my-feature
  4. Install dependencies: bun install
  5. Make your changes following the conventions below
  6. Typecheck & test: bun run typecheck must exit with 0 errors and bun test must pass before opening a PR
  7. Open a Pull Request against main on victorhhh/metwatch

Note: All pull requests are reviewed and merged exclusively by the maintainer (@elhuguito). Submitting a PR does not guarantee acceptance, but all contributions are genuinely appreciated and reviewed.

Code conventions (summary)

  • TypeScript strict mode — no any, no @ts-ignore without a comment
  • Factory functions over classes — managers use create*() returning plain objects
  • React components — widgets are .tsx files using useState/useEffect hooks; useInput for keyboard handling
  • Event bus — every bus.on() must store and call its unsubscribe in the useEffect cleanup
  • Layer rules — widgets never import from services/ directly; all data via bus + state
  • No .then() chains — use async/await everywhere

For a full architecture guide, see AGENTS.md.

Reporting issues

Please open an issue at github.com/victorhhh/metwatch/issues with:

  • OS and terminal emulator
  • Bun version (bun --version)
  • Steps to reproduce
  • Screenshot or error output if applicable

License

MIT © 2026 elhuguito

See LICENSE for the full text.