feat(health): System Health in three groups, a score that sees lag; admin user detail

Dashboard → System Health is grouped into AgentHub, Machine and Agent
activity. The machine (GET /api/host, every signed-in user) is drawn by
components/health/HostHealth.tsx: host card beside the CPU/memory history,
then CPU (threads as compact cells + temperatures/GPUs), disks & network,
busiest processes, and per-user tiles. Concurrency bars each show their own
agent type on hover.

The Health Score (client/src/lib/healthScore.ts) is now 100 minus what each
check costs, between an amber and a red threshold: event-loop p99
(server/lib/loop-delay.js, shared with /api/metrics), Linux PSI pressure for
CPU, memory and disk I/O (/api/host `pressure`; load per core without PSI),
RAM, the database's disk, V8 heap, hooks silent while agents work, and agent
errors in the last 24 h. All-time success rate, API errors and the transcript
cache hit rate no longer count. /api/settings/info adds server.event_loop and
activity {last_event_at, working_agents, agents_24h}.

Administration: a user row opens its detail in place (GET
/api/users/:id/details — spend, account pool share, live panes, workspaces,
newest sessions); the server panel is gone, member tmux folded into the table;
the Sessions list shows each row's owner in server mode.
This commit is contained in:
2026-10-27 16:39:38 +07:00
parent df15789982
commit 1c6dfea5b5
49 changed files with 5154 additions and 1971 deletions
+26 -3
View File
@@ -1,6 +1,6 @@
/**
* @file OpenAPI fragment for the Artifacts hub (`/api/artifacts/*`, server/routes/artifacts.js)
* and the Administration page's Server tab (`GET /api/host`, server/routes/host.js).
* and the Dashboard System Health tab's machine cards (`GET /api/host` and `/api/host/history`, server/routes/host.js).
* Hand-written from the routes; docs/API.md § Artifacts hub is the long form.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
@@ -14,7 +14,7 @@ const tags = [
{
name: "Host",
description:
"Administration → Server: the machine's hardware, load, disks and each user's share of CPU and memory. Read-only, staff only in server mode.",
"Dashboard → System Health: the machine's hardware, load, disks and each user's share of CPU and memory. Read-only, every signed-in user.",
},
];
@@ -385,13 +385,36 @@ const paths = {
responses: {
200: {
description:
"`{at, hostname, os, uptime, cpu, memory, disks, homesMount, gpus, usage}` — see docs/API.md.",
"`{at, hostname, os, uptime, bootTime, cpu (+physicalCores, perCore), memory, disks, homesMount, gpus, usage, network, diskIo, temperatures, pressure, topProcesses, process}` — see docs/API.md.",
content: { "application/json": { schema: { type: "object" } } },
},
500: { description: "EHOST" },
},
},
},
"/api/host/history": {
get: {
tags: ["Host"],
summary: "The machine's CPU/memory history since the server started",
operationId: "getHostHistory",
description:
"In memory only: one point every 30 s for 24 h, kept by a server-mode server on Linux (empty otherwise, `running: false`). Averaged down to at most 360 points. Server mode: admin/analyst only.",
parameters: [
{
name: "range",
in: "query",
schema: { type: "string", enum: ["1h", "6h", "24h"], default: "1h" },
},
],
responses: {
200: {
description:
"`{range, intervalMs, running, startedAt, points: [{at, cpu, memory, memoryUsed, swapUsed, load1, users: {<userId>: {cpu, memory}}}]}`.",
content: { "application/json": { schema: { type: "object" } } },
},
},
},
},
};
module.exports = { tags, schemas, paths };
+22
View File
@@ -565,6 +565,28 @@ const paths = {
},
},
},
"/api/users/{id}/details": {
get: {
tags: ["Auth"],
operationId: "getServerUserDetails",
summary:
"One account in full for the Administration page (admin/analyst): sign-ins, session counts, spend today/7d/30d, per local day (`tz_offset`), per AI tool and per model, its workspaces and live panes, newest sessions, the audit entries naming it, and per pool tool its pin, group and the accounts it may run on — each account's quota beside this user's own 5 h / 7 d share. Never a token",
parameters: [
idParam,
{
name: "tz_offset",
in: "query",
schema: { type: "integer" },
description: "The browser's getTimezoneOffset(), for the daily buckets",
},
],
responses: {
200: { description: "Account details" },
403: err("EADMIN — a developer"),
404: err("ENOUSER"),
},
},
},
"/api/users/{id}": {
get: {
tags: ["Auth"],