| 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("--agent <id>", "Agent id to inspect (default: configured default agent)") |
| .option("--all-agents", "Aggregate sessions across all configured agents", false) |
| .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 --agent work", "List sessions for one agent."], |
| ["openclaw sessions --all-agents", "Aggregate sessions across agents."], |
| ["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, |
| agent: opts.agent as string | undefined, |
| allAgents: Boolean(opts.allAgents), |
| 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("--agent <id>", "Agent id to maintain (default: configured default agent)") |
| .option("--all-agents", "Run maintenance across all configured agents", false) |
| .option("--dry-run", "Preview maintenance actions without writing", false) |
| .option("--enforce", "Apply maintenance even when configured mode is warn", false) |
| .option( |
| "--fix-missing", |
| "Remove store entries whose transcript files are missing (bypasses age/count retention)", |
| 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 --dry-run --fix-missing", |
| "Also preview pruning entries with missing transcript files.", |
| ], |
| ["openclaw sessions cleanup --enforce", "Apply maintenance now."], |
| ["openclaw sessions cleanup --agent work --dry-run", "Preview one agent store."], |
| ["openclaw sessions cleanup --all-agents --dry-run", "Preview all agent stores."], |
| [ |
| "openclaw sessions cleanup --enforce --store ./tmp/sessions.json", |
| "Use a specific store.", |
| ], |
| ])}`, |
| ) |
| .action(async (opts, command) => { |
| const parentOpts = command.parent?.opts() as |
| | { |
| store?: string; |
| agent?: string; |
| allAgents?: boolean; |
| json?: boolean; |
| } |
| | undefined; |
| await runCommandWithRuntime(defaultRuntime, async () => { |
| await sessionsCleanupCommand( |
| { |
| store: (opts.store as string | undefined) ?? parentOpts?.store, |
| agent: (opts.agent as string | undefined) ?? parentOpts?.agent, |
| allAgents: Boolean(opts.allAgents || parentOpts?.allAgents), |
| dryRun: Boolean(opts.dryRun), |
| enforce: Boolean(opts.enforce), |
| fixMissing: Boolean(opts.fixMissing), |
| activeKey: opts.activeKey as string | undefined, |
| json: Boolean(opts.json || parentOpts?.json), |
| }, |
| defaultRuntime, |
| ); |
| }); |
| }); |
| } |
|
|