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,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PO-01 — Accedere a OpenRouter senza incollare una chiave.
|
|
3
|
+
*
|
|
4
|
+
* Owner 10/09/2026: «nella fase oauth aggiungi anche oauth openrouter, è già stato fatto nel
|
|
5
|
+
* mobile». Fino a oggi sul desktop la chiave OpenRouter arrivava da un solo posto —
|
|
6
|
+
* `env.OPENROUTER_API_KEY`, letto in `src/config.mjs:537` — cioè chi non sa cos'è una variabile
|
|
7
|
+
* d'ambiente non poteva cominciare.
|
|
8
|
+
*
|
|
9
|
+
* ## Perché OpenRouter e non tutti e cinque i provider
|
|
10
|
+
*
|
|
11
|
+
* Anthropic, OpenAI e Gemini offrono OAuth solo per client «riservati», cioè con un
|
|
12
|
+
* `client_secret` custodito su un server. TALOS non ha quel server e — per la premessa
|
|
13
|
+
* local-first — non deve averlo. OpenRouter invece pubblica un flusso **PKCE per client
|
|
14
|
+
* pubblici**: nessun segreto da custodire, la prova è che chi chiude lo scambio è lo stesso che
|
|
15
|
+
* l'ha aperto.
|
|
16
|
+
*
|
|
17
|
+
* ## Qui dentro ci sono SOLO i conti
|
|
18
|
+
*
|
|
19
|
+
* Niente `http`, niente disco, niente registro globale: ogni funzione prende ciò che le serve
|
|
20
|
+
* come argomento (il caso, l'orologio, il `fetch`) e si può provare senza rete. Le rotte stanno
|
|
21
|
+
* in `src/http-app.mjs`; la custodia della chiave sta altrove e arriva qui come dipendenza —
|
|
22
|
+
* questo modulo non scrive MAI una chiave da nessuna parte.
|
|
23
|
+
*
|
|
24
|
+
* ## RICERCA — fonti primarie, lette il 10/09/2026 PRIMA di scrivere
|
|
25
|
+
*
|
|
26
|
+
* · OpenRouter, «OAuth PKCE» (openrouter.ai/docs/guides/overview/auth/oauth):
|
|
27
|
+
* - si manda la persona su `https://openrouter.ai/auth` con `callback_url`,
|
|
28
|
+
* `code_challenge` e `code_challenge_method` (`S256` raccomandato, `plain` l'alternativa);
|
|
29
|
+
* - «localhost callbacks are supported on **any port**» ⇒ il nostro 4174 va bene com'è, non
|
|
30
|
+
* serve registrare niente da nessuna parte;
|
|
31
|
+
* - lo scambio è `POST https://openrouter.ai/api/v1/auth/keys`, `Content-Type:
|
|
32
|
+
* application/json`, corpo `{code, code_verifier, code_challenge_method}`, e la risposta è
|
|
33
|
+
* `{"key": "<user_api_key>"}`;
|
|
34
|
+
* - ⭐ **modalità senza callback**: si OMETTE `callback_url` e si aggiunge
|
|
35
|
+
* `key_label=<nome dell'app>`; la pagina MOSTRA il codice a schermo, da incollare a mano.
|
|
36
|
+
* In quella modalità il `code_challenge` è OBBLIGATORIO, perché il codice è visibile a
|
|
37
|
+
* chiunque guardi lo schermo — PKCE lo rende inutile a chi non ha il verificatore;
|
|
38
|
+
* - «The code is single-use and expires after **10 minutes**»;
|
|
39
|
+
* - gli errori dichiarati dello scambio: `400 Invalid code_challenge_method`, `403 Invalid
|
|
40
|
+
* code or code_verifier`, `403 Authorization code expired`, `405 Method Not Allowed`.
|
|
41
|
+
*
|
|
42
|
+
* · ⛔ **Il vincolo che dal codice non si vedeva** — lo `state`. La documentazione di OpenRouter
|
|
43
|
+
* NON lo nomina in nessun punto (riletta il 10/09/2026 cercandolo apposta). Esiste un
|
|
44
|
+
* annuncio di OpenRouter su X del 28/04/2025 — «You can now pass a state query parameter in
|
|
45
|
+
* your callback urls when integrating with OpenRouter Oauth PKCE» — ma una funzione
|
|
46
|
+
* documentata solo su un social non è un contratto su cui appoggiare l'unica porta d'ingresso
|
|
47
|
+
* di una persona. ⇒ Il nostro `stato` viaggia **nel PERCORSO** del `callback_url`
|
|
48
|
+
* (`…/ritorno/<stato>`), che nessuna implementazione può perdere perché è l'indirizzo stesso;
|
|
49
|
+
* la forma con `?state=` resta accettata dalle rotte come ripiego, così se un giorno
|
|
50
|
+
* OpenRouter la documentasse non ci sarebbe niente da cambiare.
|
|
51
|
+
*
|
|
52
|
+
* · La fine dello scambio non è un token con una scadenza: è **una chiave API**, uguale a quella
|
|
53
|
+
* che si sarebbe incollata a mano. Niente refresh, niente scadenza, nessuno stato da
|
|
54
|
+
* mantenere: la chiave entra nella custodia e da lì in poi non esiste più nessun «accesso
|
|
55
|
+
* OAuth» da rinnovare. Un pezzo di stato in meno per sempre.
|
|
56
|
+
*
|
|
57
|
+
* · RFC 7636 §4.1 (PKCE) via la pagina IETF, letta il 10/09/2026: il verificatore è una stringa
|
|
58
|
+
* di 43-128 caratteri dell'insieme «unreserved», e la sfida `S256` è
|
|
59
|
+
* `BASE64URL(SHA256(ASCII(verifier)))`.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
63
|
+
|
|
64
|
+
export const INDIRIZZO_AUTORIZZAZIONE = 'https://openrouter.ai/auth';
|
|
65
|
+
export const INDIRIZZO_SCAMBIO = 'https://openrouter.ai/api/v1/auth/keys';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* L'etichetta che OpenRouter scrive accanto alla chiave nella pagina delle chiavi della persona.
|
|
69
|
+
* Serve a lei, non a noi: è il modo in cui riconosce quale programma le ha chiesto l'accesso e
|
|
70
|
+
* quale riga revocare se cambia idea. Obbligatoria nella modalità senza callback.
|
|
71
|
+
*/
|
|
72
|
+
export const ETICHETTA_CHIAVE = 'TALOS Harness Desktop';
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* ⛔ IL TETTO DI TEMPO DELLA COPPIA IN ATTESA — dieci minuti, e non è un numero scelto a occhio.
|
|
76
|
+
*
|
|
77
|
+
* È esattamente la vita del codice di autorizzazione dichiarata da OpenRouter («The code is
|
|
78
|
+
* single-use and expires after 10 minutes», doc letta il 10/09/2026). Questa finestra è quella in
|
|
79
|
+
* cui un codice rubato — letto dallo schermo nella modalità senza callback, o pescato dalla barra
|
|
80
|
+
* dell'indirizzo di un browser condiviso — sarebbe ancora spendibile.
|
|
81
|
+
*
|
|
82
|
+
* · Più corta sarebbe un danno senza guadagno: chi ha l'autenticazione a due fattori, o deve
|
|
83
|
+
* cercare la password nel gestore, impiega più di un minuto in modo del tutto legittimo, e si
|
|
84
|
+
* troverebbe davanti a un rifiuto senza aver sbagliato niente.
|
|
85
|
+
* · Più lunga sarebbe superficie regalata: un verificatore che sopravvive al codice che sblocca
|
|
86
|
+
* non serve a nessuno tranne a chi quel codice l'ha rubato.
|
|
87
|
+
*
|
|
88
|
+
* ⇒ Il tetto giusto è quello dell'altra metà del flusso, non uno nostro.
|
|
89
|
+
*/
|
|
90
|
+
export const TETTO_ATTESA_MS = 10 * 60 * 1000;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Quante richieste di accesso possono aspettare insieme.
|
|
94
|
+
*
|
|
95
|
+
* ⛔ DECISIONE, e il compito chiedeva di motivarla: due `inizia` di fila NON invalidano la
|
|
96
|
+
* prima. Chi preme «Accedi» e non vede subito il browser preme una seconda volta — è il gesto
|
|
97
|
+
* più comune che esista davanti a un'attesa muta — e se il secondo `inizia` uccidesse il primo,
|
|
98
|
+
* la scheda che si è aperta davvero (quella del PRIMO giro, perché il browser è lento a
|
|
99
|
+
* comparire) tornerebbe con un codice ormai orfano. Il risultato che vedrebbe la persona è
|
|
100
|
+
* «non funziona mai», con due tentativi entrambi legittimi.
|
|
101
|
+
*
|
|
102
|
+
* Il rischio opposto — un accumulo illimitato di verificatori vivi — è chiuso da due lati: il
|
|
103
|
+
* tetto di tempo qui sopra e questo tetto di numero, che butta prima le scadute e poi la più
|
|
104
|
+
* vecchia. Otto è largo per una persona e stretto per un ciclo impazzito.
|
|
105
|
+
*/
|
|
106
|
+
export const ATTESE_MASSIME = 8;
|
|
107
|
+
|
|
108
|
+
function erroreConCodice(messaggio, code) {
|
|
109
|
+
return Object.assign(new Error(messaggio), { code });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** base64url: base64 senza `+`, `/` e senza il riempimento finale (RFC 7636 §A). */
|
|
113
|
+
export function base64Url(bytes) {
|
|
114
|
+
return Buffer.from(bytes).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* La coppia PKCE: un verificatore casuale e la sua sfida.
|
|
119
|
+
*
|
|
120
|
+
* 32 byte casuali in base64url danno esattamente 43 caratteri, cioè il minimo consentito da
|
|
121
|
+
* RFC 7636 §4.1 con la massima entropia per carattere. Allungare la stringa non aggiunge niente:
|
|
122
|
+
* l'entropia sta nei byte, non nel numero di lettere.
|
|
123
|
+
*
|
|
124
|
+
* ⛔ `S256` e mai `plain`: con `plain` la sfida È il verificatore, quindi chiunque legga
|
|
125
|
+
* l'indirizzo di autorizzazione (la barra del browser, la cronologia, un log di un proxy) può
|
|
126
|
+
* chiudere lo scambio al posto nostro — che è precisamente ciò da cui PKCE difende.
|
|
127
|
+
*
|
|
128
|
+
* ⛔ Il caso arriva da `node:crypto`, mai da `Math.random()`: quello è un generatore
|
|
129
|
+
* deterministico pensato per le simulazioni, e il suo stato interno si ricostruisce da poche
|
|
130
|
+
* uscite osservate.
|
|
131
|
+
*/
|
|
132
|
+
export function creaCoppiaPkce({ random = (quanti) => randomBytes(quanti) } = {}) {
|
|
133
|
+
const byte = random(32);
|
|
134
|
+
if (!byte || typeof byte.length !== 'number' || byte.length < 32) {
|
|
135
|
+
throw erroreConCodice('Sorgente di casualità insufficiente', 'OAUTH_CASO_INSUFFICIENTE');
|
|
136
|
+
}
|
|
137
|
+
const verifier = base64Url(byte);
|
|
138
|
+
const challenge = base64Url(createHash('sha256').update(verifier, 'ascii').digest());
|
|
139
|
+
return { verifier, challenge };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Uno `stato` opaco: 32 byte di caso, nient'altro. Non contiene niente, e per questo non dice niente. */
|
|
143
|
+
export function creaStato({ random = (quanti) => randomBytes(quanti) } = {}) {
|
|
144
|
+
return base64Url(random(32));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* L'indirizzo su cui mandare la persona.
|
|
149
|
+
*
|
|
150
|
+
* Con `callbackUrl`: il browser rientra da solo su quell'indirizzo (modalità normale).
|
|
151
|
+
* Senza: la pagina di OpenRouter MOSTRA il codice a schermo e la persona lo incolla in TALOS —
|
|
152
|
+
* è la sola strada per chi usa TALOS su un server remoto, dove `127.0.0.1` è la macchina
|
|
153
|
+
* sbagliata. In quella modalità `key_label` è richiesto dalla documentazione.
|
|
154
|
+
*
|
|
155
|
+
* ⛔ `code_challenge` c'è SEMPRE, in tutte e due le modalità: la documentazione lo dichiara
|
|
156
|
+
* facoltativo nella prima e obbligatorio nella seconda, ma un flusso senza PKCE è un flusso in
|
|
157
|
+
* cui il codice, da solo, basta a farsi dare una chiave.
|
|
158
|
+
*/
|
|
159
|
+
export function indirizzoDiAutorizzazione({ challenge, callbackUrl = null, etichetta = ETICHETTA_CHIAVE } = {}) {
|
|
160
|
+
if (typeof challenge !== 'string' || challenge.trim() === '') {
|
|
161
|
+
throw erroreConCodice('Manca la sfida PKCE', 'OAUTH_SFIDA_MANCANTE');
|
|
162
|
+
}
|
|
163
|
+
const url = new URL(INDIRIZZO_AUTORIZZAZIONE);
|
|
164
|
+
if (callbackUrl !== null && callbackUrl !== undefined) {
|
|
165
|
+
if (typeof callbackUrl !== 'string' || callbackUrl.trim() === '') {
|
|
166
|
+
throw erroreConCodice('Indirizzo di ritorno non valido', 'OAUTH_RITORNO_INVALIDO');
|
|
167
|
+
}
|
|
168
|
+
url.searchParams.set('callback_url', callbackUrl.trim());
|
|
169
|
+
} else {
|
|
170
|
+
url.searchParams.set('key_label', etichetta);
|
|
171
|
+
}
|
|
172
|
+
url.searchParams.set('code_challenge', challenge.trim());
|
|
173
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
174
|
+
return url.toString();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Lo scambio: il codice più il verificatore diventano una chiave API.
|
|
179
|
+
*
|
|
180
|
+
* Nessun `client_secret`, che è tutto il punto del flusso pubblico. Se il codice fosse stato
|
|
181
|
+
* intercettato da un'altra applicazione sulla stessa macchina, questa chiamata fallirebbe: il
|
|
182
|
+
* verificatore che lo sblocca non ha mai lasciato questo processo.
|
|
183
|
+
*
|
|
184
|
+
* ⛔ I guasti sono TRE cose diverse e restano tre codici diversi — la rete che non risponde, un
|
|
185
|
+
* rifiuto di OpenRouter, una risposta che non contiene la chiave. Chi legge a schermo «non ha
|
|
186
|
+
* funzionato» e basta non sa se deve riprovare, ricominciare l'accesso o smettere.
|
|
187
|
+
* ⛔ Il corpo della risposta di errore NON viene mai rimesso nel messaggio: in un flusso di
|
|
188
|
+
* autenticazione un corpo di risposta è precisamente il posto dove passano i segreti.
|
|
189
|
+
*/
|
|
190
|
+
export async function scambiaCodicePerChiave({ codice, verifier, fetchDiRete = globalThis.fetch } = {}) {
|
|
191
|
+
const codiceRipulito = typeof codice === 'string' ? codice.trim() : '';
|
|
192
|
+
const verifierRipulito = typeof verifier === 'string' ? verifier.trim() : '';
|
|
193
|
+
if (codiceRipulito === '') throw erroreConCodice('Manca il codice di accesso', 'OAUTH_CODICE_MANCANTE');
|
|
194
|
+
if (verifierRipulito === '') throw erroreConCodice('Manca il verificatore', 'OAUTH_VERIFIER_MANCANTE');
|
|
195
|
+
if (typeof fetchDiRete !== 'function') throw erroreConCodice('Nessun modo di uscire in rete', 'OAUTH_RETE');
|
|
196
|
+
|
|
197
|
+
let risposta;
|
|
198
|
+
try {
|
|
199
|
+
risposta = await fetchDiRete(INDIRIZZO_SCAMBIO, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
code: codiceRipulito,
|
|
204
|
+
code_verifier: verifierRipulito,
|
|
205
|
+
code_challenge_method: 'S256',
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
} catch {
|
|
209
|
+
// ⛔ Il messaggio dell'eccezione di rete non viaggia: può contenere l'indirizzo completo, e
|
|
210
|
+
// con esso, in altre implementazioni, la query. Il codice basta a sapere cosa fare.
|
|
211
|
+
throw erroreConCodice('OpenRouter non è raggiungibile', 'OAUTH_RETE');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!risposta || typeof risposta.ok !== 'boolean') {
|
|
215
|
+
throw erroreConCodice('Risposta di OpenRouter non riconoscibile', 'OAUTH_RISPOSTA_INATTESA');
|
|
216
|
+
}
|
|
217
|
+
if (!risposta.ok) {
|
|
218
|
+
// Lo stato HTTP sì (403 «codice scaduto» è un'informazione utile e non è un segreto), il corpo no.
|
|
219
|
+
throw Object.assign(
|
|
220
|
+
erroreConCodice('OpenRouter ha rifiutato questo accesso', 'OAUTH_SCAMBIO_RIFIUTATO'),
|
|
221
|
+
{ stato: risposta.status ?? 0 },
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let corpo;
|
|
226
|
+
try {
|
|
227
|
+
corpo = await risposta.json();
|
|
228
|
+
} catch {
|
|
229
|
+
throw erroreConCodice('OpenRouter ha risposto qualcosa che non è JSON', 'OAUTH_RISPOSTA_INATTESA');
|
|
230
|
+
}
|
|
231
|
+
const chiave = corpo && typeof corpo === 'object' && typeof corpo.key === 'string' ? corpo.key.trim() : '';
|
|
232
|
+
if (chiave === '') {
|
|
233
|
+
/*
|
|
234
|
+
* ⛔ DA DECIDERE ALL'OWNER, registrato qui perché è il punto in cui si romperebbe: se un
|
|
235
|
+
* domani OpenRouter cambiasse il nome del campo (`api_key`, `data.key`, …) il flusso
|
|
236
|
+
* morirebbe qui, con questo codice. Non indoviniamo forme alternative — accettare un campo
|
|
237
|
+
* che oggi non esiste vuol dire accettare, il giorno in cui esisterà, qualcosa di cui non
|
|
238
|
+
* sappiamo il significato. Meglio un guasto pulito e riconoscibile.
|
|
239
|
+
*/
|
|
240
|
+
throw erroreConCodice('La risposta di OpenRouter non contiene una chiave', 'OAUTH_RISPOSTA_INATTESA');
|
|
241
|
+
}
|
|
242
|
+
return { chiave };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Il registro delle richieste di accesso in attesa: `stato` → verificatore.
|
|
247
|
+
*
|
|
248
|
+
* ⛔ Vive SOLO in memoria e SOLO in questo processo. Un verificatore su disco sarebbe un segreto
|
|
249
|
+
* scritto per una finestra di dieci minuti e lasciato lì per sempre; e non servirebbe a niente,
|
|
250
|
+
* perché un riavvio del server interrompe comunque il giro di accesso della persona.
|
|
251
|
+
*
|
|
252
|
+
* ⛔ `consuma` è a uso singolo: la voce sparisce alla prima lettura, riuscita o no. Un codice di
|
|
253
|
+
* autorizzazione è a uso singolo per OpenRouter, e non c'è nessuna ragione per cui il nostro
|
|
254
|
+
* lato debba essere più permissivo del suo.
|
|
255
|
+
*/
|
|
256
|
+
export function creaRegistroAttese({
|
|
257
|
+
clock = () => Date.now(),
|
|
258
|
+
tettoMs = TETTO_ATTESA_MS,
|
|
259
|
+
massime = ATTESE_MASSIME,
|
|
260
|
+
random = (quanti) => randomBytes(quanti),
|
|
261
|
+
} = {}) {
|
|
262
|
+
/** @type {Map<string, {verifier: string, scadenza: number, conRitorno: boolean}>} */
|
|
263
|
+
const attese = new Map();
|
|
264
|
+
|
|
265
|
+
function scadute(adesso) {
|
|
266
|
+
for (const [stato, voce] of attese) if (voce.scadenza <= adesso) attese.delete(stato);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
/**
|
|
271
|
+
* Apre una richiesta: genera la coppia, la lega a uno `stato` opaco e restituisce
|
|
272
|
+
* l'indirizzo da aprire. `costruisciRitorno(stato)` produce il `callback_url`; se è `null`
|
|
273
|
+
* si va in modalità «codice a schermo».
|
|
274
|
+
*/
|
|
275
|
+
apri({ costruisciRitorno = null, etichetta = ETICHETTA_CHIAVE } = {}) {
|
|
276
|
+
const adesso = clock();
|
|
277
|
+
scadute(adesso);
|
|
278
|
+
// Il tetto di numero si applica DOPO aver buttato le scadute: quasi sempre non morde.
|
|
279
|
+
while (attese.size >= massime) attese.delete(attese.keys().next().value);
|
|
280
|
+
const stato = creaStato({ random });
|
|
281
|
+
const { verifier, challenge } = creaCoppiaPkce({ random });
|
|
282
|
+
const callbackUrl = typeof costruisciRitorno === 'function' ? costruisciRitorno(stato) : null;
|
|
283
|
+
const indirizzo = indirizzoDiAutorizzazione({ challenge, callbackUrl, etichetta });
|
|
284
|
+
attese.set(stato, { verifier, scadenza: adesso + tettoMs, conRitorno: Boolean(callbackUrl) });
|
|
285
|
+
return { stato, indirizzo, modo: callbackUrl ? 'browser' : 'schermo', scadeTraMs: tettoMs };
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Consuma uno `stato` e restituisce il suo verificatore. Lancia — con UN SOLO codice — se lo
|
|
290
|
+
* stato è sconosciuto, già usato o scaduto.
|
|
291
|
+
*
|
|
292
|
+
* ⛔ Un solo codice per i tre casi, di proposito: distinguerli direbbe a chi bussa da fuori
|
|
293
|
+
* se uno stato è mai esistito, cioè trasformerebbe questa rotta in un oracolo su cui provare
|
|
294
|
+
* stati a caso. Dentro, per chi legge il codice, i tre casi restano distinti dalla logica.
|
|
295
|
+
* ⛔ Il confronto è a tempo costante: `Map.get` sarebbe già abbastanza, ma il pareggio va
|
|
296
|
+
* fatto sul contenuto e non sulla presenza della chiave, così un attaccante non impara nulla
|
|
297
|
+
* dal tempo di risposta.
|
|
298
|
+
*/
|
|
299
|
+
consuma(stato) {
|
|
300
|
+
const adesso = clock();
|
|
301
|
+
scadute(adesso);
|
|
302
|
+
const chiesto = Buffer.from(typeof stato === 'string' ? stato : '', 'utf8');
|
|
303
|
+
let trovata = null;
|
|
304
|
+
for (const candidato of attese.keys()) {
|
|
305
|
+
const atteso = Buffer.from(candidato, 'utf8');
|
|
306
|
+
// ⛔ `timingSafeEqual` LANCIA su lunghezze diverse: il confronto di lunghezza va fatto
|
|
307
|
+
// prima, ed è l'unica cosa che questo giro rivela (quanto è lungo uno stato: è noto).
|
|
308
|
+
if (atteso.length !== chiesto.length) continue;
|
|
309
|
+
if (timingSafeEqual(atteso, chiesto)) { trovata = candidato; break; }
|
|
310
|
+
}
|
|
311
|
+
if (trovata === null) throw erroreConCodice('Questa richiesta di accesso non è più valida', 'OAUTH_ATTESA_IGNOTA');
|
|
312
|
+
const voce = attese.get(trovata);
|
|
313
|
+
attese.delete(trovata); // uso singolo: sparisce comunque, anche se lo scambio poi fallirà
|
|
314
|
+
return { verifier: voce.verifier, conRitorno: voce.conRitorno };
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
/** Solo per le prove e per Doctor: quante ne stanno aspettando adesso. Mai i verificatori. */
|
|
318
|
+
quanteInAttesa() {
|
|
319
|
+
scadute(clock());
|
|
320
|
+
return attese.size;
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* L'indirizzo di ritorno, costruito dall'`Host` con cui la richiesta è arrivata.
|
|
327
|
+
*
|
|
328
|
+
* ⛔ L'`Host` è un'intestazione scritta dal client: non ci si costruisce sopra niente senza
|
|
329
|
+
* averla prima riconosciuta. Qui passano SOLO le forme di loopback — che sono anche le sole che
|
|
330
|
+
* OpenRouter accetta per un callback non-https («localhost callbacks are supported on any
|
|
331
|
+
* port») — e qualunque altra cosa vale `null`, cioè «vai in modalità codice a schermo», che è
|
|
332
|
+
* per l'appunto il caso di chi usa TALOS da remoto.
|
|
333
|
+
*/
|
|
334
|
+
const HOST_DI_LOOPBACK = /^(?:127\.0\.0\.1|localhost|\[::1\])(?::(?:\d{1,5}))?$/u;
|
|
335
|
+
|
|
336
|
+
export function ritornoDaHost(host, stato) {
|
|
337
|
+
if (typeof host !== 'string' || !HOST_DI_LOOPBACK.test(host.trim())) return null;
|
|
338
|
+
return `http://${host.trim()}/api/v1/auth/openrouter/ritorno/${encodeURIComponent(stato)}`;
|
|
339
|
+
}
|