cloving 1.0.1 → 1.2.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 +251 -20
- package/assets/watch-fonts/fira-code-OFL.md +93 -0
- package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-OFL.md +93 -0
- package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
- package/assets/watch-fonts/manifest.json +112 -0
- package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
- package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
- package/dist/src/authority/commands.d.ts +1 -0
- package/dist/src/authority/commands.js +33 -4
- package/dist/src/authority/dispatch-decision.d.ts +1 -0
- package/dist/src/authority/dispatch-decision.js +7 -4
- package/dist/src/authority/events.js +2 -2
- package/dist/src/authority/handoff-chain.d.ts +24 -0
- package/dist/src/authority/handoff-chain.js +46 -0
- package/dist/src/authority/reconciliation.d.ts +3 -0
- package/dist/src/authority/reconciliation.js +21 -5
- package/dist/src/authority/transitions.d.ts +1 -1
- package/dist/src/authority/transitions.js +5 -0
- package/dist/src/broker/actor/authority-queries.d.ts +4 -0
- package/dist/src/broker/actor/authority-queries.js +51 -2
- package/dist/src/broker/actor/authority-session.d.ts +2 -0
- package/dist/src/broker/actor/authority-session.js +3 -4
- package/dist/src/broker/actor/delivery-intents.d.ts +1 -0
- package/dist/src/broker/actor/effect-delivery.js +9 -0
- package/dist/src/broker/authority-admission.js +13 -5
- package/dist/src/chats/analysis-batches.d.ts +42 -0
- package/dist/src/chats/analysis-batches.js +97 -0
- package/dist/src/chats/analysis-checkpoint.d.ts +29 -0
- package/dist/src/chats/analysis-checkpoint.js +31 -0
- package/dist/src/chats/analysis-contract.d.ts +80 -0
- package/dist/src/chats/analysis-contract.js +16 -0
- package/dist/src/chats/analysis-data.d.ts +3 -0
- package/dist/src/chats/analysis-data.js +85 -0
- package/dist/src/chats/analysis-input.d.ts +18 -0
- package/dist/src/chats/analysis-input.js +97 -0
- package/dist/src/chats/analysis-links.d.ts +2 -0
- package/dist/src/chats/analysis-links.js +25 -0
- package/dist/src/chats/analysis-merge.d.ts +16 -0
- package/dist/src/chats/analysis-merge.js +134 -0
- package/dist/src/chats/analysis-prompt.d.ts +18 -0
- package/dist/src/chats/analysis-prompt.js +42 -0
- package/dist/src/chats/analysis-proposal.d.ts +4 -0
- package/dist/src/chats/analysis-proposal.js +41 -0
- package/dist/src/chats/analysis-provenance.d.ts +4 -0
- package/dist/src/chats/analysis-provenance.js +99 -0
- package/dist/src/chats/analysis-shape.d.ts +4 -0
- package/dist/src/chats/analysis-shape.js +235 -0
- package/dist/src/chats/analysis.d.ts +3 -0
- package/dist/src/chats/analysis.js +42 -0
- package/dist/src/chats/corpus-input.d.ts +5 -0
- package/dist/src/chats/corpus-input.js +288 -0
- package/dist/src/chats/destination.js +3 -1
- package/dist/src/chats/import.d.ts +8 -6
- package/dist/src/chats/import.js +195 -23
- package/dist/src/chats/input-file.d.ts +3 -0
- package/dist/src/chats/input-file.js +32 -0
- package/dist/src/chats/normalize.d.ts +11 -2
- package/dist/src/chats/normalize.js +18 -2
- package/dist/src/chats/previous-import.d.ts +5 -0
- package/dist/src/chats/previous-import.js +34 -0
- package/dist/src/chats/reader.d.ts +7 -1
- package/dist/src/chats/reader.js +46 -15
- package/dist/src/chats/redact-import.d.ts +14 -0
- package/dist/src/chats/redact-import.js +141 -0
- package/dist/src/chats/redaction.d.ts +41 -0
- package/dist/src/chats/redaction.js +161 -0
- package/dist/src/chats/reference-mapping.d.ts +16 -0
- package/dist/src/chats/reference-mapping.js +153 -0
- package/dist/src/chats/revision-input.d.ts +3 -0
- package/dist/src/chats/revision-input.js +180 -0
- package/dist/src/chats/revision.d.ts +48 -0
- package/dist/src/chats/revision.js +94 -0
- package/dist/src/chats/selection.d.ts +25 -0
- package/dist/src/chats/selection.js +109 -0
- package/dist/src/chats/source.d.ts +23 -0
- package/dist/src/chats/source.js +135 -0
- package/dist/src/chats/structure.d.ts +57 -0
- package/dist/src/chats/structure.js +216 -0
- package/dist/src/chats/workflow-generation-projection.d.ts +8 -0
- package/dist/src/chats/workflow-generation-projection.js +75 -0
- package/dist/src/chats/workflow-generation-source.d.ts +25 -0
- package/dist/src/chats/workflow-generation-source.js +124 -0
- package/dist/src/chats/workflow-generation-text.d.ts +23 -0
- package/dist/src/chats/workflow-generation-text.js +74 -0
- package/dist/src/chats/workflow-generation.d.ts +69 -0
- package/dist/src/chats/workflow-generation.js +142 -0
- package/dist/src/cli/arguments.d.ts +8 -0
- package/dist/src/cli/arguments.js +94 -13
- package/dist/src/cli/client.d.ts +1 -0
- package/dist/src/cli/client.js +38 -1
- package/dist/src/cli/commands.d.ts +3 -0
- package/dist/src/cli/commands.js +29 -11
- package/dist/src/cli/main.d.ts +2 -1
- package/dist/src/cli/main.js +61 -14
- package/dist/src/cli/render.d.ts +3 -0
- package/dist/src/cli/render.js +122 -7
- package/dist/src/cli/usage-render.d.ts +3 -0
- package/dist/src/cli/usage-render.js +28 -0
- package/dist/src/cli/usage.d.ts +3 -2
- package/dist/src/cli/usage.js +42 -7
- package/dist/src/cli/watch-actions.d.ts +10 -0
- package/dist/src/cli/watch-actions.js +58 -0
- package/dist/src/cli/watch-board-view.d.ts +44 -1
- package/dist/src/cli/watch-board-view.js +137 -18
- package/dist/src/cli/watch-board.d.ts +7 -1
- package/dist/src/cli/watch-board.js +48 -11
- package/dist/src/cli/watch-card-client.d.ts +1 -0
- package/dist/src/cli/watch-card-client.js +44 -0
- package/dist/src/cli/watch-card-forms.d.ts +2 -0
- package/dist/src/cli/watch-card-forms.js +33 -0
- package/dist/src/cli/watch-card-reply.d.ts +5 -0
- package/dist/src/cli/watch-card-reply.js +34 -0
- package/dist/src/cli/watch-card-view.d.ts +40 -0
- package/dist/src/cli/watch-card-view.js +86 -0
- package/dist/src/cli/watch-client.d.ts +1 -0
- package/dist/src/cli/watch-client.js +122 -0
- package/dist/src/cli/watch-model.d.ts +10 -4
- package/dist/src/cli/watch-model.js +61 -67
- package/dist/src/cli/watch-page.d.ts +1 -1
- package/dist/src/cli/watch-page.js +89 -86
- package/dist/src/cli/watch-phases.js +3 -3
- package/dist/src/cli/watch-pr-board.d.ts +3 -0
- package/dist/src/cli/watch-pr-board.js +26 -0
- package/dist/src/cli/watch-pr-command.d.ts +18 -0
- package/dist/src/cli/watch-pr-command.js +74 -0
- package/dist/src/cli/watch-pr-identity.d.ts +32 -0
- package/dist/src/cli/watch-pr-identity.js +87 -0
- package/dist/src/cli/watch-pr-observer.d.ts +20 -0
- package/dist/src/cli/watch-pr-observer.js +150 -0
- package/dist/src/cli/watch-pr-settings.d.ts +8 -0
- package/dist/src/cli/watch-pr-settings.js +47 -0
- package/dist/src/cli/watch-pull-request.d.ts +10 -0
- package/dist/src/cli/watch-pull-request.js +130 -0
- package/dist/src/cli/watch-reads.d.ts +7 -1
- package/dist/src/cli/watch-reads.js +44 -18
- package/dist/src/cli/watch-render.d.ts +29 -2
- package/dist/src/cli/watch-render.js +11 -3
- package/dist/src/cli/watch-run-input.d.ts +14 -0
- package/dist/src/cli/watch-run-input.js +49 -0
- package/dist/src/cli/watch-server.d.ts +3 -0
- package/dist/src/cli/watch-server.js +77 -8
- package/dist/src/cli/watch-start-view.d.ts +18 -0
- package/dist/src/cli/watch-start-view.js +77 -0
- package/dist/src/cli/watch-turn.d.ts +1 -0
- package/dist/src/cli/watch-turn.js +8 -3
- package/dist/src/cli/watch-usage.d.ts +6 -0
- package/dist/src/cli/watch-usage.js +65 -0
- package/dist/src/cli/watch-view.d.ts +22 -0
- package/dist/src/cli/watch-view.js +111 -19
- package/dist/src/cli.js +48 -6
- package/dist/src/daemon/analysis-launch.d.ts +5 -0
- package/dist/src/daemon/analysis-launch.js +81 -0
- package/dist/src/daemon/analysis-token.d.ts +6 -0
- package/dist/src/daemon/analysis-token.js +88 -0
- package/dist/src/daemon/analysis-worker.d.ts +20 -0
- package/dist/src/daemon/analysis-worker.js +271 -0
- package/dist/src/daemon/chats-batch-binding.d.ts +23 -0
- package/dist/src/daemon/chats-batch-binding.js +99 -0
- package/dist/src/daemon/chats-capture.d.ts +18 -0
- package/dist/src/daemon/chats-capture.js +194 -0
- package/dist/src/daemon/chats-checkpoints.d.ts +41 -0
- package/dist/src/daemon/chats-checkpoints.js +186 -0
- package/dist/src/daemon/chats-files.d.ts +42 -0
- package/dist/src/daemon/chats-files.js +252 -0
- package/dist/src/daemon/chats-follow-up.d.ts +38 -0
- package/dist/src/daemon/chats-follow-up.js +180 -0
- package/dist/src/daemon/chats-question.d.ts +15 -0
- package/dist/src/daemon/chats-question.js +93 -0
- package/dist/src/daemon/chats-worker.d.ts +13 -0
- package/dist/src/daemon/chats-worker.js +201 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -0
- package/dist/src/daemon/claude-turn.js +96 -15
- package/dist/src/daemon/claude-worker.d.ts +2 -1
- package/dist/src/daemon/claude-worker.js +92 -45
- package/dist/src/daemon/codex-stream.js +16 -0
- package/dist/src/daemon/daemon.d.ts +8 -1
- package/dist/src/daemon/daemon.js +51 -14
- package/dist/src/daemon/delivery-router.js +6 -0
- package/dist/src/daemon/github-calls.d.ts +4 -0
- package/dist/src/daemon/github-calls.js +16 -7
- package/dist/src/daemon/github-ci.d.ts +6 -0
- package/dist/src/daemon/github-ci.js +170 -0
- package/dist/src/daemon/github-merge-call.d.ts +14 -0
- package/dist/src/daemon/github-merge-call.js +73 -0
- package/dist/src/daemon/github-merge.d.ts +3 -5
- package/dist/src/daemon/github-merge.js +52 -95
- package/dist/src/daemon/github-methods.d.ts +28 -0
- package/dist/src/daemon/github-methods.js +84 -16
- package/dist/src/daemon/github-operation.d.ts +2 -0
- package/dist/src/daemon/github-retry.d.ts +6 -0
- package/dist/src/daemon/github-retry.js +49 -0
- package/dist/src/daemon/github-ticket-status.d.ts +3 -0
- package/dist/src/daemon/github-ticket-status.js +294 -0
- package/dist/src/daemon/github-worker.js +2 -1
- package/dist/src/daemon/handoff-worker.d.ts +10 -0
- package/dist/src/daemon/handoff-worker.js +232 -0
- package/dist/src/daemon/issue-queue.d.ts +2 -0
- package/dist/src/daemon/issue-queue.js +186 -43
- package/dist/src/daemon/notify-journal.d.ts +18 -0
- package/dist/src/daemon/notify-journal.js +108 -0
- package/dist/src/daemon/notify-message.js +2 -1
- package/dist/src/daemon/notify.js +176 -37
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/operation-journal.js +4 -2
- package/dist/src/daemon/retrying-turn.d.ts +12 -0
- package/dist/src/daemon/retrying-turn.js +231 -0
- package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
- package/dist/src/daemon/ticket-dependency-waits.js +60 -0
- package/dist/src/daemon/ticket-registration.d.ts +8 -4
- package/dist/src/daemon/ticket-registration.js +76 -22
- package/dist/src/daemon/ticket-source.d.ts +4 -1
- package/dist/src/daemon/ticket-source.js +8 -2
- package/dist/src/decision-visibility.d.ts +2 -0
- package/dist/src/decision-visibility.js +10 -0
- package/dist/src/execution-pin.d.ts +4 -0
- package/dist/src/execution-pin.js +23 -2
- package/dist/src/host/start-delivery.d.ts +2 -1
- package/dist/src/host/start-delivery.js +1 -1
- package/dist/src/host/step-bridge-host.d.ts +1 -0
- package/dist/src/host/step-bridge-host.js +16 -0
- package/dist/src/host/world-reconciliation.d.ts +3 -0
- package/dist/src/host/world-reconciliation.js +9 -0
- package/dist/src/host/world-runs.js +14 -1
- package/dist/src/issue-identity.d.ts +6 -0
- package/dist/src/issue-identity.js +23 -0
- package/dist/src/service/queue-runs.d.ts +3 -0
- package/dist/src/service/queue-runs.js +39 -0
- package/dist/src/service/registry.d.ts +8 -2
- package/dist/src/service/registry.js +20 -8
- package/dist/src/service/replies.d.ts +2 -0
- package/dist/src/service/run-handlers.js +54 -30
- package/dist/src/service/service.d.ts +3 -0
- package/dist/src/service/service.js +3 -1
- package/dist/src/service/submission-handler.d.ts +1 -0
- package/dist/src/service/submission-handler.js +92 -13
- package/dist/src/service/worker-totals.d.ts +3 -0
- package/dist/src/service/worker-totals.js +52 -0
- package/dist/src/step-bridge.d.ts +9 -0
- package/dist/src/steps/handoff.d.ts +2 -0
- package/dist/src/steps/handoff.js +13 -0
- package/dist/src/ticket-dependencies.d.ts +1 -0
- package/dist/src/ticket-dependencies.js +15 -0
- package/dist/src/ticket-status.d.ts +8 -0
- package/dist/src/ticket-status.js +48 -0
- package/dist/src/token-prices.d.ts +21 -0
- package/dist/src/token-prices.js +108 -0
- package/dist/src/worker-usage.d.ts +31 -0
- package/dist/src/worker-usage.js +44 -0
- package/dist/src/workflow-admission.d.ts +1 -0
- package/dist/src/workflow-candidate.d.ts +29 -0
- package/dist/src/workflow-candidate.js +60 -0
- package/dist/src/workflow-contracts.d.ts +4 -0
- package/dist/src/workflow-contracts.js +32 -5
- package/dist/src/workflow-declaration.d.ts +12 -1
- package/dist/src/workflow-declaration.js +71 -13
- package/dist/src/workflow-discovery.d.ts +18 -0
- package/dist/src/workflow-discovery.js +33 -0
- package/dist/src/workflow-refusals.d.ts +6 -0
- package/dist/src/workflow-refusals.js +39 -0
- package/dist/src/workflows.d.ts +3 -1
- package/dist/src/workflows.js +2 -0
- package/dist/tools/admit-workflow.d.ts +13 -0
- package/dist/tools/admit-workflow.js +94 -0
- package/dist/tools/self-host-processes.js +7 -2
- package/dist/tools/self-host-project.d.ts +17 -0
- package/dist/tools/self-host-project.js +177 -0
- package/dist/tools/self-host-workflows.d.ts +1 -1
- package/dist/tools/self-host-workflows.js +2 -2
- package/dist/tools/self-host.js +30 -84
- package/dist/tools/self-update-host.d.ts +7 -0
- package/dist/tools/self-update-host.js +48 -1
- package/dist/tools/self-update.d.ts +1 -0
- package/dist/tools/self-update.js +85 -13
- package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
- package/dist/tools/workflow-bundle-dependencies.js +93 -0
- package/dist/tools/workflow-bundles.d.ts +20 -0
- package/dist/tools/workflow-bundles.js +173 -0
- package/dist/tools/workflow-candidate-counts.d.ts +8 -0
- package/dist/tools/workflow-candidate-counts.js +251 -0
- package/dist/tools/workflow-candidate-program.d.ts +11 -0
- package/dist/tools/workflow-candidate-program.js +78 -0
- package/dist/tools/workflow-candidate-source.d.ts +6 -0
- package/dist/tools/workflow-candidate-source.js +107 -0
- package/dist/tools/workflow-candidate-syntax.d.ts +15 -0
- package/dist/tools/workflow-candidate-syntax.js +219 -0
- package/dist/tools/workflow-folder-capture.d.ts +17 -0
- package/dist/tools/workflow-folder-capture.js +216 -0
- package/dist/tools/workflow-folders.d.ts +33 -0
- package/dist/tools/workflow-folders.js +150 -0
- package/dist/tools/workflow-source-effects.d.ts +10 -0
- package/dist/tools/workflow-source-effects.js +213 -0
- package/dist/workflows/analyze-chats.d.ts +15 -0
- package/dist/workflows/analyze-chats.declaration.json +22 -0
- package/dist/workflows/analyze-chats.js +250 -0
- package/dist/workflows/develop-prd.js +71 -3
- package/dist/workflows/develop-ticket.d.ts +1 -7
- package/dist/workflows/develop-ticket.js +47 -141
- package/dist/workflows/issue-labels.d.ts +3 -1
- package/dist/workflows/issue-labels.js +34 -0
- package/dist/workflows/ticket-prompts.d.ts +35 -0
- package/dist/workflows/ticket-prompts.js +143 -0
- package/package.json +25 -5
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { prArguments, prCommandResult, } from "./watch-pr-command.js";
|
|
2
|
+
import { prIdentity, } from "./watch-pr-identity.js";
|
|
3
|
+
const systemClock = {
|
|
4
|
+
now: Date.now,
|
|
5
|
+
after: (expired, milliseconds) => {
|
|
6
|
+
const timer = setTimeout(expired, milliseconds);
|
|
7
|
+
return () => {
|
|
8
|
+
clearTimeout(timer);
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
function observationOf(entry, reply) {
|
|
13
|
+
const { result, now } = reply;
|
|
14
|
+
if (result.status !== "unavailable")
|
|
15
|
+
return { ...result, lastSuccess: { status: result.status, readAt: now } };
|
|
16
|
+
const lastSuccess = entry.result?.lastSuccess;
|
|
17
|
+
return { ...result, ...(lastSuccess === undefined ? {} : { lastSuccess }) };
|
|
18
|
+
}
|
|
19
|
+
async function readPr(state, attempt) {
|
|
20
|
+
const { entry, controller } = attempt;
|
|
21
|
+
const deadline = { expired: false };
|
|
22
|
+
const timeout = { status: "unavailable", failure: "timeout" };
|
|
23
|
+
const cancel = state.clock.after(() => {
|
|
24
|
+
deadline.expired = true;
|
|
25
|
+
entry.result = observationOf(entry, {
|
|
26
|
+
result: timeout,
|
|
27
|
+
now: state.clock.now(),
|
|
28
|
+
});
|
|
29
|
+
controller.abort();
|
|
30
|
+
}, 5000);
|
|
31
|
+
controller.signal.addEventListener("abort", cancel, { once: true });
|
|
32
|
+
try {
|
|
33
|
+
const output = await Promise.resolve().then(() => state.settings.execute({
|
|
34
|
+
command: [
|
|
35
|
+
...(state.settings.command ?? []),
|
|
36
|
+
...prArguments(entry.identity),
|
|
37
|
+
],
|
|
38
|
+
directory: state.settings.directory,
|
|
39
|
+
signal: controller.signal,
|
|
40
|
+
}));
|
|
41
|
+
return deadline.expired
|
|
42
|
+
? timeout
|
|
43
|
+
: prCommandResult(entry.identity, { output, now: state.clock.now() });
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return deadline.expired
|
|
47
|
+
? timeout
|
|
48
|
+
: { status: "unavailable", failure: "command" };
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
cancel();
|
|
52
|
+
controller.signal.removeEventListener("abort", cancel);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function pump(state) {
|
|
56
|
+
if (state.closed || state.active !== null)
|
|
57
|
+
return;
|
|
58
|
+
state.wake?.();
|
|
59
|
+
state.wake = null;
|
|
60
|
+
if (state.queue.length === 0)
|
|
61
|
+
return;
|
|
62
|
+
const now = state.clock.now();
|
|
63
|
+
const position = state.queue.findIndex((entry) => (state.retryDeadlines.get(entry.identity.host) ?? 0) <= now);
|
|
64
|
+
if (position === -1) {
|
|
65
|
+
const next = Math.min(...state.queue.map((entry) => state.retryDeadlines.get(entry.identity.host) ?? now));
|
|
66
|
+
state.wake = state.clock.after(() => {
|
|
67
|
+
state.wake = null;
|
|
68
|
+
pump(state);
|
|
69
|
+
}, Math.min(next - now, 2147483647));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const [entry] = state.queue.splice(position, 1);
|
|
73
|
+
if (entry === undefined)
|
|
74
|
+
return;
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
state.active = controller;
|
|
77
|
+
entry.started = state.clock.now();
|
|
78
|
+
const completed = (result) => {
|
|
79
|
+
if (state.closed)
|
|
80
|
+
return;
|
|
81
|
+
entry.result = observationOf(entry, { result, now: state.clock.now() });
|
|
82
|
+
if (result.status === "unavailable" && result.failure === "rate-limit")
|
|
83
|
+
state.retryDeadlines.set(entry.identity.host, Math.max(state.retryDeadlines.get(entry.identity.host) ?? 0, result.retryAt ?? state.clock.now() + 60000));
|
|
84
|
+
entry.pending = false;
|
|
85
|
+
state.active = null;
|
|
86
|
+
pump(state);
|
|
87
|
+
};
|
|
88
|
+
readPr(state, { entry, controller }).then(completed, () => {
|
|
89
|
+
completed({ status: "unavailable", failure: "command" });
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function currentObservation(state, entry) {
|
|
93
|
+
const result = entry.result ?? { status: "pending" };
|
|
94
|
+
const retryAt = state.retryDeadlines.get(entry.identity.host) ?? 0;
|
|
95
|
+
return retryAt > state.clock.now() ? { ...result, retryAt } : result;
|
|
96
|
+
}
|
|
97
|
+
function needsRead(entry, now) {
|
|
98
|
+
if (entry === undefined)
|
|
99
|
+
return true;
|
|
100
|
+
if (entry.pending || entry.result?.status === "merged")
|
|
101
|
+
return false;
|
|
102
|
+
return now - entry.started >= 60000;
|
|
103
|
+
}
|
|
104
|
+
export function prObserver(settings) {
|
|
105
|
+
const state = {
|
|
106
|
+
settings,
|
|
107
|
+
clock: settings.clock ?? systemClock,
|
|
108
|
+
cache: new Map(),
|
|
109
|
+
queue: [],
|
|
110
|
+
retryDeadlines: new Map(),
|
|
111
|
+
wake: null,
|
|
112
|
+
active: null,
|
|
113
|
+
closed: false,
|
|
114
|
+
};
|
|
115
|
+
const configured = settings.repository !== null && Boolean(settings.command?.[0]?.trim());
|
|
116
|
+
const observe = (pullRequest) => {
|
|
117
|
+
if (!configured)
|
|
118
|
+
return { status: "unavailable", failure: "configuration" };
|
|
119
|
+
const identity = prIdentity(settings.repository, pullRequest);
|
|
120
|
+
if (identity === null)
|
|
121
|
+
return { status: "unavailable", failure: "identity" };
|
|
122
|
+
const key = JSON.stringify(identity);
|
|
123
|
+
const known = state.cache.get(key);
|
|
124
|
+
if (state.closed || !needsRead(known, state.clock.now()))
|
|
125
|
+
return known === undefined
|
|
126
|
+
? { status: "pending" }
|
|
127
|
+
: currentObservation(state, known);
|
|
128
|
+
const entry = {
|
|
129
|
+
identity,
|
|
130
|
+
result: known?.result ?? null,
|
|
131
|
+
started: Infinity,
|
|
132
|
+
pending: true,
|
|
133
|
+
};
|
|
134
|
+
state.cache.set(key, entry);
|
|
135
|
+
state.queue.push(entry);
|
|
136
|
+
pump(state);
|
|
137
|
+
return currentObservation(state, entry);
|
|
138
|
+
};
|
|
139
|
+
return {
|
|
140
|
+
repository: settings.repository,
|
|
141
|
+
observe,
|
|
142
|
+
close: () => {
|
|
143
|
+
state.closed = true;
|
|
144
|
+
state.queue.length = 0;
|
|
145
|
+
state.wake?.();
|
|
146
|
+
state.wake = null;
|
|
147
|
+
state.active?.abort();
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WatchCommandRunner } from "./watch-pr-command.ts";
|
|
2
|
+
import { type PrObserver } from "./watch-pr-observer.ts";
|
|
3
|
+
export interface WatchPrSettings {
|
|
4
|
+
readonly repository: string | undefined;
|
|
5
|
+
readonly githubCommand: string | undefined;
|
|
6
|
+
readonly execute: WatchCommandRunner;
|
|
7
|
+
}
|
|
8
|
+
export declare function watchPrObserver(settings: WatchPrSettings): Promise<PrObserver>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { prRepository } from "./watch-pr-identity.js";
|
|
2
|
+
import { prObserver } from "./watch-pr-observer.js";
|
|
3
|
+
function githubCommand(given) {
|
|
4
|
+
if (given === undefined)
|
|
5
|
+
return ["gh"];
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(given);
|
|
8
|
+
if (!Array.isArray(parsed) ||
|
|
9
|
+
!parsed.every((part) => typeof part === "string"))
|
|
10
|
+
return null;
|
|
11
|
+
return parsed.map(String);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function watchPrObserver(settings) {
|
|
18
|
+
const directory = settings.repository;
|
|
19
|
+
const command = githubCommand(settings.githubCommand);
|
|
20
|
+
const output = directory === undefined
|
|
21
|
+
? null
|
|
22
|
+
: await settings
|
|
23
|
+
.execute({
|
|
24
|
+
command: [
|
|
25
|
+
"git",
|
|
26
|
+
"config",
|
|
27
|
+
"--local",
|
|
28
|
+
"--get-all",
|
|
29
|
+
"remote.origin.url",
|
|
30
|
+
],
|
|
31
|
+
directory,
|
|
32
|
+
signal: AbortSignal.timeout(5000),
|
|
33
|
+
})
|
|
34
|
+
.catch(() => null);
|
|
35
|
+
if (output?.errorCode === "ENOENT" || output?.errorCode === "EACCES")
|
|
36
|
+
return {
|
|
37
|
+
observe: () => ({ status: "unavailable", failure: "command" }),
|
|
38
|
+
close: () => undefined,
|
|
39
|
+
};
|
|
40
|
+
const repository = output?.status === 0 ? prRepository(output.stdout) : null;
|
|
41
|
+
return prObserver({
|
|
42
|
+
repository,
|
|
43
|
+
directory: directory ?? "",
|
|
44
|
+
command,
|
|
45
|
+
execute: settings.execute,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WatchRun } from "./watch-render.ts";
|
|
2
|
+
import { type PrRepository } from "./watch-pr-identity.ts";
|
|
3
|
+
export interface GithubRecords {
|
|
4
|
+
readonly started: unknown;
|
|
5
|
+
readonly result: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function pullOf(inputs: {
|
|
8
|
+
readonly records: readonly GithubRecords[];
|
|
9
|
+
readonly repository: PrRepository | null;
|
|
10
|
+
}, runId: string): Pick<WatchRun, "pullRequest" | "reviews" | "mergeCommit" | "mergeProof">;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { operationIdentity } from "../operation-identity.js";
|
|
2
|
+
import { prIdentity, } from "./watch-pr-identity.js";
|
|
3
|
+
function record(value) {
|
|
4
|
+
return typeof value === "object" && value !== null ? { ...value } : {};
|
|
5
|
+
}
|
|
6
|
+
function text(value) {
|
|
7
|
+
return typeof value === "string" ? value : "";
|
|
8
|
+
}
|
|
9
|
+
function stamp(value) {
|
|
10
|
+
return typeof value === "string" ? value : null;
|
|
11
|
+
}
|
|
12
|
+
function parsed(line) {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(line);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function list(value) {
|
|
21
|
+
return Array.isArray(value) ? value.map((item) => item) : [];
|
|
22
|
+
}
|
|
23
|
+
function count(value) {
|
|
24
|
+
return typeof value === "number" ? value : null;
|
|
25
|
+
}
|
|
26
|
+
function attributed(started, operation) {
|
|
27
|
+
const stage = list(operation[5]);
|
|
28
|
+
const checked = operationIdentity({
|
|
29
|
+
round: operation[1],
|
|
30
|
+
run: operation[2],
|
|
31
|
+
phase: operation[3],
|
|
32
|
+
operation: operation[4],
|
|
33
|
+
...(operation[5] === null
|
|
34
|
+
? {}
|
|
35
|
+
: { stage: { id: stage[0], visit: stage[1], branch: stage[2] } }),
|
|
36
|
+
...(operation[6] === null ? {} : { key: operation[6] }),
|
|
37
|
+
});
|
|
38
|
+
return (checked.kind === "valid" &&
|
|
39
|
+
checked.value === JSON.stringify(operation) &&
|
|
40
|
+
operation[3] === "pull-request" &&
|
|
41
|
+
operation[4] === `github-${text(started["method"])}` &&
|
|
42
|
+
(started["runId"] === undefined || started["runId"] === operation[2]));
|
|
43
|
+
}
|
|
44
|
+
function githubOf(records) {
|
|
45
|
+
const started = record(records.started);
|
|
46
|
+
const operation = list(parsed(text(started["operationId"])));
|
|
47
|
+
const result = record(records.result);
|
|
48
|
+
const completed = result["outcome"] === "completed";
|
|
49
|
+
return {
|
|
50
|
+
runId: text(operation[2]),
|
|
51
|
+
round: Number(operation[1]),
|
|
52
|
+
method: text(started["method"]),
|
|
53
|
+
completed,
|
|
54
|
+
attributed: attributed(started, operation) && !Object.hasOwn(result, "reason"),
|
|
55
|
+
report: completed ? record(parsed(text(result["result"]))) : {},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function pullRequestOf(results) {
|
|
59
|
+
const opened = results
|
|
60
|
+
.filter((item) => item.method === "open-pr")
|
|
61
|
+
.map((item) => item.report)
|
|
62
|
+
.findLast((report) => text(report["url"]).startsWith("https://"));
|
|
63
|
+
if (opened === undefined)
|
|
64
|
+
return null;
|
|
65
|
+
return { number: Number(opened["number"]), url: text(opened["url"]) };
|
|
66
|
+
}
|
|
67
|
+
function reviewsOf(results) {
|
|
68
|
+
return results
|
|
69
|
+
.filter((item) => item.method === "wait-review")
|
|
70
|
+
.flatMap(({ round, report }) => {
|
|
71
|
+
const score = count(report["score"]);
|
|
72
|
+
if (score === null)
|
|
73
|
+
return [];
|
|
74
|
+
return [{ round, commit: text(report["commit"]), score }];
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function mergeCommitOf(results) {
|
|
78
|
+
const merged = results
|
|
79
|
+
.filter((item) => item.method === "merge")
|
|
80
|
+
.map((item) => stamp(item.report["merged"]));
|
|
81
|
+
return merged.find((commit) => commit !== null) ?? null;
|
|
82
|
+
}
|
|
83
|
+
function mergeProofOf(results, context) {
|
|
84
|
+
if (context.repository === null || context.pullRequest === null)
|
|
85
|
+
return null;
|
|
86
|
+
const repository = context.repository;
|
|
87
|
+
const identity = prIdentity(repository, context.pullRequest);
|
|
88
|
+
if (identity === null)
|
|
89
|
+
return null;
|
|
90
|
+
const opened = results.filter((item) => item.method === "open-pr" && item.completed && item.attributed);
|
|
91
|
+
if (opened.length === 0 ||
|
|
92
|
+
opened.some(({ report }) => report["number"] !== identity.number ||
|
|
93
|
+
typeof report["url"] !== "string" ||
|
|
94
|
+
prIdentity(repository, {
|
|
95
|
+
number: identity.number,
|
|
96
|
+
url: report["url"],
|
|
97
|
+
}) === null))
|
|
98
|
+
return null;
|
|
99
|
+
const merged = results.filter((item) => item.method === "merge" &&
|
|
100
|
+
item.completed &&
|
|
101
|
+
item.attributed &&
|
|
102
|
+
(typeof item.report["reason"] !== "string" ||
|
|
103
|
+
Object.hasOwn(item.report, "merged")));
|
|
104
|
+
if (merged.length === 0 ||
|
|
105
|
+
merged.some(({ report }) => report["number"] !== identity.number ||
|
|
106
|
+
typeof report["merged"] !== "string" ||
|
|
107
|
+
report["merged"].trim() === "" ||
|
|
108
|
+
Object.hasOwn(report, "reason")))
|
|
109
|
+
return null;
|
|
110
|
+
return new Set(merged.map(({ report }) => report["merged"])).size === 1
|
|
111
|
+
? identity
|
|
112
|
+
: null;
|
|
113
|
+
}
|
|
114
|
+
export function pullOf(inputs, runId) {
|
|
115
|
+
const results = inputs.records
|
|
116
|
+
.map(githubOf)
|
|
117
|
+
.filter((item) => item.runId === runId)
|
|
118
|
+
.toSorted((first, second) => first.round - second.round);
|
|
119
|
+
const pullRequest = pullRequestOf(results);
|
|
120
|
+
const mergeProof = mergeProofOf(results, {
|
|
121
|
+
repository: inputs.repository,
|
|
122
|
+
pullRequest,
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
pullRequest,
|
|
126
|
+
reviews: reviewsOf(results),
|
|
127
|
+
mergeCommit: mergeCommitOf(results),
|
|
128
|
+
...(mergeProof === null ? {} : { mergeProof }),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { type PrRepository } from "./watch-pr-identity.ts";
|
|
2
|
+
import { type WorkflowStages } from "../workflow-declaration.ts";
|
|
1
3
|
import { type Fields } from "../protocol.ts";
|
|
4
|
+
import { type WorkflowRefusal } from "../workflow-refusals.ts";
|
|
2
5
|
interface ReadStatus extends Fields {
|
|
3
6
|
readonly runId: string;
|
|
4
7
|
readonly latestSequence: number;
|
|
@@ -13,8 +16,11 @@ interface ReadRun {
|
|
|
13
16
|
readonly events: readonly ReadEvent[];
|
|
14
17
|
}
|
|
15
18
|
export interface WatchRead {
|
|
19
|
+
readonly queue?: Fields;
|
|
16
20
|
readonly service: string;
|
|
17
21
|
readonly workflows: readonly string[];
|
|
22
|
+
readonly workflowRefusals: readonly WorkflowRefusal[];
|
|
23
|
+
readonly workflowStages?: WorkflowStages;
|
|
18
24
|
readonly summaries: readonly ReadStatus[];
|
|
19
25
|
readonly runs: readonly ReadRun[];
|
|
20
26
|
readonly completionCandidates: readonly ReadRun[];
|
|
@@ -24,5 +30,5 @@ export interface WatchReader {
|
|
|
24
30
|
readonly read: (now: string) => Promise<WatchRead>;
|
|
25
31
|
readonly run: (runId: string) => Promise<WatchRead>;
|
|
26
32
|
}
|
|
27
|
-
export declare function watchReader(runtimeDirectory: string): WatchReader;
|
|
33
|
+
export declare function watchReader(runtimeDirectory: string, repository?: PrRepository | null): WatchReader;
|
|
28
34
|
export {};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { establishedIssue } from "./watch-pr-identity.js";
|
|
2
|
+
import { isWorkflowStages, } from "../workflow-declaration.js";
|
|
1
3
|
import { record } from "../protocol.js";
|
|
2
4
|
import { send } from "./client.js";
|
|
3
5
|
import { activeRun, recentRun } from "./watch-board.js";
|
|
6
|
+
import { workflowRefusals, } from "../workflow-refusals.js";
|
|
4
7
|
const unavailable = {
|
|
5
8
|
service: "down",
|
|
6
9
|
workflows: [],
|
|
10
|
+
workflowRefusals: [],
|
|
7
11
|
summaries: [],
|
|
8
12
|
runs: [],
|
|
9
13
|
completionCandidates: [],
|
|
@@ -79,13 +83,15 @@ async function creationOf(reader, runId) {
|
|
|
79
83
|
}
|
|
80
84
|
return null;
|
|
81
85
|
}
|
|
82
|
-
function matchingCompletion(status,
|
|
83
|
-
const
|
|
86
|
+
function matchingCompletion(status, context) {
|
|
87
|
+
const issue = establishedIssue(context.repository, status["issueIdentity"]);
|
|
84
88
|
return (status.state === "completed" &&
|
|
85
89
|
status["workflowId"] === "develop-issue" &&
|
|
86
90
|
record(status["outcome"])?.["kind"] === "handoff_requested" &&
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
record(status["outcome"])?.["ticketStatus"] !== "partial" &&
|
|
92
|
+
record(status["outcome"])?.["issueUpdate"] !== "failed" &&
|
|
93
|
+
issue !== null &&
|
|
94
|
+
context.issues.has(JSON.stringify(issue)));
|
|
89
95
|
}
|
|
90
96
|
async function completionOf(reader, search) {
|
|
91
97
|
const { runId, latestSequence } = search.summary;
|
|
@@ -104,12 +110,16 @@ async function completionOf(reader, search) {
|
|
|
104
110
|
state: status.state,
|
|
105
111
|
workflowId: status["workflowId"],
|
|
106
112
|
ticketId: status["ticketId"],
|
|
113
|
+
issueIdentity: status["issueIdentity"],
|
|
107
114
|
outcome: status["outcome"],
|
|
108
115
|
},
|
|
109
116
|
};
|
|
110
117
|
reader.completions.set(runId, known);
|
|
111
118
|
}
|
|
112
|
-
if (!matchingCompletion(known.status,
|
|
119
|
+
if (!matchingCompletion(known.status, {
|
|
120
|
+
issues: search.issues,
|
|
121
|
+
repository: reader.repository,
|
|
122
|
+
}))
|
|
113
123
|
return null;
|
|
114
124
|
const creation = known.creation === undefined
|
|
115
125
|
? await creationOf(reader, runId)
|
|
@@ -117,11 +127,20 @@ async function completionOf(reader, search) {
|
|
|
117
127
|
reader.completions.set(runId, { ...known, creation });
|
|
118
128
|
return { status: known.status, events: creation === null ? [] : [creation] };
|
|
119
129
|
}
|
|
130
|
+
function refreshedLinks(run, status) {
|
|
131
|
+
const links = Object.fromEntries(["parentRunId", "chainId", "childRunIds"]
|
|
132
|
+
.filter((name) => Object.hasOwn(status, name))
|
|
133
|
+
.map((name) => [name, status[name]]));
|
|
134
|
+
return {
|
|
135
|
+
...run,
|
|
136
|
+
status: { ...run.status, ...links },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
120
139
|
async function detailed(reader, summary) {
|
|
121
140
|
const { runId, latestSequence: sequence } = summary;
|
|
122
141
|
const known = reader.details.get(runId);
|
|
123
142
|
if (known !== undefined && known.sequence === sequence)
|
|
124
|
-
return known.value;
|
|
143
|
+
return refreshedLinks(known.value, summary);
|
|
125
144
|
const shown = await asked(reader, {
|
|
126
145
|
operation: "status",
|
|
127
146
|
parameters: { runId },
|
|
@@ -158,10 +177,10 @@ async function decisionsOf(reader) {
|
|
|
158
177
|
});
|
|
159
178
|
return list(questions["decisions"]);
|
|
160
179
|
}
|
|
161
|
-
function
|
|
180
|
+
function stoppedIssues(runs, context) {
|
|
162
181
|
const stopped = runs
|
|
163
182
|
.map((run) => run.status)
|
|
164
|
-
.filter((status) => recentRun(status.latestAt, now) &&
|
|
183
|
+
.filter((status) => recentRun(status.latestAt, context.now) &&
|
|
165
184
|
status["workflowId"] === "develop-issue" &&
|
|
166
185
|
(["failed", "cancelled"].includes(status.state) ||
|
|
167
186
|
(status.state === "completed" &&
|
|
@@ -169,14 +188,15 @@ function stoppedTicketIds(runs, now) {
|
|
|
169
188
|
.toSorted((first, second) => Date.parse(second.latestAt ?? "") - Date.parse(first.latestAt ?? ""))
|
|
170
189
|
.slice(0, 20);
|
|
171
190
|
return new Set(stopped.flatMap((status) => {
|
|
172
|
-
const
|
|
173
|
-
return status.state === "completed" &&
|
|
174
|
-
|
|
175
|
-
ticketId !== ""
|
|
176
|
-
? [ticketId]
|
|
191
|
+
const issue = establishedIssue(context.repository, status["issueIdentity"]);
|
|
192
|
+
return status.state === "completed" && issue !== null
|
|
193
|
+
? [JSON.stringify(issue)]
|
|
177
194
|
: [];
|
|
178
195
|
}));
|
|
179
196
|
}
|
|
197
|
+
function stageMetadata(value) {
|
|
198
|
+
return Object.fromEntries(Object.entries(record(value) ?? {}).flatMap(([workflowId, stages]) => isWorkflowStages(stages) ? [[workflowId, stages]] : []));
|
|
199
|
+
}
|
|
180
200
|
async function service(reader, now) {
|
|
181
201
|
const status = await asked(reader, {
|
|
182
202
|
operation: "status",
|
|
@@ -190,20 +210,25 @@ async function service(reader, now) {
|
|
|
190
210
|
const current = summaries.filter((summary) => activeRun(summary.state) || recentRun(summary.latestAt, now));
|
|
191
211
|
for (const summary of current)
|
|
192
212
|
runs.push(await detailed(reader, summary));
|
|
193
|
-
const
|
|
194
|
-
const older =
|
|
213
|
+
const issues = stoppedIssues(runs, { now, repository: reader.repository });
|
|
214
|
+
const older = issues.size === 0
|
|
195
215
|
? []
|
|
196
216
|
: summaries.filter((summary) => !recentRun(summary.latestAt, now) &&
|
|
197
217
|
summary["workflowId"] === "develop-issue" &&
|
|
198
218
|
summary.state === "completed");
|
|
199
219
|
for (const summary of older) {
|
|
200
|
-
const candidate = await completionOf(reader, { summary,
|
|
220
|
+
const candidate = await completionOf(reader, { summary, issues });
|
|
201
221
|
if (candidate !== null)
|
|
202
222
|
completionCandidates.push(candidate);
|
|
203
223
|
}
|
|
204
224
|
return {
|
|
205
225
|
service: "up",
|
|
226
|
+
...(record(status["queue"]) === undefined
|
|
227
|
+
? {}
|
|
228
|
+
: { queue: record(status["queue"]) ?? {} }),
|
|
206
229
|
workflows: list(status["workflows"]).filter((value) => typeof value === "string"),
|
|
230
|
+
workflowStages: stageMetadata(status["workflowStages"]),
|
|
231
|
+
workflowRefusals: workflowRefusals(status["workflowRefusals"]),
|
|
207
232
|
summaries,
|
|
208
233
|
runs,
|
|
209
234
|
completionCandidates,
|
|
@@ -222,7 +247,7 @@ async function singleRun(reader, runId) {
|
|
|
222
247
|
const status = statusOf(reply.result["run"]);
|
|
223
248
|
const known = reader.details.get(runId);
|
|
224
249
|
const value = known !== undefined && known.sequence === status.latestSequence
|
|
225
|
-
? known.value
|
|
250
|
+
? refreshedLinks(known.value, status)
|
|
226
251
|
: await remember(reader, status);
|
|
227
252
|
return {
|
|
228
253
|
...unavailable,
|
|
@@ -231,9 +256,10 @@ async function singleRun(reader, runId) {
|
|
|
231
256
|
decisions: await decisionsOf(reader),
|
|
232
257
|
};
|
|
233
258
|
}
|
|
234
|
-
export function watchReader(runtimeDirectory) {
|
|
259
|
+
export function watchReader(runtimeDirectory, repository = null) {
|
|
235
260
|
const reader = {
|
|
236
261
|
runtimeDirectory,
|
|
262
|
+
repository,
|
|
237
263
|
details: new Map(),
|
|
238
264
|
completions: new Map(),
|
|
239
265
|
};
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import type { CardForm } from "./watch-run-input.ts";
|
|
2
|
+
import type { WorkerUsage } from "../worker-usage.ts";
|
|
3
|
+
import type { PrIdentity, PrObservation } from "./watch-pr-identity.ts";
|
|
4
|
+
import type { WorkflowRefusal } from "../workflow-refusals.ts";
|
|
5
|
+
import type { Fields } from "../protocol.ts";
|
|
1
6
|
export interface WatchItem {
|
|
2
7
|
readonly kind: string;
|
|
3
8
|
readonly text: string;
|
|
4
9
|
readonly costUsd?: number;
|
|
5
10
|
}
|
|
6
11
|
export interface WatchTurn {
|
|
12
|
+
readonly estimatedCostUsd: number | null;
|
|
13
|
+
readonly operationId: string;
|
|
14
|
+
readonly usage: WorkerUsage;
|
|
7
15
|
readonly key: string;
|
|
8
16
|
readonly runId: string;
|
|
9
17
|
readonly target: string;
|
|
@@ -56,9 +64,14 @@ export interface WatchStep {
|
|
|
56
64
|
readonly startedAt: string | null;
|
|
57
65
|
}
|
|
58
66
|
export interface WatchCard {
|
|
67
|
+
readonly parentRunId?: string | null;
|
|
68
|
+
readonly chainId?: string;
|
|
69
|
+
readonly childRunIds?: readonly string[];
|
|
70
|
+
readonly estimatedCostUsd: number | null;
|
|
59
71
|
readonly runId: string;
|
|
60
72
|
readonly title: string | null;
|
|
61
|
-
readonly claudeCostUsd: number;
|
|
73
|
+
readonly claudeCostUsd: number | null;
|
|
74
|
+
readonly usage: WorkerUsage;
|
|
62
75
|
readonly currentStep: WatchStep | null;
|
|
63
76
|
readonly endedAt: string | null;
|
|
64
77
|
readonly decisionWaiting: boolean;
|
|
@@ -66,6 +79,7 @@ export interface WatchCard {
|
|
|
66
79
|
readonly greptileScore: number | null;
|
|
67
80
|
readonly pullRequestNumber: number | null;
|
|
68
81
|
readonly completionMark: WatchCompletionMark | null;
|
|
82
|
+
readonly pullRequestObservation: PrObservation | null;
|
|
69
83
|
}
|
|
70
84
|
export interface WatchCompletionMark {
|
|
71
85
|
readonly text: string;
|
|
@@ -83,11 +97,21 @@ export interface WatchWorkflow {
|
|
|
83
97
|
readonly columns: readonly WatchColumn[];
|
|
84
98
|
}
|
|
85
99
|
export interface WatchRun {
|
|
100
|
+
readonly parentRunId?: string | null;
|
|
101
|
+
readonly chainId?: string;
|
|
102
|
+
readonly childRunIds?: readonly string[];
|
|
103
|
+
readonly mergeProof?: PrIdentity | null;
|
|
104
|
+
readonly issueIdentity?: PrIdentity | null;
|
|
105
|
+
readonly issueUpdate?: "failed";
|
|
106
|
+
readonly ticketStatus?: "complete" | "partial";
|
|
107
|
+
readonly promotionId?: string;
|
|
108
|
+
readonly estimatedCostUsd: number | null;
|
|
86
109
|
readonly ticketId: string | null;
|
|
87
110
|
readonly title: string | null;
|
|
88
111
|
readonly outcome: string | null;
|
|
89
112
|
readonly latestAt: string | null;
|
|
90
|
-
readonly claudeCostUsd: number;
|
|
113
|
+
readonly claudeCostUsd: number | null;
|
|
114
|
+
readonly usage: WorkerUsage;
|
|
91
115
|
readonly currentStep: WatchStep;
|
|
92
116
|
readonly round: number;
|
|
93
117
|
readonly runId: string;
|
|
@@ -108,6 +132,9 @@ export interface WatchRun {
|
|
|
108
132
|
readonly mergeCommit: string | null;
|
|
109
133
|
}
|
|
110
134
|
export interface WatchModel {
|
|
135
|
+
readonly queue?: Fields;
|
|
136
|
+
readonly workflowRefusals?: readonly WorkflowRefusal[];
|
|
137
|
+
readonly cardForms?: readonly CardForm[];
|
|
111
138
|
readonly workflows: readonly WatchWorkflow[];
|
|
112
139
|
readonly startWorkflowId: string | null;
|
|
113
140
|
readonly updatedAt: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { priceText } from "./usage-render.js";
|
|
1
2
|
export function counted(count, noun) {
|
|
2
3
|
return `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
|
|
3
4
|
}
|
|
@@ -49,7 +50,7 @@ export function runTone(run) {
|
|
|
49
50
|
return toneOf(run.state);
|
|
50
51
|
}
|
|
51
52
|
export function runName(run) {
|
|
52
|
-
return run.turns[0]?.target
|
|
53
|
+
return run.turns[0]?.target || run.runId.slice(0, 12);
|
|
53
54
|
}
|
|
54
55
|
export function waitingDetail(run, now) {
|
|
55
56
|
const opened = run.phases.find((phase) => phase.state === "active");
|
|
@@ -60,7 +61,7 @@ export function waitingDetail(run, now) {
|
|
|
60
61
|
export function completedDetail(run) {
|
|
61
62
|
const [first] = run.events;
|
|
62
63
|
const last = run.events.at(-1);
|
|
63
|
-
const cost = run.
|
|
64
|
+
const cost = priceText(run.claudeCostUsd, run.estimatedCostUsd);
|
|
64
65
|
const time = first === undefined || last === undefined
|
|
65
66
|
? ""
|
|
66
67
|
: ` in ${elapsed(first.recordedAt, last.recordedAt)}`;
|
|
@@ -68,7 +69,14 @@ export function completedDetail(run) {
|
|
|
68
69
|
approved: "You approved the work. ",
|
|
69
70
|
denied: "You denied the work. ",
|
|
70
71
|
};
|
|
71
|
-
|
|
72
|
+
const ticket = run.ticketStatus === undefined
|
|
73
|
+
? ""
|
|
74
|
+
: ` Ticket status: ${run.ticketStatus}.`;
|
|
75
|
+
const update = run.issueUpdate === "failed"
|
|
76
|
+
? " Issue status update failed; check the GitHub operation."
|
|
77
|
+
: "";
|
|
78
|
+
const promotion = run.promotionId === undefined ? "" : ` Promotion: ${run.promotionId}.`;
|
|
79
|
+
return `${verdicts[run.decision] ?? ""}The run completed${time} · cost ${cost}.${promotion}${ticket}${update}`;
|
|
72
80
|
}
|
|
73
81
|
export function phaseTime(phase, now) {
|
|
74
82
|
if (phase.startedAt === null)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ServiceRequest } from "../protocol.ts";
|
|
2
|
+
export interface CardField {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly multiline: boolean;
|
|
6
|
+
readonly optional: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface CardForm {
|
|
9
|
+
readonly workflowId: string;
|
|
10
|
+
readonly source: "ticket" | "prompt" | "input";
|
|
11
|
+
readonly fields: readonly CardField[];
|
|
12
|
+
}
|
|
13
|
+
export declare function builtinCardForms(): readonly CardForm[];
|
|
14
|
+
export declare function cardRunRequest(forms: readonly CardForm[], value: unknown): ServiceRequest | null;
|