looply-cli 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +137 -3
- package/dist/.src-hash +1 -0
- package/dist/acceptance-observe.d.ts +108 -0
- package/dist/acceptance-observe.d.ts.map +1 -0
- package/dist/acceptance-observe.js +152 -0
- package/dist/acceptance-observe.js.map +1 -0
- package/dist/agent-cmd.d.ts +131 -0
- package/dist/agent-cmd.d.ts.map +1 -0
- package/dist/agent-cmd.js +154 -0
- package/dist/agent-cmd.js.map +1 -0
- package/dist/agent-contained.d.ts +114 -0
- package/dist/agent-contained.d.ts.map +1 -0
- package/dist/agent-contained.js +292 -0
- package/dist/agent-contained.js.map +1 -0
- package/dist/agent-driver.d.ts +130 -0
- package/dist/agent-driver.d.ts.map +1 -0
- package/dist/agent-driver.js +170 -0
- package/dist/agent-driver.js.map +1 -0
- package/dist/agent-envelope.d.ts +94 -0
- package/dist/agent-envelope.d.ts.map +1 -0
- package/dist/agent-envelope.js +214 -0
- package/dist/agent-envelope.js.map +1 -0
- package/dist/agent-exec.d.ts +182 -0
- package/dist/agent-exec.d.ts.map +1 -0
- package/dist/agent-exec.js +125 -0
- package/dist/agent-exec.js.map +1 -0
- package/dist/agent-runtime.d.ts +23 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/agent-runtime.js +143 -0
- package/dist/agent-runtime.js.map +1 -0
- package/dist/approval-gate-read.d.ts +66 -0
- package/dist/approval-gate-read.d.ts.map +1 -0
- package/dist/approval-gate-read.js +138 -0
- package/dist/approval-gate-read.js.map +1 -0
- package/dist/approval-resolution.d.ts +87 -0
- package/dist/approval-resolution.d.ts.map +1 -0
- package/dist/approval-resolution.js +100 -0
- package/dist/approval-resolution.js.map +1 -0
- package/dist/approval-resumes.d.ts +102 -0
- package/dist/approval-resumes.d.ts.map +1 -0
- package/dist/approval-resumes.js +167 -0
- package/dist/approval-resumes.js.map +1 -0
- package/dist/ask-brain.d.ts +167 -0
- package/dist/ask-brain.d.ts.map +1 -0
- package/dist/ask-brain.js +355 -0
- package/dist/ask-brain.js.map +1 -0
- package/dist/autonomy-authority.d.ts +80 -0
- package/dist/autonomy-authority.d.ts.map +1 -0
- package/dist/autonomy-authority.js +82 -0
- package/dist/autonomy-authority.js.map +1 -0
- package/dist/autonomy-controller.d.ts +321 -0
- package/dist/autonomy-controller.d.ts.map +1 -0
- package/dist/autonomy-controller.js +517 -0
- package/dist/autonomy-controller.js.map +1 -0
- package/dist/autonomy-store.d.ts +327 -0
- package/dist/autonomy-store.d.ts.map +1 -0
- package/dist/autonomy-store.js +581 -0
- package/dist/autonomy-store.js.map +1 -0
- package/dist/autonomy-tick.d.ts +257 -0
- package/dist/autonomy-tick.d.ts.map +1 -0
- package/dist/autonomy-tick.js +616 -0
- package/dist/autonomy-tick.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +2719 -0
- package/dist/bin.js.map +1 -0
- package/dist/brain-advice.d.ts +21 -0
- package/dist/brain-advice.d.ts.map +1 -0
- package/dist/brain-advice.js +51 -0
- package/dist/brain-advice.js.map +1 -0
- package/dist/candidate-replay-runtime.d.ts +125 -0
- package/dist/candidate-replay-runtime.d.ts.map +1 -0
- package/dist/candidate-replay-runtime.js +188 -0
- package/dist/candidate-replay-runtime.js.map +1 -0
- package/dist/checkout-carry.d.ts +76 -0
- package/dist/checkout-carry.d.ts.map +1 -0
- package/dist/checkout-carry.js +125 -0
- package/dist/checkout-carry.js.map +1 -0
- package/dist/citation-readers.d.ts +26 -0
- package/dist/citation-readers.d.ts.map +1 -0
- package/dist/citation-readers.js +102 -0
- package/dist/citation-readers.js.map +1 -0
- package/dist/command-receipts.d.ts +79 -0
- package/dist/command-receipts.d.ts.map +1 -0
- package/dist/command-receipts.js +143 -0
- package/dist/command-receipts.js.map +1 -0
- package/dist/command-runner.d.ts +32 -0
- package/dist/command-runner.d.ts.map +1 -0
- package/dist/command-runner.js +108 -0
- package/dist/command-runner.js.map +1 -0
- package/dist/compatible-sets.d.ts +67 -0
- package/dist/compatible-sets.d.ts.map +1 -0
- package/dist/compatible-sets.js +81 -0
- package/dist/compatible-sets.js.map +1 -0
- package/dist/compile-check.d.ts +31 -0
- package/dist/compile-check.d.ts.map +1 -0
- package/dist/compile-check.js +34 -0
- package/dist/compile-check.js.map +1 -0
- package/dist/compile-store.d.ts +16 -0
- package/dist/compile-store.d.ts.map +1 -0
- package/dist/compile-store.js +21 -0
- package/dist/compile-store.js.map +1 -0
- package/dist/completion.d.ts +106 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +131 -0
- package/dist/completion.js.map +1 -0
- package/dist/config.d.ts +549 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +763 -0
- package/dist/config.js.map +1 -0
- package/dist/control-file.d.ts +142 -0
- package/dist/control-file.d.ts.map +1 -0
- package/dist/control-file.js +144 -0
- package/dist/control-file.js.map +1 -0
- package/dist/coordinator-state.d.ts +131 -0
- package/dist/coordinator-state.d.ts.map +1 -0
- package/dist/coordinator-state.js +380 -0
- package/dist/coordinator-state.js.map +1 -0
- package/dist/coordinator.d.ts +95 -0
- package/dist/coordinator.d.ts.map +1 -0
- package/dist/coordinator.js +97 -0
- package/dist/coordinator.js.map +1 -0
- package/dist/cursor-store.d.ts +53 -0
- package/dist/cursor-store.d.ts.map +1 -0
- package/dist/cursor-store.js +98 -0
- package/dist/cursor-store.js.map +1 -0
- package/dist/daemon-cmd.d.ts +239 -0
- package/dist/daemon-cmd.d.ts.map +1 -0
- package/dist/daemon-cmd.js +1751 -0
- package/dist/daemon-cmd.js.map +1 -0
- package/dist/daemon-lock.d.ts +140 -0
- package/dist/daemon-lock.d.ts.map +1 -0
- package/dist/daemon-lock.js +352 -0
- package/dist/daemon-lock.js.map +1 -0
- package/dist/daemon-recovery.d.ts +78 -0
- package/dist/daemon-recovery.d.ts.map +1 -0
- package/dist/daemon-recovery.js +161 -0
- package/dist/daemon-recovery.js.map +1 -0
- package/dist/daemon.d.ts +368 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +601 -0
- package/dist/daemon.js.map +1 -0
- package/dist/declared-response-shape.d.ts +93 -0
- package/dist/declared-response-shape.d.ts.map +1 -0
- package/dist/declared-response-shape.js +200 -0
- package/dist/declared-response-shape.js.map +1 -0
- package/dist/declared-surfaces.d.ts +16 -0
- package/dist/declared-surfaces.d.ts.map +1 -0
- package/dist/declared-surfaces.js +19 -0
- package/dist/declared-surfaces.js.map +1 -0
- package/dist/depends-review.d.ts +113 -0
- package/dist/depends-review.d.ts.map +1 -0
- package/dist/depends-review.js +110 -0
- package/dist/depends-review.js.map +1 -0
- package/dist/doctor.d.ts +91 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +71 -0
- package/dist/doctor.js.map +1 -0
- package/dist/edge-approval.d.ts +105 -0
- package/dist/edge-approval.d.ts.map +1 -0
- package/dist/edge-approval.js +100 -0
- package/dist/edge-approval.js.map +1 -0
- package/dist/evidence-cache.d.ts +45 -0
- package/dist/evidence-cache.d.ts.map +1 -0
- package/dist/evidence-cache.js +90 -0
- package/dist/evidence-cache.js.map +1 -0
- package/dist/evidence-cmd.d.ts +133 -0
- package/dist/evidence-cmd.d.ts.map +1 -0
- package/dist/evidence-cmd.js +347 -0
- package/dist/evidence-cmd.js.map +1 -0
- package/dist/evidence-facts.d.ts +40 -0
- package/dist/evidence-facts.d.ts.map +1 -0
- package/dist/evidence-facts.js +114 -0
- package/dist/evidence-facts.js.map +1 -0
- package/dist/exclusive-lock.d.ts +52 -0
- package/dist/exclusive-lock.d.ts.map +1 -0
- package/dist/exclusive-lock.js +150 -0
- package/dist/exclusive-lock.js.map +1 -0
- package/dist/exec-domains.d.ts +140 -0
- package/dist/exec-domains.d.ts.map +1 -0
- package/dist/exec-domains.js +287 -0
- package/dist/exec-domains.js.map +1 -0
- package/dist/exemption-store.d.ts +197 -0
- package/dist/exemption-store.d.ts.map +1 -0
- package/dist/exemption-store.js +266 -0
- package/dist/exemption-store.js.map +1 -0
- package/dist/explicit-start-gate.d.ts +96 -0
- package/dist/explicit-start-gate.d.ts.map +1 -0
- package/dist/explicit-start-gate.js +103 -0
- package/dist/explicit-start-gate.js.map +1 -0
- package/dist/fs-trouble.d.ts +70 -0
- package/dist/fs-trouble.d.ts.map +1 -0
- package/dist/fs-trouble.js +98 -0
- package/dist/fs-trouble.js.map +1 -0
- package/dist/gate.d.ts +278 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +278 -0
- package/dist/gate.js.map +1 -0
- package/dist/git-driver.d.ts +519 -0
- package/dist/git-driver.d.ts.map +1 -0
- package/dist/git-driver.js +900 -0
- package/dist/git-driver.js.map +1 -0
- package/dist/git-push-executor.d.ts +41 -0
- package/dist/git-push-executor.d.ts.map +1 -0
- package/dist/git-push-executor.js +142 -0
- package/dist/git-push-executor.js.map +1 -0
- package/dist/glob.d.ts +26 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +93 -0
- package/dist/glob.js.map +1 -0
- package/dist/goal-adaptive.d.ts +295 -0
- package/dist/goal-adaptive.d.ts.map +1 -0
- package/dist/goal-adaptive.js +393 -0
- package/dist/goal-adaptive.js.map +1 -0
- package/dist/goal-compile.d.ts +342 -0
- package/dist/goal-compile.d.ts.map +1 -0
- package/dist/goal-compile.js +444 -0
- package/dist/goal-compile.js.map +1 -0
- package/dist/goal-depends-executor.d.ts +55 -0
- package/dist/goal-depends-executor.d.ts.map +1 -0
- package/dist/goal-depends-executor.js +202 -0
- package/dist/goal-depends-executor.js.map +1 -0
- package/dist/goal-execution-evidence.d.ts +135 -0
- package/dist/goal-execution-evidence.d.ts.map +1 -0
- package/dist/goal-execution-evidence.js +159 -0
- package/dist/goal-execution-evidence.js.map +1 -0
- package/dist/goal-gap.d.ts +140 -0
- package/dist/goal-gap.d.ts.map +1 -0
- package/dist/goal-gap.js +197 -0
- package/dist/goal-gap.js.map +1 -0
- package/dist/goal-met-target.d.ts +98 -0
- package/dist/goal-met-target.d.ts.map +1 -0
- package/dist/goal-met-target.js +92 -0
- package/dist/goal-met-target.js.map +1 -0
- package/dist/goal-ownership.d.ts +416 -0
- package/dist/goal-ownership.d.ts.map +1 -0
- package/dist/goal-ownership.js +576 -0
- package/dist/goal-ownership.js.map +1 -0
- package/dist/goal-runs.d.ts +82 -0
- package/dist/goal-runs.d.ts.map +1 -0
- package/dist/goal-runs.js +120 -0
- package/dist/goal-runs.js.map +1 -0
- package/dist/goal-store.d.ts +146 -0
- package/dist/goal-store.d.ts.map +1 -0
- package/dist/goal-store.js +294 -0
- package/dist/goal-store.js.map +1 -0
- package/dist/goal-view.d.ts +156 -0
- package/dist/goal-view.d.ts.map +1 -0
- package/dist/goal-view.js +499 -0
- package/dist/goal-view.js.map +1 -0
- package/dist/governed-approvals.d.ts +152 -0
- package/dist/governed-approvals.d.ts.map +1 -0
- package/dist/governed-approvals.js +246 -0
- package/dist/governed-approvals.js.map +1 -0
- package/dist/governed-broker.d.ts +169 -0
- package/dist/governed-broker.d.ts.map +1 -0
- package/dist/governed-broker.js +196 -0
- package/dist/governed-broker.js.map +1 -0
- package/dist/governed-run.d.ts +220 -0
- package/dist/governed-run.d.ts.map +1 -0
- package/dist/governed-run.js +406 -0
- package/dist/governed-run.js.map +1 -0
- package/dist/grant-reconcile.d.ts +63 -0
- package/dist/grant-reconcile.d.ts.map +1 -0
- package/dist/grant-reconcile.js +121 -0
- package/dist/grant-reconcile.js.map +1 -0
- package/dist/index-for-head.d.ts +56 -0
- package/dist/index-for-head.d.ts.map +1 -0
- package/dist/index-for-head.js +103 -0
- package/dist/index-for-head.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/init-detect.d.ts +39 -0
- package/dist/init-detect.d.ts.map +1 -0
- package/dist/init-detect.js +188 -0
- package/dist/init-detect.js.map +1 -0
- package/dist/integration-deps.d.ts +62 -0
- package/dist/integration-deps.d.ts.map +1 -0
- package/dist/integration-deps.js +251 -0
- package/dist/integration-deps.js.map +1 -0
- package/dist/integration-runtime.d.ts +458 -0
- package/dist/integration-runtime.d.ts.map +1 -0
- package/dist/integration-runtime.js +681 -0
- package/dist/integration-runtime.js.map +1 -0
- package/dist/integration.d.ts +45 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +87 -0
- package/dist/integration.js.map +1 -0
- package/dist/integrator.d.ts +84 -0
- package/dist/integrator.d.ts.map +1 -0
- package/dist/integrator.js +193 -0
- package/dist/integrator.js.map +1 -0
- package/dist/lane-claims.d.ts +174 -0
- package/dist/lane-claims.d.ts.map +1 -0
- package/dist/lane-claims.js +218 -0
- package/dist/lane-claims.js.map +1 -0
- package/dist/lane-coverage.d.ts +87 -0
- package/dist/lane-coverage.d.ts.map +1 -0
- package/dist/lane-coverage.js +129 -0
- package/dist/lane-coverage.js.map +1 -0
- package/dist/ledger-replay.d.ts +115 -0
- package/dist/ledger-replay.d.ts.map +1 -0
- package/dist/ledger-replay.js +155 -0
- package/dist/ledger-replay.js.map +1 -0
- package/dist/ledger-store.d.ts +68 -0
- package/dist/ledger-store.d.ts.map +1 -0
- package/dist/ledger-store.js +111 -0
- package/dist/ledger-store.js.map +1 -0
- package/dist/loop-safety-observer.d.ts +132 -0
- package/dist/loop-safety-observer.d.ts.map +1 -0
- package/dist/loop-safety-observer.js +76 -0
- package/dist/loop-safety-observer.js.map +1 -0
- package/dist/loop-safety-store.d.ts +339 -0
- package/dist/loop-safety-store.d.ts.map +1 -0
- package/dist/loop-safety-store.js +800 -0
- package/dist/loop-safety-store.js.map +1 -0
- package/dist/machine-store.d.ts +54 -0
- package/dist/machine-store.d.ts.map +1 -0
- package/dist/machine-store.js +149 -0
- package/dist/machine-store.js.map +1 -0
- package/dist/mcp-main.d.ts +2 -0
- package/dist/mcp-main.d.ts.map +1 -0
- package/dist/mcp-main.js +116 -0
- package/dist/mcp-main.js.map +1 -0
- package/dist/mcp-server.d.ts +72 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +259 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/model-routing.d.ts +75 -0
- package/dist/model-routing.d.ts.map +1 -0
- package/dist/model-routing.js +108 -0
- package/dist/model-routing.js.map +1 -0
- package/dist/model-transport.d.ts +188 -0
- package/dist/model-transport.d.ts.map +1 -0
- package/dist/model-transport.js +625 -0
- package/dist/model-transport.js.map +1 -0
- package/dist/notify-cadence.d.ts +55 -0
- package/dist/notify-cadence.d.ts.map +1 -0
- package/dist/notify-cadence.js +68 -0
- package/dist/notify-cadence.js.map +1 -0
- package/dist/notify-channel.d.ts +69 -0
- package/dist/notify-channel.d.ts.map +1 -0
- package/dist/notify-channel.js +151 -0
- package/dist/notify-channel.js.map +1 -0
- package/dist/notify.d.ts +71 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +125 -0
- package/dist/notify.js.map +1 -0
- package/dist/observation-inventory.d.ts +65 -0
- package/dist/observation-inventory.d.ts.map +1 -0
- package/dist/observation-inventory.js +139 -0
- package/dist/observation-inventory.js.map +1 -0
- package/dist/owned-children.d.ts +45 -0
- package/dist/owned-children.d.ts.map +1 -0
- package/dist/owned-children.js +64 -0
- package/dist/owned-children.js.map +1 -0
- package/dist/packet-for-goal.d.ts +19 -0
- package/dist/packet-for-goal.d.ts.map +1 -0
- package/dist/packet-for-goal.js +32 -0
- package/dist/packet-for-goal.js.map +1 -0
- package/dist/pair-cmd.d.ts +85 -0
- package/dist/pair-cmd.d.ts.map +1 -0
- package/dist/pair-cmd.js +205 -0
- package/dist/pair-cmd.js.map +1 -0
- package/dist/partial-asker.d.ts +96 -0
- package/dist/partial-asker.d.ts.map +1 -0
- package/dist/partial-asker.js +117 -0
- package/dist/partial-asker.js.map +1 -0
- package/dist/pdf-text-lines.d.ts +17 -0
- package/dist/pdf-text-lines.d.ts.map +1 -0
- package/dist/pdf-text-lines.js +70 -0
- package/dist/pdf-text-lines.js.map +1 -0
- package/dist/pdf-text-worker.d.ts +2 -0
- package/dist/pdf-text-worker.d.ts.map +1 -0
- package/dist/pdf-text-worker.js +81 -0
- package/dist/pdf-text-worker.js.map +1 -0
- package/dist/pdf-text.d.ts +56 -0
- package/dist/pdf-text.d.ts.map +1 -0
- package/dist/pdf-text.js +64 -0
- package/dist/pdf-text.js.map +1 -0
- package/dist/pending-action.d.ts +154 -0
- package/dist/pending-action.d.ts.map +1 -0
- package/dist/pending-action.js +148 -0
- package/dist/pending-action.js.map +1 -0
- package/dist/preemptor.d.ts +40 -0
- package/dist/preemptor.d.ts.map +1 -0
- package/dist/preemptor.js +49 -0
- package/dist/preemptor.js.map +1 -0
- package/dist/programme-mode.d.ts +29 -0
- package/dist/programme-mode.d.ts.map +1 -0
- package/dist/programme-mode.js +50 -0
- package/dist/programme-mode.js.map +1 -0
- package/dist/progress-view.d.ts +14 -0
- package/dist/progress-view.d.ts.map +1 -0
- package/dist/progress-view.js +114 -0
- package/dist/progress-view.js.map +1 -0
- package/dist/project-graph-builder.d.ts +81 -0
- package/dist/project-graph-builder.d.ts.map +1 -0
- package/dist/project-graph-builder.js +246 -0
- package/dist/project-graph-builder.js.map +1 -0
- package/dist/project-graph-runtime.d.ts +86 -0
- package/dist/project-graph-runtime.d.ts.map +1 -0
- package/dist/project-graph-runtime.js +112 -0
- package/dist/project-graph-runtime.js.map +1 -0
- package/dist/project-index-builder.d.ts +96 -0
- package/dist/project-index-builder.d.ts.map +1 -0
- package/dist/project-index-builder.js +298 -0
- package/dist/project-index-builder.js.map +1 -0
- package/dist/project-index-facts.d.ts +51 -0
- package/dist/project-index-facts.d.ts.map +1 -0
- package/dist/project-index-facts.js +117 -0
- package/dist/project-index-facts.js.map +1 -0
- package/dist/project-index-store.d.ts +97 -0
- package/dist/project-index-store.d.ts.map +1 -0
- package/dist/project-index-store.js +140 -0
- package/dist/project-index-store.js.map +1 -0
- package/dist/project-memory-store.d.ts +127 -0
- package/dist/project-memory-store.d.ts.map +1 -0
- package/dist/project-memory-store.js +302 -0
- package/dist/project-memory-store.js.map +1 -0
- package/dist/proposal-store.d.ts +14 -0
- package/dist/proposal-store.d.ts.map +1 -0
- package/dist/proposal-store.js +19 -0
- package/dist/proposal-store.js.map +1 -0
- package/dist/prover.d.ts +80 -0
- package/dist/prover.d.ts.map +1 -0
- package/dist/prover.js +137 -0
- package/dist/prover.js.map +1 -0
- package/dist/provider-identity-store.d.ts +34 -0
- package/dist/provider-identity-store.d.ts.map +1 -0
- package/dist/provider-identity-store.js +72 -0
- package/dist/provider-identity-store.js.map +1 -0
- package/dist/provider-identity.d.ts +104 -0
- package/dist/provider-identity.d.ts.map +1 -0
- package/dist/provider-identity.js +167 -0
- package/dist/provider-identity.js.map +1 -0
- package/dist/provider-limit.d.ts +79 -0
- package/dist/provider-limit.d.ts.map +1 -0
- package/dist/provider-limit.js +148 -0
- package/dist/provider-limit.js.map +1 -0
- package/dist/provider-preflight.d.ts +171 -0
- package/dist/provider-preflight.d.ts.map +1 -0
- package/dist/provider-preflight.js +395 -0
- package/dist/provider-preflight.js.map +1 -0
- package/dist/provider-state.d.ts +120 -0
- package/dist/provider-state.d.ts.map +1 -0
- package/dist/provider-state.js +182 -0
- package/dist/provider-state.js.map +1 -0
- package/dist/providers.d.ts +132 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +161 -0
- package/dist/providers.js.map +1 -0
- package/dist/publication-recorder.d.ts +117 -0
- package/dist/publication-recorder.d.ts.map +1 -0
- package/dist/publication-recorder.js +124 -0
- package/dist/publication-recorder.js.map +1 -0
- package/dist/pursuit-delivery.d.ts +24 -0
- package/dist/pursuit-delivery.d.ts.map +1 -0
- package/dist/pursuit-delivery.js +38 -0
- package/dist/pursuit-delivery.js.map +1 -0
- package/dist/queue-store.d.ts +132 -0
- package/dist/queue-store.d.ts.map +1 -0
- package/dist/queue-store.js +185 -0
- package/dist/queue-store.js.map +1 -0
- package/dist/queue-summary.d.ts +14 -0
- package/dist/queue-summary.d.ts.map +1 -0
- package/dist/queue-summary.js +82 -0
- package/dist/queue-summary.js.map +1 -0
- package/dist/queue-view.d.ts +20 -0
- package/dist/queue-view.d.ts.map +1 -0
- package/dist/queue-view.js +163 -0
- package/dist/queue-view.js.map +1 -0
- package/dist/queue-write-lease.d.ts +106 -0
- package/dist/queue-write-lease.d.ts.map +1 -0
- package/dist/queue-write-lease.js +282 -0
- package/dist/queue-write-lease.js.map +1 -0
- package/dist/rails-home.d.ts +162 -0
- package/dist/rails-home.d.ts.map +1 -0
- package/dist/rails-home.js +452 -0
- package/dist/rails-home.js.map +1 -0
- package/dist/rails-migrations.d.ts +75 -0
- package/dist/rails-migrations.d.ts.map +1 -0
- package/dist/rails-migrations.js +199 -0
- package/dist/rails-migrations.js.map +1 -0
- package/dist/rails-stores.d.ts +49 -0
- package/dist/rails-stores.d.ts.map +1 -0
- package/dist/rails-stores.js +100 -0
- package/dist/rails-stores.js.map +1 -0
- package/dist/reaper.d.ts +69 -0
- package/dist/reaper.d.ts.map +1 -0
- package/dist/reaper.js +152 -0
- package/dist/reaper.js.map +1 -0
- package/dist/record-store.d.ts +36 -0
- package/dist/record-store.d.ts.map +1 -0
- package/dist/record-store.js +78 -0
- package/dist/record-store.js.map +1 -0
- package/dist/recovery-io.d.ts +50 -0
- package/dist/recovery-io.d.ts.map +1 -0
- package/dist/recovery-io.js +122 -0
- package/dist/recovery-io.js.map +1 -0
- package/dist/recovery.d.ts +146 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +110 -0
- package/dist/recovery.js.map +1 -0
- package/dist/redraw.d.ts +34 -0
- package/dist/redraw.d.ts.map +1 -0
- package/dist/redraw.js +62 -0
- package/dist/redraw.js.map +1 -0
- package/dist/rejected-evidence.d.ts +255 -0
- package/dist/rejected-evidence.d.ts.map +1 -0
- package/dist/rejected-evidence.js +324 -0
- package/dist/rejected-evidence.js.map +1 -0
- package/dist/report-cmd.d.ts +32 -0
- package/dist/report-cmd.d.ts.map +1 -0
- package/dist/report-cmd.js +201 -0
- package/dist/report-cmd.js.map +1 -0
- package/dist/repos-cmd.d.ts +80 -0
- package/dist/repos-cmd.d.ts.map +1 -0
- package/dist/repos-cmd.js +240 -0
- package/dist/repos-cmd.js.map +1 -0
- package/dist/request-body-shape.d.ts +93 -0
- package/dist/request-body-shape.d.ts.map +1 -0
- package/dist/request-body-shape.js +407 -0
- package/dist/request-body-shape.js.map +1 -0
- package/dist/requirement-claims.d.ts +89 -0
- package/dist/requirement-claims.d.ts.map +1 -0
- package/dist/requirement-claims.js +92 -0
- package/dist/requirement-claims.js.map +1 -0
- package/dist/retention-apply-runtime.d.ts +105 -0
- package/dist/retention-apply-runtime.d.ts.map +1 -0
- package/dist/retention-apply-runtime.js +269 -0
- package/dist/retention-apply-runtime.js.map +1 -0
- package/dist/reverify.d.ts +41 -0
- package/dist/reverify.d.ts.map +1 -0
- package/dist/reverify.js +69 -0
- package/dist/reverify.js.map +1 -0
- package/dist/round-records.d.ts +65 -0
- package/dist/round-records.d.ts.map +1 -0
- package/dist/round-records.js +61 -0
- package/dist/round-records.js.map +1 -0
- package/dist/round-runner.d.ts +924 -0
- package/dist/round-runner.d.ts.map +1 -0
- package/dist/round-runner.js +1693 -0
- package/dist/round-runner.js.map +1 -0
- package/dist/run-approval.d.ts +180 -0
- package/dist/run-approval.d.ts.map +1 -0
- package/dist/run-approval.js +199 -0
- package/dist/run-approval.js.map +1 -0
- package/dist/run-cmd.d.ts +359 -0
- package/dist/run-cmd.d.ts.map +1 -0
- package/dist/run-cmd.js +4418 -0
- package/dist/run-cmd.js.map +1 -0
- package/dist/run-delivery.d.ts +71 -0
- package/dist/run-delivery.d.ts.map +1 -0
- package/dist/run-delivery.js +104 -0
- package/dist/run-delivery.js.map +1 -0
- package/dist/run-ended.d.ts +75 -0
- package/dist/run-ended.d.ts.map +1 -0
- package/dist/run-ended.js +27 -0
- package/dist/run-ended.js.map +1 -0
- package/dist/run-ending-locator.d.ts +125 -0
- package/dist/run-ending-locator.d.ts.map +1 -0
- package/dist/run-ending-locator.js +133 -0
- package/dist/run-ending-locator.js.map +1 -0
- package/dist/run-launcher.d.ts +459 -0
- package/dist/run-launcher.d.ts.map +1 -0
- package/dist/run-launcher.js +652 -0
- package/dist/run-launcher.js.map +1 -0
- package/dist/run-lease.d.ts +208 -0
- package/dist/run-lease.d.ts.map +1 -0
- package/dist/run-lease.js +301 -0
- package/dist/run-lease.js.map +1 -0
- package/dist/run-refusal-marks.d.ts +189 -0
- package/dist/run-refusal-marks.d.ts.map +1 -0
- package/dist/run-refusal-marks.js +249 -0
- package/dist/run-refusal-marks.js.map +1 -0
- package/dist/run-start-marks.d.ts +153 -0
- package/dist/run-start-marks.d.ts.map +1 -0
- package/dist/run-start-marks.js +269 -0
- package/dist/run-start-marks.js.map +1 -0
- package/dist/scan-cmd.d.ts +14 -0
- package/dist/scan-cmd.d.ts.map +1 -0
- package/dist/scan-cmd.js +270 -0
- package/dist/scan-cmd.js.map +1 -0
- package/dist/scan-events.d.ts +50 -0
- package/dist/scan-events.d.ts.map +1 -0
- package/dist/scan-events.js +91 -0
- package/dist/scan-events.js.map +1 -0
- package/dist/scan.d.ts +191 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +417 -0
- package/dist/scan.js.map +1 -0
- package/dist/scope-rule.d.ts +148 -0
- package/dist/scope-rule.d.ts.map +1 -0
- package/dist/scope-rule.js +121 -0
- package/dist/scope-rule.js.map +1 -0
- package/dist/self-repo-id.d.ts +18 -0
- package/dist/self-repo-id.d.ts.map +1 -0
- package/dist/self-repo-id.js +70 -0
- package/dist/self-repo-id.js.map +1 -0
- package/dist/server-client.d.ts +766 -0
- package/dist/server-client.d.ts.map +1 -0
- package/dist/server-client.js +475 -0
- package/dist/server-client.js.map +1 -0
- package/dist/session-adapter-runtime.d.ts +195 -0
- package/dist/session-adapter-runtime.d.ts.map +1 -0
- package/dist/session-adapter-runtime.js +373 -0
- package/dist/session-adapter-runtime.js.map +1 -0
- package/dist/session-checkpoint-runtime.d.ts +62 -0
- package/dist/session-checkpoint-runtime.d.ts.map +1 -0
- package/dist/session-checkpoint-runtime.js +268 -0
- package/dist/session-checkpoint-runtime.js.map +1 -0
- package/dist/session-control-transport.d.ts +106 -0
- package/dist/session-control-transport.d.ts.map +1 -0
- package/dist/session-control-transport.js +232 -0
- package/dist/session-control-transport.js.map +1 -0
- package/dist/session-observation-store.d.ts +98 -0
- package/dist/session-observation-store.d.ts.map +1 -0
- package/dist/session-observation-store.js +155 -0
- package/dist/session-observation-store.js.map +1 -0
- package/dist/session-observe-runtime.d.ts +123 -0
- package/dist/session-observe-runtime.d.ts.map +1 -0
- package/dist/session-observe-runtime.js +317 -0
- package/dist/session-observe-runtime.js.map +1 -0
- package/dist/session-store.d.ts +98 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +116 -0
- package/dist/session-store.js.map +1 -0
- package/dist/sessions-cmd.d.ts +202 -0
- package/dist/sessions-cmd.d.ts.map +1 -0
- package/dist/sessions-cmd.js +609 -0
- package/dist/sessions-cmd.js.map +1 -0
- package/dist/settings-overlay.d.ts +53 -0
- package/dist/settings-overlay.d.ts.map +1 -0
- package/dist/settings-overlay.js +175 -0
- package/dist/settings-overlay.js.map +1 -0
- package/dist/setup-prompt.d.ts +34 -0
- package/dist/setup-prompt.d.ts.map +1 -0
- package/dist/setup-prompt.js +92 -0
- package/dist/setup-prompt.js.map +1 -0
- package/dist/setup.d.ts +103 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +283 -0
- package/dist/setup.js.map +1 -0
- package/dist/shared-grants.d.ts +164 -0
- package/dist/shared-grants.d.ts.map +1 -0
- package/dist/shared-grants.js +242 -0
- package/dist/shared-grants.js.map +1 -0
- package/dist/signals.d.ts +55 -0
- package/dist/signals.d.ts.map +1 -0
- package/dist/signals.js +85 -0
- package/dist/signals.js.map +1 -0
- package/dist/source-cmd.d.ts +201 -0
- package/dist/source-cmd.d.ts.map +1 -0
- package/dist/source-cmd.js +684 -0
- package/dist/source-cmd.js.map +1 -0
- package/dist/source-verdict.d.ts +99 -0
- package/dist/source-verdict.d.ts.map +1 -0
- package/dist/source-verdict.js +182 -0
- package/dist/source-verdict.js.map +1 -0
- package/dist/spend-ledger.d.ts +350 -0
- package/dist/spend-ledger.d.ts.map +1 -0
- package/dist/spend-ledger.js +546 -0
- package/dist/spend-ledger.js.map +1 -0
- package/dist/stale-run-sweep.d.ts +79 -0
- package/dist/stale-run-sweep.d.ts.map +1 -0
- package/dist/stale-run-sweep.js +116 -0
- package/dist/stale-run-sweep.js.map +1 -0
- package/dist/start-attempts.d.ts +385 -0
- package/dist/start-attempts.d.ts.map +1 -0
- package/dist/start-attempts.js +613 -0
- package/dist/start-attempts.js.map +1 -0
- package/dist/status-cmd.d.ts +28 -0
- package/dist/status-cmd.d.ts.map +1 -0
- package/dist/status-cmd.js +116 -0
- package/dist/status-cmd.js.map +1 -0
- package/dist/stop-follow-up.d.ts +107 -0
- package/dist/stop-follow-up.d.ts.map +1 -0
- package/dist/stop-follow-up.js +172 -0
- package/dist/stop-follow-up.js.map +1 -0
- package/dist/surface-lease.d.ts +118 -0
- package/dist/surface-lease.d.ts.map +1 -0
- package/dist/surface-lease.js +485 -0
- package/dist/surface-lease.js.map +1 -0
- package/dist/sync.d.ts +54 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +93 -0
- package/dist/sync.js.map +1 -0
- package/dist/syncing-ledger.d.ts +45 -0
- package/dist/syncing-ledger.d.ts.map +1 -0
- package/dist/syncing-ledger.js +53 -0
- package/dist/syncing-ledger.js.map +1 -0
- package/dist/temp-fixtures.d.ts +68 -0
- package/dist/temp-fixtures.d.ts.map +1 -0
- package/dist/temp-fixtures.js +38 -0
- package/dist/temp-fixtures.js.map +1 -0
- package/dist/token-window.d.ts +84 -0
- package/dist/token-window.d.ts.map +1 -0
- package/dist/token-window.js +143 -0
- package/dist/token-window.js.map +1 -0
- package/dist/tool-bridge.d.ts +177 -0
- package/dist/tool-bridge.d.ts.map +1 -0
- package/dist/tool-bridge.js +329 -0
- package/dist/tool-bridge.js.map +1 -0
- package/dist/tool-call-record.d.ts +197 -0
- package/dist/tool-call-record.d.ts.map +1 -0
- package/dist/tool-call-record.js +276 -0
- package/dist/tool-call-record.js.map +1 -0
- package/dist/tool-protocol.d.ts +135 -0
- package/dist/tool-protocol.d.ts.map +1 -0
- package/dist/tool-protocol.js +104 -0
- package/dist/tool-protocol.js.map +1 -0
- package/dist/tool-worker.d.ts +3 -0
- package/dist/tool-worker.d.ts.map +1 -0
- package/dist/tool-worker.js +478 -0
- package/dist/tool-worker.js.map +1 -0
- package/dist/toolchain-mirror.d.ts +86 -0
- package/dist/toolchain-mirror.d.ts.map +1 -0
- package/dist/toolchain-mirror.js +307 -0
- package/dist/toolchain-mirror.js.map +1 -0
- package/dist/trusted-git.d.ts +243 -0
- package/dist/trusted-git.d.ts.map +1 -0
- package/dist/trusted-git.js +920 -0
- package/dist/trusted-git.js.map +1 -0
- package/dist/ui.d.ts +76 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +118 -0
- package/dist/ui.js.map +1 -0
- package/dist/untrusted-exec.d.ts +275 -0
- package/dist/untrusted-exec.d.ts.map +1 -0
- package/dist/untrusted-exec.js +479 -0
- package/dist/untrusted-exec.js.map +1 -0
- package/dist/untrusted-gate.d.ts +73 -0
- package/dist/untrusted-gate.d.ts.map +1 -0
- package/dist/untrusted-gate.js +226 -0
- package/dist/untrusted-gate.js.map +1 -0
- package/dist/uploader.d.ts +153 -0
- package/dist/uploader.d.ts.map +1 -0
- package/dist/uploader.js +204 -0
- package/dist/uploader.js.map +1 -0
- package/dist/verifier.d.ts +66 -0
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +170 -0
- package/dist/verifier.js.map +1 -0
- package/dist/verify-apply.d.ts +26 -0
- package/dist/verify-apply.d.ts.map +1 -0
- package/dist/verify-apply.js +36 -0
- package/dist/verify-apply.js.map +1 -0
- package/dist/verify-cmd.d.ts +44 -0
- package/dist/verify-cmd.d.ts.map +1 -0
- package/dist/verify-cmd.js +125 -0
- package/dist/verify-cmd.js.map +1 -0
- package/dist/verify-pass.d.ts +73 -0
- package/dist/verify-pass.d.ts.map +1 -0
- package/dist/verify-pass.js +103 -0
- package/dist/verify-pass.js.map +1 -0
- package/dist/welcome.d.ts +43 -0
- package/dist/welcome.d.ts.map +1 -0
- package/dist/welcome.js +116 -0
- package/dist/welcome.js.map +1 -0
- package/dist/workspace-provisioner.d.ts +65 -0
- package/dist/workspace-provisioner.d.ts.map +1 -0
- package/dist/workspace-provisioner.js +66 -0
- package/dist/workspace-provisioner.js.map +1 -0
- package/dist/workspace-quarantine.d.ts +64 -0
- package/dist/workspace-quarantine.d.ts.map +1 -0
- package/dist/workspace-quarantine.js +156 -0
- package/dist/workspace-quarantine.js.map +1 -0
- package/dist/workspace-residue-sweep.d.ts +113 -0
- package/dist/workspace-residue-sweep.d.ts.map +1 -0
- package/dist/workspace-residue-sweep.js +158 -0
- package/dist/workspace-residue-sweep.js.map +1 -0
- package/dist/workspace.d.ts +639 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +2026 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +54 -6
- package/win/CredProbe.cs +64 -0
- package/win/FakeClaude.cs +56 -0
- package/win/HandleProbe.cs +116 -0
- package/win/TokenProbe.cs +53 -0
- package/win/appcontainer.ps1 +500 -0
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.repoConfigTrouble = repoConfigTrouble;
|
|
4
|
+
exports.resolveGitLayout = resolveGitLayout;
|
|
5
|
+
exports.repoConfigPaths = repoConfigPaths;
|
|
6
|
+
exports.repoConfigTroubleAt = repoConfigTroubleAt;
|
|
7
|
+
exports.worktreeMetadata = worktreeMetadata;
|
|
8
|
+
exports.repoRootTroubleAt = repoRootTroubleAt;
|
|
9
|
+
exports.noHooksDir = noHooksDir;
|
|
10
|
+
exports.gitBinary = gitBinary;
|
|
11
|
+
exports.trustedGitArgs = trustedGitArgs;
|
|
12
|
+
exports.trustedGitEnv = trustedGitEnv;
|
|
13
|
+
exports.mainWorktreeRoot = mainWorktreeRoot;
|
|
14
|
+
exports.isCheckoutRoot = isCheckoutRoot;
|
|
15
|
+
exports.repoIdentityPath = repoIdentityPath;
|
|
16
|
+
exports.linkedRepoCommonDir = linkedRepoCommonDir;
|
|
17
|
+
const node_fs_1 = require("node:fs");
|
|
18
|
+
const node_path_1 = require("node:path");
|
|
19
|
+
const node_os_1 = require("node:os");
|
|
20
|
+
/**
|
|
21
|
+
* THE REPOSITORY IS UNTRUSTED INPUT TO A TRUSTED PROCESS.
|
|
22
|
+
*
|
|
23
|
+
* `GitDriver` runs as the daemon. The repository it operates on is written by
|
|
24
|
+
* the coding agent, and git executes programs named by configuration — so a
|
|
25
|
+
* round that writes eight lines into `.git/config` gets arbitrary code running
|
|
26
|
+
* under the daemon's identity, with the daemon's environment and whatever
|
|
27
|
+
* credentials are ambient to the account.
|
|
28
|
+
*
|
|
29
|
+
* This is not hypothetical and it is not only hooks. Measured against the exact
|
|
30
|
+
* verbs `GitDriver` issues, EIGHT mechanisms execute:
|
|
31
|
+
*
|
|
32
|
+
* filter.<any>.clean → `add -A`, `add -N`
|
|
33
|
+
* filter.<any>.smudge → `checkout --`, `reset --hard`
|
|
34
|
+
* diff.<any>.textconv → `diff HEAD`
|
|
35
|
+
* diff.external → `diff HEAD`
|
|
36
|
+
* core.fsmonitor → `status`
|
|
37
|
+
* core.hooksPath / hooks → `commit`
|
|
38
|
+
*
|
|
39
|
+
* Only `core.pager` did not fire, and only because stdio is a pipe rather than
|
|
40
|
+
* a terminal — which is a property of how we spawn, not a property of git.
|
|
41
|
+
*
|
|
42
|
+
* TWO MECHANISMS, BECAUSE ONE IS NOT ENOUGH.
|
|
43
|
+
*
|
|
44
|
+
* `core.hooksPath` has a fixed name, so it can simply be overridden on the
|
|
45
|
+
* command line, where it beats anything the repository says. Filters cannot:
|
|
46
|
+
* `filter.<ANY NAME>.clean` is an unbounded family and no list of `-c`
|
|
47
|
+
* overrides can enumerate it. So the second mechanism is refusal — the config
|
|
48
|
+
* is read as a plain file and the operation is declined before git is invoked
|
|
49
|
+
* at all.
|
|
50
|
+
*
|
|
51
|
+
* SCOPED TO THE REPOSITORY'S OWN CONFIG, deliberately. System and global config
|
|
52
|
+
* belong to the person, not to the round: this machine's system config defines
|
|
53
|
+
* `filter.lfs.clean`, and refusing that would break every repository with Git
|
|
54
|
+
* LFS installed while protecting nothing.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Config keys that can name a program for git to run.
|
|
58
|
+
*
|
|
59
|
+
* Patterns, not names — `filter\..*\.clean` covers a filter called anything at
|
|
60
|
+
* all, which is the whole point. Each entry either fired in the audit or names
|
|
61
|
+
* a documented execution path of the same shape.
|
|
62
|
+
*/
|
|
63
|
+
const EXECUTABLE_KEYS = [
|
|
64
|
+
/^filter\..*\.(clean|smudge|process)$/,
|
|
65
|
+
/^diff\..*\.(textconv|command)$/,
|
|
66
|
+
/^diff\.external$/,
|
|
67
|
+
/^merge\..*\.driver$/,
|
|
68
|
+
/^core\.(fsmonitor|hookspath|pager|editor|sshcommand)$/,
|
|
69
|
+
/^sequence\.editor$/,
|
|
70
|
+
/^pager\./,
|
|
71
|
+
/^alias\./,
|
|
72
|
+
// An include pulls in another file wholesale, so it can reintroduce every
|
|
73
|
+
// key above from somewhere this check never looked.
|
|
74
|
+
/^include\.path$/,
|
|
75
|
+
/^includeif\./,
|
|
76
|
+
// `ext::` transports name a command to run. No remote is used today; this is
|
|
77
|
+
// here so that adding one later cannot quietly open the door.
|
|
78
|
+
/^protocol\..*\.command$/,
|
|
79
|
+
/^credential\./,
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* Config keys that move the working tree out from under the caller.
|
|
83
|
+
*
|
|
84
|
+
* A DIFFERENT HAZARD FROM THE LIST ABOVE, which is why it is a different list
|
|
85
|
+
* and a different sentence: `core.worktree` names no program, so nothing here
|
|
86
|
+
* gets code execution. It makes `git rev-parse --show-toplevel` — and every
|
|
87
|
+
* verb `GitDriver` issues — operate somewhere the caller never named.
|
|
88
|
+
*
|
|
89
|
+
* ⚠️ MEASURED. With `core.worktree` pointing at a sibling directory,
|
|
90
|
+
* `GitDriver.discard()` ran `reset --hard` and `clean -fd` against that
|
|
91
|
+
* directory: a file holding uncommitted work was overwritten and another was
|
|
92
|
+
* deleted, while `repoRootTroubleAt` — whose whole promise is "the tree is the
|
|
93
|
+
* one the caller named" — returned null, because it compares against the
|
|
94
|
+
* directory holding `.git` and git does not.
|
|
95
|
+
*
|
|
96
|
+
* `GIT_WORK_TREE` and `GIT_DIR` do the same thing from the ENVIRONMENT rather
|
|
97
|
+
* than from the repository's config. They are handled where they belong, in
|
|
98
|
+
* `trustedGitEnv` below, which builds the `GIT_` namespace up instead of
|
|
99
|
+
* inheriting it — a config key is refused, an environment variable is dropped.
|
|
100
|
+
*/
|
|
101
|
+
const REDIRECTING_KEYS = [/^core\.worktree$/];
|
|
102
|
+
/**
|
|
103
|
+
* One section header, and whatever follows it ON THE SAME LINE.
|
|
104
|
+
*
|
|
105
|
+
* 🔴 BOTH HALVES WERE EXPLOITABLE, and both were measured rather than imagined.
|
|
106
|
+
*
|
|
107
|
+
* `[filter "ev"] clean = sh evil` git resolves `filter.ev.clean`. The old
|
|
108
|
+
* parser `continue`d after a header and
|
|
109
|
+
* never looked at the rest of the line, so
|
|
110
|
+
* it reported nothing.
|
|
111
|
+
* `[filter "]"]` the old `^\[([^\]]+)\]` stopped at the
|
|
112
|
+
* FIRST `]`, which is inside the quotes.
|
|
113
|
+
* Section became `filter`, the key became
|
|
114
|
+
* `filter.clean`, and no pattern matched.
|
|
115
|
+
*
|
|
116
|
+
* Each produced a marker file on disk through `add -A`. So the closing bracket
|
|
117
|
+
* is now found by a scanner that knows about quoting and backslash escapes, and
|
|
118
|
+
* the remainder of the line is parsed as a variable.
|
|
119
|
+
*/
|
|
120
|
+
function parseHeader(line) {
|
|
121
|
+
let inQuote = false;
|
|
122
|
+
let end = -1;
|
|
123
|
+
for (let i = 1; i < line.length; i++) {
|
|
124
|
+
const c = line[i];
|
|
125
|
+
if (inQuote) {
|
|
126
|
+
if (c === '\\') {
|
|
127
|
+
i += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (c === '"')
|
|
131
|
+
inQuote = false;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (c === '"') {
|
|
135
|
+
inQuote = true;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (c === ']') {
|
|
139
|
+
end = i;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (end === -1)
|
|
144
|
+
return null;
|
|
145
|
+
const inner = line.slice(1, end);
|
|
146
|
+
const rest = line.slice(end + 1);
|
|
147
|
+
// `[filter "ev"]`, `[core]`, and the legacy `[filter.ev]` all arrive here. The
|
|
148
|
+
// quoted subsection keeps its case in git but the section and key do not, and
|
|
149
|
+
// every pattern below ignores the subsection's spelling anyway.
|
|
150
|
+
const m = /^\s*([^\s"]+)\s*(?:"((?:[^"\\]|\\.)*)")?\s*$/.exec(inner);
|
|
151
|
+
if (m === null)
|
|
152
|
+
return null;
|
|
153
|
+
const base = m[1].toLowerCase();
|
|
154
|
+
const sub = m[2];
|
|
155
|
+
return {
|
|
156
|
+
section: sub === undefined ? base : `${base}.${sub.replace(/\\(.)/g, '$1')}`,
|
|
157
|
+
rest,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Every `section.subsection.key` this text defines, lowercased as git names it.
|
|
162
|
+
*
|
|
163
|
+
* `malformed` is not a detail: a header this cannot parse is one whose keys
|
|
164
|
+
* cannot be attributed to any section, and silently dropping them is the shape
|
|
165
|
+
* of every hole above. The caller refuses instead.
|
|
166
|
+
*/
|
|
167
|
+
function dottedKeys(text) {
|
|
168
|
+
const out = [];
|
|
169
|
+
let section = '';
|
|
170
|
+
let malformed = false;
|
|
171
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
172
|
+
let line = raw.trim();
|
|
173
|
+
if (line.length === 0 || line.startsWith('#') || line.startsWith(';'))
|
|
174
|
+
continue;
|
|
175
|
+
if (line.startsWith('[')) {
|
|
176
|
+
const header = parseHeader(line);
|
|
177
|
+
if (header === null) {
|
|
178
|
+
// Git would reject the file outright. We cannot know what section the
|
|
179
|
+
// lines below belong to, so we must not guess — and must not proceed.
|
|
180
|
+
malformed = true;
|
|
181
|
+
section = '';
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
section = header.section;
|
|
185
|
+
line = header.rest.trim();
|
|
186
|
+
if (line.length === 0 || line.startsWith('#') || line.startsWith(';'))
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (section.length === 0)
|
|
190
|
+
continue;
|
|
191
|
+
// At most one variable per line: git reads a variable's value to the end of
|
|
192
|
+
// the line, so anything after `=` is the value and never another key.
|
|
193
|
+
const eq = line.indexOf('=');
|
|
194
|
+
const key = (eq === -1 ? line : line.slice(0, eq)).trim().toLowerCase();
|
|
195
|
+
if (key.length === 0)
|
|
196
|
+
continue;
|
|
197
|
+
out.push(`${section}.${key}`);
|
|
198
|
+
}
|
|
199
|
+
return { keys: out, malformed };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The first executable key this config defines, in words, or null if none.
|
|
203
|
+
*
|
|
204
|
+
* Takes the text rather than a path so the rule is testable without a
|
|
205
|
+
* repository, and so the caller decides what an unreadable config means.
|
|
206
|
+
*/
|
|
207
|
+
function repoConfigTrouble(text) {
|
|
208
|
+
const { keys, malformed } = dottedKeys(text);
|
|
209
|
+
for (const key of keys) {
|
|
210
|
+
if (EXECUTABLE_KEYS.some((p) => p.test(key))) {
|
|
211
|
+
return `this repository's git config sets ${key}, which names a program for git to run`;
|
|
212
|
+
}
|
|
213
|
+
if (REDIRECTING_KEYS.some((p) => p.test(key))) {
|
|
214
|
+
return `this repository's git config sets ${key}, which points git at a different working tree`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (malformed) {
|
|
218
|
+
return `this repository's git config has a section header this cannot parse, so it could not be checked`;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
/** `code` off an fs error without asserting the error's shape. */
|
|
223
|
+
function errCode(e) {
|
|
224
|
+
return typeof e === 'object' && e !== null && 'code' in e ? String(e.code) : 'unknown';
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Whether `dir` is really a git directory, by git's own test.
|
|
228
|
+
*
|
|
229
|
+
* 🔴 THE HOLE THIS CLOSES WAS BOTH CHECKS AT ONCE, and it was demonstrated.
|
|
230
|
+
* Accepting any directory *named* `.git` meant that
|
|
231
|
+
*
|
|
232
|
+
* mkdir -p sub/.git # empty, and the agent may create it
|
|
233
|
+
*
|
|
234
|
+
* made `resolveGitLayout(sub)` answer `{root: sub}`. Git ignores such a decoy
|
|
235
|
+
* and keeps walking — `git -C sub rev-parse --absolute-git-dir` still names the
|
|
236
|
+
* real repository above, and `git -C sub config --get filter.ev.clean` still
|
|
237
|
+
* resolved the malicious program. But looply's checks did not:
|
|
238
|
+
* `repoConfigTroubleAt(sub)` returned null, so the config was never examined,
|
|
239
|
+
* and `repoRootTroubleAt(sub)` returned null, so `reset --hard` issued from
|
|
240
|
+
* `sub` reset the whole repository above it and overwrote uncommitted work.
|
|
241
|
+
*
|
|
242
|
+
* So the markers are required, and a directory that lacks them is walked PAST
|
|
243
|
+
* rather than stopped at — which is exactly what git does, and which lands on
|
|
244
|
+
* the real repository whose config then gets read.
|
|
245
|
+
*
|
|
246
|
+
* `commondir` is the linked-worktree case: `<common>/worktrees/<name>` holds
|
|
247
|
+
* `HEAD` and `commondir` but has neither `objects` nor `refs` of its own.
|
|
248
|
+
*/
|
|
249
|
+
function looksLikeGitDir(dir) {
|
|
250
|
+
try {
|
|
251
|
+
if (!(0, node_fs_1.statSync)((0, node_path_1.join)(dir, 'HEAD')).isFile())
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'commondir')))
|
|
258
|
+
return true;
|
|
259
|
+
return (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'objects')) && (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'refs'));
|
|
260
|
+
}
|
|
261
|
+
/** The `gitdir:` line of a linked worktree's `.git` file, resolved against it. */
|
|
262
|
+
function gitdirPointer(text, from) {
|
|
263
|
+
// Git's `read_gitfile_gently` strips trailing NEWLINES, not trailing spaces —
|
|
264
|
+
// so a pointer ending in a space names a directory whose name ends in a space,
|
|
265
|
+
// and trimming it here would resolve to a different one.
|
|
266
|
+
const m = /^gitdir:[ \t]*(.*?)[\r\n]*$/.exec(text.replace(/[\r\n]+$/, ''));
|
|
267
|
+
if (m === null)
|
|
268
|
+
return null;
|
|
269
|
+
const p = m[1];
|
|
270
|
+
if (p.length === 0)
|
|
271
|
+
return null;
|
|
272
|
+
return (0, node_path_1.isAbsolute)(p) ? p : (0, node_path_1.resolve)(from, p);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Find the repository governing `cwd`, the way git does: look for a `.git`
|
|
276
|
+
* entry here, then in each parent, to the filesystem root.
|
|
277
|
+
*
|
|
278
|
+
* WALKING UP IS DELIBERATE. Run from a subdirectory, the old check looked at
|
|
279
|
+
* `<subdir>/.git`, found nothing, and returned "no trouble" — while every git
|
|
280
|
+
* command it was guarding still ran against the repository above. That was a
|
|
281
|
+
* second way to reach the same fail-open, and it did not need a worktree.
|
|
282
|
+
*/
|
|
283
|
+
function resolveGitLayout(cwd) {
|
|
284
|
+
const lexical = walkForLayout((0, node_path_1.resolve)(cwd));
|
|
285
|
+
if (lexical.kind !== 'none')
|
|
286
|
+
return lexical;
|
|
287
|
+
/**
|
|
288
|
+
* ⚠️ AND AGAIN PHYSICALLY, because this walk is lexical and git's is not.
|
|
289
|
+
*
|
|
290
|
+
* `dirname` is string arithmetic: it climbs out of a junction or symlink by
|
|
291
|
+
* name and never reaches the directory the link actually points into. Driven
|
|
292
|
+
* with a cwd inside a Windows junction, `git rev-parse --show-toplevel` named
|
|
293
|
+
* the real repository while this answered `none` — and `none` means "no
|
|
294
|
+
* repository here", which is the silent allow. Node reports an unresolved
|
|
295
|
+
* cwd for exactly this case: `process.cwd()` inside a junction returns the
|
|
296
|
+
* junction path under cmd.exe and PowerShell.
|
|
297
|
+
*
|
|
298
|
+
* Only on the `none` path, so the ordinary checkout pays one `resolve` and
|
|
299
|
+
* nothing else.
|
|
300
|
+
*/
|
|
301
|
+
const real = realOrResolved(cwd);
|
|
302
|
+
return real === (0, node_path_1.resolve)(cwd) ? lexical : walkForLayout(real);
|
|
303
|
+
}
|
|
304
|
+
function walkForLayout(start) {
|
|
305
|
+
let dir = start;
|
|
306
|
+
for (;;) {
|
|
307
|
+
const dotGit = (0, node_path_1.join)(dir, '.git');
|
|
308
|
+
let st;
|
|
309
|
+
try {
|
|
310
|
+
st = (0, node_fs_1.statSync)(dotGit);
|
|
311
|
+
}
|
|
312
|
+
catch (e) {
|
|
313
|
+
const code = errCode(e);
|
|
314
|
+
// Not here. ENOTDIR means some component of this path is a file rather
|
|
315
|
+
// than a directory, which on Windows usually surfaces as ENOENT anyway;
|
|
316
|
+
// either way the answer is the same as an absence — keep climbing, and
|
|
317
|
+
// let the repository above be found. (An earlier comment here claimed
|
|
318
|
+
// this STOPPED the walk. It never did, and stopping would be wrong.)
|
|
319
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
320
|
+
const up = (0, node_path_1.dirname)(dir);
|
|
321
|
+
if (up === dir)
|
|
322
|
+
return { kind: 'none' };
|
|
323
|
+
dir = up;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
// Present but unstattable — a permission wall, not an absence.
|
|
327
|
+
return { kind: 'fault', because: `${dotGit} could not be read (${code})` };
|
|
328
|
+
}
|
|
329
|
+
let gitDir;
|
|
330
|
+
if (st.isDirectory()) {
|
|
331
|
+
if (!looksLikeGitDir(dotGit)) {
|
|
332
|
+
// A directory called `.git` that is not one. Git walks past it; so do
|
|
333
|
+
// we, or the repository that actually governs this cwd is never read.
|
|
334
|
+
const up = (0, node_path_1.dirname)(dir);
|
|
335
|
+
if (up === dir)
|
|
336
|
+
return { kind: 'none' };
|
|
337
|
+
dir = up;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
gitDir = dotGit;
|
|
341
|
+
}
|
|
342
|
+
else if (st.isFile()) {
|
|
343
|
+
let text;
|
|
344
|
+
try {
|
|
345
|
+
text = (0, node_fs_1.readFileSync)(dotGit, 'utf8');
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
return { kind: 'fault', because: `${dotGit} names a git directory but could not be read (${errCode(e)})` };
|
|
349
|
+
}
|
|
350
|
+
const p = gitdirPointer(text, dir);
|
|
351
|
+
if (p === null) {
|
|
352
|
+
return { kind: 'fault', because: `${dotGit} is a file that does not name a git directory` };
|
|
353
|
+
}
|
|
354
|
+
// Normalised so that a forward-slashed pointer and a backslashed
|
|
355
|
+
// `commonDir` cannot become two spellings of one path in a caller that
|
|
356
|
+
// compares them as strings.
|
|
357
|
+
gitDir = (0, node_path_1.normalize)(p);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
return { kind: 'fault', because: `${dotGit} is neither a file nor a directory` };
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
if (!(0, node_fs_1.statSync)(gitDir).isDirectory()) {
|
|
364
|
+
return { kind: 'fault', because: `${gitDir} is not a git directory` };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
return { kind: 'fault', because: `${gitDir} could not be read (${errCode(e)})` };
|
|
369
|
+
}
|
|
370
|
+
// A FAULT HERE, not a walk-past. A `.git` FILE that names something which
|
|
371
|
+
// is not a git directory is corruption or misdirection, not the ordinary
|
|
372
|
+
// "there is no repository in this folder" that a bare directory means.
|
|
373
|
+
if (!looksLikeGitDir(gitDir)) {
|
|
374
|
+
return { kind: 'fault', because: `${gitDir} does not look like a git directory` };
|
|
375
|
+
}
|
|
376
|
+
// `commondir` is absent in an ordinary checkout and present in a linked
|
|
377
|
+
// worktree, where it is usually the relative `../..`.
|
|
378
|
+
let commonDir = gitDir;
|
|
379
|
+
try {
|
|
380
|
+
// `strbuf_rtrim`-style: git strips trailing newlines here, not spaces.
|
|
381
|
+
const raw = (0, node_fs_1.readFileSync)((0, node_path_1.join)(gitDir, 'commondir'), 'utf8').replace(/[\r\n]+$/, '');
|
|
382
|
+
if (raw.length === 0) {
|
|
383
|
+
return { kind: 'fault', because: `${(0, node_path_1.join)(gitDir, 'commondir')} is empty` };
|
|
384
|
+
}
|
|
385
|
+
commonDir = (0, node_path_1.normalize)((0, node_path_1.isAbsolute)(raw) ? raw : (0, node_path_1.resolve)(gitDir, raw));
|
|
386
|
+
}
|
|
387
|
+
catch (e) {
|
|
388
|
+
const code = errCode(e);
|
|
389
|
+
if (code !== 'ENOENT') {
|
|
390
|
+
return { kind: 'fault', because: `${(0, node_path_1.join)(gitDir, 'commondir')} could not be read (${code})` };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return { kind: 'ok', root: dir, gitDir, commonDir };
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Every config file that governs this repository, most-shared first.
|
|
398
|
+
*
|
|
399
|
+
* `config.worktree` is read only when `extensions.worktreeConfig` is set, but
|
|
400
|
+
* it is scanned unconditionally: a file that git might read is a file this
|
|
401
|
+
* check must not skip, and scanning one git ignores costs a refusal nobody
|
|
402
|
+
* could have exploited anyway.
|
|
403
|
+
*/
|
|
404
|
+
function repoConfigPaths(layout) {
|
|
405
|
+
const paths = [(0, node_path_1.join)(layout.commonDir, 'config')];
|
|
406
|
+
const perWorktree = (0, node_path_1.join)(layout.gitDir, 'config.worktree');
|
|
407
|
+
if (!paths.includes(perWorktree))
|
|
408
|
+
paths.push(perWorktree);
|
|
409
|
+
return paths;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Judge the config of the repository governing `cwd`, in words, or null.
|
|
413
|
+
*
|
|
414
|
+
* A missing repository is not a fault and neither is a missing config file — a
|
|
415
|
+
* config that does not exist defines nothing. A repository whose metadata or
|
|
416
|
+
* config cannot be READ is a refusal: the whole mechanism is "decline before
|
|
417
|
+
* git runs", and declining is the only answer that stays sound when the file
|
|
418
|
+
* the decision rests on is unavailable.
|
|
419
|
+
*/
|
|
420
|
+
function repoConfigTroubleAt(cwd) {
|
|
421
|
+
const layout = resolveGitLayout(cwd);
|
|
422
|
+
if (layout.kind === 'none') {
|
|
423
|
+
// No repository, here or above. Git will produce its own error if one was
|
|
424
|
+
// expected, and there is no config to have been subverted.
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
if (layout.kind === 'fault') {
|
|
428
|
+
return `this repository's git metadata could not be read, so its config could not be checked — ${layout.because}`;
|
|
429
|
+
}
|
|
430
|
+
for (const path of repoConfigPaths(layout)) {
|
|
431
|
+
let text;
|
|
432
|
+
try {
|
|
433
|
+
text = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
434
|
+
}
|
|
435
|
+
catch (e) {
|
|
436
|
+
const code = errCode(e);
|
|
437
|
+
if (code === 'ENOENT')
|
|
438
|
+
continue;
|
|
439
|
+
return `this repository's git config at ${path} could not be read (${code}), so it could not be checked`;
|
|
440
|
+
}
|
|
441
|
+
const trouble = repoConfigTrouble(text);
|
|
442
|
+
if (trouble !== null)
|
|
443
|
+
return trouble;
|
|
444
|
+
}
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* The metadata directories of the worktree AT `dir`, or null when `dir` is not
|
|
449
|
+
* itself one.
|
|
450
|
+
*
|
|
451
|
+
* ⚠️ DELIBERATELY NOT THE ENCLOSING REPOSITORY, and the difference is the whole
|
|
452
|
+
* reason this is a separate function. {@link resolveGitLayout} walks up, which
|
|
453
|
+
* is the right answer to "whose config could execute when git runs here" — a
|
|
454
|
+
* command issued in a subdirectory is still governed by the repository above.
|
|
455
|
+
* It is the WRONG answer to "what does this grant own": a directory that merely
|
|
456
|
+
* SITS INSIDE a repository is not that repository's worktree, and protecting an
|
|
457
|
+
* ancestor's metadata would describe a boundary the container does not have.
|
|
458
|
+
*
|
|
459
|
+
* Caught by `agent-contained.spec.ts`, which passes a `projectDir` that is not
|
|
460
|
+
* a repository and pins the protected list at three entries.
|
|
461
|
+
*/
|
|
462
|
+
function worktreeMetadata(dir) {
|
|
463
|
+
const layout = resolveGitLayout(dir);
|
|
464
|
+
if (layout.kind !== 'ok')
|
|
465
|
+
return null;
|
|
466
|
+
if (realOrResolved(layout.root) !== realOrResolved(dir))
|
|
467
|
+
return null;
|
|
468
|
+
return { gitDir: layout.gitDir, commonDir: layout.commonDir };
|
|
469
|
+
}
|
|
470
|
+
/** `realpathSync` where it works, the resolved path where it does not. */
|
|
471
|
+
function realOrResolved(p) {
|
|
472
|
+
try {
|
|
473
|
+
return (0, node_fs_1.realpathSync)(p);
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
return (0, node_path_1.resolve)(p);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Whether `cwd` is the root of the repository it belongs to, in words, or null.
|
|
481
|
+
*
|
|
482
|
+
* THE CHECK THAT NEVER EXISTED. `looply` derives three things from `cwd` — the
|
|
483
|
+
* project identity, `.looply/state`, and the tree every git verb operates on —
|
|
484
|
+
* and nothing established that `cwd` was a repository at all, let alone its
|
|
485
|
+
* root. Started one directory down, the run wrote its state into a subdirectory
|
|
486
|
+
* while committing, discarding and resetting the whole repository above it: a
|
|
487
|
+
* `reset --hard` scoped to a tree the person never named.
|
|
488
|
+
*
|
|
489
|
+
* A worktree layout makes it reachable by accident rather than by mistake,
|
|
490
|
+
* which is why it lands here rather than after the first worktree exists.
|
|
491
|
+
*/
|
|
492
|
+
function repoRootTroubleAt(cwd) {
|
|
493
|
+
const layout = resolveGitLayout(cwd);
|
|
494
|
+
if (layout.kind === 'none') {
|
|
495
|
+
// Unchanged from before this check existed: git says what it thinks of a
|
|
496
|
+
// directory that is not a repository, in its own words.
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
if (layout.kind === 'fault') {
|
|
500
|
+
return `this repository's git metadata could not be read — ${layout.because}`;
|
|
501
|
+
}
|
|
502
|
+
if (realOrResolved(layout.root) !== realOrResolved(cwd)) {
|
|
503
|
+
/**
|
|
504
|
+
* ⚠️ NEVER "run from `<root>` instead", which is what this said first.
|
|
505
|
+
*
|
|
506
|
+
* `<root>` may be nothing to do with the person's project. A plain folder
|
|
507
|
+
* under a dotfiles or monorepo checkout — a very ordinary thing to have on
|
|
508
|
+
* a Desktop — resolves to that enclosing repository, and following the
|
|
509
|
+
* advice would point looply at a tree it may then commit to, discard and
|
|
510
|
+
* `reset --hard`: precisely the harm this refusal exists to prevent.
|
|
511
|
+
*
|
|
512
|
+
* So the two cases are told apart, and only the one that IS a subdirectory
|
|
513
|
+
* of the intended project gets a destination.
|
|
514
|
+
*/
|
|
515
|
+
const here = (0, node_path_1.resolve)(cwd);
|
|
516
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(here, '.git'))) {
|
|
517
|
+
return (`this is not the root of its git repository — ${here} holds git metadata that git does not ` +
|
|
518
|
+
`recognise, while ${layout.root} is the repository that governs it`);
|
|
519
|
+
}
|
|
520
|
+
return (`this is not the root of a git repository — ${here} sits inside ${layout.root}. ` +
|
|
521
|
+
`If ${layout.root} is the project you mean, start looply there; if it is not, ` +
|
|
522
|
+
`initialise a repository here first`);
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* An empty directory to point `core.hooksPath` at.
|
|
528
|
+
*
|
|
529
|
+
* Created rather than merely named: a path that does not exist would also work
|
|
530
|
+
* today, but an empty directory we make is one the repository cannot fill by
|
|
531
|
+
* simply creating what git was told to look for.
|
|
532
|
+
*
|
|
533
|
+
* 🔴 AND IT IS EMPTIED EVERY TIME, WHICH IT WAS NOT.
|
|
534
|
+
*
|
|
535
|
+
* This was one machine-global `%TEMP%\looply-no-hooks`, created once and never
|
|
536
|
+
* looked at again — passed as `core.hooksPath` on EVERY trusted git call of
|
|
537
|
+
* every workspace of every project on the machine. `mkdirSync` is recursive and
|
|
538
|
+
* idempotent, so the first caller made it and every later caller trusted it
|
|
539
|
+
* without ever asking what was inside.
|
|
540
|
+
*
|
|
541
|
+
* `%TEMP%` is world-writable on Windows. Anything that can write there could
|
|
542
|
+
* drop a `pre-commit` into that directory and have it executed by the very
|
|
543
|
+
* mechanism that exists to guarantee no repository hook ever runs — with the
|
|
544
|
+
* daemon's own privileges, uncontained. Not a workspace-isolation problem: a
|
|
545
|
+
* one-line hole in the hook defence itself, which nothing would have noticed
|
|
546
|
+
* because the directory is invisible and the flag reads as already handled.
|
|
547
|
+
*
|
|
548
|
+
* ⚠️ SO IT IS CHECKED, NOT ASSUMED. Anything sitting in it is removed before
|
|
549
|
+
* the path is handed to git. Sweeping is cheap — the directory is empty in
|
|
550
|
+
* every ordinary case — and a sweep that fails still leaves git pointed at a
|
|
551
|
+
* directory whose contents this call has at least tried to control.
|
|
552
|
+
*/
|
|
553
|
+
function noHooksDir() {
|
|
554
|
+
const dir = (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'looply-no-hooks');
|
|
555
|
+
try {
|
|
556
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
// Already there, or a filesystem that will fail the same way when git
|
|
560
|
+
// looks. Either way git finds no hooks, which is the point.
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
for (const name of (0, node_fs_1.readdirSync)(dir)) {
|
|
564
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(dir, name), { recursive: true, force: true });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
// Unreadable or unwritable. Git will find whatever is there; nothing here
|
|
569
|
+
// can do better, and failing a run over a temp directory would be worse.
|
|
570
|
+
}
|
|
571
|
+
return dir;
|
|
572
|
+
}
|
|
573
|
+
let cachedGitBinary;
|
|
574
|
+
/**
|
|
575
|
+
* The absolute path of the `git` this machine will run, resolved from PATH only.
|
|
576
|
+
*
|
|
577
|
+
* 🔴 A BARE `spawn('git', …, {cwd})` LETS THE REPOSITORY SUPPLY THE BINARY.
|
|
578
|
+
*
|
|
579
|
+
* On Windows the process creation path searches the WORKING DIRECTORY BEFORE
|
|
580
|
+
* PATH. `cwd` here is the project root, and `untrustedGrants` hands the
|
|
581
|
+
* contained agent `{ path: projectDir, write: true }` — so a `git.exe` the agent
|
|
582
|
+
* writes runs OUTSIDE the container, as the daemon, on `status`, `add -A`,
|
|
583
|
+
* `diff HEAD`, `commit`, `reset --hard`, `clean -fd` and `push`. Measured:
|
|
584
|
+
* `spawnSync('git', ['--version'], { cwd: <dir with a planted git.exe> })`
|
|
585
|
+
* executed the plant; the same call from another directory ran real git.
|
|
586
|
+
*
|
|
587
|
+
* That defeats everything else in this file at once — the config check, the
|
|
588
|
+
* root check, the rebuilt `GIT_` namespace, the `GIT_DIR` pin, `core.hooksPath`
|
|
589
|
+
* and `commit.gpgsign=false` — because none of them constrains a program that
|
|
590
|
+
* is not git. `exec-domains.ts` already makes exactly this argument for the
|
|
591
|
+
* runtime mirror ("untrusted code that could replace `node.exe` would be
|
|
592
|
+
* untrusted code choosing what every later gate runs"); it simply had never
|
|
593
|
+
* been applied to git.
|
|
594
|
+
*
|
|
595
|
+
* Resolved once and cached: PATH does not change under a running daemon, and a
|
|
596
|
+
* per-call PATH walk on every git verb would be a needless cost.
|
|
597
|
+
*/
|
|
598
|
+
function gitBinary() {
|
|
599
|
+
if (cachedGitBinary !== undefined)
|
|
600
|
+
return cachedGitBinary;
|
|
601
|
+
const win = process.platform === 'win32';
|
|
602
|
+
const exts = win ? (process.env['PATHEXT'] ?? '.EXE').split(';') : [''];
|
|
603
|
+
// PATH ONLY. The cwd is deliberately not searched — it is the thing being
|
|
604
|
+
// defended against.
|
|
605
|
+
for (const dir of (process.env['PATH'] ?? '').split(win ? ';' : ':')) {
|
|
606
|
+
const d = dir.trim().replace(/^"|"$/g, '');
|
|
607
|
+
if (d.length === 0)
|
|
608
|
+
continue;
|
|
609
|
+
for (const ext of exts) {
|
|
610
|
+
const candidate = (0, node_path_1.join)(d, `git${ext.toLowerCase()}`);
|
|
611
|
+
try {
|
|
612
|
+
if ((0, node_fs_1.statSync)(candidate).isFile()) {
|
|
613
|
+
cachedGitBinary = candidate;
|
|
614
|
+
return candidate;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
// Not here; keep walking.
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
cachedGitBinary = null;
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Arguments every trusted git invocation carries, before the caller's own.
|
|
627
|
+
*
|
|
628
|
+
* `-c` beats repository config, so this is enforcement rather than a request:
|
|
629
|
+
* the repository cannot override it back.
|
|
630
|
+
*/
|
|
631
|
+
function trustedGitArgs() {
|
|
632
|
+
return [
|
|
633
|
+
'-c',
|
|
634
|
+
`core.hooksPath=${noHooksDir()}`,
|
|
635
|
+
/**
|
|
636
|
+
* SIGNING IS NOT AN ATTACK, SO IT IS NOT REFUSED — IT IS TURNED OFF HERE.
|
|
637
|
+
*
|
|
638
|
+
* A person who signs their commits has `commit.gpgsign = true` and a
|
|
639
|
+
* `gpg.program` in their GLOBAL config, and refusing their repository over
|
|
640
|
+
* it would be absurd. But `git commit` then spawns that program under the
|
|
641
|
+
* daemon, which is the same execution this file exists to prevent — and it
|
|
642
|
+
* is not hypothetical: with signing on, `commit` failed with "gpg failed to
|
|
643
|
+
* sign the data" after the named program ran.
|
|
644
|
+
*
|
|
645
|
+
* `-c` is the same scoped override `core.hooksPath` above uses: it applies
|
|
646
|
+
* to this invocation only and mutates nothing of the person's. Verified to
|
|
647
|
+
* beat both a global-config `commit.gpgsign` and one injected through
|
|
648
|
+
* `GIT_CONFIG_COUNT`, in gpg and ssh signing formats alike.
|
|
649
|
+
*
|
|
650
|
+
* `push.gpgSign` is here for a second reason as well as the first: with it
|
|
651
|
+
* set, looply's push FAILS outright — "the receiving end does not support
|
|
652
|
+
* --signed push" — so this is a correctness fix, not only a safety one.
|
|
653
|
+
*/
|
|
654
|
+
'-c',
|
|
655
|
+
'commit.gpgsign=false',
|
|
656
|
+
'-c',
|
|
657
|
+
'push.gpgSign=false',
|
|
658
|
+
];
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* The environment for a trusted git invocation.
|
|
662
|
+
*
|
|
663
|
+
* 🔴 THE `GIT_` NAMESPACE IS THE ENTIRE REDIRECT SURFACE, so it is the one
|
|
664
|
+
* namespace built up rather than inherited — exactly the principle
|
|
665
|
+
* `exec-domains.ts` already states for contained children, applied here to a
|
|
666
|
+
* single prefix instead of a whole environment.
|
|
667
|
+
*
|
|
668
|
+
* Spreading `process.env` handed every one of these to git, and each was
|
|
669
|
+
* demonstrated against looply's own verbs on a clean repository:
|
|
670
|
+
*
|
|
671
|
+
* GIT_DIR, GIT_WORK_TREE, GIT_COMMON_DIR operate on a DIFFERENT repository
|
|
672
|
+
* while both Phase 0 checks — which
|
|
673
|
+
* read the cwd — return null.
|
|
674
|
+
* `GIT_WORK_TREE` alone is the worst:
|
|
675
|
+
* git keeps this repository's gitdir
|
|
676
|
+
* and index and relocates the TREE,
|
|
677
|
+
* which is what turns `discard()`'s
|
|
678
|
+
* `reset --hard` + `clean -fd` into
|
|
679
|
+
* data loss somewhere else.
|
|
680
|
+
* GIT_CONFIG_COUNT/KEY/VALUE, inject config, so
|
|
681
|
+
* GIT_CONFIG_PARAMETERS, `filter.<any>.clean` fires on
|
|
682
|
+
* GIT_CONFIG_GLOBAL, GIT_CONFIG_SYSTEM `status`, `add -A`, `diff HEAD`
|
|
683
|
+
* and `commit`. The repository's own
|
|
684
|
+
* config is clean throughout.
|
|
685
|
+
* GIT_EXTERNAL_DIFF fires on `diff HEAD`.
|
|
686
|
+
* GIT_INDEX_FILE `add -A` writes a foreign index, so
|
|
687
|
+
* the round's own commit carries none
|
|
688
|
+
* of its work. Git EXPORTS this to
|
|
689
|
+
* hooks, so it needs no attacker.
|
|
690
|
+
* GIT_OBJECT_DIRECTORY writes this repository's blobs into
|
|
691
|
+
* a directory somebody else named.
|
|
692
|
+
* GIT_NAMESPACE the push lands at
|
|
693
|
+
* `refs/namespaces/<ns>/...` while
|
|
694
|
+
* `ls-remote` — under the same
|
|
695
|
+
* namespace — reports it present at
|
|
696
|
+
* the real ref. The write and its
|
|
697
|
+
* verification lie consistently.
|
|
698
|
+
* GIT_TRACE, GIT_TRACE2* an arbitrary file create-and-append
|
|
699
|
+
* primitive, as the daemon.
|
|
700
|
+
* GIT_LITERAL_PATHSPECS breaks the `:(exclude).looply`
|
|
701
|
+
* argument — fails closed, but only
|
|
702
|
+
* by luck.
|
|
703
|
+
*
|
|
704
|
+
* ⚠️ THE `GIT_` PREFIX IS DROPPED EXCEPT FOR `AMBIENT_GIT_KEPT` below, and both
|
|
705
|
+
* halves of that are deliberate. PATH, HOME, APPDATA, SSH_AUTH_SOCK,
|
|
706
|
+
* SSH_ASKPASS, GCM_*, LANG and the lowercase proxy variables are the person's
|
|
707
|
+
* machine, not the round's input. Most of what an ordinary setup depends on —
|
|
708
|
+
* the credential helper, LFS filters, identity in a config file — lives in
|
|
709
|
+
* FILES; the exceptions that live in the environment are named in
|
|
710
|
+
* `AMBIENT_GIT_KEPT`, because dropping them broke a CI commit outright. A rule
|
|
711
|
+
* of "refuse if any GIT_* is set" would refuse an ordinary developer session,
|
|
712
|
+
* which already carries `GIT_EDITOR`, `GIT_ASKPASS` and `GIT_TERMINAL_PROMPT`
|
|
713
|
+
* from its surrounding tooling.
|
|
714
|
+
*
|
|
715
|
+
* ⚠️ SO THE HONEST CLAIM IS NARROWER STILL, and the first version of this
|
|
716
|
+
* comment overstated it. Ambient environment cannot redirect a looply git
|
|
717
|
+
* operation, and cannot inject configuration THROUGH THE `GIT_` NAMESPACE. It
|
|
718
|
+
* can still inject configuration by other means: `XDG_CONFIG_HOME` and `HOME`
|
|
719
|
+
* survive verbatim, and git reads `$XDG_CONFIG_HOME/git/config` and
|
|
720
|
+
* `$HOME/.gitconfig`, neither of which `repoConfigPaths` scans. That is
|
|
721
|
+
* deliberate, and it is the same carve-out the header already makes for system
|
|
722
|
+
* and global config: those files are the person's, and refusing them would
|
|
723
|
+
* break every machine with Git LFS. It is also unreachable from repository
|
|
724
|
+
* content — no production file assigns `process.env` — which is the threat this
|
|
725
|
+
* file exists to stop. And it is NOT "the child can name no program":
|
|
726
|
+
* `SSH_ASKPASS` and its relatives survive as ambient machine configuration.
|
|
727
|
+
*
|
|
728
|
+
* ⚠️ AND `GIT_CONFIG_NOSYSTEM` IS DELIBERATELY NOT SET. `exec-domains.ts` sets
|
|
729
|
+
* it for the UNTRUSTED gate on purpose; setting it here would drop the system
|
|
730
|
+
* config that carries `filter.lfs.*` and `credential.helper` — the breakage the
|
|
731
|
+
* header of this file already refuses to cause.
|
|
732
|
+
*/
|
|
733
|
+
/**
|
|
734
|
+
* `GIT_` variables that are NOT redirection or injection primitives, and that an
|
|
735
|
+
* ordinary developer or CI system legitimately sets.
|
|
736
|
+
*
|
|
737
|
+
* 🔴 DROPPING THE WHOLE NAMESPACE BROKE COMMITS. `GitDriver.commit` runs a bare
|
|
738
|
+
* `git commit -m` with no `--author`, so with these gone the only identity path
|
|
739
|
+
* is a config file — and a CI runner that supplies identity through the
|
|
740
|
+
* environment gets `Author identity unknown`, exit 128. Measured: the same
|
|
741
|
+
* commit in the same repository succeeded with them and failed without. The
|
|
742
|
+
* resulting `GitError` is thrown outside every try in `runRound`, so it ended
|
|
743
|
+
* the whole run, after the paid model turn and after a passing gate.
|
|
744
|
+
*
|
|
745
|
+
* Each entry earns its place by NOT being able to move the operation: an author
|
|
746
|
+
* name is a string on a commit object. `GIT_SSH_COMMAND` and `GIT_SSH` do name a
|
|
747
|
+
* program, but only for the two network verbs, they are how deploy keys and
|
|
748
|
+
* non-default ports are configured on every CI system, and their config
|
|
749
|
+
* equivalent `core.sshCommand` is already refused when it comes from the
|
|
750
|
+
* REPOSITORY — which is the threat this file is about. Ambient is the person's
|
|
751
|
+
* machine.
|
|
752
|
+
*/
|
|
753
|
+
const AMBIENT_GIT_KEPT = [
|
|
754
|
+
/^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL|DATE)$/i,
|
|
755
|
+
/^GIT_SSH(_COMMAND)?$/i,
|
|
756
|
+
/^GIT_LFS_/i,
|
|
757
|
+
];
|
|
758
|
+
function trustedGitEnv(base, cwd) {
|
|
759
|
+
/**
|
|
760
|
+
* Built as a plain record and cast once at the end, deliberately.
|
|
761
|
+
* `NodeJS.ProcessEnv` is widened by `expo-modules-core` to REQUIRE `NODE_ENV`
|
|
762
|
+
* under the test tsconfig, so an empty literal of that type does not compile
|
|
763
|
+
* there even though it compiles for this package alone.
|
|
764
|
+
*/
|
|
765
|
+
const out = {};
|
|
766
|
+
for (const [k, v] of Object.entries(base)) {
|
|
767
|
+
if (!/^GIT_/i.test(k) || AMBIENT_GIT_KEPT.some((p) => p.test(k)))
|
|
768
|
+
out[k] = v;
|
|
769
|
+
}
|
|
770
|
+
// Never block a daemon on a credential prompt nobody can answer.
|
|
771
|
+
out['GIT_TERMINAL_PROMPT'] = '0';
|
|
772
|
+
/**
|
|
773
|
+
* PINNED, so that the repository this file CHECKED and the repository git
|
|
774
|
+
* OPERATES ON are the same one by construction.
|
|
775
|
+
*
|
|
776
|
+
* Only when the layout resolved: on `fault` the caller's two checks refuse
|
|
777
|
+
* before this matters, and on `none` nothing is pinned so git produces its own
|
|
778
|
+
* error exactly as before. The pair comes from `resolveGitLayout`, which was
|
|
779
|
+
* verified against `git rev-parse --absolute-git-dir --show-toplevel` in both
|
|
780
|
+
* an ordinary checkout and a linked worktree.
|
|
781
|
+
*
|
|
782
|
+
* ⚠️ THIS DISABLES GIT'S OWN DISCOVERY for looply's commands, which is safe
|
|
783
|
+
* only because `repoRootTroubleAt` already guarantees the cwd is the
|
|
784
|
+
* repository root. Relax that check and this becomes load-bearing in a way it
|
|
785
|
+
* is not today.
|
|
786
|
+
*/
|
|
787
|
+
if (cwd !== undefined) {
|
|
788
|
+
const layout = resolveGitLayout(cwd);
|
|
789
|
+
if (layout.kind === 'ok') {
|
|
790
|
+
out['GIT_DIR'] = layout.gitDir;
|
|
791
|
+
out['GIT_WORK_TREE'] = layout.root;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return out;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* THE MAIN WORKTREE'S ROOT — the directory a project's shared state belongs to.
|
|
798
|
+
*
|
|
799
|
+
* 🔴 A LINKED WORKTREE IS THE SAME PROJECT, and almost nothing in looply knew
|
|
800
|
+
* that. Project identity, the loop-safety history, the single-use exemption
|
|
801
|
+
* ledger, the human's control file and the ledger the daemon uploads are all
|
|
802
|
+
* derived from the working directory — so running in a worktree silently
|
|
803
|
+
* produced a DIFFERENT project with a fresh safety history, unspent exemptions,
|
|
804
|
+
* a control file nobody writes and events nobody uploads.
|
|
805
|
+
*
|
|
806
|
+
* The main worktree is where the common dir lives: for `<root>/.git` the answer
|
|
807
|
+
* is `<root>`. A linked worktree's common dir is that same `<root>/.git`, so it
|
|
808
|
+
* resolves to the same place — which is the whole point.
|
|
809
|
+
*
|
|
810
|
+
* ⚠️ NULL, NOT A GUESS, when there is no repository or its metadata cannot be
|
|
811
|
+
* read. The caller then keeps whatever it does today, so a non-repository
|
|
812
|
+
* directory behaves exactly as it did before this existed.
|
|
813
|
+
*
|
|
814
|
+
* ⚠️ A BARE OR SEPARATE-GITDIR REPOSITORY HAS NO MAIN WORKTREE ROOT. When the
|
|
815
|
+
* common dir is not named `.git`, this returns null rather than inventing a
|
|
816
|
+
* parent directory that may hold nothing at all.
|
|
817
|
+
*/
|
|
818
|
+
function mainWorktreeRoot(cwd) {
|
|
819
|
+
const layout = resolveGitLayout(cwd);
|
|
820
|
+
if (layout.kind !== 'ok')
|
|
821
|
+
return null;
|
|
822
|
+
const common = (0, node_path_1.resolve)(layout.commonDir);
|
|
823
|
+
if ((0, node_path_1.basename)(common).toLowerCase() !== '.git')
|
|
824
|
+
return null;
|
|
825
|
+
return (0, node_path_1.dirname)(common);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* WHAT ALL THE WORKTREES OF ONE REPOSITORY SHARE, when no main worktree exists.
|
|
829
|
+
*
|
|
830
|
+
* 🔴 `mainWorktreeRoot` RETURNING NULL WAS TREATED AS "NO SHARING NEEDED", and
|
|
831
|
+
* those are different statements. A repository can have several worktrees and no
|
|
832
|
+
* main one — `git clone --bare` then `git worktree add` twice is the ordinary
|
|
833
|
+
* way to get there — and for those, "no main root" was silently answered by
|
|
834
|
+
* putting the project's state inside each checkout. That is exactly the split
|
|
835
|
+
* the project/execution divide exists to prevent: a fresh loop-safety history
|
|
836
|
+
* per checkout, and one single-use exemption spendable once per checkout.
|
|
837
|
+
*
|
|
838
|
+
* The common dir is the identity every worktree of a repository agrees on, so it
|
|
839
|
+
* is what the shared location is keyed by.
|
|
840
|
+
*
|
|
841
|
+
* ⚠️ SEPARATE-GIT-DIR AND SUBMODULE LAYOUTS DO NOT REACH HERE. Both set
|
|
842
|
+
* `core.worktree`, which `repoConfigTrouble` refuses by name before any of this
|
|
843
|
+
* runs. The case this actually serves is the bare repository with linked
|
|
844
|
+
* worktrees, which nothing refuses and which was silently mis-scoped.
|
|
845
|
+
*
|
|
846
|
+
* ⚠️ NULL FOR AN ORDINARY CHECKOUT — `gitDir === commonDir` — even though one
|
|
847
|
+
* exists. There is a single worktree there, so the execution's own directory IS
|
|
848
|
+
* the project's, and answering anything else would move state that is correct
|
|
849
|
+
* where it sits.
|
|
850
|
+
*/
|
|
851
|
+
/**
|
|
852
|
+
* THE PATH THAT IDENTIFIES A REPOSITORY, whichever of its checkouts you are in.
|
|
853
|
+
*
|
|
854
|
+
* 🔴 PROJECT IDENTITY WAS THE CWD, AND A WORKTREE IS NOT A NEW PROJECT. Both
|
|
855
|
+
* `repoPathHash` (the server's project key) and `projectKeyFor` (the project
|
|
856
|
+
* memory key) hashed the directory the command happened to run in. So
|
|
857
|
+
* `looply run --in <workspace>` registered a SECOND project on the server for
|
|
858
|
+
* the same repository, uploaded its events there, and read an empty project
|
|
859
|
+
* memory — while the person watching saw one project stop producing evidence.
|
|
860
|
+
*
|
|
861
|
+
* ⚠️ AN ORDINARY CHECKOUT HASHES TO EXACTLY WHAT IT ALWAYS DID. The main
|
|
862
|
+
* worktree root of a normal repository IS its root, so every existing key,
|
|
863
|
+
* every cached `projects` entry in the machine store and every server-side
|
|
864
|
+
* project survives untouched. Only a linked worktree's answer changes, and it
|
|
865
|
+
* changes from "a project of its own" to "the project it was cut from".
|
|
866
|
+
*
|
|
867
|
+
* ⚠️ AND A NON-REPOSITORY IS UNCHANGED TOO — no layout, no answer, the path
|
|
868
|
+
* comes back as given. Tests that pass invented directories keep working, which
|
|
869
|
+
* is deliberate: this must not require a repository to be callable.
|
|
870
|
+
*/
|
|
871
|
+
/**
|
|
872
|
+
* Whether this directory IS a checkout's root — ordinary or linked.
|
|
873
|
+
*
|
|
874
|
+
* `resolveGitLayout` walks up, so every directory inside a repository has a
|
|
875
|
+
* layout. This is the question that separates `<repo>` from `<repo>/packages/a`,
|
|
876
|
+
* and it is the one both the project's identity and the project's state have to
|
|
877
|
+
* agree on or one of them is lying about what a project is.
|
|
878
|
+
*/
|
|
879
|
+
function isCheckoutRoot(cwd) {
|
|
880
|
+
const layout = resolveGitLayout(cwd);
|
|
881
|
+
return layout.kind === 'ok' && (0, node_path_1.resolve)(layout.root) === (0, node_path_1.resolve)(cwd);
|
|
882
|
+
}
|
|
883
|
+
function repoIdentityPath(cwd) {
|
|
884
|
+
const here = (0, node_path_1.resolve)(cwd);
|
|
885
|
+
const layout = resolveGitLayout(here);
|
|
886
|
+
if (layout.kind !== 'ok')
|
|
887
|
+
return here;
|
|
888
|
+
/**
|
|
889
|
+
* ⚠️ A SUBDIRECTORY RESOLVES TO ITS REPOSITORY, and that is deliberate — but
|
|
890
|
+
* it was briefly changed to the opposite, so the reasoning is recorded here.
|
|
891
|
+
*
|
|
892
|
+
* `resolveGitLayout` walks up, so `<repo>/packages/a` answers `<repo>`. An
|
|
893
|
+
* adversarial pass called that a compatibility break: two directories that had
|
|
894
|
+
* always been two projects, merged onto one key.
|
|
895
|
+
*
|
|
896
|
+
* MEASURED, AND THE PREMISE IS FALSE. Looply cannot run outside a checkout
|
|
897
|
+
* root — `repoRootTroubleAt` refuses every git verb from a subdirectory, and
|
|
898
|
+
* the config is read from `<cwd>/.looply/config.yaml`, which a subdirectory
|
|
899
|
+
* does not have. So no subdirectory has ever registered a server project,
|
|
900
|
+
* filed a project memory, or held state; there is nothing there to merge.
|
|
901
|
+
*
|
|
902
|
+
* Requiring the root instead broke the one thing subdirectories ARE used for:
|
|
903
|
+
* a person typing `looply stop` or `looply runs` from inside their tree.
|
|
904
|
+
* Those commands are not guarded by the root check, and with the narrowing
|
|
905
|
+
* they addressed `<subdir>/.looply/state` — so the brake silently wrote a file
|
|
906
|
+
* no run would ever read, and reported "no run is going" while one was.
|
|
907
|
+
* Measured on the shipped build before it was reverted.
|
|
908
|
+
*/
|
|
909
|
+
return mainWorktreeRoot(here) ?? linkedRepoCommonDir(here) ?? here;
|
|
910
|
+
}
|
|
911
|
+
function linkedRepoCommonDir(cwd) {
|
|
912
|
+
const layout = resolveGitLayout(cwd);
|
|
913
|
+
if (layout.kind !== 'ok')
|
|
914
|
+
return null;
|
|
915
|
+
const common = (0, node_path_1.resolve)(layout.commonDir);
|
|
916
|
+
if ((0, node_path_1.resolve)(layout.gitDir) === common)
|
|
917
|
+
return null;
|
|
918
|
+
return common;
|
|
919
|
+
}
|
|
920
|
+
//# sourceMappingURL=trusted-git.js.map
|