agent_plugin_service and server_template_service dispatched agent commands
through get_agent_gateway() — a function that does not exist — inside a bare
try/except. Every call raised ImportError, was swallowed, and left DB rows stuck
in 'installing'/'checking'/'remediating' forever while the UI implied the
feature worked. The agent also has no handlers for these commands.
- Plugin install now reports STATUS_ERROR with "Agent-side plugin support is not
implemented yet" instead of hanging in 'installing'.
- Template drift-check/remediate report 'unknown' + an explanatory
drift_report.error.
- Local-only ops (disable, uninstall, configure) just drop the dead dispatch.
- test_agent_features_honest.py: 3 tests asserting honest states.
Implementing the feature for real (agent handlers + sandbox) remains future
work; this stops it from lying in the meantime.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 3 (frontend), Phase 4 (template library), Phase 5 (pyenv runtimes).
Frontend:
- New Packages tab on ServerDetail: search, install/remove, upgrade-all,
update-cache, quick-install presets (nginx, redis, mariadb, postgres,
docker, fail2ban, certbot, htop). Capability-gated on packages.
- New Services tab: list systemd units with state filter, start/stop/
restart/logs per row, daemon-reload at top. Capability-gated on systemd.
- JobProgressModal subscribes to a server-stream Socket.IO room and
renders live install/build progress until the agent emits phase=done.
- ApiService: 13 packages/services methods + 9 runtime methods +
refresh-capabilities helper.
- LESS partials for both new tabs imported in main.scss.
Template library: 9 new one-click stacks added to ServerTemplate
TEMPLATE_LIBRARY: lemp, lamp, docker-host, node-host, python-host, redis,
postgres, cloudflared, fail2ban. Reuses existing packages/services/
firewall_rules JSON columns; the workflow engine's agent_command +
capability_gate nodes drive execution against the new agent surface.
Phase 5 — pyenv / pyenv-win runtime management:
- Agent: 9 new runtimes:* actions (list, pyenv:bootstrap, python:installed,
python:available, python:install, python:uninstall, python:set_global,
python:set_local, python:current). Bootstrap and install stream via
the job-channel pattern. pyenvLock mutex serializes mutations.
Capability probe surfaces RuntimeManagers["python"] = pyenv | pyenv-win.
- Panel: remote_runtimes_service.py + 9 routes under /servers/<id>/runtimes.
- Frontend: ApiService methods to drive the new endpoints.
(Dedicated RuntimesTab component left as a follow-up; the API
surface is the foundation.)
Build, vet, and tests pass across all three layers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>