mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 03:30:48 +08:00
169 lines
5.8 KiB
Python
169 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from .shared_types import GatewayRequestHandlers
|
|
from .validation import assert_valid_params, error_shape
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class _ConfigSnapshot:
|
|
exists: bool
|
|
valid: bool
|
|
config: dict[str, Any] | None
|
|
raw: str | None = None
|
|
|
|
|
|
def _read_config_file_snapshot() -> _ConfigSnapshot:
|
|
"""Staging adapter.
|
|
|
|
The upstream TS version reads JSON5 from Oclaw config root.
|
|
Here we keep a minimal in-memory placeholder until full config I/O is ported.
|
|
"""
|
|
return _ConfigSnapshot(exists=False, valid=True, config={})
|
|
|
|
|
|
def _load_schema_with_plugins() -> dict[str, Any]:
|
|
# Placeholder for `loadGatewayRuntimeConfigSchema`.
|
|
return {"schema": "stub", "uiHints": {}}
|
|
|
|
|
|
def _validate_config_get_params(params: Any) -> bool:
|
|
return isinstance(params, dict) or params is None
|
|
|
|
|
|
def _validate_config_schema_params(params: Any) -> bool:
|
|
return isinstance(params, dict) or params is None
|
|
|
|
|
|
def _validate_config_schema_lookup_params(params: Any) -> bool:
|
|
return isinstance(params, dict) and isinstance(params.get("path"), str) and bool(params["path"].strip())
|
|
|
|
|
|
def _validate_config_set_params(params: Any) -> bool:
|
|
return isinstance(params, dict) and isinstance(params.get("raw"), str)
|
|
|
|
|
|
def _validate_config_patch_params(params: Any) -> bool:
|
|
return isinstance(params, dict) and isinstance(params.get("raw"), str)
|
|
|
|
|
|
def _validate_config_apply_params(params: Any) -> bool:
|
|
return isinstance(params, dict) and isinstance(params.get("raw"), str)
|
|
|
|
|
|
def _config_get_handler(opts: dict[str, Any]) -> Any:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
if not callable(respond):
|
|
return None
|
|
if not assert_valid_params(params, _validate_config_get_params, "config.get", respond):
|
|
return None
|
|
snapshot = _read_config_file_snapshot()
|
|
schema = _load_schema_with_plugins()
|
|
respond(True, {"snapshot": snapshot.config, "schema": schema}, None, None)
|
|
return None
|
|
|
|
|
|
def _config_schema_handler(opts: dict[str, Any]) -> Any:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
if not callable(respond):
|
|
return None
|
|
if not assert_valid_params(params, _validate_config_schema_params, "config.schema", respond):
|
|
return None
|
|
respond(True, _load_schema_with_plugins(), None, None)
|
|
return None
|
|
|
|
|
|
def _config_schema_lookup_handler(opts: dict[str, Any]) -> Any:
|
|
respond = opts.get("respond")
|
|
context = opts.get("context") or {}
|
|
params = opts.get("params")
|
|
if not callable(respond):
|
|
return None
|
|
if not assert_valid_params(params, _validate_config_schema_lookup_params, "config.schema.lookup", respond):
|
|
return None
|
|
path = str((params or {}).get("path") or "").strip()
|
|
schema = _load_schema_with_plugins()
|
|
# Minimal lookup: dotted path fetch from schema dict.
|
|
cur: Any = schema
|
|
for seg in path.split("."):
|
|
if not isinstance(cur, dict) or seg not in cur:
|
|
respond(False, None, error_shape("INVALID_REQUEST", "config schema path not found"), None)
|
|
return None
|
|
cur = cur[seg]
|
|
respond(True, {"path": path, "value": cur}, None, None)
|
|
_ = context
|
|
return None
|
|
|
|
|
|
def _parse_raw_json_or_error(raw: str) -> tuple[bool, dict[str, Any] | None, str | None]:
|
|
try:
|
|
obj = json.loads(raw)
|
|
except Exception as exc:
|
|
return (False, None, f"invalid json: {exc}")
|
|
if not isinstance(obj, dict):
|
|
return (False, None, "raw must be a json object")
|
|
return (True, obj, None)
|
|
|
|
|
|
def _config_set_like_handler(opts: dict[str, Any], method: str) -> Any:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
if not callable(respond):
|
|
return None
|
|
validator = _validate_config_set_params if method == "config.set" else _validate_config_apply_params
|
|
if not assert_valid_params(params, validator, method, respond):
|
|
return None
|
|
raw = str((params or {}).get("raw") or "")
|
|
ok, obj, err = _parse_raw_json_or_error(raw)
|
|
if not ok or obj is None:
|
|
respond(False, None, error_shape("INVALID_REQUEST", err or "invalid config"), None)
|
|
return None
|
|
respond(True, {"ok": True, "config": obj}, None, None)
|
|
return None
|
|
|
|
|
|
def _config_patch_handler(opts: dict[str, Any]) -> Any:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
if not callable(respond):
|
|
return None
|
|
if not assert_valid_params(params, _validate_config_patch_params, "config.patch", respond):
|
|
return None
|
|
# Staging patch: treat raw as full next config.
|
|
raw = str((params or {}).get("raw") or "")
|
|
ok, obj, err = _parse_raw_json_or_error(raw)
|
|
if not ok or obj is None:
|
|
respond(False, None, error_shape("INVALID_REQUEST", err or "invalid config"), None)
|
|
return None
|
|
respond(True, {"ok": True, "noop": False, "config": obj}, None, None)
|
|
return None
|
|
|
|
|
|
def _config_open_file_handler(opts: dict[str, Any]) -> Any:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
if not callable(respond):
|
|
return None
|
|
if not assert_valid_params(params, _validate_config_get_params, "config.openFile", respond):
|
|
return None
|
|
config_path = os.getenv("OCLAW_CONFIG_PATH") or "oclaw.json"
|
|
respond(True, {"ok": True, "path": config_path}, None, None)
|
|
return None
|
|
|
|
|
|
config_handlers: GatewayRequestHandlers = {
|
|
"config.get": _config_get_handler,
|
|
"config.schema": _config_schema_handler,
|
|
"config.schema.lookup": _config_schema_lookup_handler,
|
|
"config.set": lambda opts: _config_set_like_handler(opts, "config.set"),
|
|
"config.patch": _config_patch_handler,
|
|
"config.apply": lambda opts: _config_set_like_handler(opts, "config.apply"),
|
|
"config.openFile": _config_open_file_handler,
|
|
}
|
|
|