taskchef 7.19.0 → 7.19.1
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/README.md +18 -10
- package/docs/spec.md +17 -11
- package/docs/workflows.md +11 -10
- package/package.json +1 -1
- package/src/dashboard-manager.js +10 -3
- package/src/dashboard.js +5 -0
- package/src/mcp.js +13 -4
- package/src/version.js +1 -1
package/README.md
CHANGED
|
@@ -271,6 +271,11 @@ and its activity timeline remain unchanged. If the bundled CLI is unavailable,
|
|
|
271
271
|
the dashboard does not fall back to another `codex` executable from `PATH`.
|
|
272
272
|
The header shows the running TaskChef package version reported by the same
|
|
273
273
|
bounded health identity used for compatible-listener checks.
|
|
274
|
+
The canonical port is owned by a dashboard initialized in the TaskChef MCP host
|
|
275
|
+
before its tool transport connects. Health identity records an `mcp` launcher,
|
|
276
|
+
and MCP recovery reuses only another exact-compatible MCP-launched dashboard;
|
|
277
|
+
a foreground `taskchef dashboard` process is intentionally standalone so its
|
|
278
|
+
child commands cannot silently inherit an agent-shell sandbox.
|
|
274
279
|
Task and result times are relative through 29 days (with minute detail for the
|
|
275
280
|
first six hours), then use a locale-aware calendar date. Each time is a keyboard-
|
|
276
281
|
accessible toggle for its full locale-aware date and time, and one shared
|
|
@@ -281,16 +286,17 @@ revision, so reconnects and non-semantic rewrites do not replay a notice and a
|
|
|
281
286
|
later task state cannot rewrite an older notice. A notice remains readable if
|
|
282
287
|
its task disappears; selecting it then explains that current details are no
|
|
283
288
|
longer available.
|
|
284
|
-
It does not mutate TaskChef data and prints its local URL.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
289
|
+
It does not mutate TaskChef data and prints its local URL. A foreground
|
|
290
|
+
dashboard identifies itself as standalone and is never reused on the canonical
|
|
291
|
+
MCP port. If a standalone, unknown, different-workspace, or stale-version
|
|
292
|
+
process owns port 3210, TaskChef reports a concise conflict and never kills or
|
|
293
|
+
replaces that process. The foreground CLI similarly asks you to stop the
|
|
294
|
+
listener or choose another `--port`.
|
|
290
295
|
|
|
291
296
|
The health endpoint contains only a fixed service marker, health schema,
|
|
292
|
-
TaskChef version, dashboard-server version,
|
|
293
|
-
no task data, credentials, environment variables, process control,
|
|
297
|
+
TaskChef version, dashboard-server version, canonical workspace, and launcher.
|
|
298
|
+
It exposes no task data, credentials, environment variables, process control,
|
|
299
|
+
or secrets.
|
|
294
300
|
|
|
295
301
|

|
|
296
302
|
|
|
@@ -351,13 +357,15 @@ does not necessarily reload Codex. Then run `$taskchef-dashboard` (or call
|
|
|
351
357
|
|
|
352
358
|
- the expected released TaskChef version;
|
|
353
359
|
- the expected dashboard protocol `serverVersion`;
|
|
360
|
+
- the `mcp` dashboard launcher;
|
|
354
361
|
- the canonical TaskChef workspace path;
|
|
355
362
|
- the canonical `http://127.0.0.1:3210/` URL.
|
|
356
363
|
|
|
357
364
|
The release-install sequence is therefore: install plugin, activate or reload
|
|
358
365
|
the new MCP process, ensure the dashboard, then verify TaskChef version,
|
|
359
|
-
protocol `serverVersion`, canonical workspace, and URL.
|
|
360
|
-
servers may be reused; unknown listeners
|
|
366
|
+
protocol `serverVersion`, `mcp` launcher, canonical workspace, and URL.
|
|
367
|
+
Exact-compatible MCP servers may be reused; standalone and unknown listeners
|
|
368
|
+
remain untouched.
|
|
361
369
|
|
|
362
370
|
## Development
|
|
363
371
|
|
package/docs/spec.md
CHANGED
|
@@ -121,13 +121,16 @@ fields MUST NOT change after recording.
|
|
|
121
121
|
|
|
122
122
|
## Required lifecycle
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
same serialized dashboard ensure path once by default. It MUST read the
|
|
124
|
+
Before the canonical TaskChef MCP server connects and exposes its transport, it
|
|
125
|
+
MUST invoke the same serialized dashboard ensure path once by default. It MUST read the
|
|
126
126
|
canonical configuration and skip this only for explicit
|
|
127
127
|
`dashboard.autostart: false`. Initialization failures, invalid workspace state,
|
|
128
128
|
port conflicts, and dashboard errors MUST NOT prevent tool registration or MCP
|
|
129
129
|
availability. They MUST emit only a bounded non-sensitive diagnostic through
|
|
130
130
|
the MCP process logging channel. MCP initialization MUST NOT open a browser.
|
|
131
|
+
If transport connection fails after dashboard startup, the MCP server MUST
|
|
132
|
+
best-effort close both its owned dashboard and partially attached transport,
|
|
133
|
+
then propagate the original connection failure even when cleanup also fails.
|
|
131
134
|
|
|
132
135
|
At the start of every dispatcher turn, the dispatcher SHOULD call
|
|
133
136
|
`ensure_dashboard` best-effort. Failure MUST NOT block direct TaskChef answers,
|
|
@@ -228,6 +231,7 @@ HTTP server; it does not mutate dispatcher workspace files.
|
|
|
228
231
|
```text
|
|
229
232
|
{ dashboard: {
|
|
230
233
|
action: "started" | "reused",
|
|
234
|
+
launcher: "mcp",
|
|
231
235
|
url: "http://127.0.0.1:3210/",
|
|
232
236
|
workspace: string,
|
|
233
237
|
taskchefVersion: string,
|
|
@@ -235,20 +239,22 @@ HTTP server; it does not mutate dispatcher workspace files.
|
|
|
235
239
|
} }
|
|
236
240
|
```
|
|
237
241
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
MCP initialization MUST complete its best-effort dashboard start before exposing
|
|
243
|
+
the MCP transport. Calls MUST serialize within one MCP process. The first call
|
|
244
|
+
starts an owned dashboard or reuses an exact compatible MCP-launched listener;
|
|
245
|
+
later and concurrent calls are idempotent and report reuse after the single start. The stable default MUST
|
|
241
246
|
bind only to `127.0.0.1:3210` and MUST NOT accept a model-supplied workspace,
|
|
242
247
|
host, or port.
|
|
243
248
|
|
|
244
249
|
Before reuse, TaskChef MUST query a bounded loopback identity endpoint and
|
|
245
250
|
require the exact fixed service/schema, TaskChef version, dashboard-server
|
|
246
|
-
version,
|
|
247
|
-
or stale-version listener MUST produce a concise actionable conflict. TaskChef
|
|
251
|
+
version, canonical workspace, and `mcp` launcher identity. A standalone,
|
|
252
|
+
unknown, malformed, different-workspace, or stale-version listener MUST produce a concise actionable conflict. TaskChef
|
|
248
253
|
MUST NOT kill, replace, signal, or otherwise control that listener. A startup
|
|
249
254
|
failure MUST leave no owned listener. The MCP server MUST close its owned
|
|
250
|
-
dashboard when its transport or process shuts down
|
|
251
|
-
|
|
255
|
+
dashboard when its transport or process shuts down. This keeps dashboard child
|
|
256
|
+
operations in the MCP host environment and prevents an agent-shell or foreground
|
|
257
|
+
CLI dashboard from being mistaken for the canonical MCP-owned runtime.
|
|
252
258
|
|
|
253
259
|
The packaged `$taskchef-dashboard` skill MUST call this tool, report `started`
|
|
254
260
|
or `reused`, and return the canonical clickable URL. It MAY use an available
|
|
@@ -510,6 +516,6 @@ closed.
|
|
|
510
516
|
Installing or replacing plugin files MUST NOT be described as activating the
|
|
511
517
|
new MCP code. Release verification MUST install the plugin, activate or reload
|
|
512
518
|
the new MCP process, ensure the dashboard, and verify the expected TaskChef
|
|
513
|
-
version, dashboard protocol `serverVersion`, canonical workspace,
|
|
514
|
-
URL. Exact-compatible listener reuse remains valid; installation MUST NOT be
|
|
519
|
+
version, dashboard protocol `serverVersion`, `mcp` launcher, canonical workspace,
|
|
520
|
+
and canonical URL. Exact-compatible MCP-listener reuse remains valid; installation MUST NOT be
|
|
515
521
|
claimed to reload Codex automatically.
|
package/docs/workflows.md
CHANGED
|
@@ -40,11 +40,11 @@ sequenceDiagram
|
|
|
40
40
|
participant H as Loopback health
|
|
41
41
|
participant S as Dashboard server
|
|
42
42
|
M->>M: Read dashboard.autostart (absent means true)
|
|
43
|
-
M->>M: Best-effort ensure
|
|
43
|
+
M->>M: Best-effort ensure before MCP transport connects
|
|
44
44
|
D->>M: ensure_dashboard()
|
|
45
45
|
M->>M: Serialize concurrent ensure calls
|
|
46
46
|
M->>H: GET 127.0.0.1:3210/api/health
|
|
47
|
-
alt Exact service, versions, and
|
|
47
|
+
alt Exact service, versions, canonical workspace, and MCP launcher
|
|
48
48
|
H-->>M: Bounded compatible identity
|
|
49
49
|
M-->>D: reused, URL, workspace, versions
|
|
50
50
|
else No listener
|
|
@@ -52,7 +52,7 @@ sequenceDiagram
|
|
|
52
52
|
M->>S: Start in this MCP process on 127.0.0.1:3210
|
|
53
53
|
S-->>M: Owned server
|
|
54
54
|
M-->>D: started, URL, workspace, versions
|
|
55
|
-
else
|
|
55
|
+
else Standalone, unknown, stale, or different workspace
|
|
56
56
|
H-->>M: Missing or incompatible identity
|
|
57
57
|
M-->>D: Actionable conflict, listener untouched
|
|
58
58
|
end
|
|
@@ -62,9 +62,10 @@ sequenceDiagram
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
When the MCP transport or plugin process closes, it closes only the server it
|
|
65
|
-
started. A
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
started. A foreground `taskchef dashboard` listener identifies itself as
|
|
66
|
+
standalone and is never reused as the canonical MCP dashboard, because its
|
|
67
|
+
archive child process may inherit a different host environment. No TaskChef
|
|
68
|
+
path terminates an incompatible listener or installs OS persistence.
|
|
68
69
|
|
|
69
70
|
Autostart and explicit ensures share the same manager promise, so concurrent
|
|
70
71
|
initialization and recovery calls produce at most one owned listener. An
|
|
@@ -79,13 +80,13 @@ The practical release handoff ends in this order:
|
|
|
79
80
|
1. Install the released plugin.
|
|
80
81
|
2. Activate or reload its new TaskChef MCP process.
|
|
81
82
|
3. Run `$taskchef-dashboard` or call `ensure_dashboard`.
|
|
82
|
-
4. Verify the expected TaskChef version, protocol `serverVersion`,
|
|
83
|
-
workspace, and canonical URL returned by the dashboard identity.
|
|
83
|
+
4. Verify the expected TaskChef version, protocol `serverVersion`, `mcp`
|
|
84
|
+
launcher, canonical workspace, and canonical URL returned by the dashboard identity.
|
|
84
85
|
|
|
85
86
|
Replacing plugin files alone cannot execute autostart because old code remains
|
|
86
87
|
in the already-running MCP process. Installation does not necessarily reload
|
|
87
|
-
Codex. An exact-compatible dashboard may be reused;
|
|
88
|
-
never terminated or replaced.
|
|
88
|
+
Codex. An exact-compatible MCP dashboard may be reused; a standalone or unknown
|
|
89
|
+
listener is never terminated or replaced.
|
|
89
90
|
|
|
90
91
|
## Normal delegation and self-linking
|
|
91
92
|
|
package/package.json
CHANGED
package/src/dashboard-manager.js
CHANGED
|
@@ -14,13 +14,14 @@ const DEFAULT_HOST = "127.0.0.1";
|
|
|
14
14
|
const DEFAULT_PORT = 3210;
|
|
15
15
|
const HEALTH_TIMEOUT_MS = 750;
|
|
16
16
|
|
|
17
|
-
function expectedIdentity(workspace, taskchefVersion, serverVersion) {
|
|
17
|
+
function expectedIdentity(workspace, taskchefVersion, serverVersion, launcher) {
|
|
18
18
|
return {
|
|
19
19
|
schemaVersion: 1,
|
|
20
20
|
service: "taskchef-dashboard",
|
|
21
21
|
taskchefVersion,
|
|
22
22
|
serverVersion,
|
|
23
23
|
workspace,
|
|
24
|
+
launcher,
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -107,6 +108,7 @@ export function createDashboardManager({
|
|
|
107
108
|
port = DEFAULT_PORT,
|
|
108
109
|
taskchefVersion = TASKCHEF_VERSION,
|
|
109
110
|
serverVersion = DASHBOARD_SERVER_VERSION,
|
|
111
|
+
launcher = "mcp",
|
|
110
112
|
createServer = createDashboardServer,
|
|
111
113
|
readIdentity = readDashboardIdentity,
|
|
112
114
|
} = {}) {
|
|
@@ -117,6 +119,7 @@ export function createDashboardManager({
|
|
|
117
119
|
|
|
118
120
|
const publicResult = (action) => ({
|
|
119
121
|
action,
|
|
122
|
+
launcher,
|
|
120
123
|
url: `http://${dashboardAuthority(host, ownedServer?.port ?? port)}/`,
|
|
121
124
|
workspace: canonicalWorkspace,
|
|
122
125
|
taskchefVersion,
|
|
@@ -132,9 +135,12 @@ export function createDashboardManager({
|
|
|
132
135
|
if (listenerAbsent(error)) return false;
|
|
133
136
|
throw listenerConflict(url, `is occupied but did not return a compatible identity (${error.message}).`);
|
|
134
137
|
}
|
|
135
|
-
const expected = expectedIdentity(canonicalWorkspace, taskchefVersion, serverVersion);
|
|
138
|
+
const expected = expectedIdentity(canonicalWorkspace, taskchefVersion, serverVersion, launcher);
|
|
136
139
|
if (!isExactIdentity(identity, expected)) {
|
|
137
|
-
throw listenerConflict(
|
|
140
|
+
throw listenerConflict(
|
|
141
|
+
url,
|
|
142
|
+
"belongs to an unknown, stale, different-workspace, or differently launched service.",
|
|
143
|
+
);
|
|
138
144
|
}
|
|
139
145
|
return true;
|
|
140
146
|
};
|
|
@@ -150,6 +156,7 @@ export function createDashboardManager({
|
|
|
150
156
|
port,
|
|
151
157
|
taskchefVersion,
|
|
152
158
|
serverVersion,
|
|
159
|
+
launcher,
|
|
153
160
|
});
|
|
154
161
|
return publicResult("started");
|
|
155
162
|
} catch (error) {
|
package/src/dashboard.js
CHANGED
|
@@ -452,6 +452,7 @@ export async function createDashboardServer({
|
|
|
452
452
|
monitorOptions = {},
|
|
453
453
|
openProject = null,
|
|
454
454
|
openThread = null,
|
|
455
|
+
launcher = "standalone",
|
|
455
456
|
taskchefVersion = TASKCHEF_VERSION,
|
|
456
457
|
serverVersion = DASHBOARD_SERVER_VERSION,
|
|
457
458
|
} = {}) {
|
|
@@ -464,6 +465,9 @@ export async function createDashboardServer({
|
|
|
464
465
|
if (!Number.isInteger(maxEventClients) || maxEventClients < 0) {
|
|
465
466
|
throw new Error("dashboard event-client limit must be a non-negative integer");
|
|
466
467
|
}
|
|
468
|
+
if (!new Set(["mcp", "standalone"]).has(launcher)) {
|
|
469
|
+
throw new Error("dashboard launcher must be mcp or standalone");
|
|
470
|
+
}
|
|
467
471
|
const monitor = new DashboardMonitor(workspace, monitorOptions);
|
|
468
472
|
await monitor.start();
|
|
469
473
|
const identity = Object.freeze({
|
|
@@ -472,6 +476,7 @@ export async function createDashboardServer({
|
|
|
472
476
|
taskchefVersion,
|
|
473
477
|
serverVersion,
|
|
474
478
|
workspace: monitor.workspace,
|
|
479
|
+
launcher,
|
|
475
480
|
});
|
|
476
481
|
if (Buffer.byteLength(`${JSON.stringify(identity)}\n`) > DASHBOARD_HEALTH_MAX_BYTES) {
|
|
477
482
|
monitor.close();
|
package/src/mcp.js
CHANGED
|
@@ -88,6 +88,7 @@ const preparationSchema = z.object({
|
|
|
88
88
|
|
|
89
89
|
const dashboardSchema = z.object({
|
|
90
90
|
action: z.enum(["started", "reused"]),
|
|
91
|
+
launcher: z.literal("mcp"),
|
|
91
92
|
url: z.string().url(),
|
|
92
93
|
workspace: z.string(),
|
|
93
94
|
taskchefVersion: z.string(),
|
|
@@ -163,7 +164,7 @@ export function createTaskChefMcpServer({
|
|
|
163
164
|
return closePromise;
|
|
164
165
|
};
|
|
165
166
|
server.server.onclose = () => {
|
|
166
|
-
void dashboardManager.close();
|
|
167
|
+
void dashboardManager.close().catch(() => {});
|
|
167
168
|
};
|
|
168
169
|
|
|
169
170
|
server.registerTool(
|
|
@@ -171,7 +172,7 @@ export function createTaskChefMcpServer({
|
|
|
171
172
|
{
|
|
172
173
|
title: "Ensure TaskChef dashboard",
|
|
173
174
|
description:
|
|
174
|
-
"Best-effort ensure the canonical TaskChef dashboard is available on 127.0.0.1:3210. Starts one dashboard inside this MCP process or reuses only an exact compatible TaskChef dashboard for the same canonical workspace; unknown listeners are never terminated or replaced.",
|
|
175
|
+
"Best-effort ensure the canonical TaskChef dashboard is available on 127.0.0.1:3210. Starts one dashboard inside this MCP process or reuses only an exact compatible MCP-launched TaskChef dashboard for the same canonical workspace; standalone and unknown listeners are never terminated or replaced.",
|
|
175
176
|
inputSchema: {},
|
|
176
177
|
outputSchema: { dashboard: dashboardSchema },
|
|
177
178
|
annotations: {
|
|
@@ -325,8 +326,16 @@ export function createTaskChefMcpServer({
|
|
|
325
326
|
...(logDashboardDiagnostic ? { log: logDashboardDiagnostic } : {}),
|
|
326
327
|
});
|
|
327
328
|
server.connect = async (...args) => {
|
|
328
|
-
await
|
|
329
|
-
|
|
329
|
+
await autostartDashboard();
|
|
330
|
+
try {
|
|
331
|
+
await originalConnect(...args);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
await Promise.allSettled([
|
|
334
|
+
Promise.resolve().then(() => dashboardManager.close()),
|
|
335
|
+
Promise.resolve().then(() => originalClose()),
|
|
336
|
+
]);
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
330
339
|
};
|
|
331
340
|
|
|
332
341
|
return server;
|
package/src/version.js
CHANGED