UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

271 lines (270 loc) • 17.5 kB
import { w as parseStrictPositiveInteger } from "./number-coercion-Dq1l5muL.mjs"; import { r as defaultRuntime } from "./runtime-BC29JSZp.mjs"; import { t as ExpectedCliError } from "./failure-output-Bu6CUgrO.mjs"; import { r as setVerbose } from "./global-state-BAD7XgmL.mjs"; import { r as theme } from "./theme-DzaUZY4q.mjs"; import "./globals-D2Pr1KIl.mjs"; import { n as runCommandWithRuntime } from "./cli-utils-DtS5_tuC.mjs"; import { n as formatHelpExamples, t as formatDocsHelp } from "./help-format-MN-qfst1.mjs"; //#region src/cli/program/register.status-health-sessions.ts const SESSIONS_PARENT_OPTION_FLAGS = { json: "--json", verbose: "--verbose", store: "--store", agent: "--agent", allAgents: "--all-agents", active: "--active", limit: "--limit" }; function throwSessionsCliError(message) { throw new ExpectedCliError({ message, humanOutput: message, machineOutput: message }); } function rejectUnsupportedSessionsParentOptions(subcommand, parentOpts, unsupportedOptions, reason) { const unsupportedFlags = unsupportedOptions.filter((option) => { const value = parentOpts?.[option]; return typeof value === "boolean" ? value : value !== void 0; }).map((option) => SESSIONS_PARENT_OPTION_FLAGS[option]); if (unsupportedFlags.length === 0) return; throwSessionsCliError(`\`sessions ${subcommand}\` does not support the parent \`sessions\` ${unsupportedFlags.length > 1 ? "options" : "option"} ${unsupportedFlags.join(", ")}; ${reason}.`); } function addSessionsListOptions(command) { return command.option("--json", "Output as JSON", false).option("--verbose", "Verbose logging", false).option("--store <path>", "Legacy session store selector path").option("--agent <id>", "Agent id to inspect (required for multiple explicit agents)").option("--all-agents", "Aggregate sessions across all configured agents", false).option("--active <minutes>", "Only show sessions updated within the past N minutes").option("--limit <count>", "Max sessions to show (default: 100; use \"all\" for full output)"); } function addSessionsGatewayOptions(command) { return command.option("--agent <id>", "Agent id that owns the session (required for global keys)").option("--url <url>", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)").option("--token <token>", "Gateway token (if required)").option("--password <password>", "Gateway password (password auth)").option("--timeout <ms>", "RPC timeout in milliseconds").option("--json", "Output JSON", false); } function mergeSessionsListOptions(opts, parentOpts) { return { json: Boolean(opts.json || parentOpts?.json), verbose: Boolean(opts.verbose || parentOpts?.verbose), store: opts.store ?? parentOpts?.store, agent: opts.agent ?? parentOpts?.agent, allAgents: Boolean(opts.allAgents || parentOpts?.allAgents), active: opts.active ?? parentOpts?.active, limit: opts.limit ?? parentOpts?.limit }; } async function runSessionsListCli(opts) { setVerbose(Boolean(opts.verbose)); const { sessionsCommand } = await import("./sessions-Bvohwa-g.mjs"); await sessionsCommand({ json: Boolean(opts.json), store: opts.store, agent: opts.agent, allAgents: Boolean(opts.allAgents), active: opts.active, limit: opts.limit }, defaultRuntime); } function registerSessionsLifecycleCommand(sessionsCmd, operation) { const destructive = operation === "delete"; const examples = destructive ? [ ["openclaw sessions delete \"agent:main:scratch-1\"", "Delete with confirmation."], ["openclaw sessions delete \"agent:main:scratch-1\" \"agent:main:scratch-2\" --yes", "Delete several sessions non-interactively."], ["openclaw sessions delete \"agent:work:scratch-1\" --agent work --dry-run", "Preview an agent-scoped delete."] ] : [ ["openclaw sessions archive \"agent:main:scratch-1\"", "Archive one session."], ["openclaw sessions archive \"agent:main:scratch-1\" \"agent:main:scratch-2\"", "Archive several sessions."], ["openclaw sessions archive \"agent:work:scratch-1\" --agent work --dry-run", "Preview an agent-scoped archive."] ]; const command = sessionsCmd.command(`${operation} <keys...>`).description(destructive ? "Delete stored sessions and their live artifacts via the running gateway. Retained archives can remain searchable." : "Archive stored sessions via the running gateway").option(`--dry-run`, `Preview ${operation} actions without writing`, false); if (destructive) command.option("--yes", "Skip the destructive confirmation prompt", false); addSessionsGatewayOptions(command).addHelpText("after", () => `\n${theme.heading("Examples:")}\n${formatHelpExamples(examples)}${destructive ? `\n\n${theme.muted("Deletion uses the Control UI lifecycle operation, including transcript archival and runtime cleanup. Retained deleted-session archives can remain eligible for memory search. To remove indexed memories, run `openclaw memory forget --agent <agent-id> --session <id-or-key>` on the Gateway host or container using its state and configuration. Use the agent that owned the deleted session, including for global keys. Memory cleanup runs locally; --url does not forward it to a remote Gateway.")}` : ""}`).action(async (keys, opts, actionCommand) => { const parentOpts = actionCommand.parent?.opts(); rejectUnsupportedSessionsParentOptions(operation, parentOpts, [ "store", "allAgents", "active", "limit", "verbose" ], "the gateway resolves target stores from each key and --agent"); const timeoutMs = parseStrictPositiveInteger(opts.timeout); if (opts.timeout !== void 0 && timeoutMs === void 0) throwSessionsCliError("--timeout must be a positive integer (milliseconds)."); await runCommandWithRuntime(defaultRuntime, async () => { const lifecycleCommands = await import("./sessions-lifecycle-CbO3lza0.mjs"); await (destructive ? lifecycleCommands.sessionsDeleteCommand : lifecycleCommands.sessionsArchiveCommand)({ keys, agent: opts.agent ?? parentOpts?.agent, dryRun: Boolean(opts.dryRun), ...destructive ? { yes: Boolean(opts.yes) } : {}, timeout: timeoutMs !== void 0 ? String(timeoutMs) : void 0, url: opts.url, token: opts.token, password: opts.password, json: Boolean(opts.json || parentOpts?.json) }, defaultRuntime); }); }); } async function runWithVerboseAndTimeout(opts, action) { const verbose = Boolean(opts.verbose || opts.debug); setVerbose(verbose); await runCommandWithRuntime(defaultRuntime, async () => { const timeoutMs = parseStrictPositiveInteger(opts.timeout); if (opts.timeout !== void 0 && timeoutMs === void 0) throw new Error("--timeout must be a positive integer (milliseconds)"); await action({ verbose, timeoutMs }); }); } /** Register status/health plus persistent session inspection command groups. */ function registerStatusHealthSessionsCommands(program) { 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("--agent <id>", "Agent id for --usage auth scope").option("--deep", "Probe channels (WhatsApp Web + Telegram + Discord + Slack + Signal)", false).option("--timeout <ms>", "Probe timeout in milliseconds").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", () => formatDocsHelp("/cli/status")).action(async (opts) => { await runWithVerboseAndTimeout(opts, async ({ verbose, timeoutMs }) => { const { statusCommand } = await import("./status-CKVnpGIL.mjs"); await statusCommand({ json: Boolean(opts.json), all: Boolean(opts.all), deep: Boolean(opts.deep), usage: Boolean(opts.usage), ...opts.agent !== void 0 ? { agent: opts.agent } : {}, 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").option("--verbose", "Verbose logging", false).option("--debug", "Alias for --verbose", false).addHelpText("after", () => formatDocsHelp("/cli/health")).action(async (opts) => { await runWithVerboseAndTimeout(opts, async ({ verbose, timeoutMs }) => { const { healthCommand } = await import("./health-Dtq_-I3u.mjs"); await healthCommand({ json: Boolean(opts.json), timeoutMs, verbose }, defaultRuntime); }); }); const sessionsCmd = addSessionsListOptions(program.command("sessions").description("List stored conversation sessions")).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 --limit 25", "Show the newest 25 sessions."], ["openclaw sessions --json", "Machine-readable output."], ["openclaw sessions --store ./tmp/sessions.sqlite", "Use a specific session store."] ])}\n\n${theme.muted("Shows token usage per session when the agent reports it; set the model entry's contextTokens to cap the window and show %.")}`).addHelpText("after", () => formatDocsHelp("/cli/sessions")).action(async (opts) => { await runSessionsListCli(opts); }); sessionsCmd.enablePositionalOptions(); addSessionsListOptions(sessionsCmd.command("list").description("List stored conversation sessions")).action(async (opts, command) => { const parentOpts = command.parent?.opts(); await runSessionsListCli(mergeSessionsListOptions(opts, parentOpts)); }); sessionsCmd.command("cleanup").description("Run session-store maintenance now").option("--store <path>", "Legacy session store selector path").option("--agent <id>", "Agent id to maintain (required for multiple explicit agents)").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("--fix-dm-scope", "Retire stale direct-DM session rows that no longer match session.dmScope=main", 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 --dry-run --fix-dm-scope", "Preview stale direct-DM rows after returning dmScope to main."], ["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.sqlite", "Use a specific store."] ])}`).action(async (opts, command) => { const parentOpts = command.parent?.opts(); rejectUnsupportedSessionsParentOptions("cleanup", parentOpts, [ "active", "limit", "verbose" ], "session-list filters cannot scope session maintenance"); await runCommandWithRuntime(defaultRuntime, async () => { const { sessionsCleanupCommand } = await import("./sessions-cleanup-CMazS7G9.mjs"); await sessionsCleanupCommand({ store: opts.store ?? parentOpts?.store, agent: opts.agent ?? parentOpts?.agent, allAgents: Boolean(opts.allAgents || parentOpts?.allAgents), dryRun: Boolean(opts.dryRun), enforce: Boolean(opts.enforce), fixMissing: Boolean(opts.fixMissing), fixDmScope: Boolean(opts.fixDmScope), activeKey: opts.activeKey, json: Boolean(opts.json || parentOpts?.json) }, defaultRuntime); }); }); sessionsCmd.command("tail").description("Tail human-readable session trajectory progress").option("--session-key <key>", "Session key to tail (default: active sessions or latest)").option("--tail <count>", "Number of existing trajectory events to show", "80").option("--follow", "Continue following for new trajectory events", false).option("--store <path>", "Legacy session store selector path").option("--agent <id>", "Agent id to inspect (required for multiple explicit agents)").option("--all-agents", "Aggregate sessions across all configured agents", false).action(async (opts, command) => { const parentOpts = command.parent?.opts(); rejectUnsupportedSessionsParentOptions("tail", parentOpts, [ "json", "active", "limit", "verbose" ], "trajectory tail emits human-readable progress and selects sessions separately"); await runCommandWithRuntime(defaultRuntime, async () => { const { sessionsTailCommand } = await import("./sessions-tail-Fcq80lYZ.mjs"); await sessionsTailCommand({ sessionKey: opts.sessionKey, store: opts.store ?? parentOpts?.store, agent: opts.agent ?? parentOpts?.agent, allAgents: Boolean(opts.allAgents || parentOpts?.allAgents), follow: Boolean(opts.follow), tail: opts.tail }, defaultRuntime); }); }); sessionsCmd.command("export-trajectory").description("Export a redacted trajectory bundle for a stored session").option("--session-key <key>", "Session key to export").option("--output <path>", "Output directory name inside .openclaw/trajectory-exports").option("--workspace <path>", "Workspace root for the export (default: current directory)").option("--store <path>", "Legacy session store selector path").option("--agent <id>", "Agent id for resolving the default session store").option("--request-json-base64 <payload>", "Base64url-encoded export request").option("--json", "Output JSON", false).action(async (opts, command) => { const parentOpts = command.parent?.opts(); rejectUnsupportedSessionsParentOptions("export-trajectory", parentOpts, [ "allAgents", "active", "limit", "verbose" ], "trajectory export targets one session and cannot apply session-list filters"); await runCommandWithRuntime(defaultRuntime, async () => { const { exportTrajectoryCommand } = await import("./export-trajectory-CLC4B5mT.mjs"); await exportTrajectoryCommand({ sessionKey: opts.sessionKey, output: opts.output, workspace: opts.workspace, store: opts.store ?? parentOpts?.store, agent: opts.agent ?? parentOpts?.agent, requestJsonBase64: opts.requestJsonBase64, json: Boolean(opts.json || parentOpts?.json) }, defaultRuntime); }); }); registerSessionsLifecycleCommand(sessionsCmd, "archive"); registerSessionsLifecycleCommand(sessionsCmd, "delete"); addSessionsGatewayOptions(sessionsCmd.command("compact <key>")).description("Compact a stored session transcript via the running gateway").option("--max-lines <count>", "Truncate to the last N transcript lines instead of LLM summarization").addHelpText("after", () => `\n${theme.heading("Examples:")}\n${formatHelpExamples([ ["openclaw sessions compact \"agent:main:main\"", "LLM-summarize a session to reclaim context budget."], ["openclaw sessions compact \"agent:main:main\" --max-lines 200", "Truncate to the last 200 transcript lines instead."], ["openclaw sessions compact \"agent:work:main\" --agent work --json", "Target one agent's session and emit JSON."] ])}\n\n${theme.muted("Backed by the sessions.compact gateway RPC; exits non-zero when compaction fails.")}`).action(async (key, opts, command) => { const parentOpts = command.parent?.opts(); rejectUnsupportedSessionsParentOptions("compact", parentOpts, [ "store", "allAgents", "active", "limit", "verbose" ], "the gateway resolves the target store from <key> and --agent"); const maxLines = parseStrictPositiveInteger(opts.maxLines); if (opts.maxLines !== void 0 && maxLines === void 0) throwSessionsCliError("--max-lines must be a positive integer."); const timeoutMs = parseStrictPositiveInteger(opts.timeout); if (opts.timeout !== void 0 && timeoutMs === void 0) throwSessionsCliError("--timeout must be a positive integer (milliseconds)."); await runCommandWithRuntime(defaultRuntime, async () => { const { sessionsCompactCommand } = await import("./sessions-compact-BguYG8Et.mjs"); await sessionsCompactCommand({ key, agent: opts.agent ?? parentOpts?.agent, maxLines, timeout: timeoutMs !== void 0 ? String(timeoutMs) : void 0, url: opts.url, token: opts.token, password: opts.password, json: Boolean(opts.json || parentOpts?.json) }, defaultRuntime); }); }); } //#endregion export { registerStatusHealthSessionsCommands };