mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
* fix(daemon): stop giving new terminals to a daemon whose AppImage files are gone On Linux AppImage, a terminal daemon can outlive the mount it runs from and keep answering health checks until it SIGBUSes. Orca now reads the daemon's /proc exe link and mount table at adoption: an idle severed daemon is replaced; a busy one is kept for its sessions but quarantined, so fresh terminals use the local provider and a healthy RPC never re-admits them. Status and a verified restart are available over RPC and as `orca terminal-daemon status|restart`. Co-Authored-By: Claude <noreply@anthropic.com> * fix(daemon): lift the severed quarantine once a new daemon owns the endpoint Co-Authored-By: Claude <noreply@anthropic.com> * fix(daemon): a severed-daemon restart counts fallback terminals and never stops a replacement The restart re-checks the authenticated endpoint owner before any teardown and again at shutdown, and never falls back to a pid kill when targeted. Status counts the local fallback terminals the restart also ends. Co-Authored-By: Claude <noreply@anthropic.com> * chore(daemon): merge duplicate import Co-Authored-By: Claude <noreply@anthropic.com> * fix(runtime): classify the terminalDaemon RPC namespace as host-owned Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude <noreply@anthropic.com>
43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
// Enums + bucketing for the `daemon_lifecycle` telemetry event (STA-2376).
|
|
// The daemon's own retirement cause (pam-rejections/probe-timeouts) lives in the
|
|
// subprocess and never crosses into the app, so every reason here is one the app
|
|
// itself decides: a startup replace, or an observed death→respawn.
|
|
|
|
// Startup launcher replaced a still-connectable daemon (each maps 1:1 to a `daemon-replacement-preflight.ts` decision).
|
|
export const DAEMON_REPLACE_REASONS = [
|
|
'unhealthy_resolver',
|
|
'stale_bundle',
|
|
'different_app_path',
|
|
'failed_health_check',
|
|
'severed_tcc_attribution',
|
|
'severed_host_image'
|
|
] as const
|
|
export type DaemonReplaceReason = (typeof DAEMON_REPLACE_REASONS)[number]
|
|
|
|
// Adapter observed the daemon die and forked a replacement.
|
|
export const DAEMON_RETIRE_REASONS = ['died_respawn'] as const
|
|
export type DaemonRetireReason = (typeof DAEMON_RETIRE_REASONS)[number]
|
|
|
|
export const DAEMON_LIFECYCLE_TRANSITIONS = ['replaced', 'retired'] as const
|
|
|
|
// Bucketed, never raw: exact live-session counts could fingerprint heavy users. `unknown` when
|
|
// the count couldn't be verified (null) — e.g. a wedged daemon or an already-dead respawn target.
|
|
export const DAEMON_LIFECYCLE_SESSION_BUCKETS = ['0', '1', '2-5', '6+', 'unknown'] as const
|
|
export type DaemonLifecycleSessionBucket = (typeof DAEMON_LIFECYCLE_SESSION_BUCKETS)[number]
|
|
|
|
export function bucketDaemonLiveSessionCount(count: number | null): DaemonLifecycleSessionBucket {
|
|
if (count === null) {
|
|
return 'unknown'
|
|
}
|
|
if (count <= 0) {
|
|
return '0'
|
|
}
|
|
if (count === 1) {
|
|
return '1'
|
|
}
|
|
if (count <= 5) {
|
|
return '2-5'
|
|
}
|
|
return '6+'
|
|
}
|