diff --git a/CHANGELOG.md b/CHANGELOG.md index f49e4575d..1e6e9fa43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Docs: https://docs.clawd.bot - Telegram: enrich forwarded message context with normalized origin details + legacy fallback. (#1090) — thanks @sleontenko. - macOS: strip prerelease/build suffixes when parsing gateway semver patches. (#1110) — thanks @zerone0x. - macOS: keep CLI install pinned to the full build suffix. (#1111) — thanks @artuskg. +- CLI: surface update availability in `clawdbot status`. ### Fixes - Doctor: avoid re-adding WhatsApp ack reaction config when only legacy auth files exist. (#1087) — thanks @YuriNachos. diff --git a/docs/cli/status.md b/docs/cli/status.md index 98d0288bd..c8794429c 100644 --- a/docs/cli/status.md +++ b/docs/cli/status.md @@ -19,3 +19,4 @@ clawdbot status --usage Notes: - `--deep` runs live probes (WhatsApp Web + Telegram + Discord + Slack + Signal). - Output includes per-agent session stores when multiple agents are configured. +- Update info surfaces in the Overview; if an update is available, status prints a hint to run `clawdbot update` (see [Updating](/install/updating)). diff --git a/src/commands/status.command.ts b/src/commands/status.command.ts index fa63b2559..1333476f4 100644 --- a/src/commands/status.command.ts +++ b/src/commands/status.command.ts @@ -19,7 +19,11 @@ import { } from "./status.format.js"; import { resolveGatewayProbeAuth } from "./status.gateway-probe.js"; import { scanStatus } from "./status.scan.js"; -import { formatUpdateOneLiner } from "./status.update.js"; +import { + formatUpdateAvailableHint, + formatUpdateOneLiner, + resolveUpdateAvailability, +} from "./status.update.js"; import { formatGatewayAuthUsed } from "./status-all/format.js"; import { statusAllCommand } from "./status-all.js"; @@ -228,6 +232,9 @@ export async function statusCommand( ? `${summary.sessions.paths.length} stores` : (summary.sessions.paths[0] ?? "unknown"); + const updateAvailability = resolveUpdateAvailability(update); + const updateLine = formatUpdateOneLiner(update).replace(/^Update:\s*/i, ""); + const overviewRows = [ { Item: "Dashboard", Value: dashboard }, { Item: "OS", Value: `${osSummary.label} · node ${process.versions.node}` }, @@ -242,7 +249,7 @@ export async function statusCommand( }, { Item: "Update", - Value: formatUpdateOneLiner(update).replace(/^Update:\s*/i, ""), + Value: updateAvailability.available ? warn(`available · ${updateLine}`) : updateLine, }, { Item: "Gateway", Value: gatewayValue }, { Item: "Daemon", Value: daemonValue }, @@ -456,6 +463,11 @@ export async function statusCommand( runtime.log("FAQ: https://docs.clawd.bot/faq"); runtime.log("Troubleshooting: https://docs.clawd.bot/troubleshooting"); runtime.log(""); + const updateHint = formatUpdateAvailableHint(update); + if (updateHint) { + runtime.log(theme.warn(updateHint)); + runtime.log(""); + } runtime.log("Next steps:"); runtime.log(" Need to share? clawdbot status --all"); runtime.log(" Need to debug live? clawdbot logs --follow"); diff --git a/src/commands/status.update.ts b/src/commands/status.update.ts index 6e7d8a4fb..f7db20c14 100644 --- a/src/commands/status.update.ts +++ b/src/commands/status.update.ts @@ -24,6 +24,48 @@ export async function getUpdateCheckResult(params: { }); } +export type UpdateAvailability = { + available: boolean; + hasGitUpdate: boolean; + hasRegistryUpdate: boolean; + latestVersion: string | null; + gitBehind: number | null; +}; + +export function resolveUpdateAvailability(update: UpdateCheckResult): UpdateAvailability { + const latestVersion = update.registry?.latestVersion ?? null; + const registryCmp = latestVersion ? compareSemverStrings(VERSION, latestVersion) : null; + const hasRegistryUpdate = registryCmp != null && registryCmp < 0; + const gitBehind = + update.installKind === "git" && typeof update.git?.behind === "number" + ? update.git.behind + : null; + const hasGitUpdate = gitBehind != null && gitBehind > 0; + + return { + available: hasGitUpdate || hasRegistryUpdate, + hasGitUpdate, + hasRegistryUpdate, + latestVersion: hasRegistryUpdate ? latestVersion : null, + gitBehind, + }; +} + +export function formatUpdateAvailableHint(update: UpdateCheckResult): string | null { + const availability = resolveUpdateAvailability(update); + if (!availability.available) return null; + + const details: string[] = []; + if (availability.hasGitUpdate && availability.gitBehind != null) { + details.push(`git behind ${availability.gitBehind}`); + } + if (availability.hasRegistryUpdate && availability.latestVersion) { + details.push(`npm ${availability.latestVersion}`); + } + const suffix = details.length > 0 ? ` (${details.join(" · ")})` : ""; + return `Update available${suffix}. Run: clawdbot update`; +} + export function formatUpdateOneLiner(update: UpdateCheckResult): string { const parts: string[] = []; if (update.installKind === "git" && update.git) {