Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Automatic native Git snapshot diffs could block concurrent OpenCode sessions after broad rewrites or line-ending conversions. Managed desktop and Agent profiles now skip snapshots that GoodBuddy does not use, while preserving file tools, native subagents, workspace Git operations, and Ask/Execute rules. Drain otherwise-unused Continue utility-host stdout to prevent console output from filling the pipe and blocking requests. Correct the lifecycle regression's body observer to watch the underlying reader rather than a buffered wrapper. Prepare Desktop 0.12.10 and Agent 0.11.22 with aligned bilingual notes and feature documentation. Existing runtimes require restart or Agent update. Full desktop UI before/after and Linux Agent production-path checks passed; 3626 tests passed, with 61 existing platform/manual skips. Release note: 修复批量文件改写或换行转换后的托管 OpenCode 并行停顿,以及 Continue 控制台输出阻塞;本机升级重启后生效,远程项目请配套更新 Agent。
102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
import { EventEmitter } from 'node:events'
|
|
import { PassThrough } from 'node:stream'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import {
|
|
createContinueUtilityProcessChild,
|
|
type ContinueUtilityProcessSource
|
|
} from './continue-utility-process-adapter'
|
|
import { waitForProcessExit } from './child-process-termination'
|
|
|
|
function createSource(stdout?: PassThrough): ContinueUtilityProcessSource & EventEmitter {
|
|
const emitter =
|
|
new EventEmitter() as ContinueUtilityProcessSource & EventEmitter
|
|
Object.defineProperties(emitter, {
|
|
pid: { value: 42 },
|
|
stdout: { value: stdout },
|
|
stderr: { value: undefined }
|
|
})
|
|
emitter.kill = vi.fn(() => true)
|
|
emitter.onExit = (listener) => {
|
|
emitter.on('exit', listener)
|
|
}
|
|
emitter.onceExit = (listener) => {
|
|
emitter.once('exit', listener)
|
|
}
|
|
emitter.onceError = (listener) => {
|
|
emitter.once('utility-error', listener)
|
|
}
|
|
emitter.removeExitListener = (listener) => {
|
|
emitter.removeListener('exit', listener)
|
|
}
|
|
emitter.removeErrorListener = (listener) => {
|
|
emitter.removeListener('utility-error', listener)
|
|
}
|
|
return emitter
|
|
}
|
|
|
|
describe('Continue utility process adapter', () => {
|
|
it('drains unused stdout so native output cannot block the host', async () => {
|
|
const stdout = new PassThrough({ highWaterMark: 1024 })
|
|
createContinueUtilityProcessChild(createSource(stdout))
|
|
|
|
expect(stdout.readableFlowing).toBe(true)
|
|
await new Promise<void>((resolve) => {
|
|
stdout.end(Buffer.alloc(128 * 1024), resolve)
|
|
})
|
|
expect(stdout.readableLength).toBe(0)
|
|
})
|
|
|
|
it('maps Electron exit to close and completes helper waits immediately', async () => {
|
|
const source = createSource()
|
|
const child = createContinueUtilityProcessChild(source)
|
|
const close = vi.fn()
|
|
|
|
child.once('close', close)
|
|
const waiting = waitForProcessExit(child)
|
|
source.emit('exit', 0)
|
|
|
|
expect(close).toHaveBeenCalledWith(0)
|
|
expect(child.exitCode).toBe(0)
|
|
await expect(waiting).resolves.toBeUndefined()
|
|
})
|
|
|
|
it('maps utility errors and removes both listener types', () => {
|
|
const source = createSource()
|
|
const child = createContinueUtilityProcessChild(source)
|
|
const close = vi.fn()
|
|
const error = vi.fn()
|
|
|
|
child.once('close', close)
|
|
child.once('error', error)
|
|
child.removeListener?.('close', close)
|
|
child.removeListener?.('error', error)
|
|
source.emit('exit', 0)
|
|
source.emit('utility-error', 'FatalError', 'worker.js:1', 'report')
|
|
|
|
expect(close).not.toHaveBeenCalled()
|
|
expect(error).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('converts utility error details to a bounded Error', () => {
|
|
const source = createSource()
|
|
const child = createContinueUtilityProcessChild(source)
|
|
const error = vi.fn()
|
|
|
|
child.once('error', error)
|
|
source.emit(
|
|
'utility-error',
|
|
'FatalError',
|
|
'worker.js:1',
|
|
'x'.repeat(1_000)
|
|
)
|
|
|
|
expect(error).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
message: expect.stringMatching(
|
|
/^Continue 宿主进程异常(worker\.js:1):x{500}$/u
|
|
)
|
|
})
|
|
)
|
|
})
|
|
})
|