|
1 | 1 | import { durableBunRuntime } from "../lib/bun-runtime"; |
2 | 2 | import { codexAutoStartEnabled, getConfigPath, getPidPath, readConfigDiagnostics, readPid, readRuntimePort, type RuntimePortState } from "../config"; |
3 | 3 | import { diagnoseCodexBundledPlugins, type CodexPluginsDiagnostic } from "../codex/plugins-doctor"; |
4 | | -import { isOpencodexHealthz, probeHostname } from "../server/proxy-liveness"; |
| 4 | +import { findLiveProxy, isOpencodexHealthz, probeHostname } from "../server/proxy-liveness"; |
5 | 5 | import type { OcxConfig } from "../types"; |
6 | 6 | import { diagnoseService } from "../service"; |
7 | 7 | import { collectStartupHealth, type StartupHealth } from "../codex/autostart-health"; |
@@ -102,6 +102,14 @@ export function selectListenTarget( |
102 | 102 | }; |
103 | 103 | } |
104 | 104 |
|
| 105 | +/** Prefer live result (including authoritative null pid) over the on-disk pid file. */ |
| 106 | +export function resolveStatusPid( |
| 107 | + live: { pid: number | null } | null, |
| 108 | + pidFile: number | null, |
| 109 | +): number | null { |
| 110 | + return live ? live.pid : pidFile; |
| 111 | +} |
| 112 | + |
105 | 113 | async function checkProxyHealth(target: ListenTarget): Promise<HealthCheck> { |
106 | 114 | const url = target.healthUrl; |
107 | 115 | const controller = new AbortController(); |
@@ -132,9 +140,33 @@ async function checkProxyHealth(target: ListenTarget): Promise<HealthCheck> { |
132 | 140 | export async function collectStatus(): Promise<CliStatusView> { |
133 | 141 | const configDiagnostics = readConfigDiagnostics(); |
134 | 142 | const config = configDiagnostics.config; |
135 | | - const pid = readPid(); |
136 | | - const listen = selectListenTarget(config, pid, pid ? readRuntimePort(pid) : null); |
137 | | - const health = await checkProxyHealth(listen); |
| 143 | + // Prefer identity-verified liveness (runtime-port + /healthz) over ocx.pid alone (#618). |
| 144 | + // Pass the already-resolved diagnostics config so findLiveProxy does not re-load and |
| 145 | + // warn on malformed config.json (status --json must stay stderr-clean). |
| 146 | + const live = await findLiveProxy({ |
| 147 | + configFn: () => ({ port: config.port, hostname: config.hostname }), |
| 148 | + }); |
| 149 | + const pidFile = readPid(); |
| 150 | + // Preserve an authoritative null from orphan/legacy liveness — do not restore pidFile. |
| 151 | + const pid = resolveStatusPid(live, pidFile); |
| 152 | + const listen = live |
| 153 | + ? { |
| 154 | + port: live.port, |
| 155 | + hostname: live.hostname, |
| 156 | + source: live.source, |
| 157 | + healthUrl: `http://${probeHostname(live.hostname)}:${live.port}/healthz`, |
| 158 | + dashboardUrl: `http://localhost:${live.port}/`, |
| 159 | + } |
| 160 | + : selectListenTarget(config, pidFile, pidFile ? readRuntimePort(pidFile) : null); |
| 161 | + // findLiveProxy already identity-probed /healthz; avoid a second fetch that can race. |
| 162 | + const health = live |
| 163 | + ? { |
| 164 | + ok: true, |
| 165 | + url: listen.healthUrl, |
| 166 | + message: `ok (pid ${live.pid ?? "unknown"})`, |
| 167 | + label: `${listen.healthUrl} ok (live)`, |
| 168 | + } |
| 169 | + : await checkProxyHealth(listen); |
138 | 170 | const bunRuntime = durableBunRuntime(); |
139 | 171 | const service = diagnoseService(); |
140 | 172 | const serviceSummary = service.summary; |
@@ -228,22 +260,24 @@ export async function collectStatus(): Promise<CliStatusView> { |
228 | 260 | runtimeVersion: clampActive ? (lastClamp?.runtimeVersion ?? null) : null, |
229 | 261 | }, |
230 | 262 | }; |
231 | | - const proxyLabel = pid && health.ok |
232 | | - ? `running (PID ${pid})` |
233 | | - : pid |
234 | | - ? `PID file points to PID ${pid}, but health check failed` |
235 | | - : health.ok |
236 | | - ? "reachable, but PID file is missing or stale" |
237 | | - : "not running"; |
| 263 | + const proxyLabel = live |
| 264 | + ? `running (PID ${live.pid ?? pid ?? "unknown"})` |
| 265 | + : pid && health.ok |
| 266 | + ? `running (PID ${pid})` |
| 267 | + : pid |
| 268 | + ? `PID file points to PID ${pid}, but health check failed` |
| 269 | + : health.ok |
| 270 | + ? "reachable, but PID file is missing or stale" |
| 271 | + : "not running"; |
238 | 272 |
|
239 | 273 | return { |
240 | 274 | proxyLabel, |
241 | 275 | healthLabel: health.label, |
242 | 276 | json: { |
243 | 277 | schemaVersion: 1, |
244 | 278 | proxy: { |
245 | | - running: Boolean(pid && health.ok), |
246 | | - pid, |
| 279 | + running: Boolean(live) || Boolean(pid && health.ok), |
| 280 | + pid: live?.pid ?? pid, |
247 | 281 | health: { |
248 | 282 | ok: health.ok, |
249 | 283 | url: health.url, |
|
0 commit comments