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,626 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* git-service.mjs — W1-05 (05/09). Lo stato Git di una sessione: **status,
|
|
3
|
+
* stage, unstage, commit, ramo**. Ledger: `.claude/LEDGER-W1-05-GIT-SERVICE-2026-09-05.md`.
|
|
4
|
+
*
|
|
5
|
+
* Serve a W1-06, la Review a **due sorgenti dichiarate**: «Ultimo giro
|
|
6
|
+
* agente» viene dagli eventi, «Non committato» viene da qui. Le due non si
|
|
7
|
+
* mescolano mai, e questo file risponde solo della seconda.
|
|
8
|
+
*
|
|
9
|
+
* ⛔⛔⛔ `push` NON ESISTE IN QUESTO FILE, in nessuna forma, e nemmeno
|
|
10
|
+
* `remote`. Non è una precauzione: è una regola dell'owner — il push si
|
|
11
|
+
* chiede a lui ogni volta. `tests/git-service.test.mjs` la **pinna** leggendo
|
|
12
|
+
* il sorgente: se qualcuno aggiungesse la parola, quel test diventa rosso.
|
|
13
|
+
*
|
|
14
|
+
* ═══════════════════════════════════════════════════════════════════════
|
|
15
|
+
* LA RICERCA CHE HA CAMBIATO IL DISEGNO (fonti + data, tutte del 05/09/2026)
|
|
16
|
+
* ═══════════════════════════════════════════════════════════════════════
|
|
17
|
+
*
|
|
18
|
+
* 1. ⛔ `git status` si legge SEMPRE con `-z` — git-scm.com/docs/git-status,
|
|
19
|
+
* letto il 05/09/2026: «Without the `-z` option, pathnames with "unusual"
|
|
20
|
+
* characters are quoted as explained for the configuration variable
|
|
21
|
+
* `core.quotePath`». MISURATO su git 2.55.0.windows.3: senza `-z`
|
|
22
|
+
* `àccento.txt` arriva come `"\303\240ccento.txt"` (ottale, fra
|
|
23
|
+
* virgolette) e `con spazio.txt` fra virgolette. Con `-z`: byte grezzi,
|
|
24
|
+
* nessun escape da disfare. Su un progetto italiano non è un caso limite.
|
|
25
|
+
*
|
|
26
|
+
* 2. ⛔ Nel formato `-z` una RINOMINA cambia forma e **ordine** — stessa
|
|
27
|
+
* pagina: «the `->` is omitted from rename entries and the field order is
|
|
28
|
+
* reversed (e.g. `from -> to` becomes `to` `from`)». MISURATO: senza `-z`
|
|
29
|
+
* → `R vecchio.txt -> nuovo.txt`; con `-z` → `R nuovo.txt<NUL>vecchio.txt`.
|
|
30
|
+
* Un parser che non lo sa mostra ogni rinomina AL CONTRARIO.
|
|
31
|
+
*
|
|
32
|
+
* 3. ⭐ Perché **v1** e non `--porcelain=v2` (la domanda era aperta nel brief).
|
|
33
|
+
* Stessa pagina: «Version 1 porcelain format is similar to the short
|
|
34
|
+
* format, but is **guaranteed not to change in a backwards-incompatible
|
|
35
|
+
* way** between Git versions or based on user configuration. This makes it
|
|
36
|
+
* ideal for parsing by scripts.» Per v2 la stessa garanzia NON è scritta;
|
|
37
|
+
* dice solo che gli header sono estensibili e che «Parsers should ignore
|
|
38
|
+
* headers they don't recognize». ⇒ v1 è la scelta con la promessa scritta,
|
|
39
|
+
* e v2 non porterebbe niente che ci serva: i suoi campi in più sono modi,
|
|
40
|
+
* oggetti e punteggio di somiglianza, che la Review non mostra.
|
|
41
|
+
* ⛔ E v2 NON risolve la rinomina: MISURATO, anche lì l'ordine è
|
|
42
|
+
* `<path><sep><origPath>`, cioè di nuovo «prima il DOVE, poi il DA».
|
|
43
|
+
*
|
|
44
|
+
* 4. ⛔⛔⛔ **`git commit -- <percorsi>` NON committa quello che è in STAGE.**
|
|
45
|
+
* git-scm.com/docs/git-commit, `--only`, letto il 05/09/2026: «Make a
|
|
46
|
+
* commit by taking the **updated working tree contents** of the paths
|
|
47
|
+
* specified on the command line, disregarding any contents that have been
|
|
48
|
+
* staged for other paths. This is the default mode of operation of git
|
|
49
|
+
* commit if any paths are given on the command line». MISURATO: messo in
|
|
50
|
+
* stage `v1-STAGED`, poi cambiato il file in `v2-WORKTREE`, poi
|
|
51
|
+
* `git commit -F msg -- a.txt` ⇒ nel commit c'è **`v2-WORKTREE`**.
|
|
52
|
+
* ⇒ Due conseguenze opposte, ed entrambe contano:
|
|
53
|
+
* ⭐ BUONA — un percorso esplicito **non trascina** ciò che un'altra
|
|
54
|
+
* sessione ha messo in stage: MISURATO, `altro.txt` è rimasto in stage
|
|
55
|
+
* e fuori dal commit. È la cura vera del difetto «due sessioni, stessa
|
|
56
|
+
* cartella, intrecciano i commit», ed è git stesso a garantirla.
|
|
57
|
+
* ⛔ CATTIVA — se il file è cambiato DOPO lo stage, finisce nel commit
|
|
58
|
+
* la versione nuova, cioè **non quella che la Review ha mostrato**.
|
|
59
|
+
* Silenziosa. Per questo `commit()` **rifiuta** quel caso per nome
|
|
60
|
+
* (`GIT_WORKTREE_DIFFERS`) invece di committare la cosa sbagliata.
|
|
61
|
+
*
|
|
62
|
+
* 5. ⛔⛔ La magia dei pathspec **sopravvive a `--`**, e scavalca lo scoping.
|
|
63
|
+
* MISURATO: da una sottocartella, `git add -- :/` ha messo in stage
|
|
64
|
+
* **l'intero repository** (`:/` = "dalla radice del repo"), e
|
|
65
|
+
* `:(exclude)…` filtra a piacere. La cura è `--literal-pathspecs` —
|
|
66
|
+
* git-scm.com/docs/git, letto il 05/09/2026: «Treat pathspecs literally
|
|
67
|
+
* (i.e. no globbing, no pathspec magic)». MISURATO: con quel flag `:/`
|
|
68
|
+
* diventa un nome di file letterale che non esiste e git rifiuta.
|
|
69
|
+
* ⇒ Ogni comando di questo file lo porta, SEMPRE, anche in lettura.
|
|
70
|
+
*
|
|
71
|
+
* 6. ⛔⛔ L'indice è CONDIVISO con l'owner e con le altre sessioni, e
|
|
72
|
+
* `git status` di suo **scrive**: git-scm.com/docs/git, letto il
|
|
73
|
+
* 05/09/2026 — «this will prevent `git status` from refreshing the index
|
|
74
|
+
* as a side effect. This is useful for processes running in the background
|
|
75
|
+
* which do not want to cause lock contention». ⇒ tutto ciò che LEGGE porta
|
|
76
|
+
* `--no-optional-locks`: un pannello Review che si aggiorna non deve
|
|
77
|
+
* prendersi `index.lock` sotto le mani di chi sta lavorando.
|
|
78
|
+
*
|
|
79
|
+
* 7. ⛔ `git branch --show-current`, non `rev-parse --abbrev-ref HEAD`.
|
|
80
|
+
* MISURATO, due casi in cui il secondo mente o muore:
|
|
81
|
+
* · repo appena creato, nessun commit ⇒ `rev-parse` esce **128**
|
|
82
|
+
* («ambiguous argument 'HEAD'»), `--show-current` risponde `master`;
|
|
83
|
+
* · HEAD staccata ⇒ `rev-parse` risponde la **stringa `HEAD`**, che
|
|
84
|
+
* sembra un nome di ramo; `--show-current` risponde `''`, cioè la
|
|
85
|
+
* verità: nessun ramo.
|
|
86
|
+
* ⛔ `src/workspace-context.mjs` usa ancora la prima forma: è un rilievo
|
|
87
|
+
* REGISTRATO nel ledger, non corretto qui — non è la mia riga.
|
|
88
|
+
*
|
|
89
|
+
* 8. ⛔ Su Windows `spawn`/`execFile` con argomenti non fidati è stato una
|
|
90
|
+
* RCE quando l'eseguibile è un `.bat`/`.cmd`: CVE-2024-27980, e il suo
|
|
91
|
+
* bypass CVE-2024-36138 (nodejs.org/en/blog/vulnerability/april-2024-security-releases-2,
|
|
92
|
+
* letto il 05/09/2026) — «command injection … even if the shell option is
|
|
93
|
+
* not enabled». Qui l'eseguibile è `git` (che risolve a `git.exe`) e
|
|
94
|
+
* `process-policy` impone `shell:false`; in più nessun argomento è mai
|
|
95
|
+
* concatenato in una stringa, i percorsi vanno dopo `--`, e i nomi che
|
|
96
|
+
* cominciano per `-` o `:` sono rifiutati a monte da `normalizzaPercorso`.
|
|
97
|
+
*
|
|
98
|
+
* ═══════════════════════════════════════════════════════════════════════
|
|
99
|
+
* I CONFINI — misurati su questo repository, non presunti
|
|
100
|
+
* ═══════════════════════════════════════════════════════════════════════
|
|
101
|
+
*
|
|
102
|
+
* ⛔ **Questa cartella è un WORKTREE**: `.git` è un FILE
|
|
103
|
+
* (`gitdir: …/AVM/.git/worktrees/AVM-harness-desktop`), non una directory.
|
|
104
|
+
* Per questo NIENTE qui dentro cerca `.git` come cartella per decidere se
|
|
105
|
+
* un posto è un repository: la domanda si fa a git, con
|
|
106
|
+
* `rev-parse --show-toplevel --show-prefix`. VERIFICATO: da qui
|
|
107
|
+
* `--show-toplevel` risponde `…/AVM-harness-desktop`, cioè la radice del
|
|
108
|
+
* worktree, non quella del clone originale.
|
|
109
|
+
*
|
|
110
|
+
* ⛔ **La cartella della sessione può essere una SOTTOCARTELLA del repo.**
|
|
111
|
+
* MISURATO: da una sottocartella, `git status` **senza** pathspec elenca
|
|
112
|
+
* TUTTO il repository padre — cioè file che il workspace non contiene. Per
|
|
113
|
+
* questo ogni lettura è ristretta con `-- .`, e i percorsi che git restituisce
|
|
114
|
+
* (sempre relativi alla RADICE del repo: «paths shown will always be relative
|
|
115
|
+
* to the repository root») vengono riportati al vocabolario della sessione
|
|
116
|
+
* togliendo `--show-prefix`. ⇒ Fuori di qui esiste **un solo vocabolario**:
|
|
117
|
+
* percorsi relativi alla cartella della sessione.
|
|
118
|
+
*
|
|
119
|
+
* ⛔ **Repo annidati — W1-13**: un repository dentro il workspace ha fiducia
|
|
120
|
+
* PROPRIA e non si attraversa (`src/workspace-context.mjs`, che li elenca
|
|
121
|
+
* fermandosi su `existsSync(join(sotto,'.git'))`). Qui il confine lo tiene
|
|
122
|
+
* git stesso, ed è MISURATO: il padre vede `?? annidato/` — la cartella, mai
|
|
123
|
+
* i file dentro — **anche con `-uall`**. Questo file non fa niente per
|
|
124
|
+
* scavalcarlo; si limita a **dirlo**, marcando quella voce `repoAnnidato:true`
|
|
125
|
+
* con lo stesso identico test del modulo di W1-13, così la Review non
|
|
126
|
+
* presenta come «file da aggiungere» il contenuto di un altro repository.
|
|
127
|
+
*/
|
|
128
|
+
import { execFile } from 'node:child_process';
|
|
129
|
+
import { existsSync } from 'node:fs';
|
|
130
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
131
|
+
import { tmpdir } from 'node:os';
|
|
132
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
133
|
+
|
|
134
|
+
import { isPathInside } from './path-policy.mjs';
|
|
135
|
+
import { createProcessPolicy } from './process-policy.mjs';
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* ⛔ Le chiavi d'ambiente che git può vedere. `HOME`/`USERPROFILE` NON sono
|
|
139
|
+
* di comodo: senza, git non trova `~/.gitconfig` e quindi non trova
|
|
140
|
+
* `user.name`/`user.email` — cioè `commit()` fallirebbe sull'identità.
|
|
141
|
+
* ⛔ Nessuna variabile `GIT_*`: le due che ci servono (`GIT_OPTIONAL_LOCKS`,
|
|
142
|
+
* `GIT_LITERAL_PATHSPECS`) sono passate come FLAG sulla riga di comando, dove
|
|
143
|
+
* si vedono in un test e in un log, invece che come ambiente invisibile.
|
|
144
|
+
*/
|
|
145
|
+
const AMBIENTE_GIT = Object.freeze([
|
|
146
|
+
'PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR', 'COMSPEC',
|
|
147
|
+
'HOME', 'USERPROFILE', 'TMP', 'TEMP', 'LANG', 'LC_ALL',
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* ⛔ Davanti a OGNI sottocomando, senza eccezioni. Il primo tiene le mani
|
|
152
|
+
* lontane da `index.lock` su un indice condiviso (ricerca 6), il secondo
|
|
153
|
+
* toglie ai percorsi del client ogni potere di pathspec (ricerca 5).
|
|
154
|
+
*/
|
|
155
|
+
const FLAG_SEMPRE = Object.freeze(['--no-optional-locks', '--literal-pathspecs']);
|
|
156
|
+
|
|
157
|
+
const TIMEOUT_LETTURA_MS = 15_000;
|
|
158
|
+
const TIMEOUT_SCRITTURA_MS = 60_000;
|
|
159
|
+
/** Un `git status` di un monorepo enorme non deve diventare un errore misterioso: oltre questo si dice PERCHÉ. */
|
|
160
|
+
const TETTO_USCITA_BYTE = 32 * 1024 * 1024;
|
|
161
|
+
const TETTO_MESSAGGIO_BYTE = 64 * 1024;
|
|
162
|
+
/** Le combinazioni XY che git documenta come "unmerged": un conflitto non è né staged né non-staged, è una terza cosa. */
|
|
163
|
+
const CONFLITTO = new Set(['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']);
|
|
164
|
+
|
|
165
|
+
export class GitServiceError extends Error {
|
|
166
|
+
constructor(message, code = 'GIT_COMMAND_FAILED') {
|
|
167
|
+
super(message);
|
|
168
|
+
this.name = 'GitServiceError';
|
|
169
|
+
this.code = code;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Un percorso arrivato dal client è **testo non fidato**. Qui si decide, in
|
|
175
|
+
* un posto solo, cosa può essere — e ogni rifiuto è per nome.
|
|
176
|
+
*
|
|
177
|
+
* ⛔ AL CONTRARIO, esplicitamente: assoluto no (porterebbe fuori dalla
|
|
178
|
+
* sessione), `..` no (stessa cosa per un'altra strada), iniziale `-` no (un
|
|
179
|
+
* argomento che sembra un'opzione), iniziale `:` no (è la magia di pathspec
|
|
180
|
+
* MISURATA alla ricerca 5 — `--literal-pathspecs` la disinnesca già, e questa
|
|
181
|
+
* è la seconda serratura sulla stessa porta), NUL no.
|
|
182
|
+
*
|
|
183
|
+
* @returns {string} il percorso normalizzato con `/`, relativo alla cartella
|
|
184
|
+
*/
|
|
185
|
+
export function normalizzaPercorso(cartella, percorso) {
|
|
186
|
+
if (typeof percorso !== 'string' || percorso.trim() === '' || percorso.includes('\0')) {
|
|
187
|
+
throw new GitServiceError('Percorso non valido', 'GIT_PATH_INVALID');
|
|
188
|
+
}
|
|
189
|
+
if (isAbsolute(percorso) || /^[a-zA-Z]:/u.test(percorso)) {
|
|
190
|
+
throw new GitServiceError('Il percorso deve essere relativo alla cartella della sessione', 'GIT_PATH_INVALID');
|
|
191
|
+
}
|
|
192
|
+
if (percorso.startsWith('-')) {
|
|
193
|
+
throw new GitServiceError('Un percorso non può cominciare con «-»', 'GIT_PATH_INVALID');
|
|
194
|
+
}
|
|
195
|
+
if (percorso.startsWith(':')) {
|
|
196
|
+
throw new GitServiceError('Un percorso non può cominciare con «:»', 'GIT_PATH_INVALID');
|
|
197
|
+
}
|
|
198
|
+
const pezzi = percorso.split(/[\\/]+/u);
|
|
199
|
+
if (pezzi.some((p) => p === '..')) {
|
|
200
|
+
throw new GitServiceError('Il percorso esce dalla cartella della sessione', 'GIT_PATH_INVALID');
|
|
201
|
+
}
|
|
202
|
+
const pulito = pezzi.filter((p) => p !== '' && p !== '.').join('/');
|
|
203
|
+
if (pulito === '') throw new GitServiceError('Percorso non valido', 'GIT_PATH_INVALID');
|
|
204
|
+
/* ⛔ Terza serratura: il controllo lessicale di containment che usa già il resto del server (`path-policy.isPathInside`), su percorsi REALI — così un giorno in cui una delle due regole sopra cambiasse, questa continuerebbe a mordere. */
|
|
205
|
+
if (!isPathInside(cartella, resolve(cartella, pulito))) {
|
|
206
|
+
throw new GitServiceError('Il percorso esce dalla cartella della sessione', 'GIT_PATH_INVALID');
|
|
207
|
+
}
|
|
208
|
+
return pulito;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Analizza l'uscita di `git status --porcelain=v1 -z`.
|
|
213
|
+
*
|
|
214
|
+
* ⛔ La forma è: `XY<spazio><percorso><NUL>`, e per una rinomina/copia
|
|
215
|
+
* **due** campi — `XY<spazio><DOVE><NUL><DA><NUL>` — con l'ordine INVERTITO
|
|
216
|
+
* rispetto alla forma leggibile (ricerca 2, misurata). Chi legge questa
|
|
217
|
+
* funzione deve sapere che `da` viene DOPO nel flusso ma è il PRIMA nel tempo.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} testo uscita grezza, già decodificata una volta sola
|
|
220
|
+
* @returns {Array<{x:string,y:string,percorsoRepo:string,daRepo:string|null}>}
|
|
221
|
+
*/
|
|
222
|
+
export function analizzaStatoPorcelain(testo) {
|
|
223
|
+
if (typeof testo !== 'string') throw new GitServiceError('Uscita di git non leggibile', 'GIT_COMMAND_FAILED');
|
|
224
|
+
const campi = testo.split('\0');
|
|
225
|
+
const voci = [];
|
|
226
|
+
let i = 0;
|
|
227
|
+
while (i < campi.length) {
|
|
228
|
+
const riga = campi[i];
|
|
229
|
+
i += 1;
|
|
230
|
+
if (riga === '') continue; // la coda dopo l'ultimo NUL, e nient'altro
|
|
231
|
+
if (riga.length < 4 || riga[2] !== ' ') {
|
|
232
|
+
throw new GitServiceError('Riga di stato git non riconosciuta', 'GIT_COMMAND_FAILED');
|
|
233
|
+
}
|
|
234
|
+
const x = riga[0];
|
|
235
|
+
const y = riga[1];
|
|
236
|
+
const percorsoRepo = riga.slice(3);
|
|
237
|
+
let daRepo = null;
|
|
238
|
+
if (x === 'R' || x === 'C' || y === 'R' || y === 'C') {
|
|
239
|
+
/* ⛔ Il campo in più c'è SOLO qui. Consumarlo sempre spezzerebbe l'allineamento di tutte le voci successive. */
|
|
240
|
+
if (i >= campi.length) throw new GitServiceError('Rinomina senza percorso di origine', 'GIT_COMMAND_FAILED');
|
|
241
|
+
daRepo = campi[i];
|
|
242
|
+
i += 1;
|
|
243
|
+
}
|
|
244
|
+
voci.push({ x, y, percorsoRepo, daRepo });
|
|
245
|
+
}
|
|
246
|
+
return voci;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Dalla coppia XY al vocabolario che la Review mostra. `X` = indice, `Y` = albero di lavoro (git-status(1)). */
|
|
250
|
+
function descrivi(x, y) {
|
|
251
|
+
const xy = `${x}${y}`;
|
|
252
|
+
if (CONFLITTO.has(xy)) return { tipo: 'conflitto', staged: false, nonStaged: false, conflitto: true };
|
|
253
|
+
if (x === '?' && y === '?') return { tipo: 'nonTracciato', staged: false, nonStaged: true, conflitto: false };
|
|
254
|
+
if (x === '!' && y === '!') return { tipo: 'ignorato', staged: false, nonStaged: false, conflitto: false };
|
|
255
|
+
const lettera = x !== ' ' && x !== '?' && x !== '!' ? x : y;
|
|
256
|
+
const tipo = { M: 'modificato', A: 'aggiunto', D: 'eliminato', R: 'rinominato', C: 'copiato', T: 'tipoCambiato' }[lettera] ?? 'modificato';
|
|
257
|
+
return {
|
|
258
|
+
tipo,
|
|
259
|
+
staged: x !== ' ' && x !== '?' && x !== '!',
|
|
260
|
+
nonStaged: y !== ' ' && y !== '?' && y !== '!',
|
|
261
|
+
conflitto: false,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @param {object} deps
|
|
267
|
+
* @param {(sessionId:string)=>string|null} deps.cartellaDiSessione ⛔ L'UNICA
|
|
268
|
+
* autorità sulla cartella, esattamente come in `terminal-registry.mjs`: il
|
|
269
|
+
* client NOMINA una sessione, non sceglie mai un percorso di lavoro. Un id
|
|
270
|
+
* sconosciuto torna `null` e qui non succede niente — nessun ripiego sul
|
|
271
|
+
* primo progetto configurato (era il difetto di `server.mjs:589`, W1-01).
|
|
272
|
+
* @param {Function} [deps.eseguiGitFn] SOLO per i test: sostituisce l'esecuzione vera.
|
|
273
|
+
*/
|
|
274
|
+
export function creaServizioGit({
|
|
275
|
+
cartellaDiSessione,
|
|
276
|
+
eseguiGitFn = null,
|
|
277
|
+
timeoutLetturaMs = TIMEOUT_LETTURA_MS,
|
|
278
|
+
timeoutScritturaMs = TIMEOUT_SCRITTURA_MS,
|
|
279
|
+
esisteFn = existsSync,
|
|
280
|
+
} = {}) {
|
|
281
|
+
if (typeof cartellaDiSessione !== 'function') {
|
|
282
|
+
throw new GitServiceError('Serve l’autorità sulla cartella di sessione', 'GIT_STORE_UNAVAILABLE');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* ⛔⛔ Perché `policy.execFile` e non `runApprovedProcess`, che è la porta
|
|
287
|
+
* che usano gli altri: `runApprovedProcess` accumula l'uscita decodificando
|
|
288
|
+
* **ogni pezzo separatamente** (`process-policy.mjs`, `appendBounded`:
|
|
289
|
+
* `chunk.toString('utf8')`). Un carattere UTF-8 multibyte che cade a
|
|
290
|
+
* cavallo di due pezzi si spezza, e `àccento.txt` tornerebbe corrotto —
|
|
291
|
+
* cioè esattamente la garanzia che `-z` esiste per dare. Qui l'uscita si
|
|
292
|
+
* prende in **Buffer** e si decodifica UNA volta sola, alla fine.
|
|
293
|
+
* ⛔ Resta tutto dentro `process-policy`: eseguibile in elenco, `shell:false`
|
|
294
|
+
* imposto, ambiente filtrato, e `cwdRoot` = la cartella della sessione,
|
|
295
|
+
* quindi la cartella di lavoro non può nemmeno essere un'altra.
|
|
296
|
+
*/
|
|
297
|
+
async function eseguiVero(cartella, argomenti, timeoutMs) {
|
|
298
|
+
const policy = createProcessPolicy({
|
|
299
|
+
allowedExecutables: ['git'],
|
|
300
|
+
cwdRoot: cartella,
|
|
301
|
+
envAllowlist: AMBIENTE_GIT,
|
|
302
|
+
});
|
|
303
|
+
return new Promise((esci) => {
|
|
304
|
+
policy.execFile('git', argomenti, {
|
|
305
|
+
cwd: cartella,
|
|
306
|
+
encoding: 'buffer',
|
|
307
|
+
timeout: timeoutMs,
|
|
308
|
+
maxBuffer: TETTO_USCITA_BYTE,
|
|
309
|
+
windowsHide: true,
|
|
310
|
+
}, (errore, stdout, stderr) => {
|
|
311
|
+
const fuori = Buffer.isBuffer(stdout) ? stdout.toString('utf8') : String(stdout ?? '');
|
|
312
|
+
const errori = Buffer.isBuffer(stderr) ? stderr.toString('utf8') : String(stderr ?? '');
|
|
313
|
+
if (!errore) { esci({ codice: 0, stdout: fuori, stderr: errori }); return; }
|
|
314
|
+
esci({
|
|
315
|
+
codice: typeof errore.code === 'number' ? errore.code : null,
|
|
316
|
+
stdout: fuori,
|
|
317
|
+
stderr: errori,
|
|
318
|
+
scaduto: errore.killed === true || errore.signal != null,
|
|
319
|
+
troppoGrande: errore.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER',
|
|
320
|
+
avvioFallito: errore.code === 'ENOENT',
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const eseguiGit = eseguiGitFn ?? eseguiVero;
|
|
327
|
+
|
|
328
|
+
/** Lancia con i flag obbligatori davanti, e traduce ogni fallimento in un codice NOMINATO. */
|
|
329
|
+
async function git(cartella, argomenti, { timeoutMs = timeoutLetturaMs, tollera = false } = {}) {
|
|
330
|
+
const esito = await eseguiGit(cartella, [...FLAG_SEMPRE, ...argomenti], timeoutMs);
|
|
331
|
+
if (esito.codice === 0) return esito;
|
|
332
|
+
if (tollera) return esito;
|
|
333
|
+
if (esito.troppoGrande) throw new GitServiceError('L’uscita di git supera il limite consentito', 'GIT_OUTPUT_TOO_LARGE');
|
|
334
|
+
if (esito.scaduto) throw new GitServiceError('git non ha risposto entro il tempo massimo', 'GIT_TIMEOUT');
|
|
335
|
+
if (esito.avvioFallito) throw new GitServiceError('git non è installato o non è raggiungibile', 'GIT_COMMAND_FAILED');
|
|
336
|
+
const detto = String(esito.stderr || '').trim();
|
|
337
|
+
if (/not a git repository/iu.test(detto)) {
|
|
338
|
+
throw new GitServiceError('Questa cartella non è un repository git', 'GIT_NOT_A_REPOSITORY');
|
|
339
|
+
}
|
|
340
|
+
throw new GitServiceError(detto || 'git ha risposto con un errore', 'GIT_COMMAND_FAILED');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** La cartella della sessione, o un rifiuto. ⛔ Mai un ripiego: un id ignoto non prende NIENTE. */
|
|
344
|
+
function cartellaDi(sessionId) {
|
|
345
|
+
if (typeof sessionId !== 'string' || sessionId === '') {
|
|
346
|
+
throw new GitServiceError('Sessione non valida', 'QUERY_INVALID');
|
|
347
|
+
}
|
|
348
|
+
const cartella = cartellaDiSessione(sessionId);
|
|
349
|
+
if (typeof cartella !== 'string' || cartella === '') {
|
|
350
|
+
throw new GitServiceError('Sessione non trovata', 'NOT_FOUND');
|
|
351
|
+
}
|
|
352
|
+
return resolve(cartella);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Dove comincia il repository, e quanto è profonda la cartella della
|
|
357
|
+
* sessione al suo interno. ⛔ Una sola domanda a git per entrambe le cose:
|
|
358
|
+
* `rev-parse` stampa una riga per ciascun argomento, in ordine. Alla radice
|
|
359
|
+
* `--show-prefix` è la riga VUOTA, e va bene così.
|
|
360
|
+
*/
|
|
361
|
+
async function collocazione(cartella) {
|
|
362
|
+
const esito = await git(cartella, ['rev-parse', '--show-toplevel', '--show-prefix']);
|
|
363
|
+
const righe = esito.stdout.split('\n');
|
|
364
|
+
const radiceRepo = (righe[0] ?? '').trim();
|
|
365
|
+
if (radiceRepo === '') throw new GitServiceError('Questa cartella non è un repository git', 'GIT_NOT_A_REPOSITORY');
|
|
366
|
+
const prefisso = (righe[1] ?? '').trim();
|
|
367
|
+
return {
|
|
368
|
+
radiceRepo,
|
|
369
|
+
prefisso,
|
|
370
|
+
/* ⛔ Un'informazione onesta, non un giudizio: la sessione è la radice del repo, oppure ci sta dentro. Il pannello lo deve poter dire a chi guarda. */
|
|
371
|
+
cartellaEradiceRepo: prefisso === '',
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Dal vocabolario di git (relativo alla RADICE del repo) a quello di questo servizio (relativo alla cartella della sessione). */
|
|
376
|
+
function versoLaSessione(prefisso, percorsoRepo) {
|
|
377
|
+
if (prefisso === '') return percorsoRepo;
|
|
378
|
+
if (!percorsoRepo.startsWith(prefisso)) return null; // fuori dalla cartella della sessione: non esiste, per chi ci parla
|
|
379
|
+
return percorsoRepo.slice(prefisso.length);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function statoInterno(cartella) {
|
|
383
|
+
const dove = await collocazione(cartella);
|
|
384
|
+
/*
|
|
385
|
+
* ⛔⛔ ONESTÀ SU CHI FA COSA — scoperto SABOTANDO, non ragionando.
|
|
386
|
+
* Tolto `-- .`, i test restavano tutti verdi: la mia prima stesura
|
|
387
|
+
* diceva in un commento che era `-- .` a impedire la fuga di file del
|
|
388
|
+
* repository padre, e NON È VERO. Il containment lo fa il **filtro sul
|
|
389
|
+
* prefisso** in `versoLaSessione()`, che scarta ogni percorso che non
|
|
390
|
+
* comincia per `--show-prefix`. Quello è il lucchetto, ed è provato
|
|
391
|
+
* dal test della sottocartella.
|
|
392
|
+
* ⇒ `-- .` resta, ma per la ragione VERA: MISURATO, senza pathspec da
|
|
393
|
+
* una sottocartella git percorre e riporta l'INTERO repository padre,
|
|
394
|
+
* che poi noi buttiamo. Su un monorepo è lavoro (e pressione sui lock
|
|
395
|
+
* dell'indice condiviso) pagato a ogni aggiornamento della Review per
|
|
396
|
+
* niente, ed è anche il modo più facile di sbattere contro il tetto di
|
|
397
|
+
* uscita. È una cura di COSTO, non di sicurezza, e il test che la pinna
|
|
398
|
+
* guarda gli argomenti passati a git, non l'elenco che ne esce.
|
|
399
|
+
*/
|
|
400
|
+
let esito = await git(cartella, ['status', '--porcelain=v1', '-z', '--', '.']);
|
|
401
|
+
let grezze = analizzaStatoPorcelain(esito.stdout);
|
|
402
|
+
/*
|
|
403
|
+
* ⛔⛔⛔ LA CARTELLA COLLASSATA. git riassume una cartella interamente
|
|
404
|
+
* non tracciata con la sola cartella (`?? sessione/`): se quella cartella
|
|
405
|
+
* è proprio la sessione, il percorso coincide con il prefisso e togliendo
|
|
406
|
+
* il prefisso resta la STRINGA VUOTA — una riga senza nome, che non si
|
|
407
|
+
* può né mostrare né mettere in stage. Succede tutte le volte che si apre
|
|
408
|
+
* una sessione su una cartella nuova dentro un repo che esiste già.
|
|
409
|
+
* ⇒ Solo in quel caso si richiede con `-uall`, che apre il riassunto.
|
|
410
|
+
* ⛔ MISURATO: `-uall` NON scavalca il confine di W1-13 — un repository
|
|
411
|
+
* annidato resta una cartella sola anche così, il suo contenuto non esce.
|
|
412
|
+
*/
|
|
413
|
+
if (dove.prefisso !== '' && grezze.some((r) => r.percorsoRepo === dove.prefisso)) {
|
|
414
|
+
esito = await git(cartella, ['status', '--porcelain=v1', '-z', '-uall', '--', '.']);
|
|
415
|
+
grezze = analizzaStatoPorcelain(esito.stdout);
|
|
416
|
+
}
|
|
417
|
+
const voci = [];
|
|
418
|
+
for (const riga of grezze) {
|
|
419
|
+
const percorso = versoLaSessione(dove.prefisso, riga.percorsoRepo);
|
|
420
|
+
if (percorso === null) continue;
|
|
421
|
+
const da = riga.daRepo === null ? null : versoLaSessione(dove.prefisso, riga.daRepo);
|
|
422
|
+
const forma = descrivi(riga.x, riga.y);
|
|
423
|
+
const cartellaVoce = percorso.endsWith('/');
|
|
424
|
+
voci.push({
|
|
425
|
+
percorso,
|
|
426
|
+
percorsoRepo: riga.percorsoRepo,
|
|
427
|
+
/* ⛔ Ricerca 2: `da` è il nome VECCHIO, e nel flusso `-z` arriva DOPO quello nuovo. Qui è già nel verso giusto. */
|
|
428
|
+
da,
|
|
429
|
+
x: riga.x,
|
|
430
|
+
y: riga.y,
|
|
431
|
+
...forma,
|
|
432
|
+
cartella: cartellaVoce,
|
|
433
|
+
/*
|
|
434
|
+
* ⭐ W1-13 — un repository dentro il workspace ha fiducia PROPRIA.
|
|
435
|
+
* git lo mostra come una cartella non tracciata e non ne elenca il
|
|
436
|
+
* contenuto (MISURATO, anche con `-uall`): qui lo si DICE, con lo
|
|
437
|
+
* stesso identico test di `workspace-context.repoAnnidati()`, così la
|
|
438
|
+
* Review non lo presenta come «una cartella di file da aggiungere».
|
|
439
|
+
*/
|
|
440
|
+
repoAnnidato: cartellaVoce && forma.tipo === 'nonTracciato'
|
|
441
|
+
? esisteFn(join(cartella, percorso, '.git'))
|
|
442
|
+
: false,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
const riepilogo = {
|
|
446
|
+
totale: voci.length,
|
|
447
|
+
staged: voci.filter((v) => v.staged).length,
|
|
448
|
+
nonStaged: voci.filter((v) => v.nonStaged && v.tipo !== 'nonTracciato').length,
|
|
449
|
+
nonTracciati: voci.filter((v) => v.tipo === 'nonTracciato').length,
|
|
450
|
+
conflitti: voci.filter((v) => v.conflitto).length,
|
|
451
|
+
};
|
|
452
|
+
return { ...dove, voci, riepilogo };
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** `git branch --show-current`, mai `rev-parse --abbrev-ref HEAD` — ricerca 7, due bugie misurate. */
|
|
456
|
+
async function ramoInterno(cartella) {
|
|
457
|
+
const dove = await collocazione(cartella);
|
|
458
|
+
const esito = await git(cartella, ['branch', '--show-current']);
|
|
459
|
+
const ramo = esito.stdout.trim();
|
|
460
|
+
return {
|
|
461
|
+
...dove,
|
|
462
|
+
ramo: ramo === '' ? null : ramo,
|
|
463
|
+
/* ⛔ `null` + `staccata:true` è la verità; la stringa «HEAD» che dà `rev-parse` sembrerebbe un ramo che si chiama così. */
|
|
464
|
+
staccata: ramo === '',
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** Ogni percorso passa dalla stessa serratura, e l'elenco non può essere vuoto o furbo. */
|
|
469
|
+
function normalizzaElenco(cartella, percorsi) {
|
|
470
|
+
if (!Array.isArray(percorsi) || percorsi.length === 0) {
|
|
471
|
+
throw new GitServiceError('Serve almeno un percorso esplicito', 'GIT_PATHS_REQUIRED');
|
|
472
|
+
}
|
|
473
|
+
if (percorsi.length > 1000) {
|
|
474
|
+
throw new GitServiceError('Troppi percorsi in una sola richiesta', 'GIT_PATHS_REQUIRED');
|
|
475
|
+
}
|
|
476
|
+
const puliti = percorsi.map((p) => normalizzaPercorso(cartella, p));
|
|
477
|
+
return [...new Set(puliti)];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return Object.freeze({
|
|
481
|
+
/** Lo stato «Non committato» della Review: una voce per file, nel vocabolario della sessione. */
|
|
482
|
+
async stato({ sessionId } = {}) {
|
|
483
|
+
try {
|
|
484
|
+
const cartella = cartellaDi(sessionId);
|
|
485
|
+
return await statoInterno(cartella);
|
|
486
|
+
} catch (errore) { return rifiuto(errore); }
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
/** Il ramo corrente. `ramo:null` + `staccata:true` quando HEAD è staccata. */
|
|
490
|
+
async ramo({ sessionId } = {}) {
|
|
491
|
+
try {
|
|
492
|
+
const cartella = cartellaDi(sessionId);
|
|
493
|
+
return await ramoInterno(cartella);
|
|
494
|
+
} catch (errore) { return rifiuto(errore); }
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Mette in stage percorsi ESPLICITI.
|
|
499
|
+
* ⛔ Non esiste una forma «tutto»: niente `-A`, niente `.`, niente
|
|
500
|
+
* `--update`. L'indice è condiviso con l'owner e con le altre sessioni, e
|
|
501
|
+
* una scrittura larga da qui raccoglierebbe lavoro non nostro — è il
|
|
502
|
+
* difetto già pagato una volta («git add -A raccoglie lavoro NON MIO»).
|
|
503
|
+
*/
|
|
504
|
+
async stage({ sessionId, percorsi } = {}) {
|
|
505
|
+
try {
|
|
506
|
+
const cartella = cartellaDi(sessionId);
|
|
507
|
+
const puliti = normalizzaElenco(cartella, percorsi);
|
|
508
|
+
await git(cartella, ['add', '--', ...puliti], { timeoutMs: timeoutScritturaMs });
|
|
509
|
+
return { ok: true, percorsi: puliti, stato: await statoInterno(cartella) };
|
|
510
|
+
} catch (errore) { return rifiuto(errore); }
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Toglie dallo stage percorsi ESPLICITI.
|
|
515
|
+
* ⛔ `git reset -- <percorsi>` e non `git restore --staged`: MISURATO, su
|
|
516
|
+
* un repository senza nemmeno un commit `reset` funziona e `restore`
|
|
517
|
+
* fallisce (non c'è un HEAD da cui ripristinare). Il primo `git add` di un
|
|
518
|
+
* progetto nuovo è esattamente il momento in cui uno vuole poter tornare
|
|
519
|
+
* indietro.
|
|
520
|
+
*/
|
|
521
|
+
async unstage({ sessionId, percorsi } = {}) {
|
|
522
|
+
try {
|
|
523
|
+
const cartella = cartellaDi(sessionId);
|
|
524
|
+
const puliti = normalizzaElenco(cartella, percorsi);
|
|
525
|
+
/* ⛔ `-q`: senza, `git reset` scrive su stdout l'elenco delle differenze residue ed esce comunque 0 — rumore, non un esito. */
|
|
526
|
+
await git(cartella, ['reset', '-q', '--', ...puliti], { timeoutMs: timeoutScritturaMs });
|
|
527
|
+
return { ok: true, percorsi: puliti, stato: await statoInterno(cartella) };
|
|
528
|
+
} catch (errore) { return rifiuto(errore); }
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Commit di percorsi ESPLICITI, con il messaggio letto da un FILE.
|
|
533
|
+
*
|
|
534
|
+
* ⛔⛔ I percorsi espliciti sono la cura del difetto «due sessioni, stessa
|
|
535
|
+
* cartella, intrecciano i commit»: MISURATO alla ricerca 4, ciò che
|
|
536
|
+
* un'altra sessione ha messo in stage su un ALTRO percorso resta fuori dal
|
|
537
|
+
* commit e resta in stage. Un `git commit` senza percorsi fotograferebbe
|
|
538
|
+
* l'intero indice condiviso — per questo qui non è nemmeno rappresentabile.
|
|
539
|
+
*
|
|
540
|
+
* ⛔⛔ E il prezzo di quella scelta è dichiarato invece che subìto: con i
|
|
541
|
+
* percorsi git prende il contenuto dell'ALBERO DI LAVORO, non quello in
|
|
542
|
+
* stage. Se il file è cambiato dopo lo stage, quello che finisce nel
|
|
543
|
+
* commit **non è quello che la Review ha mostrato** ⇒ si rifiuta per nome
|
|
544
|
+
* (`GIT_WORKTREE_DIFFERS`), dicendo quali file rimettere in stage.
|
|
545
|
+
*
|
|
546
|
+
* ⛔ Il messaggio non passa MAI dalla riga di comando: va su un file
|
|
547
|
+
* temporaneo e si passa `-F`. Motivi misurati, non estetici: su Windows la
|
|
548
|
+
* riga di comando ha un tetto duro, e un messaggio lungo o multilinea è
|
|
549
|
+
* normale. `--cleanup=whitespace` è esplicito perché il default (`default`)
|
|
550
|
+
* cambia comportamento a seconda che git creda di dover aprire un editor —
|
|
551
|
+
* qui non lo apre mai, ma il comportamento non deve dipendere da una
|
|
552
|
+
* deduzione di git (git-commit(1), letto il 05/09/2026).
|
|
553
|
+
*/
|
|
554
|
+
async commit({ sessionId, percorsi, messaggio } = {}) {
|
|
555
|
+
let cartellaMessaggio = null;
|
|
556
|
+
try {
|
|
557
|
+
const cartella = cartellaDi(sessionId);
|
|
558
|
+
const puliti = normalizzaElenco(cartella, percorsi);
|
|
559
|
+
if (typeof messaggio !== 'string' || messaggio.trim() === '') {
|
|
560
|
+
throw new GitServiceError('Il commit vuole un messaggio', 'GIT_MESSAGE_REQUIRED');
|
|
561
|
+
}
|
|
562
|
+
if (Buffer.byteLength(messaggio, 'utf8') > TETTO_MESSAGGIO_BYTE) {
|
|
563
|
+
throw new GitServiceError('Messaggio di commit troppo lungo', 'GIT_MESSAGE_REQUIRED');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const prima = await statoInterno(cartella);
|
|
567
|
+
const perPercorso = new Map(prima.voci.map((v) => [v.percorso, v]));
|
|
568
|
+
/* ⛔ Un percorso che git non ha niente da committare: lo diciamo NOI, invece di lasciare a git un «pathspec did not match any file(s)» che nessuno sa leggere. */
|
|
569
|
+
const senzaNiente = puliti.filter((p) => !perPercorso.has(p));
|
|
570
|
+
if (senzaNiente.length > 0) {
|
|
571
|
+
throw new GitServiceError(`Niente da committare per: ${senzaNiente.join(', ')}`, 'GIT_NOTHING_TO_COMMIT');
|
|
572
|
+
}
|
|
573
|
+
/* ⛔⛔ La guardia della ricerca 4: staged E modificato dopo ⇒ il commit prenderebbe la versione nuova, in silenzio. */
|
|
574
|
+
const divergenti = puliti.filter((p) => {
|
|
575
|
+
const v = perPercorso.get(p);
|
|
576
|
+
return v && v.staged && v.nonStaged;
|
|
577
|
+
});
|
|
578
|
+
if (divergenti.length > 0) {
|
|
579
|
+
throw new GitServiceError(
|
|
580
|
+
`Questi file sono cambiati dopo essere stati messi in stage, e il commit prenderebbe la versione nuova: ${divergenti.join(', ')}. Rimettili in stage e riprova.`,
|
|
581
|
+
'GIT_WORKTREE_DIFFERS',
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
const conflitti = puliti.filter((p) => perPercorso.get(p)?.conflitto);
|
|
585
|
+
if (conflitti.length > 0) {
|
|
586
|
+
throw new GitServiceError(`Prima vanno risolti i conflitti su: ${conflitti.join(', ')}`, 'GIT_NOTHING_TO_COMMIT');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
cartellaMessaggio = await mkdtemp(join(tmpdir(), 'talos-git-msg-'));
|
|
590
|
+
const fileMessaggio = join(cartellaMessaggio, 'messaggio.txt');
|
|
591
|
+
await writeFile(fileMessaggio, messaggio, 'utf8');
|
|
592
|
+
await git(
|
|
593
|
+
cartella,
|
|
594
|
+
['commit', '--cleanup=whitespace', '-F', fileMessaggio, '--', ...puliti],
|
|
595
|
+
{ timeoutMs: timeoutScritturaMs },
|
|
596
|
+
);
|
|
597
|
+
const dopo = await statoInterno(cartella);
|
|
598
|
+
const testa = await git(cartella, ['rev-parse', 'HEAD']);
|
|
599
|
+
return { ok: true, percorsi: puliti, commit: testa.stdout.trim(), stato: dopo };
|
|
600
|
+
} catch (errore) {
|
|
601
|
+
return rifiuto(errore);
|
|
602
|
+
} finally {
|
|
603
|
+
if (cartellaMessaggio) await rm(cartellaMessaggio, { recursive: true, force: true }).catch(() => {});
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Ogni porta risponde nella stessa forma del resto del server
|
|
611
|
+
* (`{erroreAvvio, code}`, come `terminal-registry.mjs`), così `http-app.mjs`
|
|
612
|
+
* la traduce senza sapere niente di git.
|
|
613
|
+
* ⛔ Un errore che NON è nostro non diventa un codice inventato: resta
|
|
614
|
+
* `GIT_COMMAND_FAILED`, che http-app mappa a 500 — mai un 200 con un esito
|
|
615
|
+
* finto.
|
|
616
|
+
*/
|
|
617
|
+
function rifiuto(errore) {
|
|
618
|
+
if (errore instanceof GitServiceError) return { erroreAvvio: errore.message, code: errore.code };
|
|
619
|
+
if (typeof errore?.code === 'string' && errore.code.startsWith('PROCESS_POLICY')) {
|
|
620
|
+
return { erroreAvvio: 'Comando git rifiutato dalla policy di processo', code: 'GIT_COMMAND_FAILED' };
|
|
621
|
+
}
|
|
622
|
+
if (typeof errore?.code === 'string' && (errore.code === 'CWD_NOT_ALLOWED' || errore.code === 'CWD_REQUIRED' || errore.code === 'CWD_NOT_FOUND' || errore.code === 'EXECUTABLE_NOT_ALLOWED')) {
|
|
623
|
+
return { erroreAvvio: 'Cartella di lavoro non autorizzata per git', code: 'GIT_COMMAND_FAILED' };
|
|
624
|
+
}
|
|
625
|
+
return { erroreAvvio: errore?.message || 'git non è riuscito', code: 'GIT_COMMAND_FAILED' };
|
|
626
|
+
}
|