Expose messageId in message CLI JSON output (#84191) · openclaw/openclaw@1bb0eba
100menotu001
·
2026-05-20
·
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 | |
12 | 12 | ### Fixes |
13 | 13 | |
| 14 | +- CLI/message: include a stable top-level `messageId` in `openclaw message --json` output when channel sends return one. (#84191) Thanks @100menotu001. |
14 | 15 | - Plugins/hooks: apply a default 30-second timeout to `before_compaction` and `after_compaction` hooks so a hung plugin handler no longer blocks compaction completion. (#84153) |
15 | 16 | - Discord: preserve disabled presentation buttons when adapting and rendering Discord message controls. (#84188) Thanks @100menotu001. |
16 | 17 | - Plugins/perf: thread explicit plugin discovery results through `loadBundledCapabilityRuntimeRegistry`, `resolveBundledPluginSources`, and `listChannelCatalogEntries` so callers that already hold a discovery result skip redundant filesystem walks. Thanks @SebTardif. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -300,6 +300,40 @@ describe("messageCommand", () => {
|
300 | 300 | expect(actionCall.params.pollQuestion).toBe("Ship it?"); |
301 | 301 | }); |
302 | 302 | |
| 303 | +it("includes a stable top-level messageId in JSON output", async () => { |
| 304 | +runMessageActionMock.mockResolvedValueOnce({ |
| 305 | +kind: "send", |
| 306 | +channel: "discord", |
| 307 | +action: "send", |
| 308 | +to: "channel:general", |
| 309 | +handledBy: "plugin", |
| 310 | +payload: { |
| 311 | +ok: true, |
| 312 | +result: { |
| 313 | +messageId: "msg-json-1", |
| 314 | +channelId: "general", |
| 315 | +}, |
| 316 | +} as { ok: boolean } & Record<string, unknown>, |
| 317 | +dryRun: false, |
| 318 | +}); |
| 319 | + |
| 320 | +await runMessageCommand({ |
| 321 | +channel: "discord", |
| 322 | +target: "channel:general", |
| 323 | +}); |
| 324 | + |
| 325 | +const output = vi.mocked(runtime.log).mock.calls[0]?.[0]; |
| 326 | +const json = JSON.parse(String(output)) as { messageId?: string; payload?: unknown }; |
| 327 | +expect(json.messageId).toBe("msg-json-1"); |
| 328 | +expect(json.payload).toEqual({ |
| 329 | +ok: true, |
| 330 | +result: { |
| 331 | +messageId: "msg-json-1", |
| 332 | +channelId: "general", |
| 333 | +}, |
| 334 | +}); |
| 335 | +}); |
| 336 | + |
303 | 337 | it("rejects unknown message actions before dispatch", async () => { |
304 | 338 | await expect(runMessageCommand({ action: "nope" })).rejects.toThrow("Unknown message action"); |
305 | 339 | expect(runMessageActionMock).not.toHaveBeenCalled(); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -17,12 +17,33 @@ import {
|
17 | 17 | normalizeOptionalString, |
18 | 18 | } from "../shared/string-coerce.js"; |
19 | 19 | |
| 20 | +function extractMessageId(payload: unknown): string | undefined { |
| 21 | +if (!payload || typeof payload !== "object") { |
| 22 | +return undefined; |
| 23 | +} |
| 24 | +const record = payload as Record<string, unknown>; |
| 25 | +const direct = normalizeOptionalString(record.messageId); |
| 26 | +if (direct) { |
| 27 | +return direct; |
| 28 | +} |
| 29 | +const result = record.result; |
| 30 | +if (result && typeof result === "object") { |
| 31 | +const nested = normalizeOptionalString((result as Record<string, unknown>).messageId); |
| 32 | +if (nested) { |
| 33 | +return nested; |
| 34 | +} |
| 35 | +} |
| 36 | +return undefined; |
| 37 | +} |
| 38 | + |
20 | 39 | function buildMessageCliJson(result: Awaited<ReturnType<typeof runMessageAction>>) { |
| 40 | +const messageId = extractMessageId(result.payload); |
21 | 41 | return { |
22 | 42 | action: result.action, |
23 | 43 | channel: result.channel, |
24 | 44 | dryRun: result.dryRun, |
25 | 45 | handledBy: result.handledBy, |
| 46 | + ...(messageId ? { messageId } : {}), |
26 | 47 | payload: result.payload, |
27 | 48 | }; |
28 | 49 | } |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。