@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.
- package/dist/.build-info.json +2 -2
- package/dist/cli/claude-sh.d.ts.map +1 -1
- package/dist/cli/claude-sh.js +13 -0
- package/dist/cli/claude-sh.js.map +1 -1
- package/dist/cli/commands/certs.d.ts.map +1 -1
- package/dist/cli/commands/certs.js +6 -2
- package/dist/cli/commands/certs.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +102 -3
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +349 -55
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/fleet-doctor-inject.d.ts +52 -0
- package/dist/cli/commands/fleet-doctor-inject.d.ts.map +1 -0
- package/dist/cli/commands/fleet-doctor-inject.js +100 -0
- package/dist/cli/commands/fleet-doctor-inject.js.map +1 -0
- package/dist/cli/commands/fleet-doctor.d.ts +236 -0
- package/dist/cli/commands/fleet-doctor.d.ts.map +1 -0
- package/dist/cli/commands/fleet-doctor.js +481 -0
- package/dist/cli/commands/fleet-doctor.js.map +1 -0
- package/dist/cli/commands/fleet.d.ts +83 -0
- package/dist/cli/commands/fleet.d.ts.map +1 -0
- package/dist/cli/commands/fleet.js +225 -0
- package/dist/cli/commands/fleet.js.map +1 -0
- package/dist/cli/commands/init.d.ts +24 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +79 -8
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrate.d.ts +1 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/ps.d.ts +17 -0
- package/dist/cli/commands/ps.d.ts.map +1 -0
- package/dist/cli/commands/ps.js +69 -0
- package/dist/cli/commands/ps.js.map +1 -0
- package/dist/cli/commands/registry-prune.d.ts +81 -0
- package/dist/cli/commands/registry-prune.d.ts.map +1 -0
- package/dist/cli/commands/registry-prune.js +163 -0
- package/dist/cli/commands/registry-prune.js.map +1 -0
- package/dist/cli/commands/restart-self.d.ts +111 -0
- package/dist/cli/commands/restart-self.d.ts.map +1 -0
- package/dist/cli/commands/restart-self.js +312 -0
- package/dist/cli/commands/restart-self.js.map +1 -0
- package/dist/cli/commands/routing-doctor-gh.d.ts +29 -0
- package/dist/cli/commands/routing-doctor-gh.d.ts.map +1 -0
- package/dist/cli/commands/routing-doctor-gh.js +103 -0
- package/dist/cli/commands/routing-doctor-gh.js.map +1 -0
- package/dist/cli/commands/routing-doctor.d.ts +183 -0
- package/dist/cli/commands/routing-doctor.d.ts.map +1 -0
- package/dist/cli/commands/routing-doctor.js +504 -0
- package/dist/cli/commands/routing-doctor.js.map +1 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +9 -0
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +16 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/env-files.d.ts.map +1 -1
- package/dist/cli/env-files.js +11 -0
- package/dist/cli/env-files.js.map +1 -1
- package/dist/cli/host-prelude.d.ts +50 -0
- package/dist/cli/host-prelude.d.ts.map +1 -0
- package/dist/cli/host-prelude.js +256 -0
- package/dist/cli/host-prelude.js.map +1 -0
- package/dist/cli/index.js +122 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/proc-scan.d.ts +81 -0
- package/dist/cli/proc-scan.d.ts.map +1 -0
- package/dist/cli/proc-scan.js +172 -0
- package/dist/cli/proc-scan.js.map +1 -0
- package/dist/cli/role-settings-model.d.ts +70 -0
- package/dist/cli/role-settings-model.d.ts.map +1 -0
- package/dist/cli/role-settings-model.js +90 -0
- package/dist/cli/role-settings-model.js.map +1 -0
- package/dist/cli/settings-writer.d.ts +27 -0
- package/dist/cli/settings-writer.d.ts.map +1 -1
- package/dist/cli/settings-writer.js +144 -2
- package/dist/cli/settings-writer.js.map +1 -1
- package/package.json +2 -2
- package/plugin/rules/coordination.md +10 -0
- package/plugin/rules/silent-fallback-hazards.md +19 -4
- package/scripts/check-gh-attribution.sh +34 -10
- package/scripts/emit-turn-receipt.sh +44 -4
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `macf restart-self` — DR-031 piece 3, the VM "be-replaceable" verb.
|
|
3
|
+
*
|
|
4
|
+
* Safely prepares the workspace and spawns a DETACHED relauncher that OUTLIVES
|
|
5
|
+
* the agent's session death, so a watchdog (or the agent itself) can trigger a
|
|
6
|
+
* clean restart without losing uncommitted work. The naive self-kill is suicide
|
|
7
|
+
* (an agent that `tmux kill-session`s its own session dies mid-command with no
|
|
8
|
+
* respawn — DR-031 §"Be-replaceable"); the detached relauncher is what makes the
|
|
9
|
+
* restart survive the kill.
|
|
10
|
+
*
|
|
11
|
+
* Orchestration (in this exact order, ALL under a confirm gate):
|
|
12
|
+
* 1. Resolve config (workspace + the canonical `<project>@<agent>` tmux session).
|
|
13
|
+
* 2. Safety gate — DRY-RUN BY DEFAULT. Without `--confirm` (or with `--dry-run`)
|
|
14
|
+
* it emits the full plan and exits 0 having done NOTHING (no stash/kill/spawn).
|
|
15
|
+
* 3. Prepare the working tree — a MARKED STASH (not auto-commit): only if there
|
|
16
|
+
* are uncommitted *tracked* changes. A marked stash is local, recoverable,
|
|
17
|
+
* non-destructive, and survives a same-host restart; auto-commit risks leaking
|
|
18
|
+
* half-baked state into history.
|
|
19
|
+
* 4. Write a RESUME-note (reason / ts / branch / HEAD / stash-ref + a recovery line).
|
|
20
|
+
* 5. Spawn a DETACHED relauncher that waits for the old session to die, re-sources
|
|
21
|
+
* the host-prelude (if present), then `exec ./claude.sh`.
|
|
22
|
+
* 6. Kill the current tmux session — the actual restart trigger. ONLY in
|
|
23
|
+
* `--confirm` mode, and ONLY as the final step after 3–5 succeeded.
|
|
24
|
+
*
|
|
25
|
+
* ALL side effects flow through `RestartSelfDeps` so `runRestartSelf` is unit-
|
|
26
|
+
* testable with fakes (no real stash / kill / spawn). Production wires the real
|
|
27
|
+
* deps via `createRealDeps`.
|
|
28
|
+
*/
|
|
29
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
30
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
31
|
+
import { join } from 'node:path';
|
|
32
|
+
import { readAgentConfig } from '../config.js';
|
|
33
|
+
/** The three restart drivers (DR-031 §"Be-replaceable" — fault / upgrade / manual). */
|
|
34
|
+
export const RESTART_REASONS = ['fault', 'upgrade', 'manual'];
|
|
35
|
+
/** Coerce an arbitrary `--reason` string to a known reason; defaults to `manual`. */
|
|
36
|
+
export function coerceReason(raw) {
|
|
37
|
+
return RESTART_REASONS.includes(raw ?? '')
|
|
38
|
+
? raw
|
|
39
|
+
: 'manual';
|
|
40
|
+
}
|
|
41
|
+
/** The `--json` state-record (mirrors `fleet doctor`'s versioned shape). */
|
|
42
|
+
export const RESTART_SELF_JSON_SCHEMA_VERSION = 1;
|
|
43
|
+
/** Derive `<project>@<agent>` (the canonical claude.sh self-wrap session), or null. */
|
|
44
|
+
export function resolveSession(opts) {
|
|
45
|
+
const explicit = opts.session?.trim();
|
|
46
|
+
if (explicit)
|
|
47
|
+
return explicit;
|
|
48
|
+
const p = opts.project?.trim();
|
|
49
|
+
const a = opts.agentName?.trim();
|
|
50
|
+
return p && a ? `${p}@${a}` : null;
|
|
51
|
+
}
|
|
52
|
+
/** The marked-stash label: `macf-restart-self/<ISO-8601-ts>/<reason>`. */
|
|
53
|
+
export function stashLabel(iso, reason) {
|
|
54
|
+
return `macf-restart-self/${iso}/${reason}`;
|
|
55
|
+
}
|
|
56
|
+
const RESUME_NOTE_REL = join('.claude', '.macf', 'RESUME-restart-self.md');
|
|
57
|
+
const RELAUNCHER_REL = join('.claude', '.macf', 'restart-self-relauncher.sh');
|
|
58
|
+
const HOST_PRELUDE_REL = join('.claude', '.macf', 'host-prelude.sh');
|
|
59
|
+
const MACF_DIR_REL = join('.claude', '.macf');
|
|
60
|
+
/** The RESUME-note body — what a future session needs to pick the work back up. */
|
|
61
|
+
export function buildResumeNote(args) {
|
|
62
|
+
const { reason, iso, branch, head, stashRef } = args;
|
|
63
|
+
const stashLine = stashRef ?? 'none';
|
|
64
|
+
const recovery = stashRef === null
|
|
65
|
+
? 'Nothing was stashed (working tree was clean) — just resume your task.'
|
|
66
|
+
: `Your uncommitted tracked changes were stashed. Recover with ` +
|
|
67
|
+
`\`git stash apply ${stashRef}\` (or \`git stash list\` to find it).`;
|
|
68
|
+
return [
|
|
69
|
+
'# macf restart-self — RESUME',
|
|
70
|
+
'',
|
|
71
|
+
`- Reason: ${reason}`,
|
|
72
|
+
`- Timestamp: ${iso}`,
|
|
73
|
+
`- Branch: ${branch}`,
|
|
74
|
+
`- HEAD: ${head}`,
|
|
75
|
+
`- Stash: ${stashLine}`,
|
|
76
|
+
'',
|
|
77
|
+
`Resume from here: ${recovery}`,
|
|
78
|
+
'',
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The detached relauncher script. Waits for the OLD session to die (up to ~30s),
|
|
83
|
+
* then `cd`s to the workspace, sources the host-prelude IF it exists (decoupled
|
|
84
|
+
* from DR-031 piece 4 — proceed if absent), and `exec`s the launcher. Uses
|
|
85
|
+
* absolute paths so it does not depend on the dying session's env beyond what it
|
|
86
|
+
* re-establishes.
|
|
87
|
+
*/
|
|
88
|
+
export function buildRelauncherScript(args) {
|
|
89
|
+
const { workspaceDir, session, iso } = args;
|
|
90
|
+
const prelude = join(workspaceDir, HOST_PRELUDE_REL);
|
|
91
|
+
return [
|
|
92
|
+
'#!/usr/bin/env bash',
|
|
93
|
+
`# macf restart-self relauncher (DR-031 piece 3) — generated ${iso}`,
|
|
94
|
+
'# Detached from the dying agent session; waits for it to exit, then relaunches.',
|
|
95
|
+
'set -uo pipefail',
|
|
96
|
+
`WORKSPACE=${shq(workspaceDir)}`,
|
|
97
|
+
`SESSION=${shq(session)}`,
|
|
98
|
+
`PRELUDE=${shq(prelude)}`,
|
|
99
|
+
'',
|
|
100
|
+
'# Wait for the dying session to actually exit (up to ~30s) so the relaunch',
|
|
101
|
+
"# self-wrap re-creates it cleanly instead of attaching to the corpse.",
|
|
102
|
+
'for _ in $(seq 1 60); do',
|
|
103
|
+
' tmux has-session -t "$SESSION" 2>/dev/null || break',
|
|
104
|
+
' sleep 0.5',
|
|
105
|
+
'done',
|
|
106
|
+
'',
|
|
107
|
+
'cd "$WORKSPACE" || exit 1',
|
|
108
|
+
'# host-prelude re-establishes the toolchain (brew/devbox PATH) for a minimal',
|
|
109
|
+
'# (cron/detached) env. Decoupled from DR-031 piece 4 — proceed if absent.',
|
|
110
|
+
'if [ -f "$PRELUDE" ]; then',
|
|
111
|
+
' # shellcheck disable=SC1090',
|
|
112
|
+
' . "$PRELUDE"',
|
|
113
|
+
'fi',
|
|
114
|
+
'exec ./claude.sh',
|
|
115
|
+
'',
|
|
116
|
+
].join('\n');
|
|
117
|
+
}
|
|
118
|
+
/** Single-quote a value for safe shell embedding (closes + escapes any `'`). */
|
|
119
|
+
function shq(value) {
|
|
120
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
121
|
+
}
|
|
122
|
+
/** Human-readable dry-run / confirm plan for the table (non-JSON) output. */
|
|
123
|
+
function renderPlanText(plan, dirty, workspaceDir) {
|
|
124
|
+
const lines = [
|
|
125
|
+
`macf restart-self — ${plan.dry_run ? 'DRY-RUN (default; pass --confirm to act)' : 'EXECUTING (--confirm)'}`,
|
|
126
|
+
'',
|
|
127
|
+
` reason: ${plan.reason}`,
|
|
128
|
+
` workspace: ${workspaceDir}`,
|
|
129
|
+
` session: ${plan.session}`,
|
|
130
|
+
` would stash: ${dirty ? 'yes (uncommitted tracked changes)' : 'no (working tree clean)'}`,
|
|
131
|
+
` resume note: ${plan.resume_note_path}`,
|
|
132
|
+
` relauncher: ${plan.relauncher_path}`,
|
|
133
|
+
` kill session: ${plan.dry_run ? 'NO (dry-run)' : `yes — tmux kill-session -t ${plan.session}`}`,
|
|
134
|
+
];
|
|
135
|
+
if (plan.dry_run) {
|
|
136
|
+
lines.push('', 'No stash, no kill, no spawn performed. Re-run with --confirm to execute.');
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
lines.push('', `Stashed: ${plan.stash_ref ?? 'none'}. Detached relauncher spawned; killing session now.`);
|
|
140
|
+
}
|
|
141
|
+
return lines.join('\n');
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Pure orchestrator. Returns the shell exit code. DRY-RUN BY DEFAULT — only a
|
|
145
|
+
* `--confirm` (and not `--dry-run`) run stashes / writes / spawns / kills.
|
|
146
|
+
* Refuses (exit 1) when the session name cannot be resolved.
|
|
147
|
+
*/
|
|
148
|
+
export async function runRestartSelf(opts, deps) {
|
|
149
|
+
const session = resolveSession(opts);
|
|
150
|
+
if (!session) {
|
|
151
|
+
console.error('macf restart-self: cannot resolve the tmux session name.\n' +
|
|
152
|
+
'Need MACF_PROJECT + MACF_AGENT_NAME (or project/agent_name in ' +
|
|
153
|
+
'.macf/macf-agent.json), or pass an explicit session. Refusing to act.');
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
const { workspaceDir, reason } = opts;
|
|
157
|
+
const resumeNotePath = join(workspaceDir, RESUME_NOTE_REL);
|
|
158
|
+
const relauncherPath = join(workspaceDir, RELAUNCHER_REL);
|
|
159
|
+
const dryRun = opts.dryRun || !opts.confirm;
|
|
160
|
+
const dirty = deps.hasUncommittedTrackedChanges();
|
|
161
|
+
const iso = deps.now().toISOString();
|
|
162
|
+
if (dryRun) {
|
|
163
|
+
const plan = makePlan({ dryRun: true, reason, session, stashRef: null, resumeNotePath, relauncherPath, killed: false });
|
|
164
|
+
if (opts.json)
|
|
165
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
166
|
+
else
|
|
167
|
+
console.log(renderPlanText(plan, dirty, workspaceDir));
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
// --- CONFIRM mode: prepare → note → spawn → kill (each exactly once) ---
|
|
171
|
+
deps.mkdirp(join(workspaceDir, MACF_DIR_REL));
|
|
172
|
+
// 3. Prepare working tree — marked stash, ONLY when there are tracked changes.
|
|
173
|
+
let stashRef = null;
|
|
174
|
+
if (dirty) {
|
|
175
|
+
const result = deps.stash(stashLabel(iso, reason));
|
|
176
|
+
stashRef = result.stashed ? (result.ref ?? 'stash@{0}') : null;
|
|
177
|
+
}
|
|
178
|
+
// 4. RESUME-note.
|
|
179
|
+
const note = buildResumeNote({
|
|
180
|
+
reason,
|
|
181
|
+
iso,
|
|
182
|
+
branch: deps.currentBranch(),
|
|
183
|
+
head: deps.headSha(),
|
|
184
|
+
stashRef,
|
|
185
|
+
});
|
|
186
|
+
deps.writeFile(resumeNotePath, note);
|
|
187
|
+
// 5. Detached relauncher (script + spawn).
|
|
188
|
+
const script = buildRelauncherScript({ workspaceDir, session, iso });
|
|
189
|
+
deps.writeFile(relauncherPath, script, 0o755);
|
|
190
|
+
deps.spawnDetached(relauncherPath, []);
|
|
191
|
+
// Emit the result BEFORE the kill — the kill terminates this very process in
|
|
192
|
+
// production (it kills our own session), so anything after it never prints.
|
|
193
|
+
const plan = makePlan({ dryRun: false, reason, session, stashRef, resumeNotePath, relauncherPath, killed: true });
|
|
194
|
+
if (opts.json)
|
|
195
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
196
|
+
else
|
|
197
|
+
console.log(renderPlanText(plan, dirty, workspaceDir));
|
|
198
|
+
// 6. Kill the current session — the actual restart trigger.
|
|
199
|
+
deps.killSession(session);
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
function makePlan(args) {
|
|
203
|
+
return {
|
|
204
|
+
schema_version: RESTART_SELF_JSON_SCHEMA_VERSION,
|
|
205
|
+
dry_run: args.dryRun,
|
|
206
|
+
reason: args.reason,
|
|
207
|
+
session: args.session,
|
|
208
|
+
stash_ref: args.stashRef,
|
|
209
|
+
resume_note_path: args.resumeNotePath,
|
|
210
|
+
relauncher_path: args.relauncherPath,
|
|
211
|
+
killed: args.killed,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// --- Real-deps factory (production wiring) ---
|
|
215
|
+
/** Run a git command in `cwd`, returning trimmed stdout (throws on non-zero). */
|
|
216
|
+
function git(cwd, args) {
|
|
217
|
+
return execFileSync('git', args, { cwd, encoding: 'utf-8' }).trim();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Real side-effect implementations bound to a workspace dir. Git reads/stash run
|
|
221
|
+
* in `workspaceDir`; the spawn is FULLY DETACHED (`detached: true` opens a new
|
|
222
|
+
* session — the Node equivalent of `setsid` — plus `stdio: 'ignore'` + `unref()`
|
|
223
|
+
* so the relauncher outlives this process when its session is killed).
|
|
224
|
+
*/
|
|
225
|
+
export function createRealDeps(workspaceDir) {
|
|
226
|
+
return {
|
|
227
|
+
now: () => new Date(),
|
|
228
|
+
hasUncommittedTrackedChanges: () => {
|
|
229
|
+
// Tracked, uncommitted (staged OR unstaged); untracked files excluded.
|
|
230
|
+
const out = git(workspaceDir, ['status', '--porcelain', '--untracked-files=no']);
|
|
231
|
+
return out.length > 0;
|
|
232
|
+
},
|
|
233
|
+
currentBranch: () => {
|
|
234
|
+
try {
|
|
235
|
+
return git(workspaceDir, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return '(unknown)';
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
headSha: () => {
|
|
242
|
+
try {
|
|
243
|
+
return git(workspaceDir, ['rev-parse', 'HEAD']);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return '(unknown)';
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
stash: (label) => {
|
|
250
|
+
const out = git(workspaceDir, ['stash', 'push', '-m', label]);
|
|
251
|
+
if (/no local changes/i.test(out))
|
|
252
|
+
return { stashed: false };
|
|
253
|
+
let ref = 'stash@{0}';
|
|
254
|
+
try {
|
|
255
|
+
ref = git(workspaceDir, ['rev-parse', 'stash@{0}']);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
/* keep the symbolic ref */
|
|
259
|
+
}
|
|
260
|
+
return { stashed: true, ref };
|
|
261
|
+
},
|
|
262
|
+
writeFile: (path, content, mode) => {
|
|
263
|
+
writeFileSync(path, content, mode !== undefined ? { mode } : undefined);
|
|
264
|
+
},
|
|
265
|
+
mkdirp: (path) => {
|
|
266
|
+
mkdirSync(path, { recursive: true });
|
|
267
|
+
},
|
|
268
|
+
spawnDetached: (scriptPath, args) => {
|
|
269
|
+
const child = spawn('/usr/bin/env', ['bash', scriptPath, ...args], {
|
|
270
|
+
detached: true,
|
|
271
|
+
stdio: 'ignore',
|
|
272
|
+
});
|
|
273
|
+
child.unref();
|
|
274
|
+
},
|
|
275
|
+
killSession: (session) => {
|
|
276
|
+
try {
|
|
277
|
+
execFileSync('tmux', ['kill-session', '-t', session], { stdio: 'ignore' });
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// The goal — the session's death — is satisfied whether or not the
|
|
281
|
+
// command "succeeds" (e.g. already gone). Never throw on the final step.
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Resolve identity (workspace / project / agent) from env first (the running
|
|
288
|
+
* agent's `claude.sh`-exported values), falling back to `.macf/macf-agent.json`.
|
|
289
|
+
* The canonical session claude.sh self-wraps into is `${MACF_PROJECT}@${MACF_AGENT_NAME}`.
|
|
290
|
+
*/
|
|
291
|
+
export function resolveIdentity(projectDir, env = process.env) {
|
|
292
|
+
const config = readAgentConfig(projectDir);
|
|
293
|
+
const workspaceDir = env['MACF_WORKSPACE_DIR']?.trim() || projectDir;
|
|
294
|
+
const project = env['MACF_PROJECT']?.trim() || config?.project;
|
|
295
|
+
const agentName = env['MACF_AGENT_NAME']?.trim() || config?.agent_name;
|
|
296
|
+
return { workspaceDir, project, agentName };
|
|
297
|
+
}
|
|
298
|
+
/** `macf restart-self` entry point — resolves config, wires real deps, runs. */
|
|
299
|
+
export async function runRestartSelfCommand(projectDir, cliOpts) {
|
|
300
|
+
const { workspaceDir, project, agentName } = resolveIdentity(projectDir);
|
|
301
|
+
const deps = createRealDeps(workspaceDir);
|
|
302
|
+
return runRestartSelf({
|
|
303
|
+
workspaceDir,
|
|
304
|
+
project,
|
|
305
|
+
agentName,
|
|
306
|
+
reason: coerceReason(cliOpts.reason),
|
|
307
|
+
confirm: cliOpts.confirm === true,
|
|
308
|
+
dryRun: cliOpts.dryRun === true,
|
|
309
|
+
json: cliOpts.json === true,
|
|
310
|
+
}, deps);
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=restart-self.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-self.js","sourceRoot":"","sources":["../../../src/cli/commands/restart-self.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CallerPinResult, RoutingConfig } from './routing-doctor.js';
|
|
2
|
+
/**
|
|
3
|
+
* List the App installation's repo-set (DR-030 Q3). Uses the installation token
|
|
4
|
+
* we already mint for the registry reads — `/installation/repositories` returns
|
|
5
|
+
* exactly the repos that token can act on, which IS the routing fleet (an agent
|
|
6
|
+
* unreachable through GitHub can't be in the fleet). Returns `owner/repo` full
|
|
7
|
+
* names. NEVER throws — an access/auth failure resolves to `[]` so the command
|
|
8
|
+
* degrades to "0 fleet repos discovered" rather than crashing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createInstallRepoLister(token: string): () => Promise<readonly string[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Read one repo's caller-pin from `.github/workflows/agent-router.yml`. Returns a
|
|
13
|
+
* tri-status result:
|
|
14
|
+
* - `pinned` — the file exists AND references `groundnuty/macf-actions@<pin>`.
|
|
15
|
+
* - `no-workflow` — the file is absent OR has no macf-actions `uses:` (the repo
|
|
16
|
+
* is App-installed but NOT a routing caller — e.g. the registry
|
|
17
|
+
* owner-repo, or macf-actions itself). Excluded from the
|
|
18
|
+
* consistency verdict (not a divergence).
|
|
19
|
+
* - `error` — the read failed for another reason.
|
|
20
|
+
* NEVER throws.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createCallerPinReader(token: string): (repo: string) => Promise<CallerPinResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Read one repo's `.github/agent-config.json` (the router's per-label config) via
|
|
25
|
+
* the GitHub contents API. Returns the parsed config or `null` (absent / unreadable
|
|
26
|
+
* / malformed). NEVER throws.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createRoutingConfigGhReader(token: string): (repo: string) => Promise<RoutingConfig | null>;
|
|
29
|
+
//# sourceMappingURL=routing-doctor-gh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing-doctor-gh.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/routing-doctor-gh.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAa1E;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAiBvF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAAC,CAqB/F;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,GACZ,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAgBjD"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub-plane I/O leaf for `macf routing doctor` (DR-030 phase-2, macf#568).
|
|
3
|
+
*
|
|
4
|
+
* Isolates the `gh` shell-outs the routing-infra checks need so the orchestration
|
|
5
|
+
* in `routing-doctor.ts` stays PURE + offline-testable (production wires these;
|
|
6
|
+
* tests inject fakes). Every call here is a READ:
|
|
7
|
+
*
|
|
8
|
+
* - `listInstallRepos` — the App INSTALL-SET (DR-030 Q3 repo-set source):
|
|
9
|
+
* `gh api /installation/repositories`. NOT a new
|
|
10
|
+
* `fleet.repos` config file (no parallel, drift-prone
|
|
11
|
+
* surface) — derived from an existing GitHub surface.
|
|
12
|
+
* - `readCallerPin` — a repo's `.github/workflows/agent-router.yml`
|
|
13
|
+
* `uses: groundnuty/macf-actions/...@<pin>` line.
|
|
14
|
+
* - `readRoutingConfigGh` — a repo's `.github/agent-config.json` (the router's
|
|
15
|
+
* per-label config), via the GitHub contents API.
|
|
16
|
+
*
|
|
17
|
+
* The token is forwarded as `GH_TOKEN` in the subprocess env (the house auth
|
|
18
|
+
* posture); none of these has a write path.
|
|
19
|
+
*/
|
|
20
|
+
import { execFile } from 'node:child_process';
|
|
21
|
+
import { promisify } from 'node:util';
|
|
22
|
+
const execFileAsync = promisify(execFile);
|
|
23
|
+
/** The macf-actions caller `uses:` reference we look for in agent-router.yml. */
|
|
24
|
+
const ACTIONS_USES_RE = /uses:\s*groundnuty\/macf-actions\/\.github\/workflows\/agent-router\.yml@(\S+)/;
|
|
25
|
+
/** Decode a GitHub contents-API `.content` base64 blob (newline-wrapped). */
|
|
26
|
+
function decodeGhContent(b64) {
|
|
27
|
+
return Buffer.from(b64.replace(/\s+/g, ''), 'base64').toString('utf-8');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* List the App installation's repo-set (DR-030 Q3). Uses the installation token
|
|
31
|
+
* we already mint for the registry reads — `/installation/repositories` returns
|
|
32
|
+
* exactly the repos that token can act on, which IS the routing fleet (an agent
|
|
33
|
+
* unreachable through GitHub can't be in the fleet). Returns `owner/repo` full
|
|
34
|
+
* names. NEVER throws — an access/auth failure resolves to `[]` so the command
|
|
35
|
+
* degrades to "0 fleet repos discovered" rather than crashing.
|
|
36
|
+
*/
|
|
37
|
+
export function createInstallRepoLister(token) {
|
|
38
|
+
const env = { ...process.env, GH_TOKEN: token };
|
|
39
|
+
return async () => {
|
|
40
|
+
try {
|
|
41
|
+
const { stdout } = await execFileAsync('gh', ['api', '--paginate', '/installation/repositories', '--jq', '.repositories[].full_name'], { encoding: 'utf-8', env, maxBuffer: 32 * 1024 * 1024 });
|
|
42
|
+
return stdout
|
|
43
|
+
.split('\n')
|
|
44
|
+
.map((l) => l.trim())
|
|
45
|
+
.filter((l) => l.length > 0);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read one repo's caller-pin from `.github/workflows/agent-router.yml`. Returns a
|
|
54
|
+
* tri-status result:
|
|
55
|
+
* - `pinned` — the file exists AND references `groundnuty/macf-actions@<pin>`.
|
|
56
|
+
* - `no-workflow` — the file is absent OR has no macf-actions `uses:` (the repo
|
|
57
|
+
* is App-installed but NOT a routing caller — e.g. the registry
|
|
58
|
+
* owner-repo, or macf-actions itself). Excluded from the
|
|
59
|
+
* consistency verdict (not a divergence).
|
|
60
|
+
* - `error` — the read failed for another reason.
|
|
61
|
+
* NEVER throws.
|
|
62
|
+
*/
|
|
63
|
+
export function createCallerPinReader(token) {
|
|
64
|
+
const env = { ...process.env, GH_TOKEN: token };
|
|
65
|
+
return async (repo) => {
|
|
66
|
+
let content;
|
|
67
|
+
try {
|
|
68
|
+
const { stdout } = await execFileAsync('gh', ['api', `repos/${repo}/contents/.github/workflows/agent-router.yml`, '--jq', '.content'], { encoding: 'utf-8', env, maxBuffer: 8 * 1024 * 1024 });
|
|
69
|
+
content = decodeGhContent(stdout);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// 404 (no workflow) is the overwhelmingly common case; treat any read
|
|
73
|
+
// failure as "not a routing caller" rather than a hard error so a single
|
|
74
|
+
// non-caller repo in the install-set doesn't poison the sweep.
|
|
75
|
+
return { repo, pin: null, status: 'no-workflow' };
|
|
76
|
+
}
|
|
77
|
+
const m = ACTIONS_USES_RE.exec(content);
|
|
78
|
+
if (!m)
|
|
79
|
+
return { repo, pin: null, status: 'no-workflow' };
|
|
80
|
+
return { repo, pin: m[1], status: 'pinned' };
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read one repo's `.github/agent-config.json` (the router's per-label config) via
|
|
85
|
+
* the GitHub contents API. Returns the parsed config or `null` (absent / unreadable
|
|
86
|
+
* / malformed). NEVER throws.
|
|
87
|
+
*/
|
|
88
|
+
export function createRoutingConfigGhReader(token) {
|
|
89
|
+
const env = { ...process.env, GH_TOKEN: token };
|
|
90
|
+
return async (repo) => {
|
|
91
|
+
try {
|
|
92
|
+
const { stdout } = await execFileAsync('gh', ['api', `repos/${repo}/contents/.github/agent-config.json`, '--jq', '.content'], { encoding: 'utf-8', env, maxBuffer: 8 * 1024 * 1024 });
|
|
93
|
+
const parsed = JSON.parse(decodeGhContent(stdout));
|
|
94
|
+
if (!parsed || typeof parsed !== 'object' || typeof parsed.agents !== 'object')
|
|
95
|
+
return null;
|
|
96
|
+
return parsed;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=routing-doctor-gh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing-doctor-gh.js","sourceRoot":"","sources":["../../../src/cli/commands/routing-doctor-gh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { AgentInfo, HealthResponse } from '@groundnuty/macf-core';
|
|
2
|
+
export type CallerPinStatus = 'pinned' | 'no-workflow' | 'error';
|
|
3
|
+
/** One repo's caller-pin read. `pinned` ones participate in the consistency verdict. */
|
|
4
|
+
export interface CallerPinResult {
|
|
5
|
+
readonly repo: string;
|
|
6
|
+
readonly pin: string | null;
|
|
7
|
+
readonly status: CallerPinStatus;
|
|
8
|
+
readonly error?: string;
|
|
9
|
+
}
|
|
10
|
+
/** One agent's entry in a repo's `.github/agent-config.json`. */
|
|
11
|
+
export interface RoutingConfigEntry {
|
|
12
|
+
readonly app_name?: string;
|
|
13
|
+
readonly tmux_session?: string;
|
|
14
|
+
}
|
|
15
|
+
/** A repo's `.github/agent-config.json` (the router's per-label config). */
|
|
16
|
+
export interface RoutingConfig {
|
|
17
|
+
readonly agents: Readonly<Record<string, RoutingConfigEntry>>;
|
|
18
|
+
}
|
|
19
|
+
/** Probe a single endpoint's `/health`; null on any failure. Injectable for tests. */
|
|
20
|
+
export type RoutingProbeFn = (host: string, port: number) => Promise<HealthResponse | null>;
|
|
21
|
+
/** Normalize a GitHub login for comparison: lowercase, strip `app/` + `[bot]`. */
|
|
22
|
+
export declare function normalizeLogin(s: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* The expected fleet caller-pin: an explicit override, else the MODAL (most
|
|
25
|
+
* common) pin among the repos that ARE routing callers. `null` when no repo pins
|
|
26
|
+
* macf-actions (nothing to be consistent about).
|
|
27
|
+
*/
|
|
28
|
+
export declare function computeExpectedPin(pinned: readonly string[], override?: string): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Self-skip correctness (#538 check b / #566). `app_name` must be the bot-LOGIN.
|
|
31
|
+
* When an authoritative `expectedBotLogin` is known (the running agent's own
|
|
32
|
+
* `github_app.bot_login`, macf#535), compare exactly (normalized). Otherwise fall
|
|
33
|
+
* back to the structural heuristic: the #566 bug is literally `app_name === <bare
|
|
34
|
+
* routing label>`, so flag that — anything else passes (we can't prove the exact
|
|
35
|
+
* bot-login without an authoritative actor source; stated in the legend).
|
|
36
|
+
*/
|
|
37
|
+
export declare function evaluateSelfSkip(label: string, appName: string | undefined, expectedBotLogin?: string): {
|
|
38
|
+
readonly ok: boolean;
|
|
39
|
+
readonly reason?: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Session-name drift (#? Stage-2 fallback). The CONVENTION check: `tmux_session`
|
|
43
|
+
* must equal `<project>@<label>`. This is a STATIC convention check — it proves the
|
|
44
|
+
* config follows the canonical naming, NOT that it matches the LIVE tmux session
|
|
45
|
+
* (a runtime fact this command can't see). Stated in the legend.
|
|
46
|
+
*/
|
|
47
|
+
export declare function evaluateSession(label: string, tmuxSession: string | undefined, project: string): {
|
|
48
|
+
readonly ok: boolean;
|
|
49
|
+
readonly expected: string;
|
|
50
|
+
readonly reason?: string;
|
|
51
|
+
};
|
|
52
|
+
/** Strict base64: only the base64 alphabet, padded to a multiple of 4. */
|
|
53
|
+
export declare function isStrictBase64(s: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* CA material check (#563). `MACF_CA_CERT` is a readable VARIABLE; validate it is
|
|
56
|
+
* present AND parses — either as a PEM cert block whose body is strict base64, or
|
|
57
|
+
* as a base64-of-PEM blob that decodes to a cert. A present-but-malformed value
|
|
58
|
+
* (truncated / garbled base64) is the #563 class: present ≠ valid.
|
|
59
|
+
*/
|
|
60
|
+
export declare function evaluateCaCert(raw: string | null | undefined): {
|
|
61
|
+
readonly present: boolean;
|
|
62
|
+
readonly valid: boolean;
|
|
63
|
+
readonly reason?: string;
|
|
64
|
+
};
|
|
65
|
+
export type FreshnessState = 'fresh' | 'stale' | 'unreachable' | 'unknown' | 'unregistered';
|
|
66
|
+
/**
|
|
67
|
+
* Classify a registry entry's freshness against the LIVE `/health`:
|
|
68
|
+
* - `/health` answers + instance_id MATCHES → `fresh`.
|
|
69
|
+
* - `/health` answers + instance_id MISMATCH → `stale` (registry points at an
|
|
70
|
+
* older instance; a newer one answered).
|
|
71
|
+
* - `/health` answers but reports no instance_id (older cs) → `unknown`.
|
|
72
|
+
* - `/health` unreachable + heartbeat aged past TTL (DR-031) → `stale` (definitively
|
|
73
|
+
* dead — ungraceful death).
|
|
74
|
+
* - `/health` unreachable, heartbeat fresh/absent → `unreachable` (can't confirm;
|
|
75
|
+
* NOT a verdict failure — liveness is
|
|
76
|
+
* `fleet doctor`'s job, not the plane's).
|
|
77
|
+
*/
|
|
78
|
+
export declare function classifyFreshness(info: AgentInfo, health: HealthResponse | null, now: number, ttlMs: number): FreshnessState;
|
|
79
|
+
export interface RepoPinRow {
|
|
80
|
+
readonly repo: string;
|
|
81
|
+
readonly pin: string | null;
|
|
82
|
+
readonly status: CallerPinStatus;
|
|
83
|
+
/** `true`/`false` for routing callers; `null` for non-callers (excluded from verdict). */
|
|
84
|
+
readonly consistent: boolean | null;
|
|
85
|
+
}
|
|
86
|
+
export interface AgentRow {
|
|
87
|
+
readonly label: string;
|
|
88
|
+
readonly appName: string | null;
|
|
89
|
+
readonly tmuxSession: string | null;
|
|
90
|
+
readonly routable: boolean;
|
|
91
|
+
readonly selfSkipOk: boolean;
|
|
92
|
+
readonly selfSkipReason?: string;
|
|
93
|
+
readonly sessionOk: boolean;
|
|
94
|
+
readonly sessionExpected: string;
|
|
95
|
+
readonly sessionReason?: string;
|
|
96
|
+
readonly freshness: FreshnessState;
|
|
97
|
+
readonly registryInstanceId?: string | null;
|
|
98
|
+
readonly healthInstanceId?: string | null;
|
|
99
|
+
}
|
|
100
|
+
export interface CaCheckResult {
|
|
101
|
+
readonly present: boolean;
|
|
102
|
+
readonly valid: boolean;
|
|
103
|
+
readonly reason?: string;
|
|
104
|
+
}
|
|
105
|
+
export interface RoutingDoctorReport {
|
|
106
|
+
readonly project: string;
|
|
107
|
+
readonly repoPins: readonly RepoPinRow[];
|
|
108
|
+
readonly expectedPin: string | null;
|
|
109
|
+
readonly hasRoutingConfig: boolean;
|
|
110
|
+
readonly agents: readonly AgentRow[];
|
|
111
|
+
readonly ca: CaCheckResult;
|
|
112
|
+
}
|
|
113
|
+
/** Injectable seam so tests drive the command fully offline. */
|
|
114
|
+
export interface RoutingDoctorDeps {
|
|
115
|
+
readonly project: string;
|
|
116
|
+
/** The App install-set (DR-030 Q3 repo-set source). */
|
|
117
|
+
readonly listRepos: () => Promise<readonly string[]>;
|
|
118
|
+
/** Read a repo's macf-actions caller-pin from agent-router.yml. */
|
|
119
|
+
readonly readCallerPin: (repo: string) => Promise<CallerPinResult>;
|
|
120
|
+
/** The CURRENT project's routing config (`.github/agent-config.json`). */
|
|
121
|
+
readonly readRoutingConfig: () => Promise<RoutingConfig | null>;
|
|
122
|
+
/** Registry agents (for routability + freshness). */
|
|
123
|
+
readonly listRegistry: () => Promise<readonly {
|
|
124
|
+
readonly name: string;
|
|
125
|
+
readonly info: AgentInfo;
|
|
126
|
+
}[]>;
|
|
127
|
+
/** mTLS `/health` probe (freshness). */
|
|
128
|
+
readonly probe: RoutingProbeFn;
|
|
129
|
+
/** The `MACF_CA_CERT` variable's raw value. */
|
|
130
|
+
readonly readCaCert: () => Promise<string | null>;
|
|
131
|
+
/** Authoritative expected bot-logins by routing label (partial; heuristic fallback). */
|
|
132
|
+
readonly botLogins?: Readonly<Record<string, string>>;
|
|
133
|
+
/** Explicit expected caller-pin (else the modal pin). */
|
|
134
|
+
readonly expectedPin?: string;
|
|
135
|
+
/** Clock for the heartbeat-TTL staleness math (defaults to `Date.now()`). */
|
|
136
|
+
readonly now?: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Run all five checks. PURE w.r.t. the injected deps — tests pass fakes so nothing
|
|
140
|
+
* hits gh / the registry / the network.
|
|
141
|
+
*/
|
|
142
|
+
export declare function gatherRoutingDoctor(deps: RoutingDoctorDeps): Promise<RoutingDoctorReport>;
|
|
143
|
+
export type RoutingVerdict = 'HEALTHY' | 'DEGRADED' | 'EMPTY';
|
|
144
|
+
export declare function routingVerdict(report: RoutingDoctorReport): RoutingVerdict;
|
|
145
|
+
/** `4 fleet repos (pins consistent); 3 agents (2 routing-OK); CA ✓; routing plane: HEALTHY`. */
|
|
146
|
+
export declare function summaryLine(report: RoutingDoctorReport): string;
|
|
147
|
+
/** ✓ consistent / ✗ divergent / `— n/a` (non-caller). */
|
|
148
|
+
export declare function pinGlyph(consistent: boolean | null): string;
|
|
149
|
+
/** Freshness → short glyph: fresh ✓, stale ✗, unreachable/unknown ?, unregistered —. */
|
|
150
|
+
export declare function freshnessGlyph(s: FreshnessState): string;
|
|
151
|
+
export declare function buildRepoRows(rows: readonly RepoPinRow[]): readonly (readonly string[])[];
|
|
152
|
+
export declare function buildAgentRows(rows: readonly AgentRow[]): readonly (readonly string[])[];
|
|
153
|
+
export declare function formatRepoTable(rows: readonly RepoPinRow[]): string;
|
|
154
|
+
export declare function formatAgentTable(rows: readonly AgentRow[]): string;
|
|
155
|
+
/**
|
|
156
|
+
* The honesty legend — these are STATIC GitHub-plane checks: they prove the
|
|
157
|
+
* routing PLUMBING is wired right, NOT that a message was delivered (that is
|
|
158
|
+
* `--e2e`, a later increment). Carried verbatim in the `--json` `disclaimer`.
|
|
159
|
+
*/
|
|
160
|
+
export declare const HONESTY_LEGEND: string;
|
|
161
|
+
/**
|
|
162
|
+
* `schema_version` is the HARD version contract (DR-006 watchdog, devops #118):
|
|
163
|
+
* a consumer asserts `schema_version === <known>` and refuses an unknown value,
|
|
164
|
+
* so ANY breaking change (rename / removal / a same-name SEMANTIC shift) fails
|
|
165
|
+
* LOUD rather than silently misreading. BUMP on any breaking change; additive-
|
|
166
|
+
* optional fields do NOT bump it. Independent from fleet doctor's schema_version
|
|
167
|
+
* (a separate command, a separate contract).
|
|
168
|
+
*/
|
|
169
|
+
export declare const ROUTING_DOCTOR_JSON_SCHEMA_VERSION = 1;
|
|
170
|
+
export declare function routingDoctorToJson(report: RoutingDoctorReport): unknown;
|
|
171
|
+
export interface RunRoutingDoctorOptions {
|
|
172
|
+
readonly json?: boolean;
|
|
173
|
+
/** Explicit expected caller-pin (else the modal pin across the fleet). */
|
|
174
|
+
readonly expectedPin?: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* `macf routing doctor` entry point. Returns the shell exit code — 1 when the
|
|
178
|
+
* routing plane is DEGRADED, 0 when HEALTHY or EMPTY (matching `fleet doctor` /
|
|
179
|
+
* `macf doctor` "non-zero on problem"). The `--json` body prints regardless; the
|
|
180
|
+
* watchdog reads `summary.verdict`. `deps` is injected by tests.
|
|
181
|
+
*/
|
|
182
|
+
export declare function runRoutingDoctor(projectDir: string, opts?: RunRoutingDoctorOptions, deps?: RoutingDoctorDeps): Promise<number>;
|
|
183
|
+
//# sourceMappingURL=routing-doctor.d.ts.map
|