feat(cli): distinguish local and connected daemon status
This commit is contained in:
@@ -15,7 +15,8 @@ interface ProviderBinaryStatus {
|
|||||||
|
|
||||||
interface DaemonStatus {
|
interface DaemonStatus {
|
||||||
serverId: string | null;
|
serverId: string | null;
|
||||||
status: "running" | "stopped" | "unresponsive";
|
localDaemon: "running" | "stopped" | "stale_pid" | "unresponsive";
|
||||||
|
connectedDaemon: "reachable" | "unreachable" | "not_probed";
|
||||||
home: string;
|
home: string;
|
||||||
listen: string;
|
listen: string;
|
||||||
hostname: string | null;
|
hostname: string | null;
|
||||||
@@ -85,11 +86,16 @@ function createStatusSchema(status: DaemonStatus): OutputSchema<StatusRow> {
|
|||||||
header: "VALUE",
|
header: "VALUE",
|
||||||
field: "value",
|
field: "value",
|
||||||
color: (_, item) => {
|
color: (_, item) => {
|
||||||
if (item.key === "Status") {
|
if (item.key === "Local Daemon") {
|
||||||
if (item.value === "running") return "green";
|
if (item.value === "running") return "green";
|
||||||
if (item.value === "unresponsive") return "yellow";
|
if (item.value === "unresponsive") return "yellow";
|
||||||
return "red";
|
return "red";
|
||||||
}
|
}
|
||||||
|
if (item.key === "Connected Daemon") {
|
||||||
|
if (item.value === "reachable") return "green";
|
||||||
|
if (item.value === "not_probed") return "yellow";
|
||||||
|
return "red";
|
||||||
|
}
|
||||||
if (item.key.startsWith(" ")) {
|
if (item.key.startsWith(" ")) {
|
||||||
if (item.value === "not found") return "red";
|
if (item.value === "not found") return "red";
|
||||||
if (item.value.endsWith("(--version failed)")) return "yellow";
|
if (item.value.endsWith("(--version failed)")) return "yellow";
|
||||||
@@ -106,7 +112,8 @@ function createStatusSchema(status: DaemonStatus): OutputSchema<StatusRow> {
|
|||||||
function toStatusRows(status: DaemonStatus): StatusRow[] {
|
function toStatusRows(status: DaemonStatus): StatusRow[] {
|
||||||
const rows: StatusRow[] = [
|
const rows: StatusRow[] = [
|
||||||
{ key: "Server ID", value: status.serverId ?? "-" },
|
{ key: "Server ID", value: status.serverId ?? "-" },
|
||||||
{ key: "Status", value: status.status },
|
{ key: "Local Daemon", value: status.localDaemon },
|
||||||
|
{ key: "Connected Daemon", value: status.connectedDaemon },
|
||||||
{ key: "Home", value: status.home },
|
{ key: "Home", value: status.home },
|
||||||
{ key: "Listen", value: status.listen },
|
{ key: "Listen", value: status.listen },
|
||||||
{ key: "Hostname", value: status.hostname ?? "-" },
|
{ key: "Hostname", value: status.hostname ?? "-" },
|
||||||
@@ -199,6 +206,7 @@ export async function runStatusCommand(
|
|||||||
): Promise<StatusResult> {
|
): Promise<StatusResult> {
|
||||||
const home = typeof options.home === "string" ? options.home : undefined;
|
const home = typeof options.home === "string" ? options.home : undefined;
|
||||||
const state = resolveLocalDaemonState({ home });
|
const state = resolveLocalDaemonState({ home });
|
||||||
|
const host = resolveTcpHostFromListen(state.listen);
|
||||||
|
|
||||||
const owner = resolveOwnerLabel(state.pidInfo?.uid, state.pidInfo?.hostname);
|
const owner = resolveOwnerLabel(state.pidInfo?.uid, state.pidInfo?.hostname);
|
||||||
let daemonNode: string;
|
let daemonNode: string;
|
||||||
@@ -211,38 +219,60 @@ export async function runStatusCommand(
|
|||||||
daemonNode = "unknown (no PID available)";
|
daemonNode = "unknown (no PID available)";
|
||||||
}
|
}
|
||||||
const cliNode = process.execPath;
|
const cliNode = process.execPath;
|
||||||
let status: DaemonStatus["status"] = state.running ? "running" : "stopped";
|
let localDaemon: DaemonStatus["localDaemon"] = state.running ? "running" : "stopped";
|
||||||
|
let connectedDaemon: DaemonStatus["connectedDaemon"] = "not_probed";
|
||||||
let runningAgents: number | null = null;
|
let runningAgents: number | null = null;
|
||||||
let idleAgents: number | null = null;
|
let idleAgents: number | null = null;
|
||||||
let note: string | undefined;
|
let note: string | undefined;
|
||||||
|
|
||||||
if (!state.running && state.stalePidFile && state.pidInfo) {
|
if (!state.running && state.stalePidFile && state.pidInfo) {
|
||||||
|
localDaemon = "stale_pid";
|
||||||
note = `Stale PID file found for PID ${state.pidInfo.pid}`;
|
note = `Stale PID file found for PID ${state.pidInfo.pid}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.running) {
|
if (host) {
|
||||||
const host = resolveTcpHostFromListen(state.listen);
|
const client = await tryConnectToDaemon({ host, timeout: 1500 });
|
||||||
if (host) {
|
if (client) {
|
||||||
const client = await tryConnectToDaemon({ host, timeout: 1500 });
|
connectedDaemon = "reachable";
|
||||||
if (client) {
|
try {
|
||||||
try {
|
const agentsPayload = await client.fetchAgents({ filter: { includeArchived: true } });
|
||||||
const agentsPayload = await client.fetchAgents({ filter: { includeArchived: true } });
|
const agents = agentsPayload.entries.map((entry) => entry.agent);
|
||||||
const agents = agentsPayload.entries.map((entry) => entry.agent);
|
runningAgents = agents.filter((a) => a.status === "running").length;
|
||||||
runningAgents = agents.filter((a) => a.status === "running").length;
|
idleAgents = agents.filter((a) => a.status === "idle").length;
|
||||||
idleAgents = agents.filter((a) => a.status === "idle").length;
|
if (!state.running) {
|
||||||
} catch {
|
daemonNode = "unknown (API reachable, PID unresolved)";
|
||||||
status = "unresponsive";
|
note = appendNote(
|
||||||
note = appendNote(note, `Daemon PID is running but API requests to ${host} failed`);
|
note,
|
||||||
} finally {
|
state.pidInfo
|
||||||
await client.close().catch(() => {});
|
? `Connected daemon is reachable at ${host} even though local daemon PID ${state.pidInfo.pid} is stale`
|
||||||
|
: `Connected daemon is reachable at ${host} but no local daemon PID file was found`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} catch {
|
||||||
status = "unresponsive";
|
if (state.running) {
|
||||||
note = appendNote(note, `Daemon PID is running but websocket at ${host} is not reachable`);
|
localDaemon = "unresponsive";
|
||||||
|
}
|
||||||
|
note = appendNote(
|
||||||
|
note,
|
||||||
|
state.running
|
||||||
|
? `Local daemon PID is running but API requests to ${host} failed`
|
||||||
|
: `Connected daemon websocket is reachable at ${host} but fetch_agents failed`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await client.close().catch(() => {});
|
||||||
}
|
}
|
||||||
|
} else if (state.running) {
|
||||||
|
connectedDaemon = "unreachable";
|
||||||
|
localDaemon = "unresponsive";
|
||||||
|
note = appendNote(
|
||||||
|
note,
|
||||||
|
`Local daemon PID is running but websocket at ${host} is not reachable`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
note = appendNote(note, "Daemon is configured for unix socket listen; API probe skipped");
|
connectedDaemon = "unreachable";
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
note = appendNote(note, "Daemon is configured for unix socket listen; API probe skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
const cliVersion = resolveCliVersion();
|
const cliVersion = resolveCliVersion();
|
||||||
@@ -258,7 +288,8 @@ export async function runStatusCommand(
|
|||||||
|
|
||||||
const daemonStatus: DaemonStatus = {
|
const daemonStatus: DaemonStatus = {
|
||||||
serverId,
|
serverId,
|
||||||
status,
|
localDaemon,
|
||||||
|
connectedDaemon,
|
||||||
home: state.home,
|
home: state.home,
|
||||||
listen: state.listen,
|
listen: state.listen,
|
||||||
hostname: state.pidInfo?.hostname ?? null,
|
hostname: state.pidInfo?.hostname ?? null,
|
||||||
|
|||||||
Reference in New Issue
Block a user