toolroll 0.9.85 → 0.9.87

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.
Files changed (220) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/agent-fence.d.ts +6 -2
  3. package/dist/agent-fence.js +3 -2
  4. package/dist/agents-board.d.ts +12 -0
  5. package/dist/agents-board.js +26 -0
  6. package/dist/approval-rules-ui.d.ts +4 -1
  7. package/dist/approval-rules-ui.js +31 -15
  8. package/dist/assignment-ui.js +1 -1
  9. package/dist/assignment.js +4 -3
  10. package/dist/backup-ui.d.ts +5 -4
  11. package/dist/backup-ui.js +65 -32
  12. package/dist/browser/terminal.css +1 -1
  13. package/dist/browser/terminal.js +57 -54
  14. package/dist/browser/workspace.css +2 -2
  15. package/dist/browser/workspace.js +35 -68
  16. package/dist/browser-crew.js +1 -1
  17. package/dist/browser-workspace.d.ts +363 -177
  18. package/dist/browser-workspace.js +10 -9
  19. package/dist/chat-actions.d.ts +11 -1
  20. package/dist/chat-actions.js +53 -1
  21. package/dist/chat-channel.js +1 -0
  22. package/dist/chat-controls.d.ts +1 -1
  23. package/dist/chat-controls.js +1 -1
  24. package/dist/chat-quiet.js +3 -2
  25. package/dist/chat-settings-page.d.ts +29 -9
  26. package/dist/chat-settings-page.js +59 -28
  27. package/dist/check-levels-ui.d.ts +2 -1
  28. package/dist/check-levels-ui.js +67 -58
  29. package/dist/claim.d.ts +3 -0
  30. package/dist/claim.js +4 -1
  31. package/dist/contracts/chat-actions.d.ts +9 -0
  32. package/dist/contracts/chat-actions.js +3 -0
  33. package/dist/contracts/cli.d.ts +43 -0
  34. package/dist/contracts/cli.js +4 -0
  35. package/dist/contracts/console-api.d.ts +4 -1
  36. package/dist/contracts/console-api.js +4 -1
  37. package/dist/contracts/flow-propose.d.ts +4 -1
  38. package/dist/contracts/flow-propose.js +1 -1
  39. package/dist/contracts/flow.d.ts +41 -0
  40. package/dist/contracts/flow.js +4 -2
  41. package/dist/contracts/lead-tools.d.ts +21 -0
  42. package/dist/contracts/lead-tools.js +2 -0
  43. package/dist/contracts/store-json.d.ts +2 -0
  44. package/dist/contracts/trigger-payloads.d.ts +20 -0
  45. package/dist/contracts/trigger-payloads.js +4 -0
  46. package/dist/coordinator.js +1 -0
  47. package/dist/credentials-ui.d.ts +5 -3
  48. package/dist/credentials-ui.js +58 -31
  49. package/dist/deploy-gate.js +4 -0
  50. package/dist/design/kit.css +685 -0
  51. package/dist/design/kit.d.ts +208 -0
  52. package/dist/design/kit.js +177 -0
  53. package/dist/design/tokens.css +19 -1
  54. package/dist/discord-settings.js +8 -4
  55. package/dist/dispatch.d.ts +3 -0
  56. package/dist/dispatch.js +4 -1
  57. package/dist/export-ui.d.ts +6 -4
  58. package/dist/export-ui.js +18 -8
  59. package/dist/flow-browser.d.ts +103 -0
  60. package/dist/flow-browser.js +204 -0
  61. package/dist/flow-changed.d.ts +14 -0
  62. package/dist/flow-changed.js +53 -0
  63. package/dist/flow-code.d.ts +5 -1
  64. package/dist/flow-code.js +4 -1
  65. package/dist/flow-engine.js +16 -0
  66. package/dist/flow-gallery-ui.d.ts +41 -4
  67. package/dist/flow-gallery-ui.js +95 -63
  68. package/dist/flow-live.js +9 -1
  69. package/dist/flow-pull-request.js +15 -7
  70. package/dist/flow-share.js +3 -1
  71. package/dist/flow-starters-ui.d.ts +3 -9
  72. package/dist/flow-starters-ui.js +12 -23
  73. package/dist/flow-steps.d.ts +9 -1
  74. package/dist/flow-steps.js +55 -5
  75. package/dist/flow-triggers.js +55 -0
  76. package/dist/flow-unattended-words.d.ts +20 -0
  77. package/dist/flow-unattended-words.js +24 -0
  78. package/dist/flow-unattended.d.ts +9 -5
  79. package/dist/flow-unattended.js +45 -23
  80. package/dist/flows-cli.d.ts +19 -0
  81. package/dist/flows-cli.js +54 -5
  82. package/dist/flows-ui.d.ts +12 -6
  83. package/dist/flows-ui.js +152 -32
  84. package/dist/flows.js +5 -2
  85. package/dist/integrations-ui.d.ts +6 -4
  86. package/dist/integrations-ui.js +34 -40
  87. package/dist/integrations.d.ts +2 -2
  88. package/dist/integrations.js +4 -4
  89. package/dist/kits-ui.d.ts +8 -10
  90. package/dist/kits-ui.js +29 -33
  91. package/dist/kits.js +1 -1
  92. package/dist/knowledge-ui.js +76 -19
  93. package/dist/lead-tools.js +35 -6
  94. package/dist/lead-voice.js +16 -5
  95. package/dist/ledger-view.d.ts +2 -0
  96. package/dist/ledger-view.js +41 -38
  97. package/dist/models-ui.d.ts +8 -3
  98. package/dist/models-ui.js +82 -59
  99. package/dist/monitoring-ui.d.ts +4 -2
  100. package/dist/monitoring-ui.js +44 -13
  101. package/dist/needs-you.d.ts +1 -1
  102. package/dist/needs-you.js +8 -0
  103. package/dist/operate-remote-arguments.js +1 -0
  104. package/dist/operate-remote.js +1 -1
  105. package/dist/operate.d.ts +1 -1
  106. package/dist/operate.js +249 -6
  107. package/dist/passwordless.d.ts +1 -1
  108. package/dist/passwordless.js +1 -1
  109. package/dist/people-audit-ui.d.ts +2 -1
  110. package/dist/people-audit-ui.js +25 -23
  111. package/dist/policy-ui.d.ts +3 -2
  112. package/dist/policy-ui.js +27 -21
  113. package/dist/policy.d.ts +3 -0
  114. package/dist/policy.js +14 -1
  115. package/dist/project-delete-ui.d.ts +4 -2
  116. package/dist/project-delete-ui.js +135 -126
  117. package/dist/project-delete.js +7 -3
  118. package/dist/project-knowledge.d.ts +10 -10
  119. package/dist/publish.d.ts +11 -0
  120. package/dist/publish.js +16 -3
  121. package/dist/pull-request-flow.js +35 -4
  122. package/dist/pull-request-ui.d.ts +2 -1
  123. package/dist/pull-request-ui.js +39 -28
  124. package/dist/recipe-ui.d.ts +4 -2
  125. package/dist/recipe-ui.js +14 -4
  126. package/dist/release-adapters.d.ts +3 -0
  127. package/dist/release-adapters.js +22 -2
  128. package/dist/release-cli.js +81 -9
  129. package/dist/release.d.ts +21 -0
  130. package/dist/release.js +40 -4
  131. package/dist/releases.js +1 -1
  132. package/dist/request-budget-ui.d.ts +4 -2
  133. package/dist/request-budget-ui.js +30 -19
  134. package/dist/result-follow-ups.js +4 -0
  135. package/dist/retention-ui.d.ts +4 -3
  136. package/dist/retention-ui.js +15 -12
  137. package/dist/scope.d.ts +8 -0
  138. package/dist/scope.js +13 -0
  139. package/dist/serve.d.ts +0 -1
  140. package/dist/serve.js +51 -7
  141. package/dist/server/agents-computers.d.ts +9 -0
  142. package/dist/server/agents-computers.js +67 -0
  143. package/dist/server/chat.js +20 -18
  144. package/dist/server/chrome.d.ts +1 -1
  145. package/dist/server/chrome.js +19 -9
  146. package/dist/server/console.css +31 -2
  147. package/dist/server/flows.js +79 -36
  148. package/dist/server/home.d.ts +35 -0
  149. package/dist/server/home.js +104 -0
  150. package/dist/server/http.d.ts +3 -0
  151. package/dist/server/http.js +5 -0
  152. package/dist/server/kit-standalone.d.ts +22 -0
  153. package/dist/server/kit-standalone.js +69 -0
  154. package/dist/server/kit.d.ts +405 -0
  155. package/dist/server/kit.js +361 -0
  156. package/dist/server/pages.d.ts +2 -1
  157. package/dist/server/pages.js +213 -174
  158. package/dist/server/people-tokens.js +26 -85
  159. package/dist/server/render-history.d.ts +51 -0
  160. package/dist/server/render-history.js +228 -0
  161. package/dist/server/render-new-task.d.ts +29 -0
  162. package/dist/server/render-new-task.js +96 -0
  163. package/dist/server/render-next.d.ts +63 -0
  164. package/dist/server/render-next.js +109 -0
  165. package/dist/server/render-pages.d.ts +35 -190
  166. package/dist/server/render-pages.js +154 -563
  167. package/dist/server/render-people.d.ts +24 -3
  168. package/dist/server/render-people.js +93 -72
  169. package/dist/server/render-projects.d.ts +103 -0
  170. package/dist/server/render-projects.js +247 -0
  171. package/dist/server/render-results.d.ts +5 -24
  172. package/dist/server/render-results.js +48 -185
  173. package/dist/server/render-settings.d.ts +5 -74
  174. package/dist/server/render-settings.js +102 -493
  175. package/dist/server/render-tasks.d.ts +43 -5
  176. package/dist/server/render-tasks.js +131 -91
  177. package/dist/server/route-table.js +31 -5
  178. package/dist/server/settings.js +276 -255
  179. package/dist/server/task-views.d.ts +8 -0
  180. package/dist/server/task-views.js +90 -2
  181. package/dist/server/tasks.d.ts +18 -0
  182. package/dist/server/tasks.js +148 -41
  183. package/dist/server/terminals.js +1 -1
  184. package/dist/server/welcome.d.ts +22 -0
  185. package/dist/server/welcome.js +69 -0
  186. package/dist/settings-groups-ui.d.ts +279 -0
  187. package/dist/settings-groups-ui.js +615 -0
  188. package/dist/settings-kit.d.ts +42 -6
  189. package/dist/settings-kit.js +69 -7
  190. package/dist/settings-proofs.js +1 -0
  191. package/dist/setup-guide.d.ts +2 -2
  192. package/dist/skills-ui.js +59 -7
  193. package/dist/slack-settings.js +12 -5
  194. package/dist/spend-ui.d.ts +45 -26
  195. package/dist/spend-ui.js +124 -56
  196. package/dist/sso-ui.d.ts +5 -2
  197. package/dist/sso-ui.js +63 -31
  198. package/dist/storage-ui.d.ts +3 -2
  199. package/dist/storage-ui.js +56 -50
  200. package/dist/store.d.ts +182 -5
  201. package/dist/store.js +512 -38
  202. package/dist/surface.js +23 -3
  203. package/dist/task-archive.d.ts +10 -0
  204. package/dist/task-archive.js +16 -0
  205. package/dist/task-control.d.ts +1 -1
  206. package/dist/teams-settings.d.ts +2 -1
  207. package/dist/teams-settings.js +24 -41
  208. package/dist/telegram-settings.d.ts +0 -4
  209. package/dist/telegram-settings.js +52 -25
  210. package/dist/tidy.d.ts +130 -0
  211. package/dist/tidy.js +503 -0
  212. package/dist/toolroll-update-ui.d.ts +8 -4
  213. package/dist/toolroll-update-ui.js +55 -45
  214. package/dist/tools-ui.d.ts +5 -3
  215. package/dist/tools-ui.js +107 -42
  216. package/dist/work-index.d.ts +24 -0
  217. package/dist/work-index.js +56 -30
  218. package/dist/workspace-ui.d.ts +1 -1
  219. package/dist/workspace-ui.js +8 -4
  220. package/package.json +1 -1
@@ -45,6 +45,11 @@ export declare const githubRunSchema: z.ZodCatch<z.ZodObject<{
45
45
  display_title: z.ZodCatch<z.ZodOptional<z.ZodString>>;
46
46
  created_at: z.ZodCatch<z.ZodOptional<z.ZodString>>;
47
47
  html_url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
48
+ id: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
49
+ event: z.ZodCatch<z.ZodOptional<z.ZodString>>;
50
+ head_repository: z.ZodCatch<z.ZodOptional<z.ZodObject<{
51
+ full_name: z.ZodCatch<z.ZodOptional<z.ZodString>>;
52
+ }, z.core.$strip>>>;
48
53
  }, z.core.$strip>>;
49
54
  /** `repos/:repo/issues` and `repos/:repo/pulls`: a list (anything else reads as an empty one). */
50
55
  export declare const githubIssueListSchema: z.ZodCatch<z.ZodArray<z.ZodCatch<z.ZodObject<{
@@ -111,6 +116,11 @@ export declare const githubRunListSchema: z.ZodCatch<z.ZodObject<{
111
116
  display_title: z.ZodCatch<z.ZodOptional<z.ZodString>>;
112
117
  created_at: z.ZodCatch<z.ZodOptional<z.ZodString>>;
113
118
  html_url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
119
+ id: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
120
+ event: z.ZodCatch<z.ZodOptional<z.ZodString>>;
121
+ head_repository: z.ZodCatch<z.ZodOptional<z.ZodObject<{
122
+ full_name: z.ZodCatch<z.ZodOptional<z.ZodString>>;
123
+ }, z.core.$strip>>>;
114
124
  }, z.core.$strip>>>>>;
115
125
  }, z.core.$strip>>;
116
126
  /** A signed GitHub webhook event (`issues`, `pull_request`, `workflow_run`; a `ping` reads as an empty one). */
@@ -176,6 +186,11 @@ export declare const githubEventSchema: z.ZodCatch<z.ZodObject<{
176
186
  display_title: z.ZodCatch<z.ZodOptional<z.ZodString>>;
177
187
  created_at: z.ZodCatch<z.ZodOptional<z.ZodString>>;
178
188
  html_url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
189
+ id: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
190
+ event: z.ZodCatch<z.ZodOptional<z.ZodString>>;
191
+ head_repository: z.ZodCatch<z.ZodOptional<z.ZodObject<{
192
+ full_name: z.ZodCatch<z.ZodOptional<z.ZodString>>;
193
+ }, z.core.$strip>>>;
179
194
  }, z.core.$strip>>>;
180
195
  }, z.core.$strip>>;
181
196
  /** A Linear issue, from the GraphQL query (`labels { nodes }`) or a webhook's `data` (`labels` as a list). */
@@ -353,6 +368,11 @@ export declare const readGithubRunList: (raw: unknown) => {
353
368
  display_title?: string | undefined;
354
369
  created_at?: string | undefined;
355
370
  html_url?: string | undefined;
371
+ id?: number | undefined;
372
+ event?: string | undefined;
373
+ head_repository?: {
374
+ full_name?: string | undefined;
375
+ } | undefined;
356
376
  }[] | undefined;
357
377
  };
358
378
  export declare const readGithubEvent: (raw: unknown) => GithubEvent;
@@ -42,6 +42,10 @@ export const githubRunSchema = z.object({
42
42
  display_title: text,
43
43
  created_at: text,
44
44
  html_url: text,
45
+ /** Read by tidy's CI observations (task-tidy.md): which run, why it ran, and whose repository its commit is in. */
46
+ id: loose(z.number()),
47
+ event: text,
48
+ head_repository: loose(z.object({ full_name: text })),
45
49
  }).catch({});
46
50
  /** `repos/:repo/issues` and `repos/:repo/pulls`: a list (anything else reads as an empty one). */
47
51
  export const githubIssueListSchema = z.array(githubIssueSchema).catch([]);
@@ -326,6 +326,7 @@ export function listTasksFor(store, who, filter, now) {
326
326
  FROM task_ref JOIN task ON task.id = task_ref.external_id AND task_ref.backend = 'built-in'
327
327
  WHERE task_ref.repo IN (${sql})
328
328
  AND (? IS NULL OR task.state = ?)
329
+ AND NOT EXISTS (SELECT 1 FROM task_archive WHERE task_archive.task_ref = task_ref.id)
329
330
  AND task_ref.id > ?
330
331
  ORDER BY task_ref.id LIMIT ?`)
331
332
  .all(now.toISOString(), ...args, filter.state ?? null, filter.state ?? null, filter.cursor ?? 0, limit + 1);
@@ -5,7 +5,9 @@
5
5
  */
6
6
  import type { ApiTokenRow, WebSessionRow } from "./store.js";
7
7
  import { type Html } from "./html.js";
8
- export declare const CREDENTIALS_CSS: string;
8
+ /** The old card look; the page is built from the kit now (src/server/kit.ts), so nothing is left to style. Kept for
9
+ * chrome.ts, which still concatenates it. */
10
+ export declare const CREDENTIALS_CSS = "";
9
11
  /** "Chrome on macOS", from a user agent, for a person to recognise a session. */
10
12
  export declare function deviceWords(agent: string | null): string;
11
13
  export type CredentialsView = {
@@ -23,9 +25,9 @@ export type CredentialsView = {
23
25
  section: Html;
24
26
  };
25
27
  };
26
- export declare function credentialsHtml(view: CredentialsView, notice: {
28
+ export declare function credentialsHtml(view: CredentialsView, outcome: {
27
29
  said?: string | null;
28
30
  problem?: string | null;
29
31
  }): Html;
30
- /** The token, once: a focused page with no script. */
32
+ /** The token, once, on a page with no script: selectable text, what it may do, and where to revoke it. */
31
33
  export declare function tokenShownHtml(name: string, token: string, access: "read" | "act", expiresAt: string): Html;
@@ -1,12 +1,12 @@
1
1
  import { daysLeft, expiryNoticeDue, TOKEN_DAYS, tokenLive } from "./api-tokens.js";
2
2
  import { projectName } from "./project.js";
3
- import { html, postForm } from "./html.js";
4
- export const CREDENTIALS_CSS = `.credentials{max-width:820px;min-width:0}.credentials .card{padding:14px 16px;margin:12px 0}.credentials h2{font-size:1.05rem;margin:0 0 8px}` +
5
- `.credentials .rows{display:grid;gap:0;border:1px solid var(--so-line);border-radius:10px;overflow:hidden}.credentials .row-item{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-bottom:1px solid var(--so-line)}` +
6
- `.credentials .row-item:last-child{border-bottom:0}.credentials .row-item p{margin:0}.credentials .row-item .meta{font-size:.85rem}.credentials form{margin:0}.credentials button{min-height:40px;white-space:nowrap}` +
7
- `.credentials .create{display:grid;gap:10px}.credentials .create label{display:grid;gap:6px}.credentials .create input,.credentials .create select{box-sizing:border-box;width:100%}.credentials .badge-here{font-size:.75rem;padding:1px 6px;border-radius:999px;background:var(--so-success-soft);color:var(--so-success);margin-left:6px}` +
8
- `.credentials .row-item>div{min-width:0}.credentials .row-item p{overflow-wrap:anywhere}.credentials .soon{color:var(--so-warning,#9a6700);font-weight:600}` +
9
- `@media(max-width:600px){.credentials .row-item{flex-wrap:wrap}.credentials .create input,.credentials .create select{font-size:16px}}`;
3
+ import { html, joinHtml, postForm } from "./html.js";
4
+ import { acts, btn, chip, disclose, formRow, infoLine, input, note, notice, select, settingsRecord, settingsSection } from "./server/kit.js";
5
+ import { kitSectionForm } from "./settings-kit.js";
6
+ import { settingsSubPage } from "./settings-groups-ui.js";
7
+ /** The old card look; the page is built from the kit now (src/server/kit.ts), so nothing is left to style. Kept for
8
+ * chrome.ts, which still concatenates it. */
9
+ export const CREDENTIALS_CSS = "";
10
10
  /** "Chrome on macOS", from a user agent, for a person to recognise a session. */
11
11
  export function deviceWords(agent) {
12
12
  if (agent === null || agent === "")
@@ -22,38 +22,65 @@ const when = (value, now) => {
22
22
  const minutes = Math.round((now - at) / 60_000);
23
23
  return minutes < 1 ? "just now" : minutes < 60 ? `${minutes} min ago` : minutes < 60 * 24 ? `${Math.round(minutes / 60)} h ago` : new Date(at).toISOString().slice(0, 10);
24
24
  };
25
- export function credentialsHtml(view, notice) {
26
- const post = (fields, label, style = "secondary") => postForm("/settings/sessions", html `<button class="${style}">${label}</button>`, { hidden: { ...fields, ...(view.everyone ? { everyone: "1" } : {}) } });
27
- const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
28
- const toggle = view.canSeeEveryone ? html `<p class="meta"><a href="/settings/sessions${view.everyone ? "" : "?everyone=1"}">${view.everyone ? "Show only mine" : "Show everyone's"}</a></p>` : "";
29
- const sessions = view.sessions.length === 0 ? html `<p class="meta">No one else is signed in.</p>` : html `<div class="rows">${view.sessions.map(one => html `<div class="row-item" data-session="${one.idHash.slice(0, 12)}"><div><p>${view.everyone ? html `<strong>${one.account}</strong> · ` : ""}${deviceWords(one.agent)}${one.here ? html `<span class="badge-here">This browser</span>` : ""}</p>\
30
- <p class="meta">${one.ssoAt === null ? "Password" : "Identity provider"} · ${one.address ?? "unknown address"} · active ${when(one.lastSeen, view.now)} · signed in ${when(one.createdAt, view.now)}</p></div>\
31
- ${one.here ? "" : post({ action: "end-session", session: one.idHash }, "Sign out")}</div>`)}</div>`;
25
+ export function credentialsHtml(view, outcome) {
26
+ // An act on this page (sign out, revoke): one button, nothing to prove (the proof row covers only making a token).
27
+ const act = (fields, label, variant = "outline") => postForm("/settings/sessions", btn(label, { sm: true, type: "submit", variant }), { hidden: { ...fields, ...(view.everyone ? { everyone: "1" } : {}) } });
28
+ const answer = outcome.problem ? notice(outcome.problem) : outcome.said ? infoLine(outcome.said) : "";
29
+ const toggle = view.canSeeEveryone ? btn(view.everyone ? "Show only mine" : "Show everyone’s", { sm: true, variant: "ghost", href: `/settings/sessions${view.everyone ? "" : "?everyone=1"}` }) : undefined;
32
30
  const others = view.sessions.filter(one => !one.here && one.account === view.who).length;
33
- const endOthers = others > 0 && !view.everyone ? post({ action: "end-others" }, `Sign out everywhere else (${others})`) : "";
31
+ const endOthers = others > 0 && !view.everyone ? acts(act({ action: "end-others" }, `Sign out everywhere else (${others})`)) : "";
32
+ const sessions = settingsSection({ id: "signed-in", title: "Signed in", ...(toggle ? { right: toggle } : {}),
33
+ body: joinHtml([
34
+ view.sessions.length === 0 ? note("No one else is signed in.") : joinHtml(view.sessions.map(one => settingsRecord({
35
+ title: html `${view.everyone ? html `<b>${one.account}</b> · ` : ""}${deviceWords(one.agent)}${one.here ? html ` ${chip("This browser", "")}` : ""}`,
36
+ sub: `${one.ssoAt === null ? "Password" : "Identity provider"} · ${one.address ?? "unknown address"} · signed in ${when(one.createdAt, view.now)}`,
37
+ when: `Active ${when(one.lastSeen, view.now)}`,
38
+ action: one.here ? "" : act({ action: "end-session", session: one.idHash }, "Sign out"),
39
+ attrs: { "data-session": one.idHash.slice(0, 12) },
40
+ }))),
41
+ endOthers,
42
+ ]) });
34
43
  const live = view.tokens.filter(one => tokenLive(one, view.now));
35
- // v111: its projects, and one state: when it stops (soon, said once and marked) or, once replaced, when it ends.
44
+ // v111: its projects, and one state: when it stops (soon, said once) or, once replaced, when it ends.
36
45
  const ends = (one) => {
37
46
  if (one.replacedBy !== null && one.overlapUntil !== null)
38
- return html `<span class="soon">Replaced · stops ${one.overlapUntil.slice(11, 16)} UTC</span>`;
47
+ return `Replaced · stops ${one.overlapUntil.slice(11, 16)} UTC`;
39
48
  if (expiryNoticeDue(one, view.now) === null)
40
- return html `Expires ${one.expiresAt.slice(0, 10)}`;
49
+ return `Expires ${one.expiresAt.slice(0, 10)}`;
41
50
  const days = daysLeft(one.expiresAt, view.now);
42
- return html `<span class="soon">Expires ${days <= 1 ? "within a day" : `in ${days} days`}</span>`;
51
+ return `Expires ${days <= 1 ? "within a day" : `in ${days} days`}`;
43
52
  };
44
53
  const scope = (one) => one.projects === null ? "All projects" : one.projects.length === 0 ? "No projects" : one.projects.map(repo => projectName(repo)).join(", ");
45
- const tokens = live.length === 0 ? html `<p class="meta">No API tokens.</p>` : html `<div class="rows">${live.map(one => html `<div class="row-item" data-token="${one.id}"><div><p>${view.everyone ? html `<strong>${one.account}</strong> · ` : ""}${one.name} · ${one.access === "act" ? "Can act" : "Read only"}</p>\
46
- <p class="meta">${scope(one)} · ${ends(one)} · last used ${when(one.lastUsedAt, view.now)}${view.limits === undefined ? "" : ` · ${view.limits.note(one)}`}</p></div>${post({ action: "revoke-token", token: one.id }, "Revoke")}</div>`)}</div>`;
47
- const create = html `<details class="card"><summary>New API token</summary>${postForm("/settings/sessions", html `\
48
- <label>Name<input type="text" name="name" maxlength="60" required placeholder="for example: CI"></label>\
49
- <label>It can<select name="access"><option value="read">Read (tasks, results, the ledger)</option><option value="act">Act as you (file and manage work; never approve)</option></select></label>\
50
- <label>Expires in<select name="days">${TOKEN_DAYS.map(days => html `<option value="${days}"${days === 90 ? " selected" : ""}>${days} days</option>`)}</select></label>\
51
- <label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label><button>Make the token</button>`, { attrs: { class: "create" }, hidden: { action: "create-token" } })}</details>`;
52
- return html `<section class="credentials">${note}${toggle}<h2>Signed in</h2>${sessions}${endOthers}<h2 style="margin-top:1.5rem">API tokens</h2>\
53
- <p class="meta">For scripts and CI: send <code>Authorization: Bearer &lt;token&gt;</code>. A token can't approve anything.</p>${tokens}${view.everyone ? "" : create}${view.limits?.section ?? ""}</section>`;
54
+ const create = disclose("New API token", kitSectionForm({ proof: "sessions-token", action: "/settings/sessions", id: "token-form", native: true, hidden: { action: "create-token" },
55
+ save: { label: "Make the token" }, confirm: "Make an API token",
56
+ body: joinHtml([
57
+ formRow("Name", "So you can tell it apart later.", input({ id: "token-name", name: "name", attrs: { maxlength: 60, required: true, placeholder: "for example: CI", autocomplete: "off" } })),
58
+ formRow("It can", null, select({ id: "token-access", name: "access", label: "It can", current: "read",
59
+ items: [["read", "Read (tasks, results, the ledger)"], ["act", "Act as you (file and manage work; never approve)"]] })),
60
+ formRow("Expires in", null, select({ id: "token-days", name: "days", label: "Expires in", current: "90", items: TOKEN_DAYS.map(days => [String(days), `${days} days`]) })),
61
+ note("Making a token is a new way in, so it always asks for your password. The token is shown once, on the next page."),
62
+ ]) }), live.length === 0);
63
+ const tokens = settingsSection({ id: "api-tokens", title: "API tokens",
64
+ lead: html `For scripts and CI: send <code class="k-mono">Authorization: Bearer &lt;token&gt;</code>. A token can’t approve anything.`,
65
+ body: joinHtml([
66
+ live.length === 0 ? note("No API tokens.") : joinHtml(live.map(one => settingsRecord({
67
+ title: html `${view.everyone ? html `<b>${one.account}</b> · ` : ""}${one.name}`,
68
+ sub: `${one.access === "act" ? "Can act" : "Read only"} · ${scope(one)} · ${ends(one)}${view.limits === undefined ? "" : ` · ${view.limits.note(one)}`}`,
69
+ when: `Last used ${when(one.lastUsedAt, view.now)}`,
70
+ action: act({ action: "revoke-token", token: one.id }, "Revoke", "danger"),
71
+ attrs: { "data-token": one.id },
72
+ }))),
73
+ view.everyone ? "" : create,
74
+ ]) });
75
+ return joinHtml([answer, sessions, tokens, view.limits?.section ?? ""]);
54
76
  }
55
- /** The token, once: a focused page with no script. */
77
+ /** The token, once, on a page with no script: selectable text, what it may do, and where to revoke it. */
56
78
  export function tokenShownHtml(name, token, access, expiresAt) {
57
- return html `<h1>${name}</h1><div class="card"><p>Your API token, shown once. Only a hash is kept, so copy it now.</p><p class="mono secret-value">${token}</p>\
58
- <p class="meta">${access === "act" ? "It acts as you (never approves)" : "It reads"} until ${expiresAt.slice(0, 10)}. Revoke it any time on Sessions &amp; tokens.</p></div><p class="meta"><a href="/settings/sessions">Back to Sessions &amp; tokens</a></p>`;
79
+ return settingsSubPage("access", "API token", settingsSection({ id: "token-shown", title: `${name} · shown once, so copy it now`,
80
+ lead: "Only a hash is kept, so this page is the only time you’ll see it.",
81
+ body: joinHtml([
82
+ html `<p class="k-secret-val">${token}</p>`,
83
+ note(`${access === "act" ? "It acts as you and never approves" : "It reads"} until ${expiresAt.slice(0, 10)}. Revoke it any time on Sessions and tokens.`),
84
+ acts(btn("Back to Sessions and tokens", { sm: true, href: "/settings/sessions" })),
85
+ ]) }));
59
86
  }
@@ -8,6 +8,7 @@
8
8
  import { assignmentOf } from "./assignment.js";
9
9
  import { liveReleaseOf, personApprovalOf, releaseApprovalOf } from "./scope.js";
10
10
  import { verificationEvidence } from "./verification-evidence.js";
11
+ import { refArchived } from "./task-archive.js";
11
12
  import { releaseCiRunningOf } from "./release-ci-hold.js";
12
13
  import { nightlyDebtHold } from "./nightly-github.js";
13
14
  /** Whether run `runId` is a deployable candidate now, read only from the plane's records and this runtime's code. */
@@ -29,6 +30,9 @@ export function deploymentGate(store, input) {
29
30
  return refuse(`Run ${input.runId} is not a built candidate at ${head.slice(0, 7)}.`);
30
31
  if (scope === null || scope.approvedDigest !== scope.digest || scope.digest !== run.scopeDigest)
31
32
  return refuse("The run's signed scope is no longer the approved one.");
33
+ // A put-away task (v145, task-tidy.md) deploys nothing: restore it first.
34
+ if (refArchived(store, run.taskRef))
35
+ return refuse("This task is put away. Restore it before deploying.");
32
36
  // A person may approve during CI, including a release without carry terms. Every deployment road must wait
33
37
  // for the plane's exact-head observation, not only the release coordinator's polling loop.
34
38
  if (releaseCiRunningOf(store, ref.externalId) !== null)