Files
James Stringer 4087baa4bf Add headless --headless daemon, HTTP/JSON API + Python SDK, and SSH key deploy
Headless mode: `appsandbox.exe --headless` runs a single-owner daemon that hosts
the core over a loopback HTTP/JSON API (http.sys) with a host.json discovery
file. A dependency-free Python SDK, examples, and a self-contained regression
suite live under tools/headless-api/ (PLAN.md = design rationale).

The API mirrors the GUI: create/start/shutdown/stop/delete/edit, snapshots and
branches (incl. base branches via the new asb_snap_base_branch_count), templates,
sshInfo, an SSE /v1/events stream, GUI-mirrored input validation, a startup
fail-fast (admin / Win11 build / VirtualMachinePlatform), and a single-instance
mutex shared with the GUI so only one of GUI/headless runs at a time.

SSH key deploy: sshDeployKey (requires sshEnabled) has the core generate an
AppSandbox ed25519 keypair once and the guest agent write the public key into
authorized_keys -- Windows administrators_authorized_keys (+ ACL), Linux/macOS
~/.ssh -- reporting a new ssh_key_deployed status (sshState 4). The GUI SSH
button and the SDK use the key for password-less login. Verified end-to-end on
Windows.

The Linux guest builds its agent/driver source from this branch's GitHub repo;
asb_core.c points the prefetch at "headless-api" (revert to "main" before merge,
see PLAN.md).
2026-06-09 20:12:18 -04:00

34 lines
1.5 KiB
Python

"""
Operator / maintenance helpers: inspect the daemon + host, list/delete templates,
and stop the daemon. The destructive actions are gated behind flags, so running
this with no arguments is read-only.
python examples/admin.py # version, host, templates (read-only)
python examples/admin.py --delete-template NAME # delete the template named NAME
python examples/admin.py --shutdown # stop the daemon (refused if VMs active)
python examples/admin.py --shutdown --force # stop the daemon, terminating active VMs
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import asb
c = asb.connect()
v = c.version()
print("daemon %s %s (api %s)" % (v.get("product"), v.get("version"), v.get("apiVersion")))
h = c.host()
print("host: %d cores, %d MB RAM, %d GB free" % (h["hostCores"], h["hostRamMb"], h["freeGb"]))
print("templates:", [t["name"] for t in c.templates()] or "(none)")
if "--delete-template" in sys.argv:
name = sys.argv[sys.argv.index("--delete-template") + 1]
print("delete template %r ->" % name, c.delete_template(name))
if "--shutdown" in sys.argv:
code, body = c.shutdown_daemon(force="--force" in sys.argv)
if code == 409:
# refused: VMs are running or building. Re-run with --force to terminate them.
print("daemon refused shutdown -- active:", body.get("error", {}).get("active"))
else:
print("daemon shutdown ->", code, body)