fix(tui): port upstream failed RPC request reply (anomalyco/opencode#54936)
Issue for this PR
Closes #1616
Type of change
- Bug fix
- New feature
- Refactor / code improvement
- Documentation
What does this PR do?
The TUI waits on RPC calls to its server worker before it draws anything (e.g. client.call("server", network) for --port). When a worker handler threw, Rpc.listen never posted a reply and Rpc.client had no reject path, so the call never settled: altimate-code --port 4096 with the port taken is a blank terminal forever.
Ported from upstream anomalyco/opencode#54936:
Rpc.listenanswers a failed request with{ type: "rpc.result", error, id }. If the error has no message (Server.listenrejects with an emptyServeErrorthat names the port in its cause), the cause's message is used.Rpc.clienttracks{ resolve, reject }per call and rejects when the result carries anerrorstring, so the failure reaches the TUI's existing error handling.
Wrapped in altimate_change markers tagged upstream_fix:. Successful calls are unchanged.
How did you verify your code works?
- Two new tests in
test/util/rpc.test.ts: the client rejects a call whose result carries an error, andlistenanswers a throwing handler (with cause fallback) and keeps answering later requests. Both fail onmainand pass here; the existing 5 RPC tests still pass. bun run typecheckinpackages/opencodeclean;bun run script/upstream/analyze.ts --markers --base origin/main --strictclean.
Cost side effects
None. No model calls, queries or resources added.
Screenshots / recordings
N/A, no UI change beyond the existing error message now being reachable.
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
- I checked for cost side effects (model calls, queries or cloud resources that grow with user data) and described them, or wrote "none"
Summary by cubic
Fixes the TUI hanging forever when an RPC worker handler throws, so --port with a port already taken now shows an error instead of a blank terminal.
Previously Rpc.listen never replied to a failed request and Rpc.client had no reject path, so the call never settled. Now listen answers failures with an error field (falling back to the error's cause message for empty errors like the port-in-use ServeError), and the client rejects on that error so the TUI's existing error handling runs. Successful calls are unchanged. Closes #1616.