diff --git a/CHANGELOG.md b/CHANGELOG.md index cb3bdd616..bb27b50d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Docs: https://docs.clawd.bot - Model picker: list the full catalog when no model allowlist is configured. - Discord: honor wildcard channel configs via shared match helpers. (#1334) Thanks @pvoo. - BlueBubbles: resolve short message IDs safely and expose full IDs in templates. (#1387) Thanks @tyler6204. +- Zalouser: parse zca JSON output when logs or ANSI codes are present. (#1379) Thanks @ptn1411. - Infra: preserve fetch helper methods when wrapping abort signals. (#1387) ## 2026.1.20 diff --git a/extensions/zalouser/src/zca.test.ts b/extensions/zalouser/src/zca.test.ts new file mode 100644 index 000000000..c6d0ff413 --- /dev/null +++ b/extensions/zalouser/src/zca.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; + +import { parseJsonOutput } from "./zca.js"; + +describe("parseJsonOutput", () => { + it("parses plain JSON output", () => { + expect(parseJsonOutput<{ ok: boolean }>('{"ok":true}')).toEqual({ ok: true }); + }); + + it("parses JSON wrapped in ANSI codes", () => { + const output = "\u001B[32m{\"ok\":true}\u001B[0m"; + expect(parseJsonOutput<{ ok: boolean }>(output)).toEqual({ ok: true }); + }); + + it("parses JSON after log prefix lines", () => { + const output = ["INFO starting up", "{\"items\":[1,2]}"].join("\n"); + expect(parseJsonOutput<{ items: number[] }>(output)).toEqual({ items: [1, 2] }); + }); + + it("skips invalid JSON blocks and returns the next payload", () => { + const output = ["INFO", "{bad}", "{\"ok\":true}"].join("\n"); + expect(parseJsonOutput<{ ok: boolean }>(output)).toEqual({ ok: true }); + }); + + it("returns null when no JSON payload is found", () => { + expect(parseJsonOutput("INFO no payload")).toBeNull(); + }); +});