59 lines
2.0 KiB
Python
59 lines
2.0 KiB
Python
import shutil
|
|
import subprocess
|
|
from typing import Any
|
|
|
|
from duck_core.tools.base import ToolResult
|
|
|
|
|
|
class OsUpdateCheckTool:
|
|
name = "os_update_check"
|
|
risk_level = "low"
|
|
|
|
def __init__(self, workspace: str, timeout_seconds: int = 60, max_lines: int = 300):
|
|
self.workspace = workspace
|
|
self.timeout_seconds = timeout_seconds
|
|
self.max_lines = max_lines
|
|
|
|
async def run(self, args: dict[str, Any]) -> ToolResult:
|
|
if shutil.which("apt"):
|
|
return self._run_apt()
|
|
return ToolResult(
|
|
ok=False,
|
|
error="No supported package manager found for update checks.",
|
|
metadata={"supported_package_managers": ["apt"]},
|
|
)
|
|
|
|
def _run_apt(self) -> ToolResult:
|
|
try:
|
|
completed = subprocess.run(
|
|
["apt", "list", "--upgradable"],
|
|
cwd=self.workspace,
|
|
text=True,
|
|
capture_output=True,
|
|
timeout=self.timeout_seconds,
|
|
check=False,
|
|
)
|
|
except subprocess.SubprocessError as exc:
|
|
return ToolResult(ok=False, error=str(exc), metadata={"package_manager": "apt"})
|
|
|
|
lines = completed.stdout.splitlines()
|
|
package_lines = [line for line in lines if "/" in line and "upgradable from:" in line]
|
|
output_lines = lines[: self.max_lines]
|
|
truncated = len(lines) > self.max_lines
|
|
if truncated:
|
|
output_lines.append(f"... truncated after {self.max_lines} lines")
|
|
|
|
return ToolResult(
|
|
ok=completed.returncode == 0,
|
|
output="\n".join(output_lines),
|
|
error=completed.stderr or None,
|
|
metadata={
|
|
"package_manager": "apt",
|
|
"command": "apt list --upgradable",
|
|
"returncode": completed.returncode,
|
|
"upgradable_count": len(package_lines),
|
|
"refreshed_cache": False,
|
|
"truncated": truncated,
|
|
},
|
|
)
|