@plurnk/plurnk-service 1.3.12 → 1.5.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 +95 -91
- package/INSTALL.md +54 -28
- package/README.md +25 -10
- package/SPEC.md +2426 -942
- package/dist/Paths.d.ts +4 -1
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +24 -23
- 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 +4 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +2 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/line-marker.d.ts +10 -9
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.js +8 -11
- package/dist/content/line-marker.js.map +1 -1
- package/dist/content/matcher.d.ts +15 -26
- 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-projector.d.ts +14 -0
- package/dist/content/read-projector.d.ts.map +1 -0
- package/dist/content/read-projector.js +86 -0
- package/dist/content/read-projector.js.map +1 -0
- package/dist/content/read-resolve.d.ts +6 -7
- package/dist/content/read-resolve.d.ts.map +1 -1
- package/dist/content/read-resolve.js +36 -59
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/BranchReceipt.d.ts +6 -0
- package/dist/core/BranchReceipt.d.ts.map +1 -0
- package/dist/core/BranchReceipt.js +24 -0
- package/dist/core/BranchReceipt.js.map +1 -0
- package/dist/core/BudgetOverflow.d.ts +20 -0
- package/dist/core/BudgetOverflow.d.ts.map +1 -0
- package/dist/core/BudgetOverflow.js +49 -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 +31 -9
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +30 -23
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +14 -12
- package/dist/core/CoreSchemeServices.d.ts +18 -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 +26 -21
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +1626 -671
- 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 +64 -29
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +1348 -835
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +329 -234
- 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 +18 -0
- package/dist/core/LogBody.d.ts.map +1 -0
- package/dist/core/LogBody.js +180 -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 -2
- package/dist/core/LoopLifecycle.d.ts.map +1 -1
- package/dist/core/LoopLifecycle.js +54 -11
- package/dist/core/LoopLifecycle.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +10 -7
- 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 +27 -25
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +321 -383
- 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 +6 -3
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +27 -56
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TerminalResult.d.ts +16 -0
- package/dist/core/TerminalResult.d.ts.map +1 -0
- package/dist/core/TerminalResult.js +49 -0
- package/dist/core/TerminalResult.js.map +1 -0
- 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 +5 -1
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js +8 -4
- 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 +30 -12
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +5 -14
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +27 -8
- 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 +18 -15
- 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 -6
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +75 -66
- 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 +20 -11
- package/dist/core/caps/DbTagCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +6 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +6 -6
- package/dist/core/caps/SchemeCtxImpl.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 +43 -29
- 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 +3 -2
- package/dist/core/git-iso.d.ts.map +1 -1
- package/dist/core/git-iso.js +36 -27
- package/dist/core/git-iso.js.map +1 -1
- package/dist/core/git-membership.d.ts +2 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +276 -162
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts +8 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +44 -26
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/namespace.d.ts +1 -0
- package/dist/core/namespace.d.ts.map +1 -1
- package/dist/core/namespace.js +20 -0
- package/dist/core/namespace.js.map +1 -1
- package/dist/core/optimistic-settlement.d.ts +2 -0
- package/dist/core/optimistic-settlement.d.ts.map +1 -0
- package/dist/core/optimistic-settlement.js +14 -0
- package/dist/core/optimistic-settlement.js.map +1 -0
- 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 +3 -21
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +305 -385
- 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 +18 -5
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +33 -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 +3 -17
- 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} +14 -14
- 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 +510 -149
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +23 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- 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 +9 -14
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +402 -262
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts +9 -9
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +49 -18
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts +7 -13
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +280 -151
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +8 -12
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +402 -233
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +71 -32
- package/dist/schemes/Prompt.d.ts +3 -3
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +10 -10
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts +2 -3
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +2 -6
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts +11 -19
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +210 -89
- 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 +11 -9
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +72 -13
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +28 -10
- package/dist/schemes/_entry-find.d.ts +49 -18
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +348 -195
- 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 +14 -15
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +121 -110
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +2 -3
- package/dist/schemes/_entry-semantic.d.ts +22 -9
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +127 -67
- 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 +328 -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 +49 -0
- package/dist/server/BranchBatches.d.ts.map +1 -0
- package/dist/server/BranchBatches.js +592 -0
- package/dist/server/BranchBatches.js.map +1 -0
- package/dist/server/Daemon.d.ts +39 -71
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +1133 -491
- 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 +154 -0
- package/dist/server/client-input.d.ts +13 -0
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +268 -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 +96 -65
- package/dist/server/envelope.d.ts +4 -7
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +118 -95
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +18 -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 +112 -2
- package/dist/server/logEntry.d.ts +4 -2
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +6 -3
- 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 +79 -61
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +1010 -0
- package/package.json +65 -32
- 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
|
-
import EntryOps from "./_entry-ops.js";
|
|
9
8
|
import EntryFind from "./_entry-find.js";
|
|
10
9
|
import EntryCrud from "./_entry-crud.js";
|
|
11
10
|
import { CoreSchemeAdapterBase } from "../core/CoreSchemeServices.js";
|
|
12
|
-
import
|
|
11
|
+
import ErrorDetail from "../core/ErrorDetail.js";
|
|
12
|
+
import Results from "../core/results.js";
|
|
13
|
+
import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
|
|
14
|
+
import { LineMarkerOps, MimetypeBinary, editReceipt, projectEditReceipt, reviewerReplacementReceipt, } from "../content/index.js";
|
|
13
15
|
// 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.
|
|
16
|
+
// supplied at {§methods-workspace-create}. Under {§fs-namespace}, core never
|
|
17
|
+
// guesses a root: headless workspaces fail file operations at 400.
|
|
19
18
|
const loadWorkspaceRoot = async (db, workspaceId) => {
|
|
20
19
|
const row = await db.envelope_get_workspace.get({ id: workspaceId });
|
|
21
20
|
return row?.project_root ?? null;
|
|
22
21
|
};
|
|
23
|
-
// Detect mimetype from a file's path
|
|
24
|
-
// when available; falls back to the text primitive (text/markdown). The
|
|
22
|
+
// Detect mimetype from a file's path through the configured registry. The
|
|
25
23
|
// `MimetypeBinary.normalizeAutoTextMimetype` wrapper ensures text/plain returned by the
|
|
26
24
|
// service is normalized to text/markdown — plurnk-service never auto-
|
|
27
25
|
// derives text/plain (see mimetype-binary.ts MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE).
|
|
28
26
|
const detectFileMimetype = async (canonical, ctx) => {
|
|
29
|
-
if (ctx.mimetypes
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE;
|
|
27
|
+
if (ctx.mimetypes === undefined)
|
|
28
|
+
throw new Error("detectFileMimetype: configured mimetype registry is required");
|
|
29
|
+
const detected = await ctx.mimetypes.detect({ path: canonical });
|
|
30
|
+
return MimetypeBinary.normalizeAutoTextMimetype(detected);
|
|
34
31
|
};
|
|
35
|
-
// SECURITY — File
|
|
36
|
-
//
|
|
37
|
-
// (scheme=
|
|
38
|
-
// entry, so read
|
|
32
|
+
// SECURITY — File's exact READ uses the core projector and broad FIND uses the
|
|
33
|
+
// shared entry query over the membership-materialized entries
|
|
34
|
+
// (stored under scheme="file"). The membership gate is now ENTRY-EXISTENCE — a non-member has no
|
|
35
|
+
// entry, so every read-side consumer 404s it at the shared entry boundary
|
|
39
36
|
// (a gitignored `.env` is never a member → never an entry → never readable). Disk
|
|
40
37
|
// 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
|
|
38
|
+
// and edit()/applyResolution()'s proposal-gated write-OUT ({§membership}), where the
|
|
42
39
|
// containment/traversal checks live.
|
|
43
40
|
//
|
|
44
|
-
// 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
|
|
41
|
+
// Core-only writeEntry() is the proposal-gated write-back: a COPY/MOVE *into* file:/// is a disk
|
|
42
|
+
// write, so it flows through the SAME {§membership} gate as EDIT (#resolveWriteTarget) — a
|
|
46
43
|
// 202 proposal, then applyResolution() writes on accept — never an ungated overwrite (the
|
|
47
|
-
// `.env`-wipe this guard prevents). COPY/MOVE *from* file:///
|
|
44
|
+
// `.env`-wipe this guard prevents). COPY/MOVE *from* file:/// uses the core-only
|
|
45
|
+
// readEntry adapter.
|
|
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,18 +57,6 @@ 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
|
-
// Entry-backed, identical to Known (the unified-addressing promise): read the
|
|
64
|
-
// membership-materialized entry (scheme=null) through the shared helper. <L> /
|
|
65
|
-
// body / binary-415 / tag-404 are all handled there; a non-member has no entry
|
|
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.
|
|
68
|
-
async read(statement, ctx) {
|
|
69
|
-
const core = this.coreContext(ctx);
|
|
70
|
-
// {§fs-namei} — canonicalize ONCE, up front: resolution never depends on what exists
|
|
71
|
-
// (the old fold-on-miss retry was existence-dependent meaning, run59's disease class).
|
|
72
|
-
const canon = File.#canonTarget(statement, await loadWorkspaceRoot(core.db, core.workspaceId));
|
|
73
|
-
return EntryOps.readWorkspaceEntry(canon ?? statement, core, File.manifest);
|
|
74
|
-
}
|
|
75
60
|
// {§fs-namei}/{§fs-canonical-name} — the ONE statement-normalizing seam: every model
|
|
76
61
|
// spelling resolves through Namespace.canonicalize before storage, comparison, or render.
|
|
77
62
|
// null (a spelling that names nothing a file can be) falls back to the original statement,
|
|
@@ -94,11 +79,16 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
94
79
|
}
|
|
95
80
|
return statement; // regex — not a path
|
|
96
81
|
}
|
|
97
|
-
// Folderhood survives canonicalization (§find-scope-prefix-filter) — the shared
|
|
82
|
+
// Folderhood survives canonicalization ({§find-scope-prefix-filter}) — the shared
|
|
98
83
|
// spelling canon lives on Namespace (the Dispatcher's log columns use the same one).
|
|
99
84
|
static #canonSpelling(raw, root) {
|
|
100
85
|
return Namespace.canonicalizeSpelling(raw, root);
|
|
101
86
|
}
|
|
87
|
+
async resolveEntryAddress(target, ctx) {
|
|
88
|
+
const core = this.coreContext(ctx);
|
|
89
|
+
const pathname = File.#canonSpelling(target.kind === "url" ? target.pathname : target.raw, await loadWorkspaceRoot(core.db, core.workspaceId));
|
|
90
|
+
return pathname === null ? null : { pathname, owner: "commons" };
|
|
91
|
+
}
|
|
102
92
|
async find(statement, ctx) {
|
|
103
93
|
const core = this.coreContext(ctx);
|
|
104
94
|
// {§fs-namei} — canonicalize the glob's path portion before the candidate scan, the
|
|
@@ -111,29 +101,47 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
111
101
|
// see the SECURITY note above.
|
|
112
102
|
async readEntry(pathname, ctx) {
|
|
113
103
|
const core = this.coreContext(ctx);
|
|
114
|
-
// §scheme-address — normalize the model-typed path (bare `brief.md`) to its `/rel` member key,
|
|
104
|
+
// {§scheme-address} — normalize the model-typed path (bare `brief.md`) to its `/rel` member key,
|
|
115
105
|
// the same parity READ/EDIT/deleteEntry have. Without it a COPY/MOVE FROM a bare file path
|
|
116
106
|
// misses the canonical-stored member and 404s a source that plainly exists.
|
|
117
107
|
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
118
108
|
const key = root === null ? pathname : Namespace.canonicalize(pathname, root);
|
|
119
109
|
return EntryCrud.readEntry(key ?? pathname, core, "file");
|
|
120
110
|
}
|
|
121
|
-
// §membership disk-write gate, shared by edit() and writeEntry() (the COPY/MOVE
|
|
111
|
+
// {§membership} disk-write gate, shared by edit() and writeEntry() (the COPY/MOVE
|
|
122
112
|
// dest). Resolves the canonical path; enforces containment (traversal → 403),
|
|
123
113
|
// membership (existing non-member → 403, the `.env` protection), the read-only
|
|
124
114
|
// overlay (→ 403), and binary (→ 415); reads current content for the diff. A
|
|
125
115
|
// not-found path is fine — we propose to CREATE. ONE home for the gate: an edit
|
|
126
116
|
// 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
|
|
117
|
+
// {§membership-edit-membership-gate} — membership/containment/read-only/binary gate before any disk write
|
|
128
118
|
async #resolveWriteTarget(pathname, ctx) {
|
|
129
119
|
const root = await loadWorkspaceRoot(ctx.db, ctx.workspaceId);
|
|
130
|
-
if (root === null)
|
|
131
|
-
return {
|
|
120
|
+
if (root === null) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
code: "project-root-required",
|
|
124
|
+
status: 400,
|
|
125
|
+
detail: "The workspace has no project root, so it cannot write files.",
|
|
126
|
+
extensions: { retryable: false },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
132
129
|
// {§fs-namei} — the write side resolves through the SAME canonicalizer as reads;
|
|
133
130
|
// a spelling that names nothing a file can be is refused before any disk touch.
|
|
134
131
|
const key = Namespace.canonicalize(pathname, root);
|
|
135
|
-
if (key === null)
|
|
136
|
-
return {
|
|
132
|
+
if (key === null) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
code: "path-outside-workspace",
|
|
136
|
+
status: 403,
|
|
137
|
+
detail: `The requested path '${pathname}' is outside the workspace.`,
|
|
138
|
+
extensions: {
|
|
139
|
+
requestedPath: pathname,
|
|
140
|
+
recovery: "Use a path within the workspace.",
|
|
141
|
+
retryable: false,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
137
145
|
const isMount = key.startsWith("../");
|
|
138
146
|
let canonical;
|
|
139
147
|
const requested = join(root, key);
|
|
@@ -148,13 +156,35 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
148
156
|
fileExists = false;
|
|
149
157
|
}
|
|
150
158
|
// {§fs-write-surface} 6 — only the root mints: no create on any mount, ever.
|
|
151
|
-
if (isMount && !fileExists)
|
|
152
|
-
return {
|
|
159
|
+
if (isMount && !fileExists) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
code: "mount-create-forbidden",
|
|
163
|
+
status: 403,
|
|
164
|
+
detail: `The mounted path '${key}' does not permit file creation.`,
|
|
165
|
+
extensions: {
|
|
166
|
+
path: key,
|
|
167
|
+
recovery: "Create the file inside the project root.",
|
|
168
|
+
retryable: false,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
153
172
|
if (!isMount) {
|
|
154
173
|
const relBare = relative(root, canonical);
|
|
155
174
|
// in-tree keys whose realpath escapes (a symlink out) stay refused — the jail holds.
|
|
156
|
-
if (relBare.startsWith("..") || isAbsolute(relBare))
|
|
157
|
-
return {
|
|
175
|
+
if (relBare.startsWith("..") || isAbsolute(relBare)) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
code: "path-outside-workspace",
|
|
179
|
+
status: 403,
|
|
180
|
+
detail: `The requested path '${pathname}' resolves outside the workspace.`,
|
|
181
|
+
extensions: {
|
|
182
|
+
requestedPath: pathname,
|
|
183
|
+
recovery: "Use a path within the workspace.",
|
|
184
|
+
retryable: false,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
158
188
|
}
|
|
159
189
|
const rel = key; // the bare canonical member key ({§fs-canonical-name}) — storage ≡ wire
|
|
160
190
|
let original = "";
|
|
@@ -164,116 +194,178 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
164
194
|
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
195
|
// {§fs-errno} — the occupancy fact (POSIX O_EXCL precedent): something invisible
|
|
166
196
|
// occupies the path; existence leaks, content stays dark. The model picks another name.
|
|
167
|
-
if (member === undefined)
|
|
168
|
-
return {
|
|
197
|
+
if (member === undefined) {
|
|
198
|
+
return {
|
|
199
|
+
ok: false,
|
|
200
|
+
code: "path-occupied-by-nonmember",
|
|
201
|
+
status: 403,
|
|
202
|
+
detail: `A non-member file already occupies '${rel}'.`,
|
|
203
|
+
extensions: {
|
|
204
|
+
path: rel,
|
|
205
|
+
recovery: "Choose an unoccupied member path.",
|
|
206
|
+
retryable: false,
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
169
210
|
// {§fs-write-surface} 5 — a git-included mount member is read-only: git's grants
|
|
170
211
|
// confer rw only within the project; only an explicit client grant carries write.
|
|
171
|
-
if (isMount && member.membership_origin === "git")
|
|
172
|
-
return {
|
|
212
|
+
if (isMount && member.membership_origin === "git") {
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
code: "member-read-only",
|
|
216
|
+
status: 403,
|
|
217
|
+
detail: `The mounted member '${rel}' is read-only.`,
|
|
218
|
+
extensions: { path: rel, retryable: false },
|
|
219
|
+
};
|
|
220
|
+
}
|
|
173
221
|
const viewGlobs = (await ctx.db.crud_list_workspace_constraints.all({ workspace_id: ctx.workspaceId }))
|
|
174
222
|
.filter((c) => c.effect === "view").map((c) => c.glob);
|
|
175
|
-
if (viewGlobs.some((g) => matchesGlob(rel, g)))
|
|
176
|
-
return {
|
|
223
|
+
if (viewGlobs.some((g) => matchesGlob(rel, g))) {
|
|
224
|
+
return {
|
|
225
|
+
ok: false,
|
|
226
|
+
code: "member-read-only",
|
|
227
|
+
status: 403,
|
|
228
|
+
detail: `The member '${rel}' is read-only.`,
|
|
229
|
+
extensions: { path: rel, retryable: false },
|
|
230
|
+
};
|
|
231
|
+
} // view = read-only member, 403 on edit - {§membership-overlay-view}
|
|
177
232
|
// The diff base is the entry's snapshot — the body channel the model READ — not a fresh
|
|
178
233
|
// disk read. EDIT is naive against the view the model saw; the write-side CAS (applyResolution)
|
|
179
234
|
// 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
|
|
235
|
+
// worker's reconcile can't advance it under the paused proposal. {§membership-edit-write-cas}
|
|
181
236
|
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
237
|
original = snapshot?.content ?? "";
|
|
183
238
|
baseSig = member.synced_sig;
|
|
184
239
|
}
|
|
185
240
|
else {
|
|
186
241
|
// {§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
|
-
//
|
|
242
|
+
// where the RESULT will be a member. A client pick admits it; otherwise
|
|
243
|
+
// Git's untracked-not-ignored membership must ({§membership-auto-add}).
|
|
244
|
+
// No staging occurs. A non-git root grants nothing by itself.
|
|
189
245
|
const picks = (await ctx.db.crud_list_workspace_constraints.all({ workspace_id: ctx.workspaceId }))
|
|
190
246
|
.filter((c) => c.effect === "pick").map((c) => c.glob);
|
|
191
247
|
const clientAdmits = picks.some((g) => matchesGlob(rel, g));
|
|
192
248
|
if (!clientAdmits && !(await GitMembership.wouldGitAdmit(root, rel, ctx.signal))) {
|
|
193
|
-
return {
|
|
249
|
+
return {
|
|
250
|
+
ok: false,
|
|
251
|
+
code: "file-not-admitted",
|
|
252
|
+
status: 403,
|
|
253
|
+
detail: `The new file '${rel}' would not belong to the workspace because Git ignores it and no client pick includes it.`,
|
|
254
|
+
extensions: {
|
|
255
|
+
path: rel,
|
|
256
|
+
recovery: "Choose a path admitted by Git or the workspace picks.",
|
|
257
|
+
retryable: false,
|
|
258
|
+
},
|
|
259
|
+
};
|
|
194
260
|
}
|
|
195
261
|
// {§fs-write-surface} — the closure PROVED the grantor; the accept stamps what was
|
|
196
262
|
// proven instead of leaving provenance NULL until the next reconcile guesses it.
|
|
197
263
|
admittedBy = clientAdmits ? "client" : "git";
|
|
198
264
|
}
|
|
199
265
|
const mimetype = await detectFileMimetype(canonical, ctx);
|
|
200
|
-
if (MimetypeBinary.isBinaryMimetype(mimetype))
|
|
201
|
-
return {
|
|
266
|
+
if (await MimetypeBinary.isBinaryMimetype(mimetype, ctx.mimetypes)) {
|
|
267
|
+
return {
|
|
268
|
+
ok: false,
|
|
269
|
+
code: "binary-write-unsupported",
|
|
270
|
+
status: 415,
|
|
271
|
+
detail: `File EDIT does not support binary mimetype '${mimetype}'.`,
|
|
272
|
+
extensions: {
|
|
273
|
+
mimetype,
|
|
274
|
+
retryable: false,
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
202
278
|
return { ok: true, canonical, rel, fileExists, original, mimetype, baseSig, ...(admittedBy !== undefined ? { admittedBy } : {}) };
|
|
203
279
|
}
|
|
204
|
-
//
|
|
205
|
-
// with a udiff body for client review
|
|
206
|
-
// content. Engine
|
|
207
|
-
// calls applyResolution() (below) after the proposal accepts.
|
|
280
|
+
// {§membership-edit-write-cas}, {§proposal-202-pauses}: return status=202
|
|
281
|
+
// with a udiff body for client review and attrs carrying the full patched
|
|
282
|
+
// content. Engine pauses dispatch and calls applyResolution() after accept.
|
|
208
283
|
async editBatch(statements, ctx) {
|
|
284
|
+
const failure = (code, status, detail, extensions = {}) => Results.failure("scheme:file", code, status, detail, {}, extensions);
|
|
209
285
|
const statement = statements[0];
|
|
210
|
-
if (statement === undefined)
|
|
211
|
-
return
|
|
286
|
+
if (statement === undefined) {
|
|
287
|
+
return failure("edit-empty", 400, "EDIT requires at least one statement.", {
|
|
288
|
+
recovery: "Provide an EDIT statement.",
|
|
289
|
+
retryable: false,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
212
292
|
const core = this.coreContext(ctx);
|
|
213
|
-
if (statement.target === null)
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
293
|
+
if (statement.target === null) {
|
|
294
|
+
return failure("edit-target-required", 400, "EDIT requires a target path.", {
|
|
295
|
+
recovery: "Provide the file target.",
|
|
296
|
+
retryable: false,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
const pathname = PathSyntax.decodeParens(statement.target.kind === "url" ? statement.target.pathname : statement.target.raw); // {§path-parentheses}
|
|
217
300
|
const target = await this.#resolveWriteTarget(pathname, core);
|
|
218
301
|
if (!target.ok)
|
|
219
|
-
return
|
|
302
|
+
return failure(target.code, target.status, target.detail, target.extensions);
|
|
220
303
|
const { canonical, rel, fileExists, original, mimetype, baseSig, admittedBy } = target;
|
|
221
304
|
for (const candidate of statements.slice(1)) {
|
|
222
|
-
if (candidate.target === null)
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
|
|
305
|
+
if (candidate.target === null) {
|
|
306
|
+
return failure("edit-batch-mismatch", 400, "The EDIT batch spans multiple resources.", {
|
|
307
|
+
recovery: "Submit a separate EDIT batch for each resource.",
|
|
308
|
+
retryable: false,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const candidatePathname = PathSyntax.decodeParens(candidate.target.kind === "url" ? candidate.target.pathname : candidate.target.raw);
|
|
226
312
|
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.
|
|
313
|
+
if (!candidateTarget.ok || candidateTarget.rel !== rel) {
|
|
314
|
+
return failure("edit-batch-mismatch", 400, "The EDIT batch spans multiple resources.", {
|
|
315
|
+
recovery: "Submit a separate EDIT batch for each resource.",
|
|
316
|
+
retryable: false,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// {§edit-marker-required-on-existing} — markerless content creates a new
|
|
321
|
+
// file; every existing-file rewrite states its range explicitly.
|
|
240
322
|
let patched;
|
|
323
|
+
let scopeNormalizations;
|
|
241
324
|
if (fileExists) {
|
|
242
|
-
if (statements.some(({ lineMarker }) => lineMarker === null))
|
|
243
|
-
return
|
|
325
|
+
if (statements.some(({ lineMarker }) => lineMarker === null)) {
|
|
326
|
+
return failure("line-marker-required", 400, "EDIT of an existing file requires a line marker.", {
|
|
327
|
+
recovery: "Use <1,-1> to replace the whole file or select a narrower range.",
|
|
328
|
+
retryable: false,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
244
331
|
const edits = statements.map((candidate) => ({ marker: candidate.lineMarker, body: candidate.body ?? "" }));
|
|
245
|
-
const result =
|
|
246
|
-
? LineMarkerOps.applyJsonItemEditBatch(original, edits)
|
|
247
|
-
: LineMarkerOps.applyLineMarkerEditBatch(original, edits);
|
|
332
|
+
const result = LineMarkerOps.applyLineMarkerEditBatch(original, edits);
|
|
248
333
|
if (result.status !== 200)
|
|
249
|
-
return
|
|
334
|
+
return Results.assert(result);
|
|
250
335
|
patched = result.result ?? "";
|
|
336
|
+
scopeNormalizations = result.scopeNormalizations;
|
|
251
337
|
}
|
|
252
338
|
else {
|
|
253
|
-
if (statements.length !== 1)
|
|
254
|
-
return
|
|
339
|
+
if (statements.length !== 1) {
|
|
340
|
+
return failure("creation-batch-conflict", 409, "Multiple EDIT operations attempted to create the same file.", {
|
|
341
|
+
recovery: "Create the file with one EDIT before applying additional edits.",
|
|
342
|
+
retryable: false,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
255
345
|
patched = statement.body ?? "";
|
|
256
346
|
}
|
|
257
347
|
if (fileExists && patched === original)
|
|
258
|
-
return {
|
|
348
|
+
return {
|
|
349
|
+
status: 304,
|
|
350
|
+
...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
|
|
351
|
+
}; // {§edit-noop-304}
|
|
259
352
|
const patch = createPatch(rel, original, patched, "current", "proposed");
|
|
260
353
|
const receiptEdits = fileExists
|
|
261
354
|
? statements.map((candidate) => ({ marker: candidate.lineMarker, body: candidate.body ?? "" }))
|
|
262
355
|
: [{ marker: { marks: [1, -1] }, body: patched }];
|
|
263
|
-
const batchReceipt = editReceipt(original, patched, receiptEdits
|
|
264
|
-
unit: editReceiptUnit(MimetypeBinary.isJsonMimetype(mimetype), original, patched),
|
|
265
|
-
});
|
|
356
|
+
const batchReceipt = editReceipt(original, patched, receiptEdits);
|
|
266
357
|
return {
|
|
267
358
|
status: 202,
|
|
268
359
|
body: patch,
|
|
269
360
|
editReceipt: batchReceipt,
|
|
361
|
+
...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
|
|
270
362
|
attrs: { path: rel, canonical, patch, patched, editReceipt: batchReceipt, baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) },
|
|
271
363
|
};
|
|
272
364
|
}
|
|
273
365
|
async edit(statement, ctx) {
|
|
274
366
|
return this.editBatch([statement], ctx);
|
|
275
367
|
}
|
|
276
|
-
// COPY/MOVE INTO file:/// — the dest write. Same §membership gate as edit, same 202
|
|
368
|
+
// COPY/MOVE INTO file:/// — the dest write. Same {§membership} gate as edit, same 202
|
|
277
369
|
// proposal + applyResolution path: a copy onto disk is a disk write and earns
|
|
278
370
|
// the identical human review. Dispatcher.#copyOrchestration propagates this 202;
|
|
279
371
|
// ProposalLifecycle.workerApply routes the accept back here via the dest scheme;
|
|
@@ -282,24 +374,28 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
282
374
|
async writeEntry(pathname, entry, ctx) {
|
|
283
375
|
const core = this.coreContext(ctx);
|
|
284
376
|
const bodyChannel = entry.channels.body;
|
|
285
|
-
if (bodyChannel === undefined)
|
|
286
|
-
return
|
|
377
|
+
if (bodyChannel === undefined) {
|
|
378
|
+
return Results.failure("scheme:file", "body-channel-required", 400, "A file write requires a body channel.", { created: false, entryId: null }, {
|
|
379
|
+
recovery: "Provide the file content in the body channel.",
|
|
380
|
+
retryable: false,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
287
383
|
const target = await this.#resolveWriteTarget(pathname, core);
|
|
288
|
-
if (!target.ok)
|
|
289
|
-
return
|
|
384
|
+
if (!target.ok) {
|
|
385
|
+
return Results.failure("scheme:file", target.code, target.status, target.detail, { created: false, entryId: null }, target.extensions);
|
|
386
|
+
}
|
|
290
387
|
const { canonical, rel, fileExists, original, baseSig, admittedBy } = target;
|
|
291
388
|
const patched = bodyChannel.content;
|
|
292
389
|
const patch = createPatch(rel, original, patched, "current", "proposed");
|
|
293
|
-
return { status: 202, created: !fileExists, entryId: null, body: patch, attrs: { path: rel, canonical, patch, patched,
|
|
390
|
+
return { status: 202, created: !fileExists, entryId: null, body: patch, attrs: { path: rel, canonical, patch, patched, baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) } };
|
|
294
391
|
}
|
|
295
392
|
// applyResolution — called by Engine.dispatch after a proposed log
|
|
296
393
|
// entry resolves with decision=accept. Two responsibilities:
|
|
297
394
|
// 1. Write the patched content to disk.
|
|
298
395
|
// 2. Register the file as an entry so it appears in the manifest and the
|
|
299
396
|
// model can READ its full landed work.
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
// entry-scheme EDIT's span; default-folding reclaims it at the next turn boundary.
|
|
397
|
+
// {§edit-result-receipt-truth} — accepted EDITs return the receipt for what
|
|
398
|
+
// landed. Dispatcher composes COPY/MOVE effects after this hook returns.
|
|
303
399
|
async applyResolution(args, ctx) {
|
|
304
400
|
const core = this.coreContext(ctx);
|
|
305
401
|
const { attrs, body } = args;
|
|
@@ -308,14 +404,22 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
308
404
|
// failure surfaces as 500, never a silent noop; ENOENT ⇒ file already gone, still deregister.
|
|
309
405
|
if (typeof attrs.deletePath === "string") {
|
|
310
406
|
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
311
|
-
if (root === null)
|
|
312
|
-
|
|
407
|
+
if (root === null) {
|
|
408
|
+
throw new InvalidOperationResultError("An accepted file deletion has no workspace project root.");
|
|
409
|
+
}
|
|
313
410
|
try {
|
|
314
411
|
await rm(join(root, attrs.deletePath));
|
|
315
412
|
}
|
|
316
413
|
catch (err) {
|
|
317
|
-
if (err.code !== "ENOENT")
|
|
318
|
-
|
|
414
|
+
if (err.code !== "ENOENT") {
|
|
415
|
+
console.error(`File deletion failed for '${attrs.deletePath}':`, err);
|
|
416
|
+
return Results.failure("scheme:file", "delete-failed", 500, `The file could not be deleted: ${ErrorDetail.preview(err)}`, {
|
|
417
|
+
outcome: "delete_failed",
|
|
418
|
+
}, {
|
|
419
|
+
path: attrs.deletePath,
|
|
420
|
+
stage: "filesystem-delete",
|
|
421
|
+
});
|
|
422
|
+
}
|
|
319
423
|
}
|
|
320
424
|
await EntryCrud.deleteEntry(attrs.deletePath, core, "file");
|
|
321
425
|
return { status: 200 };
|
|
@@ -324,19 +428,19 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
324
428
|
const relPath = attrs.path;
|
|
325
429
|
const patched = body ?? attrs.patched;
|
|
326
430
|
if (typeof canonical !== "string" || canonical.length === 0) {
|
|
327
|
-
|
|
431
|
+
throw new InvalidOperationResultError("The accepted file proposal is missing attrs.canonical.");
|
|
328
432
|
}
|
|
329
433
|
if (typeof relPath !== "string" || relPath.length === 0) {
|
|
330
|
-
|
|
434
|
+
throw new InvalidOperationResultError("The accepted file proposal is missing attrs.path.");
|
|
331
435
|
}
|
|
332
436
|
if (typeof patched !== "string") {
|
|
333
|
-
|
|
437
|
+
throw new InvalidOperationResultError("The accepted file proposal is missing patched content.");
|
|
334
438
|
}
|
|
335
439
|
// CAS — the write-side twin of #materializeMember's read-gate (synced_sig === sig). The
|
|
336
440
|
// proposal was computed against the snapshot (body + baseSig); if disk drifted out-of-band
|
|
337
441
|
// since propose, the full-blob write would clobber it. Refuse and surface a write_conflict —
|
|
338
442
|
// 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
|
|
443
|
+
// No clever re-diff, no clobber. {§membership-edit-write-cas}
|
|
340
444
|
const baseSig = (args.attrs.baseSig ?? null);
|
|
341
445
|
const existed = args.attrs.existed === true; // proposal targeted an existing member (edit) vs a fresh create
|
|
342
446
|
let currentSig = null;
|
|
@@ -353,18 +457,28 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
353
457
|
// assumed an absent path, so any file present now is the conflict.
|
|
354
458
|
const conflict = existed ? (baseSig !== null && currentSig !== baseSig) : (currentSig !== null);
|
|
355
459
|
if (conflict) {
|
|
356
|
-
|
|
460
|
+
const expected = existed ? baseSig : null;
|
|
461
|
+
const detail = `${relPath} changed on disk since the proposal; expected ${expected ?? "absence"} but found ${currentSig ?? "absence"}.`;
|
|
462
|
+
return Results.failure("scheme:file", "write-conflict", 409, detail, {
|
|
463
|
+
outcome: "write_conflict",
|
|
464
|
+
}, {
|
|
465
|
+
path: relPath,
|
|
466
|
+
expectedSignature: expected,
|
|
467
|
+
currentSignature: currentSig,
|
|
468
|
+
recovery: "READ the current file before proposing a new EDIT.",
|
|
469
|
+
retryable: false,
|
|
470
|
+
});
|
|
357
471
|
}
|
|
358
472
|
let receipt = attrs.editReceipt;
|
|
359
473
|
if (body !== undefined && body !== attrs.patched) {
|
|
360
474
|
const original = existed ? await readFile(canonical, "utf8") : "";
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
475
|
+
if (receipt === undefined) {
|
|
476
|
+
if (typeof attrs.patched !== "string") {
|
|
477
|
+
throw new InvalidOperationResultError("The reviewer-modified file proposal is missing its proposed content.");
|
|
478
|
+
}
|
|
479
|
+
receipt = editReceipt(original, attrs.patched, [{ marker: { marks: [1, -1] }, body: attrs.patched }]);
|
|
480
|
+
}
|
|
481
|
+
receipt = reviewerReplacementReceipt(original, patched, receipt);
|
|
368
482
|
}
|
|
369
483
|
try {
|
|
370
484
|
// A write into a not-yet-existing subtree creates it — an accepted proposal must not
|
|
@@ -373,20 +487,19 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
373
487
|
await writeFile(canonical, patched, "utf8");
|
|
374
488
|
}
|
|
375
489
|
catch (err) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
};
|
|
490
|
+
console.error(`File write failed for '${relPath}':`, err);
|
|
491
|
+
return Results.failure("scheme:file", "write-failed", 500, `The file could not be written: ${ErrorDetail.preview(err)}`, { outcome: "write_failed" }, {
|
|
492
|
+
path: relPath,
|
|
493
|
+
stage: "filesystem-write",
|
|
494
|
+
});
|
|
381
495
|
}
|
|
382
496
|
// Register the file as an entry so it appears in the manifest
|
|
383
497
|
// under file:///<relPath> and is READ-able. mimetype is best-effort
|
|
384
498
|
// by file extension; .md → text/markdown, anything else → text/plain.
|
|
385
499
|
const mimetype = relPath.endsWith(".md") ? "text/markdown" : "text/plain";
|
|
386
500
|
try {
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
// so render-time bare-path output requires no special case.
|
|
501
|
+
// {§entry-identity-no-null} — file members persist under the reserved
|
|
502
|
+
// `file` identity; bare-path rendering is a projection of that row.
|
|
390
503
|
const { entryId } = await EntryCrud.writeEntry(relPath, {
|
|
391
504
|
channels: { body: { content: patched, mimetype } },
|
|
392
505
|
tags: [],
|
|
@@ -396,6 +509,10 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
396
509
|
const admitted = args.attrs.admittedBy;
|
|
397
510
|
if (entryId !== null && (admitted === "client" || admitted === "git")) {
|
|
398
511
|
await core.db.crud_stamp_origin.run({ entry_id: entryId, membership_origin: admitted });
|
|
512
|
+
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
513
|
+
if (root !== null) {
|
|
514
|
+
await GitMembership.stageFile(root, relPath, ctx.signal);
|
|
515
|
+
}
|
|
399
516
|
}
|
|
400
517
|
// Restamp synced_sig to the landed write so the next reconcile recognizes our own
|
|
401
518
|
// write as the synced state — not an FsDivergence narrated back at the model.
|
|
@@ -405,37 +522,49 @@ export default class File extends CoreSchemeAdapterBase {
|
|
|
405
522
|
}
|
|
406
523
|
}
|
|
407
524
|
catch (err) {
|
|
408
|
-
// Disk
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
body: err instanceof Error ? err.message : String(err),
|
|
417
|
-
};
|
|
525
|
+
// Disk truth changed but the addressable entry did not. This is a
|
|
526
|
+
// partial failure, never a successful operation result.
|
|
527
|
+
console.error(`File registration failed for '${relPath}':`, err);
|
|
528
|
+
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" }, {
|
|
529
|
+
path: relPath,
|
|
530
|
+
stage: "entry-registration",
|
|
531
|
+
retryable: false,
|
|
532
|
+
});
|
|
418
533
|
}
|
|
419
534
|
if (receipt === undefined)
|
|
420
|
-
return { status: 200
|
|
421
|
-
return {
|
|
535
|
+
return { status: 200 };
|
|
536
|
+
return {
|
|
537
|
+
status: 200,
|
|
538
|
+
editReceipt: receipt,
|
|
539
|
+
result: { receipt: projectEditReceipt(receipt, 0) },
|
|
540
|
+
};
|
|
422
541
|
}
|
|
423
542
|
// deleteEntry — the KILL / MOVE-source counterpart of writeEntry. Deleting a host file is
|
|
424
543
|
// 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
|
|
544
|
+
// {§membership} — only a MEMBER reaches the proposal; a non-member is invisible (404), so untracked
|
|
426
545
|
// disk is never probed or touched. applyResolution unlinks + deregisters on accept.
|
|
427
546
|
async deleteEntry(pathname, ctx) {
|
|
428
547
|
const core = this.coreContext(ctx);
|
|
429
548
|
const root = await loadWorkspaceRoot(core.db, core.workspaceId);
|
|
430
|
-
if (root === null)
|
|
431
|
-
return {
|
|
549
|
+
if (root === null) {
|
|
550
|
+
return Results.failure("scheme:file", "project-root-required", 400, "File deletion requires a workspace project root.", {}, { retryable: false });
|
|
551
|
+
}
|
|
432
552
|
const rel = Namespace.canonicalize(pathname, root);
|
|
433
553
|
if (rel === null)
|
|
434
|
-
return {
|
|
554
|
+
return Results.failure("scheme:file", "entry-not-found", 404, `No file entry exists at ${pathname}.`, {}, { target: pathname });
|
|
435
555
|
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
556
|
if (member === undefined)
|
|
437
|
-
return {
|
|
557
|
+
return Results.failure("scheme:file", "entry-not-found", 404, `No file entry exists at ${rel}.`, {}, { target: rel });
|
|
438
558
|
return { status: 202, attrs: { deletePath: rel } };
|
|
439
559
|
}
|
|
560
|
+
async deleteChannel(pathname, channel, ctx) {
|
|
561
|
+
if (channel !== File.manifest.defaultChannel) {
|
|
562
|
+
return Results.failure("scheme:file", "channel-not-found", 404, `No channel named #${channel} exists at ${pathname}.`, {}, {
|
|
563
|
+
target: pathname,
|
|
564
|
+
channel,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
return this.deleteEntry(pathname, ctx);
|
|
568
|
+
}
|
|
440
569
|
}
|
|
441
570
|
//# sourceMappingURL=File.js.map
|