diff --git a/src/cli/daemon-cli.ts b/src/cli/daemon-cli.ts index 697de4ecd..d13d83e7a 100644 --- a/src/cli/daemon-cli.ts +++ b/src/cli/daemon-cli.ts @@ -575,6 +575,11 @@ function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) { if (runtimeLine) { defaultRuntime.log(`Runtime: ${runtimeLine}`); } + if (rpc && !rpc.ok && service.loaded && service.runtime?.status === "running") { + defaultRuntime.log( + "Warm-up: launch agents can take a few seconds. Try again shortly.", + ); + } if (rpc) { if (rpc.ok) { defaultRuntime.log("RPC probe: ok"); @@ -689,6 +694,8 @@ function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) { "If you need multiple gateways, isolate ports + config/state (see docs: /gateway#multiple-gateways-same-host).", ); } + defaultRuntime.log("Troubles: run clawdbot status"); + defaultRuntime.log("Troubleshooting: https://docs.clawd.bot/troubleshooting"); } export async function runDaemonStatus(opts: DaemonStatusOptions) { diff --git a/src/commands/status.test.ts b/src/commands/status.test.ts index 7b80ef758..2906581fa 100644 --- a/src/commands/status.test.ts +++ b/src/commands/status.test.ts @@ -38,6 +38,15 @@ vi.mock("../config/config.js", async (importOriginal) => { loadConfig: () => ({ session: {} }), }; }); +vi.mock("../daemon/service.js", () => ({ + resolveGatewayService: () => ({ + label: "LaunchAgent", + loadedText: "loaded", + notLoadedText: "not loaded", + isLoaded: async () => true, + readRuntime: async () => ({ status: "running", pid: 1234 }), + }), +})); import { statusCommand } from "./status.js"; @@ -69,6 +78,9 @@ describe("statusCommand", () => { expect(logs.some((l) => l.includes("Active sessions"))).toBe(true); expect(logs.some((l) => l.includes("Default model"))).toBe(true); expect(logs.some((l) => l.includes("tokens:"))).toBe(true); + expect(logs.some((l) => l.includes("Daemon:"))).toBe(true); + expect(logs.some((l) => l.includes("FAQ:"))).toBe(true); + expect(logs.some((l) => l.includes("Troubleshooting:"))).toBe(true); expect( logs.some((l) => l.includes("flags:") && l.includes("verbose:on")), ).toBe(true); diff --git a/src/commands/status.ts b/src/commands/status.ts index a51e816a2..3a0dad644 100644 --- a/src/commands/status.ts +++ b/src/commands/status.ts @@ -20,6 +20,7 @@ import { loadProviderUsageSummary, } from "../infra/provider-usage.js"; import { peekSystemEvents } from "../infra/system-events.js"; +import { resolveGatewayService } from "../daemon/service.js"; import type { RuntimeEnv } from "../runtime.js"; import { resolveWhatsAppAccount } from "../web/accounts.js"; import { resolveHeartbeatSeconds } from "../web/reconnect.js"; @@ -200,6 +201,38 @@ const classifyKey = ( return "direct"; }; +const formatDaemonRuntimeShort = (runtime?: { + status?: string; + pid?: number; + state?: string; + detail?: string; +}) => { + if (!runtime) return null; + const status = runtime.status ?? "unknown"; + const details: string[] = []; + if (runtime.pid) details.push(`pid ${runtime.pid}`); + if (runtime.state && runtime.state.toLowerCase() !== status) { + details.push(`state ${runtime.state}`); + } + if (runtime.detail) details.push(runtime.detail); + return details.length > 0 ? `${status} (${details.join(", ")})` : status; +}; + +async function getDaemonShortLine(): Promise { + try { + const service = resolveGatewayService(); + const [loaded, runtime] = await Promise.all([ + service.isLoaded({ env: process.env }).catch(() => false), + service.readRuntime(process.env).catch(() => undefined), + ]); + const loadedText = loaded ? service.loadedText : service.notLoadedText; + const runtimeShort = formatDaemonRuntimeShort(runtime); + return `Daemon: ${service.label} ${loadedText}${runtimeShort ? `, ${runtimeShort}` : ""}. Details: clawdbot daemon status`; + } catch { + return "Daemon: unknown. Details: clawdbot daemon status"; + } +} + const buildFlags = (entry: SessionEntry): string[] => { const flags: string[] = []; const think = entry?.thinkingLevel; @@ -290,6 +323,10 @@ export async function statusCommand( for (const line of summary.providerSummary) { runtime.log(` ${line}`); } + const daemonLine = await getDaemonShortLine(); + if (daemonLine) { + runtime.log(info(daemonLine)); + } if (health) { runtime.log(info("Gateway health: reachable")); @@ -350,4 +387,6 @@ export async function statusCommand( runtime.log(line); } } + runtime.log("FAQ: https://docs.clawd.bot/faq"); + runtime.log("Troubleshooting: https://docs.clawd.bot/troubleshooting"); }