@thehammer/danx-dashboard-mcp 0.1.336 → 0.1.337
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/dist/index.js +2 -5
- package/dist/plan-events-relay.js +21 -14
- package/dist/shutdown.js +16 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -88,7 +88,7 @@ import { READ_IDLE_TIMEOUT_MS } from "./listen.js";
|
|
|
88
88
|
import { REQUEST_TIMEOUT_MS, realScheduleInterval, realSleep } from "./session-stream.js";
|
|
89
89
|
import { servePlanEventsWait } from "./plan-events-tool.js";
|
|
90
90
|
import { lookupRestartNotice, RESTART_NOTICE_ARGS } from "./restart-notice.js";
|
|
91
|
-
import { stopWhenSessionEnds } from "./shutdown.js";
|
|
91
|
+
import { makeSessionStop, stopWhenSessionEnds } from "./shutdown.js";
|
|
92
92
|
import { SUBAGENTS_LIVE_SUBCOMMAND, runSubagentsLiveCommand } from "./subagents-live.js";
|
|
93
93
|
import { resolveBootCredential } from "./credential.js";
|
|
94
94
|
import { SessionKeyUnusableError, deleteSessionKey } from "./session-key.js";
|
|
@@ -868,10 +868,7 @@ async function main() {
|
|
|
868
868
|
stdin: process.stdin,
|
|
869
869
|
signals: process,
|
|
870
870
|
exit: (code) => process.exit(code),
|
|
871
|
-
stop: ()
|
|
872
|
-
permissionWatch.stop();
|
|
873
|
-
return planEventRelay.close();
|
|
874
|
-
},
|
|
871
|
+
stop: makeSessionStop(permissionWatch, planEventRelay),
|
|
875
872
|
});
|
|
876
873
|
const transport = new StdioServerTransport();
|
|
877
874
|
await server.connect(transport);
|
|
@@ -154,10 +154,11 @@ export class PlanEventRelay {
|
|
|
154
154
|
this.buffer = this.buffer.map((r) => ({ ...r, recent: boundedIds([...cursor.ids, ...r.recent]) }));
|
|
155
155
|
}
|
|
156
156
|
this.touch();
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
this.ensureReporter(args.transcriptPath);
|
|
158
|
+
// DX-5415 / DX-4233: a transcript switch's settle rides `settling` (the chain a suspend uses), so every call paused on it resumes in call order and the older one cannot overwrite the newer call's waiter.
|
|
159
|
+
if (this.settling !== null) {
|
|
160
|
+
await this.settling;
|
|
161
|
+
// The pause let a close() or a cancel through; a close already let go of the stream, heartbeat and reporter, so none is started again.
|
|
161
162
|
if (signal?.aborted || this.closed)
|
|
162
163
|
return { events: [] };
|
|
163
164
|
}
|
|
@@ -284,8 +285,12 @@ export class PlanEventRelay {
|
|
|
284
285
|
}
|
|
285
286
|
/** Creates the heartbeat reporter on the first call; a later call's transcript path (a resumed session) is handed to it. */
|
|
286
287
|
ensureReporter(transcriptPath) {
|
|
287
|
-
if (this.reporter !== null)
|
|
288
|
-
|
|
288
|
+
if (this.reporter !== null) {
|
|
289
|
+
const switching = this.reporter.useTranscript(transcriptPath);
|
|
290
|
+
if (switching !== null)
|
|
291
|
+
this.chainSettling(switching);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
289
294
|
this.reporter = new HeartbeatReporter({
|
|
290
295
|
auth: () => {
|
|
291
296
|
const auth = this.deps.auth();
|
|
@@ -302,7 +307,14 @@ export class PlanEventRelay {
|
|
|
302
307
|
announce: (text, source) => this.digest.route({ type: "event", id: null, text, origin: null, scope: "session", urgent: isUrgent({ source }) }),
|
|
303
308
|
forceReconnect: () => this.run?.forceReconnect?.(),
|
|
304
309
|
}, transcriptPath ?? null);
|
|
305
|
-
|
|
310
|
+
}
|
|
311
|
+
/** DX-5415: the one settle chain: the last settle and `stopping` both finish before it clears itself. */
|
|
312
|
+
chainSettling(stopping) {
|
|
313
|
+
const settling = Promise.all([this.settling, stopping]).then(() => {
|
|
314
|
+
if (this.settling === settling)
|
|
315
|
+
this.settling = null;
|
|
316
|
+
});
|
|
317
|
+
this.settling = settling;
|
|
306
318
|
}
|
|
307
319
|
/** Mark the relay in use and make sure the heartbeat is scheduled. */
|
|
308
320
|
touch() {
|
|
@@ -346,13 +358,8 @@ export class PlanEventRelay {
|
|
|
346
358
|
this.run = null;
|
|
347
359
|
// DX-5415: the reporter's stop settles its collector's held sub-agent finals first; callers that can wait (shutdown, the next call) do.
|
|
348
360
|
const stopping = this.reporter?.stop() ?? null;
|
|
349
|
-
if (stopping !== null)
|
|
350
|
-
|
|
351
|
-
if (this.settling === settling)
|
|
352
|
-
this.settling = null;
|
|
353
|
-
});
|
|
354
|
-
this.settling = settling;
|
|
355
|
-
}
|
|
361
|
+
if (stopping !== null)
|
|
362
|
+
this.chainSettling(stopping);
|
|
356
363
|
this.reporter = null;
|
|
357
364
|
this.digest.reset();
|
|
358
365
|
this.dropPlanScoped();
|
package/dist/shutdown.js
CHANGED
|
@@ -8,14 +8,17 @@
|
|
|
8
8
|
*
|
|
9
9
|
* DX-5415: `stop` returns the relay's bounded final settle (a sub-agent's held final report is stored before the collector stops).
|
|
10
10
|
* The first two triggers leave the process to end on its own once nothing keeps it alive, so the settle completes. A signal would
|
|
11
|
-
* kill it mid-settle, so its handler awaits `stop` and only then exits. `once
|
|
12
|
-
* default action,
|
|
11
|
+
* kill it mid-settle, so its handler awaits `stop` and only then exits. The handler is `once` per signal: a second signal of the
|
|
12
|
+
* SAME kind takes the default action, but a DIFFERENT one (SIGTERM then SIGINT) runs the handler again, which is safe because
|
|
13
|
+
* `stop` is idempotent. The settle is bounded (10 s), yet a parent that signals with an SDK client allows only about 4 s before
|
|
14
|
+
* it sends SIGKILL, so the held finals are stored best-effort within that window, not guaranteed.
|
|
13
15
|
*/
|
|
14
16
|
export function stopWhenSessionEnds(wiring) {
|
|
15
17
|
wiring.server.onclose = wiring.stop;
|
|
16
18
|
wiring.stdin.once("end", wiring.stop);
|
|
17
19
|
const exitAfterStop = () => {
|
|
18
|
-
|
|
20
|
+
// A synchronous throw from `stop` takes the same "shutdown failed" path as a rejection.
|
|
21
|
+
void Promise.resolve().then(wiring.stop).then(() => wiring.exit(0), (err) => {
|
|
19
22
|
process.stderr.write(`danx-dashboard-mcp: shutdown failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
20
23
|
wiring.exit(1);
|
|
21
24
|
});
|
|
@@ -23,3 +26,13 @@ export function stopWhenSessionEnds(wiring) {
|
|
|
23
26
|
wiring.signals.once("SIGTERM", exitAfterStop);
|
|
24
27
|
wiring.signals.once("SIGINT", exitAfterStop);
|
|
25
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* DX-5415: the server's stop callback, the one link from every shutdown trigger to the relay's settle: it returns the relay's
|
|
31
|
+
* pending `close()` so a signal handler can wait for it before exiting.
|
|
32
|
+
*/
|
|
33
|
+
export function makeSessionStop(permissionWatch, planEventRelay) {
|
|
34
|
+
return () => {
|
|
35
|
+
permissionWatch.stop();
|
|
36
|
+
return planEventRelay.close();
|
|
37
|
+
};
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thehammer/danx-dashboard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.337",
|
|
4
4
|
"description": "Stdio MCP server fronting danxbot's dashboard API for agents (operator sessions and dispatched workers alike): the generic danxbot_api/danxbot_api_list/danxbot_api_spec tools reach any route the caller's credential may call, plus dedicated agent-specific tools (plan_connect, request_permission, attach_file, and danxbot_slack_reply) (DX-3465, PLN-11 R-25).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|