78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
// Helpers specific to Claude CLI output/argv handling.
|
|
|
|
// Preferred binary name for Claude CLI invocations.
|
|
export const CLAUDE_BIN = "claude";
|
|
|
|
function extractClaudeText(payload: unknown): string | undefined {
|
|
// Best-effort walker to find the primary text field in Claude JSON outputs.
|
|
if (payload == null) return undefined;
|
|
if (typeof payload === "string") return payload;
|
|
if (Array.isArray(payload)) {
|
|
for (const item of payload) {
|
|
const found = extractClaudeText(item);
|
|
if (found) return found;
|
|
}
|
|
return undefined;
|
|
}
|
|
if (typeof payload === "object") {
|
|
const obj = payload as Record<string, unknown>;
|
|
if (typeof obj.result === "string") return obj.result;
|
|
if (typeof obj.text === "string") return obj.text;
|
|
if (typeof obj.completion === "string") return obj.completion;
|
|
if (typeof obj.output === "string") return obj.output;
|
|
if (obj.message) {
|
|
const inner = extractClaudeText(obj.message);
|
|
if (inner) return inner;
|
|
}
|
|
if (Array.isArray(obj.messages)) {
|
|
const inner = extractClaudeText(obj.messages);
|
|
if (inner) return inner;
|
|
}
|
|
if (Array.isArray(obj.content)) {
|
|
for (const block of obj.content) {
|
|
if (
|
|
block &&
|
|
typeof block === "object" &&
|
|
(block as { type?: string }).type === "text" &&
|
|
typeof (block as { text?: unknown }).text === "string"
|
|
) {
|
|
return (block as { text: string }).text;
|
|
}
|
|
const inner = extractClaudeText(block);
|
|
if (inner) return inner;
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export type ClaudeJsonParseResult = {
|
|
text?: string;
|
|
parsed: unknown;
|
|
};
|
|
|
|
export function parseClaudeJson(raw: string): ClaudeJsonParseResult | undefined {
|
|
// Handle a single JSON blob or newline-delimited JSON; return the first parsed payload.
|
|
let firstParsed: unknown;
|
|
const candidates = [raw, ...raw.split(/\n+/).map((s) => s.trim()).filter(Boolean)];
|
|
for (const candidate of candidates) {
|
|
try {
|
|
const parsed = JSON.parse(candidate);
|
|
if (firstParsed === undefined) firstParsed = parsed;
|
|
const text = extractClaudeText(parsed);
|
|
if (text) return { parsed, text };
|
|
} catch {
|
|
// ignore parse errors; try next candidate
|
|
}
|
|
}
|
|
if (firstParsed !== undefined) {
|
|
return { parsed: firstParsed, text: extractClaudeText(firstParsed) };
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function parseClaudeJsonText(raw: string): string | undefined {
|
|
const parsed = parseClaudeJson(raw);
|
|
return parsed?.text;
|
|
}
|