feat: the host agent is always on; the MCP sees and starts host maintenance (#5834)
#5834 "The host agent is always on, and the MCP sees a host's maintenance", step 1 of milestone 520 "The MCP sees what Steward has grown". The host agent is a core system (operator, 2026-10-15: "it's very core to the functionality of Steward as a whole and shouldn't be considered something that can be toggled off"). ALWAYS_ON_PLUGINS in core settings makes to_plugins_cfg report it enabled whatever its stored row says, so it always loads and its MCP tools are always listed. Settings -> Plugins shows it as "Built in: always on" with no switch, and a save keeps it enabled. Its code and URLs stay under plugins/host_agent: every installed agent posts to /plugins/host_agent/ingest. Maintenance through the MCP: - list_maintenance_plans: each plan's group, weekly slot, reboot policy, next slot, and its latest run (hosts, how far it got, who it skipped and why, how it ended). - get_maintenance_job(host | job): a host's jobs, newest and running first, or one job: state, packages updated, reboot, drained by, why it failed, when its current state times out. - run_maintenance(host, reboot="if_required" | "never"): admin, as the host page's Update now; refuses a busy host or one with no helper; audited "<user> via MCP". - The overview names a host whose latest job in the last week failed or timed out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -590,13 +590,23 @@ def grade_style(grade: str | None) -> str:
|
||||
return _STATUS_STYLE.get(grade or "", "")
|
||||
|
||||
|
||||
# Plugins Steward can't run without, so they load whatever their stored
|
||||
# `enabled` says and Settings → Plugins offers no switch for them. The host
|
||||
# agent is one (operator, 2026-10-15): maintenance, the Hosts views, drive,
|
||||
# update and mount alerts and Docker collection all rely on it.
|
||||
ALWAYS_ON_PLUGINS = frozenset({"host_agent"})
|
||||
|
||||
|
||||
def to_plugins_cfg(settings: dict[str, Any]) -> dict:
|
||||
"""Assemble {plugin_name: {...config}} from all plugin.* keys."""
|
||||
"""Assemble {plugin_name: {...config}} from all plugin.* keys. An
|
||||
always-on plugin is enabled whatever its row says, or with no row."""
|
||||
result = {}
|
||||
for key, value in settings.items():
|
||||
if key.startswith("plugin."):
|
||||
name = key[len("plugin."):]
|
||||
result[name] = value
|
||||
for name in ALWAYS_ON_PLUGINS:
|
||||
result[name] = {**(result.get(name) or {}), "enabled": True}
|
||||
return result
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user