2222
2323# Default Docker Compose service name for the standalone Box container.
2424_DOCKER_BOX_HOST = 'langbot_box'
25- _DEFAULT_RELAY_PORT = 5410
26- _DEFAULT_RPC_PORT = 5411 # relay_port + 1
25+ _DEFAULT_PORT = 5410
2726
2827
2928def _get_box_config (ap ) -> dict :
@@ -48,9 +47,9 @@ def resolve_box_ws_relay_url(ap: core_app.Application) -> str:
4847
4948 # In Docker, relay lives on the box runtime container.
5049 if platform .get_platform () == 'docker' :
51- return f'http://{ _DOCKER_BOX_HOST } :{ _DEFAULT_RELAY_PORT } '
50+ return f'http://{ _DOCKER_BOX_HOST } :{ _DEFAULT_PORT } '
5251
53- return f'http://127.0.0.1:{ _DEFAULT_RELAY_PORT } '
52+ return f'http://127.0.0.1:{ _DEFAULT_PORT } '
5453
5554
5655class BoxRuntimeConnector (ManagedRuntimeConnector ):
@@ -72,10 +71,10 @@ def __init__(self, ap: core_app.Application):
7271 self ._handler_task : asyncio .Task | None = None
7372 self ._ctrl_task : asyncio .Task | None = None
7473
75- # Parse the relay URL once for reuse (relay port, not RPC port) .
74+ # Parse the relay URL once for reuse.
7675 parsed = urlparse (self .ws_relay_base_url )
7776 self ._relay_host = parsed .hostname or '127.0.0.1'
78- self ._relay_port = parsed .port or _DEFAULT_RELAY_PORT
77+ self ._relay_port = parsed .port or _DEFAULT_PORT
7978
8079 def _uses_websocket (self ) -> bool :
8180 """Whether the connector should use WebSocket to reach the Box runtime.
@@ -142,18 +141,18 @@ async def _start_subprocess_then_ws(self) -> None:
142141 """Launch box server as detached subprocess, then connect via WS (Windows)."""
143142 self .ap .logger .info ('(windows) Use cmd to launch box runtime and communicate via ws' )
144143
145- # Launch the box server subprocess (no stdio pipe).
146- # The server will listen on _relay_port for the WS relay and
147- # _relay_port+1 for action-RPC WebSocket.
144+ # Launch the box server subprocess in ws mode (no stdio pipe).
148145 await self ._start_runtime_subprocess (
149146 '-m' ,
150147 'langbot_plugin.box.server' ,
148+ '--mode' ,
149+ 'ws' ,
151150 '--port' ,
152151 str (self ._relay_port ),
153152 )
154153
155154 # Wait for the WS endpoint to become reachable, then connect.
156- ws_url = f'ws://localhost:{ self ._relay_port + 1 } '
155+ ws_url = f'ws://localhost:{ self ._relay_port } /rpc/ws '
157156 await self ._connect_ws (ws_url , '(windows) WebSocket' )
158157
159158 async def _connect_remote_ws (self ) -> None :
@@ -167,18 +166,20 @@ async def _connect_remote_ws(self) -> None:
167166 def _resolve_rpc_ws_url (self ) -> str :
168167 """Determine the action-RPC WebSocket URL.
169168
169+ All endpoints share a single port; action RPC is at ``/rpc/ws``.
170+
170171 Priority:
171172 1. Explicit ``box.runtime_url`` from config (user-supplied, used as-is)
172- 2. Docker environment -> ``ws://langbot_box_runtime:5411 ``
173- 3. --standalone-box / Windows fallback -> ``ws://localhost:5411 ``
173+ 2. Docker environment -> ``ws://langbot_box:5410/rpc/ws ``
174+ 3. --standalone-box / Windows fallback -> ``ws://localhost:5410/rpc/ws ``
174175 """
175176 if self .configured_runtime_url :
176177 return self .configured_runtime_url
177178
178179 if platform .get_platform () == 'docker' :
179- return f'ws://{ _DOCKER_BOX_HOST } :{ _DEFAULT_RPC_PORT } '
180+ return f'ws://{ _DOCKER_BOX_HOST } :{ _DEFAULT_PORT } /rpc/ws '
180181
181- return f'ws://localhost:{ self ._relay_port + 1 } '
182+ return f'ws://localhost:{ self ._relay_port } /rpc/ws '
182183
183184 async def _connect_ws (self , ws_url : str , transport_name : str ) -> None :
184185 """Shared WebSocket connection procedure."""
0 commit comments