oh-my-opencode 5.1.47 → 5.1.51
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/dist/agents/refusal-rule.d.ts +7 -0
- package/dist/cli/index.js +1194 -752
- package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
- package/dist/cli/service-auth/commands.d.ts +2 -4
- package/dist/cli/service-auth/index.d.ts +1 -1
- package/dist/cli/service-auth/keystore.d.ts +26 -2
- package/dist/cli/service-auth/protocol.d.ts +7 -1
- package/dist/cli/service-auth/session.d.ts +6 -0
- package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
- package/dist/cli-node/index.js +1194 -752
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +1 -1
- package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
- package/dist/hooks/atlas/types.d.ts +9 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
- package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
- package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
- package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
- package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
- package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
- package/dist/index.js +2322 -1045
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/posthog-activity-state.d.ts +1 -0
- package/dist/shared/posthog.d.ts +1 -2
- package/dist/shared/resolve-file-write-path.d.ts +6 -0
- package/dist/skills/ast-grep/README.md +1 -1
- package/dist/skills/ast-grep/install.ps1 +5 -3
- package/dist/skills/ast-grep/install.sh +4 -5
- package/dist/skills/ast-grep/references/install.md +2 -2
- package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/dist/skills/frontend/ATTRIBUTION.md +1 -1
- package/dist/skills/frontend/SKILL.md +34 -12
- package/dist/skills/frontend/references/design/README.md +2 -2
- package/dist/skills/frontend/references/design/_INDEX.md +2 -1
- package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
- package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/dist/skills/frontend/references/design/grounding.md +29 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/dist/skills/ultimate-browsing/SKILL.md +5 -4
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/dist/skills/ulw-research/AGENTS.md +8 -4
- package/dist/skills/ulw-research/SKILL.md +7 -259
- package/dist/skills/ulw-research/references/deliverable-phase.md +68 -24
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
- package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/dist/skills/ulw-research/references/report-gates.md +14 -6
- package/dist/skills/ulw-research/references/research-collection.md +195 -0
- package/dist/skills/ulw-research/references/research-delivery.md +123 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/dist/skills/ulw-research/scripts/contracts.mjs +16 -15
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -109
- package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/dist/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
- package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
- package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
- package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/dist/skills/ulw-research/scripts/fact-fill.mjs +9 -6
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
- package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/dist/skills/ulw-research/scripts/report-tools.test.ts +6 -560
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +87 -29
- package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/dist/skills/ulw-research/scripts/units.mjs +14 -3
- package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
- package/dist/tools/delegate-task/types.d.ts +1 -0
- package/dist/tools/skill/description-formatter.d.ts +2 -1
- package/dist/tools/skill/types.d.ts +6 -0
- package/dist/tui.js +131 -33
- package/package.json +18 -15
- package/packages/git-bash-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/README.md +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
- package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
- package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
- package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +16 -16
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/shared/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
- package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
- package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
- package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -259
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +68 -24
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +14 -6
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +16 -15
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +9 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +87 -29
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
- package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
- package/packages/shared-skills/skill-source-filter.mjs +6 -2
- package/packages/shared-skills/skills/ast-grep/README.md +1 -1
- package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
- package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
- package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
- package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
- package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
- package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -259
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +68 -24
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +14 -6
- package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +16 -15
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -109
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
- package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +9 -6
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +6 -560
- package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +87 -29
- package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
// source-commands.mjs - the `source snapshot` and `claims index` subcommands of
|
|
2
|
-
// A source dir is immutable: the same id with identical
|
|
3
|
-
// (exit 1, snapshot_exists), because ledger rows pin the old hash
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
1
|
+
// source-commands.mjs - the `source snapshot`, `source refresh` and `claims index` subcommands of
|
|
2
|
+
// report-tools. A source dir is immutable: the same id with identical bytes is a no-op, different
|
|
3
|
+
// bytes fail (exit 1, snapshot_exists), because ledger rows pin the old hash; `source refresh`
|
|
4
|
+
// (source-refresh.mjs) is the one explicit repin. `--replace` swaps in a corrected capture under the
|
|
5
|
+
// same id (the conversion_altered_number recovery) only while no active ledger row cites the id
|
|
6
|
+
// (exit 1, snapshot_pinned, listing them). `--url` fetches with plain fetch (30 s timeout,
|
|
7
|
+
// no browser) and records extractor tool:source-snapshot@1; `--html` imports a browser-lane page
|
|
8
|
+
// capture and `--raw` alone a saved file's bytes (extractor model). `--text` alone registers a converted-only source (no raw bytes:
|
|
9
|
+
// quotes only); `--raw <bytes> --text <extraction> --extractor NAME@VERSION` keeps the original
|
|
10
|
+
// bytes and declares the text their extraction. `--command` runs an argv without a shell and
|
|
7
11
|
// snapshots its stdout, with the argv, cwd, exit status, and output hashes in command.json.
|
|
8
12
|
// node: builtins and sibling imports only.
|
|
9
13
|
|
|
@@ -13,13 +17,18 @@ import { join, resolve } from "node:path"
|
|
|
13
17
|
|
|
14
18
|
import { indexClaims } from "./claims-index.mjs"
|
|
15
19
|
import { CliError, parseArgs, sha256Of, writeJsonAtomic } from "./cli-support.mjs"
|
|
20
|
+
import { runConversionProbe } from "./conversion-probe.mjs"
|
|
21
|
+
import { readLedger } from "./fact-ledger.mjs"
|
|
16
22
|
import { parseFidelityBlock } from "./fidelity-brief.mjs"
|
|
23
|
+
import { parseSourceFloor } from "./fidelity-policy.mjs"
|
|
24
|
+
import { encodingOf, headerCharset } from "./raw-decode.mjs"
|
|
17
25
|
import { writeSnapshot } from "./source-snapshot.mjs"
|
|
26
|
+
import { readSnapshot } from "./source-view.mjs"
|
|
18
27
|
|
|
19
28
|
const FETCH_TIMEOUT_MS = 30_000
|
|
20
29
|
const TOOL_EXTRACTOR = "tool:source-snapshot@1"
|
|
21
|
-
const SNAPSHOT_USAGE =
|
|
22
|
-
"usage: source snapshot --id S<n> (--url <u> | --html <file> | --
|
|
30
|
+
export const SNAPSHOT_USAGE =
|
|
31
|
+
"usage: source probe-conversion <raw-file> <converted-file> | source snapshot --id S<n> (--url <u> | --html <file> | --raw <file> [--text <file> --extractor NAME@VERSION] | --text <file> (converted-only) | --command '<argv json>' --cwd DIR) [--charset NAME] [--kind page|command|document|converted-only] [--provenance primary|secondary] [--revision R] [--replace] --session-dir D"
|
|
23
32
|
|
|
24
33
|
function readInput(path) {
|
|
25
34
|
try {
|
|
@@ -36,20 +45,27 @@ export function sessionDirOf(flags) {
|
|
|
36
45
|
return resolve(dir)
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
/** The snapshot
|
|
48
|
+
/** The snapshot as source-view.mjs reads it ({ meta, raw, sha256, drift, view }); unreadable is a usage error. */
|
|
40
49
|
export function snapshotOf(sessionDir, id) {
|
|
41
50
|
const dir = join(sessionDir, "sources", String(id))
|
|
42
51
|
if (!existsSync(join(dir, "meta.json"))) throw new CliError(`source ${id} has no snapshot; run source snapshot --id ${id} first`)
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
try {
|
|
53
|
+
return readSnapshot(dir)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw new CliError(`source ${id}: ${error.message}`)
|
|
56
|
+
}
|
|
45
57
|
}
|
|
46
58
|
|
|
47
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Whether the brief's source floor lists this source as pointed at (`pointed only: a, b`). An absent
|
|
61
|
+
* or pending floor points at nothing; a malformed floor is a usage error (parseSourceFloor's CliError).
|
|
62
|
+
*/
|
|
48
63
|
export function pointedFlag(sessionDir, url) {
|
|
49
64
|
const brief = join(sessionDir, "brief.md")
|
|
50
65
|
const floor = existsSync(brief) ? (parseFidelityBlock(readInput(brief)).fields?.["source floor"] ?? "") : ""
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
if (floor === "" || floor === "pending") return false
|
|
67
|
+
const rule = parseSourceFloor(floor)
|
|
68
|
+
return rule?.kind === "pointed" && rule.list.includes(url)
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
async function fetchSource(url) {
|
|
@@ -67,10 +83,10 @@ async function fetchSource(url) {
|
|
|
67
83
|
const type = response.headers.get("content-type") ?? ""
|
|
68
84
|
if (/pdf/i.test(type)) {
|
|
69
85
|
await response.body?.cancel()
|
|
70
|
-
throw new CliError(`pdf response: ${url};
|
|
86
|
+
throw new CliError(`pdf response: ${url}; save the bytes and use --raw <file.pdf> --text <extracted.txt> --extractor NAME@VERSION`)
|
|
71
87
|
}
|
|
72
|
-
const
|
|
73
|
-
return /html|xml/i.test(type) || /^\s*</.test(
|
|
88
|
+
const raw = Buffer.from(await response.arrayBuffer())
|
|
89
|
+
return { url, raw, charset: headerCharset(type), markup: /html|xml/i.test(type) || /^\s*</.test(raw.subarray(0, 1024).toString("latin1")) }
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
function runCommand(argvJson, cwd) {
|
|
@@ -84,35 +100,76 @@ function runCommand(argvJson, cwd) {
|
|
|
84
100
|
throw new CliError("--command must be a non-empty JSON array of non-empty strings")
|
|
85
101
|
}
|
|
86
102
|
if (!cwd || cwd === true) throw new CliError("--command needs --cwd DIR")
|
|
87
|
-
const proc = spawnSync(argv[0], argv.slice(1), { cwd: resolve(cwd),
|
|
103
|
+
const proc = spawnSync(argv[0], argv.slice(1), { cwd: resolve(cwd), shell: false, maxBuffer: 64 * 1024 * 1024 })
|
|
88
104
|
if (proc.error) throw new CliError(`--command could not start: ${proc.error.message}`)
|
|
89
105
|
const receipt = { argv, cwd: resolve(cwd), exit_status: proc.status, stdout_sha256: sha256Of(proc.stdout), stderr_sha256: sha256Of(proc.stderr) }
|
|
90
|
-
return { path: `command:${argv.join(" ")}`,
|
|
106
|
+
return { path: `command:${argv.join(" ")}`, raw: proc.stdout, kind: "command", receipt }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function readBytes(path) {
|
|
110
|
+
try {
|
|
111
|
+
return readFileSync(path)
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throw new CliError(`cannot read ${path}: ${error.code ?? error.message}`)
|
|
114
|
+
}
|
|
91
115
|
}
|
|
92
116
|
|
|
93
|
-
|
|
117
|
+
/** The snapshot input the flags describe; shared by `source snapshot` and `source refresh`. */
|
|
118
|
+
export async function acquire(flags) {
|
|
94
119
|
const given = ["url", "html", "text", "command"].filter((name) => flags[name] !== undefined)
|
|
95
|
-
if (given.
|
|
120
|
+
if (flags.raw !== undefined && given.some((name) => name !== "text")) throw new CliError("--raw goes alone or with --text (--url, --html and --command already keep the raw bytes)")
|
|
121
|
+
if (given.length + (flags.raw !== undefined && flags.text === undefined ? 1 : 0) !== 1) throw new CliError(SNAPSHOT_USAGE)
|
|
96
122
|
if (flags.extractor !== undefined && flags.text === undefined) throw new CliError("--extractor goes with --text only")
|
|
97
|
-
if (flags.
|
|
98
|
-
|
|
99
|
-
if (flags.
|
|
123
|
+
if (flags.charset !== undefined && encodingOf(flags.charset) === null) throw new CliError(`--charset ${flags.charset} is not a known encoding`)
|
|
124
|
+
const charset = flags.charset === undefined ? {} : { charset: encodingOf(flags.charset) }
|
|
125
|
+
if (flags.url !== undefined) return { ...(await fetchSource(flags.url)), ...charset, extractor: TOOL_EXTRACTOR }
|
|
126
|
+
if (flags.html !== undefined) return { path: resolve(flags.html), raw: readBytes(flags.html), markup: true, ...charset, extractor: "model" }
|
|
127
|
+
if (flags.raw !== undefined && flags.text === undefined) return { path: resolve(flags.raw), raw: readBytes(flags.raw), ...charset, extractor: "model" }
|
|
128
|
+
if (flags.text !== undefined) {
|
|
129
|
+
const extractor = flags.extractor === undefined ? "model" : `extractor:${flags.extractor}`
|
|
130
|
+
if (flags.raw !== undefined) return { path: resolve(flags.raw), raw: readBytes(flags.raw), text: readInput(flags.text), ...charset, extractor }
|
|
131
|
+
if (flags.kind !== undefined && flags.kind !== "converted-only") throw new CliError("--text without --raw registers a converted-only source; pass --raw <original bytes> to keep a measurable snapshot")
|
|
132
|
+
return { path: resolve(flags.text), text: readInput(flags.text), kind: "converted-only", extractor }
|
|
133
|
+
}
|
|
100
134
|
return { ...runCommand(flags.command, flags.cwd), extractor: TOOL_EXTRACTOR }
|
|
101
135
|
}
|
|
102
136
|
|
|
137
|
+
/**
|
|
138
|
+
* `--replace` with `--text` alone (the conversion_altered_number recovery) keeps the source's raw.bin and
|
|
139
|
+
* declares the verified text its extraction, so value rows locate in text.txt while the original bytes stay
|
|
140
|
+
* pinned; plain `--text` without `--replace`, or over an id with no raw.bin, stays converted-only.
|
|
141
|
+
*/
|
|
142
|
+
function declareOverKeptRaw(sourcesDir, id, input) {
|
|
143
|
+
const dir = join(sourcesDir, String(id))
|
|
144
|
+
if (input.kind !== "converted-only" || !existsSync(join(dir, "raw.bin")) || !existsSync(join(dir, "meta.json"))) return
|
|
145
|
+
const before = JSON.parse(readFileSync(join(dir, "meta.json"), "utf8"))
|
|
146
|
+
Object.assign(input, { url: before.url, raw: readFileSync(join(dir, "raw.bin")), kind: before.kind, declared: true })
|
|
147
|
+
delete input.path
|
|
148
|
+
}
|
|
149
|
+
|
|
103
150
|
export async function runSource(argv) {
|
|
104
151
|
const [sub, ...rest] = argv
|
|
152
|
+
if (sub === "refresh") return (await import("./source-refresh.mjs")).runRefresh(rest)
|
|
153
|
+
if (sub === "probe-conversion") return runConversionProbe(rest)
|
|
105
154
|
if (sub !== "snapshot") throw new CliError(SNAPSHOT_USAGE)
|
|
106
|
-
const args = parseArgs(rest)
|
|
155
|
+
const args = parseArgs(rest, { booleans: ["replace"] })
|
|
107
156
|
const id = args.required("id")
|
|
108
|
-
const
|
|
157
|
+
const sessionDir = sessionDirOf(args.flags)
|
|
158
|
+
const sourcesDir = join(sessionDir, "sources")
|
|
159
|
+
const replace = args.flags.replace === true
|
|
160
|
+
const pinned = replace ? readLedger(sessionDir).rows.filter((row) => row.source_id === id && (row.status ?? "active") === "active").map((row) => row.fact_id) : []
|
|
161
|
+
if (pinned.length > 0) {
|
|
162
|
+
const message = `${id} is cited by active row(s) ${pinned.join(", ")}; retract them first, or snapshot the corrected text under a new id`
|
|
163
|
+
return { json: { command: "source snapshot", status: "fail", exitCode: 1, error: "snapshot_pinned", message, rows: pinned }, summary: `source snapshot: ${message}`, exitCode: 1 }
|
|
164
|
+
}
|
|
109
165
|
const { receipt, ...input } = await acquire(args.flags)
|
|
166
|
+
if (replace) declareOverKeptRaw(sourcesDir, id, input)
|
|
110
167
|
if (args.flags.kind !== undefined) input.kind = args.flags.kind
|
|
111
168
|
if (args.flags.provenance !== undefined) input.provenance = args.flags.provenance
|
|
112
169
|
if (args.flags.revision !== undefined) input.revision = args.flags.revision
|
|
113
170
|
let written
|
|
114
171
|
try {
|
|
115
|
-
written = writeSnapshot(sourcesDir, id, input)
|
|
172
|
+
written = writeSnapshot(sourcesDir, id, input, { replace })
|
|
116
173
|
} catch (error) {
|
|
117
174
|
if (String(error.message).startsWith("snapshot_exists")) {
|
|
118
175
|
return { json: { command: "source snapshot", status: "fail", exitCode: 1, error: "snapshot_exists", message: error.message }, summary: `source snapshot: ${error.message}`, exitCode: 1 }
|
|
@@ -122,8 +179,9 @@ export async function runSource(argv) {
|
|
|
122
179
|
}
|
|
123
180
|
if (receipt) writeFileSync(join(written.dir, "command.json"), `${JSON.stringify(receipt, null, 2)}\n`)
|
|
124
181
|
const meta = JSON.parse(readFileSync(join(written.dir, "meta.json"), "utf8"))
|
|
125
|
-
const data = { source_id: id, directory: written.dir, snapshot_sha256: written.sha256, extractor: meta.extractor, kind: meta.kind, provenance: meta.provenance }
|
|
126
|
-
|
|
182
|
+
const data = { source_id: id, directory: written.dir, snapshot_sha256: written.sha256, extractor: meta.extractor, kind: meta.kind, provenance: meta.provenance, text_source: meta.text_source, ...(written.altered ? { conversion_altered_numbers: written.altered } : {}) }
|
|
183
|
+
const probe = written.altered ? `; conversion ${written.altered.length} altered${written.altered.length ? ` (${written.altered.join(", ")}): fact add refuses those numbers` : ""}` : ""
|
|
184
|
+
return { json: { command: "source snapshot", status: "pass", exitCode: 0, data }, summary: `source snapshot: ${id} ${written.sha256.slice(0, 12)} (${meta.extractor}, ${meta.text_source})${probe}`, exitCode: 0 }
|
|
127
185
|
}
|
|
128
186
|
|
|
129
187
|
export function runClaims(argv) {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// source-refresh.mjs - `source refresh <S<n>>`: the one explicit repin of a source snapshot.
|
|
2
|
+
//
|
|
3
|
+
// Re-fetches meta.url (or reads the new capture given with the source snapshot input flags),
|
|
4
|
+
// replaces the snapshot, and re-anchors every active byte-located ledger row on that source by its
|
|
5
|
+
// exact quote plus its stored context (raw-locator.mjs reanchor): a row found once gets the new
|
|
6
|
+
// snapshot_sha256 and keeps its old locator, so `fact verify` then reports minor `relocated` with
|
|
7
|
+
// the old and new byte offsets; a row whose quote is gone keeps its old pin, is reported as
|
|
8
|
+
// quote_not_verbatim here, and stays a snapshot_hash_mismatch blocker until it is retracted or
|
|
9
|
+
// re-added. A legacy text.txt row on the source (no byte locator) cannot be re-anchored: it is
|
|
10
|
+
// listed as legacy_not_reanchored and stays a blocker. Exit 0 when every row re-anchored, 1 otherwise. node: builtins and sibling imports only.
|
|
11
|
+
|
|
12
|
+
import { writeFileSync } from "node:fs"
|
|
13
|
+
import { join } from "node:path"
|
|
14
|
+
|
|
15
|
+
import { CliError, parseArgs } from "./cli-support.mjs"
|
|
16
|
+
import { DEFECT_CODES } from "./contracts.mjs"
|
|
17
|
+
import { readLedger, writeLedger } from "./fact-ledger.mjs"
|
|
18
|
+
import { reanchor } from "./raw-locator.mjs"
|
|
19
|
+
import { acquire, sessionDirOf, snapshotOf } from "./source-commands.mjs"
|
|
20
|
+
import { writeSnapshot } from "./source-snapshot.mjs"
|
|
21
|
+
|
|
22
|
+
export const REFRESH_USAGE =
|
|
23
|
+
"usage: source refresh S<n> [--url <u> | --html <file> | --raw <file> [--text <file> --extractor NAME@VERSION] | --text <file> | --command '<argv json>' --cwd DIR] [--charset NAME] --session-dir D (no input flag re-fetches the source's http(s) url)"
|
|
24
|
+
|
|
25
|
+
const INPUTS = ["url", "html", "raw", "text", "command"]
|
|
26
|
+
|
|
27
|
+
const defect = (code, message) => ({ code, severity: DEFECT_CODES[code].severity, integrity: DEFECT_CODES[code].integrity, message })
|
|
28
|
+
|
|
29
|
+
export async function runRefresh(argv) {
|
|
30
|
+
const args = parseArgs(argv)
|
|
31
|
+
const id = args.positionals[0] ?? args.flags.id
|
|
32
|
+
if (!id || id === true) throw new CliError(REFRESH_USAGE)
|
|
33
|
+
const sessionDir = sessionDirOf(args.flags)
|
|
34
|
+
const before = snapshotOf(sessionDir, id)
|
|
35
|
+
const flags = { ...args.flags }
|
|
36
|
+
if (!INPUTS.some((name) => flags[name] !== undefined)) {
|
|
37
|
+
if (!/^https?:\/\//i.test(String(before.meta.url))) throw new CliError(`${id} was not fetched from an http(s) url; pass its new capture. ${REFRESH_USAGE}`)
|
|
38
|
+
flags.url = before.meta.url
|
|
39
|
+
}
|
|
40
|
+
const { receipt, ...input } = await acquire(flags)
|
|
41
|
+
input.kind ??= flags.kind ?? before.meta.kind
|
|
42
|
+
input.provenance = flags.provenance ?? before.meta.provenance
|
|
43
|
+
input.revision = flags.revision ?? before.meta.revision ?? undefined
|
|
44
|
+
let written
|
|
45
|
+
try {
|
|
46
|
+
written = writeSnapshot(join(sessionDir, "sources"), id, input, { replace: true })
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (error instanceof TypeError) throw new CliError(error.message)
|
|
49
|
+
throw error
|
|
50
|
+
}
|
|
51
|
+
if (receipt) writeFileSync(join(written.dir, "command.json"), `${JSON.stringify(receipt, null, 2)}\n`)
|
|
52
|
+
const after = snapshotOf(sessionDir, id)
|
|
53
|
+
const ledger = readLedger(sessionDir)
|
|
54
|
+
const rows = []
|
|
55
|
+
const defects = []
|
|
56
|
+
for (const row of ledger.rows) {
|
|
57
|
+
if (row.source_id !== id || row.status !== "active") continue
|
|
58
|
+
if (row.locator?.unit === undefined) {
|
|
59
|
+
rows.push({ fact_id: row.fact_id, status: "legacy_not_reanchored" })
|
|
60
|
+
defects.push(defect("snapshot_hash_mismatch", `${row.fact_id}: a legacy text.txt row has no byte locator to re-anchor on refreshed ${id}; add it again from the new snapshot or retract it`))
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
if (row.locator.unit !== "bytes") continue
|
|
64
|
+
const anchor = row.locator.artifact === after.view.artifact ? reanchor(after.view, row) : null
|
|
65
|
+
const span = `${row.locator.char_start}:${row.locator.char_end}`
|
|
66
|
+
if (!anchor) {
|
|
67
|
+
rows.push({ fact_id: row.fact_id, status: "not_found", from: span })
|
|
68
|
+
defects.push(defect("quote_not_verbatim", `${row.fact_id}: its quote and context are not found once in refreshed ${id}; retract it or add it again from the new snapshot`))
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
row.snapshot_sha256 = after.sha256
|
|
72
|
+
rows.push({ fact_id: row.fact_id, status: anchor.relocated ? "relocated" : "unchanged", from: span, to: `${anchor.start}:${anchor.end}` })
|
|
73
|
+
if (anchor.relocated) defects.push(defect("relocated", `${row.fact_id}: ${span} -> ${anchor.start}:${anchor.end} (bytes of ${id}/${after.view.artifact})`))
|
|
74
|
+
}
|
|
75
|
+
writeLedger(sessionDir, ledger)
|
|
76
|
+
const failed = rows.filter((row) => row.status === "not_found" || row.status === "legacy_not_reanchored").length
|
|
77
|
+
const exitCode = failed > 0 ? 1 : 0
|
|
78
|
+
const data = { source_id: id, previous_sha256: before.sha256, snapshot_sha256: after.sha256, text_source: after.view.mode, rows }
|
|
79
|
+
const json = { command: "source refresh", status: exitCode === 0 ? "pass" : "fail", exitCode, defects, data }
|
|
80
|
+
const moved = rows.filter((row) => row.status === "relocated").length
|
|
81
|
+
return { json, summary: `source refresh: ${id} ${before.sha256.slice(0, 12)} -> ${after.sha256.slice(0, 12)}; ${rows.length - failed} row(s) re-anchored (${moved} relocated), ${failed} not re-anchored`, exitCode }
|
|
82
|
+
}
|
|
@@ -1,77 +1,44 @@
|
|
|
1
1
|
// source-snapshot.mjs - the per-source snapshot that fact locators read.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// writeSnapshot writes <sourcesDir>/<S-id>/ with raw.bin (the fetched or captured bytes, absent
|
|
4
|
+
// only for converted-only sources), meta.json (sha256 of the pinned body, text_source, charset),
|
|
5
|
+
// text.txt (the locator text of source-view.mjs: the visible text nodes of a page, the decoded
|
|
6
|
+
// bytes of a document, or a declared/converted extraction), source.html for pages (decoded, a
|
|
7
|
+
// reading aid), sentences.jsonl (sid S<n>:s<k>, indexed over text.txt) and acronyms.json; a page
|
|
8
|
+
// also gets conversion.json, the conversion probe's record of the numbers its text.txt altered.
|
|
8
9
|
// Plain Node ESM, node: builtins and sibling imports only.
|
|
9
10
|
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
11
12
|
import { join } from "node:path"
|
|
12
13
|
|
|
13
14
|
import { sha256Of } from "./cli-support.mjs"
|
|
14
|
-
import {
|
|
15
|
+
import { probeConversion } from "./conversion-probe.mjs"
|
|
16
|
+
import { writeFidelityRecord } from "./fidelity-records.mjs"
|
|
17
|
+
import { extractHtml } from "./html-text-map.mjs"
|
|
15
18
|
import { splitSentences } from "./sentence-split.mjs"
|
|
19
|
+
import { buildView } from "./source-view.mjs"
|
|
16
20
|
|
|
17
|
-
const SKIP = new Set(["head", "script", "style", "svg", "code", "pre", "kbd", "samp", "template", "noscript"])
|
|
18
|
-
const INLINE = new Set([
|
|
19
|
-
"a", "abbr", "b", "bdi", "bdo", "cite", "data", "del", "dfn", "em", "font", "i", "ins", "label", "mark", "q", "s",
|
|
20
|
-
"small", "span", "strong", "sub", "sup", "time", "u", "var",
|
|
21
|
-
])
|
|
22
21
|
const SOURCE_ID = /^S[1-9]\d*$/
|
|
23
22
|
const EXTRACTOR = /^(?:tool:source-snapshot@1|model|extractor:[\w.@+-]+)$/
|
|
24
|
-
const KINDS = new Set(["page", "command", "document"])
|
|
23
|
+
const KINDS = new Set(["page", "command", "document", "converted-only"])
|
|
25
24
|
const PROVENANCE = new Set(["primary", "secondary", "unknown"])
|
|
26
25
|
const ACRONYM_AFTER = /\(([A-Z]{2,6})\)/g
|
|
27
26
|
const ACRONYM_BEFORE = /(?<![\p{L}\p{N}])([A-Z]{2,6})\s?\(([A-Z][^()\n]{2,79})\)/gu
|
|
28
27
|
const NAME_WORD = /^[A-Z][\p{L}\p{N}-]*$/u
|
|
29
28
|
const CONNECTOR = new Set(["of", "and", "for", "the", "in", "on", "to", "&"])
|
|
29
|
+
const MARKUP = /^\s*</
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Block lines of a page: { root, text, blocks: [{ node, start, end }] } where text
|
|
33
|
-
* texts with "\n"
|
|
32
|
+
* Block lines of a page: { root, text, blocks: [{ node, start, end }], starts, ends } where text
|
|
33
|
+
* joins the visible block texts with "\n" (html-text-map.mjs). fact-locate reads cells through `node`.
|
|
34
34
|
*/
|
|
35
35
|
export function snapshotBlocks(html) {
|
|
36
|
-
|
|
37
|
-
const lines = []
|
|
38
|
-
let buffer = ""
|
|
39
|
-
const flush = (node) => {
|
|
40
|
-
const line = buffer.replace(/\s+/g, " ").trim()
|
|
41
|
-
buffer = ""
|
|
42
|
-
if (line) lines.push({ node, text: line })
|
|
43
|
-
}
|
|
44
|
-
const walk = (node) => {
|
|
45
|
-
for (const child of node.children) {
|
|
46
|
-
if (child.tag === "#text") buffer += child.text
|
|
47
|
-
else if (SKIP.has(child.tag)) continue
|
|
48
|
-
else if (child.tag === "br") flush(node)
|
|
49
|
-
else if (INLINE.has(child.tag)) walk(child)
|
|
50
|
-
else if (PROSE_TAGS.has(child.tag)) {
|
|
51
|
-
flush(node)
|
|
52
|
-
const line = proseText(child)
|
|
53
|
-
if (line) lines.push({ node: child, text: line })
|
|
54
|
-
} else {
|
|
55
|
-
flush(node)
|
|
56
|
-
walk(child)
|
|
57
|
-
flush(child)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
walk(root)
|
|
62
|
-
flush(root)
|
|
63
|
-
const blocks = []
|
|
64
|
-
let at = 0
|
|
65
|
-
for (const line of lines) {
|
|
66
|
-
blocks.push({ node: line.node, start: at, end: at + line.text.length })
|
|
67
|
-
at += line.text.length + 1
|
|
68
|
-
}
|
|
69
|
-
return { root, text: lines.map((line) => line.text).join("\n"), blocks }
|
|
36
|
+
return extractHtml(html)
|
|
70
37
|
}
|
|
71
38
|
|
|
72
39
|
/** text.txt form of an html page. */
|
|
73
40
|
export function snapshotFromHtml(html) {
|
|
74
|
-
return
|
|
41
|
+
return extractHtml(html).text
|
|
75
42
|
}
|
|
76
43
|
|
|
77
44
|
/** Sentence index rows { sid: "S<n>:s<k>", start, end, text }; a form feed (page break) always ends a sentence. */
|
|
@@ -127,36 +94,61 @@ export function findAcronyms(text, sentences) {
|
|
|
127
94
|
function snapshotText(input) {
|
|
128
95
|
if (Array.isArray(input.pages)) return input.pages.join("\f")
|
|
129
96
|
if (typeof input.text === "string") return input.text
|
|
130
|
-
|
|
131
|
-
|
|
97
|
+
throw new TypeError("writeSnapshot needs raw, html, text, or pages")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Which source-view text source the input makes, with the bytes it pins. */
|
|
101
|
+
function bodiesOf(input) {
|
|
102
|
+
if (input.kind === "converted-only") return { mode: "converted", raw: null, textBody: Buffer.from(snapshotText(input)) }
|
|
103
|
+
if (input.declared === true) return { mode: "declared", raw: Buffer.from(input.raw), textBody: Buffer.from(snapshotText(input)) }
|
|
104
|
+
if (input.raw === undefined && typeof input.html === "string") return { mode: "html", raw: Buffer.from(input.html), textBody: null }
|
|
105
|
+
if (input.raw === undefined) return { mode: "decoded", raw: Buffer.from(snapshotText(input)), textBody: null }
|
|
106
|
+
const raw = Buffer.from(input.raw)
|
|
107
|
+
if (input.markup === true || typeof input.html === "string" || input.kind === "page" || MARKUP.test(raw.subarray(0, 1024).toString("latin1").replace(/^\u00ef\u00bb\u00bf/, ""))) return { mode: "html", raw, textBody: null }
|
|
108
|
+
if (input.text !== undefined || input.pages !== undefined) return { mode: "declared", raw, textBody: Buffer.from(snapshotText(input)) }
|
|
109
|
+
return { mode: "decoded", raw, textBody: null }
|
|
132
110
|
}
|
|
133
111
|
|
|
134
112
|
function checkInput(sourceId, input) {
|
|
135
113
|
if (!SOURCE_ID.test(String(sourceId))) throw new TypeError(`source id must be S<n>, got ${sourceId}`)
|
|
136
114
|
if (!EXTRACTOR.test(String(input.extractor))) throw new TypeError(`extractor must be tool:source-snapshot@1, model, or extractor:<name>, got ${input.extractor}`)
|
|
137
115
|
if (typeof (input.url ?? input.path) !== "string") throw new TypeError("writeSnapshot needs a url or a path")
|
|
138
|
-
if (input.kind !== undefined && !KINDS.has(input.kind)) throw new TypeError(`kind must be page, command, or
|
|
116
|
+
if (input.kind !== undefined && !KINDS.has(input.kind)) throw new TypeError(`kind must be page, command, document, or converted-only, got ${input.kind}`)
|
|
139
117
|
if (input.provenance !== undefined && !PROVENANCE.has(input.provenance)) throw new TypeError(`provenance must be primary, secondary, or unknown, got ${input.provenance}`)
|
|
118
|
+
if (input.observed_at !== undefined && (typeof input.observed_at !== "string" || Number.isNaN(new Date(input.observed_at).getTime()))) throw new TypeError(`observed_at must be an ISO timestamp, got ${input.observed_at}`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function pinnedHash(dir) {
|
|
122
|
+
const metaPath = join(dir, "meta.json")
|
|
123
|
+
return existsSync(metaPath) ? JSON.parse(readFileSync(metaPath, "utf8")).sha256 : null
|
|
140
124
|
}
|
|
141
125
|
|
|
142
126
|
/**
|
|
143
|
-
* Write <sourcesDir>/<sourceId>/ for one source; returns { dir, sha256 }
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
127
|
+
* Write <sourcesDir>/<sourceId>/ for one source; returns { dir, sha256, altered } over the pinned body,
|
|
128
|
+
* where altered lists the numbers the page conversion altered (null when no page conversion ran).
|
|
129
|
+
* input: { url | path, raw?, markup?, html?, text? | pages?, charset?, extractor, observed_at?, revision?,
|
|
130
|
+
* kind?, provenance? } (see bodiesOf). Re-writing the same bytes is a no-op; different bytes over
|
|
131
|
+
* an existing snapshot throw snapshot_exists, because ledger rows pin the old hash, unless
|
|
132
|
+
* options.replace (`source refresh`, which re-anchors those rows, or `source snapshot --replace`, which
|
|
133
|
+
* has checked that no active row pins it) is set; a replacement drops the old capture's command.json.
|
|
147
134
|
*/
|
|
148
|
-
export function writeSnapshot(sourcesDir, sourceId, input) {
|
|
135
|
+
export function writeSnapshot(sourcesDir, sourceId, input, options = {}) {
|
|
149
136
|
checkInput(sourceId, input)
|
|
150
|
-
const
|
|
151
|
-
const
|
|
137
|
+
const { mode, raw, textBody } = bodiesOf(input)
|
|
138
|
+
const view = buildView(mode, { raw, textBody, charset: input.charset ?? null })
|
|
139
|
+
const sha256 = sha256Of(raw ?? textBody)
|
|
152
140
|
const dir = join(sourcesDir, sourceId)
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
throw new Error(`snapshot_exists: ${sourceId} already holds
|
|
141
|
+
const pinned = pinnedHash(dir)
|
|
142
|
+
if (pinned !== null && pinned !== sha256) {
|
|
143
|
+
if (options.replace !== true) throw new Error(`snapshot_exists: ${sourceId} already holds different snapshot bytes`)
|
|
144
|
+
rmSync(join(dir, "command.json"), { force: true })
|
|
156
145
|
}
|
|
157
146
|
mkdirSync(dir, { recursive: true })
|
|
158
|
-
writeFileSync(
|
|
159
|
-
|
|
147
|
+
if (raw) writeFileSync(join(dir, "raw.bin"), raw)
|
|
148
|
+
else rmSync(join(dir, "raw.bin"), { force: true })
|
|
149
|
+
writeFileSync(join(dir, "text.txt"), textBody ?? view.text)
|
|
150
|
+
if (view.html !== null) writeFileSync(join(dir, "source.html"), view.html)
|
|
151
|
+
else rmSync(join(dir, "source.html"), { force: true })
|
|
160
152
|
const meta = {
|
|
161
153
|
source_id: sourceId,
|
|
162
154
|
url: input.url ?? input.path,
|
|
@@ -164,13 +156,25 @@ export function writeSnapshot(sourcesDir, sourceId, input) {
|
|
|
164
156
|
sha256,
|
|
165
157
|
extractor: input.extractor,
|
|
166
158
|
revision: input.revision ?? null,
|
|
167
|
-
kind: input.kind ?? (input.url !== undefined ||
|
|
159
|
+
kind: input.kind ?? (input.url !== undefined || mode === "html" ? "page" : "document"),
|
|
168
160
|
provenance: input.provenance ?? "unknown",
|
|
161
|
+
text_source: mode,
|
|
162
|
+
...(raw ? { charset: view.encoding } : {}),
|
|
163
|
+
...(mode === "declared" ? { text_sha256: sha256Of(textBody) } : {}),
|
|
169
164
|
}
|
|
170
165
|
writeFileSync(join(dir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`)
|
|
171
|
-
|
|
166
|
+
let altered = null
|
|
167
|
+
if (mode === "html") {
|
|
168
|
+
const { altered: changed, ...counts } = probeConversion(view.html, view.text, { attributes: false })
|
|
169
|
+
altered = changed
|
|
170
|
+
writeFidelityRecord(join(dir, "conversion.json"), "source_conversion", {
|
|
171
|
+
artifact_sha256: sha256Of(view.html), report: join(dir, "source.html"), defects: [],
|
|
172
|
+
conversion_altered_numbers: changed, ...counts,
|
|
173
|
+
}, new Date(meta.observed_at))
|
|
174
|
+
} else rmSync(join(dir, "conversion.json"), { force: true })
|
|
175
|
+
const sentences = indexSentences(view.text, sourceId)
|
|
172
176
|
writeFileSync(join(dir, "sentences.jsonl"), sentences.map((row) => `${JSON.stringify(row)}\n`).join(""))
|
|
173
|
-
const acronyms = { schemaVersion: 1, source_id: sourceId, snapshot_sha256: sha256, entries: findAcronyms(text, sentences) }
|
|
177
|
+
const acronyms = { schemaVersion: 1, source_id: sourceId, snapshot_sha256: sha256, entries: findAcronyms(view.text, sentences) }
|
|
174
178
|
writeFileSync(join(dir, "acronyms.json"), `${JSON.stringify(acronyms, null, 2)}\n`)
|
|
175
|
-
return { dir, sha256 }
|
|
179
|
+
return { dir, sha256, altered }
|
|
176
180
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// source-view.mjs - the one reading of a source snapshot that locators, verify, and refresh share.
|
|
2
|
+
//
|
|
3
|
+
// A snapshot's meta.json `text_source` says where its locator text comes from:
|
|
4
|
+
// html - text nodes of raw.bin (html-text-map.mjs), offsets are raw.bin bytes
|
|
5
|
+
// decoded - raw.bin decoded in its charset, offsets are raw.bin bytes
|
|
6
|
+
// declared - text.txt is a declared extraction (--raw + --text) of non-HTML raw.bin; offsets
|
|
7
|
+
// are text.txt bytes, and raw.bin and text.txt are both pinned
|
|
8
|
+
// converted - converted-only: no raw.bin, text.txt is all there is (quotes only)
|
|
9
|
+
// A snapshot without text_source and without raw.bin is a legacy text.txt snapshot. byteSpan maps
|
|
10
|
+
// a [start, end) range of `text` to [startByte, endByte) in `artifact`, never through a re-encode.
|
|
11
|
+
// node: builtins and sibling imports only.
|
|
12
|
+
|
|
13
|
+
import { readFileSync } from "node:fs"
|
|
14
|
+
import { join } from "node:path"
|
|
15
|
+
|
|
16
|
+
import { sha256Of } from "./cli-support.mjs"
|
|
17
|
+
import { extractHtml } from "./html-text-map.mjs"
|
|
18
|
+
import { decodeRaw } from "./raw-decode.mjs"
|
|
19
|
+
|
|
20
|
+
export const TEXT_SOURCES = Object.freeze(["html", "decoded", "declared", "converted"])
|
|
21
|
+
|
|
22
|
+
/** Read one snapshot file; the error names the file and the cause (missing, EACCES, ...). */
|
|
23
|
+
export function readPart(dir, name, encoding) {
|
|
24
|
+
try {
|
|
25
|
+
return readFileSync(join(dir, name), encoding)
|
|
26
|
+
} catch (error) {
|
|
27
|
+
const cause = error.code === "ENOENT" ? "is missing" : error.code === "EISDIR" ? "is not a readable file" : `is unreadable (${error.code ?? error.message})`
|
|
28
|
+
throw Object.assign(new Error(`${name} ${cause}`), { code: error.code, part: name })
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The text source a meta.json declares, inferring it for snapshots written before the field. */
|
|
33
|
+
export function textSourceOf(meta, hasRaw) {
|
|
34
|
+
if (TEXT_SOURCES.includes(meta.text_source)) return meta.text_source
|
|
35
|
+
if (!hasRaw) return meta.kind === "converted-only" ? "converted" : "legacy"
|
|
36
|
+
return meta.kind === "page" ? "html" : "decoded"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Locator text and byte map for one snapshot body. raw is raw.bin (null for converted/legacy),
|
|
41
|
+
* textBody is text.txt bytes (needed for declared, converted, legacy).
|
|
42
|
+
*/
|
|
43
|
+
export function buildView(mode, { raw = null, textBody = null, charset = null }) {
|
|
44
|
+
if (mode === "html") {
|
|
45
|
+
const decoded = decodeRaw(raw, charset, true)
|
|
46
|
+
const map = extractHtml(decoded.text)
|
|
47
|
+
const byteSpan = (start, end) => {
|
|
48
|
+
if (end <= start) return [decoded.bytes[map.ends[start - 1] ?? 0], decoded.bytes[map.ends[start - 1] ?? 0]]
|
|
49
|
+
return [decoded.bytes[map.starts[start]], decoded.bytes[map.ends[end - 1]]]
|
|
50
|
+
}
|
|
51
|
+
return { mode, artifact: "raw.bin", encoding: decoded.encoding, text: map.text, html: decoded.text, root: map.root, blocks: map.blocks, byteSpan }
|
|
52
|
+
}
|
|
53
|
+
if (mode === "legacy") return { mode, artifact: "text.txt", encoding: "utf-8", text: Buffer.from(textBody).toString("utf8"), html: null, root: null, blocks: null, byteSpan: null }
|
|
54
|
+
const body = mode === "decoded" ? raw : textBody
|
|
55
|
+
const decoded = decodeRaw(body, mode === "decoded" ? charset : "utf-8")
|
|
56
|
+
const byteSpan = (start, end) => [decoded.bytes[start], decoded.bytes[Math.max(start, end)]]
|
|
57
|
+
return { mode, artifact: mode === "decoded" ? "raw.bin" : "text.txt", encoding: decoded.encoding, text: decoded.text, html: null, root: null, blocks: null, byteSpan }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read and hash a snapshot directory. Throws (with the file and cause) when meta.json, raw.bin, or
|
|
62
|
+
* text.txt cannot be read; hash drift is returned, not thrown: { meta, view, raw, sha256, drift }.
|
|
63
|
+
* sha256 is the hash of the pinned body (raw.bin, or text.txt for converted and legacy snapshots).
|
|
64
|
+
*/
|
|
65
|
+
export function readSnapshot(dir) {
|
|
66
|
+
let meta
|
|
67
|
+
try {
|
|
68
|
+
meta = JSON.parse(readPart(dir, "meta.json", "utf8"))
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw error.part ? error : new Error("meta.json is not valid JSON")
|
|
71
|
+
}
|
|
72
|
+
if (meta === null || typeof meta !== "object" || typeof meta.sha256 !== "string") throw new Error("has invalid snapshot metadata (meta.json has no sha256)")
|
|
73
|
+
let raw = null
|
|
74
|
+
if (meta.text_source === undefined) {
|
|
75
|
+
try {
|
|
76
|
+
raw = readPart(dir, "raw.bin")
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error.code !== "ENOENT") throw error
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const mode = textSourceOf(meta, raw !== null)
|
|
82
|
+
if (raw === null && ["html", "decoded", "declared"].includes(mode)) raw = readPart(dir, "raw.bin")
|
|
83
|
+
const textBody = ["declared", "converted", "legacy"].includes(mode) ? readPart(dir, "text.txt") : null
|
|
84
|
+
const sha256 = sha256Of(raw ?? textBody)
|
|
85
|
+
const drift = []
|
|
86
|
+
if (sha256 !== meta.sha256) drift.push(`hash mismatch: ${raw ? "raw.bin" : "text.txt"} changed after its snapshot was pinned`)
|
|
87
|
+
if (mode === "declared" && sha256Of(textBody) !== meta.text_sha256) drift.push("hash mismatch: text.txt (the declared extraction) changed after it was pinned")
|
|
88
|
+
return { meta, raw, sha256, drift, view: buildView(mode, { raw, textBody, charset: meta.charset ?? null }) }
|
|
89
|
+
}
|