From 2cb5263d9e33133f6a76402738d0a50c78171dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B8rn=20Lindahl?= Date: Thu, 5 Feb 2026 00:33:58 +0100 Subject: [PATCH] feat: add comprehensive OpenCode API endpoint proxies Added proxy routes for all OpenCode internal API endpoints to support full application functionality when accessed via session manager: - project, agent, config, model endpoints - thread, chat, conversation endpoints - command, mcp, lsp, vcs endpoints - permission, question, event, status endpoints - internal session endpoint (distinct from container sessions) Also updated Caddyfile for routing configuration. --- nginx/Caddyfile | 124 +++++++++++++++++++++ session-manager/routes/proxy.py | 186 ++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) diff --git a/nginx/Caddyfile b/nginx/Caddyfile index cbb9f26..a3c15a5 100644 --- a/nginx/Caddyfile +++ b/nginx/Caddyfile @@ -9,6 +9,12 @@ http://localhost { reverse_proxy session-manager:8000 } + # OpenCode internal session API (without session_id in path) + # Must be BEFORE /session/{session_id}* to match first + handle /session { + reverse_proxy session-manager:8000 + } + # Session-specific routing - proxy to session manager for dynamic routing handle /session/{session_id}* { reverse_proxy session-manager:8000 @@ -51,6 +57,124 @@ http://localhost { reverse_proxy session-manager:8000 } + # Additional OpenCode API endpoints for root-path operation + handle /agent { + reverse_proxy session-manager:8000 + } + + handle /agent/* { + reverse_proxy session-manager:8000 + } + + handle /config { + reverse_proxy session-manager:8000 + } + + handle /config/* { + reverse_proxy session-manager:8000 + } + + handle /model { + reverse_proxy session-manager:8000 + } + + handle /model/* { + reverse_proxy session-manager:8000 + } + + handle /thread/* { + reverse_proxy session-manager:8000 + } + + handle /chat/* { + reverse_proxy session-manager:8000 + } + + handle /tree { + reverse_proxy session-manager:8000 + } + + handle /tree/* { + reverse_proxy session-manager:8000 + } + + handle /conversation { + reverse_proxy session-manager:8000 + } + + handle /conversation/* { + reverse_proxy session-manager:8000 + } + + handle /project/* { + reverse_proxy session-manager:8000 + } + + # OpenCode communication endpoints for message sending + handle /command { + reverse_proxy session-manager:8000 + } + + handle /command/* { + reverse_proxy session-manager:8000 + } + + handle /mcp { + reverse_proxy session-manager:8000 + } + + handle /mcp/* { + reverse_proxy session-manager:8000 + } + + handle /lsp { + reverse_proxy session-manager:8000 + } + + handle /lsp/* { + reverse_proxy session-manager:8000 + } + + handle /vcs { + reverse_proxy session-manager:8000 + } + + handle /vcs/* { + reverse_proxy session-manager:8000 + } + + handle /permission { + reverse_proxy session-manager:8000 + } + + handle /permission/* { + reverse_proxy session-manager:8000 + } + + handle /question { + reverse_proxy session-manager:8000 + } + + handle /question/* { + reverse_proxy session-manager:8000 + } + + handle /event { + reverse_proxy session-manager:8000 + } + + handle /event/* { + reverse_proxy session-manager:8000 + } + + handle /status { + reverse_proxy session-manager:8000 + } + + handle /status/* { + reverse_proxy session-manager:8000 + } + # Health check handle /health { reverse_proxy session-manager:8000 diff --git a/session-manager/routes/proxy.py b/session-manager/routes/proxy.py index 1e0e275..5b9adda 100644 --- a/session-manager/routes/proxy.py +++ b/session-manager/routes/proxy.py @@ -84,6 +84,192 @@ async def proxy_file_path_to_session(request: Request, path: str): return await proxy_to_session(request, session_id, f"file/{path}") +# Additional OpenCode API endpoints for root-path operation +@router.api_route("/project/{path:path}", methods=ALL_METHODS) +async def proxy_project_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"project/{path}") + + +@router.api_route("/agent", methods=ALL_METHODS) +async def proxy_agent_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "agent") + + +@router.api_route("/agent/{path:path}", methods=ALL_METHODS) +async def proxy_agent_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"agent/{path}") + + +@router.api_route("/config", methods=ALL_METHODS) +async def proxy_config_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "config") + + +@router.api_route("/config/{path:path}", methods=ALL_METHODS) +async def proxy_config_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"config/{path}") + + +@router.api_route("/model", methods=ALL_METHODS) +async def proxy_model_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "model") + + +@router.api_route("/model/{path:path}", methods=ALL_METHODS) +async def proxy_model_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"model/{path}") + + +@router.api_route("/thread/{path:path}", methods=ALL_METHODS) +async def proxy_thread_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"thread/{path}") + + +@router.api_route("/chat/{path:path}", methods=ALL_METHODS) +async def proxy_chat_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"chat/{path}") + + +@router.api_route("/tree", methods=ALL_METHODS) +async def proxy_tree_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "tree") + + +@router.api_route("/tree/{path:path}", methods=ALL_METHODS) +async def proxy_tree_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"tree/{path}") + + +@router.api_route("/conversation", methods=ALL_METHODS) +async def proxy_conversation_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "conversation") + + +@router.api_route("/conversation/{path:path}", methods=ALL_METHODS) +async def proxy_conversation_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"conversation/{path}") + + +# OpenCode session and communication endpoints for message sending +@router.api_route("/command", methods=ALL_METHODS) +async def proxy_command_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "command") + + +@router.api_route("/command/{path:path}", methods=ALL_METHODS) +async def proxy_command_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"command/{path}") + + +@router.api_route("/mcp", methods=ALL_METHODS) +async def proxy_mcp_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "mcp") + + +@router.api_route("/mcp/{path:path}", methods=ALL_METHODS) +async def proxy_mcp_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"mcp/{path}") + + +@router.api_route("/lsp", methods=ALL_METHODS) +async def proxy_lsp_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "lsp") + + +@router.api_route("/lsp/{path:path}", methods=ALL_METHODS) +async def proxy_lsp_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"lsp/{path}") + + +@router.api_route("/vcs", methods=ALL_METHODS) +async def proxy_vcs_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "vcs") + + +@router.api_route("/vcs/{path:path}", methods=ALL_METHODS) +async def proxy_vcs_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"vcs/{path}") + + +@router.api_route("/permission", methods=ALL_METHODS) +async def proxy_permission_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "permission") + + +@router.api_route("/permission/{path:path}", methods=ALL_METHODS) +async def proxy_permission_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"permission/{path}") + + +@router.api_route("/question", methods=ALL_METHODS) +async def proxy_question_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "question") + + +@router.api_route("/question/{path:path}", methods=ALL_METHODS) +async def proxy_question_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"question/{path}") + + +@router.api_route("/event", methods=ALL_METHODS) +async def proxy_event_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "event") + + +@router.api_route("/event/{path:path}", methods=ALL_METHODS) +async def proxy_event_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"event/{path}") + + +@router.api_route("/status", methods=ALL_METHODS) +async def proxy_status_to_session(request: Request): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, "status") + + +@router.api_route("/status/{path:path}", methods=ALL_METHODS) +async def proxy_status_path_to_session(request: Request, path: str): + session_id = get_session_from_cookie(request) + return await proxy_to_session(request, session_id, f"status/{path}") + + +# OpenCode internal session endpoint (different from our container sessions) +# Must be defined BEFORE /session/{session_id}/{path} to match first +@router.api_route("/session", methods=ALL_METHODS) +async def proxy_internal_session_to_session(request: Request): + session_id = get_session_from_cookie(request) + # Proxy the request directly, preserving query params + path = "session" + return await proxy_to_session(request, session_id, path) + + @router.api_route("/session/{session_id}/{path:path}", methods=ALL_METHODS) async def proxy_to_session(request: Request, session_id: str, path: str): start_time = time.time()