@shivaedev/work-fleet 0.2.0 → 0.3.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/CHANGELOG.md +47 -0
- package/README.md +60 -18
- package/dist/DispatchRequest.d.ts +13 -0
- package/dist/DispatchRequest.d.ts.map +1 -0
- package/dist/DispatchRequest.js +12 -0
- package/dist/DispatchRequest.js.map +1 -0
- package/dist/admit.d.ts +11 -0
- package/dist/admit.d.ts.map +1 -1
- package/dist/attention.d.ts +1 -0
- package/dist/attention.d.ts.map +1 -1
- package/dist/attention.js +4 -0
- package/dist/attention.js.map +1 -1
- package/dist/board/fleetField.d.ts +1 -0
- package/dist/board/fleetField.d.ts.map +1 -1
- package/dist/board/fleetField.js +4 -0
- package/dist/board/fleetField.js.map +1 -1
- package/dist/board/markdown.d.ts.map +1 -1
- package/dist/board/markdown.js +7 -2
- package/dist/board/markdown.js.map +1 -1
- package/dist/board/publish.d.ts +2 -0
- package/dist/board/publish.d.ts.map +1 -1
- package/dist/board/publishReport.d.ts +1 -0
- package/dist/board/publishReport.d.ts.map +1 -1
- package/dist/board/readPass.d.ts +1 -0
- package/dist/board/readPass.d.ts.map +1 -1
- package/dist/cli.js +13 -6
- package/dist/cli.js.map +1 -1
- package/dist/engine/abandonStalled.d.ts +1171 -1004
- package/dist/engine/abandonStalled.d.ts.map +1 -1
- package/dist/engine/admissionOrder.d.ts +1091 -0
- package/dist/engine/admissionOrder.d.ts.map +1 -0
- package/dist/engine/admissionOrder.js +15 -0
- package/dist/engine/admissionOrder.js.map +1 -0
- package/dist/engine/applyResolution.d.ts +20 -0
- package/dist/engine/applyResolution.d.ts.map +1 -1
- package/dist/engine/approveBrief.d.ts +31 -0
- package/dist/engine/approveBrief.d.ts.map +1 -1
- package/dist/engine/authorizeResolution.d.ts +1160 -1003
- package/dist/engine/authorizeResolution.d.ts.map +1 -1
- package/dist/engine/boardBrief.d.ts +32 -0
- package/dist/engine/boardBrief.d.ts.map +1 -1
- package/dist/engine/capacityRetry.d.ts +3488 -3007
- package/dist/engine/capacityRetry.d.ts.map +1 -1
- package/dist/engine/closeDecisions.d.ts +20 -0
- package/dist/engine/closeDecisions.d.ts.map +1 -1
- package/dist/engine/completeBoardWork.d.ts +20 -0
- package/dist/engine/completeBoardWork.d.ts.map +1 -1
- package/dist/engine/completeReport.d.ts +21 -0
- package/dist/engine/completeReport.d.ts.map +1 -1
- package/dist/engine/continueCommand.d.ts +10 -0
- package/dist/engine/continueCommand.d.ts.map +1 -1
- package/dist/engine/continueWorker.d.ts +20 -0
- package/dist/engine/continueWorker.d.ts.map +1 -1
- package/dist/engine/deliverPendingAnswers.d.ts +1085 -898
- package/dist/engine/deliverPendingAnswers.d.ts.map +1 -1
- package/dist/engine/dispatchCommand.d.ts +14 -1
- package/dist/engine/dispatchCommand.d.ts.map +1 -1
- package/dist/engine/dispatchCommand.js +25 -5
- package/dist/engine/dispatchCommand.js.map +1 -1
- package/dist/engine/ensureDecisionOwner.d.ts +20 -0
- package/dist/engine/ensureDecisionOwner.d.ts.map +1 -1
- package/dist/engine/ensurePublication.d.ts +20 -0
- package/dist/engine/ensurePublication.d.ts.map +1 -1
- package/dist/engine/failureContext.d.ts +1 -0
- package/dist/engine/failureContext.d.ts.map +1 -1
- package/dist/engine/failureContext.js.map +1 -1
- package/dist/engine/failureMessage.js +11 -1
- package/dist/engine/failureMessage.js.map +1 -1
- package/dist/engine/health.d.ts +10 -0
- package/dist/engine/health.d.ts.map +1 -1
- package/dist/engine/inspectRenewal.d.ts +21 -0
- package/dist/engine/inspectRenewal.d.ts.map +1 -1
- package/dist/engine/launchInput.d.ts +10 -0
- package/dist/engine/launchInput.d.ts.map +1 -1
- package/dist/engine/launchReceipt.d.ts +10 -0
- package/dist/engine/launchReceipt.d.ts.map +1 -1
- package/dist/engine/localOnlyCriteria.d.ts +1 -0
- package/dist/engine/localOnlyCriteria.d.ts.map +1 -1
- package/dist/engine/localOnlyCriteria.js +5 -1
- package/dist/engine/localOnlyCriteria.js.map +1 -1
- package/dist/engine/makeAdopt.d.ts +21 -0
- package/dist/engine/makeAdopt.d.ts.map +1 -1
- package/dist/engine/makeAdvance.d.ts +10 -0
- package/dist/engine/makeAdvance.d.ts.map +1 -1
- package/dist/engine/makeDeliver.d.ts +20 -0
- package/dist/engine/makeDeliver.d.ts.map +1 -1
- package/dist/engine/makeHuman.d.ts +20 -0
- package/dist/engine/makeHuman.d.ts.map +1 -1
- package/dist/engine/makeLaunch.d.ts +20 -0
- package/dist/engine/makeLaunch.d.ts.map +1 -1
- package/dist/engine/makeLaunch.js +4 -13
- package/dist/engine/makeLaunch.js.map +1 -1
- package/dist/engine/makeObserve.d.ts +20 -0
- package/dist/engine/makeObserve.d.ts.map +1 -1
- package/dist/engine/makeReview.d.ts +21 -0
- package/dist/engine/makeReview.d.ts.map +1 -1
- package/dist/engine/makeState.d.ts +1164 -1010
- package/dist/engine/makeState.d.ts.map +1 -1
- package/dist/engine/makeState.js +19 -11
- package/dist/engine/makeState.js.map +1 -1
- package/dist/engine/makeValidate.d.ts +21 -0
- package/dist/engine/makeValidate.d.ts.map +1 -1
- package/dist/engine/mergeReadiness.d.ts +1425 -1764
- package/dist/engine/mergeReadiness.d.ts.map +1 -1
- package/dist/engine/mergeReadiness.js +3 -4
- package/dist/engine/mergeReadiness.js.map +1 -1
- package/dist/engine/observeDeliveryFailure.d.ts +1170 -1003
- package/dist/engine/observeDeliveryFailure.d.ts.map +1 -1
- package/dist/engine/observeRenewal.d.ts +33 -0
- package/dist/engine/observeRenewal.d.ts.map +1 -1
- package/dist/engine/obtainReview.d.ts +11 -0
- package/dist/engine/obtainReview.d.ts.map +1 -1
- package/dist/engine/obtainReview.js +2 -1
- package/dist/engine/obtainReview.js.map +1 -1
- package/dist/engine/operationRecord.d.ts +1898 -1779
- package/dist/engine/operationRecord.d.ts.map +1 -1
- package/dist/engine/persistTerminalReceipts.d.ts +20 -0
- package/dist/engine/persistTerminalReceipts.d.ts.map +1 -1
- package/dist/engine/prepareCommand.d.ts +10 -0
- package/dist/engine/prepareCommand.d.ts.map +1 -1
- package/dist/engine/prepareCommand.js +1 -1
- package/dist/engine/prepareCommand.js.map +1 -1
- package/dist/engine/queueAnswerCommand.d.ts +10 -0
- package/dist/engine/queueAnswerCommand.d.ts.map +1 -1
- package/dist/engine/readOrContinueReview.d.ts +11 -0
- package/dist/engine/readOrContinueReview.d.ts.map +1 -1
- package/dist/engine/readOrContinueReview.js +2 -1
- package/dist/engine/readOrContinueReview.js.map +1 -1
- package/dist/engine/readResolution.d.ts +1 -0
- package/dist/engine/readResolution.d.ts.map +1 -1
- package/dist/engine/readRetry.d.ts +2344 -2010
- package/dist/engine/readRetry.d.ts.map +1 -1
- package/dist/engine/readRetry.js +20 -1
- package/dist/engine/readRetry.js.map +1 -1
- package/dist/engine/reassignCommand.d.ts +10 -0
- package/dist/engine/reassignCommand.d.ts.map +1 -1
- package/dist/engine/reconcileAttempt.d.ts +12 -1
- package/dist/engine/reconcileAttempt.d.ts.map +1 -1
- package/dist/engine/reconcileAttempt.js +2 -1
- package/dist/engine/reconcileAttempt.js.map +1 -1
- package/dist/engine/reconcileCommand.d.ts +10 -0
- package/dist/engine/reconcileCommand.d.ts.map +1 -1
- package/dist/engine/recordResponseCommand.d.ts +551 -0
- package/dist/engine/recordResponseCommand.d.ts.map +1 -0
- package/dist/engine/recordResponseCommand.js +30 -0
- package/dist/engine/recordResponseCommand.js.map +1 -0
- package/dist/engine/recoverStall.d.ts +1172 -1004
- package/dist/engine/recoverStall.d.ts.map +1 -1
- package/dist/engine/recoverStall.js +1 -1
- package/dist/engine/recoverStall.js.map +1 -1
- package/dist/engine/recoverUnobservable.d.ts +1172 -1004
- package/dist/engine/recoverUnobservable.d.ts.map +1 -1
- package/dist/engine/refreshBrief.d.ts +2344 -2009
- package/dist/engine/refreshBrief.d.ts.map +1 -1
- package/dist/engine/refreshReviewResult.d.ts +21 -0
- package/dist/engine/refreshReviewResult.d.ts.map +1 -1
- package/dist/engine/releaseClosedPreparation.d.ts +1171 -1003
- package/dist/engine/releaseClosedPreparation.d.ts.map +1 -1
- package/dist/engine/releaseExecution.d.ts +1170 -1003
- package/dist/engine/releaseExecution.d.ts.map +1 -1
- package/dist/engine/releaseFinishedWork.d.ts +10 -0
- package/dist/engine/releaseFinishedWork.d.ts.map +1 -1
- package/dist/engine/repository.d.ts +1115 -1003
- package/dist/engine/repository.d.ts.map +1 -1
- package/dist/engine/repository.js +12 -12
- package/dist/engine/repository.js.map +1 -1
- package/dist/engine/requestConflictRepair.d.ts +1170 -1003
- package/dist/engine/requestConflictRepair.d.ts.map +1 -1
- package/dist/engine/requestRework.d.ts +20 -0
- package/dist/engine/requestRework.d.ts.map +1 -1
- package/dist/engine/resolveCommand.d.ts +10 -0
- package/dist/engine/resolveCommand.d.ts.map +1 -1
- package/dist/engine/resolveExecution.d.ts +1170 -1003
- package/dist/engine/resolveExecution.d.ts.map +1 -1
- package/dist/engine/resumeAttempt.d.ts +1171 -1003
- package/dist/engine/resumeAttempt.d.ts.map +1 -1
- package/dist/engine/retireUnacknowledged.d.ts +20 -0
- package/dist/engine/retireUnacknowledged.d.ts.map +1 -1
- package/dist/engine/retryTerminal.d.ts +1160 -1003
- package/dist/engine/retryTerminal.d.ts.map +1 -1
- package/dist/engine/saveLaunch.d.ts +10598 -0
- package/dist/engine/saveLaunch.d.ts.map +1 -0
- package/dist/engine/saveLaunch.js +30 -0
- package/dist/engine/saveLaunch.js.map +1 -0
- package/dist/engine/scheduler.d.ts +21 -1
- package/dist/engine/scheduler.d.ts.map +1 -1
- package/dist/engine/scheduler.js +3 -5
- package/dist/engine/scheduler.js.map +1 -1
- package/dist/engine/sessionCleanup.d.ts +20 -0
- package/dist/engine/sessionCleanup.d.ts.map +1 -1
- package/dist/engine/settleMergedDelivery.d.ts +1170 -1003
- package/dist/engine/settleMergedDelivery.d.ts.map +1 -1
- package/dist/engine/steerBrief.d.ts +1075 -898
- package/dist/engine/steerBrief.d.ts.map +1 -1
- package/dist/engine/submissionFailure.d.ts +1150 -1003
- package/dist/engine/submissionFailure.d.ts.map +1 -1
- package/dist/engine/submitLaunch.d.ts +1160 -1003
- package/dist/engine/submitLaunch.d.ts.map +1 -1
- package/dist/engine/views.d.ts.map +1 -1
- package/dist/engine/views.js +4 -0
- package/dist/engine/views.js.map +1 -1
- package/dist/engine/workerReport.d.ts +30 -0
- package/dist/engine/workerReport.d.ts.map +1 -1
- package/dist/fleet.d.ts +2716 -983
- package/dist/fleet.d.ts.map +1 -1
- package/dist/fleet.js +4 -0
- package/dist/fleet.js.map +1 -1
- package/dist/host/actionCommands.d.ts.map +1 -1
- package/dist/host/actionCommands.js +7 -6
- package/dist/host/actionCommands.js.map +1 -1
- package/dist/host/admission.d.ts +24 -1
- package/dist/host/admission.d.ts.map +1 -1
- package/dist/host/admission.js +2 -2
- package/dist/host/admission.js.map +1 -1
- package/dist/host/answerWork.d.ts +12 -2
- package/dist/host/answerWork.d.ts.map +1 -1
- package/dist/host/answerWork.js +19 -23
- package/dist/host/answerWork.js.map +1 -1
- package/dist/host/commands.d.ts +28 -4
- package/dist/host/commands.d.ts.map +1 -1
- package/dist/host/commands.js +19 -5
- package/dist/host/commands.js.map +1 -1
- package/dist/host/config.d.ts +77 -1
- package/dist/host/config.d.ts.map +1 -1
- package/dist/host/config.js +37 -9
- package/dist/host/config.js.map +1 -1
- package/dist/host/configSettings.d.ts +486 -0
- package/dist/host/configSettings.d.ts.map +1 -0
- package/dist/host/configSettings.js +113 -0
- package/dist/host/configSettings.js.map +1 -0
- package/dist/host/daemon.d.ts +302 -607
- package/dist/host/daemon.d.ts.map +1 -1
- package/dist/host/daemon.js +2 -1
- package/dist/host/daemon.js.map +1 -1
- package/dist/host/daemonClient.d.ts +300 -605
- package/dist/host/daemonClient.d.ts.map +1 -1
- package/dist/host/daemonClient.js +28 -7
- package/dist/host/daemonClient.js.map +1 -1
- package/dist/host/daemonContract.d.ts +882 -1987
- package/dist/host/daemonContract.d.ts.map +1 -1
- package/dist/host/daemonContract.js +12 -4
- package/dist/host/daemonContract.js.map +1 -1
- package/dist/host/daemonDiagnostics.d.ts.map +1 -1
- package/dist/host/daemonDiagnostics.js +9 -3
- package/dist/host/daemonDiagnostics.js.map +1 -1
- package/dist/host/daemonHandlers.d.ts +2 -1
- package/dist/host/daemonHandlers.d.ts.map +1 -1
- package/dist/host/daemonHandlers.js +20 -7
- package/dist/host/daemonHandlers.js.map +1 -1
- package/dist/host/daemonSnapshots.d.ts.map +1 -1
- package/dist/host/daemonSnapshots.js +21 -1
- package/dist/host/daemonSnapshots.js.map +1 -1
- package/dist/host/dispatchRequests.d.ts +30 -0
- package/dist/host/dispatchRequests.d.ts.map +1 -0
- package/dist/host/dispatchRequests.js +40 -0
- package/dist/host/dispatchRequests.js.map +1 -0
- package/dist/host/follow.js +1 -1
- package/dist/host/follow.js.map +1 -1
- package/dist/host/followState.d.ts.map +1 -1
- package/dist/host/followState.js +5 -4
- package/dist/host/followState.js.map +1 -1
- package/dist/host/github.d.ts +5 -6
- package/dist/host/github.d.ts.map +1 -1
- package/dist/host/github.js +3 -4
- package/dist/host/github.js.map +1 -1
- package/dist/host/githubApi.d.ts +15 -0
- package/dist/host/githubApi.d.ts.map +1 -0
- package/dist/host/githubApi.js +117 -0
- package/dist/host/githubApi.js.map +1 -0
- package/dist/host/githubChecks.d.ts +13 -3
- package/dist/host/githubChecks.d.ts.map +1 -1
- package/dist/host/githubChecks.js +5 -5
- package/dist/host/githubChecks.js.map +1 -1
- package/dist/host/githubDelivery.d.ts +13 -1
- package/dist/host/githubDelivery.d.ts.map +1 -1
- package/dist/host/githubDelivery.js +39 -6
- package/dist/host/githubDelivery.js.map +1 -1
- package/dist/host/githubRateLimit.d.ts +23 -0
- package/dist/host/githubRateLimit.d.ts.map +1 -0
- package/dist/host/githubRateLimit.js +70 -0
- package/dist/host/githubRateLimit.js.map +1 -0
- package/dist/host/githubRequiredChecks.d.ts +1 -2
- package/dist/host/githubRequiredChecks.d.ts.map +1 -1
- package/dist/host/githubRequiredChecks.js +2 -2
- package/dist/host/githubRequiredChecks.js.map +1 -1
- package/dist/host/githubResponse.d.ts +10 -0
- package/dist/host/githubResponse.d.ts.map +1 -0
- package/dist/host/githubResponse.js +29 -0
- package/dist/host/githubResponse.js.map +1 -0
- package/dist/host/githubResult.d.ts +3 -2
- package/dist/host/githubResult.d.ts.map +1 -1
- package/dist/host/githubResult.js +9 -12
- package/dist/host/githubResult.js.map +1 -1
- package/dist/host/githubTransport.d.ts +13 -0
- package/dist/host/githubTransport.d.ts.map +1 -0
- package/dist/host/{githubRead.js → githubTransport.js} +9 -10
- package/dist/host/githubTransport.js.map +1 -0
- package/dist/host/integrations.d.ts +6 -0
- package/dist/host/integrations.d.ts.map +1 -0
- package/dist/host/integrations.js +34 -0
- package/dist/host/integrations.js.map +1 -0
- package/dist/host/liveConfig.d.ts +21 -0
- package/dist/host/liveConfig.d.ts.map +1 -0
- package/dist/host/liveConfig.js +74 -0
- package/dist/host/liveConfig.js.map +1 -0
- package/dist/host/providers.d.ts.map +1 -1
- package/dist/host/providers.js +2 -2
- package/dist/host/providers.js.map +1 -1
- package/dist/host/readWaitStates.d.ts.map +1 -1
- package/dist/host/readWaitStates.js +8 -18
- package/dist/host/readWaitStates.js.map +1 -1
- package/dist/host/reassignWork.d.ts +10 -0
- package/dist/host/reassignWork.d.ts.map +1 -1
- package/dist/host/recordDecisionResponse.d.ts +1 -0
- package/dist/host/recordDecisionResponse.d.ts.map +1 -1
- package/dist/host/releaseWork.d.ts +24 -3
- package/dist/host/releaseWork.d.ts.map +1 -1
- package/dist/host/releaseWork.js +11 -7
- package/dist/host/releaseWork.js.map +1 -1
- package/dist/host/renderStatus.d.ts +2 -2
- package/dist/host/renderStatus.d.ts.map +1 -1
- package/dist/host/renderStatus.js +8 -2
- package/dist/host/renderStatus.js.map +1 -1
- package/dist/host/reportBoard.d.ts +1 -1
- package/dist/host/reportBoard.d.ts.map +1 -1
- package/dist/host/reportBoard.js +6 -3
- package/dist/host/reportBoard.js.map +1 -1
- package/dist/host/reportPublication.d.ts +2 -1
- package/dist/host/reportPublication.d.ts.map +1 -1
- package/dist/host/reportPublication.js +5 -2
- package/dist/host/reportPublication.js.map +1 -1
- package/dist/host/runtime.d.ts +8 -6
- package/dist/host/runtime.d.ts.map +1 -1
- package/dist/host/runtime.js +37 -46
- package/dist/host/runtime.js.map +1 -1
- package/dist/host/snapshotStream.d.ts.map +1 -1
- package/dist/host/snapshotStream.js +4 -1
- package/dist/host/snapshotStream.js.map +1 -1
- package/dist/host/status.d.ts +22 -2
- package/dist/host/status.d.ts.map +1 -1
- package/dist/host/status.js +19 -5
- package/dist/host/status.js.map +1 -1
- package/dist/host/statusLayer.d.ts +3 -3
- package/dist/host/statusLayer.d.ts.map +1 -1
- package/dist/host/statusLayer.js +13 -3
- package/dist/host/statusLayer.js.map +1 -1
- package/dist/host/waitOutcome.d.ts.map +1 -1
- package/dist/host/waitOutcome.js +5 -2
- package/dist/host/waitOutcome.js.map +1 -1
- package/dist/model.d.ts +39 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +5 -0
- package/dist/model.js.map +1 -1
- package/dist/operations.d.ts +270 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/packageInfo.d.ts +1 -1
- package/dist/packageInfo.js +1 -1
- package/dist/policy.d.ts +7 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +2 -0
- package/dist/policy.js.map +1 -1
- package/dist/providers/codexCloud.d.ts +2 -0
- package/dist/providers/codexCloud.d.ts.map +1 -1
- package/dist/providers/codexCloud.js +2 -1
- package/dist/providers/codexCloud.js.map +1 -1
- package/dist/qualifyDependencies.d.ts +10 -0
- package/dist/qualifyDependencies.d.ts.map +1 -1
- package/dist/scheduling/admission.d.ts +1635 -0
- package/dist/scheduling/admission.d.ts.map +1 -0
- package/dist/scheduling/admission.js +11 -0
- package/dist/scheduling/admission.js.map +1 -0
- package/dist/scheduling/areas.d.ts +30 -0
- package/dist/scheduling/areas.d.ts.map +1 -1
- package/dist/scheduling/backPressure.d.ts +1637 -0
- package/dist/scheduling/backPressure.d.ts.map +1 -0
- package/dist/scheduling/backPressure.js +44 -0
- package/dist/scheduling/backPressure.js.map +1 -0
- package/dist/storage/FleetStoreOptions.d.ts +2 -2
- package/dist/storage/FleetStoreOptions.d.ts.map +1 -1
- package/dist/storage/addShapeColumns.d.ts +5 -0
- package/dist/storage/addShapeColumns.d.ts.map +1 -0
- package/dist/storage/addShapeColumns.js +24 -0
- package/dist/storage/addShapeColumns.js.map +1 -0
- package/dist/storage/dispatchStore.d.ts +47 -0
- package/dist/storage/dispatchStore.d.ts.map +1 -0
- package/dist/storage/dispatchStore.js +89 -0
- package/dist/storage/dispatchStore.js.map +1 -0
- package/dist/storage/errors.d.ts +2 -1
- package/dist/storage/errors.d.ts.map +1 -1
- package/dist/storage/errors.js +2 -1
- package/dist/storage/errors.js.map +1 -1
- package/dist/storage/fleetMigrations.d.ts +1 -1
- package/dist/storage/fleetMigrations.d.ts.map +1 -1
- package/dist/storage/fleetMigrations.js +24 -17
- package/dist/storage/fleetMigrations.js.map +1 -1
- package/dist/storage/listedRecords.d.ts +9 -0
- package/dist/storage/listedRecords.d.ts.map +1 -0
- package/dist/storage/listedRecords.js +40 -0
- package/dist/storage/listedRecords.js.map +1 -0
- package/dist/storage/makePostgresFleetStore.d.ts +2 -6
- package/dist/storage/makePostgresFleetStore.d.ts.map +1 -1
- package/dist/storage/makePostgresFleetStore.js +4 -10
- package/dist/storage/makePostgresFleetStore.js.map +1 -1
- package/dist/storage/model.d.ts +9 -0
- package/dist/storage/model.d.ts.map +1 -1
- package/dist/storage/model.js.map +1 -1
- package/dist/storage/shapeV14.d.ts +808 -0
- package/dist/storage/shapeV14.d.ts.map +1 -0
- package/dist/storage/shapeV14.js +18 -0
- package/dist/storage/shapeV14.js.map +1 -0
- package/package.json +6 -6
- package/src/DispatchRequest.ts +13 -0
- package/src/attention.ts +5 -0
- package/src/board/fleetField.ts +5 -0
- package/src/board/markdown.ts +7 -2
- package/src/cli.ts +24 -10
- package/src/engine/admissionOrder.ts +25 -0
- package/src/engine/dispatchCommand.ts +29 -5
- package/src/engine/failureContext.ts +1 -0
- package/src/engine/failureMessage.ts +12 -1
- package/src/engine/localOnlyCriteria.ts +6 -1
- package/src/engine/makeLaunch.ts +4 -17
- package/src/engine/makeState.ts +30 -26
- package/src/engine/mergeReadiness.ts +6 -5
- package/src/engine/obtainReview.ts +2 -1
- package/src/engine/prepareCommand.ts +1 -1
- package/src/engine/readOrContinueReview.ts +2 -1
- package/src/engine/readRetry.ts +23 -1
- package/src/engine/reconcileAttempt.ts +2 -1
- package/src/engine/recordResponseCommand.ts +38 -0
- package/src/engine/recoverStall.ts +1 -1
- package/src/engine/repository.ts +17 -16
- package/src/engine/saveLaunch.ts +45 -0
- package/src/engine/scheduler.ts +4 -6
- package/src/engine/views.ts +5 -0
- package/src/fleet.ts +4 -0
- package/src/host/actionCommands.ts +22 -7
- package/src/host/admission.ts +3 -3
- package/src/host/answerWork.ts +28 -27
- package/src/host/commands.ts +29 -8
- package/src/host/config.ts +46 -9
- package/src/host/configSettings.ts +124 -0
- package/src/host/daemon.ts +2 -1
- package/src/host/daemonClient.ts +42 -7
- package/src/host/daemonContract.ts +12 -4
- package/src/host/daemonDiagnostics.ts +11 -3
- package/src/host/daemonHandlers.ts +24 -7
- package/src/host/daemonSnapshots.ts +24 -1
- package/src/host/dispatchRequests.ts +53 -0
- package/src/host/follow.ts +1 -1
- package/src/host/followState.ts +6 -6
- package/src/host/github.ts +8 -6
- package/src/host/githubApi.ts +142 -0
- package/src/host/githubChecks.ts +6 -5
- package/src/host/githubDelivery.ts +47 -10
- package/src/host/githubRateLimit.ts +91 -0
- package/src/host/githubRequiredChecks.ts +2 -2
- package/src/host/githubResponse.ts +37 -0
- package/src/host/githubResult.ts +15 -17
- package/src/host/githubTransport.ts +31 -0
- package/src/host/integrations.ts +40 -0
- package/src/host/liveConfig.ts +134 -0
- package/src/host/providers.ts +4 -2
- package/src/host/readWaitStates.ts +8 -20
- package/src/host/releaseWork.ts +12 -9
- package/src/host/renderStatus.ts +10 -3
- package/src/host/reportBoard.ts +6 -3
- package/src/host/reportPublication.ts +6 -4
- package/src/host/runtime.ts +48 -56
- package/src/host/snapshotStream.ts +6 -1
- package/src/host/status.ts +24 -7
- package/src/host/statusLayer.ts +21 -5
- package/src/host/waitOutcome.ts +5 -2
- package/src/model.ts +6 -0
- package/src/packageInfo.ts +1 -1
- package/src/policy.ts +7 -0
- package/src/providers/codexCloud.ts +2 -1
- package/src/scheduling/admission.ts +19 -0
- package/src/scheduling/backPressure.ts +55 -0
- package/src/storage/FleetStoreOptions.ts +7 -2
- package/src/storage/addShapeColumns.ts +24 -0
- package/src/storage/dispatchStore.ts +104 -0
- package/src/storage/errors.ts +2 -1
- package/src/storage/fleetMigrations.ts +24 -17
- package/src/storage/listedRecords.ts +53 -0
- package/src/storage/makePostgresFleetStore.ts +4 -10
- package/src/storage/model.ts +7 -0
- package/src/storage/shapeV14.ts +19 -0
- package/dist/host/githubRead.d.ts +0 -5
- package/dist/host/githubRead.d.ts.map +0 -1
- package/dist/host/githubRead.js.map +0 -1
- package/dist/host/recordPayloads.d.ts +0 -4
- package/dist/host/recordPayloads.d.ts.map +0 -1
- package/dist/host/recordPayloads.js +0 -21
- package/dist/host/recordPayloads.js.map +0 -1
- package/dist/storage/migrateFleet.d.ts +0 -3
- package/dist/storage/migrateFleet.d.ts.map +0 -1
- package/dist/storage/migrateFleet.js +0 -11
- package/dist/storage/migrateFleet.js.map +0 -1
- package/src/host/githubRead.ts +0 -20
- package/src/host/recordPayloads.ts +0 -24
- package/src/storage/migrateFleet.ts +0 -11
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Equal, Schema } from "effect";
|
|
2
|
+
import { type FleetHost, HostConfig } from "#host/config.ts";
|
|
3
|
+
import type { AppliedConfig } from "#host/liveConfig.ts";
|
|
4
|
+
import { FleetViews } from "#model.ts";
|
|
5
|
+
|
|
6
|
+
// A running daemon applies these settings from an edited file; every other setting waits for a restart.
|
|
7
|
+
export const RELOADABLE_SETTINGS = [
|
|
8
|
+
"areaMergeWaitMs",
|
|
9
|
+
"backlog",
|
|
10
|
+
"concurrency",
|
|
11
|
+
"intervalSeconds",
|
|
12
|
+
"launch",
|
|
13
|
+
"openPullRequests",
|
|
14
|
+
"quota",
|
|
15
|
+
"stallMinutes",
|
|
16
|
+
"worker.reviewerGuidance",
|
|
17
|
+
"worker.workerGuidance",
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
export const ConfigStatus = Schema.Struct({
|
|
21
|
+
file: Schema.optional(Schema.String),
|
|
22
|
+
loadedAt: Schema.Number,
|
|
23
|
+
rejected: Schema.optional(Schema.Struct({ field: Schema.optional(Schema.String), message: Schema.String })),
|
|
24
|
+
restartRequired: Schema.Array(Schema.String),
|
|
25
|
+
settings: HostConfig,
|
|
26
|
+
});
|
|
27
|
+
export type ConfigStatus = typeof ConfigStatus.Type;
|
|
28
|
+
|
|
29
|
+
export const DaemonStatus = Schema.Struct({ ...FleetViews.fields, config: Schema.optional(ConfigStatus) });
|
|
30
|
+
|
|
31
|
+
function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function setting(config: HostConfig, name: string): unknown {
|
|
36
|
+
return name.split(".").reduce<unknown>((value, key) => (isRecord(value) ? value[key] : undefined), config);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function appliedSettings(host: FleetHost): HostConfig {
|
|
40
|
+
return {
|
|
41
|
+
areaMergeWaitMs: host.areaMergeWaitMs,
|
|
42
|
+
backlog: host.backlog,
|
|
43
|
+
...(host.cloud === undefined ? {} : { cloud: host.cloud }),
|
|
44
|
+
concurrency: host.concurrency,
|
|
45
|
+
database: { maxConnections: host.maxConnections },
|
|
46
|
+
intervalSeconds: host.interval,
|
|
47
|
+
launch: host.launch,
|
|
48
|
+
...(host.local === undefined ? {} : { local: host.local }),
|
|
49
|
+
namespace: host.namespace,
|
|
50
|
+
...(host.openPullRequests === undefined ? {} : { openPullRequests: host.openPullRequests }),
|
|
51
|
+
provider: host.provider,
|
|
52
|
+
quota: host.quota,
|
|
53
|
+
repository: host.repository,
|
|
54
|
+
...(host.result === undefined ? {} : { result: host.result }),
|
|
55
|
+
stallMinutes: host.stallMinutes,
|
|
56
|
+
...(host.worker === undefined ? {} : { worker: host.worker }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function write(target: Record<string, unknown>, [key, ...rest]: readonly string[], value: unknown) {
|
|
61
|
+
if (key === undefined) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (rest.length > 0) {
|
|
65
|
+
const child = target[key];
|
|
66
|
+
if (isRecord(child)) {
|
|
67
|
+
const copy = { ...child };
|
|
68
|
+
write(copy, rest, value);
|
|
69
|
+
target[key] = copy;
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (value === undefined) {
|
|
74
|
+
delete target[key];
|
|
75
|
+
} else {
|
|
76
|
+
target[key] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// A reloadable setting nested under a section that the running config lacks stays unapplied until a restart adds the section.
|
|
81
|
+
export function withReloadable(applied: HostConfig, next: HostConfig): Readonly<Record<string, unknown>> {
|
|
82
|
+
const merged: Record<string, unknown> = { ...applied };
|
|
83
|
+
for (const name of RELOADABLE_SETTINGS) {
|
|
84
|
+
write(merged, name.split("."), setting(next, name));
|
|
85
|
+
}
|
|
86
|
+
return merged;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function changedSettings(left: unknown, right: unknown, prefix = ""): readonly string[] {
|
|
90
|
+
if (isRecord(left) && isRecord(right)) {
|
|
91
|
+
return [...new Set([...Object.keys(left), ...Object.keys(right)])]
|
|
92
|
+
.sort((first, second) => first.localeCompare(second))
|
|
93
|
+
.flatMap((key) => changedSettings(left[key], right[key], prefix === "" ? key : `${prefix}.${key}`));
|
|
94
|
+
}
|
|
95
|
+
return Equal.equals(left, right) ? [] : [prefix];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function configStatus({ host, loadedAt, rejected, restartRequired }: AppliedConfig): ConfigStatus {
|
|
99
|
+
return {
|
|
100
|
+
...(host.configFile === undefined ? {} : { file: host.configFile }),
|
|
101
|
+
loadedAt,
|
|
102
|
+
...(rejected === undefined
|
|
103
|
+
? {}
|
|
104
|
+
: { rejected: { ...(rejected.field === undefined ? {} : { field: rejected.field }), message: rejected.message } }),
|
|
105
|
+
restartRequired,
|
|
106
|
+
settings: appliedSettings(host),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function renderConfig(config: ConfigStatus): string {
|
|
111
|
+
const applying = RELOADABLE_SETTINGS.flatMap((name) => {
|
|
112
|
+
const value = setting(config.settings, name);
|
|
113
|
+
return value === undefined ? [] : [`${name} ${typeof value === "object" ? JSON.stringify(value) : String(value)}`];
|
|
114
|
+
});
|
|
115
|
+
const rejected = config.rejected;
|
|
116
|
+
return [
|
|
117
|
+
`Config loaded ${new Date(config.loadedAt).toISOString()}${config.file === undefined ? "" : ` from ${config.file}`}`,
|
|
118
|
+
` provider ${config.settings.provider}, ${applying.join(", ")}`,
|
|
119
|
+
...(config.restartRequired.length === 0 ? [] : [` Restart to apply: ${config.restartRequired.join(", ")}`]),
|
|
120
|
+
...(rejected === undefined
|
|
121
|
+
? []
|
|
122
|
+
: [` Rejected edit${rejected.field === undefined ? "" : ` of ${rejected.field}`}, still applying the config above: ${rejected.message}`]),
|
|
123
|
+
].join("\n");
|
|
124
|
+
}
|
package/src/host/daemon.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type HostParts, hostLayer, hostScheduler, productionParts } from "#host
|
|
|
5
5
|
import type { FleetHost } from "#host/config.ts";
|
|
6
6
|
import { DaemonContract, RPC_PATH } from "#host/daemonContract.ts";
|
|
7
7
|
import { daemonHandlers } from "#host/daemonHandlers.ts";
|
|
8
|
+
import { watchedConfig } from "#host/liveConfig.ts";
|
|
8
9
|
import { fleetHandlers } from "#operations.ts";
|
|
9
10
|
|
|
10
11
|
const { DAEMON_PORT: DEFAULT_DAEMON_PORT } = diagnosticsApi;
|
|
@@ -21,7 +22,7 @@ export function daemonHostOptions(host: FleetHost, parts: HostParts = production
|
|
|
21
22
|
handlers: Layer.merge(fleetHandlers, Layer.unwrap(daemonHandlers(host))),
|
|
22
23
|
policy: "loopback" as const,
|
|
23
24
|
rpcPath: RPC_PATH,
|
|
24
|
-
services: hostLayer(host, parts),
|
|
25
|
+
services: hostLayer(host, parts, watchedConfig(host)),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
|
package/src/host/daemonClient.ts
CHANGED
|
@@ -19,18 +19,53 @@ export function daemonClient(port: number = DAEMON_PORT) {
|
|
|
19
19
|
);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function daemonRequest<A, E, R>(
|
|
23
|
-
|
|
22
|
+
export function daemonRequest<A, E, R>(
|
|
23
|
+
port: number,
|
|
24
|
+
use: (client: Effect.Success<ReturnType<typeof daemonClient>>) => Effect.Effect<A, E, R>,
|
|
25
|
+
request = "request",
|
|
26
|
+
timeoutMs = 5000,
|
|
27
|
+
) {
|
|
28
|
+
return Effect.scoped(daemonClient(port).pipe(Effect.flatMap(use))).pipe(
|
|
29
|
+
Effect.timeout(timeoutMs),
|
|
30
|
+
Effect.mapError((error) => daemonError(error, port, request)),
|
|
31
|
+
);
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
export function daemonError(error: unknown, port: number) {
|
|
27
|
-
if (isFleetError(error)) {
|
|
34
|
+
export function daemonError(error: unknown, port: number, request = "request") {
|
|
35
|
+
if (isFleetError(error) && (error._tag !== "ServiceUnavailable" || request === "request")) {
|
|
28
36
|
return error;
|
|
29
37
|
}
|
|
30
|
-
const
|
|
38
|
+
const refused = error instanceof RpcClientError && connectionRefused(error.reason);
|
|
39
|
+
const timeout = requestTimedOut(error);
|
|
40
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
41
|
+
const recording = request.startsWith("answer ") ? "; answer recording is unconfirmed; check status or watch before retrying" : "";
|
|
31
42
|
return new ServiceUnavailable({
|
|
32
|
-
message:
|
|
43
|
+
message: refused
|
|
33
44
|
? `No Fleet daemon is running at 127.0.0.1:${port}; start work-fleet serve <board-dir> --port ${port}`
|
|
34
|
-
: `Fleet daemon
|
|
45
|
+
: `Fleet daemon ${request} ${timeout ? "timed out" : "failed"} at 127.0.0.1:${port}: ${detail}${recording}`,
|
|
35
46
|
});
|
|
36
47
|
}
|
|
48
|
+
|
|
49
|
+
function requestTimedOut(value: unknown, depth = 0): boolean {
|
|
50
|
+
if (typeof value !== "object" || value === null || depth > 8) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return (
|
|
54
|
+
("_tag" in value && value._tag === "TimeoutError")
|
|
55
|
+
|| ("name" in value && value.name === "TimeoutError")
|
|
56
|
+
|| ("code" in value && ["ETIMEDOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_BODY_TIMEOUT", "UND_ERR_CONNECT_TIMEOUT"].includes(String(value.code)))
|
|
57
|
+
|| ("cause" in value && requestTimedOut(value.cause, depth + 1))
|
|
58
|
+
|| ("reason" in value && requestTimedOut(value.reason, depth + 1))
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function connectionRefused(value: unknown, depth = 0): boolean {
|
|
63
|
+
if (typeof value !== "object" || value === null || depth > 8) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return (
|
|
67
|
+
("code" in value && value.code === "ECONNREFUSED")
|
|
68
|
+
|| ("cause" in value && connectionRefused(value.cause, depth + 1))
|
|
69
|
+
|| ("reason" in value && connectionRefused(value.reason, depth + 1))
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -3,8 +3,10 @@ import { contract } from "@shivaedev/effect-contract/contract.ts";
|
|
|
3
3
|
import { command, query, stream } from "@shivaedev/effect-contract/operation.ts";
|
|
4
4
|
import { RequestTracing } from "@shivaedev/platform/rpc/middleware.ts";
|
|
5
5
|
import { diagnostics as diagnosticsApi } from "@shivaedev/work-board/integration/diagnostics.ts";
|
|
6
|
+
import { DispatchRequest } from "#DispatchRequest.ts";
|
|
6
7
|
import { fleetRejections } from "#errors.ts";
|
|
7
|
-
import {
|
|
8
|
+
import { DaemonStatus } from "#host/configSettings.ts";
|
|
9
|
+
import { FleetRecord } from "#model.ts";
|
|
8
10
|
import { FleetContract, fleetRecords } from "#operations.ts";
|
|
9
11
|
|
|
10
12
|
const { DAEMON_RPC_PATH, DiagnosticsContract } = diagnosticsApi;
|
|
@@ -31,17 +33,23 @@ export const ClientContract = contract("daemon", {
|
|
|
31
33
|
}),
|
|
32
34
|
command("dispatch", {
|
|
33
35
|
invalidates: ({ workId }) => [fleetRecords.list, fleetRecords.item(workId)],
|
|
34
|
-
payload: { workId: Schema.String },
|
|
36
|
+
payload: { ignoreAreas: Schema.optional(Schema.Boolean), now: Schema.optional(Schema.Boolean), workId: Schema.String },
|
|
35
37
|
rejections: fleetRejections,
|
|
36
|
-
success:
|
|
38
|
+
success: DispatchRequest,
|
|
37
39
|
}),
|
|
38
40
|
],
|
|
39
41
|
queries: [
|
|
42
|
+
query("dispatch-status", {
|
|
43
|
+
payload: { workId: Schema.String },
|
|
44
|
+
reads: ({ workId }) => [fleetRecords.item(workId)],
|
|
45
|
+
rejections: fleetRejections,
|
|
46
|
+
success: Schema.NullOr(DispatchRequest),
|
|
47
|
+
}),
|
|
40
48
|
query("status", {
|
|
41
49
|
payload: { workId: Schema.optional(Schema.String) },
|
|
42
50
|
reads: ({ workId }) => [workId === undefined ? fleetRecords.list : fleetRecords.item(workId)],
|
|
43
51
|
rejections: fleetRejections,
|
|
44
|
-
success:
|
|
52
|
+
success: DaemonStatus,
|
|
45
53
|
}),
|
|
46
54
|
],
|
|
47
55
|
streams: [
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
+
import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
2
3
|
import { diagnostics as diagnosticsApi } from "@shivaedev/work-board/integration/diagnostics.ts";
|
|
4
|
+
import { failureMessage } from "#engine/failureMessage.ts";
|
|
3
5
|
import { HealthSnapshot } from "#engine/health.ts";
|
|
4
6
|
import { fleetItems } from "#host/fleetItems.ts";
|
|
5
7
|
|
|
@@ -7,14 +9,20 @@ const { codexLimits, DiagnosticsContract, DiagnosticsSources, diagnostics, githu
|
|
|
7
9
|
|
|
8
10
|
export const daemonDiagnostics = Effect.gen(function* () {
|
|
9
11
|
const provided = yield* Effect.serviceOption(DiagnosticsSources);
|
|
12
|
+
const fleet = yield* HealthSnapshot;
|
|
10
13
|
const sources = {
|
|
11
14
|
codex: provided._tag === "Some" ? provided.value.codex : codexLimits(),
|
|
12
|
-
fleet
|
|
13
|
-
github:
|
|
15
|
+
fleet,
|
|
16
|
+
github: githubLimits(fleet),
|
|
14
17
|
};
|
|
15
18
|
const read = yield* diagnostics(sources);
|
|
16
19
|
const items = yield* fleetItems;
|
|
17
20
|
return DiagnosticsContract.toLayer(
|
|
18
|
-
Effect.succeed(
|
|
21
|
+
Effect.succeed(
|
|
22
|
+
DiagnosticsContract.of({
|
|
23
|
+
"daemon.diagnostics": () => read,
|
|
24
|
+
"daemon.fleetItems": () => items.pipe(Effect.mapError((error) => new ServiceUnavailable({ message: failureMessage(error) }))),
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
19
27
|
);
|
|
20
28
|
});
|
|
@@ -1,31 +1,48 @@
|
|
|
1
|
-
import { Effect, Layer } from "effect";
|
|
1
|
+
import { Effect, Fiber, Layer } from "effect";
|
|
2
2
|
import { exposeFailure } from "#exposeFailure.ts";
|
|
3
|
-
import { dispatchApproved } from "#host/admission.ts";
|
|
4
3
|
import { answerWork } from "#host/answerWork.ts";
|
|
5
4
|
import type { FleetHost } from "#host/config.ts";
|
|
5
|
+
import { configStatus } from "#host/configSettings.ts";
|
|
6
6
|
import { ClientContract } from "#host/daemonContract.ts";
|
|
7
7
|
import { daemonDiagnostics } from "#host/daemonDiagnostics.ts";
|
|
8
8
|
import { daemonSnapshots } from "#host/daemonSnapshots.ts";
|
|
9
|
+
import { dispatchRequests } from "#host/dispatchRequests.ts";
|
|
10
|
+
import { LiveConfig } from "#host/liveConfig.ts";
|
|
9
11
|
import { nameStoredThreads } from "#host/nameThreads.ts";
|
|
10
|
-
import { releaseItems } from "#host/releaseWork.ts";
|
|
12
|
+
import { decisionResponses, releaseItems } from "#host/releaseWork.ts";
|
|
11
13
|
import { fleetStatus } from "#host/status.ts";
|
|
12
14
|
|
|
13
15
|
export const daemonHandlers = Effect.fn("FleetDaemon.daemonHandlers")(function* (host: FleetHost) {
|
|
16
|
+
const scope = yield* Effect.scope;
|
|
14
17
|
const follow = yield* daemonSnapshots(host);
|
|
15
18
|
const diagnostics = yield* daemonDiagnostics;
|
|
19
|
+
const dispatch = yield* dispatchRequests(host);
|
|
20
|
+
const live = yield* LiveConfig;
|
|
21
|
+
const status = Effect.zipWith(fleetStatus(host), live.current, (views, applied) => ({ ...views, config: configStatus(applied) }));
|
|
22
|
+
// The server already accepts connections while handlers build, so the Board watcher starts beside them instead of delaying them.
|
|
23
|
+
const responses = yield* Layer.buildWithScope(decisionResponses(host), scope).pipe(Effect.forkIn(scope));
|
|
24
|
+
// A client that stops waiting closes its request; the Board response it asked for must still be recorded and applied.
|
|
25
|
+
function settle<A, E, R>(effect: Effect.Effect<A, E, R>) {
|
|
26
|
+
return Fiber.join(responses).pipe(
|
|
27
|
+
Effect.flatMap((context) => effect.pipe(Effect.provide(context))),
|
|
28
|
+
Effect.forkIn(scope),
|
|
29
|
+
Effect.flatMap(Fiber.join),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
16
32
|
return Layer.merge(
|
|
17
33
|
diagnostics,
|
|
18
34
|
ClientContract.toLayer(
|
|
19
35
|
Effect.succeed(
|
|
20
36
|
ClientContract.of({
|
|
21
37
|
"daemon.answer": ({ workId, text, release }) =>
|
|
22
|
-
exposeFailure(answerWork(host, workId, text, release).pipe(Effect.map(({ value }) => value))),
|
|
23
|
-
"daemon.dispatch": ({ workId }) => exposeFailure(
|
|
38
|
+
exposeFailure(settle(answerWork(host, workId, text, release, true)).pipe(Effect.map(({ value }) => value))),
|
|
39
|
+
"daemon.dispatch": ({ workId, now, ignoreAreas }) => exposeFailure(dispatch.accept(workId, { ignoreAreas, now })),
|
|
40
|
+
"daemon.dispatch-status": ({ workId }) => exposeFailure(dispatch.get(workId)),
|
|
24
41
|
"daemon.follow": ({ workIds, follow: continuous }) => follow(workIds, continuous),
|
|
25
42
|
"daemon.name-threads": () => exposeFailure(nameStoredThreads(host)),
|
|
26
43
|
"daemon.release": ({ workIds }) =>
|
|
27
|
-
exposeFailure(releaseItems(host, workIds).pipe(Effect.map((records) => records.map(({ value }) => value)))),
|
|
28
|
-
"daemon.status": ({ workId }) => exposeFailure(fleetStatus(host, workId)),
|
|
44
|
+
exposeFailure(settle(releaseItems(host, workIds)).pipe(Effect.map((records) => records.map(({ value }) => value)))),
|
|
45
|
+
"daemon.status": ({ workId }) => exposeFailure(workId === undefined ? status : fleetStatus(host, workId)),
|
|
29
46
|
}),
|
|
30
47
|
),
|
|
31
48
|
),
|
|
@@ -2,15 +2,21 @@ import { Effect, Schedule, Stream } from "effect";
|
|
|
2
2
|
import { SqlClient } from "effect/sql";
|
|
3
3
|
import { AppChanges } from "@shivaedev/platform/appHost.ts";
|
|
4
4
|
import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
5
|
+
import type { DispatchRequest } from "#DispatchRequest.ts";
|
|
5
6
|
import { fleetRecords } from "#fleetRecords.ts";
|
|
6
7
|
import type { FleetHost } from "#host/config.ts";
|
|
7
8
|
import { readWaitStates } from "#host/readWaitStates.ts";
|
|
8
9
|
import { snapshotStream } from "#host/snapshotStream.ts";
|
|
10
|
+
import type { State } from "#host/waitOutcome.ts";
|
|
11
|
+
import { dispatchStore } from "#storage/dispatchStore.ts";
|
|
9
12
|
|
|
10
13
|
export const daemonSnapshots = Effect.fn("FleetDaemon.daemonSnapshots")(function* (host: FleetHost) {
|
|
11
14
|
const sql = yield* SqlClient.SqlClient;
|
|
12
15
|
const changes = yield* AppChanges;
|
|
13
|
-
const
|
|
16
|
+
const dispatches = yield* dispatchStore(host.namespace);
|
|
17
|
+
const read = Effect.gen(function* () {
|
|
18
|
+
return dispatchStates(yield* readWaitStates(host), yield* dispatches.list());
|
|
19
|
+
}).pipe(
|
|
14
20
|
Effect.provideService(SqlClient.SqlClient, sql),
|
|
15
21
|
Effect.retry({ schedule: Schedule.spaced("500 millis"), times: 19 }),
|
|
16
22
|
Effect.mapError(() => new ServiceUnavailable({ message: "Fleet daemon could not read work states after 20 consecutive attempts" })),
|
|
@@ -22,3 +28,20 @@ export const daemonSnapshots = Effect.fn("FleetDaemon.daemonSnapshots")(function
|
|
|
22
28
|
);
|
|
23
29
|
return (ids: readonly string[], continuous = false) => snapshotStream(snapshots, ids, continuous);
|
|
24
30
|
});
|
|
31
|
+
|
|
32
|
+
function dispatchStates(states: readonly (typeof State.Type)[], requests: readonly DispatchRequest[]) {
|
|
33
|
+
const result = new Map<string, typeof State.Type>(states.map((state) => [state.workId, state]));
|
|
34
|
+
for (const request of requests) {
|
|
35
|
+
const state = result.get(request.workId);
|
|
36
|
+
if (request.status === "completed" || (request.status === "failed" && state !== undefined && state.stage !== "prepared")) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
result.set(request.workId, {
|
|
40
|
+
attentionOwner: request.status === "pending" ? "automation" : "orchestrator",
|
|
41
|
+
blocker: request.status === "pending" ? "Dispatch recorded; preparing in the background" : `Dispatch failed: ${request.error}`,
|
|
42
|
+
stage: request.status === "pending" ? "dispatching" : "needs-human",
|
|
43
|
+
workId: request.workId,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return [...result.values()].sort((left, right) => left.workId.localeCompare(right.workId));
|
|
47
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Cause, Effect } from "effect";
|
|
2
|
+
import { BadRequest, Forbidden } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
3
|
+
import type { DispatchRequest } from "#DispatchRequest.ts";
|
|
4
|
+
import { failureMessage } from "#engine/failureMessage.ts";
|
|
5
|
+
import { dispatchApproved } from "#host/admission.ts";
|
|
6
|
+
import type { FleetHost } from "#host/config.ts";
|
|
7
|
+
import { BoardGateway } from "#policy.ts";
|
|
8
|
+
import { dispatchStore } from "#storage/dispatchStore.ts";
|
|
9
|
+
|
|
10
|
+
export const dispatchRequests = Effect.fn("FleetDaemon.dispatchRequests")(function* (host: FleetHost) {
|
|
11
|
+
const scope = yield* Effect.scope;
|
|
12
|
+
const board = yield* BoardGateway;
|
|
13
|
+
const store = yield* dispatchStore(host.namespace);
|
|
14
|
+
const running = new Set<string>();
|
|
15
|
+
const start = Effect.fn("FleetDaemon.start")(function* (request: DispatchRequest) {
|
|
16
|
+
if (running.has(request.id)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
running.add(request.id);
|
|
20
|
+
yield* dispatchApproved(host, request.workId, request.now ? { ignoreAreas: request.ignoreAreas === true } : undefined).pipe(
|
|
21
|
+
Effect.andThen(store.finish(request)),
|
|
22
|
+
Effect.catchCause((cause) =>
|
|
23
|
+
Cause.hasInterrupts(cause) ? Effect.failCause(cause) : store.finish(request, failureMessage(Cause.squash(cause))),
|
|
24
|
+
),
|
|
25
|
+
Effect.catchCause((cause) => (Cause.hasInterrupts(cause) ? Effect.void : Effect.logError(cause))),
|
|
26
|
+
Effect.ensuring(Effect.sync(() => running.delete(request.id))),
|
|
27
|
+
Effect.forkIn(scope),
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
for (const request of yield* store.list()) {
|
|
31
|
+
if (request.status === "pending") {
|
|
32
|
+
yield* start(request);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
accept: Effect.fn("FleetDaemon.accept")(function* (
|
|
37
|
+
workId: string,
|
|
38
|
+
options: { readonly ignoreAreas?: boolean | undefined; readonly now?: boolean | undefined } = {},
|
|
39
|
+
) {
|
|
40
|
+
if (options.ignoreAreas === true && options.now !== true) {
|
|
41
|
+
return yield* new BadRequest({ message: "Ignoring area claims needs a dispatch now request" });
|
|
42
|
+
}
|
|
43
|
+
const work = yield* board.get(workId);
|
|
44
|
+
if (!work.approved) {
|
|
45
|
+
return yield* new Forbidden({ message: `${workId} needs "fleet: approved" in its Board frontmatter` });
|
|
46
|
+
}
|
|
47
|
+
const request = yield* store.accept(workId, options.now === true ? { ignoreAreas: options.ignoreAreas === true } : undefined);
|
|
48
|
+
yield* start(request);
|
|
49
|
+
return request;
|
|
50
|
+
}),
|
|
51
|
+
get: store.get,
|
|
52
|
+
};
|
|
53
|
+
});
|
package/src/host/follow.ts
CHANGED
|
@@ -34,7 +34,7 @@ export function follow(options: FollowOptions, workIds: readonly string[]) {
|
|
|
34
34
|
Stream.catch((error) => {
|
|
35
35
|
failures += 1;
|
|
36
36
|
if (!connected || isFleetError(error) || failures >= 20) {
|
|
37
|
-
return Stream.fail(daemonError(error, port));
|
|
37
|
+
return Stream.fail(daemonError(error, port, "follow"));
|
|
38
38
|
}
|
|
39
39
|
return Stream.unwrap(Effect.sleep(options.intervalMs).pipe(Effect.as(connect())));
|
|
40
40
|
}),
|
package/src/host/followState.ts
CHANGED
|
@@ -20,9 +20,7 @@ export function decodeFollowState(value: unknown): FollowState {
|
|
|
20
20
|
|
|
21
21
|
export function unrecognizedState(state: FollowState): boolean {
|
|
22
22
|
return (
|
|
23
|
-
state.undecoded !== undefined
|
|
24
|
-
|| !Schema.is(FleetRecord.fields.stage)(state.stage)
|
|
25
|
-
|| (state.decision?.code !== undefined && !Schema.is(DecisionCode)(state.decision.code))
|
|
23
|
+
state.undecoded !== undefined || !knownStage(state.stage) || (state.decision?.code !== undefined && !Schema.is(DecisionCode)(state.decision.code))
|
|
26
24
|
);
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -35,7 +33,9 @@ export function followOutcome(state: FollowState): WaitOutcome | undefined {
|
|
|
35
33
|
if (code !== undefined && !Schema.is(DecisionCode)(code)) {
|
|
36
34
|
return { code: outcome?.code ?? WAIT_EXIT.error, summary: `${outcome?.summary ?? `${state.workId} ${state.stage}`}: ${code}` };
|
|
37
35
|
}
|
|
38
|
-
return (
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
return outcome ?? (knownStage(state.stage) ? undefined : { code: WAIT_EXIT.error, summary: `${state.workId} ${state.stage}` });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function knownStage(stage: string) {
|
|
40
|
+
return stage === "dispatching" || Schema.is(FleetRecord.fields.stage)(stage);
|
|
41
41
|
}
|
package/src/host/github.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
|
-
import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
3
2
|
import type { FleetError } from "#errors.ts";
|
|
3
|
+
import { githubFailure } from "#host/githubRateLimit.ts";
|
|
4
4
|
|
|
5
5
|
const Sha = Schema.String.check(Schema.isPattern(/^[a-f\d]{40}$/u));
|
|
6
6
|
const Positive = Schema.Int.check(Schema.isGreaterThan(0));
|
|
@@ -20,13 +20,15 @@ export const FilePages = Schema.Array(
|
|
|
20
20
|
),
|
|
21
21
|
),
|
|
22
22
|
);
|
|
23
|
-
export const Repository = Schema.Struct({ nameWithOwner: Schema.String });
|
|
24
23
|
|
|
25
|
-
export
|
|
24
|
+
export interface GithubRead {
|
|
25
|
+
readonly get: (endpoint: string) => Effect.Effect<string, FleetError>;
|
|
26
|
+
readonly pages: (endpoint: string) => Effect.Effect<string, FleetError>;
|
|
27
|
+
}
|
|
26
28
|
|
|
27
|
-
export function githubJson<TSchema extends Schema.Top>(schema: TSchema, read:
|
|
28
|
-
return read
|
|
29
|
+
export function githubJson<TSchema extends Schema.Top>(schema: TSchema, read: Effect.Effect<string, FleetError>) {
|
|
30
|
+
return read.pipe(
|
|
29
31
|
Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(schema))),
|
|
30
|
-
Effect.mapError((
|
|
32
|
+
Effect.mapError(githubFailure("GitHub observation failed or returned invalid data")),
|
|
31
33
|
);
|
|
32
34
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Clock, Effect, Layer } from "effect";
|
|
2
|
+
import { defineService } from "@shivaedev/effect-service/define-service.ts";
|
|
3
|
+
import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
4
|
+
import {
|
|
5
|
+
type GithubObservation,
|
|
6
|
+
type GithubPause,
|
|
7
|
+
githubPaused,
|
|
8
|
+
pace,
|
|
9
|
+
type RecordedObservation,
|
|
10
|
+
rateLimitWait,
|
|
11
|
+
readObservation,
|
|
12
|
+
reservedUntil,
|
|
13
|
+
} from "#host/githubRateLimit.ts";
|
|
14
|
+
import { nextLink, type ParsedResponse, parseResponse, requestResource } from "#host/githubResponse.ts";
|
|
15
|
+
import { GithubTransport, ghTransport } from "#host/githubTransport.ts";
|
|
16
|
+
|
|
17
|
+
interface Page {
|
|
18
|
+
readonly body: string;
|
|
19
|
+
readonly etag: string | undefined;
|
|
20
|
+
readonly next: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
interface Block {
|
|
23
|
+
readonly failures: number;
|
|
24
|
+
readonly until: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const CACHE_LIMIT = 1000;
|
|
28
|
+
const PAGE_LIMIT = 100;
|
|
29
|
+
|
|
30
|
+
export const GithubApi = defineService({
|
|
31
|
+
id: "Fleet/GithubApi",
|
|
32
|
+
initialize: Effect.gen(function* () {
|
|
33
|
+
return {
|
|
34
|
+
blocks: new Map<string, Block>(),
|
|
35
|
+
cache: new Map<string, Page>(),
|
|
36
|
+
observations: new Map<string, RecordedObservation>(),
|
|
37
|
+
transport: yield* GithubTransport,
|
|
38
|
+
};
|
|
39
|
+
}),
|
|
40
|
+
methods: (state) => {
|
|
41
|
+
function remember(url: string, page: Page) {
|
|
42
|
+
state.cache.delete(url);
|
|
43
|
+
state.cache.set(url, page);
|
|
44
|
+
for (const key of state.cache.keys()) {
|
|
45
|
+
if (state.cache.size <= CACHE_LIMIT) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
state.cache.delete(key);
|
|
49
|
+
}
|
|
50
|
+
return page;
|
|
51
|
+
}
|
|
52
|
+
function paused(resource: string, now: number): { readonly by: GithubPause; readonly until: number } | undefined {
|
|
53
|
+
const blocked = state.blocks.get(resource)?.until;
|
|
54
|
+
if (blocked !== undefined && blocked > now) {
|
|
55
|
+
return { by: "rate-limit", until: blocked };
|
|
56
|
+
}
|
|
57
|
+
const reserved = reservedUntil(state.observations.get(resource), now);
|
|
58
|
+
return reserved === undefined ? undefined : { by: "reserve", until: reserved };
|
|
59
|
+
}
|
|
60
|
+
function limited(observation: RecordedObservation, now: number): GithubObservation {
|
|
61
|
+
const pause = paused(observation.resource, now);
|
|
62
|
+
return { ...observation, limitedBy: pause?.by ?? null, limitedUntil: pause?.until ?? null };
|
|
63
|
+
}
|
|
64
|
+
const settle = Effect.fn("Fleet.settle")(function* (url: string, exitCode: number, response: ParsedResponse, now: number) {
|
|
65
|
+
const observation = readObservation(response.headers, now);
|
|
66
|
+
const resource = observation?.resource ?? requestResource(url);
|
|
67
|
+
if (observation !== undefined) {
|
|
68
|
+
state.observations.set(resource, observation);
|
|
69
|
+
}
|
|
70
|
+
const previous = state.blocks.get(resource);
|
|
71
|
+
const wait = rateLimitWait(response, observation, now, previous?.failures ?? 0);
|
|
72
|
+
if (wait !== undefined) {
|
|
73
|
+
const until = Math.max(wait, previous?.until ?? 0);
|
|
74
|
+
state.blocks.set(resource, { failures: (previous?.failures ?? 0) + 1, until });
|
|
75
|
+
return yield* githubPaused("rate-limit", until);
|
|
76
|
+
}
|
|
77
|
+
state.blocks.delete(resource);
|
|
78
|
+
const cached = state.cache.get(url);
|
|
79
|
+
if (response.status === 304 && cached !== undefined) {
|
|
80
|
+
return remember(url, cached);
|
|
81
|
+
}
|
|
82
|
+
if (exitCode !== 0 || response.status < 200 || response.status > 299) {
|
|
83
|
+
return yield* new ServiceUnavailable({ message: `GitHub request failed with HTTP ${response.status}` });
|
|
84
|
+
}
|
|
85
|
+
return remember(url, { body: response.body, etag: response.headers.get("etag"), next: nextLink(response.headers) });
|
|
86
|
+
});
|
|
87
|
+
const request = Effect.fn("Fleet.request")(function* (url: string) {
|
|
88
|
+
const pause = paused(requestResource(url), yield* Clock.currentTimeMillis);
|
|
89
|
+
if (pause !== undefined) {
|
|
90
|
+
return yield* githubPaused(pause.by, pause.until);
|
|
91
|
+
}
|
|
92
|
+
const etag = state.cache.get(url)?.etag;
|
|
93
|
+
const { exitCode, output } = yield* state.transport([
|
|
94
|
+
"api",
|
|
95
|
+
"--include",
|
|
96
|
+
"--method",
|
|
97
|
+
"GET",
|
|
98
|
+
...(etag === undefined ? [] : ["--header", `If-None-Match: ${etag}`]),
|
|
99
|
+
url,
|
|
100
|
+
]);
|
|
101
|
+
const response = parseResponse(output);
|
|
102
|
+
if (response === undefined) {
|
|
103
|
+
return yield* new ServiceUnavailable({ message: "GitHub returned a response without an HTTP status" });
|
|
104
|
+
}
|
|
105
|
+
return yield* settle(url, exitCode, response, yield* Clock.currentTimeMillis);
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
get: (endpoint: string) => request(endpoint).pipe(Effect.map(({ body }) => body)),
|
|
109
|
+
observations: () =>
|
|
110
|
+
Effect.gen(function* () {
|
|
111
|
+
const now = yield* Clock.currentTimeMillis;
|
|
112
|
+
return [...state.observations.values()]
|
|
113
|
+
.map((observation) => limited(observation, now))
|
|
114
|
+
.sort((left, right) => left.resource.localeCompare(right.resource));
|
|
115
|
+
}),
|
|
116
|
+
pace: (intervalMs: number) =>
|
|
117
|
+
Effect.gen(function* () {
|
|
118
|
+
const now = yield* Clock.currentTimeMillis;
|
|
119
|
+
const core = state.observations.get("core");
|
|
120
|
+
return pace(core === undefined ? undefined : limited(core, now), intervalMs, now);
|
|
121
|
+
}),
|
|
122
|
+
pages: (endpoint: string) =>
|
|
123
|
+
Effect.gen(function* () {
|
|
124
|
+
const bodies: string[] = [];
|
|
125
|
+
let next: string | undefined = endpoint;
|
|
126
|
+
while (next !== undefined) {
|
|
127
|
+
if (bodies.length >= PAGE_LIMIT) {
|
|
128
|
+
return yield* new ServiceUnavailable({ message: `GitHub returned more than ${PAGE_LIMIT} pages` });
|
|
129
|
+
}
|
|
130
|
+
const page: Page = yield* request(next);
|
|
131
|
+
bodies.push(page.body);
|
|
132
|
+
next = page.next;
|
|
133
|
+
}
|
|
134
|
+
return `[${bodies.join(",")}]`;
|
|
135
|
+
}),
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
requires: [GithubTransport],
|
|
139
|
+
});
|
|
140
|
+
export type GithubApi = typeof GithubApi.Identifier;
|
|
141
|
+
|
|
142
|
+
export const githubApiLayer = GithubApi.layer.pipe(Layer.provide(ghTransport));
|