mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
fix: recover ACP parser after bracket logs
This commit is contained in:
parent
9250ffb745
commit
112b8bf182
2 changed files with 43 additions and 1 deletions
|
|
@ -289,6 +289,7 @@ function currentModelFromSessionResult(result: JsonObject): string | null {
|
|||
export function createJsonLineStream(onMessage: (message: unknown, rawLine: string) => void) {
|
||||
let buffer = '';
|
||||
let pendingJson = '';
|
||||
let pendingJsonLineCount = 0;
|
||||
|
||||
const emit = (candidate: string): boolean => {
|
||||
try {
|
||||
|
|
@ -301,18 +302,32 @@ export function createJsonLineStream(onMessage: (message: unknown, rawLine: stri
|
|||
|
||||
const startPendingJson = (line: string) => {
|
||||
pendingJson = line;
|
||||
pendingJsonLineCount = 1;
|
||||
};
|
||||
|
||||
const handleLine = (line: string) => {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) return;
|
||||
if (pendingJson) {
|
||||
if ((trimmed.startsWith('{') || trimmed.startsWith('[')) && emit(trimmed)) {
|
||||
pendingJson = '';
|
||||
pendingJsonLineCount = 0;
|
||||
return;
|
||||
}
|
||||
const nextCandidate = `${pendingJson}\n${trimmed}`;
|
||||
if (emit(nextCandidate)) {
|
||||
pendingJson = '';
|
||||
pendingJsonLineCount = 0;
|
||||
return;
|
||||
}
|
||||
pendingJson = nextCandidate.length <= 128_000 ? nextCandidate : '';
|
||||
pendingJsonLineCount += 1;
|
||||
if (nextCandidate.length <= 128_000 && pendingJsonLineCount <= 256) {
|
||||
pendingJson = nextCandidate;
|
||||
return;
|
||||
}
|
||||
pendingJson = '';
|
||||
pendingJsonLineCount = 0;
|
||||
handleLine(trimmed);
|
||||
return;
|
||||
}
|
||||
if (emit(trimmed)) return;
|
||||
|
|
|
|||
|
|
@ -408,6 +408,33 @@ test('attachAcpSession accepts pretty-printed ACP startup responses', () => {
|
|||
assert.equal(events.some((entry) => entry.event === 'error'), false);
|
||||
});
|
||||
|
||||
test('attachAcpSession recovers when bracket-prefixed logs precede JSON frames', () => {
|
||||
const child = new FakeAcpChild();
|
||||
const writes: string[] = [];
|
||||
const events: Array<{ event: string; payload: unknown }> = [];
|
||||
child.stdin.on('data', (chunk) => writes.push(String(chunk)));
|
||||
|
||||
attachAcpSession({
|
||||
child: child as never,
|
||||
prompt: 'hello',
|
||||
cwd: '/tmp/od-project',
|
||||
model: null,
|
||||
mcpServers: [],
|
||||
send: (event, payload) => events.push({ event, payload }),
|
||||
});
|
||||
|
||||
child.stdout.write('[vela] starting OpenCode bridge\n');
|
||||
child.stdout.write(`${JSON.stringify({ id: 1, result: {} })}\n`);
|
||||
child.stdout.write('{not json but looks like an object log\n');
|
||||
child.stdout.write(`${JSON.stringify({ id: 2, result: { sessionId: 'session-1' } })}\n`);
|
||||
|
||||
const methods = parseRpcWrites(writes)
|
||||
.map((entry) => entry.method)
|
||||
.filter(Boolean);
|
||||
assert.deepEqual(methods, ['initialize', 'session/new', 'session/prompt']);
|
||||
assert.equal(events.some((entry) => entry.event === 'error'), false);
|
||||
});
|
||||
|
||||
function parseRpcWrites(writes: string[]): Array<Record<string, unknown>> {
|
||||
return writes
|
||||
.join('')
|
||||
|
|
|
|||
Loading…
Reference in a new issue