fix(xworkmate_bridge): align Caddy SSE timeouts with bridge 60min max wait

Caddy /acp* used read/write_timeout 30m while the bridge max gateway wait is
60min, so long tasks had their SSE killed at the edge (ACP_HTTP_CONNECTION_CLOSED)
while OpenClaw kept running. /api*, /artifacts/* and / also lacked flush_interval
and long timeouts, making polling/streaming fragile.

- T1: introduce xworkmate_bridge_acp_stream_timeout (70m = 60min cap + grace),
  acp_dial_timeout, acp_upstream_keepalive; drive /acp* read/write_timeout from it.
- T2: apply flush_interval -1 + the same long timeouts to /api*, /artifacts/*, /.
- Update validate.yml assertions to reference the vars instead of hardcoded 30m.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Haitao Pan 2026-06-26 10:49:01 +08:00
parent 8fcff61855
commit cd9a783de7
3 changed files with 45 additions and 10 deletions

View File

@ -29,6 +29,13 @@ xworkmate_bridge_service_environment:
xworkmate_bridge_openclaw_gateway_max_active: 2 xworkmate_bridge_openclaw_gateway_max_active: 2
xworkmate_bridge_openclaw_gateway_max_queued: 20 xworkmate_bridge_openclaw_gateway_max_queued: 20
xworkmate_bridge_openclaw_gateway_queue_timeout: 10m xworkmate_bridge_openclaw_gateway_queue_timeout: 10m
# Caddy reverse_proxy 流式SSE超时。必须 >= bridge openClawAgentWaitMaxTimeout(60min)
# 才不会在长任务执行到一半时把 SSE 从入口掐断(表现为 ACP_HTTP_CONNECTION_CLOSED
# 而 OpenClaw gateway 仍在后台跑)。来源常量见 xworkmate-bridge/internal/acp/orchestrator.go:32。
# 取 70m = 60min 上限 + 10min 余量(HTTP margin + keepalive 抖动)。改这里即同时驱动 read/write_timeout。
xworkmate_bridge_acp_stream_timeout: 70m
xworkmate_bridge_acp_dial_timeout: 10s
xworkmate_bridge_acp_upstream_keepalive: 5m
xworkmate_bridge_distributed_topology: "" xworkmate_bridge_distributed_topology: ""
xworkmate_bridge_distributed_local_node_id: "" xworkmate_bridge_distributed_local_node_id: ""
xworkmate_bridge_distributed_task_forward_peer_id: "" xworkmate_bridge_distributed_task_forward_peer_id: ""

View File

@ -36,9 +36,10 @@
'reverse_proxy ' ~ xworkmate_bridge_listen_host ~ ':' ~ xworkmate_bridge_listen_port 'reverse_proxy ' ~ xworkmate_bridge_listen_host ~ ':' ~ xworkmate_bridge_listen_port
in xworkmate_bridge_fragment.stdout in xworkmate_bridge_fragment.stdout
- "'flush_interval -1' in xworkmate_bridge_fragment.stdout" - "'flush_interval -1' in xworkmate_bridge_fragment.stdout"
- "'read_timeout 30m' in xworkmate_bridge_fragment.stdout" # 流式超时与 bridge openClawAgentWaitMaxTimeout(60min) 对齐,由 xworkmate_bridge_acp_stream_timeout 驱动T1/T2
- "'write_timeout 30m' in xworkmate_bridge_fragment.stdout" - "'read_timeout ' ~ xworkmate_bridge_acp_stream_timeout in xworkmate_bridge_fragment.stdout"
- "'keepalive 5m' in xworkmate_bridge_fragment.stdout" - "'write_timeout ' ~ xworkmate_bridge_acp_stream_timeout in xworkmate_bridge_fragment.stdout"
- "'keepalive ' ~ xworkmate_bridge_acp_upstream_keepalive in xworkmate_bridge_fragment.stdout"
- "'/gateway/openclaw' not in xworkmate_bridge_fragment.stdout" - "'/gateway/openclaw' not in xworkmate_bridge_fragment.stdout"
- "'/acp-server' not in xworkmate_bridge_fragment.stdout" - "'/acp-server' not in xworkmate_bridge_fragment.stdout"
- "'127.0.0.1:18789' not in xworkmate_bridge_fragment.stdout" - "'127.0.0.1:18789' not in xworkmate_bridge_fragment.stdout"

View File

@ -11,28 +11,55 @@
respond `{"jsonrpc":"2.0","error":{"code":-32001,"message":"unauthorized"},"type":"res","ok":false}` 401 respond `{"jsonrpc":"2.0","error":{"code":-32001,"message":"unauthorized"},"type":"res","ok":false}` 401
} }
# /api* 承载 tasks.get 轮询与(部分)流式响应:与 /acp* 用同样的流式 + 长超时配置,
# 避免落到 Caddy 默认短超时把轮询/流式打断T2
handle /api* { handle /api* {
reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} {
flush_interval -1
transport http {
dial_timeout {{ xworkmate_bridge_acp_dial_timeout }}
read_timeout {{ xworkmate_bridge_acp_stream_timeout }}
write_timeout {{ xworkmate_bridge_acp_stream_timeout }}
keepalive {{ xworkmate_bridge_acp_upstream_keepalive }}
}
}
} }
handle /artifacts/* { handle /artifacts/* {
reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} {
flush_interval -1
transport http {
dial_timeout {{ xworkmate_bridge_acp_dial_timeout }}
read_timeout {{ xworkmate_bridge_acp_stream_timeout }}
write_timeout {{ xworkmate_bridge_acp_stream_timeout }}
keepalive {{ xworkmate_bridge_acp_upstream_keepalive }}
}
}
} }
# /acp* 流式超时必须 >= bridge openClawAgentWaitMaxTimeout(60min)否则长任务在入口被掐断T1
handle /acp* { handle /acp* {
reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} { reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} {
flush_interval -1 flush_interval -1
transport http { transport http {
dial_timeout 10s dial_timeout {{ xworkmate_bridge_acp_dial_timeout }}
read_timeout 30m read_timeout {{ xworkmate_bridge_acp_stream_timeout }}
write_timeout 30m write_timeout {{ xworkmate_bridge_acp_stream_timeout }}
keepalive 5m keepalive {{ xworkmate_bridge_acp_upstream_keepalive }}
} }
} }
} }
handle / { handle / {
reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} {
flush_interval -1
transport http {
dial_timeout {{ xworkmate_bridge_acp_dial_timeout }}
read_timeout {{ xworkmate_bridge_acp_stream_timeout }}
write_timeout {{ xworkmate_bridge_acp_stream_timeout }}
keepalive {{ xworkmate_bridge_acp_upstream_keepalive }}
}
}
} }
log { log {