host.sock: push a live agent-status stream instead of poll-only
This commit is contained in:
parent
2f7d3e02e9
commit
3871749da9
3 changed files with 181 additions and 27 deletions
|
|
@ -96,6 +96,38 @@ pub struct ContainerView {
|
|||
pub memory_max: String,
|
||||
}
|
||||
|
||||
impl From<ContainerView> for hive_sh4re::container::AgentStatusRow {
|
||||
/// The one place a `ContainerView` becomes the wire row `host.sock`
|
||||
/// serves it as — `handle_agent_status`'s poll path and the
|
||||
/// `SubscribeAgentStatus` push path both go through this, so the two
|
||||
/// can never quietly diverge on which fields make the cut.
|
||||
///
|
||||
/// `pending_reminders` has no source here: reminders are agent-local
|
||||
/// now, and c0re has no cross-agent visibility into pending counts
|
||||
/// anymore. Stubbed to `0` rather than dropping the wire field
|
||||
/// outright — leaves `hivectl status`/the dashboard column intact
|
||||
/// syntactically, just always empty, until iris's frontend follow-up
|
||||
/// decides whether to drop the column entirely. `port`, `cpu_quota`,
|
||||
/// `memory_max` have no equivalent on the row at all and are simply
|
||||
/// not carried over.
|
||||
fn from(v: ContainerView) -> Self {
|
||||
Self {
|
||||
name: v.name,
|
||||
running: v.running,
|
||||
failed: v.failed,
|
||||
needs_update: v.needs_update,
|
||||
needs_login: v.needs_login,
|
||||
deployed_sha: v.deployed_sha,
|
||||
pending_reminders: 0,
|
||||
parent: v.parent,
|
||||
paused: v.paused,
|
||||
active_model: v.active_model,
|
||||
status_text: v.status_text,
|
||||
status_set_at: v.status_set_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the full container list. Wraps `lifecycle::list()` and
|
||||
/// resolves every per-agent attribute the dashboard surfaces.
|
||||
///
|
||||
|
|
@ -468,4 +500,53 @@ mod tests {
|
|||
let ok = lock(r#""a":"a""#, r#""a":{"locked":{"rev":"abc"}}"#);
|
||||
assert_eq!(parse_locked_revs(&ok).len(), 1);
|
||||
}
|
||||
|
||||
/// The `From` impl both `handle_agent_status`'s poll path and
|
||||
/// `stream_agent_status`'s push path go through — pins the field
|
||||
/// mapping so the two can't quietly diverge, and that `port` /
|
||||
/// `cpu_quota` / `memory_max` (no equivalent on the row) are dropped
|
||||
/// on purpose rather than by omission.
|
||||
#[test]
|
||||
fn agent_status_row_carries_every_field_the_row_has_and_drops_the_rest() {
|
||||
use super::ContainerView;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use hive_sh4re::container::AgentStatusRow;
|
||||
|
||||
let view = ContainerView {
|
||||
name: "alice".to_owned(),
|
||||
container: "h-alice".to_owned(),
|
||||
port: 7000,
|
||||
running: true,
|
||||
failed: false,
|
||||
needs_update: true,
|
||||
needs_login: false,
|
||||
deployed_sha: Some("abc123def456".to_owned()),
|
||||
parent: Some("bob".to_owned()),
|
||||
active_model: Some("claude-opus".to_owned()),
|
||||
status_text: Some("shipping".to_owned()),
|
||||
status_set_at: Some(Utc.timestamp_opt(1_700_000_000, 0).unwrap()),
|
||||
paused: true,
|
||||
cpu_quota: "400%".to_owned(),
|
||||
memory_max: "8G".to_owned(),
|
||||
};
|
||||
|
||||
let row = AgentStatusRow::from(view);
|
||||
assert_eq!(row.name, "alice");
|
||||
assert!(row.running);
|
||||
assert!(!row.failed);
|
||||
assert!(row.needs_update);
|
||||
assert!(!row.needs_login);
|
||||
assert_eq!(row.deployed_sha.as_deref(), Some("abc123def456"));
|
||||
assert_eq!(row.parent.as_deref(), Some("bob"));
|
||||
assert!(row.paused);
|
||||
assert_eq!(row.active_model.as_deref(), Some("claude-opus"));
|
||||
assert_eq!(row.status_text.as_deref(), Some("shipping"));
|
||||
assert_eq!(
|
||||
row.status_set_at,
|
||||
Some(Utc.timestamp_opt(1_700_000_000, 0).unwrap())
|
||||
);
|
||||
// No source for reminders on this side (see the `From` impl's doc
|
||||
// comment) — always the stub, never left uninitialised.
|
||||
assert_eq!(row.pending_reminders, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue