@plurnk/plurnk-service 4.2.1 → 4.3.1
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/INSTALL.md +7 -2
- package/README.md +3 -1
- package/SPEC.md +1988 -1408
- package/dist/build-info.d.ts.map +1 -1
- package/dist/build-info.js +25 -3
- package/dist/build-info.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/completions/_plurnk-service +1 -1
- package/dist/completions/plurnk-service.bash +1 -1
- package/dist/completions/plurnk-service.fish +1 -0
- package/dist/content/body-preview.js.map +1 -1
- package/dist/content/byte-view.d.ts.map +1 -1
- package/dist/content/byte-view.js +3 -2
- package/dist/content/byte-view.js.map +1 -1
- package/dist/content/edit-collision.d.ts +2 -1
- package/dist/content/edit-collision.d.ts.map +1 -1
- package/dist/content/edit-collision.js.map +1 -1
- package/dist/content/edit-receipt.d.ts.map +1 -1
- package/dist/content/edit-receipt.js +28 -23
- package/dist/content/edit-receipt.js.map +1 -1
- package/dist/content/edited-span.js +1 -1
- package/dist/content/edited-span.js.map +1 -1
- package/dist/content/line-anchors.d.ts +3 -2
- package/dist/content/line-anchors.d.ts.map +1 -1
- package/dist/content/line-anchors.js +33 -19
- package/dist/content/line-anchors.js.map +1 -1
- package/dist/content/line-selection.js.map +1 -1
- package/dist/content/pattern-edits.js +1 -1
- package/dist/content/pattern-edits.js.map +1 -1
- package/dist/content/read-projector.d.ts +1 -1
- package/dist/content/read-projector.d.ts.map +1 -1
- package/dist/content/read-projector.js +10 -8
- package/dist/content/read-projector.js.map +1 -1
- package/dist/content/read-resolve.d.ts.map +1 -1
- package/dist/content/read-resolve.js +6 -5
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/AdmittedTurnExecutor.d.ts +4 -4
- package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
- package/dist/core/AdmittedTurnExecutor.js +20 -18
- package/dist/core/AdmittedTurnExecutor.js.map +1 -1
- package/dist/core/AmbientMembers.d.ts +1 -1
- package/dist/core/AmbientMembers.d.ts.map +1 -1
- package/dist/core/AmbientMembers.js +5 -2
- package/dist/core/AmbientMembers.js.map +1 -1
- package/dist/core/BareBatchRunner.d.ts +1 -1
- package/dist/core/BareBatchRunner.d.ts.map +1 -1
- package/dist/core/BareBatchRunner.js +2 -2
- package/dist/core/BareBatchRunner.js.map +1 -1
- package/dist/core/CapabilityPolicies.js.map +1 -1
- package/dist/core/CapabilityResolver.d.ts +1 -2
- package/dist/core/CapabilityResolver.d.ts.map +1 -1
- package/dist/core/CapabilityResolver.js +11 -7
- package/dist/core/CapabilityResolver.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +2 -2
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +27 -0
- package/dist/core/ClientInteractions.js.map +1 -1
- package/dist/core/ContextFit.js +1 -1
- package/dist/core/ContextFit.js.map +1 -1
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +4 -4
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/DataStatementRunner.d.ts.map +1 -1
- package/dist/core/DataStatementRunner.js +15 -15
- package/dist/core/DataStatementRunner.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +29 -5
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +210 -130
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/EditMutations.d.ts.map +1 -1
- package/dist/core/EditMutations.js +12 -6
- package/dist/core/EditMutations.js.map +1 -1
- package/dist/core/EditSequence.js.map +1 -1
- package/dist/core/Engine.d.ts +15 -7
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +32 -7
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/ExecutionOutputs.js.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +1 -2
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +2 -2
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/FileAddress.js.map +1 -1
- package/dist/core/FilePublication.d.ts.map +1 -1
- package/dist/core/FilePublication.js +6 -3
- package/dist/core/FilePublication.js.map +1 -1
- package/dist/core/FileSnapshot.d.ts.map +1 -1
- package/dist/core/FileSnapshot.js +8 -5
- package/dist/core/FileSnapshot.js.map +1 -1
- package/dist/core/KillHandler.d.ts.map +1 -1
- package/dist/core/KillHandler.js +7 -0
- package/dist/core/KillHandler.js.map +1 -1
- package/dist/core/LiveSubscription.d.ts +1 -0
- package/dist/core/LiveSubscription.d.ts.map +1 -1
- package/dist/core/LiveSubscription.js +3 -2
- package/dist/core/LiveSubscription.js.map +1 -1
- package/dist/core/LiveSubscriptions.d.ts +4 -1
- package/dist/core/LiveSubscriptions.d.ts.map +1 -1
- package/dist/core/LiveSubscriptions.js +42 -1
- package/dist/core/LiveSubscriptions.js.map +1 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +22 -19
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/LogVisibility.d.ts +1 -1
- package/dist/core/LogVisibility.d.ts.map +1 -1
- package/dist/core/LogVisibility.js +4 -6
- package/dist/core/LogVisibility.js.map +1 -1
- package/dist/core/LogWriter.d.ts.map +1 -1
- package/dist/core/LogWriter.js +6 -9
- package/dist/core/LogWriter.js.map +1 -1
- package/dist/core/LoopDriver.d.ts +4 -4
- package/dist/core/LoopDriver.d.ts.map +1 -1
- package/dist/core/LoopDriver.js +4 -5
- package/dist/core/LoopDriver.js.map +1 -1
- package/dist/core/LoopLifecycle.d.ts +2 -0
- package/dist/core/LoopLifecycle.d.ts.map +1 -1
- package/dist/core/LoopLifecycle.js +13 -3
- package/dist/core/LoopLifecycle.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +4 -4
- package/dist/core/LoopOutcome.d.ts.map +1 -1
- package/dist/core/LoopOutcome.js +2 -2
- package/dist/core/LoopOutcome.js.map +1 -1
- package/dist/core/MembershipMaterialization.d.ts.map +1 -1
- package/dist/core/MembershipMaterialization.js +29 -29
- package/dist/core/MembershipMaterialization.js.map +1 -1
- package/dist/core/MessageResources.d.ts +5 -1
- package/dist/core/MessageResources.d.ts.map +1 -1
- package/dist/core/MessageResources.js +12 -0
- package/dist/core/MessageResources.js.map +1 -1
- package/dist/core/MessageResources.sql +26 -0
- package/dist/core/MutationEffects.d.ts +1 -0
- package/dist/core/MutationEffects.d.ts.map +1 -1
- package/dist/core/MutationEffects.js +14 -10
- package/dist/core/MutationEffects.js.map +1 -1
- package/dist/core/OperationHistory.d.ts.map +1 -1
- package/dist/core/OperationHistory.js +4 -3
- package/dist/core/OperationHistory.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +3 -2
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +3 -7
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +40 -31
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/PacketBuilder.sql +11 -0
- package/dist/core/ProblemLog.d.ts +2 -5
- package/dist/core/ProblemLog.d.ts.map +1 -1
- package/dist/core/ProblemLog.js +2 -3
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +12 -11
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +92 -84
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ReasoningEvent.d.ts +2 -0
- package/dist/core/ReasoningEvent.d.ts.map +1 -1
- package/dist/core/ReplyMetadata.d.ts.map +1 -1
- package/dist/core/ReplyMetadata.js +4 -3
- package/dist/core/ReplyMetadata.js.map +1 -1
- package/dist/core/ResourceBindings.d.ts +2 -2
- package/dist/core/ResourceBindings.d.ts.map +1 -1
- package/dist/core/ResourceBindings.js.map +1 -1
- package/dist/core/ResourceMutations.d.ts +1 -1
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +1 -2
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/ResourceSelector.js +2 -2
- package/dist/core/ResourceSelector.js.map +1 -1
- package/dist/core/ResourceTransfers.d.ts.map +1 -1
- package/dist/core/ResourceTransfers.js +162 -120
- package/dist/core/ResourceTransfers.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +6 -5
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +32 -26
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ServiceHelp.d.ts.map +1 -1
- package/dist/core/ServiceHelp.js +2 -1
- package/dist/core/ServiceHelp.js.map +1 -1
- package/dist/core/StoredPacket.d.ts.map +1 -1
- package/dist/core/StoredPacket.js +26 -19
- package/dist/core/StoredPacket.js.map +1 -1
- package/dist/core/StrikeRail.d.ts +1 -2
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +4 -4
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TerminalResult.d.ts +0 -2
- package/dist/core/TerminalResult.d.ts.map +1 -1
- package/dist/core/TerminalResult.js +0 -18
- package/dist/core/TerminalResult.js.map +1 -1
- package/dist/core/TokenCalibration.d.ts +4 -2
- package/dist/core/TokenCalibration.d.ts.map +1 -1
- package/dist/core/TokenCalibration.js +30 -13
- package/dist/core/TokenCalibration.js.map +1 -1
- package/dist/core/TokenCalibration.sql +7 -17
- package/dist/core/ToolResources.d.ts +2 -1
- package/dist/core/ToolResources.d.ts.map +1 -1
- package/dist/core/ToolResources.js +10 -34
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/TurnDispositionHandler.d.ts +1 -1
- package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
- package/dist/core/TurnDispositionHandler.js +7 -3
- package/dist/core/TurnDispositionHandler.js.map +1 -1
- package/dist/core/TurnMaterialization.d.ts.map +1 -1
- package/dist/core/TurnMaterialization.js +3 -2
- package/dist/core/TurnMaterialization.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +13 -12
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +198 -230
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/TurnRunner.sql +3 -3
- package/dist/core/WorkerOwners.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbMessageCaps.d.ts +3 -3
- package/dist/core/caps/DbMessageCaps.d.ts.map +1 -1
- package/dist/core/caps/DbProjectionCaps.js +1 -1
- package/dist/core/caps/DbProjectionCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +1 -0
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +4 -1
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/env-catalog.js.map +1 -1
- package/dist/core/env-defaults.d.ts.map +1 -1
- package/dist/core/env-defaults.js +5 -3
- package/dist/core/env-defaults.js.map +1 -1
- package/dist/core/file-creation-policy.d.ts.map +1 -1
- package/dist/core/file-creation-policy.js +2 -1
- package/dist/core/file-creation-policy.js.map +1 -1
- package/dist/core/file-materialization.d.ts.map +1 -1
- package/dist/core/file-materialization.js +9 -11
- package/dist/core/file-materialization.js.map +1 -1
- package/dist/core/git-env.d.ts +1 -0
- package/dist/core/git-env.d.ts.map +1 -1
- package/dist/core/git-env.js +34 -3
- package/dist/core/git-env.js.map +1 -1
- package/dist/core/git-membership.d.ts +5 -0
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +55 -21
- 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 +1 -0
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/packet-wire.d.ts +26 -27
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +154 -132
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/results.d.ts +9 -4
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +14 -1
- package/dist/core/results.js.map +1 -1
- package/dist/core/runtime-floor.d.ts +8 -0
- package/dist/core/runtime-floor.d.ts.map +1 -0
- package/dist/core/runtime-floor.js +40 -0
- package/dist/core/runtime-floor.js.map +1 -0
- package/dist/core/runtime-guard.d.ts +2 -0
- package/dist/core/runtime-guard.d.ts.map +1 -0
- package/dist/core/runtime-guard.js +5 -0
- package/dist/core/runtime-guard.js.map +1 -0
- package/dist/core/sqlite-synchronous.d.ts +1 -1
- package/dist/core/sqlite-synchronous.d.ts.map +1 -1
- package/dist/core/sqlite-synchronous.js.map +1 -1
- package/dist/core/statement-primary.d.ts +2 -2
- package/dist/core/statement-primary.d.ts.map +1 -1
- package/dist/core/statement-primary.js.map +1 -1
- package/dist/core/workspace-settings.js +1 -1
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/evidence/EvidenceReader.d.ts.map +1 -1
- package/dist/evidence/EvidenceReader.js +20 -6
- package/dist/evidence/EvidenceReader.js.map +1 -1
- package/dist/launch/Launch.d.ts +1 -0
- package/dist/launch/Launch.d.ts.map +1 -1
- package/dist/launch/Launch.js +28 -12
- package/dist/launch/Launch.js.map +1 -1
- package/dist/man/plurnk-service.1 +6 -2
- package/dist/schemes/EffectPolicy.js +1 -1
- package/dist/schemes/EffectPolicy.js.map +1 -1
- package/dist/schemes/Env.d.ts +1 -1
- package/dist/schemes/Env.d.ts.map +1 -1
- package/dist/schemes/Env.js +7 -1
- package/dist/schemes/Env.js.map +1 -1
- package/dist/schemes/Exec.d.ts +10 -12
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +337 -437
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts +4 -6
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +17 -17
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/ExecScheduler.d.ts.map +1 -1
- package/dist/schemes/ExecScheduler.js +1 -2
- package/dist/schemes/ExecScheduler.js.map +1 -1
- package/dist/schemes/ExecutionInput.js +3 -3
- package/dist/schemes/ExecutionInput.js.map +1 -1
- package/dist/schemes/File.d.ts +3 -18
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +136 -79
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +193 -161
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/QuestionTool.d.ts.map +1 -1
- package/dist/schemes/QuestionTool.js +6 -8
- package/dist/schemes/QuestionTool.js.map +1 -1
- package/dist/schemes/ResourceTreeScheme.d.ts.map +1 -1
- package/dist/schemes/ResourceTreeScheme.js +2 -2
- package/dist/schemes/ResourceTreeScheme.js.map +1 -1
- package/dist/schemes/TurnSource.js +3 -3
- package/dist/schemes/TurnSource.js.map +1 -1
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +4 -2
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +187 -139
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-manifest.js +1 -1
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/schemes/exec-entry-sink.d.ts +30 -0
- package/dist/schemes/exec-entry-sink.d.ts.map +1 -0
- package/dist/schemes/exec-entry-sink.js +238 -0
- package/dist/schemes/exec-entry-sink.js.map +1 -0
- package/dist/schemes/exec-lifetime.d.ts.map +1 -1
- package/dist/schemes/exec-lifetime.js +5 -2
- package/dist/schemes/exec-lifetime.js.map +1 -1
- package/dist/server/ClientOperations.d.ts +45 -0
- package/dist/server/ClientOperations.d.ts.map +1 -0
- package/dist/server/ClientOperations.js +192 -0
- package/dist/server/ClientOperations.js.map +1 -0
- package/dist/server/ClientReads.d.ts +13 -5
- package/dist/server/ClientReads.d.ts.map +1 -1
- package/dist/server/ClientReads.js +108 -21
- package/dist/server/ClientReads.js.map +1 -1
- package/dist/server/Credentials.js +2 -2
- package/dist/server/Credentials.js.map +1 -1
- package/dist/server/Daemon.d.ts +40 -28
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +255 -839
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonLock.d.ts +2 -0
- package/dist/server/DaemonLock.d.ts.map +1 -1
- package/dist/server/DaemonLock.js +10 -1
- package/dist/server/DaemonLock.js.map +1 -1
- package/dist/server/DaemonModules.d.ts +39 -0
- package/dist/server/DaemonModules.d.ts.map +1 -0
- package/dist/server/DaemonModules.js +219 -0
- package/dist/server/DaemonModules.js.map +1 -0
- package/dist/server/DrainSupervisor.d.ts +4 -2
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +318 -290
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/EnvFunctionality.d.ts +10 -7
- package/dist/server/EnvFunctionality.d.ts.map +1 -1
- package/dist/server/EnvFunctionality.js +6 -5
- package/dist/server/EnvFunctionality.js.map +1 -1
- package/dist/server/Functionality.d.ts +8 -6
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +110 -32
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.d.ts.map +1 -1
- package/dist/server/FunctionalityManager.js +16 -2
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/HttpListener.d.ts.map +1 -1
- package/dist/server/HttpListener.js +28 -8
- package/dist/server/HttpListener.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts +19 -7
- package/dist/server/MembersFunctionality.d.ts.map +1 -1
- package/dist/server/MembersFunctionality.js +30 -16
- package/dist/server/MembersFunctionality.js.map +1 -1
- package/dist/server/MessageDelivery.d.ts +43 -0
- package/dist/server/MessageDelivery.d.ts.map +1 -0
- package/dist/server/MessageDelivery.js +186 -0
- package/dist/server/MessageDelivery.js.map +1 -0
- package/dist/server/ModuleHost.d.ts +3 -3
- package/dist/server/ModuleHost.d.ts.map +1 -1
- package/dist/server/Places.d.ts +19 -0
- package/dist/server/Places.d.ts.map +1 -0
- package/dist/server/Places.js +76 -0
- package/dist/server/Places.js.map +1 -0
- package/dist/server/PluginSources.d.ts.map +1 -1
- package/dist/server/PluginSources.js +2 -1
- package/dist/server/PluginSources.js.map +1 -1
- package/dist/server/PlurnkSkill.d.ts.map +1 -1
- package/dist/server/PlurnkSkill.js +5 -4
- package/dist/server/PlurnkSkill.js.map +1 -1
- package/dist/server/Rollback.d.ts.map +1 -1
- package/dist/server/Rollback.js +2 -1
- package/dist/server/Rollback.js.map +1 -1
- package/dist/server/WorkerModels.d.ts +87 -0
- package/dist/server/WorkerModels.d.ts.map +1 -0
- package/dist/server/WorkerModels.js +540 -0
- package/dist/server/WorkerModels.js.map +1 -0
- package/dist/server/WorkerOverview.d.ts +22 -0
- package/dist/server/WorkerOverview.d.ts.map +1 -0
- package/dist/server/WorkerOverview.js +42 -0
- package/dist/server/WorkerOverview.js.map +1 -0
- package/dist/server/WorkspacePlugins.d.ts +1 -1
- package/dist/server/WorkspacePlugins.d.ts.map +1 -1
- package/dist/server/WorkspacePlugins.js +0 -1
- package/dist/server/WorkspacePlugins.js.map +1 -1
- package/dist/server/WorkspaceResidency.d.ts +3 -3
- package/dist/server/WorkspaceResidency.d.ts.map +1 -1
- package/dist/server/WorkspaceResidency.js.map +1 -1
- package/dist/server/client-input.d.ts +2 -2
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +19 -12
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/envelope.d.ts +4 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +7 -3
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +10 -6
- package/dist/server/logEntry.d.ts +38 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +9 -9
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/logEntry.sql +4 -2
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +11 -2
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/module-discovery.d.ts +2 -3
- package/dist/server/module-discovery.d.ts.map +1 -1
- package/dist/server/module-discovery.js +6 -6
- package/dist/server/module-discovery.js.map +1 -1
- package/dist/server/places.sql +83 -0
- package/dist/server/seam-loop.sql +8 -0
- package/dist/server/worker-overview.sql +57 -0
- package/dist/service.d.ts +1 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +35 -9
- package/dist/service.js.map +1 -1
- package/docs/members.md +11 -21
- package/migrations/033_hidden_places.sql +10 -0
- package/package.json +52 -61
- package/dist/core/ToolInputSchema.d.ts +0 -7
- package/dist/core/ToolInputSchema.d.ts.map +0 -1
- package/dist/core/ToolInputSchema.js +0 -65
- package/dist/core/ToolInputSchema.js.map +0 -1
- package/dist/observe/init.d.ts +0 -8
- package/dist/observe/init.d.ts.map +0 -1
- package/dist/observe/init.js +0 -163
- package/dist/observe/init.js.map +0 -1
- package/dist/server/WorkerModelResolver.d.ts +0 -20
- package/dist/server/WorkerModelResolver.d.ts.map +0 -1
- package/dist/server/WorkerModelResolver.js +0 -200
- package/dist/server/WorkerModelResolver.js.map +0 -1
package/dist/schemes/Exec.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { parsePath } from "@plurnk/plurnk-parser";
|
|
2
|
-
import { PathSyntax } from "@plurnk/plurnk-contracts";
|
|
3
|
-
import {
|
|
2
|
+
import { errnoCode, isExecutionOp, PathSyntax } from "@plurnk/plurnk-contracts";
|
|
3
|
+
import {} from "@plurnk/plurnk-contracts";
|
|
4
|
+
import { InvalidOperationResultError, MetadataOptions, } from "@plurnk/plurnk-schemes";
|
|
4
5
|
import FunctionalityManager from "../server/FunctionalityManager.js";
|
|
5
6
|
import EffectPolicy from "./EffectPolicy.js";
|
|
6
7
|
import EntryCrud from "./_entry-crud.js";
|
|
7
8
|
import EntryFind from "./_entry-find.js";
|
|
8
|
-
import { MetadataOptions } from "@plurnk/plurnk-schemes";
|
|
9
9
|
import ChannelWrite, {} from "../core/ChannelWrite.js";
|
|
10
10
|
import LiveSubscription from "../core/LiveSubscription.js";
|
|
11
11
|
import EnvFunctionality, {} from "../server/EnvFunctionality.js";
|
|
@@ -17,22 +17,13 @@ import { extname, isAbsolute, resolve } from "node:path";
|
|
|
17
17
|
import { CoreSchemeAdapterBase } from "../core/CoreSchemeServices.js";
|
|
18
18
|
import ErrorDetail from "../core/ErrorDetail.js";
|
|
19
19
|
import Results, { OperationFailureError } from "../core/results.js";
|
|
20
|
-
import { InvalidOperationResultError, NetworkAddress, ResourceNames, } from "@plurnk/plurnk-schemes";
|
|
21
|
-
import DbProjectionCaps from "../core/caps/DbProjectionCaps.js";
|
|
22
|
-
import WorkerControlAddress from "../core/WorkerControlAddress.js";
|
|
23
|
-
import Turn from "../core/Turn.js";
|
|
24
|
-
import AdministrativeLoop from "../core/AdministrativeLoop.js";
|
|
25
|
-
import RuntimeWorker from "../core/RuntimeWorker.js";
|
|
26
|
-
import LoopLifecycle from "../core/LoopLifecycle.js";
|
|
27
|
-
import LogEntryProjection from "../core/LogEntryProjection.js";
|
|
28
|
-
import LogBody from "../core/LogBody.js";
|
|
29
20
|
import ToolResources from "../core/ToolResources.js";
|
|
30
21
|
import { setTimeout as delay } from "node:timers/promises";
|
|
31
22
|
import ExecScheduler, { readExecConcurrency } from "./ExecScheduler.js";
|
|
32
23
|
import ExecScratch from "./ExecScratch.js";
|
|
33
24
|
import ExecutionInput from "./ExecutionInput.js";
|
|
34
25
|
import { execRouteOf } from "./exec-runtime.js";
|
|
35
|
-
import {} from "
|
|
26
|
+
import ExecEntrySink, {} from "./exec-entry-sink.js";
|
|
36
27
|
// Executors are discovered + probed at boot into ExecutorRegistry and reach
|
|
37
28
|
// the scheme through ctx.executors ({§exec-registry-resolves}). Each runtime tag
|
|
38
29
|
// resolves to its sibling executor; the scheme itself stays runtime-agnostic.
|
|
@@ -216,22 +207,90 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
216
207
|
// applyResolution spawns the subprocess; output streams into the
|
|
217
208
|
// coordinate-stamped <runtime>:///<pathname> entry's stdout/stderr channels
|
|
218
209
|
// (e.g. sh:///1/1/2/sh, {§exec}). The model READs that entry on a subsequent turn.
|
|
210
|
+
//
|
|
211
|
+
// Admission runs in the order its refusals are owed — the executor, its tool, the fence's shape,
|
|
212
|
+
// the target's routing, the service's reserved metadata, the executor's preparation, a script
|
|
213
|
+
// target's existence — and each step returns either the refusal or what the next step needs.
|
|
219
214
|
async exec(statement, ctx) {
|
|
220
215
|
const core = this.coreContext(ctx);
|
|
221
216
|
const body = statement.body ?? "";
|
|
222
|
-
|
|
217
|
+
const { executors } = core;
|
|
218
|
+
if (executors === undefined)
|
|
223
219
|
throw new Error("exec dispatched without an executor registry");
|
|
224
220
|
// {§exec-executor-slot}: the fence names the executor; the path is its program.
|
|
225
221
|
const route = execRouteOf(statement);
|
|
226
222
|
const runtime = route.runtime;
|
|
223
|
+
const resolved = Exec.#admitRuntime(executors, core.workspaceId, runtime);
|
|
224
|
+
if ("status" in resolved)
|
|
225
|
+
return resolved;
|
|
226
|
+
this.#prepareExecution();
|
|
227
|
+
const invocation = Exec.#admitTool(executors, core.workspaceId, resolved, runtime, route.target === null ? null : route.target.raw);
|
|
228
|
+
if ("status" in invocation)
|
|
229
|
+
return invocation;
|
|
230
|
+
const hasBody = body.length > 0;
|
|
231
|
+
const execTarget = route.target;
|
|
232
|
+
const hasTarget = execTarget !== null;
|
|
233
|
+
const shape = Exec.#checkInvocationShape(invocation, runtime, execTarget?.raw, hasBody, hasTarget);
|
|
234
|
+
if (shape !== null)
|
|
235
|
+
return shape;
|
|
236
|
+
const routed = await this.#routeTarget(core, { runtime, invocation, execTarget, hasBody });
|
|
237
|
+
if ("status" in routed)
|
|
238
|
+
return routed;
|
|
239
|
+
const { defaultCwd, resourceSource } = routed;
|
|
240
|
+
let { target } = routed;
|
|
241
|
+
let cwd = defaultCwd;
|
|
242
|
+
let inputDeclared = false;
|
|
243
|
+
const lifetime = Exec.#readReservedMetadata(statement, runtime);
|
|
244
|
+
if ("status" in lifetime)
|
|
245
|
+
return lifetime;
|
|
246
|
+
// {§executor-metadata} Options belong to the invoked executor for every source kind.
|
|
247
|
+
const prepared = await Exec.#prepareInvocation(resolved.executor, statement, { runtime, body, target: resourceSource ?? target, cwd });
|
|
248
|
+
if ("status" in prepared)
|
|
249
|
+
return prepared;
|
|
250
|
+
({ cwd, inputDeclared } = prepared);
|
|
251
|
+
// A `script` target must be an existing file; other kinds pass through as declared.
|
|
252
|
+
if (target !== null && invocation.target?.kind === "script") {
|
|
253
|
+
const inspected = await Exec.#inspectScriptTarget(core, { runtime, target, cwd, defaultCwd, hasBody });
|
|
254
|
+
if ("status" in inspected)
|
|
255
|
+
return inspected;
|
|
256
|
+
({ target, cwd } = inspected);
|
|
257
|
+
}
|
|
258
|
+
if (!hasBody && target === null && resourceSource === null) {
|
|
259
|
+
return Exec.#refuse(runtime, "input-required", `Executable tool '${runtime}' has no executable body or realized target.`, `Provide a ${invocation.body.role} body or a non-directory target.`);
|
|
260
|
+
}
|
|
261
|
+
// One logical target derives one effect fact before policy consumes it.
|
|
262
|
+
// A non-file resource keeps its exact authored address until materialization.
|
|
263
|
+
const effectTarget = resourceSource ?? target;
|
|
264
|
+
const effect = resolved.executor.effect(effectTarget);
|
|
265
|
+
// cwd is the workspace project_root unless target routing selected a
|
|
266
|
+
// directory override. {§exec-target-routing}, {§executor-sinks}
|
|
267
|
+
// The log writer claims the workspace output address before proposal application.
|
|
268
|
+
// {§exec-lifetime} — how long the spawn may live is the metadata's one field; the scope slot
|
|
269
|
+
// is text coordinates, which an execution has none of.
|
|
270
|
+
const attrs = {
|
|
271
|
+
runtime, cwd, body, target, pathname: "", effect,
|
|
272
|
+
...(inputDeclared ? { input: "open" } : {}),
|
|
273
|
+
...(resourceSource !== null ? { resourceSource } : {}),
|
|
274
|
+
...lifetime,
|
|
275
|
+
};
|
|
276
|
+
const previewInput = body !== "" ? body : execTarget?.raw ?? "";
|
|
277
|
+
const preview = runtime !== "" ? `[${runtime}] ${previewInput}` : `$ ${previewInput}`;
|
|
278
|
+
return { status: 202, body: preview, attrs }; // host runtime proposes with 202 — {§exec-host-proposes}
|
|
279
|
+
}
|
|
280
|
+
// A refusal of the fence as written: 400, the runtime named, and the working form when there is one.
|
|
281
|
+
static #refuse(runtime, code, detail, recovery, extensions = {}) {
|
|
282
|
+
return Results.failure("scheme:exec", code, 400, detail, {}, { runtime, ...(recovery === undefined ? {} : { recovery }), retryable: false, ...extensions });
|
|
283
|
+
}
|
|
284
|
+
// The executor the fence names, or the refusal: unregistered, unavailable, or unconfigured.
|
|
285
|
+
static #admitRuntime(executors, workspaceId, runtime) {
|
|
227
286
|
// {§exec-registry-resolves} — a non-empty tag selects exactly one registered executable
|
|
228
287
|
// tool. Unknown tags are not reinterpreted as shell command words: that would make the
|
|
229
288
|
// executed command differ from the authored body.
|
|
230
|
-
const resolved =
|
|
289
|
+
const resolved = executors.entry(runtime, workspaceId);
|
|
231
290
|
if (resolved === undefined) {
|
|
232
291
|
return Results.failure("scheme:exec", "executor-not-registered", 400, `No executor or MCP service named \`${runtime}\` is registered for this worker.`, {}, {
|
|
233
292
|
requestedRuntime: runtime,
|
|
234
|
-
executors:
|
|
293
|
+
executors: executors.availableRuntimes(workspaceId),
|
|
235
294
|
retryable: false,
|
|
236
295
|
});
|
|
237
296
|
}
|
|
@@ -244,50 +303,60 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
244
303
|
retryable: false,
|
|
245
304
|
});
|
|
246
305
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
306
|
+
return resolved;
|
|
307
|
+
}
|
|
308
|
+
// The invocation contract the fence runs under: a family runtime's registered tool, named exactly, or
|
|
309
|
+
// the executor's own declaration.
|
|
310
|
+
static #admitTool(executors, workspaceId, resolved, runtime, exactTarget) {
|
|
311
|
+
const registry = executors.toolRegistry(runtime, workspaceId);
|
|
250
312
|
const registeredTool = registry?.tools.find((tool) => tool.target === exactTarget);
|
|
251
313
|
if (registry !== null && registeredTool === undefined) {
|
|
252
314
|
const availableTargets = registry.tools.map((tool) => tool.target);
|
|
315
|
+
// {§executor-blank-invocation} No target: every enabled target's fence. An unknown one: the document listing them.
|
|
316
|
+
const recovery = availableTargets.length === 0 ? {}
|
|
317
|
+
: exactTarget === null ? { recovery: registry.tools.map((tool) => ToolResources.fence(runtime, tool.invocation, tool.target)).join("\n\n") }
|
|
318
|
+
: { recovery: `Select a target from worker://${ToolResources.documentPath(runtime, resolved.resourcesPath)}.` };
|
|
253
319
|
return Results.failure("scheme:exec", exactTarget === null ? "target-required" : "target-not-registered", exactTarget === null ? 400 : 404, exactTarget === null
|
|
254
320
|
? `Runtime '${runtime}' requires one registered tool target.`
|
|
255
321
|
: `Runtime '${runtime}' has no registered target '${exactTarget}'.`, {}, {
|
|
256
322
|
runtime,
|
|
257
323
|
availableTargetCount: availableTargets.length,
|
|
258
|
-
...
|
|
259
|
-
recovery: `Select a target from worker://${ToolResources.documentPath(runtime, resolved.resourcesPath)}.`,
|
|
260
|
-
}),
|
|
324
|
+
...recovery,
|
|
261
325
|
retryable: false,
|
|
262
326
|
});
|
|
263
327
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
328
|
+
return registeredTool?.invocation ?? resolved.invocation;
|
|
329
|
+
}
|
|
330
|
+
// The fence's body and target against the invocation's declaration, refused by name. {§executor-blank-invocation} An
|
|
331
|
+
// absent input is answered with the invocation's own fence.
|
|
332
|
+
static #checkInvocationShape(invocation, runtime, exactTarget, hasBody, hasTarget) {
|
|
333
|
+
const fence = () => ToolResources.fence(runtime, invocation, exactTarget);
|
|
269
334
|
if (invocation.body.required && !hasBody) {
|
|
270
|
-
return refuse("body-required", `Executable tool '${runtime}' requires a ${invocation.body.role} body.`,
|
|
335
|
+
return Exec.#refuse(runtime, "body-required", `Executable tool '${runtime}' requires a ${invocation.body.role} body.`, fence());
|
|
271
336
|
}
|
|
272
337
|
if (invocation.target === undefined && hasTarget) {
|
|
273
|
-
return refuse("target-not-supported", `Executable tool '${runtime}' does not accept a target.`, `Remove the target and provide the ${invocation.body.role} in the body.`);
|
|
338
|
+
return Exec.#refuse(runtime, "target-not-supported", `Executable tool '${runtime}' does not accept a target.`, `Remove the target and provide the ${invocation.body.role} in the body.`);
|
|
274
339
|
}
|
|
275
340
|
if (invocation.target?.required === true && !hasTarget) {
|
|
276
|
-
return refuse("target-required", `Executable tool '${runtime}' requires a ${invocation.target.role} target.`,
|
|
341
|
+
return Exec.#refuse(runtime, "target-required", `Executable tool '${runtime}' requires a ${invocation.target.role} target.`, fence());
|
|
277
342
|
}
|
|
278
343
|
if (!hasBody && !hasTarget) {
|
|
279
|
-
return refuse("input-required", `Executable tool '${runtime}' requires a body or target.`,
|
|
344
|
+
return Exec.#refuse(runtime, "input-required", `Executable tool '${runtime}' requires a body or target.`, fence());
|
|
280
345
|
}
|
|
281
346
|
if (invocation.exclusive === true && hasBody && hasTarget) {
|
|
282
|
-
return refuse("input-conflict", `Executable tool '${runtime}' accepts its ${invocation.body.role} body and ${invocation.target?.role ?? "target"} target only as alternatives.`, "Provide either the body or target, not both.");
|
|
347
|
+
return Exec.#refuse(runtime, "input-conflict", `Executable tool '${runtime}' accepts its ${invocation.body.role} body and ${invocation.target?.role ?? "target"} target only as alternatives.`, "Provide either the body or target, not both.");
|
|
283
348
|
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
// {§exec-target-routing} The working directory the run can always name, and the target as the executor
|
|
352
|
+
// will receive it: a literal, a local path, or a non-file resource realized at apply time.
|
|
353
|
+
async #routeTarget(core, args) {
|
|
354
|
+
const { runtime, invocation, execTarget, hasBody } = args;
|
|
284
355
|
const workspaceRow = await core.db.envelope_get_workspace.get({ id: core.workspaceId });
|
|
285
356
|
const projectRoot = workspaceRow?.project_root ?? null;
|
|
286
357
|
// The working directory is always concrete — the project root, or the shell's own
|
|
287
358
|
// cwd when the workspace has none — so every receipt can name it ({§exec-target-routing}).
|
|
288
359
|
const defaultCwd = projectRoot ?? process.cwd();
|
|
289
|
-
let cwd = defaultCwd;
|
|
290
|
-
let inputDeclared = false;
|
|
291
360
|
let target = null;
|
|
292
361
|
let resourceSource = null;
|
|
293
362
|
const targetDecl = invocation.target;
|
|
@@ -307,7 +376,7 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
307
376
|
}
|
|
308
377
|
}
|
|
309
378
|
else {
|
|
310
|
-
return refuse("target-kind-invalid", `Executable tool '${runtime}' accepts only a local or file:// ${targetDecl.role} target.`, `Use a local or file:// ${targetDecl.role} target.`, { target: execTarget.raw, targetKind: targetDecl.kind });
|
|
379
|
+
return Exec.#refuse(runtime, "target-kind-invalid", `Executable tool '${runtime}' accepts only a local or file:// ${targetDecl.role} target.`, `Use a local or file:// ${targetDecl.role} target.`, { target: execTarget.raw, targetKind: targetDecl.kind });
|
|
311
380
|
}
|
|
312
381
|
}
|
|
313
382
|
}
|
|
@@ -323,7 +392,7 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
323
392
|
// cannot see the host's temporary directory, and runs markdown where it can.
|
|
324
393
|
if (resourceSource !== null && execTarget?.kind === "url" && execTarget.scheme === "worker"
|
|
325
394
|
&& isGeneratedPathname(PathSyntax.decodeParens(execTarget.pathname ?? ""))) {
|
|
326
|
-
return refuse("target-is-documentation", `\`${execTarget.raw}\` is reference documentation the harness generated, not a program; ${runtime} cannot run it.`, undefined, { target: execTarget.raw });
|
|
395
|
+
return Exec.#refuse(runtime, "target-is-documentation", `\`${execTarget.raw}\` is reference documentation the harness generated, not a program; ${runtime} cannot run it.`, undefined, { target: execTarget.raw });
|
|
327
396
|
}
|
|
328
397
|
// {§exec-scratch-directory} — a standalone source is realized under the scratch directory,
|
|
329
398
|
// where its executor must be able to read it; a directory the daemon cannot create or
|
|
@@ -332,12 +401,16 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
332
401
|
const scratch = this.#sourceScratch();
|
|
333
402
|
const unavailable = await scratch.unavailable();
|
|
334
403
|
if (unavailable !== null) {
|
|
335
|
-
return refuse("scratch-unavailable", `The execution scratch directory \`${scratch.directory}\` named by ${ExecScratch.KNOB} cannot be created or written (${unavailable}), so the ${runtime} source \`${execTarget.raw}\` cannot be realized.`, `The operator points ${ExecScratch.KNOB} at a writable absolute directory the ${runtime} executor can also read; meanwhile, target a program file the executor can reach, or put the command beneath a ${runtime} heading with no target.`, { target: execTarget.raw, scratch: scratch.directory, configuration: ExecScratch.KNOB });
|
|
404
|
+
return Exec.#refuse(runtime, "scratch-unavailable", `The execution scratch directory \`${scratch.directory}\` named by ${ExecScratch.KNOB} cannot be created or written (${unavailable}), so the ${runtime} source \`${execTarget.raw}\` cannot be realized.`, `The operator points ${ExecScratch.KNOB} at a writable absolute directory the ${runtime} executor can also read; meanwhile, target a program file the executor can reach, or put the command beneath a ${runtime} heading with no target.`, { target: execTarget.raw, scratch: scratch.directory, configuration: ExecScratch.KNOB });
|
|
336
405
|
}
|
|
337
406
|
}
|
|
407
|
+
return { defaultCwd, target, resourceSource };
|
|
408
|
+
}
|
|
409
|
+
// {§exec-lifetime} {§env-option} The service's reserved keys, read before the executor sees its options.
|
|
410
|
+
static #readReservedMetadata(statement, runtime) {
|
|
338
411
|
// {§exec-lifetime} — the scope slot is text coordinates, and an execution has none.
|
|
339
412
|
if (statement.lineMarker !== null)
|
|
340
|
-
return refuse("scope-unsupported", "An execution takes no scope.", undefined);
|
|
413
|
+
return Exec.#refuse(runtime, "scope-unsupported", "An execution takes no scope.", undefined);
|
|
341
414
|
// {§env-option} — the fence's own environment is the service's key: refused here by name,
|
|
342
415
|
// so the model learns why, never dropped at the spawn. The executor never sees it.
|
|
343
416
|
try {
|
|
@@ -359,84 +432,70 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
359
432
|
runtime, recovery: `State one lifetime: ${LIFETIME_SYNTAX}.`, retryable: false,
|
|
360
433
|
});
|
|
361
434
|
}
|
|
362
|
-
|
|
363
|
-
|
|
435
|
+
return lifetime;
|
|
436
|
+
}
|
|
437
|
+
// {§executor-metadata} The executor's preparation of its own options: the working directory it chose
|
|
438
|
+
// and whether the invocation registers an input receiver; metadata without a preparer is refused.
|
|
439
|
+
static async #prepareInvocation(executor, statement, args) {
|
|
440
|
+
const { runtime, body, target, cwd } = args;
|
|
364
441
|
if (executor.prepare !== undefined) {
|
|
365
|
-
const prepared = Results.assert(await executor.prepare({
|
|
366
|
-
runtime, body, target: resourceSource ?? target, cwd, metadata: statement.metadata,
|
|
367
|
-
}));
|
|
442
|
+
const prepared = Results.assert(await executor.prepare({ runtime, body, target, cwd, metadata: statement.metadata }));
|
|
368
443
|
if (prepared.status >= 400)
|
|
369
444
|
return prepared;
|
|
370
445
|
if (prepared.status !== 200 || (prepared.cwd !== null && (typeof prepared.cwd !== "string" || !isAbsolute(prepared.cwd)))) {
|
|
371
446
|
throw new InvalidOperationResultError(`Executable tool '${runtime}' returned invalid invocation preparation.`);
|
|
372
447
|
}
|
|
373
|
-
cwd
|
|
374
|
-
inputDeclared = prepared.input === "open";
|
|
448
|
+
return { cwd: prepared.cwd, inputDeclared: prepared.input === "open" };
|
|
375
449
|
}
|
|
376
|
-
|
|
450
|
+
if ((statement.metadata?.length ?? 0) > 0) {
|
|
377
451
|
return Results.failure("scheme:exec", "metadata-unsupported", 400, `Executable tool '${runtime}' does not accept metadata.`, {}, { runtime, retryable: false });
|
|
378
452
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
target = null;
|
|
397
|
-
}
|
|
398
|
-
else if (kind === "directory") {
|
|
399
|
-
return refuse("target-not-a-program", `${runtime} target '${target}' is a directory, not a program.`, undefined, { target });
|
|
400
|
-
}
|
|
401
|
-
if (kind === "missing") {
|
|
402
|
-
// The target is not a script here, but it may be a registered tool of another executor or an
|
|
403
|
-
// executor itself: that is what exists, said as found (#388, {§diagnostic-observation}).
|
|
404
|
-
const executors = core.executors;
|
|
405
|
-
const ownerRuntimes = executors === undefined
|
|
406
|
-
? []
|
|
407
|
-
: executors.availableRuntimes(core.workspaceId)
|
|
408
|
-
.filter((tag) => executors.toolRegistry(tag, core.workspaceId)?.tools.some((tool) => tool.target === target) === true);
|
|
409
|
-
const runtimeTarget = executors !== undefined && target !== null && target !== runtime
|
|
410
|
-
&& executors.availableRuntimes(core.workspaceId).includes(target);
|
|
411
|
-
const recovery = ownerRuntimes.length > 0
|
|
412
|
-
? `The tool \`${target}\` is registered under executor \`${ownerRuntimes[0]}\`.`
|
|
413
|
-
: runtimeTarget
|
|
414
|
-
? `\`${target}\` is its own executor.`
|
|
415
|
-
: "The target must name an existing program resource.";
|
|
416
|
-
return refuse("target-not-found", `The ${runtime} program does not resolve as a script or a registered tool for this executor.`, recovery, { target, ...(ownerRuntimes.length === 0 ? {} : { toolRuntimes: ownerRuntimes }) });
|
|
453
|
+
return { cwd, inputDeclared: false };
|
|
454
|
+
}
|
|
455
|
+
// A script target as it exists: a file runs, a directory with a body is where the body runs
|
|
456
|
+
// ({§exec-target-near-miss}), and a missing one is refused as what it is ({§diagnostic-observation}).
|
|
457
|
+
static async #inspectScriptTarget(core, args) {
|
|
458
|
+
const { runtime, defaultCwd, hasBody } = args;
|
|
459
|
+
let { cwd } = args;
|
|
460
|
+
let target = args.target;
|
|
461
|
+
const inspected = isAbsolute(target) ? target : resolve(cwd ?? process.cwd(), target);
|
|
462
|
+
let kind = "missing";
|
|
463
|
+
try {
|
|
464
|
+
kind = (await stat(inspected)).isDirectory() ? "directory" : "file";
|
|
465
|
+
}
|
|
466
|
+
catch (cause) {
|
|
467
|
+
if (errnoCode(cause) !== "ENOENT") {
|
|
468
|
+
console.error(`Execution target classification failed for '${inspected}':`, cause);
|
|
469
|
+
return Results.failure("scheme:exec", "target-classification-failed", 500, `${runtime} target '${target}' could not be inspected: ${ErrorDetail.preview(cause)}`, {}, { target, stage: "target-classification" });
|
|
417
470
|
}
|
|
418
471
|
}
|
|
419
|
-
|
|
420
|
-
|
|
472
|
+
// {§exec-target-near-miss} — a directory is never a program: with a body and no explicit
|
|
473
|
+
// cwd, it is where the body runs. Never taught.
|
|
474
|
+
if (kind === "directory" && hasBody && cwd === defaultCwd) {
|
|
475
|
+
cwd = inspected;
|
|
476
|
+
target = null;
|
|
421
477
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
478
|
+
else if (kind === "directory") {
|
|
479
|
+
return Exec.#refuse(runtime, "target-not-a-program", `${runtime} target '${target}' is a directory, not a program.`, undefined, { target });
|
|
480
|
+
}
|
|
481
|
+
if (kind === "missing") {
|
|
482
|
+
// The target is not a script here, but it may be a registered tool of another executor or an
|
|
483
|
+
// executor itself: that is what exists, said as found (#388, {§diagnostic-observation}).
|
|
484
|
+
const executors = core.executors;
|
|
485
|
+
const ownerRuntimes = executors === undefined
|
|
486
|
+
? []
|
|
487
|
+
: executors.availableRuntimes(core.workspaceId)
|
|
488
|
+
.filter((tag) => executors.toolRegistry(tag, core.workspaceId)?.tools.some((tool) => tool.target === target) === true);
|
|
489
|
+
const runtimeTarget = executors !== undefined && target !== null && target !== runtime
|
|
490
|
+
&& executors.availableRuntimes(core.workspaceId).includes(target);
|
|
491
|
+
const recovery = ownerRuntimes.length > 0
|
|
492
|
+
? `The tool \`${target}\` is registered under executor \`${ownerRuntimes[0]}\`.`
|
|
493
|
+
: runtimeTarget
|
|
494
|
+
? `\`${target}\` is its own executor.`
|
|
495
|
+
: "The target must name an existing program resource.";
|
|
496
|
+
return Exec.#refuse(runtime, "target-not-found", `The ${runtime} program does not resolve as a script or a registered tool for this executor.`, recovery, { target, ...(ownerRuntimes.length === 0 ? {} : { toolRuntimes: ownerRuntimes }) });
|
|
497
|
+
}
|
|
498
|
+
return { target, cwd };
|
|
440
499
|
}
|
|
441
500
|
async #inputRecipient(statement, ctx, runtime) {
|
|
442
501
|
const core = this.coreContext(ctx);
|
|
@@ -521,8 +580,11 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
521
580
|
if (!EffectPolicy.isEffect(effect)) {
|
|
522
581
|
throw new InvalidOperationResultError("The accepted execution proposal is missing its canonical effect fact.");
|
|
523
582
|
}
|
|
583
|
+
if (!isExecutionOp(runtime)) {
|
|
584
|
+
throw new InvalidOperationResultError(`The accepted execution proposal names runtime '${runtime}', which is not a runtime tag.`);
|
|
585
|
+
}
|
|
524
586
|
const invocation = {
|
|
525
|
-
runtime
|
|
587
|
+
runtime, aside: null, metadata: null,
|
|
526
588
|
target: attrs.resourceSource ? parsePath(attrs.resourceSource) : target === null ? null : parsePath(target),
|
|
527
589
|
lineMarker: null, body, position: { line: 1, column: 1 },
|
|
528
590
|
};
|
|
@@ -530,54 +592,112 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
530
592
|
// files retain their environment; only standalone sources need a copy.
|
|
531
593
|
let tempPath = null;
|
|
532
594
|
if (attrs.resourceSource !== undefined) {
|
|
533
|
-
const
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
595
|
+
const realized = await this.#realizeSource(attrs.resourceSource, runtime, core);
|
|
596
|
+
if ("status" in realized)
|
|
597
|
+
return realized;
|
|
598
|
+
({ target, tempPath } = realized);
|
|
599
|
+
}
|
|
600
|
+
if (body.length === 0 && target === null) {
|
|
601
|
+
throw new InvalidOperationResultError("The accepted execution proposal has neither a body nor a realized target.");
|
|
602
|
+
}
|
|
603
|
+
const stream = await Exec.#openStreamEntry(core, runtime, pathname);
|
|
604
|
+
if ("status" in stream)
|
|
605
|
+
return stream;
|
|
606
|
+
const { resolved, entryId } = stream;
|
|
607
|
+
const { subscriptionId, controller, input, lifecycle } = await this.#bindSpawnLifecycle({
|
|
608
|
+
core, attrs, resolved, runtime, body, target, pathname, entryId, effect, invocation,
|
|
609
|
+
inputTimeoutMs: execution.inputTimeoutMs,
|
|
610
|
+
});
|
|
611
|
+
const run = async () => {
|
|
612
|
+
input.started();
|
|
613
|
+
return this.#runExecutor({
|
|
614
|
+
executor: resolved.executor,
|
|
615
|
+
runtime, body, cwd, target, metadata: args.metadata, ctx: core, pathname, coordinate: attrs.coordinate,
|
|
616
|
+
entryId, lifecycle, signal: controller.signal, controller, tempPath, input,
|
|
617
|
+
timeoutSec: typeof attrs.timeoutSec === "number" ? attrs.timeoutSec : null,
|
|
618
|
+
});
|
|
619
|
+
};
|
|
620
|
+
// {§functionality-model-mutation} Configuration is an ordered core operation,
|
|
621
|
+
// not a background job competing for an external-execution slot.
|
|
622
|
+
if (resolved.executor instanceof FunctionalityManager) {
|
|
623
|
+
const tail = run();
|
|
624
|
+
this.#activeSpawns.set(subscriptionId, tail);
|
|
625
|
+
return await tail;
|
|
626
|
+
}
|
|
627
|
+
const admission = execution.scheduler.admit(core.workspaceId, controller.signal);
|
|
628
|
+
const tail = admission.ready.then(async (release) => {
|
|
629
|
+
try {
|
|
630
|
+
return await run();
|
|
565
631
|
}
|
|
566
|
-
|
|
567
|
-
|
|
632
|
+
finally {
|
|
633
|
+
release();
|
|
568
634
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
635
|
+
});
|
|
636
|
+
// External executions background + stream ({§exec-stream}): the output
|
|
637
|
+
// surfaces as the environment-observation injector's terminal delta (nothing while it runs,
|
|
638
|
+
// one visible READ when it finishes). Pure/read commands still auto-accept
|
|
639
|
+
// from the preserved effect fact; they resolve a turn later, uniformly
|
|
640
|
+
// with host streams.
|
|
641
|
+
this.#activeSpawns.set(subscriptionId, tail);
|
|
642
|
+
void tail.catch((cause) => { console.error(`Execution '${runtime}' failed to settle:`, cause); });
|
|
643
|
+
return admission.queued
|
|
644
|
+
? {
|
|
645
|
+
status: 202,
|
|
646
|
+
outcome: "queued",
|
|
647
|
+
result: {
|
|
648
|
+
executionsAhead: admission.executionsAhead,
|
|
649
|
+
concurrency: admission.concurrency,
|
|
650
|
+
},
|
|
573
651
|
}
|
|
652
|
+
: { status: 200, outcome: "started" };
|
|
653
|
+
}
|
|
654
|
+
// {§exec-source-temporary} A standalone source read through its own scheme and realized where the executor can
|
|
655
|
+
// run it: a native file is its own path; anything else is written once under the scratch directory
|
|
656
|
+
// ({§exec-scratch-directory}). A failed read stays the owning READ's failure.
|
|
657
|
+
async #realizeSource(resourceSource, runtime, core) {
|
|
658
|
+
const sourceTarget = parsePath(resourceSource);
|
|
659
|
+
if (sourceTarget?.kind !== "url" || sourceTarget.scheme === null || sourceTarget.scheme === "file") {
|
|
660
|
+
throw new InvalidOperationResultError("The accepted execution proposal has an invalid scheme source address.");
|
|
574
661
|
}
|
|
575
|
-
|
|
576
|
-
|
|
662
|
+
const source = await this.readExecSource({
|
|
663
|
+
op: "READ",
|
|
664
|
+
aside: null,
|
|
665
|
+
target: sourceTarget,
|
|
666
|
+
metadata: null,
|
|
667
|
+
lineMarker: { marks: [1, -1] },
|
|
668
|
+
matcher: null,
|
|
669
|
+
body: null,
|
|
670
|
+
position: { line: 0, column: 0 },
|
|
671
|
+
}, core);
|
|
672
|
+
const read = source.result;
|
|
673
|
+
if (read.status >= 400) {
|
|
674
|
+
// {§exec-target-routing} — the failure stays the owning READ's (#163); the execution
|
|
675
|
+
// contract rides its recovery, because a stream address in the target slot is
|
|
676
|
+
// almost always a command that belonged beneath a targetless heading (#425 F4).
|
|
677
|
+
const problem = read.problem === undefined ? undefined : {
|
|
678
|
+
...read.problem,
|
|
679
|
+
recovery: Exec.sourceRecovery(resourceSource, read.problem.recovery),
|
|
680
|
+
};
|
|
681
|
+
return Results.assert({ ...read, outcome: "scheme_source_read_failed", ...(problem === undefined ? {} : { problem }) });
|
|
682
|
+
}
|
|
683
|
+
const { content } = read;
|
|
684
|
+
if (typeof content !== "string") {
|
|
685
|
+
return Results.failure("scheme:exec", "source-content-unavailable", 422, `Scheme '${sourceTarget.scheme}' did not supply content for the ${runtime} source.`, { outcome: "scheme_source_content_unavailable" }, {
|
|
686
|
+
scheme: sourceTarget.scheme,
|
|
687
|
+
sourceStatus: read.status,
|
|
688
|
+
retryable: false,
|
|
689
|
+
});
|
|
577
690
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
691
|
+
if (source.nativePath !== null)
|
|
692
|
+
return { target: source.nativePath, tempPath: null };
|
|
693
|
+
const tempPath = this.#sourceScratch().path(extname(sourceTarget.pathname)); // {§exec-scratch-directory}
|
|
694
|
+
await writeFile(tempPath, content, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
695
|
+
return { target: tempPath, tempPath };
|
|
696
|
+
}
|
|
697
|
+
// Resolve the runtime's executor from the boot registry, then seed the stream entry's channels from its
|
|
698
|
+
// declared topology ({§executor-channels}): each executor declares its own shape (subprocess → stdout/stderr;
|
|
699
|
+
// search → results; etc.).
|
|
700
|
+
static async #openStreamEntry(core, runtime, pathname) {
|
|
581
701
|
if (core.executors === undefined) {
|
|
582
702
|
throw new InvalidOperationResultError("An accepted execution proposal has no executor registry.");
|
|
583
703
|
}
|
|
@@ -610,6 +730,13 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
610
730
|
retryable: false,
|
|
611
731
|
});
|
|
612
732
|
}
|
|
733
|
+
return { resolved, entryId };
|
|
734
|
+
}
|
|
735
|
+
// The spawn's live subscription and cancellation scope: the stream subscription opened, the controller bound
|
|
736
|
+
// to the loop's teardown unless detached ({§exec-lifetime}), the input receiver, the abort registry entry, and
|
|
737
|
+
// the LiveSubscription whose release unbinds them all.
|
|
738
|
+
async #bindSpawnLifecycle(args) {
|
|
739
|
+
const { core, attrs, resolved, runtime, body, target, pathname, entryId, effect, invocation, inputTimeoutMs } = args;
|
|
613
740
|
const subscriptionId = await ChannelWrite.openSubscription(core.db, {
|
|
614
741
|
workerId: core.workerId, entryId, scheme: runtime,
|
|
615
742
|
handle: runtime !== "" ? `${runtime}: ${body !== "" ? body : target ?? ""}` : body,
|
|
@@ -620,7 +747,7 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
620
747
|
: `log:///${attrs.coordinate.loop_seq}/${attrs.coordinate.turn_seq}/${attrs.coordinate.sequence}/${runtime}`,
|
|
621
748
|
});
|
|
622
749
|
const controller = new AbortController();
|
|
623
|
-
let
|
|
750
|
+
let detachParent = () => { };
|
|
624
751
|
// {§exec-lifetime} — a detached spawn outlives the loop: it never binds to the loop's teardown;
|
|
625
752
|
// only KILL, the worker's total reap, and daemon shutdown end it.
|
|
626
753
|
if (core.signal !== undefined && attrs.detached !== true) {
|
|
@@ -636,11 +763,11 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
636
763
|
// doubled fire is harmless. {§exec-lifetime}
|
|
637
764
|
const onParentAbort = () => controller.abort(ExecAbort.teardownReason());
|
|
638
765
|
parent.addEventListener("abort", onParentAbort, { once: true });
|
|
639
|
-
|
|
766
|
+
detachParent = () => parent.removeEventListener("abort", onParentAbort);
|
|
640
767
|
if (parent.aborted)
|
|
641
768
|
controller.abort(ExecAbort.teardownReason());
|
|
642
769
|
}
|
|
643
|
-
const input = new ExecutionInput(controller.signal,
|
|
770
|
+
const input = new ExecutionInput(controller.signal, inputTimeoutMs, attrs.input === "open");
|
|
644
771
|
this.#activeAborts.set(subscriptionId, { workspaceId: core.workspaceId, workerId: core.workerId, turnId: core.turnId, pathname, runtime, effect, controller, detached: attrs.detached === true, input, invocation, executor: resolved.executor });
|
|
645
772
|
const lifecycle = new LiveSubscription({
|
|
646
773
|
db: core.db, registry: this.liveSubscriptions(),
|
|
@@ -649,55 +776,15 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
649
776
|
target: `${runtime}://${pathname}`, subscriptionId, scheme: runtime, ...attrs.coordinate,
|
|
650
777
|
},
|
|
651
778
|
handle: { cancel: () => controller.abort(ExecAbort.teardownReason()) },
|
|
779
|
+
reaperLoopId: core.signal !== undefined && attrs.detached !== true ? core.loopId : null,
|
|
652
780
|
release: () => {
|
|
653
|
-
|
|
781
|
+
detachParent();
|
|
654
782
|
this.#activeAborts.delete(subscriptionId);
|
|
655
783
|
this.#activeSpawns.delete(subscriptionId);
|
|
656
784
|
},
|
|
657
785
|
notify: core.streamEventNotify, wake: core.wakeWorkerNotify,
|
|
658
786
|
});
|
|
659
|
-
|
|
660
|
-
input.started();
|
|
661
|
-
return this.#runExecutor({
|
|
662
|
-
executor: resolved.executor,
|
|
663
|
-
runtime, body, cwd, target, metadata: args.metadata, ctx: core, pathname, coordinate: attrs.coordinate,
|
|
664
|
-
entryId, lifecycle, signal: controller.signal, controller, tempPath, input,
|
|
665
|
-
timeoutSec: typeof attrs.timeoutSec === "number" ? attrs.timeoutSec : null,
|
|
666
|
-
});
|
|
667
|
-
};
|
|
668
|
-
// {§functionality-model-mutation} Configuration is an ordered core operation,
|
|
669
|
-
// not a background job competing for an external-execution slot.
|
|
670
|
-
if (resolved.executor instanceof FunctionalityManager) {
|
|
671
|
-
const tail = run();
|
|
672
|
-
this.#activeSpawns.set(subscriptionId, tail);
|
|
673
|
-
return await tail;
|
|
674
|
-
}
|
|
675
|
-
const admission = execution.scheduler.admit(core.workspaceId, controller.signal);
|
|
676
|
-
const tail = admission.ready.then(async (release) => {
|
|
677
|
-
try {
|
|
678
|
-
return await run();
|
|
679
|
-
}
|
|
680
|
-
finally {
|
|
681
|
-
release();
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
// External executions background + stream ({§exec-stream}): the output
|
|
685
|
-
// surfaces as the environment-observation injector's terminal delta (nothing while it runs,
|
|
686
|
-
// one visible READ when it finishes). Pure/read commands still auto-accept
|
|
687
|
-
// from the preserved effect fact; they resolve a turn later, uniformly
|
|
688
|
-
// with host streams.
|
|
689
|
-
this.#activeSpawns.set(subscriptionId, tail);
|
|
690
|
-
void tail.catch((cause) => { console.error(`Execution '${runtime}' failed to settle:`, cause); });
|
|
691
|
-
return admission.queued
|
|
692
|
-
? {
|
|
693
|
-
status: 202,
|
|
694
|
-
outcome: "queued",
|
|
695
|
-
result: {
|
|
696
|
-
executionsAhead: admission.executionsAhead,
|
|
697
|
-
concurrency: admission.concurrency,
|
|
698
|
-
},
|
|
699
|
-
}
|
|
700
|
-
: { status: 200, outcome: "started" };
|
|
787
|
+
return { subscriptionId, controller, input, lifecycle };
|
|
701
788
|
}
|
|
702
789
|
// Bridge the executor's sink-style contract (write/setState/emit)
|
|
703
790
|
// onto plurnk-service's storage primitives (appendToChannel,
|
|
@@ -762,190 +849,15 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
762
849
|
stage: "execution",
|
|
763
850
|
retryable: false,
|
|
764
851
|
});
|
|
765
|
-
let
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
let entryChainFailed = false;
|
|
775
|
-
let callerSource;
|
|
776
|
-
const resolveCallerSource = async () => {
|
|
777
|
-
if (callerSource !== undefined)
|
|
778
|
-
return callerSource;
|
|
779
|
-
const caller = await db.worker_name_by_id.get({ worker_id: ctx.workerId });
|
|
780
|
-
if (caller === undefined)
|
|
781
|
-
throw new Error(`entry(): calling worker ${ctx.workerId} does not exist`);
|
|
782
|
-
callerSource = WorkerControlAddress.render(caller.name);
|
|
783
|
-
return callerSource;
|
|
784
|
-
};
|
|
785
|
-
const publishedNames = new ResourceNames();
|
|
786
|
-
const entrySink = (requestedPath, content, opts) => {
|
|
787
|
-
let path = requestedPath;
|
|
788
|
-
if (path === null) {
|
|
789
|
-
if (content === null)
|
|
790
|
-
return Promise.reject(new Error("entry(): an invocation resource requires supplied content"));
|
|
791
|
-
path = renderAddress({ scheme: runtime, authority: "", pathname: `${pathname}/resources/${publishedNames.allocate(opts.name)}` });
|
|
792
|
-
}
|
|
793
|
-
const parsed = parsePath(path);
|
|
794
|
-
if (parsed === null || parsed.kind !== "url")
|
|
795
|
-
return Promise.reject(new Error(`entry(): '${path.slice(0, 80)}' is not a URL`));
|
|
796
|
-
if (content !== null && opts.mimetype === undefined)
|
|
797
|
-
return Promise.reject(new Error("entry(): mimetype is required when content is provided"));
|
|
798
|
-
const address = NetworkAddress.supports(parsed.scheme) ? NetworkAddress.from(parsed) : null;
|
|
799
|
-
if (address?.hasCredentials === true)
|
|
800
|
-
return Promise.reject(new Error("entry(): network URL userinfo is not allowed"));
|
|
801
|
-
const fetchAddress = address !== null && (address.scheme === "http" || address.scheme === "https")
|
|
802
|
-
? address
|
|
803
|
-
: null;
|
|
804
|
-
const binding = requestedPath === null
|
|
805
|
-
? Promise.resolve({ address: { scheme: runtime, authority: "", pathname: parsed.pathname }, result: null })
|
|
806
|
-
: this.bindEntryAddress(parsed, ctx);
|
|
807
|
-
// {§exec-entry-sink}/{§web-search-retrieval} — start content:null
|
|
808
|
-
// acquisition before the write chain so fetches run in parallel;
|
|
809
|
-
// only durable entry writes serialize. A null result rejects the sink.
|
|
810
|
-
let materialized;
|
|
811
|
-
if (content === null) {
|
|
812
|
-
if (fetchAddress === null)
|
|
813
|
-
return Promise.reject(new Error("entry(): content:null requires an http(s):// URL"));
|
|
814
|
-
materialized = this.#fetchWeb(fetchAddress.url, { signal });
|
|
815
|
-
}
|
|
816
|
-
else if (typeof content === "string" && requestedPath !== null) {
|
|
817
|
-
materialized = Promise.resolve({
|
|
818
|
-
url: fetchAddress?.url ?? "http://localhost",
|
|
819
|
-
body: content,
|
|
820
|
-
mimetype: opts.mimetype,
|
|
821
|
-
allowConfiguredMaterializer: false,
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
const op = async () => {
|
|
825
|
-
const resolved = await binding;
|
|
826
|
-
if (resolved === null) {
|
|
827
|
-
throw new Error(`entry(): no entry-bearing scheme is registered for '${path.slice(0, 80)}'`);
|
|
828
|
-
}
|
|
829
|
-
if (resolved.result !== null)
|
|
830
|
-
throw new OperationFailureError(resolved.result);
|
|
831
|
-
if (resolved.address === null) {
|
|
832
|
-
throw new Error(`entry(): '${path.slice(0, 80)}' did not resolve to an entry address`);
|
|
833
|
-
}
|
|
834
|
-
const { authority, pathname, scheme } = resolved.address;
|
|
835
|
-
const coordinate = { authority, pathname };
|
|
836
|
-
let channels;
|
|
837
|
-
let decisive;
|
|
838
|
-
let source;
|
|
839
|
-
if (materialized === undefined) {
|
|
840
|
-
if (content === null)
|
|
841
|
-
throw new Error("entry(): missing supplied content");
|
|
842
|
-
const body = typeof content === "string" ? { content, mimetype: opts.mimetype }
|
|
843
|
-
: { content: "", bytes: content, mimetype: opts.mimetype };
|
|
844
|
-
const channel = requestedPath === null ? executor.defaultChannel : ctx.defaultChannelFor?.(scheme);
|
|
845
|
-
if (channel === undefined)
|
|
846
|
-
throw new Error(`entry(): no default channel for '${scheme}'`);
|
|
847
|
-
channels = { [channel]: body };
|
|
848
|
-
decisive = typeof content === "string" ? content : `${opts.mimetype}; ${content.byteLength} bytes`;
|
|
849
|
-
source = decisive;
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
const fetched = await materialized;
|
|
853
|
-
if (fetched === null)
|
|
854
|
-
throw new Error(`entry(): '${path.slice(0, 80)}' is dead`);
|
|
855
|
-
let web;
|
|
856
|
-
try {
|
|
857
|
-
web = await this.#materializer().materialize(fetched, new DbProjectionCaps(ctx));
|
|
858
|
-
}
|
|
859
|
-
catch (error) {
|
|
860
|
-
if (!signal.aborted && error instanceof WebMaterializationError) {
|
|
861
|
-
console.error("entry() web materialization failed", { path, error });
|
|
862
|
-
}
|
|
863
|
-
throw error;
|
|
864
|
-
}
|
|
865
|
-
channels = this.#materializer().materializedChannels(web, content === null && fetchAddress !== null ? { url: fetchAddress.url, method: "GET" } : undefined);
|
|
866
|
-
decisive = web.readable?.content ?? (web.body.bytes === undefined
|
|
867
|
-
? web.body.content : `${web.body.mimetype}; ${web.body.bytes.byteLength} bytes`);
|
|
868
|
-
source = web.body.bytes === undefined ? web.body.content : decisive;
|
|
869
|
-
}
|
|
870
|
-
const causalSource = await resolveCallerSource();
|
|
871
|
-
const written = Results.assert(await EntryCrud.writeEntry(coordinate, { channels }, ctx, scheme, {
|
|
872
|
-
defaultChannel: requestedPath === null ? executor.defaultChannel : ctx.defaultChannelFor?.(scheme),
|
|
873
|
-
output: requestedPath === null,
|
|
874
|
-
}));
|
|
875
|
-
if (narration === null) {
|
|
876
|
-
const workerId = await RuntimeWorker.ensure(db, ctx.workspaceId);
|
|
877
|
-
const loop = await AdministrativeLoop.open(db, workerId);
|
|
878
|
-
const turn = await Turn.open(db, {
|
|
879
|
-
loopId: loop.id,
|
|
880
|
-
producer: "_plurnk",
|
|
881
|
-
kind: "operation",
|
|
882
|
-
});
|
|
883
|
-
narration = {
|
|
884
|
-
workerId,
|
|
885
|
-
loopId: loop.id,
|
|
886
|
-
loopSeq: loop.sequence,
|
|
887
|
-
turnId: turn.id,
|
|
888
|
-
turnSeq: turn.sequence,
|
|
889
|
-
seq: 1,
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
if (narrationResult.status < 400 || written.status >= 400)
|
|
893
|
-
narrationResult = written;
|
|
894
|
-
const sequence = narration.seq++;
|
|
895
|
-
const narrationAttrs = { kind: "entry_materialized" };
|
|
896
|
-
if (written.problem !== undefined) {
|
|
897
|
-
const coordinate = LogEntryProjection.coordinate(`${narration.loopSeq}/${narration.turnSeq}/${sequence}`, { origin: "_plurnk", op: "EDIT", attrs: narrationAttrs });
|
|
898
|
-
Results.attachInstance(written, `log:///${coordinate}`);
|
|
899
|
-
}
|
|
900
|
-
const tx = JSON.stringify({ op: "EDIT", body: source });
|
|
901
|
-
const rx = JSON.stringify(written.problem === undefined
|
|
902
|
-
? {
|
|
903
|
-
...written,
|
|
904
|
-
span: decisive.split("\n").map((l, n) => `${n + 1}:${l}`).join("\n"),
|
|
905
|
-
}
|
|
906
|
-
: written);
|
|
907
|
-
const attrs = JSON.stringify(narrationAttrs);
|
|
908
|
-
if (ctx.weigh === undefined) {
|
|
909
|
-
throw new Error("entry(): ctx.weigh is required for curation-weight accounting");
|
|
910
|
-
}
|
|
911
|
-
const logRow = await db.engine_insert_log_entry.get({
|
|
912
|
-
worker_id: narration.workerId, loop_id: narration.loopId, turn_id: narration.turnId, sequence,
|
|
913
|
-
origin: "_plurnk", source: causalSource, model_call_id: null,
|
|
914
|
-
op: "EDIT", signal: null,
|
|
915
|
-
scheme, username: null, password: null,
|
|
916
|
-
hostname: address === null ? null : parsed.hostname,
|
|
917
|
-
port: address === null ? null : parsed.port,
|
|
918
|
-
pathname: address === null ? pathname : PathSyntax.decodeParens(parsed.pathname),
|
|
919
|
-
query: address === null ? null : parsed.query,
|
|
920
|
-
fragment: null, lineMarker: null,
|
|
921
|
-
tx, mimetype_tx: "application/json",
|
|
922
|
-
rx,
|
|
923
|
-
mimetype_rx: "application/json",
|
|
924
|
-
status_rx: written.status,
|
|
925
|
-
weight: LogBody.weight({
|
|
926
|
-
op: "EDIT",
|
|
927
|
-
attrs,
|
|
928
|
-
tx,
|
|
929
|
-
rx,
|
|
930
|
-
mimetypeTx: "application/json",
|
|
931
|
-
mimetypeRx: "application/json",
|
|
932
|
-
}, ctx.weigh),
|
|
933
|
-
state: "resolved", outcome: null,
|
|
934
|
-
// Durable provenance for clients/forensics. This is machine
|
|
935
|
-
// ambience, not a human/model action waterfall item.
|
|
936
|
-
attrs,
|
|
937
|
-
initial_folded: "[]",
|
|
938
|
-
});
|
|
939
|
-
if (logRow === undefined)
|
|
940
|
-
throw new Error("entry(): log insert returned no row");
|
|
941
|
-
if (written.problem !== undefined)
|
|
942
|
-
throw new OperationFailureError(written);
|
|
943
|
-
return renderAddress({ scheme, authority, pathname });
|
|
944
|
-
};
|
|
945
|
-
const run = entryChain.then(op, op);
|
|
946
|
-
entryChain = run.then(() => undefined, () => { entryChainFailed = true; });
|
|
947
|
-
return run;
|
|
948
|
-
};
|
|
852
|
+
let channelSizes = [];
|
|
853
|
+
// {§exec-entry-sink} — the executor's entry() sink and the narration turn it owns live in their own owner;
|
|
854
|
+
// this spawn's tail settles them.
|
|
855
|
+
const sink = new ExecEntrySink({
|
|
856
|
+
ctx, runtime, pathname, signal, executor,
|
|
857
|
+
bindEntryAddress: (parsed, context) => this.bindEntryAddress(parsed, context),
|
|
858
|
+
fetchWeb: this.#fetchWeb,
|
|
859
|
+
materializer: this.#materializer,
|
|
860
|
+
});
|
|
949
861
|
const executionFailures = [];
|
|
950
862
|
const cancelled = (exitCode) => Results.failure("scheme:exec", "execution-cancelled", 499, `Execution of '${runtime}' was cancelled by the service.`, exitCode === undefined ? {} : { exitCode }, { runtime, stage: "execution", retryable: false });
|
|
951
863
|
try {
|
|
@@ -963,7 +875,7 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
963
875
|
const reported = await executor.run({
|
|
964
876
|
registerInput: (receiver) => input.register(receiver),
|
|
965
877
|
runtime, body, cwd, target, metadata, signal,
|
|
966
|
-
entry:
|
|
878
|
+
entry: sink.entry,
|
|
967
879
|
// {§executor-interaction-sink} — the executor's signal narrows the execution's, never replaces it.
|
|
968
880
|
interact: (request, interactionSignal) => {
|
|
969
881
|
if (ctx.requestInteraction === undefined) {
|
|
@@ -1014,67 +926,19 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
1014
926
|
});
|
|
1015
927
|
}
|
|
1016
928
|
}
|
|
1017
|
-
|
|
1018
|
-
//
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
runtime,
|
|
1024
|
-
lifetime,
|
|
1025
|
-
stage: "execution",
|
|
1026
|
-
retryable: false,
|
|
1027
|
-
});
|
|
1028
|
-
// The service's own abort knowledge outranks a driver's claim: a
|
|
1029
|
-
// spawn we reaped did not succeed, whatever it resolved under abort.
|
|
1030
|
-
}
|
|
1031
|
-
else if (signal.aborted && result.status < 400) {
|
|
1032
|
-
result = cancelled(exitCode ?? undefined);
|
|
1033
|
-
}
|
|
1034
|
-
else if (result.status < 400 && exitCode === 127 && target === null) {
|
|
1035
|
-
result = this.#namedToolFallThrough(result, runtime, body, ctx);
|
|
1036
|
-
}
|
|
1037
|
-
exitLabel = timedOut
|
|
1038
|
-
? `timed out after ${(timeoutSec ?? 0) / 60}m`
|
|
1039
|
-
: result.status === 499
|
|
1040
|
-
? "aborted"
|
|
1041
|
-
: exitCode !== null
|
|
1042
|
-
? `exit ${exitCode}`
|
|
1043
|
-
: result.problem?.title.toLowerCase() ?? "completed";
|
|
1044
|
-
const stdoutMeta = await db.channel_meta.get({ entry_id: entryId, channel: "stdout" });
|
|
1045
|
-
const stderrMeta = await db.channel_meta.get({ entry_id: entryId, channel: "stderr" });
|
|
1046
|
-
stdoutLength = stdoutMeta?.contentLength ?? 0;
|
|
1047
|
-
stderrLength = stderrMeta?.contentLength ?? 0;
|
|
929
|
+
result = this.#settleExecutionResult({ result, timedOut, timeoutSec, signal, target, runtime, body, ctx });
|
|
930
|
+
// {§exec-conclusion-summary} — the summary sizes the channels this executor declares, in its order.
|
|
931
|
+
channelSizes = await Promise.all(Object.keys(executor.channels).map(async (channel) => [
|
|
932
|
+
channel,
|
|
933
|
+
(await db.channel_meta.get({ entry_id: entryId, channel }))?.contentLength ?? 0,
|
|
934
|
+
]));
|
|
1048
935
|
}
|
|
1049
936
|
catch (cause) {
|
|
1050
937
|
executionFailures.push(cause);
|
|
1051
938
|
}
|
|
1052
939
|
{
|
|
1053
|
-
// {§exec-entry-sink} — the spawn tail
|
|
1054
|
-
const finalizationErrors = [...executionFailures];
|
|
1055
|
-
if (entryChainFailed && narrationResult.status < 400) {
|
|
1056
|
-
narrationResult = Results.failure("scheme:exec", "entry-narration-failed", 500, "An executor entry could not be materialized or narrated.", {}, { stage: "entry-materialization", retryable: false });
|
|
1057
|
-
}
|
|
1058
|
-
try {
|
|
1059
|
-
await entryChain;
|
|
1060
|
-
}
|
|
1061
|
-
catch (cause) {
|
|
1062
|
-
narrationResult = Results.failure("scheme:exec", "entry-narration-failed", 500, "An executor entry could not be materialized or narrated.", {}, { stage: "entry-materialization", retryable: false });
|
|
1063
|
-
finalizationErrors.push(cause);
|
|
1064
|
-
}
|
|
1065
|
-
const completedNarration = narration;
|
|
1066
|
-
if (completedNarration !== null) {
|
|
1067
|
-
try {
|
|
1068
|
-
await Turn.complete(db, completedNarration.turnId, narrationResult.status);
|
|
1069
|
-
const closed = await new LoopLifecycle(db).finish(completedNarration.loopId, narrationResult);
|
|
1070
|
-
if (closed === null) {
|
|
1071
|
-
throw new Error(`entry(): narration loop ${completedNarration.loopId} was not open at completion`);
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
catch (cause) {
|
|
1075
|
-
finalizationErrors.push(cause);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
940
|
+
// {§exec-entry-sink} — the spawn tail settles the entry chain and its narration turn before anything else.
|
|
941
|
+
const finalizationErrors = [...executionFailures, ...await sink.settle()];
|
|
1078
942
|
if (timeoutTimer !== null)
|
|
1079
943
|
clearTimeout(timeoutTimer); // a finished spawn leaves no pending timer
|
|
1080
944
|
// {§exec-source-temporary} — cleanup cannot rewrite the settled
|
|
@@ -1085,7 +949,7 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
1085
949
|
});
|
|
1086
950
|
}
|
|
1087
951
|
try {
|
|
1088
|
-
await lifecycle.close(result, `${runtime}://${pathname}
|
|
952
|
+
await lifecycle.close(result, Exec.#closeSummary(`${runtime}://${pathname}`, result, channelSizes));
|
|
1089
953
|
}
|
|
1090
954
|
catch (cause) {
|
|
1091
955
|
finalizationErrors.push(cause);
|
|
@@ -1093,11 +957,47 @@ export default class Exec extends CoreSchemeAdapterBase {
|
|
|
1093
957
|
if (finalizationErrors.length === 1)
|
|
1094
958
|
throw finalizationErrors[0];
|
|
1095
959
|
if (finalizationErrors.length > 1) {
|
|
1096
|
-
throw new AggregateError(finalizationErrors, `Execution narration ${
|
|
960
|
+
throw new AggregateError(finalizationErrors, `Execution narration ${sink.narrationTurnId ?? "unknown"} failed to settle`);
|
|
1097
961
|
}
|
|
1098
962
|
}
|
|
1099
963
|
return result;
|
|
1100
964
|
}
|
|
965
|
+
// One valid truth for the settled execution: a timeout outranks the executor's report, the service's own abort
|
|
966
|
+
// knowledge outranks a driver's claim, and a shell's 127 on a tool's name is that tool's refusal
|
|
967
|
+
// ({§exec-tool-fall-through}).
|
|
968
|
+
#settleExecutionResult(args) {
|
|
969
|
+
const { timedOut, timeoutSec, signal, target, runtime, body, ctx } = args;
|
|
970
|
+
let { result } = args;
|
|
971
|
+
const cancelled = (exitCode) => Results.failure("scheme:exec", "execution-cancelled", 499, `Execution of '${runtime}' was cancelled by the service.`, exitCode === undefined ? {} : { exitCode }, { runtime, stage: "execution", retryable: false });
|
|
972
|
+
const exitCode = typeof result.exitCode === "number" ? result.exitCode : null;
|
|
973
|
+
// A timeout aborts the spawn → the executor reports 499; replace the
|
|
974
|
+
// complete result so status and Problem remain one valid truth.
|
|
975
|
+
if (timedOut) {
|
|
976
|
+
const lifetime = formatLifetime(timeoutSec ?? 0);
|
|
977
|
+
result = Results.failure("scheme:exec", "execution-timeout", 504, `Execution of '${runtime}' outlived its ${lifetime} lifetime.`, exitCode === null ? {} : { exitCode }, {
|
|
978
|
+
runtime,
|
|
979
|
+
lifetime,
|
|
980
|
+
stage: "execution",
|
|
981
|
+
retryable: false,
|
|
982
|
+
});
|
|
983
|
+
// The service's own abort knowledge outranks a driver's claim: a
|
|
984
|
+
// spawn we reaped did not succeed, whatever it resolved under abort.
|
|
985
|
+
}
|
|
986
|
+
else if (signal.aborted && result.status < 400) {
|
|
987
|
+
result = cancelled(exitCode ?? undefined);
|
|
988
|
+
}
|
|
989
|
+
else if (result.status < 400 && exitCode === 127 && target === null) {
|
|
990
|
+
result = this.#namedToolFallThrough(result, runtime, body, ctx);
|
|
991
|
+
}
|
|
992
|
+
return result;
|
|
993
|
+
}
|
|
994
|
+
// {§exec-conclusion-summary} — the stream's address, how its settled result ended, and each declared channel's size.
|
|
995
|
+
static #closeSummary(address, result, channels) {
|
|
996
|
+
const ended = Results.isFailed(result) ? result.problem.title.toLowerCase()
|
|
997
|
+
: typeof result.exitCode === "number" ? `completed (exit ${result.exitCode})` : "completed";
|
|
998
|
+
const sizes = channels.map(([channel, size]) => `${channel}=${size} bytes`).join(", ");
|
|
999
|
+
return `${address} ${ended}${sizes === "" ? "" : `; ${sizes}`}`;
|
|
1000
|
+
}
|
|
1101
1001
|
async find(statement, ctx) {
|
|
1102
1002
|
const core = this.coreContext(ctx);
|
|
1103
1003
|
return EntryFind.findWorkspaceEntries(statement, core, Exec.manifest, {});
|