taskchef 7.7.1 → 7.8.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.7.1",
3
+ "version": "7.8.0",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
package/README.md CHANGED
@@ -176,6 +176,8 @@ taskchef dashboard --port 3211
176
176
  The loopback dashboard watches `tasks.jsonl`, groups current states, and opens
177
177
  linked Codex tasks. List snapshots and SSE events carry only the latest-result
178
178
  projection; opening task details fetches the full newest-first result history.
179
+ The header shows the running TaskChef package version reported by the same
180
+ bounded health identity used for compatible-listener checks.
179
181
  Task and result times are relative through 29 days (with minute detail for the
180
182
  first six hours), then use a locale-aware calendar date. Each time is a keyboard-
181
183
  accessible toggle for its full locale-aware date and time, and one shared
@@ -197,6 +199,8 @@ The health endpoint contains only a fixed service marker, health schema,
197
199
  TaskChef version, dashboard-server version, and canonical workspace. It exposes
198
200
  no task data, credentials, environment variables, process control, or secrets.
199
201
 
202
+ ![TaskChef dashboard identity and version](docs/images/dashboard-identity.jpg)
203
+
200
204
  ![Immutable event-time dashboard notifications](docs/images/notification-event-snapshots.jpg)
201
205
 
202
206
  ![Task detail result history](docs/images/result-history-dashboard.jpg)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.7.1",
3
+ "version": "7.8.0",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -26,6 +26,7 @@
26
26
  "docs/spec.md",
27
27
  "docs/workflows.md",
28
28
  "docs/firstmate-taskchef-comparison.md",
29
+ "docs/images/dashboard-identity.jpg",
29
30
  "docs/images/notification-event-snapshots.jpg",
30
31
  "docs/images/result-history-dashboard.jpg",
31
32
  "index.js",
@@ -49,6 +49,7 @@ const elements = {
49
49
  projectFilter: document.querySelector("#project-filter"),
50
50
  statusFilter: document.querySelector("#status-filter"),
51
51
  taskCount: document.querySelector("#task-count"),
52
+ taskchefVersion: document.querySelector("#taskchef-version"),
52
53
  taskList: document.querySelector("#task-list"),
53
54
  toastList: document.querySelector("#toast-list"),
54
55
  };
@@ -87,8 +88,8 @@ function codexIcon() {
87
88
  const image = document.createElement("img");
88
89
  image.src = "/assets/codex-app-light.png";
89
90
  image.alt = "";
90
- image.width = 16;
91
- image.height = 16;
91
+ image.width = 18;
92
+ image.height = 18;
92
93
  picture.append(dark, image);
93
94
  return picture;
94
95
  }
@@ -106,6 +107,22 @@ function setConnection(connected) {
106
107
  elements.connectionLabel.textContent = connected ? "Live" : "Reconnecting…";
107
108
  }
108
109
 
110
+ async function loadDashboardVersion() {
111
+ try {
112
+ const response = await fetch("/api/health");
113
+ if (!response.ok) return;
114
+ const identity = await response.json();
115
+ if (typeof identity.taskchefVersion !== "string" || !identity.taskchefVersion) return;
116
+ elements.taskchefVersion.textContent = `v${identity.taskchefVersion}`;
117
+ elements.taskchefVersion.setAttribute(
118
+ "aria-label",
119
+ `TaskChef version ${identity.taskchefVersion}`,
120
+ );
121
+ } catch {
122
+ // The live connection state already communicates dashboard availability.
123
+ }
124
+ }
125
+
109
126
  function replaceOptions(select, values, allLabel) {
110
127
  const selected = select.value;
111
128
  select.replaceChildren();
@@ -399,6 +416,8 @@ function showMessage(message) {
399
416
  elements.dashboardMessage.hidden = false;
400
417
  }
401
418
 
419
+ void loadDashboardVersion();
420
+
402
421
  const events = new EventSource("/api/events");
403
422
  events.addEventListener("open", () => setConnection(true));
404
423
  events.addEventListener("error", () => setConnection(false));
@@ -11,7 +11,7 @@
11
11
  <body>
12
12
  <header class="site-header">
13
13
  <div>
14
- <p class="eyebrow">TaskChef</p>
14
+ <p class="eyebrow">TaskChef <span id="taskchef-version" class="dashboard-version"></span></p>
15
15
  <div class="title-row">
16
16
  <picture class="dashboard-icon" aria-hidden="true">
17
17
  <source srcset="/assets/taskchef-dark.svg" media="(prefers-color-scheme: dark)">
@@ -87,7 +87,7 @@
87
87
  <button id="open-codex" class="primary-button task-action" type="button" aria-label="Open this task in Codex">
88
88
  <picture class="codex-icon" aria-hidden="true">
89
89
  <source srcset="/assets/codex-app-dark.png" media="(prefers-color-scheme: dark)">
90
- <img src="/assets/codex-app-light.png" alt="" width="16" height="16">
90
+ <img src="/assets/codex-app-light.png" alt="" width="18" height="18">
91
91
  </picture>
92
92
  <span>Open task</span>
93
93
  </button>
@@ -51,6 +51,7 @@ h3 { margin-bottom: 8px; font-size: 0.84rem; letter-spacing: 0.08em; text-transf
51
51
  .dashboard-icon img { display: block; width: 100%; height: 100%; object-fit: contain; }
52
52
 
53
53
  .eyebrow { margin-bottom: 10px; color: var(--accent); font-size: 0.78rem; font-weight: 750; letter-spacing: 0.15em; text-transform: uppercase; }
54
+ .dashboard-version { margin-inline-start: 0.45em; color: var(--muted); font-size: 0.72rem; letter-spacing: 0.04em; text-transform: none; }
54
55
  .subtitle, .task-project, time, .timestamp-missing { color: var(--muted); }
55
56
  .subtitle { margin-bottom: 0; }
56
57
 
@@ -71,12 +72,13 @@ label { display: grid; gap: 6px; color: var(--muted); font-size: 0.78rem; font-w
71
72
  select { min-width: 180px; padding: 9px 34px 9px 11px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); color: var(--text); }
72
73
  .task-count { margin: 0 0 9px auto; color: var(--muted); font-size: 0.9rem; }
73
74
 
74
- .task-list { display: grid; gap: 12px; }
75
+ .task-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
75
76
  .task-card { padding: 20px 22px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); box-shadow: 0 1px 0 rgb(0 0 0 / 2%); }
76
77
  .task-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; }
77
78
  .task-title { padding: 0; border: 0; background: none; font-size: 1.08rem; font-weight: 720; text-align: left; cursor: pointer; }
78
79
  .task-title:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
79
80
  .task-project { margin: 3px 0 12px; font-size: 0.86rem; }
81
+ .task-title, .task-summary { overflow-wrap: anywhere; }
80
82
  .task-summary { max-width: 78ch; margin-bottom: 14px; }
81
83
  time, .timestamp-missing { font-size: 0.78rem; }
82
84
  .timestamp-toggle { padding: 2px 0; border: 0; background: none; cursor: pointer; text-align: left; }
@@ -84,7 +86,7 @@ time, .timestamp-missing { font-size: 0.78rem; }
84
86
  .task-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
85
87
  .task-open { flex: none; }
86
88
  .task-action { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 32px; padding: 5px 9px; font-size: 0.82rem; line-height: 1.2; white-space: nowrap; }
87
- .codex-icon { display: block; flex: none; width: 16px; height: 16px; }
89
+ .codex-icon { display: block; flex: none; width: 18px; height: 18px; }
88
90
  .codex-icon img { display: block; width: 100%; height: 100%; object-fit: contain; }
89
91
 
90
92
  .status { flex: none; padding: 4px 9px; border-radius: 999px; background: var(--surface-muted); color: var(--muted); font-size: 0.72rem; font-weight: 750; text-transform: capitalize; }