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。
100 lines
2.6 KiB
TypeScript
100 lines
2.6 KiB
TypeScript
import type { ContinueHostChild } from './continue-host-adapter'
|
|
|
|
type UtilityErrorListener = (
|
|
type: 'FatalError',
|
|
location: string,
|
|
report: string
|
|
) => void
|
|
|
|
export type ContinueUtilityProcessSource = {
|
|
readonly pid?: number
|
|
readonly stdout?: Pick<NodeJS.ReadableStream, 'resume'> | null
|
|
readonly stderr?: ContinueHostChild['stderr']
|
|
kill(): boolean
|
|
onExit(listener: (code: number) => void): void
|
|
onceExit(listener: (code: number) => void): void
|
|
onceError(listener: UtilityErrorListener): void
|
|
removeExitListener(listener: (code: number) => void): void
|
|
removeErrorListener(listener: UtilityErrorListener): void
|
|
}
|
|
|
|
export function createContinueUtilityProcessChild(
|
|
utility: ContinueUtilityProcessSource
|
|
): ContinueHostChild {
|
|
// Responses arrive over HTTP, but an unread console pipe can block the host.
|
|
utility.stdout?.resume()
|
|
let exitCode: number | null = null
|
|
let killed = false
|
|
const closeListeners = new Map<
|
|
(value: Error | number | null) => void,
|
|
(code: number) => void
|
|
>()
|
|
const errorListeners = new Map<
|
|
(value: Error | number | null) => void,
|
|
UtilityErrorListener
|
|
>()
|
|
utility.onExit((code) => {
|
|
exitCode = code
|
|
})
|
|
|
|
const child: ContinueHostChild = {
|
|
get exitCode() {
|
|
return exitCode
|
|
},
|
|
get killed() {
|
|
return killed
|
|
},
|
|
get pid() {
|
|
return utility.pid
|
|
},
|
|
stderr: utility.stderr,
|
|
once: (event, listener) => {
|
|
if (event === 'close') {
|
|
const wrapped = (code: number): void => {
|
|
closeListeners.delete(listener)
|
|
listener(code)
|
|
}
|
|
closeListeners.set(listener, wrapped)
|
|
utility.onceExit(wrapped)
|
|
} else {
|
|
const wrapped: UtilityErrorListener = (
|
|
_type,
|
|
location,
|
|
report
|
|
): void => {
|
|
errorListeners.delete(listener)
|
|
listener(
|
|
new Error(
|
|
`Continue 宿主进程异常(${location}):${report.slice(0, 500)}`
|
|
)
|
|
)
|
|
}
|
|
errorListeners.set(listener, wrapped)
|
|
utility.onceError(wrapped)
|
|
}
|
|
return child
|
|
},
|
|
removeListener: (event, listener) => {
|
|
if (event === 'close') {
|
|
const wrapped = closeListeners.get(listener)
|
|
if (wrapped) {
|
|
closeListeners.delete(listener)
|
|
utility.removeExitListener(wrapped)
|
|
}
|
|
} else {
|
|
const wrapped = errorListeners.get(listener)
|
|
if (wrapped) {
|
|
errorListeners.delete(listener)
|
|
utility.removeErrorListener(wrapped)
|
|
}
|
|
}
|
|
return child
|
|
},
|
|
kill: () => {
|
|
killed = true
|
|
return utility.kill()
|
|
}
|
|
}
|
|
return child
|
|
}
|