toolroll 0.9.92 → 0.9.94
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 +87 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +4 -0
- package/dist/agent-fence.js +4 -1
- 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 -3
- package/dist/assignment.js +16 -3
- 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 +2 -2
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +2 -2
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +48 -7
- package/dist/browser-workspace.js +3 -3
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +4 -0
- package/dist/builder.js +73 -25
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +12 -2
- package/dist/chat-actions.js +25 -2
- package/dist/chat-channel.js +11 -0
- package/dist/chat-decide.d.ts +7 -2
- package/dist/chat-decide.js +37 -9
- package/dist/chat-quiet.js +3 -3
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +126 -0
- package/dist/check-facts.js +318 -0
- package/dist/check-failure.d.ts +22 -3
- package/dist/check-failure.js +57 -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-report.d.ts +7 -0
- package/dist/check-report.js +4 -3
- 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/cli-help.d.ts +4 -1
- package/dist/cli-help.js +47 -30
- package/dist/cli.js +16 -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 +2 -0
- package/dist/coding-provider.js +3 -2
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.js +4 -4
- 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 +9 -0
- package/dist/contracts/chat-actions.js +2 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -5
- package/dist/contracts/console-api.js +12 -5
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +61 -0
- package/dist/contracts/lead-tools.js +10 -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/release-check.d.ts +25 -1
- package/dist/contracts/release-check.js +19 -0
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +2 -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/desktop-update.d.ts +2 -2
- package/dist/desktop-update.js +13 -9
- package/dist/dispatch.js +18 -15
- package/dist/dispose.js +8 -2
- package/dist/easy-start.js +2 -3
- package/dist/eval-hooks.d.ts +55 -0
- package/dist/eval-hooks.js +227 -4
- package/dist/evals.js +2 -1
- 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/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 +83 -0
- package/dist/flaky.js +264 -0
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/invoke.d.ts +5 -1
- package/dist/invoke.js +49 -14
- 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-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +2 -2
- package/dist/lead-progress.js +3 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +51 -2
- package/dist/lead.js +1 -1
- 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 +11 -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/needs-you.d.ts +32 -13
- package/dist/needs-you.js +108 -49
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +6 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +540 -70
- 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/planner.js +2 -1
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +7 -2
- package/dist/project-delete-ui.js +12 -1
- package/dist/project-delete.js +13 -2
- 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/proof.js +30 -5
- package/dist/provider-auth.js +1 -1
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +26 -3
- package/dist/push.js +55 -15
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +87 -1
- package/dist/release-approval.d.ts +191 -0
- package/dist/release-approval.js +382 -0
- package/dist/release-auto.js +26 -17
- package/dist/release-card.d.ts +74 -6
- package/dist/release-card.js +179 -23
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +39 -9
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +38 -23
- 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 +50 -1
- package/dist/release.js +118 -11
- 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 +29 -21
- 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 +6 -1
- package/dist/scope.js +22 -9
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +24 -18
- package/dist/server/chat.js +14 -12
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +23 -0
- package/dist/server/guards.js +4 -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 +2 -2
- 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-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 +19 -0
- package/dist/server/render-release.js +84 -0
- package/dist/server/render-results.d.ts +2 -2
- package/dist/server/render-results.js +71 -73
- package/dist/server/render-tasks.d.ts +13 -2
- package/dist/server/render-tasks.js +156 -139
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +4 -1
- package/dist/server/route-table.js +52 -13
- package/dist/server/settings.js +129 -22
- package/dist/server/task-views.js +61 -26
- package/dist/server/tasks.d.ts +17 -6
- package/dist/server/tasks.js +273 -338
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.js +1 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/stop-proof.d.ts +67 -0
- package/dist/stop-proof.js +295 -0
- 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 +148 -34
- package/dist/store.js +626 -177
- package/dist/surface.js +46 -5
- 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.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +36 -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 +40 -49
- package/dist/teardown-git.d.ts +16 -0
- package/dist/teardown-git.js +63 -5
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- 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-ui.js +2 -2
- package/dist/toolroll-update.js +18 -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 +82 -70
- package/dist/work-summary.d.ts +2 -2
- package/dist/work-summary.js +2 -0
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +28 -22
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +68 -13
- package/package.json +1 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
package/dist/server/chrome.js
CHANGED
|
@@ -14,6 +14,7 @@ import { DISCLOSURE_CSS } from "../disclosure.js";
|
|
|
14
14
|
import { EVIDENCE_PACK_CSS } from "../evidence-pack.js";
|
|
15
15
|
import { EXPORT_CSS } from "../export-ui.js";
|
|
16
16
|
import { START_COMMAND } from "../first-run.js";
|
|
17
|
+
import { SHORTCUTS, webKeymapScript } from "../keymap.js";
|
|
17
18
|
import { GALLERY_CSS } from "../flow-gallery-ui.js";
|
|
18
19
|
import { STARTERS_CSS } from "../flow-starters-ui.js";
|
|
19
20
|
import { FLOWS_CSS } from "../flows-ui.js";
|
|
@@ -347,8 +348,9 @@ export function chromeScript() {
|
|
|
347
348
|
`navGroups.forEach(function(g){g.addEventListener("toggle",function(){` +
|
|
348
349
|
`if(g.open){navGroups.forEach(function(o){if(o!==g)o.removeAttribute("open");});}});});` +
|
|
349
350
|
// the palette
|
|
350
|
-
|
|
351
|
-
`var
|
|
351
|
+
// No index on a workspace page: search is the workspace's own there (keymap.ts search), and the keys still work.
|
|
352
|
+
`var raw=document.getElementById("palette-index");var index=[];` +
|
|
353
|
+
`if(raw){try{index=JSON.parse(raw.textContent||"[]");}catch(e){return;}}` +
|
|
352
354
|
`var open=false,box=null,list=null,input=null,items=[],paletteBack=null;` +
|
|
353
355
|
`function close(){if(!open)return;open=false;box.remove();box=null;if(paletteBack&&paletteBack.isConnected)paletteBack.focus();paletteBack=null;}` +
|
|
354
356
|
`function go(href){location.href=href;}` +
|
|
@@ -395,23 +397,22 @@ export function chromeScript() {
|
|
|
395
397
|
`var next=at===-1?(delta>0?0:set.length-1):Math.max(0,Math.min(set.length-1,at+delta));` +
|
|
396
398
|
`if(next===at)return;` +
|
|
397
399
|
`set[next].focus();ev.preventDefault();}` +
|
|
398
|
-
// key routing: never inside editable targets, no modifiers, no IME
|
|
399
|
-
// composition (finding 4); repeats allowed only for j/k
|
|
400
|
+
// key routing (keymap.ts, one table with the terminal UI): never inside editable targets, no modifiers, no IME
|
|
401
|
+
// composition (finding 4); repeats allowed only for j/k. A press key presses only a control the page rendered.
|
|
402
|
+
webKeymapScript() +
|
|
400
403
|
`var pending=null;` +
|
|
401
404
|
`document.addEventListener("keydown",function(ev){` +
|
|
402
405
|
`if(ev.isComposing||ev.metaKey||ev.ctrlKey||ev.altKey)return;` +
|
|
403
406
|
`var t=ev.target;var tag=t&&t.tagName?t.tagName.toLowerCase():"";` +
|
|
404
|
-
`if(tag==="input"||tag==="textarea"||tag==="select"||
|
|
407
|
+
`if(tag==="input"||tag==="textarea"||tag==="select"||(t&&t.isContentEditable)||(t&&t.closest&&t.closest("[role=textbox],[role=dialog]:not(.kbd-help)")))return;` +
|
|
405
408
|
`if(ev.key==="Escape"){if(helpOpen()){toggleHelp();ev.preventDefault();return;}close();return;}` +
|
|
406
409
|
`if(helpOpen())return;` +
|
|
407
410
|
`if(ev.repeat&&ev.key!=="j"&&ev.key!=="k")return;` +
|
|
408
|
-
`if(ev.key==="/"){show();ev.preventDefault();return;}` +
|
|
409
|
-
`if(ev.key==="?"){toggleHelp();ev.preventDefault();return;}` +
|
|
410
411
|
`if(ev.key==="j"||ev.key==="k"){rove(ev.key==="j"?1:-1,ev);return;}` +
|
|
411
|
-
`if(pending==="g"){pending=null
|
|
412
|
-
`
|
|
413
|
-
`
|
|
414
|
-
`if(ev.
|
|
412
|
+
`if(pending==="g"){pending=null;var after=GKEYS[ev.key];if(after&&run(after))ev.preventDefault();return;}` +
|
|
413
|
+
`if(ev.key==="g"){pending="g";setTimeout(function(){pending=null;},800);return;}` +
|
|
414
|
+
`var act=Object.prototype.hasOwnProperty.call(KEYS,ev.key)?KEYS[ev.key]:null;` +
|
|
415
|
+
`if(act&&run(act))ev.preventDefault();});` +
|
|
415
416
|
`})();`);
|
|
416
417
|
}
|
|
417
418
|
export const ROLE_TITLES = { plan: "Planner", build: "Builder", review: "Reviewer", repair: "Repair" };
|
|
@@ -440,17 +441,7 @@ FINISH: unreviewed and undocumented is unfinished; this build ends with the fini
|
|
|
440
441
|
* absent from sensitive pages. Navigation help in plain words — no key
|
|
441
442
|
* ever posts. */
|
|
442
443
|
export const KBD_HELP = html `<div class="kbd-help" hidden role="dialog" aria-label="keyboard shortcuts" tabindex="-1">\
|
|
443
|
-
<h2>Keyboard shortcuts</h2><table
|
|
444
|
-
<tr><td><kbd>/</kbd></td><td>jump to a page or an open task</td></tr>\
|
|
445
|
-
<tr><td><kbd>g</kbd> then <kbd>i</kbd></td><td>go to the inbox</td></tr>\
|
|
446
|
-
<tr><td><kbd>g</kbd> then <kbd>b</kbd></td><td>go to the board</td></tr>\
|
|
447
|
-
<tr><td><kbd>g</kbd> then <kbd>q</kbd></td><td>go to the queue</td></tr>\
|
|
448
|
-
<tr><td><kbd>g</kbd> then <kbd>f</kbd></td><td>go to the fleet</td></tr>\
|
|
449
|
-
<tr><td><kbd>g</kbd> then <kbd>w</kbd></td><td>go to the workbench</td></tr>\
|
|
450
|
-
<tr><td><kbd>g</kbd> then <kbd>t</kbd></td><td>go to the task list</td></tr>\
|
|
451
|
-
<tr><td><kbd>g</kbd> then <kbd>a</kbd></td><td>go to the activity view</td></tr>\
|
|
452
|
-
<tr><td><kbd>g</kbd> then <kbd>p</kbd></td><td>go to the projects</td></tr>\
|
|
453
|
-
<tr><td><kbd>g</kbd> then <kbd>d</kbd></td><td>go to what is done</td></tr>\
|
|
444
|
+
<h2>Keyboard shortcuts</h2><table>${SHORTCUTS.map(one => html `<tr><td>${joinHtml(one.keys.split(" ").map(key => html `<kbd>${key}</kbd>`), " then ")}</td><td>${one.says}</td></tr>`)}\
|
|
454
445
|
<tr><td><kbd>j</kbd> / <kbd>k</kbd></td><td>move through the rows on this page</td></tr>\
|
|
455
446
|
<tr><td><kbd>Escape</kbd></td><td>close this</td></tr>\
|
|
456
447
|
</table></div>`;
|
|
@@ -513,8 +504,8 @@ ${count !== undefined && count > 0 ? html ` <span class="count badge badge-open"
|
|
|
513
504
|
const scopeStatus = peekCounts === null
|
|
514
505
|
? null
|
|
515
506
|
: html `<span class="scope-status">\
|
|
516
|
-
${peekCounts.waiting > 0 ? html `<a class="hot" href="/
|
|
517
|
-
<a href="/runs">${peekCounts.running} live</a><a href="/
|
|
507
|
+
${peekCounts.waiting > 0 ? html `<a class="hot" href="/tasks?view=needs-you">${peekCounts.waiting} needs you</a>` : html `<a href="/tasks?view=needs-you">0 needs you</a>`}\
|
|
508
|
+
<a href="/runs">${peekCounts.running} live</a><a href="/tasks?view=order">${peekCounts.queued} queued</a></span>`;
|
|
518
509
|
const scopeName = effectiveScope === "project" && chrome.project !== null ? projectName(chrome.project) : "All projects";
|
|
519
510
|
// The switcher (board pass): the scope's name opens a menu of every
|
|
520
511
|
// enrolled project — one tap to open one, or to widen to all — as plain
|
|
@@ -556,7 +547,7 @@ ${scopeStatus}</div>`;
|
|
|
556
547
|
${count !== undefined && count > 0 ? html ` <span${chrome.inboxLabel === undefined ? "" : html ` aria-label="${chrome.inboxLabel}" title="${chrome.inboxLabel}"`} class="count badge badge-open">${count}${chrome.inboxSaturated ? "+" : ""}</span>` : ""}</a>`;
|
|
557
548
|
const side = joinHtml([
|
|
558
549
|
html `<aside class="side">`,
|
|
559
|
-
html `<div class="side-head"><a class="brand" href="${chrome.chat === true ? "/chat" : "/
|
|
550
|
+
html `<div class="side-head"><a class="brand" href="${chrome.chat === true ? "/chat" : "/tasks"}"><span class="brand-long">Toolroll</span><span class="brand-short">T</span></a>`,
|
|
560
551
|
...(options.sidebarToggle === true
|
|
561
552
|
? [html `<button type="button" class="side-toggle" aria-label="collapse sidebar" aria-expanded="true" title="collapse sidebar">${strokeIcon(html `<path d="m15 18-6-6 6-6"/>`)}</button>`]
|
|
562
553
|
: []),
|
|
@@ -566,7 +557,7 @@ ${count !== undefined && count > 0 ? html ` <span${chrome.inboxLabel === undefin
|
|
|
566
557
|
// gating); Work and Projects always. Every specialist tool is a dim
|
|
567
558
|
// text row inside one of the two accordion groups below.
|
|
568
559
|
...(chrome.chat ? [primaryItem("chat", "/chat", "Chat")] : []),
|
|
569
|
-
primaryItem("work", "/
|
|
560
|
+
primaryItem("work", "/tasks", "Tasks", chrome.inboxCount),
|
|
570
561
|
primaryItem("flows", "/flows", "Flows"),
|
|
571
562
|
primaryItem("projects", "/projects", "Projects"),
|
|
572
563
|
html `</nav>`,
|
|
@@ -607,7 +598,7 @@ ${postForm(update.dismissHref, html `<button type="submit">Dismiss</button>`, {
|
|
|
607
598
|
// these only below 760px; desktop keeps the sidebar untouched.
|
|
608
599
|
const mobileTop = joinHtml([
|
|
609
600
|
html `<header class="mobile-top">`,
|
|
610
|
-
html `<a class="brand-mini" href="${chrome.chat === true ? "/chat" : "/
|
|
601
|
+
html `<a class="brand-mini" href="${chrome.chat === true ? "/chat" : "/tasks"}">T</a>`,
|
|
611
602
|
// On a phone the pill IS the scope row (mobile pass): the project's
|
|
612
603
|
// name, its three counts, and the one /projects link at that
|
|
613
604
|
// breakpoint — the scope bar hides below 760px so the header is one
|
|
@@ -640,7 +631,7 @@ ${count !== undefined && count > 0 ? html `<span class="dot-badge" role="img" ar
|
|
|
640
631
|
const tabbar = joinHtml([
|
|
641
632
|
html `<nav class="tabbar">`,
|
|
642
633
|
...(chrome.chat ? [tab("chat", "/chat", "Chat")] : []),
|
|
643
|
-
tab("work", "/
|
|
634
|
+
tab("work", "/tasks", "Tasks", chrome.inboxCount),
|
|
644
635
|
tab("flows", "/flows", "Flows"),
|
|
645
636
|
tab("projects", "/projects", "Projects"),
|
|
646
637
|
html `</nav>`,
|
|
@@ -702,7 +693,7 @@ export const TO_FRONT_ICON = html `<svg viewBox="0 0 24 24" fill="none" stroke="
|
|
|
702
693
|
* wear an icon each; the foot's list stays text, the way Linear's does. */
|
|
703
694
|
export const strokeIcon = (paths) => html `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${paths}</svg>`;
|
|
704
695
|
/** Where the queue lives now: the board, flipped to dispatch order. */
|
|
705
|
-
export const QUEUE_VIEW = "/
|
|
696
|
+
export const QUEUE_VIEW = "/tasks?view=order";
|
|
706
697
|
export const FOLDER_PATHS = html `<path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/>`;
|
|
707
698
|
export const CHAT_PATHS = html `<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>`;
|
|
708
699
|
export const WORK_PATHS = html `<path d="M9 6h11"/><path d="M9 12h11"/><path d="M9 18h11"/><path d="m4 6 1 1 2-2"/><path d="m4 12 1 1 2-2"/><path d="m4 18 1 1 2-2"/>`;
|
|
@@ -730,11 +721,11 @@ export function workToolRows(scoped = false, _offersChat = false, offersCode = f
|
|
|
730
721
|
// D5: coding sessions are deprecated; /code still opens this release, but nothing links to it.
|
|
731
722
|
// Live terminals: an operator's own Claude Code, Codex or shell, in a real terminal (docs/plans/live-terminal.md).
|
|
732
723
|
...(offersCode ? [{ key: "code", href: "/term", label: "Live", hint: "your own terminals: Claude Code, Codex or a shell" }] : []),
|
|
733
|
-
{ key: "inbox", href: "/
|
|
734
|
-
{ key: "board", href: "/board", label: "Board", hint: "lanes by state, with the order view" },
|
|
735
|
-
{ key: "tasks", href: "/
|
|
724
|
+
{ key: "inbox", href: "/", label: "Home", hint: "what needs you, what runs, and what waits" },
|
|
725
|
+
{ key: "board", href: "/tasks?view=board", label: "Board", hint: "lanes by state, with the order view" },
|
|
726
|
+
{ key: "tasks", href: "/tasks", label: "Task list", hint: "everything, filterable by state" },
|
|
736
727
|
{ key: "recipes", href: "/recipes", label: "Recipes", hint: "choose, customize, and reuse a workflow" },
|
|
737
|
-
{ key: "workbench", href: "/
|
|
728
|
+
{ key: "workbench", href: "/tasks?scope=all", label: "Every project", hint: "every project's tasks in one list" },
|
|
738
729
|
{ key: "ledger", href: "/ledger", label: "Action ledger", hint: "who acted, what happened, and the result" },
|
|
739
730
|
{ key: "spend", href: "/spend", label: "Spend", hint: "what agent work cost, and monthly budgets" },
|
|
740
731
|
];
|
|
@@ -759,8 +750,8 @@ export const SETTINGS_KEYS = new Set(["settings", "fleet", "caps", "people", "mo
|
|
|
759
750
|
export function buildsViews(current) {
|
|
760
751
|
const views = [
|
|
761
752
|
["builds", "/runs", "builds"],
|
|
762
|
-
["done", "/done", "done"],
|
|
763
|
-
["review", "/
|
|
753
|
+
["done", "/tasks?view=done", "done"],
|
|
754
|
+
["review", "/tasks?view=ready", "review"],
|
|
764
755
|
["activity", "/activity", "activity"],
|
|
765
756
|
];
|
|
766
757
|
return html `<p class="meta board-view">${joinHtml(views.map(([key, href, label]) => (key === current ? html `<strong>${label}</strong>` : html `<a href="${href}">${label}</a>`)), " \u00b7 ")}</p>`;
|
package/dist/server/console.css
CHANGED
|
@@ -1229,6 +1229,9 @@
|
|
|
1229
1229
|
.approval-field-hint { color: var(--muted-foreground); font-size: 12px; line-height: 1.5; }
|
|
1230
1230
|
.approval-field textarea, .approval-field input[type=text] { width: 100%; max-width: 75ch; margin: 0; font-family: var(--font-sans); font-size: 14px; line-height: 1.55; }
|
|
1231
1231
|
.approval-requirements { display: grid; gap: 6px; margin: 2px 0 0; padding: 0; list-style: none; }
|
|
1232
|
+
.approval-requirements li { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
|
1233
|
+
.approval-requirements li input[type=text] { flex: 1 1 22rem; }
|
|
1234
|
+
.approval-how { flex: 0 1 auto; max-width: 100%; min-height: 2.125rem; margin: 0; font-size: 13px; }
|
|
1232
1235
|
.approval-edit-act { display: flex; flex-wrap: wrap; align-items: center; gap: var(--so-space-sm); }
|
|
1233
1236
|
.approval-edit-act button { margin: 0; min-height: 2.125rem; white-space: nowrap; }
|
|
1234
1237
|
.approval-link { display: inline-flex; align-items: center; min-height: 32px; padding: 0 var(--so-space-md); border: 1px solid var(--border); border-radius: var(--so-radius-md); background: var(--card); color: var(--foreground); font-size: 13px; font-weight: 500; text-decoration: none; }
|
|
@@ -2793,3 +2796,23 @@ html.so-swap::view-transition-old(*):only-child { animation: so-card-out 160ms e
|
|
|
2793
2796
|
@media (max-width: 40rem) { .welcome form > .k-btn[type=submit] { width: auto; } }
|
|
2794
2797
|
/* On a phone the step's primary lives in the dock: its copy in the step is hidden. */
|
|
2795
2798
|
@media (max-width: 760px) { .welcome .k-btn.k-docked { display: none; } }
|
|
2799
|
+
|
|
2800
|
+
/* Code map: every name is repository data, however long; nothing may push the page wider. */
|
|
2801
|
+
.cm-list { list-style: none; margin: 0; padding: 0; }
|
|
2802
|
+
.cm-item { display: grid; gap: 2px; min-width: 0; padding: 10px 0; border-bottom: 1px solid var(--so-line); overflow-wrap: anywhere; }
|
|
2803
|
+
.cm-title { font-size: 13px; font-weight: 500; color: var(--so-ink); }
|
|
2804
|
+
.cm-meta { font-size: 12.5px; color: var(--so-muted); }
|
|
2805
|
+
.cm-path { overflow-wrap: anywhere; user-select: all; }
|
|
2806
|
+
.cm-search { display: flex; gap: var(--so-space-sm); align-items: center; margin: 0 0 var(--so-space-md); }
|
|
2807
|
+
.cm-search .k-input, .cm-search input[type="search"] { flex: 1 1 auto; min-width: 0; }
|
|
2808
|
+
.cm-search .k-btn { flex: 0 0 auto; white-space: nowrap; }
|
|
2809
|
+
/* A test's name is the row: it wraps whole, its facts sit under it, and the one action stays on one line. */
|
|
2810
|
+
.flaky-rows .k-row-a .k-btn { white-space: nowrap; }
|
|
2811
|
+
@media (min-width: 761px) {
|
|
2812
|
+
.flaky-rows .k-row { grid-template-columns: 96px minmax(0, 1fr) auto auto; padding-block: 10px; }
|
|
2813
|
+
.flaky-rows .k-row-t { grid-column: 2; grid-row: 1; white-space: normal; overflow-wrap: anywhere; }
|
|
2814
|
+
.flaky-rows .k-row-d { grid-column: 2; grid-row: 2; white-space: normal; }
|
|
2815
|
+
.flaky-rows .k-row-m { grid-column: 3; grid-row: 1 / span 2; }
|
|
2816
|
+
.flaky-rows .k-row-a { grid-column: 4; grid-row: 1 / span 2; }
|
|
2817
|
+
.flaky-rows .k-row-c { grid-row: 1 / span 2; }
|
|
2818
|
+
}
|
package/dist/server/guards.js
CHANGED
|
@@ -176,6 +176,7 @@ export function createSharedGuards(runtime) {
|
|
|
176
176
|
const task = matchTaskPath(url.pathname, '(?:/[a-z-]+)?$');
|
|
177
177
|
return task === null ? null : runtime.store.lookupRef(task.taskId)?.repo ?? null;
|
|
178
178
|
}
|
|
179
|
+
case 'task-ref': return Number.isSafeInteger(id) && id > 0 ? runtime.store.refForId(id)?.repo ?? null : null;
|
|
179
180
|
case 'run': return runRepo(id);
|
|
180
181
|
case 'decision': return runRepo(runtime.store.getDecision(id)?.run);
|
|
181
182
|
case 'incident': return runRepo(runtime.store.openIncidents().find(one => one.id === id)?.run);
|
|
@@ -271,6 +272,9 @@ export function createSharedGuards(runtime) {
|
|
|
271
272
|
if (url.searchParams.get("view") === "order")
|
|
272
273
|
url.searchParams.delete("view");
|
|
273
274
|
}
|
|
275
|
+
// Tasks (P1.4) keeps the board's rule: the queue's order view stays an instance view, never a limited person's.
|
|
276
|
+
if (route.id === "tasks" && url.searchParams.get("view") === "order")
|
|
277
|
+
url.searchParams.set("view", "board");
|
|
274
278
|
return true;
|
|
275
279
|
}
|
|
276
280
|
function workAccess() {
|
package/dist/server/home.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export type HomeFacts = {
|
|
|
25
25
|
chat: boolean;
|
|
26
26
|
/** This person can answer an agent's question (an approver in a browser session). */
|
|
27
27
|
canAnswer: boolean;
|
|
28
|
+
/** Why this task's plan can't take a yes right now (render-tasks.ts consentDoorOf), or null when it can. */
|
|
29
|
+
closedDoor: (taskId: string) => {
|
|
30
|
+
title: string;
|
|
31
|
+
why: string;
|
|
32
|
+
road: "scope" | "agents";
|
|
33
|
+
} | null;
|
|
28
34
|
};
|
|
29
35
|
/** "7 need you across 2 projects." — the sentence and the figures read the same totals. */
|
|
30
36
|
export declare function homeSentence(needs: number, perProject: readonly {
|
package/dist/server/home.js
CHANGED
|
@@ -22,7 +22,7 @@ export function homeSentence(needs, perProject, scope, any) {
|
|
|
22
22
|
const waiting = perProject.filter(one => one.needs > 0);
|
|
23
23
|
return waiting.length === 1 ? `${needs} ${verb} you in ${waiting[0].name}.` : `${needs} ${verb} you across ${waiting.length} projects.`;
|
|
24
24
|
}
|
|
25
|
-
function rowOf(store, item, now, multi, canAnswer) {
|
|
25
|
+
function rowOf(store, item, now, multi, canAnswer, closedDoor) {
|
|
26
26
|
const action = item.primaryAction;
|
|
27
27
|
const href = action?.code === "open-result" && action.target.runId != null
|
|
28
28
|
? `/review?result=${encodeURIComponent(action.target.taskId)}&run=${action.target.runId}` : browserWorkActionHref(item);
|
|
@@ -41,11 +41,14 @@ function rowOf(store, item, now, multi, canAnswer) {
|
|
|
41
41
|
const line = waits
|
|
42
42
|
? (open ? decision.question : item.status.detail !== item.status.label ? item.status.detail : null)
|
|
43
43
|
: item.progress ?? (item.status.detail !== item.status.label ? item.status.detail : null);
|
|
44
|
+
// A plan that can't take a yes never offers one: the reason, and the one act that opens it, as the task page says.
|
|
45
|
+
const door = action?.code === "approve-scope" ? closedDoor(action.target.taskId) : null;
|
|
44
46
|
return {
|
|
45
47
|
id: item.rootId, title: item.title, href: taskHref(item.rootId),
|
|
46
48
|
project: multi || item.repo === null ? (item.repo === null ? "Unplaced" : projectName(item.repo)) : null,
|
|
47
|
-
age: relativeAge(item.updatedAt, now), chip: { word, tone }, line,
|
|
48
|
-
action:
|
|
49
|
+
age: relativeAge(item.updatedAt, now), chip: { word, tone }, line: door === null ? line : door.title,
|
|
50
|
+
action: door !== null ? { label: door.road === "agents" ? "Change the agents" : "Edit the plan", href: `${taskHref(action.target.taskId)}#${door.road === "agents" ? "agents" : "scope"}` }
|
|
51
|
+
: href === null || action === null ? null : { label: action.label, href }, question,
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
/** Home's view for one person and scope, from two reads of the work index (what waits, what runs). */
|
|
@@ -54,26 +57,28 @@ export function homeView(facts) {
|
|
|
54
57
|
const read = (view, limit) => workIndexPage(store, now, access, { view, limit, project, ...(facts.evidenceRoot === undefined ? {} : { root: facts.evidenceRoot }) });
|
|
55
58
|
const needs = read("needs-you", HOME_NEEDS_SHOWN);
|
|
56
59
|
const running = read("running", HOME_RUNNING_SHOWN);
|
|
60
|
+
// Waiting (docs/plans/plain-toolroll.md P1.4): what Toolroll or another task moves on, with no person asked.
|
|
61
|
+
const waiting = workIndexPage(store, now, access, { view: "queued", limit: HOME_RUNNING_SHOWN, project, ...(facts.evidenceRoot === undefined ? {} : { root: facts.evidenceRoot }) });
|
|
57
62
|
const totals = needs.totals;
|
|
58
63
|
// Queued: the Tasks list's own Queued count (the same predicate its view=queued filter lists).
|
|
59
64
|
const queued = totals.queued;
|
|
60
|
-
// Every project is said by name (all
|
|
61
|
-
const scopeQuery = project === null ? "all
|
|
65
|
+
// Every project is said by name (scope=all): /tasks alone would narrow to the session's open project.
|
|
66
|
+
const scopeQuery = project === null ? "scope=all&" : `project=${encodeURIComponent(project)}&`;
|
|
62
67
|
const counts = [
|
|
63
|
-
{ key: "needs", label: "Needs you", value: totals["needs-you"], href: `/
|
|
64
|
-
{ key: "running", label: "Running", value: totals.running, href: `/
|
|
65
|
-
{ key: "queued", label: "Queued", value: queued, href: `/
|
|
66
|
-
{ key: "finished", label: "Finished", value: totals.completed, href: `/
|
|
68
|
+
{ key: "needs", label: "Needs you", value: totals["needs-you"], href: `/tasks?${scopeQuery}view=needs-you` },
|
|
69
|
+
{ key: "running", label: "Running", value: totals.running, href: `/tasks?${scopeQuery}view=running` },
|
|
70
|
+
{ key: "queued", label: "Queued", value: queued, href: `/tasks?${scopeQuery}view=queued` },
|
|
71
|
+
{ key: "finished", label: "Finished", value: totals.completed, href: `/tasks?${scopeQuery}view=completed` },
|
|
67
72
|
];
|
|
68
73
|
const multi = project === null && new Set([...needs.items, ...running.items].map(one => one.repo ?? "")).size > 1;
|
|
69
74
|
const groups = [];
|
|
70
75
|
for (const ask of ASKS) {
|
|
71
|
-
const rows = needs.items.filter(one => one.ask === ask).map(one => rowOf(store, one, now, multi, facts.canAnswer));
|
|
76
|
+
const rows = needs.items.filter(one => one.ask === ask).map(one => rowOf(store, one, now, multi, facts.canAnswer, facts.closedDoor));
|
|
72
77
|
if (rows.length > 0)
|
|
73
78
|
groups.push({ key: ask, label: ASK_LABEL[ask], rows });
|
|
74
79
|
}
|
|
75
80
|
// A waiting row whose ask the index couldn't name still waits on this person: it goes under Decide, never lost.
|
|
76
|
-
const unnamed = needs.items.filter(one => one.ask === null).map(one => rowOf(store, one, now, multi, facts.canAnswer));
|
|
81
|
+
const unnamed = needs.items.filter(one => one.ask === null).map(one => rowOf(store, one, now, multi, facts.canAnswer, facts.closedDoor));
|
|
77
82
|
if (unnamed.length > 0) {
|
|
78
83
|
const decide = groups.find(one => one.key === "decide");
|
|
79
84
|
if (decide)
|
|
@@ -81,12 +86,15 @@ export function homeView(facts) {
|
|
|
81
86
|
else
|
|
82
87
|
groups.unshift({ key: "decide", label: ASK_LABEL.decide, rows: unnamed });
|
|
83
88
|
}
|
|
84
|
-
const live = running.items.filter(one => !one.status.views.includes("needs-you")).map(one => rowOf(store, one, now, multi, facts.canAnswer));
|
|
89
|
+
const live = running.items.filter(one => !one.status.views.includes("needs-you")).map(one => rowOf(store, one, now, multi, facts.canAnswer, facts.closedDoor));
|
|
85
90
|
if (live.length > 0)
|
|
86
91
|
groups.push({ key: "running", label: "Running", rows: live });
|
|
92
|
+
const waits = waiting.items.filter(one => one.status.label === "Waiting" && !one.status.views.includes("needs-you")).map(one => rowOf(store, one, now, multi, facts.canAnswer, facts.closedDoor));
|
|
93
|
+
if (waits.length > 0)
|
|
94
|
+
groups.push({ key: "waiting", label: "Waiting", rows: waits });
|
|
87
95
|
const perProject = needs.projects.map(one => ({ name: one.repo === null ? "Unplaced" : projectName(one.repo), needs: one.totals["needs-you"] }));
|
|
88
96
|
const any = totals.all > 0 || totals.completed > 0 || (totals.archived ?? 0) > 0;
|
|
89
|
-
const shown = groups.filter(one => one.key !== "running").reduce((sum, one) => sum + one.rows.length, 0);
|
|
97
|
+
const shown = groups.filter(one => one.key !== "running" && one.key !== "waiting").reduce((sum, one) => sum + one.rows.length, 0);
|
|
90
98
|
return {
|
|
91
99
|
kind: "home",
|
|
92
100
|
say: homeSentence(totals["needs-you"], perProject, project === null ? null : projectName(project), any),
|
package/dist/server/http.js
CHANGED
|
@@ -167,7 +167,7 @@ export function safeReturn(raw) {
|
|
|
167
167
|
* button can name. Nothing here is a second redirect framework: one
|
|
168
168
|
* same-site path, or "/".
|
|
169
169
|
*/
|
|
170
|
-
export const LOGIN_RETURN_PAGES = /^\/(oauth\/authorize|t\/[^/]+|r\/[1-9]\d*|code(?:\/[a-f0-9]{32}(?:\/ship)?)?|review|chat|work|board|projects|routines|recipes|fleet|settings(\/[a-z-]+)?|mode)$/;
|
|
170
|
+
export const LOGIN_RETURN_PAGES = /^\/(oauth\/authorize|t\/[^/]+|r\/[1-9]\d*|code(?:\/[a-f0-9]{32}(?:\/ship)?)?|review|chat|tasks|work|board|projects|routines|recipes|fleet|settings(\/[a-z-]+)?|mode)$/;
|
|
171
171
|
export function loginReturn(raw) {
|
|
172
172
|
const safe = safeReturn(raw);
|
|
173
173
|
if (safe === "/")
|
package/dist/server/kit.js
CHANGED
|
@@ -223,7 +223,7 @@ export const card = (options) => html `<a class="k-card" href="${options.href}">
|
|
|
223
223
|
export function notFound(path, options = {}) {
|
|
224
224
|
return html `${pageHeader("Not found")}<div class="k-nf">${empty({
|
|
225
225
|
title: options.title ?? NOT_FOUND.title(path), text: options.text ?? NOT_FOUND.text,
|
|
226
|
-
action: acts(btn("Go to Home", { sm: true, href: "/" }), btn("Open Tasks", { sm: true, variant: "ghost", href: "/
|
|
226
|
+
action: acts(btn("Go to Home", { sm: true, href: "/" }), btn("Open Tasks", { sm: true, variant: "ghost", href: "/tasks" })),
|
|
227
227
|
})}</div>`;
|
|
228
228
|
}
|
|
229
229
|
/** The Settings frame: Overview and the eight groups, a desk breadcrumb, a phone back header, one column. */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AssignmentSnapshot } from "../assignment.js";
|
|
2
|
+
import type { MergeReadyView } from "../browser-workspace.js";
|
|
3
|
+
import type { Store } from "../store.js";
|
|
4
|
+
import type { Who } from "./session.js";
|
|
5
|
+
/** The panel for a task page, or null when there is no result to merge yet. */
|
|
6
|
+
export declare function mergeReadyViewOf(store: Store, taskId: string, assignment: AssignmentSnapshot | null | undefined, who: Who, runChecks: {
|
|
7
|
+
action: string;
|
|
8
|
+
level: "quick" | "full";
|
|
9
|
+
returnTo: string;
|
|
10
|
+
} | null, returnTo: string): MergeReadyView | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** The task page's "Can I merge?" panel (docs/plans/plain-toolroll.md P1.6): the checklist (merge-checklist.ts) with
|
|
2
|
+
* each blocker's act as the form this page already posts for it. Run checks is the Checks row's own post; Retry is the
|
|
3
|
+
* failed box's own requeue (the page shows it only where that box would); Ask the agent to fix is Send back: the same
|
|
4
|
+
* post to /r/<build>/comment with intent=revise that the result's Send back makes, its note prefilled with the blocker
|
|
5
|
+
* and editable, sealing the same saved notes. Only a signed-in approver gets the forms; anyone else reads the rows. */
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
7
|
+
import { checklistFromAssignment } from "../merge-checklist.js";
|
|
8
|
+
import { isRevisionFeedback, revisionBatchOf, revisionSourceOf } from "../result-review.js";
|
|
9
|
+
/** The panel for a task page, or null when there is no result to merge yet. */
|
|
10
|
+
export function mergeReadyViewOf(store, taskId, assignment, who, runChecks, returnTo) {
|
|
11
|
+
if (assignment == null || assignment.receipt === null)
|
|
12
|
+
return null;
|
|
13
|
+
const list = checklistFromAssignment(store, taskId, assignment);
|
|
14
|
+
const person = who.via === "cookie" && who.role === "approver" && !store.isDemo();
|
|
15
|
+
const receipt = assignment.receipt;
|
|
16
|
+
const notes = store.liveDiffComments(receipt.runId).filter(one => isRevisionFeedback(one));
|
|
17
|
+
return {
|
|
18
|
+
answer: list.answer, sentence: list.sentence,
|
|
19
|
+
rows: list.rows.filter(one => one.mark !== "skip").map(one => {
|
|
20
|
+
const act = one.act;
|
|
21
|
+
const form = !person || act === null ? null
|
|
22
|
+
: act.kind === "run-checks" ? runChecks === null || runChecks.action !== `/r/${act.run}/checks` ? null : { kind: "run-checks", ...runChecks }
|
|
23
|
+
: act.kind === "retry" ? { kind: "retry" }
|
|
24
|
+
: act.run !== receipt.runId ? null
|
|
25
|
+
: { kind: "fix", action: `/r/${act.run}/comment`, note: act.note, batch: revisionBatchOf(notes), source: revisionSourceOf(store.getScope(receipt.taskId)?.digest ?? null),
|
|
26
|
+
request: randomBytes(16).toString("hex"), returnTo };
|
|
27
|
+
return { key: one.key, label: one.label, mark: one.mark, say: one.say, act: form };
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* typefaces, declared in the route table's edge stage and answered before authentication.
|
|
4
4
|
*/
|
|
5
5
|
import type { ServerResponse } from "node:http";
|
|
6
|
+
export declare const PWA_WORKER = "// Toolroll \u2014 push only; deliberately NO fetch handler (no offline cache of an authenticated console).\n// Every shape an older worker took, kept for good, then Home, Tasks' Ready and Board views and a task by its number.\nconst SHAPES = [/^\\/next$/, /^\\/review$/, /^\\/system$/, /^\\/routines$/, /^\\/routines\\/[0-9]+$/, /^\\/d\\/[0-9]+$/, /^\\/r\\/[0-9]+$/, /^\\/approve\\/[0-9]+$/,\n /^\\/$/, /^\\/tasks\\?view=ready$/, /^\\/tasks\\?view=board$/, /^\\/n\\/[1-9][0-9]{0,14}$/];\nself.addEventListener(\"push\", function (event) {\n var data = {};\n try { data = event.data ? event.data.json() : {}; } catch (e) {}\n var body = typeof data.body === \"string\" ? data.body : \"the console needs you\";\n var tag = typeof data.tag === \"string\" ? data.tag : \"toolroll\";\n var url = typeof data.url === \"string\" && SHAPES.some(function (s) { return s.test(data.url); }) ? data.url : \"/\";\n if (typeof data.waiting === \"number\" && data.waiting >= 0 && self.registration.setAppBadge) {\n // A count, never content. Honest no-op where unsupported.\n event.waitUntil(self.registration.setAppBadge(Math.floor(data.waiting)).catch(function () {}));\n }\n event.waitUntil(self.registration.showNotification(\"Toolroll\", { body: body, tag: tag, data: { url: url }, actions: [{ action: \"open\", title: \"Open\" }] }));\n});\nself.addEventListener(\"message\", function (event) {\n // The page recomputes on load/focus and is authoritative over any stale\n // push: {badge: N} sets, {badge: 0} clears.\n var badge = event.data && typeof event.data.badge === \"number\" ? event.data.badge : null;\n if (badge === null || !self.registration.setAppBadge) return;\n if (badge <= 0 && self.registration.clearAppBadge) { self.registration.clearAppBadge().catch(function () {}); return; }\n self.registration.setAppBadge(Math.floor(badge)).catch(function () {});\n});\n// A tap on the ping and its one \"Open\" action land in the same place.\nself.addEventListener(\"notificationclick\", function (event) {\n event.notification.close();\n if (self.registration.clearAppBadge) self.registration.clearAppBadge().catch(function () {});\n var url = event.notification.data && event.notification.data.url;\n var target = new URL(SHAPES.some(function (s) { return s.test(url); }) ? url : \"/\", self.location.origin).href;\n event.waitUntil(clients.matchAll({ type: \"window\" }).then(function (open) {\n for (var i = 0; i < open.length; i++) { if (open[i].url === target && open[i].focus) return open[i].focus(); }\n return clients.openWindow(target);\n }));\n});\n";
|
|
6
7
|
/**
|
|
7
8
|
* Answers one install asset or typeface (GET only; the caller checks the method), with nosniff and its own
|
|
8
9
|
* conservative caching/CSP. False for any other path: unknown names fall through to the router's refusal.
|
|
@@ -38,19 +38,21 @@ const PWA_MANIFEST = JSON.stringify({
|
|
|
38
38
|
// and the tap, only. notificationclick resolves ONLY allow-listed relative
|
|
39
39
|
// paths — the payload URL is data, revalidated, never handed raw to the
|
|
40
40
|
// browser (arc 3 finding 5).
|
|
41
|
-
const PWA_WORKER = `// Toolroll — push only; deliberately NO fetch handler (no offline cache of an authenticated console).
|
|
42
|
-
|
|
41
|
+
export const PWA_WORKER = `// Toolroll — push only; deliberately NO fetch handler (no offline cache of an authenticated console).
|
|
42
|
+
// Every shape an older worker took, kept for good, then Home, Tasks' Ready and Board views and a task by its number.
|
|
43
|
+
const SHAPES = [/^\\/next$/, /^\\/review$/, /^\\/system$/, /^\\/routines$/, /^\\/routines\\/[0-9]+$/, /^\\/d\\/[0-9]+$/, /^\\/r\\/[0-9]+$/, /^\\/approve\\/[0-9]+$/,
|
|
44
|
+
/^\\/$/, /^\\/tasks\\?view=ready$/, /^\\/tasks\\?view=board$/, /^\\/n\\/[1-9][0-9]{0,14}$/];
|
|
43
45
|
self.addEventListener("push", function (event) {
|
|
44
46
|
var data = {};
|
|
45
47
|
try { data = event.data ? event.data.json() : {}; } catch (e) {}
|
|
46
48
|
var body = typeof data.body === "string" ? data.body : "the console needs you";
|
|
47
49
|
var tag = typeof data.tag === "string" ? data.tag : "toolroll";
|
|
48
|
-
var url = typeof data.url === "string" && SHAPES.some(function (s) { return s.test(data.url); }) ? data.url : "/
|
|
50
|
+
var url = typeof data.url === "string" && SHAPES.some(function (s) { return s.test(data.url); }) ? data.url : "/";
|
|
49
51
|
if (typeof data.waiting === "number" && data.waiting >= 0 && self.registration.setAppBadge) {
|
|
50
52
|
// A count, never content. Honest no-op where unsupported.
|
|
51
53
|
event.waitUntil(self.registration.setAppBadge(Math.floor(data.waiting)).catch(function () {}));
|
|
52
54
|
}
|
|
53
|
-
event.waitUntil(self.registration.showNotification("Toolroll", { body: body, tag: tag, data: { url: url } }));
|
|
55
|
+
event.waitUntil(self.registration.showNotification("Toolroll", { body: body, tag: tag, data: { url: url }, actions: [{ action: "open", title: "Open" }] }));
|
|
54
56
|
});
|
|
55
57
|
self.addEventListener("message", function (event) {
|
|
56
58
|
// The page recomputes on load/focus and is authoritative over any stale
|
|
@@ -60,11 +62,12 @@ self.addEventListener("message", function (event) {
|
|
|
60
62
|
if (badge <= 0 && self.registration.clearAppBadge) { self.registration.clearAppBadge().catch(function () {}); return; }
|
|
61
63
|
self.registration.setAppBadge(Math.floor(badge)).catch(function () {});
|
|
62
64
|
});
|
|
65
|
+
// A tap on the ping and its one "Open" action land in the same place.
|
|
63
66
|
self.addEventListener("notificationclick", function (event) {
|
|
64
67
|
event.notification.close();
|
|
65
68
|
if (self.registration.clearAppBadge) self.registration.clearAppBadge().catch(function () {});
|
|
66
69
|
var url = event.notification.data && event.notification.data.url;
|
|
67
|
-
var target = new URL(SHAPES.some(function (s) { return s.test(url); }) ? url : "/
|
|
70
|
+
var target = new URL(SHAPES.some(function (s) { return s.test(url); }) ? url : "/", self.location.origin).href;
|
|
68
71
|
event.waitUntil(clients.matchAll({ type: "window" }).then(function (open) {
|
|
69
72
|
for (var i = 0; i < open.length; i++) { if (open[i].url === target && open[i].focus) return open[i].focus(); }
|
|
70
73
|
return clients.openWindow(target);
|