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,245 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { copyFile, mkdir, rename, rm, stat } from 'node:fs/promises';
|
|
4
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { createProcessPolicy } from './process-policy.mjs';
|
|
6
|
+
|
|
7
|
+
const REVISION = /^[a-f0-9]{40}$/iu;
|
|
8
|
+
const SHA256 = /^[a-f0-9]{64}$/iu;
|
|
9
|
+
const ID = /^[a-z0-9][a-z0-9._-]{0,127}$/iu;
|
|
10
|
+
|
|
11
|
+
export class HfModelTransferError extends Error {
|
|
12
|
+
constructor(message, code = 'HF_TRANSFER_FAILED') {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'HfModelTransferError';
|
|
15
|
+
this.code = code;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function fail(message, code = 'HF_TRANSFER_INVALID') {
|
|
20
|
+
throw new HfModelTransferError(message, code);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function validateRequest(value) {
|
|
24
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
25
|
+
|| !ID.test(value.id) || typeof value.repo !== 'string' || value.repo.trim() === ''
|
|
26
|
+
|| !REVISION.test(value.revision) || !Array.isArray(value.files) || value.files.length === 0
|
|
27
|
+
|| !Number.isSafeInteger(value.bytes) || value.bytes <= 0
|
|
28
|
+
|| !SHA256.test(value.sha256) || typeof value.license !== 'string' || value.license.trim() === ''
|
|
29
|
+
|| typeof value.path !== 'string' || value.path.trim() === '' || isAbsolute(value.path)) {
|
|
30
|
+
fail('model transfer request is invalid');
|
|
31
|
+
}
|
|
32
|
+
const files = value.files.map((file) => {
|
|
33
|
+
if (!file || typeof file.path !== 'string' || file.path.trim() === '' || isAbsolute(file.path)
|
|
34
|
+
|| !Number.isSafeInteger(file.bytes) || file.bytes <= 0 || !SHA256.test(file.sha256)) fail('model transfer file is invalid');
|
|
35
|
+
return { path: file.path, bytes: file.bytes, sha256: file.sha256.toLowerCase() };
|
|
36
|
+
});
|
|
37
|
+
if (files.reduce((sum, file) => sum + file.bytes, 0) !== value.bytes) fail('model transfer byte count is invalid');
|
|
38
|
+
return { ...value, files, sha256: value.sha256.toLowerCase(), state: 'incomplete' };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function assertInside(rootDir, value, label) {
|
|
42
|
+
const absolute = resolve(rootDir, value);
|
|
43
|
+
const difference = relative(resolve(rootDir), absolute);
|
|
44
|
+
if (difference === '..' || difference.startsWith(`..${'\\'}`) || isAbsolute(difference)) fail(`${label} escapes model root`);
|
|
45
|
+
return absolute;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function digestFile(path) {
|
|
49
|
+
const hash = createHash('sha256');
|
|
50
|
+
let bytes = 0;
|
|
51
|
+
const stream = createReadStream(path);
|
|
52
|
+
for await (const chunk of stream) {
|
|
53
|
+
bytes += chunk.length;
|
|
54
|
+
hash.update(chunk);
|
|
55
|
+
}
|
|
56
|
+
return { bytes, sha256: hash.digest('hex') };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function progressFrom(text) {
|
|
60
|
+
const matches = [...String(text).matchAll(/(?:^|\s)(\d{1,3}(?:\.\d+)?)%/gu)];
|
|
61
|
+
if (matches.length === 0) return null;
|
|
62
|
+
const value = Number(matches.at(-1)[1]);
|
|
63
|
+
return Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createHfModelTransfer({
|
|
67
|
+
hfExecutable = 'hf',
|
|
68
|
+
spawnImpl,
|
|
69
|
+
modelStore,
|
|
70
|
+
rootDir,
|
|
71
|
+
fsImpl = {},
|
|
72
|
+
now = () => new Date(),
|
|
73
|
+
allowedRepositories = [],
|
|
74
|
+
} = {}) {
|
|
75
|
+
if (typeof spawnImpl !== 'function' || !modelStore || typeof modelStore.inspect !== 'function'
|
|
76
|
+
|| typeof modelStore.register !== 'function' || typeof modelStore.setState !== 'function'
|
|
77
|
+
|| typeof rootDir !== 'string' || !isAbsolute(rootDir)) {
|
|
78
|
+
fail('transfer dependencies are invalid', 'HF_TRANSFER_MISCONFIGURED');
|
|
79
|
+
}
|
|
80
|
+
const fs = { copyFile, createReadStream, mkdir, rename, rm, stat, ...fsImpl };
|
|
81
|
+
const processPolicy = createProcessPolicy({
|
|
82
|
+
allowedExecutables: [hfExecutable],
|
|
83
|
+
capabilities: { models: rootDir },
|
|
84
|
+
spawnFn: spawnImpl,
|
|
85
|
+
});
|
|
86
|
+
const allowed = new Set(allowedRepositories);
|
|
87
|
+
const transfers = new Map();
|
|
88
|
+
|
|
89
|
+
function checkRepo(request) {
|
|
90
|
+
if (!allowed.has(request.repo)) fail(`repository ${request.repo} is not allowed`, 'HF_REPOSITORY_NOT_ALLOWED');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function argsFor(request, dryRun = false) {
|
|
94
|
+
const args = ['download', request.repo, '--revision', request.revision];
|
|
95
|
+
for (const file of request.files) args.push('--include', file.path);
|
|
96
|
+
args.push('--local-dir', assertInside(rootDir, request.path, 'local-dir'), '--quiet');
|
|
97
|
+
if (dryRun) args.push('--dry-run');
|
|
98
|
+
return args;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function spawn(command, args) {
|
|
102
|
+
try {
|
|
103
|
+
return processPolicy.spawn(command, args, { cwd: rootDir, shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error?.code === 'ENOENT') fail('Hugging Face CLI is not installed', 'HF_CLI_MISSING');
|
|
106
|
+
throw new HfModelTransferError(`cannot start Hugging Face CLI: ${error.message}`, 'HF_SPAWN_FAILED');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function observeProgress(record, chunk) {
|
|
111
|
+
const value = progressFrom(chunk);
|
|
112
|
+
if (value !== null) record.progress = Math.max(record.progress ?? 0, value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function wireProcess(record, child) {
|
|
116
|
+
record.child = child;
|
|
117
|
+
const onData = (chunk) => observeProgress(record, chunk);
|
|
118
|
+
child.stdout?.on('data', onData);
|
|
119
|
+
child.stderr?.on('data', onData);
|
|
120
|
+
child.once('error', (error) => {
|
|
121
|
+
if (error?.code === 'ENOENT') {
|
|
122
|
+
record.state = 'gated';
|
|
123
|
+
record.reason = 'HF_CLI_MISSING';
|
|
124
|
+
} else if (!['paused', 'cancelled'].includes(record.state)) {
|
|
125
|
+
record.state = 'failed';
|
|
126
|
+
record.reason = 'HF_CLI_ERROR';
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
child.once('close', (code) => {
|
|
130
|
+
if (record.state === 'paused' || record.state === 'cancelled') return;
|
|
131
|
+
if (code !== 0) {
|
|
132
|
+
record.state = 'failed';
|
|
133
|
+
record.reason = 'HF_DOWNLOAD_FAILED';
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
record.state = 'verifying';
|
|
137
|
+
verify(record.request).then(() => {
|
|
138
|
+
record.state = 'ready';
|
|
139
|
+
record.progress = 100;
|
|
140
|
+
}).catch((error) => {
|
|
141
|
+
record.state = 'failed';
|
|
142
|
+
record.reason = error.code || 'CHECKSUM_MISMATCH';
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function inspect(value) {
|
|
148
|
+
const request = validateRequest(value);
|
|
149
|
+
checkRepo(request);
|
|
150
|
+
const child = spawn(hfExecutable, argsFor(request, true));
|
|
151
|
+
return new Promise((resolveResult, reject) => {
|
|
152
|
+
let missing = false;
|
|
153
|
+
child.once('error', (error) => {
|
|
154
|
+
if (error?.code === 'ENOENT') {
|
|
155
|
+
missing = true;
|
|
156
|
+
resolveResult({ state: 'gated', reason: 'HF_CLI_MISSING' });
|
|
157
|
+
} else reject(new HfModelTransferError(error.message, 'HF_CLI_ERROR'));
|
|
158
|
+
});
|
|
159
|
+
child.once('close', (code) => {
|
|
160
|
+
if (missing) return;
|
|
161
|
+
if (code !== 0) reject(new HfModelTransferError('Hugging Face dry-run failed', 'HF_INSPECT_FAILED'));
|
|
162
|
+
else resolveResult({ state: 'available', repo: request.repo, revision: request.revision, files: request.files, cli: { executable: hfExecutable, version: null } });
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function start(value) {
|
|
168
|
+
const request = validateRequest(value);
|
|
169
|
+
checkRepo(request);
|
|
170
|
+
const previous = transfers.get(request.id);
|
|
171
|
+
if (previous?.state === 'running' || previous?.state === 'verifying') return status(request.id);
|
|
172
|
+
const existing = await modelStore.inspect(request.id);
|
|
173
|
+
if (!existing) await modelStore.register({ ...request, updatedAt: now().toISOString() });
|
|
174
|
+
else if (existing.repo !== request.repo || existing.revision !== request.revision) fail('existing transfer has a different revision', 'HF_TRANSFER_COLLISION');
|
|
175
|
+
const record = { id: request.id, request, state: 'running', progress: previous?.progress ?? 0, reason: null, startedAt: now().toISOString(), child: null };
|
|
176
|
+
transfers.set(request.id, record);
|
|
177
|
+
try { wireProcess(record, spawn(hfExecutable, argsFor(request))); } catch (error) {
|
|
178
|
+
if (error.code === 'HF_CLI_MISSING') { record.state = 'gated'; record.reason = error.code; }
|
|
179
|
+
else throw error;
|
|
180
|
+
}
|
|
181
|
+
return status(request.id);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function pause(id) {
|
|
185
|
+
const record = transfers.get(id);
|
|
186
|
+
if (!record || !['running', 'verifying'].includes(record.state)) return false;
|
|
187
|
+
record.state = 'paused';
|
|
188
|
+
record.reason = 'PAUSED_BY_OWNER';
|
|
189
|
+
record.child?.kill?.('SIGTERM');
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function resume(id) {
|
|
194
|
+
const record = transfers.get(id);
|
|
195
|
+
if (!record || !['paused', 'cancelled', 'incomplete'].includes(record.state)) return false;
|
|
196
|
+
return start(record.request);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function cancel(id) {
|
|
200
|
+
const record = transfers.get(id);
|
|
201
|
+
if (!record || ['ready', 'failed', 'gated'].includes(record.state)) return false;
|
|
202
|
+
record.state = 'cancelled';
|
|
203
|
+
record.reason = 'CANCELLED_BY_OWNER';
|
|
204
|
+
record.child?.kill?.('SIGTERM');
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function status(id) {
|
|
209
|
+
const record = transfers.get(id);
|
|
210
|
+
if (!record) return null;
|
|
211
|
+
return Object.freeze({ id: record.id, state: record.state, progress: record.progress, reason: record.reason, startedAt: record.startedAt });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function verify(id) {
|
|
215
|
+
const manifest = await modelStore.inspect(typeof id === 'string' ? id : id.id);
|
|
216
|
+
if (!manifest) fail('model manifest not found', 'MODEL_NOT_FOUND');
|
|
217
|
+
for (const file of manifest.files) {
|
|
218
|
+
const path = assertInside(rootDir, join(manifest.path, file.path), 'model file');
|
|
219
|
+
let actual;
|
|
220
|
+
try { actual = await digestFile(path); } catch (error) { fail(`cannot read ${file.path}: ${error.message}`, 'MODEL_FILE_UNREADABLE'); }
|
|
221
|
+
if (actual.bytes !== file.bytes || actual.sha256.toLowerCase() !== file.sha256.toLowerCase()) {
|
|
222
|
+
fail(`checksum mismatch for ${file.path}`, 'CHECKSUM_MISMATCH');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return modelStore.setState(manifest.id, 'ready');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function importLocal(value, sourcePath) {
|
|
229
|
+
const request = validateRequest(value);
|
|
230
|
+
if (!isAbsolute(sourcePath)) fail('local source must be absolute');
|
|
231
|
+
if (request.files.length !== 1) fail('local import currently supports one GGUF file', 'LOCAL_IMPORT_UNSUPPORTED');
|
|
232
|
+
checkRepo(request);
|
|
233
|
+
const source = resolve(sourcePath);
|
|
234
|
+
const destination = assertInside(rootDir, join(request.path, request.files[0].path), 'model file');
|
|
235
|
+
const existing = await modelStore.inspect(request.id);
|
|
236
|
+
if (!existing) await modelStore.register({ ...request, updatedAt: now().toISOString() });
|
|
237
|
+
await fs.mkdir(resolve(destination, '..'), { recursive: true });
|
|
238
|
+
const temporary = `${destination}.partial-${process.pid}-${Date.now()}`;
|
|
239
|
+
await fs.copyFile(source, temporary);
|
|
240
|
+
await fs.rename(temporary, destination);
|
|
241
|
+
return verify(request.id);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return Object.freeze({ inspect, start, pause, resume, cancel, status, verify, importLocal });
|
|
245
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hook-registry.mjs — FASE A del piano `elegant-spinning-dongarra.md`
|
|
3
|
+
* ("Harness Desktop al 100%"), owner 28/8. Ricerca fatta prima di
|
|
4
|
+
* scrivere: lo stato dell'arte offre due pattern diversi — hook
|
|
5
|
+
* "universali e attivi di default" su ogni tool, oppure un sistema di
|
|
6
|
+
* *trust* persistito, dove un hook nuovo o modificato non gira finché non
|
|
7
|
+
* è esplicitamente fidato (un flag esplicito esiste solo per bypassarlo).
|
|
8
|
+
* Questo file implementa entrambi i pattern, adattati.
|
|
9
|
+
*
|
|
10
|
+
* ⛔⛔ Due cartelle DIVERSE, per un motivo preciso:
|
|
11
|
+
* - `hooks.json` vive DENTRO il workspace del progetto (`cartella`
|
|
12
|
+
* della sessione) — un hook è dichiarato "per-progetto", come
|
|
13
|
+
* AGENTS.md o un file di config di build: chi clona il progetto
|
|
14
|
+
* vede quali hook esistono.
|
|
15
|
+
* - il registro di TRUST vive FUORI, accanto a `server.mjs` (stesso
|
|
16
|
+
* pattern già in uso per `.automations/`, verificato in
|
|
17
|
+
* `automation-store.mjs`/`server.mjs`: `cartella:
|
|
18
|
+
* fileURLToPath(new URL('.automations/', import.meta.url))`) — il
|
|
19
|
+
* trust è una decisione dell'OWNER su questa macchina, mai qualcosa
|
|
20
|
+
* che un progetto clonato può auto-concedersi scrivendo un file.
|
|
21
|
+
*
|
|
22
|
+
* ⛔ Correzione di una nota nel piano madre: quella sezione ipotizzava
|
|
23
|
+
* un trust "accanto a .sessions/" — verificato oggi che `.sessions/`
|
|
24
|
+
* non esiste ancora (le sessioni vivono solo in memoria, dichiarato
|
|
25
|
+
* in `automation-store.mjs`: "diverso da session-registry.mjs
|
|
26
|
+
* apposta"). Il trust usa lo stesso pattern REALE di `.automations/`.
|
|
27
|
+
*/
|
|
28
|
+
import { createHash } from 'node:crypto';
|
|
29
|
+
import { promises as fsp } from 'node:fs';
|
|
30
|
+
import { join } from 'node:path';
|
|
31
|
+
import { createProcessPolicy, parseProcessCommand } from './process-policy.mjs';
|
|
32
|
+
|
|
33
|
+
export class HookRegistryError extends Error {
|
|
34
|
+
constructor(message, code = 'HOOK_INVALID') {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'HookRegistryError';
|
|
37
|
+
this.code = code;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const NOME_FILE_HOOKS = '.harness-ui-hooks.json';
|
|
42
|
+
/**
|
|
43
|
+
* ⭐ 29/8 — esportata (era locale a caricaHooks) perché FASE G
|
|
44
|
+
* (plugin-registry.mjs) valida gli hook dichiarati DENTRO un
|
|
45
|
+
* plugin.json con la STESSA regola — un import qui, non una seconda
|
|
46
|
+
* lista che potrebbe divergere in silenzio se un evento nuovo si
|
|
47
|
+
* aggiunge un domani.
|
|
48
|
+
*/
|
|
49
|
+
export const EVENTI_VALIDI = new Set(['pre_tool_call', 'post_tool_call', 'session_start', 'session_end']);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Legge `<cartella>/.harness-ui-hooks.json`. Un progetto senza hook è
|
|
53
|
+
* uno stato valido — mai un errore, torna `{ hooks: [] }`. Uno schema
|
|
54
|
+
* malformato invece È un errore dichiarato (`HookRegistryError`, mai
|
|
55
|
+
* un hook fantasma silenziosamente ignorato): un file che l'owner ha
|
|
56
|
+
* scritto a mano e sbagliato deve saperlo, non vedere l'hook
|
|
57
|
+
* semplicemente non funzionare.
|
|
58
|
+
*/
|
|
59
|
+
export async function caricaHooks({ cartella }, deps = {}) {
|
|
60
|
+
const readFileFn = deps.readFileFn ?? fsp.readFile;
|
|
61
|
+
const percorso = join(cartella, NOME_FILE_HOOKS);
|
|
62
|
+
let testo;
|
|
63
|
+
try {
|
|
64
|
+
testo = await readFileFn(percorso, 'utf8');
|
|
65
|
+
} catch (errore) {
|
|
66
|
+
if (errore?.code === 'ENOENT') return { hooks: [] };
|
|
67
|
+
throw new HookRegistryError(`Impossibile leggere ${NOME_FILE_HOOKS}: ${errore.message}`, 'HOOK_READ_FAILED');
|
|
68
|
+
}
|
|
69
|
+
let dati;
|
|
70
|
+
try {
|
|
71
|
+
dati = JSON.parse(testo);
|
|
72
|
+
} catch {
|
|
73
|
+
throw new HookRegistryError(`${NOME_FILE_HOOKS} non è un JSON valido`, 'HOOK_MALFORMED');
|
|
74
|
+
}
|
|
75
|
+
if (!dati || !Array.isArray(dati.hooks)) {
|
|
76
|
+
throw new HookRegistryError(`${NOME_FILE_HOOKS} deve avere un campo "hooks" (array)`, 'HOOK_MALFORMED');
|
|
77
|
+
}
|
|
78
|
+
const hooks = dati.hooks.map((voce, indice) => {
|
|
79
|
+
if (typeof voce?.id !== 'string' || voce.id.length === 0) {
|
|
80
|
+
throw new HookRegistryError(`hooks[${indice}] manca di "id" (stringa non vuota)`, 'HOOK_MALFORMED');
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(voce.eventi) || voce.eventi.length === 0 || !voce.eventi.every((e) => EVENTI_VALIDI.has(e))) {
|
|
83
|
+
throw new HookRegistryError(`hooks[${indice}] ("${voce.id}") ha "eventi" non valido — atteso un array non vuoto fra ${[...EVENTI_VALIDI].join('/')}`, 'HOOK_MALFORMED');
|
|
84
|
+
}
|
|
85
|
+
if (typeof voce.comando !== 'string' || voce.comando.length === 0) {
|
|
86
|
+
throw new HookRegistryError(`hooks[${indice}] ("${voce.id}") manca di "comando" (stringa non vuota)`, 'HOOK_MALFORMED');
|
|
87
|
+
}
|
|
88
|
+
const hash = createHash('sha256').update(voce.comando).digest('hex');
|
|
89
|
+
return { id: voce.id, eventi: voce.eventi, comando: voce.comando, hash };
|
|
90
|
+
});
|
|
91
|
+
return { hooks };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function percorsoTrust(cartellaTrust, hookId) {
|
|
95
|
+
// ⛔ hookId arriva da un file scritto dall'owner (hooks.json), ma è
|
|
96
|
+
// comunque un input esterno: mai costruire un percorso da una
|
|
97
|
+
// stringa non validata come nome file — stesso principio già in uso
|
|
98
|
+
// in workspace-files.mjs per un "nome, non un percorso".
|
|
99
|
+
if (typeof hookId !== 'string' || hookId.length === 0 || /[\\/]|\.\./.test(hookId)) {
|
|
100
|
+
throw new HookRegistryError('hookId non valido — un nome, non un percorso', 'HOOK_ID_INVALID');
|
|
101
|
+
}
|
|
102
|
+
return join(cartellaTrust, `${hookId}.json`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Un hook è fidato SOLO se il registro persistito porta lo STESSO hash
|
|
107
|
+
* del comando attuale — un hook il cui contenuto è cambiato (hash
|
|
108
|
+
* diverso) torna automaticamente "non fidato", senza bisogno di
|
|
109
|
+
* un'azione esplicita di sfiducia: il trust è legato al CONTENUTO, non
|
|
110
|
+
* al nome.
|
|
111
|
+
*/
|
|
112
|
+
export async function verificaTrust({ cartellaTrust, hookId, hash }, deps = {}) {
|
|
113
|
+
const readFileFn = deps.readFileFn ?? fsp.readFile;
|
|
114
|
+
let testo;
|
|
115
|
+
try {
|
|
116
|
+
testo = await readFileFn(percorsoTrust(cartellaTrust, hookId), 'utf8');
|
|
117
|
+
} catch {
|
|
118
|
+
return false; // mai fidato per default: assente = non fidato, non un errore
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const dati = JSON.parse(testo);
|
|
122
|
+
return dati?.hash === hash;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Registra il trust — chiamata SOLO da un'azione owner esplicita (la
|
|
130
|
+
* rotta HTTP dedicata, mai automatica dal ciclo dell'agente).
|
|
131
|
+
*/
|
|
132
|
+
export async function fidaHook({ cartellaTrust, hookId, hash }, deps = {}) {
|
|
133
|
+
const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
|
|
134
|
+
const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
|
|
135
|
+
await mkdirFn(cartellaTrust, { recursive: true });
|
|
136
|
+
await writeFileFn(percorsoTrust(cartellaTrust, hookId), JSON.stringify({ hash, fidatoIl: new Date().toISOString() }), 'utf8');
|
|
137
|
+
return { fidato: true };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Esegue un hook come sottoprocesso shell — stesso pattern di
|
|
142
|
+
* `eseguiComandoSandboxato`/`eseguiProva` in talosHarness.mjs (spawn,
|
|
143
|
+
* cwd della sessione, windowsHide, timeout), qui duplicato
|
|
144
|
+
* deliberatamente invece di importato: `hook-registry.mjs` vive nel
|
|
145
|
+
* backend (`harness-ui/`), `talosHarness.mjs` nel kernel benchmarkato
|
|
146
|
+
* (`AVM-harness/`) — repository diversi, la doc di `talosHarness.mjs`
|
|
147
|
+
* vieta esplicitamente una copia del KERNEL stesso ("divergerebbe in
|
|
148
|
+
* silenzio"), ma questa è solo una utility di spawn, non il kernel.
|
|
149
|
+
*
|
|
150
|
+
* Contratto d'uscita, dichiarato non presunto: se stdout è un JSON
|
|
151
|
+
* valido `{consentito, motivo?}`, quello è l'esito. Altrimenti
|
|
152
|
+
* `exit 0` → consentito, qualunque altro codice → rifiutato (stesso
|
|
153
|
+
* principio "un contratto d'uscita chiaro" già usato per
|
|
154
|
+
* `rivelaInEsploraFile`/explorer.exe).
|
|
155
|
+
*/
|
|
156
|
+
export async function eseguiHook({ hook, evento, cartella }, deps = {}) {
|
|
157
|
+
try {
|
|
158
|
+
const [executable, ...args] = parseProcessCommand(hook.comando);
|
|
159
|
+
const policy = createProcessPolicy({
|
|
160
|
+
allowedExecutables: deps.allowedExecutables ?? ['node', 'node.exe', 'echo', 'echo.exe'],
|
|
161
|
+
capabilities: { hook: cartella },
|
|
162
|
+
envAllowlist: ['PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR', 'COMSPEC', 'TALOS_HOOK_EVENT'],
|
|
163
|
+
spawnFn: deps.spawnFn,
|
|
164
|
+
});
|
|
165
|
+
const result = await policy.runApprovedProcess({
|
|
166
|
+
executable,
|
|
167
|
+
args,
|
|
168
|
+
cwd: cartella,
|
|
169
|
+
capability: 'hook',
|
|
170
|
+
timeoutMs: 10_000,
|
|
171
|
+
envKeys: ['PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR', 'COMSPEC', 'TALOS_HOOK_EVENT'],
|
|
172
|
+
env: { TALOS_HOOK_EVENT: JSON.stringify(evento) },
|
|
173
|
+
captureLimitBytes: 64 * 1024,
|
|
174
|
+
});
|
|
175
|
+
const testoFuori = result.stdout.trim();
|
|
176
|
+
try {
|
|
177
|
+
const dati = JSON.parse(testoFuori);
|
|
178
|
+
if (typeof dati?.consentito === 'boolean') {
|
|
179
|
+
return { consentito: dati.consentito, motivo: typeof dati.motivo === 'string' ? dati.motivo : undefined };
|
|
180
|
+
}
|
|
181
|
+
} catch { /* non è JSON: si ricade sul codice di uscita */ }
|
|
182
|
+
return { consentito: result.code === 0, motivo: result.code === 0 ? undefined : (result.stderr.trim() || testoFuori || `hook exited ${result.code}`) };
|
|
183
|
+
} catch (error) {
|
|
184
|
+
return { consentito: false, motivo: String(error?.message || error) };
|
|
185
|
+
}
|
|
186
|
+
}
|