mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 07:03:15 +08:00
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
173 lines
6.2 KiB
Python
173 lines
6.2 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import platform
|
|
import shutil
|
|
from typing import Any, Dict, Optional
|
|
|
|
from .hook_manifest_core import HookMetadataSpec, HookRequiresSpec
|
|
from .hook_types import HookEligibilityContext, HookEntry, ensure_hook_entry
|
|
from .policy import resolve_hook_config, resolve_hook_enable_state
|
|
|
|
_DEFAULT_CONFIG_VALUES: Dict[str, bool] = {
|
|
"browser.enabled": True,
|
|
"browser.evaluateEnabled": True,
|
|
"workspace.dir": True,
|
|
}
|
|
|
|
|
|
def _resolve_config_path(config: Optional[Dict[str, Any]], path_str: str) -> Any:
|
|
if not isinstance(config, dict):
|
|
return None
|
|
cur: Any = config
|
|
for segment in str(path_str or "").split("."):
|
|
key = segment.strip()
|
|
if not key:
|
|
continue
|
|
if not isinstance(cur, dict):
|
|
return None
|
|
cur = cur.get(key)
|
|
return cur
|
|
|
|
|
|
def _is_config_path_truthy(config: Optional[Dict[str, Any]], path_str: str) -> bool:
|
|
key = str(path_str or "").strip()
|
|
if not key:
|
|
return False
|
|
value = _resolve_config_path(config, key)
|
|
if value is None and key in _DEFAULT_CONFIG_VALUES:
|
|
return _DEFAULT_CONFIG_VALUES[key]
|
|
return bool(value)
|
|
|
|
|
|
def _has_binary(bin_name: str) -> bool:
|
|
return bool(shutil.which(str(bin_name or "").strip()))
|
|
|
|
|
|
def _resolve_runtime_platform() -> str:
|
|
return str(platform.system() or "").strip().lower()
|
|
|
|
|
|
def _normalize_os_name(os_name: str) -> str:
|
|
v = str(os_name or "").strip().lower()
|
|
if v in {"mac", "macos", "darwin"}:
|
|
return "darwin"
|
|
if v in {"win", "windows"}:
|
|
return "windows"
|
|
if v in {"linux"}:
|
|
return "linux"
|
|
return v
|
|
|
|
|
|
def _hook_os_gate_satisfied(*, hook_os: tuple[str, ...], remote_platforms: list[str]) -> bool:
|
|
"""
|
|
Match OpenClaw ``evaluateRuntimeEligibility`` OS rule:
|
|
|
|
If the hook declares ``os``, pass when the **local** runtime matches *or* when any
|
|
advertised **remote** platform matches one of the hook's supported OS names.
|
|
"""
|
|
os_list = [str(x).strip() for x in hook_os if str(x or "").strip()]
|
|
if not os_list:
|
|
return True
|
|
cur = _normalize_os_name(_resolve_runtime_platform())
|
|
hook_names = {_normalize_os_name(x) for x in os_list}
|
|
if cur in hook_names:
|
|
return True
|
|
rem = [_normalize_os_name(x) for x in remote_platforms if str(x or "").strip()]
|
|
return any(r in hook_names for r in rem)
|
|
|
|
|
|
def _parse_requires_obj(raw: Any) -> HookRequiresSpec:
|
|
row = raw if isinstance(raw, dict) else {}
|
|
bins = tuple(str(x).strip() for x in list(row.get("bins") or []) if str(x).strip())
|
|
any_bins = tuple(str(x).strip() for x in list(row.get("anyBins") or []) if str(x).strip())
|
|
env = tuple(str(x).strip() for x in list(row.get("env") or []) if str(x).strip())
|
|
config = tuple(str(x).strip() for x in list(row.get("config") or []) if str(x).strip())
|
|
return HookRequiresSpec(bins=bins, any_bins=any_bins, env=env, config=config)
|
|
|
|
|
|
def _parse_metadata_obj(raw: Any) -> HookMetadataSpec:
|
|
row = raw if isinstance(raw, dict) else {}
|
|
node_script: bool | None
|
|
if "nodeScript" in row:
|
|
node_script = bool(row.get("nodeScript"))
|
|
else:
|
|
node_script = None
|
|
hm = row.get("hookMode")
|
|
hook_mode = str(hm).strip() if isinstance(hm, str) and str(hm).strip() else None
|
|
return HookMetadataSpec(
|
|
events=tuple(str(x).strip() for x in list(row.get("events") or []) if str(x).strip()),
|
|
always=bool(row["always"]) if isinstance(row.get("always"), bool) else None,
|
|
emoji=str(row.get("emoji")).strip() if isinstance(row.get("emoji"), str) else None,
|
|
homepage=str(row.get("homepage")).strip() if isinstance(row.get("homepage"), str) else None,
|
|
hook_key=str(row.get("hookKey")).strip() if isinstance(row.get("hookKey"), str) else None,
|
|
export=str(row.get("export")).strip() if isinstance(row.get("export"), str) else None,
|
|
os=tuple(str(x).strip() for x in list(row.get("os") or []) if str(x).strip()),
|
|
requires=_parse_requires_obj(row.get("requires")),
|
|
install=(),
|
|
hook_mode=hook_mode,
|
|
node_script=node_script,
|
|
)
|
|
|
|
|
|
def should_include_hook(
|
|
*, entry: HookEntry, config: Optional[Dict[str, Any]], eligibility: Optional[HookEligibilityContext] = None
|
|
) -> bool:
|
|
metadata = _parse_metadata_obj(entry.metadata)
|
|
hook_name = str(entry.hook.name or "").strip()
|
|
hook_key = str(metadata.hook_key or hook_name).strip() or hook_name
|
|
hook_cfg = resolve_hook_config(config, hook_key)
|
|
|
|
if not resolve_hook_enable_state(entry, config).get("enabled"):
|
|
return False
|
|
|
|
remote = (eligibility or {}).get("remote") if isinstance(eligibility, dict) else None
|
|
remote_platforms: list[str] = []
|
|
if isinstance(remote, dict):
|
|
rp = remote.get("platforms")
|
|
if isinstance(rp, list):
|
|
remote_platforms = [str(x).strip() for x in rp if str(x or "").strip()]
|
|
if not _hook_os_gate_satisfied(hook_os=metadata.os, remote_platforms=remote_platforms):
|
|
return False
|
|
|
|
req = metadata.requires or HookRequiresSpec()
|
|
bins = list(req.bins)
|
|
for b in bins:
|
|
if remote and callable(remote.get("hasBin")):
|
|
if not bool(remote["hasBin"](b)):
|
|
return False
|
|
continue
|
|
if not _has_binary(b):
|
|
return False
|
|
|
|
any_bins = list(req.any_bins)
|
|
if any_bins:
|
|
if remote and callable(remote.get("hasAnyBin")):
|
|
if not bool(remote["hasAnyBin"](any_bins)):
|
|
return False
|
|
elif not any(_has_binary(b) for b in any_bins):
|
|
return False
|
|
|
|
env_req = list(req.env)
|
|
hook_env = hook_cfg.get("env") if isinstance(hook_cfg, dict) and isinstance(hook_cfg.get("env"), dict) else {}
|
|
for env_name in env_req:
|
|
if os.getenv(env_name) or hook_env.get(env_name):
|
|
continue
|
|
return False
|
|
|
|
config_req = list(req.config)
|
|
for cfg_path in config_req:
|
|
if not _is_config_path_truthy(config, cfg_path):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def should_include_hook_compat(
|
|
*,
|
|
entry: HookEntry | Dict[str, Any],
|
|
config: Optional[Dict[str, Any]],
|
|
eligibility: Optional[HookEligibilityContext] = None,
|
|
) -> bool:
|
|
return should_include_hook(entry=ensure_hook_entry(entry), config=config, eligibility=eligibility)
|
|
|