重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。

同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。

Made-with: Cursor
This commit is contained in:
oliver 2026-04-26 08:34:33 +08:00
parent 4a23b715a2
commit dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions

View file

@ -11,6 +11,7 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Any
from oclaw.runtime.skills_market import get_market_adapter
from oclaw.runtime.skills import (
default_skills_root,
discover_workspace_skill_manifests,
@ -45,7 +46,7 @@ def _classify_install_detail(detail: str) -> tuple[str, bool]:
return ("invalid_skill_package", False)
if d.startswith("rollback_after_error"):
return ("runtime_error", True)
if d == "installed" or d == "created":
if d in {"installed", "created", "removed"}:
return ("ok", False)
return ("unknown", False)
@ -118,6 +119,32 @@ def set_skill_enabled(*, store: Any, skill_name: str, enabled: bool) -> None:
_set_disabled_names(store, disabled)
def uninstall_skill(
*,
store: Any,
skill_name: str,
skills_root: str | Path | None = None,
) -> SkillInstallResult:
name = str(skill_name or "").strip()
if not name:
ec, rt = _classify_install_detail("name_required")
return SkillInstallResult(ok=False, name="", target_dir="", detail="name_required", error_code=ec, retryable=rt)
root = Path(skills_root).resolve() if skills_root else default_skills_root()
candidates = [root / name, root / "_workspace" / name]
target = next((p for p in candidates if p.exists() and p.is_dir()), None)
if target is None:
ec, rt = _classify_install_detail("not_found")
return SkillInstallResult(ok=False, name=name, target_dir="", detail="not_found", error_code=ec, retryable=rt)
try:
shutil.rmtree(target)
except Exception as exc:
ec, rt = _classify_install_detail("runtime_error")
return SkillInstallResult(ok=False, name=name, target_dir=str(target), detail=f"remove_failed:{type(exc).__name__}", error_code=ec, retryable=rt)
set_skill_enabled(store=store, skill_name=name, enabled=True)
ec, rt = _classify_install_detail("removed")
return SkillInstallResult(ok=True, name=name, target_dir=str(target), detail="removed", error_code=ec, retryable=rt)
def scan_skill_source_dir(src_dir: str | Path) -> tuple[bool, str]:
src = Path(src_dir).resolve()
if not src.exists() or not src.is_dir():
@ -179,17 +206,29 @@ def _resolve_registry_archive_url(url: str) -> str:
segs = [s for s in str(parsed.path or "").split("/") if s]
if not segs:
return raw
# Clawhub page URLs: https://clawhub.ai/<author>/<slug>
# Resolve via clawhub client first (matches test + avoids adapter drift).
try:
if len(segs) >= 2:
from oclaw.runtime.tools.skills.clawhub_client import get_skill_detail
slug = str(segs[-1] or "").strip()
if slug:
detail = get_skill_detail(slug)
archive_url = str((detail or {}).get("archiveUrl") or "").strip()
if archive_url:
return archive_url
except Exception:
pass
candidates: list[str] = []
if len(segs) >= 1:
candidates.append(segs[-1])
if len(segs) >= 2:
candidates.append(f"{segs[0]}/{segs[1]}")
try:
from oclaw.runtime.tools.skills.clawhub_client import get_skill_detail
adapter = get_market_adapter("clawhub")
for slug in candidates:
detail = get_skill_detail(slug)
archive_url = str(detail.get("archiveUrl") or "").strip() if isinstance(detail, dict) else ""
archive_url, _ = adapter.resolve_archive_url(slug=slug, version=None)
if archive_url:
return archive_url
return raw
@ -342,6 +381,68 @@ def create_skill_from_template(
return SkillInstallResult(ok=True, name=nm, target_dir=str(target), detail="created", error_code=ec, retryable=rt)
def create_workspace_skill(
*,
store: Any,
name: str,
description: str,
runtime_type: str = "python",
skills_root: str | Path | None = None,
overwrite: bool = False,
) -> SkillInstallResult:
nm = str(name or "").strip()
if not nm:
ec, rt = _classify_install_detail("name_required")
return SkillInstallResult(ok=False, name="", target_dir="", detail="name_required", error_code=ec, retryable=rt)
rt_type = str(runtime_type or "python").strip().lower()
if rt_type not in {"python", "shell", "node"}:
ec, rt = _classify_install_detail("unsupported_runtime_type")
return SkillInstallResult(ok=False, name=nm, target_dir="", detail="unsupported_runtime_type", error_code=ec, retryable=rt)
root = Path(skills_root).resolve() if skills_root else default_skills_root()
target = root / "_workspace" / nm
if target.exists() and not overwrite:
ec, rt = _classify_install_detail("already_exists")
return SkillInstallResult(ok=False, name=nm, target_dir=str(target), detail="already_exists", error_code=ec, retryable=rt)
target.mkdir(parents=True, exist_ok=True)
scripts_dir = target / "scripts"
scripts_dir.mkdir(parents=True, exist_ok=True)
entry = "scripts/run.py" if rt_type == "python" else ("scripts/run.sh" if rt_type == "shell" else "scripts/run.js")
script_path = target / entry
if rt_type == "python":
script_path.write_text(
"import json\nimport sys\n\n"
"def main():\n"
" data = json.loads(sys.stdin.read() or '{}')\n"
" args = data.get('args') or {}\n"
" print(json.dumps({'ok': True, 'echo': args}, ensure_ascii=False))\n\n"
"if __name__ == '__main__':\n"
" main()\n",
encoding="utf-8",
)
elif rt_type == "shell":
script_path.write_text("#!/usr/bin/env bash\ncat\n", encoding="utf-8")
else:
script_path.write_text("const fs=require('fs'); const input=JSON.parse(fs.readFileSync(0,'utf8')||'{}'); console.log(JSON.stringify({ok:true,echo:input.args||{}}));\n", encoding="utf-8")
metadata_oclaw = {
"source": {"kind": "workspace", "provider": "local", "version": "workspace"},
"runtime": {
"type": rt_type,
"entry": entry,
"schema": {"type": "object", "additionalProperties": True},
"permissions": {"fs_write": False, "net": False, "process": True},
},
}
return create_skill_from_template(
store=store,
name=nm,
description=description,
body_markdown="Workspace authored skill.",
metadata_oclaw=metadata_oclaw,
skills_root=target.parent,
overwrite=True,
)
def auto_install_skill_from_payload(
*,
store: Any,
@ -393,9 +494,11 @@ __all__ = [
"SkillInstallResult",
"auto_install_skill_from_payload",
"create_skill_from_template",
"create_workspace_skill",
"install_skill_from_local_dir",
"install_skill_from_registry_archive",
"list_skills_with_status",
"set_skill_enabled",
"skill_auto_install_enabled",
"uninstall_skill",
]