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,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser-stream.mjs — M2 (07/09/2026). Trasmette lo schermo di un Chromium di
|
|
3
|
+
* sistema dentro la nostra pagina, e riporta indietro i gesti della persona.
|
|
4
|
+
*
|
|
5
|
+
* È il ripiego B della vista Browser: quando la pagina vieta la cornice
|
|
6
|
+
* (`X-Frame-Options`, `frame-ancestors`) non c'è niente da riscrivere, si
|
|
7
|
+
* guarda il browser vero. Lo schermo esce dal compositor già compresso
|
|
8
|
+
* (`Page.startScreencast`) invece di serializzare il DOM a ogni scatto: è la
|
|
9
|
+
* stessa strada di Browserbase, Steel e Cloudflare Browser Rendering, e la
|
|
10
|
+
* stessa che il pannello «Screencast» di DevTools usa da sempre.
|
|
11
|
+
*
|
|
12
|
+
* ⛔ La pagina di terzi è SEMPRE contenuto non affidabile: qui dentro non c'è
|
|
13
|
+
* nessuna nostra API, solo byte di immagine in uscita e coordinate in entrata.
|
|
14
|
+
* Il client CDP arriva come parametro (`cdp`), non lo costruisce questo modulo:
|
|
15
|
+
* chi apre il browser (M1) decide profilo, porta e filtro degli indirizzi.
|
|
16
|
+
*
|
|
17
|
+
* FONTI, lette il 07/09/2026:
|
|
18
|
+
* - CDP «Page» (chromedevtools.github.io/devtools-protocol/tot/Page/):
|
|
19
|
+
* `Page.startScreencast{format,quality,maxWidth,maxHeight,everyNthFrame,maxFramesInFlight}`;
|
|
20
|
+
* `maxFramesInFlight` vale 3 di default — è il numero di fotogrammi che
|
|
21
|
+
* Chrome manda PRIMA di pretendere una conferma, quindi senza
|
|
22
|
+
* `Page.screencastFrameAck` il flusso si ferma dopo pochi fotogrammi.
|
|
23
|
+
* `ScreencastFrameMetadata{offsetTop,pageScaleFactor,deviceWidth,deviceHeight,scrollOffsetX,scrollOffsetY,timestamp}`:
|
|
24
|
+
* le prime quattro in pixel indipendenti dal dispositivo (DIP), lo scorrimento
|
|
25
|
+
* in pixel CSS.
|
|
26
|
+
* - CDP «Input»: `dispatchMouseEvent{type,x,y,button,buttons,clickCount,deltaX,deltaY,modifiers,pointerType}`
|
|
27
|
+
* con x/y «relative to the main frame's viewport in CSS pixels»; bit dei
|
|
28
|
+
* modificatori Alt=1, Ctrl=2, Meta=4, Shift=8; `buttons` sinistro=1, destro=2,
|
|
29
|
+
* centrale=4.
|
|
30
|
+
* - CDP «Emulation»: `setDeviceMetricsOverride{width,height,deviceScaleFactor,mobile,scale}`,
|
|
31
|
+
* width/height fra 0 e 10.000.000 dove 0 disattiva l'override;
|
|
32
|
+
* `clearDeviceMetricsOverride` non prende parametri.
|
|
33
|
+
* - devtools-frontend `panels/screencast/ScreencastView.ts` e `InputModel.ts`:
|
|
34
|
+
* la conversione VERA delle coordinate è
|
|
35
|
+
* `x = round(offsetX / screenZoom)`, `y = round(offsetY / screenZoom - screenOffsetTop)`
|
|
36
|
+
* con `screenZoom = larghezzaDisegnata / metadata.deviceWidth`. Nessuna
|
|
37
|
+
* proporzione inventata: lo scorrimento e `pageScaleFactor` NON entrano nel
|
|
38
|
+
* clic (li applica Chrome), servono a dire dov'è il punto nel documento.
|
|
39
|
+
* - chrome-debugging-protocol, thread «Dispatch key events to fill an input
|
|
40
|
+
* field» (Ophir Back): per scrivere un carattere servono `keyDown` → `char` →
|
|
41
|
+
* `keyUp`, e il campo `text` va riempito SOLO nell'evento `char` — se lo si
|
|
42
|
+
* mette anche su `keyDown` il carattere si scrive due volte.
|
|
43
|
+
* - puppeteer `USKeyboardLayout.ts`: codici Windows dei tasti non stampabili, e
|
|
44
|
+
* il testo di Invio che è `\r` (non `\n`).
|
|
45
|
+
* - chrome-remote-interface #241: `Page.screencastFrameAck` vuole un INTERO;
|
|
46
|
+
* il numero del fotogramma arriva nell'evento come `sessionId`, che NON è la
|
|
47
|
+
* sessione CDP della scheda — due cose diverse con lo stesso nome.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/* ─────────────────────────── i tetti ───────────────────────────
|
|
51
|
+
* Lo stream passa DENTRO il nostro server prima di arrivare alla pagina: un
|
|
52
|
+
* fotogramma non compresso a 60 al secondo lo affogherebbe. I tetti stanno qui,
|
|
53
|
+
* non nelle opzioni di chi chiama, così nessuna chiamata può alzarli.
|
|
54
|
+
*/
|
|
55
|
+
export const QUALITA_PREDEFINITA = 60;
|
|
56
|
+
export const QUALITA_MASSIMA = 85;
|
|
57
|
+
export const QUALITA_MINIMA = 10;
|
|
58
|
+
/* ⛔ 08/09/2026: questi due erano 1280 e 800, e finivano dritti in `maxWidth`/`maxHeight` di
|
|
59
|
+
* `Page.startScreencast`. Ma quei due parametri sono un TETTO sul fotogramma, non la misura della
|
|
60
|
+
* pagina: la misura la decide il viewport (`Emulation.setDeviceMetricsOverride`). Con un tetto a
|
|
61
|
+
* 800 un riquadro alto 830 riceveva un fotogramma 893×800 — misurato sul 4174 — e restava una
|
|
62
|
+
* banda scoperta che nessun ridimensionamento poteva togliere, perche' `startScreencast` non si
|
|
63
|
+
* ri-negozia da solo. ⇒ il tetto sta al tetto; a dire quanto e' grande la pagina ci pensa il
|
|
64
|
+
* viewport, che invece si cambia a caldo. */
|
|
65
|
+
export const LARGHEZZA_PREDEFINITA = 2560;
|
|
66
|
+
export const ALTEZZA_PREDEFINITA = 1600;
|
|
67
|
+
export const LARGHEZZA_MASSIMA = 2560; /* 08/9: la vista si estende quanto il riquadro, e un 1440p ci sta */
|
|
68
|
+
export const ALTEZZA_MASSIMA = 1600;
|
|
69
|
+
/* ⛔ 08/09/2026, owner: «il frame rate è bassissimo». MISURATO prima di toccare, con una pagina
|
|
70
|
+
* animata e tre configurazioni a confronto (C35/C35-bis nello scratchpad): il trasporto CDP su
|
|
71
|
+
* 127.0.0.1 consegna ~100 fotogrammi al secondo, identici con la finestra davanti, con la finestra
|
|
72
|
+
* COPERTA e in headless. Il collo NON era Chrome: era questo tetto a 15, che buttava l'85% dei
|
|
73
|
+
* fotogrammi che Chrome aveva gia' prodotto E compresso in JPEG — lavoro pagato e gettato.
|
|
74
|
+
*
|
|
75
|
+
* ⭐ La sonda e' stata provata prima di crederle: con `everyNthFrame:3` il conteggio scende a un
|
|
76
|
+
* terzo esatto (499 → 166) e senza conferma si ferma a 3 fotogrammi, come dice il protocollo.
|
|
77
|
+
* Tre numeri identici sono un allarme, non una conferma: quello era il caso.
|
|
78
|
+
*
|
|
79
|
+
* ⛔ Perche' 60 e non 100: oltre il refresh dello schermo i fotogrammi in piu' non si vedono, e
|
|
80
|
+
* ognuno costa una compressione JPEG lato Chrome e un disegno lato pagina. 60 e' quanto un
|
|
81
|
+
* monitor mostra; il tetto a 90 lascia spazio agli schermi piu' veloci senza aprire la porta a
|
|
82
|
+
* una richiesta che chieda l'impossibile. */
|
|
83
|
+
export const FOTOGRAMMI_AL_SECONDO_PREDEFINITI = 60;
|
|
84
|
+
export const FOTOGRAMMI_AL_SECONDO_MASSIMI = 90;
|
|
85
|
+
/* ⛔ 08/09/2026, owner: «alza al 60 fps il minimo del browser headless reale». Non e solo il valore
|
|
86
|
+
di partenza: e il PAVIMENTO. Nessuna chiamata puo chiedere una trasmissione piu lenta di quanto
|
|
87
|
+
uno schermo mostra — un ripiego a 15 o a 30 tornerebbe a essere la fluidita che ha fatto dire
|
|
88
|
+
«il frame rate e bassissimo», e stavolta arriverebbe da un parametro invece che da un tetto. */
|
|
89
|
+
export const FOTOGRAMMI_AL_SECONDO_MINIMI = 60;
|
|
90
|
+
/** Quanti fotogrammi Chrome può mandare prima di pretendere una conferma. Il default del protocollo è 3; 2 tiene la latenza più bassa a parità di banda. */
|
|
91
|
+
export const FOTOGRAMMI_IN_VOLO = 2;
|
|
92
|
+
|
|
93
|
+
const TASTI_MOUSE = new Map([
|
|
94
|
+
['sinistro', { nome: 'left', bit: 1 }],
|
|
95
|
+
['left', { nome: 'left', bit: 1 }],
|
|
96
|
+
['destro', { nome: 'right', bit: 2 }],
|
|
97
|
+
['right', { nome: 'right', bit: 2 }],
|
|
98
|
+
['centrale', { nome: 'middle', bit: 4 }],
|
|
99
|
+
['middle', { nome: 'middle', bit: 4 }],
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* I tasti non stampabili, coi codici virtuali Windows di `USKeyboardLayout.ts`
|
|
104
|
+
* (puppeteer). `testo` c'è solo dove il tasto scrive davvero qualcosa: Invio
|
|
105
|
+
* scrive `\r`, le frecce non scrivono niente.
|
|
106
|
+
*/
|
|
107
|
+
export const TASTI_SPECIALI = new Map([
|
|
108
|
+
['Enter', { codice: 13, testo: '\r' }],
|
|
109
|
+
['Backspace', { codice: 8, testo: '' }],
|
|
110
|
+
['Tab', { codice: 9, testo: '' }],
|
|
111
|
+
['Escape', { codice: 27, testo: '' }],
|
|
112
|
+
['ArrowLeft', { codice: 37, testo: '' }],
|
|
113
|
+
['ArrowUp', { codice: 38, testo: '' }],
|
|
114
|
+
['ArrowRight', { codice: 39, testo: '' }],
|
|
115
|
+
['ArrowDown', { codice: 40, testo: '' }],
|
|
116
|
+
['Delete', { codice: 46, testo: '' }],
|
|
117
|
+
['Home', { codice: 36, testo: '' }],
|
|
118
|
+
['End', { codice: 35, testo: '' }],
|
|
119
|
+
['PageUp', { codice: 33, testo: '' }],
|
|
120
|
+
['PageDown', { codice: 34, testo: '' }],
|
|
121
|
+
[' ', { codice: 32, testo: ' ' }],
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const numeroFinito = (valore, ripiego = 0) => (Number.isFinite(Number(valore)) ? Number(valore) : ripiego);
|
|
125
|
+
const dentro = (valore, minimo, massimo) => Math.min(massimo, Math.max(minimo, valore));
|
|
126
|
+
const positivo = (valore) => {
|
|
127
|
+
const n = Number(valore);
|
|
128
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Porta le opzioni di chi chiama dentro i tetti. Esportata perché è il punto in
|
|
133
|
+
* cui si legge, senza avviare niente, quanto costerà lo stream.
|
|
134
|
+
* @param {{qualita?:number, larghezzaMax?:number, altezzaMax?:number, ogniNFrame?:number, fotogrammiAlSecondo?:number}} [opzioni]
|
|
135
|
+
*/
|
|
136
|
+
export function opzioniTrasmissione(opzioni = {}) {
|
|
137
|
+
const o = opzioni && typeof opzioni === 'object' ? opzioni : {};
|
|
138
|
+
const qualita = Math.round(dentro(numeroFinito(o.qualita, QUALITA_PREDEFINITA), QUALITA_MINIMA, QUALITA_MASSIMA));
|
|
139
|
+
const larghezzaMax = Math.round(dentro(numeroFinito(o.larghezzaMax, LARGHEZZA_PREDEFINITA), 64, LARGHEZZA_MASSIMA));
|
|
140
|
+
const altezzaMax = Math.round(dentro(numeroFinito(o.altezzaMax, ALTEZZA_PREDEFINITA), 64, ALTEZZA_MASSIMA));
|
|
141
|
+
const ogniNFrame = Math.round(dentro(numeroFinito(o.ogniNFrame, 1), 1, 10));
|
|
142
|
+
const fotogrammiAlSecondo = dentro(numeroFinito(o.fotogrammiAlSecondo, FOTOGRAMMI_AL_SECONDO_PREDEFINITI), FOTOGRAMMI_AL_SECONDO_MINIMI, FOTOGRAMMI_AL_SECONDO_MASSIMI);
|
|
143
|
+
return { qualita, larghezzaMax, altezzaMax, ogniNFrame, fotogrammiAlSecondo, intervalloMinimoMs: 1000 / fotogrammiAlSecondo };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Il numero del fotogramma da confermare, o `null` se questo evento non è un
|
|
148
|
+
* fotogramma. ⛔ È il campo `sessionId` dell'evento, e non ha NIENTE a che
|
|
149
|
+
* vedere con la sessione CDP della scheda: `Page.screencastFrameAck` vuole
|
|
150
|
+
* questo intero (chrome-remote-interface #241).
|
|
151
|
+
*/
|
|
152
|
+
export function numeroDelFotogramma(evento) {
|
|
153
|
+
if (!evento || typeof evento !== 'object') return null;
|
|
154
|
+
const n = Number(evento.sessionId);
|
|
155
|
+
if (!Number.isFinite(n) || !Number.isInteger(n)) return null;
|
|
156
|
+
if (typeof evento.data !== 'string' || evento.data.length === 0) return null;
|
|
157
|
+
return n;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Accende la trasmissione e resta ad ascoltare i fotogrammi.
|
|
162
|
+
*
|
|
163
|
+
* ⛔ La conferma parte SEMPRE, per ogni fotogramma davvero arrivato: anche se
|
|
164
|
+
* `onFrame` lancia, anche se il fotogramma viene saltato dal tetto sui
|
|
165
|
+
* fotogrammi al secondo. Saltare una conferma significa fermare lo stream dopo
|
|
166
|
+
* `maxFramesInFlight` fotogrammi, cioè uno schermo che si congela senza un
|
|
167
|
+
* errore da nessuna parte.
|
|
168
|
+
*
|
|
169
|
+
* @param {{invia:Function, su:Function}} cdp client CDP (lo dà M1)
|
|
170
|
+
* @param {string|undefined} sessionId la sessione CDP della scheda
|
|
171
|
+
* @param {object} opzioni vedi `opzioniTrasmissione`; `orologio` solo per le prove
|
|
172
|
+
* @param {(fotogramma:{dati:string, formato:string, numeroFrame:number, metadati:object, quando:number})=>void} onFrame
|
|
173
|
+
*/
|
|
174
|
+
export async function avviaTrasmissione(cdp, sessionId, opzioni = {}, onFrame = () => {}) {
|
|
175
|
+
const scelte = opzioniTrasmissione(opzioni);
|
|
176
|
+
const orologio = typeof opzioni?.orologio === 'function' ? opzioni.orologio : () => Date.now();
|
|
177
|
+
const conteggi = { arrivati: 0, consegnati: 0, saltati: 0, confermati: 0, ignorati: 0, errori: 0 };
|
|
178
|
+
let viva = true;
|
|
179
|
+
let ultimoConsegnato = -Infinity;
|
|
180
|
+
|
|
181
|
+
const gestore = async (evento, contesto) => {
|
|
182
|
+
if (!viva) return;
|
|
183
|
+
/* Un client CDP «flat» tiene TUTTE le schede su una connessione sola e passa
|
|
184
|
+
* la sessione dell'evento come secondo argomento: i fotogrammi di un'altra
|
|
185
|
+
* scheda non sono nostri e non si confermano — confermarli ruberebbe il
|
|
186
|
+
* credito a chi li aspetta e lascerebbe l'altro flusso fermo.
|
|
187
|
+
* ⛔ La forma del secondo argomento cambia da client a client: M1
|
|
188
|
+
* (`browser-vivo.mjs`, `creaClientCdp`) passa `{ sessionId, metodo }`, altri
|
|
189
|
+
* passano la stringa nuda. Si accettano tutt'e due, e quando non c'è non si
|
|
190
|
+
* filtra niente. */
|
|
191
|
+
if (sessioneAltrui(contesto, sessionId)) return;
|
|
192
|
+
const numeroFrame = numeroDelFotogramma(evento);
|
|
193
|
+
if (numeroFrame === null) { conteggi.ignorati += 1; return; } // niente conferma per ciò che non è un fotogramma
|
|
194
|
+
conteggi.arrivati += 1;
|
|
195
|
+
try {
|
|
196
|
+
const adesso = numeroFinito(orologio(), 0);
|
|
197
|
+
if (adesso - ultimoConsegnato >= scelte.intervalloMinimoMs) {
|
|
198
|
+
ultimoConsegnato = adesso;
|
|
199
|
+
conteggi.consegnati += 1;
|
|
200
|
+
onFrame({ dati: evento.data, formato: 'jpeg', numeroFrame, metadati: evento.metadata || {}, quando: adesso });
|
|
201
|
+
} else {
|
|
202
|
+
conteggi.saltati += 1; // il tetto sui fotogrammi al secondo: si butta l'immagine, MAI la conferma
|
|
203
|
+
}
|
|
204
|
+
} catch (errore) {
|
|
205
|
+
conteggi.errori += 1; // un consumatore che lancia non deve poter fermare lo stream
|
|
206
|
+
} finally {
|
|
207
|
+
try {
|
|
208
|
+
await cdp.invia('Page.screencastFrameAck', { sessionId: numeroFrame }, sessionId);
|
|
209
|
+
conteggi.confermati += 1;
|
|
210
|
+
} catch {
|
|
211
|
+
conteggi.errori += 1; // la scheda può essere già chiusa: non è un motivo per lanciare qui
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/* Gli eventi del dominio Page arrivano solo dopo `Page.enable`, e l'ascolto si
|
|
217
|
+
* registra PRIMA di `startScreencast`: fra le due chiamate Chrome manda già
|
|
218
|
+
* fotogrammi, e uno perso è un fotogramma non confermato. */
|
|
219
|
+
await cdp.invia('Page.enable', {}, sessionId);
|
|
220
|
+
const sgancia = cdp.su('Page.screencastFrame', gestore);
|
|
221
|
+
await cdp.invia('Page.startScreencast', {
|
|
222
|
+
format: 'jpeg',
|
|
223
|
+
quality: scelte.qualita,
|
|
224
|
+
maxWidth: scelte.larghezzaMax,
|
|
225
|
+
maxHeight: scelte.altezzaMax,
|
|
226
|
+
everyNthFrame: scelte.ogniNFrame,
|
|
227
|
+
maxFramesInFlight: FOTOGRAMMI_IN_VOLO,
|
|
228
|
+
}, sessionId);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
opzioni: scelte,
|
|
232
|
+
conteggi,
|
|
233
|
+
/** Stacca l'ascolto senza toccare il browser (per una riconnessione). */
|
|
234
|
+
sgancia() {
|
|
235
|
+
viva = false; // ⛔ vale anche se `su` non restituisce niente: da qui il gestore è inerte
|
|
236
|
+
if (typeof sgancia === 'function') { try { sgancia(); } catch { /* già staccato */ } }
|
|
237
|
+
},
|
|
238
|
+
async ferma() {
|
|
239
|
+
this.sgancia();
|
|
240
|
+
return fermaTrasmissione(cdp, sessionId);
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Spegne la trasmissione. Non lancia: si chiama anche quando la scheda è già
|
|
247
|
+
* morta, e in quel caso «fermata» è comunque il risultato che si voleva.
|
|
248
|
+
*/
|
|
249
|
+
export async function fermaTrasmissione(cdp, sessionId) {
|
|
250
|
+
try {
|
|
251
|
+
await cdp.invia('Page.stopScreencast', {}, sessionId);
|
|
252
|
+
return { fermata: true, motivo: null };
|
|
253
|
+
} catch (errore) {
|
|
254
|
+
return { fermata: false, motivo: String(errore?.message || errore) };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Dal punto toccato sul NOSTRO canvas al punto vero nella pagina.
|
|
260
|
+
*
|
|
261
|
+
* La formula è quella di DevTools (`ScreencastView.convertIntoScreenSpace`):
|
|
262
|
+
* si divide per lo zoom e POI si toglie `offsetTop`, perché `offsetTop` è già
|
|
263
|
+
* espresso in DIP dello schermo emulato (la fascia sopra la pagina), non in
|
|
264
|
+
* pixel del canvas. Lo zoom è il rapporto fra ciò che abbiamo disegnato e
|
|
265
|
+
* `deviceWidth`; si prende il minimo fra i due assi perché l'immagine si adatta
|
|
266
|
+
* dentro il canvas mantenendo le proporzioni, e ciò che avanza è bordo nero.
|
|
267
|
+
*
|
|
268
|
+
* ⛔ `scrollOffsetX/Y` e `pageScaleFactor` NON entrano nel clic: `Input.dispatchMouseEvent`
|
|
269
|
+
* vuole coordinate «relative to the main frame's viewport», e lo scorrimento lo
|
|
270
|
+
* applica Chrome. Servono a dire dove sta il punto nel DOCUMENTO, che è ciò che
|
|
271
|
+
* serve per appuntarci sopra una nota.
|
|
272
|
+
*
|
|
273
|
+
* @param {{x:number,y:number}} punto sul canvas
|
|
274
|
+
* @param {{larghezzaCanvas:number, altezzaCanvas:number, metadatiFrame:object}} vista
|
|
275
|
+
* @returns {{x:number,y:number,documentoX:number,documentoY:number,scala:number,fuoriBordo:boolean}}
|
|
276
|
+
*/
|
|
277
|
+
export function coordinateVerso(punto, vista = {}) {
|
|
278
|
+
const m = vista.metadatiFrame && typeof vista.metadatiFrame === 'object' ? vista.metadatiFrame : {};
|
|
279
|
+
const larghezzaCanvas = positivo(vista.larghezzaCanvas) ?? 1;
|
|
280
|
+
const altezzaCanvas = positivo(vista.altezzaCanvas) ?? 1;
|
|
281
|
+
/* Metadati assurdi (zero, negativi, NaN, assenti) non devono produrre un clic
|
|
282
|
+
* fuori pagina: si ripiega sulla misura del canvas, cioè zoom 1. */
|
|
283
|
+
const larghezzaSchermo = positivo(m.deviceWidth) ?? larghezzaCanvas;
|
|
284
|
+
const altezzaSchermo = positivo(m.deviceHeight) ?? altezzaCanvas;
|
|
285
|
+
const offsetTop = dentro(numeroFinito(m.offsetTop, 0), 0, altezzaSchermo);
|
|
286
|
+
const altezzaUtile = Math.max(0, altezzaSchermo - offsetTop);
|
|
287
|
+
|
|
288
|
+
let scala = Math.min(larghezzaCanvas / larghezzaSchermo, altezzaCanvas / altezzaSchermo);
|
|
289
|
+
if (!Number.isFinite(scala) || scala <= 0) scala = 1;
|
|
290
|
+
|
|
291
|
+
const xCanvas = numeroFinito(punto?.x, 0);
|
|
292
|
+
const yCanvas = numeroFinito(punto?.y, 0);
|
|
293
|
+
const xGrezza = xCanvas / scala;
|
|
294
|
+
const yGrezza = yCanvas / scala - offsetTop;
|
|
295
|
+
const x = Math.round(dentro(xGrezza, 0, larghezzaSchermo));
|
|
296
|
+
const y = Math.round(dentro(yGrezza, 0, altezzaUtile));
|
|
297
|
+
/* Fuori dall'immagine disegnata (il bordo del «contain», o un tocco oltre il
|
|
298
|
+
* canvas): il chiamante può ignorare il gesto invece di cliccare sul bordo. */
|
|
299
|
+
const fuoriBordo = xGrezza < 0 || yGrezza < 0 || xGrezza > larghezzaSchermo || yGrezza > altezzaUtile;
|
|
300
|
+
|
|
301
|
+
const fattorePagina = positivo(m.pageScaleFactor) ?? 1;
|
|
302
|
+
const documentoX = Math.round(numeroFinito(m.scrollOffsetX, 0) + x / fattorePagina);
|
|
303
|
+
const documentoY = Math.round(numeroFinito(m.scrollOffsetY, 0) + y / fattorePagina);
|
|
304
|
+
return { x, y, documentoX, documentoY, scala, fuoriBordo };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* L'evento è di un'ALTRA scheda? Il secondo argomento di `su` è la stringa della
|
|
309
|
+
* sessione in certi client e `{ sessionId }` in altri (M1 dà l'oggetto).
|
|
310
|
+
* Se non si sa dire di chi è, non si scarta: meglio confermare un fotogramma in
|
|
311
|
+
* più che congelare il proprio flusso.
|
|
312
|
+
*/
|
|
313
|
+
export function sessioneAltrui(contesto, sessionId) {
|
|
314
|
+
if (typeof sessionId !== 'string' || !sessionId) return false;
|
|
315
|
+
const dellEvento = typeof contesto === 'string' ? contesto : (contesto && typeof contesto === 'object' ? contesto.sessionId : null);
|
|
316
|
+
if (typeof dellEvento !== 'string' || !dellEvento) return false;
|
|
317
|
+
return dellEvento !== sessionId;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** I bit dei modificatori: Alt=1, Ctrl=2, Meta=4, Shift=8 (dominio Input). */
|
|
321
|
+
export function bitModificatori(modificatori) {
|
|
322
|
+
if (typeof modificatori === 'number' && Number.isFinite(modificatori)) return modificatori & 0b1111;
|
|
323
|
+
const m = modificatori && typeof modificatori === 'object' ? modificatori : {};
|
|
324
|
+
return (m.alt ? 1 : 0) | (m.ctrl || m.control ? 2 : 0) | (m.meta ? 4 : 0) | (m.shift ? 8 : 0);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Un clic vero è una COPPIA di eventi, non un evento solo: premuto e rilasciato
|
|
329
|
+
* con lo stesso `clickCount`. Il doppio clic sono due coppie con `clickCount`
|
|
330
|
+
* 1 e poi 2 — è il secondo numero che fa scattare il `dblclick` nella pagina.
|
|
331
|
+
* Prima si manda un `mouseMoved`, come fa puppeteer, altrimenti gli stati
|
|
332
|
+
* `:hover` che aprono i menù non si accendono mai.
|
|
333
|
+
*/
|
|
334
|
+
export async function mandaClic(cdp, sessionId, { x, y, tasto = 'sinistro', doppio = false, modificatori = 0 } = {}) {
|
|
335
|
+
const scelto = TASTI_MOUSE.get(String(tasto).toLowerCase()) || TASTI_MOUSE.get('sinistro');
|
|
336
|
+
const modifiers = bitModificatori(modificatori);
|
|
337
|
+
const posizione = { x: Math.round(numeroFinito(x, 0)), y: Math.round(numeroFinito(y, 0)) };
|
|
338
|
+
const comune = { ...posizione, modifiers, pointerType: 'mouse' };
|
|
339
|
+
await cdp.invia('Input.dispatchMouseEvent', { ...comune, type: 'mouseMoved', button: 'none', buttons: 0, clickCount: 0 }, sessionId);
|
|
340
|
+
const conteggi = doppio ? [1, 2] : [1];
|
|
341
|
+
for (const clickCount of conteggi) {
|
|
342
|
+
await cdp.invia('Input.dispatchMouseEvent', { ...comune, type: 'mousePressed', button: scelto.nome, buttons: scelto.bit, clickCount }, sessionId);
|
|
343
|
+
await cdp.invia('Input.dispatchMouseEvent', { ...comune, type: 'mouseReleased', button: scelto.nome, buttons: 0, clickCount }, sessionId);
|
|
344
|
+
}
|
|
345
|
+
return { coppie: conteggi.length, tasto: scelto.nome };
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Un tasto stampabile vuole `keyDown` → `char` → `keyUp`, e il testo va SOLO
|
|
350
|
+
* nell'evento `char`: metterlo anche su `keyDown` scrive il carattere due volte
|
|
351
|
+
* (Chrome genera lui il `char` quando `text` è pieno).
|
|
352
|
+
*
|
|
353
|
+
* ⛔ Con Ctrl, Alt o Meta premuti NON si manda il `char`: quella è una
|
|
354
|
+
* scorciatoia (Ctrl+A seleziona tutto), non una lettera da scrivere. Shift no:
|
|
355
|
+
* shift fa parte della lettera.
|
|
356
|
+
*/
|
|
357
|
+
export async function mandaTasto(cdp, sessionId, { chiave, testo, modificatori = 0 } = {}) {
|
|
358
|
+
const key = String(chiave ?? '');
|
|
359
|
+
if (!key) return { eventi: 0, scritto: false };
|
|
360
|
+
const modifiers = bitModificatori(modificatori);
|
|
361
|
+
const speciale = TASTI_SPECIALI.get(key);
|
|
362
|
+
const testoScritto = testo !== undefined ? String(testo) : (speciale ? speciale.testo : (Array.from(key).length === 1 ? key : ''));
|
|
363
|
+
const codice = speciale ? speciale.codice : (Array.from(key).length === 1 ? key.toUpperCase().codePointAt(0) : 0);
|
|
364
|
+
const scorciatoia = (modifiers & 0b0111) !== 0; // Alt|Ctrl|Meta, ma non Shift
|
|
365
|
+
const scrive = testoScritto.length > 0 && !scorciatoia;
|
|
366
|
+
const identita = { key, code: codiceFisico(key, speciale), windowsVirtualKeyCode: codice, nativeVirtualKeyCode: codice, modifiers };
|
|
367
|
+
|
|
368
|
+
await cdp.invia('Input.dispatchKeyEvent', { ...identita, type: 'keyDown', autoRepeat: false, isKeypad: false, location: 0 }, sessionId);
|
|
369
|
+
if (scrive) {
|
|
370
|
+
await cdp.invia('Input.dispatchKeyEvent', { type: 'char', key, text: testoScritto, unmodifiedText: testoScritto.toLowerCase(), modifiers }, sessionId);
|
|
371
|
+
}
|
|
372
|
+
await cdp.invia('Input.dispatchKeyEvent', { ...identita, type: 'keyUp', location: 0 }, sessionId);
|
|
373
|
+
return { eventi: scrive ? 3 : 2, scritto: scrive };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** Il nome del tasto FISICO: per le lettere è `KeyA`, per le cifre `Digit1`, per gli altri il nome stesso. */
|
|
377
|
+
function codiceFisico(key, speciale) {
|
|
378
|
+
if (speciale) return key === ' ' ? 'Space' : key;
|
|
379
|
+
if (/^[a-zA-Z]$/.test(key)) return `Key${key.toUpperCase()}`;
|
|
380
|
+
if (/^[0-9]$/.test(key)) return `Digit${key}`;
|
|
381
|
+
return '';
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* La rotella è un `mouseWheel` con i delta in pixel CSS, nel punto in cui sta il
|
|
386
|
+
* puntatore: la pagina scorre l'elemento sotto quel punto, non sempre il
|
|
387
|
+
* documento.
|
|
388
|
+
*/
|
|
389
|
+
export async function mandaRotella(cdp, sessionId, { x, y, dx = 0, dy = 0, modificatori = 0 } = {}) {
|
|
390
|
+
await cdp.invia('Input.dispatchMouseEvent', {
|
|
391
|
+
type: 'mouseWheel',
|
|
392
|
+
x: Math.round(numeroFinito(x, 0)),
|
|
393
|
+
y: Math.round(numeroFinito(y, 0)),
|
|
394
|
+
deltaX: numeroFinito(dx, 0),
|
|
395
|
+
deltaY: numeroFinito(dy, 0),
|
|
396
|
+
modifiers: bitModificatori(modificatori),
|
|
397
|
+
button: 'none',
|
|
398
|
+
buttons: 0,
|
|
399
|
+
clickCount: 0,
|
|
400
|
+
pointerType: 'mouse',
|
|
401
|
+
}, sessionId);
|
|
402
|
+
return { dx: numeroFinito(dx, 0), dy: numeroFinito(dy, 0) };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* La misura della finestra vista dalla pagina. `width`/`height` a 0 tolgono
|
|
407
|
+
* l'override (lo dice il protocollo), quindi «zero per zero» qui significa
|
|
408
|
+
* «torna com'eri»: si chiama `clearDeviceMetricsOverride`, che è la stessa cosa
|
|
409
|
+
* detta senza ambiguità.
|
|
410
|
+
*/
|
|
411
|
+
export async function ridimensiona(cdp, sessionId, { larghezza, altezza, scala = 1 } = {}) {
|
|
412
|
+
const l = Math.round(numeroFinito(larghezza, 0));
|
|
413
|
+
const a = Math.round(numeroFinito(altezza, 0));
|
|
414
|
+
if (l <= 0 && a <= 0) {
|
|
415
|
+
await cdp.invia('Emulation.clearDeviceMetricsOverride', {}, sessionId);
|
|
416
|
+
return { azzerato: true, larghezza: 0, altezza: 0, scala: 1 };
|
|
417
|
+
}
|
|
418
|
+
const misura = {
|
|
419
|
+
width: dentro(l, 1, LARGHEZZA_MASSIMA),
|
|
420
|
+
height: dentro(a, 1, ALTEZZA_MASSIMA),
|
|
421
|
+
deviceScaleFactor: dentro(numeroFinito(scala, 1), 0.5, 3),
|
|
422
|
+
mobile: false,
|
|
423
|
+
};
|
|
424
|
+
await cdp.invia('Emulation.setDeviceMetricsOverride', misura, sessionId);
|
|
425
|
+
/*
|
|
426
|
+
* ⛔ 08/09/2026 — cambiare il viewport non basta a far arrivare un fotogramma nuovo: una pagina
|
|
427
|
+
* FERMA non ridisegna, e lo schermo resta quello di prima, con la forma vecchia. Visto in una
|
|
428
|
+
* foto: dopo il ridimensionamento la pagina restava 926×448 dentro un riquadro più alto, con
|
|
429
|
+
* una banda nera sotto che spariva solo appena si scorreva.
|
|
430
|
+
* ⇒ Si ri-chiede lo screencast con gli stessi parametri: `Page.startScreencast` e' idempotente e
|
|
431
|
+
* il primo fotogramma dopo la chiamata e' completo, quindi arriva subito la forma nuova.
|
|
432
|
+
* L'ascolto e' gia' registrato altrove: qui non si tocca, si sveglia soltanto.
|
|
433
|
+
*/
|
|
434
|
+
try {
|
|
435
|
+
await cdp.invia('Page.startScreencast', {
|
|
436
|
+
format: 'jpeg',
|
|
437
|
+
quality: QUALITA_PREDEFINITA,
|
|
438
|
+
maxWidth: LARGHEZZA_MASSIMA,
|
|
439
|
+
maxHeight: ALTEZZA_MASSIMA,
|
|
440
|
+
everyNthFrame: 1,
|
|
441
|
+
maxFramesInFlight: FOTOGRAMMI_IN_VOLO,
|
|
442
|
+
}, sessionId);
|
|
443
|
+
} catch { /* la scheda puo' essere chiusa fra le due chiamate: il ridimensionamento resta valido */ }
|
|
444
|
+
return { azzerato: false, larghezza: misura.width, altezza: misura.height, scala: misura.deviceScaleFactor };
|
|
445
|
+
}
|