Initial project import

This commit is contained in:
2026-08-14 09:40:51 +03:00
parent 00040e5ce4
commit d7099bf80d
146 changed files with 30509 additions and 1055 deletions
+89 -13
View File
@@ -1003,6 +1003,12 @@ ADAPTER_BASE_ID_REQUIRED_PREFIXES = (
"storage.",
"templates.",
)
AGENT_FORBIDDEN_TECHNICAL_SELECTOR_FIELDS = {
"table", "file_name", "file_names", "module_ref", "module_id", "stream_index",
"bsl_offset", "cas_key", "storage_key", "include_storage", "guid", "object_guid",
"form_guid", "extension_guid",
}
AGENT_CONFIGURATION_METHOD_PREFIXES = ("metadata.", "modules.", "code.", "templates.", "extension.")
def adapter_method_requires_base_id(method: str) -> bool:
@@ -1018,29 +1024,99 @@ def validate_adapter_call(method: str, params: dict[str, Any] | None) -> None:
if adapter_method_requires_base_id(method) and not str(params.get("base_id") or "").strip():
raise ValueError(f"adapter method {method} requires params.base_id")
def agent_technical_selector_fields(value: Any) -> list[str]:
"""Reject storage coordinates even when a caller nests them in JSON."""
found: set[str] = set()
if isinstance(value, dict):
for key, nested in value.items():
if key in AGENT_FORBIDDEN_TECHNICAL_SELECTOR_FIELDS:
found.add(key)
found.update(agent_technical_selector_fields(nested))
elif isinstance(value, list):
for nested in value:
found.update(agent_technical_selector_fields(nested))
return sorted(found)
def prepare_agent_adapter_call(method: str, params: dict[str, Any]) -> dict[str, Any]:
"""Keep the agent on public metadata selectors rather than SQL routes."""
prepared = dict(params)
diagnostic_allowed = str(os.environ.get("ONEC_AGENT_ALLOW_DIAGNOSTIC") or "").strip().casefold() in {"1", "true", "yes", "on"}
technical = agent_technical_selector_fields(prepared)
if technical and not diagnostic_allowed:
raise ValueError(
"agent adapter calls require public names/selectors; forbidden technical fields: " + ", ".join(technical)
)
if method.startswith(AGENT_CONFIGURATION_METHOD_PREFIXES):
prepared.setdefault("configuration_view", "effective_working")
prepared.setdefault("source_state", "working")
return prepared
def call_adapter(method: str, params: dict[str, Any] | None, *, base_url: str | None = None) -> dict[str, Any]:
params = params or {}
"""Call the adapter through its public MCP boundary, never its SQL REST surface."""
params = prepare_agent_adapter_call(method, params or {})
validate_adapter_call(method, params)
adapter_url = normalize_base_url(base_url or os.environ.get("ONEC_ADAPTER_URL", "http://docker-gpu.cin.su:8011"))
headers = {"Content-Type": "application/json"}
token = os.environ.get("ONEC_ADAPTER_TOKEN", "").strip()
if token:
headers["Authorization"] = f"Bearer {token}"
request = Request(
f"{adapter_url}/rpc",
data=json.dumps({"method": method, "payload": params}, ensure_ascii=False).encode("utf-8"),
mcp_url = normalize_base_url(base_url or os.environ.get("ONEC_MCP_URL", "http://docker.cin.su:8021"))
headers = {"Content-Type": "application/json", "Accept": "application/json, text/event-stream"}
initialize = {
"jsonrpc": "2.0",
"id": f"onec-agent-init-{uuid.uuid4().hex}",
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "onec-agent", "version": "1"},
},
}
init_request = Request(
f"{mcp_url}/mcp",
data=json.dumps(initialize, ensure_ascii=False).encode("utf-8"),
headers=headers,
method="POST",
)
try:
with urlopen(init_request, timeout=30) as response:
init_raw = json.loads(response.read().decode("utf-8"))
session_id = response.headers.get("Mcp-Session-Id")
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise ValueError(f"MCP initialize returned HTTP {exc.code}: {body}") from exc
if not isinstance(init_raw, dict) or not isinstance(init_raw.get("result"), dict):
raise ValueError("MCP initialize response is not JSON-RPC success")
call_headers = dict(headers)
if session_id:
call_headers["Mcp-Session-Id"] = session_id
call = {
"jsonrpc": "2.0",
"id": f"onec-agent-call-{uuid.uuid4().hex}",
"method": "tools/call",
"params": {"name": "onec_request", "arguments": {"method": method, "payload": params}},
}
request = Request(
f"{mcp_url}/mcp",
data=json.dumps(call, ensure_ascii=False).encode("utf-8"),
headers=call_headers,
method="POST",
)
try:
with urlopen(request, timeout=120) as response:
raw = json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise ValueError(f"adapter returned HTTP {exc.code}: {body}") from exc
if not isinstance(raw, dict):
raise ValueError("adapter response is not JSON")
return raw
raise ValueError(f"MCP tool call returned HTTP {exc.code}: {body}") from exc
result = raw.get("result") if isinstance(raw, dict) and isinstance(raw.get("result"), dict) else None
content = result.get("content") if isinstance(result, dict) and isinstance(result.get("content"), list) else []
text = content[0].get("text") if content and isinstance(content[0], dict) else None
if not isinstance(text, str):
raise ValueError("MCP tool response has no JSON text content")
try:
decoded = json.loads(text)
except json.JSONDecodeError as exc:
raise ValueError("MCP tool response text is not JSON") from exc
if not isinstance(decoded, dict):
raise ValueError("MCP tool response payload is not an object")
return decoded
class AgentHandler(BaseHTTPRequestHandler):