@groundnuty/macf-channel-server 0.2.38 → 0.2.41
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/collision.d.ts +8 -0
- package/dist/collision.d.ts.map +1 -1
- package/dist/collision.js +42 -1
- package/dist/collision.js.map +1 -1
- package/dist/comms-ledger.d.ts +77 -0
- package/dist/comms-ledger.d.ts.map +1 -1
- package/dist/comms-ledger.js +133 -1
- package/dist/comms-ledger.js.map +1 -1
- package/dist/health.d.ts +106 -2
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +240 -2
- package/dist/health.js.map +1 -1
- package/dist/https.d.ts +31 -0
- package/dist/https.d.ts.map +1 -1
- package/dist/https.js +78 -3
- package/dist/https.js.map +1 -1
- package/dist/notify-formatter.d.ts.map +1 -1
- package/dist/notify-formatter.js +17 -1
- package/dist/notify-formatter.js.map +1 -1
- package/dist/notify-peer.d.ts.map +1 -1
- package/dist/notify-peer.js +20 -5
- package/dist/notify-peer.js.map +1 -1
- package/dist/registry-heartbeat.d.ts +53 -0
- package/dist/registry-heartbeat.d.ts.map +1 -0
- package/dist/registry-heartbeat.js +102 -0
- package/dist/registry-heartbeat.js.map +1 -0
- package/dist/server.js +41 -2
- package/dist/server.js.map +1 -1
- package/dist/shutdown.d.ts +45 -3
- package/dist/shutdown.d.ts.map +1 -1
- package/dist/shutdown.js +96 -12
- package/dist/shutdown.js.map +1 -1
- package/package.json +2 -2
package/dist/shutdown.js
CHANGED
|
@@ -1,26 +1,95 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registers SIGTERM and
|
|
3
|
-
* registry variable and stop the HTTPS server.
|
|
2
|
+
* Registers SIGTERM, SIGINT, and MCP-stdin-close handlers that clean up
|
|
3
|
+
* the agent's registry variable and stop the HTTPS server.
|
|
4
|
+
*
|
|
5
|
+
* The channel-server runs as the Claude TUI's MCP stdio child. A normal TUI
|
|
6
|
+
* exit (`/exit`, or SIGTERM-to-the-TUI) does NOT deliver a SIGTERM/SIGINT to
|
|
7
|
+
* this process — it only sees its stdin reach EOF as the parent goes away.
|
|
8
|
+
* So the stdin `'end'`/`'close'` wiring (macf#627) is the graceful-deregister
|
|
9
|
+
* trigger on a normal TUI exit; the signal handlers cover direct kills; the
|
|
10
|
+
* DR-031 TTL heartbeat (#589) remains the backstop for hard kills (SIGKILL /
|
|
11
|
+
* OOM / power loss) where no handler runs at all.
|
|
4
12
|
*
|
|
5
13
|
* Returns a cleanup function that can also be called directly.
|
|
6
14
|
*/
|
|
7
15
|
export function registerShutdownHandler(config) {
|
|
8
|
-
const { agentName, registry, httpsServer, logger } = config;
|
|
16
|
+
const { agentName, registry, instanceId, httpsServer, healthState, registryHeartbeat, logger } = config;
|
|
9
17
|
let shuttingDown = false;
|
|
10
18
|
let lastResult = true;
|
|
11
|
-
async function cleanup() {
|
|
19
|
+
async function cleanup(trigger) {
|
|
12
20
|
if (shuttingDown)
|
|
13
21
|
return lastResult;
|
|
14
22
|
shuttingDown = true;
|
|
15
|
-
logger.info('shutdown_start', {
|
|
23
|
+
logger.info('shutdown_start', {
|
|
24
|
+
agent: agentName,
|
|
25
|
+
...(trigger !== undefined ? { trigger } : {}),
|
|
26
|
+
});
|
|
16
27
|
let ok = true;
|
|
28
|
+
// Release the background OTLP-reachability probe interval (DR-030). Best-
|
|
29
|
+
// effort + never flips `ok`: a probe-cleanup hiccup must not mask a real
|
|
30
|
+
// deregister/stop failure.
|
|
31
|
+
try {
|
|
32
|
+
healthState?.dispose?.();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// ignore — the interval is unref()'d, so a missed clear can't pin exit
|
|
36
|
+
}
|
|
37
|
+
// Clear the registry-heartbeat interval (DR-031, groundnuty/macf#568). Same
|
|
38
|
+
// best-effort posture as the otel probe above — the interval is unref()'d, so
|
|
39
|
+
// a missed clear can't pin exit, and a hiccup must not mask a real failure.
|
|
40
|
+
try {
|
|
41
|
+
registryHeartbeat?.stop?.();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// ignore — unref()'d interval; a missed clear can't pin exit
|
|
45
|
+
}
|
|
46
|
+
// Instance-id-guarded deregister (DR-031, groundnuty/macf#553 root-cause):
|
|
47
|
+
// remove our registry slot ONLY if it is still ours. `deregisterConditional`
|
|
48
|
+
// is contracted never to throw — a registry hiccup surfaces as reason:'error'
|
|
49
|
+
// — but we still wrap it so a future contract slip can't crash shutdown.
|
|
17
50
|
try {
|
|
18
|
-
await registry.
|
|
19
|
-
|
|
51
|
+
const result = await registry.deregisterConditional(agentName, instanceId);
|
|
52
|
+
if (result.deregistered) {
|
|
53
|
+
logger.info('shutdown_deregistered', { agent: agentName, instance_id: instanceId });
|
|
54
|
+
}
|
|
55
|
+
else if (result.reason === 'not-ours') {
|
|
56
|
+
// A newer instance took over our slot (groundnuty/macf#424) while we ran.
|
|
57
|
+
// Leaving its registration intact is CORRECT — deleting it would re-
|
|
58
|
+
// introduce the missing/stale-entry bug (#553) for a live peer. Not a
|
|
59
|
+
// failure: keep `ok` true.
|
|
60
|
+
logger.info('shutdown_deregister_skipped', {
|
|
61
|
+
agent: agentName,
|
|
62
|
+
instance_id: instanceId,
|
|
63
|
+
reason: result.reason,
|
|
64
|
+
detail: 'registry slot held by a different instance — not ours to delete (DR-031, #424)',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else if (result.reason === 'absent') {
|
|
68
|
+
// Slot already gone (or unreadable) — nothing to deregister. Not a failure.
|
|
69
|
+
logger.info('shutdown_deregister_skipped', {
|
|
70
|
+
agent: agentName,
|
|
71
|
+
instance_id: instanceId,
|
|
72
|
+
reason: result.reason,
|
|
73
|
+
detail: 'registry slot already empty',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// reason === 'error': a registry read/delete failed. Surface it loud and
|
|
78
|
+
// flip `ok` so external monitors (systemd, macf-actions heartbeat) see
|
|
79
|
+
// the degraded exit rather than absorbing a possibly-stale slot into a
|
|
80
|
+
// clean exit (#103 R2). Shutdown still proceeds — fail-open, not crash.
|
|
81
|
+
logger.error('shutdown_deregister_failed', {
|
|
82
|
+
agent: agentName,
|
|
83
|
+
instance_id: instanceId,
|
|
84
|
+
reason: result.reason,
|
|
85
|
+
});
|
|
86
|
+
ok = false;
|
|
87
|
+
}
|
|
20
88
|
}
|
|
21
89
|
catch (err) {
|
|
22
90
|
logger.error('shutdown_deregister_failed', {
|
|
23
91
|
agent: agentName,
|
|
92
|
+
instance_id: instanceId,
|
|
24
93
|
error: err instanceof Error ? err.message : String(err),
|
|
25
94
|
});
|
|
26
95
|
ok = false;
|
|
@@ -42,12 +111,27 @@ export function registerShutdownHandler(config) {
|
|
|
42
111
|
}
|
|
43
112
|
// Exit 1 when any cleanup step failed so external monitors (systemd,
|
|
44
113
|
// macf-actions heartbeat) surface the degraded state instead of
|
|
45
|
-
// silently absorbing it into a clean exit (#103 R2).
|
|
46
|
-
|
|
47
|
-
|
|
114
|
+
// silently absorbing it into a clean exit (#103 R2). The `trigger` is
|
|
115
|
+
// recorded once (in `shutdown_start`) by whichever path wins the once-guard.
|
|
116
|
+
const handlerFor = (trigger) => () => {
|
|
117
|
+
cleanup(trigger).then(ok => process.exit(ok ? 0 : 1), () => process.exit(1));
|
|
48
118
|
};
|
|
49
|
-
process.on('SIGTERM',
|
|
50
|
-
process.on('SIGINT',
|
|
119
|
+
process.on('SIGTERM', handlerFor('SIGTERM'));
|
|
120
|
+
process.on('SIGINT', handlerFor('SIGINT'));
|
|
121
|
+
// macf#627: a normal Claude-TUI exit does NOT signal this MCP-stdio child —
|
|
122
|
+
// it only sees stdin reach EOF ('end') / be destroyed ('close') as the parent
|
|
123
|
+
// departs. Wire the SAME deregister handler to both events so a graceful TUI
|
|
124
|
+
// exit deregisters rather than stranding the registry slot until the DR-031
|
|
125
|
+
// TTL backstop (#589). `cleanup()`'s once-guard (set synchronously before any
|
|
126
|
+
// await) makes a stdin-close racing a SIGTERM — or 'end' then 'close' — run
|
|
127
|
+
// the deregister exactly once. The child lingers ~8s after TUI exit
|
|
128
|
+
// (devops-observed), ample for the async deregister: the in-flight `cleanup()`
|
|
129
|
+
// promise plus the still-bound HTTPS server keep the event loop alive, and we
|
|
130
|
+
// exit only from `cleanup()`'s `.then()` — never mid-deregister.
|
|
131
|
+
const stdin = config.stdin ?? process.stdin;
|
|
132
|
+
const stdinCloseHandler = handlerFor('mcp-stdin-close');
|
|
133
|
+
stdin.on('end', stdinCloseHandler);
|
|
134
|
+
stdin.on('close', stdinCloseHandler);
|
|
51
135
|
return cleanup;
|
|
52
136
|
}
|
|
53
137
|
//# sourceMappingURL=shutdown.js.map
|
package/dist/shutdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groundnuty/macf-channel-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.41",
|
|
4
4
|
"description": "MCP channel server for the Multi-Agent Coordination Framework. HTTPS + mTLS endpoint agents connect to for inter-agent messaging (notify), CI-completion routing, and /sign certificate issuance. Invoked by Claude Code's plugin via `npx -y @groundnuty/macf-channel-server` per DR-022.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@groundnuty/macf-core": "0.2.
|
|
36
|
+
"@groundnuty/macf-core": "0.2.41",
|
|
37
37
|
"@modelcontextprotocol/sdk": "~1.29.0",
|
|
38
38
|
"@opentelemetry/api": "1.9.1",
|
|
39
39
|
"@opentelemetry/core": "2.7.0",
|