cloving 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +235 -206
- package/dist/src/agent-harness-shell.d.ts +2 -1
- package/dist/src/agent-harness-shell.js +8 -5
- package/dist/src/authority/command-wait-checks.d.ts +8 -0
- package/dist/src/authority/command-wait-checks.js +88 -0
- package/dist/src/authority/command-wait-results.d.ts +15 -0
- package/dist/src/authority/command-wait-results.js +73 -0
- package/dist/src/authority/command-wait-view.d.ts +21 -0
- package/dist/src/authority/command-wait-view.js +34 -0
- package/dist/src/authority/command-waits.d.ts +39 -0
- package/dist/src/authority/command-waits.js +97 -0
- package/dist/src/authority/commands.js +22 -1
- package/dist/src/authority/dispatch-decision.d.ts +8 -0
- package/dist/src/authority/dispatch-decision.js +26 -1
- package/dist/src/authority/effect-requests.js +6 -3
- package/dist/src/authority/effect-results.js +4 -2
- package/dist/src/authority/history-state.d.ts +2 -0
- package/dist/src/authority/history-state.js +13 -1
- package/dist/src/authority/instants.d.ts +1 -0
- package/dist/src/authority/instants.js +21 -0
- package/dist/src/authority/reduction-state.d.ts +2 -0
- package/dist/src/authority/reduction-state.js +5 -0
- package/dist/src/authority/reduction.js +24 -0
- package/dist/src/authority/status-input.d.ts +2 -0
- package/dist/src/authority/status.d.ts +2 -0
- package/dist/src/authority/status.js +1 -0
- package/dist/src/authority/stop-sequence.d.ts +1 -0
- package/dist/src/authority/stop-sequence.js +2 -1
- package/dist/src/authority/transitions.d.ts +6 -4
- package/dist/src/authority/transitions.js +7 -0
- package/dist/src/broker/actor/authority-queries.d.ts +2 -1
- package/dist/src/broker/actor/authority-queries.js +14 -5
- package/dist/src/broker/actor/authority-receipts.js +1 -1
- package/dist/src/broker/actor/authority-settings.js +2 -2
- package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
- package/dist/src/broker/actor/authority-upgrades.js +7 -2
- package/dist/src/broker/actor/delivery-intents.js +2 -0
- package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
- package/dist/src/broker/actor/effect-delivery.js +15 -2
- package/dist/src/broker/actor/effect-plan.js +11 -2
- package/dist/src/broker/actor/effect-queries.d.ts +4 -0
- package/dist/src/broker/actor/effect-queries.js +9 -1
- package/dist/src/broker/actor/effect-turn.js +2 -0
- package/dist/src/broker/actor/stop-plan.js +9 -3
- package/dist/src/broker/actor/stop-turn.d.ts +2 -0
- package/dist/src/broker/actor/stop-turn.js +24 -11
- package/dist/src/broker/actor/turn-draft.d.ts +1 -0
- package/dist/src/broker/actor/turn-draft.js +4 -0
- package/dist/src/broker/actor/turn-state.js +5 -1
- package/dist/src/broker/authority-admission.js +60 -0
- package/dist/src/broker/authority-restore.d.ts +2 -0
- package/dist/src/broker/authority-restore.js +35 -1
- package/dist/src/broker/command-wait-restore.d.ts +31 -0
- package/dist/src/broker/command-wait-restore.js +86 -0
- package/dist/src/broker/decision-restore.d.ts +1 -1
- package/dist/src/broker/decision-restore.js +1 -0
- package/dist/src/broker/effect-upgrade.d.ts +2 -0
- package/dist/src/broker/effect-upgrade.js +8 -2
- package/dist/src/cli/arguments.d.ts +13 -0
- package/dist/src/cli/arguments.js +42 -3
- package/dist/src/cli/chat-stores.d.ts +2 -0
- package/dist/src/cli/chat-stores.js +113 -0
- package/dist/src/cli/client.d.ts +4 -1
- package/dist/src/cli/client.js +9 -3
- package/dist/src/cli/command-requests.d.ts +17 -0
- package/dist/src/cli/command-requests.js +43 -0
- package/dist/src/cli/command-waits.d.ts +9 -0
- package/dist/src/cli/command-waits.js +51 -0
- package/dist/src/cli/commands.js +14 -41
- package/dist/src/cli/config-file.d.ts +2 -2
- package/dist/src/cli/config-file.js +4 -2
- package/dist/src/cli/doctor-workflows.d.ts +5 -2
- package/dist/src/cli/doctor-workflows.js +12 -7
- package/dist/src/cli/doctor.js +6 -9
- package/dist/src/cli/harness-probes.d.ts +2 -0
- package/dist/src/cli/harness-probes.js +1 -1
- package/dist/src/cli/harness-processes.d.ts +6 -0
- package/dist/src/cli/harness-processes.js +36 -0
- package/dist/src/cli/init-command.d.ts +9 -0
- package/dist/src/cli/init-command.js +52 -0
- package/dist/src/cli/init-prompt-command.d.ts +10 -0
- package/dist/src/cli/init-prompt-command.js +145 -0
- package/dist/src/cli/init-prompt.d.ts +35 -0
- package/dist/src/cli/init-prompt.js +74 -0
- package/dist/src/cli/init-selection.d.ts +13 -0
- package/dist/src/cli/init-selection.js +75 -0
- package/dist/src/cli/main.d.ts +2 -0
- package/dist/src/cli/main.js +37 -7
- package/dist/src/cli/mcp-server.d.ts +1 -0
- package/dist/src/cli/mcp-server.js +156 -0
- package/dist/src/cli/mcp-tools.d.ts +15 -0
- package/dist/src/cli/mcp-tools.js +80 -0
- package/dist/src/cli/render.d.ts +1 -1
- package/dist/src/cli/render.js +17 -30
- package/dist/src/cli/report-ci-jobs.d.ts +23 -0
- package/dist/src/cli/report-ci-jobs.js +103 -0
- package/dist/src/cli/report-ci-usage.d.ts +9 -0
- package/dist/src/cli/report-ci-usage.js +185 -0
- package/dist/src/cli/report-ci.d.ts +13 -0
- package/dist/src/cli/report-ci.js +74 -0
- package/dist/src/cli/report-closures.d.ts +6 -0
- package/dist/src/cli/report-closures.js +97 -0
- package/dist/src/cli/report-flow.d.ts +7 -0
- package/dist/src/cli/report-flow.js +122 -0
- package/dist/src/cli/report-github-reader.d.ts +29 -0
- package/dist/src/cli/report-github-reader.js +157 -0
- package/dist/src/cli/report-github.d.ts +15 -8
- package/dist/src/cli/report-github.js +131 -150
- package/dist/src/cli/report-operator.d.ts +3 -0
- package/dist/src/cli/report-operator.js +40 -0
- package/dist/src/cli/report-ready.d.ts +3 -0
- package/dist/src/cli/report-ready.js +96 -0
- package/dist/src/cli/report-text.d.ts +9 -0
- package/dist/src/cli/report-text.js +33 -0
- package/dist/src/cli/report.d.ts +1 -0
- package/dist/src/cli/report.js +104 -27
- package/dist/src/cli/runtime-arguments.d.ts +2 -1
- package/dist/src/cli/runtime-arguments.js +7 -2
- package/dist/src/cli/schedule-arguments.d.ts +14 -0
- package/dist/src/cli/schedule-arguments.js +26 -0
- package/dist/src/cli/schedule-render.d.ts +22 -0
- package/dist/src/cli/schedule-render.js +73 -0
- package/dist/src/cli/terminal-text.d.ts +3 -0
- package/dist/src/cli/terminal-text.js +28 -0
- package/dist/src/cli/usage-render.d.ts +1 -0
- package/dist/src/cli/usage-render.js +28 -1
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +50 -7
- package/dist/src/cli/watch-actions.js +2 -2
- package/dist/src/cli/watch-board-view.js +19 -2
- package/dist/src/cli/watch-board.d.ts +4 -2
- package/dist/src/cli/watch-board.js +53 -43
- package/dist/src/cli/watch-card-forms.d.ts +1 -1
- package/dist/src/cli/watch-card-forms.js +11 -10
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +2 -11
- package/dist/src/cli/watch-header-view.d.ts +0 -5
- package/dist/src/cli/watch-header-view.js +8 -20
- package/dist/src/cli/watch-model.d.ts +4 -1
- package/dist/src/cli/watch-model.js +17 -11
- package/dist/src/cli/watch-page.js +12 -14
- package/dist/src/cli/watch-phases.d.ts +12 -3
- package/dist/src/cli/watch-phases.js +68 -11
- package/dist/src/cli/watch-pr-board.js +1 -1
- package/dist/src/cli/watch-pr-command.d.ts +1 -0
- package/dist/src/cli/watch-pull-request.js +18 -8
- package/dist/src/cli/watch-reads.d.ts +2 -0
- package/dist/src/cli/watch-reads.js +17 -16
- package/dist/src/cli/watch-render.d.ts +14 -0
- package/dist/src/cli/watch-run-input.d.ts +0 -1
- package/dist/src/cli/watch-run-input.js +0 -13
- package/dist/src/cli/watch-server.js +6 -6
- package/dist/src/cli/watch-view.d.ts +0 -3
- package/dist/src/cli/watch-view.js +13 -13
- package/dist/src/cli.js +59 -5
- package/dist/src/command-wait.d.ts +4 -0
- package/dist/src/command-wait.js +45 -0
- package/dist/src/configuration-change.d.ts +7 -0
- package/dist/src/configuration-change.js +14 -0
- package/dist/src/configuration-schema.d.ts +9 -0
- package/dist/src/configuration-schema.js +6 -2
- package/dist/src/daemon/attachment-file.d.ts +6 -0
- package/dist/src/daemon/attachment-file.js +86 -0
- package/dist/src/daemon/attachments.d.ts +14 -0
- package/dist/src/daemon/attachments.js +17 -0
- package/dist/src/daemon/claude-recovery.d.ts +50 -0
- package/dist/src/daemon/claude-recovery.js +204 -0
- package/dist/src/daemon/claude-stream.d.ts +27 -0
- package/dist/src/daemon/claude-stream.js +63 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -25
- package/dist/src/daemon/claude-turn.js +64 -81
- package/dist/src/daemon/claude-worker.d.ts +2 -1
- package/dist/src/daemon/claude-worker.js +124 -122
- package/dist/src/daemon/daemon-adapters.d.ts +2 -0
- package/dist/src/daemon/daemon-adapters.js +26 -2
- package/dist/src/daemon/daemon-loops.d.ts +3 -1
- package/dist/src/daemon/daemon-loops.js +3 -1
- package/dist/src/daemon/daemon.js +14 -6
- package/dist/src/daemon/github-attach.d.ts +2 -0
- package/dist/src/daemon/github-attach.js +149 -0
- package/dist/src/daemon/github-ci-filters.d.ts +6 -0
- package/dist/src/daemon/github-ci-filters.js +138 -0
- package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
- package/dist/src/daemon/github-ci-workflows.js +148 -0
- package/dist/src/daemon/github-ci.js +22 -4
- package/dist/src/daemon/github-issue-methods.d.ts +2 -0
- package/dist/src/daemon/github-issue-methods.js +37 -2
- package/dist/src/daemon/github-merge-call.d.ts +2 -1
- package/dist/src/daemon/github-merge-call.js +10 -3
- package/dist/src/daemon/github-merge.js +52 -24
- package/dist/src/daemon/github-methods.js +12 -2
- package/dist/src/daemon/github-operation.d.ts +4 -0
- package/dist/src/daemon/github-operation.js +6 -0
- package/dist/src/daemon/github-worker.d.ts +4 -0
- package/dist/src/daemon/github-worker.js +6 -0
- package/dist/src/daemon/issue-queue.d.ts +1 -1
- package/dist/src/daemon/issue-queue.js +16 -11
- package/dist/src/daemon/notify-events.d.ts +6 -0
- package/dist/src/daemon/notify-events.js +84 -0
- package/dist/src/daemon/notify-journal.js +1 -1
- package/dist/src/daemon/notify-message.d.ts +4 -1
- package/dist/src/daemon/notify-message.js +36 -0
- package/dist/src/daemon/notify.d.ts +1 -1
- package/dist/src/daemon/notify.js +5 -77
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/queue-status.d.ts +9 -3
- package/dist/src/daemon/queue-status.js +17 -4
- package/dist/src/daemon/resume-index-lock.d.ts +13 -0
- package/dist/src/daemon/resume-index-lock.js +160 -0
- package/dist/src/daemon/resume-worktree.d.ts +7 -0
- package/dist/src/daemon/resume-worktree.js +29 -0
- package/dist/src/daemon/retrying-turn.d.ts +2 -12
- package/dist/src/daemon/retrying-turn.js +98 -172
- package/dist/src/daemon/schedule-control.d.ts +26 -0
- package/dist/src/daemon/schedule-control.js +110 -0
- package/dist/src/daemon/schedule-journal.d.ts +2 -15
- package/dist/src/daemon/schedule-journal.js +67 -4
- package/dist/src/daemon/ticket-registration.d.ts +2 -7
- package/dist/src/daemon/ticket-registration.js +15 -81
- package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
- package/dist/src/daemon/turn-attempt-journal.js +136 -0
- package/dist/src/daemon/turn-attempts.d.ts +47 -0
- package/dist/src/daemon/turn-attempts.js +166 -0
- package/dist/src/daemon/turn-report.d.ts +8 -1
- package/dist/src/daemon/turn-report.js +39 -0
- package/dist/src/daemon/work-tree.d.ts +9 -0
- package/dist/src/daemon/work-tree.js +20 -0
- package/dist/src/daemon/worker-profile.d.ts +8 -0
- package/dist/src/daemon/worker-profile.js +40 -0
- package/dist/src/daemon/workflow-schedules.d.ts +3 -0
- package/dist/src/daemon/workflow-schedules.js +98 -45
- package/dist/src/daemon/worktree-processes.d.ts +9 -0
- package/dist/src/daemon/worktree-processes.js +151 -0
- package/dist/src/doctor-report.d.ts +3 -5
- package/dist/src/doctor-report.js +6 -13
- package/dist/src/harness-start.d.ts +15 -0
- package/dist/src/harness-start.js +37 -0
- package/dist/src/host/command-wait.d.ts +38 -0
- package/dist/src/host/command-wait.js +115 -0
- package/dist/src/host/start-delivery.d.ts +5 -7
- package/dist/src/host/start-delivery.js +3 -15
- package/dist/src/host/step-bridge-host.d.ts +2 -0
- package/dist/src/host/step-bridge-host.js +21 -0
- package/dist/src/operation-identity.d.ts +1 -0
- package/dist/src/operation-identity.js +4 -0
- package/dist/src/private-config.schema.json +40 -0
- package/dist/src/project-config.schema.json +40 -0
- package/dist/src/project-settings.d.ts +2 -2
- package/dist/src/project-settings.js +3 -1
- package/dist/src/queue-spend.d.ts +4 -0
- package/dist/src/queue-spend.js +21 -0
- package/dist/src/relay/relay-service.d.ts +3 -0
- package/dist/src/relay/relay-service.js +5 -3
- package/dist/src/report-ci.d.ts +164 -0
- package/dist/src/report-ci.js +32 -0
- package/dist/src/report-effects.d.ts +15 -0
- package/dist/src/report-effects.js +212 -0
- package/dist/src/report-flow-types.d.ts +38 -0
- package/dist/src/report-flow-types.js +7 -0
- package/dist/src/report-flow.d.ts +43 -0
- package/dist/src/report-flow.js +193 -0
- package/dist/src/report-operator.d.ts +67 -0
- package/dist/src/report-operator.js +25 -0
- package/dist/src/report-quality.d.ts +221 -0
- package/dist/src/report-quality.js +94 -0
- package/dist/src/report-ready.d.ts +23 -0
- package/dist/src/report-ready.js +117 -0
- package/dist/src/report-statistics.d.ts +2 -1
- package/dist/src/report-statistics.js +40 -4
- package/dist/src/report.d.ts +588 -47
- package/dist/src/report.js +156 -41
- package/dist/src/schedule-progress.d.ts +33 -0
- package/dist/src/schedule-progress.js +45 -0
- package/dist/src/service/active-tickets.js +6 -6
- package/dist/src/service/command-waits.d.ts +34 -0
- package/dist/src/service/command-waits.js +86 -0
- package/dist/src/service/expiry.js +2 -0
- package/dist/src/service/gateway.js +3 -1
- package/dist/src/service/queue-spend.d.ts +6 -0
- package/dist/src/service/queue-spend.js +68 -0
- package/dist/src/service/replies.d.ts +8 -1
- package/dist/src/service/replies.js +11 -0
- package/dist/src/service/run-handlers.js +22 -2
- package/dist/src/service/run-metadata.d.ts +15 -1
- package/dist/src/service/run-metadata.js +93 -8
- package/dist/src/service/service.d.ts +4 -0
- package/dist/src/service/service.js +8 -8
- package/dist/src/service/submission-handler.js +18 -4
- package/dist/src/service/worker-port.js +9 -1
- package/dist/src/service/worker-totals.d.ts +2 -0
- package/dist/src/service/worker-totals.js +1 -1
- package/dist/src/step-bridge.d.ts +20 -0
- package/dist/src/steps/command-wait.d.ts +12 -0
- package/dist/src/steps/command-wait.js +26 -0
- package/dist/src/steps/open-decision-wait.d.ts +2 -0
- package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
- package/dist/src/steps/review-by-person.js +4 -1
- package/dist/src/steps/wait-for-command.d.ts +3 -0
- package/dist/src/steps/wait-for-command.js +17 -0
- package/dist/src/steps/wait-for-decision.js +1 -1
- package/dist/src/storage/command-wait-rows.d.ts +30 -0
- package/dist/src/storage/command-wait-rows.js +96 -0
- package/dist/src/storage/decision-snapshots.d.ts +8 -1
- package/dist/src/storage/decision-snapshots.js +15 -1
- package/dist/src/storage/domain-changes.d.ts +2 -0
- package/dist/src/storage/domain-changes.js +3 -0
- package/dist/src/storage/domain-schema.d.ts +13 -1
- package/dist/src/storage/domain-schema.js +31 -4
- package/dist/src/storage/effect-schema.d.ts +1 -0
- package/dist/src/storage/effect-schema.js +5 -0
- package/dist/src/testing-requests.d.ts +15 -0
- package/dist/src/testing-requests.js +93 -0
- package/dist/src/testing.d.ts +4 -10
- package/dist/src/testing.js +33 -86
- package/dist/src/worker-usage.d.ts +1 -0
- package/dist/src/worker-usage.js +3 -0
- package/dist/src/workflow-candidate.d.ts +1 -0
- package/dist/src/workflow-candidate.js +12 -1
- package/dist/src/workflow-contracts.d.ts +1 -0
- package/dist/src/workflow-contracts.js +9 -2
- package/dist/src/workflow-discovery.d.ts +2 -13
- package/dist/src/workflow-discovery.js +1 -13
- package/dist/src/workflow-refusals.js +0 -1
- package/dist/src/workflows.d.ts +1 -1
- package/dist/src/workflows.js +1 -1
- package/dist/tools/red-green-command.d.ts +1 -0
- package/dist/tools/red-green-command.js +174 -0
- package/dist/tools/red-green-process.d.ts +10 -0
- package/dist/tools/red-green-process.js +59 -0
- package/dist/tools/self-host-processes.d.ts +2 -0
- package/dist/tools/self-host-processes.js +32 -13
- package/dist/tools/self-host-project.js +13 -78
- package/dist/tools/self-host-runtime.js +1 -1
- package/dist/tools/self-host.js +42 -10
- package/dist/tools/template-command.d.ts +1 -0
- package/dist/tools/template-command.js +160 -0
- package/dist/tools/template-copy.d.ts +11 -0
- package/dist/tools/template-copy.js +130 -0
- package/dist/tools/template-files.d.ts +17 -0
- package/dist/tools/template-files.js +74 -0
- package/dist/tools/workflow-bundles.js +1 -0
- package/dist/tools/workflow-candidate-counts.js +2 -2
- package/dist/tools/workflow-candidate-shape.d.ts +3 -0
- package/dist/tools/workflow-candidate-shape.js +90 -0
- package/dist/tools/workflow-candidate-source.js +15 -1
- package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
- package/dist/tools/workflow-candidate-syntax.js +24 -94
- package/dist/tools/workflow-command.js +1 -2
- package/dist/tools/workflow-folders.d.ts +2 -3
- package/dist/tools/workflow-folders.js +1 -1
- package/package.json +16 -8
- package/templates/develop/README.md +12 -0
- package/templates/develop/develop/flow-tests.ts +37 -0
- package/templates/develop/develop/pull.ts +5 -1
- package/templates/develop/develop/refusal-tests.ts +77 -0
- package/templates/develop/develop/turn.ts +2 -1
- package/templates/idea-to-issue/README.md +27 -0
- package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
- package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
- package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
- package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
- package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
- package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
- package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
- package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
- package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
- package/templates/idea-to-issue/idea-to-issue.ts +49 -0
- package/templates/idea-to-issue/template.json +81 -0
- package/templates/review-day/README.md +97 -0
- package/templates/review-day/review-day/critique.ts +58 -0
- package/templates/review-day/review-day/decision.ts +29 -0
- package/templates/review-day/review-day/facts.ts +69 -0
- package/templates/review-day/review-day/label.ts +29 -0
- package/templates/review-day/review-day/next.ts +28 -0
- package/templates/review-day/review-day/post.ts +27 -0
- package/templates/review-day/review-day/report.ts +98 -0
- package/templates/review-day/review-day/rules.ts +12 -0
- package/templates/review-day/review-day/scenarios.ts +76 -0
- package/templates/review-day/review-day/turn.ts +73 -0
- package/templates/review-day/review-day.declaration.json +16 -0
- package/templates/review-day/review-day.test.ts +362 -0
- package/templates/review-day/review-day.ts +37 -0
- package/templates/review-day/template.json +56 -0
- package/dist/src/cli/watch-start-view.d.ts +0 -18
- package/dist/src/cli/watch-start-view.js +0 -77
- package/dist/src/steps/decision-gate.d.ts +0 -3
- package/dist/workflows/develop-prd.d.ts +0 -7
- package/dist/workflows/develop-prd.js +0 -165
- package/dist/workflows/develop-ticket.d.ts +0 -9
- package/dist/workflows/develop-ticket.js +0 -207
- package/dist/workflows/issue-labels.d.ts +0 -16
- package/dist/workflows/issue-labels.js +0 -70
- package/dist/workflows/ticket-prompts.d.ts +0 -35
- package/dist/workflows/ticket-prompts.js +0 -147
|
@@ -43,7 +43,7 @@ const outcomes = {
|
|
|
43
43
|
},
|
|
44
44
|
handoff_requested: {
|
|
45
45
|
keys: ["cycleId", "kind", "promotionId"],
|
|
46
|
-
optional: ["ticketStatus", "issueUpdate", "reason"],
|
|
46
|
+
optional: ["ticketStatus", "issueUpdate", "reason", "report"],
|
|
47
47
|
parse: (fields) => {
|
|
48
48
|
const cycleId = textField(fields, {
|
|
49
49
|
key: "cycleId",
|
|
@@ -58,6 +58,9 @@ const outcomes = {
|
|
|
58
58
|
const ticketStatus = fields["ticketStatus"];
|
|
59
59
|
const issueUpdate = fields["issueUpdate"];
|
|
60
60
|
const reason = fields["reason"];
|
|
61
|
+
const report = fields["report"];
|
|
62
|
+
if (!validReport(report))
|
|
63
|
+
return null;
|
|
61
64
|
if (issueUpdate !== undefined && issueUpdate !== "failed")
|
|
62
65
|
return null;
|
|
63
66
|
if (ticketStatus !== undefined && !isTicketStatus(ticketStatus))
|
|
@@ -68,11 +71,15 @@ const outcomes = {
|
|
|
68
71
|
kind: "handoff_requested",
|
|
69
72
|
cycleId,
|
|
70
73
|
promotionId,
|
|
71
|
-
...Object.fromEntries(Object.entries({ issueUpdate, ticketStatus, reason }).filter(([, value]) => value !== undefined)),
|
|
74
|
+
...Object.fromEntries(Object.entries({ issueUpdate, ticketStatus, reason, report }).filter(([, value]) => value !== undefined)),
|
|
72
75
|
};
|
|
73
76
|
},
|
|
74
77
|
},
|
|
75
78
|
};
|
|
79
|
+
function validReport(report) {
|
|
80
|
+
return (report === undefined ||
|
|
81
|
+
(typeof report === "string" && report.length > 0 && report.length <= 65536));
|
|
82
|
+
}
|
|
76
83
|
export function validateDevelopmentInput(value) {
|
|
77
84
|
const fields = exactFields(value, { required: ["cycleId"] });
|
|
78
85
|
const cycleId = fields && textField(fields, { key: "cycleId", pattern: identifier });
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
export interface WorkflowReservation {
|
|
2
|
-
readonly baseName?: string;
|
|
3
|
-
readonly workflowId?: string;
|
|
4
|
-
readonly functionName?: string;
|
|
5
|
-
}
|
|
6
1
|
export interface WorkflowDiscoveryIdentity {
|
|
7
2
|
readonly queue?: boolean;
|
|
8
3
|
readonly baseName: string;
|
|
9
4
|
readonly workflowId: string | undefined;
|
|
10
5
|
readonly functionNames: readonly string[];
|
|
11
6
|
}
|
|
12
|
-
export type WorkflowConflictCode = "workflow-conflict" | "
|
|
13
|
-
export declare function workflowConflicts(candidates: readonly WorkflowDiscoveryIdentity[]
|
|
14
|
-
export declare const bundledWorkflowIds: {
|
|
15
|
-
readonly ticket: "develop-ticket";
|
|
16
|
-
readonly issue: "develop-issue";
|
|
17
|
-
readonly prompt: "develop-prompt";
|
|
18
|
-
readonly prd: "develop-prd";
|
|
19
|
-
};
|
|
7
|
+
export type WorkflowConflictCode = "workflow-conflict" | "queue-workflow-conflict";
|
|
8
|
+
export declare function workflowConflicts(candidates: readonly WorkflowDiscoveryIdentity[]): readonly (readonly WorkflowConflictCode[])[];
|
|
@@ -5,32 +5,20 @@ function identityKeys(identity) {
|
|
|
5
5
|
...identity.functionNames.map((name) => `function:${name}`),
|
|
6
6
|
];
|
|
7
7
|
}
|
|
8
|
-
export function workflowConflicts(candidates
|
|
8
|
+
export function workflowConflicts(candidates) {
|
|
9
9
|
const keys = candidates.map(identityKeys);
|
|
10
10
|
const counts = new Map();
|
|
11
11
|
for (const names of keys) {
|
|
12
12
|
for (const name of new Set(names))
|
|
13
13
|
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
14
14
|
}
|
|
15
|
-
const reserved = new Set(bundled.flatMap((entry) => identityKeys({
|
|
16
|
-
...entry,
|
|
17
|
-
functionNames: entry.functionName === undefined ? [] : [entry.functionName],
|
|
18
|
-
})));
|
|
19
15
|
const queued = candidates.filter((candidate) => candidate.queue === true);
|
|
20
16
|
return keys.map((names, index) => {
|
|
21
17
|
const codes = [];
|
|
22
18
|
if (queued.length > 1 && candidates[index]?.queue === true)
|
|
23
19
|
codes.push("queue-workflow-conflict");
|
|
24
|
-
if (names.some((name) => reserved.has(name)))
|
|
25
|
-
codes.push("bundled-name-reserved");
|
|
26
20
|
if (names.some((name) => (counts.get(name) ?? 0) > 1))
|
|
27
21
|
codes.push("workflow-conflict");
|
|
28
22
|
return codes;
|
|
29
23
|
});
|
|
30
24
|
}
|
|
31
|
-
export const bundledWorkflowIds = {
|
|
32
|
-
ticket: "develop-ticket",
|
|
33
|
-
issue: "develop-issue",
|
|
34
|
-
prompt: "develop-prompt",
|
|
35
|
-
prd: "develop-prd",
|
|
36
|
-
};
|
package/dist/src/workflows.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { loadCycle, type CycleLoad } from "./steps/load-cycle.ts";
|
|
2
2
|
export { protectedEffect } from "./steps/protected-effect.ts";
|
|
3
|
-
export { decisionGate } from "./steps/decision-gate.ts";
|
|
4
3
|
export { waitForDecision } from "./steps/wait-for-decision.ts";
|
|
5
4
|
export { reviewByPerson } from "./steps/review-by-person.ts";
|
|
6
5
|
export { reviewByAgent } from "./steps/review-by-agent.ts";
|
|
@@ -11,6 +10,7 @@ export type { MergeCheckName, MergeReason, MergeRequest, MergeResult, PullReques
|
|
|
11
10
|
export { handoff } from "./steps/handoff.ts";
|
|
12
11
|
export { waitForRun } from "./steps/wait-for-run.ts";
|
|
13
12
|
export { runCommand } from "./steps/run-command.ts";
|
|
13
|
+
export { waitForCommand, type CommandWaitRequest, type CommandWaitOutcome, } from "./steps/wait-for-command.ts";
|
|
14
14
|
export { sleep } from "./steps/sleep.ts";
|
|
15
15
|
export type { CommandRequest, GateRequest, GateResult, DecisionResult, HandoffRequest, HandoffValue, RunWaitRequest, RunWaitOutcome, ProtectedRequest, ProtectedResult, ProtectedScope, } from "./step-bridge.ts";
|
|
16
16
|
export type { CycleOutcome, DevelopmentInput } from "./workflow-contracts.ts";
|
package/dist/src/workflows.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { loadCycle } from "./steps/load-cycle.js";
|
|
2
2
|
export { protectedEffect } from "./steps/protected-effect.js";
|
|
3
|
-
export { decisionGate } from "./steps/decision-gate.js";
|
|
4
3
|
export { waitForDecision } from "./steps/wait-for-decision.js";
|
|
5
4
|
export { reviewByPerson } from "./steps/review-by-person.js";
|
|
6
5
|
export { reviewByAgent } from "./steps/review-by-agent.js";
|
|
@@ -9,5 +8,6 @@ export { mergeWhenGreen } from "./steps/merge-when-green.js";
|
|
|
9
8
|
export { handoff } from "./steps/handoff.js";
|
|
10
9
|
export { waitForRun } from "./steps/wait-for-run.js";
|
|
11
10
|
export { runCommand } from "./steps/run-command.js";
|
|
11
|
+
export { waitForCommand, } from "./steps/wait-for-command.js";
|
|
12
12
|
export { sleep } from "./steps/sleep.js";
|
|
13
13
|
export { ticketStatus } from "./ticket-status.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { record } from "../src/protocol.js";
|
|
6
|
+
import { configuration } from "../src/configuration-schema.js";
|
|
7
|
+
import { parseCommandSeconds } from "../src/command-settings.js";
|
|
8
|
+
import { redGreenProcess } from "./red-green-process.js";
|
|
9
|
+
function git(argumentsList) {
|
|
10
|
+
return execFileSync("git", [...argumentsList], {
|
|
11
|
+
encoding: "utf8",
|
|
12
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function run(input) {
|
|
16
|
+
const temporary = mkdtempSync(join(tmpdir(), "cloving-red-green-"));
|
|
17
|
+
const tree = join(temporary, "tree");
|
|
18
|
+
try {
|
|
19
|
+
git(["worktree", "add", "--detach", tree, input.revision]);
|
|
20
|
+
if (input.revision !== input.head)
|
|
21
|
+
git([
|
|
22
|
+
"--literal-pathspecs",
|
|
23
|
+
"-C",
|
|
24
|
+
tree,
|
|
25
|
+
"restore",
|
|
26
|
+
"--source",
|
|
27
|
+
input.head,
|
|
28
|
+
"--worktree",
|
|
29
|
+
"--",
|
|
30
|
+
...input.files,
|
|
31
|
+
]);
|
|
32
|
+
const options = {
|
|
33
|
+
directory: tree,
|
|
34
|
+
signal: input.signal,
|
|
35
|
+
milliseconds: input.milliseconds,
|
|
36
|
+
};
|
|
37
|
+
const prepared = input.prepare === undefined
|
|
38
|
+
? undefined
|
|
39
|
+
: await redGreenProcess(input.prepare, options);
|
|
40
|
+
if (prepared !== undefined && prepared.status !== 0)
|
|
41
|
+
return { ...prepared, prepareFailed: true };
|
|
42
|
+
const tested = await redGreenProcess([...input.test, ...input.files], options);
|
|
43
|
+
return tested;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
git(["worktree", "remove", "--force", tree]);
|
|
47
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function redGreenCommand(signal) {
|
|
51
|
+
if (process.argv.slice(2).includes("--help")) {
|
|
52
|
+
process.stdout.write('Usage: cloving red-green\n\nReads {"head":"<full commit ID>"} on standard input.\nCLOVING_RED_GREEN configures test, testFiles, prepare, assertionPattern and skipPattern.\nPrints one JSON verdict from temporary detached worktrees.\n');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let text = "";
|
|
56
|
+
for await (const chunk of process.stdin)
|
|
57
|
+
text += String(chunk);
|
|
58
|
+
const head = inputHead(text);
|
|
59
|
+
const { settings, assertion, skip } = redGreenSettings();
|
|
60
|
+
const { test, testFiles: prefixes, prepare } = settings;
|
|
61
|
+
if (git(["cat-file", "-t", head]).trim() !== "commit")
|
|
62
|
+
throw new Error("invalid-red-green-input: head must be a full commit ID");
|
|
63
|
+
if (git(["rev-parse", "--verify", head]).trim() !== head.toLowerCase())
|
|
64
|
+
throw new Error("invalid-red-green-input: head must be a full commit ID");
|
|
65
|
+
const base = git(["merge-base", head, "refs/remotes/origin/HEAD"]).trim();
|
|
66
|
+
const files = changedTests({ base, head, prefixes });
|
|
67
|
+
if (files.length === 0) {
|
|
68
|
+
process.stdout.write(JSON.stringify({
|
|
69
|
+
verdict: "no-test-change",
|
|
70
|
+
base,
|
|
71
|
+
head,
|
|
72
|
+
files,
|
|
73
|
+
output: "",
|
|
74
|
+
}) + "\n");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const milliseconds = parseCommandSeconds(process.env["CLOVING_COMMAND_SECONDS"]) * 1000;
|
|
78
|
+
const options = { head, files, test, prepare, signal, milliseconds };
|
|
79
|
+
const red = await run({ ...options, revision: base });
|
|
80
|
+
signal.throwIfAborted();
|
|
81
|
+
const green = await run({ ...options, revision: head });
|
|
82
|
+
signal.throwIfAborted();
|
|
83
|
+
const result = classify({ red, green, assertion, skip });
|
|
84
|
+
process.stdout.write(JSON.stringify({
|
|
85
|
+
...result,
|
|
86
|
+
output: result.output.slice(-16000),
|
|
87
|
+
base,
|
|
88
|
+
head,
|
|
89
|
+
files,
|
|
90
|
+
}) + "\n");
|
|
91
|
+
}
|
|
92
|
+
function changedTests(input) {
|
|
93
|
+
return git([
|
|
94
|
+
"diff",
|
|
95
|
+
"--no-renames",
|
|
96
|
+
"--name-only",
|
|
97
|
+
"--diff-filter=AMT",
|
|
98
|
+
"-z",
|
|
99
|
+
input.base,
|
|
100
|
+
input.head,
|
|
101
|
+
])
|
|
102
|
+
.split("\0")
|
|
103
|
+
.filter((file) => input.prefixes.some((prefix) => file.startsWith(prefix)));
|
|
104
|
+
}
|
|
105
|
+
function classify(input) {
|
|
106
|
+
const { red, green, assertion, skip } = input;
|
|
107
|
+
for (const result of [red, green])
|
|
108
|
+
if (result.prepareFailed === true)
|
|
109
|
+
return { verdict: "prepare-failed", output: result.output };
|
|
110
|
+
for (const result of [red, green])
|
|
111
|
+
if (skip.test(result.output))
|
|
112
|
+
return { verdict: "skipped-test", output: result.output };
|
|
113
|
+
if (red.status === 0)
|
|
114
|
+
return { verdict: "false-red", output: red.output };
|
|
115
|
+
if (red.status === null || !assertion.test(red.output))
|
|
116
|
+
return { verdict: "red-not-assertion", output: red.output };
|
|
117
|
+
if (green.status !== 0)
|
|
118
|
+
return { verdict: "green-failed", output: green.output };
|
|
119
|
+
return { verdict: "accepted", output: green.output };
|
|
120
|
+
}
|
|
121
|
+
function inputHead(text) {
|
|
122
|
+
let input;
|
|
123
|
+
try {
|
|
124
|
+
input = JSON.parse(text);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
throw new Error("invalid-red-green-input: expected JSON with a full commit ID in head");
|
|
128
|
+
}
|
|
129
|
+
const head = record(input)?.["head"];
|
|
130
|
+
if (typeof head !== "string" ||
|
|
131
|
+
!/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(head))
|
|
132
|
+
throw new Error("invalid-red-green-input: head must be a full commit ID");
|
|
133
|
+
return head;
|
|
134
|
+
}
|
|
135
|
+
function redGreenSettings() {
|
|
136
|
+
try {
|
|
137
|
+
const value = JSON.parse(process.env["CLOVING_RED_GREEN"] ?? "null");
|
|
138
|
+
const settings = configuration({ version: 1, checks: { redGreen: value } }, "project").checks?.redGreen;
|
|
139
|
+
if (settings === undefined)
|
|
140
|
+
throw new Error("missing settings");
|
|
141
|
+
return {
|
|
142
|
+
settings,
|
|
143
|
+
assertion: new RegExp(settings.assertionPattern ?? "ERR_ASSERTION"),
|
|
144
|
+
skip: new RegExp(settings.skipPattern ?? "# SKIP\\b"),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new Error("invalid-CLOVING_RED_GREEN: " +
|
|
149
|
+
(error instanceof Error ? error.message : String(error)), { cause: error });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const controller = new AbortController();
|
|
153
|
+
const interrupt = () => {
|
|
154
|
+
controller.abort();
|
|
155
|
+
};
|
|
156
|
+
process.on("disconnect", interrupt);
|
|
157
|
+
if (!process.connected)
|
|
158
|
+
interrupt();
|
|
159
|
+
process.on("SIGTERM", interrupt);
|
|
160
|
+
process.on("SIGINT", interrupt);
|
|
161
|
+
try {
|
|
162
|
+
await redGreenCommand(controller.signal);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
process.stderr.write((error instanceof Error ? error.message : String(error)) + "\n");
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
process.off("disconnect", interrupt);
|
|
170
|
+
if (process.connected)
|
|
171
|
+
process.disconnect?.();
|
|
172
|
+
process.off("SIGTERM", interrupt);
|
|
173
|
+
process.off("SIGINT", interrupt);
|
|
174
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface RunResult {
|
|
2
|
+
readonly status: number | null;
|
|
3
|
+
readonly output: string;
|
|
4
|
+
readonly prepareFailed?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function redGreenProcess(argumentsList: readonly string[], options: {
|
|
7
|
+
readonly directory: string;
|
|
8
|
+
readonly signal: AbortSignal;
|
|
9
|
+
readonly milliseconds: number;
|
|
10
|
+
}): Promise<RunResult>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { addAbortListener } from "node:events";
|
|
3
|
+
function stop(child) {
|
|
4
|
+
if (child.pid === undefined)
|
|
5
|
+
return;
|
|
6
|
+
try {
|
|
7
|
+
process.kill(-child.pid, "SIGKILL");
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (Reflect.get(Object(error), "code") !== "ESRCH")
|
|
11
|
+
child.kill("SIGKILL");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function redGreenProcess(argumentsList, options) {
|
|
15
|
+
options.signal.throwIfAborted();
|
|
16
|
+
const child = spawn(argumentsList[0] ?? "", argumentsList.slice(1), {
|
|
17
|
+
cwd: options.directory,
|
|
18
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
19
|
+
detached: true,
|
|
20
|
+
env: { ...process.env, NODE_TEST_CONTEXT: undefined },
|
|
21
|
+
});
|
|
22
|
+
let output = "";
|
|
23
|
+
let stopped = false;
|
|
24
|
+
child.stdout.setEncoding("utf8");
|
|
25
|
+
child.stderr.setEncoding("utf8");
|
|
26
|
+
child.stdout.on("data", (chunk) => {
|
|
27
|
+
output += chunk;
|
|
28
|
+
});
|
|
29
|
+
child.stderr.on("data", (chunk) => {
|
|
30
|
+
output += chunk;
|
|
31
|
+
});
|
|
32
|
+
child.once("error", (error) => {
|
|
33
|
+
output += error.message;
|
|
34
|
+
});
|
|
35
|
+
const closed = new Promise((resolve) => {
|
|
36
|
+
child.once("close", (status) => {
|
|
37
|
+
resolve({ status: stopped ? null : status, output });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
const terminate = () => {
|
|
41
|
+
stopped = true;
|
|
42
|
+
stop(child);
|
|
43
|
+
child.stdout.destroy();
|
|
44
|
+
child.stderr.destroy();
|
|
45
|
+
};
|
|
46
|
+
const listener = addAbortListener(options.signal, terminate);
|
|
47
|
+
const timeout = setTimeout(() => {
|
|
48
|
+
output += "\nred-green command timed out\n";
|
|
49
|
+
terminate();
|
|
50
|
+
}, options.milliseconds);
|
|
51
|
+
try {
|
|
52
|
+
return await closed;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
listener[Symbol.dispose]();
|
|
57
|
+
stop(child);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export interface HostProcesses {
|
|
2
|
+
readonly environment?: Readonly<NodeJS.ProcessEnv>;
|
|
2
3
|
readonly project: string;
|
|
3
4
|
readonly product: string;
|
|
4
5
|
readonly runtime: string;
|
|
@@ -7,5 +8,6 @@ export interface HostProcesses {
|
|
|
7
8
|
readonly watchPort: string;
|
|
8
9
|
readonly phoneRelay: string;
|
|
9
10
|
readonly updateMilliseconds: number | undefined;
|
|
11
|
+
readonly stopMilliseconds?: number;
|
|
10
12
|
}
|
|
11
13
|
export declare function hostedPrograms(settings: HostProcesses): Promise<number>;
|
|
@@ -18,13 +18,26 @@ function launched(argumentsList, settings) {
|
|
|
18
18
|
settle(status ?? 1);
|
|
19
19
|
});
|
|
20
20
|
});
|
|
21
|
-
return { child, ended };
|
|
21
|
+
return { name: settings.name, child, ended };
|
|
22
22
|
}
|
|
23
|
-
async function stopped(programs) {
|
|
23
|
+
async function stopped(programs, milliseconds) {
|
|
24
24
|
for (const program of programs)
|
|
25
|
-
if (!program.child.killed
|
|
25
|
+
if (!program.child.killed &&
|
|
26
|
+
program.child.exitCode === null &&
|
|
27
|
+
program.child.signalCode === null)
|
|
26
28
|
program.child.kill("SIGTERM");
|
|
27
|
-
await Promise.all(programs.map((program) =>
|
|
29
|
+
await Promise.all(programs.map(async (program) => {
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
if (program.child.kill("SIGKILL"))
|
|
32
|
+
process.stderr.write(`Cloving stopped the ${program.name} child with SIGKILL after ${String(milliseconds / 1000)} s\n`);
|
|
33
|
+
}, milliseconds);
|
|
34
|
+
try {
|
|
35
|
+
await program.ended;
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
28
41
|
}
|
|
29
42
|
function updatingGit(settings, stopping) {
|
|
30
43
|
const git = promisify(execFile);
|
|
@@ -97,9 +110,10 @@ function productChild(settings, argumentsList) {
|
|
|
97
110
|
join(settings.product, `src/${program}${extname(import.meta.filename)}`),
|
|
98
111
|
...rest,
|
|
99
112
|
], {
|
|
113
|
+
name: program === "cli" ? "watch" : program,
|
|
100
114
|
directory: settings.product,
|
|
101
115
|
environment: {
|
|
102
|
-
...process.env,
|
|
116
|
+
...(settings.environment ?? process.env),
|
|
103
117
|
CLOVING_RUNTIME_DIR: settings.runtime,
|
|
104
118
|
CLOVING_WATCH_REPOSITORY: settings.repository,
|
|
105
119
|
},
|
|
@@ -107,8 +121,9 @@ function productChild(settings, argumentsList) {
|
|
|
107
121
|
}
|
|
108
122
|
function hostedChildren(settings) {
|
|
109
123
|
const daemon = launched([join(settings.project, "start.mjs")], {
|
|
124
|
+
name: "daemon",
|
|
110
125
|
directory: settings.project,
|
|
111
|
-
environment: process.env,
|
|
126
|
+
environment: settings.environment ?? process.env,
|
|
112
127
|
});
|
|
113
128
|
const watch = settings.watchPort === "off"
|
|
114
129
|
? []
|
|
@@ -127,15 +142,19 @@ export async function hostedPrograms(settings) {
|
|
|
127
142
|
const { daemon, programs } = hostedChildren(settings);
|
|
128
143
|
const stopping = new AbortController();
|
|
129
144
|
let updating = false;
|
|
130
|
-
|
|
145
|
+
let shutdown;
|
|
146
|
+
const stop = () => (shutdown ??= stopped(programs, settings.stopMilliseconds ?? 60000));
|
|
147
|
+
const signal = (signal) => {
|
|
131
148
|
stopping.abort();
|
|
132
149
|
for (const program of programs)
|
|
133
|
-
program.child.kill(
|
|
150
|
+
program.child.kill(signal);
|
|
151
|
+
shutdown = stop();
|
|
152
|
+
};
|
|
153
|
+
const interrupt = () => {
|
|
154
|
+
signal("SIGINT");
|
|
134
155
|
};
|
|
135
156
|
const terminate = () => {
|
|
136
|
-
|
|
137
|
-
for (const program of programs)
|
|
138
|
-
program.child.kill("SIGTERM");
|
|
157
|
+
signal("SIGTERM");
|
|
139
158
|
};
|
|
140
159
|
process.on("SIGINT", interrupt);
|
|
141
160
|
process.on("SIGTERM", terminate);
|
|
@@ -143,7 +162,7 @@ export async function hostedPrograms(settings) {
|
|
|
143
162
|
stopping: stopping.signal,
|
|
144
163
|
stop: async () => {
|
|
145
164
|
updating = true;
|
|
146
|
-
await
|
|
165
|
+
await stop();
|
|
147
166
|
},
|
|
148
167
|
});
|
|
149
168
|
try {
|
|
@@ -159,7 +178,7 @@ export async function hostedPrograms(settings) {
|
|
|
159
178
|
}
|
|
160
179
|
finally {
|
|
161
180
|
stopping.abort();
|
|
162
|
-
await
|
|
181
|
+
await stop();
|
|
163
182
|
await polling;
|
|
164
183
|
process.off("SIGINT", interrupt);
|
|
165
184
|
process.off("SIGTERM", terminate);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { findPackageJSON } from "node:module";
|
|
5
|
-
import {
|
|
5
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
6
6
|
import packageMetadata from "#package" with { type: "json" };
|
|
7
7
|
import { record } from "../src/protocol.js";
|
|
8
8
|
import { isWorkflowDeclaration, } from "../src/workflow-declaration.js";
|
|
9
|
-
import {
|
|
10
|
-
import { workflowDeclarations, workflowFiles, workflowManifests, } from "./self-host-workflows.js";
|
|
9
|
+
import { workflowManifests } from "./self-host-workflows.js";
|
|
11
10
|
import { loadWorkflowFolders, } from "./workflow-folders.js";
|
|
12
11
|
import { isWorkflowManifest } from "./workflow-manifest.js";
|
|
13
12
|
import { retainWorkflowBundle } from "./workflow-bundles.js";
|
|
@@ -17,9 +16,6 @@ async function initializeProject(settings) {
|
|
|
17
16
|
await rm(project, { recursive: true, force: true });
|
|
18
17
|
await mkdir(join(project, "workflows"), { recursive: true, mode: 0o700 });
|
|
19
18
|
await writeFile(join(project, "package.json"), '{"name":"cloving-self-host","private":true,"type":"module"}');
|
|
20
|
-
const files = await workflowFiles(join(product, "workflows"));
|
|
21
|
-
for (const file of files)
|
|
22
|
-
await copyWorkflow(settings, file);
|
|
23
19
|
const modules = join(project, "node_modules");
|
|
24
20
|
await mkdir(join(modules, "cloving"), { recursive: true });
|
|
25
21
|
const dependencies = new Map(Object.keys(packageMetadata.dependencies).map((name) => [name, join(product, "package.json")]));
|
|
@@ -46,7 +42,6 @@ async function initializeProject(settings) {
|
|
|
46
42
|
`./src/${subpath.slice("./workflows/".length) || "workflows"}${extname(import.meta.filename)}`,
|
|
47
43
|
])),
|
|
48
44
|
}));
|
|
49
|
-
return files;
|
|
50
45
|
}
|
|
51
46
|
async function compile(settings) {
|
|
52
47
|
const { product, project } = settings;
|
|
@@ -70,23 +65,6 @@ async function compile(settings) {
|
|
|
70
65
|
throw new Error("The compiled workflow manifest is invalid.");
|
|
71
66
|
return compiled;
|
|
72
67
|
}
|
|
73
|
-
function reservations(input) {
|
|
74
|
-
const { files, manifests, declarations } = input;
|
|
75
|
-
return [
|
|
76
|
-
...files.map((file) => ({
|
|
77
|
-
baseName: basename(file, extname(file)),
|
|
78
|
-
})),
|
|
79
|
-
...Object.entries(manifests).map(([functionName, manifest]) => ({
|
|
80
|
-
baseName: manifest.name,
|
|
81
|
-
workflowId: manifest.workflowId,
|
|
82
|
-
functionName,
|
|
83
|
-
})),
|
|
84
|
-
...[
|
|
85
|
-
...Object.values(bundledWorkflowIds),
|
|
86
|
-
...declarations.map((item) => item.workflowId),
|
|
87
|
-
].map((workflowId) => ({ workflowId })),
|
|
88
|
-
];
|
|
89
|
-
}
|
|
90
68
|
async function stageCandidates(project, candidates) {
|
|
91
69
|
if (candidates.length === 0)
|
|
92
70
|
return [];
|
|
@@ -135,37 +113,6 @@ function operatorDeclaration(staged, compiled) {
|
|
|
135
113
|
functionName: workflow.workflowId.slice(workflow.workflowId.lastIndexOf("//") + 2),
|
|
136
114
|
};
|
|
137
115
|
}
|
|
138
|
-
async function copyWorkflow(settings, file) {
|
|
139
|
-
const { product, project } = settings;
|
|
140
|
-
await copyFile(join(product, "workflows", file), join(project, "workflows", file));
|
|
141
|
-
const sidecar = `${basename(file, extname(file))}.declaration.json`;
|
|
142
|
-
const declaration = await readFile(join(product, "workflows", sidecar)).catch((error) => {
|
|
143
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
144
|
-
return undefined;
|
|
145
|
-
throw error;
|
|
146
|
-
});
|
|
147
|
-
if (declaration !== undefined)
|
|
148
|
-
await writeFile(join(project, "workflows", sidecar), declaration);
|
|
149
|
-
}
|
|
150
|
-
function bundledListings(input) {
|
|
151
|
-
const { product, files, manifests, declarations } = input;
|
|
152
|
-
const byFunction = new Map(declarations.map(({ functionName, ...declaration }) => [
|
|
153
|
-
functionName,
|
|
154
|
-
declaration,
|
|
155
|
-
]));
|
|
156
|
-
return Object.entries(manifests).map(([functionName, manifest]) => {
|
|
157
|
-
const declaration = byFunction.get(functionName);
|
|
158
|
-
const file = files.find((file) => basename(file, extname(file)) === manifest.name);
|
|
159
|
-
return {
|
|
160
|
-
scope: "bundled",
|
|
161
|
-
workflowId: declaration?.workflowId ?? manifest.name,
|
|
162
|
-
file: join(product, "workflows", file ?? manifest.name),
|
|
163
|
-
admitted: true,
|
|
164
|
-
codes: [],
|
|
165
|
-
...(declaration === undefined ? {} : { declaration }),
|
|
166
|
-
};
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
116
|
async function writeManifests(project, compiled) {
|
|
170
117
|
const manifests = {};
|
|
171
118
|
for (const [functionName, manifest] of Object.entries(compiled)) {
|
|
@@ -180,21 +127,13 @@ async function writeManifests(project, compiled) {
|
|
|
180
127
|
}
|
|
181
128
|
export async function preparedProject(settings) {
|
|
182
129
|
const { product, project } = settings;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const folders = await loadWorkflowFolders({
|
|
188
|
-
...settings,
|
|
189
|
-
bundled: reservations({ files, manifests: bundled, declarations }),
|
|
190
|
-
}, { packageRoot: await workflowPackageRoot(product) });
|
|
130
|
+
await initializeProject(settings);
|
|
131
|
+
const folders = await loadWorkflowFolders(settings, {
|
|
132
|
+
packageRoot: await workflowPackageRoot(product),
|
|
133
|
+
});
|
|
191
134
|
const staged = await stageCandidates(project, folders.candidates);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const combined = [
|
|
195
|
-
...declarations,
|
|
196
|
-
...staged.map((candidate) => operatorDeclaration({ ...candidate, project }, compiled)),
|
|
197
|
-
];
|
|
135
|
+
const compiled = await compile(settings);
|
|
136
|
+
const combined = staged.map((candidate) => operatorDeclaration({ ...candidate, project }, compiled));
|
|
198
137
|
const manifests = await writeManifests(project, workflowManifests(compiled, combined));
|
|
199
138
|
await writeFile(join(project, "declarations.json"), JSON.stringify(combined));
|
|
200
139
|
await writeFile(join(project, "dependencies.json"), await workflowBundleDependencies({
|
|
@@ -206,15 +145,11 @@ export async function preparedProject(settings) {
|
|
|
206
145
|
repository: folders.repository,
|
|
207
146
|
directory: join(dirname(project), "workflow-bundles"),
|
|
208
147
|
});
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
manifests: bundled,
|
|
213
|
-
declarations,
|
|
214
|
-
});
|
|
215
|
-
workflows.push(...folders.workflows);
|
|
148
|
+
const modules = join(dirname(project), "workflow-bundles/node_modules");
|
|
149
|
+
await rm(modules, { force: true });
|
|
150
|
+
await symlink(join(project, "node_modules"), modules);
|
|
216
151
|
return {
|
|
217
|
-
workflows,
|
|
152
|
+
workflows: folders.workflows,
|
|
218
153
|
repository: folders.repository,
|
|
219
154
|
bundleDigest,
|
|
220
155
|
manifests,
|
|
@@ -45,7 +45,7 @@ export async function adoptRuntimeDirectory(input) {
|
|
|
45
45
|
}
|
|
46
46
|
async function belongsToRepository(directory, repository) {
|
|
47
47
|
try {
|
|
48
|
-
const ids = (await readdir(directory)).filter((id) => id !== ".history");
|
|
48
|
+
const ids = (await readdir(directory)).filter((id) => id !== ".history" && id !== "node_modules");
|
|
49
49
|
if (ids.length === 0)
|
|
50
50
|
return false;
|
|
51
51
|
for (const id of ids)
|