import { untilAborted } from "@oh-my-pi/pi-utils"; import type { Markit, StreamInfo } from "markit-ai"; import { ToolAbortError } from "../tools/tool-errors"; export interface MarkitConversionResult { content: string; ok: boolean; error?: string; } let markit: () => Markit | Promise = async () => { const promise = import("markit-ai").then(({ Markit }) => { const instance = new Markit(); markit = () => instance; return instance; }); markit = () => promise; return promise; }; function normalizeExtension(extension: string): string { const trimmed = extension.trim().toLowerCase(); if (!trimmed) return ".bin"; return trimmed.startsWith(".") ? trimmed : `.${trimmed}`; } function normalizeError(error: unknown): string { if (error instanceof Error && error.message.trim().length > 0) { return error.message.trim(); } return "Conversion failed"; } async function runMarkitConversion(task: (markit: Markit) => Promise, signal?: AbortSignal): Promise { try { const instance = await markit(); return signal ? await untilAborted(signal, () => task(instance)) : await task(instance); } catch (error) { if (error instanceof ToolAbortError) { throw error; } if (error instanceof Error && error.name === "AbortError") { throw new ToolAbortError(); } throw error; } } function finalizeConversion(markdown?: string): MarkitConversionResult { if (typeof markdown === "string" && markdown.length > 0) { return { content: markdown, ok: true }; } return { content: "", ok: false, error: "Conversion produced no output" }; } export async function convertFileWithMarkit(filePath: string, signal?: AbortSignal): Promise { try { const result = await runMarkitConversion(markit => markit.convertFile(filePath), signal); return finalizeConversion(result.markdown); } catch (error) { if (error instanceof ToolAbortError) { throw error; } return { content: "", ok: false, error: normalizeError(error) }; } } export async function convertBufferWithMarkit( buffer: Uint8Array, extension: string, signal?: AbortSignal, ): Promise { const normalizedExtension = normalizeExtension(extension); const streamInfo: StreamInfo = { extension: normalizedExtension, filename: `input${normalizedExtension}`, }; try { const result = await runMarkitConversion(markit => markit.convert(Buffer.from(buffer), streamInfo), signal); return finalizeConversion(result.markdown); } catch (error) { if (error instanceof ToolAbortError) { throw error; } return { content: "", ok: false, error: normalizeError(error) }; } }