Public: anyone can read this; the crew can change it.
/system groups crew:<name> processes as the crew kind #2814
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/2811-system-groups-crew-name-processes-as-the → main 2 commits fork
3 files
changed +19
−4
src/web/pages/system.ts +2 −2
| @@ -42,8 +42,8 @@ const meterRow = (name: string, value: number | null, max: number, figure: strin | ||
| 42 | 42 | <meter class="bt-bar" aria-label="${name} ${figure}" min="0" max="${String(max)}" high="${String(max * 0.85)}" value="${String(value ?? 0)}">${figure}</meter> |
| 43 | 43 | <span class="bt-val">${figure}</span>${history ? html`<span class="bt-hist" aria-hidden="true">${history}</span>` : null}</div>`; |
| 44 | 44 | |
| 45 | /** Processes grouped forge, check, app, host; the client re-sorts them by CPU or memory (`system-live.ts`). */ | |
| 46 | const KIND_ORDER = ["forge", "check", "app", "host"] as const; | |
| 45 | /** Processes grouped forge, crew, check, app, host; the client re-sorts them by CPU or memory (`system-live.ts`). */ | |
| 46 | const KIND_ORDER = ["forge", "crew", "check", "app", "host"] as const; | |
| 47 | 47 | function procTable(v: SystemView): Html { |
| 48 | 48 | const groups = KIND_ORDER.flatMap((k) => v.groups.filter((g) => g.kind === k)); |
| 49 | 49 | return html`<div class="table-wrap"><table class="table bt-procs" aria-label="Processes by what launched them"> |
src/web/system-live.ts +1 −1
| @@ -24,7 +24,7 @@ function rank(items, key, kinds) { | ||
| 24 | 24 | export const systemLiveJs = /* js */ ` |
| 25 | 25 | (function () { |
| 26 | 26 | ${procSortJs} |
| 27 | var KINDS = ['forge', 'check', 'app', 'host'], key = 'cpu'; | |
| 27 | var KINDS = ['forge', 'crew', 'check', 'app', 'host'], key = 'cpu'; | |
| 28 | 28 | function read(el) { |
| 29 | 29 | return { el: el, kind: el.getAttribute('data-kind'), cpu: Number(el.getAttribute('data-cpu')) || 0, mem: Number(el.getAttribute('data-mem')) || 0 }; |
| 30 | 30 | } |
test/system-btop.test.ts +16 −1
| @@ -9,7 +9,7 @@ import { tmpdir } from "node:os"; | ||
| 9 | 9 | import { join } from "node:path"; |
| 10 | 10 | import { createLive } from "../src/system/live.ts"; |
| 11 | 11 | import { parseDiskstats, type Proc, type Raw } from "../src/system/proc.ts"; |
| 12 | import { buildSystem } from "../src/system/view.ts"; | |
| 12 | import { buildSystem, forAgent } from "../src/system/view.ts"; | |
| 13 | 13 | import { regions } from "../src/web/pages/system.ts"; |
| 14 | 14 | import { systemLines } from "../src/tui/screens/system.ts"; |
| 15 | 15 | import { procSortJs, systemLiveJs } from "../src/web/system-live.ts"; |
| @@ -54,6 +54,21 @@ describe("/system as btop", () => { | ||
| 54 | 54 | expect(html).toMatch(/data-cpu="[\d.]+" data-mem="\d+"/); |
| 55 | 55 | }); |
| 56 | 56 | |
| 57 | test("crew:<name> is its own kind, after forge, and another agent never sees its name", () => { | |
| 58 | const r = raw(2000, 300); | |
| 59 | r.procs.push(proc(500, 1, "bwrap", 0, 1 << 20), proc(501, 500, "claude", 10, 80 << 20)); | |
| 60 | const v = buildSystem(undefined, r, [...roots.slice(0, 2), { label: "crew:atlas", root: 500 }], 1); | |
| 61 | const html = regions(v)["sys-procs"]!; | |
| 62 | const kinds = [...html.matchAll(/<tbody data-kind="(\w+)"/g)].map((m) => m[1]); | |
| 63 | expect(kinds).toEqual(["forge", "crew", "check", "app"]); | |
| 64 | expect(v.groups.find((g) => g.label === "crew:atlas")?.kind).toBe("crew"); | |
| 65 | const hidden = forAgent(v, () => false); | |
| 66 | expect(JSON.stringify(hidden)).not.toContain("atlas"); | |
| 67 | expect(hidden.groups.find((g) => g.kind === "crew")?.label).toBe("crew:private-1"); | |
| 68 | expect(hidden.groups.find((g) => g.kind === "crew")?.top).toEqual([]); | |
| 69 | expect(JSON.stringify(forAgent(v, (l) => l === "crew:atlas"))).toContain("crew:atlas"); | |
| 70 | }); | |
| 71 | ||
| 57 | 72 | test("cores, memory, swap, load, disk and network rates are drawn from the sample", () => { |
| 58 | 73 | const r = regions(view); |
| 59 | 74 | expect(r["cores"]).toContain("cpu0"); |
new above
offline