Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

sparkDash ⚡ — Multi-unit monitoring dashboard for NVIDIA DGX Spark

Platform: ARM64 React 19 Express 5 MIT License
by Mia'a AI Lab

Follow Mia on X

sparkDash is a real-time web dashboard for one or more NVIDIA DGX Spark (GB10) machines in a single browser window. It streams GPU, CPU, unified memory, storage, network, and local LLM metrics — and lets you add, edit, reorder, or remove Sparks from the UI without restarts or code changes.

It also supports non-Spark units: any Linux machine with an NVIDIA GPU (e.g. a workstation with a dedicated RTX/L-series card) can be added as a dedicated GPU host and monitored the same way via SSH and nvidia-smi. For these units the dashboard correctly separates RAM (system memory) from VRAM (discrete GPU memory).

sparkDash Overview page with multiple DGX Spark units, GPU metrics, and LLM status

LLM Prompt Showcase

LLM Prompt Showcase — multi-terminal streaming demo (click for MP4)

Download MP4 · also in assets/llm-showcase.mp4


Table of contents


Latest version changelog

Version 1.8.6 — prefill benchmark

  • Prefill benchmark on the LLM card: sweep context sizes from 1k to 300k, report prefill tok/s (prompt_tokens ÷ TTFT) and TTFT. Unique prefix per size so prefix-cache does not inflate later runs. Timeouts scale with size (up to 45 min).
  • CPU temperature on remote Sparks (Overview bar + GPU panel row when above 0°C).

Full history: CHANGELOG.md


Features

AreaWhat you get
Multi-unitAny number of units; each has a tabbed detail page plus a shared Overview
Non-Spark GPU hostsLinux boxes with a dedicated NVIDIA GPU are first-class units: same nvidia-smi collectors over SSH, detected hardware summary, and separate RAM / VRAM panels. Detail page: GPU (left) + RAM → Network → Storage (right column); Overview cards show RAM and VRAM bars
Live streamingWebSocket metrics with configurable poll intervals; central history store for sparklines across tab switches
Local + remoteHost metrics via sysfs/proc/nvidia-smi; remotes over SSH (key or password)
LLM probeAuto-detects llama.cpp, vLLM, sglang, ds4-server, or EXL3; live decode/prefill tok/s; cached vs uncached prefill on ds4, llama.cpp, and SGLang; daily peak history on the LLM card
ComfyUIOpt-in probe: queue/jobs, progress, cancel, Open link, inventory, overview chip
Hermes AgentOpt-in per unit: background update check (10 min), status badges, one-click or batch hermes update
TailnetOpt-in probe: flags a unit that is healthy on the LAN but off its tailnet
Decode benchmarkMulti-concurrency streaming decode tok/s; type picker (Structured / Prose / Code / JSON); lab protocol (temp 0, thinking off); persisted last run
Prefill benchmarkContext-size sweep (1k–300k) of prefill tok/s and TTFT; unique prefix per size; persisted last run
Prompt ShowcaseFull-page multi-terminal LLM streaming demo (up to 32 prompts) with live tok/s and copy-out
vLLM healthKV cache %, run/wait queue, TTFT/E2E/ITL p95, preemptions, prefix cache, MTP accept from Prometheus /metrics
Multiple LLM portsMonitor several LLM servers on different ports simultaneously — each gets its own panel with independent backend detection and metrics
GPU processesSee the top GPU processes by VRAM usage directly in the GPU panel, including process name and memory allocation
Spark uptimeSystem uptime displayed inline on each Spark header for at-a-glance availability
Power controlsGraceful shutdown (SSH host script) and Wake-on-LAN; batch actions on Overview
Spark rolesHead / Worker / Standalone — worker label + head link; standalone can disable LLM monitoring
Unified memoryGB10 128 GB LPDDR5X pool (~273 GB/s), GPU/CPU split, bandwidth via nvidia-smi dmon. Non-Spark hosts show discrete VRAM (nvidia-smi) and system RAM separately
ThemesDark, light, cool white, OLED — neutral palettes, persisted in localStorage
SecretsSSH passwords AES-256-GCM encrypted; never in sparks.json or API responses
Docker-firstSingle privileged container for host metrics; prod and dev Compose files
Hot configAdd / edit / remove / reorder Sparks from the UI with no process restart

ComfyUI monitoring

sparkDash can optionally monitor a ComfyUI instance on each Spark — the same way it probes local LLMs, but focused on jobs and queue, not a second copy of GPU/RAM bars (those stay on the GPU / CPU panels).

What is supported

CapabilityDetails
Opt-in per SparkcomfyMonitoring (default off) + comfyPort (default 8188)
Any roleHead, worker, and standalone can enable ComfyUI independently of LLM cluster role
LivenessGET /system_stats — online, ComfyUI / PyTorch version, device type (cpu/cuda)
Queue / jobsGET /queue — running + pending items; workflow title, model/LoRA filenames from the graph, footprint (resolution · steps · sampler · batch · node count)
ProgressProgress bar on the active job — Comfy WebSocket when events are available; otherwise elapsed / average-duration estimate
Last finished jobStatus + duration via /api/jobs (fallback /history)
Queue ETAEstimate from recent job durations × pending (+ progress remainder when known)
Cancel / removeFrom the Comfy card: interrupt a running job or dequeue a pending one (POST /api/sparks/:id/comfy/cancel)
Open ComfyUIOne-click link to http://{lanIp}:{comfyPort} (LAN IP preferred so remote browsers do not hit localhost)
Model inventoryCheckpoints + LoRAs from /models/* (UI section only when at least one file is listed)
Overview chipWhen monitoring is on: Comfy · idle / run / Nq / muted if unreachable
LayoutUnder Services: primary LLM + Comfy side-by-side when both are enabled; collapsible Resources / Services sections

Not claimed: true per-job VRAM (Comfy does not expose that cleanly over HTTP). Host GPU/VRAM remains on the GPU panel. Live step progress depends on Comfy broadcasting WS events; stock Comfy often scopes detailed progress to the client that submitted the prompt.

How to enable (per Spark)

  1. Open the Spark tab → Edit (pencil).
  2. Enable ComfyUI monitoring.
  3. Set port if needed (default 8188).
  4. Save.

The Spark page Services section shows the ComfyUI card. On Overview, a small Comfy chip appears for that unit.

Connectivity Test (in Edit) includes ComfyUI when monitoring is enabled.

ComfyUI side requirements

  • ComfyUI must be reachable from the sparkDash server on the probe host:
    • Local Spark (isLocal): sparkDash probes 127.0.0.1:{port} (use Docker network_mode: host if the dashboard runs in a container).
    • Remote Spark: probe uses the Spark LAN IP (same as LLM probes).
  • For Open from another machine’s browser, Comfy should listen on a reachable interface (e.g. --listen 0.0.0.0), not only loopback, and the Spark’s LAN IP must be set correctly in Edit.

Config fields (persisted on the Spark)

FieldDefaultDescription
comfyMonitoringfalseProbe ComfyUI and show the card / overview chip
comfyPort8188ComfyUI HTTP port

Related API

MethodPathPurpose
POST/api/sparks/:id/comfy/cancelCancel a job ({ "promptId": "<uuid>" }) — interrupt running and/or remove from queue

Env (optional): COMFY_PORT (default 8188), COMFY_PROBE_TIMEOUT_MS, POLL_INTERVAL_COMFY.


Hermes Agent monitoring

sparkDash can optionally monitor Hermes Agent (nousresearch/hermes-agent) on each unit and run one-click updates for you over SSH.

What is supported

CapabilityDetails
Opt-in per SparkhermesMonitoring (default off) in Edit Spark
Auto update checkBackground hermes update --check over SSH (default every 10 min) — returns update availability + pending commits
Status badgesIn the Spark header: Hermes (installed version), Hermes not found if the binary is missing
One-click updateUpdate Hermes button opens a dialog with live status, real pending commits, and release notes; Update now runs hermes update via SSH (non-interactive go)
Update stateRunning / success / error surfaced live (button turns into a “Hermes updating… / failed” state)
Batch updateUpdate Hermes on Overview runs hermes update on every monitored unit, with a live per-unit progress bar

How to enable (per Spark)

  1. Open the Spark tab → Edit (pencil).
  2. Enable Hermes Agent.
  3. Save — background checks start immediately.

The Update Hermes button appears in the Spark header/mobile action row; it turns warning-yellow with a commit-count badge only when an update is actually available. It also appears on Overview (batch) when at least one unit has Hermes enabled.

Connectivity check note: local units run the check as the host user (via setpriv/nsenter, never as container root); remote units run it over SSH. Either way, the logged-in user needs permission to read the Hermes repo.

Side requirements

  • Hermes Agent must be installed on the target machine — sparkDash only checks & updates; it does not install it. The binary is looked up in ~/.local/bin and /usr/local/bin.
  • SSH user must be able to run hermes update --check / hermes update non-interactively (key auth recommended).
  • An update can take a few minutes (repo pull + dependency reinstall); a stale *.lock file from a crashed run is cleared before each attempt.

Config fields (persisted on the Spark)

FieldDefaultDescription
hermesMonitoringfalseCheck/update Hermes Agent on this machine

Related API

MethodPathPurpose
POST/api/sparks/hermes/update-allBatch hermes update on every monitored Spark (Overview button)
POST/api/sparks/:id/hermes/checkForce hermes update --check now
POST/api/sparks/:id/hermes/updateRun hermes update in the background (202)
GET/api/sparks/:id/hermes/updatesUpdate preview: latest release + installed version + real pending commits + resolved view

Env (optional): POLL_INTERVAL_HERMES (default 600000 ms), HERMES_UPDATE_TIMEOUT_MS (default 600000 ms).


Tailnet monitoring

Opt-in per unit (default off). Runs tailscale status --json on the host and shows a Tailnet card under Resources.

This closes a blind spot every LAN-based check shares, including sparkDash's own SSH liveness. When tailscaled loses its session with the coordination server, SSH/GPU/LLM can all stay healthy while the box is unreachable from off-LAN.

What is supported

CapabilityDetails
Opt-in per unittailscaleMonitoring (default off) in Edit Spark
Off-tailnet detectionSelf.Online — the node's own view of the coordination server
Reason, not just stateTailscale Health messages, backend state, tailnet IP, DERP relay, version, expired-key warning

Asked of each node about itself. Peer state is never the verdict. The probe is read-only (tailscale up / down / login are never run).

How to enable

  1. Open Edit Spark.
  2. Tick Tailnet monitoring.
  3. Save. The Tailnet card appears under Resources.

Host requirements

  • tailscale CLI on the monitored host, and tailscaled running.
  • Remote units: existing SSH. Local Docker: nsenter into the host mount namespace (same as nvidia-smi; /host/proc is already bind-mounted).

Config fields

FieldDefaultDescription
tailscaleMonitoringfalseRun tailscale status --json and show the Tailnet card

Env (optional): POLL_INTERVAL_TAILSCALE (default 30000), TAILSCALE_PROBE_TIMEOUT_MS (default 8000).


Quick start

git clone https://github.com/MiaAI-Lab/sparkDash.git
cd sparkDash

# Production (Docker)
docker compose up --build -d

# Or development (host, with hot reload)
npm install
npm run dev
  • Docker: open http://<host-ip>:5555 (arm64 image, auto-restart, host mounts for GPU/metrics access)
  • Dev: Vite on http://localhost:5173 (proxies API/WS to Express)

For development with Docker (source-mounted, HMR):

docker compose -f docker-compose.dev.yml up --build

Remote units + SSH keys (Docker): SSH is executed inside the container on the sparkDash host (typically the head DGX). Configured LAN IPs are from that host’s point of view, not your laptop. OpenSSH looks for keys under /root/.ssh in the container — the host user’s ~/.ssh is not used unless you bind-mount it. Uncomment this volume in docker-compose.yml (and recreate the container):

- ${HOME}/.ssh/id_ed25519:/root/.ssh/id_ed25519:ro

If the key file has a non-default name (e.g. id_ed25519_shared), mount it as id_ed25519, or set SSH_IDENTITY_FILE to the path inside the container. Keep the file mode 600. The unit that runs sparkDash itself should be added with This host (local collectors — no SSH for metrics).


Architecture

Design principle: one Spark model, N instances. Every unit is a record in config/sparks.json with a kind field (spark or host). The same SparkMonitor, SystemCollector, and LlmProbe code runs for all of them. Adding a unit is a config change, not a code change.

┌────────────────────── Docker container (sparkDash) ────────────────────────┐
│  Express (server/)                                                         │
│  ├─ config/sparks.json        Spark registry (API read/write)              │
│  ├─ SparkRegistry             load/persist Sparks; change events           │
│  ├─ SparkMonitor (per Spark)  collector + LLM probe + rate baselines       │
│  │   ├─ SystemCollector       local sysfs/proc OR remote SSH               │
│  │   └─ LlmProbe              HTTP to host:LLM_PORT, backend autodetect    │
│  ├─ REST /api/*                                                            │
│  └─ WebSocket /ws             snapshot stream to browsers                  │
│  React SPA (src/)  — Overview + per-Spark pages, themes, dialogs           │
└────────────────────────────────────────────────────────────────────────────┘
         │ SSH (key or sshpass)                    │ HTTP :8888
         ▼                                         ▼
    remote Spark(s)                         each Spark’s LLM server

Data flow

Browser  ←→  WebSocket /ws   ←→  SparkMonitor.snapshot()  ←→  collectors
Browser  ←→  REST /api/*     ←→  SparkRegistry + SparkMonitor

Poll loops run in the background (even with no clients) so rate metrics — tokens/s, network bytes/s, disk I/O — stay correct.


Tech stack

LayerStack
FrontendReact 19, TypeScript, Vite 8, Tailwind CSS v4
BackendNode.js (ESM), Express 5, ws
PlatformARM64 — DGX Spark GB10 (Neoverse V2)
DeployDocker multi-stage (arm64), Compose
SecretsAES-256-GCM SSH password store
Ports5555 dashboard/API; 5173 Vite (dev only)

Repository layout

sparkDash/
├── src/                 React + TypeScript SPA
│   ├── api/             REST client + shared types
│   ├── components/      Overview, Spark pages, dialogs, UI primitives
│   ├── hooks/           WebSocket snapshot, routing
│   └── theme / CSS      Tailwind v4 + four themes
├── server/              Express + WebSocket (plain JS ESM)
│   ├── sparks/          SparkRegistry, SparkMonitor
│   ├── collectors/      SystemCollector, LlmProbe, ssh
│   ├── secretsStore.js  Encrypted password persistence
│   └── validate.js      Host/user validation (SSRF-minded)
├── config/              Runtime state (volume; secrets gitignored)
├── assets/              Screenshots
├── Dockerfile           Production multi-stage arm64
├── docker-compose.yml   Production
├── docker-compose.dev.yml
└── deploy.sh            Rebuild / recreate helpers

REST API

MethodPathPurpose
GET/api/sparksList Sparks (passwords redacted)
POST/api/sparksAdd Spark and start its monitor
PATCH/api/sparks/:idUpdate Spark (hot-swap config)
DELETE/api/sparks/:idRemove Spark and drain monitor
PUT/api/sparks/orderPersist tab order
GET/api/sparks/:id/metricsOne-shot metrics snapshot
POST/api/sparks/testEphemeral SSH + LLM (+ Comfy if enabled) test (no persist)
POST/api/sparks/:id/testConnectivity test (can save password)
POST/api/sparks/:id/comfy/cancelCancel ComfyUI job by promptId
PUT/api/sparks/:id/passwordSave SSH password (works offline)
PUT/api/sparks/:id/disabled-devicesHide storage devices (hot)
PUT/api/sparks/:id/disabled-interfacesHide network interfaces (hot)
PUT/api/sparks/:id/llm-portsReplace all LLM ports (hot)
POST/api/sparks/:id/llm-portsAdd an LLM port (hot)
DELETE/api/sparks/:id/llm-ports/:portRemove an LLM port (hot)
PUT/api/sparks/:id/llm-portLLM port — backward-compat (hot)
GET/api/sparks/:id/llm/dailyDaily busy decode/prefill tok/s (port, days)
POST/api/sparks/:id/llm/benchStart decode benchmark (202); poll/cancel/clear on the same path
POST/api/sparks/:id/llm/prefill-benchStart prefill + TTFT context sweep (202); poll/cancel/clear on the same path
GET/api/settingsGlobal settings
PUT/api/settingsUpdate global settings
WS/wsReal-time metrics stream

There is no authentication on the HTTP/WebSocket API. Run sparkDash only on a trusted network (or behind your own reverse proxy with auth).


Configuration

Global settings (UI or API)

Gear icon in the header, or GET/PUT /api/settings:

SettingDefaultDescription
Poll interval2000 msWebSocket broadcast interval (minimum 1000 ms)
Default LLM port8888Default for new Sparks
Auto-hide offlinefalseHide offline Sparks on Overview
Temperature unitCelsiusDisplay GPU temperature in °C or °F

Environment variables

Copy .env.example to .env if needed:

VariableDefaultDescription
BIND_HOST127.0.0.1HTTP and WebSocket listen address. Loopback by default — the dashboard exposes SSH + power controls, so set a LAN IP (or 0.0.0.0) to allow remote access.
PORT5555HTTP + WebSocket listen port
LLM_PORT8888Default LLM probe port
COMFY_PORT8188Default ComfyUI probe port
POLL_INTERVAL_GPU2000GPU poll (ms)
POLL_INTERVAL_COMFY2000ComfyUI probe poll (ms)
POLL_INTERVAL_CPU2000CPU / RAM poll (ms)
POLL_INTERVAL_NETWORK2000Network poll (ms)
POLL_INTERVAL_STORAGE5000Storage poll (ms)
POLL_INTERVAL_LLM2000LLM probe poll (ms)
POLL_INTERVAL_BANDWIDTH2000Memory bandwidth / dmon poll (ms)
POLL_INTERVAL_HERMES600000Hermes Agent update check poll (ms)
POLL_INTERVAL_TAILSCALE30000Tailnet probe poll (ms)
TAILSCALE_PROBE_TIMEOUT_MS8000Timeout for tailscale status --json (ms)
HERMES_UPDATE_TIMEOUT_MS600000Hard timeout for running hermes update over SSH (ms)
POLL_INTERVAL_LIVENESS5000Online/SSH liveness check (ms)
SPARKDASH_SECRETS_KEY(auto)Passphrase or 64-char hex for secret encryption
HOST_PROC_PATH/host/procHost proc mount inside container
HOST_SYS_PATH/host/sysHost sys mount
HOST_ROOT_PATH/host/rootHost root mount
SSH_IDENTITY_FILE(unset)Path inside the process to a private key (ssh -i). Use when the bind-mount is not a default OpenSSH name.

The listener defaults to 127.0.0.1 (loopback) so the dashboard — which can SSH into and power off your Sparks — isn't reachable on the LAN by default. Set BIND_HOST to the host's LAN IP (or 0.0.0.0) to reach it from another machine. The provided docker-compose.yml (network_mode: host) sets BIND_HOST=0.0.0.0 explicitly (prod and docker-compose.dev.yml); restrict access at the network layer, or set 127.0.0.1 when running behind a reverse proxy.

Adding a unit

  1. Open the + tab.
  2. Choose Unit type:
    • NVIDIA DGX Spark — the default; hardware summary shows DGX Spark specs and the CX7 IP field is available.
    • Dedicated GPU host — any Linux machine with an NVIDIA GPU. It is monitored exactly like a Spark (SSH + nvidia-smi) but is not reported as a DGX Spark: the header shows a detected hardware summary (GPU model, CPU, RAM) instead of fixed GB10 specs, and the page shows separate RAM and VRAM panels (VRAM from nvidia-smi, RAM from system memory). On the unit page, RAM → Network → Storage stack in the right column with GPU filling the left column.
  3. Set Name, LAN IP (required), optional CX7 IP (Sparks only), SSH user, and auth (key or password). LAN IP is probed from the sparkDash host. Key auth in Docker needs a key mounted into the container (see Quick start). Wake-on-LAN MAC is auto-read from enP7s7 when online (optional override in Edit).
  4. Test Connection for SSH + LLM reachability.
  5. Save — a tab appears and metrics start streaming.

Power controls (shutdown / Wake-on-LAN)

  • Shutdown (per Spark or Shutdown All on Overview) runs over SSH:
    sudo -n /usr/local/bin/spark-shutdown
    Install that script on each Spark and allow passwordless sudo for it only.
  • Wake / Wake All send a UDP magic packet (port 9). The MAC is taken from the enP7s7 interface automatically while the Spark is online (persisted as detectedMacAddress). Optionally set a MAC override in Edit Spark. Broadcast is derived as /24 from LAN IP, or 255.255.255.255 if LAN IP is missing.
  • Batch shutdown only targets online Sparks; offline nodes are skipped.
  • Same trust model as the rest of the API: do not expose port 5555 beyond a trusted network — power actions are not separately authenticated.

Themes

Header theme control cycles:

ThemeNotes
Dark (default)Neutral grays, true black base, muted amber accent
LightWarm paper whites
WhiteCool neutral whites
OLEDTrue black for OLED panels

Choice is stored in localStorage.


Security

  • SSH passwords are not stored in sparks.json and are never returned by the API.
  • Passwords are encrypted with AES-256-GCM in config/sparks-secrets.json (survives restarts).
  • Encryption key: config/.secrets-key (auto-generated) or SPARKDASH_SECRETS_KEY. Do not delete the key file or encrypted secrets become unreadable.
  • Target validation rejects clearly unsafe IPv4 targets (link-local 169.254.0.0/16, 0.0.0.0/8, multicast/reserved ≥ 224). Private, loopback, and public addresses are allowed so LAN and remote Sparks work.
  • SSH and HTTP probes use short timeouts (about 5 s SSH connect, 3 s HTTP) so a hung host cannot stall the poll loop.
  • Prefer SSH keys over passwords. In Docker, mount the private key into /root/.ssh (see Quick start); passwords are the only SSH secret the app stores itself.
  • Treat the dashboard as LAN-trusted: the API is intentionally unauthenticated for ease of use on a private network. That includes power APIs (shutdown / Wake-on-LAN): anyone who can reach the dashboard can request fleet power actions.

Scripts

CommandPurpose
npm run devVite (5173) + Express (5555) together
npm run dev:serverExpress only (node --watch)
npm run dev:clientVite only
npm run buildProduction frontend → dist/
npm run typechecktsc --noEmit
npm startProduction server (node server/index.js)
npm run docker:updocker compose up -d
npm run docker:prodSame as docker:up
npm run docker:rebuilddocker compose up --build -d
npm run docker:devDev Compose
npm run docker:dev:buildDev Compose with rebuild
./deploy.shRecreate container; --build, --frontend flags

How it works

Local vs remote Sparks

One SystemCollector path for both modes. When spark.isLocal is true, metrics come from host sysfs/proc and nvidia-smi (often via nsenter into the host namespace). Remote Sparks wrap the same commands in a shared sshExec() helper (key agent or sshpass). For kind: "host" units, actual hardware (GPU model, driver version, CPU, RAM) is detected once and cached in place of the static DGX Spark specs, and GPU VRAM comes straight from nvidia-smi while system RAM is read from /proc/meminfo.

Graceful degradation

Collectors catch errors and return zero/default metrics instead of crashing the loop. After sustained liveness failures, a Spark is marked offline; the UI shows stale or empty states rather than hard errors.

Hot configuration

Name, IP, SSH credentials, LLM port, and device/interface filters update the running SparkMonitor without tearing down poll loops or losing rate baselines. Registry writes are atomic (temp file + rename).

LLM probe

Each configured LLM port gets its own LlmProbe instance running in parallel. Probes auto-detect backends:

  • llama.cpp/slots for live decode rates; model from /props
  • ds4-server (Entrpi/ds4-on-spark) — /v1/models (owned_by: ds4.c) + Prometheus ds4_* token counters for live tok/s
  • vLLM / sglang/v1/models; sglang via /server_info (last_gen_throughput when metrics off; /get_server_info fallback), vLLM via Prometheus /metrics counters (scientific notation supported)

Rates are derived from per-probe cumulative counter diffs (or SGLang sticky throughput while it moves). Multiple ports can be added or removed at runtime without restarting the monitor.


Contributing

Contributions are welcome. Conventions:

  • Server: plain JavaScript ESM
  • Client: TypeScript + React
  • Prefer extending the shared Spark model over per-unit special cases

License

MIT — Copyright (c) 2026 Mia'a AI Lab


Acknowledgements

  • Built for the NVIDIA DGX Spark (GB10) on ARM64
  • Rebuilt from a legacy multi-unit dashboard with a single shared Spark model (no copy-pasted “Spark N” code paths)
  • LLM probe behavior refined from production monitoring experience

关于 About

sparkDash ⚡ — Multi-DGX Spark Monitoring Dashboard

语言 Languages

JavaScript53.0%
TypeScript40.2%
CSS6.0%
Shell0.5%
Dockerfile0.2%
HTML0.1%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
156
Total Commits
峰值: 95次/周
Less
More

核心贡献者 Contributors