mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-10 17:23:25 +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
97
runtime/hooks/internal_hooks.py
Normal file
97
runtime/hooks/internal_hooks.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime as _dt
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Awaitable, Callable, Dict, List, MutableMapping, Optional, Sequence, Set, Tuple, Union
|
||||
|
||||
log = logging.getLogger("oclaw.hooks")
|
||||
|
||||
HookEventType = str
|
||||
HookAction = str
|
||||
HookEventKey = str
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class HookEvent:
|
||||
"""
|
||||
A lightweight, transport-agnostic hook event.
|
||||
|
||||
This intentionally mirrors the Oclaw internal hook model:
|
||||
- handlers may register on "type" or "type:action"
|
||||
- failures are isolated per-handler
|
||||
"""
|
||||
|
||||
type: HookEventType
|
||||
action: HookAction
|
||||
sessionKey: str
|
||||
context: Dict[str, Any] = field(default_factory=dict)
|
||||
timestamp: _dt.datetime = field(default_factory=lambda: _dt.datetime.now(tz=_dt.timezone.utc))
|
||||
messages: List[Any] = field(default_factory=list)
|
||||
|
||||
|
||||
HookHandler = Callable[[HookEvent], Union[None, Awaitable[None]]]
|
||||
|
||||
_handlers: MutableMapping[HookEventKey, List[HookHandler]] = {}
|
||||
_enabled_state: Dict[str, bool] = {"enabled": True}
|
||||
|
||||
|
||||
def register_hook(event_key: str, handler: HookHandler) -> None:
|
||||
_handlers.setdefault(event_key, []).append(handler)
|
||||
|
||||
|
||||
def unregister_hook(event_key: str, handler: HookHandler) -> None:
|
||||
existing = _handlers.get(event_key)
|
||||
if not existing:
|
||||
return
|
||||
try:
|
||||
existing.remove(handler)
|
||||
except ValueError:
|
||||
return
|
||||
if not existing:
|
||||
_handlers.pop(event_key, None)
|
||||
|
||||
|
||||
def clear_hooks() -> None:
|
||||
_handlers.clear()
|
||||
|
||||
|
||||
def set_hooks_enabled(enabled: bool) -> None:
|
||||
_enabled_state["enabled"] = bool(enabled)
|
||||
|
||||
|
||||
def get_registered_hook_event_keys() -> List[str]:
|
||||
return list(_handlers.keys())
|
||||
|
||||
|
||||
def _has_listeners(event_type: str, action: str) -> bool:
|
||||
return bool(_handlers.get(event_type)) or bool(_handlers.get(f"{event_type}:{action}"))
|
||||
|
||||
|
||||
def create_hook_event(
|
||||
event_type: HookEventType,
|
||||
action: HookAction,
|
||||
session_key: str,
|
||||
context: Optional[Dict[str, Any]] = None,
|
||||
) -> HookEvent:
|
||||
return HookEvent(type=event_type, action=action, sessionKey=session_key, context=context or {})
|
||||
|
||||
|
||||
async def trigger_hook(event: HookEvent) -> None:
|
||||
if not _enabled_state["enabled"]:
|
||||
return
|
||||
if not _has_listeners(event.type, event.action):
|
||||
return
|
||||
|
||||
all_handlers: List[HookHandler] = []
|
||||
all_handlers.extend(_handlers.get(event.type, []))
|
||||
all_handlers.extend(_handlers.get(f"{event.type}:{event.action}", []))
|
||||
|
||||
for handler in all_handlers:
|
||||
try:
|
||||
res = handler(event)
|
||||
if hasattr(res, "__await__"):
|
||||
await res # type: ignore[misc]
|
||||
except Exception:
|
||||
log.exception("Hook error [%s:%s]", event.type, event.action)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue