mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 07:20:44 +08:00
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
195 lines
6 KiB
Python
195 lines
6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class HookInstallSpec:
|
|
kind: str
|
|
id: str | None = None
|
|
label: str | None = None
|
|
package: str | None = None
|
|
repository: str | None = None
|
|
bins: tuple[str, ...] = ()
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
out: dict[str, Any] = {"kind": self.kind}
|
|
if self.id:
|
|
out["id"] = self.id
|
|
if self.label:
|
|
out["label"] = self.label
|
|
if self.package:
|
|
out["package"] = self.package
|
|
if self.repository:
|
|
out["repository"] = self.repository
|
|
if self.bins:
|
|
out["bins"] = list(self.bins)
|
|
return out
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class HookRequiresSpec:
|
|
bins: tuple[str, ...] = ()
|
|
any_bins: tuple[str, ...] = ()
|
|
env: tuple[str, ...] = ()
|
|
config: tuple[str, ...] = ()
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
out: dict[str, Any] = {}
|
|
if self.bins:
|
|
out["bins"] = list(self.bins)
|
|
if self.any_bins:
|
|
out["anyBins"] = list(self.any_bins)
|
|
if self.env:
|
|
out["env"] = list(self.env)
|
|
if self.config:
|
|
out["config"] = list(self.config)
|
|
return out
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class HookMetadataSpec:
|
|
events: tuple[str, ...] = ()
|
|
always: bool | None = None
|
|
emoji: str | None = None
|
|
homepage: str | None = None
|
|
hook_key: str | None = None
|
|
export: str | None = None
|
|
os: tuple[str, ...] = ()
|
|
requires: HookRequiresSpec | None = None
|
|
install: tuple[HookInstallSpec, ...] = ()
|
|
hook_mode: str | None = None
|
|
node_script: bool | None = None
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
out: dict[str, Any] = {"events": list(self.events)}
|
|
if self.always is not None:
|
|
out["always"] = bool(self.always)
|
|
if self.emoji:
|
|
out["emoji"] = self.emoji
|
|
if self.homepage:
|
|
out["homepage"] = self.homepage
|
|
if self.hook_key:
|
|
out["hookKey"] = self.hook_key
|
|
if self.export:
|
|
out["export"] = self.export
|
|
if self.os:
|
|
out["os"] = list(self.os)
|
|
if self.requires:
|
|
req = self.requires.as_dict()
|
|
if req:
|
|
out["requires"] = req
|
|
if self.install:
|
|
out["install"] = [row.as_dict() for row in self.install]
|
|
if self.hook_mode is not None:
|
|
out["hookMode"] = str(self.hook_mode)
|
|
if self.node_script is not None:
|
|
out["nodeScript"] = bool(self.node_script)
|
|
return out
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ParsedHookManifest:
|
|
name: str
|
|
description: str
|
|
invocation_enabled: bool
|
|
metadata: HookMetadataSpec = field(default_factory=HookMetadataSpec)
|
|
|
|
|
|
def _read_str(value: Any) -> str | None:
|
|
if isinstance(value, str):
|
|
s = value.strip()
|
|
return s if s else None
|
|
return None
|
|
|
|
|
|
def _normalize_str_list(value: Any) -> tuple[str, ...]:
|
|
if not isinstance(value, list):
|
|
return ()
|
|
out: list[str] = []
|
|
for row in value:
|
|
s = _read_str(row)
|
|
if s:
|
|
out.append(s)
|
|
return tuple(out)
|
|
|
|
|
|
def _parse_install_spec(row: Any) -> HookInstallSpec | None:
|
|
if not isinstance(row, dict):
|
|
return None
|
|
kind = (_read_str(row.get("kind")) or "").lower()
|
|
if kind not in {"bundled", "npm", "git"}:
|
|
return None
|
|
return HookInstallSpec(
|
|
kind=kind,
|
|
id=_read_str(row.get("id")),
|
|
label=_read_str(row.get("label")),
|
|
package=_read_str(row.get("package")),
|
|
repository=_read_str(row.get("repository")),
|
|
bins=_normalize_str_list(row.get("bins")),
|
|
)
|
|
|
|
|
|
def _resolve_raw_oclaw_metadata(frontmatter: dict[str, Any]) -> dict[str, Any]:
|
|
meta = frontmatter.get("metadata")
|
|
if isinstance(meta, str):
|
|
try:
|
|
parsed = json.loads(meta)
|
|
meta = parsed if isinstance(parsed, dict) else None
|
|
except Exception:
|
|
meta = None
|
|
if not isinstance(meta, dict):
|
|
return {}
|
|
oc = meta.get("oclaw")
|
|
return oc if isinstance(oc, dict) else {}
|
|
|
|
|
|
def parse_hook_manifest(*, frontmatter: dict[str, Any], default_name: str) -> ParsedHookManifest:
|
|
name = _read_str(frontmatter.get("name")) or default_name
|
|
description = _read_str(frontmatter.get("description")) or ""
|
|
enabled_raw = frontmatter.get("enabled")
|
|
invocation_enabled = bool(enabled_raw) if isinstance(enabled_raw, bool) else True
|
|
|
|
oc = _resolve_raw_oclaw_metadata(frontmatter)
|
|
requires_obj = oc.get("requires") if isinstance(oc.get("requires"), dict) else {}
|
|
requires = HookRequiresSpec(
|
|
bins=_normalize_str_list(requires_obj.get("bins")),
|
|
any_bins=_normalize_str_list(requires_obj.get("anyBins")),
|
|
env=_normalize_str_list(requires_obj.get("env")),
|
|
config=_normalize_str_list(requires_obj.get("config")),
|
|
)
|
|
install_rows: list[HookInstallSpec] = []
|
|
if isinstance(oc.get("install"), list):
|
|
for row in oc["install"]:
|
|
parsed = _parse_install_spec(row)
|
|
if parsed:
|
|
install_rows.append(parsed)
|
|
|
|
node_script: bool | None
|
|
if "nodeScript" in oc:
|
|
node_script = bool(oc.get("nodeScript"))
|
|
else:
|
|
node_script = None
|
|
|
|
metadata = HookMetadataSpec(
|
|
events=_normalize_str_list(oc.get("events")),
|
|
always=bool(oc["always"]) if isinstance(oc.get("always"), bool) else None,
|
|
emoji=_read_str(oc.get("emoji")),
|
|
homepage=_read_str(oc.get("homepage")),
|
|
hook_key=_read_str(oc.get("hookKey")),
|
|
export=_read_str(oc.get("export")),
|
|
os=_normalize_str_list(oc.get("os")),
|
|
requires=requires if requires.as_dict() else None,
|
|
install=tuple(install_rows),
|
|
hook_mode=_read_str(oc.get("hookMode")),
|
|
node_script=node_script,
|
|
)
|
|
return ParsedHookManifest(
|
|
name=name,
|
|
description=description,
|
|
invocation_enabled=invocation_enabled,
|
|
metadata=metadata,
|
|
)
|
|
|