Fleet: node hostname/IP on register + node terminal in the infra computer
ci / gates (push) Failing after 5s
ci / rust (push) Has been skipped
ci / sandbox-k8s (push) Has been skipped
ci / frontend (push) Has been skipped
ci / e2e (push) Has been skipped

Hostname/IP:
- Daemon reports the machine's hostname (sysinfo) + primary outbound IPv4 on each
  heartbeat. migrations/0021 adds nodes.hostname/local_ip; cm-db heartbeat stores
  them; node JSON exposes them. Cards now title on the real hostname (falling back
  to name) + show the IP, instead of the "New node" placeholder. `name` stays
  user-overridable (rename).

Terminal moved into the pull-out computer (no more per-card modal):
- New infra computer app NodeTerminalApp (computer/apps/infra) — xterm bridged to
  a node's host shell over the node control channel, filling the app window
  (mirrors the agent Terminal's layout + ResizeObserver). Added "terminal" to the
  INFRA_CATALOG grid; a ?node= panel param targets a specific node (picker when
  unset). Clicking Terminal on a node card now opens the infra computer to that
  node's shell instead of a separate full-screen window. Deleted NodeTerminal.tsx.

Rebuilt + re-hosted both daemon binaries (hostname change).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-24 20:13:15 -07:00
co-authored by Claude Opus 4.8
parent cfa16751c5
commit cf6c331b02
10 changed files with 180 additions and 96 deletions
+13 -2
View File
@@ -29,6 +29,8 @@ pub struct NodeHealth {
pub struct NodeRow {
pub id: NodeId,
pub name: String,
pub hostname: Option<String>,
pub local_ip: Option<String>,
pub status: String,
pub agent_version: Option<String>,
pub tailscale_ip: Option<String>,
@@ -69,7 +71,7 @@ pub async fn auth(pool: &PgPool, token: &str) -> Result<Option<(NodeId, Workspac
}))
}
const SELECT_WITH_HEALTH: &str = "SELECT n.id, n.name, n.status, n.agent_version, n.tailscale_ip, n.last_seen, n.created_at,
const SELECT_WITH_HEALTH: &str = "SELECT n.id, n.name, n.hostname, n.local_ip, n.status, n.agent_version, n.tailscale_ip, n.last_seen, n.created_at,
h.node_id AS health_node, h.cpu_pct, h.mem_total, h.mem_used, h.mem_pressure, h.swap_used,
h.disk_total, h.disk_free, h.load1, h.load5, h.load15, h.container_count
FROM nodes n LEFT JOIN node_health h ON h.node_id = n.id";
@@ -103,22 +105,29 @@ pub async fn get(
/// Record a heartbeat: mark the node online + refresh its version/tailscale IP,
/// and upsert its latest host-health snapshot.
#[allow(clippy::too_many_arguments)]
pub async fn heartbeat(
pool: &PgPool,
id: NodeId,
agent_version: Option<&str>,
tailscale_ip: Option<&str>,
hostname: Option<&str>,
local_ip: Option<&str>,
h: &NodeHealth,
) -> Result<(), DbError> {
sqlx::query(
"UPDATE nodes SET status = 'online', last_seen = now(),
agent_version = COALESCE($2, agent_version),
tailscale_ip = COALESCE($3, tailscale_ip)
tailscale_ip = COALESCE($3, tailscale_ip),
hostname = COALESCE($4, hostname),
local_ip = COALESCE($5, local_ip)
WHERE id = $1",
)
.bind(id.as_uuid())
.bind(agent_version)
.bind(tailscale_ip)
.bind(hostname)
.bind(local_ip)
.execute(pool)
.await?;
sqlx::query(
@@ -203,6 +212,8 @@ fn map_node(r: sqlx::postgres::PgRow) -> NodeRow {
NodeRow {
id: NodeId::from(r.get::<uuid::Uuid, _>("id")),
name: r.get("name"),
hostname: r.get("hostname"),
local_ip: r.get("local_ip"),
status: r.get("status"),
agent_version: r.get("agent_version"),
tailscale_ip: r.get("tailscale_ip"),