mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-10 19:42:21 +00:00
* refactor(agy): make sweepAgyHookCarriers async via fs/promises Convert the carrier sweep's directory scan (readdir/lstat/readFile/rm) from sync fs calls to fs/promises, so it no longer blocks the event loop while it runs. The call site in runAgy.ts still awaits it in place, so this preserves the current blocking-before-hook-server ordering exactly -- only the mechanism changes. Also adds racing-safety tests proving a carrier created after the readdir() snapshot was taken (or written by this session's own prepareAgyHookCarrier() while a sweep is in flight) is never examined, since decoupling the call site (next commit) makes that interleaving possible for the first time. * perf(agy): decouple carrier sweep from session startup sweepAgyHookCarriers is a backup path -- normal teardown already removes a carrier via cleanupAgyHookCarrier, so sweep only matters after a crash. There is no reason for it to delay this session's own startup (hook server, carrier prep, PTY spawn). Now that it is async (previous commit), fire it without awaiting it instead of blocking on it before startHookServer. It runs concurrently with this session's own prepareAgyHookCarrier() call further down; the previous commit's racing-safety tests prove that interleaving is safe. Still guaranteed to never throw or leave an unhandled rejection. * refactor(agy): add a process-lifetime cache for carrier scope The upcoming macOS/Windows identity probes are async child-process calls (ioreg/sysctl, reg query), but writeOwnerMetadata is called synchronously from prepareAgyHookCarrier -- including from agyPtyLauncher.ts's respawn path, which must stay synchronous per its fail-closed contract. A warm cache lets that synchronous call read a value computed ahead of time instead of needing to await a probe. Adds computeLocalCarrierScopeAsync (platform dispatcher, Linux-only for now -- delegates to the exact same sync read computeLocalCarrierScope already does, so no platform's observable output changes here), warmCarrierScope (populates the cache once, including caching a failed probe so it is never retried), and resolveLocalCarrierScope (used by sweepAgyHookCarriers; bypasses the cache for any non-default probe so the many existing custom-probe tests keep forcing a fresh computation per call). writeOwnerMetadata reads the cache first and falls back to the existing synchronous Linux computation when the cache was never warmed -- unchanged behavior wherever nothing calls warmCarrierScope yet. * feat(agy): enable carrier sweep on macOS computeLocalCarrierScope was Linux-only, so sweepAgyHookCarriers was a no-op on macOS -- only crash leftovers there ever accumulated (normal teardown still works via cleanupAgyHookCarrier), but they accumulated forever. Adds a strong-identity scope for macOS: IOPlatformUUID (ioreg) + kern.bootsessionuuid (sysctl), the macOS analogue of Linux's boot_id -- NOT kern.boottime, which a live re-measurement showed drifts by over a second across 8 days on the same boot (recomputed from NTP-adjusted wall clock time under the hood). The probe runs by absolute path via execFile (never a shell, never PATH-dependent) with a 2s timeout; a failed or timed-out probe still resolves to undefined, preserving sweepAgyHookCarriers's existing "cannot identify -> preserve everything" contract unchanged. Windows is deliberately not enabled. MachineGuid -- the obvious candidate for a win32 scope -- is a machine identifier, not a PID-space identifier: it is written once at OS install time and is NOT regenerated by cloning a disk image (that is exactly what sysprep exists to fix). Two clones of the same image sharing a HAPI_HOME (SMB share, sync folder, shared VM folder) would compute the identical win32:<guid> scope while having completely independent PID spaces, so sweep could delete a live session's carrier and its --dangerously-skip-permissions approval bridge with it. No cheap per-boot alternative exists on Windows: no boot-id registry key, Get-CimInstance's LastBootUpTime measured 1.4-2.5s per call (an order of magnitude too slow for identity plumbing), and net statistics/systeminfo output is locale-dependent. computeLocalCarrierScopeAsync's docstring records the full reasoning and the bar for re-enabling it (a boot-scoped, not machine-scoped, identifier cheaper than CIM). Windows carriers keep accumulating exactly as before this change -- this is a purely additive capability for macOS. Wires warmCarrierScope into runAgy.ts's PTY setup: fired without awaiting it right after the sweep call (so the probe cost -- two child processes on macOS -- overlaps with hook server startup instead of adding to it), then awaited immediately before prepareAgyHookCarrier() so its synchronous owner.json write reads a warm cache. agyPtyLauncher.ts's respawn path needs no equivalent wiring: it always runs in the same, by-then-warm process.