fix: stream bash output and harden OpenCode connectivity (#2522)
* fix(ui): stream bash tool output while running * perf(ui): render streaming bash output incrementally * fix(ui): keep tool duration timer running * fix(web): recover stalled OpenCode SSE streams * fix(web): prevent OpenCode restart storms * fix: address streaming recovery review
This commit is contained in:
committed by
GitHub
parent
c84305bf7e
commit
0f830f8804
@@ -6,6 +6,9 @@ import {
|
||||
shouldForwardProxyResponseHeader,
|
||||
} from '../../proxy-headers.js';
|
||||
import { createRealpathCache } from '../path-realpath-cache.js';
|
||||
import { DEFAULT_UPSTREAM_STALL_TIMEOUT_MS } from '../event-stream/upstream-reader.js';
|
||||
|
||||
const DEFAULT_SSE_HEARTBEAT_INTERVAL_MS = 20_000;
|
||||
|
||||
export const createDirectoryQueryCanonicalizer = ({ realpath, ...cacheOptions } = {}) => {
|
||||
const realpathCache = createRealpathCache({ fallbackOnError: true, realpath, ...cacheOptions });
|
||||
@@ -186,6 +189,9 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
getOpenCodeAuthHeaders,
|
||||
buildOpenCodeUrl,
|
||||
ensureOpenCodeApiPrefix,
|
||||
SSE_HEARTBEAT_INTERVAL_MS = DEFAULT_SSE_HEARTBEAT_INTERVAL_MS,
|
||||
SSE_UPSTREAM_STALL_TIMEOUT_MS = DEFAULT_UPSTREAM_STALL_TIMEOUT_MS,
|
||||
getSseUpstreamStallTimeoutMs = () => SSE_UPSTREAM_STALL_TIMEOUT_MS,
|
||||
} = deps;
|
||||
|
||||
if (app.get('opencodeProxyConfigured')) {
|
||||
@@ -340,6 +346,8 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
let upstream = null;
|
||||
let reader = null;
|
||||
let heartbeatTimer = null;
|
||||
let upstreamStallTimer = null;
|
||||
let didUpstreamStall = false;
|
||||
let writeQueue = Promise.resolve(true);
|
||||
const sseBoundary = createSseBoundaryTracker();
|
||||
|
||||
@@ -392,8 +400,6 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
res.socket.setNoDelay(true);
|
||||
}
|
||||
|
||||
const SSE_HEARTBEAT_INTERVAL_MS = 20_000;
|
||||
|
||||
const scheduleHeartbeat = () => {
|
||||
heartbeatTimer = setTimeout(async () => {
|
||||
if (abortController.signal.aborted || res.writableEnded || res.destroyed) {
|
||||
@@ -410,6 +416,20 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
}, SSE_HEARTBEAT_INTERVAL_MS);
|
||||
};
|
||||
|
||||
const clearUpstreamStallTimer = () => {
|
||||
clearTimeout(upstreamStallTimer);
|
||||
upstreamStallTimer = null;
|
||||
};
|
||||
|
||||
const resetUpstreamStallTimer = () => {
|
||||
clearUpstreamStallTimer();
|
||||
upstreamStallTimer = setTimeout(() => {
|
||||
didUpstreamStall = true;
|
||||
abortController.abort();
|
||||
}, getSseUpstreamStallTimeoutMs());
|
||||
upstreamStallTimer.unref?.();
|
||||
};
|
||||
|
||||
const enqueueSseWrite = (value) => {
|
||||
writeQueue = writeQueue
|
||||
.catch(() => false)
|
||||
@@ -423,6 +443,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
};
|
||||
|
||||
scheduleHeartbeat();
|
||||
resetUpstreamStallTimer();
|
||||
|
||||
reader = upstream.body.getReader();
|
||||
while (!abortController.signal.aborted) {
|
||||
@@ -431,6 +452,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
break;
|
||||
}
|
||||
if (value && value.length > 0) {
|
||||
resetUpstreamStallTimer();
|
||||
sseBoundary.observe(value);
|
||||
const canContinue = await enqueueSseWrite(value);
|
||||
if (!canContinue) {
|
||||
@@ -442,6 +464,10 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
res.end();
|
||||
} catch (error) {
|
||||
if (isAbortError(error)) {
|
||||
if (didUpstreamStall && !res.writableEnded && !res.destroyed) {
|
||||
await writeQueue.catch(() => false);
|
||||
res.end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
console.error('[proxy] OpenCode SSE proxy error:', error?.message ?? error);
|
||||
@@ -455,6 +481,10 @@ export const registerOpenCodeProxy = (app, deps) => {
|
||||
clearTimeout(heartbeatTimer);
|
||||
heartbeatTimer = null;
|
||||
}
|
||||
if (upstreamStallTimer) {
|
||||
clearTimeout(upstreamStallTimer);
|
||||
upstreamStallTimer = null;
|
||||
}
|
||||
req.off('close', closeUpstream);
|
||||
try {
|
||||
if (reader) {
|
||||
|
||||
Reference in New Issue
Block a user