Sensors: read-only tokens, live stream, Prometheus, status sensors, widgets, embeds

- sensors:read tokens open the sensor endpoints (here and on managed machines
  through the hub) and nothing else; full tokens are never accepted from a URL
- /api/sensors/stream: a shaped query that keeps answering, over SSE
- /metrics: Prometheus text format
- services, updates, ZFS and SMART as background status sensors
- widget library over the sensor API; custom widgets on the dashboard
- /embed/widget: one live widget in its own bundle, the only frameable path
- Dashboard tab is now Monitors

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-19 07:37:10 -07:00
co-authored by Claude Fable 5.1
parent 468f45251d
commit b05a053920
20 changed files with 950 additions and 56 deletions
+2 -2
View File
@@ -27,7 +27,7 @@ const FolderIcon = (
)
const GROUPS: Group[] = [
{ id: 'dashboard', label: 'Dashboard', icon: '◔' },
{ id: 'dashboard', label: 'Monitors', icon: '◔' },
{ id: 'procs', label: 'Processes', icon: '≡' },
{ id: 'terminal', label: 'Terminal', icon: '>_' },
{ id: 'services', label: 'Services', icon: '◈' },
@@ -207,7 +207,7 @@ export default function Shell({ me: hubMe, onLoggedOut }: { me: Me; onLoggedOut:
<span>{machine ? `${machine} · ` : ''}{me.version.startsWith('v') ? me.version : 'v' + me.version}</span>
<span>up {fmtUptime(me.uptime)}</span>
<span className="spacer" />
<span className="muted">{view === 'dashboard' ? 'Dashboard' : view === 'services' ? 'Services' : view === 'cron' ? 'Cron' : view === 'updates' ? 'Updates' : view === 'disks' ? 'Disks' : view === 'files' ? 'Files' : view === 'procs' ? 'Processes' : view === 'machines' ? 'Machines' : view === 'settings' ? 'Settings' : activeTerm ? activeTerm.name : ''}</span>
<span className="muted">{view === 'dashboard' ? 'Monitors' : view === 'services' ? 'Services' : view === 'cron' ? 'Cron' : view === 'updates' ? 'Updates' : view === 'disks' ? 'Disks' : view === 'files' ? 'Files' : view === 'procs' ? 'Processes' : view === 'machines' ? 'Machines' : view === 'settings' ? 'Settings' : activeTerm ? activeTerm.name : ''}</span>
</footer>
</div>
)