Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ This file defines how coding agents should work in this repository.
show semantic meaning instead of raw numeric shapes; for example,
`busy_threshold=-1` is unconditional keepalive mode, not `-1%` utilization.
- Dashboard start-form `gpu_ids` validation must stay aligned with the public
session contract, including rejecting malformed or duplicate visible
ordinals before submitting to REST.
session contract, including rejecting malformed, duplicate, or over-limit
visible ordinal lists before submitting to REST.
- Dashboard source and packaged static assets must stay self-contained:
no Google Fonts, CDN, remote CSS/JS/font/image imports, or other runtime
network assets. Rebuild `src/keep_gpu/mcp/static/` after dashboard changes.
Expand Down
3 changes: 2 additions & 1 deletion docs/guides/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ The dashboard provides:

The start form follows the same numeric token rules as the CLI: use plain ASCII
digits, omit leading plus signs, and use `-1` only for the busy-threshold
unconditional mode. Duplicate GPU IDs are rejected before the form submits.
unconditional mode. Duplicate GPU IDs and selections with more than 64 entries
are rejected before the form submits.

Telemetry refresh is manual by default so an idle browser tab does not keep
probing GPU backends. Use **Refresh Now** for a one-shot update, or enable
Expand Down
3 changes: 2 additions & 1 deletion docs/guides/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ so the backend can return structured startup-wait plus release-timeout payloads.
Telemetry refresh is manual by default. The **Auto refresh** toggle enables
10-second polling while the tab is visible and pauses when the tab is hidden.
Telemetry cards show the visible ordinal to type into the start form before any
physical/vendor metadata.
physical/vendor metadata. The start form rejects duplicate GPU IDs and
selections with more than 64 entries before submitting to REST.
CUDA and ROCm devices include memory and utilization when the platform APIs are
available. Mac M series devices report best-effort MPS memory counters and use
`null` for unsupported or invalid fields such as unavailable utilization,
Expand Down
10 changes: 5 additions & 5 deletions src/keep_gpu/mcp/static/assets/dashboard.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions web/dashboard/src/lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const BUSY_THRESHOLD_PATTERN = /^-?[0-9]+$/
const NUMERIC_TOKEN_ERROR = "Use plain ASCII numbers without leading plus signs."
const BUSY_THRESHOLD_ERROR =
"Busy threshold must be -1 or an integer between 0 and 100"
const MAX_GPU_IDS = 64

function isSignedZeroInteger(value) {
return (
Expand All @@ -28,6 +29,9 @@ export function parseGpuIds(raw) {
}

const gpuIds = parts.map((part) => Number(part))
if (gpuIds.length > MAX_GPU_IDS) {
throw new Error("GPU IDs have too many items")
}
if (new Set(gpuIds).size !== gpuIds.length) {
throw new Error("GPU IDs must not contain duplicate values")
}
Expand Down
6 changes: 6 additions & 0 deletions web/dashboard/src/lib/session.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ describe("parseGpuIds", () => {
expect(() => parseGpuIds("0,1,0")).toThrow("duplicate")
})

it("throws when more than 64 visible ordinals are supplied", () => {
const gpuIds = Array.from({ length: 65 }, (_, index) => index).join(",")

expect(() => parseGpuIds(gpuIds)).toThrow("too many")
})

it("names visible ordinals in validation errors", () => {
expect(() => parseGpuIds("0,a")).toThrow(
"GPU IDs must be comma-separated visible ordinals"
Expand Down