From 7d94ec0d18b46893e23680cf8438109a34cc2a10 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sun, 22 Feb 2026 16:55:03 +0100
Subject: [PATCH] feat: promote/sync/rebuild UI, operations page, bidirectional sync, lifecycle ops
---
app/ops_runner.py | 31 ++++++++++++++++++++++++++++++-
1 files changed, 30 insertions(+), 1 deletions(-)
diff --git a/app/ops_runner.py b/app/ops_runner.py
index 226fdaa..d9a460a 100644
--- a/app/ops_runner.py
+++ b/app/ops_runner.py
@@ -39,10 +39,11 @@
data = json.loads(result["output"])
return {"success": True, "data": data, "error": ""}
except json.JSONDecodeError as exc:
+ raw = result["output"][:500]
return {
"success": False,
"data": None,
- "error": f"Failed to parse JSON: {exc}\nRaw: {result['output'][:500]}",
+ "error": f"Failed to parse JSON: {exc}\nRaw: {raw}",
}
@@ -72,12 +73,40 @@
return await _run_exec(_NSENTER_PREFIX + [OPS_CLI] + args, timeout=timeout)
+async def run_ops_host_json(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> dict:
+ """Run the ops CLI on the host via nsenter with --json and return parsed JSON."""
+ result = await run_ops_host(args + ["--json"], timeout=timeout)
+ if not result["success"]:
+ return {"success": False, "data": None, "error": result["error"] or result["output"]}
+ try:
+ data = json.loads(result["output"])
+ return {"success": True, "data": data, "error": ""}
+ except json.JSONDecodeError as exc:
+ raw = result["output"][:500]
+ return {
+ "success": False,
+ "data": None,
+ "error": f"Failed to parse JSON: {exc}\nRaw: {raw}",
+ }
+
+
async def stream_ops_host(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> AsyncGenerator[str, None]:
"""Stream ops CLI output from the host via nsenter."""
async for line in _stream_exec(_NSENTER_PREFIX + [OPS_CLI] + args, timeout=timeout):
yield line
+async def run_command_host(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> dict:
+ """Run an arbitrary command on the host via nsenter."""
+ return await _run_exec(_NSENTER_PREFIX + args, timeout=timeout)
+
+
+async def stream_command_host(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> AsyncGenerator[str, None]:
+ """Stream arbitrary command output from the host via nsenter."""
+ async for line in _stream_exec(_NSENTER_PREFIX + args, timeout=timeout):
+ yield line
+
+
# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------
--
Gitblit v1.3.1