@clemrig/factory 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +6 -17
  2. package/dist/app/doctor.d.ts +2 -4
  3. package/dist/app/doctor.js +6 -4
  4. package/dist/app/doctor.js.map +1 -1
  5. package/dist/app/factory.d.ts +2 -0
  6. package/dist/app/factory.js +13 -2
  7. package/dist/app/factory.js.map +1 -1
  8. package/dist/app/init.js +1 -1
  9. package/dist/app/init.js.map +1 -1
  10. package/dist/app/options.d.ts +17 -0
  11. package/dist/app/options.js +78 -0
  12. package/dist/app/options.js.map +1 -0
  13. package/dist/app/run.d.ts +4 -20
  14. package/dist/app/run.js +22 -93
  15. package/dist/app/run.js.map +1 -1
  16. package/dist/app/runs.d.ts +2 -34
  17. package/dist/app/runs.js +3 -63
  18. package/dist/app/runs.js.map +1 -1
  19. package/dist/cli/application.d.ts +1 -1
  20. package/dist/cli/application.js +1 -1
  21. package/dist/cli/args.js +2 -2
  22. package/dist/cli/args.js.map +1 -1
  23. package/dist/cli/doctor.d.ts +1 -1
  24. package/dist/cli/doctor.js +1 -1
  25. package/dist/cli/doctor.js.map +1 -1
  26. package/dist/cli/help.d.ts +1 -1
  27. package/dist/cli/help.js +1 -1
  28. package/dist/cli/init.d.ts +1 -1
  29. package/dist/cli/init.js +1 -1
  30. package/dist/cli/launcher.d.ts +1 -1
  31. package/dist/cli/launcher.js +4 -4
  32. package/dist/cli/launcher.js.map +1 -1
  33. package/dist/cli/reporter.d.ts +1 -1
  34. package/dist/cli/reporter.js +1 -2
  35. package/dist/cli/reporter.js.map +1 -1
  36. package/dist/cli/run.d.ts +1 -5
  37. package/dist/cli/run.js +24 -11
  38. package/dist/cli/run.js.map +1 -1
  39. package/dist/cli/status.js +3 -3
  40. package/dist/cli/status.js.map +1 -1
  41. package/dist/cli/ui.d.ts +1 -1
  42. package/dist/cli/ui.js +10 -3
  43. package/dist/cli/ui.js.map +1 -1
  44. package/dist/index.d.ts +3 -7
  45. package/dist/index.js +3 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/system/factory.d.ts +1 -1
  48. package/dist/system/factory.js +1 -1
  49. package/dist/system/factory.js.map +1 -1
  50. package/dist/system/templates.d.ts +1 -1
  51. package/dist/system/templates.js +1 -1
  52. package/package.json +7 -17
  53. package/dist/engine/metrics.d.ts +0 -45
  54. package/dist/engine/metrics.js +0 -60
  55. package/dist/engine/metrics.js.map +0 -1
  56. package/dist/engine/primitives.d.ts +0 -34
  57. package/dist/engine/primitives.js +0 -81
  58. package/dist/engine/primitives.js.map +0 -1
  59. package/dist/engine/profiles.d.ts +0 -53
  60. package/dist/engine/profiles.js +0 -117
  61. package/dist/engine/profiles.js.map +0 -1
  62. package/dist/engine/runner.d.ts +0 -13
  63. package/dist/engine/runner.js +0 -157
  64. package/dist/engine/runner.js.map +0 -1
  65. package/dist/engine/types.d.ts +0 -194
  66. package/dist/engine/types.js +0 -2
  67. package/dist/engine/types.js.map +0 -1
  68. package/dist/engine/workflow.d.ts +0 -18
  69. package/dist/engine/workflow.js +0 -196
  70. package/dist/engine/workflow.js.map +0 -1
  71. package/dist/system/process.d.ts +0 -21
  72. package/dist/system/process.js +0 -238
  73. package/dist/system/process.js.map +0 -1
  74. package/dist/system/runtimes/adapter.d.ts +0 -90
  75. package/dist/system/runtimes/adapter.js +0 -20
  76. package/dist/system/runtimes/adapter.js.map +0 -1
  77. package/dist/system/runtimes/agent-runtime.d.ts +0 -45
  78. package/dist/system/runtimes/agent-runtime.js +0 -184
  79. package/dist/system/runtimes/agent-runtime.js.map +0 -1
  80. package/dist/system/runtimes/claude-code.d.ts +0 -10
  81. package/dist/system/runtimes/claude-code.js +0 -383
  82. package/dist/system/runtimes/claude-code.js.map +0 -1
  83. package/dist/system/runtimes/inspect.d.ts +0 -25
  84. package/dist/system/runtimes/inspect.js +0 -129
  85. package/dist/system/runtimes/inspect.js.map +0 -1
  86. package/dist/system/runtimes/pi.d.ts +0 -7
  87. package/dist/system/runtimes/pi.js +0 -235
  88. package/dist/system/runtimes/pi.js.map +0 -1
  89. package/dist/system/runtimes/protocol.d.ts +0 -9
  90. package/dist/system/runtimes/protocol.js +0 -38
  91. package/dist/system/runtimes/protocol.js.map +0 -1
  92. package/dist/system/runtimes/registry.d.ts +0 -5
  93. package/dist/system/runtimes/registry.js +0 -5
  94. package/dist/system/runtimes/registry.js.map +0 -1
  95. package/dist/system/store.d.ts +0 -20
  96. package/dist/system/store.js +0 -126
  97. package/dist/system/store.js.map +0 -1
  98. package/dist/ui/server.d.ts +0 -8
  99. package/dist/ui/server.js +0 -111
  100. package/dist/ui/server.js.map +0 -1
  101. package/dist/ui/static/dashboard.js +0 -266
  102. package/dist/ui/static/follow.js +0 -67
  103. package/dist/ui/static/index.html +0 -20
  104. package/dist/ui/static/style.css +0 -166
  105. package/dist/ui/static/timeline.js +0 -180
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/system/store.ts"],"names":[],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- import { type Server } from "node:http";
2
- import type { Factory } from "../app/factory.js";
3
- /** A read-only view of the repository's runs on 127.0.0.1, on `port` or one chosen by the system. */
4
- export declare function startDashboard(factory: Factory, listen?: number): Promise<{
5
- url: string;
6
- server: Server;
7
- close(): Promise<void>;
8
- }>;
package/dist/ui/server.js DELETED
@@ -1,111 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { createServer, } from "node:http";
3
- import { basename } from "node:path";
4
- import { listRuns, loadRun, loadRunEvents, presentResult, retryMaximums, summarizeRun, UnknownRunError, } from "../app/runs.js";
5
- /** The runs, newest first; an unreadable run with its error. */
6
- async function runs(factory) {
7
- return {
8
- repository: basename(factory.dir),
9
- runs: (await listRuns(factory))
10
- .reverse()
11
- .map((entry) => ("state" in entry ? summarizeRun(entry.state) : entry)),
12
- };
13
- }
14
- /** A run with its events from the `after`-th: the page asks only for what it has not received. */
15
- async function detail(factory, id, after) {
16
- const state = await loadRun(factory, id);
17
- const events = await loadRunEvents(factory, id);
18
- const max = retryMaximums(factory, state.workflow);
19
- return {
20
- run: summarizeRun(state),
21
- runtimes: state.runtimes ?? {},
22
- error: state.error ?? null,
23
- events: events.slice(after),
24
- next: events.length,
25
- corrections: Object.entries(state.retries ?? {}).map(([key, count]) => ({
26
- key,
27
- count,
28
- max: max.get(key) ?? null,
29
- })),
30
- result: presentResult(factory, state),
31
- };
32
- }
33
- /** The page's files, served as they are: no script or style is ever inline. */
34
- const pageFiles = {
35
- "/": ["index.html", "text/html; charset=utf-8"],
36
- "/style.css": ["style.css", "text/css; charset=utf-8"],
37
- "/dashboard.js": ["dashboard.js", "text/javascript; charset=utf-8"],
38
- "/timeline.js": ["timeline.js", "text/javascript; charset=utf-8"],
39
- "/follow.js": ["follow.js", "text/javascript; charset=utf-8"],
40
- };
41
- async function loadPage() {
42
- const page = new Map();
43
- for (const [path, [file, type]] of Object.entries(pageFiles))
44
- page.set(path, {
45
- body: await readFile(new URL(`./static/${file}`, import.meta.url), "utf8"),
46
- type,
47
- });
48
- return page;
49
- }
50
- function send(response, status, body, type = "text/plain; charset=utf-8") {
51
- response.writeHead(status, {
52
- "content-type": type,
53
- "cache-control": "no-store",
54
- "x-content-type-options": "nosniff",
55
- "content-security-policy": "default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
56
- });
57
- response.end(body);
58
- }
59
- /** A read-only view of the repository's runs on 127.0.0.1, on `port` or one chosen by the system. */
60
- export async function startDashboard(factory, listen = 0) {
61
- let port = 0;
62
- const page = await loadPage();
63
- const handle = async (request, response) => {
64
- // Another Host means another site reached this port (DNS rebinding).
65
- if (request.headers.host !== `127.0.0.1:${port}` &&
66
- request.headers.host !== `localhost:${port}`)
67
- return send(response, 403, "Forbidden");
68
- if (request.method !== "GET")
69
- return send(response, 405, "Method Not Allowed");
70
- const url = new URL(request.url ?? "/", "http://127.0.0.1");
71
- const file = page.get(url.pathname);
72
- if (file)
73
- return send(response, 200, file.body, file.type);
74
- if (url.pathname === "/api/runs")
75
- return send(response, 200, JSON.stringify(await runs(factory)), "application/json");
76
- const id = /^\/api\/runs\/([^/]+)$/.exec(url.pathname)?.[1];
77
- if (id === undefined)
78
- return send(response, 404, "Not Found");
79
- const after = url.searchParams.get("after") ?? "0";
80
- if (!/^\d+$/.test(after))
81
- return send(response, 400, "Bad Request");
82
- let body;
83
- try {
84
- body = await detail(factory, decodeURIComponent(id), Number(after));
85
- }
86
- catch (error) {
87
- // A run that cannot be read now may be read at the next request.
88
- return error instanceof UnknownRunError
89
- ? send(response, 404, "Not Found")
90
- : send(response, 500, error instanceof Error ? error.message : String(error));
91
- }
92
- return send(response, 200, JSON.stringify(body), "application/json");
93
- };
94
- const server = createServer((request, response) => {
95
- handle(request, response).catch((error) => send(response, 500, error instanceof Error ? error.message : String(error)));
96
- });
97
- await new Promise((resolve, reject) => {
98
- server.once("error", reject);
99
- server.listen(listen, "127.0.0.1", () => resolve());
100
- });
101
- port = server.address().port;
102
- return {
103
- url: `http://127.0.0.1:${port}/`,
104
- server,
105
- close: () => new Promise((resolve, reject) => {
106
- server.close((error) => (error ? reject(error) : resolve()));
107
- server.closeAllConnections();
108
- }),
109
- };
110
- }
111
- //# sourceMappingURL=server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ui/server.ts"],"names":[],"mappings":"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"}
@@ -1,266 +0,0 @@
1
- // @ts-check
2
- /**
3
- * The dashboard: the run list every 2 seconds, and the selected run as
4
- * `follow.js` follows it. Text only, never HTML.
5
- */
6
- import { follower } from "./follow.js";
7
- import { cost, duration, timeline } from "./timeline.js";
8
-
9
- /** @typedef {import("../../engine/types.js").RunEvent} RunEvent */
10
- /** @typedef {import("./timeline.js").Block} Block */
11
-
12
- /**
13
- * @typedef {object} Summary
14
- * @property {string} id
15
- * @property {string} [workflow]
16
- * @property {string} [status]
17
- * @property {string} [stage]
18
- * @property {number | null} [durationMs]
19
- * @property {number | null} [costUsd]
20
- * @property {boolean} [partial]
21
- * @property {string} [updatedAt]
22
- * @property {string} [error] Why the run cannot be read.
23
- */
24
-
25
- /**
26
- * @typedef {object} Detail
27
- * @property {Required<Omit<Summary, "error">>} run
28
- * @property {Record<string, string>} runtimes
29
- * @property {string | null} error
30
- * @property {RunEvent[]} events
31
- * @property {number} next
32
- * @property {{ key: string; count: number; max: number | null }[]} corrections
33
- * @property {string[]} result
34
- */
35
-
36
- const LIST_EVERY_MS = 2000;
37
- const DETAIL_EVERY_MS = 1000;
38
-
39
- /**
40
- * @param {string} id
41
- * @returns {HTMLElement}
42
- */
43
- function byId(id) {
44
- const found = document.getElementById(id);
45
- if (!found) throw new Error(`Missing element #${id}`);
46
- return found;
47
- }
48
-
49
- /**
50
- * @param {string} tag
51
- * @param {string} [text]
52
- * @param {string} [className]
53
- * @returns {HTMLElement}
54
- */
55
- function el(tag, text, className) {
56
- const node = document.createElement(tag);
57
- if (text !== undefined) node.textContent = text;
58
- if (className) node.className = className;
59
- return node;
60
- }
61
-
62
- /** @param {string} iso */
63
- function time(iso) {
64
- const date = new Date(iso);
65
- return Number.isNaN(date.getTime())
66
- ? iso
67
- : date.toLocaleString(undefined, {
68
- dateStyle: "short",
69
- timeStyle: "medium",
70
- });
71
- }
72
-
73
- /** @param {import("../../engine/metrics.js").TokenCounts} tokens */
74
- function tokenLine(tokens) {
75
- return `${tokens.input} input · ${tokens.output} output · ${tokens.cacheRead} cache read · ${tokens.cacheWrite} cache write tokens`;
76
- }
77
-
78
- /**
79
- * @param {string} path
80
- * @returns {Promise<any>}
81
- */
82
- async function load(path) {
83
- const response = await fetch(path, { cache: "no-store" });
84
- if (!response.ok)
85
- throw Object.assign(
86
- new Error((await response.text()) || `HTTP ${response.status}`),
87
- { status: response.status },
88
- );
89
- return response.json();
90
- }
91
-
92
- /** The run the address names, or none when it cannot be read. */
93
- function fromHash() {
94
- try {
95
- return decodeURIComponent(location.hash.slice(1));
96
- } catch {
97
- return "";
98
- }
99
- }
100
-
101
- let selected = fromHash();
102
-
103
- /** @param {Summary[]} runs */
104
- function showRuns(runs) {
105
- byId("none").hidden = runs.length > 0;
106
- byId("runs").replaceChildren(
107
- ...runs.map((run) => {
108
- const row = el("tr");
109
- row.title = run.id;
110
- if (run.id === selected) row.className = "selected";
111
- if (run.error !== undefined) {
112
- const cause = el("td", run.error);
113
- cause.setAttribute("colspan", "4");
114
- row.append(el("td", run.id), el("td", "unreadable", "failed"), cause);
115
- } else
116
- row.append(
117
- el("td", run.workflow),
118
- el("td", run.status, run.status),
119
- el("td", run.stage),
120
- el("td", duration(run.durationMs ?? null)),
121
- el("td", cost(run.costUsd ?? null, run.partial ?? false)),
122
- el("td", time(run.updatedAt ?? "")),
123
- );
124
- row.addEventListener("click", () => select(run.id));
125
- return row;
126
- }),
127
- );
128
- }
129
-
130
- /** @param {Block} block */
131
- function showBlock(block) {
132
- const item = el("li", undefined, "block");
133
- const head = el("p");
134
- head.append(
135
- `${block.kind} ${block.stage} · `,
136
- el("span", block.status, block.status),
137
- block.durationMs === null ? "" : ` · ${duration(block.durationMs)}`,
138
- );
139
- item.append(head);
140
- const line = (/** @type {string} */ text, className = "line") =>
141
- item.append(el("p", text, className));
142
- const { call } = block;
143
- if (call?.model)
144
- line(`${call.runtime} ${call.model} · session ${call.session}`);
145
- for (const tool of block.tools)
146
- line(
147
- tool.status === "running"
148
- ? `${tool.name} …`
149
- : `${tool.name}${tool.status === "failed" ? " failed" : ""} ${((tool.durationMs ?? 0) / 1000).toFixed(1)} s`,
150
- );
151
- if (call?.usage)
152
- line(
153
- `${call.scope === "session" ? "Session total" : "Call"}: ${tokenLine(call.usage)} · ${cost(call.usage.costUsd, false)}`,
154
- );
155
- else if (block.tokens)
156
- line(
157
- `So far: ${tokenLine(block.tokens)}${block.provisional ? " (provisional)" : ""}`,
158
- );
159
- for (const warning of block.warnings) line(warning, "line warning");
160
- for (const entry of block.journal) line(`${time(entry.at)} ${entry.message}`);
161
- if (block.decision) {
162
- const { verdict, reason, destination } = block.decision;
163
- const retry = block.retry
164
- ? ` [${block.retry.key} ${block.retry.count}/${block.retry.max}]`
165
- : "";
166
- line(`${verdict} — ${reason} → ${destination ?? "end"}${retry}`);
167
- }
168
- if (block.detail) line(block.detail, "line failed");
169
- return item;
170
- }
171
-
172
- /**
173
- * @param {Detail} detail
174
- * @param {RunEvent[]} events Every event received for the run.
175
- */
176
- function showDetail(detail, events) {
177
- const { run } = detail;
178
- const summary = el("section");
179
- const line = el("p", undefined, "empty");
180
- line.append(
181
- el("span", run.status, run.status),
182
- ` · ${run.stage} · ${duration(run.durationMs)} · ${cost(run.costUsd, run.partial)}`,
183
- );
184
- summary.append(el("h3", `${run.workflow} · ${run.id}`), line);
185
- for (const runtime of Object.values(detail.runtimes))
186
- summary.append(el("p", runtime, "line"));
187
- if (detail.error) summary.append(el("pre", detail.error, "failed"));
188
- const stages = el("section");
189
- const blocks = el("ol");
190
- blocks.append(...timeline(events).map(showBlock));
191
- stages.append(el("h2", "Timeline"), blocks);
192
- const sections = [summary, stages];
193
- if (detail.corrections.length) {
194
- const corrections = el("section");
195
- corrections.append(el("h2", "Corrections"));
196
- for (const { key, count, max } of detail.corrections)
197
- corrections.append(el("p", `${key} ${count}/${max ?? "?"}`, "line"));
198
- sections.push(corrections);
199
- }
200
- const result = el("section");
201
- result.append(el("h2", "Result"), el("pre", detail.result.join("\n")));
202
- sections.push(result);
203
- byId("detail").replaceChildren(...sections);
204
- }
205
-
206
- /** @param {string} message */
207
- function updated(message) {
208
- byId("updated").textContent = message;
209
- }
210
-
211
- async function refreshRuns() {
212
- try {
213
- const { repository, runs } = await load("/api/runs");
214
- byId("repository").textContent = repository;
215
- showRuns(runs);
216
- updated(`Updated ${new Date().toLocaleTimeString()}`);
217
- } catch (error) {
218
- unavailable(error);
219
- }
220
- setTimeout(refreshRuns, LIST_EVERY_MS);
221
- }
222
-
223
- /** @param {unknown} error */
224
- const unavailable = (error) =>
225
- updated(
226
- `Unavailable: ${error instanceof Error ? error.message : String(error)}`,
227
- );
228
-
229
- /** @param {string} text */
230
- const placeholder = (text) =>
231
- byId("detail").replaceChildren(el("p", text, "empty"));
232
-
233
- const follow = follower({
234
- load,
235
- schedule: (work) => setTimeout(work, DETAIL_EVERY_MS),
236
- cancel: (timer) => clearTimeout(timer),
237
- show: (detail, events) => showDetail(detail, events),
238
- fail: (error) => {
239
- const { status } = /** @type {{ status?: number }} */ (error);
240
- // The follower stops on a run that does not exist and retries the rest.
241
- if (status === 404) placeholder("This run does not exist.");
242
- else if (status)
243
- placeholder(
244
- `This run cannot be read: ${error instanceof Error ? error.message : String(error)}`,
245
- );
246
- else unavailable(error);
247
- },
248
- });
249
-
250
- /** @param {string} id */
251
- function select(id) {
252
- if (id === selected) return;
253
- selected = id;
254
- if (fromHash() !== id) location.hash = encodeURIComponent(id);
255
- placeholder(id ? "Loading…" : "Select a run.");
256
- for (const row of byId("runs").children)
257
- row.className =
258
- /** @type {HTMLElement} */ (row).title === id ? "selected" : "";
259
- follow.select(id);
260
- }
261
-
262
- // A link or the history can change the selected run without reloading the page.
263
- window.addEventListener("hashchange", () => select(fromHash()));
264
- refreshRuns();
265
- if (selected) placeholder("Loading…");
266
- follow.select(selected);
@@ -1,67 +0,0 @@
1
- // @ts-check
2
- /**
3
- * Follows the selected run: asks only for the events not received yet, one
4
- * request after the answer to the previous one, drops an answer for a selection
5
- * that has changed since, and stops once the run has ended or does not exist.
6
- * Requests and timers are given, so that this logic is tested without a browser.
7
- */
8
-
9
- /** @typedef {import("../../engine/types.js").RunEvent} RunEvent */
10
-
11
- /**
12
- * @typedef {object} Answer
13
- * @property {{ status: string }} run
14
- * @property {RunEvent[]} events Events from the index the request asked for.
15
- * @property {number} next The index to ask for next.
16
- */
17
-
18
- /**
19
- * @template Timer
20
- * @param {object} options
21
- * @param {(path: string) => Promise<any>} options.load Rejects with `status` set on an HTTP error.
22
- * @param {(work: () => void) => Timer} options.schedule Runs `work` after the polling interval.
23
- * @param {(timer: Timer | undefined) => void} options.cancel
24
- * @param {(answer: any, events: RunEvent[]) => void} options.show The latest answer and every event received.
25
- * @param {(error: unknown) => void} options.fail
26
- */
27
- export function follower({ load, schedule, cancel, show, fail }) {
28
- /** One selection: a new object each time, so that an old answer never matches. */
29
- let current = { id: "", events: /** @type {RunEvent[]} */ ([]), next: 0 };
30
- /** @type {Timer | undefined} */
31
- let timer;
32
-
33
- /** @param {typeof current} target */
34
- async function refresh(target) {
35
- if (!target.id) return;
36
- /** @type {Answer} */
37
- let answer;
38
- try {
39
- answer = await load(
40
- `/api/runs/${encodeURIComponent(target.id)}?after=${target.next}`,
41
- );
42
- } catch (error) {
43
- if (target !== current) return;
44
- fail(error);
45
- // A run that does not exist will not appear later.
46
- const status = /** @type {{ status?: number }} */ (error).status;
47
- if (status !== 404) timer = schedule(() => refresh(target));
48
- return;
49
- }
50
- if (target !== current) return;
51
- target.events.push(...answer.events);
52
- target.next = answer.next;
53
- show(answer, target.events);
54
- if (answer.run.status === "running")
55
- timer = schedule(() => refresh(target));
56
- }
57
-
58
- return {
59
- /** @param {string} id */
60
- select(id) {
61
- if (id === current.id) return;
62
- cancel(timer);
63
- current = { id, events: [], next: 0 };
64
- refresh(current);
65
- },
66
- };
67
- }
@@ -1,20 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>factory</title>
7
- <link rel="stylesheet" href="/style.css">
8
- <script type="module" src="/dashboard.js"></script>
9
- </head>
10
- <body>
11
- <header><h1>factory</h1><span id="repository"></span><span id="updated"></span></header>
12
- <main>
13
- <div>
14
- <div class="table"><table><thead><tr><th>Workflow</th><th>Status</th><th>Stage</th><th>Duration</th><th>Cost</th><th>Updated</th></tr></thead><tbody id="runs"></tbody></table></div>
15
- <p id="none" class="empty" hidden>No run yet.</p>
16
- </div>
17
- <div id="detail"><p class="empty">Select a run.</p></div>
18
- </main>
19
- </body>
20
- </html>
@@ -1,166 +0,0 @@
1
- :root {
2
- color-scheme: light dark;
3
- --bg: #f7f7f5;
4
- --panel: #fff;
5
- --fg: #1d1d1b;
6
- --muted: #6f6e69;
7
- --line: #e4e3df;
8
- --accent: #2557d6;
9
- --ok: #19773a;
10
- --bad: #b42318;
11
- --run: #a15c07;
12
- }
13
- @media (prefers-color-scheme: dark) {
14
- :root {
15
- --bg: #111110;
16
- --panel: #1b1b19;
17
- --fg: #e8e7e3;
18
- --muted: #9b9a94;
19
- --line: #2c2c29;
20
- --accent: #7aa2ff;
21
- --ok: #5bd28a;
22
- --bad: #ff7a6b;
23
- --run: #f2b14c;
24
- }
25
- }
26
- * {
27
- box-sizing: border-box;
28
- }
29
- body {
30
- margin: 0;
31
- background: var(--bg);
32
- color: var(--fg);
33
- font:
34
- 14px / 1.45 ui-sans-serif,
35
- system-ui,
36
- -apple-system,
37
- sans-serif;
38
- }
39
- header {
40
- display: flex;
41
- flex-wrap: wrap;
42
- gap: 4px 12px;
43
- align-items: baseline;
44
- padding: 12px 16px;
45
- border-bottom: 1px solid var(--line);
46
- }
47
- header h1 {
48
- margin: 0;
49
- font-size: 16px;
50
- }
51
- header span {
52
- color: var(--muted);
53
- font-size: 13px;
54
- }
55
- #updated {
56
- margin-left: auto;
57
- }
58
- main {
59
- display: grid;
60
- grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
61
- gap: 16px;
62
- padding: 16px;
63
- }
64
- @media (max-width: 900px) {
65
- main {
66
- grid-template-columns: minmax(0, 1fr);
67
- }
68
- }
69
- .table {
70
- overflow-x: auto;
71
- background: var(--panel);
72
- border: 1px solid var(--line);
73
- border-radius: 6px;
74
- }
75
- table {
76
- width: 100%;
77
- border-collapse: collapse;
78
- }
79
- th,
80
- td {
81
- padding: 7px 10px;
82
- border-bottom: 1px solid var(--line);
83
- text-align: left;
84
- white-space: nowrap;
85
- }
86
- th {
87
- color: var(--muted);
88
- font-size: 12px;
89
- font-weight: 500;
90
- }
91
- tbody tr {
92
- cursor: pointer;
93
- }
94
- tbody tr:hover,
95
- tbody tr.selected {
96
- background: color-mix(in srgb, var(--accent) 12%, transparent);
97
- }
98
- .completed,
99
- .passed {
100
- color: var(--ok);
101
- }
102
- .failed {
103
- color: var(--bad);
104
- }
105
- .running {
106
- color: var(--run);
107
- }
108
- section {
109
- margin-bottom: 12px;
110
- padding: 10px 12px;
111
- background: var(--panel);
112
- border: 1px solid var(--line);
113
- border-radius: 6px;
114
- }
115
- h2 {
116
- margin: 0 0 8px;
117
- color: var(--muted);
118
- font-size: 12px;
119
- font-weight: 600;
120
- letter-spacing: 0.04em;
121
- text-transform: uppercase;
122
- }
123
- h3 {
124
- margin: 0 0 4px;
125
- font-size: 14px;
126
- overflow-wrap: anywhere;
127
- }
128
- ol {
129
- margin: 0;
130
- padding: 0;
131
- list-style: none;
132
- }
133
- .block {
134
- padding: 6px 0;
135
- border-bottom: 1px dotted var(--line);
136
- }
137
- .block:last-child {
138
- border-bottom: 0;
139
- }
140
- .block > p {
141
- margin: 0;
142
- }
143
- .line,
144
- pre {
145
- font:
146
- 12px / 1.45 ui-monospace,
147
- SFMono-Regular,
148
- Menlo,
149
- monospace;
150
- overflow-wrap: anywhere;
151
- }
152
- .line {
153
- margin: 2px 0 0 16px;
154
- color: var(--muted);
155
- }
156
- .warning {
157
- color: var(--run);
158
- }
159
- pre {
160
- margin: 0;
161
- white-space: pre-wrap;
162
- }
163
- .empty {
164
- margin: 0;
165
- color: var(--muted);
166
- }