zzy-12345678/dsh-file-convert

dsh-file-convert

Local-first file conversion for DeepSeek Harness — images, PDF, data, audio/video & office docs. 7 tools, 26 conversions, no API keys, no uploads.

未分類deepseek-harnessffmpegfile-conversion

dsh-file-convert

Local-first file conversion for DeepSeek Harness.

Convert images, PDFs and data files directly inside your DSH agent sessions — no API keys, no uploads, no servers, no token cost for the conversion itself. Files never leave your machine.

Unofficial community plugin. Not affiliated with or endorsed by DeepSeek.
Compatibility: v0.5.0+ targets DeepSeek Harness Desktop 0.2.1-rc.3 and its canonical-output tool API. Node.js ^22.19.0 || >=24.0.0 is required.

Why

Agents constantly need file conversions: "turn this PDF into images", "give me that JSON as YAML", "convert all JPGs in this folder to WebP". Instead of shelling out or pasting data around, dsh-file-convert gives your agent eight purpose-built tools backed by battle-tested local libraries.

  • ✅ Local execution — files never leave the machine
  • ✅ No API key, no server, no conversion tokens
  • ✅ Natural language friendly — the agent calls the tools, you just ask
  • ✅ Batch conversions with a compact summary
  • ✅ Honest failures — missing dependency, unsupported pair, existing output: every error says exactly why
  • ✅ Images, PDF and data need zero external binaries — prebuilt npm packages only. Media needs one tool (ffmpeg), clearly reported when missing

Supported conversions

Source · Targets PNG, JPG, WEBP · PNG, JPG, WEBP (any-to-any) SVG · PNG, JPG, WEBP PDF · PNG, JPG, TXT JSON · YAML, CSV YAML · JSON, CSV DOCX, PPTX, XLSX · PDF PDF · PNG, JPG, TXT, DOCX (experimental); TXT supports OCR for scanned PDFs MP4 · GIF, MP3 MOV · MP4 WAV · MP3 CSV · JSON, YAML

26 conversions. Images, PDF and data work out of the box via npm install. Optional tools unlock the rest, each clearly reported by list_conversions when missing:

  • FFmpeg -> media rows. Install it system-wide (preferred for untrusted media), or ask the agent to run `install_media_dependencies` — it downloads pinned current builds (FFmpeg 6.1.1, ~56 MB total, one time) into the plugin cache, sha256-verified, from the npmmirror binary CDN with the GitHub release as fallback.
  • LibreOffice -> DOCX/PPTX/XLSX to PDF. winget install TheDocumentFoundation.LibreOffice / brew install --cask libreoffice / apt install libreoffice.
  • Ghostscript -> PDF compression in optimize_file.
  • Python + pdf2docx -> the experimental PDF to DOCX row (pip install pdf2docx).
  • Tesseract (optional) -> faster OCR for scanned PDFs; without it the bundled tesseract.js is used and its language data is fetched explicitly via install_ocr_dependencies (winget install UB-Mannheim.TesseractOCR).

Install

Inside a DSH profile — three ways, easiest first:

# 1. from npm (once published)
dsh plugin --profile default add dsh-file-convert

# 2. straight from GitHub (a `prepare` build runs; allow it once)
dsh plugin --profile default add github:zzy-12345678/dsh-file-convert

Git installs may ask you to allow the build step in the profile's pnpm-workspace.yaml:

First-install note: pnpm may also ask you to decide on a build script for tesseract.js (it only shows a donation notice — skipping is safe). Set it in the same file and re-run the install:
```yaml
allowBuilds:
tesseract.js: false
`
allowBuilds:
  dsh-file-convert: true

From a local checkout (e.g. while hacking on it):

git clone https://github.com/zzy-12345678/dsh-file-convert
cd dsh-file-convert && npm install && npm run build
dsh plugin --profile default add /absolute/path/to/dsh-file-convert

Then restart DSH (dsh web or your usual entry point). All eight tools appear automatically.

The eight tools

convert_file

Convert one file.

{ "input": "/tmp/report.pdf", "output_format": "png", "dpi": 200 }
Converted: /tmp/report.pdf (pdf) -> /tmp/report.png (png)
1.2 MB -> 431.0 KB in 1.4s
  • Default output: next to the input file, same base name, new extension.
  • Multi-page PDFs produce -. for every page; pages: "1-3,5" selects pages (outputs keep their real page numbers, text joins only the selection).
  • Scanned PDFs → TXT: ocr: true (optionally ocr_lang, default chi_sim+eng) recognizes the rendered pages instead of the text layer. Engine priority: a local Tesseract CLI, then the bundled tesseract.js (whose language data is never downloaded implicitly — run install_ocr_dependencies first, about 10-30 MB per language).
  • Existing outputs are refused unless overwrite: true.
  • Options: output, overwrite, quality (1–100), dpi (PDF/SVG rasterization), pages, ocr, ocr_lang.

batch_convert

Convert every matching file in a directory (top level).

{ "input_dir": "/home/me/Pictures", "output_format": "webp" }
Batch convert in /home/me/Pictures -> WEBP
Converted: 18, skipped: 2, failed: 0
Output dir: /home/me/Pictures/output
  • input_format filters by source format; omit it to auto-detect every convertible file.
  • output_dir defaults to /output.
  • Existing outputs are skipped (not overwritten) unless overwrite: true.

inspect_file

Facts before action, detected from file content — not just the extension:

{ "input": "/tmp/scan.pdf" }
{ "kind": "pdf", "pages": 24, "encrypted": false, "likelyScanned": true, "bytes": 13000000 }

optimize_file

Shrink a file toward a target size instead of converting it:

{ "input": "video.mp4", "target_size_mb": 20 }
Optimized: video.mp4 (mp4) -> video-min.mp4
18.3 MB -> 19.7 MB (target 20 MB) in 41.2s
Applied: two-pass x264: video 512k + audio 128k over 185.0s
  • MP4/MOV: two-pass x264, the video bitrate is computed from the target (audio 128k, dropping to 64k for tight targets); output is always MP4. Requires ffmpeg + ffprobe.
  • JPG/WEBP: binary-searches the highest encoder quality that fits; PNG uses palette reduction. No external tools needed.
  • PDF: Ghostscript quality presets (printer/ebook/screen), first preset that fits the target wins; requires Ghostscript.
  • Targets below what the codec can physically reach are refused with the achievable minimum.
  • GIF optimization is not supported yet.

install_media_dependencies

One-call media setup: downloads pinned FFmpeg 6.1.1 static builds (ffmpeg + ffprobe) into the plugin cache (~/.dsh-file-convert/bin), verifies the pinned sha256, and proves the binaries run before reporting success. Served from the npmmirror binary CDN with the GitHub release as a byte-identical fallback. System installs keep priority over the cache. Ask the user for consent first — it is a sizable download.

install_ocr_dependencies

Downloads the tesseract.js language data (about 10-30 MB per language, chi_sim+eng by default) into the plugin cache, so ocr: true works without a local Tesseract. Skips when a local Tesseract CLI is installed or the data is already cached. Ask the user for consent first — conversions never download language data implicitly.

list_conversions

All 26 conversions with their live availability on *this* machine — unavailable rows name the missing tool and how to install it. Images, PDF and data rows are usable out of the box; media, office and PDF-compression rows depend on the optional tools (media can even be set up by the agent via install_media_dependencies).

open_ui

Open the local drag-and-drop web UI from inside a DSH session. The first call starts the server inside the plugin process and opens the browser; later calls return the running URL without starting anything twice. In DSH Desktop the converter is also always available as a native "文件转换" tab in the conversation view — open_ui is only needed when you want it in a separate browser window.

{}
dsh-file-convert UI is running at http://127.0.0.1:5757/ (opened in your browser; ...)

Local web UI (drag & drop)

The engine ships with a zero-dependency web app: drag files into the browser, pick a target format, download the result. Everything runs on your machine. It is available two ways:

  • Native in DSH Desktop (default): the plugin mounts the app at /dsh-file-convert on the DSH web server and injects a client bundle that adds a "文件转换" tab to the conversation view — click the tab and the converter appears in place, no extra server. The iframe talks to the same origin, and the DSH host's own gate protects it.
  • Standalone (npm run ui, or ask the agent to call open_ui): its own server on http://127.0.0.1:5757 (walks up to the next free port if taken), gated by a random per-session token. Useful outside DSH or when you want the converter in a normal browser window.

Feature parity in both modes:

  • Drag & drop files (or click to browse); jobs queue one by one with upload progress, warnings and per-file download links. Multi-page PDFs produce one download per page.
  • The target-format picker is driven by the live capability matrix (the same data as list_conversions), so unavailable pairs are hidden and the real detected input format corrects a wrong guess after the first attempt.
  • Closing the tab mid-conversion aborts the underlying work (the same true-cancellation semantics as the tools).
  • Standalone security: binds to 127.0.0.1 only and refuses any other interface; every API call requires a random per-session token.
  • Standalone flags: --port (default 5757), --no-open, --timeout-min (default 30 per conversion), --max-upload-mb (default 2048).

Plugin config

Key · Default · Meaning quality · 85 · Default JPEG/WebP quality (1–100) dpi · 150 · Default rasterization DPI for PDF inputs timeoutMs · 120000 · Cooperative timeout for one conversion maxInputMb · 2048 · Refuse inputs above this size (MB) maxPdfPages · 200 · Full-document PDF rasterization refuses more pages; use pages for larger documents maxOutputPixels · 16000000 · Clamp rasterized pixels per page (width × height) to this budget uiAutoStart · false · Start the web UI as soon as the plugin loads (otherwise the agent starts it on demand via open_ui) uiTimeoutMin · 30 · Per-conversion deadline inside the web UI (longer than timeoutMs because UI users convert long videos)

Limit coverage: maxInputMb applies to convert, batch, inspect and optimize alike; maxPdfPages applies to PDF rasterization and text extraction (both implicit full-document runs and explicit pages selections); maxOutputPixels applies to PDF rasterization, OCR rendering and SVG rasterization.

What `timeoutMs` cancellation stops: external binaries are killed mid-run (ffmpeg, LibreOffice, Ghostscript, Tesseract CLI); an in-flight PDF page render is cancelled through pdf.js's render task; tesseract.js recognition is stopped by terminating its worker; pure-JS pipelines (sharp encoding, JSON/YAML/CSV parsing) are interrupted at every step boundary and never write output after an abort. One large synchronous step (a single sharp encode or JSON.parse) cannot be interrupted mid-flight — its result is discarded instead of written. Every final-path write is atomic (sibling temp file + rename), so an aborted conversion never leaves a half-written file behind. batchMaxFiles · 500 · Max files examined per batch_convert run; beyond it the summary reports what was skipped instead of silently capping outputRoots · [] · When non-empty, explicit output paths must resolve inside one of these directories (recommended for shared deployments; the default next-to-input output is always exempt) ffmpegPath / ffprobePath · - · Explicit binary paths when ffmpeg is not on PATH (common on Windows) sofficePath / ghostscriptPath / pythonPath / tesseractPath · - · Explicit paths for the optional tools, overriding auto-detection

Architecture

DSH
                          │
                    dsh-file-convert
           ┌──────────────┴──────────────┐
     src/index.ts                   src/core/            ← the whole engine,
     (thin DSH glue:                (no DSH imports)       testable standalone
      name/inject/apply,                 │
      Config schema,                ConversionRouter
      8 tool registrations)              │
                       ┌─────────────────┼─────────────────┐
                       ↓                 ↓                 ↓
                 ImageConverter     PdfConverter      DataConverter
                    sharp          pdfjs-dist         js-yaml
                 (libvips npm)   @napi-rs/canvas    csv-parse / stringify
                                                     MediaConverter        OfficeConverter
                                                        ffmpeg (detected)      LibreOffice (detected)
                                                     PdfToDocxConverter    optimize_file/pdf
                                                     python + pdf2docx         Ghostscript (detected)
  • Declarative matrix: every conversion is a data row ({ from, to }) on its converter. Routing, list_conversions and dependency checks are all derived from it.
  • Detection: content first - binary magic (file-type), SVG sniffing, and JSON parsing (plus a YAML document-marker guess for extension-less files) - with the extension as fallback. Conflicts resolve in favor of the content, with a warning.
  • Core is DSH-agnostic: src/core never imports Cordis/DSH, so the engine can be unit-tested, wrapped in a CLI, or served over MCP later. If the DSH developer-preview API shifts, only the glue layer changes.
  • Dependencies: external binaries (FFmpeg, LibreOffice, Poppler) are *detected, never auto-installed* — list_conversions reports them and prints per-platform install hints. The interface is already in place (BinaryDependency).

Development

npm install
npm run build     # tsc -> lib/
npm test          # vitest, 105 tests (plus environment-gated suites)
npm run smoke     # end-to-end against lib/

Local development pulls the dsh-tools peer closure into devDependencies so typecheck and vitest can load the 0.2.1 tool registry. End users of the published plugin do not install these — the host profile provides them.

Add a conversion = add one capability row + implement it in a converter. Add a backend = implement the Converter interface and register it in createRouter().

Fidelity & safety expectations

  • Lossy by nature: PDF→DOCX (experimental), OCR and office→PDF are reconstructions — expect layout and recognition differences. inspect_file's likelyScanned flag tells you when OCR is the right tool, and results carry warnings.
  • Cached ffmpeg builds: the download convenience installs pinned sha256-verified FFmpeg 6.1.1 static builds. For untrusted media, a current system FFmpeg takes priority — prefer it in security-sensitive setups.
  • Not a sandbox: outputRoots resolves symlinks and resource limits (maxInputMb, maxPdfPages, maxOutputPixels, batchMaxFiles) cap runaway jobs, but the default next-to-input output is intentionally exempt from roots, and an agent that may write files can always write somewhere. For hostile multi-tenant use, add OS-level isolation on top.

Compatibility

Component · Verified version DeepSeek Harness Desktop · 0.2.1-rc.3 @deepseek-ai/dsh-tools · 0.2.1-rc.3 @deepseek-ai/cordis · 4.0.5 Node.js · ^22.19.0 \ · \ · >=24.0.0 (CI covers 22)

DSH is a developer preview and its APIs will move - the plugin keeps all DSH imports inside a thin glue layer so adapting stays cheap.

Roadmap

  • ~~V0.2 — Media (FFmpeg)~~ shipped: MP4→GIF/MP3, WAV→MP3, MOV→MP4, plus optimize_file with target-size two-pass encoding.
  • ~~V0.3 — Office + PDF tooling~~ shipped: DOCX/PPTX/XLSX→PDF via LibreOffice, experimental PDF→DOCX via python pdf2docx, PDF compression via Ghostscript; on-demand dependency downloads with an automatic CN mirror.
  • ~~V0.4 — OCR (Tesseract)~~ shipped: PDF→TXT supports ocr: true (optional ocr_lang), local Tesseract CLI first with the bundled tesseract.js as fallback, language data fetched explicitly via install_ocr_dependencies.
  • Later: OCR → DOCX for scanned PDFs, conversion chains (PPTX→PDF→PNG), video downscaling in optimize_file, resize/rotate image options.

License

MIT. dsh-file-convert distributes no external binaries; runtime libraries (sharp, pdfjs-dist, @napi-rs/canvas, js-yaml, csv) are installed from npm under their own licenses.