Matthias Nott
2026-02-22 7d94ec0d18b46893e23680cf8438109a34cc2a10
app/ops_runner.py
....@@ -39,10 +39,11 @@
3939 data = json.loads(result["output"])
4040 return {"success": True, "data": data, "error": ""}
4141 except json.JSONDecodeError as exc:
42
+ raw = result["output"][:500]
4243 return {
4344 "success": False,
4445 "data": None,
45
- "error": f"Failed to parse JSON: {exc}\nRaw: {result['output'][:500]}",
46
+ "error": f"Failed to parse JSON: {exc}\nRaw: {raw}",
4647 }
4748
4849
....@@ -72,12 +73,40 @@
7273 return await _run_exec(_NSENTER_PREFIX + [OPS_CLI] + args, timeout=timeout)
7374
7475
76
+async def run_ops_host_json(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> dict:
77
+ """Run the ops CLI on the host via nsenter with --json and return parsed JSON."""
78
+ result = await run_ops_host(args + ["--json"], timeout=timeout)
79
+ if not result["success"]:
80
+ return {"success": False, "data": None, "error": result["error"] or result["output"]}
81
+ try:
82
+ data = json.loads(result["output"])
83
+ return {"success": True, "data": data, "error": ""}
84
+ except json.JSONDecodeError as exc:
85
+ raw = result["output"][:500]
86
+ return {
87
+ "success": False,
88
+ "data": None,
89
+ "error": f"Failed to parse JSON: {exc}\nRaw: {raw}",
90
+ }
91
+
92
+
7593 async def stream_ops_host(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> AsyncGenerator[str, None]:
7694 """Stream ops CLI output from the host via nsenter."""
7795 async for line in _stream_exec(_NSENTER_PREFIX + [OPS_CLI] + args, timeout=timeout):
7896 yield line
7997
8098
99
+async def run_command_host(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> dict:
100
+ """Run an arbitrary command on the host via nsenter."""
101
+ return await _run_exec(_NSENTER_PREFIX + args, timeout=timeout)
102
+
103
+
104
+async def stream_command_host(args: list[str], timeout: int = _DEFAULT_TIMEOUT) -> AsyncGenerator[str, None]:
105
+ """Stream arbitrary command output from the host via nsenter."""
106
+ async for line in _stream_exec(_NSENTER_PREFIX + args, timeout=timeout):
107
+ yield line
108
+
109
+
81110 # ---------------------------------------------------------------------------
82111 # Internal helpers
83112 # ---------------------------------------------------------------------------