Commit Graph

4 Commits

Author SHA1 Message Date
현성 13ff147233 fix(browse): checkpoint on real cookie mutators and after the command runs
The debounced auto-cookie checkpoint had two gaps. First, the trigger set
was a hand-maintained list that missed the actual cookie-mutating commands
(cookie, cookie-import, load-html) while listing a non-existent set-cookie.
Drive the decision off WRITE_COMMANDS (the real mutator registry) plus a
small extra set for js/eval and the meta commands (chain, state, newtab,
tab-each) that can change the cookie jar. Second, the schedule call ran
before dispatch, so a slow navigation or login redirect snapshotted the
pre-command jar; move it to after the command attempt (success and error
paths, plus the scoped-newtab early return) so the debounce captures the
post-mutation state.

Also wire cookie-picker import/remove: those flow through the /cookie-picker
route, not /command, so they never scheduled a save. Pass
scheduleAutoCookieCheckpoint as an onCookieMutation callback and fire it
after addCookies / removal.

Serialize checkpoints with an in-flight promise guard so an overlapping
periodic + debounced + shutdown checkpoint can't race two concurrent
saveAutoCookieState writes onto the same slot. The shutdown flush awaits
any in-flight checkpoint first, then runs its own, so no save is lost.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 21:01:35 +09:00
현성 fd976e9b66 fix(browse): distinguish pid reuse when reclaiming a stale cookie lock
reclaimIfStale treated any live pid matching the recorded owner as proof
the lock is still held. After a crash the OS can recycle that pid for an
unrelated process, so a liveness check alone would refuse to reclaim a
lock whose real owner is long gone, permanently disabling auto-persistence
for the workspace. Cross-check the owner's recorded lock-acquire time
against the live process start time via `ps -o lstart=`: if the live
process started after the lock was taken, the pid was reused and the lock
is stale. Fallback is conservative — if start-time can't be read (ps
fails, timeout, non-Unix, unparseable), we keep the lock rather than
risk stealing an active peer's slot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 21:00:38 +09:00
현성 c0d85f7c0e fix(browse): reject NaN/Infinity expires and case-fold internal domains
filterPersistableCookies trusted expires as a finite number and matched
internal-network domains case-sensitively. A cookie with expires=NaN or
Infinity slipped past the `=== -1` session check (NaN <= nowSec is false),
and an uppercase `LOCALHOST` / `Foo.INTERNAL` bypassed the internal-domain
block. Add a `Number.isFinite` guard and a shared `normalizeCookieDomain`
that lower-cases before the internal-domain comparison. Allowlist matching
already lower-cases internally, so behavior there is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 20:59:22 +09:00
현성 2f2ed32b46 test(browse): auto-cookie persistence — unit, round-trip, launch, wiring
- Unit: opt-in gate, persistent-only/expiry/internal-network/allowlist filter,
  order-independent content hash, atomic-write helper, mkdir-atomic lock
  (acquire / same-pid contend / dead-pid reclaim / release).
- Real Playwright round-trip: a persistent __Host- cookie saves and reloads via
  newContext({ storageState }); session/expired cookies are excluded; an
  unchanged set skips the second write.
- Real BrowserManager.launch() integration: a pre-seeded auto-state file is
  restored into the launched context (simulates a daemon restart).
- Static wiring tripwires: save gated on the opt-in flag, shutdown flush runs
  before the browser closes, and the crash path never checkpoints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 19:17:28 +09:00