feat: show update availability in status

This commit is contained in:
Peter Steinberger
2026-01-17 18:23:27 +00:00
parent 8a67d29748
commit 7a3fa9ce03
4 changed files with 58 additions and 2 deletions

View File

@@ -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.

View File

@@ -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)).

View File

@@ -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");

View File

@@ -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) {