car-registry 0.38.0

File-based agent registry + lifecycle supervisor for Common Agent Runtime.
Documentation

car-registry

File-based agent registry for cross-process discovery, used by menubar / tray UIs that want to enumerate locally running agents without running a coordinating daemon.

How it works

Each agent owns one JSON file under ~/.car/registry/<name>.json. On startup it calls AgentRegistry::register, which atomically writes the entry (temp file + rename). Periodically it calls heartbeat to bump last_heartbeat_at. On shutdown — or after a panic, on next launch — unregister removes the file. UIs poll with list, optionally calling reap_stale(timeout) first to drop entries whose last heartbeat is too old.

There is no daemon, no shared lock, no IPC. The directory is the shared state; atomic writes give consistency.

API

use car_registry::{AgentEntry, AgentRegistry, AgentStatus};

let reg = AgentRegistry::new()?;             // ~/.car/registry/
reg.register(AgentEntry {
    name: "musicart".into(),
    display_name: Some("MusicArt".into()),
    dashboard_url: "http://127.0.0.1:8723".into(),
    status: AgentStatus::Running,
    port: Some(8723),
    pid: Some(std::process::id()),
    registered_at: 0,                        // 0 = now
    last_heartbeat_at: 0,
})?;

reg.heartbeat("musicart")?;
let agents = reg.list()?;
reg.reap_stale(std::time::Duration::from_secs(120))?;
reg.unregister("musicart")?;

File layout

~/.car/registry/
  musicart.json
  flyx.json
  car-host.json

Each file is a AgentEntry serialized as JSON. Filenames mirror the agent name; non-filesystem-safe characters are rejected at register time.

Why a separate crate

Agents that consume the registry don't necessarily depend on car-engine (a tray UI does not need the runtime). Splitting the registry off keeps it a thin dependency.

Tracking issue

#111 — agent registry + menubar discovery.