@groundnuty/macf 0.2.37 → 0.2.39

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.
Files changed (82) hide show
  1. package/dist/.build-info.json +2 -2
  2. package/dist/cli/claude-sh.d.ts.map +1 -1
  3. package/dist/cli/claude-sh.js +13 -0
  4. package/dist/cli/claude-sh.js.map +1 -1
  5. package/dist/cli/commands/certs.d.ts.map +1 -1
  6. package/dist/cli/commands/certs.js +6 -2
  7. package/dist/cli/commands/certs.js.map +1 -1
  8. package/dist/cli/commands/doctor.d.ts +102 -3
  9. package/dist/cli/commands/doctor.d.ts.map +1 -1
  10. package/dist/cli/commands/doctor.js +349 -55
  11. package/dist/cli/commands/doctor.js.map +1 -1
  12. package/dist/cli/commands/fleet-doctor-inject.d.ts +52 -0
  13. package/dist/cli/commands/fleet-doctor-inject.d.ts.map +1 -0
  14. package/dist/cli/commands/fleet-doctor-inject.js +100 -0
  15. package/dist/cli/commands/fleet-doctor-inject.js.map +1 -0
  16. package/dist/cli/commands/fleet-doctor.d.ts +236 -0
  17. package/dist/cli/commands/fleet-doctor.d.ts.map +1 -0
  18. package/dist/cli/commands/fleet-doctor.js +481 -0
  19. package/dist/cli/commands/fleet-doctor.js.map +1 -0
  20. package/dist/cli/commands/fleet.d.ts +83 -0
  21. package/dist/cli/commands/fleet.d.ts.map +1 -0
  22. package/dist/cli/commands/fleet.js +225 -0
  23. package/dist/cli/commands/fleet.js.map +1 -0
  24. package/dist/cli/commands/init.d.ts +24 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +79 -8
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/migrate.d.ts +1 -0
  29. package/dist/cli/commands/migrate.d.ts.map +1 -1
  30. package/dist/cli/commands/ps.d.ts +17 -0
  31. package/dist/cli/commands/ps.d.ts.map +1 -0
  32. package/dist/cli/commands/ps.js +69 -0
  33. package/dist/cli/commands/ps.js.map +1 -0
  34. package/dist/cli/commands/registry-prune.d.ts +81 -0
  35. package/dist/cli/commands/registry-prune.d.ts.map +1 -0
  36. package/dist/cli/commands/registry-prune.js +163 -0
  37. package/dist/cli/commands/registry-prune.js.map +1 -0
  38. package/dist/cli/commands/restart-self.d.ts +111 -0
  39. package/dist/cli/commands/restart-self.d.ts.map +1 -0
  40. package/dist/cli/commands/restart-self.js +312 -0
  41. package/dist/cli/commands/restart-self.js.map +1 -0
  42. package/dist/cli/commands/routing-doctor-gh.d.ts +29 -0
  43. package/dist/cli/commands/routing-doctor-gh.d.ts.map +1 -0
  44. package/dist/cli/commands/routing-doctor-gh.js +103 -0
  45. package/dist/cli/commands/routing-doctor-gh.js.map +1 -0
  46. package/dist/cli/commands/routing-doctor.d.ts +183 -0
  47. package/dist/cli/commands/routing-doctor.d.ts.map +1 -0
  48. package/dist/cli/commands/routing-doctor.js +504 -0
  49. package/dist/cli/commands/routing-doctor.js.map +1 -0
  50. package/dist/cli/commands/update.d.ts.map +1 -1
  51. package/dist/cli/commands/update.js +9 -0
  52. package/dist/cli/commands/update.js.map +1 -1
  53. package/dist/cli/config.d.ts +2 -0
  54. package/dist/cli/config.d.ts.map +1 -1
  55. package/dist/cli/config.js +16 -0
  56. package/dist/cli/config.js.map +1 -1
  57. package/dist/cli/env-files.d.ts.map +1 -1
  58. package/dist/cli/env-files.js +11 -0
  59. package/dist/cli/env-files.js.map +1 -1
  60. package/dist/cli/host-prelude.d.ts +50 -0
  61. package/dist/cli/host-prelude.d.ts.map +1 -0
  62. package/dist/cli/host-prelude.js +256 -0
  63. package/dist/cli/host-prelude.js.map +1 -0
  64. package/dist/cli/index.js +122 -5
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/proc-scan.d.ts +81 -0
  67. package/dist/cli/proc-scan.d.ts.map +1 -0
  68. package/dist/cli/proc-scan.js +172 -0
  69. package/dist/cli/proc-scan.js.map +1 -0
  70. package/dist/cli/role-settings-model.d.ts +70 -0
  71. package/dist/cli/role-settings-model.d.ts.map +1 -0
  72. package/dist/cli/role-settings-model.js +90 -0
  73. package/dist/cli/role-settings-model.js.map +1 -0
  74. package/dist/cli/settings-writer.d.ts +27 -0
  75. package/dist/cli/settings-writer.d.ts.map +1 -1
  76. package/dist/cli/settings-writer.js +144 -2
  77. package/dist/cli/settings-writer.js.map +1 -1
  78. package/package.json +2 -2
  79. package/plugin/rules/coordination.md +10 -0
  80. package/plugin/rules/silent-fallback-hazards.md +19 -4
  81. package/scripts/check-gh-attribution.sh +34 -10
  82. package/scripts/emit-turn-receipt.sh +44 -4
@@ -0,0 +1,481 @@
1
+ /**
2
+ * `macf fleet doctor` — NON-INVASIVE mesh-interconnect test (DR-030 phase-1
3
+ * increment 1d, macf#568).
4
+ *
5
+ * For the current project's registry, run a two-tier delivery ladder per agent:
6
+ *
7
+ * 1. REACHABLE — the agent's registry `host:port` answers an mTLS `/health`
8
+ * ping (reuses `pingAgentHealth`, the same probe `macf fleet status` uses).
9
+ * 2. ACCEPTED — an mTLS `/notify` POST carrying a DIAGNOSTIC payload is ACK'd
10
+ * by the server's receive path. The diagnostic body short-circuits to an
11
+ * ACK BEFORE any MCP push, and the ACK echoes back the correlation token we
12
+ * sent. "Accepted" is green iff HTTP 200 AND `ack === true` AND the echoed
13
+ * `correlation_token` matches what we sent (the echo proves a real
14
+ * round-trip, not a coincidental 200).
15
+ *
16
+ * HONESTY (DR-030 §3, rendered LOUDLY in the output legend): the default NON-
17
+ * invasive checks prove the protocol REACHES THE SERVER (mTLS auth + body parse)
18
+ * ONLY — they do NOT prove DELIVERY TO THE AGENT (the MCP-push → agent-reads gap
19
+ * is not exercised). Real deliver→process proof needs `--inject` (the INVASIVE
20
+ * Processed-now tier in this file — routes a real marker-bearing prompt + wakes
21
+ * each agent + matches the echoed run_id off `/health.last_processed`);
22
+ * routing-plane delivery needs an e2e run.
23
+ *
24
+ * Reuses `macf fleet status`'s plumbing: the registry list
25
+ * (`createRegistryFromConfig`), the mTLS `/health` probe (`pingAgentHealth`),
26
+ * the CA-cert read (`createClientFromConfig`), and the `formatTable` renderer
27
+ * (shared with `macf ps` / `macf fleet status`). The diagnostic POST mirrors
28
+ * `pingAgentHealth`'s mTLS request shape. Registry + both mTLS calls are
29
+ * injectable (`FleetDoctorDeps`) so tests run fully offline.
30
+ */
31
+ import { randomUUID } from 'node:crypto';
32
+ import { request } from 'node:https';
33
+ import { readFileSync, existsSync } from 'node:fs';
34
+ import { readAgentConfig, tokenSourceFromConfig, agentCertPath, agentKeyPath, } from '../config.js';
35
+ import { createClientFromConfig } from '../registry-helper.js';
36
+ import { createRegistryFromConfig, generateToken, pingAgentHealth, toVariableSegment, } from '@groundnuty/macf-core';
37
+ import { formatTable } from './ps.js';
38
+ import { postInjectNotify, pollLastProcessedToken, } from './fleet-doctor-inject.js';
39
+ const DEFAULT_TIMEOUT_MS = 5000;
40
+ const DEFAULT_INJECT_MAX_POLLS = 7;
41
+ const DEFAULT_INJECT_POLL_INTERVAL_MS = 4000;
42
+ /** Default digits-only run id: epoch-ms + 6 random digits → unique, `[0-9]+`. */
43
+ export function defaultRunId() {
44
+ return `${Date.now()}${Math.floor(Math.random() * 1_000_000)}`;
45
+ }
46
+ /**
47
+ * Sanitize a registry name into a marker-legal agent slug. The
48
+ * `emit-turn-receipt.sh` grep is `\[macf-route:[0-9]+:[a-z0-9-]+\]`, so an
49
+ * UNsanitized label with an underscore/uppercase would fail the whole-marker
50
+ * match → no receipt written → a false UNCONFIRMED. Lowercase + map any
51
+ * out-of-class char to `-`; fall back to `agent` if the result is empty.
52
+ */
53
+ export function sanitizeMarkerAgent(name) {
54
+ const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
55
+ return slug === '' ? 'agent' : slug;
56
+ }
57
+ /**
58
+ * Accepted = HTTP 200 AND `ack === true` AND the echoed `correlation_token`
59
+ * matches what we sent. The token echo is the load-bearing check — it proves
60
+ * the 200 is a real round-trip of OUR request, not a coincidental success.
61
+ */
62
+ export function isAccepted(sentToken, ack) {
63
+ return ack.status === 200 && ack.body?.ack === true && ack.body?.correlation_token === sentToken;
64
+ }
65
+ /** Human-readable reason the Accepted tier failed (used in `--json`/diagnostics). */
66
+ export function acceptFailureReason(sentToken, ack) {
67
+ if (ack.error)
68
+ return ack.error;
69
+ if (ack.status === null)
70
+ return 'no response';
71
+ if (ack.status !== 200)
72
+ return `http ${ack.status}`;
73
+ if (ack.body?.ack !== true)
74
+ return 'ack not true';
75
+ if (ack.body?.correlation_token !== sentToken)
76
+ return 'correlation_token mismatch';
77
+ return 'unknown';
78
+ }
79
+ /**
80
+ * Run the Processed-now (`--inject`) tier for ONE reachable agent: route a real
81
+ * marker-bearing mention, then poll `/health.last_processed.correlation_token`
82
+ * for our `run_id`. Returns the tri-state outcome + diagnostics. PURE w.r.t.
83
+ * `inject.post`/`inject.poll`/`inject.sleep` — tests inject fakes (and a no-op
84
+ * sleep) so nothing hits the network and there are no real waits.
85
+ */
86
+ export async function runProcessedInject(peer, ackAgent, inject) {
87
+ const { host, port } = peer.info;
88
+ const runId = (inject.genRunId ?? defaultRunId)();
89
+ const markerAgent = sanitizeMarkerAgent(ackAgent ?? peer.name);
90
+ const post = await inject.post(host, port, runId, markerAgent);
91
+ if (!post.delivered) {
92
+ return { processedInject: false, injectRunId: runId, injectError: post.error ?? 'inject POST not delivered' };
93
+ }
94
+ const maxPolls = inject.maxPolls ?? DEFAULT_INJECT_MAX_POLLS;
95
+ const intervalMs = inject.pollIntervalMs ?? DEFAULT_INJECT_POLL_INTERVAL_MS;
96
+ const sleep = inject.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
97
+ for (let i = 0; i < maxPolls; i++) {
98
+ const token = await inject.poll(host, port);
99
+ if (token === runId)
100
+ return { processedInject: true, injectRunId: runId };
101
+ if (i < maxPolls - 1)
102
+ await sleep(intervalMs);
103
+ }
104
+ return {
105
+ processedInject: false,
106
+ injectRunId: runId,
107
+ injectError: `processing unconfirmed within ~${Math.round(((maxPolls - 1) * intervalMs) / 1000)}s`,
108
+ };
109
+ }
110
+ /**
111
+ * Run the ladder per peer: probe `/health`, then (only if reachable) POST the
112
+ * diagnostic `/notify`. When `inject` is supplied (`--inject`), ALSO run the
113
+ * INVASIVE Processed-now tier on each reachable agent (routes a real prompt +
114
+ * wakes the agent). PURE w.r.t. `probe`/`diagnose`/`genToken`/`inject` — tests
115
+ * inject fakes so nothing hits the network.
116
+ */
117
+ export async function gatherFleetDoctor(peers, probe, diagnose, genToken = randomUUID, inject) {
118
+ const out = [];
119
+ for (const peer of peers) {
120
+ const { host, port } = peer.info;
121
+ const health = await probe(host, port);
122
+ if (health === null) {
123
+ // Tier 1 failed → ladder stops; Accepted + Processed are N/A (not attempted).
124
+ out.push({
125
+ name: peer.name,
126
+ host,
127
+ port,
128
+ reachable: false,
129
+ accepted: null,
130
+ processedInject: inject ? null : undefined,
131
+ });
132
+ continue;
133
+ }
134
+ const token = genToken();
135
+ const ack = await diagnose(host, port, token);
136
+ const accepted = isAccepted(token, ack);
137
+ const ackAgent = ack.body?.agent;
138
+ const proc = inject ? await runProcessedInject(peer, ackAgent, inject) : undefined;
139
+ out.push({
140
+ name: peer.name,
141
+ host,
142
+ port,
143
+ reachable: true,
144
+ accepted,
145
+ ackAgent,
146
+ instanceId: ack.body?.instance_id,
147
+ acceptError: accepted ? undefined : acceptFailureReason(token, ack),
148
+ processedInject: proc ? proc.processedInject : undefined,
149
+ injectRunId: proc?.injectRunId,
150
+ injectError: proc?.injectError,
151
+ });
152
+ }
153
+ return out;
154
+ }
155
+ const HEADERS = ['NAME', 'HOST:PORT', 'REACHABLE', 'ACCEPTED'];
156
+ const HEADERS_INJECT = [...HEADERS, 'PROCESSED'];
157
+ /** ✓ / ✗ for a boolean reachability flag. */
158
+ export function reachableGlyph(reachable) {
159
+ return reachable ? '✓' : '✗';
160
+ }
161
+ /** ✓ / ✗ / — for the tri-state Accepted (null = not attempted). */
162
+ export function acceptedGlyph(accepted) {
163
+ if (accepted === null)
164
+ return '—';
165
+ return accepted ? '✓' : '✗';
166
+ }
167
+ /**
168
+ * Processed glyph (inject tier). ✓ = run_id echoed (processed end-to-end);
169
+ * `?` = UNCONFIRMED within the window (delivered but not echoed — possibly a
170
+ * busy agent, NOT necessarily a gap — deliberately NOT `✗`); `—` = not
171
+ * attempted (unreachable / inject off).
172
+ */
173
+ export function processedGlyph(processed) {
174
+ if (processed === true)
175
+ return '✓';
176
+ if (processed === false)
177
+ return '?';
178
+ return '—';
179
+ }
180
+ /**
181
+ * Build one display row per agent (pure — exported for tests). When `inject`
182
+ * is true, append the PROCESSED column.
183
+ */
184
+ export function buildDoctorRows(results, inject = false) {
185
+ return results.map((r) => {
186
+ const base = [r.name, `${r.host}:${r.port}`, reachableGlyph(r.reachable), acceptedGlyph(r.accepted)];
187
+ return inject ? [...base, processedGlyph(r.processedInject)] : base;
188
+ });
189
+ }
190
+ /** Full rendered table (pure — exported for tests). */
191
+ export function formatDoctorTable(results, inject = false) {
192
+ return formatTable(inject ? HEADERS_INJECT : HEADERS, buildDoctorRows(results, inject));
193
+ }
194
+ /** Reachable AND Accepted both green. */
195
+ function isFullyOk(r) {
196
+ return r.reachable && r.accepted === true;
197
+ }
198
+ export function meshVerdict(results) {
199
+ if (results.length === 0)
200
+ return 'EMPTY';
201
+ return results.every(isFullyOk) ? 'HEALTHY' : 'DEGRADED';
202
+ }
203
+ /** `3/4 agents reachable + accepting; mesh interconnect: DEGRADED`. */
204
+ export function summaryLine(results) {
205
+ const total = results.length;
206
+ const ok = results.filter(isFullyOk).length;
207
+ return `${ok}/${total} agents reachable + accepting; mesh interconnect: ${meshVerdict(results)}`;
208
+ }
209
+ /**
210
+ * The honesty footnote — non-invasive checks prove protocol-to-server ONLY.
211
+ * Kept as a single block so it renders identically in the table output and is
212
+ * carried verbatim in the `--json` `disclaimer` field.
213
+ */
214
+ export const HONESTY_LEGEND = [
215
+ 'Legend: REACHABLE = mTLS /health answered. ACCEPTED = diagnostic /notify ACK (HTTP 200 + ack + token echo).',
216
+ 'NOTE: these non-invasive checks prove the protocol REACHES THE SERVER (mTLS auth + parse) ONLY —',
217
+ ' NOT delivery to the agent (the MCP-push → agent-reads gap is not exercised). Actual mesh',
218
+ ' delivery needs `macf fleet doctor --inject` (INVASIVE — wakes each agent); routing-plane',
219
+ ' delivery needs an e2e run.',
220
+ ].join('\n');
221
+ const HONESTY_DISCLAIMER = 'Non-invasive checks prove protocol-to-server (mTLS auth + parse) ONLY, NOT delivery to the ' +
222
+ 'agent (MCP-push → agent-reads gap). Mesh delivery needs --inject (later increment); ' +
223
+ 'routing-plane delivery needs e2e.';
224
+ /**
225
+ * The `--inject` Processed-tier legend. INVASIVE: it routes a real probe to each
226
+ * reachable agent + costs it a turn. A `?` (UNCONFIRMED) is NOT "deaf" — a busy
227
+ * agent mid-long-turn legitimately won't process the probe within the window.
228
+ */
229
+ export const INJECT_LEGEND = [
230
+ 'Legend (--inject): PROCESSED ✓ = the probe run_id echoed back via /health.last_processed —',
231
+ ' the FULL chain proven (deliver → MCP push → agent read it as a TURN → local turn-receipt',
232
+ ' → /health surfaced it). ? = UNCONFIRMED within the window (delivered, but no echo yet —',
233
+ ' a busy agent mid-long-turn legitimately may not have processed it; NOT proof of a gap).',
234
+ ' — = not attempted (agent unreachable).',
235
+ 'INVASIVE: --inject routes a REAL /notify to each reachable agent, WAKING each + costing it a turn.',
236
+ ' It is the IDLE-agent fallback; for a busy/active agent prefer the passive last_processed self-report.',
237
+ ].join('\n');
238
+ /** `2/3 reachable agents processed an injected probe (1 unconfirmed — possibly busy)`. */
239
+ export function injectSummaryLine(results) {
240
+ const attempted = results.filter((r) => r.processedInject !== null && r.processedInject !== undefined);
241
+ const processed = attempted.filter((r) => r.processedInject === true).length;
242
+ const unconfirmed = attempted.length - processed;
243
+ const tail = unconfirmed > 0 ? ` (${unconfirmed} unconfirmed — possibly busy, NOT necessarily a gap)` : '';
244
+ return `${processed}/${attempted.length} reachable agents processed an injected probe${tail}`;
245
+ }
246
+ /** Count of reachable agents an `--inject` run will WAKE (for the up-front warning). */
247
+ export function injectableCount(results) {
248
+ return results.filter((r) => r.reachable).length;
249
+ }
250
+ /**
251
+ * Structured `--json` shape for automation. THIS IS THE INPUT CONTRACT the
252
+ * DR-031 watchdog consumes — keep it clean + stable. `accepted` is tri-state
253
+ * (`true`/`false`/`null`); `summary.verdict` is the machine-readable health.
254
+ *
255
+ * `schema_version` is the HARD version contract (DR-006 watchdog request,
256
+ * macf-devops-toolkit#115): a consumer asserts `schema_version === <known>`
257
+ * and refuses an unknown value, so it fails LOUD on ANY breaking change —
258
+ * not just a renamed key (which a presence-check catches) but a same-name
259
+ * SEMANTIC change (e.g. `accepted` going tri-state-bool → string-enum) that
260
+ * would otherwise parse clean and silently misread (the Instance-13-adjacent
261
+ * silent-fallback at the supervisor's own input). BUMP this on any breaking
262
+ * change (rename / removal / semantic shift); additive-optional fields do NOT
263
+ * bump it.
264
+ */
265
+ export const FLEET_DOCTOR_JSON_SCHEMA_VERSION = 1;
266
+ /**
267
+ * `--inject` ADDS `processed_inject` (+ `inject_run_id` / `inject_error` /
268
+ * top-level `inject`) WITHOUT a `schema_version` bump. Per the documented policy
269
+ * above, additive-OPTIONAL fields do NOT bump the version (a bump is reserved
270
+ * for rename / removal / a same-name SEMANTIC shift). `processed_inject` is
271
+ * present ONLY when `--inject` ran (`opts.inject`); a watchdog that pins
272
+ * `schema_version === 1` keeps parsing clean and simply ignores the new key —
273
+ * exactly the additive-tolerance the version contract is built for.
274
+ */
275
+ export function fleetDoctorToJson(results, project, opts = {}) {
276
+ return {
277
+ schema_version: FLEET_DOCTOR_JSON_SCHEMA_VERSION,
278
+ project: project ?? null,
279
+ ...(opts.inject
280
+ ? {
281
+ // Surfaced ONLY in inject mode so a consumer never misreads a
282
+ // mode-off run as "0 processed". Loudly flags the invasive nature.
283
+ inject: {
284
+ invasive: true,
285
+ woke: injectableCount(results),
286
+ processed: results.filter((r) => r.processedInject === true).length,
287
+ },
288
+ }
289
+ : {}),
290
+ summary: {
291
+ total: results.length,
292
+ reachable: results.filter((r) => r.reachable).length,
293
+ accepting: results.filter(isFullyOk).length,
294
+ verdict: meshVerdict(results),
295
+ },
296
+ agents: results.map((r) => ({
297
+ name: r.name,
298
+ host: r.host,
299
+ port: r.port,
300
+ reachable: r.reachable,
301
+ accepted: r.accepted,
302
+ ack_agent: r.ackAgent ?? null,
303
+ instance_id: r.instanceId ?? null,
304
+ accept_error: r.acceptError ?? null,
305
+ // Additive-optional (no schema bump): tri-state true/false/null, present
306
+ // only under --inject. null = not attempted (unreachable).
307
+ ...(opts.inject
308
+ ? {
309
+ processed_inject: r.processedInject ?? null,
310
+ inject_run_id: r.injectRunId ?? null,
311
+ inject_error: r.injectError ?? null,
312
+ }
313
+ : {}),
314
+ })),
315
+ disclaimer: HONESTY_DISCLAIMER,
316
+ };
317
+ }
318
+ /**
319
+ * The real mTLS diagnostic POST. Mirrors `pingAgentHealth`'s request shape
320
+ * (same CA + client cert/key, `rejectUnauthorized`, timeout) but POSTs the
321
+ * diagnostic body to `/notify`. Resolves with a `DiagnosticAck` describing the
322
+ * outcome; never throws (failures become `{ status: null, error }`).
323
+ */
324
+ export async function postDiagnosticNotify(config) {
325
+ const { host, port, caCertPem, certPath, keyPath, correlationToken, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
326
+ if (!existsSync(certPath) || !existsSync(keyPath)) {
327
+ return { status: null, body: null, error: 'client cert/key missing' };
328
+ }
329
+ const payload = JSON.stringify({
330
+ type: 'mention',
331
+ diagnostic: true,
332
+ correlation_token: correlationToken,
333
+ });
334
+ return new Promise((resolve) => {
335
+ const req = request({
336
+ hostname: host,
337
+ port,
338
+ method: 'POST',
339
+ path: '/notify',
340
+ ca: Buffer.from(caCertPem),
341
+ cert: readFileSync(certPath),
342
+ key: readFileSync(keyPath),
343
+ rejectUnauthorized: true,
344
+ timeout: timeoutMs,
345
+ headers: {
346
+ 'content-type': 'application/json',
347
+ 'content-length': Buffer.byteLength(payload),
348
+ },
349
+ }, (res) => {
350
+ const chunks = [];
351
+ res.on('data', (chunk) => chunks.push(chunk));
352
+ res.on('end', () => {
353
+ const status = res.statusCode ?? null;
354
+ let body;
355
+ try {
356
+ body = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
357
+ }
358
+ catch {
359
+ body = null;
360
+ }
361
+ resolve({ status, body, error: body ? undefined : 'non-JSON response' });
362
+ });
363
+ });
364
+ req.on('error', (e) => resolve({ status: null, body: null, error: e.message }));
365
+ req.on('timeout', () => {
366
+ req.destroy();
367
+ resolve({ status: null, body: null, error: 'timeout' });
368
+ });
369
+ req.write(payload);
370
+ req.end();
371
+ });
372
+ }
373
+ /** Wire the registry + both mTLS calls from a project's config. */
374
+ async function resolveDepsFromRegistry(projectDir) {
375
+ const config = readAgentConfig(projectDir);
376
+ if (!config) {
377
+ console.error('No macf-agent.json found. Run `macf init` first.');
378
+ return { ok: false, code: 1 };
379
+ }
380
+ const token = await generateToken(tokenSourceFromConfig(projectDir, config));
381
+ const registry = createRegistryFromConfig(config.registry, config.project, token);
382
+ const client = createClientFromConfig(config.registry, token);
383
+ const caCertPem = await client.readVariable(`${toVariableSegment(config.project)}_CA_CERT`);
384
+ if (!caCertPem) {
385
+ console.error('CA certificate not found in registry. Run `macf certs init` first.');
386
+ return { ok: false, code: 1 };
387
+ }
388
+ const certPath = agentCertPath(projectDir);
389
+ const keyPath = agentKeyPath(projectDir);
390
+ return {
391
+ ok: true,
392
+ deps: {
393
+ project: config.project,
394
+ listPeers: () => registry.list(''),
395
+ probe: (host, port) => pingAgentHealth({ host, port, caCertPem, certPath, keyPath }),
396
+ diagnose: (host, port, correlationToken) => postDiagnosticNotify({ host, port, caCertPem, certPath, keyPath, correlationToken }),
397
+ injectPost: (host, port, runId, markerAgent) => postInjectNotify({ host, port, caCertPem, certPath, keyPath, runId, markerAgent }),
398
+ injectPoll: (host, port) => pollLastProcessedToken({ host, port, caCertPem, certPath, keyPath }),
399
+ },
400
+ };
401
+ }
402
+ /**
403
+ * Build the `FleetInjectConfig` from deps + options when `--inject` is on.
404
+ * Returns `undefined` when inject is off OR the deps lack the inject seams
405
+ * (defensive — production always wires them). A `~4s` poll interval; the
406
+ * timeout-seconds budget derives `maxPolls`.
407
+ */
408
+ function buildInjectConfig(deps, opts) {
409
+ if (!opts.inject)
410
+ return undefined;
411
+ if (!deps.injectPost || !deps.injectPoll)
412
+ return undefined;
413
+ const intervalMs = DEFAULT_INJECT_POLL_INTERVAL_MS;
414
+ const budgetSec = opts.injectTimeoutSec ?? 24;
415
+ // maxPolls so that (maxPolls-1)*interval ≈ budget; floor at 2 polls.
416
+ const maxPolls = Math.max(2, Math.round((budgetSec * 1000) / intervalMs) + 1);
417
+ return {
418
+ post: deps.injectPost,
419
+ poll: deps.injectPoll,
420
+ genRunId: deps.injectGenRunId,
421
+ sleep: deps.injectSleep,
422
+ maxPolls,
423
+ pollIntervalMs: intervalMs,
424
+ };
425
+ }
426
+ /**
427
+ * `macf fleet doctor` entry point. Returns the shell exit code — 1 when the
428
+ * mesh is DEGRADED (any agent unreachable or not-accepting), 0 when HEALTHY or
429
+ * when no agents are registered (matching the `macf doctor` "non-zero on
430
+ * problem" convention). The `--json` body still prints to stdout regardless of
431
+ * exit code; the watchdog reads `summary.verdict` from it. `deps` is injected
432
+ * by tests; production resolves it from the project's registry config.
433
+ */
434
+ export async function runFleetDoctor(projectDir, opts = {}, deps) {
435
+ let resolved = deps;
436
+ if (!resolved) {
437
+ const r = await resolveDepsFromRegistry(projectDir);
438
+ if (!r.ok)
439
+ return r.code;
440
+ resolved = r.deps;
441
+ }
442
+ const inject = buildInjectConfig(resolved, opts);
443
+ const peers = await resolved.listPeers();
444
+ // Loud up-front INVASIVE warning (DR-030 §3) to stderr BEFORE we route any
445
+ // real probe. Skipped under --json (machine consumer) + when no agents exist.
446
+ // The exact reachable count is reported post-gather (we'd otherwise double-
447
+ // probe every agent just to count).
448
+ if (inject && !opts.json && peers.length > 0) {
449
+ console.error(`⚠️ --inject is INVASIVE: it routes a REAL /notify to each REACHABLE agent ` +
450
+ `(up to ${String(peers.length)} registered), WAKING it + costing it a turn. ` +
451
+ `IDLE-agent fallback — prefer the passive last_processed self-report for a busy agent.`);
452
+ }
453
+ const results = await gatherFleetDoctor(peers, resolved.probe, resolved.diagnose, resolved.genToken, inject);
454
+ if (opts.json) {
455
+ console.log(JSON.stringify(fleetDoctorToJson(results, resolved.project, { inject: !!inject }), null, 2));
456
+ return meshVerdict(results) === 'DEGRADED' ? 1 : 0;
457
+ }
458
+ const header = `macf fleet doctor${resolved.project ? ` — ${resolved.project}` : ''}`;
459
+ if (results.length === 0) {
460
+ console.log(`${header}\n\nNo agents registered in the registry.`);
461
+ return 0;
462
+ }
463
+ console.log(`${header}\n`);
464
+ console.log(formatDoctorTable(results, !!inject));
465
+ console.log('');
466
+ console.log(summaryLine(results));
467
+ if (inject) {
468
+ console.log(`--inject routed a REAL probe to ${String(injectableCount(results))} reachable agent(s), waking each.`);
469
+ console.log(injectSummaryLine(results));
470
+ }
471
+ console.log('');
472
+ console.log(HONESTY_LEGEND);
473
+ if (inject) {
474
+ console.log('');
475
+ console.log(INJECT_LEGEND);
476
+ }
477
+ // The Processed tier does NOT fold into the verdict/exit code: an UNCONFIRMED
478
+ // is "possibly busy," not "broken." Exit stays driven by Reachable+Accepted.
479
+ return meshVerdict(results) === 'DEGRADED' ? 1 : 0;
480
+ }
481
+ //# sourceMappingURL=fleet-doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet-doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/fleet-doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import type { AgentInfo, HealthResponse } from '@groundnuty/macf-core';
2
+ /**
3
+ * DR-030 sibling-increment `/health.state` object self-report — read
4
+ * DEFENSIVELY (NOT yet in the `HealthResponse` type). All fields optional; the
5
+ * field may also arrive as a plain `"idle"|"busy"` string (DR-030 §5).
6
+ */
7
+ export interface AgentRunState {
8
+ readonly status?: string;
9
+ readonly turn_number?: number;
10
+ readonly elapsed_ms?: number;
11
+ }
12
+ /** DR-030 sibling-increment `/health.otel` self-report — read DEFENSIVELY. */
13
+ export interface AgentOtelReport {
14
+ readonly endpoint_reachable?: boolean;
15
+ }
16
+ /** One agent's roster + reachability + raw self-report body. */
17
+ export interface FleetAgentStatus {
18
+ readonly name: string;
19
+ readonly host: string;
20
+ readonly port: number;
21
+ readonly online: boolean;
22
+ /** Raw `/health` body (incl. any defensively-read `state`/`otel`), or null when offline. */
23
+ readonly health: HealthResponse | null;
24
+ }
25
+ /** Probe a single endpoint's `/health`; null on any failure. Injectable for tests. */
26
+ export type FleetProbeFn = (host: string, port: number) => Promise<HealthResponse | null>;
27
+ /**
28
+ * Probe every peer once and collect roster + reachability + raw body. PURE
29
+ * w.r.t. `probe` — tests inject a fake so nothing hits the network.
30
+ */
31
+ export declare function gatherFleetStatus(peers: readonly {
32
+ readonly name: string;
33
+ readonly info: AgentInfo;
34
+ }[], probe: FleetProbeFn): Promise<readonly FleetAgentStatus[]>;
35
+ /** Human-readable elapsed from whole seconds: `45s` / `18m` / `2h5m`. */
36
+ export declare function formatUptime(seconds: number): string;
37
+ /** Whole days from `now` (ms) until an ISO timestamp; negative when past. */
38
+ export declare function daysUntil(isoDate: string, now: number): number;
39
+ /**
40
+ * Render `cert_expiry` with a severity marker: crit (`✗`) when <7d / already
41
+ * expired, warn (`⚠`) when <30d. An expired leaf = silent off-channels
42
+ * (DR-030 §5), so it earns the loudest marker.
43
+ */
44
+ export declare function formatCertExpiry(certExpiry: string | null | undefined, now: number): string;
45
+ /**
46
+ * Render the `state` self-report. Tolerates the field being absent, a plain
47
+ * `"idle"|"busy"` string (DR-030 §5), or a `{ status, turn_number, elapsed_ms }`
48
+ * object (rendered like `busy 18m on turn 7`).
49
+ */
50
+ export declare function formatRunState(raw: unknown): string;
51
+ /** Render the `otel` self-report's `endpoint_reachable` flag. */
52
+ export declare function formatOtel(raw: unknown): string;
53
+ /** Build one display row per agent (pure — exported for tests). */
54
+ export declare function buildFleetRows(statuses: readonly FleetAgentStatus[], now: number): readonly (readonly string[])[];
55
+ /** Full rendered table for a fleet status list (pure — exported for tests). */
56
+ export declare function formatFleetTable(statuses: readonly FleetAgentStatus[], now: number): string;
57
+ /**
58
+ * Structured `--json` shape for automation. Carries the RAW `/health` body so
59
+ * any present `state`/`otel`/future fields pass through untouched.
60
+ */
61
+ export declare function fleetStatusToJson(statuses: readonly FleetAgentStatus[]): unknown;
62
+ /** Options for `runFleetStatus`. */
63
+ export interface RunFleetStatusOptions {
64
+ /** Emit the structured data as JSON instead of a table. */
65
+ readonly json?: boolean;
66
+ /** Clock for cert-expiry math (defaults to `Date.now()`; injected in tests). */
67
+ readonly now?: number;
68
+ }
69
+ /** Injectable seam so tests drive the command without touching the registry/network. */
70
+ export interface FleetStatusDeps {
71
+ readonly listPeers: () => Promise<readonly {
72
+ readonly name: string;
73
+ readonly info: AgentInfo;
74
+ }[]>;
75
+ readonly probe: FleetProbeFn;
76
+ readonly project?: string;
77
+ }
78
+ /**
79
+ * `macf fleet status` entry point. Returns the shell exit code. `deps` is
80
+ * injected by tests; production resolves it from the project's registry config.
81
+ */
82
+ export declare function runFleetStatus(projectDir: string, opts?: RunFleetStatusOptions, deps?: FleetStatusDeps): Promise<number>;
83
+ //# sourceMappingURL=fleet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/fleet.ts"],"names":[],"mappings":"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"}