mirror of
https://github.com/stablyai/orca.git
synced 2026-10-26 00:03:55 +00:00
106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
export type ProcessGoneSource = 'renderer' | 'child'
|
|
export type ExpectedTeardownScope = 'none' | 'renderer-reload' | 'app-shutdown'
|
|
|
|
const WINDOWS_CONTROL_TERMINATION_EXIT_CODES = new Set([0xc000013a, 0x40010004])
|
|
const RECOVERABLE_CHILD_PROCESS_TYPES = new Set(['gpu'])
|
|
const RECOVERABLE_UTILITY_SERVICE_NAMES = new Set([
|
|
'audio.mojom.AudioService',
|
|
'network.mojom.NetworkService',
|
|
// Why: Windows media/screen capture can churn this Chromium utility without
|
|
// taking down Orca; prompting users for those child exits is noise.
|
|
'video_capture.mojom.VideoCaptureService'
|
|
])
|
|
const RECOVERABLE_CHILD_PROCESS_REASONS = new Set(['abnormal-exit', 'crashed', 'killed'])
|
|
const NON_RECOVERABLE_RENDERER_REASONS = new Set(['integrity-failure'])
|
|
|
|
function isWindowsControlTerminationExitCode(exitCode: number | null): boolean {
|
|
if (exitCode === null) {
|
|
return false
|
|
}
|
|
return WINDOWS_CONTROL_TERMINATION_EXIT_CODES.has(exitCode >>> 0)
|
|
}
|
|
|
|
function isRecoverableChromiumChildProcess({
|
|
source,
|
|
processType,
|
|
serviceName,
|
|
reason
|
|
}: {
|
|
source: ProcessGoneSource
|
|
processType?: string
|
|
serviceName?: string
|
|
reason: string
|
|
}): boolean {
|
|
if (source !== 'child') {
|
|
return false
|
|
}
|
|
if (!RECOVERABLE_CHILD_PROCESS_REASONS.has(reason)) {
|
|
return false
|
|
}
|
|
const normalizedProcessType = processType?.toLowerCase()
|
|
if (normalizedProcessType && RECOVERABLE_CHILD_PROCESS_TYPES.has(normalizedProcessType)) {
|
|
return true
|
|
}
|
|
return (
|
|
normalizedProcessType === 'utility' &&
|
|
serviceName !== undefined &&
|
|
RECOVERABLE_UTILITY_SERVICE_NAMES.has(serviceName)
|
|
)
|
|
}
|
|
|
|
export function shouldRecordProcessGoneCrash({
|
|
source,
|
|
processType,
|
|
serviceName,
|
|
reason,
|
|
exitCode,
|
|
expectedTeardown
|
|
}: {
|
|
source: ProcessGoneSource
|
|
processType?: string
|
|
serviceName?: string
|
|
reason: string
|
|
exitCode: number | null
|
|
expectedTeardown: ExpectedTeardownScope
|
|
}): boolean {
|
|
// Why: GPU, Network Service, and Audio Service exits are recoverable Chromium
|
|
// child-process churn; treating them as app crashes creates noisy user prompts.
|
|
if (isRecoverableChromiumChildProcess({ source, processType, serviceName, reason })) {
|
|
return false
|
|
}
|
|
// Why: Electron reports intentional reload/update/quit teardown as `killed`.
|
|
// Real renderer OOMs and Chromium crashes should still reach crash reporting.
|
|
if (reason !== 'killed') {
|
|
return true
|
|
}
|
|
// Why: Electron reports expected Chromium teardown during reload/update as
|
|
// `killed` + SIGTERM or Windows control termination statuses. Treat real
|
|
// crash reasons as reportable, but skip these normal termination shapes.
|
|
if (exitCode === 15 || isWindowsControlTerminationExitCode(exitCode)) {
|
|
return false
|
|
}
|
|
if (expectedTeardown === 'app-shutdown') {
|
|
return false
|
|
}
|
|
return !(source === 'renderer' && expectedTeardown === 'renderer-reload')
|
|
}
|
|
|
|
export function shouldRecoverRendererAfterProcessGone({
|
|
reason,
|
|
expectedTeardown
|
|
}: {
|
|
reason: string
|
|
expectedTeardown: ExpectedTeardownScope
|
|
}): boolean {
|
|
if (expectedTeardown === 'app-shutdown') {
|
|
return false
|
|
}
|
|
// Why: an integrity failure means Chromium cannot trust the renderer, so a
|
|
// reload cannot safely recover it. Launch failures can be transient and are
|
|
// bounded by the caller's renderer-recovery circuit breaker.
|
|
if (NON_RECOVERABLE_RENDERER_REASONS.has(reason)) {
|
|
return false
|
|
}
|
|
return !(reason === 'killed' && expectedTeardown === 'renderer-reload')
|
|
}
|