Memory/QMD: robustly parse noisy qmd JSON output

This commit is contained in:
Vignesh Natarajan
2026-02-14 14:56:48 -08:00
parent 6bf333bf31
commit 19df928e7f
3 changed files with 102 additions and 3 deletions

View File

@@ -25,11 +25,19 @@ export function parseQmdQueryJson(stdout: string, stderr: string): QmdQueryResul
throw new Error(`qmd query returned invalid JSON: ${message}`);
}
try {
const parsed = JSON.parse(trimmedStdout) as unknown;
if (!Array.isArray(parsed)) {
const parsed = parseQmdQueryResultArray(trimmedStdout);
if (parsed !== null) {
return parsed;
}
const noisyPayload = extractFirstJsonArray(trimmedStdout);
if (!noisyPayload) {
throw new Error("qmd query JSON response was not an array");
}
return parsed as QmdQueryResult[];
const fallback = parseQmdQueryResultArray(noisyPayload);
if (fallback !== null) {
return fallback;
}
throw new Error("qmd query JSON response was not an array");
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
log.warn(`qmd query returned invalid JSON: ${message}`);
@@ -45,3 +53,56 @@ function isQmdNoResultsOutput(raw: string): boolean {
function summarizeQmdStderr(raw: string): string {
return raw.length <= 120 ? raw : `${raw.slice(0, 117)}...`;
}
function parseQmdQueryResultArray(raw: string): QmdQueryResult[] | null {
try {
const parsed = JSON.parse(raw) as unknown;
if (!Array.isArray(parsed)) {
return null;
}
return parsed as QmdQueryResult[];
} catch {
return null;
}
}
function extractFirstJsonArray(raw: string): string | null {
const start = raw.indexOf("[");
if (start < 0) {
return null;
}
let depth = 0;
let inString = false;
let escaped = false;
for (let i = start; i < raw.length; i += 1) {
const char = raw[i];
if (char === undefined) {
break;
}
if (inString) {
if (escaped) {
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
} else if (char === '"') {
inString = false;
}
continue;
}
if (char === '"') {
inString = true;
continue;
}
if (char === "[") {
depth += 1;
} else if (char === "]") {
depth -= 1;
if (depth === 0) {
return raw.slice(start, i + 1);
}
}
}
return null;
}