Skip to content

v0.6.0 — CLI/GUI reparados, ligaduras, Keychain estável

Latest

Choose a tag to compare

@phant0um phant0um released this 24 Sep 16:39
2b39c7c

Added

  • Presets de LLM: OpenCode Zen (https://opencode.ai/zen/v1), OpenCode
    Go
    (https://opencode.ai/zen/go/v1) e Ollama Cloud
    (https://ollama.com/v1) no fallback de qualidade — GUI (picker de
    provider), docstring de env do CLI e README (#32).

  • DOCX unificado com o PDF (T19): doc_to_md para de embutir base64 por
    padrão (decisão D2) e passa a respeitar --imagens — extrair grava
    assets com posição preservada no documento, ambos usa OCR como
    alt-text, ignorar descarta. Maquinaria de segurança compartilhada via
    core/image_assets.py (dedup, nomenclatura, limites, symlink).
    core/pdf_images.py fica só com a parte PyMuPDF.

  • --imagens transcrever|extrair|ambos|ignorar (PDF-only): política de
    imagens embutidas. extrair salva os assets (default <stem>_assets/) e
    linka ![](...); ambos extrai e usa OCR como alt-text; ignorar
    descarta sem OCR; default transcrever é byte-idêntico ao atual.
    --assets-dir sobrescreve o diretório; --obsidian usa
    vault/attachments/ com wikilinks ![[...]].

  • Segurança de extração (ADR-0005): nomes sempre gerados (nunca do
    metadado do PDF), dedup por SHA-256, limites anti resource-bomb (500
    imagens/documento, 50 MB/imagem), recusa de symlink no assets dir, ext
    exótica normalizada para PNG.

  • core/pdf_images.py com 8 testes (extração, dedup, limites, nome
    hostil, symlink, PDF sem imagem).

  • ADR-0005: decisões D1–D5 (destino, base64 rejeitado, render de scan,
    dedup, nomenclatura).

  • LLM provider/model picker in the GUI (Settings window) — fixes the
    silent no-op of the AI Enhance toggle in the .app: BatchProcessor now
    injects PDF2MD_LLM_URL/MODEL/KEY into the process environment. Provider
    presets (Ollama, Gemini, Groq, OpenRouter, custom), live model list and
    connection status come from the new pdf2md llm subcommands; the API key
    is stored in the macOS Keychain (never in argv — ps aux safe).

  • CLI flags --llm-url / --llm-modelo with precedence flag > env >
    default, propagated cli → batch → quality → llm_enhancer via ConfigLLM.

  • pdf2md llm modelos --json — lists models from the endpoint, with
    vision capability detection for local Ollama (/api/show).

  • pdf2md llm testar --json — uncached connectivity probe with latency
    measurement; failure returns {"ok": false} with exit 0 (JSON is the GUI
    contract, no traceback).

  • ADR-0007: LLM config decisions D6–D10 (Keychain, env injection, SSRF
    by design, keychain/ad-hoc signing limitation).

Changed

  • Desempenho do pipeline de qualidade: corrigir_mojibake (~40 travessias
    O(n) de count+replace) e limpar_artefatos (6× replace) agora rodam em
    1 passada (regex sub + str.translate compilada) (#30).
  • Tabela do CLI determinística: resultados em batch seguem a ordem de
    entrada (antes: ordem de conclusão do ThreadPool, variava entre runs) (#30).
  • _linha_tabela_md extraído (DRY pptx/xlsx) (#30).
  • GUI: drop/picker aceitam .docx, .pptx e .xlsx de novo — o filtro por
    UTI hardcoded nunca casava com a resolução do LaunchServices (docx →
    org.openxmlformats.wordprocessingml.document sem officedocument.);
    filtro agora é por extensão, espelhando EXTENSOES_PERMITIDAS do core (#31).

Fixed

  • Aviso falso de "texto corrompido" em PDF com fórmula (BAIXA): glifo de
    fórmula sem ToUnicode (Σ, = de fontes TeX como pxfonts) gerava o aviso
    "texto provavelmente corrompido por problema de encoding", mas as palavras
    estavam intactas. classificar_fffd separa os U+FFFD: colado em letra de
    texto corrido continua como encoding corrompido; fora de palavra (vizinho
    de espaço, colchete, letra grega ou letra matemática) vira aviso próprio,
    "símbolo(s) sem mapeamento Unicode … revise as fórmulas".

  • Ligaduras tipográficas (fl, fi, ff...) viravam U+FFFD na conversão (MÉDIA):
    o engine de layout do pymupdf4llm mapeia glifos de ligadura para U+FFFD quando
    a fonte embutida não traz ToUnicode — "Workflow" saía como "Work�ow" e
    "reflect" como "re�ect". Reparo por dicionário do próprio documento:
    page.get_text() como referência, substituição 1-para-1, sem adivinhação.
    Limitação: FFFD de fonte matemática (Σ, ∫) não tem referência no texto cru e
    continua avisado.

  • OCR interno do pymupdf4llm duplicava trabalho (~40% do tempo de extração):
    use_ocr=False quando o engine de layout está ativo. O projeto já tem OCR
    próprio controlado por ModoImagem.

  • Assets com espaço/acento no nome quebravam --obsidian/--assets-dir
    (ALTA): o prefixo {stem}__ com "Relatório.pdf" violava o regex de nome
    seguro → ValueError → arquivo inteiro virava ERRO. Prefixo agora passa por
    slugify ASCII (NFKD). Revisão fullstack 2026-08-13 (#29).

  • DOCX com imagem gigante derrubava o processo (ALTA): f.read() lia a
    imagem inteira antes do check de 50 MB — docx malicioso de 2 GB causava OOM.
    Tamanho agora é checado via seek/tell antes da leitura (#29).

  • GUI: erros do subprocesso invisíveis — stderr era descartado e exit code
    nunca verificado; todo erro virava "Falha ao executar processo". ProcessRunner
    agora propaga (stdout, stderr, exitCode) e a GUI exibe o erro real (#29).

  • Default-command com flags antes do positional (pdf2md --sobrescrever in.pdf out/) falhava com "No such option" — flags são reordenadas (#29).

  • Markup Rich vazava no JSON erro ([red]...[/red] renderizado cru na GUI)
    — mensagem limpa no caminho --json (#29).

  • XLSX com linhas vazias no topo descartava o conteúdo — primeira linha
    não-vazia vira header; streaming read_only preservado (2 passadas) (#29).

  • TesseractError mascarado como "arquivo corrompido" — mensagem real de OCR
    (idioma ausente) agora é exibida (#29).

  • CLI/GUI: o .app não convertia (desde v0.4.0). O CLI exigia o
    subcomando converter, mas a GUI chamava pdf2md <arquivo> <destino> --json → "No such command". Default-command no entry point: a forma
    canônica pdf2md <origem> [destino] [opções] agora funciona (a explícita
    pdf2md converter ... continua). Auditado por Grok 4.5 max (C1).

  • GUI: avisos de qualidade eram descartados — atualizarProgresso
    não propagava item.avisos; o ícone âmbar nunca aparecia (A1).

  • --imagens: crash em ambos com GIF/PPM (ocr_bytes normaliza para
    PNG); renders de scan contam no limite anti bomb; colisão de stem
    em dirs de assets compartilhados
    (prefixo pelo stem do .md destino) —
    fixes da Emenda 2 do ADR-0005 (#22/cf9ab44).

Security

  • CI: gate detect-secrets era vácuo — scan --baseline auto-atualiza o
    baseline e sai 0 com segredo novo; --fail-on-unknown não existe no 1.5.0.
    Gate real: detect-secrets-hook + grep ERROR: Potential secrets (#29).

  • CI: smoke llm testar validava valor, não contrato — sem servidor no CI,
    ok: false é legítimo; gate agora valida o shape JSON ("ok": true|false).

  • CI: permissions: contents: read (least privilege) + actions pinadas por
    SHA (checkout v4.4.0, setup-python v5.6.0, setup-uv v5.4.2, cache v4.3.0).

  • mypy strict agora roda no CI — config existia mas estava morta; 21 erros
    resolvidos (anotações + ignore_missing_imports para libs sem stubs).

  • Prompt injection documentado em SECURITY.md (texto do documento vai ao
    LLM sem isolamento — risco aceito, ADR-0004).

  • Error messages from llm modelos/llm testar are sanitized (CWE-209/532):
    only HTTP code or failure category is exposed, never URL/path/credential.

  • API key is transmitted only via process.environment (D8) and the
    Authorization header — never through argv or JSON output.

  • Image extraction hardening (ADR-0005): path traversal, symlink and
    resource-bomb mitigations with tests proving each one.

Testes

  • 206 → 273 testes; cobertura 87% → 94% (cli.py 68→88%, image_converter
    75→95%, pdf_images 79→100%) (#29, #30).
  • Teste-fantasma corrigido: testar importado de llm_enhancer fazia
    chamada de rede real a localhost:11434 na suíte — renomeado para _testar_llm.

Instalação

  • Build Apple Silicon (arm64), macOS 13+. OCR requer Tesseract: brew install tesseract tesseract-lang.
  • App sem notarização: na primeira execução, botão direito → Abrir.
  • SHA-256 de PDF2MD-v0.6.0.dmg: a2c983888e5927de3159ce1c925acc300fa88802afef435bd71112ae09e86d1e