修复 Admin Skills 安装区块不显示,并支持卸载 _workspace/public lane。

同时补齐 _workspace/public 目录约定、公共技能免绑定规则,并为 run_command 增加默认禁用开关与回归测试。

Made-with: Cursor
This commit is contained in:
oliver 2026-05-01 03:34:32 +08:00
parent da2413afad
commit 5d67303d22
11 changed files with 118 additions and 5 deletions

View file

@ -240,6 +240,11 @@ class AdminSkillsApiTests(unittest.TestCase):
self.assertFalse(bool(result2.get("ok")))
self.assertIn(str(result2.get("error_code") or ""), {"path_restricted", "runtime_error"})
# Uninstall should be able to remove workspace lane skills.
u = self.client.post("/admin/api/skills/uninstall", json={"name": "ws_demo_skill"}, headers=self._h())
self.assertEqual(u.status_code, 200, u.text)
self.assertTrue((u.json() or {}).get("ok"))
def test_skills_self_check_endpoint(self) -> None:
c = self.client.post(
"/admin/api/skills/create-workspace",

View file

@ -61,6 +61,7 @@ def test_local_public_high_risk_tools_visible_when_enabled(monkeypatch) -> None:
def test_local_adapter_backend_roundtrip(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setenv("OPS_WORKSPACE_ROOT", str(tmp_path))
monkeypatch.setenv("AIA_ENABLE_RUN_COMMAND", "1")
adapter = LocalAdapter()
out_w = adapter.write_file(path="a.txt", content="hello\nworld\n")
assert out_w.get("ok") is True
@ -124,6 +125,7 @@ def test_local_tool_integration_roundtrip(monkeypatch) -> None:
tmpdir = Path(tempfile.mkdtemp(prefix="local_it_"))
monkeypatch.setenv("OPS_WORKSPACE_ROOT", str(tmpdir))
monkeypatch.setenv("AIA_ENABLE_RUN_COMMAND", "1")
target_rel = "data/workspace/it_sample.txt"
out_write = write_spec.handler({"path": "it_sample.txt", "content": "line1\nline2\n", "mode": "overwrite"})
@ -178,6 +180,7 @@ def test_p1_p2_read_tools_smoke(tmp_path: Path, monkeypatch) -> None:
def test_run_command_does_not_follow_cd_state(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setenv("OPS_WORKSPACE_ROOT", str(tmp_path))
monkeypatch.setenv("AIA_ENABLE_RUN_COMMAND", "1")
(tmp_path / "data" / "workspace").mkdir(parents=True, exist_ok=True)
(tmp_path / "subdir").mkdir(parents=True, exist_ok=True)
(tmp_path / "subdir" / "echo_dir.py").write_text(

View file

@ -17,6 +17,7 @@ def test_build_gateway_executor_defaults_to_generalist(tmp_path: Path) -> None:
def test_build_gateway_executor_generalist_run_command_is_disabled_by_default(tmp_path: Path) -> None:
os.environ.pop("AIA_ENABLE_RUN_COMMAND", None)
os.environ["AIA_PUBLIC_TOOLS_ALLOW_HIGH"] = "1"
os.environ["OPS_ASSISTANT_DB_PATH"] = str(tmp_path / "ops.sqlite")
try:
store = SqliteStore(str(tmp_path / "ops.sqlite"))
@ -28,11 +29,13 @@ def test_build_gateway_executor_generalist_run_command_is_disabled_by_default(tm
assert out.get("ok") is False
assert out.get("error") == "disabled"
finally:
os.environ.pop("AIA_PUBLIC_TOOLS_ALLOW_HIGH", None)
os.environ.pop("OPS_ASSISTANT_DB_PATH", None)
def test_build_gateway_executor_generalist_run_command_prefers_db_setting(tmp_path: Path) -> None:
os.environ["AIA_ENABLE_RUN_COMMAND"] = "0"
os.environ["AIA_PUBLIC_TOOLS_ALLOW_HIGH"] = "1"
os.environ["OPS_ASSISTANT_DB_PATH"] = str(tmp_path / "ops.sqlite")
try:
store = SqliteStore(str(tmp_path / "ops.sqlite"))
@ -45,5 +48,6 @@ def test_build_gateway_executor_generalist_run_command_prefers_db_setting(tmp_pa
assert out.get("ok") is True
finally:
os.environ.pop("AIA_ENABLE_RUN_COMMAND", None)
os.environ.pop("AIA_PUBLIC_TOOLS_ALLOW_HIGH", None)
os.environ.pop("OPS_ASSISTANT_DB_PATH", None)

View file

@ -30,6 +30,7 @@ def test_collect_respects_role_binding_union(tmp_path: Path, monkeypatch) -> Non
skills_root.mkdir(parents=True, exist_ok=True)
_write_skill(skills_root, "skill-alpha")
_write_skill(skills_root, "skill-beta")
_write_skill(skills_root / "_workspace" / "public", "skill-public")
monkeypatch.setenv("AIA_SKILLS_ROOT", str(skills_root))
store.set_setting(SKILL_ROLE_BINDING_ENABLED_SETTING, "1")
@ -48,6 +49,7 @@ def test_collect_respects_role_binding_union(tmp_path: Path, monkeypatch) -> Non
names = {e[0] for e in entries}
assert "skill-alpha" in names
assert "skill-beta" in names
assert "skill-public" in names
def test_collect_unfiltered_when_binding_disabled(tmp_path: Path, monkeypatch) -> None: