talos-code 0.0.1 → 0.1.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/LICENSE +661 -0
- package/README.md +111 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +97 -0
- package/dist/automations/history-store.js +34 -0
- package/dist/automations/policy-store.js +43 -0
- package/dist/automations/runner.js +107 -0
- package/dist/automations/schedule.js +85 -0
- package/dist/automations/windows-task.js +53 -0
- package/dist/commands/advanced-cli.js +233 -0
- package/dist/commands/checkpoint-cli.js +45 -0
- package/dist/commands/config-cli.js +107 -0
- package/dist/commands/context.js +17 -0
- package/dist/commands/extensions-cli.js +207 -0
- package/dist/commands/project-cli.js +74 -0
- package/dist/commands/project-commands.js +143 -0
- package/dist/commands/provider-cli.js +185 -0
- package/dist/commands/services-cli.js +248 -0
- package/dist/commands/session-cli.js +171 -0
- package/dist/commands/system-cli.js +405 -0
- package/dist/config/commands.js +60 -0
- package/dist/config/load.js +382 -0
- package/dist/config/migrations.js +48 -0
- package/dist/config/types.js +11 -0
- package/dist/diagnostics/development-log.js +154 -0
- package/dist/diagnostics/doctor.js +104 -0
- package/dist/diagnostics/redact.js +80 -0
- package/dist/diagnostics/zip.js +52 -0
- package/dist/errors.js +156 -0
- package/dist/events/bridge.js +130 -0
- package/dist/extensions/installer.js +178 -0
- package/dist/extensions/package-schema.js +24 -0
- package/dist/headless/result.js +84 -0
- package/dist/headless/run.js +230 -0
- package/dist/i18n/en/approval.js +43 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +82 -0
- package/dist/i18n/en/errors.js +246 -0
- package/dist/i18n/en/firstrun.js +89 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +736 -0
- package/dist/i18n/en/tools.js +127 -0
- package/dist/i18n/en.js +14 -0
- package/dist/i18n/error-view.js +307 -0
- package/dist/i18n/index.js +19 -0
- package/dist/i18n/kernel-map.js +139 -0
- package/dist/io.js +45 -0
- package/dist/main.js +330 -0
- package/dist/paths.js +41 -0
- package/dist/protocol/v2/codec.js +9 -0
- package/dist/protocol/v2/events.js +555 -0
- package/dist/protocol/v2/index.js +4 -0
- package/dist/protocol/v2/replay.js +28 -0
- package/dist/protocol/v2/types.js +1 -0
- package/dist/provider/control-plane.js +135 -0
- package/dist/provider/environment-keys.js +275 -0
- package/dist/provider/health.js +110 -0
- package/dist/provider/missing-key.js +48 -0
- package/dist/provider/model-catalog.js +168 -0
- package/dist/provider/provider-text.js +13 -0
- package/dist/provider/store.js +95 -0
- package/dist/provider/system-keyring.js +214 -0
- package/dist/runtime/active-run.js +63 -0
- package/dist/runtime/agent-tree.js +74 -0
- package/dist/runtime/attachments.js +84 -0
- package/dist/runtime/brokered-executor.js +395 -0
- package/dist/runtime/context-status.js +76 -0
- package/dist/runtime/create-runtime.js +111 -0
- package/dist/runtime/model-profile.js +361 -0
- package/dist/runtime/output-store.js +137 -0
- package/dist/runtime/provider-attempts.js +185 -0
- package/dist/runtime/replay-buffer.js +153 -0
- package/dist/runtime/repo.js +95 -0
- package/dist/runtime/session-facade.js +135 -0
- package/dist/runtime/session-summary.js +125 -0
- package/dist/runtime/supervisor.js +262 -0
- package/dist/runtime/talos-composition.js +1006 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/usage-snapshot.js +82 -0
- package/dist/security/auto-classifier.js +38 -0
- package/dist/security/credential-free-environment.js +54 -0
- package/dist/security/evaluate.js +243 -0
- package/dist/security/execution-backends.js +236 -0
- package/dist/security/execution-broker.js +102 -0
- package/dist/security/forge-scan.js +74 -0
- package/dist/security/from-approval.js +39 -0
- package/dist/security/mxc-execution-backend.js +281 -0
- package/dist/security/permission-engine.js +138 -0
- package/dist/security/permission-explanation.js +54 -0
- package/dist/security/persist.js +179 -0
- package/dist/security/plugin-guard.js +230 -0
- package/dist/security/process-tree-evidence-store.js +74 -0
- package/dist/security/process-tree-probe.js +554 -0
- package/dist/security/project-resource-inventory.js +186 -0
- package/dist/security/project-trust-gate.js +38 -0
- package/dist/security/project-trust.js +367 -0
- package/dist/security/rule-parser.js +201 -0
- package/dist/security/shell-segmentation.js +68 -0
- package/dist/security/trust-authority.js +324 -0
- package/dist/security/types.js +1 -0
- package/dist/security/workspace-identity.js +102 -0
- package/dist/services/automation-facade.js +59 -0
- package/dist/services/forge-facade.js +77 -0
- package/dist/services/hook-facade.js +240 -0
- package/dist/services/index.js +16 -0
- package/dist/services/library-facade.js +174 -0
- package/dist/services/mcp-facade.js +348 -0
- package/dist/services/memory-facade.js +126 -0
- package/dist/services/notes-facade.js +157 -0
- package/dist/services/plugin-facade.js +308 -0
- package/dist/services/research-facade.js +110 -0
- package/dist/services/task-board-facade.js +236 -0
- package/dist/services/workflow-catalog.js +246 -0
- package/dist/sessions/export-format.js +98 -0
- package/dist/sessions/metadata-store.js +160 -0
- package/dist/sessions/share.js +122 -0
- package/dist/sessions/transfer.js +16 -0
- package/dist/subcommands.js +62 -0
- package/dist/tui/agent-roster.js +36 -0
- package/dist/tui/app.js +3761 -0
- package/dist/tui/approval.js +36 -0
- package/dist/tui/boot/boot-sequence.js +76 -0
- package/dist/tui/boot/cinematic.js +243 -0
- package/dist/tui/boot/desktop-logo.js +82 -0
- package/dist/tui/boot.js +3 -0
- package/dist/tui/busy-input.js +58 -0
- package/dist/tui/catalog-service.js +559 -0
- package/dist/tui/components/assistant-stream.js +1 -0
- package/dist/tui/components/command-menu.js +68 -0
- package/dist/tui/components/composer.js +219 -0
- package/dist/tui/components/diff.js +35 -0
- package/dist/tui/components/footer.js +48 -0
- package/dist/tui/components/header.js +6 -0
- package/dist/tui/components/markdown.js +305 -0
- package/dist/tui/components/status-indicator.js +32 -0
- package/dist/tui/components/terminal-shell.js +205 -0
- package/dist/tui/components/thinking-row.js +19 -0
- package/dist/tui/components/tool-row.js +97 -0
- package/dist/tui/components/transcript-virtualizer.js +165 -0
- package/dist/tui/components/transcript.js +43 -0
- package/dist/tui/diff-model.js +77 -0
- package/dist/tui/editor-history.js +21 -0
- package/dist/tui/editor.js +210 -0
- package/dist/tui/event-adapter.js +436 -0
- package/dist/tui/exit-output.js +58 -0
- package/dist/tui/external-editor.js +53 -0
- package/dist/tui/file-completion.js +89 -0
- package/dist/tui/focus-manager.js +5 -0
- package/dist/tui/highlight.js +30 -0
- package/dist/tui/input-router.js +18 -0
- package/dist/tui/interrupt.js +99 -0
- package/dist/tui/keybindings.js +194 -0
- package/dist/tui/keymap-resolver.js +91 -0
- package/dist/tui/launch-state.js +139 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +45 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +39 -0
- package/dist/tui/overlays/approval-dialog.js +640 -0
- package/dist/tui/overlays/automation-center.js +51 -0
- package/dist/tui/overlays/checkpoint-picker.js +47 -0
- package/dist/tui/overlays/context-inspector.js +36 -0
- package/dist/tui/overlays/effort-line.js +110 -0
- package/dist/tui/overlays/forge-center.js +46 -0
- package/dist/tui/overlays/help-dialog.js +16 -0
- package/dist/tui/overlays/history-picker.js +37 -0
- package/dist/tui/overlays/hook-center.js +70 -0
- package/dist/tui/overlays/library-center.js +55 -0
- package/dist/tui/overlays/mcp-center.js +60 -0
- package/dist/tui/overlays/memory-center.js +63 -0
- package/dist/tui/overlays/model-picker.js +72 -0
- package/dist/tui/overlays/notes-center.js +53 -0
- package/dist/tui/overlays/overlay-host.js +8 -0
- package/dist/tui/overlays/plugin-center.js +76 -0
- package/dist/tui/overlays/provider-picker.js +145 -0
- package/dist/tui/overlays/queue-editor.js +32 -0
- package/dist/tui/overlays/research-center.js +59 -0
- package/dist/tui/overlays/scroll-window.js +234 -0
- package/dist/tui/overlays/session-picker.js +115 -0
- package/dist/tui/overlays/tasks-center.js +83 -0
- package/dist/tui/overlays/theme-picker.js +21 -0
- package/dist/tui/overlays/transcript-search.js +58 -0
- package/dist/tui/overlays/trust-center.js +29 -0
- package/dist/tui/overlays/workflow-center.js +46 -0
- package/dist/tui/project-file-index.js +214 -0
- package/dist/tui/project-references.js +85 -0
- package/dist/tui/project-trust-prompt.js +287 -0
- package/dist/tui/prompt-history-store.js +116 -0
- package/dist/tui/queue-store.js +110 -0
- package/dist/tui/regions/budget.js +59 -0
- package/dist/tui/regions/views.js +96 -0
- package/dist/tui/render-coordinator.js +148 -0
- package/dist/tui/render-scheduler.js +4 -0
- package/dist/tui/run.js +69 -0
- package/dist/tui/selection-list.js +29 -0
- package/dist/tui/session-controller.js +778 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/shell-input.js +35 -0
- package/dist/tui/shell-layout.js +48 -0
- package/dist/tui/shell-model.js +98 -0
- package/dist/tui/slash-commands.js +80 -0
- package/dist/tui/state.js +151 -0
- package/dist/tui/status-bar.js +125 -0
- package/dist/tui/status-view.js +40 -0
- package/dist/tui/terminal-capabilities.js +9 -0
- package/dist/tui/terminal-session.js +11 -0
- package/dist/tui/text-width.js +66 -0
- package/dist/tui/theme-catalog.js +25 -0
- package/dist/tui/theme-store.js +23 -0
- package/dist/tui/theme.js +23 -0
- package/dist/tui/tool-display.js +135 -0
- package/dist/tui/tool-facts.js +1 -0
- package/dist/tui/tools/bash-renderer.js +32 -0
- package/dist/tui/tools/change.js +61 -0
- package/dist/tui/tools/edit-renderer.js +17 -0
- package/dist/tui/tools/generic-renderer.js +13 -0
- package/dist/tui/tools/list-renderer.js +14 -0
- package/dist/tui/tools/read-renderer.js +13 -0
- package/dist/tui/tools/registry.js +20 -0
- package/dist/tui/tools/row-format.js +187 -0
- package/dist/tui/tools/search-renderer.js +38 -0
- package/dist/tui/tools/shared.js +97 -0
- package/dist/tui/tools/write-renderer.js +15 -0
- package/dist/tui/transcript-model.js +441 -0
- package/dist/tui/ui-preferences.js +79 -0
- package/dist/tui/usage-view.js +77 -0
- package/dist/tui/vim-mode.js +99 -0
- package/dist/update/check.js +15 -0
- package/dist/update/npm.js +51 -0
- package/dist/update/run.js +129 -0
- package/dist/version.js +2 -0
- package/dist/workspace/checkpoint-store.js +210 -0
- package/dist/workspace/checkpoint.js +413 -0
- package/dist/workspace/restore.js +232 -0
- package/package.json +63 -5
- package/vendor/context-engine/package.json +11 -0
- package/vendor/context-engine/src/compaction-planner.mjs +57 -0
- package/vendor/context-engine/src/contracts.mjs +48 -0
- package/vendor/context-engine/src/engine.mjs +445 -0
- package/vendor/context-engine/src/node/context-export.mjs +164 -0
- package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
- package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
- package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
- package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
- package/vendor/context-engine/src/profiles.mjs +10 -0
- package/vendor/context-engine/src/retrieval.mjs +104 -0
- package/vendor/context-engine/src/summary.mjs +97 -0
- package/vendor/context-engine/src/usage.mjs +34 -0
- package/vendor/harness-ui/package.json +38 -0
- package/vendor/harness-ui/src/acp-agent.mjs +350 -0
- package/vendor/harness-ui/src/agent-service.mjs +2188 -0
- package/vendor/harness-ui/src/agui-events.mjs +452 -0
- package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
- package/vendor/harness-ui/src/artifact-store.mjs +39 -0
- package/vendor/harness-ui/src/assistenza.mjs +123 -0
- package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
- package/vendor/harness-ui/src/automation-store.mjs +145 -0
- package/vendor/harness-ui/src/browser-annota.mjs +639 -0
- package/vendor/harness-ui/src/browser-frame.mjs +211 -0
- package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
- package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
- package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
- package/vendor/harness-ui/src/browser-stream.mjs +445 -0
- package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
- package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
- package/vendor/harness-ui/src/config.mjs +697 -0
- package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
- package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
- package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
- package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
- package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
- package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
- package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
- package/vendor/harness-ui/src/context-runtime.mjs +118 -0
- package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
- package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
- package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
- package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
- package/vendor/harness-ui/src/custom-task.mjs +171 -0
- package/vendor/harness-ui/src/doctor.mjs +142 -0
- package/vendor/harness-ui/src/document-filename.mjs +97 -0
- package/vendor/harness-ui/src/document-generator.mjs +493 -0
- package/vendor/harness-ui/src/document-report.mjs +331 -0
- package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
- package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
- package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
- package/vendor/harness-ui/src/forge-contract.mjs +221 -0
- package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
- package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
- package/vendor/harness-ui/src/generation-idle.mjs +332 -0
- package/vendor/harness-ui/src/gguf-header.mjs +207 -0
- package/vendor/harness-ui/src/git-service.mjs +626 -0
- package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
- package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
- package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
- package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
- package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
- package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
- package/vendor/harness-ui/src/hook-registry.mjs +186 -0
- package/vendor/harness-ui/src/http-app.mjs +6259 -0
- package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
- package/vendor/harness-ui/src/id-archivio.mjs +27 -0
- package/vendor/harness-ui/src/image-generator.mjs +143 -0
- package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
- package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
- package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
- package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
- package/vendor/harness-ui/src/library-store.mjs +652 -0
- package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
- package/vendor/harness-ui/src/local-model-store.mjs +339 -0
- package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
- package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
- package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
- package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
- package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
- package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
- package/vendor/harness-ui/src/mcp-client.mjs +98 -0
- package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
- package/vendor/harness-ui/src/mcp-session.mjs +177 -0
- package/vendor/harness-ui/src/memory-store.mjs +227 -0
- package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
- package/vendor/harness-ui/src/model-catalog.mjs +129 -0
- package/vendor/harness-ui/src/model-destination.mjs +189 -0
- package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
- package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
- package/vendor/harness-ui/src/notes-store.mjs +250 -0
- package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
- package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
- package/vendor/harness-ui/src/path-policy.mjs +442 -0
- package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
- package/vendor/harness-ui/src/plugin-session.mjs +180 -0
- package/vendor/harness-ui/src/process-policy.mjs +345 -0
- package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
- package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
- package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
- package/vendor/harness-ui/src/provider-probe.mjs +582 -0
- package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
- package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
- package/vendor/harness-ui/src/public-problem.mjs +109 -0
- package/vendor/harness-ui/src/research/card.mjs +235 -0
- package/vendor/harness-ui/src/research/citations.mjs +142 -0
- package/vendor/harness-ui/src/research/collector.mjs +275 -0
- package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
- package/vendor/harness-ui/src/research/dossier.mjs +114 -0
- package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
- package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
- package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
- package/vendor/harness-ui/src/research/independence.mjs +159 -0
- package/vendor/harness-ui/src/research/ledger.mjs +166 -0
- package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
- package/vendor/harness-ui/src/research/narration.mjs +181 -0
- package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
- package/vendor/harness-ui/src/research/opposing.mjs +305 -0
- package/vendor/harness-ui/src/research/outline.mjs +111 -0
- package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
- package/vendor/harness-ui/src/research/pdf.mjs +291 -0
- package/vendor/harness-ui/src/research/plan.mjs +301 -0
- package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
- package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
- package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
- package/vendor/harness-ui/src/research/recheck.mjs +194 -0
- package/vendor/harness-ui/src/research/report.mjs +203 -0
- package/vendor/harness-ui/src/research/run.mjs +527 -0
- package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
- package/vendor/harness-ui/src/research/verification.mjs +572 -0
- package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
- package/vendor/harness-ui/src/research-store.mjs +1133 -0
- package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
- package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
- package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
- package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
- package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
- package/vendor/harness-ui/src/search-source-store.mjs +172 -0
- package/vendor/harness-ui/src/session-registry.mjs +6095 -0
- package/vendor/harness-ui/src/session-store.mjs +220 -0
- package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
- package/vendor/harness-ui/src/setup-stato.mjs +31 -0
- package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
- package/vendor/harness-ui/src/skill-registry.mjs +120 -0
- package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
- package/vendor/harness-ui/src/static-files.mjs +96 -0
- package/vendor/harness-ui/src/stream-partition.mjs +123 -0
- package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
- package/vendor/harness-ui/src/task-catalog.mjs +65 -0
- package/vendor/harness-ui/src/tasks-store.mjs +220 -0
- package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
- package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
- package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
- package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
- package/vendor/harness-ui/src/usage-cache.mjs +315 -0
- package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
- package/vendor/harness-ui/src/workspace-context.mjs +124 -0
- package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
- package/vendor/harness-ui/src/workspace-files.mjs +589 -0
- package/vendor/harness-ui/src/workspace-info.mjs +189 -0
- package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
- package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
- package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
- package/vendor/manifest.json +170 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ════════════════════════════════════════════════════════════════════════════════════════════
|
|
3
|
+
* ⛔⛔⛔ 12/09/2026 — IL MARKDOWN DEL RAPPORTO VA **RESO**, NON STAMPATO
|
|
4
|
+
* ════════════════════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* Il difetto, visto in una FOTO dell'esportazione HTML di una ricerca vera (L8, 4174):
|
|
7
|
+
*
|
|
8
|
+
* # Agentic Desktop Harness Evolution…
|
|
9
|
+
* ## Executive Summary
|
|
10
|
+
* ### Market Growth Trajectory - Current market size: $7.8B - Projected market size…
|
|
11
|
+
* **Key Components:** - Hierarchical multi-agent system - Two-tier agent hierarchy…
|
|
12
|
+
*
|
|
13
|
+
* Cioè: i cancelletti e gli asterischi **letterali** a schermo, e ogni elenco schiacciato dentro
|
|
14
|
+
* un paragrafo solo. La causa era una riga sola in `esportazioni.mjs`: la prosa veniva spezzata
|
|
15
|
+
* sulle righe vuote (`split(/\n{2,}/)`) e ogni pezzo infilato in un `<p>` dopo `escapeHtml`. Per
|
|
16
|
+
* un elenco — che è separato da UN a capo, non da due — quello vuol dire un muro di testo.
|
|
17
|
+
*
|
|
18
|
+
* ⛔ È il difetto «integro ≠ bello» che il rapporto di ieri aveva dichiarato in «cosa NON ho
|
|
19
|
+
* verificato»: i file erano rileggibili, e nessuno li aveva **guardati**.
|
|
20
|
+
*
|
|
21
|
+
* ── Perché un renderer nuovo, e non uno che c'era già ───────────────────────────────────────
|
|
22
|
+
* Cercato PRIMA nel proprio codebase (lezione 06/09, «chi guarda da fuori inventa quello che
|
|
23
|
+
* dentro aveva già»), e trovati due mezzi renderer, nessuno dei quali serve qui:
|
|
24
|
+
*
|
|
25
|
+
* · `frontend/src/components/markdown.js` (BC-29, 12/09) — è IL renderer di TALOS e copre le
|
|
26
|
+
* stesse cose che copre questo. ⛔ Ma costruisce **nodi DOM** (`doc.createElement`,
|
|
27
|
+
* `createTextNode`) apposta, perché la sua regola è «MAI innerHTML con testo non fidato».
|
|
28
|
+
* Qui non c'è un DOM: serve una **stringa** HTML da mettere in un file. Importarlo vorrebbe
|
|
29
|
+
* dire portarsi dietro un DOM finto sul server, e comunque non è roba mia (`frontend/`).
|
|
30
|
+
* · `document-generator.mjs`, `specToReport` — converte `body` in blocchi per il PDF, ma solo
|
|
31
|
+
* titoli `#`/`##`/`###`, elenchi `-`/`*` e paragrafi: niente grassetto, corsivo, codice,
|
|
32
|
+
* citazioni, elenchi numerati, tabelle o link. È il motivo per cui anche il PDF usciva con
|
|
33
|
+
* `**Key Components:**` dentro.
|
|
34
|
+
*
|
|
35
|
+
* ⇒ Un parser **solo**, qui, con TRE uscite: HTML (stringa), blocchi per `document-report.mjs`
|
|
36
|
+
* (il PDF), testo semplice. Anche il DOCX legge questi blocchi. Parser separati darebbero
|
|
37
|
+
* rese diverse dello stesso rapporto, cioè documenti che si contraddicono.
|
|
38
|
+
* ⛔ Zero dipendenze nuove: `package.json` non ha un motore Markdown e non ne prende uno per
|
|
39
|
+
* questo.
|
|
40
|
+
* ⛔ **NON è un motore CommonMark**, e non deve diventarlo — stessa dichiarazione del renderer
|
|
41
|
+
* del frontend. Sono «le basi», quelle che un rapporto di ricerca usa davvero.
|
|
42
|
+
*
|
|
43
|
+
* ── Ricerca web PRIMA di scrivere (fonte + data) ────────────────────────────────────────────
|
|
44
|
+
* · **GitHub Flavored Markdown Spec** — <https://github.github.com/gfm/>, letta il 12/09/2026.
|
|
45
|
+
* Da lì vengono le regole scritte nel codice, alla lettera:
|
|
46
|
+
* - titolo ATX: «between an opening sequence of 1–6 unescaped # characters», con 0-3 spazi
|
|
47
|
+
* di rientro e uno spazio (o fine riga) dopo i cancelletti;
|
|
48
|
+
* - recinto: «a sequence of at least three consecutive backtick characters (`) or tildes»,
|
|
49
|
+
* e «if the end of the containing block is reached and no closing code fence has been
|
|
50
|
+
* found, the code block contains all of the lines after the opening code fence»;
|
|
51
|
+
* - citazione: «0-3 spaces of initial indent, plus (a) the character > together with a
|
|
52
|
+
* following space, or (b) a single character > not followed by a space»;
|
|
53
|
+
* - elenco numerato: «a sequence of 1–9 arabic digits, followed by either a . or a )»;
|
|
54
|
+
* - separatore: «three or more matching -, _, or * characters»;
|
|
55
|
+
* - ⛔ **TABELLE, e sono i due vincoli che non conoscevo**: «the header row must match the
|
|
56
|
+
* delimiter row in the number of cells. **If not, a table will not be recognized**» — e
|
|
57
|
+
* per le righe di dati «if there are a number of cells fewer than the number of cells in
|
|
58
|
+
* the header row, **empty cells are inserted**. If there are greater, **the excess is
|
|
59
|
+
* ignored**». Sono tre comportamenti diversi per tre casi che a occhio sembrano lo stesso,
|
|
60
|
+
* e senza la spec ne avrei scritto uno solo.
|
|
61
|
+
* · **OWASP, XSS Prevention Cheat Sheet** —
|
|
62
|
+
* <https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html>,
|
|
63
|
+
* letto il 12/09/2026. Contenuto di un elemento: entità per `& < > " '`. Attributi: sempre fra
|
|
64
|
+
* virgolette. ⛔ E il vincolo sugli indirizzi, che ha prodotto `hrefSicuro`: «Allow-list http
|
|
65
|
+
* and HTTPS URLs only (Avoid the JavaScript Protocol to Open a new Window)» e «never place
|
|
66
|
+
* untrusted data into javascript: protocol handlers». Il testo che passa di qui viene dal
|
|
67
|
+
* modello e dalle pagine del web: un `[clicca](javascript:…)` dentro un rapporto è una cosa
|
|
68
|
+
* che si può scrivere.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/* ─────────────────────────────── il modello dei blocchi ─────────────────────────────── */
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @typedef {{t:'h', lvl:number, x:string}
|
|
75
|
+
* | {t:'p', x:string}
|
|
76
|
+
* | {t:'lista', ordinata:boolean, inizio?:number, voci:Array<string|{x:string, figli:BloccoMarkdown[]}>}
|
|
77
|
+
* | {t:'citazione', x:string}
|
|
78
|
+
* | {t:'codice', lingua:string|null, x:string}
|
|
79
|
+
* | {t:'riga'}
|
|
80
|
+
* | {t:'tabella', intestazione:string[], allineamenti:Array<'l'|'c'|'r'>, righe:string[][]}
|
|
81
|
+
* } BloccoMarkdown
|
|
82
|
+
*
|
|
83
|
+
* ⛔ `x` e le celle restano **Markdown in linea GREZZO**: l'inline si risolve negli emettitori,
|
|
84
|
+
* perché HTML e PDF lo rendono in due modi diversi (tag contro run di pdfmake) e risolverlo qui
|
|
85
|
+
* costringerebbe uno dei due a disfare il lavoro dell'altro.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
const RE_TITOLO = /^ {0,3}(#{1,6})(?:\s+(.*?))?\s*#*\s*$/;
|
|
89
|
+
const RE_RECINTO = /^ {0,3}(`{3,}|~{3,})\s*([^\s`]*)\s*$/;
|
|
90
|
+
const RE_CITAZIONE = /^ {0,3}>(?: ?)(.*)$/;
|
|
91
|
+
const RE_PUNTO = /^ {0,3}([-+*])(?:\s+(.*))?$/;
|
|
92
|
+
const RE_SEPARATORE = /^ {0,3}(?:(?:-[ \t]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})$/;
|
|
93
|
+
const RE_DELIMITATORE_TABELLA = /^ {0,3}\|?(?:\s*:?-+:?\s*\|)+\s*:?-+:?\s*\|?\s*$/;
|
|
94
|
+
|
|
95
|
+
// BC35, CommonMark 0.31.2 §5.2; docx 9.5.1 ammette livelli 0–8.
|
|
96
|
+
// Oltre il limite il testo resta nella voce più profonda, senza ulteriore ricorsione.
|
|
97
|
+
export const PROFONDITA_MASSIMA_ELENCHI = 9;
|
|
98
|
+
|
|
99
|
+
function colonneRientro(spazi, colonna = 0) {
|
|
100
|
+
for (const c of spazi) colonna += c === '\t' ? 4 - colonna % 4 : 1;
|
|
101
|
+
return colonna;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function marcatoreElenco(riga) {
|
|
105
|
+
const m = /^([ \t]*)([-+*]|(\d{1,9})[.)])(?:([ \t]+)(.*)|$)/.exec(riga);
|
|
106
|
+
if (!m) return null;
|
|
107
|
+
const rientro = colonneRientro(m[1]);
|
|
108
|
+
const fine = rientro + m[2].length;
|
|
109
|
+
const spazi = colonneRientro(m[4] ?? '', fine) - fine;
|
|
110
|
+
return {
|
|
111
|
+
rientro, contenuto: fine + (spazi >= 1 && spazi <= 4 && m[5] ? spazi : 1),
|
|
112
|
+
ordinata: m[3] !== undefined, numero: Number(m[3] ?? 1),
|
|
113
|
+
tipo: m[3] === undefined ? m[2] : m[2].at(-1), x: m[5] ?? '',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Estende le sole liste: le voci senza figli mantengono la precedente forma stringa. */
|
|
118
|
+
function analizzaElenco(righe, inizio, base = 0, profondita = 0) {
|
|
119
|
+
const primo = marcatoreElenco(righe[inizio]);
|
|
120
|
+
const lista = { t: 'lista', ordinata: primo.ordinata, voci: [primo.x] };
|
|
121
|
+
// Compatibilità storica: i numeri iniziali delle liste piatte restano normalizzati a 1.
|
|
122
|
+
if (profondita > 0 && primo.ordinata && primo.numero !== 1) lista.inizio = primo.numero;
|
|
123
|
+
let corrente = primo;
|
|
124
|
+
let i = inizio + 1;
|
|
125
|
+
const voceConFigli = () => {
|
|
126
|
+
if (typeof lista.voci.at(-1) === 'string') lista.voci[lista.voci.length - 1] = { x: lista.voci.at(-1), figli: [] };
|
|
127
|
+
return lista.voci.at(-1);
|
|
128
|
+
};
|
|
129
|
+
const continua = (testo) => {
|
|
130
|
+
const voce = lista.voci.at(-1);
|
|
131
|
+
if (typeof voce === 'string') lista.voci[lista.voci.length - 1] = `${voce} ${testo}`.trim();
|
|
132
|
+
else if (voce.figli.at(-1)?.t === 'p') voce.figli.at(-1).x += ` ${testo}`;
|
|
133
|
+
else voce.figli.push({ t: 'p', x: testo });
|
|
134
|
+
};
|
|
135
|
+
for (; i < righe.length;) {
|
|
136
|
+
const riga = righe[i];
|
|
137
|
+
if (!riga.trim()) {
|
|
138
|
+
let j = i + 1;
|
|
139
|
+
while (j < righe.length && !righe[j].trim()) j++;
|
|
140
|
+
const prossimo = j < righe.length ? marcatoreElenco(righe[j]) : null;
|
|
141
|
+
// Righe vuote interne ai figli: una lista piatta invece si separa come prima.
|
|
142
|
+
if (prossimo && (prossimo.rientro >= corrente.contenuto
|
|
143
|
+
|| (profondita > 0 && prossimo.rientro >= base))) { i = j; continue; }
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
const altro = marcatoreElenco(riga);
|
|
147
|
+
const rientro = colonneRientro(/^[ \t]*/.exec(riga)[0]);
|
|
148
|
+
if (rientro < base) break;
|
|
149
|
+
if (altro && altro.rientro >= corrente.contenuto) {
|
|
150
|
+
if (profondita + 1 >= PROFONDITA_MASSIMA_ELENCHI) { continua(riga.trim()); i++; continue; }
|
|
151
|
+
if (altro.rientro <= corrente.contenuto + 3) {
|
|
152
|
+
const figlio = analizzaElenco(righe, i, corrente.contenuto, profondita + 1);
|
|
153
|
+
voceConFigli().figli.push(figlio.lista);
|
|
154
|
+
i = figlio.fine;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (altro && altro.rientro <= base + 3) {
|
|
159
|
+
if (altro.ordinata !== lista.ordinata || (profondita > 0 && altro.tipo !== primo.tipo)) break;
|
|
160
|
+
lista.voci.push(altro.x);
|
|
161
|
+
corrente = altro;
|
|
162
|
+
i++;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
// Il testo meno rientrato del contenuto di un figlio appartiene alla voce padre.
|
|
166
|
+
if (profondita > 0 && rientro < corrente.contenuto) break;
|
|
167
|
+
if (RE_TITOLO.test(riga) || RE_RECINTO.test(riga)) break;
|
|
168
|
+
continua(riga.trim());
|
|
169
|
+
i++;
|
|
170
|
+
}
|
|
171
|
+
lista.voci = lista.voci.filter(v => v !== '');
|
|
172
|
+
return { lista, fine: i };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Le celle di una riga di tabella. ⛔ Un `\|` è una barra DENTRO la cella, non un separatore. */
|
|
176
|
+
function celleDiRiga(riga) {
|
|
177
|
+
const pulita = riga.trim().replace(/^\|/, '').replace(/\|$/, '');
|
|
178
|
+
const celle = [];
|
|
179
|
+
let corrente = '';
|
|
180
|
+
for (let i = 0; i < pulita.length; i += 1) {
|
|
181
|
+
if (pulita[i] === '\\' && pulita[i + 1] === '|') { corrente += '|'; i += 1; continue; }
|
|
182
|
+
if (pulita[i] === '|') { celle.push(corrente.trim()); corrente = ''; continue; }
|
|
183
|
+
corrente += pulita[i];
|
|
184
|
+
}
|
|
185
|
+
celle.push(corrente.trim());
|
|
186
|
+
return celle;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function allineamentiDiDelimitatore(riga) {
|
|
190
|
+
return celleDiRiga(riga).map((cella) => {
|
|
191
|
+
const inizia = cella.startsWith(':');
|
|
192
|
+
const finisce = cella.endsWith(':');
|
|
193
|
+
if (inizia && finisce) return 'c';
|
|
194
|
+
if (finisce) return 'r';
|
|
195
|
+
return 'l';
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Il testo in blocchi. PURA: nessun I/O, si prova con una stringa letterale.
|
|
201
|
+
* @param {string} testo
|
|
202
|
+
* @returns {BloccoMarkdown[]}
|
|
203
|
+
*/
|
|
204
|
+
export function analizzaMarkdown(testo) {
|
|
205
|
+
const righe = String(testo ?? '').replace(/\r\n?/g, '\n').split('\n');
|
|
206
|
+
const blocchi = [];
|
|
207
|
+
let paragrafo = [];
|
|
208
|
+
|
|
209
|
+
const chiudiParagrafo = () => {
|
|
210
|
+
if (paragrafo.length === 0) return;
|
|
211
|
+
blocchi.push({ t: 'p', x: paragrafo.join(' ').trim() });
|
|
212
|
+
paragrafo = [];
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < righe.length; i += 1) {
|
|
216
|
+
const riga = righe[i];
|
|
217
|
+
|
|
218
|
+
if (riga.trim() === '') { chiudiParagrafo(); continue; }
|
|
219
|
+
|
|
220
|
+
const recinto = RE_RECINTO.exec(riga);
|
|
221
|
+
if (recinto) {
|
|
222
|
+
chiudiParagrafo();
|
|
223
|
+
const chiusura = recinto[1][0];
|
|
224
|
+
const lunghezza = recinto[1].length;
|
|
225
|
+
const dentro = [];
|
|
226
|
+
let j = i + 1;
|
|
227
|
+
for (; j < righe.length; j += 1) {
|
|
228
|
+
const fine = new RegExp(`^ {0,3}${chiusura === '`' ? '`' : '~'}{${lunghezza},}\\s*$`).exec(righe[j]);
|
|
229
|
+
if (fine) break;
|
|
230
|
+
dentro.push(righe[j]);
|
|
231
|
+
}
|
|
232
|
+
// ⛔ GFM: un recinto mai chiuso arriva fino alla fine del documento, non annulla il blocco.
|
|
233
|
+
blocchi.push({ t: 'codice', lingua: recinto[2] || null, x: dentro.join('\n') });
|
|
234
|
+
i = j;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (RE_SEPARATORE.test(riga)) { chiudiParagrafo(); blocchi.push({ t: 'riga' }); continue; }
|
|
239
|
+
|
|
240
|
+
const titolo = RE_TITOLO.exec(riga);
|
|
241
|
+
if (titolo) {
|
|
242
|
+
chiudiParagrafo();
|
|
243
|
+
blocchi.push({ t: 'h', lvl: titolo[1].length, x: (titolo[2] ?? '').trim() });
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
* ⛔ LA TABELLA si riconosce DUE righe alla volta, e non una: GFM dice che «the header row
|
|
249
|
+
* must match the delimiter row in the number of cells. If not, a table will not be
|
|
250
|
+
* recognized» ⇒ se i conti non tornano questa resta una riga di paragrafo qualunque,
|
|
251
|
+
* che è esattamente ciò che GitHub mostra.
|
|
252
|
+
*/
|
|
253
|
+
if (riga.includes('|') && i + 1 < righe.length && RE_DELIMITATORE_TABELLA.test(righe[i + 1])) {
|
|
254
|
+
const intestazione = celleDiRiga(riga);
|
|
255
|
+
const allineamenti = allineamentiDiDelimitatore(righe[i + 1]);
|
|
256
|
+
if (intestazione.length === allineamenti.length) {
|
|
257
|
+
chiudiParagrafo();
|
|
258
|
+
const corpo = [];
|
|
259
|
+
let j = i + 2;
|
|
260
|
+
for (; j < righe.length && righe[j].trim() !== '' && righe[j].includes('|'); j += 1) {
|
|
261
|
+
const celle = celleDiRiga(righe[j]);
|
|
262
|
+
// ⛔ Meno celle ⇒ si riempie; più celle ⇒ si butta l'eccesso. Sono le parole della spec.
|
|
263
|
+
corpo.push(Array.from({ length: intestazione.length }, (_v, k) => celle[k] ?? ''));
|
|
264
|
+
}
|
|
265
|
+
blocchi.push({ t: 'tabella', intestazione, allineamenti, righe: corpo });
|
|
266
|
+
i = j - 1;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const citazione = RE_CITAZIONE.exec(riga);
|
|
272
|
+
if (citazione) {
|
|
273
|
+
chiudiParagrafo();
|
|
274
|
+
const dentro = [citazione[1]];
|
|
275
|
+
let j = i + 1;
|
|
276
|
+
// Laziness (GFM): le righe di continuazione di un paragrafo citato possono non riportare il `>`.
|
|
277
|
+
for (; j < righe.length && righe[j].trim() !== ''; j += 1) {
|
|
278
|
+
const seguito = RE_CITAZIONE.exec(righe[j]);
|
|
279
|
+
if (seguito) { dentro.push(seguito[1]); continue; }
|
|
280
|
+
if (RE_TITOLO.test(righe[j]) || RE_PUNTO.test(righe[j]) || RE_RECINTO.test(righe[j])) break;
|
|
281
|
+
dentro.push(righe[j]);
|
|
282
|
+
}
|
|
283
|
+
blocchi.push({ t: 'citazione', x: dentro.join(' ').trim() });
|
|
284
|
+
i = j - 1;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const marcatore = marcatoreElenco(riga);
|
|
289
|
+
if (marcatore && marcatore.rientro <= 3) {
|
|
290
|
+
chiudiParagrafo();
|
|
291
|
+
const risultato = analizzaElenco(righe, i);
|
|
292
|
+
blocchi.push(risultato.lista);
|
|
293
|
+
i = risultato.fine - 1;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
paragrafo.push(riga.trim());
|
|
298
|
+
}
|
|
299
|
+
chiudiParagrafo();
|
|
300
|
+
return blocchi;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* ───────────────────────────────── il Markdown in linea ───────────────────────────────── */
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @typedef {{k:'testo'|'codice', v:string, forte?:boolean, corsivo?:boolean}
|
|
307
|
+
* | {k:'link', v:string, href:string, forte?:boolean, corsivo?:boolean}} Frammento
|
|
308
|
+
*/
|
|
309
|
+
|
|
310
|
+
/*
|
|
311
|
+
* ⛔ L'ordine delle alternative NON è estetico: il codice in linea viene PRIMO, così un
|
|
312
|
+
* `` `**a**` `` resta due asterischi dentro il codice invece di diventare grassetto.
|
|
313
|
+
* ⛔ `_` è ammesso solo fuori da una parola (`snake_case` non è corsivo): GFM lo dice, e senza la
|
|
314
|
+
* guardia ogni nome di variabile in un rapporto tecnico diventerebbe obliquo a metà.
|
|
315
|
+
*/
|
|
316
|
+
const RE_INLINE = /(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)|\[([^\]\n]*)\]\(\s*<?([^)\s>]*)>?(?:\s+"[^"]*")?\s*\)|\*\*(?=\S)([\s\S]*?\S)\*\*|(?<![\w])__(?=\S)([\s\S]*?\S)__(?![\w])|\*(?=\S)([^*\n]*?\S)\*|(?<![\w])_(?=\S)([^_\n]*?\S)_(?![\w])/;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Il Markdown in linea in frammenti. PURA.
|
|
320
|
+
* @param {string} testo
|
|
321
|
+
* @param {{forte?:boolean, corsivo?:boolean}} [stile]
|
|
322
|
+
* @returns {Frammento[]}
|
|
323
|
+
*/
|
|
324
|
+
export function analizzaInline(testo, stile = {}) {
|
|
325
|
+
const frammenti = [];
|
|
326
|
+
let resto = String(testo ?? '');
|
|
327
|
+
|
|
328
|
+
for (;;) {
|
|
329
|
+
const trovato = RE_INLINE.exec(resto);
|
|
330
|
+
if (!trovato) break;
|
|
331
|
+
if (trovato.index > 0) frammenti.push({ k: 'testo', v: resto.slice(0, trovato.index), ...stile });
|
|
332
|
+
|
|
333
|
+
const [, , codice, testoLink, indirizzo, forteA, forteB, corsivoA, corsivoB] = trovato;
|
|
334
|
+
if (codice !== undefined) {
|
|
335
|
+
frammenti.push({ k: 'codice', v: codice.trim(), ...stile });
|
|
336
|
+
} else if (testoLink !== undefined) {
|
|
337
|
+
frammenti.push({ k: 'link', v: testoLink, href: indirizzo ?? '', ...stile });
|
|
338
|
+
} else if (forteA !== undefined || forteB !== undefined) {
|
|
339
|
+
frammenti.push(...analizzaInline(forteA ?? forteB, { ...stile, forte: true }));
|
|
340
|
+
} else {
|
|
341
|
+
frammenti.push(...analizzaInline(corsivoA ?? corsivoB, { ...stile, corsivo: true }));
|
|
342
|
+
}
|
|
343
|
+
resto = resto.slice(trovato.index + trovato[0].length);
|
|
344
|
+
}
|
|
345
|
+
if (resto !== '') frammenti.push({ k: 'testo', v: resto, ...stile });
|
|
346
|
+
return frammenti;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* ─────────────────────────────────── uscita 1: HTML ─────────────────────────────────── */
|
|
350
|
+
|
|
351
|
+
/** Le cinque entità di OWASP, e nessuna in meno. */
|
|
352
|
+
export function escapeHtml(valore) {
|
|
353
|
+
return String(valore ?? '')
|
|
354
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
355
|
+
.replace(/"/g, '"').replace(/'/g, ''');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* L'indirizzo di un link, o `null` se non è uno che si possa aprire.
|
|
360
|
+
*
|
|
361
|
+
* ⛔ Allowlist di TRE schemi, come chiede OWASP: «Allow-list http and HTTPS URLs only». Tutto il
|
|
362
|
+
* resto — `javascript:`, `data:`, `vbscript:`, e ogni schema inventato — non diventa un `href`:
|
|
363
|
+
* il testo del link resta a schermo, l'indirizzo no. Il testo che passa di qui viene dal modello
|
|
364
|
+
* e dalle pagine del web, e questo file finisce aperto con `file://` da un browser vero.
|
|
365
|
+
* ⛔ Un indirizzo RELATIVO non passa: in un file scaricato non punterebbe a niente di sensato.
|
|
366
|
+
*/
|
|
367
|
+
export function hrefSicuro(indirizzo) {
|
|
368
|
+
const pulito = String(indirizzo ?? '').trim();
|
|
369
|
+
// ⛔ Niente caratteri di controllo né spazi DENTRO l'indirizzo: `java\nscript:` è il modo
|
|
370
|
+
// classico di far sopravvivere uno schema a un controllo che guarda solo l'inizio.
|
|
371
|
+
if (/[\u0000-\u0020]/.test(pulito)) return null;
|
|
372
|
+
if (!/^(https?:|mailto:)/i.test(pulito)) return null;
|
|
373
|
+
return pulito;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function htmlDiFrammenti(frammenti) {
|
|
377
|
+
return frammenti.map((frammento) => {
|
|
378
|
+
let dentro = frammento.k === 'codice'
|
|
379
|
+
? `<code>${escapeHtml(frammento.v)}</code>`
|
|
380
|
+
: escapeHtml(frammento.v);
|
|
381
|
+
if (frammento.k === 'link') {
|
|
382
|
+
const href = hrefSicuro(frammento.href);
|
|
383
|
+
// ⛔ `rel="noopener noreferrer"`: il file si apre con `file://`, e una pagina esterna
|
|
384
|
+
// non deve poter toccare la finestra che l'ha aperta.
|
|
385
|
+
dentro = href
|
|
386
|
+
? `<a href="${escapeHtml(href)}" rel="noopener noreferrer">${dentro}</a>`
|
|
387
|
+
: dentro;
|
|
388
|
+
}
|
|
389
|
+
if (frammento.corsivo) dentro = `<em>${dentro}</em>`;
|
|
390
|
+
if (frammento.forte) dentro = `<strong>${dentro}</strong>`;
|
|
391
|
+
return dentro;
|
|
392
|
+
}).join('');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Il Markdown in linea ridotto a testo nudo: i marcatori spariscono, il testo resta.
|
|
397
|
+
* ⛔ Serve ai TITOLI del PDF: un titolo che porta dentro un grassetto e' rumore, ma un titolo che
|
|
398
|
+
* porta dentro `**` e' il difetto che stiamo togliendo.
|
|
399
|
+
*/
|
|
400
|
+
export function inlineInTestoSemplice(testo) {
|
|
401
|
+
return analizzaInline(testo).map((frammento) => frammento.v).join('')
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Il Markdown in linea reso in HTML, senza blocchi attorno. */
|
|
405
|
+
export function inlineInHtml(testo) {
|
|
406
|
+
return htmlDiFrammenti(analizzaInline(testo));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function elencoInHtml(blocco) {
|
|
410
|
+
const tag = blocco.ordinata ? 'ol' : 'ul';
|
|
411
|
+
const inizio = blocco.inizio !== undefined ? ` start="${blocco.inizio}"` : '';
|
|
412
|
+
return `<${tag}${inizio}>${blocco.voci.map(voce => {
|
|
413
|
+
if (typeof voce === 'string') return `<li>${inlineInHtml(voce)}</li>`;
|
|
414
|
+
const figli = voce.figli.map(b => b.t === 'lista' ? elencoInHtml(b) : `<p>${inlineInHtml(b.x)}</p>`).join('');
|
|
415
|
+
return `<li>${inlineInHtml(voce.x)}${figli}</li>`;
|
|
416
|
+
}).join('')}</${tag}>`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Il Markdown reso in HTML. ⛔ Ogni valore passa da `escapeHtml`: un `<script>` nel rapporto
|
|
421
|
+
* resta testo a schermo, sempre.
|
|
422
|
+
* @param {string} testo
|
|
423
|
+
* @param {{livelloMinimo?:number}} [opzioni] — il livello del primo titolo (2 quando la pagina ha
|
|
424
|
+
* già il suo `<h1>`: due `<h1>` in una pagina sono due titoli, cioè nessuno).
|
|
425
|
+
* @returns {string}
|
|
426
|
+
*/
|
|
427
|
+
export function markdownInHtml(testo, opzioni = {}) {
|
|
428
|
+
const base = Number.isSafeInteger(opzioni.livelloMinimo) ? opzioni.livelloMinimo : 1;
|
|
429
|
+
const fuori = [];
|
|
430
|
+
for (const blocco of analizzaMarkdown(testo)) {
|
|
431
|
+
switch (blocco.t) {
|
|
432
|
+
case 'h': {
|
|
433
|
+
const livello = Math.min(6, Math.max(1, blocco.lvl + base - 1));
|
|
434
|
+
fuori.push(`<h${livello}>${inlineInHtml(blocco.x)}</h${livello}>`);
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
case 'p': fuori.push(`<p>${inlineInHtml(blocco.x)}</p>`); break;
|
|
438
|
+
case 'citazione': fuori.push(`<blockquote>${inlineInHtml(blocco.x)}</blockquote>`); break;
|
|
439
|
+
case 'riga': fuori.push('<hr>'); break;
|
|
440
|
+
case 'codice': {
|
|
441
|
+
const classe = blocco.lingua ? ` class="language-${escapeHtml(blocco.lingua)}"` : '';
|
|
442
|
+
fuori.push(`<pre><code${classe}>${escapeHtml(blocco.x)}</code></pre>`);
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
case 'lista': {
|
|
446
|
+
fuori.push(elencoInHtml(blocco));
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
case 'tabella': {
|
|
450
|
+
const stile = (indice) => ` style="text-align:${{ l: 'left', c: 'center', r: 'right' }[blocco.allineamenti[indice] ?? 'l']}"`;
|
|
451
|
+
const intestazione = blocco.intestazione.map((cella, i) => `<th${stile(i)}>${inlineInHtml(cella)}</th>`).join('');
|
|
452
|
+
const corpo = blocco.righe
|
|
453
|
+
.map((riga) => `<tr>${riga.map((cella, i) => `<td${stile(i)}>${inlineInHtml(cella)}</td>`).join('')}</tr>`)
|
|
454
|
+
.join('');
|
|
455
|
+
// ⛔ Avvolta: una tabella larga non deve far scorrere la PAGINA di lato.
|
|
456
|
+
fuori.push(`<div class="tabella"><table><thead><tr>${intestazione}</tr></thead><tbody>${corpo}</tbody></table></div>`);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
/* c8 ignore next 2 — `analizzaMarkdown` non produce altri tipi. */
|
|
460
|
+
default: break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return fuori.join('\n');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* ──────────────────── uscita 2: i blocchi di `document-report.mjs` (il PDF) ──────────────────── */
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* I frammenti come «run» di pdfmake. ⛔ `text` accetta un ARRAY di run, e ogni run porta i suoi
|
|
470
|
+
* `bold`/`italics`/`link`: è il motivo per cui il grassetto arriva nel PDF **senza** toccare
|
|
471
|
+
* `document-report.mjs`, che è il costruttore condiviso di `document_create`.
|
|
472
|
+
*/
|
|
473
|
+
export function runsDiMarkdown(testo) {
|
|
474
|
+
const runs = analizzaInline(testo).map((frammento) => ({
|
|
475
|
+
text: frammento.v,
|
|
476
|
+
...(frammento.forte ? { bold: true } : {}),
|
|
477
|
+
...(frammento.corsivo ? { italics: true } : {}),
|
|
478
|
+
...(frammento.k === 'link' && hrefSicuro(frammento.href) ? { link: hrefSicuro(frammento.href) } : {}),
|
|
479
|
+
}));
|
|
480
|
+
// ⛔ Una cella VUOTA deve tornare `''` e non `[]`: `document-report.mjs` imbottisce le righe
|
|
481
|
+
// corte con `row[index] ?? ''`, e un array vuoto passa quel controllo pur non essendo testo.
|
|
482
|
+
if (runs.length === 0) return '';
|
|
483
|
+
// Un solo pezzo senza stile è una stringa: meno rumore nel documento, e identico a stampare.
|
|
484
|
+
return runs.length === 1 && !runs[0].bold && !runs[0].italics && !runs[0].link ? runs[0].text : runs;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function elencoInReport(blocco) {
|
|
488
|
+
return {
|
|
489
|
+
t: 'list', ordered: blocco.ordinata,
|
|
490
|
+
...(blocco.inizio !== undefined ? { start: blocco.inizio } : {}),
|
|
491
|
+
items: blocco.voci.map(voce => typeof voce === 'string' ? runsDiMarkdown(voce) : {
|
|
492
|
+
x: runsDiMarkdown(voce.x),
|
|
493
|
+
figli: voce.figli.map(b => b.t === 'lista' ? elencoInReport(b) : { t: 'p', x: runsDiMarkdown(b.x) }),
|
|
494
|
+
}),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Il Markdown in blocchi del rapporto. Per le voci con figli, `document-generator.mjs`
|
|
500
|
+
* adatta l'uscita di `document-report.mjs` alle liste native di pdfmake.
|
|
501
|
+
* ⛔ Non esiste un blocco «citazione» in quello schema: si usa `note`, che è il riquadro tenue —
|
|
502
|
+
* la stessa forma con cui il PDF della ricerca mostra già i passaggi citati.
|
|
503
|
+
* @returns {object[]}
|
|
504
|
+
*/
|
|
505
|
+
export function markdownInBlocchiReport(testo, opzioni = {}) {
|
|
506
|
+
const base = Number.isSafeInteger(opzioni.livelloMinimo) ? opzioni.livelloMinimo : 1;
|
|
507
|
+
const fuori = [];
|
|
508
|
+
for (const blocco of analizzaMarkdown(testo)) {
|
|
509
|
+
switch (blocco.t) {
|
|
510
|
+
case 'h': fuori.push({ t: 'h', lvl: Math.min(3, Math.max(1, blocco.lvl + base - 1)), x: blocco.x }); break;
|
|
511
|
+
case 'p': fuori.push({ t: 'p', x: runsDiMarkdown(blocco.x) }); break;
|
|
512
|
+
case 'citazione': fuori.push({ t: 'note', x: runsDiMarkdown(blocco.x) }); break;
|
|
513
|
+
case 'riga': fuori.push({ t: 'spacer' }); break;
|
|
514
|
+
// Un recinto resta monospazio-che-non-abbiamo: almeno non si spezza in paragrafi.
|
|
515
|
+
case 'codice': fuori.push({ t: 'note', x: blocco.x }); break;
|
|
516
|
+
case 'lista': fuori.push(elencoInReport(blocco)); break;
|
|
517
|
+
case 'tabella': fuori.push({
|
|
518
|
+
t: 'table',
|
|
519
|
+
head: blocco.intestazione.map(runsDiMarkdown),
|
|
520
|
+
align: blocco.allineamenti,
|
|
521
|
+
rows: blocco.righe.map((riga) => riga.map(runsDiMarkdown)),
|
|
522
|
+
}); break;
|
|
523
|
+
/* c8 ignore next 2 */
|
|
524
|
+
default: break;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return fuori;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* ──────────────────────────── uscita 3: testo semplice ──────────────────────────── */
|
|
531
|
+
|
|
532
|
+
function elencoInTesto(blocco, profondita = 0) {
|
|
533
|
+
return blocco.voci.map((voce, i) => {
|
|
534
|
+
const x = typeof voce === 'string' ? voce : voce.x;
|
|
535
|
+
const prefisso = blocco.ordinata ? `${(blocco.inizio ?? 1) + i}. ` : '• ';
|
|
536
|
+
const riga = `${' '.repeat(profondita)}${prefisso}${inlineInTestoSemplice(x)}`;
|
|
537
|
+
if (typeof voce === 'string') return riga;
|
|
538
|
+
return [riga, ...voce.figli.map(b => b.t === 'lista' ? elencoInTesto(b, profondita + 1)
|
|
539
|
+
: `${' '.repeat(profondita + 1)}${inlineInTestoSemplice(b.x)}`)].join('\n');
|
|
540
|
+
}).join('\n');
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Il Markdown ridotto a prosa leggibile: niente marcatori, elenchi con un punto vero, una riga
|
|
545
|
+
* vuota fra i blocchi.
|
|
546
|
+
* Il DOCX ora usa direttamente `analizzaMarkdown`: questa uscita conserva la prosa leggibile.
|
|
547
|
+
*/
|
|
548
|
+
export function markdownInTestoSemplice(testo) {
|
|
549
|
+
const fuori = [];
|
|
550
|
+
for (const blocco of analizzaMarkdown(testo)) {
|
|
551
|
+
const piatto = (x) => analizzaInline(x).map((f) => f.v).join('');
|
|
552
|
+
switch (blocco.t) {
|
|
553
|
+
case 'h': fuori.push(piatto(blocco.x)); break;
|
|
554
|
+
case 'p': fuori.push(piatto(blocco.x)); break;
|
|
555
|
+
case 'citazione': fuori.push(`« ${piatto(blocco.x)} »`); break;
|
|
556
|
+
case 'riga': fuori.push('———'); break;
|
|
557
|
+
case 'codice': fuori.push(blocco.x); break;
|
|
558
|
+
case 'lista': fuori.push(elencoInTesto(blocco)); break;
|
|
559
|
+
case 'tabella': fuori.push([blocco.intestazione, ...blocco.righe].map((riga) => riga.map(piatto).join(' · ')).join('\n')); break;
|
|
560
|
+
/* c8 ignore next 2 */
|
|
561
|
+
default: break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return fuori.join('\n\n');
|
|
565
|
+
}
|