Host service#3157
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds persistent host-service manifests, a discovery/adoption lifecycle in HostServiceManager (with richer status/compatibility), new tRPC procedures/subscriptions, host-service-aware auto-update checks, quit/window lifecycle refactor, tray integration for host services, terminal transport auto-reconnect, and new REST session endpoints. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Desktop App
participant Manager as HostServiceManager
participant Manifest as Manifest Layer
participant Process as Host Service Process
participant IPC as IPC Channel
App->>Manager: discoverAndAdoptAll()
Manager->>Manifest: listManifests()
Manifest-->>Manager: [manifests...]
loop each manifest
Manager->>Manifest: isProcessAlive(pid)
Manifest-->>Manager: alive?/dead?
Manager->>Manager: run health & compatibility checks
alt alive & compatible
Manager->>Process: adopt (no spawn)
Process->>IPC: "ready" (serviceVersion, protocolVersion, startedAt)
IPC->>Manager: notify ready
Manager->>Manager: emit "status-changed":"running"
else dead or incompatible
Manager->>Manager: mark "degraded" / schedule restart
end
end
Manager-->>App: adoption complete
App->>App: initialize tray with service statuses
sequenceDiagram
participant Tray as Tray Menu
participant Manager as HostServiceManager
participant Service as Host Service
participant App as Desktop App
Tray->>Manager: getActiveOrganizationIds + getServiceInfo()
Manager-->>Tray: [{orgId, status, uptime, compatibility, ...}]
Tray->>Tray: render menu
Manager->>Tray: emit "status-changed" events
Tray->>Tray: rebuild menu immediately
alt User selects "Restart"
Tray->>Manager: restart(orgId)
Manager->>Service: stop/kill -> spawn new process
Service->>Manager: "ready"
Manager->>Tray: emit "status-changed":"running"
Tray->>Tray: refresh menu
else User selects "Stop"
Tray->>Manager: stop(orgId)
Manager->>Service: terminate
Manager->>Tray: emit "status-changed":"stopped"
Tray->>Tray: refresh menu
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ❌ 3❌ Failed checks (1 warning, 2 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🚀 Preview Deployment🔗 Preview Links
Preview updates automatically with new commits |
Greptile SummaryThis PR promotes Key findings:
Confidence Score: 3/5Not yet safe to merge — two concrete logic bugs (broken backoff, stale _exited flag) affect crash-recovery reliability before the primary feature path is stable. The overall architecture is sound and the new features are well-structured. However, two P1 logic bugs undermine the crash-recovery guarantees central to this PR: the exponential backoff never advances beyond the base delay, and terminal auto-reconnect can silently stop working after a session exits. Both are straightforward to fix but important for the reliability story this PR is building. apps/desktop/src/main/lib/host-service-manager.ts (broken restartCount propagation in scheduleRestart) and apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts (_exited not reset in connect()) Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([start / restart called]) --> B[spawn: new instance restartCount=previousInstance?.restartCount ?? 0]
B --> C{async env build still current?}
C -- no --> D([throw cancelled])
C -- yes --> E[fork host-service child process]
E --> F{ready message within 10 s?}
F -- yes --> G[status: running, restartCount reset to 0, check compatibility]
F -- timeout --> H[failStartup, status: degraded, scheduleRestart]
G --> I{process exits?}
I -- intentional stop --> J([status: stopped])
I -- unexpected exit --> K[status: degraded, scheduleRestart]
K --> L[delay = BASE x 2^restartCount, instance.restartCount++]
L --> M[timer fires: DELETE instance, spawn again - restartCount lost!]
M --> B
style M fill:#f88,stroke:#c00
style B fill:#ffd,stroke:#aa0
Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin' in..." | Re-trigger Greptile |
There was a problem hiding this comment.
6 issues found across 12 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/main/lib/auto-updater.ts">
<violation number="1" location="apps/desktop/src/main/lib/auto-updater.ts:276">
P2: Avoid an empty `catch` here; log a warning with context so failures in the host-service compatibility check are observable.
(Based on your team's feedback about handling async/errors without silent catches.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts">
<violation number="1" location="apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts:105">
P2: `_exited` is never reset on new connections, which can permanently disable auto-reconnect after a prior exit event.</violation>
</file>
<file name="apps/desktop/src/main/lib/host-service-manager.ts">
<violation number="1" location="apps/desktop/src/main/lib/host-service-manager.ts:161">
P1: Exponential backoff is broken: `this.instances.delete(organizationId)` discards the `restartCount` right before `spawn()` tries to read it, so `restartCount` is always 0 and the delay is always 1 second. Remove the delete — `spawn()` already overwrites the entry via `this.instances.set()`.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsx:59">
P1: Use the same `getLocalPort` input shape for cache writes/queries and cache reads. Adding `organizationName` only to `ensureData/useQuery` can make `getData({ organizationId })` miss cached entries.</violation>
</file>
<file name="apps/desktop/src/main/lib/tray/index.ts">
<violation number="1" location="apps/desktop/src/main/lib/tray/index.ts:296">
P2: The new `status-changed` subscription is never unsubscribed in `disposeTray`, which can leak listeners and duplicate menu updates across tray re-initializations.</violation>
</file>
<file name="apps/desktop/src/lib/electron-app/factories/app/setup.ts">
<violation number="1" location="apps/desktop/src/lib/electron-app/factories/app/setup.ts:61">
P1: On Windows/Linux this change can leave the app running headless with no way to reopen a window, because quit is skipped when host-service is active but non-macOS has no tray/reopen path.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts (1)
166-178:⚠️ Potential issue | 🟡 Minor
_exitedflag is not reset indisconnect(), preventing future reconnection.When
disconnect()is called explicitly (e.g., user closes terminal),_exitedremainstruefrom a previous session. If the same transport is reused with a newconnect()call,scheduleReconnectwill bail out early due to the stale_exitedflag.🛠️ Proposed fix
export function disconnect(transport: TerminalTransport) { cancelReconnect(transport); if (transport.socket) { transport.socket.close(); transport.socket = null; } transport.currentUrl = null; transport._terminal = null; transport._reconnectAttempt = 0; + transport._exited = false; setConnectionState(transport, "disconnected"); transport.onDataDisposable?.dispose(); transport.onDataDisposable = null; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts` around lines 166 - 178, The disconnect function leaves the transport._exited flag set, which prevents future reconnects; update disconnect(transport: TerminalTransport) to clear the flag (set transport._exited = false) so subsequent connect()/scheduleReconnect() calls won't bail out due to a stale exit state; locate the disconnect function and add the reset alongside resetting _terminal and _reconnectAttempt, ensuring the same TerminalTransport instance can reconnect after an explicit disconnect.apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsx (1)
83-122:⚠️ Potential issue | 🟠 Major
servicescan go stale after a host-service restart.In
apps/desktop/src/main/lib/host-service-manager.ts, Line 286 generates a new secret on every spawn, andapps/desktop/src/lib/trpc/routers/host-service-manager/index.tsLines 53-64 return the fresh{ port, secret }on restart. This memo only reuses cachedgetLocalPortdata, so after a crash/manual restart it can keep stale credentials, and possibly a stale port, until the provider remounts. Please invalidate/refetchgetLocalPortfrom the new status subscription or update the cache from the restart path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsx` around lines 83 - 122, The services Map produced in the useMemo (function addOrg / variable services) can keep stale port/secret because it only reads cached utils.hostServiceManager.getLocalPort.getData; update the code so the provider listens to the host-service status subscription (or the restart code path that returns {port, secret}) and triggers a fresh refetch/invalidation of getLocalPort before building services: on restart/status change call utils.hostServiceManager.getLocalPort.invalidate or explicit utils.hostServiceManager.getLocalPort.fetch (or update the cache with the new {port, secret}), then rebuild the Map (ensuring addOrg calls setHostServiceSecret and getHostServiceClient with the fresh values) so services never use stale credentials or ports.
🧹 Nitpick comments (1)
apps/desktop/src/main/lib/auto-updater.ts (1)
270-278: Consider logging unexpected errors for debuggability.The empty
catch {}block silently swallows all errors, not just the expected "not initialized" case. While this doesn't break functionality, logging unexpected errors could help diagnose issues during development.💡 Optional: Add debug logging for unexpected errors
try { const { getHostServiceManager } = require("../host-service-manager"); getHostServiceManager().checkAllCompatibility(); - } catch { - // Host service manager may not be initialized yet + } catch (error) { + // Host service manager may not be initialized yet; log other errors for debugging + if (error instanceof Error && !error.message.includes("not initialized")) { + console.debug("[auto-updater] checkAllCompatibility error:", error.message); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/lib/auto-updater.ts` around lines 270 - 278, Replace the empty catch with a caught-error handler that logs unexpected exceptions when calling getHostServiceManager().checkAllCompatibility(); e.g., change catch {} to catch (err) { (processLogger ?? console).error("auto-updater: error checking host-service compatibility", err); } so you still ignore the expected “not initialized” case but surface other unexpected errors from getHostServiceManager()/checkAllCompatibility().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/host-service/index.ts`:
- Around line 24-27: The protocolVersion assignment can become NaN when
process.env.HOST_SERVICE_PROTOCOL_VERSION is non-numeric; update the logic
around protocolVersion to parse the env var (e.g., Number or parseInt), check
isNaN on the result, and set protocolVersion to null when invalid so downstream
comparisons don't use NaN; reference the variables serviceVersion and
protocolVersion and the env var HOST_SERVICE_PROTOCOL_VERSION when making this
change.
In `@apps/desktop/src/main/lib/host-service-manager.ts`:
- Around line 218-226: hasActiveInstances() currently only treats "running" and
"starting" as active, causing the app to quit during backoff when services are
"recovering"/"degraded"/"restarting"; update the hasActiveInstances method to
consider those recovery-related statuses (e.g., include "recovering",
"degraded", and "restarting" alongside "running"/"starting") as active so the
method returns true while services are in recovery; keep the existing
pendingStarts.size check as well.
- Around line 243-260: The failing tests show HostServiceManager directly calls
app.getVersion (seen in checkCompatibility and buildHostServiceEnv) causing a
TypeError when Electron's app mock lacks getVersion; refactor by centralizing
version access behind a single helper or injectable accessor (e.g., create a
getAppVersion() helper or a versionProvider passed into HostServiceManager) and
replace direct app.getVersion usages in checkCompatibility and
buildHostServiceEnv with that helper; update tests to stub/mock that single
helper (or inject a mock versionProvider) so all HostServiceManager tests can
control the app version in one place.
In `@apps/desktop/src/main/lib/tray/index.ts`:
- Around line 294-298: The "status-changed" listener added via
getHostServiceManager().on("status-changed", ...) must be unregistered in
disposeTray(); define the listener as a named/const function (e.g.,
onHostServiceStatusChanged or similar) when you register it so you can call
manager.off("status-changed", thatHandler) (or removeListener) inside
disposeTray() using getHostServiceManager() to get the same singleton manager;
ensure disposeTray() removes the exact same handler reference to prevent stacked
handlers when the tray is re-initialized.
In
`@apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsx`:
- Around line 55-60: The cache key for getLocalPort is inconsistent: ensureData
is called with { organizationId, organizationName } but later reads only use {
organizationId }, causing different cache entries; update every read of
utils.hostServiceManager.getLocalPort (including the one that currently passes
only { organizationId } and the occurrences around the block flagged as also
affected) to use the same key shape by passing organizationName (e.g.
organizationName: org?.name ?? undefined) together with organizationId so all
lookups use the identical cache key as ensureData.
---
Outside diff comments:
In `@apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts`:
- Around line 166-178: The disconnect function leaves the transport._exited flag
set, which prevents future reconnects; update disconnect(transport:
TerminalTransport) to clear the flag (set transport._exited = false) so
subsequent connect()/scheduleReconnect() calls won't bail out due to a stale
exit state; locate the disconnect function and add the reset alongside resetting
_terminal and _reconnectAttempt, ensuring the same TerminalTransport instance
can reconnect after an explicit disconnect.
In
`@apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsx`:
- Around line 83-122: The services Map produced in the useMemo (function addOrg
/ variable services) can keep stale port/secret because it only reads cached
utils.hostServiceManager.getLocalPort.getData; update the code so the provider
listens to the host-service status subscription (or the restart code path that
returns {port, secret}) and triggers a fresh refetch/invalidation of
getLocalPort before building services: on restart/status change call
utils.hostServiceManager.getLocalPort.invalidate or explicit
utils.hostServiceManager.getLocalPort.fetch (or update the cache with the new
{port, secret}), then rebuild the Map (ensuring addOrg calls
setHostServiceSecret and getHostServiceClient with the fresh values) so services
never use stale credentials or ports.
---
Nitpick comments:
In `@apps/desktop/src/main/lib/auto-updater.ts`:
- Around line 270-278: Replace the empty catch with a caught-error handler that
logs unexpected exceptions when calling
getHostServiceManager().checkAllCompatibility(); e.g., change catch {} to catch
(err) { (processLogger ?? console).error("auto-updater: error checking
host-service compatibility", err); } so you still ignore the expected “not
initialized” case but surface other unexpected errors from
getHostServiceManager()/checkAllCompatibility().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aefb92e7-ecc6-4aa8-a5db-b21b20eb0118
📒 Files selected for processing (12)
apps/desktop/src/lib/electron-app/factories/app/setup.tsapps/desktop/src/lib/trpc/routers/host-service-manager/index.tsapps/desktop/src/main/host-service/index.tsapps/desktop/src/main/lib/auto-updater.tsapps/desktop/src/main/lib/host-service-manager.tsapps/desktop/src/main/lib/tray/index.tsapps/desktop/src/renderer/lib/terminal/terminal-ws-transport.tsapps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsxpackages/host-service/src/app.tspackages/host-service/src/terminal/terminal.tspackages/host-service/src/trpc/router/health/health.tspackages/host-service/src/types.ts
There was a problem hiding this comment.
3 issues found across 9 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/main/lib/host-service-manifest.ts">
<violation number="1" location="apps/desktop/src/main/lib/host-service-manifest.ts:64">
P2: Validate that the manifest’s `organizationId` matches the requested `organizationId` before returning parsed data.</violation>
</file>
<file name="apps/desktop/src/main/host-service/index.ts">
<violation number="1" location="apps/desktop/src/main/host-service/index.ts:77">
P1: Manifest writes `authToken` to disk using default permissions, which can expose the host-service secret to other local users.</violation>
<violation number="2" location="apps/desktop/src/main/host-service/index.ts:94">
P2: Do not silently swallow manifest cleanup failures; log a warning with context.
(Based on your team's feedback about handling async/error paths without silent catches.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
apps/desktop/src/main/lib/host-service-manager.ts (1)
293-300:⚠️ Potential issue | 🟠 MajorKeep recovering instances counted as active.
hasActiveInstances()drops tofalsewhile an org isdegradedorrestarting. Line 193 inapps/desktop/src/main/index.tsnow uses this to decide whether macOS should stay alive, so quitting during the backoff window can tear down the app even though recovery is still in flight.💡 Suggested fix
hasActiveInstances(): boolean { for (const instance of this.instances.values()) { - if (instance.status === "running" || instance.status === "starting") { + if (instance.status !== "stopped") { return true; } } return this.pendingStarts.size > 0; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/lib/host-service-manager.ts` around lines 293 - 300, hasActiveInstances() incorrectly returns false when instances are in recovery/restarting states; update the check in hasActiveInstances() to treat additional statuses (e.g., "recovering", "restarting", or any other non-terminal states your InstanceStatus enum uses) as active by including them in the conditional that currently tests for "running" or "starting", and ensure pendingStarts still contributes to the result; modify the loop over this.instances.values() in hasActiveInstances() to return true for those recovery-related statuses so the app remains alive while recovery is in flight.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/lib/host-service-manager.ts`:
- Around line 508-517: The restart backoff is lost because scheduleRestart()
increments current.restartCount then deletes the instance before spawn()
recreates it with restartCount reset to 0; update spawn() (or the restart flow)
to preserve the incremented value by reading the previous instance's
restartCount (e.g., previousInstance?.restartCount) and using that value when
constructing the new HostServiceProcess, or avoid deleting current before spawn
snapshots it — ensure the restartCount from the existing map entry is carried
into the new instance creation in spawn() so crash loops back off past
BASE_RESTART_DELAY (also apply the same fix for the similar code around
scheduleRestart()/spawn() at the other location mentioned).
- Around line 170-183: The stop() logic currently sends SIGTERM (via
process.kill(instance.adoptedPid) or instance.process?.kill()) then immediately
deletes state (this.instances.delete, removeManifest) and emits "stopped";
instead, after sending SIGTERM wait for the target process to actually exit
before clearing tracking and removing the manifest: after kill, attach/await the
process exit (for adopted PIDs poll/wait for the PID to die or listen to the
ChildProcess 'exit' event for instance.process), with a sensible
timeout/fallback to force-kill, then only call
this.instances.delete(organizationId), removeManifest(organizationId) and
emitStatus(organizationId, "stopped", previousStatus); apply the same pattern to
the other stop/unregister code paths referenced (around lines 224-237 and
377-401) so we never drop tracking while the old PID may still be running.
---
Duplicate comments:
In `@apps/desktop/src/main/lib/host-service-manager.ts`:
- Around line 293-300: hasActiveInstances() incorrectly returns false when
instances are in recovery/restarting states; update the check in
hasActiveInstances() to treat additional statuses (e.g., "recovering",
"restarting", or any other non-terminal states your InstanceStatus enum uses) as
active by including them in the conditional that currently tests for "running"
or "starting", and ensure pendingStarts still contributes to the result; modify
the loop over this.instances.values() in hasActiveInstances() to return true for
those recovery-related statuses so the app remains alive while recovery is in
flight.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e7e5f40f-e958-4afb-95d3-ff942b69c982
📒 Files selected for processing (9)
apps/desktop/HOST_SERVICE_LIFECYCLE.mdapps/desktop/src/lib/electron-app/factories/app/setup.tsapps/desktop/src/main/host-service/index.tsapps/desktop/src/main/index.tsapps/desktop/src/main/lib/host-service-manager.test.tsapps/desktop/src/main/lib/host-service-manager.tsapps/desktop/src/main/lib/host-service-manifest.tsapps/desktop/src/main/lib/tray/index.tsapps/desktop/src/main/windows/main.ts
✅ Files skipped from review due to trivial changes (2)
- apps/desktop/HOST_SERVICE_LIFECYCLE.md
- apps/desktop/src/main/lib/tray/index.ts
| @@ -0,0 +1,40 @@ | |||
| # Host Service Lifecycle | |||
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
Defines the target architecture for the host service package and Electron desktop layer — API shapes, ownership boundaries, and what needs to move to make the host service deployable standalone without Electron awareness.
"Quit (Keep Services Running)" was exiting the process entirely, destroying the tray. Now it destroys windows but keeps the tray so users can still monitor and manage running services.
Description
Related Issues
Type of Change
Testing
Screenshots (if applicable)
Additional Notes
Summary by cubic
Adds a durable per‑organization host service that survives UI quits via on‑disk manifests, plus a tray to monitor/manage instances, terminal auto‑reconnect, and version/protocol compatibility so work persists across restarts and updates. Also includes architecture/boundaries/lifecycle docs to make the host service deployable standalone without Electron.
New Features
KEEP_ALIVE_AFTER_PARENT; adopted on app start with PID + endpoint health checks; stale/corrupted manifests cleaned; incompatible instances killed and respawned; update‑available vs pending‑restart surfaced.serviceVersion(app.getVersion()), passes it to the host service and manifests; introducedHOST_SERVICE_PROTOCOL_VERSIONwith compatibility checks.getLocalPortacceptsorganizationName; addedgetServiceInfo,restart, andonStatusChangesubscription. Health/info includesserviceVersion,protocolVersion,organizationId, andstartedAt. Host REST:GET /terminal/sessionsandDELETE /terminal/sessions/:terminalId.HOST_SERVICE_LIFECYCLE.md,HOST_SERVICE_ARCHITECTURE.md, andHOST_SERVICE_BOUNDARIES.mdinapps/desktop.Refactors
releaseAll()and are adopted on next launch. AddedrequestQuit("release"|"stop"),prepareQuit, andexitImmediately; auto‑updater callsprepareQuit("release")and triggerscheckAllCompatibility()after download.starting/running/degraded/restarting/stopped) and status events; adoption from on‑disk manifests with periodic liveness checks of adopted PIDs; exponential‑backoff restarts;discoverAndAdoptAll(),releaseAll(),getServiceInfo(), andcheckAllCompatibility()to surface versions/uptime and flag incompatible or restart‑pending instances.Written for commit 00343d6. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes