@plurnk/plurnk-service 1.3.12 → 1.4.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/.env.defaults +84 -77
- package/INSTALL.md +50 -25
- package/README.md +24 -10
- package/SPEC.md +2311 -928
- package/dist/Paths.d.ts +4 -1
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +23 -21
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/content/edit-receipt.d.ts +13 -28
- package/dist/content/edit-receipt.d.ts.map +1 -1
- package/dist/content/edit-receipt.js +285 -104
- package/dist/content/edit-receipt.js.map +1 -1
- package/dist/content/edited-span.d.ts.map +1 -1
- package/dist/content/edited-span.js +2 -5
- package/dist/content/edited-span.js.map +1 -1
- package/dist/content/index.d.ts +3 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +1 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/line-marker.d.ts +6 -9
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.js +7 -11
- package/dist/content/line-marker.js.map +1 -1
- package/dist/content/matcher.d.ts +14 -25
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +77 -107
- package/dist/content/matcher.js.map +1 -1
- package/dist/content/mimetype-binary.d.ts +2 -2
- package/dist/content/mimetype-binary.d.ts.map +1 -1
- package/dist/content/mimetype-binary.js +10 -10
- package/dist/content/mimetype-binary.js.map +1 -1
- package/dist/content/path-mimetype.js +3 -3
- package/dist/content/path-mimetype.js.map +1 -1
- package/dist/content/read-resolve.d.ts +8 -4
- package/dist/content/read-resolve.d.ts.map +1 -1
- package/dist/content/read-resolve.js +101 -49
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/BranchReceipt.d.ts +7 -0
- package/dist/core/BranchReceipt.d.ts.map +1 -0
- package/dist/core/BranchReceipt.js +27 -0
- package/dist/core/BranchReceipt.js.map +1 -0
- package/dist/core/BudgetOverflow.d.ts +23 -0
- package/dist/core/BudgetOverflow.d.ts.map +1 -0
- package/dist/core/BudgetOverflow.js +61 -0
- package/dist/core/BudgetOverflow.js.map +1 -0
- package/dist/core/BudgetReadout.d.ts +8 -0
- package/dist/core/BudgetReadout.d.ts.map +1 -0
- package/dist/core/BudgetReadout.js +79 -0
- package/dist/core/BudgetReadout.js.map +1 -0
- package/dist/core/ChannelWrite.d.ts +28 -9
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +28 -21
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +11 -10
- package/dist/core/CoreSchemeServices.d.ts +10 -4
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +7 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +29 -20
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +1488 -599
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/DurableStatement.d.ts +6 -0
- package/dist/core/DurableStatement.d.ts.map +1 -0
- package/dist/core/DurableStatement.js +51 -0
- package/dist/core/DurableStatement.js.map +1 -0
- package/dist/core/Engine.d.ts +48 -20
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +1208 -789
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +309 -217
- package/dist/core/EnvFlags.js +1 -1
- package/dist/core/EnvFlags.js.map +1 -1
- package/dist/core/ErrorDetail.d.ts +6 -0
- package/dist/core/ErrorDetail.d.ts.map +1 -0
- package/dist/core/ErrorDetail.js +20 -0
- package/dist/core/ErrorDetail.js.map +1 -0
- package/dist/core/ExecutorRegistry.d.ts +14 -3
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +56 -30
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/GitBranch.d.ts +20 -0
- package/dist/core/GitBranch.d.ts.map +1 -0
- package/dist/core/GitBranch.js +110 -0
- package/dist/core/GitBranch.js.map +1 -0
- package/dist/core/JournalTurn.d.ts +9 -0
- package/dist/core/JournalTurn.d.ts.map +1 -0
- package/dist/core/JournalTurn.js +14 -0
- package/dist/core/JournalTurn.js.map +1 -0
- package/dist/core/JournalTurn.sql +10 -0
- package/dist/core/LogBody.d.ts +17 -0
- package/dist/core/LogBody.d.ts.map +1 -0
- package/dist/core/LogBody.js +148 -0
- package/dist/core/LogBody.js.map +1 -0
- package/dist/core/LoopFlagsReader.d.ts +7 -0
- package/dist/core/LoopFlagsReader.d.ts.map +1 -0
- package/dist/core/LoopFlagsReader.js +33 -0
- package/dist/core/LoopFlagsReader.js.map +1 -0
- package/dist/core/LoopLifecycle.d.ts +8 -1
- package/dist/core/LoopLifecycle.d.ts.map +1 -1
- package/dist/core/LoopLifecycle.js +55 -9
- package/dist/core/LoopLifecycle.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +9 -3
- package/dist/core/NoticeChannel.d.ts +13 -0
- package/dist/core/NoticeChannel.d.ts.map +1 -0
- package/dist/core/NoticeChannel.js +40 -0
- package/dist/core/NoticeChannel.js.map +1 -0
- package/dist/core/Owner.d.ts +0 -1
- package/dist/core/Owner.d.ts.map +1 -1
- package/dist/core/Owner.js +2 -5
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +25 -24
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +313 -375
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.d.ts +26 -0
- package/dist/core/ProblemLog.d.ts.map +1 -0
- package/dist/core/ProblemLog.js +58 -0
- package/dist/core/ProblemLog.js.map +1 -0
- package/dist/core/ProposalLifecycle.d.ts +15 -21
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +291 -78
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +51 -48
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +14 -8
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +189 -114
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ServiceTeardown.d.ts +11 -0
- package/dist/core/ServiceTeardown.d.ts.map +1 -0
- package/dist/core/ServiceTeardown.js +70 -0
- package/dist/core/ServiceTeardown.js.map +1 -0
- package/dist/core/StoredPacket.d.ts +29 -0
- package/dist/core/StoredPacket.d.ts.map +1 -0
- package/dist/core/StoredPacket.js +147 -0
- package/dist/core/StoredPacket.js.map +1 -0
- package/dist/core/StrikeRail.d.ts +1 -2
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +25 -55
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/WorkerControlAddress.d.ts +17 -0
- package/dist/core/WorkerControlAddress.d.ts.map +1 -0
- package/dist/core/WorkerControlAddress.js +44 -0
- package/dist/core/WorkerControlAddress.js.map +1 -0
- package/dist/core/WorkerName.d.ts +31 -0
- package/dist/core/WorkerName.d.ts.map +1 -0
- package/dist/core/WorkerName.js +95 -0
- package/dist/core/WorkerName.js.map +1 -0
- package/dist/core/WorkerName.sql +36 -0
- package/dist/core/WorkspaceGate.d.ts +14 -0
- package/dist/core/WorkspaceGate.d.ts.map +1 -0
- package/dist/core/WorkspaceGate.js +155 -0
- package/dist/core/WorkspaceGate.js.map +1 -0
- package/dist/core/caps/CapsResolve.d.ts +1 -1
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts +5 -11
- package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +24 -8
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +4 -13
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +15 -6
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +12 -11
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbProjectionCaps.d.ts +5 -5
- package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbProjectionCaps.js +21 -4
- package/dist/core/caps/DbProjectionCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -4
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +57 -62
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/DbTagCaps.d.ts +5 -12
- package/dist/core/caps/DbTagCaps.d.ts.map +1 -1
- package/dist/core/caps/DbTagCaps.js +14 -7
- package/dist/core/caps/DbTagCaps.js.map +1 -1
- package/dist/core/content-hash.js +1 -1
- package/dist/core/content-hash.js.map +1 -1
- package/dist/core/env-defaults.d.ts.map +1 -1
- package/dist/core/env-defaults.js.map +1 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +55 -30
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +41 -27
- package/dist/core/git-env.d.ts +2 -0
- package/dist/core/git-env.d.ts.map +1 -1
- package/dist/core/git-env.js +13 -18
- package/dist/core/git-env.js.map +1 -1
- package/dist/core/git-iso.d.ts.map +1 -1
- package/dist/core/git-iso.js +9 -15
- package/dist/core/git-iso.js.map +1 -1
- package/dist/core/git-membership.d.ts +1 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +198 -146
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +10 -11
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/owner.sql +1 -1
- package/dist/core/packet-inject.d.ts.map +1 -1
- package/dist/core/packet-inject.js +7 -11
- package/dist/core/packet-inject.js.map +1 -1
- package/dist/core/packet-wire.d.ts +2 -20
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +242 -368
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +12 -1
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +49 -29
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/results.d.ts +16 -5
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +27 -18
- package/dist/core/results.js.map +1 -1
- package/dist/core/ruler_count.d.ts +4 -0
- package/dist/core/ruler_count.d.ts.map +1 -0
- package/dist/core/ruler_count.js +7 -0
- package/dist/core/ruler_count.js.map +1 -0
- package/dist/core/scheme-types.d.ts +9 -11
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/core/search-gate.d.ts.map +1 -1
- package/dist/core/search-gate.js +3 -11
- package/dist/core/search-gate.js.map +1 -1
- package/dist/core/teaching-corpus.d.ts +6 -0
- package/dist/core/teaching-corpus.d.ts.map +1 -0
- package/dist/core/teaching-corpus.js +23 -0
- package/dist/core/teaching-corpus.js.map +1 -0
- package/dist/core/teaching.d.ts +0 -1
- package/dist/core/teaching.d.ts.map +1 -1
- package/dist/core/teaching.js +2 -8
- package/dist/core/teaching.js.map +1 -1
- package/dist/core/token-ruler.d.ts.map +1 -1
- package/dist/core/token-ruler.js +3 -16
- package/dist/core/token-ruler.js.map +1 -1
- package/dist/core/turn-scheduler.d.ts +1 -1
- package/dist/core/turn-scheduler.d.ts.map +1 -1
- package/dist/core/turn-scheduler.js +1 -1
- package/dist/core/turn-scheduler.js.map +1 -1
- package/dist/core/worker-cap.d.ts +2 -4
- package/dist/core/worker-cap.d.ts.map +1 -1
- package/dist/core/worker-cap.js +13 -4
- package/dist/core/worker-cap.js.map +1 -1
- package/dist/core/{run-ops.sql → worker-ops.sql} +11 -11
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +17 -9
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts +2 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +337 -138
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +19 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/matrix/live-specimen.d.ts +9 -0
- package/dist/matrix/live-specimen.d.ts.map +1 -0
- package/dist/matrix/live-specimen.js +37 -0
- package/dist/matrix/live-specimen.js.map +1 -0
- package/dist/observe/api.d.ts +6 -0
- package/dist/observe/api.d.ts.map +1 -0
- package/dist/observe/api.js +11 -0
- package/dist/observe/api.js.map +1 -0
- package/dist/observe/init.d.ts +7 -0
- package/dist/observe/init.d.ts.map +1 -0
- package/dist/observe/init.js +159 -0
- package/dist/observe/init.js.map +1 -0
- package/dist/observe/metrics.d.ts +6 -0
- package/dist/observe/metrics.d.ts.map +1 -0
- package/dist/observe/metrics.js +15 -0
- package/dist/observe/metrics.js.map +1 -0
- package/dist/observe/spans.d.ts +4 -0
- package/dist/observe/spans.d.ts.map +1 -0
- package/dist/observe/spans.js +51 -0
- package/dist/observe/spans.js.map +1 -0
- package/dist/schemes/EffectPolicy.d.ts +1 -0
- package/dist/schemes/EffectPolicy.d.ts.map +1 -1
- package/dist/schemes/EffectPolicy.js +5 -2
- package/dist/schemes/EffectPolicy.js.map +1 -1
- package/dist/schemes/Exec.d.ts +6 -11
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +364 -247
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts +8 -7
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +48 -9
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts +7 -11
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +279 -142
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +4 -8
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +475 -208
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +62 -30
- package/dist/schemes/Prompt.d.ts +3 -2
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +9 -6
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts +1 -1
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +2 -3
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts +8 -16
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +179 -69
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-chunk.d.ts.map +1 -1
- package/dist/schemes/_entry-chunk.js +12 -5
- package/dist/schemes/_entry-chunk.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +10 -9
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +59 -13
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +20 -8
- package/dist/schemes/_entry-find.d.ts +20 -15
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +339 -174
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +20 -8
- package/dist/schemes/_entry-graph.d.ts +4 -2
- package/dist/schemes/_entry-graph.d.ts.map +1 -1
- package/dist/schemes/_entry-graph.js +55 -31
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-graph.sql +37 -22
- package/dist/schemes/_entry-manifest.d.ts +10 -15
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +50 -268
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +9 -14
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +149 -77
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +1 -1
- package/dist/schemes/_entry-semantic.d.ts +23 -8
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +141 -60
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_entry-semantic.sql +63 -45
- package/dist/schemes/_entry-send.d.ts +3 -4
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +48 -23
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_path-scope.d.ts +23 -0
- package/dist/schemes/_path-scope.d.ts.map +1 -0
- package/dist/schemes/_path-scope.js +61 -0
- package/dist/schemes/_path-scope.js.map +1 -0
- package/dist/schemes/_search-candidate.d.ts +20 -0
- package/dist/schemes/_search-candidate.d.ts.map +1 -0
- package/dist/schemes/_search-candidate.js +19 -0
- package/dist/schemes/_search-candidate.js.map +1 -0
- package/dist/schemes/_search-exclusion.d.ts +7 -0
- package/dist/schemes/_search-exclusion.d.ts.map +1 -0
- package/dist/schemes/_search-exclusion.js +23 -0
- package/dist/schemes/_search-exclusion.js.map +1 -0
- package/dist/schemes/_search-index.d.ts +6 -0
- package/dist/schemes/_search-index.d.ts.map +1 -0
- package/dist/schemes/_search-index.js +326 -0
- package/dist/schemes/_search-index.js.map +1 -0
- package/dist/schemes/cosine.js +1 -1
- package/dist/schemes/cosine.js.map +1 -1
- package/dist/schemes/exec-abort.js +3 -3
- package/dist/schemes/exec-abort.js.map +1 -1
- package/dist/schemes/exec-env.js +3 -3
- package/dist/schemes/exec-env.js.map +1 -1
- package/dist/server/BranchBatches.d.ts +48 -0
- package/dist/server/BranchBatches.d.ts.map +1 -0
- package/dist/server/BranchBatches.js +591 -0
- package/dist/server/BranchBatches.js.map +1 -0
- package/dist/server/Daemon.d.ts +36 -66
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +920 -446
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +31 -0
- package/dist/server/DaemonModule.d.ts.map +1 -0
- package/dist/server/DaemonModule.js +2 -0
- package/dist/server/DaemonModule.js.map +1 -0
- package/dist/server/branch-batch.sql +152 -0
- package/dist/server/client-input.d.ts +12 -0
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +262 -57
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +15 -7
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +92 -62
- package/dist/server/envelope.d.ts +2 -5
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +119 -95
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +19 -23
- package/dist/server/exec-poll-backoff.js +1 -1
- package/dist/server/exec-poll-backoff.js.map +1 -1
- package/dist/server/lifecycle-recovery.sql +79 -2
- package/dist/server/logEntry.d.ts +3 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +4 -2
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/logEntry.sql +1 -1
- package/dist/server/loop-model.d.ts.map +1 -1
- package/dist/server/loop-model.js +22 -9
- package/dist/server/loop-model.js.map +1 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +5 -4
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/seam-entry-read.sql +15 -10
- package/dist/server/seam-log-read.sql +2 -2
- package/dist/server/seam-loop.sql +1 -1
- package/dist/server/seam-proposal-list.sql +17 -7
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +66 -60
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +897 -0
- package/package.json +57 -26
- package/dist/core/PluginLoader.d.ts +0 -20
- package/dist/core/PluginLoader.d.ts.map +0 -1
- package/dist/core/PluginLoader.js +0 -142
- package/dist/core/PluginLoader.js.map +0 -1
- package/dist/core/TelemetryChannel.d.ts +0 -37
- package/dist/core/TelemetryChannel.d.ts.map +0 -1
- package/dist/core/TelemetryChannel.js +0 -80
- package/dist/core/TelemetryChannel.js.map +0 -1
- package/dist/core/path-decode.d.ts +0 -3
- package/dist/core/path-decode.d.ts.map +0 -1
- package/dist/core/path-decode.js +0 -9
- package/dist/core/path-decode.js.map +0 -1
- package/dist/core/plugin-attribution.d.ts +0 -5
- package/dist/core/plugin-attribution.d.ts.map +0 -1
- package/dist/core/plugin-attribution.js +0 -39
- package/dist/core/plugin-attribution.js.map +0 -1
- package/dist/core/world-state.d.ts +0 -10
- package/dist/core/world-state.d.ts.map +0 -1
- package/dist/core/world-state.js +0 -35
- package/dist/core/world-state.js.map +0 -1
- package/dist/core/world-state.sql +0 -35
- package/dist/core/zero-pin.d.ts +0 -3
- package/dist/core/zero-pin.d.ts.map +0 -1
- package/dist/core/zero-pin.js +0 -16
- package/dist/core/zero-pin.js.map +0 -1
- package/dist/server/auto.d.ts +0 -6
- package/dist/server/auto.d.ts.map +0 -1
- package/dist/server/auto.js +0 -63
- package/dist/server/auto.js.map +0 -1
- package/dist/server/clientTurn.d.ts +0 -6
- package/dist/server/clientTurn.d.ts.map +0 -1
- package/dist/server/clientTurn.js +0 -22
- package/dist/server/clientTurn.js.map +0 -1
- package/dist/server/clientTurn.sql +0 -10
- package/dist/server/noProposals.d.ts +0 -6
- package/dist/server/noProposals.d.ts.map +0 -1
- package/dist/server/noProposals.js +0 -37
- package/dist/server/noProposals.js.map +0 -1
- package/dist/server/version-info.d.ts +0 -14
- package/dist/server/version-info.d.ts.map +0 -1
- package/dist/server/version-info.js +0 -69
- package/dist/server/version-info.js.map +0 -1
- package/migrations/0000-00-00.01_schema.sql +0 -536
- package/migrations/0004_loop-provider.sql +0 -3
- package/migrations/0005_subscription-published-channel.sql +0 -3
- package/migrations/0006_loop-max-turns.sql +0 -3
package/dist/schemes/File.js
CHANGED
|
@@ -3,56 +3,53 @@ import Namespace from "../core/namespace.js";
|
|
|
3
3
|
import Owner from "../core/Owner.js";
|
|
4
4
|
import { dirname, relative, isAbsolute, join, matchesGlob } from "node:path";
|
|
5
5
|
import { createPatch } from "diff";
|
|
6
|
-
import {
|
|
6
|
+
import { PathSyntax } from "@plurnk/plurnk-contracts";
|
|
7
7
|
import GitMembership from "../core/git-membership.js";
|
|
8
8
|
import EntryOps from "./_entry-ops.js";
|
|
9
9
|
import EntryFind from "./_entry-find.js";
|
|
10
10
|
import EntryCrud from "./_entry-crud.js";
|
|
11
11
|
import { CoreSchemeAdapterBase } from "../core/CoreSchemeServices.js";
|
|
12
|
-
import
|
|
12
|
+
import ErrorDetail from "../core/ErrorDetail.js";
|
|
13
|
+
import Results from "../core/results.js";
|
|
14
|
+
import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
|
|
15
|
+
import { LineMarkerOps, MimetypeBinary, editReceipt, projectEditReceipt, reviewerReplacementReceipt, } from "../content/index.js";
|
|
13
16
|
// Workspace root for file ops is sourced from `workspaces.project_root`,
|
|
14
|
-
// supplied
|
|
15
|
-
//
|
|
16
|
-
// the client owns workspace identity. If a workspace is headless
|
|
17
|
-
// (project_root=null), file ops fail at 400; the client either supplies
|
|
18
|
-
// a root or the op isn't appropriate for this workspace.
|
|
17
|
+
// supplied at {§methods-workspace-create}. Under {§fs-namespace}, core never
|
|
18
|
+
// guesses a root: headless workspaces fail file operations at 400.
|
|
19
19
|
const loadWorkspaceRoot = async (db, workspaceId) => {
|
|
20
20
|
const row = await db.envelope_get_workspace.get({ id: workspaceId });
|
|
21
21
|
return row?.project_root ?? null;
|
|
22
22
|
};
|
|
23
|
-
// Detect mimetype from a file's path
|
|
24
|
-
// when available; falls back to the text primitive (text/markdown). The
|
|
23
|
+
// Detect mimetype from a file's path through the configured registry. The
|
|
25
24
|
// `MimetypeBinary.normalizeAutoTextMimetype` wrapper ensures text/plain returned by the
|
|
26
25
|
// service is normalized to text/markdown — plurnk-service never auto-
|
|
27
26
|
// derives text/plain (see mimetype-binary.ts MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE).
|
|
28
27
|
const detectFileMimetype = async (canonical, ctx) => {
|
|
29
|
-
if (ctx.mimetypes
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE;
|
|
28
|
+
if (ctx.mimetypes === undefined)
|
|
29
|
+
throw new Error("detectFileMimetype: configured mimetype registry is required");
|
|
30
|
+
const detected = await ctx.mimetypes.detect({ path: canonical });
|
|
31
|
+
return MimetypeBinary.normalizeAutoTextMimetype(detected);
|
|
34
32
|
};
|
|
35
|
-
// SECURITY — File is entry-backed
|
|
33
|
+
// SECURITY — File is entry-backed: read()/find()/readEntry()
|
|
36
34
|
// delegate to the shared Entry* helpers over the membership-materialized entries
|
|
37
|
-
// (scheme=
|
|
38
|
-
// entry, so read/find/readEntry 404 it
|
|
35
|
+
// (stored under scheme="file"). The membership gate is now ENTRY-EXISTENCE — a non-member has no
|
|
36
|
+
// entry, so read/find/readEntry 404 it at the shared entry boundary
|
|
39
37
|
// (a gitignored `.env` is never a member → never an entry → never readable). Disk
|
|
40
38
|
// I/O is confined to the two edges that own it: the git-membership materialize-IN
|
|
41
|
-
// and edit()/applyResolution()'s proposal-gated write-OUT (§membership), where the
|
|
39
|
+
// and edit()/applyResolution()'s proposal-gated write-OUT ({§membership}), where the
|
|
42
40
|
// containment/traversal checks live.
|
|
43
41
|
//
|
|
44
42
|
// writeEntry() is the proposal-gated write-back: a COPY/MOVE *into* file:/// is a disk
|
|
45
|
-
// write, so it flows through the SAME §membership gate as EDIT (#resolveWriteTarget) — a
|
|
43
|
+
// write, so it flows through the SAME {§membership} gate as EDIT (#resolveWriteTarget) — a
|
|
46
44
|
// 202 proposal, then applyResolution() writes on accept — never an ungated overwrite (the
|
|
47
45
|
// `.env`-wipe this guard prevents). COPY/MOVE *from* file:/// is readEntry (read-only).
|
|
48
46
|
export default class File extends CoreSchemeAdapterBase {
|
|
49
47
|
static manifest = {
|
|
50
48
|
name: "file",
|
|
51
|
-
storedScheme: "file", // {§entry-identity-no-null} —
|
|
49
|
+
storedScheme: "file", // {§entry-identity-no-null} — durable identity; file addresses still render as bare paths
|
|
52
50
|
channels: {}, // dynamic mimetype per file extension
|
|
53
51
|
defaultChannel: "body",
|
|
54
52
|
category: "data",
|
|
55
|
-
scope: "workspace",
|
|
56
53
|
writableBy: ["model", "client", "plugin"],
|
|
57
54
|
volatile: false,
|
|
58
55
|
modelVisible: true,
|
|
@@ -60,15 +57,13 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
60
57
|
example: "<<READ(README.md)::READ",
|
|
61
58
|
documentation: "The project's workspace files (git-tracked members, shown as bare paths) — THE TASK'S FILES: when asked to change the project, EDIT these, not your notes or scratch. READ and FIND them like any entry; EDIT proposes a diff for review and only writes to disk once accepted — the review is normal, not a refusal, so propose the edit rather than working around it. Non-members are invisible, so you can't read or clobber a file outside the tracked surface.",
|
|
62
59
|
};
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// → 404, the same gate Known runs on. Disk is reached only at the materialize
|
|
67
|
-
// and write-back edges (git-membership, applyResolution) — never on a read.
|
|
60
|
+
// {§membership} — read the membership-materialized `file` entry through the
|
|
61
|
+
// shared entry helper. Disk is touched only at materialization and accepted
|
|
62
|
+
// write-back boundaries, never by READ itself.
|
|
68
63
|
async read(statement, ctx) {
|
|
69
64
|
const core = this.coreContext(ctx);
|
|
70
|
-
// {§fs-namei} — canonicalize
|
|
71
|
-
//
|
|
65
|
+
// {§fs-namei} — canonicalize once, before the entry-existence gate;
|
|
66
|
+
// resolution never depends on whether the target currently exists.
|
|
72
67
|
const canon = File.#canonTarget(statement, await loadWorkspaceRoot(core.db, core.workspaceId));
|
|
73
68
|
return EntryOps.readWorkspaceEntry(canon ?? statement, core, File.manifest);
|
|
74
69
|
}
|
|
@@ -94,11 +89,18 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
94
89
|
}
|
|
95
90
|
return statement; // regex — not a path
|
|
96
91
|
}
|
|
97
|
-
// Folderhood survives canonicalization (§find-scope-prefix-filter) — the shared
|
|
92
|
+
// Folderhood survives canonicalization ({§find-scope-prefix-filter}) — the shared
|
|
98
93
|
// spelling canon lives on Namespace (the Dispatcher's log columns use the same one).
|
|
99
94
|
static #canonSpelling(raw, root) {
|
|
100
95
|
return Namespace.canonicalizeSpelling(raw, root);
|
|
101
96
|
}
|
|
97
|
+
async resolveEntryAddress(target, ctx) {
|
|
98
|
+
if (target.kind !== "url")
|
|
99
|
+
return null;
|
|
100
|
+
const core = this.coreContext(ctx);
|
|
101
|
+
const pathname = File.#canonSpelling(target.pathname, await loadWorkspaceRoot(core.db, core.workspaceId));
|
|
102
|
+
return pathname === null ? null : { pathname, owner: "commons" };
|
|
103
|
+
}
|
|
102
104
|
async find(statement, ctx) {
|
|
103
105
|
const core = this.coreContext(ctx);
|
|
104
106
|
// {§fs-namei} — canonicalize the glob's path portion before the candidate scan, the
|
|
@@ -111,29 +113,47 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
111
113
|
// see the SECURITY note above.
|
|
112
114
|
async readEntry(pathname, ctx) {
|
|
113
115
|
const core = this.coreContext(ctx);
|
|
114
|
-
// §scheme-address — normalize the model-typed path (bare `brief.md`) to its `/rel` member key,
|
|
116
|
+
// {§scheme-address} — normalize the model-typed path (bare `brief.md`) to its `/rel` member key,
|
|
115
117
|
// the same parity READ/EDIT/deleteEntry have. Without it a COPY/MOVE FROM a bare file path
|
|
116
118
|
// misses the canonical-stored member and 404s a source that plainly exists.
|
|
117
119
|
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
118
120
|
const key = root === null ? pathname : Namespace.canonicalize(pathname, root);
|
|
119
121
|
return EntryCrud.readEntry(key ?? pathname, core, "file");
|
|
120
122
|
}
|
|
121
|
-
// §membership disk-write gate, shared by edit() and writeEntry() (the COPY/MOVE
|
|
123
|
+
// {§membership} disk-write gate, shared by edit() and writeEntry() (the COPY/MOVE
|
|
122
124
|
// dest). Resolves the canonical path; enforces containment (traversal → 403),
|
|
123
125
|
// membership (existing non-member → 403, the `.env` protection), the read-only
|
|
124
126
|
// overlay (→ 403), and binary (→ 415); reads current content for the diff. A
|
|
125
127
|
// not-found path is fine — we propose to CREATE. ONE home for the gate: an edit
|
|
126
128
|
// and a copy into file:/// are the same disk write under the same review.
|
|
127
|
-
// §membership-edit-membership-gate — membership/containment/read-only/binary gate before any disk write
|
|
129
|
+
// {§membership-edit-membership-gate} — membership/containment/read-only/binary gate before any disk write
|
|
128
130
|
async #resolveWriteTarget(pathname, ctx) {
|
|
129
131
|
const root = await loadWorkspaceRoot(ctx.db, ctx.workspaceId);
|
|
130
|
-
if (root === null)
|
|
131
|
-
return {
|
|
132
|
+
if (root === null) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
code: "project-root-required",
|
|
136
|
+
status: 400,
|
|
137
|
+
detail: "The workspace has no project root, so it cannot write files.",
|
|
138
|
+
extensions: { retryable: false },
|
|
139
|
+
};
|
|
140
|
+
}
|
|
132
141
|
// {§fs-namei} — the write side resolves through the SAME canonicalizer as reads;
|
|
133
142
|
// a spelling that names nothing a file can be is refused before any disk touch.
|
|
134
143
|
const key = Namespace.canonicalize(pathname, root);
|
|
135
|
-
if (key === null)
|
|
136
|
-
return {
|
|
144
|
+
if (key === null) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
code: "path-outside-workspace",
|
|
148
|
+
status: 403,
|
|
149
|
+
detail: `The requested path '${pathname}' is outside the workspace.`,
|
|
150
|
+
extensions: {
|
|
151
|
+
requestedPath: pathname,
|
|
152
|
+
recovery: "Use a path within the workspace.",
|
|
153
|
+
retryable: false,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
137
157
|
const isMount = key.startsWith("../");
|
|
138
158
|
let canonical;
|
|
139
159
|
const requested = join(root, key);
|
|
@@ -148,13 +168,35 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
148
168
|
fileExists = false;
|
|
149
169
|
}
|
|
150
170
|
// {§fs-write-surface} 6 — only the root mints: no create on any mount, ever.
|
|
151
|
-
if (isMount && !fileExists)
|
|
152
|
-
return {
|
|
171
|
+
if (isMount && !fileExists) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
code: "mount-create-forbidden",
|
|
175
|
+
status: 403,
|
|
176
|
+
detail: `The mounted path '${key}' does not permit file creation.`,
|
|
177
|
+
extensions: {
|
|
178
|
+
path: key,
|
|
179
|
+
recovery: "Create the file inside the project root.",
|
|
180
|
+
retryable: false,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
153
184
|
if (!isMount) {
|
|
154
185
|
const relBare = relative(root, canonical);
|
|
155
186
|
// in-tree keys whose realpath escapes (a symlink out) stay refused — the jail holds.
|
|
156
|
-
if (relBare.startsWith("..") || isAbsolute(relBare))
|
|
157
|
-
return {
|
|
187
|
+
if (relBare.startsWith("..") || isAbsolute(relBare)) {
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
code: "path-outside-workspace",
|
|
191
|
+
status: 403,
|
|
192
|
+
detail: `The requested path '${pathname}' resolves outside the workspace.`,
|
|
193
|
+
extensions: {
|
|
194
|
+
requestedPath: pathname,
|
|
195
|
+
recovery: "Use a path within the workspace.",
|
|
196
|
+
retryable: false,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
158
200
|
}
|
|
159
201
|
const rel = key; // the bare canonical member key ({§fs-canonical-name}) — storage ≡ wire
|
|
160
202
|
let original = "";
|
|
@@ -164,116 +206,178 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
164
206
|
const member = await ctx.db.crud_get_member_sig.get({ workspace_id: ctx.workspaceId, owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId), scheme: "file", pathname: rel });
|
|
165
207
|
// {§fs-errno} — the occupancy fact (POSIX O_EXCL precedent): something invisible
|
|
166
208
|
// occupies the path; existence leaks, content stays dark. The model picks another name.
|
|
167
|
-
if (member === undefined)
|
|
168
|
-
return {
|
|
209
|
+
if (member === undefined) {
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
code: "path-occupied-by-nonmember",
|
|
213
|
+
status: 403,
|
|
214
|
+
detail: `A non-member file already occupies '${rel}'.`,
|
|
215
|
+
extensions: {
|
|
216
|
+
path: rel,
|
|
217
|
+
recovery: "Choose an unoccupied member path.",
|
|
218
|
+
retryable: false,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
169
222
|
// {§fs-write-surface} 5 — a git-included mount member is read-only: git's grants
|
|
170
223
|
// confer rw only within the project; only an explicit client grant carries write.
|
|
171
|
-
if (isMount && member.membership_origin === "git")
|
|
172
|
-
return {
|
|
224
|
+
if (isMount && member.membership_origin === "git") {
|
|
225
|
+
return {
|
|
226
|
+
ok: false,
|
|
227
|
+
code: "member-read-only",
|
|
228
|
+
status: 403,
|
|
229
|
+
detail: `The mounted member '${rel}' is read-only.`,
|
|
230
|
+
extensions: { path: rel, retryable: false },
|
|
231
|
+
};
|
|
232
|
+
}
|
|
173
233
|
const viewGlobs = (await ctx.db.crud_list_workspace_constraints.all({ workspace_id: ctx.workspaceId }))
|
|
174
234
|
.filter((c) => c.effect === "view").map((c) => c.glob);
|
|
175
|
-
if (viewGlobs.some((g) => matchesGlob(rel, g)))
|
|
176
|
-
return {
|
|
235
|
+
if (viewGlobs.some((g) => matchesGlob(rel, g))) {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
code: "member-read-only",
|
|
239
|
+
status: 403,
|
|
240
|
+
detail: `The member '${rel}' is read-only.`,
|
|
241
|
+
extensions: { path: rel, retryable: false },
|
|
242
|
+
};
|
|
243
|
+
} // view = read-only member, 403 on edit - {§membership-overlay-view}
|
|
177
244
|
// The diff base is the entry's snapshot — the body channel the model READ — not a fresh
|
|
178
245
|
// disk read. EDIT is naive against the view the model saw; the write-side CAS (applyResolution)
|
|
179
246
|
// guards the landing. baseSig is that snapshot's stat, carried with the proposal so a sibling
|
|
180
|
-
// worker's reconcile can't advance it under the paused proposal. §membership-edit-write-cas
|
|
247
|
+
// worker's reconcile can't advance it under the paused proposal. {§membership-edit-write-cas}
|
|
181
248
|
const snapshot = await ctx.db.ops_read_channel.get({ workspace_id: ctx.workspaceId, owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId), scheme: "file", pathname: rel, channel: "body" });
|
|
182
249
|
original = snapshot?.content ?? "";
|
|
183
250
|
baseSig = member.synced_sig;
|
|
184
251
|
}
|
|
185
252
|
else {
|
|
186
253
|
// {§fs-write-surface} 1 — the blind-write closure: an exclusive CREATE is legal only
|
|
187
|
-
// where the RESULT will be a member. A client pick admits it;
|
|
188
|
-
//
|
|
254
|
+
// where the RESULT will be a member. A client pick admits it; otherwise
|
|
255
|
+
// Git's untracked-not-ignored membership must ({§membership-auto-add}).
|
|
256
|
+
// No staging occurs. A non-git root grants nothing by itself.
|
|
189
257
|
const picks = (await ctx.db.crud_list_workspace_constraints.all({ workspace_id: ctx.workspaceId }))
|
|
190
258
|
.filter((c) => c.effect === "pick").map((c) => c.glob);
|
|
191
259
|
const clientAdmits = picks.some((g) => matchesGlob(rel, g));
|
|
192
260
|
if (!clientAdmits && !(await GitMembership.wouldGitAdmit(root, rel, ctx.signal))) {
|
|
193
|
-
return {
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
code: "file-not-admitted",
|
|
264
|
+
status: 403,
|
|
265
|
+
detail: `The new file '${rel}' would not belong to the workspace because Git ignores it and no client pick includes it.`,
|
|
266
|
+
extensions: {
|
|
267
|
+
path: rel,
|
|
268
|
+
recovery: "Choose a path admitted by Git or the workspace picks.",
|
|
269
|
+
retryable: false,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
194
272
|
}
|
|
195
273
|
// {§fs-write-surface} — the closure PROVED the grantor; the accept stamps what was
|
|
196
274
|
// proven instead of leaving provenance NULL until the next reconcile guesses it.
|
|
197
275
|
admittedBy = clientAdmits ? "client" : "git";
|
|
198
276
|
}
|
|
199
277
|
const mimetype = await detectFileMimetype(canonical, ctx);
|
|
200
|
-
if (MimetypeBinary.isBinaryMimetype(mimetype))
|
|
201
|
-
return {
|
|
278
|
+
if (await MimetypeBinary.isBinaryMimetype(mimetype, ctx.mimetypes)) {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
code: "binary-write-unsupported",
|
|
282
|
+
status: 415,
|
|
283
|
+
detail: `File EDIT does not support binary mimetype '${mimetype}'.`,
|
|
284
|
+
extensions: {
|
|
285
|
+
mimetype,
|
|
286
|
+
retryable: false,
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
}
|
|
202
290
|
return { ok: true, canonical, rel, fileExists, original, mimetype, baseSig, ...(admittedBy !== undefined ? { admittedBy } : {}) };
|
|
203
291
|
}
|
|
204
|
-
//
|
|
205
|
-
// with a udiff body for client review
|
|
206
|
-
// content. Engine
|
|
207
|
-
// calls applyResolution() (below) after the proposal accepts.
|
|
292
|
+
// {§membership-edit-write-cas}, {§proposal-202-pauses}: return status=202
|
|
293
|
+
// with a udiff body for client review and attrs carrying the full patched
|
|
294
|
+
// content. Engine pauses dispatch and calls applyResolution() after accept.
|
|
208
295
|
async editBatch(statements, ctx) {
|
|
296
|
+
const failure = (code, status, detail, extensions = {}) => Results.failure("scheme:file", code, status, detail, {}, extensions);
|
|
209
297
|
const statement = statements[0];
|
|
210
|
-
if (statement === undefined)
|
|
211
|
-
return
|
|
298
|
+
if (statement === undefined) {
|
|
299
|
+
return failure("edit-empty", 400, "EDIT requires at least one statement.", {
|
|
300
|
+
recovery: "Provide an EDIT statement.",
|
|
301
|
+
retryable: false,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
212
304
|
const core = this.coreContext(ctx);
|
|
213
|
-
if (statement.target === null)
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
305
|
+
if (statement.target === null) {
|
|
306
|
+
return failure("edit-target-required", 400, "EDIT requires a target path.", {
|
|
307
|
+
recovery: "Provide the file target.",
|
|
308
|
+
retryable: false,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const pathname = PathSyntax.decodeParens(statement.target.kind === "url" ? statement.target.pathname : statement.target.raw); // {§path-parentheses}
|
|
217
312
|
const target = await this.#resolveWriteTarget(pathname, core);
|
|
218
313
|
if (!target.ok)
|
|
219
|
-
return
|
|
314
|
+
return failure(target.code, target.status, target.detail, target.extensions);
|
|
220
315
|
const { canonical, rel, fileExists, original, mimetype, baseSig, admittedBy } = target;
|
|
221
316
|
for (const candidate of statements.slice(1)) {
|
|
222
|
-
if (candidate.target === null)
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
|
|
317
|
+
if (candidate.target === null) {
|
|
318
|
+
return failure("edit-batch-mismatch", 400, "The EDIT batch spans multiple resources.", {
|
|
319
|
+
recovery: "Submit a separate EDIT batch for each resource.",
|
|
320
|
+
retryable: false,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
const candidatePathname = PathSyntax.decodeParens(candidate.target.kind === "url" ? candidate.target.pathname : candidate.target.raw);
|
|
226
324
|
const candidateTarget = await this.#resolveWriteTarget(candidatePathname, core);
|
|
227
|
-
if (!candidateTarget.ok || candidateTarget.rel !== rel)
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// {§edit-marker-required-on-existing}
|
|
235
|
-
//
|
|
236
|
-
// that intent explicitly). run126: an omitted marker (a misplaced `<356>` landed
|
|
237
|
-
// in the body, not the marker slot) silently replaced a 1,693-line file with 2 —
|
|
238
|
-
// the model's own subsequent reads told it so, repeatedly, and it never noticed.
|
|
239
|
-
// Refusing the omission converts a silent accident into a loud, immediate one.
|
|
325
|
+
if (!candidateTarget.ok || candidateTarget.rel !== rel) {
|
|
326
|
+
return failure("edit-batch-mismatch", 400, "The EDIT batch spans multiple resources.", {
|
|
327
|
+
recovery: "Submit a separate EDIT batch for each resource.",
|
|
328
|
+
retryable: false,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// {§edit-marker-required-on-existing} — markerless content creates a new
|
|
333
|
+
// file; every existing-file rewrite states its range explicitly.
|
|
240
334
|
let patched;
|
|
335
|
+
let scopeNormalizations;
|
|
241
336
|
if (fileExists) {
|
|
242
|
-
if (statements.some(({ lineMarker }) => lineMarker === null))
|
|
243
|
-
return
|
|
337
|
+
if (statements.some(({ lineMarker }) => lineMarker === null)) {
|
|
338
|
+
return failure("line-marker-required", 400, "EDIT of an existing file requires a line marker.", {
|
|
339
|
+
recovery: "Use <1,-1> to replace the whole file or select a narrower range.",
|
|
340
|
+
retryable: false,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
244
343
|
const edits = statements.map((candidate) => ({ marker: candidate.lineMarker, body: candidate.body ?? "" }));
|
|
245
|
-
const result =
|
|
246
|
-
? LineMarkerOps.applyJsonItemEditBatch(original, edits)
|
|
247
|
-
: LineMarkerOps.applyLineMarkerEditBatch(original, edits);
|
|
344
|
+
const result = LineMarkerOps.applyLineMarkerEditBatch(original, edits);
|
|
248
345
|
if (result.status !== 200)
|
|
249
|
-
return
|
|
346
|
+
return Results.assert(result);
|
|
250
347
|
patched = result.result ?? "";
|
|
348
|
+
scopeNormalizations = result.scopeNormalizations;
|
|
251
349
|
}
|
|
252
350
|
else {
|
|
253
|
-
if (statements.length !== 1)
|
|
254
|
-
return
|
|
351
|
+
if (statements.length !== 1) {
|
|
352
|
+
return failure("creation-batch-conflict", 409, "Multiple EDIT operations attempted to create the same file.", {
|
|
353
|
+
recovery: "Create the file with one EDIT before applying additional edits.",
|
|
354
|
+
retryable: false,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
255
357
|
patched = statement.body ?? "";
|
|
256
358
|
}
|
|
257
359
|
if (fileExists && patched === original)
|
|
258
|
-
return {
|
|
360
|
+
return {
|
|
361
|
+
status: 304,
|
|
362
|
+
...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
|
|
363
|
+
}; // {§edit-noop-304}
|
|
259
364
|
const patch = createPatch(rel, original, patched, "current", "proposed");
|
|
260
365
|
const receiptEdits = fileExists
|
|
261
366
|
? statements.map((candidate) => ({ marker: candidate.lineMarker, body: candidate.body ?? "" }))
|
|
262
367
|
: [{ marker: { marks: [1, -1] }, body: patched }];
|
|
263
|
-
const batchReceipt = editReceipt(original, patched, receiptEdits
|
|
264
|
-
unit: editReceiptUnit(MimetypeBinary.isJsonMimetype(mimetype), original, patched),
|
|
265
|
-
});
|
|
368
|
+
const batchReceipt = editReceipt(original, patched, receiptEdits);
|
|
266
369
|
return {
|
|
267
370
|
status: 202,
|
|
268
371
|
body: patch,
|
|
269
372
|
editReceipt: batchReceipt,
|
|
373
|
+
...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
|
|
270
374
|
attrs: { path: rel, canonical, patch, patched, editReceipt: batchReceipt, baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) },
|
|
271
375
|
};
|
|
272
376
|
}
|
|
273
377
|
async edit(statement, ctx) {
|
|
274
378
|
return this.editBatch([statement], ctx);
|
|
275
379
|
}
|
|
276
|
-
// COPY/MOVE INTO file:/// — the dest write. Same §membership gate as edit, same 202
|
|
380
|
+
// COPY/MOVE INTO file:/// — the dest write. Same {§membership} gate as edit, same 202
|
|
277
381
|
// proposal + applyResolution path: a copy onto disk is a disk write and earns
|
|
278
382
|
// the identical human review. Dispatcher.#copyOrchestration propagates this 202;
|
|
279
383
|
// ProposalLifecycle.workerApply routes the accept back here via the dest scheme;
|
|
@@ -282,24 +386,28 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
282
386
|
async writeEntry(pathname, entry, ctx) {
|
|
283
387
|
const core = this.coreContext(ctx);
|
|
284
388
|
const bodyChannel = entry.channels.body;
|
|
285
|
-
if (bodyChannel === undefined)
|
|
286
|
-
return
|
|
389
|
+
if (bodyChannel === undefined) {
|
|
390
|
+
return Results.failure("scheme:file", "body-channel-required", 400, "A file write requires a body channel.", { created: false, entryId: null }, {
|
|
391
|
+
recovery: "Provide the file content in the body channel.",
|
|
392
|
+
retryable: false,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
287
395
|
const target = await this.#resolveWriteTarget(pathname, core);
|
|
288
|
-
if (!target.ok)
|
|
289
|
-
return
|
|
396
|
+
if (!target.ok) {
|
|
397
|
+
return Results.failure("scheme:file", target.code, target.status, target.detail, { created: false, entryId: null }, target.extensions);
|
|
398
|
+
}
|
|
290
399
|
const { canonical, rel, fileExists, original, baseSig, admittedBy } = target;
|
|
291
400
|
const patched = bodyChannel.content;
|
|
292
401
|
const patch = createPatch(rel, original, patched, "current", "proposed");
|
|
293
|
-
return { status: 202, created: !fileExists, entryId: null, body: patch, attrs: { path: rel, canonical, patch, patched,
|
|
402
|
+
return { status: 202, created: !fileExists, entryId: null, body: patch, attrs: { path: rel, canonical, patch, patched, baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) } };
|
|
294
403
|
}
|
|
295
404
|
// applyResolution — called by Engine.dispatch after a proposed log
|
|
296
405
|
// entry resolves with decision=accept. Two responsibilities:
|
|
297
406
|
// 1. Write the patched content to disk.
|
|
298
407
|
// 2. Register the file as an entry so it appears in the manifest and the
|
|
299
408
|
// model can READ its full landed work.
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
// entry-scheme EDIT's span; default-folding reclaims it at the next turn boundary.
|
|
409
|
+
// {§edit-result-receipt-truth} — accepted EDITs return the receipt for what
|
|
410
|
+
// landed. Dispatcher composes COPY/MOVE effects after this hook returns.
|
|
303
411
|
async applyResolution(args, ctx) {
|
|
304
412
|
const core = this.coreContext(ctx);
|
|
305
413
|
const { attrs, body } = args;
|
|
@@ -308,14 +416,22 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
308
416
|
// failure surfaces as 500, never a silent noop; ENOENT ⇒ file already gone, still deregister.
|
|
309
417
|
if (typeof attrs.deletePath === "string") {
|
|
310
418
|
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
311
|
-
if (root === null)
|
|
312
|
-
|
|
419
|
+
if (root === null) {
|
|
420
|
+
throw new InvalidOperationResultError("An accepted file deletion has no workspace project root.");
|
|
421
|
+
}
|
|
313
422
|
try {
|
|
314
423
|
await rm(join(root, attrs.deletePath));
|
|
315
424
|
}
|
|
316
425
|
catch (err) {
|
|
317
|
-
if (err.code !== "ENOENT")
|
|
318
|
-
|
|
426
|
+
if (err.code !== "ENOENT") {
|
|
427
|
+
console.error(`File deletion failed for '${attrs.deletePath}':`, err);
|
|
428
|
+
return Results.failure("scheme:file", "delete-failed", 500, `The file could not be deleted: ${ErrorDetail.preview(err)}`, {
|
|
429
|
+
outcome: "delete_failed",
|
|
430
|
+
}, {
|
|
431
|
+
path: attrs.deletePath,
|
|
432
|
+
stage: "filesystem-delete",
|
|
433
|
+
});
|
|
434
|
+
}
|
|
319
435
|
}
|
|
320
436
|
await EntryCrud.deleteEntry(attrs.deletePath, core, "file");
|
|
321
437
|
return { status: 200 };
|
|
@@ -324,19 +440,19 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
324
440
|
const relPath = attrs.path;
|
|
325
441
|
const patched = body ?? attrs.patched;
|
|
326
442
|
if (typeof canonical !== "string" || canonical.length === 0) {
|
|
327
|
-
|
|
443
|
+
throw new InvalidOperationResultError("The accepted file proposal is missing attrs.canonical.");
|
|
328
444
|
}
|
|
329
445
|
if (typeof relPath !== "string" || relPath.length === 0) {
|
|
330
|
-
|
|
446
|
+
throw new InvalidOperationResultError("The accepted file proposal is missing attrs.path.");
|
|
331
447
|
}
|
|
332
448
|
if (typeof patched !== "string") {
|
|
333
|
-
|
|
449
|
+
throw new InvalidOperationResultError("The accepted file proposal is missing patched content.");
|
|
334
450
|
}
|
|
335
451
|
// CAS — the write-side twin of #materializeMember's read-gate (synced_sig === sig). The
|
|
336
452
|
// proposal was computed against the snapshot (body + baseSig); if disk drifted out-of-band
|
|
337
453
|
// since propose, the full-blob write would clobber it. Refuse and surface a write_conflict —
|
|
338
454
|
// the next reconcile narrates disk truth via FsDivergence; the model re-reads + re-proposes.
|
|
339
|
-
// No clever re-diff, no clobber. §membership-edit-write-cas
|
|
455
|
+
// No clever re-diff, no clobber. {§membership-edit-write-cas}
|
|
340
456
|
const baseSig = (args.attrs.baseSig ?? null);
|
|
341
457
|
const existed = args.attrs.existed === true; // proposal targeted an existing member (edit) vs a fresh create
|
|
342
458
|
let currentSig = null;
|
|
@@ -353,18 +469,28 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
353
469
|
// assumed an absent path, so any file present now is the conflict.
|
|
354
470
|
const conflict = existed ? (baseSig !== null && currentSig !== baseSig) : (currentSig !== null);
|
|
355
471
|
if (conflict) {
|
|
356
|
-
|
|
472
|
+
const expected = existed ? baseSig : null;
|
|
473
|
+
const detail = `${relPath} changed on disk since the proposal; expected ${expected ?? "absence"} but found ${currentSig ?? "absence"}.`;
|
|
474
|
+
return Results.failure("scheme:file", "write-conflict", 409, detail, {
|
|
475
|
+
outcome: "write_conflict",
|
|
476
|
+
}, {
|
|
477
|
+
path: relPath,
|
|
478
|
+
expectedSignature: expected,
|
|
479
|
+
currentSignature: currentSig,
|
|
480
|
+
recovery: "READ the current file before proposing a new EDIT.",
|
|
481
|
+
retryable: false,
|
|
482
|
+
});
|
|
357
483
|
}
|
|
358
484
|
let receipt = attrs.editReceipt;
|
|
359
485
|
if (body !== undefined && body !== attrs.patched) {
|
|
360
486
|
const original = existed ? await readFile(canonical, "utf8") : "";
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
487
|
+
if (receipt === undefined) {
|
|
488
|
+
if (typeof attrs.patched !== "string") {
|
|
489
|
+
throw new InvalidOperationResultError("The reviewer-modified file proposal is missing its proposed content.");
|
|
490
|
+
}
|
|
491
|
+
receipt = editReceipt(original, attrs.patched, [{ marker: { marks: [1, -1] }, body: attrs.patched }]);
|
|
492
|
+
}
|
|
493
|
+
receipt = reviewerReplacementReceipt(original, patched, receipt);
|
|
368
494
|
}
|
|
369
495
|
try {
|
|
370
496
|
// A write into a not-yet-existing subtree creates it — an accepted proposal must not
|
|
@@ -373,20 +499,19 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
373
499
|
await writeFile(canonical, patched, "utf8");
|
|
374
500
|
}
|
|
375
501
|
catch (err) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
};
|
|
502
|
+
console.error(`File write failed for '${relPath}':`, err);
|
|
503
|
+
return Results.failure("scheme:file", "write-failed", 500, `The file could not be written: ${ErrorDetail.preview(err)}`, { outcome: "write_failed" }, {
|
|
504
|
+
path: relPath,
|
|
505
|
+
stage: "filesystem-write",
|
|
506
|
+
});
|
|
381
507
|
}
|
|
382
508
|
// Register the file as an entry so it appears in the manifest
|
|
383
509
|
// under file:///<relPath> and is READ-able. mimetype is best-effort
|
|
384
510
|
// by file extension; .md → text/markdown, anything else → text/plain.
|
|
385
511
|
const mimetype = relPath.endsWith(".md") ? "text/markdown" : "text/plain";
|
|
386
512
|
try {
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
// so render-time bare-path output requires no special case.
|
|
513
|
+
// {§entry-identity-no-null} — file members persist under the reserved
|
|
514
|
+
// `file` identity; bare-path rendering is a projection of that row.
|
|
390
515
|
const { entryId } = await EntryCrud.writeEntry(relPath, {
|
|
391
516
|
channels: { body: { content: patched, mimetype } },
|
|
392
517
|
tags: [],
|
|
@@ -405,37 +530,49 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
405
530
|
}
|
|
406
531
|
}
|
|
407
532
|
catch (err) {
|
|
408
|
-
// Disk
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
body: err instanceof Error ? err.message : String(err),
|
|
417
|
-
};
|
|
533
|
+
// Disk truth changed but the addressable entry did not. This is a
|
|
534
|
+
// partial failure, never a successful operation result.
|
|
535
|
+
console.error(`File registration failed for '${relPath}':`, err);
|
|
536
|
+
return Results.failure("scheme:file", "materialization-failed", 500, `The file was written to disk but could not be registered: ${ErrorDetail.preview(err)}`, { outcome: "materialize_failed" }, {
|
|
537
|
+
path: relPath,
|
|
538
|
+
stage: "entry-registration",
|
|
539
|
+
retryable: false,
|
|
540
|
+
});
|
|
418
541
|
}
|
|
419
542
|
if (receipt === undefined)
|
|
420
|
-
return { status: 200
|
|
421
|
-
return {
|
|
543
|
+
return { status: 200 };
|
|
544
|
+
return {
|
|
545
|
+
status: 200,
|
|
546
|
+
editReceipt: receipt,
|
|
547
|
+
result: { receipt: projectEditReceipt(receipt, 0) },
|
|
548
|
+
};
|
|
422
549
|
}
|
|
423
550
|
// deleteEntry — the KILL / MOVE-source counterpart of writeEntry. Deleting a host file is
|
|
424
551
|
// DESTRUCTIVE, so it PROPOSES for review (202), exactly as edit() does — never an ungated unlink.
|
|
425
|
-
// §membership — only a MEMBER reaches the proposal; a non-member is invisible (404), so untracked
|
|
552
|
+
// {§membership} — only a MEMBER reaches the proposal; a non-member is invisible (404), so untracked
|
|
426
553
|
// disk is never probed or touched. applyResolution unlinks + deregisters on accept.
|
|
427
554
|
async deleteEntry(pathname, ctx) {
|
|
428
555
|
const core = this.coreContext(ctx);
|
|
429
556
|
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
430
|
-
if (root === null)
|
|
431
|
-
return {
|
|
557
|
+
if (root === null) {
|
|
558
|
+
return Results.failure("scheme:file", "project-root-required", 400, "File deletion requires a workspace project root.", {}, { retryable: false });
|
|
559
|
+
}
|
|
432
560
|
const rel = Namespace.canonicalize(pathname, root);
|
|
433
561
|
if (rel === null)
|
|
434
|
-
return {
|
|
562
|
+
return Results.failure("scheme:file", "entry-not-found", 404, `No file entry exists at ${pathname}.`, {}, { target: pathname });
|
|
435
563
|
const member = await core.db.crud_find_workspace_entry.get({ workspace_id: core.workspaceId, owner_id: await Owner.commonsId(core.db, core.workspaceId), scheme: "file", pathname: rel });
|
|
436
564
|
if (member === undefined)
|
|
437
|
-
return {
|
|
565
|
+
return Results.failure("scheme:file", "entry-not-found", 404, `No file entry exists at ${rel}.`, {}, { target: rel });
|
|
438
566
|
return { status: 202, attrs: { deletePath: rel } };
|
|
439
567
|
}
|
|
568
|
+
async deleteChannel(pathname, channel, ctx) {
|
|
569
|
+
if (channel !== File.manifest.defaultChannel) {
|
|
570
|
+
return Results.failure("scheme:file", "channel-not-found", 404, `No channel named #${channel} exists at ${pathname}.`, {}, {
|
|
571
|
+
target: pathname,
|
|
572
|
+
channel,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
return this.deleteEntry(pathname, ctx);
|
|
576
|
+
}
|
|
440
577
|
}
|
|
441
578
|
//# sourceMappingURL=File.js.map
|