talos-code 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/LICENSE +661 -0
- package/README.md +116 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +100 -0
- package/dist/automations/history-store.js +34 -0
- package/dist/automations/policy-store.js +43 -0
- package/dist/automations/runner.js +107 -0
- package/dist/automations/schedule.js +85 -0
- package/dist/automations/windows-task.js +53 -0
- package/dist/commands/advanced-cli.js +233 -0
- package/dist/commands/checkpoint-cli.js +45 -0
- package/dist/commands/config-cli.js +107 -0
- package/dist/commands/context.js +17 -0
- package/dist/commands/extensions-cli.js +207 -0
- package/dist/commands/project-cli.js +74 -0
- package/dist/commands/project-commands.js +143 -0
- package/dist/commands/provider-cli.js +244 -0
- package/dist/commands/services-cli.js +248 -0
- package/dist/commands/session-cli.js +171 -0
- package/dist/commands/system-cli.js +405 -0
- package/dist/config/commands.js +60 -0
- package/dist/config/load.js +382 -0
- package/dist/config/migrations.js +48 -0
- package/dist/config/types.js +11 -0
- package/dist/diagnostics/development-log.js +154 -0
- package/dist/diagnostics/doctor.js +104 -0
- package/dist/diagnostics/redact.js +80 -0
- package/dist/diagnostics/zip.js +52 -0
- package/dist/errors.js +156 -0
- package/dist/events/bridge.js +130 -0
- package/dist/extensions/installer.js +178 -0
- package/dist/extensions/package-schema.js +24 -0
- package/dist/headless/result.js +84 -0
- package/dist/headless/run.js +230 -0
- package/dist/i18n/en/approval.js +46 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +92 -0
- package/dist/i18n/en/errors.js +267 -0
- package/dist/i18n/en/firstrun.js +101 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +774 -0
- package/dist/i18n/en/tools.js +127 -0
- package/dist/i18n/en.js +14 -0
- package/dist/i18n/error-view.js +307 -0
- package/dist/i18n/index.js +19 -0
- package/dist/i18n/kernel-map.js +139 -0
- package/dist/io.js +45 -0
- package/dist/main.js +331 -0
- package/dist/paths.js +41 -0
- package/dist/protocol/v2/codec.js +9 -0
- package/dist/protocol/v2/events.js +555 -0
- package/dist/protocol/v2/index.js +4 -0
- package/dist/protocol/v2/replay.js +28 -0
- package/dist/protocol/v2/types.js +1 -0
- package/dist/provider/control-plane.js +135 -0
- package/dist/provider/environment-keys.js +275 -0
- package/dist/provider/health.js +110 -0
- package/dist/provider/missing-key.js +48 -0
- package/dist/provider/model-catalog.js +168 -0
- package/dist/provider/openrouter-login.js +172 -0
- package/dist/provider/provider-text.js +13 -0
- package/dist/provider/store.js +95 -0
- package/dist/provider/system-keyring.js +214 -0
- package/dist/runtime/active-run.js +63 -0
- package/dist/runtime/agent-tree.js +74 -0
- package/dist/runtime/attachments.js +84 -0
- package/dist/runtime/brokered-executor.js +395 -0
- package/dist/runtime/context-archive.js +69 -0
- package/dist/runtime/context-status.js +76 -0
- package/dist/runtime/create-runtime.js +111 -0
- package/dist/runtime/model-profile.js +361 -0
- package/dist/runtime/output-store.js +137 -0
- package/dist/runtime/provider-attempts.js +185 -0
- package/dist/runtime/replay-buffer.js +153 -0
- package/dist/runtime/repo.js +95 -0
- package/dist/runtime/session-facade.js +135 -0
- package/dist/runtime/session-summary.js +125 -0
- package/dist/runtime/supervisor.js +265 -0
- package/dist/runtime/talos-composition.js +1011 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/usage-snapshot.js +82 -0
- package/dist/security/auto-classifier.js +38 -0
- package/dist/security/credential-free-environment.js +54 -0
- package/dist/security/evaluate.js +243 -0
- package/dist/security/execution-backends.js +236 -0
- package/dist/security/execution-broker.js +102 -0
- package/dist/security/forge-scan.js +74 -0
- package/dist/security/from-approval.js +39 -0
- package/dist/security/mxc-execution-backend.js +281 -0
- package/dist/security/permission-engine.js +138 -0
- package/dist/security/permission-explanation.js +54 -0
- package/dist/security/persist.js +179 -0
- package/dist/security/plugin-guard.js +230 -0
- package/dist/security/process-tree-evidence-store.js +74 -0
- package/dist/security/process-tree-probe.js +554 -0
- package/dist/security/project-resource-inventory.js +186 -0
- package/dist/security/project-trust-gate.js +38 -0
- package/dist/security/project-trust.js +367 -0
- package/dist/security/rule-parser.js +201 -0
- package/dist/security/shell-segmentation.js +68 -0
- package/dist/security/trust-authority.js +324 -0
- package/dist/security/types.js +1 -0
- package/dist/security/workspace-identity.js +102 -0
- package/dist/services/automation-facade.js +59 -0
- package/dist/services/forge-facade.js +77 -0
- package/dist/services/hook-facade.js +240 -0
- package/dist/services/index.js +16 -0
- package/dist/services/library-facade.js +174 -0
- package/dist/services/mcp-facade.js +348 -0
- package/dist/services/memory-facade.js +126 -0
- package/dist/services/notes-facade.js +157 -0
- package/dist/services/plugin-facade.js +308 -0
- package/dist/services/research-facade.js +110 -0
- package/dist/services/task-board-facade.js +236 -0
- package/dist/services/workflow-catalog.js +246 -0
- package/dist/sessions/export-format.js +98 -0
- package/dist/sessions/metadata-store.js +160 -0
- package/dist/sessions/share.js +122 -0
- package/dist/sessions/transfer.js +16 -0
- package/dist/subcommands.js +62 -0
- package/dist/tui/agent-roster.js +36 -0
- package/dist/tui/agent-view.js +51 -0
- package/dist/tui/app.js +4098 -0
- package/dist/tui/approval.js +36 -0
- package/dist/tui/boot/boot-sequence.js +76 -0
- package/dist/tui/boot/cinematic.js +243 -0
- package/dist/tui/boot/desktop-logo.js +82 -0
- package/dist/tui/boot.js +3 -0
- package/dist/tui/busy-input.js +58 -0
- package/dist/tui/catalog-service.js +560 -0
- package/dist/tui/components/assistant-stream.js +1 -0
- package/dist/tui/components/command-menu.js +68 -0
- package/dist/tui/components/composer.js +219 -0
- package/dist/tui/components/diff.js +35 -0
- package/dist/tui/components/footer.js +48 -0
- package/dist/tui/components/header.js +6 -0
- package/dist/tui/components/markdown.js +305 -0
- package/dist/tui/components/status-indicator.js +38 -0
- package/dist/tui/components/terminal-shell.js +214 -0
- package/dist/tui/components/thinking-row.js +19 -0
- package/dist/tui/components/tool-row.js +97 -0
- package/dist/tui/components/transcript-virtualizer.js +165 -0
- package/dist/tui/components/transcript.js +43 -0
- package/dist/tui/descendant-approvals.js +104 -0
- package/dist/tui/diff-model.js +77 -0
- package/dist/tui/editor-history.js +21 -0
- package/dist/tui/editor.js +210 -0
- package/dist/tui/event-adapter.js +436 -0
- package/dist/tui/exit-output.js +58 -0
- package/dist/tui/external-editor.js +53 -0
- package/dist/tui/file-completion.js +89 -0
- package/dist/tui/focus-manager.js +5 -0
- package/dist/tui/highlight.js +30 -0
- package/dist/tui/input-router.js +18 -0
- package/dist/tui/interrupt.js +99 -0
- package/dist/tui/keybindings.js +194 -0
- package/dist/tui/keymap-resolver.js +91 -0
- package/dist/tui/launch-state.js +148 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +56 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +43 -0
- package/dist/tui/overlays/approval-dialog.js +642 -0
- package/dist/tui/overlays/automation-center.js +51 -0
- package/dist/tui/overlays/checkpoint-picker.js +47 -0
- package/dist/tui/overlays/context-inspector.js +36 -0
- package/dist/tui/overlays/effort-line.js +110 -0
- package/dist/tui/overlays/forge-center.js +46 -0
- package/dist/tui/overlays/help-dialog.js +16 -0
- package/dist/tui/overlays/history-picker.js +37 -0
- package/dist/tui/overlays/hook-center.js +70 -0
- package/dist/tui/overlays/library-center.js +55 -0
- package/dist/tui/overlays/mcp-center.js +60 -0
- package/dist/tui/overlays/memory-center.js +63 -0
- package/dist/tui/overlays/model-picker.js +77 -0
- package/dist/tui/overlays/notes-center.js +53 -0
- package/dist/tui/overlays/overlay-host.js +8 -0
- package/dist/tui/overlays/plugin-center.js +76 -0
- package/dist/tui/overlays/provider-picker.js +171 -0
- package/dist/tui/overlays/queue-editor.js +32 -0
- package/dist/tui/overlays/research-center.js +59 -0
- package/dist/tui/overlays/scroll-window.js +234 -0
- package/dist/tui/overlays/session-picker.js +115 -0
- package/dist/tui/overlays/tasks-center.js +83 -0
- package/dist/tui/overlays/theme-picker.js +21 -0
- package/dist/tui/overlays/transcript-search.js +58 -0
- package/dist/tui/overlays/trust-center.js +29 -0
- package/dist/tui/overlays/workflow-center.js +46 -0
- package/dist/tui/project-file-index.js +214 -0
- package/dist/tui/project-references.js +85 -0
- package/dist/tui/project-trust-prompt.js +289 -0
- package/dist/tui/prompt-history-store.js +116 -0
- package/dist/tui/queue-store.js +110 -0
- package/dist/tui/regions/budget.js +59 -0
- package/dist/tui/regions/views.js +96 -0
- package/dist/tui/render-coordinator.js +148 -0
- package/dist/tui/render-scheduler.js +4 -0
- package/dist/tui/run.js +69 -0
- package/dist/tui/selection-list.js +29 -0
- package/dist/tui/session-controller.js +791 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/setup-wizard.js +46 -0
- package/dist/tui/shell-input.js +35 -0
- package/dist/tui/shell-layout.js +48 -0
- package/dist/tui/shell-model.js +98 -0
- package/dist/tui/slash-commands.js +82 -0
- package/dist/tui/state.js +151 -0
- package/dist/tui/status-bar.js +125 -0
- package/dist/tui/status-view.js +40 -0
- package/dist/tui/terminal-capabilities.js +9 -0
- package/dist/tui/terminal-session.js +11 -0
- package/dist/tui/text-width.js +66 -0
- package/dist/tui/theme-catalog.js +27 -0
- package/dist/tui/theme-store.js +23 -0
- package/dist/tui/theme.js +23 -0
- package/dist/tui/tool-display.js +135 -0
- package/dist/tui/tool-facts.js +1 -0
- package/dist/tui/tools/bash-renderer.js +32 -0
- package/dist/tui/tools/change.js +61 -0
- package/dist/tui/tools/edit-renderer.js +17 -0
- package/dist/tui/tools/generic-renderer.js +13 -0
- package/dist/tui/tools/list-renderer.js +14 -0
- package/dist/tui/tools/read-renderer.js +13 -0
- package/dist/tui/tools/registry.js +20 -0
- package/dist/tui/tools/row-format.js +187 -0
- package/dist/tui/tools/search-renderer.js +38 -0
- package/dist/tui/tools/shared.js +97 -0
- package/dist/tui/tools/write-renderer.js +15 -0
- package/dist/tui/transcript-model.js +441 -0
- package/dist/tui/ui-preferences.js +79 -0
- package/dist/tui/usage-view.js +77 -0
- package/dist/tui/vim-mode.js +99 -0
- package/dist/update/check.js +15 -0
- package/dist/update/npm.js +51 -0
- package/dist/update/run.js +129 -0
- package/dist/version.js +2 -0
- package/dist/workspace/checkpoint-store.js +210 -0
- package/dist/workspace/checkpoint.js +413 -0
- package/dist/workspace/restore.js +232 -0
- package/package.json +64 -5
- package/vendor/context-engine/package.json +11 -0
- package/vendor/context-engine/src/compaction-planner.mjs +57 -0
- package/vendor/context-engine/src/contracts.mjs +48 -0
- package/vendor/context-engine/src/engine.mjs +445 -0
- package/vendor/context-engine/src/node/context-export.mjs +164 -0
- package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
- package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
- package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
- package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
- package/vendor/context-engine/src/profiles.mjs +10 -0
- package/vendor/context-engine/src/retrieval.mjs +104 -0
- package/vendor/context-engine/src/summary.mjs +97 -0
- package/vendor/context-engine/src/usage.mjs +34 -0
- package/vendor/harness-ui/package.json +38 -0
- package/vendor/harness-ui/src/acp-agent.mjs +350 -0
- package/vendor/harness-ui/src/agent-service.mjs +2188 -0
- package/vendor/harness-ui/src/agui-events.mjs +452 -0
- package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
- package/vendor/harness-ui/src/artifact-store.mjs +39 -0
- package/vendor/harness-ui/src/assistenza.mjs +123 -0
- package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
- package/vendor/harness-ui/src/automation-store.mjs +145 -0
- package/vendor/harness-ui/src/browser-annota.mjs +639 -0
- package/vendor/harness-ui/src/browser-frame.mjs +211 -0
- package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
- package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
- package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
- package/vendor/harness-ui/src/browser-stream.mjs +445 -0
- package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
- package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
- package/vendor/harness-ui/src/config.mjs +697 -0
- package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
- package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
- package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
- package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
- package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
- package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
- package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
- package/vendor/harness-ui/src/context-runtime.mjs +118 -0
- package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
- package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
- package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
- package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
- package/vendor/harness-ui/src/custom-task.mjs +171 -0
- package/vendor/harness-ui/src/doctor.mjs +142 -0
- package/vendor/harness-ui/src/document-filename.mjs +97 -0
- package/vendor/harness-ui/src/document-generator.mjs +493 -0
- package/vendor/harness-ui/src/document-report.mjs +331 -0
- package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
- package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
- package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
- package/vendor/harness-ui/src/forge-contract.mjs +221 -0
- package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
- package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
- package/vendor/harness-ui/src/generation-idle.mjs +332 -0
- package/vendor/harness-ui/src/gguf-header.mjs +207 -0
- package/vendor/harness-ui/src/git-service.mjs +626 -0
- package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
- package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
- package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
- package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
- package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
- package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
- package/vendor/harness-ui/src/hook-registry.mjs +186 -0
- package/vendor/harness-ui/src/http-app.mjs +6259 -0
- package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
- package/vendor/harness-ui/src/id-archivio.mjs +27 -0
- package/vendor/harness-ui/src/image-generator.mjs +143 -0
- package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
- package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
- package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
- package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
- package/vendor/harness-ui/src/library-store.mjs +652 -0
- package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
- package/vendor/harness-ui/src/local-model-store.mjs +339 -0
- package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
- package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
- package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
- package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
- package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
- package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
- package/vendor/harness-ui/src/mcp-client.mjs +98 -0
- package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
- package/vendor/harness-ui/src/mcp-session.mjs +177 -0
- package/vendor/harness-ui/src/memory-store.mjs +227 -0
- package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
- package/vendor/harness-ui/src/model-catalog.mjs +129 -0
- package/vendor/harness-ui/src/model-destination.mjs +189 -0
- package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
- package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
- package/vendor/harness-ui/src/notes-store.mjs +250 -0
- package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
- package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
- package/vendor/harness-ui/src/path-policy.mjs +442 -0
- package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
- package/vendor/harness-ui/src/plugin-session.mjs +180 -0
- package/vendor/harness-ui/src/process-policy.mjs +345 -0
- package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
- package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
- package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
- package/vendor/harness-ui/src/provider-probe.mjs +582 -0
- package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
- package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
- package/vendor/harness-ui/src/public-problem.mjs +109 -0
- package/vendor/harness-ui/src/research/card.mjs +235 -0
- package/vendor/harness-ui/src/research/citations.mjs +142 -0
- package/vendor/harness-ui/src/research/collector.mjs +275 -0
- package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
- package/vendor/harness-ui/src/research/dossier.mjs +114 -0
- package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
- package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
- package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
- package/vendor/harness-ui/src/research/independence.mjs +159 -0
- package/vendor/harness-ui/src/research/ledger.mjs +166 -0
- package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
- package/vendor/harness-ui/src/research/narration.mjs +181 -0
- package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
- package/vendor/harness-ui/src/research/opposing.mjs +305 -0
- package/vendor/harness-ui/src/research/outline.mjs +111 -0
- package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
- package/vendor/harness-ui/src/research/pdf.mjs +291 -0
- package/vendor/harness-ui/src/research/plan.mjs +301 -0
- package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
- package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
- package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
- package/vendor/harness-ui/src/research/recheck.mjs +194 -0
- package/vendor/harness-ui/src/research/report.mjs +203 -0
- package/vendor/harness-ui/src/research/run.mjs +527 -0
- package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
- package/vendor/harness-ui/src/research/verification.mjs +572 -0
- package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
- package/vendor/harness-ui/src/research-store.mjs +1133 -0
- package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
- package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
- package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
- package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
- package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
- package/vendor/harness-ui/src/search-source-store.mjs +172 -0
- package/vendor/harness-ui/src/session-registry.mjs +6095 -0
- package/vendor/harness-ui/src/session-store.mjs +220 -0
- package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
- package/vendor/harness-ui/src/setup-stato.mjs +31 -0
- package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
- package/vendor/harness-ui/src/skill-registry.mjs +120 -0
- package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
- package/vendor/harness-ui/src/static-files.mjs +96 -0
- package/vendor/harness-ui/src/stream-partition.mjs +123 -0
- package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
- package/vendor/harness-ui/src/task-catalog.mjs +65 -0
- package/vendor/harness-ui/src/tasks-store.mjs +220 -0
- package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
- package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
- package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
- package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
- package/vendor/harness-ui/src/usage-cache.mjs +315 -0
- package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
- package/vendor/harness-ui/src/workspace-context.mjs +124 -0
- package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
- package/vendor/harness-ui/src/workspace-files.mjs +589 -0
- package/vendor/harness-ui/src/workspace-info.mjs +189 -0
- package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
- package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
- package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
- package/vendor/manifest.json +170 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstat, open, readFile, readdir, realpath } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export const PLUGIN_GUARD_MAX_FILES = 10_000;
|
|
5
|
+
export const PLUGIN_GUARD_MAX_SOURCE_FILE_BYTES = 1024 * 1024;
|
|
6
|
+
export const PLUGIN_GUARD_MAX_SOURCE_BYTES = 16 * 1024 * 1024;
|
|
7
|
+
const SOURCE_FILE = /\.(?:[cm]?[jt]sx?|mjs|cjs|mts|cts|ps1|sh|bash|zsh)$/iu;
|
|
8
|
+
function inside(root, candidate) { const relative = path.relative(root, candidate); return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative)); }
|
|
9
|
+
function verdict(findings) { return findings.some(finding => finding.severity === 'dangerous') ? 'dangerous' : findings.length ? 'caution' : 'safe'; }
|
|
10
|
+
function report(findings, capabilities) { return { verdict: verdict(findings), findings, capabilities: [...capabilities].sort() }; }
|
|
11
|
+
function finding(severity, code, origin, message) { return { severity, code, origin, message }; }
|
|
12
|
+
function safePluginId(id) { return /^[A-Za-z0-9][A-Za-z0-9._-]{0,120}$/u.test(id) && !id.includes('..'); }
|
|
13
|
+
function boundedLimit(value, ceiling) { if (value === undefined)
|
|
14
|
+
return ceiling; if (!Number.isFinite(value))
|
|
15
|
+
return ceiling; return Math.max(1, Math.min(Math.floor(value), ceiling)); }
|
|
16
|
+
function packageRelative(pluginId, relative) { return path.posix.join('.harness-ui-plugins', pluginId, relative); }
|
|
17
|
+
function pluginResourceFingerprint(relativePath, content) { return createHash('sha256').update('talos-executable-resource-v1').update('\0').update('plugin').update('\0').update(relativePath).update('\0').update(relativePath).update('\0').update(content).digest('hex'); }
|
|
18
|
+
function reviewedBytesMatch(review, relativePath, content, origin, findings) {
|
|
19
|
+
if (!review)
|
|
20
|
+
return true;
|
|
21
|
+
const expected = review.resources.find(row => row.relativePath === relativePath);
|
|
22
|
+
if (expected && expected.fingerprint === pluginResourceFingerprint(relativePath, content))
|
|
23
|
+
return true;
|
|
24
|
+
findings.push(finding('dangerous', 'PACKAGE_REVIEW_CHANGED', origin, 'package bytes differ from the reviewed trust snapshot; remediation: review the current package again before trusting it'));
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
export function pluginReviewFromSnapshot(snapshot, pluginId) {
|
|
28
|
+
const prefix = `.harness-ui-plugins/${pluginId}/`;
|
|
29
|
+
return { workspaceIdentityHash: snapshot.workspace.identityHash, resources: snapshot.resources.flatMap(row => row.kind === 'plugin' && row.relativePath.startsWith(prefix) && row.current ? [{ relativePath: row.current.relativePath, fingerprint: row.current.fingerprint }] : []) };
|
|
30
|
+
}
|
|
31
|
+
async function readSourceBounded(sourcePath, maxBytes) {
|
|
32
|
+
const handle = await open(sourcePath, 'r');
|
|
33
|
+
try {
|
|
34
|
+
const buffer = Buffer.allocUnsafe(maxBytes + 1);
|
|
35
|
+
let bytes = 0;
|
|
36
|
+
while (bytes < buffer.length) {
|
|
37
|
+
const { bytesRead } = await handle.read(buffer, bytes, buffer.length - bytes, bytes);
|
|
38
|
+
if (bytesRead === 0)
|
|
39
|
+
break;
|
|
40
|
+
bytes += bytesRead;
|
|
41
|
+
}
|
|
42
|
+
const content = buffer.subarray(0, bytes);
|
|
43
|
+
return { bytes, overflow: bytes > maxBytes, content, text: bytes > maxBytes ? '' : content.toString('utf8') };
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
await handle.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function scanDangerousText(text, origin, findings, capabilities) {
|
|
50
|
+
const network = /\b(?:curl|wget|Invoke-WebRequest|iwr|nc|ncat)\b|\bfetch\s*\(|\bWebSocket\s*\(|\baxios(?:\.[A-Za-z]+)?\s*\(|\bhttps?\s*\.\s*(?:request|get)\s*\(/iu.test(text);
|
|
51
|
+
const credentials = /\b(?:process|Bun)\.env(?:\[['"][^'"]*(?:KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION)[^'"]*['"]\]|\.[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION))|\$env:[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION)|\$[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION)/iu.test(text);
|
|
52
|
+
if (network)
|
|
53
|
+
capabilities.add('network');
|
|
54
|
+
if (credentials)
|
|
55
|
+
capabilities.add('credentials');
|
|
56
|
+
if (/\b(?:curl|wget)\b[^|\r\n]*\|\s*(?:sh|bash|zsh|pwsh|powershell)\b|\b(?:iwr|Invoke-WebRequest)\b[^|\r\n]*\|\s*(?:iex|Invoke-Expression)\b/iu.test(text))
|
|
57
|
+
findings.push(finding('dangerous', 'REMOTE_PIPE_EXECUTION', origin, 'downloads and executes remote content in one step'));
|
|
58
|
+
if (credentials && network)
|
|
59
|
+
findings.push(finding('dangerous', 'CREDENTIAL_EXFILTRATION', origin, 'combines credential access with an outbound network primitive'));
|
|
60
|
+
if (/\bnc\s+-[A-Za-z]*e\b|\/dev\/tcp\/|\b(?:Invoke-Expression|iex)\b[^\r\n]*(?:FromBase64String|-enc(?:odedcommand)?\b)/iu.test(text))
|
|
61
|
+
findings.push(finding('dangerous', 'DYNAMIC_REMOTE_SHELL', origin, 'contains a reverse-shell or encoded dynamic execution pattern'));
|
|
62
|
+
}
|
|
63
|
+
export function scanPluginManifest(plugin) {
|
|
64
|
+
const findings = [];
|
|
65
|
+
const capabilities = new Set();
|
|
66
|
+
for (const [kind, rows] of [['tool', plugin.tools ?? []], ['hook', plugin.hooks ?? []]]) {
|
|
67
|
+
for (const [index, row] of rows.entries()) {
|
|
68
|
+
const command = String(row.comando ?? '');
|
|
69
|
+
const origin = `${kind}:${String(row.nome ?? row.id ?? index)}`;
|
|
70
|
+
capabilities.add('process');
|
|
71
|
+
findings.push(finding('caution', 'LOCAL_COMMAND_EXECUTION', origin, 'executes a local command'));
|
|
72
|
+
if (/\b(?:curl|wget|Invoke-WebRequest|iwr|fetch)\b/iu.test(command)) {
|
|
73
|
+
capabilities.add('network');
|
|
74
|
+
findings.push(finding('caution', 'NETWORK_ACCESS', origin, 'command can access the network'));
|
|
75
|
+
}
|
|
76
|
+
if (/\b(?:API_KEY|SECRET|TOKEN|PASSWORD|AUTHORIZATION)\b|\$(?:env:)?(?:[A-Z_]*(?:KEY|TOKEN|SECRET|PASSWORD))/iu.test(command))
|
|
77
|
+
capabilities.add('credentials');
|
|
78
|
+
if (/\b(?:rm\s+-[A-Za-z]*r[A-Za-z]*f|del\s+\/[sq]|Remove-Item\b[^\r\n]*-Recurse|format\s+[A-Za-z]:)/iu.test(command))
|
|
79
|
+
capabilities.add('filesystem-write');
|
|
80
|
+
if (/\b(?:curl|wget)\b[^|\r\n]*\|\s*(?:sh|bash|zsh|pwsh|powershell)\b|\b(?:iwr|Invoke-WebRequest)\b[^|\r\n]*\|\s*(?:iex|Invoke-Expression)\b/iu.test(command))
|
|
81
|
+
findings.push(finding('dangerous', 'REMOTE_PIPE_EXECUTION', origin, 'downloads and executes remote content in one command'));
|
|
82
|
+
if (/(?:API_KEY|SECRET|TOKEN|PASSWORD|AUTHORIZATION)/iu.test(command) && /\b(?:curl|wget|nc|ncat|Invoke-WebRequest|iwr)\b/iu.test(command))
|
|
83
|
+
findings.push(finding('dangerous', 'CREDENTIAL_EXFILTRATION', origin, 'combines credential access with a network command'));
|
|
84
|
+
if (/\bnc\s+-[A-Za-z]*e\b|\/dev\/tcp\/|\b(?:Invoke-Expression|iex)\b[^\r\n]*(?:FromBase64String|-enc(?:odedcommand)?\b)/iu.test(command))
|
|
85
|
+
findings.push(finding('dangerous', 'DYNAMIC_REMOTE_SHELL', origin, 'contains a reverse-shell or encoded dynamic execution pattern'));
|
|
86
|
+
if (/\brm\s+-[A-Za-z]*r[A-Za-z]*f\s+(?:\/|[A-Za-z]:\\)(?:\s|$)|\bdel\s+\/[sq]\s+[A-Za-z]:\\/iu.test(command))
|
|
87
|
+
findings.push(finding('dangerous', 'FILESYSTEM_ROOT_DELETION', origin, 'recursively deletes a filesystem root'));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return report(findings, capabilities);
|
|
91
|
+
}
|
|
92
|
+
export async function scanPluginPackage({ projectRoot, plugin, limits, expectedReview }) {
|
|
93
|
+
const base = path.join(projectRoot, '.harness-ui-plugins');
|
|
94
|
+
const findings = [];
|
|
95
|
+
const capabilities = new Set();
|
|
96
|
+
const maxFiles = boundedLimit(limits?.maxFiles, PLUGIN_GUARD_MAX_FILES);
|
|
97
|
+
const maxSourceFileBytes = boundedLimit(limits?.maxSourceFileBytes, PLUGIN_GUARD_MAX_SOURCE_FILE_BYTES);
|
|
98
|
+
const maxSourceBytes = boundedLimit(limits?.maxSourceBytes, PLUGIN_GUARD_MAX_SOURCE_BYTES);
|
|
99
|
+
if (!safePluginId(plugin.id)) {
|
|
100
|
+
findings.push(finding('dangerous', 'PLUGIN_ID_INVALID', 'package', 'plugin id is not a single safe path component'));
|
|
101
|
+
return report(findings, capabilities);
|
|
102
|
+
}
|
|
103
|
+
const pluginPath = path.join(base, plugin.id);
|
|
104
|
+
try {
|
|
105
|
+
const [canonicalBase, canonicalRoot] = await Promise.all([realpath(base), realpath(pluginPath)]);
|
|
106
|
+
if (!inside(canonicalBase, canonicalRoot)) {
|
|
107
|
+
findings.push(finding('dangerous', 'PLUGIN_ROOT_ESCAPE', 'package', 'plugin root resolves outside the plugin directory'));
|
|
108
|
+
return report(findings, capabilities);
|
|
109
|
+
}
|
|
110
|
+
const manifestBytes = await readFile(path.join(canonicalRoot, 'plugin.json'));
|
|
111
|
+
reviewedBytesMatch(expectedReview, packageRelative(plugin.id, 'plugin.json'), manifestBytes, 'plugin.json', findings);
|
|
112
|
+
const diskManifest = JSON.parse(manifestBytes.toString('utf8'));
|
|
113
|
+
const initial = scanPluginManifest({ id: plugin.id, tools: diskManifest.tools ?? [], hooks: diskManifest.hooks ?? [] });
|
|
114
|
+
findings.push(...initial.findings);
|
|
115
|
+
for (const capability of initial.capabilities)
|
|
116
|
+
capabilities.add(capability);
|
|
117
|
+
const sources = [];
|
|
118
|
+
let regularFiles = 0;
|
|
119
|
+
let fileLimitHit = false;
|
|
120
|
+
async function walk(directory) {
|
|
121
|
+
if (fileLimitHit)
|
|
122
|
+
return;
|
|
123
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
124
|
+
if (fileLimitHit)
|
|
125
|
+
return;
|
|
126
|
+
const entryPath = path.join(directory, entry.name);
|
|
127
|
+
const relative = path.relative(canonicalRoot, entryPath).split(path.sep).join('/');
|
|
128
|
+
const metadata = await lstat(entryPath);
|
|
129
|
+
if (metadata.isSymbolicLink()) {
|
|
130
|
+
const target = await realpath(entryPath);
|
|
131
|
+
if (!inside(canonicalRoot, target))
|
|
132
|
+
findings.push(finding('dangerous', 'SYMLINK_ESCAPE', relative, 'symbolic link resolves outside the plugin root'));
|
|
133
|
+
else
|
|
134
|
+
findings.push(finding('caution', 'SYMLINK_PRESENT', relative, 'package contains a symbolic link'));
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (metadata.isDirectory()) {
|
|
138
|
+
await walk(entryPath);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!metadata.isFile())
|
|
142
|
+
continue;
|
|
143
|
+
regularFiles += 1;
|
|
144
|
+
if (regularFiles > maxFiles) {
|
|
145
|
+
findings.push(finding('dangerous', 'PACKAGE_FILE_LIMIT', 'package', `package exceeds the Plugin Guard file limit (${maxFiles}); remediation: remove generated/vendor files or split the plugin before trust review`));
|
|
146
|
+
fileLimitHit = true;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (/\.(?:exe|dll|node|com|msi)$/iu.test(entry.name)) {
|
|
150
|
+
capabilities.add('native-code');
|
|
151
|
+
findings.push(finding('caution', 'NATIVE_BINARY', relative, 'package contains native executable code'));
|
|
152
|
+
}
|
|
153
|
+
if (SOURCE_FILE.test(entry.name))
|
|
154
|
+
sources.push({ path: entryPath, relative, size: metadata.size });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
await walk(canonicalRoot);
|
|
158
|
+
if (fileLimitHit)
|
|
159
|
+
return report(findings, capabilities);
|
|
160
|
+
if (expectedReview) {
|
|
161
|
+
const prefix = `.harness-ui-plugins/${plugin.id}/`;
|
|
162
|
+
const seen = new Set(sources.map(source => source.relative));
|
|
163
|
+
for (const row of expectedReview.resources) {
|
|
164
|
+
if (!row.relativePath.startsWith(prefix))
|
|
165
|
+
continue;
|
|
166
|
+
const relative = row.relativePath.slice(prefix.length);
|
|
167
|
+
if (SOURCE_FILE.test(relative) && !seen.has(relative))
|
|
168
|
+
findings.push(finding('dangerous', 'PACKAGE_REVIEW_CHANGED', relative, 'a reviewed source file was missing during inspection; remediation: review the current package again before trusting it'));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
let metadataSourceBytes = 0;
|
|
172
|
+
let sourceLimitHit = false;
|
|
173
|
+
for (const source of sources) {
|
|
174
|
+
if (source.size > maxSourceFileBytes) {
|
|
175
|
+
findings.push(finding('dangerous', 'SOURCE_FILE_BYTE_LIMIT', source.relative, `source file exceeds the Plugin Guard per-file byte limit (${maxSourceFileBytes}); remediation: split or reduce this source file before trust review`));
|
|
176
|
+
sourceLimitHit = true;
|
|
177
|
+
}
|
|
178
|
+
metadataSourceBytes += source.size;
|
|
179
|
+
}
|
|
180
|
+
if (metadataSourceBytes > maxSourceBytes) {
|
|
181
|
+
findings.push(finding('dangerous', 'PACKAGE_SOURCE_BYTE_LIMIT', 'package', `scannable source exceeds the Plugin Guard total-byte limit (${maxSourceBytes}); remediation: remove generated/vendor source or split the plugin before trust review`));
|
|
182
|
+
sourceLimitHit = true;
|
|
183
|
+
}
|
|
184
|
+
if (sourceLimitHit)
|
|
185
|
+
return report(findings, capabilities);
|
|
186
|
+
let remainingSourceBytes = maxSourceBytes;
|
|
187
|
+
for (const source of sources) {
|
|
188
|
+
if (remainingSourceBytes <= 0) {
|
|
189
|
+
findings.push(finding('dangerous', 'PACKAGE_SOURCE_BYTE_LIMIT', 'package', `scannable source exceeds the Plugin Guard total-byte limit (${maxSourceBytes}); remediation: stop concurrent generation, remove generated/vendor source or split the plugin before trust review`));
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
const readLimit = Math.min(maxSourceFileBytes, remainingSourceBytes);
|
|
193
|
+
const bounded = await readSourceBounded(source.path, readLimit);
|
|
194
|
+
if (bounded.overflow) {
|
|
195
|
+
if (readLimit === maxSourceFileBytes)
|
|
196
|
+
findings.push(finding('dangerous', 'SOURCE_FILE_BYTE_LIMIT', source.relative, `source file exceeded the Plugin Guard per-file byte limit while being read (${maxSourceFileBytes}); remediation: stop concurrent generation or split/reduce this source file before trust review`));
|
|
197
|
+
if (readLimit === remainingSourceBytes)
|
|
198
|
+
findings.push(finding('dangerous', 'PACKAGE_SOURCE_BYTE_LIMIT', 'package', `scannable source exceeded the Plugin Guard total-byte limit while being read (${maxSourceBytes}); remediation: stop concurrent generation, remove generated/vendor source or split the plugin before trust review`));
|
|
199
|
+
if (readLimit === remainingSourceBytes)
|
|
200
|
+
break;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
remainingSourceBytes -= bounded.bytes;
|
|
204
|
+
if (!reviewedBytesMatch(expectedReview, packageRelative(plugin.id, source.relative), bounded.content, source.relative, findings))
|
|
205
|
+
continue;
|
|
206
|
+
scanDangerousText(bounded.text, source.relative, findings, capabilities);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
findings.push(finding('dangerous', 'PACKAGE_SCAN_FAILED', 'package', error instanceof Error ? error.message : 'package scan failed'));
|
|
211
|
+
}
|
|
212
|
+
return report(findings, capabilities);
|
|
213
|
+
}
|
|
214
|
+
export function createGuardedPluginTrustVerifier({ projectRoot, inspectReview, verifyTrusted }) {
|
|
215
|
+
return async (input) => {
|
|
216
|
+
if (!safePluginId(input.pluginId))
|
|
217
|
+
return false;
|
|
218
|
+
try {
|
|
219
|
+
const snapshot = inspectReview ? await inspectReview() : undefined;
|
|
220
|
+
const expectedReview = snapshot ? pluginReviewFromSnapshot(snapshot, input.pluginId) : undefined;
|
|
221
|
+
const guard = await scanPluginPackage({ projectRoot, plugin: { id: input.pluginId }, ...(expectedReview ? { expectedReview } : {}) });
|
|
222
|
+
if (guard.verdict === 'dangerous')
|
|
223
|
+
return false;
|
|
224
|
+
return await verifyTrusted(input);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
const RECORD_SCHEMA = 'talos.cli.process-tree-evidence-store.v1';
|
|
6
|
+
const RECORD_FILE = 'process-tree-evidence.json';
|
|
7
|
+
const POSITIVE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
8
|
+
const NEGATIVE_TTL_MS = 15 * 60 * 1000;
|
|
9
|
+
/**
|
|
10
|
+
* A process-tree measurement is only meaningful on the host that produced it. Any change in
|
|
11
|
+
* platform, kernel release, architecture, or machine name invalidates the cached evidence rather
|
|
12
|
+
* than carrying a stale "verified" across a host that is no longer the same machine.
|
|
13
|
+
*/
|
|
14
|
+
export function hostFingerprint(input = {}) {
|
|
15
|
+
const platform = input.platform ?? os.platform();
|
|
16
|
+
const release = input.release ?? os.release();
|
|
17
|
+
const arch = input.arch ?? os.arch();
|
|
18
|
+
const hostname = input.hostname ?? os.hostname();
|
|
19
|
+
return createHash('sha256').update('talos.cli.process-tree-host.v1\0').update(`${platform}\0${release}\0${arch}\0${hostname}`).digest('hex');
|
|
20
|
+
}
|
|
21
|
+
export function createProcessTreeEvidenceStore(options) {
|
|
22
|
+
const file = path.join(options.cacheRoot, RECORD_FILE);
|
|
23
|
+
const now = options.now ?? (() => Date.now());
|
|
24
|
+
const positiveTtlMs = options.positiveTtlMs ?? POSITIVE_TTL_MS;
|
|
25
|
+
const negativeTtlMs = options.negativeTtlMs ?? NEGATIVE_TTL_MS;
|
|
26
|
+
const fingerprint = options.fingerprint ?? hostFingerprint();
|
|
27
|
+
return {
|
|
28
|
+
async read() {
|
|
29
|
+
let record;
|
|
30
|
+
try {
|
|
31
|
+
record = JSON.parse(await readFile(file, 'utf8'));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (record?.schema !== RECORD_SCHEMA)
|
|
37
|
+
return null;
|
|
38
|
+
if (record.hostFingerprint !== fingerprint)
|
|
39
|
+
return null;
|
|
40
|
+
if (typeof record.recordedAtMs !== 'number' || typeof record.result?.verified !== 'boolean')
|
|
41
|
+
return null;
|
|
42
|
+
const age = now() - record.recordedAtMs;
|
|
43
|
+
if (age < 0)
|
|
44
|
+
return null;
|
|
45
|
+
return age > (record.result.verified ? positiveTtlMs : negativeTtlMs) ? null : record.result;
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Never written in place. A project incident recorded `open(path,'w')` truncating a 36 KB file
|
|
49
|
+
* before the write that was meant to replace it, so the record is serialized first, written to
|
|
50
|
+
* a temporary file in the same directory, flushed, and only then renamed over the target.
|
|
51
|
+
*/
|
|
52
|
+
async write(result) {
|
|
53
|
+
const record = { schema: RECORD_SCHEMA, hostFingerprint: fingerprint, recordedAtMs: now(), result };
|
|
54
|
+
const serialized = `${JSON.stringify(record, null, 2)}\n`;
|
|
55
|
+
await mkdir(options.cacheRoot, { recursive: true, mode: 0o700 });
|
|
56
|
+
const temporary = `${file}.tmp-${process.pid}-${now()}`;
|
|
57
|
+
try {
|
|
58
|
+
await writeFile(temporary, serialized, { encoding: 'utf8', mode: 0o600 });
|
|
59
|
+
const handle = await open(temporary, 'r+');
|
|
60
|
+
try {
|
|
61
|
+
await handle.sync();
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await handle.close();
|
|
65
|
+
}
|
|
66
|
+
await rename(temporary, file);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
await rm(temporary, { force: true });
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|