| @@ -37,6 +37,8 @@ const sidecarLog = log.scope("sidecar"); · 36 unmodified lines |
| 37 | 37 | const STDERR_TAIL_LIMIT = 8 * 1024; |
| 38 | 38 | const READY_SENTINEL = "EXECUTOR_READY"; |
| 39 | 39 | const ATTACHED_SENTINEL = "EXECUTOR_ATTACHED"; |
| 40 | +const ATTACH_OWNER_TIMEOUT_MS = 15_000; |
| 41 | +const ATTACH_OWNER_POLL_MS = 150; |
| 40 | 42 | |
| 41 | 43 | // Children deliberately stopped via stopSidecar (quit, restart, update) — |
| 42 | 44 | // their exits are expected and must not be reported as crashes. |
| @@ -281,6 +283,7 @@ export async function startSidecar(options: StartOptions = {}): Promise<SidecarC · 238 unmodified lines |
| 281 | 283 | let stdoutControlBuffer = ""; |
| 282 | 284 | let resolved = false; |
| 283 | 285 | let rejected = false; |
| 286 | + let attachedOwnerResolution: Promise<boolean> | null = null; |
| 284 | 287 | |
| 285 | 288 | const logStdoutLine = makeLineSplitter((line) => sidecarLog.info(line)); |
| 286 | 289 | const logStderrLine = makeLineSplitter((line) => sidecarLog.error(line)); |
| @@ -294,12 +297,21 @@ export async function startSidecar(options: StartOptions = {}): Promise<SidecarC · 7 unmodified lines |
| 294 | 297 | |
| 295 | 298 | const resolveAttachedOwner = async (): Promise<boolean> => { |
| 296 | 299 | if (resolved || rejected) return true; |
| 297 | | − const attached = await attachToReachableLocalServer(dataDir); |
| 298 | | − if (!attached) return false; |
| 299 | | − resolved = true; |
| 300 | | − expectedExits.add(child); |
| 301 | | − resolveStart(attached); |
| 302 | | − return true; |
| 300 | + if (!attachedOwnerResolution) { |
| 301 | + attachedOwnerResolution = waitForReachableLocalServer(dataDir) |
| 302 | + .then((attached) => { |
| 303 | + if (resolved || rejected) return true; |
| 304 | + if (!attached) return false; |
| 305 | + resolved = true; |
| 306 | + expectedExits.add(child); |
| 307 | + resolveStart(attached); |
| 308 | + return true; |
| 309 | + }) |
| 310 | + .finally(() => { |
| 311 | + attachedOwnerResolution = null; |
| 312 | + }); |
| 313 | + } |
| 314 | + return attachedOwnerResolution; |
| 303 | 315 | }; |
| 304 | 316 | |
| 305 | 317 | const onStdout = (chunk: Buffer) => { |
| @@ -418,6 +430,21 @@ const isDaemonReachable = async (origin: string): Promise<boolean> => { · 112 unmodified lines |
| 418 | 430 | return false; |
| 419 | 431 | }; |
| 420 | 432 | |
| 433 | +const waitForReachableLocalServer = async ( |
| 434 | + dataDir: string, |
| 435 | + options: { readonly requireCliDaemon?: boolean } = {}, |
| 436 | +): Promise<SidecarConnection | null> => { |
| 437 | + const deadline = Date.now() + ATTACH_OWNER_TIMEOUT_MS; |
| 438 | + while (Date.now() <= deadline) { |
| 439 | + const attached = await attachToReachableLocalServer(dataDir, options); |
| 440 | + if (attached) return attached; |
| 441 | + const remaining = deadline - Date.now(); |
| 442 | + if (remaining <= 0) return null; |
| 443 | + await delay(Math.min(ATTACH_OWNER_POLL_MS, remaining)); |
| 444 | + } |
| 445 | + return null; |
| 446 | +}; |
| 447 | + |
| 421 | 448 | const attachToReachableLocalServer = async ( |
| 422 | 449 | dataDir: string, |
| 423 | 450 | options: { readonly requireCliDaemon?: boolean } = {}, |