toolroll 0.9.93 → 0.9.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +10 -0
- package/dist/agent-fence.js +6 -3
- package/dist/approval-authority.d.ts +7 -1
- package/dist/approval-authority.js +11 -2
- package/dist/approval-password.d.ts +31 -0
- package/dist/approval-password.js +26 -0
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +3 -1
- package/dist/assignment.js +5 -0
- package/dist/attest.d.ts +3 -0
- package/dist/attest.js +10 -0
- package/dist/boot-identity.d.ts +2 -1
- package/dist/boot-identity.js +6 -4
- package/dist/bounded-spawn.d.ts +57 -0
- package/dist/bounded-spawn.js +220 -0
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-crew.js +2 -0
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +50 -2
- package/dist/browser-workspace.js +3 -3
- package/dist/build-review.js +2 -0
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +11 -2
- package/dist/builder.js +234 -101
- package/dist/card-catalog.d.ts +209 -0
- package/dist/card-catalog.js +133 -0
- package/dist/card-cli.d.ts +43 -0
- package/dist/card-cli.js +44 -0
- package/dist/card-render.d.ts +26 -0
- package/dist/card-render.js +223 -0
- package/dist/card-sources.d.ts +68 -0
- package/dist/card-sources.js +234 -0
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +106 -9
- package/dist/chat-actions.js +473 -46
- package/dist/chat-approval.d.ts +13 -0
- package/dist/chat-approval.js +39 -9
- package/dist/chat-card-part.d.ts +11 -0
- package/dist/chat-card-part.js +18 -0
- package/dist/chat-card.d.ts +46 -0
- package/dist/chat-card.js +99 -0
- package/dist/chat-channel.d.ts +2 -0
- package/dist/chat-channel.js +37 -14
- package/dist/chat-controls.d.ts +4 -0
- package/dist/chat-controls.js +4 -0
- package/dist/chat-decide.d.ts +10 -16
- package/dist/chat-decide.js +79 -62
- package/dist/chat-delivery.js +13 -4
- package/dist/chat-flow.d.ts +0 -27
- package/dist/chat-flow.js +31 -2
- package/dist/chat-quiet.js +4 -4
- package/dist/chat-task-actions.d.ts +3 -2
- package/dist/chat-task-actions.js +19 -5
- package/dist/chat-terms.d.ts +14 -2
- package/dist/chat-terms.js +35 -0
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +3 -1
- package/dist/check-facts.js +57 -11
- package/dist/check-failure.d.ts +24 -3
- package/dist/check-failure.js +89 -13
- package/dist/check-levels-ui.d.ts +4 -0
- package/dist/check-levels-ui.js +10 -1
- package/dist/check-load.d.ts +85 -0
- package/dist/check-load.js +147 -0
- package/dist/check-only-standing.d.ts +36 -0
- package/dist/check-only-standing.js +39 -0
- package/dist/check-only.d.ts +15 -0
- package/dist/check-only.js +99 -0
- package/dist/check-repair.d.ts +47 -0
- package/dist/check-repair.js +240 -0
- package/dist/check-report.d.ts +2 -0
- package/dist/check-report.js +1 -1
- package/dist/check-stamp.d.ts +46 -0
- package/dist/check-stamp.js +93 -0
- package/dist/checkout-cleanup.d.ts +36 -5
- package/dist/checkout-cleanup.js +166 -14
- package/dist/claim.d.ts +6 -0
- package/dist/claim.js +13 -2
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +49 -30
- package/dist/cli.js +15 -1
- package/dist/code-extract.d.ts +44 -0
- package/dist/code-extract.js +88 -0
- package/dist/code-map-acts.d.ts +24 -0
- package/dist/code-map-acts.js +53 -0
- package/dist/code-map-admission.d.ts +52 -0
- package/dist/code-map-admission.js +295 -0
- package/dist/code-map-build.d.ts +63 -0
- package/dist/code-map-build.js +415 -0
- package/dist/code-map-cli.d.ts +153 -0
- package/dist/code-map-cli.js +103 -0
- package/dist/code-map-main.d.ts +1 -0
- package/dist/code-map-main.js +48 -0
- package/dist/code-map-read.d.ts +106 -0
- package/dist/code-map-read.js +171 -0
- package/dist/code-map-report.d.ts +37 -0
- package/dist/code-map-report.js +95 -0
- package/dist/code-map-shape.d.ts +172 -0
- package/dist/code-map-shape.js +327 -0
- package/dist/code-map-store.d.ts +136 -0
- package/dist/code-map-store.js +233 -0
- package/dist/coding-provider.d.ts +3 -1
- package/dist/coding-provider.js +12 -7
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.d.ts +1 -1
- package/dist/coding-workspace.js +8 -8
- package/dist/console-chrome.js +2 -2
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +14 -1
- package/dist/contracts/chat-actions.d.ts +92 -0
- package/dist/contracts/chat-actions.js +20 -0
- package/dist/contracts/chat-card.d.ts +91 -0
- package/dist/contracts/chat-card.js +54 -0
- package/dist/contracts/chat-content.d.ts +191 -0
- package/dist/contracts/chat-content.js +4 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -3
- package/dist/contracts/console-api.js +12 -3
- package/dist/contracts/flow-propose.d.ts +8 -1
- package/dist/contracts/flow-propose.js +3 -1
- package/dist/contracts/flow-send.d.ts +126 -0
- package/dist/contracts/flow-send.js +3 -0
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +234 -0
- package/dist/contracts/lead-tools.js +25 -1
- package/dist/contracts/project-knowledge.d.ts +38 -5
- package/dist/contracts/project-knowledge.js +11 -5
- package/dist/contracts/project-memory.d.ts +3 -1
- package/dist/contracts/project-memory.js +3 -2
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +18 -0
- package/dist/contracts/youiyoux-request.d.ts +1 -1
- package/dist/contracts/youiyoux-request.js +1 -1
- package/dist/desktop-access.js +6 -2
- package/dist/discord-chat.js +14 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +25 -16
- package/dist/dispose.d.ts +19 -0
- package/dist/dispose.js +55 -9
- package/dist/easy-start.js +2 -3
- package/dist/eval-oracle.js +4 -1
- package/dist/evals.js +2 -1
- package/dist/evidence-pair.d.ts +53 -0
- package/dist/evidence-pair.js +128 -0
- package/dist/exhaustion.d.ts +17 -4
- package/dist/exhaustion.js +44 -4
- package/dist/export.js +1 -1
- package/dist/failing-first.d.ts +31 -0
- package/dist/failing-first.js +44 -0
- package/dist/fallback-schema.d.ts +8 -0
- package/dist/fallback-schema.js +40 -0
- package/dist/fallback.d.ts +138 -0
- package/dist/fallback.js +605 -0
- package/dist/fast-start.d.ts +136 -0
- package/dist/fast-start.js +431 -0
- package/dist/fast-terms.d.ts +9 -0
- package/dist/fast-terms.js +23 -0
- package/dist/fenced-check-coverage.d.ts +62 -0
- package/dist/fenced-check-coverage.js +54 -0
- package/dist/findings.d.ts +224 -0
- package/dist/findings.js +449 -0
- package/dist/first-run.js +1 -1
- package/dist/flaky-ci.d.ts +31 -0
- package/dist/flaky-ci.js +149 -0
- package/dist/flaky-cli.d.ts +60 -0
- package/dist/flaky-cli.js +60 -0
- package/dist/flaky.d.ts +75 -9
- package/dist/flaky.js +255 -0
- package/dist/fleet-schema.d.ts +17 -0
- package/dist/fleet-schema.js +135 -0
- package/dist/flow-browser.js +2 -2
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +9 -5
- package/dist/flow-result-card.d.ts +22 -0
- package/dist/flow-result-card.js +55 -0
- package/dist/flow-send.js +12 -9
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +2 -2
- package/dist/flow-unattended.d.ts +26 -0
- package/dist/flow-unattended.js +12 -3
- package/dist/flows-ui.js +10 -2
- package/dist/follow-through-brief.d.ts +12 -0
- package/dist/follow-through-brief.js +31 -0
- package/dist/follow-through-cli.d.ts +23 -0
- package/dist/follow-through-cli.js +153 -0
- package/dist/git-incident.d.ts +16 -0
- package/dist/git-incident.js +69 -6
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/handover-note.d.ts +33 -0
- package/dist/handover-note.js +106 -0
- package/dist/invoke.d.ts +25 -2
- package/dist/invoke.js +157 -58
- package/dist/job-object-helper.ps1 +10 -3
- package/dist/keymap.d.ts +41 -0
- package/dist/keymap.js +51 -0
- package/dist/knowledge-cli.js +1 -1
- package/dist/knowledge-ui.d.ts +21 -4
- package/dist/knowledge-ui.js +75 -7
- package/dist/laptop-background.d.ts +16 -0
- package/dist/laptop-background.js +108 -2
- package/dist/lead-cli.js +26 -24
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +3 -2
- package/dist/lead-doors.d.ts +4 -0
- package/dist/lead-doors.js +2 -2
- package/dist/lead-progress.js +5 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +162 -34
- package/dist/lead.js +2 -2
- package/dist/lesson-notes.d.ts +81 -0
- package/dist/lesson-notes.js +336 -0
- package/dist/limits-ui.js +1 -1
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +16 -0
- package/dist/machine-check.d.ts +16 -0
- package/dist/machine-check.js +26 -0
- package/dist/machine-identity.d.ts +75 -0
- package/dist/machine-identity.js +170 -0
- package/dist/mcp-core.js +11 -1
- package/dist/memory-cli.d.ts +300 -1
- package/dist/memory-cli.js +62 -9
- package/dist/memory-pass.d.ts +16 -0
- package/dist/memory-pass.js +67 -3
- package/dist/memory-tidy.d.ts +181 -0
- package/dist/memory-tidy.js +523 -0
- package/dist/memory-use.d.ts +35 -0
- package/dist/memory-use.js +109 -0
- package/dist/merge-checklist.d.ts +55 -0
- package/dist/merge-checklist.js +182 -0
- package/dist/modes.d.ts +6 -6
- package/dist/modes.js +3 -1
- package/dist/needs-you.d.ts +31 -9
- package/dist/needs-you.js +110 -41
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +7 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +565 -64
- package/dist/phase-routing.js +2 -2
- package/dist/plain-schema.d.ts +23 -0
- package/dist/plain-schema.js +94 -0
- package/dist/plain-words.d.ts +20 -0
- package/dist/plain-words.js +66 -0
- package/dist/plan-login.d.ts +14 -0
- package/dist/plan-login.js +58 -0
- package/dist/planner.d.ts +1 -1
- package/dist/planner.js +4 -2
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +19 -2
- package/dist/project-delete-ui.js +29 -1
- package/dist/project-delete.js +14 -1
- package/dist/project-knowledge.d.ts +18 -11
- package/dist/project-knowledge.js +39 -9
- package/dist/project-learning.d.ts +2 -1
- package/dist/project-learning.js +15 -45
- package/dist/project-memory.d.ts +5 -0
- package/dist/project-memory.js +22 -2
- package/dist/project-tools.d.ts +4 -4
- package/dist/project-tools.js +23 -7
- package/dist/proof.js +30 -5
- package/dist/protected-storage-look.d.ts +1 -0
- package/dist/protected-storage-look.js +13 -0
- package/dist/protected-storage.d.ts +70 -39
- package/dist/protected-storage.js +196 -147
- package/dist/provider-auth.js +1 -1
- package/dist/provider-capture.d.ts +34 -0
- package/dist/provider-capture.js +53 -0
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +18 -2
- package/dist/push.js +40 -16
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +76 -0
- package/dist/release-auto.js +13 -13
- package/dist/release-card.d.ts +1 -2
- package/dist/release-card.js +11 -0
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +49 -10
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +29 -22
- package/dist/release-coverage.js +1 -1
- package/dist/release-next.d.ts +48 -0
- package/dist/release-next.js +161 -0
- package/dist/release.d.ts +20 -1
- package/dist/release.js +85 -9
- package/dist/remote-destination.d.ts +36 -0
- package/dist/remote-destination.js +217 -0
- package/dist/remote-main.d.ts +48 -0
- package/dist/remote-main.js +84 -5
- package/dist/repository-context.d.ts +3 -2
- package/dist/repository-context.js +112 -70
- package/dist/restart-certification.d.ts +1 -0
- package/dist/restart-certification.js +8 -6
- package/dist/result-acts.d.ts +27 -19
- package/dist/result-acts.js +36 -25
- package/dist/result-follow-ups.d.ts +2 -2
- package/dist/result-follow-ups.js +18 -4
- package/dist/result-review.d.ts +1 -1
- package/dist/route-change-check.d.ts +4 -0
- package/dist/route-change-check.js +13 -0
- package/dist/route-change.d.ts +22 -0
- package/dist/route-change.js +89 -0
- package/dist/route-hooks.js +3 -2
- package/dist/route-results.d.ts +6 -1
- package/dist/route-results.js +14 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +7 -5
- package/dist/runner-process.js +3 -2
- package/dist/runner.js +2 -1
- package/dist/scope.d.ts +28 -4
- package/dist/scope.js +92 -28
- package/dist/scout.d.ts +1 -1
- package/dist/scout.js +2 -1
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +26 -18
- package/dist/server/chat.js +16 -14
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +73 -0
- package/dist/server/guards.js +3 -0
- package/dist/server/home.d.ts +6 -0
- package/dist/server/home.js +21 -13
- package/dist/server/http.js +1 -1
- package/dist/server/kit.js +1 -1
- package/dist/server/merge-ready-view.d.ts +10 -0
- package/dist/server/merge-ready-view.js +30 -0
- package/dist/server/pages-assets.d.ts +1 -0
- package/dist/server/pages-assets.js +8 -5
- package/dist/server/pages.js +103 -82
- package/dist/server/render-chat.js +16 -3
- package/dist/server/render-code-map.d.ts +30 -0
- package/dist/server/render-code-map.js +137 -0
- package/dist/server/render-flaky.d.ts +20 -0
- package/dist/server/render-flaky.js +59 -0
- package/dist/server/render-follow-through.d.ts +11 -0
- package/dist/server/render-follow-through.js +68 -0
- package/dist/server/render-history.js +1 -1
- package/dist/server/render-new-task.d.ts +26 -1
- package/dist/server/render-new-task.js +30 -5
- package/dist/server/render-pages.d.ts +1 -175
- package/dist/server/render-pages.js +38 -313
- package/dist/server/render-projects.d.ts +2 -1
- package/dist/server/render-projects.js +9 -7
- package/dist/server/render-release.d.ts +2 -0
- package/dist/server/render-release.js +3 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +68 -68
- package/dist/server/render-tasks.d.ts +16 -2
- package/dist/server/render-tasks.js +177 -141
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +48 -12
- package/dist/server/settings.js +166 -34
- package/dist/server/task-views.js +66 -26
- package/dist/server/tasks.d.ts +2 -6
- package/dist/server/tasks.js +250 -321
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.d.ts +1 -0
- package/dist/settings-groups-ui.js +4 -2
- package/dist/settings-kit.js +3 -2
- package/dist/settings-proofs.js +3 -1
- package/dist/slack-chat.js +14 -1
- package/dist/steer-mailbox.d.ts +8 -1
- package/dist/steer-mailbox.js +164 -6
- package/dist/stop-proof.js +6 -4
- package/dist/storage-sweep-process.js +1 -1
- package/dist/storage-sweep.d.ts +5 -3
- package/dist/storage-sweep.js +30 -9
- package/dist/storage-ui.js +28 -17
- package/dist/storage.d.ts +27 -2
- package/dist/storage.js +39 -2
- package/dist/store.d.ts +89 -7
- package/dist/store.js +386 -114
- package/dist/surface.js +67 -6
- package/dist/sweep-quarantine.d.ts +1 -0
- package/dist/sweep-quarantine.js +6 -4
- package/dist/tamper-scan.d.ts +4 -0
- package/dist/tamper-scan.js +90 -0
- package/dist/task-aliases.d.ts +5 -0
- package/dist/task-aliases.js +9 -0
- package/dist/task-control.d.ts +2 -1
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +17 -17
- package/dist/task-sizing.d.ts +5 -0
- package/dist/task-sizing.js +2 -0
- package/dist/task-source.d.ts +7 -0
- package/dist/task-source.js +14 -0
- package/dist/task-status.d.ts +9 -0
- package/dist/task-status.js +45 -49
- package/dist/teardown-git.d.ts +18 -0
- package/dist/teardown-git.js +105 -21
- package/dist/telegram-lead.js +23 -6
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/telegram.js +12 -1
- package/dist/test-infra.d.ts +8 -0
- package/dist/test-infra.js +18 -0
- package/dist/tidy.js +1 -1
- package/dist/toolroll-update.js +19 -5
- package/dist/tui-contract.d.ts +24 -0
- package/dist/tui-source.js +67 -8
- package/dist/tui.js +51 -51
- package/dist/vanished-retry.d.ts +30 -0
- package/dist/vanished-retry.js +174 -0
- package/dist/work-index.d.ts +2 -0
- package/dist/work-index.js +95 -66
- package/dist/work-summary.d.ts +1 -1
- package/dist/work-summary.js +7 -2
- package/dist/workspace-ui.d.ts +7 -3
- package/dist/workspace-ui.js +38 -23
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +91 -14
- package/package.json +2 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Code map page (docs/plans/code-map.md §6): read-only, from the shared reader. Every name on it came from the
|
|
3
|
+
* repository: `html` escapes it, links carry it through encodeURIComponent, and the handler looks it up exactly.
|
|
4
|
+
*/
|
|
5
|
+
import { githubRepoOf } from "../pull-request-flow.js";
|
|
6
|
+
import { areaRows, areaView, fileView, searchMap } from "../code-map-read.js";
|
|
7
|
+
import { NOT_ENOUGH, reportRows } from "../code-map-report.js";
|
|
8
|
+
import { html, joinHtml, postForm } from "../html.js";
|
|
9
|
+
import { relativeAge, screen } from "./chrome.js";
|
|
10
|
+
import { btn, disclose, doc, docHeader, empty, facts, infoLine, input, note, notice, phoneHeader } from "./kit.js";
|
|
11
|
+
const WARNING_WORDS = {
|
|
12
|
+
"symbols-truncated": "Some files have more exported names than the map keeps.",
|
|
13
|
+
"edges-truncated": "This repository has more imports than the map keeps.",
|
|
14
|
+
"read-limit": "Past the read limit, the remaining code files are listed by path only.",
|
|
15
|
+
"areas-folded": "The smallest areas are folded into “other areas”.",
|
|
16
|
+
"tsconfig-extends": "Settings inherited through tsconfig “extends” aren't read, so some imports aren't connected.",
|
|
17
|
+
"tsconfig-unreadable": "tsconfig.json couldn't be read; relative imports still work.",
|
|
18
|
+
};
|
|
19
|
+
export const LIMITS_WORDS = { system: "Limits enforced by the system.", checked: "Limits checked by Toolroll every 250 ms." };
|
|
20
|
+
const mono = (text) => html `<code class="k-mono cm-path">${text}</code>`;
|
|
21
|
+
const short = (commit) => commit.slice(0, 7);
|
|
22
|
+
const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
23
|
+
const hrefOf = (data, extra) => {
|
|
24
|
+
const params = new URLSearchParams(data.versionId === null ? extra : { v: String(data.versionId), ...extra });
|
|
25
|
+
return `${data.base}?${params.toString()}`;
|
|
26
|
+
};
|
|
27
|
+
function areaBlock(data, one, map) {
|
|
28
|
+
const named = (id) => map.areas.find(area => area.id === id)?.name ?? id;
|
|
29
|
+
const links = (list) => joinHtml(list.slice(0, 6).map(use => html `<a class="k-link" href="${hrefOf(data, { area: use.id })}">${named(use.id)}</a>`), ", ");
|
|
30
|
+
return html `<li class="cm-item"><a class="k-link cm-title" href="${hrefOf(data, { area: one.id })}">${one.name}</a>
|
|
31
|
+
<span class="cm-meta">${plural(one.files, "file")}${one.languages.length === 0 ? "" : ` · ${one.languages.join(", ")}`}${one.tests === 0 ? "" : ` · ${plural(one.tests, "test")}`}</span>
|
|
32
|
+
${one.uses.length === 0 ? "" : html `<span class="cm-meta">Uses ${links(one.uses)}</span>`}
|
|
33
|
+
${one.usedBy.length === 0 ? "" : html `<span class="cm-meta">Used by ${links(one.usedBy)}</span>`}</li>`;
|
|
34
|
+
}
|
|
35
|
+
const fileBlock = (data, file) => html `<li class="cm-item"><a class="k-link cm-title" href="${hrefOf(data, { file: file.path })}">${file.path}</a>
|
|
36
|
+
<span class="cm-meta">${file.lang} · ${plural(file.bytes, "byte")}${file.test === null ? "" : html ` · Tests: ${file.test}`}</span>
|
|
37
|
+
${file.symbols.length === 0 ? "" : html `<span class="cm-meta">Exported names: ${file.symbols.slice(0, 12).map(one => one.name).join(", ")}${file.symbols.length > 12 ? ` and ${file.symbols.length - 12} more` : ""}</span>`}</li>`;
|
|
38
|
+
function overview(data, map) {
|
|
39
|
+
return html `<section class="k-sec" aria-labelledby="cm-areas"><h3 id="cm-areas">Areas</h3>
|
|
40
|
+
${map.areas.length === 0 ? note("This repository is empty.") : html `<ul class="cm-list">${areaRows(map).map(one => areaBlock(data, one, map))}</ul>`}</section>`;
|
|
41
|
+
}
|
|
42
|
+
function areaPage(data, map, id) {
|
|
43
|
+
const view = areaView(map, id);
|
|
44
|
+
if (view === null)
|
|
45
|
+
return notice("That area isn't in this version of the map.");
|
|
46
|
+
return html `<section class="k-sec" aria-labelledby="cm-area"><h3 id="cm-area">${view.area.name}</h3>
|
|
47
|
+
${facts([
|
|
48
|
+
["Files", String(view.area.files)], ["Tests", String(view.area.tests)],
|
|
49
|
+
...(view.entryPoints.length === 0 ? [] : [["Entry points", joinHtml(view.entryPoints.map(one => html `<a class="k-link" href="${hrefOf(data, { file: one })}">${one}</a>`), ", ")]]),
|
|
50
|
+
...(view.external.length === 0 ? [] : [["Packages", view.external.join(", ")]]),
|
|
51
|
+
...(view.area.uses.length === 0 ? [] : [["Uses", joinHtml(view.area.uses.map(one => html `<a class="k-link" href="${hrefOf(data, { area: one.id })}">${one.id}</a> (${String(one.count)})`), ", ")]]),
|
|
52
|
+
...(view.area.usedBy.length === 0 ? [] : [["Used by", joinHtml(view.area.usedBy.map(one => html `<a class="k-link" href="${hrefOf(data, { area: one.id })}">${one.id}</a> (${String(one.count)})`), ", ")]]),
|
|
53
|
+
])}</section>
|
|
54
|
+
<section class="k-sec" aria-labelledby="cm-files"><h3 id="cm-files">Files</h3><ul class="cm-list">${view.files.slice(0, 500).map(one => fileBlock(data, one))}</ul>
|
|
55
|
+
${view.files.length > 500 ? note(`${view.files.length - 500} more. Search to find them.`) : ""}</section>`;
|
|
56
|
+
}
|
|
57
|
+
function filePage(data, map, path) {
|
|
58
|
+
const view = fileView(map, path);
|
|
59
|
+
if (view === null)
|
|
60
|
+
return notice("That file isn't in this version of the map.");
|
|
61
|
+
const github = data.remoteUrl === null ? null : githubRepoOf(data.remoteUrl);
|
|
62
|
+
const list = (rows) => rows.length === 0 ? note("None.") : html `<ul class="cm-list">${rows.slice(0, 200).map(one => html `<li class="cm-item"><a class="k-link cm-title" href="${hrefOf(data, { file: one.path })}">${one.path}</a><span class="cm-meta">line ${String(one.line)}</span></li>`)}</ul>`;
|
|
63
|
+
return html `<section class="k-sec" aria-labelledby="cm-file"><h3 id="cm-file">${mono(view.file.path)}</h3>
|
|
64
|
+
${github === null ? "" : html `<p><a class="k-link" rel="noopener noreferrer" target="_blank" href="${`https://github.com/${github}/blob/${map.commit}/${view.file.path.split("/").map(encodeURIComponent).join("/")}`}">Open on GitHub at ${short(map.commit)}</a></p>`}
|
|
65
|
+
${facts([["Area", html `<a class="k-link" href="${hrefOf(data, { area: view.file.area })}">${view.file.area}</a>`], ["Language", view.file.lang], ...(view.file.test === null ? [] : [["Tests", html `<a class="k-link" href="${hrefOf(data, { file: view.file.test })}">${view.file.test}</a>`]])])}</section>
|
|
66
|
+
<section class="k-sec" aria-labelledby="cm-names"><h3 id="cm-names">Exported names</h3>${view.file.symbols.length === 0 ? note(view.file.state === "read" ? "None." : "Names and imports aren't read for this file.") : html `<ul class="cm-list">${view.file.symbols.map(one => html `<li class="cm-item"><span class="cm-title">${one.name}</span><span class="cm-meta">${one.kind} · line ${String(one.line)}</span></li>`)}</ul>`}</section>
|
|
67
|
+
<section class="k-sec" aria-labelledby="cm-imports"><h3 id="cm-imports">Imports</h3>${list(view.imports)}</section>
|
|
68
|
+
<section class="k-sec" aria-labelledby="cm-by"><h3 id="cm-by">Imported by</h3>${list(view.importedBy)}</section>`;
|
|
69
|
+
}
|
|
70
|
+
function searchPage(data, map, query) {
|
|
71
|
+
const hits = searchMap(map, query, 200);
|
|
72
|
+
return html `<section class="k-sec" aria-labelledby="cm-found"><h3 id="cm-found">${plural(hits.length, "match", "matches")}</h3>
|
|
73
|
+
${hits.length === 0 ? note("Nothing in this version matches.") : html `<ul class="cm-list">${hits.map(one => html `<li class="cm-item"><a class="k-link cm-title" href="${hrefOf(data, { file: one.path })}">${one.name ?? one.path}</a>${one.name === null ? "" : html `<span class="cm-meta">${one.path} · line ${String(one.line)}</span>`}</li>`)}</ul>`}</section>`;
|
|
74
|
+
}
|
|
75
|
+
function provenance(data, map) {
|
|
76
|
+
const report = (() => { try {
|
|
77
|
+
return JSON.parse(data.status.version?.report ?? "{}");
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return {};
|
|
81
|
+
} })();
|
|
82
|
+
const c = map.counts;
|
|
83
|
+
return disclose("Where this comes from", joinHtml([
|
|
84
|
+
note(map.label),
|
|
85
|
+
facts([
|
|
86
|
+
["Remote", data.remoteUrl === null ? "None" : mono(data.remoteUrl)],
|
|
87
|
+
["Files", `${c.files} (${c.read} read, ${c.pathOnly} by path only, ${c.tooLarge} too large to read)`],
|
|
88
|
+
...(c.parseErrors > 0 ? [["Unreadable", plural(c.parseErrors, "file")]] : []),
|
|
89
|
+
...(c.withheld > 0 ? [["Withheld", `${plural(c.withheld, "name")} Toolroll won't show`]] : []),
|
|
90
|
+
...(c.links + c.submodules > 0 ? [["Not followed", `${plural(c.links, "link")}, ${plural(c.submodules, "submodule")}`]] : []),
|
|
91
|
+
...(map.vendored.length === 0 ? [] : [["Left out", map.vendored.map(one => `${one.path} (${plural(one.files, "file")})`).join(", ")]]),
|
|
92
|
+
]),
|
|
93
|
+
note("require() calls aren't counted. Other languages show paths and sizes only."),
|
|
94
|
+
...map.warnings.map(code => note(WARNING_WORDS[code])),
|
|
95
|
+
...(report.proxy ? [note(`Read through your proxy at ${report.proxy}.`)] : []),
|
|
96
|
+
...(report.enforced === undefined ? [] : [note(LIMITS_WORDS[report.enforced])]),
|
|
97
|
+
]));
|
|
98
|
+
}
|
|
99
|
+
/** "Builds with and without the map": read by people; nothing switches on it. */
|
|
100
|
+
export function codeMapReportBlock(report) {
|
|
101
|
+
if (report.arms.map.families + report.arms.holdout.families === 0)
|
|
102
|
+
return null;
|
|
103
|
+
return html `<section class="k-sec" aria-labelledby="cm-report"><h3 id="cm-report">Builds with and without the map</h3>
|
|
104
|
+
${report.enough ? "" : note(NOT_ENOUGH)}
|
|
105
|
+
${facts(reportRows(report).map(row => [row.label, `With ${row.map} · without ${row.holdout}`]))}</section>`;
|
|
106
|
+
}
|
|
107
|
+
export function codeMapPage(chrome, data) {
|
|
108
|
+
const { status, map } = data;
|
|
109
|
+
const age = status.version === null ? "" : ` · ${relativeAge(status.version.builtAt, data.now)}`;
|
|
110
|
+
const meta = map === null ? status.words : `main at ${short(map.commit)}${age}`;
|
|
111
|
+
const check = data.canRefresh && status.state !== "not-available" && status.state !== "off"
|
|
112
|
+
? postForm(`${data.base}/refresh`, btn("Check now", { sm: true, type: "submit" }), { returnTo: data.selfHref, hidden: { repo: data.repo } }) : "";
|
|
113
|
+
const stated = data.problem !== null ? notice(data.problem) : data.said !== null ? infoLine(data.said) : "";
|
|
114
|
+
const warn = status.state === "couldnt-check" || status.state === "too-large" || status.state === "remote-changed" ? notice(status.words) : status.state === "stale" ? infoLine(status.words) : "";
|
|
115
|
+
let content;
|
|
116
|
+
if (map === null) {
|
|
117
|
+
content = status.state === "making" ? empty({ title: "Making the first map.", text: "It appears here when it's ready." })
|
|
118
|
+
: status.state === "off" ? empty({ title: "The code map is off.", text: "Turn it on in this project's settings." })
|
|
119
|
+
: status.state === "not-available" ? empty({ title: "No code map.", text: status.words })
|
|
120
|
+
: status.state === "no-main" ? empty({ title: "No main yet.", text: status.words })
|
|
121
|
+
: "";
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const search = html `<form class="cm-search" method="get" action="${data.base}" role="search">
|
|
125
|
+
${data.versionId === null ? "" : html `<input type="hidden" name="v" value="${String(data.versionId)}">`}
|
|
126
|
+
${input({ id: "cm-q", name: "q", type: "search", value: data.q ?? "", attrs: { "aria-label": "Search files and exported names", placeholder: "Search files and exported names" } })}
|
|
127
|
+
${btn("Search", { sm: true, type: "submit" })}</form>`;
|
|
128
|
+
const view = data.q !== null && data.q.trim() !== "" ? searchPage(data, map, data.q) : data.file !== null ? filePage(data, map, data.file) : data.area !== null ? areaPage(data, map, data.area) : overview(data, map);
|
|
129
|
+
const atTop = data.q === null && data.file === null && data.area === null;
|
|
130
|
+
content = joinHtml([search, provenance(data, map), atTop ? "" : html `<p><a class="k-link" href="${hrefOf(data, {})}">All areas</a></p>`, view, atTop ? data.report ?? "" : ""]);
|
|
131
|
+
}
|
|
132
|
+
const body = joinHtml([
|
|
133
|
+
phoneHeader("Code map", { back: data.selfHref.replace(/\/map(?:\?.*)?$/, "") }),
|
|
134
|
+
doc(docHeader({ back: [data.base.replace(/\/map$/, ""), data.name], title: "Code map", meta, ...(check === "" ? {} : { right: check }), same: true }), stated, warn, content),
|
|
135
|
+
]);
|
|
136
|
+
return screen("Code map", body, { chrome });
|
|
137
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type FlakyList, type FlakyObservation, type FlakyRow } from "../flaky.js";
|
|
2
|
+
import { type Chrome, type Screen } from "./chrome.js";
|
|
3
|
+
/** The list. */
|
|
4
|
+
export declare function flakyPage(chrome: Chrome, data: {
|
|
5
|
+
name: string;
|
|
6
|
+
repo: string;
|
|
7
|
+
back: string;
|
|
8
|
+
selfHref: string;
|
|
9
|
+
list: FlakyList;
|
|
10
|
+
now: Date;
|
|
11
|
+
}): Screen;
|
|
12
|
+
/** One row's history. */
|
|
13
|
+
export declare function flakyRowPage(chrome: Chrome, data: {
|
|
14
|
+
name: string;
|
|
15
|
+
repo: string;
|
|
16
|
+
back: string;
|
|
17
|
+
row: FlakyRow;
|
|
18
|
+
history: readonly FlakyObservation[];
|
|
19
|
+
now: Date;
|
|
20
|
+
}): Screen;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The flaky tests list (docs/plans/learning-loops.md, "The list"): a project's tests with mixed outcomes under recorded
|
|
3
|
+
* conditions, on the kit. Reads only; the one action is "Make a task to fix it", the ordinary /tasks/add form, and a
|
|
4
|
+
* person approves that task as usual. Test names are candidate-written text, escaped at the sink like everything else.
|
|
5
|
+
*/
|
|
6
|
+
import { flakyCoverageWords, flakyStatusWords } from "../flaky-cli.js";
|
|
7
|
+
import { fixTaskOf } from "../flaky.js";
|
|
8
|
+
import { html, joinHtml, postForm } from "../html.js";
|
|
9
|
+
import { relativeAge, screen } from "./chrome.js";
|
|
10
|
+
import { btn, chip, disclose, doc, docHeader, empty, facts, note, phoneHeader, row } from "./kit.js";
|
|
11
|
+
const KIND_WORDS = { regate: "Checks run again on the same commit", "same-tree": "Two checks of the same code", "in-check-retry": "Passed on a retry inside one check", github: "GitHub re-run" };
|
|
12
|
+
/** "Make a task to fix it": the ordinary task form, filled in. */
|
|
13
|
+
function fixForm(repo, data, returnTo) {
|
|
14
|
+
const task = fixTaskOf(data);
|
|
15
|
+
return postForm("/tasks/add", btn("Make a task to fix it", { sm: true, type: "submit" }), { returnTo, hidden: { title: task.title, goal: task.goal, repo } });
|
|
16
|
+
}
|
|
17
|
+
const statusChip = (data) => chip(flakyStatusWords(data), "", { dot: false });
|
|
18
|
+
/** The list. */
|
|
19
|
+
export function flakyPage(chrome, data) {
|
|
20
|
+
const { list } = data;
|
|
21
|
+
const rowHref = (key) => `${data.selfHref}${data.selfHref.includes("?") ? "&" : "?"}row=${encodeURIComponent(key)}`;
|
|
22
|
+
const rows = list.rows.map(one => row({
|
|
23
|
+
href: rowHref(one.key), chip: statusChip(one), title: one.name,
|
|
24
|
+
desc: `${one.observations} in ${list.days} days · ${one.conditions.join(", ") || "conditions not recorded"} · ${one.hardFailures} hard failure${one.hardFailures === 1 ? "" : "s"} · ${one.sources.join(", ") || "no source recorded"}`,
|
|
25
|
+
meta: html `<span class="k-num">${relativeAge(one.lastSeen, data.now)}</span>`,
|
|
26
|
+
action: fixForm(data.repo, one, data.selfHref),
|
|
27
|
+
}));
|
|
28
|
+
const shortHistory = list.complete ? "" : note(`Not enough recorded history: test results are kept for 180 days, so ${list.days} days can't be counted in full.`);
|
|
29
|
+
const body = list.rows.length === 0
|
|
30
|
+
? (list.complete ? empty({ title: "No mixed outcomes recorded" }) : "")
|
|
31
|
+
: html `<div class="flaky-rows">${joinHtml(rows)}</div>`;
|
|
32
|
+
return screen("Flaky tests", joinHtml([
|
|
33
|
+
phoneHeader("Flaky tests", { back: data.back }),
|
|
34
|
+
doc(docHeader({ back: [data.back, data.name], title: "Flaky tests", meta: "Mixed outcomes under recorded conditions" }), shortHistory, body, disclose("What this covers", joinHtml([
|
|
35
|
+
note(flakyCoverageWords(list)),
|
|
36
|
+
list.notPaired === 0 ? "" : note(`${list.notPaired} GitHub re-run${list.notPaired === 1 ? "" : "s"} not paired.`),
|
|
37
|
+
note("Not recorded: network, other processes, time of day."),
|
|
38
|
+
]))),
|
|
39
|
+
], "\n"), { chrome });
|
|
40
|
+
}
|
|
41
|
+
/** One row's history. */
|
|
42
|
+
export function flakyRowPage(chrome, data) {
|
|
43
|
+
const one = data.row;
|
|
44
|
+
const history = data.history.length === 0 ? note("No history recorded.") : joinHtml(data.history.map(entry => row({
|
|
45
|
+
chip: html ``, title: KIND_WORDS[entry.kind],
|
|
46
|
+
desc: `${entry.failed} failed · ${entry.passed} passed · ${entry.commit ?? "commit not recorded"} · ${entry.conditions}`,
|
|
47
|
+
meta: html `<span class="k-num">${relativeAge(entry.at, data.now)}</span>`,
|
|
48
|
+
})));
|
|
49
|
+
return screen(one.name, joinHtml([
|
|
50
|
+
phoneHeader("Flaky test", { back: data.back }),
|
|
51
|
+
doc(docHeader({ back: [data.back, "Flaky tests"], title: one.name, meta: flakyStatusWords(one), right: fixForm(data.repo, one, data.back) }), facts([
|
|
52
|
+
["Seen", `${one.observations} time${one.observations === 1 ? "" : "s"}, last ${relativeAge(one.lastSeen, data.now)}`],
|
|
53
|
+
["Hard failures", String(one.hardFailures)],
|
|
54
|
+
["Conditions", one.conditions.join(", ") || "not recorded"],
|
|
55
|
+
["Recorded by", one.sources.join(", ") || "not recorded"],
|
|
56
|
+
["Test file last changed", one.fileChanged === null ? "not recorded" : relativeAge(one.fileChanged, data.now)],
|
|
57
|
+
]), html `<section class="k-sec" aria-labelledby="f-history"><h3 id="f-history">History</h3>${history}</section>`),
|
|
58
|
+
], "\n"), { chrome });
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Html } from "../html.js";
|
|
2
|
+
import type { Store } from "../store.js";
|
|
3
|
+
export type FollowThroughView = {
|
|
4
|
+
findings: Html | "";
|
|
5
|
+
findingCount: number;
|
|
6
|
+
openFindings: number;
|
|
7
|
+
repairs: Html | "";
|
|
8
|
+
evidence: Html | "";
|
|
9
|
+
handover: Html | "";
|
|
10
|
+
};
|
|
11
|
+
export declare function followThroughView(store: Store, root: string, taskId: string, taskHref: string, canAct: boolean, now: Date): FollowThroughView;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** The task page's follow-through sections (docs/plans/fleet-follow-through.md): findings, failed checks, before and after,
|
|
2
|
+
* and the handover note. All of a finding's text is model output or repository text: it renders as text, in `html`'s
|
|
3
|
+
* escaping, and no action here is reachable from it. Every form posts to the one action in tasks.ts. */
|
|
4
|
+
import { checkRepairsOfTask, checkRepairWords } from "../check-repair.js";
|
|
5
|
+
import { evidencePairsOf, evidenceWords, failingFirstOf, failingFirstWords } from "../evidence-pair.js";
|
|
6
|
+
import { findingLine, findingFromWords, findingsOfTask, findingStandingWords } from "../findings.js";
|
|
7
|
+
import { handoverLines, handoverNoteOf } from "../handover-note.js";
|
|
8
|
+
import { html, joinHtml, postForm } from "../html.js";
|
|
9
|
+
import { providerCaptures } from "../provider-capture.js";
|
|
10
|
+
import { btn, disclose, field, input, note, textarea } from "./kit.js";
|
|
11
|
+
const act = (taskHref, name, label, hidden = {}, variant = "outline") => postForm(`${taskHref}/follow-through`, joinHtml([
|
|
12
|
+
html `<input type="hidden" name="act" value="${name}">`,
|
|
13
|
+
...Object.entries(hidden).map(([key, value]) => html `<input type="hidden" name="${key}" value="${String(value)}">`),
|
|
14
|
+
btn(label, { variant, type: "submit" }),
|
|
15
|
+
]));
|
|
16
|
+
function findingRow(one, taskHref, canAct) {
|
|
17
|
+
const answered = one.state === "accepted" || one.state === "rejected";
|
|
18
|
+
const closing = canAct && one.state !== "closed" ? disclose("Close it", postForm(`${taskHref}/follow-through`, joinHtml([
|
|
19
|
+
html `<input type="hidden" name="act" value="close"><input type="hidden" name="finding" value="${String(one.id)}">`,
|
|
20
|
+
field({ id: `finding-close-${one.id}`, label: "Why it can close", control: input({ id: `finding-close-${one.id}`, name: "note" }) }),
|
|
21
|
+
btn("Close finding", { variant: "outline", type: "submit" }),
|
|
22
|
+
]))) : "";
|
|
23
|
+
return html `<li data-finding="${one.state}"><p>${findingLine(one)}</p><p class="meta">${findingStandingWords(one)}</p><p class="meta">From ${findingFromWords(one)}. Requested: ${one.action}</p>${canAct && answered ? act(taskHref, "resend", "Send it back", { finding: one.id }) : ""}${closing}</li>`;
|
|
24
|
+
}
|
|
25
|
+
function sendForm(taskHref) {
|
|
26
|
+
return disclose("Send a finding", postForm(`${taskHref}/follow-through`, joinHtml([
|
|
27
|
+
html `<input type="hidden" name="act" value="send">`,
|
|
28
|
+
field({ id: "finding-note", label: "What's wrong", control: textarea({ id: "finding-note", name: "note", rows: 3 }) }),
|
|
29
|
+
field({ id: "finding-file", label: "File, if there is one", control: input({ id: "finding-file", name: "file" }) }),
|
|
30
|
+
field({ id: "finding-line", label: "Line", control: input({ id: "finding-line", name: "line", type: "number" }) }),
|
|
31
|
+
btn("Send to the agent", { variant: "outline", type: "submit" }),
|
|
32
|
+
])));
|
|
33
|
+
}
|
|
34
|
+
const captureRow = (one, taskHref, canAct) => html `<li><p class="meta">${one.provider}, build #${one.run}${one.label === null ? "" : `, marked ${one.label}`}</p><p class="mono">${one.text.slice(0, 500)}</p>${canAct && one.label === null ? joinHtml([act(taskHref, "mark", "Used-up plan", { capture: one.id, label: "exhausted" }), act(taskHref, "mark", "Something else", { capture: one.id, label: "not-exhausted" })]) : ""}</li>`;
|
|
35
|
+
export function followThroughView(store, root, taskId, taskHref, canAct, now) {
|
|
36
|
+
const findings = findingsOfTask(store, taskId);
|
|
37
|
+
const open = findings.filter(one => one.state === "open" || one.state === "with-person").length;
|
|
38
|
+
const repairs = checkRepairsOfTask(store, taskId).filter(one => one.state !== "closed");
|
|
39
|
+
const pairs = evidencePairsOf(store, root, taskId, now);
|
|
40
|
+
const step = failingFirstOf(store, taskId);
|
|
41
|
+
const stepWords = failingFirstWords(step);
|
|
42
|
+
const hasRun = store.lookupRef(taskId) !== null && store.runsFor(store.lookupRef(taskId).id).some(one => one.role === "builder" && one.finishedAt !== null);
|
|
43
|
+
const family = store.taskFamilyOf(taskId, null, true);
|
|
44
|
+
const familyRuns = new Set((family?.versions.map(one => one.id) ?? [taskId]).flatMap(id => { const ref = store.lookupRef(id); return ref === null ? [] : store.runsFor(ref.id).map(run => run.id); }));
|
|
45
|
+
const captures = providerCaptures(store, null, 200).filter(one => familyRuns.has(one.run)).slice(0, 5);
|
|
46
|
+
const latestFailed = repairs.find(one => one.state !== "fixed");
|
|
47
|
+
const note1 = handoverNoteOf(store, root, taskId, now);
|
|
48
|
+
return {
|
|
49
|
+
findingCount: findings.length, openFindings: open,
|
|
50
|
+
findings: findings.length === 0 && !canAct ? "" : joinHtml([
|
|
51
|
+
findings.length === 0 ? "" : html `<ul class="result-changes">${findings.map(one => findingRow(one, taskHref, canAct))}</ul>`,
|
|
52
|
+
canAct ? sendForm(taskHref) : "",
|
|
53
|
+
]),
|
|
54
|
+
repairs: repairs.length === 0 ? "" : html `<ul class="result-changes">${repairs.map(one => html `<li data-check-repair="${one.state}"><p>${checkRepairWords(one)}</p>${one.repairTask === null ? "" : html `<p class="meta"><a class="k-link" href="/t/${encodeURIComponent(one.repairTask)}">Open the repair</a></p>`}${canAct && one.repairTask === null && (one.state === "waiting" || one.state === "parked") ? act(taskHref, "repair", "Start a repair", { run: one.sourceRun }, "primary") : ""}</li>`)}</ul>`,
|
|
55
|
+
evidence: pairs.length === 0 && stepWords === null && captures.length === 0 && !(canAct && hasRun) ? "" : joinHtml([
|
|
56
|
+
pairs.length === 0 ? "" : html `<ul class="result-changes">${pairs.map(one => html `<li data-evidence="${one.reading}"><p>${evidenceWords(one)}</p><p class="meta"><a class="k-link" href="/r/${String(one.before.run)}">Before</a>${one.after === null ? "" : html ` · <a class="k-link" href="/r/${String(one.after.run)}">After</a>`}${one.before.screenshots.length + (one.after?.screenshots.length ?? 0) === 0 ? "" : ` · ${one.before.screenshots.length + (one.after?.screenshots.length ?? 0)} screenshots`}</p></li>`)}</ul>`,
|
|
57
|
+
stepWords === null ? "" : note(stepWords),
|
|
58
|
+
step?.output == null ? "" : html `<p class="mono">${step.output}</p>`,
|
|
59
|
+
canAct && hasRun ? html `<div class="k-acts">${joinHtml([
|
|
60
|
+
...(latestFailed === undefined ? [] : [act(taskHref, "save-evidence", "Save the failing result", { run: latestFailed.sourceRun })]),
|
|
61
|
+
...(pairs.length === 0 ? [] : [act(taskHref, "rerun-evidence", "Run the same check again")]),
|
|
62
|
+
])}</div>` : "",
|
|
63
|
+
canAct ? act(taskHref, step === null ? "failing-first-on" : "failing-first-off", step === null ? "Ask for a failing test first" : "Turn off failing test first") : "",
|
|
64
|
+
captures.length === 0 ? "" : disclose("What the provider said", html `<ul class="result-changes">${captures.map(one => captureRow(one, taskHref, canAct))}</ul>`),
|
|
65
|
+
]),
|
|
66
|
+
handover: note1 === null ? "" : html `<pre class="mono handover-note">${handoverLines(note1).join("\n")}</pre>`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -22,7 +22,7 @@ export function runPage(chrome, run, taskId, artifacts, terminal = null, notes =
|
|
|
22
22
|
? note(html `The live transcript is off. Start serve with <span class="k-mono">--runner <name></span> naming this machine's worker, and it appears here.`)
|
|
23
23
|
: run.provider !== "claude"
|
|
24
24
|
? note(`The live transcript needs the claude harness for now; this build runs on ${run.provider}.`)
|
|
25
|
-
: html
|
|
25
|
+
: html `<pre id="live-transcript" class="k-mono h-pre h-live"></pre><p class="k-note" id="live-transcript-state"></p>`);
|
|
26
26
|
// The live peek region (A2): the poller fills it only on a serve that asserted its runner; otherwise the section
|
|
27
27
|
// says why it is empty, so the click never lands on silence.
|
|
28
28
|
const peek = !running ? "" : sec("What is changing right now", peekable
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { QualityMode } from "../quality.js";
|
|
2
2
|
import type { UnattendedPermissionMode } from "../scope.js";
|
|
3
3
|
import { type Chrome, type Screen } from "./chrome.js";
|
|
4
|
+
import type { Scope } from "../scope.js";
|
|
5
|
+
import type { Store } from "../store.js";
|
|
4
6
|
export type TaskComposerPrefill = {
|
|
5
7
|
title: string;
|
|
6
8
|
goal: string;
|
|
@@ -26,4 +28,27 @@ draft?: string | null,
|
|
|
26
28
|
suggestions?: ReadonlyArray<{
|
|
27
29
|
label: string;
|
|
28
30
|
draft: string;
|
|
29
|
-
}
|
|
31
|
+
}>,
|
|
32
|
+
/** A link the lead or a person put in the address (GET ?from=). */
|
|
33
|
+
fromLink?: string | null): Screen;
|
|
34
|
+
/** The terms as label and words: the lines every approval surface reads (fast-terms.ts), with nothing hidden. */
|
|
35
|
+
export declare function fastTermsRows(store: Store, scope: Scope, repo: string): ReadonlyArray<readonly [string, string]>;
|
|
36
|
+
/** Build (docs/plans/plain-toolroll.md P2.4): the exact terms this small task would be approved on, and the one button
|
|
37
|
+
* that approves them. The fields of "Plan it first" are the person's own words, on the ordinary road. */
|
|
38
|
+
export declare function fastStartPage(chrome: Chrome, input: {
|
|
39
|
+
title: string;
|
|
40
|
+
repo: string;
|
|
41
|
+
preview: string;
|
|
42
|
+
digest: string;
|
|
43
|
+
terms: ReadonlyArray<readonly [string, string]>;
|
|
44
|
+
}): Screen;
|
|
45
|
+
/** An issue or pull request, read and quoted before anything is filed. Add task files it for the ordinary approval. */
|
|
46
|
+
export declare function importPage(chrome: Chrome, input: {
|
|
47
|
+
repo: string;
|
|
48
|
+
url: string;
|
|
49
|
+
title: string;
|
|
50
|
+
body: string;
|
|
51
|
+
kind: "issue" | "pull";
|
|
52
|
+
number: number;
|
|
53
|
+
head: string | null;
|
|
54
|
+
}): Screen;
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
import { html, joinHtml, postForm } from "../html.js";
|
|
8
8
|
import { projectName } from "../project.js";
|
|
9
9
|
import { screen } from "./chrome.js";
|
|
10
|
-
import { btn, composer, disclose, doc, docHeader, dock, field, ideas, input, note, phoneHeader, radios, select, sendButton, textarea, toggleChip } from "./kit.js";
|
|
10
|
+
import { btn, composer, disclose, doc, docHeader, dock, facts, field, ideas, input, note, phoneHeader, radios, select, sendButton, textarea, toggleChip } from "./kit.js";
|
|
11
|
+
import { fastTermsLines } from "../fast-terms.js";
|
|
11
12
|
import { TASK_TEXT_LIMITS } from "../task-text.js";
|
|
12
13
|
const FORM_ID = "nt-form";
|
|
13
14
|
const PLAN_ON = "The agent reads the code and asks you anything unclear, then writes the plan.";
|
|
@@ -34,9 +35,11 @@ prefill = null,
|
|
|
34
35
|
/** Words an idea's link put in the box (GET ?text=). */
|
|
35
36
|
draft = null,
|
|
36
37
|
/** Ideas for the chosen project: one tap drafts the words into the box. */
|
|
37
|
-
suggestions = []
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
suggestions = [],
|
|
39
|
+
/** A link the lead or a person put in the address (GET ?from=). */
|
|
40
|
+
fromLink = null) {
|
|
41
|
+
const head = html `${phoneHeader("New task", { back: "/tasks" })}`;
|
|
42
|
+
const header = docHeader({ title: "New task", back: ["/tasks", "Tasks"], meta: "Nothing is built until you approve its plan.", same: true });
|
|
40
43
|
const items = projectItems(projects, project);
|
|
41
44
|
const values = prefill?.values;
|
|
42
45
|
// No project to choose from: the task names its folder (the server proves it), and adding a project is one link away.
|
|
@@ -48,7 +51,7 @@ suggestions = []) {
|
|
|
48
51
|
const permission = values?.get("permission-mode") === "bypassPermissions" ? "bypassPermissions" : values?.get("permission-mode") === "auto" ? "auto" : permissionDefault;
|
|
49
52
|
const text = prefill?.title ?? draft ?? "";
|
|
50
53
|
// More options opens on a template, or when a refused filing had used any of them.
|
|
51
|
-
const optionsUsed = prefill !== null && (values === undefined || ["goal", "acceptance", "not", "touches", "id", "after"].some(name => (values.get(name) ?? "") !== "") || values.get("scout") === "1");
|
|
54
|
+
const optionsUsed = fromLink !== null || prefill !== null && (values === undefined || ["from", "goal", "acceptance", "not", "touches", "id", "after"].some(name => (values.get(name) ?? "") !== "") || values.get("scout") === "1");
|
|
52
55
|
const folder = !typed ? "" : field({ id: "nt-repo", label: "Project folder", hint: "No projects yet: name the folder of a git repository.",
|
|
53
56
|
control: input({ id: "nt-repo", name: "repo", value: chosen, hint: true, attrs: { required: true, placeholder: "/path/to/repository", autocapitalize: "none", spellcheck: "false" } }) });
|
|
54
57
|
const bar = joinHtml([
|
|
@@ -66,6 +69,7 @@ suggestions = []) {
|
|
|
66
69
|
textAttrs: { maxlength: TASK_TEXT_LIMITS.title, autofocus: true },
|
|
67
70
|
});
|
|
68
71
|
const options = html `<div class="k-form">\
|
|
72
|
+
${field({ id: "nt-from", label: "From an issue or PR (optional)", hint: "A github.com link in this project. Its text becomes the plan, and you approve it.", control: input({ id: "nt-from", name: "from", value: values?.get("from") ?? fromLink ?? "", attrs: { placeholder: "https://github.com/…", autocapitalize: "none", spellcheck: "false", inputmode: "url" } }) })}\
|
|
69
73
|
${field({ id: "nt-quality", label: "Checks", control: select({ id: "nt-quality", name: "quality-mode", items: [["default", "Default checks"], ["strict", "Strict (release)"]], current: quality, wide: true }) })}\
|
|
70
74
|
${candidates.length === 0 ? "" : field({ id: "nt-after", label: "Starts", control: select({ id: "nt-after", name: "after", wide: true, current: after,
|
|
71
75
|
items: [["", "Right away"], ...candidates.map(one => [one.id, `After ${one.id}: ${one.title}`])] }) })}\
|
|
@@ -98,3 +102,24 @@ ${field({ id: "nt-id", label: "Task id (optional)", control: input({ id: "nt-id"
|
|
|
98
102
|
const recipes = note(html `Work you do often? <a class="k-link" href="/recipes">Use a saved recipe</a>.`);
|
|
99
103
|
return screen("New task", html `${head}${doc(header, form, ideaList, recipes)}${dock(btn("File task", { variant: "primary", type: "submit", attrs: { form: FORM_ID } }))}`, { chrome });
|
|
100
104
|
}
|
|
105
|
+
/** The terms as label and words: the lines every approval surface reads (fast-terms.ts), with nothing hidden. */
|
|
106
|
+
export function fastTermsRows(store, scope, repo) {
|
|
107
|
+
return fastTermsLines(store, scope, repo).map(line => [line.label, line.words]);
|
|
108
|
+
}
|
|
109
|
+
/** Build (docs/plans/plain-toolroll.md P2.4): the exact terms this small task would be approved on, and the one button
|
|
110
|
+
* that approves them. The fields of "Plan it first" are the person's own words, on the ordinary road. */
|
|
111
|
+
export function fastStartPage(chrome, input) {
|
|
112
|
+
const head = html `${phoneHeader("Build this?", { back: "/tasks/new" })}`;
|
|
113
|
+
const header = docHeader({ title: "Build this?", back: ["/tasks/new", "New task"], meta: "Build approves exactly these terms.", same: true });
|
|
114
|
+
const build = postForm("/tasks/fast", btn("Build", { variant: "primary", type: "submit" }), { attrs: { class: "k-form", id: "fast-build" }, hidden: { preview: input.preview, digest: input.digest } });
|
|
115
|
+
const plan = postForm("/tasks/add", btn("Plan it first", { type: "submit" }), { attrs: { class: "k-form" }, hidden: { plain: "1", title: input.title, repo: input.repo, "planning-policy": "choice", "plan-first": "1" } });
|
|
116
|
+
return screen("Build this?", html `${head}${doc(header, facts(input.terms.map(([label, words]) => [label, joinHtml(words.split("\n").map(line => html `<div>${line}</div>`))])), html `<div class="k-acts">${build}${plan}</div>`)}`, { chrome });
|
|
117
|
+
}
|
|
118
|
+
/** An issue or pull request, read and quoted before anything is filed. Add task files it for the ordinary approval. */
|
|
119
|
+
export function importPage(chrome, input) {
|
|
120
|
+
const name = input.kind === "pull" ? `Pull request #${input.number}` : `Issue #${input.number}`;
|
|
121
|
+
const head = html `${phoneHeader(name, { back: "/tasks/new" })}`;
|
|
122
|
+
const header = docHeader({ title: name, back: ["/tasks/new", "New task"], meta: "Written outside Toolroll. You'll approve its plan before anything builds.", same: true });
|
|
123
|
+
const file = postForm("/tasks/add", btn("Add task", { variant: "primary", type: "submit" }), { attrs: { class: "k-form" }, hidden: { from: input.url, repo: input.repo, title: input.title, ...(input.head === null ? {} : { "from-head": input.head }), "from-read": "1" } });
|
|
124
|
+
return screen(name, html `${head}${doc(header, html `<blockquote class="k-quote"><p><strong>${input.title}</strong></p><p class="k-pre">${input.body === "" ? "No description." : input.body}</p></blockquote>`, file)}`, { chrome });
|
|
125
|
+
}
|
|
@@ -5,12 +5,10 @@ import { type AssignmentSnapshot } from "../assignment.js";
|
|
|
5
5
|
import { type BoardCard } from "../board.js";
|
|
6
6
|
import { type BrowserLimits } from "../browser-workspace.js";
|
|
7
7
|
import { type DispatchDiagnosis } from "../dispatch.js";
|
|
8
|
-
import { type FirstRunStep } from "../first-run.js";
|
|
9
8
|
import { type Gap } from "../gaps.js";
|
|
10
9
|
import { type parseBaseTreeSnapshot } from "../peek.js";
|
|
11
|
-
import { type CriterionMatrixRow } from "../proof.js";
|
|
12
10
|
import { type Runner } from "../runner.js";
|
|
13
|
-
import { type Decision, type Incident, type
|
|
11
|
+
import { type Decision, type Incident, type Run, type Store, type TaskState, type WorktreeRow } from "../store.js";
|
|
14
12
|
import { tally } from "../summary.js";
|
|
15
13
|
import { type WorkIndexGroup, type WorkIndexItem, type WorkIndexPage } from "../work-index.js";
|
|
16
14
|
import { type ChipTone } from "../design/kit.js";
|
|
@@ -23,90 +21,6 @@ export declare const TASK_STATES: readonly TaskState[];
|
|
|
23
21
|
* auto-refresh — approval links lead to the step-up screen, and a page
|
|
24
22
|
* that might hold typed input never re-renders itself.
|
|
25
23
|
*/
|
|
26
|
-
export type InboxTab = "needs-you" | "ready" | "running" | "all";
|
|
27
|
-
export declare const INBOX_TABS: readonly {
|
|
28
|
-
id: InboxTab;
|
|
29
|
-
label: string;
|
|
30
|
-
}[];
|
|
31
|
-
export declare function parseInboxTab(raw: string | null): InboxTab | undefined;
|
|
32
|
-
/** What each tab holds, as a short fingerprint: a tab is unread when its
|
|
33
|
-
* fingerprint differs from the one this browser saw there last. */
|
|
34
|
-
export declare function inboxFingerprints(data: Pick<Parameters<typeof inboxPage>[1], "decisions" | "approvals" | "requeueables" | "cancelledBlockers" | "gaps" | "needsVerification" | "ready" | "running">): Record<InboxTab, string>;
|
|
35
|
-
export declare function inboxPage(chrome: Chrome, data: {
|
|
36
|
-
csrf: string;
|
|
37
|
-
revision: number;
|
|
38
|
-
/** No project open in scoped mode: every admitted project at once,
|
|
39
|
-
* chips on every row, links only — acting means opening the project. */
|
|
40
|
-
rollup: boolean;
|
|
41
|
-
/** A project is OPEN: only then do decisions answer on the card. The
|
|
42
|
-
* legacy unscoped projectless inbox stays links-only too (commit-1
|
|
43
|
-
* review, finding 4) — the partial belongs to a chosen project. */
|
|
44
|
-
interactive: boolean;
|
|
45
|
-
decisions: (Decision & {
|
|
46
|
-
taskId: string;
|
|
47
|
-
repo?: string | null;
|
|
48
|
-
})[];
|
|
49
|
-
approvals: {
|
|
50
|
-
taskId: string;
|
|
51
|
-
title: string;
|
|
52
|
-
goal: string;
|
|
53
|
-
proposedAt: string;
|
|
54
|
-
repo?: string | null; /** v48 authority repair: the closed consent door's title, or null when a yes could bind. */
|
|
55
|
-
closed?: string | null;
|
|
56
|
-
}[];
|
|
57
|
-
requeueables: {
|
|
58
|
-
taskId: string;
|
|
59
|
-
title: string;
|
|
60
|
-
state: TaskState;
|
|
61
|
-
strikes: number;
|
|
62
|
-
incidentCount: number;
|
|
63
|
-
repo?: string | null;
|
|
64
|
-
}[];
|
|
65
|
-
cancelledBlockers: {
|
|
66
|
-
blockerId: string;
|
|
67
|
-
dependentCount: number;
|
|
68
|
-
exampleDependent: string;
|
|
69
|
-
repo?: string | null;
|
|
70
|
-
blockerRepo?: string | null;
|
|
71
|
-
}[];
|
|
72
|
-
gaps: Gap[];
|
|
73
|
-
/** A completed task whose proof is short or refuted and not yet accepted
|
|
74
|
-
* (Priority 2) — reads "needs verification" here too, never silently
|
|
75
|
-
* "done" just because the inbox does not otherwise look at finished work. */
|
|
76
|
-
needsVerification: {
|
|
77
|
-
taskId: string;
|
|
78
|
-
title: string;
|
|
79
|
-
verdict: "short" | "refuted";
|
|
80
|
-
repo?: string | null;
|
|
81
|
-
matrix?: CriterionMatrixRow[];
|
|
82
|
-
repairChain?: RepairChainRow | null;
|
|
83
|
-
}[];
|
|
84
|
-
/** The first-run steps; null once the installation has had its first Ready result. */
|
|
85
|
-
wizard: FirstRunStep[] | null;
|
|
86
|
-
/** Whether any worker is answering right now — said at the top when none is. */
|
|
87
|
-
worker: {
|
|
88
|
-
answering: number;
|
|
89
|
-
registered: number;
|
|
90
|
-
lastHeard: string | null;
|
|
91
|
-
};
|
|
92
|
-
now: Date;
|
|
93
|
-
/** Console v2: the tab shown, results ready to review, work running now,
|
|
94
|
-
* and the tabs holding something this browser hasn't seen (dots on a phone). */
|
|
95
|
-
tab?: InboxTab;
|
|
96
|
-
ready?: {
|
|
97
|
-
taskId: string;
|
|
98
|
-
title: string;
|
|
99
|
-
detail: string;
|
|
100
|
-
repo: string | null;
|
|
101
|
-
}[];
|
|
102
|
-
running?: {
|
|
103
|
-
taskId: string;
|
|
104
|
-
title: string;
|
|
105
|
-
detail: string;
|
|
106
|
-
repo: string | null;
|
|
107
|
-
}[];
|
|
108
|
-
unread?: readonly InboxTab[];
|
|
109
|
-
}): Screen;
|
|
110
24
|
/** Start a builder: the one command, as text to select (/start-builder, where every "Connect a builder" leads, and
|
|
111
25
|
* the Agents page). `project`: the open project's name, when the viewer may see it; `connected`: whether a builder
|
|
112
26
|
* that may build there is checking in (null: say nothing); `managed`: whether this person can start one themselves
|
|
@@ -248,94 +162,6 @@ export type WorkbenchDone = {
|
|
|
248
162
|
outcome: string | null;
|
|
249
163
|
repo: string | null;
|
|
250
164
|
};
|
|
251
|
-
/**
|
|
252
|
-
* The control-room rail (attended A1): every state that matters while a
|
|
253
|
-
* person supervises, ordered by intervention cost. The project chip is
|
|
254
|
-
* deliberately repeated on every row — a title is not a workspace, and
|
|
255
|
-
* switching context must never depend on remembering which repo is open.
|
|
256
|
-
*/
|
|
257
|
-
export declare function workbenchRail(data: {
|
|
258
|
-
attention: BoardCard[];
|
|
259
|
-
building: BoardCard[];
|
|
260
|
-
waiting: BoardCard[];
|
|
261
|
-
queued: BoardCard[];
|
|
262
|
-
done: WorkbenchDone[];
|
|
263
|
-
selected: string | null;
|
|
264
|
-
saturated: boolean;
|
|
265
|
-
}): Html;
|
|
266
|
-
/**
|
|
267
|
-
* The portfolio overview (arc slice 1a): what waits on you, what the last
|
|
268
|
-
* 24 hours of run starts amounted to, what is running, and the terminal-run
|
|
269
|
-
* ledger — all of it across every admitted project, a project chip on every
|
|
270
|
-
* row. The caller has already applied admission and per-row visibility.
|
|
271
|
-
*/
|
|
272
|
-
export declare function portfolioOverview(data: {
|
|
273
|
-
attention: BoardCard[];
|
|
274
|
-
building: BoardCard[];
|
|
275
|
-
waiting: BoardCard[];
|
|
276
|
-
queued: BoardCard[];
|
|
277
|
-
done: WorkbenchDone[];
|
|
278
|
-
saturated: boolean;
|
|
279
|
-
decisions: (Decision & {
|
|
280
|
-
taskId: string;
|
|
281
|
-
repo?: string | null;
|
|
282
|
-
})[];
|
|
283
|
-
approvals: {
|
|
284
|
-
taskId: string;
|
|
285
|
-
title: string;
|
|
286
|
-
goal: string;
|
|
287
|
-
proposedAt: string;
|
|
288
|
-
repo?: string | null; /** v48 authority repair: the closed consent door's title, or null when a yes could bind. */
|
|
289
|
-
closed?: string | null;
|
|
290
|
-
}[];
|
|
291
|
-
requeueables: {
|
|
292
|
-
taskId: string;
|
|
293
|
-
title: string;
|
|
294
|
-
state: TaskState;
|
|
295
|
-
strikes: number;
|
|
296
|
-
incidentCount: number;
|
|
297
|
-
repo?: string | null;
|
|
298
|
-
}[];
|
|
299
|
-
cancelledBlockers: {
|
|
300
|
-
blockerId: string;
|
|
301
|
-
dependentCount: number;
|
|
302
|
-
exampleDependent: string;
|
|
303
|
-
repo?: string | null;
|
|
304
|
-
blockerRepo?: string | null;
|
|
305
|
-
}[];
|
|
306
|
-
gaps: Gap[];
|
|
307
|
-
gapsProject: string | null;
|
|
308
|
-
runs24: (Run & {
|
|
309
|
-
taskId: string;
|
|
310
|
-
})[];
|
|
311
|
-
live: {
|
|
312
|
-
taskId: string;
|
|
313
|
-
runner: string;
|
|
314
|
-
claimedAt: string;
|
|
315
|
-
expiresAt: string;
|
|
316
|
-
model: string | null;
|
|
317
|
-
repo: string | null;
|
|
318
|
-
}[];
|
|
319
|
-
ledger: {
|
|
320
|
-
runId: number;
|
|
321
|
-
taskId: string;
|
|
322
|
-
title: string;
|
|
323
|
-
repo: string | null;
|
|
324
|
-
outcome: string;
|
|
325
|
-
role: string;
|
|
326
|
-
provider: string | null;
|
|
327
|
-
model: string | null;
|
|
328
|
-
startedAt: string;
|
|
329
|
-
ranMinutes: number | null;
|
|
330
|
-
costUsd: number | null;
|
|
331
|
-
authMode: "subscription" | "api-key" | "agent-login" | null;
|
|
332
|
-
prNumber: number | null;
|
|
333
|
-
prUrl: string | null;
|
|
334
|
-
}[];
|
|
335
|
-
/** "" when there is no session token: then no form renders. postForm adds the field itself. */
|
|
336
|
-
csrf: string;
|
|
337
|
-
now: Date;
|
|
338
|
-
}): Html;
|
|
339
165
|
export type OnboardCardState = {
|
|
340
166
|
enabled: false;
|
|
341
167
|
why: string;
|