refactor: update imports to use provisioning_db and gateway_rpc modules

This commit is contained in:
Abhimanyu Saharan
2026-02-11 00:00:19 +05:30
parent 997d21c913
commit b038d0df4c
22 changed files with 1391 additions and 1438 deletions

View File

@@ -1,6 +1,6 @@
"""OpenClaw lifecycle package.
Import concrete modules directly (for example: ``app.services.openclaw.agent_service``)
Import concrete modules directly (for example: ``app.services.openclaw.provisioning_db``)
to keep architectural boundaries explicit.
"""

View File

@@ -14,11 +14,6 @@ from app.core.agent_tokens import generate_agent_token, hash_agent_token
from app.core.auth import AuthContext
from app.core.time import utcnow
from app.db import crud
from app.integrations.openclaw_gateway import GatewayConfig as GatewayClientConfig
from app.integrations.openclaw_gateway import (
OpenClawGatewayError,
openclaw_call,
)
from app.models.activity_events import ActivityEvent
from app.models.agents import Agent
from app.models.approvals import Approval
@@ -26,6 +21,8 @@ from app.models.gateways import Gateway
from app.models.tasks import Task
from app.schemas.gateways import GatewayTemplatesSyncResult
from app.services.openclaw.constants import DEFAULT_HEARTBEAT_CONFIG
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
from app.services.openclaw.gateway_rpc import OpenClawGatewayError, openclaw_call
from app.services.openclaw.provisioning import OpenClawGatewayProvisioner
from app.services.openclaw.provisioning_db import (
GatewayTemplateSyncOptions,

File diff suppressed because it is too large Load Diff

View File

@@ -14,8 +14,6 @@ from sqlmodel import col, select
from app.core.config import settings
from app.core.time import utcnow
from app.integrations.openclaw_gateway import GatewayConfig as GatewayClientConfig
from app.integrations.openclaw_gateway import OpenClawGatewayError, openclaw_call
from app.models.agents import Agent
from app.models.boards import Board
from app.models.gateways import Gateway
@@ -34,6 +32,8 @@ from app.services.openclaw.exceptions import (
map_gateway_error_message,
map_gateway_error_to_http_exception,
)
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
from app.services.openclaw.gateway_rpc import OpenClawGatewayError, openclaw_call
from app.services.openclaw.internal import agent_key, with_coordination_gateway_retry
from app.services.openclaw.policies import OpenClawAuthorizationPolicy
from app.services.openclaw.provisioning_db import (

View File

@@ -0,0 +1,317 @@
"""OpenClaw gateway websocket RPC client and protocol constants.
This is the low-level, DB-free interface for talking to the OpenClaw gateway.
Keep gateway RPC protocol details and client helpers here so OpenClaw services
operate within a single scope (no `app.integrations.*` plumbing).
"""
from __future__ import annotations
import asyncio
import json
from dataclasses import dataclass
from typing import Any
from urllib.parse import urlencode, urlparse, urlunparse
from uuid import uuid4
import websockets
from websockets.exceptions import WebSocketException
PROTOCOL_VERSION = 3
# NOTE: These are the base gateway methods from the OpenClaw gateway repo.
# The gateway can expose additional methods at runtime via channel plugins.
GATEWAY_METHODS = [
"health",
"logs.tail",
"channels.status",
"channels.logout",
"status",
"usage.status",
"usage.cost",
"tts.status",
"tts.providers",
"tts.enable",
"tts.disable",
"tts.convert",
"tts.setProvider",
"config.get",
"config.set",
"config.apply",
"config.patch",
"config.schema",
"exec.approvals.get",
"exec.approvals.set",
"exec.approvals.node.get",
"exec.approvals.node.set",
"exec.approval.request",
"exec.approval.resolve",
"wizard.start",
"wizard.next",
"wizard.cancel",
"wizard.status",
"talk.mode",
"models.list",
"agents.list",
"agents.create",
"agents.update",
"agents.delete",
"agents.files.list",
"agents.files.get",
"agents.files.set",
"skills.status",
"skills.bins",
"skills.install",
"skills.update",
"update.run",
"voicewake.get",
"voicewake.set",
"sessions.list",
"sessions.preview",
"sessions.patch",
"sessions.reset",
"sessions.delete",
"sessions.compact",
"last-heartbeat",
"set-heartbeats",
"wake",
"node.pair.request",
"node.pair.list",
"node.pair.approve",
"node.pair.reject",
"node.pair.verify",
"device.pair.list",
"device.pair.approve",
"device.pair.reject",
"device.token.rotate",
"device.token.revoke",
"node.rename",
"node.list",
"node.describe",
"node.invoke",
"node.invoke.result",
"node.event",
"cron.list",
"cron.status",
"cron.add",
"cron.update",
"cron.remove",
"cron.run",
"cron.runs",
"system-presence",
"system-event",
"send",
"agent",
"agent.identity.get",
"agent.wait",
"browser.request",
"chat.history",
"chat.abort",
"chat.send",
]
GATEWAY_EVENTS = [
"connect.challenge",
"agent",
"chat",
"presence",
"tick",
"talk.mode",
"shutdown",
"health",
"heartbeat",
"cron",
"node.pair.requested",
"node.pair.resolved",
"node.invoke.request",
"device.pair.requested",
"device.pair.resolved",
"voicewake.changed",
"exec.approval.requested",
"exec.approval.resolved",
]
GATEWAY_METHODS_SET = frozenset(GATEWAY_METHODS)
GATEWAY_EVENTS_SET = frozenset(GATEWAY_EVENTS)
def is_known_gateway_method(method: str) -> bool:
"""Return whether a method name is part of the known base gateway methods."""
return method in GATEWAY_METHODS_SET
class OpenClawGatewayError(RuntimeError):
"""Raised when OpenClaw gateway calls fail."""
@dataclass(frozen=True)
class GatewayConfig:
"""Connection configuration for the OpenClaw gateway."""
url: str
token: str | None = None
def _build_gateway_url(config: GatewayConfig) -> str:
base_url: str = (config.url or "").strip()
if not base_url:
message = "Gateway URL is not configured."
raise OpenClawGatewayError(message)
token = config.token
if not token:
return base_url
parsed = urlparse(base_url)
query = urlencode({"token": token})
return str(urlunparse(parsed._replace(query=query)))
async def _await_response(
ws: websockets.ClientConnection,
request_id: str,
) -> object:
while True:
raw = await ws.recv()
data = json.loads(raw)
if data.get("type") == "res" and data.get("id") == request_id:
ok = data.get("ok")
if ok is not None and not ok:
error = data.get("error", {}).get("message", "Gateway error")
raise OpenClawGatewayError(error)
return data.get("payload")
if data.get("id") == request_id:
if data.get("error"):
message = data["error"].get("message", "Gateway error")
raise OpenClawGatewayError(message)
return data.get("result")
async def _send_request(
ws: websockets.ClientConnection,
method: str,
params: dict[str, Any] | None,
) -> object:
request_id = str(uuid4())
message = {
"type": "req",
"id": request_id,
"method": method,
"params": params or {},
}
await ws.send(json.dumps(message))
return await _await_response(ws, request_id)
def _build_connect_params(config: GatewayConfig) -> dict[str, Any]:
params: dict[str, Any] = {
"minProtocol": PROTOCOL_VERSION,
"maxProtocol": PROTOCOL_VERSION,
"client": {
"id": "gateway-client",
"version": "1.0.0",
"platform": "web",
"mode": "ui",
},
}
if config.token:
params["auth"] = {"token": config.token}
return params
async def _ensure_connected(
ws: websockets.ClientConnection,
first_message: str | bytes | None,
config: GatewayConfig,
) -> None:
if first_message:
if isinstance(first_message, bytes):
first_message = first_message.decode("utf-8")
data = json.loads(first_message)
if data.get("type") != "event" or data.get("event") != "connect.challenge":
pass
connect_id = str(uuid4())
response = {
"type": "req",
"id": connect_id,
"method": "connect",
"params": _build_connect_params(config),
}
await ws.send(json.dumps(response))
await _await_response(ws, connect_id)
async def openclaw_call(
method: str,
params: dict[str, Any] | None = None,
*,
config: GatewayConfig,
) -> object:
"""Call a gateway RPC method and return the result payload."""
gateway_url = _build_gateway_url(config)
try:
async with websockets.connect(gateway_url, ping_interval=None) as ws:
first_message = None
try:
first_message = await asyncio.wait_for(ws.recv(), timeout=2)
except TimeoutError:
first_message = None
await _ensure_connected(ws, first_message, config)
return await _send_request(ws, method, params)
except OpenClawGatewayError:
raise
except (
TimeoutError,
ConnectionError,
OSError,
ValueError,
WebSocketException,
) as exc: # pragma: no cover - network/protocol errors
raise OpenClawGatewayError(str(exc)) from exc
async def send_message(
message: str,
*,
session_key: str,
config: GatewayConfig,
deliver: bool = False,
) -> object:
"""Send a chat message to a session."""
params: dict[str, Any] = {
"sessionKey": session_key,
"message": message,
"deliver": deliver,
"idempotencyKey": str(uuid4()),
}
return await openclaw_call("chat.send", params, config=config)
async def get_chat_history(
session_key: str,
config: GatewayConfig,
limit: int | None = None,
) -> object:
"""Fetch chat history for a session."""
params: dict[str, Any] = {"sessionKey": session_key}
if limit is not None:
params["limit"] = limit
return await openclaw_call("chat.history", params, config=config)
async def delete_session(session_key: str, *, config: GatewayConfig) -> object:
"""Delete a session by key."""
return await openclaw_call("sessions.delete", {"key": session_key}, config=config)
async def ensure_session(
session_key: str,
*,
config: GatewayConfig,
label: str | None = None,
) -> object:
"""Ensure a session exists and optionally update its label."""
params: dict[str, Any] = {"key": session_key}
if label:
params["label"] = label
return await openclaw_call("sessions.patch", params, config=config)

View File

@@ -6,7 +6,6 @@ import asyncio
from collections.abc import Awaitable, Callable
from typing import TypeVar
from app.integrations.openclaw_gateway import OpenClawGatewayError
from app.services.openclaw.constants import (
_COORDINATION_GATEWAY_BASE_DELAY_S,
_COORDINATION_GATEWAY_MAX_DELAY_S,
@@ -15,6 +14,7 @@ from app.services.openclaw.constants import (
_SECURE_RANDOM,
_TRANSIENT_GATEWAY_ERROR_MARKERS,
)
from app.services.openclaw.gateway_rpc import OpenClawGatewayError
_T = TypeVar("_T")

View File

@@ -2,11 +2,11 @@
from __future__ import annotations
from app.integrations.openclaw_gateway import OpenClawGatewayError
from app.models.board_onboarding import BoardOnboardingSession
from app.models.boards import Board
from app.services.openclaw.coordination_service import AbstractGatewayMessagingService
from app.services.openclaw.exceptions import GatewayOperation, map_gateway_error_to_http_exception
from app.services.openclaw.gateway_rpc import OpenClawGatewayError
from app.services.openclaw.shared import (
GatewayAgentIdentity,
require_gateway_config_for_board,

View File

@@ -18,13 +18,6 @@ from uuid import uuid4
from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape
from app.core.config import settings
from app.integrations.openclaw_gateway import GatewayConfig as GatewayClientConfig
from app.integrations.openclaw_gateway import (
OpenClawGatewayError,
ensure_session,
openclaw_call,
send_message,
)
from app.models.agents import Agent
from app.models.boards import Board
from app.models.gateways import Gateway
@@ -40,6 +33,13 @@ from app.services.openclaw.constants import (
MAIN_TEMPLATE_MAP,
PRESERVE_AGENT_EDITABLE_FILES,
)
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
from app.services.openclaw.gateway_rpc import (
OpenClawGatewayError,
ensure_session,
openclaw_call,
send_message,
)
from app.services.openclaw.internal import agent_key as _agent_key
from app.services.openclaw.shared import GatewayAgentIdentity

File diff suppressed because it is too large Load Diff

View File

@@ -11,14 +11,6 @@ from uuid import UUID
from fastapi import HTTPException, status
from sqlmodel import col
from app.integrations.openclaw_gateway import GatewayConfig as GatewayClientConfig
from app.integrations.openclaw_gateway import (
OpenClawGatewayError,
ensure_session,
get_chat_history,
openclaw_call,
send_message,
)
from app.models.agents import Agent
from app.models.boards import Board
from app.models.gateways import Gateway
@@ -30,6 +22,14 @@ from app.schemas.gateway_api import (
GatewaySessionsResponse,
GatewaysStatusResponse,
)
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
from app.services.openclaw.gateway_rpc import (
OpenClawGatewayError,
ensure_session,
get_chat_history,
openclaw_call,
send_message,
)
from app.services.openclaw.policies import OpenClawAuthorizationPolicy
from app.services.organizations import require_board_access

View File

@@ -8,8 +8,6 @@ from uuid import UUID, uuid4
from fastapi import HTTPException, status
from app.integrations.openclaw_gateway import GatewayConfig as _GatewayClientConfig
from app.integrations.openclaw_gateway import OpenClawGatewayError, ensure_session, send_message
from app.models.boards import Board
from app.models.gateways import Gateway
from app.services.openclaw.constants import (
@@ -17,6 +15,8 @@ from app.services.openclaw.constants import (
_GATEWAY_AGENT_SUFFIX,
_GATEWAY_OPENCLAW_AGENT_PREFIX,
)
from app.services.openclaw.gateway_rpc import GatewayConfig as _GatewayClientConfig
from app.services.openclaw.gateway_rpc import OpenClawGatewayError, ensure_session, send_message
if TYPE_CHECKING:
from sqlmodel.ext.asyncio.session import AsyncSession