mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
docs: document status summary helpers
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
// Top-level status scan entrypoint.
|
||||
// Chooses fast JSON policy or full human scan and returns one normalized scan result.
|
||||
|
||||
import { withProgress } from "../cli/progress.js";
|
||||
import { hasConfiguredChannelsForReadOnlyScope } from "../plugins/channel-plugin-ids.js";
|
||||
import { buildPluginCompatibilitySnapshotNotices } from "../plugins/status.js";
|
||||
@@ -8,6 +11,7 @@ import { collectStatusScanOverview } from "./status.scan-overview.ts";
|
||||
import type { StatusScanResult } from "./status.scan-result.ts";
|
||||
import { scanStatusJsonWithPolicy } from "./status.scan.fast-json.js";
|
||||
|
||||
/** Runs the status scan for text or JSON command modes. */
|
||||
export async function scanStatus(
|
||||
opts: {
|
||||
json?: boolean;
|
||||
@@ -18,6 +22,7 @@ export async function scanStatus(
|
||||
_runtime: RuntimeEnv,
|
||||
): Promise<StatusScanResult> {
|
||||
if (opts.json) {
|
||||
// JSON mode uses a policy wrapper so tests and `status-json` can tune fast-path behavior.
|
||||
return await scanStatusJsonWithPolicy(
|
||||
{
|
||||
timeoutMs: opts.timeoutMs,
|
||||
@@ -54,6 +59,7 @@ export async function scanStatus(
|
||||
showSecrets: process.env.OPENCLAW_SHOW_SECRETS?.trim() !== "0",
|
||||
includeLiveChannelStatus: isFullScan,
|
||||
includeChannelSetupRuntimeFallback: isFullScan,
|
||||
// Fast status avoids local secret resolution and relies on config/runtime hints.
|
||||
channelCredentialResolutionSkipped: !isFullScan,
|
||||
includeChannelSecretTargets: isFullScan ? undefined : false,
|
||||
fetchGitUpdate: isFullScan,
|
||||
@@ -80,6 +86,7 @@ export async function scanStatus(
|
||||
const result = await executeStatusScanFromOverview({
|
||||
overview,
|
||||
resolveMemory: async ({ cfg, agentStatus, memoryPlugin }) =>
|
||||
// Memory plugin probing can touch disk/plugin state; reserve it for full scans.
|
||||
opts.all
|
||||
? await resolveStatusMemoryStatusSnapshot({
|
||||
cfg,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Reads service manager state for status reports.
|
||||
// Converts gateway/node launchd/systemd state into a compact summary shape.
|
||||
|
||||
import {
|
||||
summarizeGatewayServiceLayout,
|
||||
type GatewayServiceLayoutSummary,
|
||||
@@ -16,6 +19,7 @@ export type ServiceStatusSummary = {
|
||||
layout?: GatewayServiceLayoutSummary;
|
||||
};
|
||||
|
||||
/** Reads a daemon service summary, falling back to unknown when service inspection fails. */
|
||||
export async function readServiceStatusSummary(
|
||||
service: GatewayService,
|
||||
fallbackLabel: string,
|
||||
@@ -24,6 +28,7 @@ export async function readServiceStatusSummary(
|
||||
const state = await readGatewayServiceState(service, { env: process.env });
|
||||
const layout = await summarizeGatewayServiceLayout(state.command);
|
||||
const managedByOpenClaw = state.installed;
|
||||
// A running unmanaged process still counts as installed for status display.
|
||||
const externallyManaged = !managedByOpenClaw && state.running;
|
||||
const installed = managedByOpenClaw || externallyManaged;
|
||||
const loadedText = externallyManaged
|
||||
@@ -42,6 +47,7 @@ export async function readServiceStatusSummary(
|
||||
...(layout ? { layout } : {}),
|
||||
};
|
||||
} catch {
|
||||
// Status output should survive service-manager errors and show an unknown row.
|
||||
return {
|
||||
label: fallbackLabel,
|
||||
installed: null,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Runtime helpers for building status summaries.
|
||||
// Kept behind a lazy surface because status summary imports model/session/runtime metadata helpers.
|
||||
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
@@ -27,6 +30,7 @@ function resolveStatusModelRefFromRaw(params: {
|
||||
}
|
||||
const configuredModels = params.cfg.agents?.defaults?.models ?? {};
|
||||
if (!trimmed.includes("/")) {
|
||||
// Bare model names may be aliases from agents.defaults.models before falling back to default provider.
|
||||
const aliasKey = normalizeLowercaseStringOrEmpty(trimmed);
|
||||
for (const [modelKey, entry] of Object.entries(configuredModels)) {
|
||||
const aliasValue = (entry as { alias?: unknown } | undefined)?.alias;
|
||||
@@ -60,6 +64,7 @@ function resolveConfiguredStatusModelRef(params: {
|
||||
)
|
||||
: undefined;
|
||||
if (agentRawModel) {
|
||||
// Agent-specific primary model wins over global defaults for session status rows.
|
||||
const parsed = resolveStatusModelRefFromRaw({
|
||||
cfg: params.cfg,
|
||||
rawModel: agentRawModel,
|
||||
@@ -141,6 +146,7 @@ function resolveSessionModelRef(
|
||||
agentId,
|
||||
});
|
||||
return (
|
||||
// Persisted selected model or overrides describe the active session, not just current config.
|
||||
resolvePersistedSelectedModelRef({
|
||||
defaultProvider: resolved.provider || DEFAULT_PROVIDER,
|
||||
runtimeProvider: entry?.modelProvider,
|
||||
@@ -178,6 +184,7 @@ function resolveSessionRuntimeLabel(params: {
|
||||
acpBackend: acpMeta?.backend,
|
||||
});
|
||||
const id = normalizeOptionalLowercaseString(runtime.id);
|
||||
// OpenClaw/auto are generic labels; concrete harness ids give better operator signal.
|
||||
const resolvedHarness = id && id !== "openclaw" && id !== "auto" ? id : undefined;
|
||||
return resolveAgentRuntimeLabel({
|
||||
config: params.cfg,
|
||||
@@ -196,6 +203,7 @@ function resolveContextTokensForModel(params: {
|
||||
allowAsyncLoad?: boolean;
|
||||
}): number | undefined {
|
||||
void params.allowAsyncLoad;
|
||||
// Status summaries are synchronous/read-only; caller passes allowAsyncLoad for interface parity only.
|
||||
if (typeof params.contextTokensOverride === "number" && params.contextTokensOverride > 0) {
|
||||
return params.contextTokensOverride;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Builds the status summary used by human and JSON status output.
|
||||
// It aggregates sessions, tasks, heartbeat, channel summary, and model/runtime metadata.
|
||||
|
||||
import { DEFAULT_CONTEXT_TOKENS, DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
||||
import { areRuntimeModelRefsEquivalent } from "../agents/model-runtime-aliases.js";
|
||||
import { getRuntimeConfig } from "../config/config.js";
|
||||
@@ -96,6 +99,7 @@ function discountRetainedLostTaskFailures(
|
||||
tasks: StatusSummary["tasks"],
|
||||
retainedLostCount: number,
|
||||
): StatusSummary["tasks"] {
|
||||
// Retained lost tasks are reported separately; avoid double-counting them as active failures.
|
||||
if (retainedLostCount <= 0 || tasks.failures <= 0) {
|
||||
return tasks;
|
||||
}
|
||||
@@ -129,16 +133,20 @@ function compareSessionCandidatesByUpdatedAt(left: SessionCandidate, right: Sess
|
||||
}
|
||||
|
||||
function listSessionCandidates(store: Record<string, SessionEntry | undefined>) {
|
||||
return Object.entries(store)
|
||||
.filter(([key]) => key !== "global" && key !== "unknown")
|
||||
.map(([key, entry]) => ({
|
||||
key,
|
||||
entry,
|
||||
updatedAt: entry?.updatedAt ?? null,
|
||||
}))
|
||||
.toSorted(compareSessionCandidatesByUpdatedAt);
|
||||
return (
|
||||
Object.entries(store)
|
||||
// Compatibility aggregate buckets are not real user sessions.
|
||||
.filter(([key]) => key !== "global" && key !== "unknown")
|
||||
.map(([key, entry]) => ({
|
||||
key,
|
||||
entry,
|
||||
updatedAt: entry?.updatedAt ?? null,
|
||||
}))
|
||||
.toSorted(compareSessionCandidatesByUpdatedAt)
|
||||
);
|
||||
}
|
||||
|
||||
/** Removes session paths and recent session details from a status summary. */
|
||||
export function redactSensitiveStatusSummary(summary: StatusSummary): StatusSummary {
|
||||
return {
|
||||
...summary,
|
||||
@@ -159,6 +167,7 @@ export function redactSensitiveStatusSummary(summary: StatusSummary): StatusSumm
|
||||
};
|
||||
}
|
||||
|
||||
/** Builds the aggregate status summary for agents, sessions, tasks, heartbeat, and channels. */
|
||||
export async function getStatusSummary(
|
||||
options: {
|
||||
includeSensitive?: boolean;
|
||||
@@ -212,6 +221,7 @@ export async function getStatusSummary(
|
||||
const mainSessionKey = resolveMainSessionKey(cfg);
|
||||
const queuedSystemEvents = peekSystemEvents(mainSessionKey);
|
||||
const taskMaintenanceModule = await loadTaskRegistryMaintenanceModule();
|
||||
// Configure maintenance store before reading task summaries so cron-backed tasks are in scope.
|
||||
taskMaintenanceModule.configureTaskRegistryMaintenance({
|
||||
cronStorePath: resolveCronJobsStorePath(cfg.cron?.store),
|
||||
});
|
||||
@@ -285,6 +295,7 @@ export async function getStatusSummary(
|
||||
selectedModelLabel !== configuredSessionModelLabel &&
|
||||
!areRuntimeModelRefsEquivalent(selectedModelLabel, configuredSessionModelLabel) &&
|
||||
hasUserPinnedModelSelection(entry);
|
||||
// Session rows show the live selected model but warn only for user-pinned differences.
|
||||
const contextTokens =
|
||||
resolveContextTokensForModel({
|
||||
cfg,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Public status command barrel.
|
||||
// Exposes the command, summary builder, and summary types without importing implementation details.
|
||||
|
||||
export { statusCommand } from "./status.command.js";
|
||||
export { getStatusSummary } from "./status.summary.js";
|
||||
export type { SessionStatus, StatusSummary } from "./status.types.js";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Shared status output types.
|
||||
// These shapes are consumed by scan, summary, text report, and JSON status builders.
|
||||
|
||||
import type { ChannelId } from "../channels/plugins/types.public.js";
|
||||
import type { SessionKind } from "../sessions/classify-session-kind.js";
|
||||
import type {
|
||||
@@ -38,6 +41,7 @@ export type SessionStatus = {
|
||||
flags: string[];
|
||||
};
|
||||
|
||||
/** Heartbeat schedule state for one agent. */
|
||||
export type HeartbeatStatus = {
|
||||
agentId: string;
|
||||
enabled: boolean;
|
||||
@@ -45,6 +49,7 @@ export type HeartbeatStatus = {
|
||||
everyMs: number | null;
|
||||
};
|
||||
|
||||
/** Aggregate status summary before text or JSON formatting. */
|
||||
export type StatusSummary = {
|
||||
runtimeVersion?: string | null;
|
||||
eventLoop?: import("../gateway/server/event-loop-health.js").GatewayEventLoopHealth;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Update status helpers for `openclaw status`.
|
||||
// Wraps registry/git update checks and formats compact update rows/hints.
|
||||
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveOpenClawPackageRoot } from "../infra/openclaw-root.js";
|
||||
import { normalizeUpdateChannel, resolveRegistryUpdateChannel } from "../infra/update-channels.js";
|
||||
@@ -8,6 +11,7 @@ import {
|
||||
} from "../infra/update-check.js";
|
||||
import { VERSION } from "../version.js";
|
||||
|
||||
/** Runs the update check using the configured update channel and current install root. */
|
||||
export async function getUpdateCheckResult(params: {
|
||||
timeoutMs: number;
|
||||
fetchGit: boolean;
|
||||
@@ -40,6 +44,7 @@ export type UpdateAvailability = {
|
||||
gitBehind: number | null;
|
||||
};
|
||||
|
||||
/** Determines whether git and/or registry data indicate an available update. */
|
||||
export function resolveUpdateAvailability(update: UpdateCheckResult): UpdateAvailability {
|
||||
const latestVersion = update.registry?.latestVersion ?? null;
|
||||
const registryCmp = latestVersion ? compareSemverStrings(VERSION, latestVersion) : null;
|
||||
@@ -59,6 +64,7 @@ export function resolveUpdateAvailability(update: UpdateCheckResult): UpdateAvai
|
||||
};
|
||||
}
|
||||
|
||||
/** Formats the actionable update hint shown in status footers. */
|
||||
export function formatUpdateAvailableHint(update: UpdateCheckResult): string | null {
|
||||
const availability = resolveUpdateAvailability(update);
|
||||
if (!availability.available) {
|
||||
@@ -76,6 +82,7 @@ export function formatUpdateAvailableHint(update: UpdateCheckResult): string | n
|
||||
return `Update available${suffix}. Run: ${formatCliCommand("openclaw update")}`;
|
||||
}
|
||||
|
||||
/** Formats a compact one-line update summary for overview rows. */
|
||||
export function formatUpdateOneLiner(update: UpdateCheckResult): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
@@ -90,6 +97,7 @@ export function formatUpdateOneLiner(update: UpdateCheckResult): string {
|
||||
if (update.installKind !== "git") {
|
||||
parts.push("up to date");
|
||||
}
|
||||
// Git installs still show registry latest, but git ahead/behind remains the primary state.
|
||||
parts.push(`${registryLabel} ${update.registry.latestVersion}`);
|
||||
} else if (cmp != null && cmp < 0) {
|
||||
parts.push(
|
||||
|
||||
Reference in New Issue
Block a user