midnight-monitor
v1.0.1
Published
Midnight LLM Monitor for local LLM server hardware intelligence.
Readme
Midnight LLM Monitor
Midnight LLM Monitor is a lightweight hardware intelligence daemon for machines running local LLM servers such as Ollama, llama.cpp, and vLLM-adjacent tooling.
It exposes live metrics over REST and WebSocket so Midnight Coder can make decisions from real hardware state instead of guesses.
Current focus
- GPU and VRAM visibility in the overview
- Ollama running model CPU/GPU split, context usage, and model metadata
- llama.cpp process discovery and model path/context hints
- low-overhead polling with cached expensive collectors
- plugin discovery for third-party collectors
Install
Requires Node.js 20 or newer.
npm install -g midnight-monitorRun without installing:
npx midnight-monitorAccess
Start the daemon and open the dashboard in a browser:
midnight-monitor startThen visit:
http://127.0.0.1:9898/when the monitor runs on your own machinehttp://<server-ip>:9898/when the monitor runs on another host
The default HTTP port is 9898. If you change it in midnight-monitor.config.json, use that port instead.
CLI
midnight-monitor
midnight-monitor start
midnight-monitor stop
midnight-monitor status
midnight-monitor doctor
midnight-monitor benchmarkmidnight-monitor without arguments prints help. Use midnight-monitor start to start the daemon.
Release
Releases are published from GitHub Actions, not from a local npm publish.
Prerequisites:
- GitHub repository secret
NPM_TOKENmust contain an npm automation token with publish access tomidnight-monitor. - Commits merged to
mainmust use Conventional Commits sosemantic-releasecan decide the next version.
Publish flow:
npm run typecheck
npm run lint
npm test
npm run build
git push origin mainThe Release workflow runs npx semantic-release, creates the GitHub release, and publishes the package to npm.
Configuration
Create midnight-monitor.config.json in the working directory.
{
"server": {
"host": "127.0.0.1",
"port": 9898,
"wsPath": "/ws"
},
"collectors": {
"enabled": [
"cpu",
"ram",
"swap",
"gpu",
"disk",
"network",
"temperatures",
"processes",
"ollama",
"llamacpp",
"history"
],
"modules": []
},
"intervals": {
"cpu": 1000,
"ram": 1000,
"swap": 1000,
"gpu": 1000,
"disk": 10000,
"temperatures": 5000,
"network": 1000,
"processes": 1000,
"ollama": 2000,
"llamacpp": 2000,
"history": 1000
}
}REST API
GET /dashboard webGET /healthGET /metricsGET /cpuGET /memoryGET /swapGET /gpuGET /diskGET /networkGET /ollamaGET /historyGET /processes
Example:
curl http://127.0.0.1:9898/metricsWebSocket
Connect to /ws for live updates.
const socket = new WebSocket("ws://127.0.0.1:9898/ws");
socket.onmessage = (event) => {
console.log(JSON.parse(event.data));
};Web Monitor
Open http://127.0.0.1:9898/ to see the visual monitor with:
- a GPU/VRAM-first overview for quick capacity checks
- live CPU, RAM, swap, disk, network, and temperature cards
- Ollama running models with CPU %, GPU %, context, context limit, and size
- installed Ollama models with architecture and quantization
- llama.cpp process detection with model path and context hints
- top resource-consuming processes
- history charts for recent pressure and speed changes
- a gear menu for appearance settings
- restore defaults for theme and layout
- draggable and resizable widgets
- warning, critical, and info analysis badges

Example payload
{
"timestamp": "2026-07-17T12:00:00.000Z",
"cpu": { "usage": 32, "cores": 8, "threads": 16, "frequencyMhz": 4300 },
"ram": {
"usedBytes": 123456789,
"totalBytes": 17179869184,
"usagePercent": 41.2
},
"swap": { "usedBytes": 0, "totalBytes": 34359738368, "usagePercent": 0 },
"gpu": {
"vendor": "AMD",
"model": "Radeon RX 580",
"usagePercent": 91,
"temperatureCelsius": 72,
"vram": {
"usedBytes": 7488270336,
"totalBytes": 8589934592,
"freeBytes": 1101664256
}
},
"ollama": {
"running": [
{
"name": "midnightcoderagent/MidnightCoder-30B:latest",
"cpuPercent": 59,
"gpuPercent": 41,
"context": 32768,
"contextLength": 262144,
"quantization": "IQ2_M",
"architecture": "qwen3moe"
}
],
"installed": []
},
"llamacpp": { "running": [] },
"analysis": [
{
"severity": "warning",
"source": "gpu.vram",
"message": "VRAM almost full. Model may spill into RAM."
}
]
}JSON Schemas
CpuMetrics
{
"usage": "number",
"cores": "number",
"threads": "number",
"loadAverage": "[number, number, number]",
"frequencyMhz": "number",
"uptimeSeconds": "number"
}GpuMetrics
{
"vendor": "string",
"model": "string",
"usagePercent": "number | null",
"temperatureCelsius": "number | null",
"vram": {
"totalBytes": "number | null",
"usedBytes": "number | null",
"freeBytes": "number | null"
}
}Architecture
flowchart LR
C[Collectors] --> M[Monitor Scheduler]
M --> H[History Store]
M --> A[Analysis Engine]
M --> R[REST API]
M --> W[WebSocket Stream]
P[Plugin Packages] --> CCollector plugins
Collectors are discovered from the built-in src/collectors/ directory and can also be loaded from installed npm packages.
Each collector implements:
initialize(context);
collect(context);
health();
dispose();Third-party packages can export a createCollector-style factory and be enabled in midnight-monitor.config.json.
Contributing
- Fork or branch.
- Run
npm install. - Use
npm run typecheck,npm run lint, andnpm test. - Keep collectors isolated and failure-tolerant.
- Include tests for parsing and analysis changes.
- If a change needs a new dependency, add it to
package.jsonand update the lockfile before opening a PR.
