spexcode 0.6.2 → 0.6.5
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/README.md +3 -1
- package/bin/spex.mjs +4 -0
- package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
- package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
- package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
- package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
- package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
- package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
- package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
- package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
- package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
- package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
- package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
- package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
- package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
- package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
- package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
- package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
- package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
- package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
- package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
- package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
- package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
- package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
- package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
- package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
- package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
- package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
- package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
- package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
- package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
- package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
- package/node_modules/@spexcode/spec-cli/package.json +47 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
- package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
- package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
- package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
- package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
- package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
- package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
- package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
- package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
- package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
- package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
- package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
- package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
- package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
- package/node_modules/@spexcode/spec-core/package.json +28 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
- package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
- package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
- package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
- package/node_modules/@spexcode/spec-eval/package.json +36 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
- package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
- package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
- package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
- package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
- package/node_modules/@spexcode/spec-forge/package.json +32 -0
- package/package.json +33 -19
- package/spec-cli/bin/spex.mjs +0 -69
- package/spec-cli/src/anchors.ts +0 -728
- package/spec-cli/src/attach.ts +0 -43
- package/spec-cli/src/claude-headless.ts +0 -321
- package/spec-cli/src/cli.ts +0 -1258
- package/spec-cli/src/client.ts +0 -414
- package/spec-cli/src/cockpit.ts +0 -43
- package/spec-cli/src/codex-runtime-generations.ts +0 -646
- package/spec-cli/src/commit-surgery.ts +0 -68
- package/spec-cli/src/contract-filter.ts +0 -202
- package/spec-cli/src/delivery-queue.ts +0 -165
- package/spec-cli/src/doctor.ts +0 -516
- package/spec-cli/src/execution-trace.ts +0 -444
- package/spec-cli/src/file-write.ts +0 -22
- package/spec-cli/src/gateway-auth.ts +0 -181
- package/spec-cli/src/gateway-hub.ts +0 -358
- package/spec-cli/src/gateway.ts +0 -461
- package/spec-cli/src/git.ts +0 -2556
- package/spec-cli/src/graph.ts +0 -271
- package/spec-cli/src/graphCache.ts +0 -710
- package/spec-cli/src/graphDelta.ts +0 -77
- package/spec-cli/src/graphStream.ts +0 -1019
- package/spec-cli/src/harness-select.ts +0 -77
- package/spec-cli/src/harness.ts +0 -3025
- package/spec-cli/src/headless-controller.ts +0 -63
- package/spec-cli/src/help.ts +0 -492
- package/spec-cli/src/host-resources.ts +0 -762
- package/spec-cli/src/host.ts +0 -611
- package/spec-cli/src/identity-presets.d.ts +0 -13
- package/spec-cli/src/identity-presets.js +0 -138
- package/spec-cli/src/index.ts +0 -818
- package/spec-cli/src/init.ts +0 -258
- package/spec-cli/src/issues-cli.ts +0 -276
- package/spec-cli/src/issues.ts +0 -237
- package/spec-cli/src/layout.ts +0 -640
- package/spec-cli/src/lint.ts +0 -488
- package/spec-cli/src/localIssues.ts +0 -538
- package/spec-cli/src/materialize.ts +0 -417
- package/spec-cli/src/mentions.ts +0 -81
- package/spec-cli/src/opencode-headless.ts +0 -205
- package/spec-cli/src/pi-headless.ts +0 -153
- package/spec-cli/src/plugin-harness.ts +0 -157
- package/spec-cli/src/process-identity.ts +0 -207
- package/spec-cli/src/project-identity.ts +0 -73
- package/spec-cli/src/project-store.ts +0 -17
- package/spec-cli/src/pty-bridge.ts +0 -396
- package/spec-cli/src/pty-helper.mjs +0 -96
- package/spec-cli/src/pty-native-helper.mjs +0 -22
- package/spec-cli/src/ranker.ts +0 -191
- package/spec-cli/src/reaper.ts +0 -125
- package/spec-cli/src/resilience.ts +0 -41
- package/spec-cli/src/reviewSnapshot.ts +0 -28
- package/spec-cli/src/reviews.ts +0 -434
- package/spec-cli/src/runtime-guard.ts +0 -35
- package/spec-cli/src/runtime-ownership.ts +0 -88
- package/spec-cli/src/search.bench.mjs +0 -80
- package/spec-cli/src/search.ts +0 -63
- package/spec-cli/src/session-cursors.ts +0 -91
- package/spec-cli/src/session-execution.ts +0 -68
- package/spec-cli/src/session-files.ts +0 -121
- package/spec-cli/src/session-follow.ts +0 -182
- package/spec-cli/src/session-reparent.ts +0 -33
- package/spec-cli/src/session-timeline.ts +0 -247
- package/spec-cli/src/session-web.ts +0 -135
- package/spec-cli/src/sessions.ts +0 -3883
- package/spec-cli/src/sh.ts +0 -3
- package/spec-cli/src/slash-commands.ts +0 -318
- package/spec-cli/src/source-files.ts +0 -104
- package/spec-cli/src/specs.ts +0 -498
- package/spec-cli/src/supervise.ts +0 -245
- package/spec-cli/src/tree.ts +0 -118
- package/spec-cli/src/tsx-bin.ts +0 -18
- package/spec-cli/src/uninstall.ts +0 -171
- package/spec-cli/src/uploads.ts +0 -253
- package/spec-cli/src/worktree-sources.ts +0 -42
- package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
- package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
- package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
- package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
- package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
- package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
- package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
- package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
- package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
- package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
- package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
- package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
- package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
- package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
- package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
- package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
- package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
- package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
- package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
- package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
- package/spec-dashboard/dist/index.html +0 -29
- package/spec-dashboard/src/reviewFilters.js +0 -322
- package/spec-dashboard/src/reviewQuery.js +0 -174
- package/spec-dashboard/src/session.js +0 -199
- package/spec-eval/src/cache.ts +0 -57
- package/spec-eval/src/cli.ts +0 -894
- package/spec-eval/src/evaltab.ts +0 -264
- package/spec-eval/src/filing.ts +0 -39
- package/spec-eval/src/freshness.ts +0 -836
- package/spec-eval/src/humanok.ts +0 -28
- package/spec-eval/src/scenariofresh.ts +0 -244
- package/spec-eval/src/scenarios.ts +0 -672
- package/spec-eval/src/sessioneval.ts +0 -2220
- package/spec-eval/src/sidecar.ts +0 -93
- package/spec-eval/src/timeline.ts +0 -54
- package/spec-forge/src/__fixtures__/github-forge.json +0 -63
- package/spec-forge/src/cache.ts +0 -66
- package/spec-forge/src/cli.ts +0 -99
- package/spec-forge/src/drivers/github.ts +0 -132
- package/spec-forge/src/drivers/gitlab.ts +0 -153
- package/spec-forge/src/drivers.ts +0 -73
- package/spec-forge/src/links.ts +0 -72
- package/spec-forge/src/needs-eval.ts +0 -29
- package/spec-forge/src/port.ts +0 -36
- package/spec-forge/src/resident.ts +0 -46
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process'
|
|
2
|
-
import { relative } from 'node:path'
|
|
3
|
-
import { materialize, stripSpexcodeBlock, GENERATED_MARK } from './materialize.js'
|
|
4
|
-
import { HARNESSES } from './harness.js'
|
|
5
|
-
import { gitBinary } from './git.js'
|
|
6
|
-
|
|
7
|
-
// GIT ENV, deliberately INVERTED from git.ts's git(): every call here PRESERVES the hook's environment —
|
|
8
|
-
// GIT_INDEX_FILE must be honored so the surgery reads/writes the EXACT index this commit is being built
|
|
9
|
-
// from (a `git commit <path>` pathspec commit and `git commit -a` both run hooks against a TEMPORARY index;
|
|
10
|
-
// operating on the real one would silently miss them). git.ts strips that env for repo DISCOVERY reasons;
|
|
11
|
-
// index surgery is the one place the env is the point.
|
|
12
|
-
const raw = (args: string[], input?: string): string =>
|
|
13
|
-
execFileSync(gitBinary(process.env), args, { input, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
|
|
14
|
-
|
|
15
|
-
function inHead(p: string): boolean {
|
|
16
|
-
try { raw(['cat-file', '-e', `HEAD:${p}`]); return true } catch { return false }
|
|
17
|
-
}
|
|
18
|
-
function stagedBlob(p: string): string | null {
|
|
19
|
-
try { return raw(['show', `:${p}`]) } catch { return null }
|
|
20
|
-
}
|
|
21
|
-
function evict(p: string, why: string): void {
|
|
22
|
-
raw(['update-index', '--force-remove', '--', p])
|
|
23
|
-
console.error(`spexcode: unstaged ${p} (${why} — generated artifacts are never tracked; the file stays on disk)`)
|
|
24
|
-
}
|
|
25
|
-
function replaceBlob(p: string, content: string): void {
|
|
26
|
-
const stage = raw(['ls-files', '--stage', '--', p]).trim() // "100644 <sha> 0\t<p>"
|
|
27
|
-
const mode = stage.split(/\s/)[0] || '100644'
|
|
28
|
-
const sha = raw(['hash-object', '-w', '--stdin'], content).trim()
|
|
29
|
-
raw(['update-index', '--cacheinfo', `${mode},${sha},${p}`])
|
|
30
|
-
console.error(`spexcode: stripped the <!-- spexcode --> block from staged ${p} (the block is working-tree context, never history)`)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function commitSurgery(proj = process.cwd()): void {
|
|
34
|
-
// (1) unconditional materialize — machine-fixable state (exclude entries, filter binding, kind flips)
|
|
35
|
-
// is repaired BEFORE the index is inspected, so the inspection below judges against fresh masks.
|
|
36
|
-
try { materialize(proj) } catch (e) {
|
|
37
|
-
console.error(`spexcode: pre-commit materialize failed (${(e as Error).message}) — footprint may be stale this commit`)
|
|
38
|
-
}
|
|
39
|
-
const staged = raw(['diff', '--cached', '--name-only', '-z']).split('\0').filter(Boolean)
|
|
40
|
-
if (!staged.length) return
|
|
41
|
-
const rel = (f: string) => relative(proj, f)
|
|
42
|
-
const contracts = new Set(HARNESSES.flatMap((h) => h.contractFiles(proj)).map(rel))
|
|
43
|
-
const machine = new Set<string>(['spexcode.local.json', '.session'])
|
|
44
|
-
for (const h of HARNESSES) {
|
|
45
|
-
machine.add(rel(h.shimFile(proj)))
|
|
46
|
-
const a = h.worktreeHookAnchor(proj)
|
|
47
|
-
if (a) machine.add(rel(a))
|
|
48
|
-
}
|
|
49
|
-
const generatedDirs = HARNESSES.flatMap((h) => [h.skillDir(proj), h.agentDir(proj)])
|
|
50
|
-
.filter((d): d is string => !!d).map((d) => `${rel(d)}/`)
|
|
51
|
-
for (const p of staged) {
|
|
52
|
-
if (contracts.has(p)) {
|
|
53
|
-
const blob = stagedBlob(p)
|
|
54
|
-
if (blob === null) continue
|
|
55
|
-
const stripped = stripSpexcodeBlock(blob)
|
|
56
|
-
if (stripped === blob) continue // no block staged — clean already did its job
|
|
57
|
-
if (!stripped.trim() && !inHead(p)) evict(p, 'wholly a spexcode materialized artifact')
|
|
58
|
-
else replaceBlob(p, stripped)
|
|
59
|
-
} else if (machine.has(p) || p.startsWith('.worktrees/')) {
|
|
60
|
-
if (!inHead(p)) evict(p, 'a machine-local spexcode file')
|
|
61
|
-
else console.error(`spexcode: ${p} is a machine-local spexcode file but HEAD already tracks it — not touching a tracked file; untrack it yourself (git rm --cached ${p})`)
|
|
62
|
-
} else if (generatedDirs.some((d) => p.startsWith(d))) {
|
|
63
|
-
if (inHead(p)) continue // historically tracked — the host's call
|
|
64
|
-
const blob = stagedBlob(p)
|
|
65
|
-
if (blob !== null && blob.includes(GENERATED_MARK)) evict(p, 'a generated skill/agent artifact')
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, rmSync, chmodSync, existsSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
-
import { join, relative } from 'node:path'
|
|
3
|
-
import { execFileSync } from 'node:child_process'
|
|
4
|
-
import { git, gitBinary } from './git.js'
|
|
5
|
-
import { writeManagedBlock, removeManagedBlock } from './harness.js'
|
|
6
|
-
import { encodeProject, runtimeRoot, treeSlotDir } from './layout.js'
|
|
7
|
-
import { writeFileIfChanged } from './file-write.js'
|
|
8
|
-
|
|
9
|
-
// the three field-sharpened edges this module owes ([[content-filter]]):
|
|
10
|
-
// ① the configured command points at a STABLE shim path and degrades to `cat` (identity) when the shim is
|
|
11
|
-
// missing — a bare missing filter command makes git spray "cannot fork" fatals on EVERY operation;
|
|
12
|
-
// ② a changed contract does NOT propagate by itself (git re-smudges only on checkout) — materialize's
|
|
13
|
-
// re-materialize writes the managed block straight into the working file (writeManagedBlock IS the re-smudge),
|
|
14
|
-
// and this module refreshes the block file the shim reads so future checkouts agree;
|
|
15
|
-
// ③ unplanting must strip the block from the WORKING FILES before the config goes away, or the block
|
|
16
|
-
// residue surfaces as an uncommitted modification — the caller (dematerialize) removes the managed
|
|
17
|
-
// blocks first and only then calls removeContractFilter.
|
|
18
|
-
|
|
19
|
-
function commonDirOf(proj: string): string {
|
|
20
|
-
return git(['-C', proj, 'rev-parse', '--path-format=absolute', '--git-common-dir']).trim()
|
|
21
|
-
}
|
|
22
|
-
const filterDir = (common: string) => join(common, 'spexcode')
|
|
23
|
-
const shimPath = (common: string) => join(filterDir(common), 'contract-filter.sh')
|
|
24
|
-
const rootPath = (common: string) => join(filterDir(common), 'contract-filter-root')
|
|
25
|
-
const bindingsPath = (common: string) => join(filterDir(common), 'contract-filter-bindings')
|
|
26
|
-
const treeFilterDir = (proj: string) => join(treeSlotDir(proj), 'contract-filter')
|
|
27
|
-
const attributesPath = (common: string) => join(common, 'info', 'attributes')
|
|
28
|
-
|
|
29
|
-
export type ContractFilterPayload = { file: string; content: string }
|
|
30
|
-
export type ContractFilterBinding = { file: string; start: string; end: string }
|
|
31
|
-
|
|
32
|
-
// The common shim both filter directions run through. Pure shell/awk (no node boot on git's hot path), it
|
|
33
|
-
// resolves the invoking checkout to that tree's payload before mirroring managed-block normalization:
|
|
34
|
-
// clean : drop the sentinel block (+ the blank line smudge printed before it) → the pristine host prose.
|
|
35
|
-
// smudge: clean first (a block already in the index can never double-inject), then append the tree payload.
|
|
36
|
-
// Byte-exactness holds for text ending in exactly one newline (git's own well-formed-text shape); a pristine
|
|
37
|
-
// file ending in zero or 2+ newlines is normalized to one on the first round-trip and stable after.
|
|
38
|
-
const SHIM = `#!/usr/bin/env bash
|
|
39
|
-
# spexcode managed-text filter (generated by spex materialize; see [[content-filter]]).
|
|
40
|
-
# One common driver selects the payload belonging to the Git checkout that invoked it.
|
|
41
|
-
set -u
|
|
42
|
-
mode="\${1:?usage: contract-filter.sh smudge|clean <path>}"
|
|
43
|
-
path="\${2:?usage: contract-filter.sh smudge|clean <path>}"
|
|
44
|
-
here="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
45
|
-
binding="$(awk -F '\t' -v p="$path" '$1 == p { print $2 "\t" $3; exit }' "$here/contract-filter-bindings" 2>/dev/null)"
|
|
46
|
-
[ -n "$binding" ] || { cat; exit 0; }
|
|
47
|
-
start="\${binding%%$'\t'*}"; rest="\${binding#*$'\t'}"
|
|
48
|
-
end="$rest"
|
|
49
|
-
top="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
50
|
-
root="$(cat "$here/contract-filter-root" 2>/dev/null || true)"
|
|
51
|
-
key="$(printf '%s' "$top" | sed 's#[/.]#-#g')"
|
|
52
|
-
manifest="$root/trees/$key/contract-filter/manifest"
|
|
53
|
-
payload="$(awk -F '\t' -v p="$path" '$1 == p { print $2; exit }' "$manifest" 2>/dev/null)"
|
|
54
|
-
strip() {
|
|
55
|
-
awk -v sline="$start" -v eline="$end" 'BEGIN { n = 0 }
|
|
56
|
-
{ lines[n++] = $0 }
|
|
57
|
-
END {
|
|
58
|
-
s = -1; e = -1
|
|
59
|
-
for (i = 0; i < n; i++) {
|
|
60
|
-
if (lines[i] == sline && s < 0) s = i
|
|
61
|
-
if (lines[i] == eline && s >= 0 && e < 0) e = i
|
|
62
|
-
}
|
|
63
|
-
if (s >= 0 && e >= s) {
|
|
64
|
-
a = s; while (a > 0 && lines[a-1] == "") a--
|
|
65
|
-
b = e; while (b + 1 < n && lines[b+1] == "") b++
|
|
66
|
-
j = 0
|
|
67
|
-
for (i = 0; i < n; i++) if (i < a || i > b) lines[j++] = lines[i]
|
|
68
|
-
n = j
|
|
69
|
-
}
|
|
70
|
-
for (i = 0; i < n; i++) print lines[i]
|
|
71
|
-
}'
|
|
72
|
-
}
|
|
73
|
-
case "$mode" in
|
|
74
|
-
clean) strip ;;
|
|
75
|
-
smudge)
|
|
76
|
-
if [ ! -r "$payload" ]; then strip; exit 0; fi
|
|
77
|
-
strip | awk -v b="$payload" -v sline="$start" -v eline="$end" 'BEGIN { n = 0 }
|
|
78
|
-
{ lines[n++] = $0 }
|
|
79
|
-
END {
|
|
80
|
-
while (n > 0 && lines[n-1] == "") n--
|
|
81
|
-
for (i = 0; i < n; i++) print lines[i]
|
|
82
|
-
if (n > 0) print ""
|
|
83
|
-
print sline
|
|
84
|
-
while ((getline l < b) > 0) print l
|
|
85
|
-
print eline
|
|
86
|
-
}' ;;
|
|
87
|
-
*) echo "contract-filter.sh: unknown mode $mode" >&2; exit 1 ;;
|
|
88
|
-
esac
|
|
89
|
-
`
|
|
90
|
-
|
|
91
|
-
// edge ①: the command git runs is a tolerant wrapper — the shim path is an ARGUMENT ($0), and a missing/
|
|
92
|
-
// unreadable shim degrades to `cat` (identity) instead of a per-operation fatal.
|
|
93
|
-
const filterCmd = (shim: string, mode: 'smudge' | 'clean') =>
|
|
94
|
-
`sh -c 'test -r "$0" && exec bash "$0" ${mode} "$1" || exec cat' '${shim.replace(/'/g, `'\\''`)}' %f`
|
|
95
|
-
|
|
96
|
-
// plant (or refresh) the filter for the given contract files (tracked, or untracked-with-host-content —
|
|
97
|
-
// pre-armed): the shim + the block content it smudges, the per-clone git config, and the attribute lines
|
|
98
|
-
// binding each file to the filter. `contract` is the assembled block body (guide + surface:system).
|
|
99
|
-
// settleIndexStat skips untracked entries (no index blob) by design and only runs after a real filter change.
|
|
100
|
-
export function plantContractFilter(proj: string, payloads: ContractFilterPayload[], bindings: ContractFilterBinding[], changedFiles: readonly string[] = []): boolean {
|
|
101
|
-
const common = commonDirOf(proj)
|
|
102
|
-
mkdirSync(filterDir(common), { recursive: true })
|
|
103
|
-
let changed = writeFileIfChanged(shimPath(common), SHIM)
|
|
104
|
-
if (changed || (statSync(shimPath(common)).mode & 0o777) !== 0o755) chmodSync(shimPath(common), 0o755)
|
|
105
|
-
const commands = new Map([
|
|
106
|
-
['filter.spexcode.smudge', filterCmd(shimPath(common), 'smudge')],
|
|
107
|
-
['filter.spexcode.clean', filterCmd(shimPath(common), 'clean')],
|
|
108
|
-
])
|
|
109
|
-
const configured = new Map<string, string>()
|
|
110
|
-
try {
|
|
111
|
-
for (const row of git(['-C', proj, 'config', '--get-regexp', '^filter\\.spexcode\\.(smudge|clean)$']).trimEnd().split('\n')) {
|
|
112
|
-
const at = row.indexOf(' ')
|
|
113
|
-
if (at > 0) configured.set(row.slice(0, at), row.slice(at + 1))
|
|
114
|
-
}
|
|
115
|
-
} catch {}
|
|
116
|
-
for (const [key, command] of commands) {
|
|
117
|
-
if (configured.get(key) === command) continue
|
|
118
|
-
git(['-C', proj, 'config', key, command])
|
|
119
|
-
changed = true
|
|
120
|
-
}
|
|
121
|
-
changed = writeFileIfChanged(bindingsPath(common), bindings.map((b) => `${b.file}\t${b.start}\t${b.end}`).join('\n') + '\n') || changed
|
|
122
|
-
const dir = treeFilterDir(proj)
|
|
123
|
-
mkdirSync(dir, { recursive: true })
|
|
124
|
-
const manifest: string[] = []
|
|
125
|
-
const wanted = new Set(['manifest'])
|
|
126
|
-
for (const [i, payload] of payloads.entries()) {
|
|
127
|
-
const target = join(dir, String(i))
|
|
128
|
-
wanted.add(String(i))
|
|
129
|
-
changed = writeFileIfChanged(target, payload.content.endsWith('\n') ? payload.content : `${payload.content}\n`) || changed
|
|
130
|
-
manifest.push(`${payload.file}\t${target}`)
|
|
131
|
-
}
|
|
132
|
-
for (const entry of readdirSync(dir)) {
|
|
133
|
-
if (!wanted.has(entry)) { rmSync(join(dir, entry), { recursive: true, force: true }); changed = true }
|
|
134
|
-
}
|
|
135
|
-
const manifestPath = join(dir, 'manifest')
|
|
136
|
-
changed = writeFileIfChanged(manifestPath, manifest.join('\n') + (manifest.length ? '\n' : '')) || changed
|
|
137
|
-
changed = writeFileIfChanged(rootPath(common), `${runtimeRoot(proj)}\n`) || changed
|
|
138
|
-
// Attribute patterns are checkout-relative; the stable binding set is safe in the common git dir because
|
|
139
|
-
// the driver selects a payload from the invoking checkout's tree slot.
|
|
140
|
-
const entries = bindings.map((b) => `/${b.file} filter=spexcode`).sort().join('\n')
|
|
141
|
-
mkdirSync(join(common, 'info'), { recursive: true })
|
|
142
|
-
changed = writeManagedBlock(attributesPath(common), entries, ['# ', '']) || changed
|
|
143
|
-
const settle = changed ? payloads.map((p) => join(proj, p.file)) : changedFiles
|
|
144
|
-
if (settle.length) settleIndexStat(proj, [...new Set(settle)])
|
|
145
|
-
return changed
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// settle the index STAT for each file — the famous filtered-path phantom-`M`: git cannot verify a
|
|
149
|
-
// clean-filtered path by stat alone (worktree size ≠ blob size by design), and `git status` reports such an
|
|
150
|
-
// entry modified FOREVER without ever content-checking it (field-verified on git 2.43; `git diff` meanwhile
|
|
151
|
-
// runs the filter and shows nothing; even `update-index --really-refresh` leaves it). The block-strip side of
|
|
152
|
-
// a mode switch/backout leaves the same stale stat on the then-unfiltered path. `git add --renormalize`
|
|
153
|
-
// re-cleans the file and refreshes the cached stat — run ONLY when the (possibly filtered) worktree already
|
|
154
|
-
// EQUALS the index blob, so it is a pure stat refresh that can never stage a user's real unstaged edit (a
|
|
155
|
-
// genuine edit keeps its honest `M`). Exported for dematerialize (the unplant side); plant calls it below.
|
|
156
|
-
// Best-effort: an unsettled stat is cosmetic noise, never corruption.
|
|
157
|
-
export function settleIndexStat(proj: string, files: string[]): void {
|
|
158
|
-
const env = { ...process.env }
|
|
159
|
-
delete env.GIT_DIR; delete env.GIT_WORK_TREE; delete env.GIT_INDEX_FILE; delete env.GIT_OBJECT_DIRECTORY
|
|
160
|
-
for (const f of files) {
|
|
161
|
-
const rel = relative(proj, f)
|
|
162
|
-
try {
|
|
163
|
-
const indexBlob = git(['-C', proj, 'rev-parse', `:${rel}`]).trim()
|
|
164
|
-
const filtered = execFileSync(gitBinary(env), ['-C', proj, 'hash-object', '--path', rel, '--stdin'],
|
|
165
|
-
{ input: readFileSync(f), env, stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
166
|
-
if (indexBlob === filtered) git(['-C', proj, 'add', '--renormalize', '--', rel])
|
|
167
|
-
} catch { /* best-effort */ }
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// the full inverse (edge ③ — call AFTER the managed blocks left the working files): attribute lines out,
|
|
172
|
-
// config keys unset, shim + block content removed. `<common>/spexcode/` may host other spexcode data
|
|
173
|
-
// (evidence blobs), so only OUR two files go, never the dir.
|
|
174
|
-
export function clearContractFilterPayload(proj: string, files: string[] = []): void {
|
|
175
|
-
try { rmSync(treeFilterDir(proj), { recursive: true, force: true }) } catch { /* inaccessible tree */ }
|
|
176
|
-
settleIndexStat(proj, files)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export function removeContractFilter(proj: string, files: string[] = [], final = false): void {
|
|
180
|
-
let common: string
|
|
181
|
-
try { common = commonDirOf(proj) } catch { return } // not a git repo → nothing was ever planted
|
|
182
|
-
clearContractFilterPayload(proj, files)
|
|
183
|
-
const anotherPayload = final ? false : (() => {
|
|
184
|
-
const rows = git(['-C', proj, 'worktree', 'list', '--porcelain', '-z']).split('\0')
|
|
185
|
-
const root = runtimeRoot(proj)
|
|
186
|
-
return rows
|
|
187
|
-
.filter((row) => row.startsWith('worktree '))
|
|
188
|
-
.map((row) => row.slice('worktree '.length))
|
|
189
|
-
.some((tree) => existsSync(join(root, 'trees', encodeProject(tree), 'contract-filter', 'manifest')))
|
|
190
|
-
})()
|
|
191
|
-
if (!final && anotherPayload) return
|
|
192
|
-
removeManagedBlock(attributesPath(common), ['# ', ''], true)
|
|
193
|
-
for (const key of ['filter.spexcode.smudge', 'filter.spexcode.clean']) {
|
|
194
|
-
try { git(['-C', proj, 'config', '--unset-all', key]) } catch { /* not set — already clean */ }
|
|
195
|
-
}
|
|
196
|
-
for (const path of [shimPath(common), rootPath(common), bindingsPath(common)]) rmSync(path, { force: true })
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// is the filter currently planted? (the assert-side probe tests use; cheap: one config read)
|
|
200
|
-
export function contractFilterPlanted(proj: string): boolean {
|
|
201
|
-
try { return git(['-C', proj, 'config', 'filter.spexcode.clean']).trim().length > 0 } catch { return false }
|
|
202
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, writeSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { runtimeRoot, sessionArtifactPath, sessionStoreDir } from './layout.js'
|
|
4
|
-
|
|
5
|
-
// @@@ delivery-queue - what a session still OWES its agent. The log ([[session-timeline]]) is the record and
|
|
6
|
-
// grows forever; this is the debt and is consumed, so it lives in its own small file whose resting state is
|
|
7
|
-
// EMPTY. Nothing here reads the log: an entry carries the text it will hand over, so history could be trimmed
|
|
8
|
-
// or archived without changing what is owed. A session that predates this mechanism owes nothing, because a
|
|
9
|
-
// queue is only ever filled by an enqueue — which is why no backlog migration exists.
|
|
10
|
-
|
|
11
|
-
export type PendingMessage = {
|
|
12
|
-
mid: string
|
|
13
|
-
text: string
|
|
14
|
-
from: string | null
|
|
15
|
-
dispatch?: { operation: 'merge'; requestDigest: string }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const queuePath = (id: string): string => sessionArtifactPath(id, 'pending.json')
|
|
19
|
-
const revokedSenderRoot = (): string => join(runtimeRoot(), '.revoked-senders')
|
|
20
|
-
const revokedSenderPath = (id: string): string => join(revokedSenderRoot(), id)
|
|
21
|
-
|
|
22
|
-
// @@@ its own lock, deliberately NOT the record lock - the drain holds this across the adapter insert, which
|
|
23
|
-
// is what makes "claim" real: two processes draining the same session cannot both hand over one message. The
|
|
24
|
-
// record lock could never span that call — a native turn runs lifecycle hooks that re-enter the record writer,
|
|
25
|
-
// and holding it there deadlocks the adapter's own confirmation. Nothing in the delivery path takes this one,
|
|
26
|
-
// so spanning the insert costs no contention. PID liveness reclaims a lock whose holder died mid-insert.
|
|
27
|
-
const lockRoot = (): string => join(runtimeRoot(), '.delivery-locks')
|
|
28
|
-
const lockPath = (id: string): string => join(lockRoot(), `${id}.lock`)
|
|
29
|
-
|
|
30
|
-
const pause = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))
|
|
31
|
-
|
|
32
|
-
async function acquire(id: string, timeoutMs: number): Promise<(() => void) | null> {
|
|
33
|
-
mkdirSync(lockRoot(), { recursive: true })
|
|
34
|
-
const path = lockPath(id), deadline = Date.now() + timeoutMs
|
|
35
|
-
for (;;) {
|
|
36
|
-
try {
|
|
37
|
-
const fd = openSync(path, 'wx')
|
|
38
|
-
writeSync(fd, String(process.pid))
|
|
39
|
-
closeSync(fd)
|
|
40
|
-
return () => { try { unlinkSync(path) } catch { /* a liveness reclaim already removed it */ } }
|
|
41
|
-
} catch (e) {
|
|
42
|
-
if ((e as NodeJS.ErrnoException).code !== 'EEXIST') throw e
|
|
43
|
-
let owner = 0
|
|
44
|
-
try { owner = Number(readFileSync(path, 'utf8').trim()) || 0 } catch { /* race with creator/releaser */ }
|
|
45
|
-
if (owner && owner !== process.pid) {
|
|
46
|
-
try { process.kill(owner, 0) } catch { try { unlinkSync(path) } catch { /* race */ }; continue }
|
|
47
|
-
}
|
|
48
|
-
// A drain is never urgent enough to fight for: whoever holds the lock is delivering these same messages,
|
|
49
|
-
// and the retry sweep will come back. Declining is not a lost message.
|
|
50
|
-
if (Date.now() >= deadline) return null
|
|
51
|
-
await pause(25)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function read(id: string): PendingMessage[] {
|
|
57
|
-
try {
|
|
58
|
-
const raw = JSON.parse(readFileSync(queuePath(id), 'utf8')) as unknown
|
|
59
|
-
if (!Array.isArray(raw)) return []
|
|
60
|
-
return raw.filter((m): m is PendingMessage =>
|
|
61
|
-
!!m && typeof m === 'object'
|
|
62
|
-
&& typeof (m as PendingMessage).mid === 'string'
|
|
63
|
-
&& typeof (m as PendingMessage).text === 'string'
|
|
64
|
-
&& ((m as PendingMessage).dispatch === undefined
|
|
65
|
-
|| ((m as PendingMessage).dispatch?.operation === 'merge'
|
|
66
|
-
&& typeof (m as PendingMessage).dispatch?.requestDigest === 'string')))
|
|
67
|
-
} catch { return [] } // absent, empty, or unparseable all mean the honest thing: nothing owed
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Written whole and atomically; an empty queue is REMOVED rather than left as `[]`, so "is anything owed?" is
|
|
71
|
-
// one existsSync on the sweep's hot path.
|
|
72
|
-
function write(id: string, msgs: PendingMessage[]): void {
|
|
73
|
-
const path = queuePath(id)
|
|
74
|
-
if (!msgs.length) { try { unlinkSync(path) } catch { /* already gone */ } ; return }
|
|
75
|
-
mkdirSync(sessionStoreDir(id), { recursive: true })
|
|
76
|
-
const tmp = `${path}.${process.pid}.tmp`
|
|
77
|
-
writeFileSync(tmp, JSON.stringify(msgs, null, 2) + '\n')
|
|
78
|
-
renameSync(tmp, path)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// A closed sender may have left debt in many other sessions' queues. The marker is deliberately outside its
|
|
82
|
-
// own store (which close removes) and is checked under the sender's record lock by dispatch: a close cannot
|
|
83
|
-
// return while an old process can still append, and a later sweep cannot hand over what it sees here.
|
|
84
|
-
export function revokeSenderDelivery(id: string): void {
|
|
85
|
-
mkdirSync(revokedSenderRoot(), { recursive: true })
|
|
86
|
-
writeFileSync(revokedSenderPath(id), `${id}\n`)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const senderDeliveryRevoked = (id: string): boolean => existsSync(revokedSenderPath(id))
|
|
90
|
-
|
|
91
|
-
export function pendingSnapshot(id: string): PendingMessage[] { return read(id) }
|
|
92
|
-
|
|
93
|
-
// These two writes require the target's delivery lock. They are the queue half of a larger transaction
|
|
94
|
-
// (currently reparent), which must be able to restore the exact previous debt if a later record write fails.
|
|
95
|
-
export function replacePendingWhileLocked(id: string, msgs: PendingMessage[]): void { write(id, msgs) }
|
|
96
|
-
export function revokePendingFromWhileLocked(id: string, sender: string): number {
|
|
97
|
-
const current = read(id)
|
|
98
|
-
const next = current.filter((msg) => msg.from !== sender)
|
|
99
|
-
write(id, next)
|
|
100
|
-
return current.length - next.length
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// The enqueue rides the timeline append ([[dispatch]]): the caller holds the session's RECORD lock across
|
|
104
|
-
// both, and the record is written first, so delivery is never unrecorded. A keyed dispatch also carries its
|
|
105
|
-
// exact delivery bytes in the timeline receipt, letting its retry restore a queue write lost to a crash.
|
|
106
|
-
export function enqueue(id: string, msg: PendingMessage): void {
|
|
107
|
-
write(id, [...read(id), msg])
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Caller holds this target's delivery lock. A retry may reach this after either side of the receipt->queue
|
|
111
|
-
// crash boundary; exact mid identity makes reconstruction idempotent without inspecting message text.
|
|
112
|
-
export function ensurePendingWhileLocked(id: string, msg: PendingMessage): boolean {
|
|
113
|
-
const current = read(id)
|
|
114
|
-
if (current.some((pending) => pending.mid === msg.mid)) return false
|
|
115
|
-
write(id, [...current, msg])
|
|
116
|
-
return true
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export const pendingMessages = (id: string): PendingMessage[] => read(id)
|
|
120
|
-
|
|
121
|
-
export const owesDelivery = (id: string): boolean => existsSync(queuePath(id))
|
|
122
|
-
|
|
123
|
-
// Record transitions and queue mutations take locks in the same direction: record locks first, then these
|
|
124
|
-
// target queue locks. A batch reparent needs all child queues held at once so its pointer/watch/debt change
|
|
125
|
-
// either commits together or restores together.
|
|
126
|
-
export async function withDeliveryLocks<T>(rawIds: string[], body: () => Promise<T>, index = 0, ids = [...new Set(rawIds)].sort()): Promise<T> {
|
|
127
|
-
if (index >= ids.length) return body()
|
|
128
|
-
const release = await acquire(ids[index], 30_000)
|
|
129
|
-
if (!release) throw new Error(`delivery queue ${ids[index]}: timed out waiting for transaction lock`)
|
|
130
|
-
try { return await withDeliveryLocks(ids, body, index + 1, ids) }
|
|
131
|
-
finally { release() }
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Hand over what is owed, in order, exactly once. `insert` reports whether the adapter took the message: only
|
|
135
|
-
// then is the entry dropped. A refusal ENDS the pass with that entry still queued and everything behind it
|
|
136
|
-
// still behind it — order is a property of a conversation, so a message is never skipped to deliver a later
|
|
137
|
-
// one. Returns how many were handed over and how many are still owed.
|
|
138
|
-
export async function drain(
|
|
139
|
-
id: string,
|
|
140
|
-
insert: (msg: PendingMessage) => Promise<boolean>,
|
|
141
|
-
timeoutMs = 5_000,
|
|
142
|
-
): Promise<{ delivered: number; remaining: number }> {
|
|
143
|
-
const release = await acquire(id, timeoutMs)
|
|
144
|
-
if (!release) return { delivered: 0, remaining: read(id).length }
|
|
145
|
-
let delivered = 0
|
|
146
|
-
try {
|
|
147
|
-
for (;;) {
|
|
148
|
-
const queued = read(id)
|
|
149
|
-
if (!queued.length) return { delivered, remaining: 0 }
|
|
150
|
-
if (queued[0].from && senderDeliveryRevoked(queued[0].from)) {
|
|
151
|
-
// Closing a sender voids its undelivered output, not the recipient's immutable conversation history.
|
|
152
|
-
// Drop a revoked head and continue so it cannot permanently block the messages behind it.
|
|
153
|
-
write(id, read(id).filter((m) => m.mid !== queued[0].mid))
|
|
154
|
-
continue
|
|
155
|
-
}
|
|
156
|
-
let ok = false
|
|
157
|
-
try { ok = await insert(queued[0]) } catch { ok = false }
|
|
158
|
-
if (!ok) return { delivered, remaining: queued.length }
|
|
159
|
-
// Re-read before removing: a send that landed while this pass ran appended to the tail, and rewriting a
|
|
160
|
-
// stale snapshot minus the head would silently drop it.
|
|
161
|
-
write(id, read(id).filter((m) => m.mid !== queued[0].mid))
|
|
162
|
-
delivered++
|
|
163
|
-
}
|
|
164
|
-
} finally { release() }
|
|
165
|
-
}
|