Files
wehub-resource-sync ba1d0b91a4
Test Suite / webui (push) Failing after 1s
Test Suite / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:06:36 +08:00

159 lines
5.2 KiB
Python

from __future__ import annotations
import asyncio
import json
import subprocess
import time
from pathlib import Path
from nanobot.agent.tools.cli_apps import CliAppsTool
from nanobot.agent.tools.context import RequestContext
from nanobot.apps.cli.service import CliAppManager, CliAppsRuntimeConfig
def _write_cache(path: Path, registry: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps({"_cached_at": time.time(), "data": registry}),
encoding="utf-8",
)
def test_run_cli_app_uses_installed_registry_app(
tmp_path: Path,
monkeypatch,
) -> None:
workspace = tmp_path / "workspace"
workspace.mkdir()
data_dir = tmp_path / "data"
registry = {
"meta": {"updated": "2026-04-16"},
"clis": [
{
"name": "gimp",
"display_name": "GIMP",
"version": "1.0.0",
"description": "Image editing",
"category": "image",
"install_cmd": "pip install cli-anything-gimp",
"entry_point": "cli-anything-gimp",
}
],
}
_write_cache(data_dir / "harness_registry_cache.json", registry)
_write_cache(data_dir / "public_registry_cache.json", {"meta": {}, "clis": []})
_write_cache(data_dir / "extensions_registry_cache.json", {"meta": {}, "clis": []})
CliAppManager(workspace=workspace, data_dir=data_dir)._save_installed(
{"gimp": {"entry_point": "cli-anything-gimp"}}
)
resolved = str(tmp_path / "bin" / "cli-anything-gimp")
monkeypatch.setattr(
"nanobot.apps.cli.service.shutil.which",
lambda entry: resolved if entry == "cli-anything-gimp" else None,
)
def fake_run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]:
assert "shell" not in kwargs or kwargs["shell"] is False
return subprocess.CompletedProcess(
argv,
0,
stdout="tool:" + " ".join(argv[1:]),
stderr="",
)
monkeypatch.setattr("nanobot.apps.cli.service.subprocess.run", fake_run)
monkeypatch.setattr("nanobot.apps.cli.service.get_runtime_subdir", lambda _name: data_dir)
tool = CliAppsTool(
workspace=workspace,
restrict_to_workspace=True,
runtime=CliAppsRuntimeConfig(run_timeout=5),
)
assert tool.name == "run_cli_app"
result = asyncio.run(
tool.execute(
name="gimp",
args=["project", "list"],
json=True,
working_dir=str(workspace),
)
)
assert "CLI app 'gimp' exited 0" in result
assert "tool:--json project list" in result
def test_run_cli_app_rejects_uninstalled_app(tmp_path: Path, monkeypatch) -> None:
workspace = tmp_path / "workspace"
workspace.mkdir()
data_dir = tmp_path / "data"
registry = {
"meta": {"updated": "2026-04-16"},
"clis": [
{
"name": "gimp",
"display_name": "GIMP",
"version": "1.0.0",
"description": "Image editing",
"category": "image",
"install_cmd": "pip install cli-anything-gimp",
"entry_point": "cli-anything-gimp",
}
],
}
_write_cache(data_dir / "harness_registry_cache.json", registry)
_write_cache(data_dir / "public_registry_cache.json", {"meta": {}, "clis": []})
_write_cache(data_dir / "extensions_registry_cache.json", {"meta": {}, "clis": []})
monkeypatch.setattr("nanobot.apps.cli.service.get_runtime_subdir", lambda _name: data_dir)
tool = CliAppsTool(workspace=workspace, restrict_to_workspace=True)
result = asyncio.run(tool.execute(name="gimp"))
assert "not installed" in result
def test_run_cli_app_description_names_only_settings_installed_apps(tmp_path: Path, monkeypatch) -> None:
workspace = tmp_path / "workspace"
workspace.mkdir()
data_dir = tmp_path / "data"
CliAppManager(workspace=workspace, data_dir=data_dir)._save_installed(
{"drawio": {"entry_point": "cli-anything-drawio"}}
)
monkeypatch.setattr("nanobot.apps.cli.service.get_runtime_subdir", lambda _name: data_dir)
tool = CliAppsTool(workspace=workspace)
assert "Settings CLI Apps: drawio" in tool.description
assert "ordinary system CLIs such as git, gh" in tool.description
def test_cli_app_tool_provides_context_only_for_attachment(tmp_path: Path) -> None:
tool = CliAppsTool(workspace=tmp_path)
provider = tool.runtime_context_provider()
assert provider is not None
empty = asyncio.run(provider(RequestContext(
channel="websocket",
chat_id="chat",
original_user_text="hello",
workspace=tmp_path,
)))
attached = asyncio.run(provider(RequestContext(
channel="websocket",
chat_id="chat",
original_user_text="use @drawio",
metadata={
"cli_apps": [{
"name": "drawio",
"entry_point": "cli-anything-drawio",
}],
},
workspace=tmp_path,
)))
assert empty is None
assert attached is not None
assert attached.source == "cli_apps"
assert "CLI App Attachment: @drawio" in attached.content