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,49 @@
|
|
|
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
|
+
function validFields(form, fields) {
|
|
16
|
+
if (Object.keys(fields).some((name) => !form.fields.some((field) => field.name === name)))
|
|
17
|
+
return false;
|
|
18
|
+
return form.fields.every((field) => {
|
|
19
|
+
if (field.optional && !Object.hasOwn(fields, field.name))
|
|
20
|
+
return true;
|
|
21
|
+
const text = fields[field.name];
|
|
22
|
+
return typeof text === "string" && text.trim() !== "";
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function cardRunRequest(forms, value) {
|
|
26
|
+
const body = record(value);
|
|
27
|
+
if (body === undefined ||
|
|
28
|
+
Object.keys(body).some((key) => !["workflowId", "fields", "commandId"].includes(key)))
|
|
29
|
+
return null;
|
|
30
|
+
const { workflowId, commandId } = body;
|
|
31
|
+
const form = forms.find((item) => item.workflowId === workflowId);
|
|
32
|
+
const fields = record(body["fields"]);
|
|
33
|
+
if (form === undefined ||
|
|
34
|
+
fields === undefined ||
|
|
35
|
+
typeof commandId !== "string" ||
|
|
36
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(commandId))
|
|
37
|
+
return null;
|
|
38
|
+
if (!validFields(form, fields))
|
|
39
|
+
return null;
|
|
40
|
+
return {
|
|
41
|
+
operation: "run",
|
|
42
|
+
parameters: {
|
|
43
|
+
workflowId,
|
|
44
|
+
source: form.source,
|
|
45
|
+
submission: fields,
|
|
46
|
+
commandId,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { type WatchPrSettings } from "./watch-pr-settings.ts";
|
|
1
2
|
export declare const defaultWatchPort = 3663;
|
|
2
3
|
export interface WatchSettings {
|
|
4
|
+
readonly actions?: string;
|
|
5
|
+
readonly pullRequests?: WatchPrSettings;
|
|
3
6
|
readonly runtimeDirectory: string;
|
|
4
7
|
readonly port: number;
|
|
5
8
|
readonly client: string;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { watchAction, watchActions, watchHost, } from "./watch-actions.js";
|
|
2
|
+
import { observedBoard } from "./watch-pr-board.js";
|
|
3
|
+
import { watchPrObserver } from "./watch-pr-settings.js";
|
|
1
4
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
5
|
import { createServer, } from "node:http";
|
|
3
6
|
import { homedir } from "node:os";
|
|
@@ -8,7 +11,22 @@ import { turnOf, watchModel } from "./watch-model.js";
|
|
|
8
11
|
import { pageDocument } from "./watch-page.js";
|
|
9
12
|
import { watchStream } from "./watch-stream.js";
|
|
10
13
|
export const defaultWatchPort = 3663;
|
|
14
|
+
const forbidden = { status: 403, type: "text/plain", body: "refused" };
|
|
11
15
|
const notFound = { status: 404, type: "text/plain", body: "not found" };
|
|
16
|
+
const fontFiles = new Map([
|
|
17
|
+
"fira-code-latin-400-normal.woff2",
|
|
18
|
+
"fira-code-latin-500-normal.woff2",
|
|
19
|
+
"heebo-latin-500-normal.woff2",
|
|
20
|
+
"heebo-latin-700-normal.woff2",
|
|
21
|
+
"heebo-latin-800-normal.woff2",
|
|
22
|
+
"space-grotesk-latin-400-normal.woff2",
|
|
23
|
+
"space-grotesk-latin-500-normal.woff2",
|
|
24
|
+
"space-grotesk-latin-600-normal.woff2",
|
|
25
|
+
"space-grotesk-latin-700-normal.woff2",
|
|
26
|
+
].map((name) => [
|
|
27
|
+
`/watch-fonts-v1/${name}`,
|
|
28
|
+
new URL(`assets/watch-fonts/${name}`, import.meta.resolve("#package")),
|
|
29
|
+
]));
|
|
12
30
|
const libraryFile = fileURLToPath(import.meta.resolve("preact"));
|
|
13
31
|
function parsed(text) {
|
|
14
32
|
try {
|
|
@@ -75,25 +93,59 @@ async function snapshot(watch, runId) {
|
|
|
75
93
|
turns(watch),
|
|
76
94
|
githubRecords(watch),
|
|
77
95
|
]);
|
|
78
|
-
|
|
96
|
+
const model = watchModel({
|
|
79
97
|
...read,
|
|
98
|
+
repository: watch.observer.repository ?? null,
|
|
80
99
|
home: homedir(),
|
|
81
100
|
client: watch.settings.client,
|
|
82
101
|
updatedAt,
|
|
83
102
|
turns: journal,
|
|
84
103
|
githubRecords: github,
|
|
85
104
|
});
|
|
105
|
+
const shown = runId === null ? observedBoard(model, watch.observer) : model;
|
|
106
|
+
return {
|
|
107
|
+
...shown,
|
|
108
|
+
cardForms: watch.actions?.forms.filter((form) => shown.workflows.some((workflow) => workflow.workflowId === form.workflowId)) ?? [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async function fontReply(request) {
|
|
112
|
+
const fontFile = fontFiles.get(request.url ?? "");
|
|
113
|
+
if (fontFile === undefined)
|
|
114
|
+
return request.url?.startsWith("/watch-fonts-v1/") ? notFound : null;
|
|
115
|
+
if (request.method !== "GET")
|
|
116
|
+
return notFound;
|
|
117
|
+
return {
|
|
118
|
+
status: 200,
|
|
119
|
+
type: "font/woff2",
|
|
120
|
+
body: await readFile(fontFile),
|
|
121
|
+
cache: "public, max-age=31536000, immutable",
|
|
122
|
+
};
|
|
86
123
|
}
|
|
87
124
|
async function answered(watch, request) {
|
|
125
|
+
if (!watchHost(request))
|
|
126
|
+
return forbidden;
|
|
127
|
+
const font = await fontReply(request);
|
|
128
|
+
if (font !== null)
|
|
129
|
+
return font;
|
|
88
130
|
const url = new URL(String(request.url), "http://localhost");
|
|
89
|
-
|
|
90
|
-
|
|
131
|
+
if (request.method === "GET")
|
|
132
|
+
return readReply(watch, url);
|
|
133
|
+
if (request.method !== "POST" ||
|
|
134
|
+
url.pathname !== "/actions/run" ||
|
|
135
|
+
watch.actions === null)
|
|
91
136
|
return notFound;
|
|
137
|
+
const body = await watchAction(watch.actions, request).catch(() => null);
|
|
138
|
+
return body === null
|
|
139
|
+
? forbidden
|
|
140
|
+
: { status: 200, type: "application/json", body };
|
|
141
|
+
}
|
|
142
|
+
async function readReply(watch, url) {
|
|
143
|
+
const path = url.pathname;
|
|
92
144
|
if (path === "/")
|
|
93
145
|
return {
|
|
94
146
|
status: 200,
|
|
95
147
|
type: "text/html; charset=utf-8",
|
|
96
|
-
body: pageDocument(),
|
|
148
|
+
body: pageDocument(watch.actions?.token ?? null),
|
|
97
149
|
};
|
|
98
150
|
if (path === "/preact.mjs")
|
|
99
151
|
return {
|
|
@@ -116,7 +168,7 @@ async function answered(watch, request) {
|
|
|
116
168
|
function replied(response, reply) {
|
|
117
169
|
response.writeHead(reply.status, {
|
|
118
170
|
"content-type": reply.type,
|
|
119
|
-
"cache-control": "no-store",
|
|
171
|
+
"cache-control": reply.cache ?? "no-store",
|
|
120
172
|
});
|
|
121
173
|
response.end(reply.body);
|
|
122
174
|
}
|
|
@@ -132,10 +184,15 @@ function subscribed(stream, exchange) {
|
|
|
132
184
|
changed();
|
|
133
185
|
request.once("close", stream.subscribe(changed));
|
|
134
186
|
}
|
|
135
|
-
export function watchServer(settings) {
|
|
187
|
+
export async function watchServer(settings) {
|
|
188
|
+
const observer = await observerFor(settings);
|
|
136
189
|
const watch = {
|
|
190
|
+
actions: settings.actions === "off"
|
|
191
|
+
? null
|
|
192
|
+
: await watchActions(settings.runtimeDirectory),
|
|
137
193
|
settings,
|
|
138
|
-
|
|
194
|
+
observer,
|
|
195
|
+
reader: watchReader(settings.runtimeDirectory, observer.repository ?? null),
|
|
139
196
|
streams: new Map(),
|
|
140
197
|
github: new Map(),
|
|
141
198
|
};
|
|
@@ -145,7 +202,9 @@ export function watchServer(settings) {
|
|
|
145
202
|
retryMilliseconds: 1000,
|
|
146
203
|
});
|
|
147
204
|
const server = createServer((request, response) => {
|
|
148
|
-
if (request
|
|
205
|
+
if (watchHost(request) &&
|
|
206
|
+
request.method === "GET" &&
|
|
207
|
+
request.url === "/events") {
|
|
149
208
|
subscribed(stream, { request, response });
|
|
150
209
|
return;
|
|
151
210
|
}
|
|
@@ -157,6 +216,7 @@ export function watchServer(settings) {
|
|
|
157
216
|
});
|
|
158
217
|
return new Promise((settle) => {
|
|
159
218
|
server.once("error", (error) => {
|
|
219
|
+
observer.close();
|
|
160
220
|
stream.close();
|
|
161
221
|
settings.complain(`watch-not-started: ${error.message}\n`);
|
|
162
222
|
settle(1);
|
|
@@ -166,6 +226,7 @@ export function watchServer(settings) {
|
|
|
166
226
|
settings.announce(`Watch page: http://127.0.0.1:${String(bound)}/\n`);
|
|
167
227
|
});
|
|
168
228
|
const closed = () => {
|
|
229
|
+
observer.close();
|
|
169
230
|
stream.close();
|
|
170
231
|
server.close();
|
|
171
232
|
server.closeAllConnections();
|
|
@@ -174,3 +235,11 @@ export function watchServer(settings) {
|
|
|
174
235
|
settings.stopped.then(closed, closed);
|
|
175
236
|
});
|
|
176
237
|
}
|
|
238
|
+
function observerFor(settings) {
|
|
239
|
+
if (settings.pullRequests !== undefined)
|
|
240
|
+
return watchPrObserver(settings.pullRequests);
|
|
241
|
+
return Promise.resolve({
|
|
242
|
+
observe: () => ({ status: "unavailable", failure: "configuration" }),
|
|
243
|
+
close: () => undefined,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Shown } from "./watch-element.ts";
|
|
2
|
+
import type { Copying, WatchModel } from "./watch-render.ts";
|
|
3
|
+
interface StartPrompt {
|
|
4
|
+
readonly workflowId: string;
|
|
5
|
+
readonly sentence: string;
|
|
6
|
+
readonly prompt: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function startPrompts(): readonly StartPrompt[];
|
|
9
|
+
export declare function StartCard(props: {
|
|
10
|
+
readonly item: StartPrompt;
|
|
11
|
+
readonly copying: Copying;
|
|
12
|
+
}): Shown;
|
|
13
|
+
export declare function StartRun(props: {
|
|
14
|
+
readonly workflows: WatchModel["workflows"];
|
|
15
|
+
readonly copying: Copying;
|
|
16
|
+
readonly shown?: boolean;
|
|
17
|
+
}): Shown;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { component, element } from "./watch-element.js";
|
|
2
|
+
import { Command } from "./watch-turn.js";
|
|
3
|
+
export function startPrompts() {
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
workflowId: "develop-issue",
|
|
7
|
+
sentence: "Work on a GitHub issue through a pull request.",
|
|
8
|
+
prompt: `Start a Cloving run for a GitHub issue in this repository.
|
|
9
|
+
Run: cloving run develop-issue --ticket ISSUE_NUMBER
|
|
10
|
+
Use the issue number that I give on the last line.
|
|
11
|
+
Report the run ID. Then run cloving status RUN_ID until the run completes or stops, and tell me the result and the pull request.
|
|
12
|
+
Do not push, merge or change labels yourself.
|
|
13
|
+
Issue number:`,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
workflowId: "develop-ticket",
|
|
17
|
+
sentence: "Work on a ticket with your review of the diff.",
|
|
18
|
+
prompt: `Start a Cloving run for a ticket in this repository.
|
|
19
|
+
Run: cloving run develop-ticket --ticket TICKET_ID
|
|
20
|
+
Use the ticket ID that I give on the last line.
|
|
21
|
+
When the run waits for a decision, run cloving questions, show me the diff and the decision, and give me the approve and deny commands.
|
|
22
|
+
Run only the command that I choose. Do not decide for me.
|
|
23
|
+
Ticket ID:`,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
workflowId: "develop-prd",
|
|
27
|
+
sentence: "Turn an idea into a GitHub issue.",
|
|
28
|
+
prompt: `Start a Cloving run that turns an idea into a GitHub issue.
|
|
29
|
+
Run: cloving run develop-prd --prompt "IDEA"
|
|
30
|
+
Use the idea that I give on the last line as IDEA.
|
|
31
|
+
When the run completes, show me the new issue.
|
|
32
|
+
Add the label cloving: ready only when I tell you to.
|
|
33
|
+
Idea:`,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
workflowId: "develop-prompt",
|
|
37
|
+
sentence: "Do a small task with your review of the diff.",
|
|
38
|
+
prompt: `Start a Cloving run for a small task in this repository.
|
|
39
|
+
Run: cloving run develop-prompt --prompt "TASK"
|
|
40
|
+
Use the task that I give on the last line as TASK.
|
|
41
|
+
When the run waits for a decision, run cloving questions, show me the diff, and give me the approve and deny commands.
|
|
42
|
+
Run only the command that I choose. Do not decide for me.
|
|
43
|
+
Task:`,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
export function StartCard(props) {
|
|
48
|
+
const { item, copying } = props;
|
|
49
|
+
return element("article", {
|
|
50
|
+
class: "start-card",
|
|
51
|
+
children: [
|
|
52
|
+
element("h3", { children: item.workflowId }),
|
|
53
|
+
element("p", { children: item.sentence }),
|
|
54
|
+
component(Command, {
|
|
55
|
+
label: "Agent prompt",
|
|
56
|
+
command: item.prompt,
|
|
57
|
+
copyLabel: "Copy the prompt",
|
|
58
|
+
copying,
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function StartRun(props) {
|
|
64
|
+
const registered = startPrompts().filter((item) => props.workflows.some((workflow) => workflow.workflowId === item.workflowId));
|
|
65
|
+
const cards = registered.map((item) => component(StartCard, {
|
|
66
|
+
key: item.workflowId,
|
|
67
|
+
item,
|
|
68
|
+
copying: props.copying,
|
|
69
|
+
}));
|
|
70
|
+
return element("section", {
|
|
71
|
+
class: "start-run",
|
|
72
|
+
id: "start-run",
|
|
73
|
+
"aria-label": "Start a run",
|
|
74
|
+
hidden: props.shown !== true,
|
|
75
|
+
children: element("div", { class: "start-cards", children: cards }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { priceText, usageText } from "./usage-render.js";
|
|
1
2
|
import { component, element } from "./watch-element.js";
|
|
2
3
|
import { clockTime, counted, groupActivity, turnTime, } from "./watch-render.js";
|
|
3
4
|
export function Item(props) {
|
|
@@ -30,8 +31,8 @@ export function Turn(props) {
|
|
|
30
31
|
const { turn, now } = props;
|
|
31
32
|
const groups = groupActivity(turn.activity);
|
|
32
33
|
const working = turn.outcome === "in work";
|
|
33
|
-
const cost = turn.costUsd
|
|
34
|
-
const heading = ["
|
|
34
|
+
const cost = priceText(turn.costUsd, turn.estimatedCostUsd);
|
|
35
|
+
const heading = ["Agent turn", turn.outcome, turnTime(turn, now), cost];
|
|
35
36
|
const place = `Branch ${turn.branch} · work tree ${turn.worktree}`;
|
|
36
37
|
const activity = groups.map((items, index) => component(Group, { items, open: working && index === groups.length - 1 }));
|
|
37
38
|
return element("section", {
|
|
@@ -42,6 +43,10 @@ export function Turn(props) {
|
|
|
42
43
|
children: heading.filter((part) => part !== "").join(" · "),
|
|
43
44
|
}),
|
|
44
45
|
element("p", { class: "meta", children: place }),
|
|
46
|
+
element("p", {
|
|
47
|
+
class: "turn-usage",
|
|
48
|
+
children: usageText(turn.usage),
|
|
49
|
+
}),
|
|
45
50
|
element("div", { class: "activity", children: activity }),
|
|
46
51
|
],
|
|
47
52
|
});
|
|
@@ -77,7 +82,7 @@ export function Command(props) {
|
|
|
77
82
|
const onClick = () => {
|
|
78
83
|
copying.onCopy(command);
|
|
79
84
|
};
|
|
80
|
-
const shown = copying.command === command ? copying.label : "Copy";
|
|
85
|
+
const shown = copying.command === command ? copying.label : (props.copyLabel ?? "Copy");
|
|
81
86
|
return element("div", {
|
|
82
87
|
class: "command code-window",
|
|
83
88
|
children: [
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Fields } from "../protocol.ts";
|
|
2
|
+
import type { WatchTurn } from "./watch-render.ts";
|
|
3
|
+
export declare function priceFields(value: unknown): {
|
|
4
|
+
readonly costUsd?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function statusTurns(turns: readonly WatchTurn[], status: Fields): readonly WatchTurn[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { record } from "../protocol.js";
|
|
2
|
+
import { priceOf, usageOf } from "../worker-usage.js";
|
|
3
|
+
function text(value) {
|
|
4
|
+
return typeof value === "string" ? value : "";
|
|
5
|
+
}
|
|
6
|
+
function emptyTurn(operationId, runId) {
|
|
7
|
+
return {
|
|
8
|
+
operationId,
|
|
9
|
+
runId,
|
|
10
|
+
key: operationId,
|
|
11
|
+
usage: usageOf(null),
|
|
12
|
+
costUsd: null,
|
|
13
|
+
estimatedCostUsd: null,
|
|
14
|
+
target: "",
|
|
15
|
+
branch: "",
|
|
16
|
+
worktree: "",
|
|
17
|
+
startedAt: "",
|
|
18
|
+
endedAt: null,
|
|
19
|
+
activeAt: null,
|
|
20
|
+
outcome: "ended",
|
|
21
|
+
activity: [],
|
|
22
|
+
commits: [],
|
|
23
|
+
files: [],
|
|
24
|
+
uncommitted: [],
|
|
25
|
+
summary: "",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function priceFields(value) {
|
|
29
|
+
const cost = priceOf(value);
|
|
30
|
+
return cost === null ? {} : { costUsd: cost };
|
|
31
|
+
}
|
|
32
|
+
export function statusTurns(turns, status) {
|
|
33
|
+
const runId = text(status["runId"]);
|
|
34
|
+
const journal = turns.filter((turn) => turn.runId === runId);
|
|
35
|
+
if (!Array.isArray(status["workerTurns"]))
|
|
36
|
+
return journal;
|
|
37
|
+
const ended = status["workerTurns"].map((value) => {
|
|
38
|
+
const fields = record(value) ?? {};
|
|
39
|
+
const operationId = text(fields["operationId"]);
|
|
40
|
+
const known = journal.find((turn) => turn.operationId === operationId) ??
|
|
41
|
+
emptyTurn(operationId, runId);
|
|
42
|
+
return {
|
|
43
|
+
...known,
|
|
44
|
+
runId,
|
|
45
|
+
operationId,
|
|
46
|
+
outcome: known.outcome === "in work" ? "ended" : known.outcome,
|
|
47
|
+
activeAt: null,
|
|
48
|
+
usage: usageOf(fields["usage"]),
|
|
49
|
+
costUsd: priceOf(fields["costUsd"]),
|
|
50
|
+
estimatedCostUsd: priceOf(fields["estimatedCostUsd"]),
|
|
51
|
+
activity: known.activity.map((item) => item.kind === "result"
|
|
52
|
+
? {
|
|
53
|
+
kind: item.kind,
|
|
54
|
+
text: item.text,
|
|
55
|
+
...priceFields(fields["costUsd"]),
|
|
56
|
+
}
|
|
57
|
+
: item),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
return [
|
|
61
|
+
...ended,
|
|
62
|
+
...journal.filter((turn) => turn.outcome === "in work" &&
|
|
63
|
+
!ended.some((item) => item.operationId === turn.operationId)),
|
|
64
|
+
];
|
|
65
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import type { CardControls } from "./watch-card-view.ts";
|
|
1
2
|
import { type Shown } from "./watch-element.ts";
|
|
2
3
|
import { type Copying, type WatchModel, type WatchPhase, type WatchRun } from "./watch-render.ts";
|
|
4
|
+
import { type ColumnControls } from "./watch-board-view.ts";
|
|
5
|
+
export declare function Queue(props: {
|
|
6
|
+
readonly model: WatchModel;
|
|
7
|
+
}): Shown;
|
|
3
8
|
interface Timed {
|
|
4
9
|
readonly run: WatchRun;
|
|
5
10
|
readonly now: string;
|
|
@@ -24,16 +29,33 @@ export declare function PullRequest(props: {
|
|
|
24
29
|
export declare function Run(props: Timed & {
|
|
25
30
|
readonly copying: Copying;
|
|
26
31
|
}): Shown;
|
|
32
|
+
export interface StartControls {
|
|
33
|
+
readonly shown: boolean;
|
|
34
|
+
readonly onToggle: () => void;
|
|
35
|
+
}
|
|
27
36
|
export declare function Header(props: {
|
|
28
37
|
readonly model: WatchModel | null;
|
|
29
38
|
readonly stale: boolean;
|
|
39
|
+
readonly start?: StartControls;
|
|
30
40
|
}): Shown;
|
|
31
41
|
export interface PageProps {
|
|
42
|
+
readonly cards?: CardControls;
|
|
32
43
|
readonly model: WatchModel | null;
|
|
44
|
+
readonly observationNow?: string | undefined;
|
|
33
45
|
readonly selected: string;
|
|
34
46
|
readonly now: string;
|
|
35
47
|
readonly stale: boolean;
|
|
36
48
|
readonly copying: Copying;
|
|
49
|
+
readonly columns?: ColumnControls;
|
|
50
|
+
readonly start?: StartControls;
|
|
37
51
|
}
|
|
52
|
+
export declare function RefusedWorkflows(props: {
|
|
53
|
+
readonly model: WatchModel;
|
|
54
|
+
}): Shown;
|
|
38
55
|
export declare function Page(props: PageProps): Shown;
|
|
56
|
+
export declare function BoardPage(props: PageProps & {
|
|
57
|
+
readonly model: WatchModel;
|
|
58
|
+
readonly workflowId: string | null;
|
|
59
|
+
readonly down: Shown;
|
|
60
|
+
}): Shown;
|
|
39
61
|
export {};
|
|
@@ -1,8 +1,36 @@
|
|
|
1
|
+
import { priceText, usageText, totalsSuffix } from "./usage-render.js";
|
|
1
2
|
import { component, element } from "./watch-element.js";
|
|
2
3
|
import { clockTime, completedDetail, counted, elapsed, phaseTime, runName, runTone, waitingDetail, } from "./watch-render.js";
|
|
3
|
-
import { Board, WorkflowList } from "./watch-board-view.js";
|
|
4
|
+
import { Board, HandoffLinks, WorkflowList, } from "./watch-board-view.js";
|
|
4
5
|
import { boardWorkflow, watchHash, watchLocation } from "./watch-navigation.js";
|
|
5
6
|
import { Decision, Events, Turn } from "./watch-turn.js";
|
|
7
|
+
import { StartRun } from "./watch-start-view.js";
|
|
8
|
+
import { record } from "../protocol.js";
|
|
9
|
+
export function Queue(props) {
|
|
10
|
+
const queue = props.model.queue;
|
|
11
|
+
if (queue === undefined)
|
|
12
|
+
return null;
|
|
13
|
+
const skipped = Array.isArray(queue["skipped"])
|
|
14
|
+
? queue["skipped"]
|
|
15
|
+
: [];
|
|
16
|
+
return [
|
|
17
|
+
element("p", {
|
|
18
|
+
class: "meta",
|
|
19
|
+
children: `Queue: ${String(queue["busy"])} of ${String(queue["lanes"])} lanes busy`,
|
|
20
|
+
}),
|
|
21
|
+
...skipped.map((value) => {
|
|
22
|
+
const ticket = record(value) ?? {};
|
|
23
|
+
const dependencies = ticket["dependencies"];
|
|
24
|
+
const needs = Array.isArray(dependencies)
|
|
25
|
+
? `; needs ${dependencies.map(String).join(", ")}`
|
|
26
|
+
: "";
|
|
27
|
+
return element("p", {
|
|
28
|
+
class: "meta",
|
|
29
|
+
children: `Skipped ticket ${String(ticket["ticketId"])}: ${String(ticket["reason"])}${needs}`,
|
|
30
|
+
});
|
|
31
|
+
}),
|
|
32
|
+
];
|
|
33
|
+
}
|
|
6
34
|
export const clove = {
|
|
7
35
|
viewBox: "0.6625858545303345 0.6630800366401672 46.674827575683594 46.673831939697266",
|
|
8
36
|
paths: [
|
|
@@ -96,7 +124,7 @@ export function PullRequest(props) {
|
|
|
96
124
|
children: `#${String(pullRequest.number)}`,
|
|
97
125
|
});
|
|
98
126
|
const scores = reviews.map((review) => element("li", {
|
|
99
|
-
children: `Round ${String(review.round)} ·
|
|
127
|
+
children: `Round ${String(review.round)} · Review ${String(review.score)}/5 · commit ${review.commit.slice(0, 7)}`,
|
|
100
128
|
}));
|
|
101
129
|
const merge = mergeCommit === null
|
|
102
130
|
? null
|
|
@@ -122,7 +150,7 @@ export function Run(props) {
|
|
|
122
150
|
const budget = run.spent === null || run.limit === null
|
|
123
151
|
? ""
|
|
124
152
|
: ` · ${String(run.spent)} of ${String(run.limit)} effect units`;
|
|
125
|
-
const none = "No
|
|
153
|
+
const none = "No agent turn for this run.";
|
|
126
154
|
const turns = run.turns.length === 0
|
|
127
155
|
? element("p", { class: "meta", children: none })
|
|
128
156
|
: run.turns.map((item, index) => [
|
|
@@ -140,6 +168,17 @@ export function Run(props) {
|
|
|
140
168
|
];
|
|
141
169
|
return [
|
|
142
170
|
component(Banner, { run, now }),
|
|
171
|
+
component(HandoffLinks, { run }),
|
|
172
|
+
element("section", {
|
|
173
|
+
class: "run-usage",
|
|
174
|
+
children: [
|
|
175
|
+
element("h3", { children: `Run totals${totalsSuffix(run.state)}` }),
|
|
176
|
+
element("p", { children: usageText(run.usage) }),
|
|
177
|
+
element("p", {
|
|
178
|
+
children: priceText(run.claudeCostUsd, run.estimatedCostUsd),
|
|
179
|
+
}),
|
|
180
|
+
],
|
|
181
|
+
}),
|
|
143
182
|
component(Phases, { phases: run.phases, now }),
|
|
144
183
|
component(PullRequest, { run }),
|
|
145
184
|
run.decisions.map((decision) => component(Decision, { decision, turn, copying })),
|
|
@@ -173,6 +212,32 @@ export function Header(props) {
|
|
|
173
212
|
],
|
|
174
213
|
}),
|
|
175
214
|
stamp,
|
|
215
|
+
props.start === undefined
|
|
216
|
+
? null
|
|
217
|
+
: element("button", {
|
|
218
|
+
type: "button",
|
|
219
|
+
class: "start-toggle",
|
|
220
|
+
"aria-controls": "start-run",
|
|
221
|
+
"aria-expanded": String(props.start.shown),
|
|
222
|
+
onClick: props.start.onToggle,
|
|
223
|
+
children: "Start a run",
|
|
224
|
+
}),
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
export function RefusedWorkflows(props) {
|
|
229
|
+
const refusals = props.model.workflowRefusals ?? [];
|
|
230
|
+
if (refusals.length === 0)
|
|
231
|
+
return null;
|
|
232
|
+
return element("section", {
|
|
233
|
+
class: "workflow-refusals",
|
|
234
|
+
children: [
|
|
235
|
+
element("h2", { children: "Refused workflows" }),
|
|
236
|
+
element("ul", {
|
|
237
|
+
children: refusals.map((entry) => element("li", {
|
|
238
|
+
children: `${entry.scope} ${entry.label}: ${entry.codes.join(", ")}`,
|
|
239
|
+
})),
|
|
240
|
+
}),
|
|
176
241
|
],
|
|
177
242
|
});
|
|
178
243
|
}
|
|
@@ -193,6 +258,8 @@ export function Page(props) {
|
|
|
193
258
|
return [
|
|
194
259
|
head,
|
|
195
260
|
down,
|
|
261
|
+
component(Queue, { model }),
|
|
262
|
+
component(RefusedWorkflows, { model }),
|
|
196
263
|
element("a", {
|
|
197
264
|
class: "back",
|
|
198
265
|
href: watchHash({ workflowId: back, runId: null }),
|
|
@@ -203,21 +270,46 @@ export function Page(props) {
|
|
|
203
270
|
: component(Run, { key: run.runId, run, now, copying }),
|
|
204
271
|
];
|
|
205
272
|
}
|
|
273
|
+
return component(BoardPage, { ...props, model, workflowId, down });
|
|
274
|
+
}
|
|
275
|
+
export function BoardPage(props) {
|
|
276
|
+
const { model, workflowId, down, now, stale, copying } = props;
|
|
206
277
|
const workflow = model.workflows.find((item) => item.workflowId === workflowId);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
278
|
+
const start = props.start ?? { shown: false, onToggle: () => undefined };
|
|
279
|
+
return element("div", {
|
|
280
|
+
class: "board-page",
|
|
281
|
+
children: [
|
|
282
|
+
component(Header, { model, stale, start }),
|
|
283
|
+
component(Queue, { model }),
|
|
284
|
+
down,
|
|
285
|
+
component(RefusedWorkflows, { model }),
|
|
286
|
+
component(StartRun, {
|
|
287
|
+
workflows: model.workflows,
|
|
288
|
+
copying,
|
|
289
|
+
shown: start.shown,
|
|
290
|
+
}),
|
|
291
|
+
component(WorkflowList, {
|
|
292
|
+
workflows: model.workflows,
|
|
293
|
+
selected: workflowId,
|
|
294
|
+
}),
|
|
295
|
+
element("div", {
|
|
296
|
+
class: "workspace",
|
|
297
|
+
children: [
|
|
298
|
+
workflow === undefined
|
|
299
|
+
? element("p", {
|
|
300
|
+
class: "meta",
|
|
301
|
+
children: "No workflows registered.",
|
|
302
|
+
})
|
|
303
|
+
: component(Board, {
|
|
304
|
+
workflow,
|
|
305
|
+
now,
|
|
306
|
+
observationNow: props.observationNow,
|
|
307
|
+
controls: props.columns,
|
|
308
|
+
cardForm: model.cardForms?.find((form) => form.workflowId === workflowId),
|
|
309
|
+
cards: props.cards,
|
|
310
|
+
}),
|
|
311
|
+
],
|
|
312
|
+
}),
|
|
313
|
+
],
|
|
314
|
+
});
|
|
223
315
|
}
|