feat(cron): add computed status field to --json output (#78701) · openclaw/openclaw@8974a78
aweiker
·
2026-05-07
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
11 | 11 | - Telegram: treat successful same-chat `message` tool outbound sends during an inbound telegram turn as delivered when deciding whether to emit the rewritten silent reply fallback (#78685). Thanks @neeravmakwana. |
12 | 12 | - Gateway/tasks: reconcile stale CLI run-context tasks whose live run context disappeared even when a child session row remains, and apply the default bounded reload deferral timeout to channel hot reloads so stale task records cannot block Discord/Slack/Telegram reloads forever. |
13 | 13 | - Discord/voice: make `openclaw channels capabilities --channel discord --target channel:<id>` and `channels status --probe` audit voice-channel permissions, including auto-join targets, so missing Connect/Speak/Read Message History permissions show up before `/vc join`. |
| 14 | +- CLI/cron: add computed `status` field to `cron list --json` and `cron show <id> --json` output, mirroring the human-readable status column (disabled/running/ok/error/skipped/idle) so external tooling can determine job state without re-deriving it from raw state fields. (#78701) Thanks @aweiker. |
14 | 15 | - Docs/iMessage: deprecate BlueBubbles for new OpenClaw setups, document the upstream server-release rationale, and point new iMessage deployments toward the native `imsg` path while keeping BlueBubbles as a supported legacy fallback. |
15 | 16 | - Discord/voice: make voice capture less choppy by extending the default post-speech silence grace to 2.5s, add `voice.captureSilenceGraceMs` for noisy Discord sessions, and tighten the spoken-output prompt around live STT fragments. Thanks @vincentkoc. |
16 | 17 | - Discord/streaming: default Discord replies to progress draft previews so tool/work activity appears in one edited Discord message unless `channels.discord.streaming.mode` is set to `off`. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -14,6 +14,7 @@ import { resolveCronCreateSchedule } from "./schedule-options.js";
|
14 | 14 | import { |
15 | 15 | getCronChannelOptions, |
16 | 16 | coerceCronDeliveryPreviews, |
| 17 | +enrichCronJsonWithStatus, |
17 | 18 | handleCronCliError, |
18 | 19 | parseCronToolsAllow, |
19 | 20 | printCronJson, |
@@ -58,7 +59,7 @@ export function registerCronListCommand(cron: Command) {
|
58 | 59 | } |
59 | 60 | const res = await callGatewayFromCli("cron.list", opts, listParams); |
60 | 61 | if (opts.json) { |
61 | | -printCronJson(res); |
| 62 | +printCronJson(enrichCronJsonWithStatus(res)); |
62 | 63 | return; |
63 | 64 | } |
64 | 65 | const jobs = (res as { jobs?: CronJob[] } | null)?.jobs ?? []; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -6,6 +6,7 @@ import type { GatewayRpcOpts } from "../gateway-rpc.js";
|
6 | 6 | import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js"; |
7 | 7 | import { |
8 | 8 | coerceCronDeliveryPreviews, |
| 9 | +enrichCronJsonWithStatus, |
9 | 10 | handleCronCliError, |
10 | 11 | printCronJson, |
11 | 12 | printCronShow, |
@@ -122,7 +123,7 @@ export function registerCronSimpleCommands(cron: Command) {
|
122 | 123 | throw new Error(`cron job not found: ${String(id)}`); |
123 | 124 | } |
124 | 125 | if (opts.json) { |
125 | | -printCronJson(job); |
| 126 | +printCronJson(enrichCronJsonWithStatus(job)); |
126 | 127 | return; |
127 | 128 | } |
128 | 129 | printCronShow(job, defaultRuntime, { deliveryPreview }); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -29,6 +29,45 @@ export function printCronJson(value: unknown) {
|
29 | 29 | defaultRuntime.writeJson(value); |
30 | 30 | } |
31 | 31 | |
| 32 | +/** |
| 33 | + * Enrich a CronJob (or list response) with a computed `status` field |
| 34 | + * derived from enabled + state.runningAtMs + state.lastRunStatus. |
| 35 | + * This mirrors the human-readable status shown by `cron list` / `cron show`. |
| 36 | + */ |
| 37 | +export function enrichCronJsonWithStatus(value: unknown): unknown { |
| 38 | +if (!value || typeof value !== "object") { |
| 39 | +return value; |
| 40 | +} |
| 41 | +const obj = value as Record<string, unknown>; |
| 42 | + |
| 43 | +// Single job object (has 'state' and 'enabled') |
| 44 | +if ("state" in obj && "enabled" in obj) { |
| 45 | +return { ...obj, status: computeStatus(obj as unknown as CronJob) }; |
| 46 | +} |
| 47 | + |
| 48 | +// List response (has 'jobs' array) |
| 49 | +if ("jobs" in obj && Array.isArray(obj.jobs)) { |
| 50 | +const enrichedJobs = (obj.jobs as CronJob[]).map((job) => { |
| 51 | +const status = computeStatus(job); |
| 52 | +return Object.assign({}, job, { status }); |
| 53 | +}); |
| 54 | +return { ...obj, jobs: enrichedJobs }; |
| 55 | +} |
| 56 | + |
| 57 | +return value; |
| 58 | +} |
| 59 | + |
| 60 | +function computeStatus(job: CronJob): string { |
| 61 | +if (!job.enabled) { |
| 62 | +return "disabled"; |
| 63 | +} |
| 64 | +const state = job.state ?? {}; |
| 65 | +if (state.runningAtMs) { |
| 66 | +return "running"; |
| 67 | +} |
| 68 | +return state.lastRunStatus ?? state.lastStatus ?? "idle"; |
| 69 | +} |
| 70 | + |
32 | 71 | export function handleCronCliError(err: unknown) { |
33 | 72 | defaultRuntime.error(danger(String(err))); |
34 | 73 | defaultRuntime.exit(1); |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。