taskchef 7.23.3 → 7.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/package.json +24 -2
- package/src/dashboard/app.js +2 -82
- package/src/dashboard/dist/app.js +164 -0
- package/src/dashboard/dist/app.js.map +1 -0
- package/src/dashboard/dist/index.html +16 -0
- package/src/dashboard/dist/styles.css +1 -0
- package/src/dashboard/react/App.tsx +432 -0
- package/src/dashboard/react/api.test.ts +24 -0
- package/src/dashboard/react/api.ts +95 -0
- package/src/dashboard/react/components/ActivityTimeline.tsx +63 -0
- package/src/dashboard/react/components/GitHubLinks.tsx +25 -0
- package/src/dashboard/react/components/NotificationCenter.test.tsx +71 -0
- package/src/dashboard/react/components/NotificationCenter.tsx +111 -0
- package/src/dashboard/react/components/RelativeTime.tsx +28 -0
- package/src/dashboard/react/components/ShimmerText.tsx +9 -0
- package/src/dashboard/react/components/StatusBadge.tsx +7 -0
- package/src/dashboard/react/components/TaskCard.tsx +62 -0
- package/src/dashboard/react/components/TaskDetail.test.tsx +62 -0
- package/src/dashboard/react/components/TaskDetail.tsx +251 -0
- package/src/dashboard/react/components/UsagePanel.tsx +41 -0
- package/src/dashboard/react/fixtures.ts +52 -0
- package/src/dashboard/react/index.html +15 -0
- package/src/dashboard/react/main.tsx +12 -0
- package/src/dashboard/react/overlay-state.test.ts +79 -0
- package/src/dashboard/react/overlay-state.ts +38 -0
- package/src/dashboard/react/presentation.test.tsx +128 -0
- package/src/dashboard/react/presentation.ts +80 -0
- package/src/dashboard/react/styles.css +123 -0
- package/src/dashboard/react/test-setup.ts +36 -0
- package/src/dashboard/react/types.ts +106 -0
- package/src/dashboard/styles.css +16 -3
- package/src/dashboard.js +29 -18
package/src/dashboard/styles.css
CHANGED
|
@@ -185,12 +185,25 @@ dialog section + section { margin-top: 24px; }
|
|
|
185
185
|
.usage-pending, .usage-calculating { display: flex; align-items: center; }
|
|
186
186
|
.text-shimmer {
|
|
187
187
|
color: var(--muted);
|
|
188
|
+
display: inline-block;
|
|
189
|
+
width: fit-content;
|
|
190
|
+
max-width: 100%;
|
|
191
|
+
justify-self: start;
|
|
192
|
+
vertical-align: top;
|
|
193
|
+
background: linear-gradient(100deg, var(--muted) 48%, var(--text) 50%, var(--muted) 52%);
|
|
194
|
+
background-size: 250% 100%;
|
|
195
|
+
background-position: 100% 0;
|
|
196
|
+
background-clip: text;
|
|
197
|
+
-webkit-background-clip: text;
|
|
198
|
+
-webkit-text-fill-color: transparent;
|
|
199
|
+
animation: text-shimmer-sweep 6.4s linear infinite;
|
|
188
200
|
}
|
|
189
|
-
|
|
190
|
-
|
|
201
|
+
@keyframes text-shimmer-sweep {
|
|
202
|
+
0% { background-position: 100% 0; }
|
|
203
|
+
72%, 100% { background-position: -100% 0; }
|
|
191
204
|
}
|
|
192
205
|
@media (prefers-reduced-motion: reduce) {
|
|
193
|
-
.text-shimmer
|
|
206
|
+
.text-shimmer { background: none; animation: none; -webkit-text-fill-color: currentcolor; }
|
|
194
207
|
}
|
|
195
208
|
pre { max-height: 280px; margin: 0; padding: 14px; overflow: auto; border-radius: 7px; background: var(--surface-muted); white-space: pre-wrap; overflow-wrap: anywhere; font: 0.86rem/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
196
209
|
.metadata { display: grid; grid-template-columns: minmax(100px, 150px) 1fr; margin: 0; font-size: 0.88rem; }
|
package/src/dashboard.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
2
3
|
import { constants, watch } from "node:fs";
|
|
3
4
|
import { open, readFile, realpath, stat } from "node:fs/promises";
|
|
4
5
|
import http from "node:http";
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
45
46
|
|
|
46
47
|
const TASKS_FILE_NAME = "tasks.jsonl";
|
|
47
48
|
const STATIC_ROOT = fileURLToPath(new URL("./dashboard/", import.meta.url));
|
|
49
|
+
const DASHBOARD_BUNDLE_ROOT = path.join(STATIC_ROOT, "dist");
|
|
48
50
|
const ASSET_ROOT = fileURLToPath(new URL("../assets/", import.meta.url));
|
|
49
51
|
const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1"]);
|
|
50
52
|
const DEFAULT_MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
@@ -58,26 +60,30 @@ const DASHBOARD_CONTROL_NONCE_TTL_MS = 5 * 60_000;
|
|
|
58
60
|
const MAX_MANUAL_TRANSITION_BODY_BYTES = 4 * 1024;
|
|
59
61
|
export const DASHBOARD_HEALTH_PATH = "/api/health";
|
|
60
62
|
export const DASHBOARD_HEALTH_MAX_BYTES = 8 * 1024;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
function contentSecurityPolicy(styleNonce = null) {
|
|
64
|
+
return [
|
|
65
|
+
"default-src 'self'",
|
|
66
|
+
"base-uri 'none'",
|
|
67
|
+
"connect-src 'self'",
|
|
68
|
+
"form-action 'none'",
|
|
69
|
+
"frame-ancestors 'none'",
|
|
70
|
+
"img-src 'self' data:",
|
|
71
|
+
"object-src 'none'",
|
|
72
|
+
"script-src 'self'",
|
|
73
|
+
`style-src 'self'${styleNonce ? ` 'nonce-${styleNonce}'` : ""}`,
|
|
74
|
+
// Mantine uses style attributes for component variables and dynamic positioning.
|
|
75
|
+
"style-src-attr 'unsafe-inline'",
|
|
76
|
+
].join("; ");
|
|
77
|
+
}
|
|
72
78
|
|
|
73
79
|
const STATIC_FILES = new Map([
|
|
74
|
-
["/", [path.join(
|
|
80
|
+
["/", [path.join(DASHBOARD_BUNDLE_ROOT, "index.html"), "text/html; charset=utf-8"]],
|
|
75
81
|
["/actions.js", [path.join(STATIC_ROOT, "actions.js"), "text/javascript; charset=utf-8"]],
|
|
76
|
-
["/app.js", [path.join(
|
|
82
|
+
["/app.js", [path.join(DASHBOARD_BUNDLE_ROOT, "app.js"), "text/javascript; charset=utf-8"]],
|
|
77
83
|
["/github-links.js", [path.join(STATIC_ROOT, "github-links.js"), "text/javascript; charset=utf-8"]],
|
|
78
84
|
["/time.js", [path.join(STATIC_ROOT, "time.js"), "text/javascript; charset=utf-8"]],
|
|
79
85
|
["/state.js", [path.join(STATIC_ROOT, "state.js"), "text/javascript; charset=utf-8"]],
|
|
80
|
-
["/styles.css", [path.join(
|
|
86
|
+
["/styles.css", [path.join(DASHBOARD_BUNDLE_ROOT, "styles.css"), "text/css; charset=utf-8"]],
|
|
81
87
|
["/assets/taskchef-dark.svg", [path.join(ASSET_ROOT, "taskchef-dark.svg"), "image/svg+xml"]],
|
|
82
88
|
["/assets/taskchef.svg", [path.join(ASSET_ROOT, "taskchef.svg"), "image/svg+xml"]],
|
|
83
89
|
["/assets/codex-app-dark.png", [path.join(ASSET_ROOT, "codex-app-dark.png"), "image/png"]],
|
|
@@ -401,10 +407,10 @@ export class DashboardMonitor extends EventEmitter {
|
|
|
401
407
|
}
|
|
402
408
|
}
|
|
403
409
|
|
|
404
|
-
function securityHeaders(contentType) {
|
|
410
|
+
function securityHeaders(contentType, { styleNonce = null } = {}) {
|
|
405
411
|
return {
|
|
406
412
|
"Cache-Control": "no-store",
|
|
407
|
-
"Content-Security-Policy":
|
|
413
|
+
"Content-Security-Policy": contentSecurityPolicy(styleNonce),
|
|
408
414
|
"Content-Type": contentType,
|
|
409
415
|
"Cross-Origin-Opener-Policy": "same-origin",
|
|
410
416
|
"Referrer-Policy": "no-referrer",
|
|
@@ -1175,8 +1181,13 @@ export async function createDashboardServer({
|
|
|
1175
1181
|
return;
|
|
1176
1182
|
}
|
|
1177
1183
|
const [filePath, contentType] = staticFile;
|
|
1178
|
-
|
|
1179
|
-
|
|
1184
|
+
let body = await readFile(filePath);
|
|
1185
|
+
let styleNonce = null;
|
|
1186
|
+
if (url.pathname === "/") {
|
|
1187
|
+
styleNonce = randomBytes(18).toString("base64");
|
|
1188
|
+
body = Buffer.from(body.toString("utf8").replaceAll("__TASKCHEF_STYLE_NONCE__", styleNonce));
|
|
1189
|
+
}
|
|
1190
|
+
response.writeHead(200, securityHeaders(contentType, { styleNonce }));
|
|
1180
1191
|
response.end(method === "HEAD" ? undefined : body);
|
|
1181
1192
|
};
|
|
1182
1193
|
|