mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-08 22:20:54 +08:00
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。 Made-with: Cursor
315 lines
11 KiB
Python
315 lines
11 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from oclaw.runtime.agents.agent_scope import resolve_default_agent_id
|
|
|
|
from .shared_types import GatewayRequestHandlers
|
|
from .telegram_send_normalize import normalize_transport_target_for_channel
|
|
from .validation import error_shape
|
|
|
|
|
|
def _ok(respond, payload: dict[str, Any] | None = None) -> None:
|
|
if callable(respond):
|
|
respond(True, payload or {"ok": True}, None, None)
|
|
|
|
|
|
def _bad(respond, message: str) -> None:
|
|
if callable(respond):
|
|
respond(False, None, error_shape("INVALID_REQUEST", message), None)
|
|
|
|
|
|
def _normalize_session_key(value: Any) -> str | None:
|
|
if isinstance(value, str) and value.strip():
|
|
return value.strip()
|
|
return None
|
|
|
|
|
|
def _extract_session_key(params: Any) -> str | None:
|
|
if not isinstance(params, dict):
|
|
return None
|
|
return _normalize_session_key(params.get("sessionKey")) or _normalize_session_key(params.get("key"))
|
|
|
|
|
|
def _require_session_key(params: Any, respond) -> str | None:
|
|
if not isinstance(params, dict):
|
|
_bad(respond, "params must be object")
|
|
return None
|
|
key = _extract_session_key(params)
|
|
if not key:
|
|
_bad(respond, "sessionKey (or key) is required")
|
|
return None
|
|
return key
|
|
|
|
|
|
def _context_subscribers(context: Any, name: str) -> set[str]:
|
|
if not isinstance(context, dict):
|
|
return set()
|
|
value = context.get(name)
|
|
if isinstance(value, set):
|
|
return value
|
|
created: set[str] = set()
|
|
context[name] = created
|
|
return created
|
|
|
|
|
|
def _sessions_list_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
context = opts.get("context")
|
|
sessions = []
|
|
if isinstance(context, dict):
|
|
list_fn = context.get("list_sessions")
|
|
if callable(list_fn):
|
|
try:
|
|
rows = list_fn()
|
|
if isinstance(rows, list):
|
|
sessions = [x for x in rows if isinstance(x, dict)]
|
|
except Exception:
|
|
sessions = []
|
|
_ok(respond, {"sessions": sessions, "total": len(sessions)})
|
|
|
|
|
|
def _sessions_subscribe_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
client = opts.get("client") or {}
|
|
context = opts.get("context")
|
|
conn_id = client.get("conn_id") if isinstance(client, dict) else None
|
|
if isinstance(conn_id, str) and conn_id.strip():
|
|
_context_subscribers(context, "session_event_subscribers").add(conn_id.strip())
|
|
_ok(respond, {"subscribed": True, "connId": conn_id})
|
|
|
|
|
|
def _sessions_unsubscribe_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
client = opts.get("client") or {}
|
|
context = opts.get("context")
|
|
conn_id = client.get("conn_id") if isinstance(client, dict) else None
|
|
if isinstance(conn_id, str) and conn_id.strip():
|
|
_context_subscribers(context, "session_event_subscribers").discard(conn_id.strip())
|
|
_ok(respond, {"subscribed": False, "connId": conn_id})
|
|
|
|
|
|
def _sessions_messages_subscribe_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
client = opts.get("client") or {}
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
conn_id = client.get("conn_id") if isinstance(client, dict) else None
|
|
if isinstance(conn_id, str) and conn_id.strip() and isinstance(context, dict):
|
|
message_subscribers = context.get("session_message_subscribers")
|
|
if not isinstance(message_subscribers, dict):
|
|
message_subscribers = {}
|
|
context["session_message_subscribers"] = message_subscribers
|
|
bucket = message_subscribers.get(key)
|
|
if not isinstance(bucket, set):
|
|
bucket = set()
|
|
message_subscribers[key] = bucket
|
|
bucket.add(conn_id.strip())
|
|
_ok(respond, {"sessionKey": key, "messagesSubscribed": True, "connId": conn_id})
|
|
|
|
|
|
def _sessions_messages_unsubscribe_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
client = opts.get("client") or {}
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
conn_id = client.get("conn_id") if isinstance(client, dict) else None
|
|
if isinstance(conn_id, str) and conn_id.strip() and isinstance(context, dict):
|
|
message_subscribers = context.get("session_message_subscribers")
|
|
if isinstance(message_subscribers, dict):
|
|
bucket = message_subscribers.get(key)
|
|
if isinstance(bucket, set):
|
|
bucket.discard(conn_id.strip())
|
|
_ok(respond, {"sessionKey": key, "messagesSubscribed": False, "connId": conn_id})
|
|
|
|
|
|
def _sessions_preview_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"preview": []})
|
|
|
|
|
|
def _sessions_resolve_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
_ok(respond, {"sessionKey": key, "resolved": True})
|
|
|
|
|
|
def _sessions_compaction_list_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"checkpoints": []})
|
|
|
|
|
|
def _sessions_compaction_get_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"checkpoint": None})
|
|
|
|
|
|
def _sessions_create_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
key = _extract_session_key(params) or "main"
|
|
agent_id = ""
|
|
if isinstance(params, dict):
|
|
agent_id = str(params.get("agentId") or "").strip()
|
|
cfg = params.get("config") if isinstance(params.get("config"), dict) else {}
|
|
if not agent_id and isinstance(cfg, dict) and cfg:
|
|
agent_id = resolve_default_agent_id(cfg)
|
|
if agent_id and ":" not in key:
|
|
key = f"{agent_id}:{key}"
|
|
session = {"sessionKey": key}
|
|
if isinstance(context, dict):
|
|
create_fn = context.get("create_session")
|
|
if callable(create_fn):
|
|
try:
|
|
created = create_fn(key, params if isinstance(params, dict) else {})
|
|
if isinstance(created, dict):
|
|
session = created
|
|
key = _normalize_session_key(created.get("sessionKey")) or key
|
|
except Exception:
|
|
pass
|
|
_ok(respond, {"sessionKey": key, "created": True, "session": session})
|
|
|
|
|
|
def _sessions_compaction_branch_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"branched": True})
|
|
|
|
|
|
def _sessions_compaction_restore_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"restored": True})
|
|
|
|
|
|
def _sessions_send_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
message = params.get("message") if isinstance(params, dict) else None
|
|
if not isinstance(message, str) or not message.strip():
|
|
_bad(respond, "message is required")
|
|
return
|
|
payload: dict[str, Any] = {"sent": True, "sessionKey": key, "message": message.strip()}
|
|
if isinstance(params, dict):
|
|
channel = params.get("channel")
|
|
to = params.get("to")
|
|
if isinstance(channel, str) and channel.strip().lower() == "telegram" and isinstance(to, str) and to.strip():
|
|
normalized_to, extra = normalize_transport_target_for_channel(
|
|
channel="telegram",
|
|
to=to.strip(),
|
|
params=params,
|
|
)
|
|
payload.update({"channel": "telegram", "to": normalized_to, **extra})
|
|
if isinstance(context, dict):
|
|
send_fn = context.get("enqueue_session_send")
|
|
if callable(send_fn):
|
|
try:
|
|
out = send_fn(key, message.strip(), dict(params or {}))
|
|
if isinstance(out, dict):
|
|
payload.update(out)
|
|
except Exception as exc:
|
|
_bad(respond, f"sessions.send failed: {exc}")
|
|
return
|
|
_ok(respond, payload)
|
|
|
|
|
|
def _sessions_steer_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"steered": True})
|
|
|
|
|
|
def _sessions_abort_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
_ok(respond, {"aborted": True, "sessionKey": key})
|
|
|
|
|
|
def _sessions_patch_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
_ok(respond, {"patched": True, "sessionKey": key})
|
|
|
|
|
|
def _sessions_reset_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
_ok(respond, {"reset": True, "sessionKey": key})
|
|
|
|
|
|
def _sessions_delete_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
_ok(respond, {"deleted": True, "sessionKey": key})
|
|
|
|
|
|
def _sessions_get_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
key = _require_session_key(params, respond)
|
|
if not key:
|
|
return
|
|
session: dict[str, Any] = {}
|
|
if isinstance(context, dict):
|
|
get_fn = context.get("get_session")
|
|
if callable(get_fn):
|
|
try:
|
|
loaded = get_fn(key)
|
|
if isinstance(loaded, dict):
|
|
session = loaded
|
|
except Exception:
|
|
session = {}
|
|
_ok(respond, {"sessionKey": key, "session": session})
|
|
|
|
|
|
def _sessions_compact_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
_ok(respond, {"compacted": True})
|
|
|
|
|
|
sessions_handlers: GatewayRequestHandlers = {
|
|
"sessions.list": _sessions_list_handler,
|
|
"sessions.subscribe": _sessions_subscribe_handler,
|
|
"sessions.unsubscribe": _sessions_unsubscribe_handler,
|
|
"sessions.messages.subscribe": _sessions_messages_subscribe_handler,
|
|
"sessions.messages.unsubscribe": _sessions_messages_unsubscribe_handler,
|
|
"sessions.preview": _sessions_preview_handler,
|
|
"sessions.resolve": _sessions_resolve_handler,
|
|
"sessions.compaction.list": _sessions_compaction_list_handler,
|
|
"sessions.compaction.get": _sessions_compaction_get_handler,
|
|
"sessions.create": _sessions_create_handler,
|
|
"sessions.compaction.branch": _sessions_compaction_branch_handler,
|
|
"sessions.compaction.restore": _sessions_compaction_restore_handler,
|
|
"sessions.send": _sessions_send_handler,
|
|
"sessions.steer": _sessions_steer_handler,
|
|
"sessions.abort": _sessions_abort_handler,
|
|
"sessions.patch": _sessions_patch_handler,
|
|
"sessions.reset": _sessions_reset_handler,
|
|
"sessions.delete": _sessions_delete_handler,
|
|
"sessions.get": _sessions_get_handler,
|
|
"sessions.compact": _sessions_compact_handler,
|
|
}
|