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,135 @@
|
|
|
1
|
+
import { providerDescriptionText, providerLabelText } from "./provider-text.js";
|
|
2
|
+
import { UNKNOWN_PROVIDER_HEALTH, providerCooldownFromRow, providerHealthFromProbe, providerReadinessDecision, remediationForProvider, } from "./health.js";
|
|
3
|
+
const SECRET_QUERY_NAME = /(?:api[-_]?key|authorization|password|secret|token)/iu;
|
|
4
|
+
function safeEndpoint(value) {
|
|
5
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
6
|
+
return { value: null, host: null, valid: false };
|
|
7
|
+
try {
|
|
8
|
+
const url = new URL(value);
|
|
9
|
+
if (url.username || url.password)
|
|
10
|
+
return { value: null, host: null, valid: false };
|
|
11
|
+
for (const name of url.searchParams.keys())
|
|
12
|
+
if (SECRET_QUERY_NAME.test(name))
|
|
13
|
+
return { value: null, host: null, valid: false };
|
|
14
|
+
return { value: url.toString(), host: url.host, valid: true };
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return { value: null, host: null, valid: false };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function authSource({ record, row, usable, environment }) {
|
|
21
|
+
const required = typeof row?.requiresKey === 'boolean' ? row.requiresKey : record?.chiaveObbligatoria === true;
|
|
22
|
+
if (!required)
|
|
23
|
+
return 'not-required';
|
|
24
|
+
if (row?.origineChiave === 'custodia')
|
|
25
|
+
return 'saved';
|
|
26
|
+
if (row?.origineChiave === 'ambiente' || usable)
|
|
27
|
+
return 'environment';
|
|
28
|
+
const state = String(environment?.state ?? '');
|
|
29
|
+
if (state === 'approved' || state === 'accepted')
|
|
30
|
+
return 'environment';
|
|
31
|
+
if (state === 'unanswered')
|
|
32
|
+
return 'environment-unanswered';
|
|
33
|
+
if (state === 'declined')
|
|
34
|
+
return 'environment-declined';
|
|
35
|
+
return 'missing';
|
|
36
|
+
}
|
|
37
|
+
export function createProviderControlPlane(deps) {
|
|
38
|
+
const healthByProvider = new Map();
|
|
39
|
+
const now = deps.now ?? Date.now;
|
|
40
|
+
function publicRows() { try {
|
|
41
|
+
return deps.publicProviders() ?? [];
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
} }
|
|
46
|
+
function records() {
|
|
47
|
+
return Object.values(deps.registry ?? {}).filter((record) => record?.destinazioneChat === true);
|
|
48
|
+
}
|
|
49
|
+
function rowFor(id) { return publicRows().find((row) => String(row?.id ?? '') === id) ?? {}; }
|
|
50
|
+
function recordFor(id) {
|
|
51
|
+
const record = deps.registry?.[id];
|
|
52
|
+
if (!record || record.destinazioneChat !== true)
|
|
53
|
+
throw Object.assign(new Error('PROVIDER_INVALID'), { code: 'PROVIDER_INVALID', provider: id });
|
|
54
|
+
return record;
|
|
55
|
+
}
|
|
56
|
+
function snapshot(id) {
|
|
57
|
+
const record = recordFor(id);
|
|
58
|
+
const row = rowFor(id);
|
|
59
|
+
let runtime = {};
|
|
60
|
+
try {
|
|
61
|
+
runtime = deps.runtimeFor(id) ?? {};
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
runtime = {};
|
|
65
|
+
}
|
|
66
|
+
let usableSecret = null;
|
|
67
|
+
try {
|
|
68
|
+
usableSecret = deps.keyFor(id) ?? null;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
usableSecret = null;
|
|
72
|
+
}
|
|
73
|
+
const usable = Boolean(usableSecret);
|
|
74
|
+
let environment = null;
|
|
75
|
+
try {
|
|
76
|
+
environment = deps.environmentKey?.(id) ?? null;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
environment = null;
|
|
80
|
+
}
|
|
81
|
+
const required = typeof row?.requiresKey === 'boolean' ? row.requiresKey : record.chiaveObbligatoria === true;
|
|
82
|
+
const source = authSource({ record, row, usable, environment });
|
|
83
|
+
const configured = !required || usable || row?.keyConfigured === true || source === 'environment' || source === 'saved';
|
|
84
|
+
const rawEndpoint = row?.endpoint ?? runtime?.endpoint ?? record?.baseUrl ?? null;
|
|
85
|
+
const safe = safeEndpoint(rawEndpoint);
|
|
86
|
+
const endpointless = record?.wire === 'acp';
|
|
87
|
+
const endpoint = {
|
|
88
|
+
value: safe.value,
|
|
89
|
+
host: safe.host,
|
|
90
|
+
valid: safe.valid || endpointless,
|
|
91
|
+
configured: row?.endpointConfigured === true || runtime?.endpointConfigured === true,
|
|
92
|
+
supportsCustom: row?.supportsEndpoint === true || record?.indirizzoModificabile === true,
|
|
93
|
+
};
|
|
94
|
+
const cooldown = providerCooldownFromRow(row);
|
|
95
|
+
const lastHealth = healthByProvider.get(id) ?? UNKNOWN_PROVIDER_HEALTH;
|
|
96
|
+
const execution = String(row?.execution ?? record?.esecuzione ?? 'unknown');
|
|
97
|
+
const runtimeConfigured = record?.wire === 'acp' ? execution === 'configurato' : true;
|
|
98
|
+
const base = {
|
|
99
|
+
id,
|
|
100
|
+
label: providerLabelText(id, String(row?.label ?? record?.etichetta ?? id)),
|
|
101
|
+
description: providerDescriptionText(id, String(record?.descrizione ?? '')),
|
|
102
|
+
auth: {
|
|
103
|
+
required,
|
|
104
|
+
type: String(record?.auth?.tipo ?? (required ? 'key' : 'none')),
|
|
105
|
+
source,
|
|
106
|
+
configured,
|
|
107
|
+
usable: required ? usable : true,
|
|
108
|
+
environmentVariable: typeof environment?.variable === 'string' ? environment.variable : null,
|
|
109
|
+
},
|
|
110
|
+
endpoint,
|
|
111
|
+
execution,
|
|
112
|
+
runtimeConfigured,
|
|
113
|
+
local: record?.wire === 'locale' || record?.catalogo?.fonte === 'runtime-locale' || /locale/u.test(String(record?.esecuzione ?? '')),
|
|
114
|
+
cloud: Boolean(record?.cloud),
|
|
115
|
+
supportsOAuth: row?.supportsOAuth === true || Boolean(record?.oauth),
|
|
116
|
+
cooldown,
|
|
117
|
+
lastHealth,
|
|
118
|
+
};
|
|
119
|
+
const remediation = remediationForProvider(base);
|
|
120
|
+
const decision = providerReadinessDecision(base);
|
|
121
|
+
return { ...base, remediation, ready: decision.ready };
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
list() { return records().map((record) => snapshot(String(record.id))); },
|
|
125
|
+
get(id) { return snapshot(id); },
|
|
126
|
+
async probe(id, signal) {
|
|
127
|
+
recordFor(id);
|
|
128
|
+
const result = await deps.createProbe(signal).prova(id);
|
|
129
|
+
healthByProvider.set(id, providerHealthFromProbe(result, now()));
|
|
130
|
+
return snapshot(id);
|
|
131
|
+
},
|
|
132
|
+
invalidate(id) { recordFor(id); healthByProvider.delete(id); },
|
|
133
|
+
readiness(id) { return providerReadinessDecision(snapshot(id)); },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { providerLabelText } from "./provider-text.js";
|
|
2
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { t } from "../i18n/index.js";
|
|
6
|
+
import { missingKeyText } from "./missing-key.js";
|
|
7
|
+
const RECORD_FILE = 'provider-consent.json';
|
|
8
|
+
const HASH_KEY_FILE = 'provider-consent.key';
|
|
9
|
+
const FINGERPRINT_PREFIX = 'hmac-sha256:';
|
|
10
|
+
const VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/u;
|
|
11
|
+
const PROVIDER_ID = /^[a-z0-9][a-z0-9-]{0,63}$/u;
|
|
12
|
+
/** The name rule of `diagnostics/redact.ts` (SECRET_KEY). A name that looks like a secret never reaches the store unapproved,
|
|
13
|
+
* even when the registry that would name it is missing: the filter closes on what it cannot identify. */
|
|
14
|
+
const SECRET_LOOKING_NAME = /^(?:.*(?:api[-_]?key|authorization|password|secret|token).*)$/iu;
|
|
15
|
+
export function keyVariablesFor(registry, provider) {
|
|
16
|
+
const names = registry[provider]?.auth?.nomeVariabile;
|
|
17
|
+
return Array.isArray(names) ? names.filter(name => typeof name === 'string' && VARIABLE_NAME.test(name)) : [];
|
|
18
|
+
}
|
|
19
|
+
/** The credential store also reads `<first variable>_POOL`, a JSON list of keys. */
|
|
20
|
+
export function poolVariableFor(registry, provider) {
|
|
21
|
+
const first = keyVariablesFor(registry, provider)[0];
|
|
22
|
+
return first ? `${first}_POOL` : null;
|
|
23
|
+
}
|
|
24
|
+
/** Every environment name through which the credential store can receive a provider key. */
|
|
25
|
+
export function credentialVariableNames(registry) {
|
|
26
|
+
const names = new Set();
|
|
27
|
+
for (const provider of Object.keys(registry)) {
|
|
28
|
+
for (const name of keyVariablesFor(registry, provider))
|
|
29
|
+
names.add(name);
|
|
30
|
+
const pool = poolVariableFor(registry, provider);
|
|
31
|
+
if (pool)
|
|
32
|
+
names.add(pool);
|
|
33
|
+
}
|
|
34
|
+
return names;
|
|
35
|
+
}
|
|
36
|
+
/*
|
|
37
|
+
* ⛔ B1 slice 18, fix round — condition 8. On win32 environment names are case-insensitive: `process.env.OPENROUTER_API_KEY` finds a
|
|
38
|
+
* variable spelled `OpenRouter_Api_Key`, but spreading `process.env` into a plain object keeps the spelling it was set with, and
|
|
39
|
+
* the credential store reads the canonical name from that plain object. Measured by the coordinator: the approval was recorded
|
|
40
|
+
* and the store saw no key. Every lookup here therefore folds case on win32, and the store receives the canonical name only.
|
|
41
|
+
*/
|
|
42
|
+
function foldName(name, platform) { return platform === 'win32' ? name.toUpperCase() : name; }
|
|
43
|
+
/** The value of `name` as this platform resolves it: the exact spelling first, then (win32 only) any case variant. */
|
|
44
|
+
function readVariable(env, name, platform) {
|
|
45
|
+
const exact = env[name];
|
|
46
|
+
if (typeof exact === 'string')
|
|
47
|
+
return exact;
|
|
48
|
+
if (platform !== 'win32')
|
|
49
|
+
return undefined;
|
|
50
|
+
const wanted = foldName(name, platform);
|
|
51
|
+
for (const [candidate, value] of Object.entries(env))
|
|
52
|
+
if (typeof value === 'string' && foldName(candidate, platform) === wanted)
|
|
53
|
+
return value;
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
/** The variable the credential store would read for this provider: the first non-empty one, trimmed, as the store does. */
|
|
57
|
+
export function environmentKeyFor(env, registry, provider, platform = process.platform) {
|
|
58
|
+
for (const variable of keyVariablesFor(registry, provider)) {
|
|
59
|
+
const value = readVariable(env, variable, platform);
|
|
60
|
+
if (typeof value === 'string' && value.trim() !== '')
|
|
61
|
+
return { variable, value: value.trim() };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function emptyRecord() { return { version: 1, chosenProvider: null, environmentKeys: {} }; }
|
|
66
|
+
function readRecord(file) {
|
|
67
|
+
if (!existsSync(file))
|
|
68
|
+
return emptyRecord();
|
|
69
|
+
try {
|
|
70
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
71
|
+
if (parsed?.version !== 1 || !parsed.environmentKeys || typeof parsed.environmentKeys !== 'object' || Array.isArray(parsed.environmentKeys))
|
|
72
|
+
return emptyRecord();
|
|
73
|
+
const environmentKeys = {};
|
|
74
|
+
for (const [variable, row] of Object.entries(parsed.environmentKeys)) {
|
|
75
|
+
if (!VARIABLE_NAME.test(variable) || typeof row?.fingerprint !== 'string' || !row.fingerprint.startsWith(FINGERPRINT_PREFIX) || (row.answer !== 'yes' && row.answer !== 'no'))
|
|
76
|
+
continue;
|
|
77
|
+
environmentKeys[variable] = { fingerprint: row.fingerprint, answer: row.answer };
|
|
78
|
+
}
|
|
79
|
+
const chosenProvider = typeof parsed.chosenProvider === 'string' && PROVIDER_ID.test(parsed.chosenProvider) ? parsed.chosenProvider : null;
|
|
80
|
+
return { version: 1, chosenProvider, environmentKeys };
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return emptyRecord();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function writeAtomically(file, text) {
|
|
87
|
+
const temporary = `${file}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`;
|
|
88
|
+
try {
|
|
89
|
+
writeFileSync(temporary, text, { encoding: 'utf8', mode: 0o600 });
|
|
90
|
+
renameSync(temporary, file);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
try {
|
|
94
|
+
if (existsSync(temporary))
|
|
95
|
+
unlinkSync(temporary);
|
|
96
|
+
}
|
|
97
|
+
catch { /* best effort */ }
|
|
98
|
+
throw Object.assign(new Error('PROVIDER_CONSENT_UNAVAILABLE'), { code: 'PROVIDER_CONSENT_UNAVAILABLE', cause: error });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function createEnvironmentKeyConsent({ dataRoot, registry, env, platform = process.platform }) {
|
|
102
|
+
const recordFile = join(dataRoot, RECORD_FILE);
|
|
103
|
+
const hashKeyFile = join(dataRoot, HASH_KEY_FILE);
|
|
104
|
+
function readHashKey() {
|
|
105
|
+
try {
|
|
106
|
+
const text = readFileSync(hashKeyFile, 'utf8').trim();
|
|
107
|
+
return /^[a-f0-9]{64}$/u.test(text) ? Buffer.from(text, 'hex') : null;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function hashKey(create) {
|
|
114
|
+
const existing = readHashKey();
|
|
115
|
+
if (existing || !create)
|
|
116
|
+
return existing;
|
|
117
|
+
mkdirSync(dataRoot, { recursive: true, mode: 0o700 });
|
|
118
|
+
const created = randomBytes(32);
|
|
119
|
+
writeAtomically(hashKeyFile, `${created.toString('hex')}\n`);
|
|
120
|
+
return created;
|
|
121
|
+
}
|
|
122
|
+
function fingerprint(value, key) { return `${FINGERPRINT_PREFIX}${createHmac('sha256', key).update(value, 'utf8').digest('hex')}`; }
|
|
123
|
+
function matches(expected, value, key) {
|
|
124
|
+
if (!key)
|
|
125
|
+
return false;
|
|
126
|
+
const actual = Buffer.from(fingerprint(value, key));
|
|
127
|
+
const wanted = Buffer.from(expected);
|
|
128
|
+
return actual.length === wanted.length && timingSafeEqual(actual, wanted);
|
|
129
|
+
}
|
|
130
|
+
function answerFor(variable, value, record = readRecord(recordFile), key = readHashKey()) {
|
|
131
|
+
const row = record.environmentKeys[variable];
|
|
132
|
+
return row && matches(row.fingerprint, value, key) ? row.answer : null;
|
|
133
|
+
}
|
|
134
|
+
function stateFor(provider) {
|
|
135
|
+
const found = environmentKeyFor(env, registry, provider, platform);
|
|
136
|
+
if (!found)
|
|
137
|
+
return null;
|
|
138
|
+
const answer = answerFor(found.variable, found.value);
|
|
139
|
+
return { provider, variable: found.variable, state: answer === 'yes' ? 'approved' : answer === 'no' ? 'declined' : 'unanswered' };
|
|
140
|
+
}
|
|
141
|
+
function answer(provider, value) {
|
|
142
|
+
if (value !== 'yes' && value !== 'no')
|
|
143
|
+
throw Object.assign(new Error('PROVIDER_CONSENT_ANSWER_INVALID'), { code: 'PROVIDER_CONSENT_ANSWER_INVALID' });
|
|
144
|
+
const found = environmentKeyFor(env, registry, provider, platform);
|
|
145
|
+
if (!found)
|
|
146
|
+
throw Object.assign(new Error('PROVIDER_ENVIRONMENT_KEY_NOT_FOUND'), { code: 'PROVIDER_ENVIRONMENT_KEY_NOT_FOUND' });
|
|
147
|
+
const key = hashKey(true);
|
|
148
|
+
const record = readRecord(recordFile);
|
|
149
|
+
record.environmentKeys[found.variable] = { fingerprint: fingerprint(found.value, key), answer: value };
|
|
150
|
+
mkdirSync(dataRoot, { recursive: true, mode: 0o700 });
|
|
151
|
+
writeAtomically(recordFile, `${JSON.stringify(record, null, 2)}\n`);
|
|
152
|
+
return { provider, variable: found.variable, state: value === 'yes' ? 'approved' : 'declined' };
|
|
153
|
+
}
|
|
154
|
+
function chosenProvider() { return readRecord(recordFile).chosenProvider; }
|
|
155
|
+
function chooseProvider(provider) {
|
|
156
|
+
if (typeof provider !== 'string' || !PROVIDER_ID.test(provider) || !registry[provider])
|
|
157
|
+
throw Object.assign(new Error('PROVIDER_INVALID'), { code: 'PROVIDER_INVALID' });
|
|
158
|
+
const record = readRecord(recordFile);
|
|
159
|
+
record.chosenProvider = provider;
|
|
160
|
+
mkdirSync(dataRoot, { recursive: true, mode: 0o700 });
|
|
161
|
+
writeAtomically(recordFile, `${JSON.stringify(record, null, 2)}\n`);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The environment the credential store may see in an interactive run: every provider-key variable is
|
|
165
|
+
* removed unless its CURRENT value was approved. Pool variables are never offered, so they never pass.
|
|
166
|
+
* Any other variable whose NAME looks like a secret is removed too. Endpoints and paths pass unchanged.
|
|
167
|
+
* ⛔ On win32 a provider-key or pool variable is recognised in ANY case, every spelling of it is removed, and an approved value
|
|
168
|
+
* is put back under the registry's canonical name, the one the credential store reads.
|
|
169
|
+
*/
|
|
170
|
+
function environmentForStore() {
|
|
171
|
+
const record = readRecord(recordFile);
|
|
172
|
+
const key = readHashKey();
|
|
173
|
+
const keyNames = new Set(Object.keys(registry).flatMap(provider => keyVariablesFor(registry, provider)));
|
|
174
|
+
const credentialNames = new Set([...credentialVariableNames(registry)].map(name => foldName(name, platform)));
|
|
175
|
+
const out = {};
|
|
176
|
+
for (const [name, value] of Object.entries(env)) {
|
|
177
|
+
if (credentialNames.has(foldName(name, platform)) || SECRET_LOOKING_NAME.test(name))
|
|
178
|
+
continue;
|
|
179
|
+
out[name] = value;
|
|
180
|
+
}
|
|
181
|
+
for (const name of keyNames) {
|
|
182
|
+
const value = readVariable(env, name, platform);
|
|
183
|
+
if (typeof value === 'string' && value.trim() !== '' && answerFor(name, value.trim(), record, key) === 'yes')
|
|
184
|
+
out[name] = value;
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
return { stateFor, answer, chosenProvider, chooseProvider, environmentForStore, files: { record: recordFile, hashKey: hashKeyFile } };
|
|
189
|
+
}
|
|
190
|
+
export function providerOfModel(model) {
|
|
191
|
+
const index = typeof model === 'string' ? model.indexOf(':') : -1;
|
|
192
|
+
return index > 0 ? model.slice(0, index) : null;
|
|
193
|
+
}
|
|
194
|
+
/** Where the key a run would use comes from, as the credential store sees it. Never the key itself. */
|
|
195
|
+
export function describeKeyOrigin({ registry, provider, publicRow, storeEnv, mode, store }) {
|
|
196
|
+
const record = registry[provider];
|
|
197
|
+
if (!record)
|
|
198
|
+
return null;
|
|
199
|
+
const label = providerLabelText(provider, String(record.etichetta ?? provider));
|
|
200
|
+
const required = typeof publicRow?.requiresKey === 'boolean' ? publicRow.requiresKey : record.chiaveObbligatoria === true;
|
|
201
|
+
if (!required)
|
|
202
|
+
return { provider, label, required, origin: 'not-required', variable: null, askedFirst: false };
|
|
203
|
+
if (publicRow?.origineChiave === 'custodia')
|
|
204
|
+
return { provider, label, required, origin: 'keyring', variable: null, askedFirst: false, ...(store === 'memory' ? { store } : {}) };
|
|
205
|
+
if (publicRow?.origineChiave === 'ambiente')
|
|
206
|
+
return { provider, label, required, origin: 'environment', variable: environmentKeyFor(storeEnv, registry, provider)?.variable ?? null, askedFirst: mode === 'consent' };
|
|
207
|
+
return { provider, label, required, origin: 'none', variable: null, askedFirst: false };
|
|
208
|
+
}
|
|
209
|
+
export function keyOriginNotice(origin) {
|
|
210
|
+
/* R2 (E1, E13): the words come from the dictionary, and a key "saved" in the memory store is not said to be in the keyring. */
|
|
211
|
+
if (origin.origin === 'keyring')
|
|
212
|
+
return t(origin.store === 'memory' ? 'key.origin.saved.memory' : 'key.origin.saved.system', { provider: origin.label });
|
|
213
|
+
if (origin.origin === 'environment')
|
|
214
|
+
return t(origin.askedFirst ? 'key.origin.environment.approved' : 'key.origin.environment.unasked', { provider: origin.label, variable: origin.variable ?? t('key.origin.unknownVariable') });
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
/*
|
|
218
|
+
* ⭐ B1 slice 18, fix round — condition 3: WHO decides whether an environment key is used without asking. The ENTRIES do,
|
|
219
|
+
* explicitly: the interactive screen and `talos provider` ask (`consent`); `talos -p` and the one-shot commands that run a model
|
|
220
|
+
* (`research start`, `automation run`, `automation serve`, `session resume`) use it and state where it came from (`use`).
|
|
221
|
+
* The composition and the entry factories keep `consent` for a caller that forgets.
|
|
222
|
+
* ⛔ Every child the screen spawns carries `TALOS_ENVIRONMENT_KEYS=consent`. An entry honours the variable ONLY to close: the value
|
|
223
|
+
* `consent` forces consent, and any other value, `use` included, is ignored. A variable can never open what an entry closed.
|
|
224
|
+
*/
|
|
225
|
+
export const ENVIRONMENT_KEYS_MARKER = 'TALOS_ENVIRONMENT_KEYS';
|
|
226
|
+
export function environmentKeyModeForEntry(requested, env = process.env) {
|
|
227
|
+
return env[ENVIRONMENT_KEYS_MARKER] === 'consent' ? 'consent' : requested;
|
|
228
|
+
}
|
|
229
|
+
/** The environment a child of the interactive screen runs with: the screen's own, closed. */
|
|
230
|
+
export function screenChildEnvironment(env = process.env) { return { ...env, [ENVIRONMENT_KEYS_MARKER]: 'consent' }; }
|
|
231
|
+
/**
|
|
232
|
+
* For a one-shot command that runs a model, as `talos -p` does: the origin statement for that model's key, or null when the
|
|
233
|
+
* runtime cannot tell. With `refuseMissing`, a provider that needs a key it does not have is refused BEFORE anything is sent,
|
|
234
|
+
* with a coded error naming that provider (exit 11 through `exitCodeForError`).
|
|
235
|
+
*/
|
|
236
|
+
export function keyOriginForEntry(runtime, model, { refuseMissing }) {
|
|
237
|
+
const origin = keyOriginForRun(runtime, model);
|
|
238
|
+
if (!origin)
|
|
239
|
+
return null;
|
|
240
|
+
/* R2 (E8): the one missing-key sentence; the code is what lane E maps, never the text. */
|
|
241
|
+
if (refuseMissing && origin.required && origin.origin === 'none')
|
|
242
|
+
throw Object.assign(new Error(missingKeyText(origin.provider)), { code: 'PROVIDER_KEY_MISSING', provider: origin.provider });
|
|
243
|
+
return keyOriginNotice(origin);
|
|
244
|
+
}
|
|
245
|
+
/** States the origin the way the owner decided: one stderr line in text mode, a `warnings` entry in JSON. */
|
|
246
|
+
export function stateKeyOrigin(io, outputFormat, notice) {
|
|
247
|
+
if (!notice)
|
|
248
|
+
return {};
|
|
249
|
+
if (outputFormat !== 'json')
|
|
250
|
+
io.writeErr(`${notice}\n`);
|
|
251
|
+
return { warnings: [notice] };
|
|
252
|
+
}
|
|
253
|
+
const ORIGINS = new WeakMap();
|
|
254
|
+
/** Lets a run started through this runtime state its key origin without the runtime contract growing a field. */
|
|
255
|
+
export function attachKeyOrigin(runtime, resolve) { ORIGINS.set(runtime, resolve); }
|
|
256
|
+
/**
|
|
257
|
+
* ⛔ R0 (forensics B rows 2 and 5): a runtime that wraps another one (the M7-C supervisor, `26e46a339`) answers with the resolver of
|
|
258
|
+
* the runtime it delegates to NOW, read at every lookup, so a restarted inner runtime keeps stating its key origin. Without it
|
|
259
|
+
* the lookup on the wrapper found nothing: `talos -p` and `talos research start` stopped saying that an environment key was used
|
|
260
|
+
* without asking, and the headless missing-key refusal lost its `talos provider set-key` remedy.
|
|
261
|
+
*/
|
|
262
|
+
export function forwardKeyOrigin(wrapper, delegate) {
|
|
263
|
+
attachKeyOrigin(wrapper, (model) => { const inner = delegate(); return inner && inner !== wrapper ? keyOriginForRun(inner, model) : null; });
|
|
264
|
+
}
|
|
265
|
+
export function keyOriginForRun(runtime, model) {
|
|
266
|
+
const resolve = ORIGINS.get(runtime);
|
|
267
|
+
if (!resolve)
|
|
268
|
+
return null;
|
|
269
|
+
try {
|
|
270
|
+
return resolve(model);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { t, tn } from "../i18n/index.js";
|
|
2
|
+
import { missingKeyText } from "./missing-key.js";
|
|
3
|
+
/*
|
|
4
|
+
* R2 (gate E1): the bench causes the kernel classifies (`provider-credential-store.mjs`, `PANCHINE_PROVIDER_MS`), in a person's
|
|
5
|
+
* words, from the dictionary (`key.bench.cause.<code>`). This is the one table: `tui/catalog-service.ts` had a second copy.
|
|
6
|
+
* A cause not in the table is shown as no cause, never guessed.
|
|
7
|
+
*/
|
|
8
|
+
const BENCH_CAUSE_CODES = new Set(['traffico', 'credenziale', 'credito', 'rete', 'timeout-fornitore', 'guasto-fornitore', 'flusso-interrotto']);
|
|
9
|
+
export function benchCauseText(code) {
|
|
10
|
+
return typeof code === 'string' && BENCH_CAUSE_CODES.has(code) ? t(`key.bench.cause.${code}`) : null;
|
|
11
|
+
}
|
|
12
|
+
export const UNKNOWN_PROVIDER_HEALTH = Object.freeze({
|
|
13
|
+
state: 'unknown', detail: '', latencyMs: null, observedAt: null, httpStatus: null,
|
|
14
|
+
});
|
|
15
|
+
export function providerCooldownFromRow(row) {
|
|
16
|
+
const pool = Array.isArray(row?.pool) ? row.pool : [];
|
|
17
|
+
const benched = pool.filter(entry => entry?.stato === 'in-panchina');
|
|
18
|
+
const timed = benched.filter(entry => Number.isFinite(entry?.inPanchinaFino)).sort((a, b) => Number(a.inPanchinaFino) - Number(b.inPanchinaFino));
|
|
19
|
+
const first = timed[0] ?? benched[0] ?? null;
|
|
20
|
+
const cause = benchCauseText(first?.causa);
|
|
21
|
+
return {
|
|
22
|
+
active: benched.length > 0,
|
|
23
|
+
cause,
|
|
24
|
+
until: Number.isFinite(first?.inPanchinaFino) ? Number(first.inPanchinaFino) : null,
|
|
25
|
+
keys: pool.length,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function providerHealthFromProbe(result, observedAt) {
|
|
29
|
+
const esito = String(result?.esito ?? '');
|
|
30
|
+
const code = typeof result?.codice === 'string' ? result.codice : null;
|
|
31
|
+
const state = esito === 'collegato' ? 'available' :
|
|
32
|
+
esito === 'non-autorizzato' ? 'auth-failed' :
|
|
33
|
+
esito === 'irraggiungibile' ? 'unreachable' :
|
|
34
|
+
esito === 'non-sondabile' ? 'unprobeable' :
|
|
35
|
+
esito === 'non-provabile' ? (code === 'PROVIDER_RUNTIME_INVALID' || code === 'CATALOG_CONFIGURATION_REQUIRED' || code === 'PROVIDER_KEY_MISSING' ? 'unconfigured' : 'unprobeable') :
|
|
36
|
+
esito === 'errore' ? 'error' : 'unknown';
|
|
37
|
+
return {
|
|
38
|
+
state,
|
|
39
|
+
detail: String(result?.motivo ?? esito),
|
|
40
|
+
latencyMs: Number.isFinite(result?.millisecondi) ? Number(result.millisecondi) : null,
|
|
41
|
+
observedAt: Number.isFinite(observedAt) ? observedAt : null,
|
|
42
|
+
httpStatus: Number.isInteger(result?.httpStatus) ? Number(result.httpStatus) : null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** "until when" as a person reads it: local HH:MM, with the local date in front when it is not today. */
|
|
46
|
+
export function benchClock(until, now = Date.now()) {
|
|
47
|
+
const at = new Date(until);
|
|
48
|
+
const two = (n) => String(n).padStart(2, '0');
|
|
49
|
+
const time = `${two(at.getHours())}:${two(at.getMinutes())}`;
|
|
50
|
+
return at.toDateString() === new Date(now).toDateString() ? time : `${at.getFullYear()}-${two(at.getMonth() + 1)}-${two(at.getDate())} ${time}`;
|
|
51
|
+
}
|
|
52
|
+
/** "The OpenRouter key is benched until 15:04 (rate limit)". */
|
|
53
|
+
export function benchedKeyText(label, bench, now = Date.now()) {
|
|
54
|
+
let state = tn('key.benched', bench.keys > 1 ? bench.keys : 1, { provider: label });
|
|
55
|
+
if (bench.until !== null)
|
|
56
|
+
state = t('key.benched.until', { state, time: benchClock(bench.until, now) });
|
|
57
|
+
if (bench.cause)
|
|
58
|
+
state = t('key.benched.because', { state, cause: bench.cause });
|
|
59
|
+
return state;
|
|
60
|
+
}
|
|
61
|
+
function benchedKeyRemedy(bench) { return t(bench.until !== null ? 'key.benched.remedy.until' : 'key.benched.remedy'); }
|
|
62
|
+
/** The refusal of a send whose only keys are benched: the same sentence from the screen's readiness and from the session runtime. */
|
|
63
|
+
export function benchedKeyRefusal(label, bench, now = Date.now()) {
|
|
64
|
+
return t('key.benched.refusal', { state: benchedKeyText(label, bench, now), provider: label, remedy: benchedKeyRemedy(bench) });
|
|
65
|
+
}
|
|
66
|
+
export function remediationForProvider(input) {
|
|
67
|
+
const provider = input.label;
|
|
68
|
+
if (input.auth.required && !input.auth.usable) {
|
|
69
|
+
if (input.cooldown.active) {
|
|
70
|
+
/* The state and the remedy; nothing was attempted yet, so the remediation does not say "nothing was sent". */
|
|
71
|
+
return { code: 'wait-or-add-key', message: t('key.benched.status', { state: benchedKeyText(provider, input.cooldown), remedy: benchedKeyRemedy(input.cooldown) }) };
|
|
72
|
+
}
|
|
73
|
+
/* R2 (E8 / Q-R2-7): the one missing-key sentence, the same on the screen, `-p`, `provider test` and `doctor`. */
|
|
74
|
+
return { code: 'add-key', message: missingKeyText(input.id) };
|
|
75
|
+
}
|
|
76
|
+
if (input.lastHealth.state === 'auth-failed')
|
|
77
|
+
return { code: 'replace-key', message: t('key.remedy.replaceKey', { provider }) };
|
|
78
|
+
if (!input.runtimeConfigured)
|
|
79
|
+
return { code: 'configure-provider', message: t('key.remedy.configureProvider', { provider }) };
|
|
80
|
+
if (!input.endpoint.valid)
|
|
81
|
+
return { code: 'configure-endpoint', message: t('key.remedy.configureEndpoint', { provider }) };
|
|
82
|
+
if (input.lastHealth.state === 'unreachable')
|
|
83
|
+
return { code: 'check-endpoint', message: t('key.remedy.checkEndpoint', { provider }) };
|
|
84
|
+
if (input.lastHealth.state === 'error')
|
|
85
|
+
return { code: 'retry', message: t('key.remedy.retry', { provider }) };
|
|
86
|
+
if (input.lastHealth.state === 'unconfigured')
|
|
87
|
+
return { code: 'configure-endpoint', message: t('key.remedy.completeConfiguration', { provider }) };
|
|
88
|
+
if (input.lastHealth.state === 'unprobeable')
|
|
89
|
+
return { code: 'inspect-provider', message: t('key.remedy.inspect', { provider }) };
|
|
90
|
+
return { code: 'none', message: '' };
|
|
91
|
+
}
|
|
92
|
+
export function providerReadinessDecision(input) {
|
|
93
|
+
const remediation = remediationForProvider(input);
|
|
94
|
+
if (input.auth.required && !input.auth.usable) {
|
|
95
|
+
if (input.cooldown.active)
|
|
96
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_KEY_BENCHED', message: benchedKeyRefusal(input.label, input.cooldown), remediation };
|
|
97
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_KEY_MISSING', message: remediation.message, remediation };
|
|
98
|
+
}
|
|
99
|
+
if (input.lastHealth.state === 'auth-failed')
|
|
100
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_AUTH_FAILED', message: remediation.message, remediation };
|
|
101
|
+
if (!input.runtimeConfigured)
|
|
102
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_NOT_CONFIGURED', message: remediation.message, remediation };
|
|
103
|
+
if (!input.endpoint.valid)
|
|
104
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_ENDPOINT_INVALID', message: remediation.message, remediation };
|
|
105
|
+
if (input.lastHealth.state === 'unreachable')
|
|
106
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_UNREACHABLE', message: remediation.message, remediation };
|
|
107
|
+
if (input.lastHealth.state === 'error' || input.lastHealth.state === 'unconfigured')
|
|
108
|
+
return { ready: false, provider: input.id, code: 'PROVIDER_HEALTH_ERROR', message: remediation.message, remediation };
|
|
109
|
+
return { ready: true, provider: input.id, remediation };
|
|
110
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* R2 CONTRACT (owner: lane K; stub written by the coordinator so parallel lanes compile). The ONE missing-key sentence,
|
|
3
|
+
* used verbatim by the TUI refusal, `-p` (text and JSON message), `provider test` and `doctor` (Q-R2-7, gate E8).
|
|
4
|
+
* Lanes other than K only CALL `missingKeyText`; lane K owns the implementation and may not change the signature.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ Where the label and the variable come from, measured: the kernel's registry (`harness-ui/src/provider-registry.mjs`,
|
|
7
|
+
* `REGISTRO_FORNITORI[id].etichetta` and `.auth.nomeVariabile`), the table every path already reads. The CLI's
|
|
8
|
+
* `ProviderDefinition.env` (`provider/store.ts`) is used by no production path. The registry is a kernel module loaded
|
|
9
|
+
* asynchronously from the repo root, so the composition hands it here once (`useProviderRegistry`) and this function stays
|
|
10
|
+
* synchronous. The variable is the FIRST one (gate E8): `kimi` names MOONSHOT_API_KEY, the one the store reads first.
|
|
11
|
+
* ⛔ A provider the registry does not name gets the sentence without a variable, never an invented one.
|
|
12
|
+
*/
|
|
13
|
+
import { providerLabelText } from "./provider-text.js";
|
|
14
|
+
import { t } from "../i18n/index.js";
|
|
15
|
+
import { keyVariablesFor } from "./environment-keys.js";
|
|
16
|
+
let registry = {};
|
|
17
|
+
/** Called by the composition with the kernel's registry; the last composition in the process wins (the table is the same). */
|
|
18
|
+
export function useProviderRegistry(value) { registry = value && typeof value === 'object' ? value : {}; }
|
|
19
|
+
/** The provider id for `ref`, which is an id or (for a caller that only holds a row's label) a label. */
|
|
20
|
+
function providerIdOf(ref) {
|
|
21
|
+
if (Object.hasOwn(registry, ref))
|
|
22
|
+
return ref;
|
|
23
|
+
for (const [id, record] of Object.entries(registry))
|
|
24
|
+
if (record?.etichetta === ref || providerLabelText(id, '') === ref)
|
|
25
|
+
return id;
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The label a person reads for `ref` (an id or a label); `fallback` (or `ref`) when the registry does not know it.
|
|
30
|
+
* ⛔ Open (gate §1.4, E4 "provider id for labels"): four registry labels carry Italian (`esterno`, `local`, `zai-anthropic`,
|
|
31
|
+
* `minimax-anthropic`). They are NOT replaced here alone: the provider overlay takes its label from `provider/control-plane.ts`,
|
|
32
|
+
* and an English label in this sentence under an Italian overlay title was measured in one frame. This function is the seam
|
|
33
|
+
* for that mapping once every surface reads it.
|
|
34
|
+
*/
|
|
35
|
+
export function providerLabel(ref, fallback = ref) {
|
|
36
|
+
const id = providerIdOf(ref);
|
|
37
|
+
if (!id)
|
|
38
|
+
return fallback;
|
|
39
|
+
return providerLabelText(id, String(registry[id]?.etichetta ?? id));
|
|
40
|
+
}
|
|
41
|
+
export function missingKeyText(providerId) {
|
|
42
|
+
const id = providerIdOf(providerId);
|
|
43
|
+
if (!id)
|
|
44
|
+
return t('key.missing.noEnv', { provider: providerId, id: providerId });
|
|
45
|
+
const provider = providerLabel(id);
|
|
46
|
+
const env = keyVariablesFor(registry, id)[0];
|
|
47
|
+
return env ? t('key.missing', { provider, env, id }) : t('key.missing.noEnv', { provider, id });
|
|
48
|
+
}
|