@blackbelt-technology/pi-dashboard-document-converter0.9.0
TypeScript facade over a Dockerized Python document engine (pi-doc-engine). Bidirectional: ingest any format to provenance-stamped Markdown for kb, produce templated DOCX/PDF with diagrams. Engine quarantined in Docker; TS is the only call surface.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@blackbelt-technology/pi-dashboard-document-converter
TypeScript facade over a Dockerized Python document engine (pi-doc-engine).
The only call surface for document conversion in the repo. Callers never
touch Python, docling, pandoc, or the nano-banana CLI.
- Ingest (feeds kb): PDF/DOCX/PPTX/XLSX → provenance-stamped Markdown.
- Produce: Markdown → templated DOCX/PDF, diagrams, round-trip edit/merge.
- Engine quarantined in Docker; TS is the contract + orchestrator.
Facade API
import { createDocumentConverter } from "@blackbelt-technology/pi-dashboard-document-converter";
const dc = createDocumentConverter({
image: "pi-doc-engine:0.2.0",
stagingDir: "/abs/kb-staging",
// Files outside process.cwd() must sit under a configured root:
mounts: ["/docs", "/out"], // or workspaceRoot: "/abs/project"
});
// Ingest -> Markdown with provenance frontmatter (written into stagingDir).
const { output, provenance } = await dc.convertToMarkdown("/docs/report.pdf", {
ocr: { mode: "auto", lang: ["hungarian", "english"], engine: "tesseract" },
});
// Produce -> templated DOCX (styled diagrams opt-in via nanoBanana).
await dc.renderDocx("/docs/spec.md", {
output: "/out/spec.docx",
template: "default",
nanoBanana: { enabled: true, style: "ros-3d" },
});
await dc.renderPdf("/out/spec.docx", { output: "/out/spec.pdf", pageSize: "a4" });
// Round-trip.
const { meta } = await dc.extractForEdit("/in.docx", "/edit.md");
await dc.mergeBack("/in.docx", "/edit.md", { output: "/out.docx", meta });
// Frontmatter tooling over the unified schema.
await dc.fillFrontmatter(["spec/**/*.md"], { language: "hu" });
await dc.profileTables(["spec/**/*.md"]);
Every method returns a typed result or rejects with DocConverterError
(.code, .stderr, .exitCode). OCR canonical language names are mapped to
per-engine codes (mapOcrCodes); an unsupported name/engine raises
OCR_LANG_UNSUPPORTED before the engine runs.
Unified frontmatter schema
./schema (also src/schema.json) is the single contract every stage reads/
writes: template vars (frontmatter-filler) · table_profiles (table profiler) ·
nano_banana (styled diagrams) · provenance (ingest, read by kb).
kb consumption seam (kb is NOT modified)
Ingest writes provenance-stamped .md into stagingDir. kb consumes it via its
existing filesystem source — no kb code change:
# Point a kb filesystem source at the staging dir and index it.
kb index --source /abs/kb-staging
kb search "deferred tax liabilities" --source /abs/kb-staging
The provenance frontmatter (source_path, sha256, doc_type,
converted_at, optional page/slide) lets kb chunks trace back to the
originating file. Re-ingesting an unchanged file yields the same sha256, so
staging output is byte-stable (idempotent by hash).
Path confinement
Every absolute path in a request must lie under a configured root:
stagingDir (mounted read-write), mounts, or workspaceRoot (default
process.cwd()). Comparison uses real paths, so a symlink cannot escape a root.
Sensitive dirs (/etc, /root, /var/run, /proc, /sys, /dev, ~/.ssh,
~/.aws, ~/.gnupg, ~/.config, ~/.pi, ~/.docker, ~/.kube) are refused
unless a root containing the path is itself inside that dir. A root of / is
refused. Violations reject with DocConverterError code PATH_NOT_ALLOWED
before docker runs. Inputs of convertToMarkdown, renderPdf, and
fillFrontmatter/profileTables with apply:false mount read-only.
Templates (runtime-mounted, not baked)
DOCX templates are NOT baked into the image — pass templatesDir (an absolute
host dir containing <template>/template.docx, under a configured root — add it
to mounts); the facade bind-mounts it. Keeps
branded/template assets out of the image and the repo.
Engine image
Built from engine/ (vendored Python; see engine/VENDOR.md):
npm run build:image # -> pi-doc-engine:$(cat engine/IMAGE_VERSION)
Heavy (docling ML models + LibreOffice + pandoc + arch-selected headless browser: chrome-headless-shell on amd64, chromium on arm64). The
image runs as root, so an mmdc shim injects --no-sandbox; opencv needs
libgl1; @puppeteer/browsers needs unzip (all handled in the Dockerfile).
See engine/README.md for the JSON docker run contract.
Breaking (next release)
DocConverterErrorCode gains MMDC_UNAVAILABLE (exhaustive switches must add a case).
It is raised when a mermaid render was attempted and the engine's headless browser cannot
launch. The earlier no-hard-fail guarantee for styled rendering is withdrawn: nano-banana
failure falls back to mmdc, and a broken browser now fails the produce call with MMDC_UNAVAILABLE.
Rebuild the image (pi-doc-engine:0.2.0) to pick up the arch-selected browser.
Integration test (opt-in)
Build the image (pi-doc-engine:0.2.0), then:
DOC_ENGINE_IMAGE=pi-doc-engine:0.2.0 \
DOC_ENGINE_PDF=/abs/sample.pdf \
DOC_ENGINE_TEMPLATES=/abs/templates \
npx vitest run integration
Tests
npm test # TS facade unit tests (vitest)
python -m pytest engine/tests -q # engine styled-diagram/cache tests