mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 18:47:40 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
270 lines
9.1 KiB
JavaScript
270 lines
9.1 KiB
JavaScript
// Race real daemons over a dead endpoint: loser cleanup must leave winner reachable at the canonical socket with its own token.
|
||
// Unix only: Windows named pipes are not directory entries. Run: node config/scripts/daemon-endpoint-handover-smoke.mjs
|
||
import { fork } from 'node:child_process'
|
||
import { connect } from 'node:net'
|
||
import { randomUUID } from 'node:crypto'
|
||
import { existsSync, mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs'
|
||
import { tmpdir } from 'node:os'
|
||
import { join, resolve } from 'node:path'
|
||
|
||
const repoRoot = resolve(import.meta.dirname, '..', '..')
|
||
const entryPath = join(repoRoot, 'out', 'main', 'daemon-entry.js')
|
||
|
||
// Read incumbent exit code from source; launcher adoption depends on this exact value.
|
||
const DAEMON_EXIT_ENDPOINT_OCCUPIED = Number(
|
||
readFileSync(join(repoRoot, 'src/main/daemon/daemon-endpoint-ownership.ts'), 'utf8').match(
|
||
/DAEMON_EXIT_ENDPOINT_OCCUPIED = (\d+)/
|
||
)?.[1]
|
||
)
|
||
const READY_TIMEOUT_MS = 20_000
|
||
const EXIT_TIMEOUT_MS = 15_000
|
||
const REACHABILITY_TIMEOUT_MS = 2_000
|
||
|
||
const log = (msg) => console.log(`[endpoint-handover-smoke] ${msg}`)
|
||
|
||
function readProtocolVersion() {
|
||
const source = readFileSync(join(repoRoot, 'src/main/daemon/daemon-protocol-version.ts'), 'utf8')
|
||
const match = source.match(/PROTOCOL_VERSION\s*=\s*(\d+)/)
|
||
if (!match) {
|
||
throw new Error('could not read daemon protocol version')
|
||
}
|
||
return Number(match[1])
|
||
}
|
||
|
||
function bootDaemon(tag, dir, socketPath) {
|
||
const tokenPath = join(dir, `${tag}.token`)
|
||
const pidPath = join(dir, `${tag}.pid`)
|
||
const child = fork(
|
||
entryPath,
|
||
[
|
||
'--socket',
|
||
socketPath,
|
||
'--token',
|
||
tokenPath,
|
||
'--pid-record',
|
||
pidPath,
|
||
'--launch-nonce',
|
||
randomUUID(),
|
||
'--entry-path',
|
||
entryPath,
|
||
'--app-version',
|
||
'endpoint-handover-smoke'
|
||
],
|
||
{
|
||
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
||
env: { ...process.env, ORCA_USER_DATA_PATH: dir }
|
||
}
|
||
)
|
||
let stderr = ''
|
||
child.stderr?.on('data', (chunk) => {
|
||
stderr += chunk.toString('utf8')
|
||
})
|
||
const ready = new Promise((resolveReady, rejectReady) => {
|
||
const timer = setTimeout(
|
||
() => rejectReady(new Error(`daemon ${tag} never signaled ready.\nstderr:\n${stderr}`)),
|
||
READY_TIMEOUT_MS
|
||
)
|
||
child.on('message', (msg) => {
|
||
if (msg && typeof msg === 'object' && msg.type === 'ready') {
|
||
clearTimeout(timer)
|
||
resolveReady()
|
||
}
|
||
})
|
||
child.on('exit', (code) => {
|
||
clearTimeout(timer)
|
||
rejectReady(new Error(`daemon ${tag} exited with ${code}.\nstderr:\n${stderr}`))
|
||
})
|
||
})
|
||
return { child, tokenPath, pidPath, ready }
|
||
}
|
||
|
||
function isReachable(socketPath) {
|
||
return new Promise((resolveReachable) => {
|
||
const socket = connect({ path: socketPath })
|
||
socket.once('connect', () => {
|
||
socket.destroy()
|
||
resolveReachable(true)
|
||
})
|
||
socket.once('error', () => {
|
||
socket.destroy()
|
||
resolveReachable(false)
|
||
})
|
||
})
|
||
}
|
||
|
||
function isDaemonReachable(socketPath, tokenPath, protocolVersion) {
|
||
if (!existsSync(tokenPath)) {
|
||
return Promise.resolve(false)
|
||
}
|
||
const token = readFileSync(tokenPath, 'utf8').trim()
|
||
return new Promise((resolveReachable) => {
|
||
let buffer = ''
|
||
let settled = false
|
||
const socket = connect({ path: socketPath })
|
||
const finish = (reachable) => {
|
||
if (settled) {
|
||
return
|
||
}
|
||
settled = true
|
||
clearTimeout(timer)
|
||
socket.destroy()
|
||
resolveReachable(reachable)
|
||
}
|
||
const timer = setTimeout(() => finish(false), REACHABILITY_TIMEOUT_MS)
|
||
socket.once('error', () => finish(false))
|
||
socket.once('connect', () => {
|
||
socket.write(
|
||
`${JSON.stringify({
|
||
type: 'hello',
|
||
version: protocolVersion,
|
||
token,
|
||
clientId: randomUUID(),
|
||
role: 'control'
|
||
})}\n`
|
||
)
|
||
})
|
||
socket.on('data', (chunk) => {
|
||
buffer += chunk.toString('utf8')
|
||
const newlineIndex = buffer.indexOf('\n')
|
||
if (newlineIndex === -1) {
|
||
return
|
||
}
|
||
try {
|
||
const message = JSON.parse(buffer.slice(0, newlineIndex))
|
||
finish(message.type === 'hello' && message.ok === true)
|
||
} catch {
|
||
finish(false)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
function killAndWait(child) {
|
||
if (child.exitCode !== null || child.signalCode !== null) {
|
||
return Promise.resolve(child.exitCode)
|
||
}
|
||
return new Promise((resolveExit, rejectExit) => {
|
||
const timer = setTimeout(() => rejectExit(new Error('daemon did not exit')), EXIT_TIMEOUT_MS)
|
||
child.on('exit', (code) => {
|
||
clearTimeout(timer)
|
||
resolveExit(code)
|
||
})
|
||
child.kill('SIGTERM')
|
||
})
|
||
}
|
||
|
||
async function main() {
|
||
if (process.platform === 'win32') {
|
||
log('SKIP: named pipes are not filesystem entries, so endpoint handover cannot occur')
|
||
return
|
||
}
|
||
if (!existsSync(entryPath)) {
|
||
throw new Error(`missing ${entryPath} — run \`pnpm build\` first`)
|
||
}
|
||
|
||
const dir = mkdtempSync(join(tmpdir(), 'orca-endpoint-handover-'))
|
||
const socketPath = join(dir, 'daemon.sock')
|
||
const protocolVersion = readProtocolVersion()
|
||
const daemons = []
|
||
try {
|
||
const departed = bootDaemon('departed', dir, socketPath)
|
||
daemons.push(departed)
|
||
await departed.ready
|
||
const deadInode = statSync(socketPath).ino
|
||
await killAndWait(departed.child)
|
||
if (!existsSync(socketPath) || statSync(socketPath).ino !== deadInode) {
|
||
throw new Error('departing daemon did not leave its dead endpoint entry in place')
|
||
}
|
||
if (await isReachable(socketPath)) {
|
||
throw new Error('departed daemon remains reachable')
|
||
}
|
||
log('daemon A departed and left a dead endpoint entry')
|
||
|
||
const racers = [bootDaemon('racer-b', dir, socketPath), bootDaemon('racer-c', dir, socketPath)]
|
||
daemons.push(...racers)
|
||
const readiness = await Promise.allSettled(racers.map((daemon) => daemon.ready))
|
||
if (readiness.every((result) => result.status === 'rejected')) {
|
||
throw new Error(
|
||
`neither racing daemon published the endpoint:\n${readiness
|
||
.map((result) => (result.status === 'rejected' ? result.reason.message : ''))
|
||
.join('\n')}`
|
||
)
|
||
}
|
||
|
||
const owners = []
|
||
for (const daemon of racers) {
|
||
if (await isDaemonReachable(socketPath, daemon.tokenPath, protocolVersion)) {
|
||
owners.push(daemon)
|
||
}
|
||
}
|
||
if (owners.length !== 1) {
|
||
throw new Error(`expected one reachable racing daemon, found ${owners.length}`)
|
||
}
|
||
const survivor = owners[0]
|
||
const loser = racers.find((daemon) => daemon !== survivor)
|
||
const survivorInode = statSync(socketPath).ino
|
||
if (survivorInode === deadInode) {
|
||
throw new Error('survivor did not replace the dead endpoint entry')
|
||
}
|
||
log('racing daemon published over the dead entry and is reachable')
|
||
|
||
await killAndWait(loser.child)
|
||
if (!existsSync(socketPath) || statSync(socketPath).ino !== survivorInode) {
|
||
throw new Error("losing racer's exit removed the survivor's endpoint")
|
||
}
|
||
if (!(await isDaemonReachable(socketPath, survivor.tokenPath, protocolVersion))) {
|
||
throw new Error("survivor became unreachable after the losing racer's exit")
|
||
}
|
||
if (!existsSync(survivor.pidPath)) {
|
||
throw new Error('survivor lost its ownership record')
|
||
}
|
||
if (existsSync(loser.pidPath)) {
|
||
throw new Error('losing racer left its ownership record behind')
|
||
}
|
||
|
||
// Force live-incumbent collision separately: racing losers usually exit before winner selection, so killing them proves little.
|
||
const survivorInodeBeforeBlocked = statSync(socketPath).ino
|
||
const blocked = bootDaemon('blocked', dir, socketPath)
|
||
daemons.push(blocked)
|
||
await blocked.ready.then(
|
||
() => {
|
||
throw new Error('a daemon published onto an endpoint a live daemon already owned')
|
||
},
|
||
() => {}
|
||
)
|
||
// Pin adoption exit code or concurrently starting apps silently fall back to non-persistent terminals.
|
||
const blockedExit = await killAndWait(blocked.child)
|
||
if (blockedExit !== DAEMON_EXIT_ENDPOINT_OCCUPIED) {
|
||
throw new Error(
|
||
`a daemon that lost the endpoint exited ${blockedExit}, not ${DAEMON_EXIT_ENDPOINT_OCCUPIED}`
|
||
)
|
||
}
|
||
if (!existsSync(socketPath) || statSync(socketPath).ino !== survivorInodeBeforeBlocked) {
|
||
throw new Error("a daemon that could not publish removed the live owner's endpoint")
|
||
}
|
||
if (!(await isDaemonReachable(socketPath, survivor.tokenPath, protocolVersion))) {
|
||
throw new Error('live owner became unreachable after a newcomer failed to publish')
|
||
}
|
||
if (existsSync(blocked.pidPath)) {
|
||
throw new Error('a daemon that could not publish left an ownership record behind')
|
||
}
|
||
log('a newcomer refused the live owner’s endpoint and left it intact')
|
||
|
||
log('PASS: the racing survivor remains reachable through the canonical endpoint')
|
||
} finally {
|
||
for (const daemon of daemons) {
|
||
if (daemon.child.exitCode === null && daemon.child.signalCode === null) {
|
||
try {
|
||
daemon.child.kill('SIGKILL')
|
||
} catch {}
|
||
}
|
||
}
|
||
rmSync(dir, { recursive: true, force: true })
|
||
}
|
||
}
|
||
|
||
main().catch((error) => {
|
||
console.error(`[endpoint-handover-smoke] FAIL: ${error.message}`)
|
||
process.exit(1)
|
||
})
|