Files
Pierre Tachoire f4c7fe2c91 feat(browser): add Lightpanda backend support
Lightpanda requires one CDP connection per tab (vs Chrome's shared-WS
model). Add a Backend selector in pkg/browser/, branched lifecycle, and
per-tab connection tracking via pageConns.

- Backend type (chrome | lightpanda), WithBackend option, Backend()
  accessor. Auto-detected from /json/version when unset.
- Lightpanda OpenTab mints a fresh conn, calls
  Target.createBrowserContext (required per-conn) + createTarget.
- ListTabs / getPage are local-map only on Lightpanda (no /json/list,
  no auto-reconnect).
- Shared closeManagedPageLocked helper used by CloseTab, evict, and
  the idle-page reaper.
- Screenshot tool action errors out on Lightpanda (returns a
  placeholder image upstream; route the agent to snapshot instead).
2026-07-01 10:23:03 +02:00

46 lines
938 B
Go

package browser
import "time"
// runReaper periodically closes pages that have been idle longer than idleTimeout.
// Runs as a goroutine; exits when stopReaper is closed.
func (m *Manager) runReaper() {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
for {
select {
case <-m.stopReaper:
return
case <-ticker.C:
m.reapIdlePages()
}
}
}
// reapIdlePages closes pages idle longer than idleTimeout.
func (m *Manager) reapIdlePages() {
m.mu.Lock()
defer m.mu.Unlock()
if !m.isRunningLocked() {
return
}
now := time.Now()
for targetID, lastUsed := range m.pageLastUsed {
if now.Sub(lastUsed) <= m.idleTimeout {
continue
}
if _, ok := m.pages[targetID]; !ok {
delete(m.pageLastUsed, targetID)
continue
}
idleFor := now.Sub(lastUsed).Round(time.Second)
m.closeManagedPageLocked(targetID)
m.logger.Info("reaper: closed idle page", "targetId", targetID, "idle", idleFor)
}
}