Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
aef76f4
fix(update): preserve service-home authority during recovery
luvs01 Oct 15, 2026
29f3414
fix(update): allow guarded package-only updates under initial uncerta…
luvs01 Oct 15, 2026
9897f45
test(update): relocate service-home registrations to avoid merge conf…
luvs01 Oct 15, 2026
2d8102d
test(windows): follow extracted update teardown helper
luvs01 Oct 15, 2026
1da3dfb
test(update): isolate fallback fixtures from host service managers
luvs01 Oct 16, 2026
c238a67
Merge current dev and keep service-home test registration within the …
github-actions[bot] Oct 21, 2026
31ee7fe
Merge dev and repair update recovery checks
luvs01 Oct 22, 2026
08f0494
test(update): recognize wrapped npm registry queries in lease fixtures
luvs01 Oct 22, 2026
48c8da4
fix(update): preserve authority through delegated recovery
luvs01 Oct 22, 2026
c61bedc
test(update): follow delegated tray handoff helper
luvs01 Oct 22, 2026
4ca5868
fix(update): preserve tray recovery state when handoff rejects
luvs01 Oct 22, 2026
302668c
fix(update): preserve service-home authority during recovery
luvs01 Oct 15, 2026
21e46e7
fix(update): allow guarded package-only updates under initial uncerta…
luvs01 Oct 15, 2026
773a904
test(update): relocate service-home registrations to avoid merge conf…
luvs01 Oct 15, 2026
915dcc0
test(windows): follow extracted update teardown helper
luvs01 Oct 15, 2026
2219f90
test(update): isolate fallback fixtures from host service managers
luvs01 Oct 16, 2026
236b738
test(update): recognize wrapped npm registry queries in lease fixtures
luvs01 Oct 22, 2026
bb7c840
fix(update): preserve authority through delegated recovery
luvs01 Oct 22, 2026
7600a84
test(update): follow delegated tray handoff helper
luvs01 Oct 22, 2026
d5cb4ca
fix(update): preserve tray recovery state when handoff rejects
luvs01 Oct 22, 2026
1178f3e
fix(update): restore merge-resolution content dropped by the flat rebase
luvs01 Oct 23, 2026
eadd533
fix(update): re-check service-home authority after replacement-time a…
luvs01 Oct 23, 2026
8244012
fix(update): gate reclaim mutations and shim probe on live authority
luvs01 Oct 23, 2026
234fced
fix(update): preserve reclaim authority refusals across transient rec…
luvs01 Oct 23, 2026
c84a322
merge: preserve original service-home authority history
luvs01 Oct 23, 2026
315502d
Merge origin/dev into update service-home authority
luvs01 Oct 23, 2026
9e5a917
fix(test): promote Bun 1.4.4 with independent runner role verification
luvs01 Oct 23, 2026
1deb67d
docs: align Docker Bun comments with unified role defaults
luvs01 Oct 23, 2026
41f08ff
test: use a draining TLS origin for early upload replies
luvs01 Oct 23, 2026
a9c3d12
test: confine draining origin to buffered HTTP/1 upload
luvs01 Oct 23, 2026
f011a94
merge: align test-runner role branch with dev 2.93.0 dependency baseline
luvs01 Oct 24, 2026
8b4c34c
Merge branch 'dev' into codex/test-runner-role-verification-20261023
lidge-jun Oct 24, 2026
629fc35
Merge commit '26598df2c4c07d781e344ac6119953fed77ecb27' into codex/pr…
luvs01 Oct 24, 2026
2430fd7
Merge commit '8b4c34c7e6103314dcb061f0f82a8eaafdb4d386' into codex/pr…
luvs01 Oct 24, 2026
5d1dee3
Merge commit '8ebe46b2076445d100cef1e52af4d98d78f8f1e1' into codex/71…
luvs01 Oct 25, 2026
5b6358c
Merge origin/dev c2f3d8b42b into codex/update-service-home-authority-…
lidge-jun Oct 26, 2026
3b23d9a
fix(update): gate ghost recovery and handle retained ownership leases
lidge-jun Oct 26, 2026
ad87532
fix(update): gate pinned-start preparation mutations
lidge-jun Oct 26, 2026
4cb19fd
fix(update): recheck recovery home authority after ownership awaits
lidge-jun Oct 26, 2026
655cec3
Merge remote-tracking branch 'origin/dev' into la-7154-fix3
lidge-jun Oct 26, 2026
3f2ed2d
test(update): tie post-stop home transition to stopped runtime
lidge-jun Oct 26, 2026
f173a0a
test(update): preserve async Node updater in production-body fixture
lidge-jun Oct 26, 2026
8c480f5
test(update): inject Windows async package transaction in home fixture
lidge-jun Oct 26, 2026
367e7d0
fix(update): revalidate staged swap and foreground recovery authority
lidge-jun Oct 26, 2026
2aebb3d
Merge current origin/dev into la-7154-fix4
lidge-jun Oct 26, 2026
5e981a5
test(update): cover delegated staging and final liveness home checks
lidge-jun Oct 26, 2026
8e13310
fix(update): retain home refusal across service stop and ownership ve…
lidge-jun Oct 26, 2026
23f10d5
test(update): preserve absent-service foreground stop control
lidge-jun Oct 26, 2026
e4b8335
fix(update): authorize every recovery mutation and terminal success
lidge-jun Oct 26, 2026
714ea24
Merge current origin/dev into PR 7154 round-six guard fix
lidge-jun Oct 26, 2026
cd4f2d2
fix(update): preserve fresh wrapper and scanner authority
lidge-jun Oct 26, 2026
b99de55
Merge round-7 update authority fixes with current origin/dev
lidge-jun Oct 26, 2026
0c46a8e
test(update): reject blocking mutation argument evaluation
lidge-jun Oct 26, 2026
c92bf59
test(update): cover computed names and formatting accessors
lidge-jun Oct 26, 2026
f62b968
test(update): refresh extracted restart source contracts
lidge-jun Oct 26, 2026
c0c8e08
test(update): require inert guarded mutation expressions
lidge-jun Oct 26, 2026
7305bee
Merge current origin/dev into la-7154-fix9
lidge-jun Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 110 additions & 24 deletions bin/ocx.mjs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ and, afterwards, a folder next to the package that cannot be deleted because `bu
The updater stops the proxy, installs the new version into a separate staging folder, checks it,
and only then swaps it in. When the install step fails, the swap never happens: the previous version
stays installed and runnable, and the updater restarts the previous background service (or the
previous proxy) before it exits. You do not need to reinstall to get back to a working proxy.
previous proxy) before it exits, when ownership and service-home checks authorize recovery.

If the initial service-home check is inconclusive, for example because Bun is missing or a service
query fails, the updater can replace an idle package without stopping or restarting the proxy.
A successful package-only update prints a manual restart hint and explains that service ownership
could not be verified. Check `ocx service status` and the registered installation before restarting.
Changes in authority, unknown durable runtime ownership, Desktop supervision, a live package
runtime or an unresolved teardown receipt still block package replacement; an inconclusive check is not permission to control them.

Check that with:

Expand Down
7 changes: 7 additions & 0 deletions scripts/test-layout/layout-update-home.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"update-home-launcher.test.ts": "update",
"update-home-recovery.test.ts": "update",
"update-home-worker.test.ts": "update",
"update-service-home-policy.test.ts": "update",
"update-service-home.test.ts": "update"
}
2 changes: 1 addition & 1 deletion scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": 1,
"root": "tests",
"explicitShards": ["layout-clients.json", "layout-codex-collision.json", "layout-agent-assertion.json"],
"explicitShards": ["layout-clients.json", "layout-codex-collision.json", "layout-update-home.json", "layout-agent-assertion.json"],
"explicit": { "combo-description.test.ts": "routing", "combo-description-catalog.test.ts": "codex-integration", "codex-provider-routing.test.ts": "codex-integration", "config-native-reasoning-retention.test.ts": "config", "server-native-reasoning-retention-merge.test.ts": "server", "server-native-reasoning-retention.test.ts": "server", "responses-retained-reasoning-stream-recovery.test.ts": "responses", "openai-native-hosted-search-history.test.ts": "adapters/openai", "responses-native-compact-search-history.test.ts": "responses", "config-hosted-search-projection.test.ts": "config", "gcp-credential-boundary.test.ts": "adapters/google", "gcp-sa-marker.test.ts": "adapters/google", "gcp-sa-adapter.test.ts": "adapters/google", "management-gcp-credential-input.test.ts": "server", "codex-preserve-signed-in.test.ts": "codex-integration", "cli-account-preservation.test.ts": "cli", "cli-provider-list-active.test.ts": "cli", "service-state-isolation.test.ts": "service", "compaction-history-framing.test.ts": "responses", "catalog-native-max-context.test.ts": "codex-integration", "protocol-argon-thought-boundary.test.ts": "responses", "service-log.test.ts": "service", "messages-native-empty-inline.test.ts": "claude-integration", "catalog-auto-review-override.test.ts": "codex-integration", "management-auto-review-settings.test.ts": "server", "management-auto-review-auth.test.ts": "server", "prefer-model-alias-ids.test.ts": "server", "responses-mimo-envelope.test.ts": "responses", "responses-call-id-linkage.test.ts": "responses", "bridge-terminal-refusal.test.ts": "adapters", "loop-terminal-refusal.test.ts": "images", "web-search-terminal-refusal.test.ts": "web-search", "combo-terminal-refusal.test.ts": "routing", "server-terminal-refusal-wiring.test.ts": "server", "incomplete-reason-normalization.test.ts": "responses", "advisor-retention.test.ts": "advisor", "responses-native-key-cooldown.test.ts": "responses", "responses-run-turn-key-readmission.test.ts": "responses", "qoder-spawn-readmission.test.ts": "adapters", "api-key-selection-cooldown.test.ts": "providers", "api-key-auto-pause-distribution.test.ts": "providers", "explicit-key-pause-oauth-boundary.test.ts": "providers", "server-chat-native-key-selection.test.ts": "server", "compact-key-selection.test.ts": "responses", "messages-native-key-selection.test.ts": "claude-integration",
"openai-chat-standalone-bare-block.test.ts": "adapters/openai",
"owner-registry-isolation.test.ts": "config",
Expand Down
76 changes: 66 additions & 10 deletions src/update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { ServiceOwnership } from "../service/state";
import { planUpdateRuntimeHandling, planStoppedRuntimeRecovery, inspectPackageRuntimeLiveness } from "./runtime-ownership.mjs";
import { unprivilegedOwnershipMutationEnvironment } from "../service/ownership-mutation-lease.mjs";
import { withUpdateOwnershipLease, readUpdateRuntimeTarget } from "./ownership-transaction";
import { createUpdateServiceHomePolicy, parseUpdateWorkerHome, UPDATE_PACKAGE_ONLY_EXIT_CODE } from "./service-home-policy.mjs";
import { npmInvocation } from "./npm-invocation.mjs";
import { pnpmInvocation, pnpmInvocationForPath, resolvePnpmCommands } from "./pnpm-invocation.mjs";
import { detectInstallOwnershipFromPath } from "./install-detection.mjs";
Expand Down Expand Up @@ -489,12 +490,17 @@ export async function runUpdate(): Promise<void> {

const { serviceStatePaths } = await import("../service");
const updateExitCode = await withUpdateOwnershipLease(serviceStatePaths(), async mutation => {
// Ownership inspection imports the service CLI, which reaches this update entry again.
// Load it only after entering the lease; the policy itself stays a dependency-free import.
const { inspectUpdateServiceHome } = await import("./service-home-authority");
const homePolicy = createUpdateServiceHomePolicy(inspectUpdateServiceHome, message => console.warn(message), process.argv.includes("--update-package-only"), parseUpdateWorkerHome(process.argv));
if (!homePolicy.checkPackage()) return 1;
// Remember whether a background service manages the proxy BEFORE stopping — `ocx stop`
// unloads it, so a successful update must repair/restart it afterwards.
let serviceWasInstalled = false;
try {
const { isServiceInstalled } = await import("../service");
serviceWasInstalled = isServiceInstalled();
serviceWasInstalled = homePolicy.serviceAllowed && isServiceInstalled();
} catch { /* best-effort */ }
// What this update may do to the runtime. A desktop takeover vetoes both the stop and the
// service refresh below; see `planUpdateRuntimeHandling` for why each half is wrong.
Expand All @@ -513,7 +519,7 @@ export async function runUpdate(): Promise<void> {
}
let trayWasInstalled = false;
let trayWasRunning = false;
if (process.platform === "win32") {
if (!homePolicy.packageOnly && process.platform === "win32") {
try {
const { getWindowsTrayStatus, startWindowsTray, stopWindowsTray } = await import("../tray/windows");
const tray = getWindowsTrayStatus();
Expand Down Expand Up @@ -566,10 +572,13 @@ export async function runUpdate(): Promise<void> {
};
/** `"vetoed"`: another owner holds the runtime, so not starting is the correct outcome. */
const startProxyDirectly = async (): Promise<boolean | "vetoed"> => {
// Every caller, including error recovery, owns the lock and fresh home proof here.
mutation.reacquire();
if (!homePolicy.check()) return "vetoed";
const ownership = await resolvedRuntimeOwnership();
const supervision = observeSupervision();
const plan = planUpdateRuntimeHandling({ ...ownership, serviceInstalled: false, supervision });
if (!plan.mayStopRuntime) {
if (!sameOwner(ownership) || !plan.mayStopRuntime || !homePolicy.check()) {
console.warn(supervision
? "OpenCodex Desktop supervises the proxy; no CLI runtime was restored."
: plan.notice);
Expand Down Expand Up @@ -600,8 +609,14 @@ export async function runUpdate(): Promise<void> {
// and count it only when the serving proxy is that job. Runs unleased: the manager's child
// cannot join this lease (#5760). `"unsettled"` means the manager may still finish the restart,
// so no competing proxy may be started.
const restartThroughServiceManager = async (): Promise<"supervised" | "failed" | "unsettled"> => {
const restartThroughServiceManager = async (): Promise<"supervised" | "failed" | "unsettled" | "refused"> => {
if (!hasRestartableServiceManager()) return "failed";
mutation.release();
const owner = await resolvedRuntimeOwnership();
if (!homePolicy.check()) return "refused";
if (!homePolicy.serviceAllowed) return "failed";
if (!sameOwner(owner) || !planUpdateRuntimeHandling({ ...owner, serviceInstalled: true,
supervision: observeSupervision() }).mayRestoreService) return "refused";
console.log("🔁 Asking the service manager to restart the background service...");
const before = readUpdateRuntimeTarget(getRuntimePortPath(), capturedListen.hostname);
const staleRecordPid = before.kind === "target" ? before.target.pid : null;
Expand Down Expand Up @@ -652,12 +667,14 @@ export async function runUpdate(): Promise<void> {
});
};
let recovery = await planRecovery();
if (!homePolicy.check()) return;
if (recovery.action === "service") {
// The service manager starts the proxy outside this process tree, so it
// cannot join this lease, and holding it through the repair's health wait
// keeps that proxy from starting (#5760). Release it, then decide again —
// a claim could have landed while the repair ran unleased.
mutation.release();
if (!homePolicy.check()) return;
recovery = await planRecovery();
}
if (recovery.reason === "desktop-supervised") {
Expand All @@ -666,6 +683,8 @@ export async function runUpdate(): Promise<void> {
console.warn(`⚠️ ${reason}; runtime recovery requires manual review (${recovery.reason}).`);
} else if (recovery.action === "service") {
const { serviceReinstallArgs, isServiceViable } = await import("../service");
if (!homePolicy.check()) return;
if (!homePolicy.serviceAllowed) { await startProxyDirectly(); return; }
recovery = await planRecovery();
if (recovery.action !== "service") {
console.warn("Runtime authority changed before service recovery; no CLI runtime was restored.");
Expand All @@ -675,6 +694,8 @@ export async function runUpdate(): Promise<void> {
stdio: updateChildStdio(), windowsHide: true,
env: mutation.controlEnvironment({ ...process.env, OCX_BAKE_PORT: String(capturedListen.port) }),
});
if (!homePolicy.check()) return;
if (!homePolicy.serviceAllowed) { await startProxyDirectly(); return; }
if (service.status !== 0 || !isServiceViable()) {
// Ownership may have moved while the refresh ran unleased; only a recovery plan that
// still names the service may restart the manager's job.
Expand Down Expand Up @@ -716,7 +737,12 @@ export async function runUpdate(): Promise<void> {
// silently skips the recovery the receipt was written to trigger (#3008).
// Full `ocx stop` semantics (drain, service stop, restore).

if (runtimePlan.mayStopRuntime && (serviceWasInstalled || readPid() || readRuntimePort() || pendingTeardownOutstanding())) {
if (homePolicy.packageOnly && pendingTeardownOutstanding()) {
console.error("Pending teardown requires manual recovery before package replacement; run 'ocx stop' from the owning installation.");
return 1;
}
if (!homePolicy.packageOnly && runtimePlan.mayStopRuntime && (serviceWasInstalled || readPid() || readRuntimePort() || pendingTeardownOutstanding())) {
if (!homePolicy.check()) return 1;
const preStopPlan = planUpdateRuntimeHandling({
...(await resolvedRuntimeOwnership()),
serviceInstalled: serviceWasInstalled,
Expand Down Expand Up @@ -805,7 +831,7 @@ export async function runUpdate(): Promise<void> {
supervision: observeSupervision(),
});
const replacementLiveness = currentPackageRuntimeLiveness();
if (replacementOwnership.subjectToken !== initialOwnership.subjectToken
if (!homePolicy.checkPackage() || replacementOwnership.subjectToken !== initialOwnership.subjectToken
|| !replacementPlan.mayReplacePackage
|| replacementLiveness !== "dead") {
replacementRefusal = replacementPlan.notice
Expand Down Expand Up @@ -885,6 +911,23 @@ export async function runUpdate(): Promise<void> {
});
if (r.status === 0) {
console.log(`\n✅ Updated${latest ? ` to v${latest}` : ""}.`);
if (homePolicy.packageOnly) {
homePolicy.check(homePolicy.initial);
console.log(`Restart the proxy: ${launcherStartHint(postUpdateLauncher, capturedListen.port)}`);
return process.argv.includes("--update-worker-result") ? UPDATE_PACKAGE_ONLY_EXIT_CODE : undefined;
}
// Authority gates every post-replacement control action (shim, tray, service), like
// the Node launcher; refusal may only restore the own tray stopped before replacement.
if (!homePolicy.check()) {
if (trayWasRunning && postUpdateLauncherUsable && existsSync(postUpdateLauncher)) {
try {
spawnSync(process.execPath, [postUpdateLauncher, "tray", "start"], {
stdio: "ignore", windowsHide: true, timeout: 15_000, env: mutation.unprivilegedEnvironment(),
});
} catch { /* preserve the authority refusal */ }
}
return 1;
}
if (!postInstallPlan.mayStopRuntime) console.warn("Runtime authority does not permit CLI recovery; no service was refreshed or proxy started.");
// Re-enter through the verified active package launcher. This keeps the Codex
// shim, tray, service and proxy recovery paths on the same package/group that
Expand Down Expand Up @@ -915,20 +958,28 @@ export async function runUpdate(): Promise<void> {
// The stop above unloaded any managed service; repair it with the NEW files
// (spawn the fresh cli.ts so updated code writes the baked paths) so a
// launchd/schtasks/systemd user isn't left with the background proxy down.
if (!homePolicy.serviceAllowed && postInstallPlan.mayStopRuntime) {
if (stopAttempted) return await startProxyDirectly() === true ? undefined : 1;
console.log(`Restart the proxy: ${launcherStartHint(postUpdateLauncher, capturedListen.port)}`);
return;
}
if (postInstallPlan.mayRestoreService) {
let homeRefused = false;
const mayRefresh = async () => {
if (!homePolicy.check()) { homeRefused = true; return false; }
const ownership = await resolvedRuntimeOwnership();
const supervision = observeSupervision();
const plan = planUpdateRuntimeHandling({ ...ownership, serviceInstalled: true, supervision });
if (!plan.mayRestoreService) console.warn(supervision
? "OpenCodex Desktop supervises the proxy; CLI recovery was skipped. Use the app's updater (tray → Check for Updates)."
: plan.notice);
return plan.mayRestoreService;
return plan.mayRestoreService && homePolicy.serviceAllowed;
};
if (!await mayRefresh()) return;
if (!await mayRefresh()) return homeRefused ? 1 : undefined;
console.log("🔁 Refreshing the background service with the updated files...");
const { serviceReinstallArgs } = await import("../service");
const { reclaimListenPort } = await import("../server/port-reclaim");
if (!homePolicy.check()) return 1;
const freed = await reclaimListenPort(capturedListen.port, capturedListen.hostname, {
timeoutMs: 30_000,
intervalMs: 100,
Expand All @@ -944,17 +995,21 @@ export async function runUpdate(): Promise<void> {
// wait, the lease makes that child die at the acquire deadline (#5760).
// The kill-authorizing reclaim above already ran under the lease.
mutation.release();
if (!homePolicy.check()) return 1;
if (!homePolicy.serviceAllowed) return await startProxyDirectly() === true ? undefined : 1;
const prevBake = process.env.OCX_BAKE_PORT;
process.env.OCX_BAKE_PORT = String(capturedListen.port);
try {
if (!await mayRefresh()) return;
if (!await mayRefresh()) return homeRefused ? 1 : undefined;
const svcStdio = updateChildStdio();
const svc = spawnSync(process.execPath, [postUpdateLauncher, ...serviceReinstallArgs()], {
env: mutation.controlEnvironment(),
stdio: svcStdio,
encoding: svcStdio === "pipe" ? "utf8" : undefined,
windowsHide: true,
});
if (!homePolicy.check()) return 1;
if (!homePolicy.serviceAllowed) return await startProxyDirectly() === true ? undefined : 1;
if (svcStdio === "pipe") logSpawnOutput("", svc);
const serviceRefreshed = svc.status === 0;
let serviceViable = serviceRefreshed;
Expand All @@ -969,10 +1024,11 @@ export async function runUpdate(): Promise<void> {
if (!serviceRefreshed || !serviceViable) {
// #7043: the manager first. Only a proxy it owns counts as recovered; everything
// below this line leaves an unsupervised runtime and exits non-zero.
if (!await mayRefresh()) return;
if (!await mayRefresh()) return homeRefused ? 1 : undefined;
const managed = await restartThroughServiceManager();
if (managed === "supervised") return;
if (managed === "unsettled") return 1;
if (managed === "refused") return 1;
// Repair normally avoids elevation for a healthy scheduler task, but a stale
// definition may require guarded create/elevation. It can also fail — or exit 0
// while leaving stale/missing assets that never start
Expand Down
8 changes: 7 additions & 1 deletion src/update/job-supervised-restart.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readRuntimePort } from "../config/process-state";
import { proxyIdentityAt } from "../server/proxy-liveness";
import { inspectUpdateServiceHome, updateServiceHomeRefusal, type UpdateServiceHomeInspector } from "./service-home-authority";
import {
freshServingPid, hasRestartableServiceManager, readParentPid, readServiceManagerPid, restartServiceManagerJob,
supervisionVerdict, unsettledManagerWarning, unsupervisedRuntimeWarning,
Expand All @@ -12,9 +13,10 @@ import {
* detached `ocx start`, and counts it only when the serving proxy is that job. It lives beside the
* worker because `src/update/job.ts` sits at the repository's size cap.
*/
export type ManagerRestartOutcome = "supervised" | "failed" | "unsettled";
export type ManagerRestartOutcome = "supervised" | "failed" | "unsettled" | "refused";

export interface ManagerRestartDeps {
serviceHomeAuthorityFn?: UpdateServiceHomeInspector;
hasManager?: () => boolean;
restartJob?: () => { ok: true } | { ok: false; reason: string; unsettled?: true };
managerPid?: () => number | null;
Expand Down Expand Up @@ -43,6 +45,10 @@ export async function restartServiceManagerAfterUpdate(
): Promise<ManagerRestartOutcome> {
if (!(deps.hasManager ?? hasRestartableServiceManager)()) return "failed";
target.release?.();
const home = (deps.serviceHomeAuthorityFn ?? inspectUpdateServiceHome)();
const refusal = updateServiceHomeRefusal(home);
if (refusal) { log(refusal); return "refused"; }
if (home.kind === "absent") return "failed";
log("Asking the service manager to restart the background service...");
const readRecord = deps.readRecord ?? (() => readRuntimePort());
const probe = deps.probeServing ?? (async (port: number, hostname: string, pid: number, deadlineAt: number) => (
Expand Down
Loading
Loading