mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 05:50:44 +08:00
重构仓库目录为统一的 runtime 分层并清理历史 openclaw 残留。
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。 Made-with: Cursor
This commit is contained in:
parent
ba3836f00f
commit
4a23b715a2
498 changed files with 2760 additions and 2200 deletions
140
runtime/hooks/workspace.py
Normal file
140
runtime/hooks/workspace.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterable, List, Literal, Optional, Sequence, Tuple
|
||||
|
||||
from .frontmatter import parse_frontmatter, resolve_oclaw_metadata
|
||||
from .policy import HookSource, resolve_hook_entries
|
||||
|
||||
|
||||
HookEntry = Dict[str, Any]
|
||||
|
||||
|
||||
def _read_text(path: Path) -> Optional[str]:
|
||||
try:
|
||||
return path.read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _safe_is_dir(p: Path) -> bool:
|
||||
try:
|
||||
return p.exists() and p.is_dir()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _handler_candidates() -> Tuple[str, ...]:
|
||||
return ("handler.py", "index.py")
|
||||
|
||||
|
||||
def _load_hook_from_dir(hook_dir: Path, source: HookSource, plugin_id: Optional[str] = None) -> Optional[HookEntry]:
|
||||
hook_md = hook_dir / "HOOK.md"
|
||||
content = _read_text(hook_md)
|
||||
if content is None:
|
||||
return None
|
||||
|
||||
parsed = parse_frontmatter(content)
|
||||
fm = parsed.frontmatter
|
||||
name = fm.get("name") or hook_dir.name
|
||||
if not isinstance(name, str) or not name.strip():
|
||||
name = hook_dir.name
|
||||
description = fm.get("description") or ""
|
||||
if not isinstance(description, str):
|
||||
description = ""
|
||||
|
||||
handler_path: Optional[Path] = None
|
||||
for candidate in _handler_candidates():
|
||||
cand = hook_dir / candidate
|
||||
if cand.exists() and cand.is_file():
|
||||
handler_path = cand
|
||||
break
|
||||
if handler_path is None:
|
||||
return None
|
||||
|
||||
metadata = resolve_oclaw_metadata(fm) or {}
|
||||
|
||||
return {
|
||||
"hook": {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"source": source,
|
||||
"pluginId": plugin_id,
|
||||
"filePath": str(hook_md),
|
||||
"baseDir": str(hook_dir.resolve()),
|
||||
"handlerPath": str(handler_path.resolve()),
|
||||
},
|
||||
"frontmatter": fm,
|
||||
"metadata": metadata,
|
||||
}
|
||||
|
||||
|
||||
def load_hook_entries_from_dir(dir_path: str, source: HookSource, plugin_id: Optional[str] = None) -> List[HookEntry]:
|
||||
base = Path(dir_path)
|
||||
if not _safe_is_dir(base):
|
||||
return []
|
||||
out: List[HookEntry] = []
|
||||
try:
|
||||
for child in base.iterdir():
|
||||
if not child.is_dir():
|
||||
continue
|
||||
entry = _load_hook_from_dir(child, source=source, plugin_id=plugin_id)
|
||||
if entry:
|
||||
out.append(entry)
|
||||
except Exception:
|
||||
return out
|
||||
return out
|
||||
|
||||
|
||||
def discover_workspace_hook_entries(
|
||||
workspace_dir: str,
|
||||
*,
|
||||
managed_hooks_dir: Optional[str] = None,
|
||||
bundled_hooks_dir: Optional[str] = None,
|
||||
extra_dirs: Optional[Sequence[str]] = None,
|
||||
) -> List[HookEntry]:
|
||||
"""
|
||||
Python port of Oclaw hook discovery strategy.
|
||||
|
||||
- bundled: shipped with runtime
|
||||
- managed: user config dir (~/.oclaw/hooks by default)
|
||||
- workspace: <workspace>/hooks (explicit opt-in by default)
|
||||
- extra: any extra managed dirs
|
||||
"""
|
||||
ws = Path(workspace_dir)
|
||||
managed = Path(managed_hooks_dir) if managed_hooks_dir else (Path.home() / ".oclaw" / "hooks")
|
||||
workspace_hooks = ws / "hooks"
|
||||
|
||||
entries: List[HookEntry] = []
|
||||
|
||||
if extra_dirs:
|
||||
for raw in extra_dirs:
|
||||
p = Path(os.path.expanduser(raw)).resolve()
|
||||
entries.extend(load_hook_entries_from_dir(str(p), source="oclaw-managed"))
|
||||
|
||||
if bundled_hooks_dir:
|
||||
entries.extend(load_hook_entries_from_dir(bundled_hooks_dir, source="oclaw-bundled"))
|
||||
|
||||
entries.extend(load_hook_entries_from_dir(str(managed), source="oclaw-managed"))
|
||||
entries.extend(load_hook_entries_from_dir(str(workspace_hooks), source="oclaw-workspace"))
|
||||
return entries
|
||||
|
||||
|
||||
def load_workspace_hook_entries(
|
||||
workspace_dir: str,
|
||||
*,
|
||||
managed_hooks_dir: Optional[str] = None,
|
||||
bundled_hooks_dir: Optional[str] = None,
|
||||
extra_dirs: Optional[Sequence[str]] = None,
|
||||
) -> List[HookEntry]:
|
||||
discovered = discover_workspace_hook_entries(
|
||||
workspace_dir,
|
||||
managed_hooks_dir=managed_hooks_dir,
|
||||
bundled_hooks_dir=bundled_hooks_dir,
|
||||
extra_dirs=extra_dirs,
|
||||
)
|
||||
return resolve_hook_entries(discovered)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue