Add a CLI server bind timeout and an app-wide heartbeat log

Prompted by a real incident: Confab's process silently wedged, and
was indistinguishable from "just idle" after the fact — lsof showed
the CLI socket held open, every connection was refused, and nothing
had been logged for the rest of that session. No crash, no error.

- CLIServerService: NWListener's state transitions are normally
  near-instant, but had no bound on that assumption. Now times out
  after 8s if .ready is never reached, logging clearly and clearing
  the listener slot instead of silently pretending to work forever.
- oAIApp: a 5-minute heartbeat log line, otherwise meaningless on its
  own, turns "the log's gone quiet" from an ambiguous signal into a
  plain read of roughly when a future freeze started.

Neither fixes a known root cause (none was found - no crash report,
no error, just silence), so this is diagnostics and a narrow
failsafe, not a claim the underlying freeze is resolved.
This commit is contained in:
2026-08-14 11:21:46 +02:00
parent 7527cc4091
commit 5f39448896
2 changed files with 35 additions and 0 deletions
+11
View File
@@ -91,6 +91,17 @@ struct oAIApp: App {
// Check for updates in the background
UpdateCheckService.shared.checkForUpdates()
// Periodic heartbeat: on its own, a quiet Confab.log is ambiguous it could mean the
// user just wasn't chatting, or that the app silently froze (confirmed possible on
// 2026-08-14: a stuck process left the CLI socket open per `lsof` yet refused every
// connection, with the log not written to at all for the rest of that session no
// crash, no error, nothing to distinguish "idle" from "wedged" after the fact). A
// regular, otherwise-meaningless log line turns that ambiguity into a plain read: if the
// *next* freeze happens, the gap since the last heartbeat pins down roughly when.
Timer.scheduledTimer(withTimeInterval: 300, repeats: true) { _ in
Log.general.info("heartbeat")
}
}
var body: some Scene {