openclaw/src/cli/program/register.status-health-sess...

193 lines
7.1 KiB
TypeScript

import type { Command } from "commander";
import { healthCommand } from "../../commands/health.js";
import { sessionsCleanupCommand } from "../../commands/sessions-cleanup.js";
import { sessionsCommand } from "../../commands/sessions.js";
import { statusCommand } from "../../commands/status.js";
import { setVerbose } from "../../globals.js";
import { defaultRuntime } from "../../runtime.js";
import { formatDocsLink } from "../../terminal/links.js";
import { theme } from "../../terminal/theme.js";
import { runCommandWithRuntime } from "../cli-utils.js";
import { formatHelpExamples } from "../help-format.js";
import { parsePositiveIntOrUndefined } from "./helpers.js";
function resolveVerbose(opts: { verbose?: boolean; debug?: boolean }): boolean {
return Boolean(opts.verbose || opts.debug);
}
function parseTimeoutMs(timeout: unknown): number | null | undefined {
const parsed = parsePositiveIntOrUndefined(timeout);
if (timeout !== undefined && parsed === undefined) {
defaultRuntime.error("--timeout must be a positive integer (milliseconds)");
defaultRuntime.exit(1);
return null;
}
return parsed;
}
async function runWithVerboseAndTimeout(
opts: { verbose?: boolean; debug?: boolean; timeout?: unknown },
action: (params: { verbose: boolean; timeoutMs: number | undefined }) => Promise<void>,
): Promise<void> {
const verbose = resolveVerbose(opts);
setVerbose(verbose);
const timeoutMs = parseTimeoutMs(opts.timeout);
if (timeoutMs === null) {
return;
}
await runCommandWithRuntime(defaultRuntime, async () => {
await action({ verbose, timeoutMs });
});
}
export function registerStatusHealthSessionsCommands(program: Command) {
program
.command("status")
.description("Show channel health and recent session recipients")
.option("--json", "Output JSON instead of text", false)
.option("--all", "Full diagnosis (read-only, pasteable)", false)
.option("--usage", "Show model provider usage/quota snapshots", false)
.option("--deep", "Probe channels (WhatsApp Web + Telegram + Discord + Slack + Signal)", false)
.option("--timeout <ms>", "Probe timeout in milliseconds", "10000")
.option("--verbose", "Verbose logging", false)
.option("--debug", "Alias for --verbose", false)
.addHelpText(
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["openclaw status", "Show channel health + session summary."],
["openclaw status --all", "Full diagnosis (read-only)."],
["openclaw status --json", "Machine-readable output."],
["openclaw status --usage", "Show model provider usage/quota snapshots."],
[
"openclaw status --deep",
"Run channel probes (WA + Telegram + Discord + Slack + Signal).",
],
["openclaw status --deep --timeout 5000", "Tighten probe timeout."],
])}`,
)
.addHelpText(
"after",
() =>
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/status", "docs.openclaw.ai/cli/status")}\n`,
)
.action(async (opts) => {
await runWithVerboseAndTimeout(opts, async ({ verbose, timeoutMs }) => {
await statusCommand(
{
json: Boolean(opts.json),
all: Boolean(opts.all),
deep: Boolean(opts.deep),
usage: Boolean(opts.usage),
timeoutMs,
verbose,
},
defaultRuntime,
);
});
});
program
.command("health")
.description("Fetch health from the running gateway")
.option("--json", "Output JSON instead of text", false)
.option("--timeout <ms>", "Connection timeout in milliseconds", "10000")
.option("--verbose", "Verbose logging", false)
.option("--debug", "Alias for --verbose", false)
.addHelpText(
"after",
() =>
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/health", "docs.openclaw.ai/cli/health")}\n`,
)
.action(async (opts) => {
await runWithVerboseAndTimeout(opts, async ({ verbose, timeoutMs }) => {
await healthCommand(
{
json: Boolean(opts.json),
timeoutMs,
verbose,
},
defaultRuntime,
);
});
});
const sessionsCmd = program
.command("sessions")
.description("List stored conversation sessions")
.option("--json", "Output as JSON", false)
.option("--verbose", "Verbose logging", false)
.option("--store <path>", "Path to session store (default: resolved from config)")
.option("--active <minutes>", "Only show sessions updated within the past N minutes")
.addHelpText(
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["openclaw sessions", "List all sessions."],
["openclaw sessions --active 120", "Only last 2 hours."],
["openclaw sessions --json", "Machine-readable output."],
["openclaw sessions --store ./tmp/sessions.json", "Use a specific session store."],
])}\n\n${theme.muted(
"Shows token usage per session when the agent reports it; set agents.defaults.contextTokens to cap the window and show %.",
)}`,
)
.addHelpText(
"after",
() =>
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/sessions", "docs.openclaw.ai/cli/sessions")}\n`,
)
.action(async (opts) => {
setVerbose(Boolean(opts.verbose));
await sessionsCommand(
{
json: Boolean(opts.json),
store: opts.store as string | undefined,
active: opts.active as string | undefined,
},
defaultRuntime,
);
});
sessionsCmd.enablePositionalOptions();
sessionsCmd
.command("cleanup")
.description("Run session-store maintenance now")
.option("--store <path>", "Path to session store (default: resolved from config)")
.option("--dry-run", "Preview maintenance actions without writing", false)
.option("--enforce", "Apply maintenance even when configured mode is warn", false)
.option("--active-key <key>", "Protect this session key from budget-eviction")
.option("--json", "Output JSON", false)
.addHelpText(
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["openclaw sessions cleanup --dry-run", "Preview stale/cap cleanup."],
["openclaw sessions cleanup --enforce", "Apply maintenance now."],
[
"openclaw sessions cleanup --enforce --store ./tmp/sessions.json",
"Use a specific store.",
],
])}`,
)
.action(async (opts, command) => {
const parentOpts = command.parent?.opts() as
| {
store?: string;
json?: boolean;
}
| undefined;
await runCommandWithRuntime(defaultRuntime, async () => {
await sessionsCleanupCommand(
{
store: (opts.store as string | undefined) ?? parentOpts?.store,
dryRun: Boolean(opts.dryRun),
enforce: Boolean(opts.enforce),
activeKey: opts.activeKey as string | undefined,
json: Boolean(opts.json || parentOpts?.json),
},
defaultRuntime,
);
});
});
}