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
|
@@ -5,8 +5,7 @@ const endings = [
|
|
|
5
5
|
];
|
|
6
6
|
const review = { open: "gate.opened", close: "gate.decided" };
|
|
7
7
|
const effect = { open: "effect.requested", close: "effect.reported" };
|
|
8
|
-
const
|
|
9
|
-
export const issueSteps = [
|
|
8
|
+
export const pullRequestSteps = [
|
|
10
9
|
{ name: "Work tree", operation: "github-prepare" },
|
|
11
10
|
{ name: "Agent turn", operation: "claude-turn" },
|
|
12
11
|
{ name: "Pull request", operation: "github-open-pr" },
|
|
@@ -79,24 +78,82 @@ function operationOf(event) {
|
|
|
79
78
|
return [];
|
|
80
79
|
}
|
|
81
80
|
}
|
|
81
|
+
export function pullRequestStepOf(event) {
|
|
82
|
+
const scope = operationOf(record(event));
|
|
83
|
+
return pullRequestSteps.find((step) => step.operation === scope[4] ||
|
|
84
|
+
(step.name === "Code review" && scope[3] === "review") ||
|
|
85
|
+
(step.name === "Merge" && scope[3] === "merge"));
|
|
86
|
+
}
|
|
87
|
+
function stepEffects(events, context) {
|
|
88
|
+
const { step, round } = context;
|
|
89
|
+
const repeated = roundOperations.includes(step.operation);
|
|
90
|
+
const currentTurn = events.findLastIndex((event) => event["type"] === effect.open && operationOf(event)[4] === "claude-turn");
|
|
91
|
+
const requested = new Set(events
|
|
92
|
+
.slice(currentTurn + 1)
|
|
93
|
+
.filter((event) => event["type"] === effect.open)
|
|
94
|
+
.map((event) => record(event["payload"])["operationId"]));
|
|
95
|
+
return events.filter((event) => {
|
|
96
|
+
if (pullRequestStepOf(event) !== step)
|
|
97
|
+
return false;
|
|
98
|
+
if (!repeated)
|
|
99
|
+
return true;
|
|
100
|
+
const scope = operationOf(event);
|
|
101
|
+
return roundOperations.includes(String(scope[4]))
|
|
102
|
+
? scope[1] === round
|
|
103
|
+
: requested.has(record(event["payload"])["operationId"]);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export function stageOf(event, stages) {
|
|
107
|
+
const fields = record(event);
|
|
108
|
+
if ([effect.open, effect.close].includes(String(fields["type"]))) {
|
|
109
|
+
const operation = operationOf(fields)[4];
|
|
110
|
+
return stages.find((stage) => stage.operations?.some((name) => name === operation));
|
|
111
|
+
}
|
|
112
|
+
const payload = record(fields["payload"]);
|
|
113
|
+
return (stages.find((stage) => stage.gateIds?.some((name) => name === payload["gateId"])) ??
|
|
114
|
+
stages.find((stage) => stage.gateKinds?.some((name) => name === payload["gateKind"])));
|
|
115
|
+
}
|
|
116
|
+
function declaredSpans(events, stages) {
|
|
117
|
+
return stages.map((stage) => {
|
|
118
|
+
const index = events.findLastIndex((event) => [effect.open, review.open].includes(String(event["type"])) &&
|
|
119
|
+
stageOf(event, stages) === stage);
|
|
120
|
+
const opening = events[index];
|
|
121
|
+
const pair = opening?.["type"] === review.open ? review : effect;
|
|
122
|
+
const identity = pair === review ? "gateId" : "operationId";
|
|
123
|
+
const key = record(opening?.["payload"])[identity];
|
|
124
|
+
const closing = events
|
|
125
|
+
.slice(index + 1)
|
|
126
|
+
.findLast((event) => event["type"] === pair.close &&
|
|
127
|
+
key !== undefined &&
|
|
128
|
+
record(event["payload"])[identity] === key);
|
|
129
|
+
const payload = record(closing?.["payload"]);
|
|
130
|
+
const failed = unfinished.includes(String(payload["outcome"])) ||
|
|
131
|
+
payload["decision"] === "denied";
|
|
132
|
+
return {
|
|
133
|
+
name: stage.label,
|
|
134
|
+
startedAt: timeOf(opening),
|
|
135
|
+
endedAt: timeOf(closing),
|
|
136
|
+
ending: failed ? "failed" : "done",
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
}
|
|
82
140
|
function currentRound(events) {
|
|
83
141
|
const requested = events.findLast((event) => event["type"] === effect.open && operationOf(event)[4] === "claude-turn");
|
|
84
142
|
const round = operationOf(requested)[1];
|
|
85
143
|
return typeof round === "number" ? round : 1;
|
|
86
144
|
}
|
|
87
|
-
function spans(events,
|
|
88
|
-
if (
|
|
145
|
+
function spans(events, workflow) {
|
|
146
|
+
if (workflow.stages !== undefined)
|
|
147
|
+
return [...admission(events), ...declaredSpans(events, workflow.stages)];
|
|
148
|
+
if (workflow.pullRequest == null)
|
|
89
149
|
return ticketSpans(events);
|
|
90
150
|
const effects = events.filter((event) => [effect.open, effect.close].includes(String(event["type"])));
|
|
91
151
|
const round = currentRound(effects);
|
|
92
152
|
return [
|
|
93
153
|
...admission(events),
|
|
94
|
-
...
|
|
154
|
+
...pullRequestSteps.map((step) => {
|
|
95
155
|
const repeated = roundOperations.includes(step.operation);
|
|
96
|
-
const own = effects
|
|
97
|
-
const scope = operationOf(event);
|
|
98
|
-
return scope[4] === step.operation && (!repeated || scope[1] === round);
|
|
99
|
-
});
|
|
156
|
+
const own = stepEffects(effects, { step, round });
|
|
100
157
|
const last = own.findLast((event) => event["type"] === effect.close);
|
|
101
158
|
const outcome = String(record(last?.["payload"])["outcome"]);
|
|
102
159
|
return {
|
|
@@ -132,9 +189,9 @@ function advanced(phases) {
|
|
|
132
189
|
? { ...item, state: "active" }
|
|
133
190
|
: item);
|
|
134
191
|
}
|
|
135
|
-
export function phasesOf(events,
|
|
192
|
+
export function phasesOf(events, workflow = {}) {
|
|
136
193
|
const known = events.map(record);
|
|
137
|
-
const steps = spans(known,
|
|
194
|
+
const steps = spans(known, workflow);
|
|
138
195
|
const [end] = ends(known);
|
|
139
196
|
if (end !== undefined)
|
|
140
197
|
return [...steps.map((span) => phase(span, "failed")), end];
|
|
@@ -2,7 +2,7 @@ function observedCard(context, card) {
|
|
|
2
2
|
if (card.completionMark?.text !== "Needs attention")
|
|
3
3
|
return card;
|
|
4
4
|
const run = context.model.runs.find((run) => run.runId === card.runId);
|
|
5
|
-
if (run
|
|
5
|
+
if (run === undefined ||
|
|
6
6
|
run.state !== "completed" ||
|
|
7
7
|
run.outcome !== "blocked" ||
|
|
8
8
|
run.pullRequest === null)
|
|
@@ -23,6 +23,11 @@ function list(value) {
|
|
|
23
23
|
function count(value) {
|
|
24
24
|
return typeof value === "number" ? value : null;
|
|
25
25
|
}
|
|
26
|
+
function methodScope(method, operation) {
|
|
27
|
+
if (operation[3] === "pull-request")
|
|
28
|
+
return operation[4] === `github-${method}`;
|
|
29
|
+
return (method === "merge" && operation[3] === "merge" && operation[4] === "merge");
|
|
30
|
+
}
|
|
26
31
|
function attributed(started, operation) {
|
|
27
32
|
const stage = list(operation[5]);
|
|
28
33
|
const checked = operationIdentity({
|
|
@@ -37,8 +42,7 @@ function attributed(started, operation) {
|
|
|
37
42
|
});
|
|
38
43
|
return (checked.kind === "valid" &&
|
|
39
44
|
checked.value === JSON.stringify(operation) &&
|
|
40
|
-
|
|
41
|
-
operation[4] === `github-${text(started["method"])}` &&
|
|
45
|
+
methodScope(text(started["method"]), operation) &&
|
|
42
46
|
(started["runId"] === undefined || started["runId"] === operation[2]));
|
|
43
47
|
}
|
|
44
48
|
function githubOf(records) {
|
|
@@ -47,22 +51,28 @@ function githubOf(records) {
|
|
|
47
51
|
const result = record(records.result);
|
|
48
52
|
const completed = result["outcome"] === "completed";
|
|
49
53
|
return {
|
|
50
|
-
runId:
|
|
54
|
+
runId: started["runId"] === undefined || started["runId"] === operation[2]
|
|
55
|
+
? text(operation[2])
|
|
56
|
+
: "",
|
|
51
57
|
round: Number(operation[1]),
|
|
58
|
+
startedAt: Date.parse(text(started["startedAt"])) || 0,
|
|
52
59
|
method: text(started["method"]),
|
|
53
60
|
completed,
|
|
54
61
|
attributed: attributed(started, operation) && !Object.hasOwn(result, "reason"),
|
|
55
62
|
report: completed ? record(parsed(text(result["result"]))) : {},
|
|
56
63
|
};
|
|
57
64
|
}
|
|
58
|
-
function pullRequestOf(results) {
|
|
65
|
+
function pullRequestOf(results, repository) {
|
|
59
66
|
const opened = results
|
|
60
|
-
.filter((item) => item.method === "open-pr")
|
|
67
|
+
.filter((item) => item.method === "open-pr" && item.completed)
|
|
61
68
|
.map((item) => item.report)
|
|
62
69
|
.findLast((report) => text(report["url"]).startsWith("https://"));
|
|
63
70
|
if (opened === undefined)
|
|
64
71
|
return null;
|
|
65
|
-
|
|
72
|
+
const pull = { number: Number(opened["number"]), url: text(opened["url"]) };
|
|
73
|
+
return repository === null || prIdentity(repository, pull) !== null
|
|
74
|
+
? pull
|
|
75
|
+
: null;
|
|
66
76
|
}
|
|
67
77
|
function reviewsOf(results) {
|
|
68
78
|
return results
|
|
@@ -115,8 +125,8 @@ export function pullOf(inputs, runId) {
|
|
|
115
125
|
const results = inputs.records
|
|
116
126
|
.map(githubOf)
|
|
117
127
|
.filter((item) => item.runId === runId)
|
|
118
|
-
.toSorted((first, second) => first.round - second.round);
|
|
119
|
-
const pullRequest = pullRequestOf(results);
|
|
128
|
+
.toSorted((first, second) => first.round - second.round || first.startedAt - second.startedAt);
|
|
129
|
+
const pullRequest = pullRequestOf(results, inputs.repository);
|
|
120
130
|
const mergeProof = mergeProofOf(results, {
|
|
121
131
|
repository: inputs.repository,
|
|
122
132
|
pullRequest,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PrRepository } from "./watch-pr-identity.ts";
|
|
2
|
+
import { type ShownSchedules } from "./schedule-render.ts";
|
|
2
3
|
import { type WorkflowStages } from "../workflow-declaration.ts";
|
|
3
4
|
import { type Fields } from "../protocol.ts";
|
|
4
5
|
import { type WorkflowRefusal } from "../workflow-refusals.ts";
|
|
@@ -16,6 +17,7 @@ interface ReadRun {
|
|
|
16
17
|
readonly events: readonly ReadEvent[];
|
|
17
18
|
}
|
|
18
19
|
export interface WatchRead {
|
|
20
|
+
readonly schedules?: ShownSchedules;
|
|
19
21
|
readonly queue?: Fields;
|
|
20
22
|
readonly service: string;
|
|
21
23
|
readonly workflows: readonly string[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { establishedIssue } from "./watch-pr-identity.js";
|
|
2
|
+
import { shownSchedules } from "./schedule-render.js";
|
|
2
3
|
import { isWorkflowStages, } from "../workflow-declaration.js";
|
|
3
4
|
import { record } from "../protocol.js";
|
|
4
5
|
import { send } from "./client.js";
|
|
@@ -86,7 +87,6 @@ async function creationOf(reader, runId) {
|
|
|
86
87
|
function matchingCompletion(status, context) {
|
|
87
88
|
const issue = establishedIssue(context.repository, status["issueIdentity"]);
|
|
88
89
|
return (status.state === "completed" &&
|
|
89
|
-
status["workflowId"] === "develop-issue" &&
|
|
90
90
|
record(status["outcome"])?.["kind"] === "handoff_requested" &&
|
|
91
91
|
record(status["outcome"])?.["ticketStatus"] !== "partial" &&
|
|
92
92
|
record(status["outcome"])?.["issueUpdate"] !== "failed" &&
|
|
@@ -127,20 +127,17 @@ async function completionOf(reader, search) {
|
|
|
127
127
|
reader.completions.set(runId, { ...known, creation });
|
|
128
128
|
return { status: known.status, events: creation === null ? [] : [creation] };
|
|
129
129
|
}
|
|
130
|
-
function
|
|
131
|
-
const links = Object.fromEntries(["parentRunId", "chainId", "childRunIds"]
|
|
130
|
+
function refreshedStatus(run, status) {
|
|
131
|
+
const links = Object.fromEntries(["parentRunId", "chainId", "childRunIds", "commandWaits"]
|
|
132
132
|
.filter((name) => Object.hasOwn(status, name))
|
|
133
133
|
.map((name) => [name, status[name]]));
|
|
134
|
-
return {
|
|
135
|
-
...run,
|
|
136
|
-
status: { ...run.status, ...links },
|
|
137
|
-
};
|
|
134
|
+
return { ...run, status: { ...run.status, ...links } };
|
|
138
135
|
}
|
|
139
136
|
async function detailed(reader, summary) {
|
|
140
137
|
const { runId, latestSequence: sequence } = summary;
|
|
141
138
|
const known = reader.details.get(runId);
|
|
142
139
|
if (known !== undefined && known.sequence === sequence)
|
|
143
|
-
return
|
|
140
|
+
return refreshedStatus(known.value, summary);
|
|
144
141
|
const shown = await asked(reader, {
|
|
145
142
|
operation: "status",
|
|
146
143
|
parameters: { runId },
|
|
@@ -181,13 +178,14 @@ function stoppedIssues(runs, context) {
|
|
|
181
178
|
const stopped = runs
|
|
182
179
|
.map((run) => run.status)
|
|
183
180
|
.filter((status) => recentRun(status.latestAt, context.now) &&
|
|
184
|
-
status["workflowId"] === "develop-issue" &&
|
|
185
181
|
(["failed", "cancelled"].includes(status.state) ||
|
|
186
182
|
(status.state === "completed" &&
|
|
187
183
|
record(status["outcome"])?.["kind"] === "blocked")))
|
|
188
|
-
.toSorted((first, second) => Date.parse(second.latestAt ?? "") - Date.parse(first.latestAt ?? ""))
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
.toSorted((first, second) => Date.parse(second.latestAt ?? "") - Date.parse(first.latestAt ?? ""));
|
|
185
|
+
const retained = [
|
|
186
|
+
...Map.groupBy(stopped, (status) => status["workflowId"]).values(),
|
|
187
|
+
].flatMap((group) => group.slice(0, 20));
|
|
188
|
+
return new Set(retained.flatMap((status) => {
|
|
191
189
|
const issue = establishedIssue(context.repository, status["issueIdentity"]);
|
|
192
190
|
return status.state === "completed" && issue !== null
|
|
193
191
|
? [JSON.stringify(issue)]
|
|
@@ -213,9 +211,7 @@ async function service(reader, now) {
|
|
|
213
211
|
const issues = stoppedIssues(runs, { now, repository: reader.repository });
|
|
214
212
|
const older = issues.size === 0
|
|
215
213
|
? []
|
|
216
|
-
: summaries.filter((summary) => !recentRun(summary.latestAt, now) &&
|
|
217
|
-
summary["workflowId"] === "develop-issue" &&
|
|
218
|
-
summary.state === "completed");
|
|
214
|
+
: summaries.filter((summary) => !recentRun(summary.latestAt, now) && summary.state === "completed");
|
|
219
215
|
for (const summary of older) {
|
|
220
216
|
const candidate = await completionOf(reader, { summary, issues });
|
|
221
217
|
if (candidate !== null)
|
|
@@ -223,6 +219,9 @@ async function service(reader, now) {
|
|
|
223
219
|
}
|
|
224
220
|
return {
|
|
225
221
|
service: "up",
|
|
222
|
+
...("schedules" in status
|
|
223
|
+
? { schedules: shownSchedules(status["schedules"]) }
|
|
224
|
+
: {}),
|
|
226
225
|
...(record(status["queue"]) === undefined
|
|
227
226
|
? {}
|
|
228
227
|
: { queue: record(status["queue"]) ?? {} }),
|
|
@@ -247,11 +246,13 @@ async function singleRun(reader, runId) {
|
|
|
247
246
|
const status = statusOf(reply.result["run"]);
|
|
248
247
|
const known = reader.details.get(runId);
|
|
249
248
|
const value = known !== undefined && known.sequence === status.latestSequence
|
|
250
|
-
?
|
|
249
|
+
? refreshedStatus(known.value, status)
|
|
251
250
|
: await remember(reader, status);
|
|
251
|
+
const metadata = await asked(reader, { operation: "status", parameters: {} });
|
|
252
252
|
return {
|
|
253
253
|
...unavailable,
|
|
254
254
|
service: "up",
|
|
255
|
+
workflowStages: stageMetadata(metadata["workflowStages"]),
|
|
255
256
|
runs: [value],
|
|
256
257
|
decisions: await decisionsOf(reader),
|
|
257
258
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ShownSchedule, ShownSchedules } from "./schedule-render.ts";
|
|
2
|
+
import type { CommandWaitView } from "../authority/command-wait-view.ts";
|
|
1
3
|
import type { CommandEffect } from "../command-settings.ts";
|
|
2
4
|
import type { CardForm } from "./watch-run-input.ts";
|
|
3
5
|
import type { CiRerunNotice } from "./github-ci-progress.ts";
|
|
@@ -68,6 +70,12 @@ export interface WatchStep {
|
|
|
68
70
|
readonly startedAt: string | null;
|
|
69
71
|
}
|
|
70
72
|
export interface WatchCard {
|
|
73
|
+
readonly scheduled?: {
|
|
74
|
+
readonly slot: number;
|
|
75
|
+
};
|
|
76
|
+
readonly commandWaits?: readonly CommandWaitView[];
|
|
77
|
+
readonly spent?: number | null;
|
|
78
|
+
readonly limit?: number | null;
|
|
71
79
|
readonly awaitedRunId?: string | null;
|
|
72
80
|
readonly parentRunId?: string | null;
|
|
73
81
|
readonly chainId?: string;
|
|
@@ -95,6 +103,7 @@ export interface WatchColumn {
|
|
|
95
103
|
readonly cards: readonly WatchCard[];
|
|
96
104
|
}
|
|
97
105
|
export interface WatchWorkflow {
|
|
106
|
+
readonly schedule?: ShownSchedule;
|
|
98
107
|
readonly workflowId: string;
|
|
99
108
|
readonly activeCount: number;
|
|
100
109
|
readonly waitingCount: number;
|
|
@@ -102,6 +111,10 @@ export interface WatchWorkflow {
|
|
|
102
111
|
readonly columns: readonly WatchColumn[];
|
|
103
112
|
}
|
|
104
113
|
export interface WatchRun {
|
|
114
|
+
readonly scheduled?: {
|
|
115
|
+
readonly slot: number;
|
|
116
|
+
};
|
|
117
|
+
readonly commandWaits?: readonly CommandWaitView[];
|
|
105
118
|
readonly awaitedRunId?: string | null;
|
|
106
119
|
readonly commandEffects?: readonly CommandEffect[];
|
|
107
120
|
readonly ciReruns?: readonly CiRerunNotice[];
|
|
@@ -140,6 +153,7 @@ export interface WatchRun {
|
|
|
140
153
|
readonly mergeCommit: string | null;
|
|
141
154
|
}
|
|
142
155
|
export interface WatchModel {
|
|
156
|
+
readonly schedules?: ShownSchedules;
|
|
143
157
|
readonly queue?: Fields;
|
|
144
158
|
readonly workflowRefusals?: readonly WorkflowRefusal[];
|
|
145
159
|
readonly cardForms?: readonly CardForm[];
|
|
@@ -10,5 +10,4 @@ export interface CardForm {
|
|
|
10
10
|
readonly source: "ticket" | "prompt" | "input";
|
|
11
11
|
readonly fields: readonly CardField[];
|
|
12
12
|
}
|
|
13
|
-
export declare function builtinCardForms(): readonly CardForm[];
|
|
14
13
|
export declare function cardRunRequest(forms: readonly CardForm[], value: unknown): ServiceRequest | null;
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
import { record } from "../protocol.js";
|
|
2
|
-
export function builtinCardForms() {
|
|
3
|
-
const entries = [
|
|
4
|
-
["develop-issue", "ticket", "Issue number"],
|
|
5
|
-
["develop-ticket", "ticket", "Ticket ID"],
|
|
6
|
-
["develop-prd", "prompt", "Idea"],
|
|
7
|
-
["develop-prompt", "prompt", "Task"],
|
|
8
|
-
];
|
|
9
|
-
return entries.map(([workflowId, name, label]) => ({
|
|
10
|
-
workflowId,
|
|
11
|
-
source: name === "ticket" ? "ticket" : "prompt",
|
|
12
|
-
fields: [{ name, label, multiline: name === "prompt", optional: false }],
|
|
13
|
-
}));
|
|
14
|
-
}
|
|
15
2
|
function validFields(form, fields) {
|
|
16
3
|
if (Object.keys(fields).some((name) => !form.fields.some((field) => field.name === name)))
|
|
17
4
|
return false;
|
|
@@ -9,7 +9,7 @@ import { dirname, join } from "node:path";
|
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { watchReader } from "./watch-reads.js";
|
|
11
11
|
import { ciRerunNotices } from "./github-ci-progress.js";
|
|
12
|
-
import { turnOf, watchModel } from "./watch-model.js";
|
|
12
|
+
import { turnActivity, turnOf, watchModel, } from "./watch-model.js";
|
|
13
13
|
import { pageDocument } from "./watch-page.js";
|
|
14
14
|
import { watchStream } from "./watch-stream.js";
|
|
15
15
|
export const defaultWatchPort = 3663;
|
|
@@ -39,7 +39,7 @@ function parsed(text) {
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
const noStream = { size: 0,
|
|
42
|
+
const noStream = { size: 0, activity: [], activeAt: null };
|
|
43
43
|
async function streamed(watch, file) {
|
|
44
44
|
const found = await stat(file).catch(() => null);
|
|
45
45
|
if (found === null)
|
|
@@ -48,9 +48,9 @@ async function streamed(watch, file) {
|
|
|
48
48
|
const activeAt = found.mtime.toISOString();
|
|
49
49
|
if (known?.size === found.size)
|
|
50
50
|
return { ...known, activeAt };
|
|
51
|
-
const
|
|
52
|
-
watch.streams.set(file, { size: found.size,
|
|
53
|
-
return { size: found.size,
|
|
51
|
+
const activity = turnActivity((await readFile(file, "utf8").catch(() => "")).split("\n"));
|
|
52
|
+
watch.streams.set(file, { size: found.size, activity });
|
|
53
|
+
return { size: found.size, activity, activeAt };
|
|
54
54
|
}
|
|
55
55
|
async function stored(file) {
|
|
56
56
|
return parsed(await readFile(file, "utf8").catch(() => "null"));
|
|
@@ -67,7 +67,7 @@ async function turns(watch) {
|
|
|
67
67
|
key,
|
|
68
68
|
started,
|
|
69
69
|
result: await stored(join(journal, key, "result.json")),
|
|
70
|
-
|
|
70
|
+
activity: stream.activity,
|
|
71
71
|
activeAt: stream.activeAt,
|
|
72
72
|
}));
|
|
73
73
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { CardControls } from "./watch-card-view.ts";
|
|
2
2
|
import type { PhoneControls } from "./watch-phone-view.ts";
|
|
3
|
-
import { type StartControls } from "./watch-header-view.ts";
|
|
4
3
|
export { clove, Header } from "./watch-header-view.ts";
|
|
5
|
-
export type { StartControls } from "./watch-header-view.ts";
|
|
6
4
|
import { type Shown } from "./watch-element.ts";
|
|
7
5
|
import { type Copying, type WatchModel, type WatchPhase, type WatchRun } from "./watch-render.ts";
|
|
8
6
|
import { type ColumnControls } from "./watch-board-view.ts";
|
|
@@ -33,7 +31,6 @@ export interface PageProps {
|
|
|
33
31
|
readonly stale: boolean;
|
|
34
32
|
readonly copying: Copying;
|
|
35
33
|
readonly columns?: ColumnControls;
|
|
36
|
-
readonly start?: StartControls;
|
|
37
34
|
readonly phone?: PhoneControls | undefined;
|
|
38
35
|
}
|
|
39
36
|
export declare function RefusedWorkflows(props: {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { scheduledText } from "./schedule-render.js";
|
|
2
|
+
import { CommandWaits, effectBudgetText } from "./command-waits.js";
|
|
1
3
|
import { Header, Queue } from "./watch-header-view.js";
|
|
2
4
|
export { clove, Header } from "./watch-header-view.js";
|
|
3
5
|
import { priceText, usageText, totalsSuffix } from "./usage-render.js";
|
|
@@ -8,7 +10,6 @@ import { boardWorkflow, watchHash, watchLocation } from "./watch-navigation.js";
|
|
|
8
10
|
import { Decision, Events, Turn, PullRequest } from "./watch-turn.js";
|
|
9
11
|
import { Commands } from "./command-effects.js";
|
|
10
12
|
export { PullRequest } from "./watch-turn.js";
|
|
11
|
-
import { StartRun } from "./watch-start-view.js";
|
|
12
13
|
export function BannerDetail(props) {
|
|
13
14
|
const { run, now } = props;
|
|
14
15
|
const turn = run.turns.at(-1);
|
|
@@ -88,9 +89,7 @@ export function Review(props) {
|
|
|
88
89
|
export function Run(props) {
|
|
89
90
|
const { run, now, copying } = props;
|
|
90
91
|
const turn = run.turns.at(-1);
|
|
91
|
-
const budget = run.spent
|
|
92
|
-
? ""
|
|
93
|
-
: ` · ${String(run.spent)} of ${String(run.limit)} effect units`;
|
|
92
|
+
const budget = effectBudgetText(run.spent, run.limit);
|
|
94
93
|
const none = "No agent turn for this run.";
|
|
95
94
|
const turns = run.turns.length === 0
|
|
96
95
|
? element("p", { class: "meta", children: none })
|
|
@@ -103,13 +102,17 @@ export function Run(props) {
|
|
|
103
102
|
}),
|
|
104
103
|
]);
|
|
105
104
|
const foot = [
|
|
106
|
-
`${run.workflowId} · ${run.runId}${budget}`,
|
|
105
|
+
`${run.workflowId} · ${run.runId}${budget === "" ? "" : ` · ${budget}`}`,
|
|
107
106
|
element("br", {}),
|
|
108
107
|
`${run.code}: ${run.detail}`,
|
|
109
108
|
];
|
|
110
109
|
return [
|
|
111
110
|
component(Banner, { run, now }),
|
|
111
|
+
run.scheduled === undefined
|
|
112
|
+
? null
|
|
113
|
+
: element("p", { class: "meta", children: scheduledText(run.scheduled) }),
|
|
112
114
|
component(HandoffLinks, { run }),
|
|
115
|
+
component(CommandWaits, { waits: run.commandWaits ?? [] }),
|
|
113
116
|
element("section", {
|
|
114
117
|
class: "run-usage",
|
|
115
118
|
children: [
|
|
@@ -181,21 +184,18 @@ export function Page(props) {
|
|
|
181
184
|
return component(BoardPage, { ...props, model, workflowId, down });
|
|
182
185
|
}
|
|
183
186
|
export function BoardPage(props) {
|
|
184
|
-
const { model, workflowId, down, now, stale
|
|
187
|
+
const { model, workflowId, down, now, stale } = props;
|
|
185
188
|
const workflow = model.workflows.find((item) => item.workflowId === workflowId);
|
|
186
|
-
const start = props.start ?? { shown: false, onToggle: () => undefined };
|
|
187
189
|
return element("div", {
|
|
188
190
|
class: "board-page",
|
|
189
191
|
children: [
|
|
190
|
-
component(Header, { model, stale,
|
|
192
|
+
component(Header, { model, stale, phone: props.phone, now }),
|
|
191
193
|
component(Queue, { model }),
|
|
192
194
|
down,
|
|
195
|
+
model.schedules !== undefined && "status" in model.schedules
|
|
196
|
+
? element("p", { class: "meta", children: "Schedules: unavailable" })
|
|
197
|
+
: null,
|
|
193
198
|
component(RefusedWorkflows, { model }),
|
|
194
|
-
component(StartRun, {
|
|
195
|
-
workflows: model.workflows,
|
|
196
|
-
copying,
|
|
197
|
-
shown: start.shown,
|
|
198
|
-
}),
|
|
199
199
|
component(WorkflowList, {
|
|
200
200
|
workflows: model.workflows,
|
|
201
201
|
selected: workflowId,
|
package/dist/src/cli.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createProbeRunner } from "./cli/probe-command.js";
|
|
3
|
+
import { terminateHarness } from "./cli/harness-processes.js";
|
|
3
4
|
import { settingsEnvironment } from "./cli/project-settings.js";
|
|
5
|
+
import { constants as systemConstants } from "node:os";
|
|
4
6
|
import { addAbortListener } from "node:events";
|
|
5
7
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
6
8
|
import { existsSync, realpathSync } from "node:fs";
|
|
@@ -30,7 +32,7 @@ const watchCommand = async (request) => {
|
|
|
30
32
|
cwd: request.directory,
|
|
31
33
|
signal: request.signal,
|
|
32
34
|
killSignal: "SIGKILL",
|
|
33
|
-
maxBuffer: 1024 * 1024,
|
|
35
|
+
maxBuffer: request.outputBytes ?? 1024 * 1024,
|
|
34
36
|
encoding: "utf8",
|
|
35
37
|
}, (error, stdout) => {
|
|
36
38
|
const code = error === null ? 0 : error.code;
|
|
@@ -58,6 +60,45 @@ const watchCommand = async (request) => {
|
|
|
58
60
|
listener[Symbol.dispose]();
|
|
59
61
|
}
|
|
60
62
|
};
|
|
63
|
+
function startHarness(request) {
|
|
64
|
+
return new Promise((settle) => {
|
|
65
|
+
try {
|
|
66
|
+
const child = spawn(request.executable, request.arguments, {
|
|
67
|
+
cwd: request.directory,
|
|
68
|
+
env: request.environment,
|
|
69
|
+
stdio: "inherit",
|
|
70
|
+
});
|
|
71
|
+
const interrupt = () => undefined;
|
|
72
|
+
const terminate = () => {
|
|
73
|
+
terminateHarness(child.pid, {
|
|
74
|
+
snapshot: () => execFileSync("/bin/ps", ["-axo", "pid=,ppid="], {
|
|
75
|
+
encoding: "utf8",
|
|
76
|
+
timeout: 1000,
|
|
77
|
+
}),
|
|
78
|
+
kill: (identifier, signal) => process.kill(identifier, signal),
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const cleanup = () => {
|
|
82
|
+
process.off("SIGINT", interrupt);
|
|
83
|
+
process.off("SIGTERM", terminate);
|
|
84
|
+
};
|
|
85
|
+
process.on("SIGINT", interrupt);
|
|
86
|
+
process.on("SIGTERM", terminate);
|
|
87
|
+
child.once("error", () => {
|
|
88
|
+
cleanup();
|
|
89
|
+
settle(null);
|
|
90
|
+
});
|
|
91
|
+
child.once("close", (status, signal) => {
|
|
92
|
+
cleanup();
|
|
93
|
+
settle(status ??
|
|
94
|
+
(signal === null ? 1 : 128 + systemConstants.signals[signal]));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
settle(null);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
61
102
|
function startArguments(input) {
|
|
62
103
|
const { runtime, repository, argumentsList } = input;
|
|
63
104
|
const located = [
|
|
@@ -82,7 +123,10 @@ function startArguments(input) {
|
|
|
82
123
|
function toolProcess(input) {
|
|
83
124
|
const program = join(import.meta.dirname, "..", "tools", `${input.tool}${extname(import.meta.filename)}`);
|
|
84
125
|
const child = spawn(process.execPath, [program, ...input.argumentsList], {
|
|
85
|
-
|
|
126
|
+
detached: input.tool === "red-green-command",
|
|
127
|
+
stdio: input.tool === "red-green-command"
|
|
128
|
+
? ["inherit", "inherit", "inherit", "ipc"]
|
|
129
|
+
: "inherit",
|
|
86
130
|
env: {
|
|
87
131
|
...process.env,
|
|
88
132
|
PATH: [dirname(process.execPath), process.env["PATH"]].join(delimiter),
|
|
@@ -101,9 +145,13 @@ function toolProcess(input) {
|
|
|
101
145
|
process.on("SIGTERM", terminate);
|
|
102
146
|
return new Promise((settle) => {
|
|
103
147
|
child.once("close", (status) => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
148
|
+
setImmediate(() => {
|
|
149
|
+
setImmediate(() => {
|
|
150
|
+
process.off("SIGINT", interrupt);
|
|
151
|
+
process.off("SIGTERM", terminate);
|
|
152
|
+
settle(stopping && status === 75 ? 0 : (status ?? 1));
|
|
153
|
+
});
|
|
154
|
+
});
|
|
107
155
|
});
|
|
108
156
|
});
|
|
109
157
|
}
|
|
@@ -160,6 +208,11 @@ else if (command === "templates")
|
|
|
160
208
|
tool: "template-command",
|
|
161
209
|
argumentsList: options,
|
|
162
210
|
});
|
|
211
|
+
else if (command === "red-green")
|
|
212
|
+
process.exitCode = await toolProcess({
|
|
213
|
+
tool: "red-green-command",
|
|
214
|
+
argumentsList: options,
|
|
215
|
+
});
|
|
163
216
|
else if (command === "start")
|
|
164
217
|
process.exitCode = await start();
|
|
165
218
|
else {
|
|
@@ -174,6 +227,7 @@ else {
|
|
|
174
227
|
await main(process.argv.slice(2), {
|
|
175
228
|
repository,
|
|
176
229
|
environment,
|
|
230
|
+
startHarness,
|
|
177
231
|
doctorProbe: createProbeRunner({
|
|
178
232
|
spawn: (input) => spawn(input.executable, input.arguments, input.options),
|
|
179
233
|
kill: (identifier, signal) => process.kill(identifier, signal),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { HandoffValue, CommandWaitRequest } from "./step-bridge.ts";
|
|
2
|
+
export type { CommandWaitRequest, CommandWaitOutcome, CommandWaitProgress, } from "./step-bridge.ts";
|
|
3
|
+
export declare function isCommandWaitRequest(value: unknown): value is CommandWaitRequest;
|
|
4
|
+
export declare function commandWaitValue(value: unknown, ancestors?: ReadonlySet<unknown>): value is HandoffValue;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function isCommandWaitRequest(value) {
|
|
2
|
+
if (!dataFields(value, ["name", "input"]))
|
|
3
|
+
return false;
|
|
4
|
+
const name = Reflect.get(value, "name");
|
|
5
|
+
return (typeof name === "string" &&
|
|
6
|
+
name.length > 0 &&
|
|
7
|
+
commandWaitValue(Reflect.get(value, "input")));
|
|
8
|
+
}
|
|
9
|
+
function dataFields(value, keys) {
|
|
10
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
11
|
+
return false;
|
|
12
|
+
return (Reflect.ownKeys(value).length === keys.length &&
|
|
13
|
+
keys.every((key) => {
|
|
14
|
+
const field = Object.getOwnPropertyDescriptor(value, key);
|
|
15
|
+
return field?.enumerable === true && Object.hasOwn(field, "value");
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
export function commandWaitValue(value, ancestors = new Set()) {
|
|
19
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
20
|
+
return true;
|
|
21
|
+
if (typeof value === "number")
|
|
22
|
+
return Number.isFinite(value);
|
|
23
|
+
if (typeof value !== "object" || ancestors.has(value))
|
|
24
|
+
return false;
|
|
25
|
+
return commandWaitObject(value, ancestors);
|
|
26
|
+
}
|
|
27
|
+
function commandWaitObject(value, ancestors) {
|
|
28
|
+
const prototype = Object.getPrototypeOf(value);
|
|
29
|
+
const expectedPrototype = Object.getPrototypeOf(Array.isArray(value) ? [] : {});
|
|
30
|
+
if (prototype !== expectedPrototype && prototype !== null)
|
|
31
|
+
return false;
|
|
32
|
+
const keys = Array.isArray(value)
|
|
33
|
+
? Array.from({ length: value.length }, (_, index) => String(index))
|
|
34
|
+
: Object.keys(value);
|
|
35
|
+
const ownKeys = Reflect.ownKeys(value).filter((key) => !Array.isArray(value) || key !== "length");
|
|
36
|
+
if (keys.length !== ownKeys.length)
|
|
37
|
+
return false;
|
|
38
|
+
const seen = new Set([...ancestors, value]);
|
|
39
|
+
return keys.every((key) => {
|
|
40
|
+
const field = Object.getOwnPropertyDescriptor(value, key);
|
|
41
|
+
return (field?.enumerable === true &&
|
|
42
|
+
Object.hasOwn(field, "value") &&
|
|
43
|
+
commandWaitValue(field.value, seen));
|
|
44
|
+
});
|
|
45
|
+
}
|