@yadsh/dsh-qa-surface 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +235 -14
- package/compatibility.json +10 -1
- package/cordis.patch.yml +17 -0
- package/lib/account-remotes.js +10 -0
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/credentials.js +12 -4
- package/lib/accounts/credentials.js.map +1 -1
- package/lib/accounts/errors.js.map +1 -1
- package/lib/accounts/file.js.map +1 -1
- package/lib/accounts/store.js +38 -1
- package/lib/accounts/store.js.map +1 -1
- package/lib/approvals.js +179 -0
- package/lib/approvals.js.map +1 -0
- package/lib/cli.js +10 -0
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +29 -0
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaSessionController.js +261 -48
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +138 -56
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaSurfaceGuard.js +96 -0
- package/lib/client/QaSurfaceGuard.js.map +1 -0
- package/lib/client/QaTranscriptAdapter.js +11 -5
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/approvals.js +104 -0
- package/lib/client/approvals.js.map +1 -0
- package/lib/client/attestation.js +3 -0
- package/lib/client/attestation.js.map +1 -1
- package/lib/client/components/QaApproval.js +23 -0
- package/lib/client/components/QaApproval.js.map +1 -0
- package/lib/client/components/QaChangelog.js +55 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaComposer.js +1 -1
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaHeader.js +2 -8
- package/lib/client/components/QaHeader.js.map +1 -1
- package/lib/client/components/QaMessage.js +20 -7
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaModal.js +24 -6
- package/lib/client/components/QaModal.js.map +1 -1
- package/lib/client/components/QaQuestions.js +96 -0
- package/lib/client/components/QaQuestions.js.map +1 -0
- package/lib/client/components/QaSidebar.js +2 -6
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaSourcesPanel.js +6 -5
- package/lib/client/components/QaSourcesPanel.js.map +1 -1
- package/lib/client/components/QaWidthHandle.js +21 -1
- package/lib/client/components/QaWidthHandle.js.map +1 -1
- package/lib/client/components/surface-utils.js +2 -0
- package/lib/client/components/surface-utils.js.map +1 -1
- package/lib/client/index.js +81 -22
- package/lib/client/index.js.map +1 -1
- package/lib/client/panels/PanelHost.js +209 -0
- package/lib/client/panels/PanelHost.js.map +1 -0
- package/lib/client/panels/PanelLauncher.js +39 -0
- package/lib/client/panels/PanelLauncher.js.map +1 -0
- package/lib/client/panels/contract.js +2 -0
- package/lib/client/panels/contract.js.map +1 -0
- package/lib/client/panels/index.js +2 -0
- package/lib/client/panels/index.js.map +1 -0
- package/lib/client/panels/registry.js +171 -0
- package/lib/client/panels/registry.js.map +1 -0
- package/lib/client/project-session-state.js +8 -3
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/questions.js +114 -0
- package/lib/client/questions.js.map +1 -0
- package/lib/client/settings/card.js +2 -2
- package/lib/client/settings/card.js.map +1 -1
- package/lib/client/settings/sections/accounts.js +3 -1
- package/lib/client/settings/sections/accounts.js.map +1 -1
- package/lib/client/settings/sections/documents.js +88 -0
- package/lib/client/settings/sections/documents.js.map +1 -0
- package/lib/client/settings/sections/interface.js +2 -0
- package/lib/client/settings/sections/interface.js.map +1 -1
- package/lib/client/settings/sections/sources.js +2 -0
- package/lib/client/settings/sections/sources.js.map +1 -1
- package/lib/client/settings/sections.js +1 -0
- package/lib/client/settings/sections.js.map +1 -1
- package/lib/client/settings-extensions/contract.js +2 -0
- package/lib/client/settings-extensions/contract.js.map +1 -0
- package/lib/client/settings-extensions/index.js +2 -0
- package/lib/client/settings-extensions/index.js.map +1 -0
- package/lib/client/settings-extensions/registry.js +63 -0
- package/lib/client/settings-extensions/registry.js.map +1 -0
- package/lib/client/settlement.js +51 -5
- package/lib/client/settlement.js.map +1 -1
- package/lib/client/source-preview.js +24 -0
- package/lib/client/source-preview.js.map +1 -0
- package/lib/client/styles.js +199 -32
- package/lib/client/styles.js.map +1 -1
- package/lib/client/subagent-codenames.js +109 -0
- package/lib/client/subagent-codenames.js.map +1 -0
- package/lib/client/types.js +3 -0
- package/lib/client/types.js.map +1 -1
- package/lib/client/user-settings/GeneralSettingsPage.js +13 -0
- package/lib/client/user-settings/GeneralSettingsPage.js.map +1 -0
- package/lib/client/user-settings/ProfileSettingsPage.js +59 -0
- package/lib/client/user-settings/ProfileSettingsPage.js.map +1 -0
- package/lib/client/user-settings/SkillCatalog.js +23 -0
- package/lib/client/user-settings/SkillCatalog.js.map +1 -0
- package/lib/client/user-settings/SkillEditor.js +89 -0
- package/lib/client/user-settings/SkillEditor.js.map +1 -0
- package/lib/client/user-settings/SkillToolPicker.js +58 -0
- package/lib/client/user-settings/SkillToolPicker.js.map +1 -0
- package/lib/client/user-settings/SkillsSettingsPage.js +171 -0
- package/lib/client/user-settings/SkillsSettingsPage.js.map +1 -0
- package/lib/client/user-settings/StartersSettingsPage.js +78 -0
- package/lib/client/user-settings/StartersSettingsPage.js.map +1 -0
- package/lib/client/user-settings/UserSettingsDialog.js +50 -0
- package/lib/client/user-settings/UserSettingsDialog.js.map +1 -0
- package/lib/client/user-settings/copy.js +84 -0
- package/lib/client/user-settings/copy.js.map +1 -0
- package/lib/client/user-settings/draft.js +127 -0
- package/lib/client/user-settings/draft.js.map +1 -0
- package/lib/client/user-settings/fields.js +24 -0
- package/lib/client/user-settings/fields.js.map +1 -0
- package/lib/client.js +6629 -1579
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/accounts.js +30 -0
- package/lib/config-resolvers/accounts.js.map +1 -1
- package/lib/config-resolvers/basics.js +4 -2
- package/lib/config-resolvers/basics.js.map +1 -1
- package/lib/config-resolvers/defaults.js +27 -0
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/lockdown.js +11 -0
- package/lib/config-resolvers/lockdown.js.map +1 -1
- package/lib/config-resolvers/session.js +2 -2
- package/lib/config-resolvers/session.js.map +1 -1
- package/lib/config-resolvers/shared.js +9 -0
- package/lib/config-resolvers/shared.js.map +1 -1
- package/lib/config-resolvers/sources.js +2 -0
- package/lib/config-resolvers/sources.js.map +1 -1
- package/lib/config-resolvers/tools.js +37 -0
- package/lib/config-resolvers/tools.js.map +1 -0
- package/lib/config-resolvers/ui.js +2 -0
- package/lib/config-resolvers/ui.js.map +1 -1
- package/lib/config.js +299 -2
- package/lib/config.js.map +1 -1
- package/lib/documents/artifacts/ids.js +43 -0
- package/lib/documents/artifacts/ids.js.map +1 -0
- package/lib/documents/artifacts/manifest.js +66 -0
- package/lib/documents/artifacts/manifest.js.map +1 -0
- package/lib/documents/artifacts/store.js +169 -0
- package/lib/documents/artifacts/store.js.map +1 -0
- package/lib/documents/capabilities.js +93 -0
- package/lib/documents/capabilities.js.map +1 -0
- package/lib/documents/config.js +302 -0
- package/lib/documents/config.js.map +1 -0
- package/lib/documents/defaults.js +78 -0
- package/lib/documents/defaults.js.map +1 -0
- package/lib/documents/errors.js +101 -0
- package/lib/documents/errors.js.map +1 -0
- package/lib/documents/index.js +80 -0
- package/lib/documents/index.js.map +1 -0
- package/lib/documents/inspect/facts.js +161 -0
- package/lib/documents/inspect/facts.js.map +1 -0
- package/lib/documents/inspect/zip.js +108 -0
- package/lib/documents/inspect/zip.js.map +1 -0
- package/lib/documents/markdown/directives.js +135 -0
- package/lib/documents/markdown/directives.js.map +1 -0
- package/lib/documents/markdown/frontmatter.js +35 -0
- package/lib/documents/markdown/frontmatter.js.map +1 -0
- package/lib/documents/markdown/normalize.js +113 -0
- package/lib/documents/markdown/normalize.js.map +1 -0
- package/lib/documents/orchestrator/assets.js +243 -0
- package/lib/documents/orchestrator/assets.js.map +1 -0
- package/lib/documents/orchestrator/convert-document.js +186 -0
- package/lib/documents/orchestrator/convert-document.js.map +1 -0
- package/lib/documents/orchestrator/create-document.js +292 -0
- package/lib/documents/orchestrator/create-document.js.map +1 -0
- package/lib/documents/orchestrator/extract-document.js +162 -0
- package/lib/documents/orchestrator/extract-document.js.map +1 -0
- package/lib/documents/orchestrator/inspect-document.js +89 -0
- package/lib/documents/orchestrator/inspect-document.js.map +1 -0
- package/lib/documents/orchestrator/runtime-deps.js +59 -0
- package/lib/documents/orchestrator/runtime-deps.js.map +1 -0
- package/lib/documents/orchestrator/scope.js +132 -0
- package/lib/documents/orchestrator/scope.js.map +1 -0
- package/lib/documents/providers/docling.js +247 -0
- package/lib/documents/providers/docling.js.map +1 -0
- package/lib/documents/providers/libreoffice.js +130 -0
- package/lib/documents/providers/libreoffice.js.map +1 -0
- package/lib/documents/providers/markitdown.js +89 -0
- package/lib/documents/providers/markitdown.js.map +1 -0
- package/lib/documents/providers/pandoc.js +161 -0
- package/lib/documents/providers/pandoc.js.map +1 -0
- package/lib/documents/providers/process.js +164 -0
- package/lib/documents/providers/process.js.map +1 -0
- package/lib/documents/providers/registry.js +152 -0
- package/lib/documents/providers/registry.js.map +1 -0
- package/lib/documents/providers/shared.js +75 -0
- package/lib/documents/providers/shared.js.map +1 -0
- package/lib/documents/runtime.js +114 -0
- package/lib/documents/runtime.js.map +1 -0
- package/lib/documents/security/file-types.js +188 -0
- package/lib/documents/security/file-types.js.map +1 -0
- package/lib/documents/security/limits.js +78 -0
- package/lib/documents/security/limits.js.map +1 -0
- package/lib/documents/security/mime.js +43 -0
- package/lib/documents/security/mime.js.map +1 -0
- package/lib/documents/security/paths.js +133 -0
- package/lib/documents/security/paths.js.map +1 -0
- package/lib/documents/templates/registry.js +147 -0
- package/lib/documents/templates/registry.js.map +1 -0
- package/lib/documents/templates/resolver.js +59 -0
- package/lib/documents/templates/resolver.js.map +1 -0
- package/lib/documents/tools/convert.js +125 -0
- package/lib/documents/tools/convert.js.map +1 -0
- package/lib/documents/tools/create.js +187 -0
- package/lib/documents/tools/create.js.map +1 -0
- package/lib/documents/tools/index.js +41 -0
- package/lib/documents/tools/index.js.map +1 -0
- package/lib/documents/tools/inspect.js +135 -0
- package/lib/documents/tools/inspect.js.map +1 -0
- package/lib/documents/tools/shared.js +88 -0
- package/lib/documents/tools/shared.js.map +1 -0
- package/lib/documents/tools/to-markdown.js +125 -0
- package/lib/documents/tools/to-markdown.js.map +1 -0
- package/lib/documents/types.js +12 -0
- package/lib/documents/types.js.map +1 -0
- package/lib/index.js +299 -24
- package/lib/index.js.map +1 -1
- package/lib/integration-principals.js +22 -0
- package/lib/integration-principals.js.map +1 -0
- package/lib/lockdown-policy.js +15 -3
- package/lib/lockdown-policy.js.map +1 -1
- package/lib/personal-skills/errors.js +9 -0
- package/lib/personal-skills/errors.js.map +1 -0
- package/lib/personal-skills/host.js +80 -0
- package/lib/personal-skills/host.js.map +1 -0
- package/lib/personal-skills/index.js +10 -0
- package/lib/personal-skills/index.js.map +1 -0
- package/lib/personal-skills/paths.js +127 -0
- package/lib/personal-skills/paths.js.map +1 -0
- package/lib/personal-skills/provider.js +109 -0
- package/lib/personal-skills/provider.js.map +1 -0
- package/lib/personal-skills/remotes.js +40 -0
- package/lib/personal-skills/remotes.js.map +1 -0
- package/lib/personal-skills/service.js +686 -0
- package/lib/personal-skills/service.js.map +1 -0
- package/lib/personal-skills/skill-file.js +278 -0
- package/lib/personal-skills/skill-file.js.map +1 -0
- package/lib/personal-skills/skill-format.js +202 -0
- package/lib/personal-skills/skill-format.js.map +1 -0
- package/lib/personal-skills/watcher.js +96 -0
- package/lib/personal-skills/watcher.js.map +1 -0
- package/lib/profile.js +6 -4
- package/lib/profile.js.map +1 -1
- package/lib/prompt-notes.js +19 -0
- package/lib/prompt-notes.js.map +1 -1
- package/lib/provenance/file-preview.js +68 -21
- package/lib/provenance/file-preview.js.map +1 -1
- package/lib/provenance/host-store.js +69 -20
- package/lib/provenance/host-store.js.map +1 -1
- package/lib/provenance/index.js +1 -0
- package/lib/provenance/index.js.map +1 -1
- package/lib/provenance/normalize.js +3 -1
- package/lib/provenance/normalize.js.map +1 -1
- package/lib/provenance/reported.js +37 -12
- package/lib/provenance/reported.js.map +1 -1
- package/lib/provenance/snapshot-store.js +154 -0
- package/lib/provenance/snapshot-store.js.map +1 -0
- package/lib/qa-tools/activation-detector.js +53 -0
- package/lib/qa-tools/activation-detector.js.map +1 -0
- package/lib/qa-tools/activation-manager.js +111 -0
- package/lib/qa-tools/activation-manager.js.map +1 -0
- package/lib/qa-tools/catalog.js +108 -0
- package/lib/qa-tools/catalog.js.map +1 -0
- package/lib/qa-tools/durable-marker.js +115 -0
- package/lib/qa-tools/durable-marker.js.map +1 -0
- package/lib/qa-tools/index.js +83 -0
- package/lib/qa-tools/index.js.map +1 -0
- package/lib/qa-tools/lifecycle.js +64 -0
- package/lib/qa-tools/lifecycle.js.map +1 -0
- package/lib/qa-tools/types.js +2 -0
- package/lib/qa-tools/types.js.map +1 -0
- package/lib/questions.js +185 -0
- package/lib/questions.js.map +1 -0
- package/lib/resolve-config.js +5 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +104 -20
- package/lib/secure-session.js.map +1 -1
- package/lib/session-ownership.js +55 -0
- package/lib/session-ownership.js.map +1 -0
- package/lib/starters.js +117 -0
- package/lib/starters.js.map +1 -0
- package/lib/typert.host.js +1162 -77
- package/lib/typert.remote-client.d.ts +27 -1
- package/lib/typert.remote-client.js +879 -14
- package/lib/types/account-remotes.d.ts +3 -1
- package/lib/types/accounts/credentials.d.ts +6 -0
- package/lib/types/accounts/errors.d.ts +1 -1
- package/lib/types/accounts/file.d.ts +10 -0
- package/lib/types/accounts/store.d.ts +15 -1
- package/lib/types/approvals.d.ts +65 -0
- package/lib/types/client/QaAccountsController.d.ts +7 -1
- package/lib/types/client/QaSessionController.d.ts +50 -1
- package/lib/types/client/QaSurface.d.ts +21 -4
- package/lib/types/client/QaSurfaceGuard.d.ts +27 -0
- package/lib/types/client/QaTranscriptAdapter.d.ts +7 -0
- package/lib/types/client/approvals.d.ts +39 -0
- package/lib/types/client/components/QaApproval.d.ts +13 -0
- package/lib/types/client/components/QaComposer.d.ts +3 -1
- package/lib/types/client/components/QaHeader.d.ts +3 -1
- package/lib/types/client/components/QaModal.d.ts +5 -4
- package/lib/types/client/components/QaQuestions.d.ts +13 -0
- package/lib/types/client/components/QaSidebar.d.ts +8 -11
- package/lib/types/client/components/QaWidthHandle.d.ts +13 -0
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/panels/PanelHost.d.ts +18 -0
- package/lib/types/client/panels/PanelLauncher.d.ts +5 -0
- package/lib/types/client/panels/contract.d.ts +83 -0
- package/lib/types/client/panels/index.d.ts +3 -0
- package/lib/types/client/panels/registry.d.ts +40 -0
- package/lib/types/client/project-session-state.d.ts +13 -1
- package/lib/types/client/questions.d.ts +45 -0
- package/lib/types/client/settings/sections/documents.d.ts +13 -0
- package/lib/types/client/settings/sections.d.ts +1 -0
- package/lib/types/client/settings-extensions/contract.d.ts +32 -0
- package/lib/types/client/settings-extensions/index.d.ts +3 -0
- package/lib/types/client/settings-extensions/registry.d.ts +15 -0
- package/lib/types/client/settlement.d.ts +41 -5
- package/lib/types/client/source-preview.d.ts +8 -0
- package/lib/types/client/styles.d.ts +2 -0
- package/lib/types/client/subagent-codenames.d.ts +18 -0
- package/lib/types/client/types.d.ts +82 -3
- package/lib/types/client/user-settings/GeneralSettingsPage.d.ts +14 -0
- package/lib/types/client/user-settings/ProfileSettingsPage.d.ts +18 -0
- package/lib/types/client/user-settings/SkillCatalog.d.ts +16 -0
- package/lib/types/client/user-settings/SkillEditor.d.ts +42 -0
- package/lib/types/client/user-settings/SkillToolPicker.d.ts +17 -0
- package/lib/types/client/user-settings/SkillsSettingsPage.d.ts +15 -0
- package/lib/types/client/user-settings/StartersSettingsPage.d.ts +14 -0
- package/lib/types/client/user-settings/UserSettingsDialog.d.ts +43 -0
- package/lib/types/client/user-settings/copy.d.ts +25 -0
- package/lib/types/client/user-settings/draft.d.ts +51 -0
- package/lib/types/client/user-settings/fields.d.ts +43 -0
- package/lib/types/config-resolvers/shared.d.ts +7 -0
- package/lib/types/config-resolvers/tools.d.ts +6 -0
- package/lib/types/documents/artifacts/ids.d.ts +14 -0
- package/lib/types/documents/artifacts/manifest.d.ts +32 -0
- package/lib/types/documents/artifacts/store.d.ts +67 -0
- package/lib/types/documents/capabilities.d.ts +32 -0
- package/lib/types/documents/config.d.ts +27 -0
- package/lib/types/documents/defaults.d.ts +161 -0
- package/lib/types/documents/errors.d.ts +42 -0
- package/lib/types/documents/index.d.ts +63 -0
- package/lib/types/documents/inspect/facts.d.ts +43 -0
- package/lib/types/documents/inspect/zip.d.ts +28 -0
- package/lib/types/documents/markdown/directives.d.ts +39 -0
- package/lib/types/documents/markdown/frontmatter.d.ts +15 -0
- package/lib/types/documents/markdown/normalize.d.ts +30 -0
- package/lib/types/documents/orchestrator/assets.d.ts +55 -0
- package/lib/types/documents/orchestrator/convert-document.d.ts +25 -0
- package/lib/types/documents/orchestrator/create-document.d.ts +18 -0
- package/lib/types/documents/orchestrator/extract-document.d.ts +24 -0
- package/lib/types/documents/orchestrator/inspect-document.d.ts +12 -0
- package/lib/types/documents/orchestrator/runtime-deps.d.ts +43 -0
- package/lib/types/documents/orchestrator/scope.d.ts +52 -0
- package/lib/types/documents/providers/docling.d.ts +54 -0
- package/lib/types/documents/providers/libreoffice.d.ts +40 -0
- package/lib/types/documents/providers/markitdown.d.ts +28 -0
- package/lib/types/documents/providers/pandoc.d.ts +42 -0
- package/lib/types/documents/providers/process.d.ts +54 -0
- package/lib/types/documents/providers/registry.d.ts +68 -0
- package/lib/types/documents/providers/shared.d.ts +27 -0
- package/lib/types/documents/runtime.d.ts +59 -0
- package/lib/types/documents/security/file-types.d.ts +39 -0
- package/lib/types/documents/security/limits.d.ts +24 -0
- package/lib/types/documents/security/mime.d.ts +13 -0
- package/lib/types/documents/security/paths.d.ts +38 -0
- package/lib/types/documents/templates/registry.d.ts +50 -0
- package/lib/types/documents/templates/resolver.d.ts +30 -0
- package/lib/types/documents/tools/convert.d.ts +9 -0
- package/lib/types/documents/tools/create.d.ts +14 -0
- package/lib/types/documents/tools/index.d.ts +33 -0
- package/lib/types/documents/tools/inspect.d.ts +9 -0
- package/lib/types/documents/tools/shared.d.ts +77 -0
- package/lib/types/documents/tools/to-markdown.d.ts +12 -0
- package/lib/types/documents/types.d.ts +294 -0
- package/lib/types/index.d.ts +103 -5
- package/lib/types/integration-principals.d.ts +9 -0
- package/lib/types/lockdown-policy.d.ts +12 -2
- package/lib/types/personal-skills/errors.d.ts +11 -0
- package/lib/types/personal-skills/host.d.ts +42 -0
- package/lib/types/personal-skills/index.d.ts +10 -0
- package/lib/types/personal-skills/paths.d.ts +39 -0
- package/lib/types/personal-skills/provider.d.ts +33 -0
- package/lib/types/personal-skills/remotes.d.ts +35 -0
- package/lib/types/personal-skills/service.d.ts +131 -0
- package/lib/types/personal-skills/skill-file.d.ts +64 -0
- package/lib/types/personal-skills/skill-format.d.ts +100 -0
- package/lib/types/personal-skills/watcher.d.ts +39 -0
- package/lib/types/profile.d.ts +13 -0
- package/lib/types/prompt-notes.d.ts +9 -0
- package/lib/types/provenance/file-preview.d.ts +47 -4
- package/lib/types/provenance/host-store.d.ts +7 -1
- package/lib/types/provenance/index.d.ts +1 -0
- package/lib/types/provenance/reported.d.ts +9 -1
- package/lib/types/provenance/snapshot-store.d.ts +23 -0
- package/lib/types/qa-tools/activation-detector.d.ts +29 -0
- package/lib/types/qa-tools/activation-manager.d.ts +47 -0
- package/lib/types/qa-tools/catalog.d.ts +40 -0
- package/lib/types/qa-tools/durable-marker.d.ts +16 -0
- package/lib/types/qa-tools/index.d.ts +40 -0
- package/lib/types/qa-tools/lifecycle.d.ts +44 -0
- package/lib/types/qa-tools/types.d.ts +45 -0
- package/lib/types/questions.d.ts +83 -0
- package/lib/types/secure-session.d.ts +45 -4
- package/lib/types/session-ownership.d.ts +30 -0
- package/lib/types/starters.d.ts +45 -0
- package/lib/types/types.d.ts +282 -7
- package/lib/types/user-workspace.d.ts +5 -1
- package/lib/user-workspace.js +21 -6
- package/lib/user-workspace.js.map +1 -1
- package/package.json +22 -9
- package/scripts/repair-session-events.mjs +260 -0
- package/lib/client/components/QaProfile.js +0 -64
- package/lib/client/components/QaProfile.js.map +0 -1
- package/lib/types/client/components/QaProfile.d.ts +0 -25
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docling provider — the default DOCX/PDF → Markdown extractor (§6.1, §15).
|
|
3
|
+
*
|
|
4
|
+
* Docling runs as a separate HTTP service (`docling-serve`) rather than inside
|
|
5
|
+
* the host process: it is the heaviest dependency of the pipeline, it is where
|
|
6
|
+
* OCR happens, and keeping it across a network boundary is what lets the QA
|
|
7
|
+
* image stay small (§21, §39).
|
|
8
|
+
*
|
|
9
|
+
* Two behaviours are deliberate:
|
|
10
|
+
*
|
|
11
|
+
* - images come back inline as base64 data URIs (the only representation that
|
|
12
|
+
* survives an HTTP boundary without a second fetch), and the extractor writes
|
|
13
|
+
* them out as real files under `assets/`, rewriting references to relative
|
|
14
|
+
* paths (§17.2);
|
|
15
|
+
* - nothing about the response shape is assumed beyond "there is Markdown
|
|
16
|
+
* somewhere in it": a deployment upgrades docling-serve independently of
|
|
17
|
+
* this plugin, so both the wrapped (`document.md_content`) and flat
|
|
18
|
+
* (`md_content`) spellings are read.
|
|
19
|
+
*/
|
|
20
|
+
import { mkdir } from "node:fs/promises";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { DocumentError, sanitizeBackendOutput } from "../errors.js";
|
|
23
|
+
import { backendInfo } from "./shared.js";
|
|
24
|
+
const HEALTH_TIMEOUT_MS = 5_000;
|
|
25
|
+
const DATA_URI_PATTERN = /!\[([^\]]*)\]\(data:image\/(png|jpe?g|gif|webp|bmp|tiff);base64,([A-Za-z0-9+/=\s]+)\)/gu;
|
|
26
|
+
const EXTENSION_BY_MIME = {
|
|
27
|
+
png: "png",
|
|
28
|
+
jpg: "jpeg",
|
|
29
|
+
jpeg: "jpeg",
|
|
30
|
+
gif: "gif",
|
|
31
|
+
webp: "webp",
|
|
32
|
+
bmp: "bmp",
|
|
33
|
+
tiff: "tiff",
|
|
34
|
+
};
|
|
35
|
+
function joinUrl(baseUrl, route) {
|
|
36
|
+
const trimmed = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
37
|
+
return `${trimmed}${route}`;
|
|
38
|
+
}
|
|
39
|
+
export class DoclingExtractor {
|
|
40
|
+
options;
|
|
41
|
+
name = "docling";
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.options = options;
|
|
44
|
+
}
|
|
45
|
+
supports(_format) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
get fetchImpl() {
|
|
49
|
+
return this.options.fetchImpl ?? fetch;
|
|
50
|
+
}
|
|
51
|
+
async health() {
|
|
52
|
+
for (const route of ["/health", "/v1/health"]) {
|
|
53
|
+
try {
|
|
54
|
+
const response = await this.fetchImpl(joinUrl(this.options.baseUrl, route), {
|
|
55
|
+
method: "GET",
|
|
56
|
+
signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),
|
|
57
|
+
});
|
|
58
|
+
if (response.ok)
|
|
59
|
+
return "ok";
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return "unavailable";
|
|
66
|
+
}
|
|
67
|
+
async extract(input) {
|
|
68
|
+
const warnings = [];
|
|
69
|
+
const body = new FormData();
|
|
70
|
+
const file = await import("node:fs/promises").then((fs) => fs.readFile(input.inputPath));
|
|
71
|
+
body.append("files", new Blob([new Uint8Array(file)], { type: "application/octet-stream" }), path.basename(input.inputPath));
|
|
72
|
+
body.append("to_formats", "md");
|
|
73
|
+
body.append("image_export_mode", input.extractImages ? "embedded" : "placeholder");
|
|
74
|
+
body.append("abort_on_error", "false");
|
|
75
|
+
if (input.extractTables)
|
|
76
|
+
body.append("table_mode", "accurate");
|
|
77
|
+
if (input.ocr === "force") {
|
|
78
|
+
body.append("do_ocr", "true");
|
|
79
|
+
body.append("force_ocr", "true");
|
|
80
|
+
warnings.push({
|
|
81
|
+
code: "OCR_USED",
|
|
82
|
+
message: "Document was processed using OCR.",
|
|
83
|
+
backend: this.name,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else if (input.ocr === "off") {
|
|
87
|
+
body.append("do_ocr", "false");
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Let Docling decide per page; the service turns OCR on for pages that
|
|
91
|
+
// carry no text layer.
|
|
92
|
+
body.append("do_ocr", "true");
|
|
93
|
+
body.append("force_ocr", "false");
|
|
94
|
+
}
|
|
95
|
+
for (const language of input.ocrLanguages ?? []) {
|
|
96
|
+
const clean = language.trim();
|
|
97
|
+
if (clean !== "")
|
|
98
|
+
body.append("ocr_lang", clean);
|
|
99
|
+
}
|
|
100
|
+
let response;
|
|
101
|
+
try {
|
|
102
|
+
response = await this.fetchImpl(joinUrl(this.options.baseUrl, "/v1/convert/file"), {
|
|
103
|
+
method: "POST",
|
|
104
|
+
body,
|
|
105
|
+
signal: input.signal ?? AbortSignal.timeout(this.options.timeoutMs),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const aborted = input.signal?.aborted === true;
|
|
110
|
+
throw new DocumentError(aborted ? "BACKEND_TIMEOUT" : "BACKEND_UNAVAILABLE", aborted
|
|
111
|
+
? "the document extractor was cancelled"
|
|
112
|
+
: `the document extractor is unreachable: ${sanitizeBackendOutput(error instanceof Error ? error.message : String(error))}`, { backend: this.name, cause: error });
|
|
113
|
+
}
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
const detail = sanitizeBackendOutput(await response.text().catch(() => ""));
|
|
116
|
+
throw new DocumentError(response.status === 503 ? "BACKEND_UNAVAILABLE" : "EXTRACTION_FAILED", `the document extractor answered ${response.status}${detail === "" ? "" : `: ${detail}`}`, { backend: this.name, details: { status: response.status } });
|
|
117
|
+
}
|
|
118
|
+
const raw = await response.text();
|
|
119
|
+
if (raw.length > this.options.maxResponseBytes) {
|
|
120
|
+
throw new DocumentError("DOCUMENT_TOO_LARGE", "the extracted document exceeds the configured response budget", { backend: this.name, details: { bytes: raw.length } });
|
|
121
|
+
}
|
|
122
|
+
let payload;
|
|
123
|
+
try {
|
|
124
|
+
payload = JSON.parse(raw);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new DocumentError("EXTRACTION_FAILED", "the document extractor returned a response that is not JSON", { backend: this.name, cause: error });
|
|
128
|
+
}
|
|
129
|
+
const { markdown, pages, serviceVersion } = readDoclingPayload(payload, this.name);
|
|
130
|
+
const { text, assets } = await this.materializeAssets(markdown, input.assetsDir, input.extractImages, warnings);
|
|
131
|
+
if (input.preservePageMarkers && !/\f/u.test(text)) {
|
|
132
|
+
warnings.push({
|
|
133
|
+
code: "METADATA_PARTIALLY_EXTRACTED",
|
|
134
|
+
message: "the extractor reported no page boundaries for this document",
|
|
135
|
+
backend: this.name,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
markdown: text,
|
|
140
|
+
assets,
|
|
141
|
+
...(pages === undefined ? {} : { pages }),
|
|
142
|
+
backend: backendInfo(this.name, serviceVersion),
|
|
143
|
+
warnings,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Write inline images out as files and rewrite the Markdown to reference
|
|
148
|
+
* them relatively. Images beyond the configured cap are dropped with a
|
|
149
|
+
* warning rather than silently inflating the artifact (§25).
|
|
150
|
+
*/
|
|
151
|
+
async materializeAssets(markdown, assetsDir, extractImages, warnings) {
|
|
152
|
+
const matches = [...markdown.matchAll(DATA_URI_PATTERN)];
|
|
153
|
+
if (matches.length === 0) {
|
|
154
|
+
if (extractImages && markdown.includes("data:image/")) {
|
|
155
|
+
warnings.push({
|
|
156
|
+
code: "IMAGE_SKIPPED",
|
|
157
|
+
message: "images reported by the extractor could not be decoded",
|
|
158
|
+
backend: this.name,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return { text: markdown, assets: [] };
|
|
162
|
+
}
|
|
163
|
+
await mkdir(assetsDir, { recursive: true });
|
|
164
|
+
const assets = [];
|
|
165
|
+
const replacements = new Map();
|
|
166
|
+
let index = 0;
|
|
167
|
+
let skipped = 0;
|
|
168
|
+
const { writeFile } = await import("node:fs/promises");
|
|
169
|
+
for (const match of matches) {
|
|
170
|
+
const [full, alt = "", mime = "png", base64 = ""] = match;
|
|
171
|
+
if (index >= this.options.maxImages) {
|
|
172
|
+
skipped += 1;
|
|
173
|
+
replacements.set(full, alt);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const extension = EXTENSION_BY_MIME[mime.toLowerCase()] ?? "bin";
|
|
177
|
+
const fileName = `image-${String(index + 1).padStart(3, "0")}.${extension}`;
|
|
178
|
+
const payload = Buffer.from(base64.replace(/\s+/gu, ""), "base64");
|
|
179
|
+
if (payload.length === 0) {
|
|
180
|
+
skipped += 1;
|
|
181
|
+
replacements.set(full, alt);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
await writeFile(path.join(assetsDir, fileName), payload);
|
|
185
|
+
assets.push({
|
|
186
|
+
path: fileName,
|
|
187
|
+
mediaType: `image/${extension === "jpg" ? "jpeg" : extension}`,
|
|
188
|
+
});
|
|
189
|
+
replacements.set(full, ``);
|
|
190
|
+
index += 1;
|
|
191
|
+
}
|
|
192
|
+
let text = markdown;
|
|
193
|
+
for (const [from, to] of replacements) {
|
|
194
|
+
text = text.split(from).join(to);
|
|
195
|
+
}
|
|
196
|
+
if (skipped > 0) {
|
|
197
|
+
warnings.push({
|
|
198
|
+
code: "IMAGE_SKIPPED",
|
|
199
|
+
message: `${skipped} image(s) were left out: the configured image budget is ${this.options.maxImages}`,
|
|
200
|
+
backend: this.name,
|
|
201
|
+
details: { skipped },
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (!extractImages && assets.length > 0) {
|
|
205
|
+
warnings.push({
|
|
206
|
+
code: "IMAGE_SKIPPED",
|
|
207
|
+
message: "extracted images were written anyway; extraction asked for none",
|
|
208
|
+
backend: this.name,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return { text, assets };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** Read the Markdown out of a docling-serve response, in either spelling. */
|
|
215
|
+
export function readDoclingPayload(payload, backend) {
|
|
216
|
+
if (payload === null || typeof payload !== "object") {
|
|
217
|
+
throw new DocumentError("EXTRACTION_FAILED", "the extractor returned no document", {
|
|
218
|
+
backend,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const root = payload;
|
|
222
|
+
const status = root["status"];
|
|
223
|
+
if (typeof status === "string" && status.toLowerCase() !== "success") {
|
|
224
|
+
const errors = Array.isArray(root["errors"])
|
|
225
|
+
? root["errors"]
|
|
226
|
+
.map((entry) => sanitizeBackendOutput(JSON.stringify(entry), 300))
|
|
227
|
+
.join("; ")
|
|
228
|
+
: "";
|
|
229
|
+
throw new DocumentError("EXTRACTION_FAILED", `the extractor reported "${status}"${errors === "" ? "" : `: ${errors}`}`, { backend, details: { status } });
|
|
230
|
+
}
|
|
231
|
+
const document = root["document"] !== null && typeof root["document"] === "object"
|
|
232
|
+
? root["document"]
|
|
233
|
+
: root;
|
|
234
|
+
const markdown = document["md_content"] ?? document["markdown"];
|
|
235
|
+
if (typeof markdown !== "string") {
|
|
236
|
+
throw new DocumentError("EXTRACTION_FAILED", "the extractor returned no Markdown content", { backend });
|
|
237
|
+
}
|
|
238
|
+
const pagesSource = document["page_count"] ?? document["pages"] ?? root["page_count"];
|
|
239
|
+
const pages = typeof pagesSource === "number" ? pagesSource : undefined;
|
|
240
|
+
const version = root["version"] ?? document["version"];
|
|
241
|
+
return {
|
|
242
|
+
markdown,
|
|
243
|
+
...(pages === undefined ? {} : { pages }),
|
|
244
|
+
...(typeof version === "string" ? { serviceVersion: version } : {}),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=docling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docling.js","sourceRoot":"","sources":["../../../src/documents/providers/docling.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LibreOffice provider — DOCX → PDF (§6.1, §14.1, §46).
|
|
3
|
+
*
|
|
4
|
+
* Two details matter beyond "call the converter":
|
|
5
|
+
*
|
|
6
|
+
* - every invocation gets its own `-env:UserInstallation` profile, because
|
|
7
|
+
* concurrent headless LibreOffice processes sharing one profile corrupt each
|
|
8
|
+
* other's state; the temp profile is removed afterwards on success and on
|
|
9
|
+
* failure alike;
|
|
10
|
+
* - a conversion that produced no file, timed out, or died is reported as a
|
|
11
|
+
* failure of this step only. The orchestrator keeps whatever earlier steps
|
|
12
|
+
* already produced (§45).
|
|
13
|
+
*/
|
|
14
|
+
import { mkdtemp, readdir, rm, stat } from "node:fs/promises";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { sha256OfFile } from "../artifacts/store.js";
|
|
18
|
+
import { DocumentError } from "../errors.js";
|
|
19
|
+
import { expectProcessOk, runProcess } from "./process.js";
|
|
20
|
+
import { backendInfo, parseVersionOutput, verifyOutput } from "./shared.js";
|
|
21
|
+
import { toFileUri } from "../security/paths.js";
|
|
22
|
+
const VERSION_TIMEOUT_MS = 20_000;
|
|
23
|
+
const MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
24
|
+
/** Flags that keep a headless conversion from touching shared user state. */
|
|
25
|
+
const HEADLESS_FLAGS = [
|
|
26
|
+
"--headless",
|
|
27
|
+
"--invisible",
|
|
28
|
+
"--norestore",
|
|
29
|
+
"--nolockcheck",
|
|
30
|
+
"--nodefault",
|
|
31
|
+
"--nofirststartwizard",
|
|
32
|
+
"--nologo",
|
|
33
|
+
];
|
|
34
|
+
export class LibreOfficePdfConverter {
|
|
35
|
+
options;
|
|
36
|
+
name = "libreoffice";
|
|
37
|
+
cachedVersion;
|
|
38
|
+
constructor(options) {
|
|
39
|
+
this.options = options;
|
|
40
|
+
}
|
|
41
|
+
supports(source, target) {
|
|
42
|
+
return source === "docx" && target === "pdf";
|
|
43
|
+
}
|
|
44
|
+
async version() {
|
|
45
|
+
this.cachedVersion ??= this.probeVersion();
|
|
46
|
+
return await this.cachedVersion;
|
|
47
|
+
}
|
|
48
|
+
async probeVersion() {
|
|
49
|
+
if (this.options.versionProbe !== undefined)
|
|
50
|
+
return await this.options.versionProbe();
|
|
51
|
+
try {
|
|
52
|
+
const result = await runProcess(this.options.executable, ["--version"], {
|
|
53
|
+
timeoutMs: VERSION_TIMEOUT_MS,
|
|
54
|
+
maxStdoutBytes: 8 * 1024,
|
|
55
|
+
backend: this.name,
|
|
56
|
+
context: "reporting its version",
|
|
57
|
+
...(this.options.programPrefixArgs === undefined
|
|
58
|
+
? {}
|
|
59
|
+
: { programPrefixArgs: this.options.programPrefixArgs }),
|
|
60
|
+
});
|
|
61
|
+
if (result.exitCode !== 0)
|
|
62
|
+
return undefined;
|
|
63
|
+
return parseVersionOutput(result.stdout, this.name);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async convert(input) {
|
|
70
|
+
const outputDir = path.dirname(input.outputPath);
|
|
71
|
+
const profile = await mkdtemp(path.join(this.options.profileRoot ?? tmpdir(), "qa-libreoffice-"));
|
|
72
|
+
try {
|
|
73
|
+
const argv = [
|
|
74
|
+
...HEADLESS_FLAGS,
|
|
75
|
+
`-env:UserInstallation=${toFileUri(profile)}`,
|
|
76
|
+
"--convert-to",
|
|
77
|
+
"pdf",
|
|
78
|
+
"--outdir",
|
|
79
|
+
outputDir,
|
|
80
|
+
input.inputPath,
|
|
81
|
+
];
|
|
82
|
+
const result = await runProcess(this.options.executable, argv, {
|
|
83
|
+
cwd: input.workDir,
|
|
84
|
+
timeoutMs: this.options.timeoutMs,
|
|
85
|
+
maxStdoutBytes: MAX_OUTPUT_BYTES,
|
|
86
|
+
backend: this.name,
|
|
87
|
+
context: "converting DOCX to PDF",
|
|
88
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
89
|
+
...(this.options.programPrefixArgs === undefined
|
|
90
|
+
? {}
|
|
91
|
+
: { programPrefixArgs: this.options.programPrefixArgs }),
|
|
92
|
+
});
|
|
93
|
+
expectProcessOk(result, {
|
|
94
|
+
backend: this.name,
|
|
95
|
+
context: "converting DOCX to PDF",
|
|
96
|
+
fallbackCode: "CONVERSION_FAILED",
|
|
97
|
+
});
|
|
98
|
+
const produced = await this.locateOutput(outputDir, input.outputPath);
|
|
99
|
+
const verified = await verifyOutput(produced, this.name, "CONVERSION_FAILED");
|
|
100
|
+
return {
|
|
101
|
+
path: verified.path,
|
|
102
|
+
size: verified.size,
|
|
103
|
+
sha256: await sha256OfFile(verified.path),
|
|
104
|
+
backend: backendInfo(this.name, await this.version()),
|
|
105
|
+
warnings: [],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
await rm(profile, { recursive: true, force: true }).catch(() => undefined);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* LibreOffice derives the output name from the input, so the expected path
|
|
114
|
+
* is a prediction. When it is absent exactly one PDF in the output directory
|
|
115
|
+
* is accepted; anything else is a failure rather than a guess.
|
|
116
|
+
*/
|
|
117
|
+
async locateOutput(outputDir, expected) {
|
|
118
|
+
const details = await stat(expected).catch(() => undefined);
|
|
119
|
+
if (details?.isFile() === true)
|
|
120
|
+
return expected;
|
|
121
|
+
const entries = await readdir(outputDir).catch(() => []);
|
|
122
|
+
const pdfs = entries.filter((entry) => entry.toLowerCase().endsWith(".pdf"));
|
|
123
|
+
if (pdfs.length === 1)
|
|
124
|
+
return path.join(outputDir, pdfs[0] ?? "");
|
|
125
|
+
throw new DocumentError("CONVERSION_FAILED", pdfs.length === 0
|
|
126
|
+
? "the PDF converter reported success but produced no PDF"
|
|
127
|
+
: "the PDF converter produced an unexpected number of PDF files", { backend: this.name, details: { candidates: pdfs.length } });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=libreoffice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libreoffice.js","sourceRoot":"","sources":["../../../src/documents/providers/libreoffice.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarkItDown provider — the optional fast extractor (§6.2, §9).
|
|
3
|
+
*
|
|
4
|
+
* Kept behind `documents.markitdown.enabled` (off by default): it is a
|
|
5
|
+
* convenient fallback when Docling is unreachable and high-fidelity parsing is
|
|
6
|
+
* not needed, but it is not the extractor a QA report should be built on.
|
|
7
|
+
*/
|
|
8
|
+
import { DocumentError } from "../errors.js";
|
|
9
|
+
import { expectProcessOk, runProcess } from "./process.js";
|
|
10
|
+
import { backendInfo, parseVersionOutput } from "./shared.js";
|
|
11
|
+
const VERSION_TIMEOUT_MS = 10_000;
|
|
12
|
+
export class MarkItDownExtractor {
|
|
13
|
+
options;
|
|
14
|
+
name = "markitdown";
|
|
15
|
+
cachedVersion;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
}
|
|
19
|
+
supports(format) {
|
|
20
|
+
return format === "docx" || format === "pdf";
|
|
21
|
+
}
|
|
22
|
+
async version() {
|
|
23
|
+
this.cachedVersion ??= this.probeVersion();
|
|
24
|
+
return await this.cachedVersion;
|
|
25
|
+
}
|
|
26
|
+
async probeVersion() {
|
|
27
|
+
if (this.options.versionProbe !== undefined)
|
|
28
|
+
return await this.options.versionProbe();
|
|
29
|
+
try {
|
|
30
|
+
const result = await runProcess(this.options.executable, ["--version"], {
|
|
31
|
+
timeoutMs: VERSION_TIMEOUT_MS,
|
|
32
|
+
maxStdoutBytes: 8 * 1024,
|
|
33
|
+
backend: this.name,
|
|
34
|
+
context: "reporting its version",
|
|
35
|
+
});
|
|
36
|
+
if (result.exitCode !== 0)
|
|
37
|
+
return undefined;
|
|
38
|
+
return parseVersionOutput(result.stdout, this.name);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async health() {
|
|
45
|
+
try {
|
|
46
|
+
return (await this.version()) === undefined ? "unavailable" : "ok";
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return "unavailable";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async extract(input) {
|
|
53
|
+
const result = await runProcess(this.options.executable, [input.inputPath], {
|
|
54
|
+
cwd: input.workDir,
|
|
55
|
+
timeoutMs: this.options.timeoutMs,
|
|
56
|
+
maxStdoutBytes: this.options.maxStdoutBytes,
|
|
57
|
+
backend: this.name,
|
|
58
|
+
context: "extracting Markdown",
|
|
59
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
60
|
+
...(this.options.programPrefixArgs === undefined
|
|
61
|
+
? {}
|
|
62
|
+
: { programPrefixArgs: this.options.programPrefixArgs }),
|
|
63
|
+
});
|
|
64
|
+
expectProcessOk(result, {
|
|
65
|
+
backend: this.name,
|
|
66
|
+
context: "extracting Markdown",
|
|
67
|
+
fallbackCode: "EXTRACTION_FAILED",
|
|
68
|
+
});
|
|
69
|
+
const text = result.stdout.trimStart();
|
|
70
|
+
if (text.trim() === "") {
|
|
71
|
+
throw new DocumentError("EXTRACTION_FAILED", "the extractor produced no text for this document", { backend: this.name });
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
markdown: text,
|
|
75
|
+
assets: [],
|
|
76
|
+
backend: backendInfo(this.name, await this.version()),
|
|
77
|
+
warnings: input.extractImages
|
|
78
|
+
? [
|
|
79
|
+
{
|
|
80
|
+
code: "IMAGE_SKIPPED",
|
|
81
|
+
message: "this extractor does not report images; the response carries text only",
|
|
82
|
+
backend: this.name,
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
: [],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=markitdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markitdown.js","sourceRoot":"","sources":["../../../src/documents/providers/markitdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pandoc provider (§6.1, §13, §14.2).
|
|
3
|
+
*
|
|
4
|
+
* The command line is assembled here and nowhere else: the agent supplies
|
|
5
|
+
* content, a template *name* and a handful of enumerated options, and this
|
|
6
|
+
* module turns them into argv. There is no path through the pipeline by which
|
|
7
|
+
* a caller can add `--lua-filter`, an arbitrary `--resource-path`, or a
|
|
8
|
+
* `--pdf-engine-opt` (§26.1).
|
|
9
|
+
*/
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { sha256OfFile } from "../artifacts/store.js";
|
|
12
|
+
import { DocumentError } from "../errors.js";
|
|
13
|
+
import { expectProcessOk, runProcess } from "./process.js";
|
|
14
|
+
import { backendInfo, metadataArguments, parseVersionOutput, verifyOutput, } from "./shared.js";
|
|
15
|
+
const VERSION_TIMEOUT_MS = 10_000;
|
|
16
|
+
const MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
17
|
+
function spawnPandoc(options, argv, context) {
|
|
18
|
+
return runProcess(options.executable, argv, {
|
|
19
|
+
cwd: context.cwd,
|
|
20
|
+
timeoutMs: options.timeoutMs,
|
|
21
|
+
maxStdoutBytes: MAX_OUTPUT_BYTES,
|
|
22
|
+
backend: context.backend,
|
|
23
|
+
context: context.action,
|
|
24
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
25
|
+
...(options.programPrefixArgs === undefined
|
|
26
|
+
? {}
|
|
27
|
+
: { programPrefixArgs: options.programPrefixArgs }),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function probeVersion(options, backend) {
|
|
31
|
+
if (options.versionProbe !== undefined)
|
|
32
|
+
return await options.versionProbe();
|
|
33
|
+
try {
|
|
34
|
+
const result = await runProcess(options.executable, ["--version"], {
|
|
35
|
+
timeoutMs: VERSION_TIMEOUT_MS,
|
|
36
|
+
maxStdoutBytes: 8 * 1024,
|
|
37
|
+
backend,
|
|
38
|
+
context: "reporting its version",
|
|
39
|
+
...(options.programPrefixArgs === undefined
|
|
40
|
+
? {}
|
|
41
|
+
: { programPrefixArgs: options.programPrefixArgs }),
|
|
42
|
+
});
|
|
43
|
+
if (result.exitCode !== 0)
|
|
44
|
+
return undefined;
|
|
45
|
+
return parseVersionOutput(result.stdout, backend);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Pandoc renders Markdown into DOCX, optionally through a reference document. */
|
|
52
|
+
export class PandocDocxRenderer {
|
|
53
|
+
options;
|
|
54
|
+
name = "pandoc";
|
|
55
|
+
cachedVersion;
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
}
|
|
59
|
+
async version() {
|
|
60
|
+
this.cachedVersion ??= probeVersion(this.options, this.name);
|
|
61
|
+
return await this.cachedVersion;
|
|
62
|
+
}
|
|
63
|
+
async render(input) {
|
|
64
|
+
const argv = [
|
|
65
|
+
input.sourcePath,
|
|
66
|
+
"--from=gfm",
|
|
67
|
+
"--to=docx",
|
|
68
|
+
`--output=${input.outputPath}`,
|
|
69
|
+
...(input.referenceDocPath === undefined
|
|
70
|
+
? []
|
|
71
|
+
: [`--reference-doc=${input.referenceDocPath}`]),
|
|
72
|
+
...(input.assetsDir === undefined
|
|
73
|
+
? []
|
|
74
|
+
: [`--resource-path=${input.assetsDir}`]),
|
|
75
|
+
...(input.toc === true ? ["--toc", "--toc-depth=3"] : []),
|
|
76
|
+
...(input.title === undefined ? [] : [`--metadata=title=${input.title}`]),
|
|
77
|
+
...metadataArguments(input.metadata),
|
|
78
|
+
];
|
|
79
|
+
const result = await spawnPandoc(this.options, argv, {
|
|
80
|
+
cwd: input.workDir,
|
|
81
|
+
backend: this.name,
|
|
82
|
+
action: "rendering DOCX",
|
|
83
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
84
|
+
});
|
|
85
|
+
if (result.truncated) {
|
|
86
|
+
throw new DocumentError("RENDER_FAILED", "the DOCX renderer produced more output than the pipeline accepts", { backend: this.name });
|
|
87
|
+
}
|
|
88
|
+
expectProcessOk(result, {
|
|
89
|
+
backend: this.name,
|
|
90
|
+
context: "rendering DOCX",
|
|
91
|
+
fallbackCode: "RENDER_FAILED",
|
|
92
|
+
});
|
|
93
|
+
const verified = await verifyOutput(input.outputPath, this.name, "RENDER_FAILED");
|
|
94
|
+
return {
|
|
95
|
+
path: verified.path,
|
|
96
|
+
size: verified.size,
|
|
97
|
+
sha256: await sha256OfFile(verified.path),
|
|
98
|
+
backend: backendInfo(this.name, await this.version()),
|
|
99
|
+
warnings: [],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Pandoc-driven PDF renderer. The only engine this pipeline enables is Typst,
|
|
105
|
+
* so the provider exists for `pdfMode: "typst"` alone (§14.2); the office route
|
|
106
|
+
* renders DOCX first and converts it with LibreOffice.
|
|
107
|
+
*/
|
|
108
|
+
export class PandocTypstPdfRenderer {
|
|
109
|
+
options;
|
|
110
|
+
name = "typst";
|
|
111
|
+
cachedVersion;
|
|
112
|
+
constructor(options) {
|
|
113
|
+
this.options = options;
|
|
114
|
+
}
|
|
115
|
+
async version() {
|
|
116
|
+
this.cachedVersion ??= probeVersion(this.options, this.name);
|
|
117
|
+
return await this.cachedVersion;
|
|
118
|
+
}
|
|
119
|
+
async render(input) {
|
|
120
|
+
const argv = [
|
|
121
|
+
input.sourcePath,
|
|
122
|
+
"--from=gfm",
|
|
123
|
+
"--to=pdf",
|
|
124
|
+
"--standalone",
|
|
125
|
+
"--pdf-engine=typst",
|
|
126
|
+
`--output=${input.outputPath}`,
|
|
127
|
+
...(input.typstTemplateDir === undefined
|
|
128
|
+
? []
|
|
129
|
+
: [`--template=${path.join(input.typstTemplateDir, "main.typ")}`]),
|
|
130
|
+
...(input.assetsDir === undefined
|
|
131
|
+
? []
|
|
132
|
+
: [`--resource-path=${input.assetsDir}`]),
|
|
133
|
+
...(input.pageSize === undefined
|
|
134
|
+
? []
|
|
135
|
+
: [`-V=papersize=${input.pageSize}`]),
|
|
136
|
+
...(input.toc === true ? ["--toc", "--toc-depth=3"] : []),
|
|
137
|
+
...(input.title === undefined ? [] : [`--metadata=title=${input.title}`]),
|
|
138
|
+
...metadataArguments(input.metadata),
|
|
139
|
+
];
|
|
140
|
+
const result = await spawnPandoc(this.options, argv, {
|
|
141
|
+
cwd: input.workDir,
|
|
142
|
+
backend: this.name,
|
|
143
|
+
action: "rendering PDF with Typst",
|
|
144
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
145
|
+
});
|
|
146
|
+
expectProcessOk(result, {
|
|
147
|
+
backend: this.name,
|
|
148
|
+
context: "rendering PDF with Typst",
|
|
149
|
+
fallbackCode: "RENDER_FAILED",
|
|
150
|
+
});
|
|
151
|
+
const verified = await verifyOutput(input.outputPath, this.name, "RENDER_FAILED");
|
|
152
|
+
return {
|
|
153
|
+
path: verified.path,
|
|
154
|
+
size: verified.size,
|
|
155
|
+
sha256: await sha256OfFile(verified.path),
|
|
156
|
+
backend: backendInfo(this.name, await this.version()),
|
|
157
|
+
warnings: [],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=pandoc.js.map
|