{"id":"add-dashboard","name":"add-dashboard","summary":"NanoClawにモニタリングダッシュボードを追加してください。@nanoco/nanoclaw-dashboardと、定期的にJSONスナップショットを送信するプッシャーをインストールします。","body":"# /add-dashboard — NanoClaw Dashboard\n\nAdds a local monitoring dashboard showing agent groups, sessions, channels, users, token usage, context windows, message activity, and real-time logs.\n\n## Architecture\n\n```\nNanoClaw (pusher)              Dashboard (npm package)\n┌──────────┐    POST JSON      ┌──────────────┐\n│ collects │ ────────────────→ │ /api/ingest  │\n│ DB data  │   every 60s       │ in-memory    │\n│ tails    │ ────────────────→ │ /api/logs/   │\n│ log file │   every 2s        │   push       │\n└──────────┘                   │ serves UI    │\n                               └──────────────┘\n```\n\n## Steps\n\n### 1. Install the npm package\n\n```bash\npnpm install @nanoco/nanoclaw-dashboard\n```\n\n### 2. Copy the pusher module and its tests\n\nCopy all three resource files into `src/`. The tests ship with the skill and run against the composed project — they're how you confirm the skill works and is wired in correctly.\n\n```\n.claude/skills/add-dashboard/resources/dashboard-pusher.ts       → src/dashboard-pusher.ts\n.claude/skills/add-dashboard/resources/dashboard-pusher.test.ts  → src/dashboard-pusher.test.ts\n.claude/skills/add-dashboard/resources/dashboard-wiring.test.ts  → src/dashboard-wiring.test.ts\n```\n\n- `dashboard-pusher.test.ts` — behavior: starts the pusher, posts a real snapshot to a fake dashboard.\n- `dashboard-wiring.test.ts` — the code edit in step 3: asserts (via the TS AST) that `index.ts` dynamically imports `./dashboard-pusher.js` and `await`s `startDashboard()` as colocated statements of `main()`, after DB init and before the boot-complete log. Delete or misplace the edit and this goes red.\n\n### 3. Wire into src/index.ts\n\nThis is the skill's one integration point, and it's deliberately minimal and self-contained: all the startup logic lives in `dashboard-pusher.ts`, and the import is **colocated** with the call so the whole edit is a single block in one place — there's no separate top-of-file import to add (or to remember to remove).\n\nAdd this block inside `main()`, just before the `log.info('NanoClaw running')` line:\n\n```typescript\n  // Dashboard (optional; no-ops without DASHBOARD_SECRET)\n  const { startDashboard } = await import('./dashboard-pusher.js');\n  await startDashboard();\n```\n\n`startDashboard()` reads `DASHBOARD_SECRET`/`DASHBOARD_PORT` itself and no-ops if the secret is unset, so nothing else in core needs to change.\n\n### 4. Add environment variables to .env\n\n```\nDASHBOARD_SECRET=<generate-a-random-secret>\nDASHBOARD_PORT=3100\n```\n\nGenerate the secret: `node -e \"console.log('nc-' + require('crypto').randomBytes(16).toString('hex'))\"`\n\n### 5. Build, test, and restart\n\nRun from your NanoClaw project root:\n\n```bash\npnpm run build\npnpm exec vitest run src/dashboard-pusher.test.ts src/dashboard-wiring.test.ts   # behavior + wiring\nsource setup/lib/install-slug.sh\nsystemctl --user restart $(systemd_unit)              # Linux\n# or: launchctl kickstart -k gui/$(id -u)/$(launchd_label)  # macOS\n```\n\nRun `build` **before** the tests: it's what guards the `@nanoco/nanoclaw-dashboard` dependency. `dashboard-pusher.ts` reaches the package through `await import('@nanoco/nanoclaw-dashboard')`, so if step 4 was skipped, `pnpm run build` fails with `TS2307: Cannot find module`. The behavior test deliberately *mocks* that package — its `startDashboard` binds a real dashboard port, a side effect we don't want in a test — so the test alone would pass with the dependency missing. Build is therefore the leg that verifies the dependency is installed; keep it ahead of the tests in the validate step.\n\n### 6. Verify (runtime smoke check)\n\nOnce the service is restarted, confirm the dashboard is live:\n\n```bash\ncurl -s http://localhost:3100/api/status\ncurl -s -H \"Authorization: Bearer <secret>\" http://localhost:3100/api/overview\n```\n\nOpen `http://localhost:3100/dashboard` in a browser.\n\n## Dashboard Pages\n\n| Page | Shows |\n|------|-------|\n| Overview | Stats, token usage + cache hit rate, context windows, activity chart |\n| Agent Groups | Sessions, wirings, destinations, members, admins |\n| Sessions | Status, container state, context window usage bars |\n| Channels | Live/offline status, messaging groups, sender policies |\n| Messages | Per-session inbound/outbound messages |\n| Users | Privilege hierarchy: owner > admin > member |\n| Logs | Real-time log streaming with level filter |\n\n## Troubleshooting\n\n- **\"No data yet\"**: Wait 60s for first push, or check logs for push errors\n- **401 errors**: Verify `DASHBOARD_SECRET` matches in `.env`\n- **Port conflict**: Change `DASHBOARD_PORT` in `.env`\n- **No logs**: Check `logs/nanoclaw.log` exists\n\n## Removal\n\nReverse the apply steps. Safe to re-run even if some pieces are already gone.\n\n```bash\nrm -f src/dashboard-pusher.ts src/dashboard-pusher.test.ts src/dashboard-wiring.test.ts\npnpm uninstall @nanoco/nanoclaw-dashboard 2>/dev/null || true\n```\n\nThen, by hand, remove the single dashboard block the skill added to `main()` in `src/index.ts` (the `// Dashboard (optional…)` comment, the `await import('./dashboard-pusher.js')` line, and the `await startDashboard();` call), and remove `DASHBOARD_SECRET` and `DASHBOARD_PORT` from `.env`.\n\n```bash\npnpm run build\n```","author":"@nanocoai","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nanocoai/nanoclaw/tree/main/.claude/skills/add-dashboard","license":"MIT","category":"writing","lang":"en","tokens":1322,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"resources/dashboard-pusher.test.ts","size":4840,"sha256":"1c3d16df209e0f92da62b7385ba73d75ca45be870eb30e29395d6894c5ff4281"},{"path":"resources/dashboard-pusher.ts","size":20812,"sha256":"90e22282fb844c5b2c1b56e9719822fe3781ac321c3b8b2a1691f0a1e8679af5"},{"path":"resources/dashboard-wiring.test.ts","size":4023,"sha256":"63fea8fb0526c484ebd89c31c7b3f53a381b36dfe7a7df530e20ed9731ea9981"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":[]}}