talos-code 0.0.1 → 0.2.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/CHANGELOG.md +87 -0
- package/LICENSE +661 -0
- package/README.md +116 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +100 -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 +244 -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 +46 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +92 -0
- package/dist/i18n/en/errors.js +267 -0
- package/dist/i18n/en/firstrun.js +101 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +774 -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 +331 -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/openrouter-login.js +172 -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-archive.js +69 -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 +265 -0
- package/dist/runtime/talos-composition.js +1011 -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/agent-view.js +51 -0
- package/dist/tui/app.js +4098 -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 +560 -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 +38 -0
- package/dist/tui/components/terminal-shell.js +214 -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/descendant-approvals.js +104 -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 +148 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +56 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +43 -0
- package/dist/tui/overlays/approval-dialog.js +642 -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 +77 -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 +171 -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 +289 -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 +791 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/setup-wizard.js +46 -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 +82 -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 +27 -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 +64 -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,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* elenco-profondo.mjs — P-13: il COSTRUTTORE dell'elenco profondo dei file.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ Perché esiste, misurato (non da rifare): l'attrezzo di elenco del kernel arriva a
|
|
5
|
+
* profondità 2. I 106 percorsi dei task del corpus `storia` stanno a profondità 4-6, ZERO a
|
|
6
|
+
* profondità ≤ 2, e 35 consegne su 35 non nominano nessun file. TALOS non può risolverne
|
|
7
|
+
* nessuno — e non per bravura del modello: non li VEDE. Pass-rate oggi 0 su 35.
|
|
8
|
+
* Su questo repo, con potatura: profondità 2 → 336 file (~3.400 token) · tutto l'albero, con
|
|
9
|
+
* fuori anche ciò che `git` ignora → 717 file (~7.900 token). Il costo della vista completa è
|
|
10
|
+
* ~4.500 token: si paga. (Misurato con questo modulo il 10/09/2026, e non combacia al file con
|
|
11
|
+
* la stima che me l'ha commissionato — 333 e 676 — perché quella non contava `frontend/`.)
|
|
12
|
+
*
|
|
13
|
+
* ⛔ PURA rispetto alla configurazione: il filesystem si inietta (`fs`), come in
|
|
14
|
+
* `local-model-store.mjs` (`fsImpl`) e `workspace-disk.mjs` (`readdirFn`). Nessuna decisione
|
|
15
|
+
* presa leggendo l'ambiente: tutto arriva dagli argomenti.
|
|
16
|
+
*
|
|
17
|
+
* ── LE QUATTRO DECISIONI, con le loro fonti ──────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* (1) COSA SI POTA. La lista non è inventata qui: è l'unione di quelle che questo repo ha già
|
|
20
|
+
* — `src/workspace-info.mjs:40` (la più ampia) e `src/workspace-context.mjs:25` — più
|
|
21
|
+
* `bower_components`, che è l'altra metà del default di VS Code («search.exclude» di
|
|
22
|
+
* serie esclude `node_modules` e `bower_components` a ogni livello, microsoft/vscode
|
|
23
|
+
* #84619 e #8934, letti il 10/09/2026). Prima di cercare fuori ho cercato dentro: la lezione
|
|
24
|
+
* `chi-guarda-da-fuori-inventa-quello-che-dentro-aveva-gia` dice esattamente questo.
|
|
25
|
+
* ⛔ `dist`, `build`, `out`, `target`, `vendor` a volte contengono codice VERO (il
|
|
26
|
+
* `vendor/` di Go è sorgente). Si potano lo stesso — sono grandi, generati e non si
|
|
27
|
+
* modificano — ma la potatura è un ARGOMENTO (`escludiCartelle`), non una legge: chi
|
|
28
|
+
* lavora su un progetto Go passa la sua lista.
|
|
29
|
+
*
|
|
30
|
+
* (2) QUALI ESTENSIONI TENERE — e perché una DENYLIST, non una allowlist.
|
|
31
|
+
* Gli strumenti veri non decidono per estensione: ripgrep chiama binario un file se e
|
|
32
|
+
* solo se contiene un byte NUL, guardando i primi kilobyte (BurntSushi/ripgrep
|
|
33
|
+
* discussion #2050 e la guida ufficiale, letti il 10/09/2026). Qui la firma NON si usa,
|
|
34
|
+
* per due ragioni misurabili: (a) questo elenco è fatto di NOMI, non di contenuti —
|
|
35
|
+
* leggere i primi byte di 676 file sono 676 `open`+`read` in più per rispondere a una
|
|
36
|
+
* domanda che non riguarda il contenuto; (b) non risolverebbe il caso vero: un `.png` da
|
|
37
|
+
* 4 MB è legittimamente binario, la firma lo confermerebbe e basta.
|
|
38
|
+
* ⛔ E il verso della lista conta più della lista: una ALLOWLIST («tieni solo .js e .md»)
|
|
39
|
+
* invecchiando fa sparire un file che esiste — `.astro`, `.zig`, `Dockerfile`, `Makefile`
|
|
40
|
+
* (che estensione non ha), il linguaggio nato il mese scorso — e il modello concluderebbe
|
|
41
|
+
* che non c'è: è la BUGIA che questo modulo esiste per non dire. Una DENYLIST che
|
|
42
|
+
* invecchia produce al massimo una riga di rumore. I due errori non costano uguale, e si
|
|
43
|
+
* sceglie il verso che sbaglia dalla parte del rumore.
|
|
44
|
+
*
|
|
45
|
+
* (3) L'ORDINE — deterministico, e imposto DURANTE la camminata, non solo alla fine.
|
|
46
|
+
* La cache del prefisso si aggancia solo se i byte sono IDENTICI: il suo indice è il
|
|
47
|
+
* rendering esatto fino al punto di taglio, e un solo byte diverso alla posizione N
|
|
48
|
+
* invalida tutto da lì in poi; chi assembla da un dizionario o da un insieme deve
|
|
49
|
+
* ordinare per nome, altrimenti la cache si azzera in silenzio (documentazione Claude
|
|
50
|
+
* «Prompt caching» e la guida `anthropics/skills` prompt-caching.md, letti il
|
|
51
|
+
* 10/09/2026). In memoria è già misurato: la cache costa 1/6 e prende dalla terza
|
|
52
|
+
* chiamata — l'87% dei token entra e non esce.
|
|
53
|
+
* ⇒ Tre conseguenze, tutte e tre nel codice qui sotto:
|
|
54
|
+
* · le voci di ogni cartella si ordinano PRIMA di scendere — `readdir` non promette
|
|
55
|
+
* nessun ordine (su ext4 è per hash del nome), quindi ordinare solo alla fine
|
|
56
|
+
* renderebbe deterministico l'elenco COMPLETO ma non quello TRONCATO: il tetto
|
|
57
|
+
* taglierebbe file diversi a ogni giro;
|
|
58
|
+
* · si confronta per unità di codice (`<`), MAI con `localeCompare`: l'ordine locale
|
|
59
|
+
* dipende dai dati ICU compilati dentro Node, e su un ambiente senza ICU completo
|
|
60
|
+
* cambia. È la stessa trappola già scritta in `src/workspace-info.mjs:29`;
|
|
61
|
+
* · si cammina in AMPIEZZA (un livello alla volta): se il tetto morde, ciò che
|
|
62
|
+
* sopravvive è la roba vicina alla radice — `package.json`, `src/`, `tests/` — non il
|
|
63
|
+
* primo sottoalbero incontrato fino in fondo.
|
|
64
|
+
*
|
|
65
|
+
* (4) IL TETTO — 1.500, non 4.000. Il 4.000 di partenza non era una misura. La misura, fatta
|
|
66
|
+
* con questo stesso modulo su questo repo il 10/09/2026:
|
|
67
|
+
* · albero intero, sola potatura di serie ...... 1.700 file · 4.718 esclusi per estensione
|
|
68
|
+
* · togliendo ciò che `git` già ignora ......... 717 file · 27.640 caratteri · ~7.900 token
|
|
69
|
+
* · costo per percorso .......................... ~11 token (per il codice il rapporto
|
|
70
|
+
* carattere/token scende a ~3-3,5 contro i ~4 della prosa, regola confermata il
|
|
71
|
+
* 10/09/2026: nomi lunghi e separatori pagano più della prosa)
|
|
72
|
+
* A ~11-17 token per riga, 4.000 file sono 44.000-67.000 token di sola lista dentro un
|
|
73
|
+
* prompt che si paga a ogni giro (scontato dalla cache, non gratis). 1.500 file sono
|
|
74
|
+
* ~16.000-25.000 token nel caso peggiore misurato: abbastanza per mostrare per intero un
|
|
75
|
+
* repo grande, poco abbastanza da non mangiare il contesto.
|
|
76
|
+
* ⛔ Misurato lo stesso giorno, e vale per chi aggancia: SENZA il filtro del `.gitignore`
|
|
77
|
+
* il tetto morde già qui, e non per colpa del codice — 944 dei 1.700 file sono artefatti di
|
|
78
|
+
* una cartella di esiti che `git` ignora. Col filtro attaccato restano 717 file e il tetto
|
|
79
|
+
* non si avvicina nemmeno. Il filtro non è un abbellimento: è ciò che rende il tetto raro.
|
|
80
|
+
* ⛔ E resta un tetto GREZZO: la moneta vera sono i token, e il contatore vero è già in
|
|
81
|
+
* `src/context-token-counters.mjs`. Chi aggancia questo modulo decida se tagliare lì.
|
|
82
|
+
*/
|
|
83
|
+
import { readdir, realpath, stat } from 'node:fs/promises';
|
|
84
|
+
import { basename, join } from 'node:path';
|
|
85
|
+
|
|
86
|
+
export class ElencoProfondoError extends Error {
|
|
87
|
+
constructor(message) {
|
|
88
|
+
super(message);
|
|
89
|
+
this.name = 'ElencoProfondoError';
|
|
90
|
+
this.code = 'ELENCO_INVALIDO';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Vedi decisione (1). Unione di `workspace-info.mjs:40` + `workspace-context.mjs:25` + il default di VS Code. */
|
|
95
|
+
export const CARTELLE_ESCLUSE_PREDEFINITE = Object.freeze([
|
|
96
|
+
// controllo di versione
|
|
97
|
+
'.git', '.hg', '.svn',
|
|
98
|
+
// dipendenze scaricate
|
|
99
|
+
'node_modules', 'bower_components', 'vendor', '.pnpm-store', '.yarn',
|
|
100
|
+
// prodotti della compilazione
|
|
101
|
+
'dist', 'build', 'out', 'target', '.next', '.nuxt', '.svelte-kit', '.output',
|
|
102
|
+
// cache e prodotti degli strumenti
|
|
103
|
+
'.cache', '.turbo', '.parcel-cache', '.gradle', 'coverage', '.nyc_output',
|
|
104
|
+
'__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache', '.tox',
|
|
105
|
+
// ambienti virtuali
|
|
106
|
+
'.venv', 'venv', 'virtualenv',
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
/** Vedi decisione (2): denylist corta e sostituibile, mai una allowlist. */
|
|
110
|
+
export const ESTENSIONI_ESCLUSE_PREDEFINITE = Object.freeze([
|
|
111
|
+
// immagini e media (`.svg` NO: è testo, e dice com'è fatta la UI)
|
|
112
|
+
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.avif', '.tiff', '.psd',
|
|
113
|
+
'.mp3', '.wav', '.ogg', '.flac', '.m4a', '.aac', '.mp4', '.mov', '.avi', '.mkv', '.webm',
|
|
114
|
+
// archivi e pacchetti
|
|
115
|
+
'.zip', '.gz', '.tgz', '.bz2', '.xz', '.7z', '.rar', '.tar', '.jar', '.war', '.apk', '.aab',
|
|
116
|
+
'.iso', '.dmg',
|
|
117
|
+
// eseguibili e oggetti
|
|
118
|
+
'.exe', '.dll', '.so', '.dylib', '.bin', '.o', '.a', '.lib', '.pdb', '.obj', '.class',
|
|
119
|
+
'.pyc', '.pyo', '.node', '.wasm',
|
|
120
|
+
// caratteri tipografici
|
|
121
|
+
'.woff', '.woff2', '.ttf', '.otf', '.eot',
|
|
122
|
+
// pesi dei modelli: questo repo ne scarica, e sono i file più grandi del disco
|
|
123
|
+
'.gguf', '.safetensors', '.onnx', '.pt', '.pth', '.npz', '.h5', '.ckpt',
|
|
124
|
+
// basi di dati e mappe sorgente generate
|
|
125
|
+
'.db', '.sqlite', '.sqlite3', '.mdb', '.map',
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
/** File senza estensione che nessuno ha scritto: li lascia il sistema operativo. */
|
|
129
|
+
const NOMI_ESCLUSI = new Set(['.ds_store', 'thumbs.db', 'desktop.ini']);
|
|
130
|
+
|
|
131
|
+
export const TETTO_FILE_PREDEFINITO = 1500;
|
|
132
|
+
export const PROFONDITA_PREDEFINITA = 8;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Ordine totale, stabile e indipendente dall'ambiente (decisione 3).
|
|
136
|
+
* Si confronta segmento per segmento — così un sottoalbero resta unito invece di essere
|
|
137
|
+
* spezzato dal separatore — e a parità di cartella i file vengono prima delle sottocartelle:
|
|
138
|
+
* chi legge vede cosa c'è QUI prima di scendere.
|
|
139
|
+
* ⛔ Mai `localeCompare`: dipende da ICU, e un ordine che cambia da una macchina all'altra
|
|
140
|
+
* rompe il prefisso della cache.
|
|
141
|
+
*/
|
|
142
|
+
export function confrontaPercorsi(a, b) {
|
|
143
|
+
const sa = String(a).split('/');
|
|
144
|
+
const sb = String(b).split('/');
|
|
145
|
+
const comuni = Math.min(sa.length, sb.length);
|
|
146
|
+
for (let i = 0; i < comuni; i += 1) {
|
|
147
|
+
if (sa[i] === sb[i]) continue;
|
|
148
|
+
const ultimoA = i === sa.length - 1;
|
|
149
|
+
const ultimoB = i === sb.length - 1;
|
|
150
|
+
if (ultimoA !== ultimoB) return ultimoA ? -1 : 1;
|
|
151
|
+
return sa[i] < sb[i] ? -1 : 1;
|
|
152
|
+
}
|
|
153
|
+
return sa.length - sb.length;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function insiemeMinuscolo(valore, predefinito) {
|
|
157
|
+
const grezzo = valore === undefined || valore === null ? predefinito : valore;
|
|
158
|
+
const voci = grezzo instanceof Set ? [...grezzo] : Array.isArray(grezzo) ? grezzo : [grezzo];
|
|
159
|
+
return new Set(voci.filter((v) => typeof v === 'string').map((v) => v.toLowerCase()));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function estensioneDi(nome) {
|
|
163
|
+
const punto = nome.lastIndexOf('.');
|
|
164
|
+
// `.gitignore` non ha estensione: è un nome che comincia col punto
|
|
165
|
+
return punto > 0 ? nome.slice(punto).toLowerCase() : '';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function ordinaVoci(voci) {
|
|
169
|
+
return [...voci].sort((a, b) => (a.name === b.name ? 0 : a.name < b.name ? -1 : 1));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Cammina l'albero e restituisce i percorsi dei file, potati e ordinati.
|
|
174
|
+
*
|
|
175
|
+
* @param {object} input
|
|
176
|
+
* @param {string} input.radice — cartella da cui partire (assoluta o relativa a chi chiama)
|
|
177
|
+
* @param {number} [input.profonditaMax=8] — quanti segmenti può avere il percorso di un file:
|
|
178
|
+
* `README.md` è 1, `src/a.mjs` è 2, `a/b/c/d/e/f.txt` è 6. Con 2 si riproduce esattamente la
|
|
179
|
+
* vista di oggi, quella che non vede nessuno dei 106 percorsi del corpus.
|
|
180
|
+
* @param {number} [input.tettoFile=1500] — vedi decisione (4)
|
|
181
|
+
* @param {string[]|Set<string>} [input.escludiCartelle] — nomi di cartella da non aprire
|
|
182
|
+
* @param {string[]|Set<string>} [input.escludiEstensioni] — estensioni da non elencare
|
|
183
|
+
* @param {object} [input.fs] — `{readdir, realpath, stat}`; si inietta, come negli altri moduli
|
|
184
|
+
* @param {(percorsoRelativo: string, info?: {cartella: boolean}) => boolean} [input.filtro]
|
|
185
|
+
* true = tieni. Riceve SIA i file SIA le cartelle (una cartella negata non si apre: è così
|
|
186
|
+
* che un `.gitignore` pota un sottoalbero intero senza camminarlo). Il secondo argomento
|
|
187
|
+
* dice quale dei due è; una funzione a un solo argomento continua a funzionare.
|
|
188
|
+
* @returns {Promise<{percorsi: string[], troncato: boolean, cartelleSaltate: number,
|
|
189
|
+
* fileEsclusi: number, dettaglio: object}>}
|
|
190
|
+
*/
|
|
191
|
+
export async function costruisciElencoProfondo({
|
|
192
|
+
radice,
|
|
193
|
+
profonditaMax = PROFONDITA_PREDEFINITA,
|
|
194
|
+
tettoFile = TETTO_FILE_PREDEFINITO,
|
|
195
|
+
escludiCartelle,
|
|
196
|
+
escludiEstensioni,
|
|
197
|
+
fs,
|
|
198
|
+
filtro,
|
|
199
|
+
} = {}) {
|
|
200
|
+
if (typeof radice !== 'string' || radice.length === 0 || radice.includes('\0')) {
|
|
201
|
+
throw new ElencoProfondoError('La cartella di partenza non è valida');
|
|
202
|
+
}
|
|
203
|
+
if (filtro !== undefined && typeof filtro !== 'function') {
|
|
204
|
+
throw new ElencoProfondoError('Il filtro deve essere una funzione');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const disco = { readdir, realpath, stat, ...(fs ?? {}) };
|
|
208
|
+
const cartelleEscluse = insiemeMinuscolo(escludiCartelle, CARTELLE_ESCLUSE_PREDEFINITE);
|
|
209
|
+
const estensioniEscluse = insiemeMinuscolo(escludiEstensioni, ESTENSIONI_ESCLUSE_PREDEFINITE);
|
|
210
|
+
const tetto = Number.isFinite(tettoFile) ? Math.max(0, Math.trunc(tettoFile)) : TETTO_FILE_PREDEFINITO;
|
|
211
|
+
const profondita = Number.isFinite(profonditaMax) ? Math.trunc(profonditaMax) : PROFONDITA_PREDEFINITA;
|
|
212
|
+
const tieni = (percorso, cartella) => (filtro ? filtro(percorso, { cartella }) !== false : true);
|
|
213
|
+
|
|
214
|
+
const percorsi = [];
|
|
215
|
+
const dettaglio = { perNome: 0, perProfondita: 0, perFiltro: 0, perCiclo: 0, illeggibili: 0, perEstensione: 0, fileFiltrati: 0 };
|
|
216
|
+
let troncato = false;
|
|
217
|
+
let fileEsclusi = 0;
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
* ⛔ Anello simbolico: seguire un link a una cartella genitore fa camminare per sempre — è un
|
|
221
|
+
* modo noto di piantare un processo (nodejs/node #51749 sulla `readdir` ricorsiva; la cura
|
|
222
|
+
* standard è confrontare i percorsi REALI, non quelli costruiti, letto il 10/09/2026). Qui si
|
|
223
|
+
* paga una `realpath` per cartella: una syscall contro una `readdir` intera, e l'unica
|
|
224
|
+
* alternativa — fidarsi del percorso costruito — non regge un link.
|
|
225
|
+
*
|
|
226
|
+
* ⛔ Ma si guarda la CATENA DEGLI ANTENATI, non un insieme globale di «già viste». La prima
|
|
227
|
+
* versione teneva un insieme globale, e la prova `ELENCO-ANELLO-05` l'ha bocciata: con due
|
|
228
|
+
* strade diverse verso la stessa cartella (un collegamento `scorciatoia` → `vera`) vinceva
|
|
229
|
+
* quella incontrata per prima in ordine alfabetico, e `vera/dentro.txt` — un percorso che sul
|
|
230
|
+
* disco funziona — spariva dall'elenco. Cioè esattamente la bugia che questo modulo esiste per
|
|
231
|
+
* non dire, prodotta dalla guardia contro i cicli. Un ciclo è tornare su un ANTENATO del
|
|
232
|
+
* proprio cammino; due rami che finiscono nello stesso posto non lo sono, e il cammino resta
|
|
233
|
+
* finito. Il prezzo è che una cartella raggiungibile per due strade si elenca due volte: è la
|
|
234
|
+
* verità sul disco, e costa qualche riga invece di un file invisibile.
|
|
235
|
+
*/
|
|
236
|
+
const radiceReale = await disco.realpath(radice).catch(() => radice);
|
|
237
|
+
|
|
238
|
+
const coda = [{ assoluto: radice, relativo: '', livello: 0, antenati: [radiceReale] }];
|
|
239
|
+
while (coda.length > 0 && !troncato) {
|
|
240
|
+
const corrente = coda.shift();
|
|
241
|
+
let voci;
|
|
242
|
+
try {
|
|
243
|
+
voci = await disco.readdir(corrente.assoluto, { withFileTypes: true });
|
|
244
|
+
} catch {
|
|
245
|
+
// Una cartella che non si lascia leggere (permessi, sparita nel frattempo) si salta e si
|
|
246
|
+
// CONTA: tacere qui vorrebbe dire dichiarare completo un elenco che non lo è.
|
|
247
|
+
dettaglio.illeggibili += 1;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const voce of ordinaVoci(voci)) {
|
|
252
|
+
const nome = typeof voce === 'string' ? voce : voce.name;
|
|
253
|
+
const relativo = corrente.relativo ? `${corrente.relativo}/${nome}` : nome;
|
|
254
|
+
const assoluto = join(corrente.assoluto, nome);
|
|
255
|
+
|
|
256
|
+
let eCartella = Boolean(voce.isDirectory?.());
|
|
257
|
+
let eFile = Boolean(voce.isFile?.());
|
|
258
|
+
if (voce.isSymbolicLink?.()) {
|
|
259
|
+
// Un link non dice da solo cosa c'è dall'altra parte: si guarda.
|
|
260
|
+
const informazioni = await disco.stat(assoluto).catch(() => null);
|
|
261
|
+
if (!informazioni) { dettaglio.illeggibili += 1; continue; } // link rotto: onesto, non fatale
|
|
262
|
+
eCartella = Boolean(informazioni.isDirectory?.());
|
|
263
|
+
eFile = Boolean(informazioni.isFile?.());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (eCartella) {
|
|
267
|
+
if (cartelleEscluse.has(nome.toLowerCase())) { dettaglio.perNome += 1; continue; }
|
|
268
|
+
if (!tieni(relativo, true)) { dettaglio.perFiltro += 1; continue; }
|
|
269
|
+
// Si apre solo se i file che contiene starebbero dentro la profondità chiesta.
|
|
270
|
+
if (corrente.livello + 2 > profondita) { dettaglio.perProfondita += 1; continue; }
|
|
271
|
+
const reale = await disco.realpath(assoluto).catch(() => assoluto);
|
|
272
|
+
if (corrente.antenati.includes(reale)) { dettaglio.perCiclo += 1; continue; }
|
|
273
|
+
coda.push({ assoluto, relativo, livello: corrente.livello + 1, antenati: [...corrente.antenati, reale] });
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!eFile) continue; // socket, fifo, dispositivo: non è un file da nominare a nessuno
|
|
278
|
+
|
|
279
|
+
if (NOMI_ESCLUSI.has(nome.toLowerCase())) { fileEsclusi += 1; dettaglio.perEstensione += 1; continue; }
|
|
280
|
+
if (estensioniEscluse.has(estensioneDi(nome))) { fileEsclusi += 1; dettaglio.perEstensione += 1; continue; }
|
|
281
|
+
if (!tieni(relativo, false)) { fileEsclusi += 1; dettaglio.fileFiltrati += 1; continue; }
|
|
282
|
+
|
|
283
|
+
if (percorsi.length >= tetto) { troncato = true; break; }
|
|
284
|
+
percorsi.push(relativo);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
percorsi.sort(confrontaPercorsi);
|
|
289
|
+
const cartelleSaltate = dettaglio.perNome + dettaglio.perProfondita + dettaglio.perFiltro
|
|
290
|
+
+ dettaglio.perCiclo + dettaglio.illeggibili;
|
|
291
|
+
return { percorsi, troncato, cartelleSaltate, fileEsclusi, dettaglio };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Il testo da mettere nel prompt, con la sua intestazione.
|
|
296
|
+
*
|
|
297
|
+
* ⛔ Se il tetto ha morso, lo DICE — in testa e in coda. In testa perché sia la prima cosa
|
|
298
|
+
* letta; in coda perché dopo millecinquecento righe la prima riga è lontana, e l'ultima cosa
|
|
299
|
+
* letta prima della domanda è quella che pesa. Un elenco tagliato che sembra completo è
|
|
300
|
+
* peggio di nessun elenco: porta a concludere che un file non esiste.
|
|
301
|
+
*
|
|
302
|
+
* ⛔ Nell'intestazione va il NOME della cartella, mai il percorso intero: un percorso assoluto
|
|
303
|
+
* contiene il nome della persona e questo testo finisce dentro un prompt, cioè fuori dalla
|
|
304
|
+
* macchina. È la lezione `cancello-4-non-guardava-tutto-mobile` — un percorso personale
|
|
305
|
+
* pubblicato in chiaro perché nessuno aveva guardato dove finiva.
|
|
306
|
+
*
|
|
307
|
+
* @param {string[]} percorsi
|
|
308
|
+
* @param {{troncato?: boolean, radice?: string, fileEsclusi?: number}} [stato] — accetta
|
|
309
|
+
* direttamente il risultato di `costruisciElencoProfondo`
|
|
310
|
+
* @returns {string}
|
|
311
|
+
*/
|
|
312
|
+
export function testoElenco(percorsi = [], { troncato = false, radice = '', fileEsclusi = 0 } = {}) {
|
|
313
|
+
const elenco = Array.isArray(percorsi) ? percorsi.filter((p) => typeof p === 'string') : [];
|
|
314
|
+
const nome = basename(String(radice ?? '').replace(/[\\/]+$/, '')) || 'la cartella di lavoro';
|
|
315
|
+
|
|
316
|
+
if (elenco.length === 0) {
|
|
317
|
+
return troncato
|
|
318
|
+
? `Nessun file da mostrare per «${nome}»: l'elenco si è fermato prima di raccoglierne uno.`
|
|
319
|
+
: `Nessun file da mostrare per «${nome}».`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const righe = [];
|
|
323
|
+
if (troncato) {
|
|
324
|
+
righe.push(`⚠ ELENCO INCOMPLETO — mi sono fermato a ${elenco.length} percorsi, l'albero ne ha altri.`);
|
|
325
|
+
}
|
|
326
|
+
righe.push(`File di «${nome}» — ${elenco.length} percorsi, dalla cartella principale in giù.`);
|
|
327
|
+
if (fileEsclusi > 0) {
|
|
328
|
+
righe.push(`(${fileEsclusi} file non compaiono: immagini, archivi, pesi dei modelli e simili.)`);
|
|
329
|
+
}
|
|
330
|
+
righe.push('');
|
|
331
|
+
righe.push(...elenco);
|
|
332
|
+
if (troncato) {
|
|
333
|
+
righe.push('');
|
|
334
|
+
righe.push('⚠ Fine di un elenco INCOMPLETO: se un file non compare qui sopra non vuol dire che non esista — cercalo prima di dire che manca.');
|
|
335
|
+
}
|
|
336
|
+
return righe.join('\n');
|
|
337
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⭐⭐⭐ LE FAVICON DELLE FONTI — prese dal server, una volta, e tenute su disco.
|
|
3
|
+
*
|
|
4
|
+
* Owner, 10/09: «i favicon dei siti delle fonti non ci sono (il mobile l'ha già fatto)».
|
|
5
|
+
*
|
|
6
|
+
* ## ⛔ Perché non si scaricano dal browser, ed è il mobile stesso a dirlo
|
|
7
|
+
*
|
|
8
|
+
* `mobile/src/composables/useTalosSourceCardIcons.ts`, letto oggi: «fetching a favicon at display
|
|
9
|
+
* time is a request to every site every time the surface is opened» — ed è la ragione per cui là la
|
|
10
|
+
* pillola mostrava solo le lettere finché non è esistito un archivio di card su disco. Aprire una
|
|
11
|
+
* conversazione di mesi fa non deve bussare a ogni sito che quella conversazione ha citato.
|
|
12
|
+
*
|
|
13
|
+
* Sul desktop quell'archivio non c'è. Ma c'è una cosa che il telefono non ha: **un server nostro**.
|
|
14
|
+
*
|
|
15
|
+
* ⇒ Il browser chiede l'icona SOLO a noi (`/api/v1/favicon?dominio=…`); il server la prende una
|
|
16
|
+
* volta e la tiene su disco. Da quel momento nessuno esce più. È l'architettura di SearXNG,
|
|
17
|
+
* verificata prima di scriverla (ricerca 10/09/2026, docs.searxng.org «Favicons»): «To protect the
|
|
18
|
+
* privacy of users, favicons are provided via a proxy… A cache has been developed to massively
|
|
19
|
+
* reduce both incoming and outgoing requests.»
|
|
20
|
+
*
|
|
21
|
+
* ⛔ E ciò che il server NON fa, per scelta: non segue redirect fuori dal dominio chiesto, non
|
|
22
|
+
* accetta percorsi arbitrari (solo un dominio), non serve niente che non sia un'immagine piccola.
|
|
23
|
+
* Un proxy che scarica quello che gli dici è una porta aperta, non una comodità.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { createHash } from 'node:crypto';
|
|
27
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
|
|
30
|
+
/** Un'icona è piccola per definizione: oltre questo non è un'icona, è qualcos'altro. */
|
|
31
|
+
export const MAX_BYTE_ICONA = 100 * 1024;
|
|
32
|
+
|
|
33
|
+
/** Quanto a lungo vale un'icona presa. Un mese: i siti cambiano logo di rado. */
|
|
34
|
+
export const VALIDITA_MS = 30 * 24 * 60 * 60 * 1000;
|
|
35
|
+
|
|
36
|
+
const TIMEOUT_MS = 6000;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* ⛔ Solo un dominio, e con una forma dichiarata: niente porte, niente percorsi, niente utente:password.
|
|
40
|
+
* Ciò che non combacia non viene «ripulito» — viene rifiutato. Una sanificazione permissiva è il
|
|
41
|
+
* modo classico di trasformare un proxy in una porta aperta.
|
|
42
|
+
*/
|
|
43
|
+
const DOMINIO_VALIDO = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
44
|
+
|
|
45
|
+
export function dominioAmmesso(raw) {
|
|
46
|
+
const d = String(raw ?? '').trim().toLowerCase();
|
|
47
|
+
if (!d || d.length > 253 || !DOMINIO_VALIDO.test(d)) return null;
|
|
48
|
+
/* ⛔ Niente rete interna: un proxy che accetta «localhost» fa bussare il server a sé stesso. */
|
|
49
|
+
if (/^(localhost|127\.|10\.|192\.168\.|169\.254\.|0\.)/.test(d) || d.endsWith('.local') || d.endsWith('.internal')) return null;
|
|
50
|
+
return d;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const TIPI = new Map([
|
|
54
|
+
['image/png', '.png'], ['image/x-icon', '.ico'], ['image/vnd.microsoft.icon', '.ico'],
|
|
55
|
+
['image/svg+xml', '.svg'], ['image/jpeg', '.jpg'], ['image/webp', '.webp'], ['image/gif', '.gif'],
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
const nomeDiCache = (dominio) => `${createHash('sha256').update(dominio).digest('hex').slice(0, 24)}.json`;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Prende l'icona di un dominio: prima dalla cache su disco, poi dalla rete, una volta sola.
|
|
62
|
+
*
|
|
63
|
+
* @returns {Promise<{tipo: string, byte: Buffer, daCache: boolean}|null>} `null` se non c'è
|
|
64
|
+
*/
|
|
65
|
+
export async function iconaDelDominio(dominioGrezzo, { cartellaCache, fetchFn = globalThis.fetch, adesso = () => Date.now() } = {}) {
|
|
66
|
+
const dominio = dominioAmmesso(dominioGrezzo);
|
|
67
|
+
if (!dominio || !cartellaCache) return null;
|
|
68
|
+
|
|
69
|
+
const file = join(cartellaCache, nomeDiCache(dominio));
|
|
70
|
+
try {
|
|
71
|
+
const salvato = JSON.parse(await readFile(file, 'utf8'));
|
|
72
|
+
if (salvato && adesso() - salvato.quando < VALIDITA_MS) {
|
|
73
|
+
/* ⛔ Anche un «non ce l'ha» si tiene: senza, ogni apertura riprova per ogni sito senza icona. */
|
|
74
|
+
if (!salvato.base64) return null;
|
|
75
|
+
return { tipo: salvato.tipo, byte: Buffer.from(salvato.base64, 'base64'), daCache: true };
|
|
76
|
+
}
|
|
77
|
+
} catch { /* niente in cache: si prende */ }
|
|
78
|
+
|
|
79
|
+
let trovata = null;
|
|
80
|
+
try {
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
83
|
+
try {
|
|
84
|
+
const risposta = await fetchFn(`https://${dominio}/favicon.ico`, {
|
|
85
|
+
signal: controller.signal,
|
|
86
|
+
/* ⛔ `redirect: 'error'`: un redirect porterebbe altrove, e «altrove» è esattamente ciò che un
|
|
87
|
+
proxy non deve seguire per conto di chi lo chiama. */
|
|
88
|
+
redirect: 'error',
|
|
89
|
+
headers: { accept: 'image/*' },
|
|
90
|
+
});
|
|
91
|
+
if (risposta.ok) {
|
|
92
|
+
const tipo = String(risposta.headers?.get?.('content-type') ?? '').split(';')[0].trim().toLowerCase();
|
|
93
|
+
if (TIPI.has(tipo)) {
|
|
94
|
+
const byte = Buffer.from(await risposta.arrayBuffer());
|
|
95
|
+
if (byte.length > 0 && byte.length <= MAX_BYTE_ICONA) trovata = { tipo, byte };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} finally {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
}
|
|
101
|
+
} catch { /* il sito non risponde, non ha l'icona, o il redirect esce: nessuna icona, e si ricorda */ }
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await mkdir(cartellaCache, { recursive: true });
|
|
105
|
+
await writeFile(file, JSON.stringify({
|
|
106
|
+
quando: adesso(),
|
|
107
|
+
tipo: trovata?.tipo ?? null,
|
|
108
|
+
base64: trovata ? trovata.byte.toString('base64') : null,
|
|
109
|
+
}), 'utf8');
|
|
110
|
+
} catch { /* cache non scrivibile: si serve lo stesso, si riproverà */ }
|
|
111
|
+
|
|
112
|
+
return trovata ? { ...trovata, daCache: false } : null;
|
|
113
|
+
}
|